Skip to content

Commit 5ff9435

Browse files
Yuxuan Shuijulliard
Yuxuan Shui
authored andcommitted
dmime: Parse note on/off events and generate a seqtrack.
1 parent 1df0e34 commit 5ff9435

File tree

4 files changed

+235
-17
lines changed

4 files changed

+235
-17
lines changed

dlls/dmime/dmime_private.h

+2
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ extern BOOL segment_state_has_track(IDirectMusicSegmentState *iface, DWORD track
8989
extern HRESULT wave_track_create_from_chunk(IStream *stream, struct chunk_entry *parent,
9090
IDirectMusicTrack8 **ret_iface);
9191

92+
extern void sequence_track_set_items(IDirectMusicTrack8 *track, DMUS_IO_SEQ_ITEM *items, unsigned int count);
93+
9294
extern HRESULT performance_get_dsound(IDirectMusicPerformance8 *iface, IDirectSound **dsound);
9395
extern HRESULT performance_send_segment_start(IDirectMusicPerformance8 *iface, MUSIC_TIME music_time,
9496
IDirectMusicSegmentState *state);

dlls/dmime/midi.c

+121-4
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,20 @@ struct midi_event
4949
};
5050
};
5151

52+
struct midi_seqtrack_item
53+
{
54+
struct list entry;
55+
DMUS_IO_SEQ_ITEM item;
56+
};
57+
5258
struct midi_parser
5359
{
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+
5466
IDirectMusicTrack *chordtrack;
5567
IDirectMusicTrack *bandtrack;
5668
IDirectMusicTrack *tempotrack;
@@ -174,17 +186,22 @@ static HRESULT read_midi_event(IStream *stream, struct midi_event *event, BYTE *
174186
}
175187

176188
status_type = event->status & 0xf0;
189+
event->data[0] = byte;
177190
if (status_type == MIDI_PROGRAM_CHANGE)
178191
{
179-
event->data[0] = byte;
180192
TRACE("MIDI program change event status %#02x, data: %#02x, time +%lu\n", event->status,
181193
event->data[0], delta_time);
182194
}
183195
else
184196
{
185197
if (status_type != MIDI_CHANNEL_PRESSURE && (hr = stream_read_at_most(stream, &byte, 1, bytes_left)) != S_OK)
186198
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);
188205
}
189206

190207
return S_OK;
@@ -234,11 +251,71 @@ static HRESULT midi_parser_handle_program_change(struct midi_parser *parser, str
234251
return hr;
235252
}
236253

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+
237312
static HRESULT midi_parser_parse(struct midi_parser *parser, IDirectMusicSegment8 *segment)
238313
{
239314
WORD i = 0;
240315
HRESULT hr;
241316
MUSIC_TIME music_length = 0;
317+
DMUS_IO_SEQ_ITEM *seq_items = NULL;
318+
struct midi_seqtrack_item *item;
242319

243320
TRACE("(%p, %p): semi-stub\n", parser, segment);
244321

@@ -266,35 +343,70 @@ static HRESULT midi_parser_parse(struct midi_parser *parser, IDirectMusicSegment
266343
parser->time += event.delta_time;
267344
if (event.status == 0xff && event.meta_type == MIDI_META_SET_TEMPO)
268345
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+
}
271362
if (FAILED(hr)) break;
272363
}
273364

274365
if (FAILED(hr)) break;
366+
275367
TRACE("End of track %u\n", i);
276368
if (parser->time > music_length) music_length = parser->time;
277369
parser->time = 0;
370+
memset(parser->note_states, 0, sizeof(parser->note_states));
278371
}
372+
if (FAILED(hr)) return hr;
279373

280374
TRACE("End of file\n");
281375

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+
282385
music_length = (ULONGLONG)music_length * DMUS_PPQ / parser->division + 1;
283386
if (SUCCEEDED(hr)) hr = IDirectMusicSegment8_SetLength(segment, music_length);
284387
if (SUCCEEDED(hr)) hr = IDirectMusicSegment8_InsertTrack(segment, parser->bandtrack, 0xffff);
285388
if (SUCCEEDED(hr)) hr = IDirectMusicSegment8_InsertTrack(segment, parser->chordtrack, 0xffff);
286389
if (SUCCEEDED(hr) && parser->tempotrack)
287390
hr = IDirectMusicSegment8_InsertTrack(segment, parser->tempotrack, 0xffff);
391+
if (SUCCEEDED(hr))
392+
hr = IDirectMusicSegment8_InsertTrack(segment, (IDirectMusicTrack *)parser->seqtrack, 0xffff);
288393

289394
return hr;
290395
}
291396

292397
static void midi_parser_destroy(struct midi_parser *parser)
293398
{
399+
struct midi_seqtrack_item *item, *next_item;
294400
IStream_Release(parser->stream);
295401
if (parser->bandtrack) IDirectMusicTrack_Release(parser->bandtrack);
296402
if (parser->chordtrack) IDirectMusicTrack_Release(parser->chordtrack);
297403
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+
}
298410
free(parser);
299411
}
300412

@@ -334,6 +446,7 @@ static HRESULT midi_parser_new(IStream *stream, struct midi_parser **out_parser)
334446

335447
parser = calloc(1, sizeof(struct midi_parser));
336448
if (!parser) return E_OUTOFMEMORY;
449+
list_init(&parser->seqtrack_items);
337450
IStream_AddRef(stream);
338451
parser->stream = stream;
339452
parser->division = division;
@@ -342,6 +455,10 @@ static HRESULT midi_parser_new(IStream *stream, struct midi_parser **out_parser)
342455
if (SUCCEEDED(hr))
343456
hr = CoCreateInstance(&CLSID_DirectMusicChordTrack, NULL, CLSCTX_INPROC_SERVER,
344457
&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+
345462
if (FAILED(hr)) midi_parser_destroy(parser);
346463
else *out_parser = parser;
347464
return hr;

dlls/dmime/seqtrack.c

+8
Original file line numberDiff line numberDiff line change
@@ -483,3 +483,11 @@ HRESULT create_dmseqtrack(REFIID lpcGUID, void **ppobj)
483483

484484
return hr;
485485
}
486+
487+
void sequence_track_set_items(IDirectMusicTrack8 *track, DMUS_IO_SEQ_ITEM *items, unsigned int count)
488+
{
489+
struct sequence_track *This = impl_from_IDirectMusicTrack8(track);
490+
free(This->items);
491+
This->items = items;
492+
This->count = count;
493+
}

0 commit comments

Comments
 (0)