From: Anton Baskanov baskanov@gmail.com
--- dlls/dmime/performance.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index f4504c4d36f..a7f63f959e7 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -97,11 +97,11 @@ static inline struct message *message_from_DMUS_PMSG(DMUS_PMSG *msg) return msg ? CONTAINING_RECORD(msg, struct message, msg) : NULL; }
-static void performance_queue_message(struct performance *This, struct message *message, struct list *hint) +static void performance_queue_message(struct performance *This, struct message *message) { struct message *prev;
- LIST_FOR_EACH_ENTRY_REV(prev, hint ? hint : &This->messages, struct message, entry) + LIST_FOR_EACH_ENTRY_REV(prev, &This->messages, struct message, entry) { if (&prev->entry == &This->messages) break; if (prev->msg.rtTime <= message->msg.rtTime) break; @@ -170,12 +170,11 @@ static DWORD WINAPI message_thread_proc(void *args) while ((ptr = list_head(&This->messages))) { struct message *message = LIST_ENTRY(ptr, struct message, entry); - struct list *next = ptr->next; list_remove(&message->entry); list_init(&message->entry);
hr = performance_process_message(This, &message->msg, &timeout); - if (hr == DMUS_S_REQUEUE) performance_queue_message(This, message, next); + if (hr == DMUS_S_REQUEUE) performance_queue_message(This, message); if (hr != S_OK) break; }
@@ -828,7 +827,7 @@ static HRESULT WINAPI performance_SendPMsg(IDirectMusicPerformance8 *iface, DMUS if (hr != DMUS_S_REQUEUE) goto done; }
- performance_queue_message(This, message, NULL); + performance_queue_message(This, message); hr = S_OK; }
From: Anton Baskanov baskanov@gmail.com
--- dlls/dmime/tests/dmime.c | 76 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+)
diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index e1d88c11d62..6a8a3ef0a87 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -2511,11 +2511,26 @@ static void test_performance_pchannel(void)
static void test_performance_tool(void) { + static const DMUS_NOTE_PMSG note60 = + { + .dwSize = sizeof(DMUS_NOTE_PMSG), + .dwFlags = DMUS_PMSGF_REFTIME | DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_QUEUE, + .dwType = DMUS_PMSGT_NOTE, + .mtDuration = 1000, + .wMusicValue = 60, + .bVelocity = 127, + .bFlags = DMUS_NOTEF_NOTEON, + .bPlayModeFlags = DMUS_PLAYMODE_FIXED, + .bMidiValue = 60, + }; IDirectMusicPerformance *performance; IDirectMusicGraph *graph; IDirectMusicTool *tool; DWORD value, types[1]; + DMUS_NOTE_PMSG note; DMUS_PMSG msg = {0}; + REFERENCE_TIME rt; + MUSIC_TIME mt; HRESULT hr;
hr = CoCreateInstance(&CLSID_DirectMusicPerformance, NULL, CLSCTX_INPROC_SERVER, @@ -2546,6 +2561,67 @@ static void test_performance_tool(void) hr = IDirectMusicTool_Flush(tool, performance, &msg, 0); todo_wine ok(hr == S_OK, "got %#lx\n", hr);
+ hr = IDirectMusicPerformance_Init(performance, NULL, NULL, NULL); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = IDirectMusicPerformance_AddPort(performance, NULL); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = IDirectMusicPerformance_GetTime(performance, NULL, &mt); + ok(hr == S_OK, "got %#lx\n", hr); + hr = IDirectMusicPerformance_MusicToReferenceTime(performance, mt, &rt); + ok(hr == S_OK, "got %#lx\n", hr); + + note = note60; + note.rtTime = rt; + note.mtTime = mt; + note.bFlags = 0; + hr = IDirectMusicTool_ProcessPMsg(tool, performance, (DMUS_PMSG *)¬e); + ok(hr == DMUS_S_FREE, "got %#lx\n", hr); + + note = note60; + note.rtTime = rt; + note.mtTime = mt; + note.mtDuration = 0; + hr = IDirectMusicTool_ProcessPMsg(tool, performance, (DMUS_PMSG *)¬e); + ok(hr == DMUS_S_FREE, "got %#lx\n", hr); + + note = note60; + note.rtTime = rt; + note.mtTime = mt; + hr = IDirectMusicTool_ProcessPMsg(tool, performance, (DMUS_PMSG *)¬e); + todo_wine ok(hr == DMUS_S_REQUEUE, "got %#lx\n", hr); + ok(note.dwSize == sizeof(DMUS_NOTE_PMSG), "got %lu\n", note.dwSize); + todo_wine ok(llabs(note.rtTime - rt - 6503906) < 10, "got %I64d\n", note.rtTime - rt); + todo_wine ok(note.mtTime - mt == 999, "got %ld\n", note.mtTime - mt); + ok(note.dwFlags == (DMUS_PMSGF_REFTIME | DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_QUEUE), + "got %#lx\n", note.dwFlags); + ok(!note.dwPChannel, "got %lu\n", note.dwPChannel); + ok(!note.dwVirtualTrackID, "got %lu\n", note.dwVirtualTrackID); + ok(!note.pGraph, "got %p\n", note.pGraph); + ok(note.dwType == DMUS_PMSGT_NOTE, "got %#lx\n", note.dwType); + ok(!note.dwVoiceID, "got %lu\n", note.dwVoiceID); + ok(!note.dwGroupID, "got %lu\n", note.dwGroupID); + ok(!note.punkUser, "got %p\n", note.punkUser); + ok(note.mtDuration == 1000, "got %ld\n", note.mtDuration); + ok(note.wMusicValue == 60, "got %d\n", note.wMusicValue); + ok(!note.wMeasure, "got %d\n", note.wMeasure); + ok(!note.nOffset, "got %d\n", note.nOffset); + ok(!note.bBeat, "got %d\n", note.bBeat); + ok(!note.bGrid, "got %d\n", note.bGrid); + ok(note.bVelocity == 127, "got %d\n", note.bVelocity); + todo_wine ok(!note.bFlags, "got %#x\n", note.bFlags); + ok(!note.bTimeRange, "got %d\n", note.bTimeRange); + ok(!note.bDurRange, "got %d\n", note.bDurRange); + ok(!note.bVelRange, "got %d\n", note.bVelRange); + ok(note.bPlayModeFlags == DMUS_PLAYMODE_FIXED, "got %#x\n", note.bPlayModeFlags); + ok(!note.bSubChordLevel, "got %d\n", note.bSubChordLevel); + ok(note.bMidiValue == 60, "got %d\n", note.bMidiValue); + ok(!note.cTranspose, "got %d\n", note.cTranspose); + + hr = IDirectMusicPerformance_CloseDown(performance); + ok(hr == S_OK, "got %#lx\n", hr); + IDirectMusicGraph_Release(graph); IDirectMusicTool_Release(tool);
From: Anton Baskanov baskanov@gmail.com
--- dlls/dmime/performance.c | 23 ++++++++++++++++++----- dlls/dmime/tests/dmime.c | 4 ++-- 2 files changed, 20 insertions(+), 7 deletions(-)
diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index a7f63f959e7..dd7395f457f 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -2046,12 +2046,25 @@ static HRESULT WINAPI performance_tool_ProcessPMsg(IDirectMusicTool *iface, DMUS_NOTE_PMSG *note = (DMUS_NOTE_PMSG *)msg;
msg->mtTime += note->nOffset; - if (FAILED(hr = performance_send_midi_pmsg(This, msg, DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_IMMEDIATE, - MIDI_NOTE_ON, note->bMidiValue, note->bVelocity))) - WARN("Failed to translate message to MIDI, hr %#lx\n", hr);
- msg->mtTime += note->mtDuration; - if (FAILED(hr = performance_send_midi_pmsg(This, msg, DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_QUEUE, + if (note->bFlags & DMUS_NOTEF_NOTEON) + { + if (FAILED(hr = performance_send_midi_pmsg(This, msg, DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_IMMEDIATE, + MIDI_NOTE_ON, note->bMidiValue, note->bVelocity))) + WARN("Failed to translate message to MIDI, hr %#lx\n", hr); + + if (note->mtDuration) + { + msg->mtTime -= note->nOffset; + msg->mtTime += note->mtDuration; + if (FAILED(hr = IDirectMusicPerformance8_MusicToReferenceTime(performance, msg->mtTime, &msg->rtTime))) + return hr; + note->bFlags &= ~DMUS_NOTEF_NOTEON; + return DMUS_S_REQUEUE; + } + } + + if (FAILED(hr = performance_send_midi_pmsg(This, msg, DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_IMMEDIATE, MIDI_NOTE_OFF, note->bMidiValue, 0))) WARN("Failed to translate message to MIDI, hr %#lx\n", hr);
diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index 6a8a3ef0a87..8290d3e595c 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -2590,7 +2590,7 @@ static void test_performance_tool(void) note.rtTime = rt; note.mtTime = mt; hr = IDirectMusicTool_ProcessPMsg(tool, performance, (DMUS_PMSG *)¬e); - todo_wine ok(hr == DMUS_S_REQUEUE, "got %#lx\n", hr); + ok(hr == DMUS_S_REQUEUE, "got %#lx\n", hr); ok(note.dwSize == sizeof(DMUS_NOTE_PMSG), "got %lu\n", note.dwSize); todo_wine ok(llabs(note.rtTime - rt - 6503906) < 10, "got %I64d\n", note.rtTime - rt); todo_wine ok(note.mtTime - mt == 999, "got %ld\n", note.mtTime - mt); @@ -2610,7 +2610,7 @@ static void test_performance_tool(void) ok(!note.bBeat, "got %d\n", note.bBeat); ok(!note.bGrid, "got %d\n", note.bGrid); ok(note.bVelocity == 127, "got %d\n", note.bVelocity); - todo_wine ok(!note.bFlags, "got %#x\n", note.bFlags); + ok(!note.bFlags, "got %#x\n", note.bFlags); ok(!note.bTimeRange, "got %d\n", note.bTimeRange); ok(!note.bDurRange, "got %d\n", note.bDurRange); ok(!note.bVelRange, "got %d\n", note.bVelRange);
From: Anton Baskanov baskanov@gmail.com
--- dlls/dmime/performance.c | 2 +- dlls/dmime/tests/dmime.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/dlls/dmime/performance.c b/dlls/dmime/performance.c index dd7395f457f..ce73be848c3 100644 --- a/dlls/dmime/performance.c +++ b/dlls/dmime/performance.c @@ -2056,7 +2056,7 @@ static HRESULT WINAPI performance_tool_ProcessPMsg(IDirectMusicTool *iface, if (note->mtDuration) { msg->mtTime -= note->nOffset; - msg->mtTime += note->mtDuration; + msg->mtTime += note->mtDuration - 1; if (FAILED(hr = IDirectMusicPerformance8_MusicToReferenceTime(performance, msg->mtTime, &msg->rtTime))) return hr; note->bFlags &= ~DMUS_NOTEF_NOTEON; diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c index 8290d3e595c..9e407a97548 100644 --- a/dlls/dmime/tests/dmime.c +++ b/dlls/dmime/tests/dmime.c @@ -2592,8 +2592,8 @@ static void test_performance_tool(void) hr = IDirectMusicTool_ProcessPMsg(tool, performance, (DMUS_PMSG *)¬e); ok(hr == DMUS_S_REQUEUE, "got %#lx\n", hr); ok(note.dwSize == sizeof(DMUS_NOTE_PMSG), "got %lu\n", note.dwSize); - todo_wine ok(llabs(note.rtTime - rt - 6503906) < 10, "got %I64d\n", note.rtTime - rt); - todo_wine ok(note.mtTime - mt == 999, "got %ld\n", note.mtTime - mt); + ok(llabs(note.rtTime - rt - 6503906) < 10, "got %I64d\n", note.rtTime - rt); + ok(note.mtTime - mt == 999, "got %ld\n", note.mtTime - mt); ok(note.dwFlags == (DMUS_PMSGF_REFTIME | DMUS_PMSGF_MUSICTIME | DMUS_PMSGF_TOOL_QUEUE), "got %#lx\n", note.dwFlags); ok(!note.dwPChannel, "got %lu\n", note.dwPChannel);
From: Anton Baskanov baskanov@gmail.com
Fixes hanging notes that occur when noteon gets reordered with noteoff. --- dlls/dmsynth/synth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dlls/dmsynth/synth.c b/dlls/dmsynth/synth.c index bbe77e43cc9..222481c8769 100644 --- a/dlls/dmsynth/synth.c +++ b/dlls/dmsynth/synth.c @@ -977,7 +977,7 @@ static HRESULT WINAPI synth_PlayBuffer(IDirectMusicSynth8 *iface,
EnterCriticalSection(&This->cs); LIST_FOR_EACH_ENTRY(next_event, &This->events, struct event, entry) - if (next_event->position >= event->position) break; + if (next_event->position > event->position) break; list_add_before(&next_event->entry, &event->entry); LeaveCriticalSection(&This->cs); }
From: Anton Baskanov baskanov@gmail.com
Different time values can map to the same position. --- dlls/dmsynth/synth.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/dlls/dmsynth/synth.c b/dlls/dmsynth/synth.c index 222481c8769..3eed05f35a0 100644 --- a/dlls/dmsynth/synth.c +++ b/dlls/dmsynth/synth.c @@ -322,6 +322,7 @@ struct preset struct event { struct list entry; + REFERENCE_TIME time; LONGLONG position; BYTE midi[3]; }; @@ -973,11 +974,12 @@ static HRESULT WINAPI synth_PlayBuffer(IDirectMusicSynth8 *iface, { if (!(event = calloc(1, sizeof(*event)))) return E_OUTOFMEMORY; memcpy(event->midi, data, head->cbEvent); + event->time = time + head->rtDelta; event->position = position;
EnterCriticalSection(&This->cs); LIST_FOR_EACH_ENTRY(next_event, &This->events, struct event, entry) - if (next_event->position > event->position) break; + if (next_event->time > event->time) break; list_add_before(&next_event->entry, &event->entry); LeaveCriticalSection(&This->cs); }
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=140051
Your paranoid android.
=== debian11 (32 bit ar:MA report) ===
dmime: dmime.c:3344: Test failed: got punkUser 00000000 dmime.c:3344: Test failed: got guidNotificationType {81f75bc5-4e5d-11d2-bcc7-00a0c922e6eb} dmime.c:3351: Test failed: got punkUser 034D0A10 dmime.c:3351: Test failed: got guidNotificationType {d2ac2899-b39b-11d1-8704-00600893b1bd}
=== debian11 (32 bit ja:JP report) ===
dmime: dmime.c:3344: Test failed: got punkUser 00000000 dmime.c:3344: Test failed: got guidNotificationType {81f75bc5-4e5d-11d2-bcc7-00a0c922e6eb} dmime.c:3351: Test failed: got punkUser 03400F58 dmime.c:3351: Test failed: got guidNotificationType {d2ac2899-b39b-11d1-8704-00600893b1bd}
=== debian11 (32 bit zh:CN report) ===
dmime: dmime.c:3344: Test failed: got punkUser 00000000 dmime.c:3344: Test failed: got guidNotificationType {81f75bc5-4e5d-11d2-bcc7-00a0c922e6eb} dmime.c:3351: Test failed: got punkUser 03510C60 dmime.c:3351: Test failed: got guidNotificationType {d2ac2899-b39b-11d1-8704-00600893b1bd}
=== debian11b (32 bit WoW report) ===
dmime: dmime.c:3344: Test failed: got punkUser 00000000 dmime.c:3344: Test failed: got guidNotificationType {81f75bc5-4e5d-11d2-bcc7-00a0c922e6eb} dmime.c:3351: Test failed: got punkUser 03CE08B0 dmime.c:3351: Test failed: got guidNotificationType {d2ac2899-b39b-11d1-8704-00600893b1bd}
Rémi Bernon (@rbernon) commented about dlls/dmime/performance.c:
return msg ? CONTAINING_RECORD(msg, struct message, msg) : NULL;
}
-static void performance_queue_message(struct performance *This, struct message *message, struct list *hint) +static void performance_queue_message(struct performance *This, struct message *message)
The issue here is that `performance_process_message` returns DMUS_S_REQUEUE too whenever we need to wait, and in this case I think we should put the message back where it was.
We could perhaps keep the old times around, and compare if they have been changed, or use a different return value for `performance_process_message` for this case (keeping the hint).
Rémi Bernon (@rbernon) commented about dlls/dmime/tests/dmime.c:
- ok(hr == DMUS_S_FREE, "got %#lx\n", hr);
- note = note60;
- note.rtTime = rt;
- note.mtTime = mt;
- note.mtDuration = 0;
- hr = IDirectMusicTool_ProcessPMsg(tool, performance, (DMUS_PMSG *)¬e);
- ok(hr == DMUS_S_FREE, "got %#lx\n", hr);
- note = note60;
- note.rtTime = rt;
- note.mtTime = mt;
- hr = IDirectMusicTool_ProcessPMsg(tool, performance, (DMUS_PMSG *)¬e);
- todo_wine ok(hr == DMUS_S_REQUEUE, "got %#lx\n", hr);
- ok(note.dwSize == sizeof(DMUS_NOTE_PMSG), "got %lu\n", note.dwSize);
- todo_wine ok(llabs(note.rtTime - rt - 6503906) < 10, "got %I64d\n", note.rtTime - rt);
This doesn't look flexible enough, and the hardcoded value isn't great. I'd do something like this to leave around 1ms of leeway.
``` todo_wine ok(fabs(note.rtTime - rt - scale_music_time(999, 120.0)) < 10000.0, "got %I64d\n", note.rtTime - rt); ```
Rémi Bernon (@rbernon) commented about dlls/dmime/tests/dmime.c:
hr = IDirectMusicTool_Flush(tool, performance, &msg, 0); todo_wine ok(hr == S_OK, "got %#lx\n", hr);
- hr = IDirectMusicPerformance_Init(performance, NULL, NULL, NULL);
- ok(hr == S_OK, "got %#lx\n", hr);
- hr = IDirectMusicPerformance_AddPort(performance, NULL);
- ok(hr == S_OK, "got %#lx\n", hr);
- hr = IDirectMusicPerformance_GetTime(performance, NULL, &mt);
- ok(hr == S_OK, "got %#lx\n", hr);
- hr = IDirectMusicPerformance_MusicToReferenceTime(performance, mt, &rt);
- ok(hr == S_OK, "got %#lx\n", hr);
```suggestion:-3+0 hr = IDirectMusicPerformance_GetTime(performance, &rt, &mt); ok(hr == S_OK, "got %#lx\n", hr); ```
I also think it'd be better to get the time before each test, to avoid diverging.
Fwiw, I'd just make note60 a local variable, and use its `rtTime` / `mtTime` members directly instead of `rt` / `mt`.
Rémi Bernon (@rbernon) commented about dlls/dmime/tests/dmime.c:
- ok(!note.punkUser, "got %p\n", note.punkUser);
- ok(note.mtDuration == 1000, "got %ld\n", note.mtDuration);
- ok(note.wMusicValue == 60, "got %d\n", note.wMusicValue);
- ok(!note.wMeasure, "got %d\n", note.wMeasure);
- ok(!note.nOffset, "got %d\n", note.nOffset);
- ok(!note.bBeat, "got %d\n", note.bBeat);
- ok(!note.bGrid, "got %d\n", note.bGrid);
- ok(note.bVelocity == 127, "got %d\n", note.bVelocity);
- todo_wine ok(!note.bFlags, "got %#x\n", note.bFlags);
- ok(!note.bTimeRange, "got %d\n", note.bTimeRange);
- ok(!note.bDurRange, "got %d\n", note.bDurRange);
- ok(!note.bVelRange, "got %d\n", note.bVelRange);
- ok(note.bPlayModeFlags == DMUS_PLAYMODE_FIXED, "got %#x\n", note.bPlayModeFlags);
- ok(!note.bSubChordLevel, "got %d\n", note.bSubChordLevel);
- ok(note.bMidiValue == 60, "got %d\n", note.bMidiValue);
- ok(!note.cTranspose, "got %d\n", note.cTranspose);
You could use `check_dmus_note_pmsg` here, with a few changes let it check bFlags and to note60 initializer. I think it'd make adding more checks easier. I'd only leave the exact time checks separately as that helper doesn't validate them very strictly.
In particular, you could check that `nOffset` has no effect on the new times, and that a very small `mtDuration` still returns `DMUS_S_REQUEUE` (and you should then notice that the -1 duration offset isn't always there).
Rémi Bernon (@rbernon) commented about dlls/dmime/performance.c:
while ((ptr = list_head(&This->messages))) { struct message *message = LIST_ENTRY(ptr, struct message, entry);
struct list *next = ptr->next; list_remove(&message->entry); list_init(&message->entry); hr = performance_process_message(This, &message->msg, &timeout);
if (hr == DMUS_S_REQUEUE) performance_queue_message(This, message, next);
if (hr == DMUS_S_REQUEUE) performance_queue_message(This, message); if (hr != S_OK) break;
Changing `performance_process_message` return value to `E_PENDING` for instance, I think something like that could work:
```suggestion:-2+0 hr = performance_process_message(This, &message->msg, &timeout); if (hr == DMUS_S_REQUEUE) performance_queue_message(This, message, NULL); else if (hr == E_PENDING) performance_queue_message(This, message, next); if (FAILED(hr)) break; ```
Then, this still breaks FF VIII intro music for some reason which I haven't investigated yet. More specifically b5fc1a444f5c2d160baa0808259373f7f1951a44 is the first bad commit.
On Mon Nov 20 11:27:02 2023 +0000, Rémi Bernon wrote:
Changing `performance_process_message` return value to `E_PENDING` for instance, I think something like that could work:
hr = performance_process_message(This, &message->msg, &timeout); if (hr == DMUS_S_REQUEUE) performance_queue_message(This, message, NULL); else if (hr == E_PENDING) performance_queue_message(This, message, next); if (FAILED(hr)) break;
Or maybe better, unlink the message only before calling `IDirectMusicTool_ProcessPMsg`.
On Mon Nov 20 12:49:39 2023 +0000, Rémi Bernon wrote:
Then, this still breaks FF VIII intro music for some reason which I haven't investigated yet. More specifically b5fc1a444f5c2d160baa0808259373f7f1951a44 is the first bad commit.
Tracing the messages at various points, here's what I get:
[ffviii-good.log](/uploads/d1206cba21a66e37a0a754b43dd198ac/ffviii-good.log) [ffviii-bad.log](/uploads/33db99890b3c8d29e55f9a1366d6089e/ffviii-bad.log)
On Mon Nov 20 12:06:01 2023 +0000, Rémi Bernon wrote:
Or maybe better, unlink the message only before calling `IDirectMusicTool_ProcessPMsg`.
~~Btw, `performance_queue_message` is also probably not implemented correctly if `DMUS_S_REQUEUE` returns with an updated time.~~ Nvm.
On Mon Nov 20 13:52:32 2023 +0000, Rémi Bernon wrote:
~~Btw, `performance_queue_message` is also probably not implemented correctly if `DMUS_S_REQUEUE` returns with an updated time.~~ Nvm, but `performance_process_message` probably shouldn't loop.
Something like https://gitlab.winehq.org/rbernon/wine/-/commit/79b40ed9008d21ae245a9fd4a286... makes the MR work fine with FF VIII.