From: Yuxuan Shui yshui@codeweavers.com
And use the tempo information for time keeping. --- dlls/dmime/miditracks.c | 145 ++++++++++++++++++++++++++++------------ 1 file changed, 103 insertions(+), 42 deletions(-)
diff --git a/dlls/dmime/miditracks.c b/dlls/dmime/miditracks.c index 84c59928e14..d556b20a186 100644 --- a/dlls/dmime/miditracks.c +++ b/dlls/dmime/miditracks.c @@ -85,6 +85,7 @@ struct midi_sequence_track UINT ticks_per_quarter_note; UINT ticks_per_second; UINT event_count; + BOOL has_tempo_events;
struct list events; }; @@ -233,12 +234,8 @@ static HRESULT WINAPI midi_sequence_track_Play( IDirectMusicGraph *graph; HRESULT hr; UINT i; - /* # of quarter notes is the most reasonable common units of time between - * MIDI and other parts of DirectMusic. */ - double number_of_quarter_notes = 0, usec_per_quarter_note = 500000; - /* reference time is in 100 nanoseconds units, or 10 million ticks per - * second. */ - double ref_time = 0; + ULONG ticks = 0; + MUSIC_TIME music_time;
TRACE("(%p, %p, %ld, %ld, %ld, %#lx, %p, %p, %ld)\n", This, state_data, start_time, end_time, time_offset, segment_flags, performance, @@ -260,41 +257,28 @@ static HRESULT WINAPI midi_sequence_track_Play( struct event *item = LIST_ENTRY(cursor, struct event, entry); DMUS_MIDI_PMSG *msg;
+ ticks += item->deltaTime; if (This->ticks_per_quarter_note) - { - ref_time += item->deltaTime * usec_per_quarter_note * 10 / - This->ticks_per_quarter_note; - number_of_quarter_notes += - (double)item->deltaTime / This->ticks_per_quarter_note; - } + music_time = + (ULONGLONG)ticks * DMUS_PPQ / This->ticks_per_quarter_note; else - { - double elapsed = - item->deltaTime * 1000000.0 / This->ticks_per_second; - ref_time += elapsed; - number_of_quarter_notes += elapsed / 10 / usec_per_quarter_note; - } + /* This is the "fake" tempo case, ticks and MUSIC_TIME should mean + * the same thing. */ + music_time = ticks;
- if (item->status == 0xff) - { - hr = handle_meta_events(This, item, &usec_per_quarter_note); - if (hr != S_OK) - break; + if (music_time < start_time || music_time >= end_time) continue; - }
- if ((MUSIC_TIME)number_of_quarter_notes < - (start_time + DMUS_PPQ - 1) / DMUS_PPQ) - continue; - if ((MUSIC_TIME)number_of_quarter_notes >= end_time / DMUS_PPQ) + if (item->status == 0xff || item->status == 0xf0 || + item->status == 0xf7) continue;
if (FAILED(hr = IDirectMusicPerformance_AllocPMsg( performance, sizeof(*msg), (DMUS_PMSG **)&msg))) break;
- msg->dwFlags = DMUS_PMSGF_REFTIME; - msg->rtTime = (REFERENCE_TIME)ref_time; + msg->dwFlags = DMUS_PMSGF_MUSICTIME; + msg->mtTime = music_time; msg->dwPChannel = item->status & 0xf; msg->dwVirtualTrackID = track_id; msg->dwType = DMUS_PMSGT_MIDI; @@ -322,10 +306,51 @@ static HRESULT WINAPI midi_sequence_track_GetParam(IDirectMusicTrack8 *iface, void *param) { struct midi_sequence_track *This = impl_from_IDirectMusicTrack8(iface); + struct event *event; + ULONG ticks = 0; + MUSIC_TIME music_time; + DMUS_TEMPO_PARAM *tempo = param; + + TRACE("(%p, %s, %ld, %p, %p): semi-stub\n", This, debugstr_dmguid(type), + time, next, param); + if (!param) + return E_POINTER; + if (!IsEqualGUID(type, &GUID_TempoParam)) + return DMUS_E_GET_UNSUPPORTED; + if (!This->has_tempo_events) + return DMUS_E_NOT_FOUND; + if (This->ticks_per_quarter_note == 0) + { + /* Return a "fake" tempo to make MIDI time and MUSIC_TIME equivalent. */ + tempo->mtTime = -time; /* set since the start of MUSIC_TIME. */ + if (next) + *next = 0; /* always valid. */ + tempo->dblTempo = (double)This->ticks_per_second / DMUS_PPQ; + return S_OK; + }
- TRACE("(%p, %s, %ld, %p, %p): method not implemented\n", This, - debugstr_dmguid(type), time, next, param); - return E_NOTIMPL; + if (next) + *next = 0; + tempo->dblTempo = 0; + LIST_FOR_EACH_ENTRY(event, &This->events, struct event, entry) + { + ticks += event->deltaTime; + music_time = (ULONGLONG)ticks * DMUS_PPQ / This->ticks_per_quarter_note; + if (event->status == 0xff && event->type == MIDI_META_SET_TEMPO) + { + if (next) + *next = music_time - time; + if (music_time > time) + break; + tempo->mtTime = music_time - time; + tempo->dblTempo = 6e7 / event->data.integer; + } + } + if (tempo->dblTempo == 0) + return DMUS_E_NOT_FOUND; + TRACE("Found tempo %f at time offset %ld\n", tempo->dblTempo, + tempo->mtTime); + return S_OK; }
static HRESULT WINAPI midi_sequence_track_SetParam(IDirectMusicTrack8 *iface, @@ -336,7 +361,32 @@ static HRESULT WINAPI midi_sequence_track_SetParam(IDirectMusicTrack8 *iface,
TRACE("(%p, %s, %ld, %p): method not implemented\n", This, debugstr_dmguid(type), time, param); - return E_NOTIMPL; + if (!This->has_tempo_events) + return DMUS_E_SET_UNSUPPORTED; + + if (IsEqualGUID(type, &GUID_DisableTempo)) + { + if (!param) + return DMUS_E_TYPE_DISABLED; + FIXME("GUID_DisableTempo not handled yet\n"); + return S_OK; + } + if (IsEqualGUID(type, &GUID_EnableTempo)) + { + if (!param) + return DMUS_E_TYPE_DISABLED; + FIXME("GUID_EnableTempo not handled yet\n"); + return S_OK; + } + if (IsEqualGUID(type, &GUID_TempoParam)) + { + if (!param) + return E_POINTER; + FIXME("GUID_TempoParam not handled yet\n"); + return S_OK; + } + + return DMUS_E_SET_UNSUPPORTED; }
static HRESULT WINAPI @@ -344,8 +394,16 @@ midi_sequence_track_IsParamSupported(IDirectMusicTrack8 *iface, REFGUID type) { struct midi_sequence_track *This = impl_from_IDirectMusicTrack8(iface);
- TRACE("(%p, %s): method not implemented\n", This, debugstr_dmguid(type)); - return E_NOTIMPL; + TRACE("(%p, %s): semi-sub\n", This, debugstr_dmguid(type)); + + if (!This->has_tempo_events) + return DMUS_E_GET_UNSUPPORTED; + + if (IsEqualGUID(type, &GUID_DisableTempo) || + IsEqualGUID(type, &GUID_EnableTempo) || + IsEqualGUID(type, &GUID_TempoParam)) + return S_OK; + return DMUS_E_TYPE_UNSUPPORTED; }
static HRESULT WINAPI midi_sequence_track_AddNotificationType( @@ -552,11 +610,11 @@ static HRESULT read_midi_event(IStream *stream, struct event *event, event->data.integer = (event->data.fixed.byte[0] << 16) | (event->data.fixed.byte[1] << 8) | event->data.fixed.byte[2]; - if (event->data.integer == 0) - { - WARN("Invalid tempo value 0\n"); - hr = E_FAIL; - } + if (event->data.integer == 0) + { + WARN("Invalid tempo value 0\n"); + hr = E_FAIL; + } } } } @@ -617,6 +675,7 @@ midi_sequence_track_IPersistStream_Load(IPersistStream *iface, IStream *stream)
TRACE("(%p, %p): stub\n", This, stream);
+ This->has_tempo_events = FALSE; hr = IStream_Read(stream, &magic, sizeof(magic), NULL); if (FAILED(hr)) return hr; @@ -645,6 +704,8 @@ midi_sequence_track_IPersistStream_Load(IPersistStream *iface, IStream *stream) last_status = event->status; is_end_of_track = event->status == 0xff && event->type == MIDI_META_END_OF_TRACK; + if (event->status == 0xff && event->type == MIDI_META_SET_TEMPO) + This->has_tempo_events = TRUE; event = NULL; }
@@ -676,7 +737,7 @@ MUSIC_TIME get_midiseqtrack_length(IDirectMusicTrack8 *iface) struct event *event; MUSIC_TIME length = 0; LIST_FOR_EACH_ENTRY(event, &This->events, struct event, entry) - length += event->deltaTime; + length += event->deltaTime; return length; }