Combined with https://gitlab.winehq.org/wine/wine/-/merge_requests/4134 this should allow FF VIII intro video music to play, quite close to native (https://www.youtube.com/watch?v=A9pDAFS4oOA). There's a couple of differences in the note sustain duration, which I'm not completely sure why but doesn't hurt too much the experience.
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/dmsynth/synthsink.c | 153 ++++++++++++++++++++++++++++++++++- dlls/dmsynth/tests/dmsynth.c | 4 +- 2 files changed, 154 insertions(+), 3 deletions(-)
diff --git a/dlls/dmsynth/synthsink.c b/dlls/dmsynth/synthsink.c index 28cee0c3a28..fb7ea5fd283 100644 --- a/dlls/dmsynth/synthsink.c +++ b/dlls/dmsynth/synthsink.c @@ -25,6 +25,8 @@
WINE_DEFAULT_DEBUG_CHANNEL(dmsynth);
+#define BUFFER_SUBDIVISIONS 8 + struct synth_sink { IDirectMusicSynthSink IDirectMusicSynthSink_iface; @@ -35,10 +37,17 @@ struct synth_sink IReferenceClock *master_clock; IDirectMusicSynth *synth; /* No reference hold! */ IDirectSound *dsound; + IDirectSoundBuffer *dsound_buffer;
BOOL active; REFERENCE_TIME activate_time; + + CRITICAL_SECTION cs; REFERENCE_TIME latency_time; + + DWORD written; /* number of bytes written out */ + HANDLE stop_event; + HANDLE render_thread; };
static inline struct synth_sink *impl_from_IDirectMusicSynthSink(IDirectMusicSynthSink *iface) @@ -65,8 +74,93 @@ static void synth_sink_get_format(struct synth_sink *This, WAVEFORMATEX *format) } }
+struct render_thread_params +{ + struct synth_sink *sink; + IDirectMusicSynth *synth; + IDirectSoundBuffer *buffer; + HANDLE started_event; +}; + +static DWORD CALLBACK synth_sink_render_thread(void *args) +{ + struct render_thread_params *params = args; + DSBCAPS caps = {.dwSize = sizeof(DSBCAPS)}; + IDirectSoundBuffer *buffer = params->buffer; + IDirectMusicSynth *synth = params->synth; + struct synth_sink *sink = params->sink; + IDirectSoundNotify *notify; + HANDLE buffer_event; + HRESULT hr; + + TRACE("Starting thread, args %p\n", args); + SetThreadDescription(GetCurrentThread(), L"wine_dmsynth_sink"); + + if (FAILED(hr = IDirectSoundBuffer_Stop(buffer))) + ERR("Failed to stop sound buffer, hr %#lx.\n", hr); + + if (!(buffer_event = CreateEventW(NULL, FALSE, FALSE, NULL))) + ERR("Failed to create buffer event, error %lu\n", GetLastError()); + else if (FAILED(hr = IDirectSoundBuffer_GetCaps(buffer, &caps))) + ERR("Failed to query sound buffer caps, hr %#lx.\n", hr); + else if (FAILED(hr = IDirectSoundBuffer_QueryInterface(buffer, &IID_IDirectSoundNotify, + (void **)¬ify))) + ERR("Failed to query IDirectSoundNotify iface, hr %#lx.\n", hr); + else + { + DSBPOSITIONNOTIFY positions[BUFFER_SUBDIVISIONS] = {{.dwOffset = 0, .hEventNotify = buffer_event}}; + int i; + + for (i = 1; i < ARRAY_SIZE(positions); ++i) + { + positions[i] = positions[i - 1]; + positions[i].dwOffset += caps.dwBufferBytes / ARRAY_SIZE(positions); + } + + if (FAILED(hr = IDirectSoundNotify_SetNotificationPositions(notify, + ARRAY_SIZE(positions), positions))) + ERR("Failed to set notification positions, hr %#lx\n", hr); + + IDirectSoundNotify_Release(notify); + } + + if (FAILED(hr = IDirectSoundBuffer_Play(buffer, 0, 0, DSBPLAY_LOOPING))) + ERR("Failed to start sound buffer, hr %#lx.\n", hr); + SetEvent(params->started_event); + + while (hr == S_OK) + { + HANDLE handles[] = {sink->stop_event, buffer_event}; + DWORD ret; + + if (!(ret = WaitForMultipleObjects(ARRAY_SIZE(handles), handles, FALSE, INFINITE)) + || ret >= ARRAY_SIZE(handles)) + { + ERR("WaitForMultipleObjects returned %lu\n", ret); + hr = HRESULT_FROM_WIN32(ret); + break; + } + } + + if (FAILED(hr)) + { + ERR("Thread unexpected termination, hr %#lx\n", hr); + return hr; + } + + IDirectSoundBuffer_Release(buffer); + IDirectMusicSynth_Release(synth); + CloseHandle(buffer_event); + + return 0; +} + static HRESULT synth_sink_activate(struct synth_sink *This) { + IDirectMusicSynthSink *iface = &This->IDirectMusicSynthSink_iface; + DSBUFFERDESC desc = {.dwSize = sizeof(DSBUFFERDESC)}; + struct render_thread_params params; + WAVEFORMATEX format; HRESULT hr;
if (!This->synth) return DMUS_E_SYNTHNOTCONFIGURED; @@ -77,13 +171,54 @@ static HRESULT synth_sink_activate(struct synth_sink *This) if (FAILED(hr = IReferenceClock_GetTime(This->master_clock, &This->activate_time))) return hr; This->latency_time = This->activate_time;
+ if ((params.buffer = This->dsound_buffer)) + IDirectMusicBuffer_AddRef(params.buffer); + else + { + synth_sink_get_format(This, &format); + desc.lpwfxFormat = (WAVEFORMATEX *)&format; + desc.dwBufferBytes = format.nAvgBytesPerSec; + if (FAILED(hr = IDirectMusicSynthSink_GetDesiredBufferSize(iface, &desc.dwBufferBytes))) + ERR("Failed to get desired buffer size, hr %#lx\n", hr); + + desc.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLPOSITIONNOTIFY; + if (FAILED(hr = IDirectSound8_CreateSoundBuffer(This->dsound, &desc, ¶ms.buffer, NULL))) + { + ERR("Failed to create sound buffer, hr %#lx.\n", hr); + return hr; + } + } + + params.sink = This; + params.synth = This->synth; + IDirectMusicSynth_AddRef(This->synth); + + if (!(params.started_event = CreateEventW(NULL, FALSE, FALSE, NULL)) + || !(This->render_thread = CreateThread(NULL, 0, synth_sink_render_thread, ¶ms, 0, NULL))) + { + ERR("Failed to create render thread, error %lu\n", GetLastError()); + hr = HRESULT_FROM_WIN32(GetLastError()); + IDirectSoundBuffer_Release(params.buffer); + IDirectMusicSynth_Release(params.synth); + CloseHandle(params.started_event); + return hr; + } + + WaitForSingleObject(params.started_event, INFINITE); + CloseHandle(params.started_event); This->active = TRUE; return S_OK; }
static HRESULT synth_sink_deactivate(struct synth_sink *This) { + if (!This->active) return S_OK; + + SetEvent(This->stop_event); + WaitForSingleObject(This->render_thread, INFINITE); + This->render_thread = NULL; This->active = FALSE; + return S_OK; }
@@ -133,8 +268,15 @@ static ULONG WINAPI synth_sink_Release(IDirectMusicSynthSink *iface) TRACE("(%p): new ref = %lu\n", This, ref);
if (!ref) { + if (This->active) + IDirectMusicSynthSink_Activate(iface, FALSE); if (This->master_clock) IReferenceClock_Release(This->master_clock); + + This->cs.DebugInfo->Spare[0] = 0; + DeleteCriticalSection(&This->cs); + CloseHandle(This->stop_event); + free(This); }
@@ -238,15 +380,17 @@ static HRESULT WINAPI synth_sink_SetDirectSound(IDirectMusicSynthSink *iface,
TRACE("(%p)->(%p, %p)\n", This, dsound, dsound_buffer);
- if (dsound_buffer) FIXME("Ignoring IDirectSoundBuffer parameter.\n"); if (This->active) return DMUS_E_SYNTHACTIVE;
if (This->dsound) IDirectSound_Release(This->dsound); This->dsound = NULL; + if (This->dsound_buffer) IDirectSoundBuffer_Release(This->dsound_buffer); + This->dsound_buffer = NULL; if (!dsound) return S_OK;
if (!This->synth) return DMUS_E_SYNTHNOTCONFIGURED; if ((This->dsound = dsound)) IDirectSound_AddRef(This->dsound); + if ((This->dsound_buffer = dsound_buffer)) IDirectSoundBuffer_AddRef(This->dsound_buffer);
return S_OK; } @@ -413,7 +557,10 @@ static HRESULT WINAPI latency_clock_GetTime(IReferenceClock *iface, REFERENCE_TI
if (!time) return E_INVALIDARG; if (!This->active) return E_FAIL; + + EnterCriticalSection(&This->cs); *time = This->latency_time; + LeaveCriticalSection(&This->cs);
return S_OK; } @@ -462,6 +609,10 @@ HRESULT synth_sink_create(IUnknown **ret_iface) obj->IReferenceClock_iface.lpVtbl = &latency_clock_vtbl; obj->ref = 1;
+ obj->stop_event = CreateEventW(NULL, FALSE, FALSE, NULL); + InitializeCriticalSection(&obj->cs); + obj->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": cs"); + TRACE("Created DirectMusicSynthSink %p\n", obj); *ret_iface = (IUnknown *)&obj->IDirectMusicSynthSink_iface; return S_OK; diff --git a/dlls/dmsynth/tests/dmsynth.c b/dlls/dmsynth/tests/dmsynth.c index 25ecff97048..6529bdeac34 100644 --- a/dlls/dmsynth/tests/dmsynth.c +++ b/dlls/dmsynth/tests/dmsynth.c @@ -1294,7 +1294,7 @@ static void test_IDirectMusicSynthSink(void) hr = IDirectMusicSynthSink_Activate(sink, TRUE); ok(hr == DMUS_E_SYNTHACTIVE, "got %#lx\n", hr); ref = get_refcount(synth); - ok(ref == 1, "got %#lx\n", ref); + todo_wine ok(ref == 1, "got %#lx\n", ref);
hr = IDirectMusicSynthSink_GetDesiredBufferSize(sink, &size); ok(hr == S_OK, "got %#lx\n", hr); @@ -1328,7 +1328,7 @@ static void test_IDirectMusicSynthSink(void) hr = IDirectMusicSynthSink_Init(sink, NULL); ok(hr == S_OK, "got %#lx\n", hr); ref = get_refcount(synth); - ok(ref == 1, "got %#lx\n", ref); + todo_wine ok(ref == 1, "got %#lx\n", ref); hr = IDirectMusicSynthSink_Activate(sink, TRUE); ok(hr == DMUS_E_SYNTHNOTCONFIGURED, "got %#lx\n", hr);
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/dmsynth/synthsink.c | 148 ++++++++++++++++++++++++++++++++++- dlls/dmsynth/tests/dmsynth.c | 2 +- 2 files changed, 147 insertions(+), 3 deletions(-)
diff --git a/dlls/dmsynth/synthsink.c b/dlls/dmsynth/synthsink.c index fb7ea5fd283..d0a89358652 100644 --- a/dlls/dmsynth/synthsink.c +++ b/dlls/dmsynth/synthsink.c @@ -25,7 +25,7 @@
WINE_DEFAULT_DEBUG_CHANNEL(dmsynth);
-#define BUFFER_SUBDIVISIONS 8 +#define BUFFER_SUBDIVISIONS 100
struct synth_sink { @@ -74,6 +74,128 @@ static void synth_sink_get_format(struct synth_sink *This, WAVEFORMATEX *format) } }
+static HRESULT synth_sink_write_data(struct synth_sink *sink, IDirectSoundBuffer *buffer, + DSBCAPS *caps, WAVEFORMATEX *format, const void *data, DWORD size) +{ + DWORD write_end, size1, size2, current_pos; + void *data1, *data2; + HRESULT hr; + + TRACE("sink %p, data %p, size %#lx\n", sink, data, size); + + current_pos = sink->written % caps->dwBufferBytes; + + if (sink->written) + { + DWORD play_pos, write_pos; + + if (FAILED(hr = IDirectSoundBuffer_GetCurrentPosition(buffer, &play_pos, &write_pos))) return hr; + + if (current_pos - play_pos <= write_pos - play_pos) + { + ERR("Underrun detected, sink %p, play pos %#lx, write pos %#lx, current pos %#lx!\n", + buffer, play_pos, write_pos, current_pos); + current_pos = write_pos; + } + + write_end = (current_pos + size) % caps->dwBufferBytes; + if (write_end - current_pos >= play_pos - current_pos) return S_FALSE; + } + + if (FAILED(hr = IDirectSoundBuffer_Lock(buffer, current_pos, size, + &data1, &size1, &data2, &size2, 0))) + { + ERR("IDirectSoundBuffer_Lock failed, hr %#lx\n", hr); + return hr; + } + + if (!data) + { + memset(data1, format->wBitsPerSample == 8 ? 128 : 0, size1); + memset(data2, format->wBitsPerSample == 8 ? 128 : 0, size2); + } + else + { + memcpy(data1, data, size1); + data = (char *)data + size1; + memcpy(data2, data, size2); + } + + if (FAILED(hr = IDirectSoundBuffer_Unlock(buffer, data1, size1, data2, size2))) + { + ERR("IDirectSoundBuffer_Unlock failed, hr %#lx\n", hr); + return hr; + } + + sink->written += size; + TRACE("Written size %#lx, total %#lx\n", size, sink->written); + return S_OK; +} + +static HRESULT synth_sink_wait_play_end(struct synth_sink *sink, IDirectSoundBuffer *buffer, + DSBCAPS *caps, WAVEFORMATEX *format, HANDLE buffer_event) +{ + DWORD current_pos, start_pos, play_pos, written, played = 0; + HRESULT hr; + + if (FAILED(hr = IDirectSoundBuffer_GetCurrentPosition(buffer, &start_pos, NULL))) + { + ERR("IDirectSoundBuffer_GetCurrentPosition failed, hr %#lx\n", hr); + return hr; + } + + current_pos = sink->written % caps->dwBufferBytes; + written = current_pos - start_pos + (current_pos < start_pos ? caps->dwBufferBytes : 0); + if (FAILED(hr = synth_sink_write_data(sink, buffer, caps, format, NULL, caps->dwBufferBytes / 2))) return hr; + + for (;;) + { + DWORD ret; + + if (FAILED(hr = IDirectSoundBuffer_GetCurrentPosition(buffer, &play_pos, NULL))) + { + ERR("IDirectSoundBuffer_GetCurrentPosition failed, hr %#lx\n", hr); + return hr; + } + + played += play_pos - start_pos + (play_pos < start_pos ? caps->dwBufferBytes : 0); + if (played >= written) break; + + TRACE("Waiting for EOS, start_pos %#lx, play_pos %#lx, written %#lx, played %#lx\n", + start_pos, play_pos, written, played); + if ((ret = WaitForMultipleObjects(1, &buffer_event, FALSE, INFINITE))) + { + ERR("WaitForMultipleObjects returned %#lx\n", ret); + break; + } + + start_pos = play_pos; + } + + return S_OK; +} + +static HRESULT synth_sink_render_data(struct synth_sink *sink, IDirectMusicSynth *synth, + IDirectSoundBuffer *buffer, WAVEFORMATEX *format, short *samples, DWORD samples_size) +{ + REFERENCE_TIME sample_time; + HRESULT hr; + + if (FAILED(hr = IDirectMusicSynth_Render(synth, samples, samples_size / format->nBlockAlign, + sink->written / format->nBlockAlign))) + ERR("Failed to render synthesizer samples, hr %#lx\n", hr); + + if (FAILED(hr = IDirectMusicSynthSink_SampleToRefTime(&sink->IDirectMusicSynthSink_iface, + (sink->written + samples_size) / format->nBlockAlign, &sample_time))) + ERR("Failed to convert sample position to time, hr %#lx\n", hr); + + EnterCriticalSection(&sink->cs); + sink->latency_time = sample_time; + LeaveCriticalSection(&sink->cs); + + return hr; +} + struct render_thread_params { struct synth_sink *sink; @@ -90,7 +212,10 @@ static DWORD CALLBACK synth_sink_render_thread(void *args) IDirectMusicSynth *synth = params->synth; struct synth_sink *sink = params->sink; IDirectSoundNotify *notify; + WAVEFORMATEX format; HANDLE buffer_event; + DWORD samples_size; + short *samples; HRESULT hr;
TRACE("Starting thread, args %p\n", args); @@ -103,6 +228,8 @@ static DWORD CALLBACK synth_sink_render_thread(void *args) ERR("Failed to create buffer event, error %lu\n", GetLastError()); else if (FAILED(hr = IDirectSoundBuffer_GetCaps(buffer, &caps))) ERR("Failed to query sound buffer caps, hr %#lx.\n", hr); + else if (FAILED(hr = IDirectSoundBuffer_GetFormat(buffer, &format, sizeof(format), NULL))) + ERR("Failed to query sound buffer format, hr %#lx.\n", hr); else if (FAILED(hr = IDirectSoundBuffer_QueryInterface(buffer, &IID_IDirectSoundNotify, (void **)¬ify))) ERR("Failed to query IDirectSoundNotify iface, hr %#lx.\n", hr); @@ -124,15 +251,28 @@ static DWORD CALLBACK synth_sink_render_thread(void *args) IDirectSoundNotify_Release(notify); }
+ samples_size = caps.dwBufferBytes / BUFFER_SUBDIVISIONS; + if (!(samples = malloc(samples_size))) + { + ERR("Failed to allocate memory for samples\n"); + goto done; + } + + if (FAILED(hr = synth_sink_render_data(sink, synth, buffer, &format, samples, samples_size))) + ERR("Failed to render initial buffer data, hr %#lx.\n", hr); if (FAILED(hr = IDirectSoundBuffer_Play(buffer, 0, 0, DSBPLAY_LOOPING))) ERR("Failed to start sound buffer, hr %#lx.\n", hr); SetEvent(params->started_event);
- while (hr == S_OK) + while (SUCCEEDED(hr) && SUCCEEDED(hr = synth_sink_write_data(sink, buffer, + &caps, &format, samples, samples_size))) { HANDLE handles[] = {sink->stop_event, buffer_event}; DWORD ret;
+ if (hr == S_OK) /* if successfully written, render more data */ + hr = synth_sink_render_data(sink, synth, buffer, &format, samples, samples_size); + if (!(ret = WaitForMultipleObjects(ARRAY_SIZE(handles), handles, FALSE, INFINITE)) || ret >= ARRAY_SIZE(handles)) { @@ -148,6 +288,10 @@ static DWORD CALLBACK synth_sink_render_thread(void *args) return hr; }
+ synth_sink_wait_play_end(sink, buffer, &caps, &format, buffer_event); + free(samples); + +done: IDirectSoundBuffer_Release(buffer); IDirectMusicSynth_Release(synth); CloseHandle(buffer_event); diff --git a/dlls/dmsynth/tests/dmsynth.c b/dlls/dmsynth/tests/dmsynth.c index 6529bdeac34..ad09a651094 100644 --- a/dlls/dmsynth/tests/dmsynth.c +++ b/dlls/dmsynth/tests/dmsynth.c @@ -1317,7 +1317,7 @@ static void test_IDirectMusicSynthSink(void) tmp_time = time; hr = IReferenceClock_GetTime(latency_clock, &tmp_time); ok(hr == S_OK, "got %#lx\n", hr); - todo_wine ok(tmp_time > time, "got %I64d\n", tmp_time - time); + ok(tmp_time > time, "got %I64d\n", tmp_time - time); ok(tmp_time - time <= 2000000, "got %I64d\n", tmp_time - time);
/* setting the clock while active is fine */
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/dmsynth/synth.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/dlls/dmsynth/synth.c b/dlls/dmsynth/synth.c index 941fc465d17..5fcc1e9d075 100644 --- a/dlls/dmsynth/synth.c +++ b/dlls/dmsynth/synth.c @@ -1570,7 +1570,10 @@ static fluid_preset_t *synth_sfont_get_preset(fluid_sfont_t *fluid_sfont, int ba EnterCriticalSection(&synth->cs);
LIST_FOR_EACH_ENTRY(instrument, &synth->instruments, struct instrument, entry) - if (instrument->patch == patch) break; + { + if (bank == 128 && instrument->patch == (0x80000000 | patch)) break; + else if (instrument->patch == ((bank << 8) | patch)) break; + }
if (&instrument->entry == &synth->instruments) {
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/dmsynth/synth.c | 40 ++++++++++++++++++---------------------- 1 file changed, 18 insertions(+), 22 deletions(-)
diff --git a/dlls/dmsynth/synth.c b/dlls/dmsynth/synth.c index 5fcc1e9d075..30a2dd6efe9 100644 --- a/dlls/dmsynth/synth.c +++ b/dlls/dmsynth/synth.c @@ -1302,7 +1302,7 @@ static int synth_preset_get_num(fluid_preset_t *fluid_preset) return instrument->patch; }
-static BOOL fluid_gen_from_connection(CONNECTION *conn, UINT *gen) +static BOOL gen_from_connection(const CONNECTION *conn, UINT *gen) { switch (conn->usDestination) { @@ -1333,7 +1333,7 @@ static BOOL fluid_gen_from_connection(CONNECTION *conn, UINT *gen) } }
-static BOOL set_gen_from_connection(fluid_voice_t *fluid_voice, CONNECTION *conn) +static BOOL set_gen_from_connection(fluid_voice_t *fluid_voice, const CONNECTION *conn) { UINT gen;
@@ -1342,7 +1342,7 @@ static BOOL set_gen_from_connection(fluid_voice_t *fluid_voice, CONNECTION *conn
if (conn->usSource == CONN_SRC_NONE) { - if (!fluid_gen_from_connection(conn, &gen)) return FALSE; + if (!gen_from_connection(conn, &gen)) return FALSE; } if (conn->usSource == CONN_SRC_KEYNUMBER) { @@ -1391,7 +1391,7 @@ static BOOL set_gen_from_connection(fluid_voice_t *fluid_voice, CONNECTION *conn return TRUE; }
-static BOOL fluid_source_from_connection(USHORT source, USHORT transform, UINT *fluid_source, UINT *fluid_flags) +static BOOL mod_from_connection(USHORT source, USHORT transform, UINT *fluid_source, UINT *fluid_flags) { UINT flags = FLUID_MOD_GC; if (source >= CONN_SRC_CC1 && source <= CONN_SRC_CC1 + 0x7f) @@ -1425,8 +1425,8 @@ static BOOL fluid_source_from_connection(USHORT source, USHORT transform, UINT * return TRUE; }
-static BOOL add_mod_from_connection(fluid_voice_t *fluid_voice, CONNECTION *conn, UINT src1, UINT flags1, - UINT src2, UINT flags2) +static BOOL add_mod_from_connection(fluid_voice_t *fluid_voice, const CONNECTION *conn, + UINT src1, UINT flags1, UINT src2, UINT flags2) { fluid_mod_t *mod; UINT gen = -1; @@ -1453,7 +1453,7 @@ static BOOL add_mod_from_connection(fluid_voice_t *fluid_voice, CONNECTION *conn flags2 = 0; }
- if (gen == -1 && !fluid_gen_from_connection(conn, &gen)) return FALSE; + if (gen == -1 && !gen_from_connection(conn, &gen)) return FALSE;
if (!(mod = new_fluid_mod())) return FALSE; fluid_mod_set_source1(mod, src1, flags1); @@ -1465,35 +1465,28 @@ static BOOL add_mod_from_connection(fluid_voice_t *fluid_voice, CONNECTION *conn return TRUE; }
-static void fluid_voice_add_articulation(fluid_voice_t *fluid_voice, struct articulation *articulation) +static void add_voice_connections(fluid_voice_t *fluid_voice, const CONNECTIONLIST *list, + const CONNECTION *connections) { UINT i;
- for (i = 0; i < articulation->list.cConnections; i++) + for (i = 0; i < list->cConnections; i++) { UINT src1 = FLUID_MOD_NONE, flags1 = 0, src2 = FLUID_MOD_NONE, flags2 = 0; - CONNECTION *conn = articulation->connections + i; + const CONNECTION *conn = connections + i;
if (set_gen_from_connection(fluid_voice, conn)) continue;
- if (!fluid_source_from_connection(conn->usSource, (conn->usTransform >> 10) & 0x3f, + if (!mod_from_connection(conn->usSource, (conn->usTransform >> 10) & 0x3f, &src1, &flags1)) continue; - if (!fluid_source_from_connection(conn->usControl, (conn->usControl >> 4) & 0x3f, + if (!mod_from_connection(conn->usControl, (conn->usControl >> 4) & 0x3f, &src2, &flags2)) continue; add_mod_from_connection(fluid_voice, conn, src1, flags1, src2, flags2); } }
-static void fluid_voice_add_articulations(fluid_voice_t *fluid_voice, struct list *list) -{ - struct articulation *articulation; - - LIST_FOR_EACH_ENTRY(articulation, list, struct articulation, entry) - fluid_voice_add_articulation(fluid_voice, articulation); -} - static int synth_preset_noteon(fluid_preset_t *fluid_preset, fluid_synth_t *fluid_synth, int chan, int key, int vel) { struct instrument *instrument = fluid_preset_get_data(fluid_preset); @@ -1508,6 +1501,7 @@ static int synth_preset_noteon(fluid_preset_t *fluid_preset, fluid_synth_t *flui
LIST_FOR_EACH_ENTRY(region, &instrument->regions, struct region, entry) { + struct articulation *articulation; struct wave *wave = region->wave;
if (key < region->key_range.usLow || key > region->key_range.usHigh) continue; @@ -1535,8 +1529,10 @@ static int synth_preset_noteon(fluid_preset_t *fluid_preset, fluid_synth_t *flui return FLUID_FAILED; }
- fluid_voice_add_articulations(fluid_voice, &instrument->articulations); - fluid_voice_add_articulations(fluid_voice, ®ion->articulations); + LIST_FOR_EACH_ENTRY(articulation, &instrument->articulations, struct articulation, entry) + add_voice_connections(fluid_voice, &articulation->list, articulation->connections); + LIST_FOR_EACH_ENTRY(articulation, ®ion->articulations, struct articulation, entry) + add_voice_connections(fluid_voice, &articulation->list, articulation->connections); fluid_synth_start_voice(synth->fluid_synth, fluid_voice); return FLUID_OK; }
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/dmsynth/synth.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/dlls/dmsynth/synth.c b/dlls/dmsynth/synth.c index 30a2dd6efe9..47c570d18af 100644 --- a/dlls/dmsynth/synth.c +++ b/dlls/dmsynth/synth.c @@ -33,6 +33,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(dmsynth);
#define ROUND_ADDR(addr, mask) ((void *)((UINT_PTR)(addr) & ~(UINT_PTR)(mask)))
+#define CONN_SRC_CC 0x0080 #define CONN_SRC_CC2 0x0082 #define CONN_SRC_RPN0 0x0100
@@ -1394,9 +1395,9 @@ static BOOL set_gen_from_connection(fluid_voice_t *fluid_voice, const CONNECTION static BOOL mod_from_connection(USHORT source, USHORT transform, UINT *fluid_source, UINT *fluid_flags) { UINT flags = FLUID_MOD_GC; - if (source >= CONN_SRC_CC1 && source <= CONN_SRC_CC1 + 0x7f) + if (source >= CONN_SRC_CC && source <= CONN_SRC_CC + 0x7f) { - *fluid_source = source; + *fluid_source = source - CONN_SRC_CC; flags = FLUID_MOD_CC; } else switch (source)
From: Rémi Bernon rbernon@codeweavers.com
--- 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 47c570d18af..45940e25b59 100644 --- a/dlls/dmsynth/synth.c +++ b/dlls/dmsynth/synth.c @@ -1345,7 +1345,7 @@ static BOOL set_gen_from_connection(fluid_voice_t *fluid_voice, const CONNECTION { if (!gen_from_connection(conn, &gen)) return FALSE; } - if (conn->usSource == CONN_SRC_KEYNUMBER) + else if (conn->usSource == CONN_SRC_KEYNUMBER) { switch (conn->usDestination) {
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/dmsynth/synth.c | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-)
diff --git a/dlls/dmsynth/synth.c b/dlls/dmsynth/synth.c index 45940e25b59..cb1858f3ce4 100644 --- a/dlls/dmsynth/synth.c +++ b/dlls/dmsynth/synth.c @@ -1336,6 +1336,7 @@ static BOOL gen_from_connection(const CONNECTION *conn, UINT *gen)
static BOOL set_gen_from_connection(fluid_voice_t *fluid_voice, const CONNECTION *conn) { + double value; UINT gen;
if (conn->usControl != CONN_SRC_NONE) return FALSE; @@ -1388,7 +1389,16 @@ static BOOL set_gen_from_connection(fluid_voice_t *fluid_voice, const CONNECTION return FALSE; }
- fluid_voice_gen_set(fluid_voice, gen, conn->lScale); + /* SF2 / FluidSynth use 0.1% as "Sustain Level" unit, DLS2 uses percent, meaning is also reversed */ + if (gen == GEN_MODENVSUSTAIN || gen == GEN_VOLENVSUSTAIN) value = 1000 - conn->lScale * 10 / 65536.; + /* FIXME: SF2 and FluidSynth use 1200 * log2(f / 8.176) for absolute freqs, + * whereas DLS2 uses (1200 * log2(f / 440.) + 6900) * 65536. The values + * are very close but not strictly identical and we may need a conversion. + */ + else if (conn->lScale == 0x80000000) value = -32768; + else value = conn->lScale / 65536.; + fluid_voice_gen_set(fluid_voice, gen, value); + return TRUE; }
@@ -1431,6 +1441,7 @@ static BOOL add_mod_from_connection(fluid_voice_t *fluid_voice, const CONNECTION { fluid_mod_t *mod; UINT gen = -1; + double value;
switch (MAKELONG(conn->usSource, conn->usDestination)) { @@ -1460,7 +1471,17 @@ static BOOL add_mod_from_connection(fluid_voice_t *fluid_voice, const CONNECTION fluid_mod_set_source1(mod, src1, flags1); fluid_mod_set_source2(mod, src2, flags2); fluid_mod_set_dest(mod, gen); - fluid_mod_set_amount(mod, conn->lScale); + + /* SF2 / FluidSynth use 0.1% as "Sustain Level" unit, DLS2 uses percent, meaning is also reversed */ + if (gen == GEN_MODENVSUSTAIN || gen == GEN_VOLENVSUSTAIN) value = 1000 - conn->lScale * 10 / 65536.; + /* FIXME: SF2 and FluidSynth use 1200 * log2(f / 8.176) for absolute freqs, + * whereas DLS2 uses (1200 * log2(f / 440.) + 6900) * 65536. The values + * are very close but not strictly identical and we may need a conversion. + */ + else if (conn->lScale == 0x80000000) value = -32768; + else value = conn->lScale / 65536.; + fluid_mod_set_amount(mod, value); + fluid_voice_add_mod(fluid_voice, mod, FLUID_VOICE_OVERWRITE);
return TRUE;
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/dmsynth/synth.c | 210 ++++++++++++++++++++++++ libs/fluidsynth/src/synth/fluid_gen.c | 4 + libs/fluidsynth/src/synth/fluid_synth.c | 2 + 3 files changed, 216 insertions(+)
diff --git a/dlls/dmsynth/synth.c b/dlls/dmsynth/synth.c index cb1858f3ce4..5c1bb4eeb15 100644 --- a/dlls/dmsynth/synth.c +++ b/dlls/dmsynth/synth.c @@ -28,6 +28,7 @@ #include "dls2.h"
#include <fluidsynth.h> +#include <math.h>
WINE_DEFAULT_DEBUG_CHANNEL(dmsynth);
@@ -36,10 +37,14 @@ WINE_DEFAULT_DEBUG_CHANNEL(dmsynth); #define CONN_SRC_CC 0x0080 #define CONN_SRC_CC2 0x0082 #define CONN_SRC_RPN0 0x0100 +#define CONN_SRC_RPN1 0x0101 +#define CONN_SRC_RPN2 0x0102
#define CONN_TRN_BIPOLAR (1<<4) #define CONN_TRN_INVERT (1<<5)
+#define CONN_TRANSFORM(src, ctrl, dst) (((src) & 0x3f) << 10) | (((ctrl) & 0x3f) << 4) | ((dst) & 0xf) + static const char *debugstr_conn_src(UINT src) { switch (src) @@ -64,6 +69,7 @@ static const char *debugstr_conn_src(UINT src)
case CONN_SRC_CC2: return "SRC_CC2"; case CONN_SRC_RPN0: return "SRC_RPN0"; + case CONN_SRC_RPN2: return "SRC_RPN2"; }
return wine_dbg_sprintf("%#x", src); @@ -421,6 +427,52 @@ static ULONG WINAPI synth_Release(IDirectMusicSynth8 *iface) return ref; }
+static void synth_reset_default_values(struct synth *This) +{ + BYTE chan; + + fluid_synth_system_reset(This->fluid_synth); + + for (chan = 0; chan < 0x10; chan++) + { + fluid_synth_cc(This->fluid_synth, chan | 0xe0 /* PITCH_BEND */, 0, 0); + fluid_synth_cc(This->fluid_synth, chan | 0xd0 /* CHANNEL_PRESSURE */, 0, 0); + + fluid_synth_cc(This->fluid_synth, chan | 0xb0 /* CONTROL_CHANGE */, 0x01 /* MODULATION_MSB */, 0); + fluid_synth_cc(This->fluid_synth, chan | 0xb0 /* CONTROL_CHANGE */, 0x21 /* MODULATION_LSB */, 0); + fluid_synth_cc(This->fluid_synth, chan | 0xb0 /* CONTROL_CHANGE */, 0x07 /* VOLUME_MSB */, 0); + fluid_synth_cc(This->fluid_synth, chan | 0xb0 /* CONTROL_CHANGE */, 0x27 /* VOLUME_LSB */, 100); + fluid_synth_cc(This->fluid_synth, chan | 0xb0 /* CONTROL_CHANGE */, 0x0a /* PAN_MSB */, 0); + fluid_synth_cc(This->fluid_synth, chan | 0xb0 /* CONTROL_CHANGE */, 0x0a /* PAN_LSB */, 64); + fluid_synth_cc(This->fluid_synth, chan | 0xb0 /* CONTROL_CHANGE */, 0x0b /* EXPRESSION_MSB */, 0); + fluid_synth_cc(This->fluid_synth, chan | 0xb0 /* CONTROL_CHANGE */, 0x2b /* EXPRESSION_LSB */, 127); + fluid_synth_cc(This->fluid_synth, chan | 0xb0 /* CONTROL_CHANGE */, 0x40 /* SUSTAIN_SWITCH */, 0); + fluid_synth_cc(This->fluid_synth, chan | 0xb0 /* CONTROL_CHANGE */, 0x5b /* EFFECTS_DEPTH1 / Reverb Send */, 40); + fluid_synth_cc(This->fluid_synth, chan | 0xb0 /* CONTROL_CHANGE */, 0x5d /* EFFECTS_DEPTH3 / Chorus Send */, 0); + + /* RPN0 Pitch Bend Range */ + fluid_synth_cc(This->fluid_synth, chan | 0xb0 /* CONTROL_CHANGE */, 0x64 /* RPN_LSB */, 0); + fluid_synth_cc(This->fluid_synth, chan | 0xb0 /* CONTROL_CHANGE */, 0x65 /* RPN_MSB */, 0); + fluid_synth_cc(This->fluid_synth, chan | 0xb0 /* CONTROL_CHANGE */, 0x06 /* DATA_ENTRY_MSB */, 0); + fluid_synth_cc(This->fluid_synth, chan | 0xb0 /* CONTROL_CHANGE */, 0x26 /* DATA_ENTRY_LSB */, 2); + + /* RPN1 Fine Tuning */ + fluid_synth_cc(This->fluid_synth, chan | 0xb0 /* CONTROL_CHANGE */, 0x64 /* RPN_LSB */, 0); + fluid_synth_cc(This->fluid_synth, chan | 0xb0 /* CONTROL_CHANGE */, 0x65 /* RPN_MSB */, 1); + fluid_synth_cc(This->fluid_synth, chan | 0xb0 /* CONTROL_CHANGE */, 0x06 /* DATA_ENTRY_MSB */, 0); + fluid_synth_cc(This->fluid_synth, chan | 0xb0 /* CONTROL_CHANGE */, 0x26 /* DATA_ENTRY_LSB */, 0); + + /* RPN2 Coarse Tuning */ + fluid_synth_cc(This->fluid_synth, chan | 0xb0 /* CONTROL_CHANGE */, 0x64 /* RPN_LSB */, 0); + fluid_synth_cc(This->fluid_synth, chan | 0xb0 /* CONTROL_CHANGE */, 0x65 /* RPN_MSB */, 1); + fluid_synth_cc(This->fluid_synth, chan | 0xb0 /* CONTROL_CHANGE */, 0x06 /* DATA_ENTRY_MSB */, 0); + fluid_synth_cc(This->fluid_synth, chan | 0xb0 /* CONTROL_CHANGE */, 0x26 /* DATA_ENTRY_LSB */, 0); + + fluid_synth_cc(This->fluid_synth, chan | 0xb0 /* CONTROL_CHANGE */, 0x64 /* RPN_LSB */, 127); + fluid_synth_cc(This->fluid_synth, chan | 0xb0 /* CONTROL_CHANGE */, 0x65 /* RPN_MSB */, 127); + } +} + static HRESULT WINAPI synth_Open(IDirectMusicSynth8 *iface, DMUS_PORTPARAMS *params) { struct synth *This = impl_from_IDirectMusicSynth8(iface); @@ -511,6 +563,7 @@ static HRESULT WINAPI synth_Open(IDirectMusicSynth8 *iface, DMUS_PORTPARAMS *par if (!(This->fluid_synth = new_fluid_synth(This->fluid_settings))) return E_OUTOFMEMORY; if ((id = fluid_synth_add_sfont(This->fluid_synth, This->fluid_sfont)) == FLUID_FAILED) WARN("Failed to add fluid_sfont to fluid_synth\n"); + synth_reset_default_values(This);
This->params = actual; This->open = TRUE; @@ -1509,6 +1562,162 @@ static void add_voice_connections(fluid_voice_t *fluid_voice, const CONNECTIONLI } }
+static void set_default_voice_connections(fluid_voice_t *fluid_voice) +{ + const CONNECTION connections[] = + { +#define ABS_PITCH_HZ(f) (LONG)((1200 * log2((f) / 440.) + 6900) * 65536) +#define ABS_TIME_MS(x) ((x) ? (LONG)(1200 * log2((x) / 1000.) * 65536) : 0x80000000) +#define REL_PITCH_CTS(x) ((x) * 65536) +#define REL_GAIN_DB(x) ((-x) * 10 * 65536) + + /* Modulator LFO */ + {.usDestination = CONN_DST_LFO_FREQUENCY, .lScale = ABS_PITCH_HZ(5)}, + {.usDestination = CONN_DST_LFO_STARTDELAY, .lScale = ABS_TIME_MS(10)}, + + /* Vibrato LFO */ + {.usDestination = CONN_DST_VIB_FREQUENCY, .lScale = ABS_PITCH_HZ(5)}, + {.usDestination = CONN_DST_VIB_STARTDELAY, .lScale = ABS_TIME_MS(10)}, + /* Vol EG */ + {.usDestination = CONN_DST_EG1_DELAYTIME, .lScale = ABS_TIME_MS(0)}, + {.usDestination = CONN_DST_EG1_ATTACKTIME, .lScale = ABS_TIME_MS(0)}, + {.usDestination = CONN_DST_EG1_HOLDTIME, .lScale = ABS_TIME_MS(0)}, + {.usDestination = CONN_DST_EG1_DECAYTIME, .lScale = ABS_TIME_MS(0)}, + {.usDestination = CONN_DST_EG1_SUSTAINLEVEL, .lScale = 100 * 65536}, + {.usDestination = CONN_DST_EG1_RELEASETIME, .lScale = ABS_TIME_MS(0)}, + /* FIXME: {.usDestination = CONN_DST_EG1_SHUTDOWNTIME, .lScale = ABS_TIME_MS(15)}, */ + {.usSource = CONN_SRC_KEYONVELOCITY, .usDestination = CONN_DST_EG1_ATTACKTIME, .lScale = 0}, + {.usSource = CONN_SRC_KEYNUMBER, .usDestination = CONN_DST_EG1_DECAYTIME, .lScale = 0}, + {.usSource = CONN_SRC_KEYNUMBER, .usDestination = CONN_DST_EG1_HOLDTIME, .lScale = 0}, + + /* Modulator EG */ + {.usDestination = CONN_DST_EG2_DELAYTIME, .lScale = ABS_TIME_MS(0)}, + {.usDestination = CONN_DST_EG2_ATTACKTIME, .lScale = ABS_TIME_MS(0)}, + {.usDestination = CONN_DST_EG2_HOLDTIME, .lScale = ABS_TIME_MS(0)}, + {.usDestination = CONN_DST_EG2_DECAYTIME, .lScale = ABS_TIME_MS(0)}, + {.usDestination = CONN_DST_EG2_SUSTAINLEVEL, .lScale = 100 * 65536}, + {.usDestination = CONN_DST_EG2_RELEASETIME, .lScale = ABS_TIME_MS(0)}, + {.usSource = CONN_SRC_KEYONVELOCITY, .usDestination = CONN_DST_EG2_ATTACKTIME, .lScale = 0}, + {.usSource = CONN_SRC_KEYNUMBER, .usDestination = CONN_DST_EG2_DECAYTIME, .lScale = 0}, + {.usSource = CONN_SRC_KEYNUMBER, .usDestination = CONN_DST_EG2_HOLDTIME, .lScale = 0}, + + /* Key number generator */ + /* FIXME: This doesn't seem to be supported by FluidSynth, there's also no MIDINOTE source */ + /* {.usSource = CONN_SRC_MIDINOTE?, .usDestination = CONN_DST_KEYNUMBER, .lScale = REL_PITCH_CTS(12800)}, */ + { + .usSource = CONN_SRC_RPN2, .usDestination = CONN_DST_KEYNUMBER, .lScale = REL_PITCH_CTS(6400), + .usTransform = CONN_TRANSFORM(CONN_TRN_BIPOLAR, CONN_TRN_NONE, CONN_TRN_NONE), + }, + + /* Filter */ + {.usDestination = CONN_DST_FILTER_CUTOFF, .lScale = 0x7fffffff}, + {.usDestination = CONN_DST_FILTER_Q, .lScale = 0}, + { + .usSource = CONN_SRC_LFO, .usDestination = CONN_DST_FILTER_CUTOFF, .lScale = 0, + .usTransform = CONN_TRANSFORM(CONN_TRN_BIPOLAR, CONN_TRN_NONE, CONN_TRN_NONE), + }, + { + .usSource = CONN_SRC_LFO, .usControl = CONN_SRC_CC1, .usDestination = CONN_DST_FILTER_CUTOFF, .lScale = 0, + .usTransform = CONN_TRANSFORM(CONN_TRN_BIPOLAR, CONN_TRN_NONE, CONN_TRN_NONE), + }, + { + .usSource = CONN_SRC_LFO, .usControl = CONN_SRC_CHANNELPRESSURE, .usDestination = CONN_DST_FILTER_CUTOFF, .lScale = 0, + .usTransform = CONN_TRANSFORM(CONN_TRN_BIPOLAR, CONN_TRN_NONE, CONN_TRN_NONE), + }, + {.usSource = CONN_SRC_EG2, .usDestination = CONN_DST_FILTER_CUTOFF, .lScale = 0}, + {.usSource = CONN_SRC_KEYONVELOCITY, .usDestination = CONN_DST_FILTER_CUTOFF, .lScale = 0}, + {.usSource = CONN_SRC_KEYNUMBER, .usDestination = CONN_DST_FILTER_CUTOFF, .lScale = 0}, + + /* Gain */ + { + .usSource = CONN_SRC_LFO, .usDestination = CONN_DST_GAIN, .lScale = REL_GAIN_DB(0), + .usTransform = CONN_TRANSFORM(CONN_TRN_BIPOLAR, CONN_TRN_NONE, CONN_TRN_NONE), + }, + { + .usSource = CONN_SRC_LFO, .usControl = CONN_SRC_CC1, .usDestination = CONN_DST_GAIN, .lScale = REL_GAIN_DB(0), + .usTransform = CONN_TRANSFORM(CONN_TRN_BIPOLAR, CONN_TRN_NONE, CONN_TRN_NONE), + }, + { + .usSource = CONN_SRC_LFO, .usControl = CONN_SRC_CHANNELPRESSURE, .usDestination = CONN_DST_GAIN, .lScale = REL_GAIN_DB(0), + .usTransform = CONN_TRANSFORM(CONN_TRN_BIPOLAR, CONN_TRN_NONE, CONN_TRN_NONE), + }, + { + .usSource = CONN_SRC_KEYONVELOCITY, .usDestination = CONN_DST_GAIN, .lScale = REL_GAIN_DB(-96), + .usTransform = CONN_TRANSFORM(CONN_TRN_CONCAVE | CONN_TRN_INVERT, CONN_TRN_NONE, CONN_TRN_NONE), + }, + { + .usSource = CONN_SRC_CC7, .usDestination = CONN_DST_GAIN, .lScale = REL_GAIN_DB(-96), + .usTransform = CONN_TRANSFORM(CONN_TRN_CONCAVE | CONN_TRN_INVERT, CONN_TRN_NONE, CONN_TRN_NONE), + }, + { + .usSource = CONN_SRC_CC11, .usDestination = CONN_DST_GAIN, .lScale = REL_GAIN_DB(-96), + .usTransform = CONN_TRANSFORM(CONN_TRN_CONCAVE | CONN_TRN_INVERT, CONN_TRN_NONE, CONN_TRN_NONE), + }, + + /* Pitch */ + {.usDestination = CONN_DST_PITCH, .lScale = REL_PITCH_CTS(0)}, + { + .usSource = CONN_SRC_PITCHWHEEL, .usControl = CONN_SRC_RPN0, .usDestination = CONN_DST_PITCH, .lScale = REL_PITCH_CTS(12800), + .usTransform = CONN_TRANSFORM(CONN_TRN_BIPOLAR, CONN_TRN_NONE, CONN_TRN_NONE), + }, + /* FIXME: key to pitch default should be 12800 but FluidSynth GEN_PITCH modulator doesn't work as expected */ + {.usSource = CONN_SRC_KEYNUMBER, .usDestination = CONN_DST_PITCH, .lScale = REL_PITCH_CTS(0)}, + { + .usSource = CONN_SRC_RPN1, .usDestination = CONN_DST_PITCH, .lScale = REL_PITCH_CTS(100), + .usTransform = CONN_TRANSFORM(CONN_TRN_BIPOLAR, CONN_TRN_NONE, CONN_TRN_NONE), + }, + { + .usSource = CONN_SRC_VIBRATO, .usDestination = CONN_DST_PITCH, .lScale = REL_PITCH_CTS(0), + .usTransform = CONN_TRANSFORM(CONN_TRN_BIPOLAR, CONN_TRN_NONE, CONN_TRN_NONE), + }, + { + .usSource = CONN_SRC_VIBRATO, .usControl = CONN_SRC_CC1, .usDestination = CONN_DST_PITCH, .lScale = REL_PITCH_CTS(0), + .usTransform = CONN_TRANSFORM(CONN_TRN_BIPOLAR, CONN_TRN_NONE, CONN_TRN_NONE), + }, + { + .usSource = CONN_SRC_VIBRATO, .usControl = CONN_SRC_CHANNELPRESSURE, .usDestination = CONN_DST_PITCH, .lScale = REL_PITCH_CTS(0), + .usTransform = CONN_TRANSFORM(CONN_TRN_BIPOLAR, CONN_TRN_NONE, CONN_TRN_NONE), + }, + { + .usSource = CONN_SRC_LFO, .usDestination = CONN_DST_PITCH, .lScale = REL_PITCH_CTS(0), + .usTransform = CONN_TRANSFORM(CONN_TRN_BIPOLAR, CONN_TRN_NONE, CONN_TRN_NONE), + }, + { + .usSource = CONN_SRC_LFO, .usControl = CONN_SRC_CC1, .usDestination = CONN_DST_PITCH, .lScale = REL_PITCH_CTS(0), + .usTransform = CONN_TRANSFORM(CONN_TRN_BIPOLAR, CONN_TRN_NONE, CONN_TRN_NONE), + }, + { + .usSource = CONN_SRC_LFO, .usControl = CONN_SRC_CHANNELPRESSURE, .usDestination = CONN_DST_PITCH, .lScale = REL_PITCH_CTS(0), + .usTransform = CONN_TRANSFORM(CONN_TRN_BIPOLAR, CONN_TRN_NONE, CONN_TRN_NONE), + }, + {.usSource = CONN_SRC_EG2, .usDestination = CONN_DST_PITCH, .lScale = REL_PITCH_CTS(0)}, + + /* Output */ + {.usDestination = CONN_DST_PAN, .lScale = 0}, + { + .usSource = CONN_SRC_CC10, .usDestination = CONN_DST_PAN, .lScale = 508 * 65536, + .usTransform = CONN_TRANSFORM(CONN_TRN_BIPOLAR, CONN_TRN_NONE, CONN_TRN_NONE), + }, + {.usSource = CONN_SRC_CC91, .usDestination = CONN_DST_REVERB, .lScale = 1000 * 65536}, + {.usDestination = CONN_DST_REVERB, .lScale = 0}, + {.usSource = CONN_SRC_CC93, .usDestination = CONN_DST_CHORUS, .lScale = 1000 * 65536}, + {.usDestination = CONN_DST_CHORUS, .lScale = 0}, + +#undef ABS_PITCH_HZ +#undef ABS_TIME_MS +#undef REL_PITCH_CTS +#undef REL_GAIN_DB + }; + CONNECTIONLIST list = {.cbSize = sizeof(CONNECTIONLIST), .cConnections = ARRAY_SIZE(connections)}; + + fluid_voice_gen_set(fluid_voice, GEN_KEYNUM, -1.); + fluid_voice_gen_set(fluid_voice, GEN_VELOCITY, -1.); + fluid_voice_gen_set(fluid_voice, GEN_SCALETUNE, 100.0); + fluid_voice_gen_set(fluid_voice, GEN_OVERRIDEROOTKEY, -1.); + + add_voice_connections(fluid_voice, &list, connections); +} + static int synth_preset_noteon(fluid_preset_t *fluid_preset, fluid_synth_t *fluid_synth, int chan, int key, int vel) { struct instrument *instrument = fluid_preset_get_data(fluid_preset); @@ -1551,6 +1760,7 @@ static int synth_preset_noteon(fluid_preset_t *fluid_preset, fluid_synth_t *flui return FLUID_FAILED; }
+ set_default_voice_connections(fluid_voice); LIST_FOR_EACH_ENTRY(articulation, &instrument->articulations, struct articulation, entry) add_voice_connections(fluid_voice, &articulation->list, articulation->connections); LIST_FOR_EACH_ENTRY(articulation, ®ion->articulations, struct articulation, entry) diff --git a/libs/fluidsynth/src/synth/fluid_gen.c b/libs/fluidsynth/src/synth/fluid_gen.c index 2ce2b0f74b5..232ea294fd9 100644 --- a/libs/fluidsynth/src/synth/fluid_gen.c +++ b/libs/fluidsynth/src/synth/fluid_gen.c @@ -109,7 +109,11 @@ fluid_gen_init(fluid_gen_t *gen, fluid_channel_t *channel) gen[i].flags = GEN_UNUSED; gen[i].mod = 0.0; gen[i].nrpn = (channel == NULL) ? 0.0 : fluid_channel_get_gen(channel, i); +#if 0 /* unused in Wine */ gen[i].val = fluid_gen_info[i].def; +#else + gen[i].val = 0.0; +#endif } }
diff --git a/libs/fluidsynth/src/synth/fluid_synth.c b/libs/fluidsynth/src/synth/fluid_synth.c index 3b83e3d8b73..0580ed271f6 100644 --- a/libs/fluidsynth/src/synth/fluid_synth.c +++ b/libs/fluidsynth/src/synth/fluid_synth.c @@ -300,6 +300,7 @@ fluid_synth_init(void)
init_dither();
+#if 0 /* unused in Wine */ /* custom_breath2att_mod is not a default modulator specified in SF2.01. it is intended to replace default_vel2att_mod on demand using API fluid_set_breath_mode() or command shell setbreathmode. @@ -480,6 +481,7 @@ fluid_synth_init(void) fluid_mod_set_dest(&custom_balance_mod, GEN_CUSTOM_BALANCE); /* Destination: stereo balance */ /* Amount: 96 dB of attenuation (on the opposite channel) */ fluid_mod_set_amount(&custom_balance_mod, FLUID_PEAK_ATTENUATION); /* Amount: 960 */ +#endif /* unused in Wine */
#if defined(LIBINSTPATCH_SUPPORT) /* defer libinstpatch init to fluid_instpatch.c to avoid #include "libinstpatch.h" */
This merge request was approved by Michael Stefaniuc.