@@ -49,8 +49,20 @@ struct midi_event
49
49
};
50
50
};
51
51
52
+ struct midi_seqtrack_item
53
+ {
54
+ struct list entry ;
55
+ DMUS_IO_SEQ_ITEM item ;
56
+ };
57
+
52
58
struct midi_parser
53
59
{
60
+ IDirectMusicTrack8 * seqtrack ;
61
+ ULONG seqtrack_items_count ;
62
+ struct list seqtrack_items ;
63
+ /* Track the initial note on event generated for a note that is currently on. NULL if the note is off. */
64
+ struct midi_seqtrack_item * note_states [128 * 16 ];
65
+
54
66
IDirectMusicTrack * chordtrack ;
55
67
IDirectMusicTrack * bandtrack ;
56
68
IDirectMusicTrack * tempotrack ;
@@ -174,17 +186,22 @@ static HRESULT read_midi_event(IStream *stream, struct midi_event *event, BYTE *
174
186
}
175
187
176
188
status_type = event -> status & 0xf0 ;
189
+ event -> data [0 ] = byte ;
177
190
if (status_type == MIDI_PROGRAM_CHANGE )
178
191
{
179
- event -> data [0 ] = byte ;
180
192
TRACE ("MIDI program change event status %#02x, data: %#02x, time +%lu\n" , event -> status ,
181
193
event -> data [0 ], delta_time );
182
194
}
183
195
else
184
196
{
185
197
if (status_type != MIDI_CHANNEL_PRESSURE && (hr = stream_read_at_most (stream , & byte , 1 , bytes_left )) != S_OK )
186
198
return hr ;
187
- FIXME ("MIDI event status %#02x, time +%lu, not supported\n" , event -> status , delta_time );
199
+ event -> data [1 ] = byte ;
200
+ if (status_type == MIDI_NOTE_ON || status_type == MIDI_NOTE_OFF )
201
+ TRACE ("MIDI note event status %#02x, data: %#02x, %#02x, time +%lu\n" , event -> status ,
202
+ event -> data [0 ], event -> data [1 ], delta_time );
203
+ else
204
+ FIXME ("MIDI event status %#02x, time +%lu, not supported\n" , event -> status , delta_time );
188
205
}
189
206
190
207
return S_OK ;
@@ -234,11 +251,71 @@ static HRESULT midi_parser_handle_program_change(struct midi_parser *parser, str
234
251
return hr ;
235
252
}
236
253
254
+ static HRESULT midi_parser_handle_note_on_off (struct midi_parser * parser , struct midi_event * event )
255
+ {
256
+ BYTE new_velocity = (event -> status & 0xf0 ) == MIDI_NOTE_OFF ? 0 : event -> data [1 ]; /* DirectMusic doesn't have noteoff velocity */
257
+ BYTE note = event -> data [0 ], channel = event -> status & 0xf ;
258
+ DWORD index = (DWORD )channel * 128 + note ;
259
+ MUSIC_TIME dmusic_time ;
260
+ struct midi_seqtrack_item * note_state = parser -> note_states [index ];
261
+ DMUS_IO_SEQ_ITEM * seq_item ;
262
+ struct midi_seqtrack_item * item ;
263
+
264
+ /* Testing shows there are 2 cases to deal with here:
265
+ *
266
+ * 1. Got note on when the note is already on, generate a NOTE event with
267
+ * new velocity and duration 1.
268
+ * 2. Got note on when the note is off, generate a NOTE event that lasts
269
+ * until the next note off event, intervening note on event doesn't matter.
270
+ */
271
+ if (new_velocity )
272
+ {
273
+ TRACE ("Adding note event at time %lu, note %u, velocity %u\n" , parser -> time , note , new_velocity );
274
+
275
+ dmusic_time = (ULONGLONG )parser -> time * DMUS_PPQ / parser -> division ;
276
+
277
+ item = calloc (1 , sizeof (struct midi_seqtrack_item ));
278
+ if (!item ) return E_OUTOFMEMORY ;
279
+
280
+ seq_item = & item -> item ;
281
+ seq_item -> mtTime = dmusic_time ;
282
+ seq_item -> mtDuration = 1 ;
283
+ seq_item -> dwPChannel = channel ;
284
+ seq_item -> bStatus = MIDI_NOTE_ON ;
285
+ seq_item -> bByte1 = note ;
286
+ seq_item -> bByte2 = new_velocity ;
287
+ list_add_tail (& parser -> seqtrack_items , & item -> entry );
288
+ parser -> seqtrack_items_count ++ ;
289
+
290
+ if (!note_state ) parser -> note_states [index ] = item ;
291
+ }
292
+ else if (note_state )
293
+ {
294
+ note_state -> item .mtDuration = (ULONGLONG )parser -> time * DMUS_PPQ / parser -> division -
295
+ note_state -> item .mtTime ;
296
+ if (note_state -> item .mtDuration == 0 ) note_state -> item .mtDuration = 1 ;
297
+
298
+ TRACE ("Note off at time %lu, note %u, duration %ld\n" , parser -> time , note , note_state -> item .mtDuration );
299
+
300
+ parser -> note_states [index ] = NULL ;
301
+ }
302
+
303
+ return S_OK ;
304
+ }
305
+
306
+ static int midi_seqtrack_item_compare (const void * a , const void * b )
307
+ {
308
+ const DMUS_IO_SEQ_ITEM * item_a = a , * item_b = b ;
309
+ return item_a -> mtTime - item_b -> mtTime ;
310
+ }
311
+
237
312
static HRESULT midi_parser_parse (struct midi_parser * parser , IDirectMusicSegment8 * segment )
238
313
{
239
314
WORD i = 0 ;
240
315
HRESULT hr ;
241
316
MUSIC_TIME music_length = 0 ;
317
+ DMUS_IO_SEQ_ITEM * seq_items = NULL ;
318
+ struct midi_seqtrack_item * item ;
242
319
243
320
TRACE ("(%p, %p): semi-stub\n" , parser , segment );
244
321
@@ -266,35 +343,70 @@ static HRESULT midi_parser_parse(struct midi_parser *parser, IDirectMusicSegment
266
343
parser -> time += event .delta_time ;
267
344
if (event .status == 0xff && event .meta_type == MIDI_META_SET_TEMPO )
268
345
hr = midi_parser_handle_set_tempo (parser , & event );
269
- else if ((event .status & 0xf0 ) == MIDI_PROGRAM_CHANGE )
270
- hr = midi_parser_handle_program_change (parser , & event );
346
+ else
347
+ {
348
+ switch (event .status & 0xf0 )
349
+ {
350
+ case MIDI_NOTE_ON :
351
+ case MIDI_NOTE_OFF :
352
+ hr = midi_parser_handle_note_on_off (parser , & event );
353
+ break ;
354
+ case MIDI_PROGRAM_CHANGE :
355
+ hr = midi_parser_handle_program_change (parser , & event );
356
+ break ;
357
+ default :
358
+ FIXME ("Unhandled MIDI event type %#02x at time +%lu\n" , event .status , parser -> time );
359
+ break ;
360
+ }
361
+ }
271
362
if (FAILED (hr )) break ;
272
363
}
273
364
274
365
if (FAILED (hr )) break ;
366
+
275
367
TRACE ("End of track %u\n" , i );
276
368
if (parser -> time > music_length ) music_length = parser -> time ;
277
369
parser -> time = 0 ;
370
+ memset (parser -> note_states , 0 , sizeof (parser -> note_states ));
278
371
}
372
+ if (FAILED (hr )) return hr ;
279
373
280
374
TRACE ("End of file\n" );
281
375
376
+ if ((seq_items = calloc (parser -> seqtrack_items_count , sizeof (DMUS_IO_SEQ_ITEM ))) == NULL )
377
+ return E_OUTOFMEMORY ;
378
+
379
+ i = 0 ;
380
+ LIST_FOR_EACH_ENTRY (item , & parser -> seqtrack_items , struct midi_seqtrack_item , entry )
381
+ seq_items [i ++ ] = item -> item ;
382
+ sequence_track_set_items (parser -> seqtrack , seq_items , parser -> seqtrack_items_count );
383
+ qsort (seq_items , parser -> seqtrack_items_count , sizeof (DMUS_IO_SEQ_ITEM ), midi_seqtrack_item_compare );
384
+
282
385
music_length = (ULONGLONG )music_length * DMUS_PPQ / parser -> division + 1 ;
283
386
if (SUCCEEDED (hr )) hr = IDirectMusicSegment8_SetLength (segment , music_length );
284
387
if (SUCCEEDED (hr )) hr = IDirectMusicSegment8_InsertTrack (segment , parser -> bandtrack , 0xffff );
285
388
if (SUCCEEDED (hr )) hr = IDirectMusicSegment8_InsertTrack (segment , parser -> chordtrack , 0xffff );
286
389
if (SUCCEEDED (hr ) && parser -> tempotrack )
287
390
hr = IDirectMusicSegment8_InsertTrack (segment , parser -> tempotrack , 0xffff );
391
+ if (SUCCEEDED (hr ))
392
+ hr = IDirectMusicSegment8_InsertTrack (segment , (IDirectMusicTrack * )parser -> seqtrack , 0xffff );
288
393
289
394
return hr ;
290
395
}
291
396
292
397
static void midi_parser_destroy (struct midi_parser * parser )
293
398
{
399
+ struct midi_seqtrack_item * item , * next_item ;
294
400
IStream_Release (parser -> stream );
295
401
if (parser -> bandtrack ) IDirectMusicTrack_Release (parser -> bandtrack );
296
402
if (parser -> chordtrack ) IDirectMusicTrack_Release (parser -> chordtrack );
297
403
if (parser -> tempotrack ) IDirectMusicTrack_Release (parser -> tempotrack );
404
+ if (parser -> seqtrack ) IDirectMusicTrack_Release (parser -> seqtrack );
405
+ LIST_FOR_EACH_ENTRY_SAFE (item , next_item , & parser -> seqtrack_items , struct midi_seqtrack_item , entry )
406
+ {
407
+ list_remove (& item -> entry );
408
+ free (item );
409
+ }
298
410
free (parser );
299
411
}
300
412
@@ -334,6 +446,7 @@ static HRESULT midi_parser_new(IStream *stream, struct midi_parser **out_parser)
334
446
335
447
parser = calloc (1 , sizeof (struct midi_parser ));
336
448
if (!parser ) return E_OUTOFMEMORY ;
449
+ list_init (& parser -> seqtrack_items );
337
450
IStream_AddRef (stream );
338
451
parser -> stream = stream ;
339
452
parser -> division = division ;
@@ -342,6 +455,10 @@ static HRESULT midi_parser_new(IStream *stream, struct midi_parser **out_parser)
342
455
if (SUCCEEDED (hr ))
343
456
hr = CoCreateInstance (& CLSID_DirectMusicChordTrack , NULL , CLSCTX_INPROC_SERVER ,
344
457
& IID_IDirectMusicTrack , (void * * )& parser -> chordtrack );
458
+ if (SUCCEEDED (hr ))
459
+ hr = CoCreateInstance (& CLSID_DirectMusicSeqTrack , NULL , CLSCTX_INPROC_SERVER ,
460
+ & IID_IDirectMusicTrack , (void * * )& parser -> seqtrack );
461
+
345
462
if (FAILED (hr )) midi_parser_destroy (parser );
346
463
else * out_parser = parser ;
347
464
return hr ;
0 commit comments