From: Yuxuan Shui yshui@codeweavers.com
--- dlls/dmime/audiopath.c | 274 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 269 insertions(+), 5 deletions(-)
diff --git a/dlls/dmime/audiopath.c b/dlls/dmime/audiopath.c index 90843f7a941..70985a261f8 100644 --- a/dlls/dmime/audiopath.c +++ b/dlls/dmime/audiopath.c @@ -32,10 +32,28 @@ struct IDirectMusicAudioPathImpl { BOOL fActive; };
+struct audio_path_pchannel_to_buffer { + struct list entry; + DMUS_IO_PCHANNELTOBUFFER_HEADER header; + + ULONG num_of_guids; + GUID *guids; +}; + +struct audio_path_port_config { + struct list entry; + DMUS_IO_PORTCONFIG_HEADER header; + DMUS_PORTPARAMS8 params; + + struct list pchannel_to_buffer_entries; +}; + struct audio_path_config { IUnknown unk; struct dmobject dmobj; LONG ref; + + struct list port_config_entries; };
static inline struct IDirectMusicAudioPathImpl *impl_from_IDirectMusicAudioPath(IDirectMusicAudioPath *iface) @@ -297,16 +315,240 @@ static const IDirectMusicObjectVtbl dmobject_vtbl = { dmobj_IDirectMusicObject_SetDescriptor, path_IDirectMusicObject_ParseDescriptor }; +static HRESULT parse_pchannel_to_buffers_list(struct audio_path_port_config *This, IStream *stream, const struct chunk_entry *pchl) +{ + struct chunk_entry chunk = { .parent = pchl }; + struct audio_path_pchannel_to_buffer *pchannel_to_buffer = NULL; + ULONG bytes, i; + HRESULT hr; + + while ((hr = stream_next_chunk(stream, &chunk)) == S_OK) { + switch (chunk.id) + { + case DMUS_FOURCC_PCHANNELS_ITEM: + pchannel_to_buffer = calloc(1, sizeof(*pchannel_to_buffer)); + if (!pchannel_to_buffer) + return E_OUTOFMEMORY; + + hr = stream_read(stream, &pchannel_to_buffer->header, sizeof(pchannel_to_buffer->header)); + if (FAILED(hr)) + goto done; + + TRACE("Got PChannelToBuffer header:\n"); + TRACE("\tdwPChannelCount: %lu\n", pchannel_to_buffer->header.dwPChannelCount); + TRACE("\tdwPChannelBase: %lu\n", pchannel_to_buffer->header.dwPChannelBase); + TRACE("\tdwBufferCount: %lu\n", pchannel_to_buffer->header.dwBufferCount); + TRACE("\tdwFlags: %lx\n", pchannel_to_buffer->header.dwFlags); + + bytes = chunk.size - sizeof(pchannel_to_buffer->header); + + if (bytes % sizeof(GUID)) + { + WARN("Invalid size of GUIDs array\n"); + hr = E_FAIL; + goto done; + } + pchannel_to_buffer->num_of_guids = bytes / sizeof(GUID); + pchannel_to_buffer->guids = calloc(pchannel_to_buffer->num_of_guids, sizeof(GUID)); + + TRACE("number of GUIDs: %lu\n", pchannel_to_buffer->num_of_guids); + + if (!pchannel_to_buffer->guids) + { + hr = E_OUTOFMEMORY; + goto done; + } + hr = stream_read(stream, pchannel_to_buffer->guids, bytes); + + for (i = 0; i < pchannel_to_buffer->num_of_guids; i++) + TRACE("\tguids[%lu]: %s\n", i, debugstr_dmguid(&pchannel_to_buffer->guids[i])); + + if (FAILED(hr)) + goto done; + + list_add_tail(&This->pchannel_to_buffer_entries, &pchannel_to_buffer->entry); + pchannel_to_buffer = NULL; + break; + case FOURCC_LIST: + if (chunk.type == DMUS_FOURCC_UNFO_LIST) + TRACE("Unfo list in PChannelToBuffer is ignored\n"); + else + WARN("Unknown %s\n", debugstr_chunk(&chunk)); + break; + case MAKEFOURCC('p','b','g','d'): + FIXME("Undocumented %s\n", debugstr_chunk(&chunk)); + break; + default: + WARN("Unknown %s\n", debugstr_chunk(&chunk)); + break; + } + } +done: + if (FAILED(hr)) { + if (pchannel_to_buffer) + { + free(pchannel_to_buffer->guids); + free(pchannel_to_buffer); + } + return hr; + } + return S_OK; +} +static HRESULT parse_port_config_list(struct audio_path_config *This, IStream *stream, const struct chunk_entry *pcfl) +{ + struct chunk_entry chunk = { .parent = pcfl }; + struct audio_path_port_config *port_config = calloc(1, sizeof(*port_config)); + HRESULT hr; + + if (!port_config) + return E_OUTOFMEMORY; + + list_init(&port_config->pchannel_to_buffer_entries); + + while ((hr = stream_next_chunk(stream, &chunk)) == S_OK) { + switch (chunk.id) + { + case DMUS_FOURCC_PORTCONFIG_ITEM: + hr = stream_chunk_get_data(stream, &chunk, &port_config->header, sizeof(port_config->header)); + if (FAILED(hr)) + goto done; + TRACE("Got PortConfig header:\n"); + TRACE("\tdwPChannelBase: %lu\n", port_config->header.dwPChannelBase); + TRACE("\tdwPChannelCount: %lu\n", port_config->header.dwPChannelCount); + TRACE("\tdwFlags: %lx\n", port_config->header.dwFlags); + TRACE("\tguidPort: %s\n", debugstr_dmguid(&port_config->header.guidPort)); + break; + case DMUS_FOURCC_PORTPARAMS_ITEM: + hr = stream_chunk_get_data(stream, &chunk, &port_config->params, sizeof(port_config->params)); + if (FAILED(hr)) + goto done; + TRACE("Got PortConfig params:\n"); + TRACE("\tdwSize: %lu\n", port_config->params.dwSize); + TRACE("\tdwValidParams: %lx\n", port_config->params.dwValidParams); + if (port_config->params.dwValidParams & DMUS_PORTPARAMS_VOICES) + TRACE("\tvoices: %lu\n", port_config->params.dwVoices); + if (port_config->params.dwValidParams & DMUS_PORTPARAMS_CHANNELGROUPS) + TRACE("\tchannel groups: %lu\n", port_config->params.dwChannelGroups); + if (port_config->params.dwValidParams & DMUS_PORTPARAMS_AUDIOCHANNELS) + TRACE("\taudio channels: %lu\n", port_config->params.dwAudioChannels); + if (port_config->params.dwValidParams & DMUS_PORTPARAMS_SAMPLERATE) + TRACE("\tsample rate: %lu\n", port_config->params.dwSampleRate); + if (port_config->params.dwValidParams & DMUS_PORTPARAMS_EFFECTS) + TRACE("\teffects: %lx\n", port_config->params.dwEffectFlags); + if (port_config->params.dwValidParams & DMUS_PORTPARAMS_SHARE) + TRACE("\tshare: %d\n", port_config->params.fShare); + if (port_config->params.dwValidParams & DMUS_PORTPARAMS_FEATURES) + TRACE("\tfeatures: %lx\n", port_config->params.dwFeatures); + break; + case FOURCC_LIST: + if (chunk.type == DMUS_FOURCC_DSBUFFER_LIST) + FIXME("DirectSound buffer descriptors is not supported\n"); + else if (chunk.type == DMUS_FOURCC_PCHANNELS_LIST) + { + hr = parse_pchannel_to_buffers_list(port_config, stream, &chunk); + if (FAILED(hr)) + goto done; + } + else if (chunk.type == DMUS_FOURCC_UNFO_LIST) + TRACE("Unfo list in PortConfig is ignored\n"); + else + WARN("Unknown %s\n", debugstr_chunk(&chunk)); + break; + default: + WARN("Unknown %s\n", debugstr_chunk(&chunk)); + } + } + +done: + if (FAILED(hr)) { + free(port_config); + return hr; + } + list_add_tail(&This->port_config_entries, &port_config->entry); + return S_OK; +} + +static HRESULT parse_port_configs_list(struct audio_path_config *This, IStream *stream, const struct chunk_entry *pcsl) +{ + struct chunk_entry chunk = { .parent = pcsl }; + HRESULT hr; + + while ((hr = stream_next_chunk(stream, &chunk)) == S_OK) { + switch (MAKE_IDTYPE(chunk.id, chunk.type)) + { + case MAKE_IDTYPE(FOURCC_LIST, DMUS_FOURCC_PORTCONFIG_LIST): + hr = parse_port_config_list(This, stream, &chunk); + if (FAILED(hr)) + return hr; + break; + default: + WARN("Unknown %s\n", debugstr_chunk(&chunk)); + break; + } + } + return SUCCEEDED(hr) ? S_OK : hr; +}
/* IPersistStream */ static HRESULT WINAPI path_IPersistStream_Load(IPersistStream *iface, IStream *stream) { struct audio_path_config *This = impl_from_IPersistStream(iface); + struct chunk_entry riff = {0}, chunk = {0}; + HRESULT hr = S_OK; + + FIXME("(%p, %p): Loading\n", This, stream); + + if (!stream) + return E_POINTER; + + hr = stream_get_chunk(stream, &riff); + if (FAILED(hr)) { + WARN("Failed to get chunk %#lx.\n", hr); + return hr; + }
- FIXME("(%p, %p): Loading not implemented yet\n", This, stream); + if (MAKE_IDTYPE(riff.id, riff.type) != MAKE_IDTYPE(FOURCC_RIFF, DMUS_FOURCC_AUDIOPATH_FORM)) + { + WARN("loading failed: unexpected %s\n", debugstr_chunk(&riff)); + return E_UNEXPECTED; + }
- return IDirectMusicObject_ParseDescriptor(&This->dmobj.IDirectMusicObject_iface, stream, - &This->dmobj.desc); + if (FAILED(hr = dmobj_parsedescriptor(stream, &riff, &This->dmobj.desc, + DMUS_OBJ_OBJECT | DMUS_OBJ_VERSION | DMUS_OBJ_NAME | DMUS_OBJ_CATEGORY)) + || FAILED(hr = stream_reset_chunk_data(stream, &riff))) { + WARN("Failed to parse descriptor %#lx.\n", hr); + return hr; + } + This->dmobj.desc.guidClass = CLSID_DirectMusicAudioPathConfig; + This->dmobj.desc.dwValidData |= DMUS_OBJ_CLASS; + + chunk.parent = &riff; + while ((hr = stream_next_chunk(stream, &chunk)) == S_OK) { + switch (chunk.id) { + case FOURCC_LIST: + if (chunk.type == DMUS_FOURCC_PORTCONFIGS_LIST) { + hr = parse_port_configs_list(This, stream, &chunk); + if (FAILED(hr)) + return hr; + } + else if (chunk.type == DMUS_FOURCC_DSBUFFER_LIST) + FIXME("buffer attributes are not supported\n"); + else if (chunk.type == MAKEFOURCC('p','a','p','d')) + FIXME("Undocumented %s\n", debugstr_chunk(&chunk)); + else if (chunk.type != DMUS_FOURCC_UNFO_LIST) + WARN("Unknown %s\n", debugstr_chunk(&chunk)); + break; + case DMUS_FOURCC_GUID_CHUNK: + case DMUS_FOURCC_VERSION_CHUNK: + /* Already parsed in dmobj_parsedescriptor. */ + break; + default: + WARN("Unknown %s\n", debugstr_chunk(&chunk)); + break; + } + } + TRACE("Finished parsing %#lx\n", hr); + return SUCCEEDED(hr) ? S_OK : hr; }
static const IPersistStreamVtbl persiststream_vtbl = { @@ -352,7 +594,11 @@ static HRESULT WINAPI path_config_IUnknown_QueryInterface(IUnknown *unk, REFIID static ULONG WINAPI path_config_IUnknown_AddRef(IUnknown *unk) { struct audio_path_config *This = impl_from_IUnknown(unk); - return InterlockedIncrement(&This->ref); + ULONG ref = InterlockedIncrement(&This->ref); + + TRACE("(%p): ref=%ld\n", This, ref); + + return ref; }
static ULONG WINAPI path_config_IUnknown_Release(IUnknown *unk) @@ -360,8 +606,24 @@ static ULONG WINAPI path_config_IUnknown_Release(IUnknown *unk) struct audio_path_config *This = impl_from_IUnknown(unk); ULONG ref = InterlockedDecrement(&This->ref);
- if (!ref) + TRACE("(%p): ref=%ld\n", This, ref); + + if (!ref) { + struct audio_path_port_config *i, *tmp; + LIST_FOR_EACH_ENTRY_SAFE(i, tmp, &This->port_config_entries, struct audio_path_port_config, entry) + { + struct audio_path_pchannel_to_buffer *j, *tmp2; + LIST_FOR_EACH_ENTRY_SAFE(j, tmp2, &i->pchannel_to_buffer_entries, struct audio_path_pchannel_to_buffer, entry) + { + list_remove(&j->entry); + free(j->guids); + free(j); + } + list_remove(&i->entry); + free(i); + } free(This); + } return ref; }
@@ -402,6 +664,8 @@ HRESULT create_dmaudiopath_config(REFIID riid, void **ppobj) obj->dmobj.IPersistStream_iface.lpVtbl = &persiststream_vtbl; obj->ref = 1;
+ list_init(&obj->port_config_entries); + hr = IUnknown_QueryInterface(&obj->unk, riid, ppobj); IUnknown_Release(&obj->unk); return hr;