[PATCH] mmdevapi: Implement SpatialAudio features
Andrew Eikum
aeikum at codeweavers.com
Mon Feb 1 11:23:07 CST 2021
On Mon, Feb 01, 2021 at 04:56:38PM +0200, Arkadiusz Hiler wrote:
> From: Andrew Eikum <aeikum at codeweavers.com>
>
I'm fine with how you've done it, but if you'd like Git authorship for
the tests, you could send that patch separately.
In spatialaudio.c:
> +static HRESULT activate_stream(SpatialAudioStreamImpl *stream)
> +{
> + WAVEFORMATEXTENSIBLE *object_fmtex = (WAVEFORMATEXTENSIBLE *)stream->params.ObjectFormat;
> + HRESULT hr;
> + REFERENCE_TIME period;
> +
> + if(!(object_fmtex->Format.wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
> + (object_fmtex->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
> + IsEqualGUID(&object_fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)))){
> + FIXME("Only float formats are supported for now\n");
> + return E_INVALIDARG;
> + }
> +
> + hr = IMMDevice_Activate(stream->sa_client->mmdev, &IID_IAudioClient,
> + CLSCTX_INPROC_SERVER, NULL, (void**)&stream->client);
> + if(FAILED(hr)){
> + WARN("Activate failed: %08x\n", hr);
> + return hr;
> + }
> +
> + hr = IAudioClient_GetDevicePeriod(stream->client, &period, NULL);
> + if(FAILED(hr)){
> + WARN("GetDevicePeriod failed: %08x\n", hr);
> + IAudioClient_Release(stream->client);
> + return hr;
> + }
> +
> + stream->stream_fmtex.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
> + static_mask_to_channels(stream->params.StaticObjectTypeMask,
> + &stream->stream_fmtex.Format.nChannels, &stream->stream_fmtex.dwChannelMask,
> + stream->static_object_map);
> + stream->stream_fmtex.Format.nSamplesPerSec = stream->params.ObjectFormat->nSamplesPerSec;
> + stream->stream_fmtex.Format.wBitsPerSample = stream->params.ObjectFormat->wBitsPerSample;
> + stream->stream_fmtex.Format.nBlockAlign = (stream->stream_fmtex.Format.nChannels * stream->stream_fmtex.Format.wBitsPerSample) / 8;
> + stream->stream_fmtex.Format.nAvgBytesPerSec = stream->stream_fmtex.Format.nSamplesPerSec * stream->stream_fmtex.Format.nBlockAlign;
> + stream->stream_fmtex.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
> + stream->stream_fmtex.Samples.wValidBitsPerSample = stream->stream_fmtex.Format.wBitsPerSample;
> + stream->stream_fmtex.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
> +
> + hr = IAudioClient_Initialize(stream->client, AUDCLNT_SHAREMODE_SHARED,
> + AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST,
> + period * MAX_PERIODS, 0, &stream->stream_fmtex.Format, NULL);
> + if(FAILED(hr)){
> + WARN("Initialize failed: %08x\n", hr);
> + IAudioClient_Release(stream->client);
> + return hr;
> + }
> +
> + /* XXX: OK that this is the user's handle? */
We can remove this comment, we are cloning the handle below.
> +static HRESULT WINAPI SAC_ActivateSpatialAudioStream(ISpatialAudioClient *iface,
> + const PROPVARIANT *prop, REFIID riid, void **stream)
> +{
> + SpatialAudioImpl *This = impl_from_ISpatialAudioClient(iface);
> + SpatialAudioObjectRenderStreamActivationParams *params;
> + HRESULT hr;
> +
> + TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), stream);
> +
> + if(IsEqualIID(riid, &IID_ISpatialAudioObjectRenderStream)){
> + SpatialAudioStreamImpl *obj;
> +
> + if(prop &&
> + (prop->vt != VT_BLOB ||
> + prop->u.blob.cbSize != sizeof(SpatialAudioObjectRenderStreamActivationParams))){
> + WARN("Got invalid params\n");
> + *stream = NULL;
> + return E_INVALIDARG;
> + }
> +
> + params = (SpatialAudioObjectRenderStreamActivationParams*) prop->u.blob.pBlobData;
> +
> + if(params->StaticObjectTypeMask & AudioObjectType_Dynamic){
> + *stream = NULL;
> + return E_INVALIDARG;
> + }
> +
> + if(!params->ObjectFormat || memcmp(params->ObjectFormat, &This->object_fmtex.Format, sizeof(*params->ObjectFormat) + params->ObjectFormat->cbSize)) {
> + *stream = NULL;
> + return AUDCLNT_E_UNSUPPORTED_FORMAT;
> + }
> +
> + obj = heap_alloc_zero(sizeof(SpatialAudioStreamImpl));
> +
> + obj->ISpatialAudioObjectRenderStream_iface.lpVtbl = &ISpatialAudioObjectRenderStream_vtbl;
> + obj->ref = 1;
> + memcpy(&obj->params, params, sizeof(obj->params));
> +
> + obj->update_frames = ~0;
> +
> + InitializeCriticalSection(&obj->lock);
> + list_init(&obj->objects);
> +
> + obj->sa_client = This;
> + SAC_AddRef(&This->ISpatialAudioClient_iface);
> +
> + obj->params.ObjectFormat = clone_fmtex(obj->params.ObjectFormat);
> +
> + if(obj->params.EventHandle != INVALID_HANDLE_VALUE &&
> + obj->params.EventHandle != 0)
The tests show you can't give a NULL or invalid handle, so I think you
could check for this and bail out earlier.
In tests/spatialaudio.c:
> +static void fill_activation_params(SpatialAudioObjectRenderStreamActivationParams *activation_params)
> +{
> + activation_params->StaticObjectTypeMask = \
> + AudioObjectType_FrontLeft |
> + AudioObjectType_FrontRight |
> + AudioObjectType_FrontCenter |
> + AudioObjectType_LowFrequency |
> + AudioObjectType_SideLeft |
> + AudioObjectType_SideRight |
> + AudioObjectType_BackLeft |
> + AudioObjectType_BackRight |
> + AudioObjectType_TopFrontLeft |
> + AudioObjectType_TopFrontRight |
> + AudioObjectType_TopBackLeft |
> + AudioObjectType_TopBackRight;
> +
> + activation_params->MinDynamicObjectCount = 0;
> + activation_params->MaxDynamicObjectCount = 0;
> + activation_params->Category = AudioCategory_GameEffects;
> + activation_params->EventHandle = event;
> + activation_params->NotifyObject = NULL;
> +
> + activation_params->ObjectFormat = &format;
Mixed tabs/spaces (and all over this file).
> +static void test_audio_object_buffers(void)
> +{
> + HRESULT hr;
> + ISpatialAudioObjectRenderStream *sas = NULL;
> + ISpatialAudioObject *sao[4];
> + UINT32 dyn_object_count, frame_count, buffer_length;
> + BYTE *buffer;
> + INT i;
> +
> + SpatialAudioObjectRenderStreamActivationParams activation_params;
> + PROPVARIANT activation_params_prop;
> +
> + PropVariantInit(&activation_params_prop);
> + activation_params_prop.vt = VT_BLOB;
> + activation_params_prop.blob.cbSize = sizeof(activation_params);
> + activation_params_prop.blob.pBlobData = (BYTE*) &activation_params;
> +
> + fill_activation_params(&activation_params);
> + hr = ISpatialAudioClient_ActivateSpatialAudioStream(sac, &activation_params_prop, &IID_ISpatialAudioObjectRenderStream, (void**)&sas);
> + ok(hr == S_OK, "Failed to activate spatial audio stream: 0x%08x\n", hr);
> +
> + hr = ISpatialAudioObjectRenderStream_ActivateSpatialAudioObject(sas, AudioObjectType_FrontLeft, &sao[0]);
> + ok(hr == S_OK, "Failed to activate spatial audio object: 0x%08x\n", hr);
> +
> + hr = ISpatialAudioObjectRenderStream_Start(sas);
> + ok(hr == S_OK, "Failed to activate spatial audio render stream: 0x%08x\n", hr);
> +
> + hr = ISpatialAudioObjectRenderStream_ActivateSpatialAudioObject(sas, AudioObjectType_FrontRight, &sao[1]);
> + ok(hr == S_OK, "Failed to activate spatial audio object: 0x%08x\n", hr);
> +
> + hr = WaitForSingleObject(event, 200);
> + ok(hr == WAIT_OBJECT_0, "Expected event to be flagged: 0x%08x\n", hr);
> +
> + hr = ISpatialAudioObjectRenderStream_ActivateSpatialAudioObject(sas, AudioObjectType_SideLeft, &sao[2]);
> + ok(hr == S_OK, "Failed to activate spatial audio object: 0x%08x\n", hr);
> +
> + hr = ISpatialAudioObjectRenderStream_BeginUpdatingAudioObjects(sas, &dyn_object_count, &frame_count);
> + ok(hr == S_OK, "Failed to beging updating audio objects: 0x%08x\n", hr);
> + ok(dyn_object_count == 0, "Unexpected dynamic objects\n");
> + ok(frame_count > 0, "Expected to get non-zero frames to update\n");
> +
> + hr = ISpatialAudioObjectRenderStream_ActivateSpatialAudioObject(sas, AudioObjectType_SideRight, &sao[3]);
> + ok(hr == S_OK, "Failed to activate spatial audio object: 0x%08x\n", hr);
> +
> + for (i = 0; i < ARRAYSIZE(sao); i++)
> + {
> + hr = ISpatialAudioObject_GetBuffer(sao[i], &buffer, &buffer_length);
> + ok(hr == S_OK, "Expected to be able to get buffers for audio object: 0x%08x\n", hr);
> + ok(buffer != NULL, "Expected to get a non-NULL buffer\n");
> + ok(buffer_length == frame_count * format.wBitsPerSample / 8, "Expected buffer length to be sample_size * frame_count = %hu but got %u\n",
> + frame_count * format.wBitsPerSample / 8, buffer_length);
> + ok(is_buffer_zeroed(buffer, buffer_length), "Expected audio object's buffer to be zeroed\n");
> + }
> +
> + hr = ISpatialAudioObjectRenderStream_EndUpdatingAudioObjects(sas);
> + ok(hr == S_OK, "Failed to end updating audio objects: 0x%08x\n", hr);
> +
> + hr = WaitForSingleObject(event, 200);
> + ok(hr == WAIT_OBJECT_0, "Expected event to be flagged: 0x%08x\n", hr);
> +
> + hr = ISpatialAudioObjectRenderStream_BeginUpdatingAudioObjects(sas, &dyn_object_count, &frame_count);
> + ok(hr == S_OK, "Failed to beging updating audio objects: 0x%08x\n", hr);
> + ok(dyn_object_count == 0, "Unexpected dynamic objects\n");
> + ok(frame_count > 0, "Expected to get non-zero frames to update\n");
> +
> + /* one more iteration but not with every object */
> + for (i = 0; i < ARRAYSIZE(sao) - 1; i++)
> + {
> + hr = ISpatialAudioObject_GetBuffer(sao[i], &buffer, &buffer_length);
> + ok(hr == S_OK, "Expected to be able to get buffers for audio object: 0x%08x\n", hr);
> + ok(buffer != NULL, "Expected to get a non-NULL buffer\n");
> + ok(buffer_length == frame_count * format.wBitsPerSample / 8, "Expected buffer length to be sample_size * frame_count = %hu but got %u\n",
> + frame_count * format.wBitsPerSample / 8, buffer_length);
> + ok(is_buffer_zeroed(buffer, buffer_length), "Expected audio object's buffer to be zeroed\n");
> + }
> +
> + hr = ISpatialAudioObjectRenderStream_EndUpdatingAudioObjects(sas);
> + ok(hr == S_OK, "Failed to end updating audio objects: 0x%08x\n", hr);
> +
> + /* ending the stream */
> + hr = ISpatialAudioObject_SetEndOfStream(sao[0], 0);
> + todo_wine ok(hr == SPTLAUDCLNT_E_OUT_OF_ORDER, "Expected that ending the stream at this point won't be allowed: 0x%08x\n", hr);
> +
> + hr = WaitForSingleObject(event, 10000);
This wait timeout can be decreased.
> +START_TEST(spatialaudio)
> +{
> + HRESULT hr;
> +
> + event = CreateEventA(NULL, FALSE, FALSE, "spatial-audio-test-prog-event");
> + ok (event != NULL, "Failed to create event, last error: 0x%08x\n", GetLastError());
Extra space here.
Andrew
More information about the wine-devel
mailing list