From: Anton Baskanov baskanov@gmail.com
Some soundfonts contain multiple zones at both preset and instrument levels, which leads to creation of multiple regions that reference the same wave. Reusing already downloaded waves prevents running out of memory when loading FluidR3_GM.sf2. --- dlls/dmusic/dmusic_wave.h | 5 ++- dlls/dmusic/instrument.c | 31 ++++--------- dlls/dmusic/wave.c | 92 ++++++++++++++++++++++++++++++++++----- 3 files changed, 95 insertions(+), 33 deletions(-)
diff --git a/dlls/dmusic/dmusic_wave.h b/dlls/dmusic/dmusic_wave.h index bdad8ca9605..c31b6aa0677 100644 --- a/dlls/dmusic/dmusic_wave.h +++ b/dlls/dmusic/dmusic_wave.h @@ -28,11 +28,14 @@
struct soundfont; struct chunk_entry; +struct wave_download;
extern HRESULT wave_create(IDirectMusicObject **ret_iface); extern HRESULT wave_create_from_soundfont(struct soundfont *soundfont, UINT index, IDirectMusicObject **out); extern HRESULT wave_create_from_chunk(IStream *stream, struct chunk_entry *parent, IDirectMusicObject **out); -extern HRESULT wave_download_to_port(IDirectMusicObject *iface, IDirectMusicPortDownload *port, DWORD *id); +extern HRESULT wave_download_to_port(IDirectMusicObject *iface, IDirectMusicPortDownload *port, DWORD *id, + struct wave_download **out_download); +extern HRESULT wave_unload_from_port(struct wave_download *download); extern HRESULT wave_download_to_dsound(IDirectMusicObject *iface, IDirectSound *dsound, IDirectSoundBuffer **ret_iface); extern HRESULT wave_get_duration(IDirectMusicObject *iface, REFERENCE_TIME *duration); diff --git a/dlls/dmusic/instrument.c b/dlls/dmusic/instrument.c index 8311c690877..a0211c0dfa9 100644 --- a/dlls/dmusic/instrument.c +++ b/dlls/dmusic/instrument.c @@ -52,6 +52,8 @@ struct region WSMPL wave_sample; WLOOP wave_loop; BOOL loop_present; + + struct wave_download *wave_download; };
static void region_destroy(struct region *region) @@ -826,7 +828,8 @@ HRESULT instrument_download_to_port(IDirectMusicInstrument *iface, IDirectMusicP
if (SUCCEEDED(hr = collection_get_wave(This->collection, region->wave_link.ulTableIndex, &wave))) { - hr = wave_download_to_port(wave, port, &dmus_region->WaveLink.ulTableIndex); + hr = wave_download_to_port(wave, port, &dmus_region->WaveLink.ulTableIndex, + ®ion->wave_download); IDirectMusicObject_Release(wave); } if (FAILED(hr)) goto failed; @@ -854,35 +857,19 @@ failed: HRESULT instrument_unload_from_port(IDirectMusicDownloadedInstrument *iface, IDirectMusicPortDownload *port) { struct instrument *This = impl_from_IDirectMusicDownloadedInstrument(iface); - struct download_buffer *buffer; - DWORD size; HRESULT hr;
if (!This->download) return DMUS_E_NOT_DOWNLOADED_TO_PORT;
if (FAILED(hr = IDirectMusicPortDownload_Unload(port, This->download))) WARN("Failed to unload instrument download buffer, hr %#lx\n", hr); - else if (SUCCEEDED(hr = IDirectMusicDownload_GetBuffer(This->download, (void **)&buffer, &size))) + else { - IDirectMusicDownload *wave_download; - DMUS_INSTRUMENT *instrument; - BYTE *ptr = (BYTE *)buffer; - DMUS_REGION *region; - UINT index; - - instrument = (DMUS_INSTRUMENT *)(ptr + buffer->offsets[0]); - for (index = instrument->ulFirstRegionIdx; index; index = region->ulNextRegionIdx) + struct region *region; + LIST_FOR_EACH_ENTRY(region, &This->regions, struct region, entry) { - region = (DMUS_REGION *)(ptr + buffer->offsets[index]); - - if (FAILED(hr = IDirectMusicPortDownload_GetBuffer(port, region->WaveLink.ulTableIndex, &wave_download))) - WARN("Failed to get wave download with id %#lx, hr %#lx\n", region->WaveLink.ulTableIndex, hr); - else - { - if (FAILED(hr = IDirectMusicPortDownload_Unload(port, wave_download))) - WARN("Failed to unload wave download buffer, hr %#lx\n", hr); - IDirectMusicDownload_Release(wave_download); - } + if (FAILED(hr = wave_unload_from_port(region->wave_download))) + WARN("Failed to unload wave download buffer, hr %#lx\n", hr); } }
diff --git a/dlls/dmusic/wave.c b/dlls/dmusic/wave.c index 2c8ccbc2170..ea50e0e8908 100644 --- a/dlls/dmusic/wave.c +++ b/dlls/dmusic/wave.c @@ -29,6 +29,17 @@ struct sample
C_ASSERT(sizeof(struct sample) == offsetof(struct sample, loops[0]));
+struct wave_download +{ + struct list entry; + LONG ref; + + IDirectMusicPortDownload *port; + DWORD id; + + IDirectMusicDownload *download; +}; + struct wave { IUnknown IUnknown_iface; @@ -39,6 +50,8 @@ struct wave WAVEFORMATEX *format; UINT data_size; IStream *data; + + struct list downloads; };
static inline struct wave *impl_from_IUnknown(IUnknown *iface) @@ -95,6 +108,7 @@ static ULONG WINAPI wave_Release(IUnknown *iface)
if (!ref) { + list_remove(&This->downloads); free(This->format); if (This->data) IStream_Release(This->data); free(This->sample); @@ -280,6 +294,7 @@ HRESULT wave_create(IDirectMusicObject **ret_iface) dmobject_init(&obj->dmobj, &CLSID_DirectSoundWave, &obj->IUnknown_iface); obj->dmobj.IDirectMusicObject_iface.lpVtbl = &wave_object_vtbl; obj->dmobj.IPersistStream_iface.lpVtbl = &wave_persist_stream_vtbl; + list_init(&obj->downloads);
*ret_iface = &obj->dmobj.IDirectMusicObject_iface; return S_OK; @@ -430,7 +445,8 @@ static HRESULT wave_read_data(struct wave *This, void *data, DWORD *data_size) return hr; }
-HRESULT wave_download_to_port(IDirectMusicObject *iface, IDirectMusicPortDownload *port, DWORD *id) +HRESULT wave_download_to_port(IDirectMusicObject *iface, IDirectMusicPortDownload *port, DWORD *id, + struct wave_download **out_download) { struct download_buffer { @@ -442,15 +458,39 @@ HRESULT wave_download_to_port(IDirectMusicObject *iface, IDirectMusicPortDownloa
struct wave *This = impl_from_IDirectMusicObject(iface); DWORD size = offsetof(struct download_buffer, data.byData[This->data_size]); - IDirectMusicDownload *download; + struct wave_download *download; HRESULT hr;
- if (FAILED(hr = IDirectMusicPortDownload_AllocateBuffer(port, size, &download))) return hr; + LIST_FOR_EACH_ENTRY(download, &This->downloads, struct wave_download, entry) + { + if (download->port == port) + break; + }
- if (SUCCEEDED(hr = IDirectMusicDownload_GetBuffer(download, (void **)&buffer, &size)) - && SUCCEEDED(hr = IDirectMusicPortDownload_GetDLId(port, &buffer->info.dwDLId, 1))) + if (&download->entry == &This->downloads) { + download = calloc(1, sizeof(struct wave_download)); + if (!download) + return E_OUTOFMEMORY; + + download->port = port; + + if (FAILED(hr = IDirectMusicPortDownload_AllocateBuffer(port, size, &download->download))) + { + free(download); + return hr; + } + + if (FAILED(hr = IDirectMusicDownload_GetBuffer(download->download, (void **)&buffer, &size)) + || FAILED(hr = IDirectMusicPortDownload_GetDLId(port, &download->id, 1))) + { + IDirectMusicDownload_Release(download->download); + free(download); + return hr; + } + buffer->info.dwDLType = DMUS_DOWNLOADINFO_WAVE; + buffer->info.dwDLId = download->id; buffer->info.dwNumOffsetTableEntries = 2; buffer->info.cbSize = size;
@@ -463,15 +503,47 @@ HRESULT wave_download_to_port(IDirectMusicObject *iface, IDirectMusicPortDownloa buffer->wave.ulFirstExtCkIdx = 0;
if (FAILED(hr = wave_read_data(This, buffer->data.byData, &buffer->data.cbSize))) + { + IDirectMusicDownload_Release(download->download); + free(download); WARN("Failed to read wave data from stream, hr %#lx\n", hr); - else if (FAILED(hr = IDirectMusicPortDownload_Download(port, download))) + return hr; + } + if (FAILED(hr = IDirectMusicPortDownload_Download(port, download->download))) + { + IDirectMusicDownload_Release(download->download); + free(download); WARN("Failed to download wave to port, hr %#lx\n", hr); - else - *id = buffer->info.dwDLId; + return hr; + } + + list_add_tail(&This->downloads, &download->entry); }
- IDirectMusicDownload_Release(download); - return hr; + ++download->ref; + + *id = download->id; + *out_download = download; + + return S_OK; +} + +HRESULT wave_unload_from_port(struct wave_download *download) +{ + HRESULT hr; + + --download->ref; + + if (!download->ref) + { + list_remove(&download->entry); + if (FAILED(hr = IDirectMusicPortDownload_Unload(download->port, download->download))) + WARN("Failed to unload wave download buffer, hr %#lx\n", hr); + IDirectMusicDownload_Release(download->download); + free(download); + } + + return S_OK; }
HRESULT wave_download_to_dsound(IDirectMusicObject *iface, IDirectSound *dsound, IDirectSoundBuffer **ret_iface)