From: Rémi Bernon rbernon@codeweavers.com
--- dlls/dmusic/Makefile.in | 3 +- dlls/dmusic/collection.c | 14 ++- dlls/dmusic/dmusic_private.h | 2 + dlls/dmusic/wave.c | 214 +++++++++++++++++++++++++++++++++++ 4 files changed, 228 insertions(+), 5 deletions(-) create mode 100644 dlls/dmusic/wave.c
diff --git a/dlls/dmusic/Makefile.in b/dlls/dmusic/Makefile.in index a8955a2ab42..d9438af0d18 100644 --- a/dlls/dmusic/Makefile.in +++ b/dlls/dmusic/Makefile.in @@ -10,7 +10,8 @@ C_SRCS = \ dmusic_main.c \ download.c \ instrument.c \ - port.c + port.c \ + wave.c
IDL_SRCS = dmusic.idl
diff --git a/dlls/dmusic/collection.c b/dlls/dmusic/collection.c index db636055668..1a613e8b821 100644 --- a/dlls/dmusic/collection.c +++ b/dlls/dmusic/collection.c @@ -40,6 +40,7 @@ C_ASSERT(sizeof(struct pool) == offsetof(struct pool, cues[0])); struct wave_entry { struct list entry; + IUnknown *wave; DWORD offset; };
@@ -123,6 +124,7 @@ static ULONG WINAPI collection_Release(IDirectMusicCollection *iface) LIST_FOR_EACH_ENTRY_SAFE(wave_entry, next, &This->waves, struct wave_entry, entry) { list_remove(&wave_entry->entry); + IDirectMusicInstrument_Release(wave_entry->wave); free(wave_entry); }
@@ -227,8 +229,12 @@ static HRESULT parse_wvpl_list(struct collection *This, IStream *stream, struct { case MAKE_IDTYPE(FOURCC_LIST, FOURCC_wave): if (!(entry = malloc(sizeof(*entry)))) return E_OUTOFMEMORY; - entry->offset = chunk.offset.QuadPart - parent->offset.QuadPart - 12; - list_add_tail(&This->waves, &entry->entry); + if (FAILED(hr = wave_create_from_chunk(stream, &chunk, &entry->wave))) free(entry); + else + { + entry->offset = chunk.offset.QuadPart - parent->offset.QuadPart - 12; + list_add_tail(&This->waves, &entry->entry); + } break;
default: @@ -395,13 +401,13 @@ static HRESULT WINAPI collection_stream_Load(IPersistStream *iface, IStream *str i++; }
- TRACE(" - cues: size %lu\n", This->pool->table.cbSize); + TRACE(" - cues:\n"); for (i = 0; i < This->pool->table.cCues; i++) TRACE(" - index: %u, offset: %lu\n", i, This->pool->cues[i].ulOffset);
TRACE(" - waves:\n"); LIST_FOR_EACH_ENTRY(wave_entry, &This->waves, struct wave_entry, entry) - TRACE(" - offset: %lu\n", wave_entry->offset); + TRACE(" - offset: %lu, wave %p\n", wave_entry->offset, wave_entry->wave); }
stream_skip_chunk(stream, &chunk); diff --git a/dlls/dmusic/dmusic_private.h b/dlls/dmusic/dmusic_private.h index 2d195574b89..2fe6afde643 100644 --- a/dlls/dmusic/dmusic_private.h +++ b/dlls/dmusic/dmusic_private.h @@ -92,6 +92,8 @@ extern HRESULT instrument_download_to_port(IDirectMusicInstrument *iface, IDirec IDirectMusicDownloadedInstrument **downloaded); extern HRESULT instrument_unload_from_port(IDirectMusicDownloadedInstrument *iface, IDirectMusicPortDownload *port);
+extern HRESULT wave_create_from_chunk(IStream *stream, struct chunk_entry *parent, IUnknown **out); + /***************************************************************************** * IDirectMusic8Impl implementation structure */ diff --git a/dlls/dmusic/wave.c b/dlls/dmusic/wave.c new file mode 100644 index 00000000000..cda895484cb --- /dev/null +++ b/dlls/dmusic/wave.c @@ -0,0 +1,214 @@ +/* + * Copyright 2023 Rémi Bernon for CodeWeavers + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "dmusic_private.h" + +WINE_DEFAULT_DEBUG_CHANNEL(dmusic); + +struct sample +{ + WSMPL head; + WLOOP loops[]; +}; + +C_ASSERT(sizeof(struct sample) == offsetof(struct sample, loops[0])); + +struct wave +{ + IUnknown IUnknown_iface; + LONG ref; + + struct sample *sample; + WAVEFORMATEX *format; + UINT data_size; + void *data; +}; + +static inline struct wave *impl_from_IUnknown(IUnknown *iface) +{ + return CONTAINING_RECORD(iface, struct wave, IUnknown_iface); +} + +static HRESULT WINAPI wave_QueryInterface(IUnknown *iface, REFIID riid, void **ret_iface) +{ + struct wave *This = impl_from_IUnknown(iface); + + TRACE("(%p, %s, %p)\n", This, debugstr_dmguid(riid), ret_iface); + + if (IsEqualIID(riid, &IID_IUnknown)) + { + *ret_iface = &This->IUnknown_iface; + IUnknown_AddRef(&This->IUnknown_iface); + return S_OK; + } + + WARN("(%p, %s, %p): not found\n", This, debugstr_dmguid(riid), ret_iface); + *ret_iface = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI wave_AddRef(IUnknown *iface) +{ + struct wave *This = impl_from_IUnknown(iface); + LONG ref = InterlockedIncrement(&This->ref); + TRACE("(%p) ref=%ld\n", This, ref); + return ref; +} + +static ULONG WINAPI wave_Release(IUnknown *iface) +{ + struct wave *This = impl_from_IUnknown(iface); + LONG ref = InterlockedDecrement(&This->ref); + + TRACE("(%p) ref=%ld\n", This, ref); + + if (!ref) + { + free(This->format); + free(This->data); + free(This->sample); + free(This); + } + + return ref; +} + +static const IUnknownVtbl unknown_vtbl = +{ + wave_QueryInterface, + wave_AddRef, + wave_Release, +}; + +static HRESULT wave_create(IUnknown **ret_iface) +{ + struct wave *obj; + + if (!(obj = calloc(1, sizeof(*obj)))) return E_OUTOFMEMORY; + obj->IUnknown_iface.lpVtbl = &unknown_vtbl; + obj->ref = 1; + + *ret_iface = &obj->IUnknown_iface; + return S_OK; +} + +static HRESULT parse_wsmp_chunk(struct wave *This, IStream *stream, struct chunk_entry *chunk) +{ + struct sample *sample; + WSMPL wsmpl; + HRESULT hr; + UINT size; + + if (chunk->size < sizeof(wsmpl)) return E_INVALIDARG; + if (FAILED(hr = stream_read(stream, &wsmpl, sizeof(wsmpl)))) return hr; + if (chunk->size != wsmpl.cbSize + sizeof(WLOOP) * wsmpl.cSampleLoops) return E_INVALIDARG; + if (wsmpl.cbSize != sizeof(wsmpl)) return E_INVALIDARG; + if (wsmpl.cSampleLoops > 1) FIXME("Not implemented: found more than one wave loop\n"); + + size = offsetof(struct sample, loops[wsmpl.cSampleLoops]); + if (!(sample = malloc(size))) return E_OUTOFMEMORY; + sample->head = wsmpl; + + size = sizeof(WLOOP) * wsmpl.cSampleLoops; + if (FAILED(hr = stream_read(stream, sample->loops, size))) free(sample); + else This->sample = sample; + + return hr; +} + +static HRESULT parse_wave_chunk(struct wave *This, IStream *stream, struct chunk_entry *parent) +{ + struct chunk_entry chunk = {.parent = parent}; + HRESULT hr; + + while ((hr = stream_next_chunk(stream, &chunk)) == S_OK) + { + switch (MAKE_IDTYPE(chunk.id, chunk.type)) + { + case mmioFOURCC('f','m','t',' '): + if (!(This->format = malloc(chunk.size))) return E_OUTOFMEMORY; + hr = stream_chunk_get_data(stream, &chunk, This->format, chunk.size); + break; + + case mmioFOURCC('d','a','t','a'): + if (!(This->data = malloc(chunk.size))) return E_OUTOFMEMORY; + hr = stream_chunk_get_data(stream, &chunk, This->data, chunk.size); + if (SUCCEEDED(hr)) This->data_size = chunk.size; + break; + + case FOURCC_WSMP: + hr = parse_wsmp_chunk(This, stream, &chunk); + break; + + default: + FIXME("Ignoring chunk %s %s\n", debugstr_fourcc(chunk.id), debugstr_fourcc(chunk.type)); + break; + } + + if (FAILED(hr)) break; + } + + return hr; +} + +HRESULT wave_create_from_chunk(IStream *stream, struct chunk_entry *parent, IUnknown **ret_iface) +{ + struct wave *This; + IUnknown *iface; + HRESULT hr; + + TRACE("(%p, %p, %p)\n", stream, parent, ret_iface); + + if (FAILED(hr = wave_create(&iface))) return hr; + This = impl_from_IUnknown(iface); + + if (FAILED(hr = parse_wave_chunk(This, stream, parent))) + { + IUnknown_Release(iface); + return DMUS_E_UNSUPPORTED_STREAM; + } + + if (TRACE_ON(dmusic)) + { + UINT i; + + TRACE("*** Created DirectMusicWave %p\n", This); + TRACE(" - format: %p\n", This->format); + if (This->format) + { + TRACE(" - wFormatTag: %u\n", This->format->wFormatTag); + TRACE(" - nChannels: %u\n", This->format->nChannels); + TRACE(" - nSamplesPerSec: %lu\n", This->format->nSamplesPerSec); + TRACE(" - nAvgBytesPerSec: %lu\n", This->format->nAvgBytesPerSec); + TRACE(" - nBlockAlign: %u\n", This->format->nBlockAlign); + TRACE(" - wBitsPerSample: %u\n", This->format->wBitsPerSample); + TRACE(" - cbSize: %u\n", This->format->cbSize); + } + TRACE(" - sample: {size: %lu, unity_note: %u, fine_tune: %d, attenuation: %ld, options: %#lx, loops: %lu}\n", + This->sample->head.cbSize, This->sample->head.usUnityNote, + This->sample->head.sFineTune, This->sample->head.lAttenuation, + This->sample->head.fulOptions, This->sample->head.cSampleLoops); + for (i = 0; i < This->sample->head.cSampleLoops; i++) + TRACE(" - loops[%u]: {size: %lu, type: %lu, start: %lu, length: %lu}\n", i, + This->sample->loops[i].cbSize, This->sample->loops[i].ulType, + This->sample->loops[i].ulStart, This->sample->loops[i].ulLength); + } + + *ret_iface = iface; + return S_OK; +}