dmime: Parse AudioPathConfig.

This commit is contained in:
Yuxuan Shui 2024-01-17 16:39:39 +00:00 committed by Alexandre Julliard
parent a300815c0f
commit 289dfacfd4

View file

@ -32,11 +32,31 @@ struct IDirectMusicAudioPathImpl {
BOOL fActive;
};
struct audio_path_pchannel_to_buffer
{
struct list entry;
DMUS_IO_PCHANNELTOBUFFER_HEADER header;
GUID guids[];
};
C_ASSERT(sizeof(struct audio_path_pchannel_to_buffer) == offsetof(struct audio_path_pchannel_to_buffer, guids[0]));
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 IUnknown_iface;
struct dmobject dmobj;
LONG ref;
struct list port_config_entries;
};
static inline struct IDirectMusicAudioPathImpl *impl_from_IDirectMusicAudioPath(IDirectMusicAudioPath *iface)
@ -299,15 +319,252 @@ static const IDirectMusicObjectVtbl dmobject_vtbl = {
path_config_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;
DMUS_IO_PCHANNELTOBUFFER_HEADER header;
SIZE_T bytes;
ULONG i;
HRESULT hr;
while ((hr = stream_next_chunk(stream, &chunk)) == S_OK)
{
switch (chunk.id)
{
case DMUS_FOURCC_PCHANNELS_ITEM:
hr = stream_read(stream, &header, sizeof(header));
if (FAILED(hr))
break;
TRACE("Got PChannelToBuffer header:\n");
TRACE("\tdwPChannelCount: %lu\n", header.dwPChannelCount);
TRACE("\tdwPChannelBase: %lu\n", header.dwPChannelBase);
TRACE("\tdwBufferCount: %lu\n", header.dwBufferCount);
TRACE("\tdwFlags: %lx\n", header.dwFlags);
bytes = chunk.size - sizeof(header);
if (bytes != sizeof(GUID) * header.dwBufferCount)
{
WARN("Invalid size of GUIDs array\n");
hr = E_FAIL;
break;
}
pchannel_to_buffer = calloc(1, sizeof(*pchannel_to_buffer) + bytes);
if (!pchannel_to_buffer)
{
hr = E_OUTOFMEMORY;
break;
}
pchannel_to_buffer->header = header;
TRACE("number of GUIDs: %lu\n", pchannel_to_buffer->header.dwBufferCount);
hr = stream_read(stream, pchannel_to_buffer->guids, bytes);
if (FAILED(hr))
break;
for (i = 0; i < pchannel_to_buffer->header.dwBufferCount; i++)
TRACE("\tguids[%lu]: %s\n", i, debugstr_dmguid(&pchannel_to_buffer->guids[i]));
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;
}
if (FAILED(hr))
break;
}
if (FAILED(hr) && pchannel_to_buffer)
free(pchannel_to_buffer);
return hr;
}
static void port_config_destroy(struct audio_path_port_config *port_config)
{
struct audio_path_pchannel_to_buffer *pchannel_to_buffer, *next_pchannel_to_buffer;
LIST_FOR_EACH_ENTRY_SAFE(pchannel_to_buffer, next_pchannel_to_buffer, &port_config->pchannel_to_buffer_entries,
struct audio_path_pchannel_to_buffer, entry)
{
list_remove(&pchannel_to_buffer->entry);
free(pchannel_to_buffer);
}
free(port_config);
}
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))
break;
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))
break;
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);
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));
}
if (FAILED(hr))
break;
}
if (FAILED(hr))
port_config_destroy(port_config);
else
list_add_tail(&This->port_config_entries, &port_config->entry);
return hr;
}
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_config_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 not implemented yet\n", This, stream);
FIXME("(%p, %p): Loading\n", This, stream);
return IDirectMusicObject_ParseDescriptor(&This->dmobj.IDirectMusicObject_iface, stream,
&This->dmobj.desc);
if (!stream)
return E_POINTER;
hr = stream_get_chunk(stream, &riff);
if (FAILED(hr))
{
WARN("Failed to get chunk %#lx.\n", hr);
return hr;
}
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;
}
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 (MAKE_IDTYPE(chunk.id, chunk.type))
{
case MAKE_IDTYPE(FOURCC_LIST, DMUS_FOURCC_PORTCONFIGS_LIST):
hr = parse_port_configs_list(This, stream, &chunk);
if (FAILED(hr))
return hr;
case MAKE_IDTYPE(FOURCC_LIST, DMUS_FOURCC_DSBUFFER_LIST):
FIXME("buffer attributes are not supported\n");
break;
case MAKE_IDTYPE(FOURCC_LIST, MAKEFOURCC('p','a','p','d')):
FIXME("Undocumented %s\n", debugstr_chunk(&chunk));
break;
case MAKE_IDTYPE(FOURCC_LIST, 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 = {
@ -354,7 +611,11 @@ static HRESULT WINAPI path_config_IUnknown_QueryInterface(IUnknown *iface, REFII
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)
@ -362,8 +623,18 @@ static ULONG WINAPI path_config_IUnknown_Release(IUnknown *unk)
struct audio_path_config *This = impl_from_IUnknown(unk);
ULONG ref = InterlockedDecrement(&This->ref);
TRACE("(%p): ref=%ld\n", This, ref);
if (!ref)
{
struct audio_path_port_config *config, *next_config;
LIST_FOR_EACH_ENTRY_SAFE(config, next_config, &This->port_config_entries, struct audio_path_port_config, entry)
{
list_remove(&config->entry);
port_config_destroy(config);
}
free(This);
}
return ref;
}
@ -403,6 +674,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->IUnknown_iface, riid, ppobj);
IUnknown_Release(&obj->IUnknown_iface);
return hr;