[PATCH 6/7] winecoreaudio: Move create_stream and release_stream to the unixlib.

Andrew Eikum aeikum at codeweavers.com
Mon Nov 22 14:55:14 CST 2021


Signed-off-by: Andrew Eikum <aeikum at codeweavers.com>

On Thu, Nov 18, 2021 at 07:52:37AM +0000, Huw Davies wrote:
> Signed-off-by: Huw Davies <huw at codeweavers.com>
> ---
>  dlls/winecoreaudio.drv/coreaudio.c | 464 +++++++++++++++++++++++++++++
>  dlls/winecoreaudio.drv/mmdevdrv.c  | 304 +++----------------
>  dlls/winecoreaudio.drv/unixlib.h   |  20 ++
>  3 files changed, 527 insertions(+), 261 deletions(-)
> 
> diff --git a/dlls/winecoreaudio.drv/coreaudio.c b/dlls/winecoreaudio.drv/coreaudio.c
> index f3af24f80fb..3ab80efc4e2 100644
> --- a/dlls/winecoreaudio.drv/coreaudio.c
> +++ b/dlls/winecoreaudio.drv/coreaudio.c
> @@ -85,6 +85,30 @@ static HRESULT osstatus_to_hresult(OSStatus sc)
>      return E_FAIL;
>  }
>  
> +/* copied from kernelbase */
> +static int muldiv( int a, int b, int c )
> +{
> +    LONGLONG ret;
> +
> +    if (!c) return -1;
> +
> +    /* We want to deal with a positive divisor to simplify the logic. */
> +    if (c < 0)
> +    {
> +        a = -a;
> +        c = -c;
> +    }
> +
> +    /* If the result is positive, we "add" to round. else, we subtract to round. */
> +    if ((a < 0 && b < 0) || (a >= 0 && b >= 0))
> +        ret = (((LONGLONG)a * b) + (c / 2)) / c;
> +    else
> +        ret = (((LONGLONG)a * b) - (c / 2)) / c;
> +
> +    if (ret > 2147483647 || ret < -2147483647) return -1;
> +    return ret;
> +}
> +
>  static AudioObjectPropertyScope get_scope(EDataFlow flow)
>  {
>      return (flow == eRender) ? kAudioDevicePropertyScopeOutput : kAudioDevicePropertyScopeInput;
> @@ -241,7 +265,447 @@ static NTSTATUS get_endpoint_ids(void *args)
>      return STATUS_SUCCESS;
>  }
>  
> +static WAVEFORMATEX *clone_format(const WAVEFORMATEX *fmt)
> +{
> +    WAVEFORMATEX *ret;
> +    size_t size;
> +
> +    if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
> +        size = sizeof(WAVEFORMATEXTENSIBLE);
> +    else
> +        size = sizeof(WAVEFORMATEX);
> +
> +    ret = malloc(size);
> +    if(!ret)
> +        return NULL;
> +
> +    memcpy(ret, fmt, size);
> +
> +    ret->cbSize = size - sizeof(WAVEFORMATEX);
> +
> +    return ret;
> +}
> +
> +static void silence_buffer(struct coreaudio_stream *stream, BYTE *buffer, UINT32 frames)
> +{
> +    WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)stream->fmt;
> +    if((stream->fmt->wFormatTag == WAVE_FORMAT_PCM ||
> +        (stream->fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
> +         IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))) &&
> +       stream->fmt->wBitsPerSample == 8)
> +        memset(buffer, 128, frames * stream->fmt->nBlockAlign);
> +    else
> +        memset(buffer, 0, frames * stream->fmt->nBlockAlign);
> +}
> +
> +/* CA is pulling data from us */
> +static OSStatus ca_render_cb(void *user, AudioUnitRenderActionFlags *flags,
> +        const AudioTimeStamp *ts, UInt32 bus, UInt32 nframes,
> +        AudioBufferList *data)
> +{
> +    struct coreaudio_stream *stream = user;
> +    UINT32 to_copy_bytes, to_copy_frames, chunk_bytes, lcl_offs_bytes;
> +
> +    OSSpinLockLock(&stream->lock);
> +
> +    if(stream->playing){
> +        lcl_offs_bytes = stream->lcl_offs_frames * stream->fmt->nBlockAlign;
> +        to_copy_frames = min(nframes, stream->held_frames);
> +        to_copy_bytes = to_copy_frames * stream->fmt->nBlockAlign;
> +
> +        chunk_bytes = (stream->bufsize_frames - stream->lcl_offs_frames) * stream->fmt->nBlockAlign;
> +
> +        if(to_copy_bytes > chunk_bytes){
> +            memcpy(data->mBuffers[0].mData, stream->local_buffer + lcl_offs_bytes, chunk_bytes);
> +            memcpy(((BYTE *)data->mBuffers[0].mData) + chunk_bytes, stream->local_buffer, to_copy_bytes - chunk_bytes);
> +        }else
> +            memcpy(data->mBuffers[0].mData, stream->local_buffer + lcl_offs_bytes, to_copy_bytes);
> +
> +        stream->lcl_offs_frames += to_copy_frames;
> +        stream->lcl_offs_frames %= stream->bufsize_frames;
> +        stream->held_frames -= to_copy_frames;
> +    }else
> +        to_copy_bytes = to_copy_frames = 0;
> +
> +    if(nframes > to_copy_frames)
> +        silence_buffer(stream, ((BYTE *)data->mBuffers[0].mData) + to_copy_bytes, nframes - to_copy_frames);
> +
> +    OSSpinLockUnlock(&stream->lock);
> +
> +    return noErr;
> +}
> +
> +static void ca_wrap_buffer(BYTE *dst, UINT32 dst_offs, UINT32 dst_bytes,
> +                           BYTE *src, UINT32 src_bytes)
> +{
> +    UINT32 chunk_bytes = dst_bytes - dst_offs;
> +
> +    if(chunk_bytes < src_bytes){
> +        memcpy(dst + dst_offs, src, chunk_bytes);
> +        memcpy(dst, src + chunk_bytes, src_bytes - chunk_bytes);
> +    }else
> +        memcpy(dst + dst_offs, src, src_bytes);
> +}
> +
> +/* we need to trigger CA to pull data from the device and give it to us
> + *
> + * raw data from CA is stored in cap_buffer, possibly via wrap_buffer
> + *
> + * raw data is resampled from cap_buffer into resamp_buffer in period-size
> + * chunks and copied to local_buffer
> + */
> +static OSStatus ca_capture_cb(void *user, AudioUnitRenderActionFlags *flags,
> +                              const AudioTimeStamp *ts, UInt32 bus, UInt32 nframes,
> +                              AudioBufferList *data)
> +{
> +    struct coreaudio_stream *stream = user;
> +    AudioBufferList list;
> +    OSStatus sc;
> +    UINT32 cap_wri_offs_frames;
> +
> +    OSSpinLockLock(&stream->lock);
> +
> +    cap_wri_offs_frames = (stream->cap_offs_frames + stream->cap_held_frames) % stream->cap_bufsize_frames;
> +
> +    list.mNumberBuffers = 1;
> +    list.mBuffers[0].mNumberChannels = stream->fmt->nChannels;
> +    list.mBuffers[0].mDataByteSize = nframes * stream->fmt->nBlockAlign;
> +
> +    if(!stream->playing || cap_wri_offs_frames + nframes > stream->cap_bufsize_frames){
> +        if(stream->wrap_bufsize_frames < nframes){
> +            free(stream->wrap_buffer);
> +            stream->wrap_buffer = malloc(list.mBuffers[0].mDataByteSize);
> +            stream->wrap_bufsize_frames = nframes;
> +        }
> +
> +        list.mBuffers[0].mData = stream->wrap_buffer;
> +    }else
> +        list.mBuffers[0].mData = stream->cap_buffer + cap_wri_offs_frames * stream->fmt->nBlockAlign;
> +
> +    sc = AudioUnitRender(stream->unit, flags, ts, bus, nframes, &list);
> +    if(sc != noErr){
> +        OSSpinLockUnlock(&stream->lock);
> +        return sc;
> +    }
> +
> +    if(stream->playing){
> +        if(list.mBuffers[0].mData == stream->wrap_buffer){
> +            ca_wrap_buffer(stream->cap_buffer,
> +                    cap_wri_offs_frames * stream->fmt->nBlockAlign,
> +                    stream->cap_bufsize_frames * stream->fmt->nBlockAlign,
> +                    stream->wrap_buffer, list.mBuffers[0].mDataByteSize);
> +        }
> +
> +        stream->cap_held_frames += list.mBuffers[0].mDataByteSize / stream->fmt->nBlockAlign;
> +        if(stream->cap_held_frames > stream->cap_bufsize_frames){
> +            stream->cap_offs_frames += stream->cap_held_frames % stream->cap_bufsize_frames;
> +            stream->cap_offs_frames %= stream->cap_bufsize_frames;
> +            stream->cap_held_frames = stream->cap_bufsize_frames;
> +        }
> +    }
> +
> +    OSSpinLockUnlock(&stream->lock);
> +    return noErr;
> +}
> +
> +static AudioComponentInstance get_audiounit(EDataFlow dataflow, AudioDeviceID adevid)
> +{
> +    AudioComponentInstance unit;
> +    AudioComponent comp;
> +    AudioComponentDescription desc;
> +    OSStatus sc;
> +
> +    memset(&desc, 0, sizeof(desc));
> +    desc.componentType = kAudioUnitType_Output;
> +    desc.componentSubType = kAudioUnitSubType_HALOutput;
> +    desc.componentManufacturer = kAudioUnitManufacturer_Apple;
> +
> +    if(!(comp = AudioComponentFindNext(NULL, &desc))){
> +        WARN("AudioComponentFindNext failed\n");
> +        return NULL;
> +    }
> +
> +    sc = AudioComponentInstanceNew(comp, &unit);
> +    if(sc != noErr){
> +        WARN("AudioComponentInstanceNew failed: %x\n", (int)sc);
> +        return NULL;
> +    }
> +
> +    if(dataflow == eCapture){
> +        UInt32 enableio;
> +
> +        enableio = 1;
> +        sc = AudioUnitSetProperty(unit, kAudioOutputUnitProperty_EnableIO,
> +                kAudioUnitScope_Input, 1, &enableio, sizeof(enableio));
> +        if(sc != noErr){
> +            WARN("Couldn't enable I/O on input element: %x\n", (int)sc);
> +            AudioComponentInstanceDispose(unit);
> +            return NULL;
> +        }
> +
> +        enableio = 0;
> +        sc = AudioUnitSetProperty(unit, kAudioOutputUnitProperty_EnableIO,
> +                kAudioUnitScope_Output, 0, &enableio, sizeof(enableio));
> +        if(sc != noErr){
> +            WARN("Couldn't disable I/O on output element: %x\n", (int)sc);
> +            AudioComponentInstanceDispose(unit);
> +            return NULL;
> +        }
> +    }
> +
> +    sc = AudioUnitSetProperty(unit, kAudioOutputUnitProperty_CurrentDevice,
> +            kAudioUnitScope_Global, 0, &adevid, sizeof(adevid));
> +    if(sc != noErr){
> +        WARN("Couldn't set audio unit device\n");
> +        AudioComponentInstanceDispose(unit);
> +        return NULL;
> +    }
> +
> +    return unit;
> +}
> +
> +static void dump_adesc(const char *aux, AudioStreamBasicDescription *desc)
> +{
> +    TRACE("%s: mSampleRate: %f\n", aux, desc->mSampleRate);
> +    TRACE("%s: mBytesPerPacket: %u\n", aux, (unsigned int)desc->mBytesPerPacket);
> +    TRACE("%s: mFramesPerPacket: %u\n", aux, (unsigned int)desc->mFramesPerPacket);
> +    TRACE("%s: mBytesPerFrame: %u\n", aux, (unsigned int)desc->mBytesPerFrame);
> +    TRACE("%s: mChannelsPerFrame: %u\n", aux, (unsigned int)desc->mChannelsPerFrame);
> +    TRACE("%s: mBitsPerChannel: %u\n", aux, (unsigned int)desc->mBitsPerChannel);
> +}
> +
> +static HRESULT ca_get_audiodesc(AudioStreamBasicDescription *desc,
> +                                const WAVEFORMATEX *fmt)
> +{
> +    const WAVEFORMATEXTENSIBLE *fmtex = (const WAVEFORMATEXTENSIBLE *)fmt;
> +
> +    desc->mFormatFlags = 0;
> +
> +    if(fmt->wFormatTag == WAVE_FORMAT_PCM ||
> +            (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
> +             IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))){
> +        desc->mFormatID = kAudioFormatLinearPCM;
> +        if(fmt->wBitsPerSample > 8)
> +            desc->mFormatFlags = kAudioFormatFlagIsSignedInteger;
> +    }else if(fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
> +            (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
> +             IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))){
> +        desc->mFormatID = kAudioFormatLinearPCM;
> +        desc->mFormatFlags = kAudioFormatFlagIsFloat;
> +    }else if(fmt->wFormatTag == WAVE_FORMAT_MULAW ||
> +            (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
> +             IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_MULAW))){
> +        desc->mFormatID = kAudioFormatULaw;
> +    }else if(fmt->wFormatTag == WAVE_FORMAT_ALAW ||
> +            (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
> +             IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_ALAW))){
> +        desc->mFormatID = kAudioFormatALaw;
> +    }else
> +        return AUDCLNT_E_UNSUPPORTED_FORMAT;
> +
> +    desc->mSampleRate = fmt->nSamplesPerSec;
> +    desc->mBytesPerPacket = fmt->nBlockAlign;
> +    desc->mFramesPerPacket = 1;
> +    desc->mBytesPerFrame = fmt->nBlockAlign;
> +    desc->mChannelsPerFrame = fmt->nChannels;
> +    desc->mBitsPerChannel = fmt->wBitsPerSample;
> +    desc->mReserved = 0;
> +
> +    return S_OK;
> +}
> +
> +static HRESULT ca_setup_audiounit(EDataFlow dataflow, AudioComponentInstance unit,
> +                                  const WAVEFORMATEX *fmt, AudioStreamBasicDescription *dev_desc,
> +                                  AudioConverterRef *converter)
> +{
> +    OSStatus sc;
> +    HRESULT hr;
> +
> +    if(dataflow == eCapture){
> +        AudioStreamBasicDescription desc;
> +        UInt32 size;
> +        Float64 rate;
> +        fenv_t fenv;
> +        BOOL fenv_stored = TRUE;
> +
> +        hr = ca_get_audiodesc(&desc, fmt);
> +        if(FAILED(hr))
> +            return hr;
> +        dump_adesc("requested", &desc);
> +
> +        /* input-only units can't perform sample rate conversion, so we have to
> +         * set up our own AudioConverter to support arbitrary sample rates. */
> +        size = sizeof(*dev_desc);
> +        sc = AudioUnitGetProperty(unit, kAudioUnitProperty_StreamFormat,
> +                                  kAudioUnitScope_Input, 1, dev_desc, &size);
> +        if(sc != noErr){
> +            WARN("Couldn't get unit format: %x\n", (int)sc);
> +            return osstatus_to_hresult(sc);
> +        }
> +        dump_adesc("hardware", dev_desc);
> +
> +        rate = dev_desc->mSampleRate;
> +        *dev_desc = desc;
> +        dev_desc->mSampleRate = rate;
> +
> +        dump_adesc("final", dev_desc);
> +        sc = AudioUnitSetProperty(unit, kAudioUnitProperty_StreamFormat,
> +                                  kAudioUnitScope_Output, 1, dev_desc, sizeof(*dev_desc));
> +        if(sc != noErr){
> +            WARN("Couldn't set unit format: %x\n", (int)sc);
> +            return osstatus_to_hresult(sc);
> +        }
> +
> +        /* AudioConverterNew requires divide-by-zero SSE exceptions to be masked */
> +        if(feholdexcept(&fenv)){
> +            WARN("Failed to store fenv state\n");
> +            fenv_stored = FALSE;
> +        }
> +
> +        sc = AudioConverterNew(dev_desc, &desc, converter);
> +
> +        if(fenv_stored && fesetenv(&fenv))
> +            WARN("Failed to restore fenv state\n");
> +
> +        if(sc != noErr){
> +            WARN("Couldn't create audio converter: %x\n", (int)sc);
> +            return osstatus_to_hresult(sc);
> +        }
> +    }else{
> +        hr = ca_get_audiodesc(dev_desc, fmt);
> +        if(FAILED(hr))
> +            return hr;
> +
> +        dump_adesc("final", dev_desc);
> +        sc = AudioUnitSetProperty(unit, kAudioUnitProperty_StreamFormat,
> +                                  kAudioUnitScope_Input, 0, dev_desc, sizeof(*dev_desc));
> +        if(sc != noErr){
> +            WARN("Couldn't set format: %x\n", (int)sc);
> +            return osstatus_to_hresult(sc);
> +        }
> +    }
> +
> +    return S_OK;
> +}
> +
> +static NTSTATUS create_stream(void *args)
> +{
> +    struct create_stream_params *params = args;
> +    struct coreaudio_stream *stream = calloc(1, sizeof(*stream));
> +    AURenderCallbackStruct input;
> +    OSStatus sc;
> +
> +    if(!stream){
> +        params->result = E_OUTOFMEMORY;
> +        return STATUS_SUCCESS;
> +    }
> +
> +    stream->fmt = clone_format(params->fmt);
> +    if(!stream->fmt){
> +        params->result = E_OUTOFMEMORY;
> +        goto end;
> +    }
> +
> +    stream->period_ms = params->period / 10000;
> +    stream->period_frames = muldiv(params->period, stream->fmt->nSamplesPerSec, 10000000);
> +    stream->dev_id = params->dev_id;
> +    stream->flow = params->flow;
> +    stream->share = params->share;
> +
> +    stream->bufsize_frames = muldiv(params->duration, stream->fmt->nSamplesPerSec, 10000000);
> +    if(params->share == AUDCLNT_SHAREMODE_EXCLUSIVE)
> +        stream->bufsize_frames -= stream->bufsize_frames % stream->period_frames;
> +
> +    if(!(stream->unit = get_audiounit(stream->flow, stream->dev_id))){
> +        params->result = AUDCLNT_E_DEVICE_INVALIDATED;
> +        goto end;
> +    }
> +
> +    params->result = ca_setup_audiounit(stream->flow, stream->unit, stream->fmt, &stream->dev_desc, &stream->converter);
> +    if(FAILED(params->result)) goto end;
> +
> +    input.inputProcRefCon = stream;
> +    if(stream->flow == eCapture){
> +        input.inputProc = ca_capture_cb;
> +        sc = AudioUnitSetProperty(stream->unit, kAudioOutputUnitProperty_SetInputCallback,
> +                                  kAudioUnitScope_Output, 1, &input, sizeof(input));
> +    }else{
> +        input.inputProc = ca_render_cb;
> +        sc = AudioUnitSetProperty(stream->unit, kAudioUnitProperty_SetRenderCallback,
> +                                  kAudioUnitScope_Input, 0, &input, sizeof(input));
> +    }
> +    if(sc != noErr){
> +        WARN("Couldn't set callback: %x\n", (int)sc);
> +        params->result = osstatus_to_hresult(sc);
> +        goto end;
> +    }
> +
> +    sc = AudioUnitInitialize(stream->unit);
> +    if(sc != noErr){
> +        WARN("Couldn't initialize: %x\n", (int)sc);
> +        params->result = osstatus_to_hresult(sc);
> +        goto end;
> +    }
> +
> +    /* we play audio continuously because AudioOutputUnitStart sometimes takes
> +     * a while to return */
> +    sc = AudioOutputUnitStart(stream->unit);
> +    if(sc != noErr){
> +        WARN("Unit failed to start: %x\n", (int)sc);
> +        params->result = osstatus_to_hresult(sc);
> +        goto end;
> +    }
> +
> +    stream->local_buffer_size = stream->bufsize_frames * stream->fmt->nBlockAlign;
> +    if(NtAllocateVirtualMemory(GetCurrentProcess(), (void **)&stream->local_buffer, 0, &stream->local_buffer_size,
> +                               MEM_COMMIT, PAGE_READWRITE)){
> +        params->result = E_OUTOFMEMORY;
> +        goto end;
> +    }
> +    silence_buffer(stream, stream->local_buffer, stream->bufsize_frames);
> +
> +    if(stream->flow == eCapture){
> +        stream->cap_bufsize_frames = muldiv(params->duration, stream->dev_desc.mSampleRate, 10000000);
> +        stream->cap_buffer = malloc(stream->cap_bufsize_frames * stream->fmt->nBlockAlign);
> +    }
> +    params->result = S_OK;
> +
> +end:
> +    if(FAILED(params->result)){
> +        if(stream->converter) AudioConverterDispose(stream->converter);
> +        if(stream->unit) AudioComponentInstanceDispose(stream->unit);
> +        free(stream->fmt);
> +        free(stream);
> +    } else
> +        params->stream = stream;
> +
> +    return STATUS_SUCCESS;
> +}
> +
> +static NTSTATUS release_stream( void *args )
> +{
> +    struct release_stream_params *params = args;
> +    struct coreaudio_stream *stream = params->stream;
> +
> +    if(stream->unit){
> +        AudioOutputUnitStop(stream->unit);
> +        AudioComponentInstanceDispose(stream->unit);
> +    }
> +
> +    if(stream->converter) AudioConverterDispose(stream->converter);
> +    free(stream->wrap_buffer);
> +    free(stream->cap_buffer);
> +    if(stream->local_buffer)
> +        NtFreeVirtualMemory(GetCurrentProcess(), (void **)&stream->local_buffer,
> +                            &stream->local_buffer_size, MEM_RELEASE);
> +    free(stream->fmt);
> +    params->result = S_OK;
> +    return STATUS_SUCCESS;
> +}
> +
>  unixlib_entry_t __wine_unix_call_funcs[] =
>  {
>      get_endpoint_ids,
> +    create_stream,
> +    release_stream,
>  };
> diff --git a/dlls/winecoreaudio.drv/mmdevdrv.c b/dlls/winecoreaudio.drv/mmdevdrv.c
> index 0abfebfb1a5..572126562c6 100644
> --- a/dlls/winecoreaudio.drv/mmdevdrv.c
> +++ b/dlls/winecoreaudio.drv/mmdevdrv.c
> @@ -138,6 +138,10 @@ struct ACImpl {
>  
>      struct coreaudio_stream *stream;
>      struct list entry;
> +
> +    /* Temporary */
> +    BYTE *feed_wrap_buffer;
> +    UINT32 feed_wrap_bufsize_frames;
>  };
>  
>  static const IAudioClient3Vtbl AudioClient3_Vtbl;
> @@ -613,7 +617,9 @@ static ULONG WINAPI AudioClient_AddRef(IAudioClient3 *iface)
>  static ULONG WINAPI AudioClient_Release(IAudioClient3 *iface)
>  {
>      ACImpl *This = impl_from_IAudioClient3(iface);
> +    struct release_stream_params params;
>      ULONG ref;
> +
>      ref = InterlockedDecrement(&This->ref);
>      TRACE("(%p) Refcount now %u\n", This, ref);
>      if(!ref){
> @@ -627,22 +633,13 @@ static ULONG WINAPI AudioClient_Release(IAudioClient3 *iface)
>                  WaitForSingleObject(event, INFINITE);
>              CloseHandle(event);
>          }
> -        if (This->stream){
> -            AudioOutputUnitStop(This->stream->unit);
> -            AudioComponentInstanceDispose(This->stream->unit);
> -            if(This->stream->converter)
> -                AudioConverterDispose(This->stream->converter);
> -            HeapFree(GetProcessHeap(), 0, This->stream->cap_buffer);
> -            if(This->stream->local_buffer)
> -                NtFreeVirtualMemory(GetCurrentProcess(), (void **)&This->stream->local_buffer,
> -                                    &This->stream->local_buffer_size, MEM_RELEASE);
> +        if(This->stream){
>              if(This->stream->tmp_buffer)
>                  NtFreeVirtualMemory(GetCurrentProcess(), (void **)&This->stream->tmp_buffer,
>                                      &This->stream->tmp_buffer_size, MEM_RELEASE);
> -            free(This->stream->wrap_buffer);
>              HeapFree(GetProcessHeap(), 0, This->stream->resamp_buffer);
> -            CoTaskMemFree(This->stream->fmt);
> -            HeapFree(GetProcessHeap(), 0, This->stream);
> +            params.stream = This->stream;
> +            UNIX_CALL(release_stream, &params);
>          }
>          if(This->session){
>              EnterCriticalSection(&g_sessions_lock);
> @@ -650,6 +647,7 @@ static ULONG WINAPI AudioClient_Release(IAudioClient3 *iface)
>              LeaveCriticalSection(&g_sessions_lock);
>          }
>          HeapFree(GetProcessHeap(), 0, This->vols);
> +        free(This->feed_wrap_buffer);
>          IMMDevice_Release(This->parent);
>          IUnknown_Release(This->pUnkFTMarshal);
>          HeapFree(GetProcessHeap(), 0, This);
> @@ -717,27 +715,6 @@ static DWORD get_channel_mask(unsigned int channels)
>      return 0;
>  }
>  
> -static WAVEFORMATEX *clone_format(const WAVEFORMATEX *fmt)
> -{
> -    WAVEFORMATEX *ret;
> -    size_t size;
> -
> -    if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
> -        size = sizeof(WAVEFORMATEXTENSIBLE);
> -    else
> -        size = sizeof(WAVEFORMATEX);
> -
> -    ret = CoTaskMemAlloc(size);
> -    if(!ret)
> -        return NULL;
> -
> -    memcpy(ret, fmt, size);
> -
> -    ret->cbSize = size - sizeof(WAVEFORMATEX);
> -
> -    return ret;
> -}
> -
>  static HRESULT ca_get_audiodesc(AudioStreamBasicDescription *desc,
>          const WAVEFORMATEX *fmt)
>  {
> @@ -881,43 +858,6 @@ static void silence_buffer(struct coreaudio_stream *stream, BYTE *buffer, UINT32
>          memset(buffer, 0, frames * stream->fmt->nBlockAlign);
>  }
>  
> -/* CA is pulling data from us */
> -static OSStatus ca_render_cb(void *user, AudioUnitRenderActionFlags *flags,
> -        const AudioTimeStamp *ts, UInt32 bus, UInt32 nframes,
> -        AudioBufferList *data)
> -{
> -    ACImpl *This = user;
> -    UINT32 to_copy_bytes, to_copy_frames, chunk_bytes, lcl_offs_bytes;
> -
> -    OSSpinLockLock(&This->stream->lock);
> -
> -    if(This->stream->playing){
> -        lcl_offs_bytes = This->stream->lcl_offs_frames * This->stream->fmt->nBlockAlign;
> -        to_copy_frames = min(nframes, This->stream->held_frames);
> -        to_copy_bytes = to_copy_frames * This->stream->fmt->nBlockAlign;
> -
> -        chunk_bytes = (This->stream->bufsize_frames - This->stream->lcl_offs_frames) * This->stream->fmt->nBlockAlign;
> -
> -        if(to_copy_bytes > chunk_bytes){
> -            memcpy(data->mBuffers[0].mData, This->stream->local_buffer + lcl_offs_bytes, chunk_bytes);
> -            memcpy(((BYTE *)data->mBuffers[0].mData) + chunk_bytes, This->stream->local_buffer, to_copy_bytes - chunk_bytes);
> -        }else
> -            memcpy(data->mBuffers[0].mData, This->stream->local_buffer + lcl_offs_bytes, to_copy_bytes);
> -
> -        This->stream->lcl_offs_frames += to_copy_frames;
> -        This->stream->lcl_offs_frames %= This->stream->bufsize_frames;
> -        This->stream->held_frames -= to_copy_frames;
> -    }else
> -        to_copy_bytes = to_copy_frames = 0;
> -
> -    if(nframes > to_copy_frames)
> -        silence_buffer(This->stream, ((BYTE *)data->mBuffers[0].mData) + to_copy_bytes, nframes - to_copy_frames);
> -
> -    OSSpinLockUnlock(&This->stream->lock);
> -
> -    return noErr;
> -}
> -
>  static UINT buf_ptr_diff(UINT left, UINT right, UINT bufsize)
>  {
>      if(left <= right)
> @@ -945,18 +885,18 @@ static OSStatus feed_cb(AudioConverterRef converter, UInt32 *nframes, AudioBuffe
>      if(This->stream->cap_offs_frames + *nframes > This->stream->cap_bufsize_frames){
>          UINT32 chunk_frames = This->stream->cap_bufsize_frames - This->stream->cap_offs_frames;
>  
> -        if(This->stream->wrap_bufsize_frames < *nframes){
> -            free(This->stream->wrap_buffer);
> -            This->stream->wrap_buffer = malloc(data->mBuffers[0].mDataByteSize);
> -            This->stream->wrap_bufsize_frames = *nframes;
> +        if(This->feed_wrap_bufsize_frames < *nframes){
> +            free(This->feed_wrap_buffer);
> +            This->feed_wrap_buffer = malloc(data->mBuffers[0].mDataByteSize);
> +            This->feed_wrap_bufsize_frames = *nframes;
>          }
>  
> -        memcpy(This->stream->wrap_buffer, This->stream->cap_buffer + This->stream->cap_offs_frames * This->stream->fmt->nBlockAlign,
> +        memcpy(This->feed_wrap_buffer, This->stream->cap_buffer + This->stream->cap_offs_frames * This->stream->fmt->nBlockAlign,
>                  chunk_frames * This->stream->fmt->nBlockAlign);
> -        memcpy(This->stream->wrap_buffer + chunk_frames * This->stream->fmt->nBlockAlign, This->stream->cap_buffer,
> +        memcpy(This->feed_wrap_buffer + chunk_frames * This->stream->fmt->nBlockAlign, This->stream->cap_buffer,
>                  (*nframes - chunk_frames) * This->stream->fmt->nBlockAlign);
>  
> -        data->mBuffers[0].mData = This->stream->wrap_buffer;
> +        data->mBuffers[0].mData = This->feed_wrap_buffer;
>      }else
>          data->mBuffers[0].mData = This->stream->cap_buffer + This->stream->cap_offs_frames * This->stream->fmt->nBlockAlign;
>  
> @@ -1017,67 +957,6 @@ static void capture_resample(ACImpl *This)
>      }
>  }
>  
> -/* we need to trigger CA to pull data from the device and give it to us
> - *
> - * raw data from CA is stored in cap_buffer, possibly via wrap_buffer
> - *
> - * raw data is resampled from cap_buffer into resamp_buffer in period-size
> - * chunks and copied to local_buffer
> - */
> -static OSStatus ca_capture_cb(void *user, AudioUnitRenderActionFlags *flags,
> -        const AudioTimeStamp *ts, UInt32 bus, UInt32 nframes,
> -        AudioBufferList *data)
> -{
> -    ACImpl *This = user;
> -    AudioBufferList list;
> -    OSStatus sc;
> -    UINT32 cap_wri_offs_frames;
> -
> -    OSSpinLockLock(&This->stream->lock);
> -
> -    cap_wri_offs_frames = (This->stream->cap_offs_frames + This->stream->cap_held_frames) % This->stream->cap_bufsize_frames;
> -
> -    list.mNumberBuffers = 1;
> -    list.mBuffers[0].mNumberChannels = This->stream->fmt->nChannels;
> -    list.mBuffers[0].mDataByteSize = nframes * This->stream->fmt->nBlockAlign;
> -
> -    if(!This->stream->playing || cap_wri_offs_frames + nframes > This->stream->cap_bufsize_frames){
> -        if(This->stream->wrap_bufsize_frames < nframes){
> -            free(This->stream->wrap_buffer);
> -            This->stream->wrap_buffer = malloc(list.mBuffers[0].mDataByteSize);
> -            This->stream->wrap_bufsize_frames = nframes;
> -        }
> -
> -        list.mBuffers[0].mData = This->stream->wrap_buffer;
> -    }else
> -        list.mBuffers[0].mData = This->stream->cap_buffer + cap_wri_offs_frames * This->stream->fmt->nBlockAlign;
> -
> -    sc = AudioUnitRender(This->stream->unit, flags, ts, bus, nframes, &list);
> -    if(sc != noErr){
> -        OSSpinLockUnlock(&This->stream->lock);
> -        return sc;
> -    }
> -
> -    if(This->stream->playing){
> -        if(list.mBuffers[0].mData == This->stream->wrap_buffer){
> -            ca_wrap_buffer(This->stream->cap_buffer,
> -                    cap_wri_offs_frames * This->stream->fmt->nBlockAlign,
> -                    This->stream->cap_bufsize_frames * This->stream->fmt->nBlockAlign,
> -                    This->stream->wrap_buffer, list.mBuffers[0].mDataByteSize);
> -        }
> -
> -        This->stream->cap_held_frames += list.mBuffers[0].mDataByteSize / This->stream->fmt->nBlockAlign;
> -        if(This->stream->cap_held_frames > This->stream->cap_bufsize_frames){
> -            This->stream->cap_offs_frames += This->stream->cap_held_frames % This->stream->cap_bufsize_frames;
> -            This->stream->cap_offs_frames %= This->stream->cap_bufsize_frames;
> -            This->stream->cap_held_frames = This->stream->cap_bufsize_frames;
> -        }
> -    }
> -
> -    OSSpinLockUnlock(&This->stream->lock);
> -    return noErr;
> -}
> -
>  static void dump_adesc(const char *aux, AudioStreamBasicDescription *desc)
>  {
>      TRACE("%s: mSampleRate: %f\n", aux, desc->mSampleRate);
> @@ -1168,9 +1047,8 @@ static HRESULT WINAPI AudioClient_Initialize(IAudioClient3 *iface,
>          const GUID *sessionguid)
>  {
>      ACImpl *This = impl_from_IAudioClient3(iface);
> -    struct coreaudio_stream *stream;
> -    HRESULT hr;
> -    OSStatus sc;
> +    struct release_stream_params release_params;
> +    struct create_stream_params params;
>      UINT32 i;
>  
>      TRACE("(%p)->(%x, %x, %s, %s, %p, %s)\n", This, mode, flags,
> @@ -1233,146 +1111,50 @@ static HRESULT WINAPI AudioClient_Initialize(IAudioClient3 *iface,
>          return AUDCLNT_E_ALREADY_INITIALIZED;
>      }
>  
> -    stream = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*stream));
> -    if(!stream){
> -        LeaveCriticalSection(&g_sessions_lock);
> -        return E_OUTOFMEMORY;
> -    }
> -
> -    stream->fmt = clone_format(fmt);
> -    if(!stream->fmt){
> -        HeapFree(GetProcessHeap(), 0, stream);
> -        LeaveCriticalSection(&g_sessions_lock);
> -        return E_OUTOFMEMORY;
> -    }
> -
> -    stream->period_ms = period / 10000;
> -    stream->period_frames = MulDiv(period, stream->fmt->nSamplesPerSec, 10000000);
> -
> -    stream->bufsize_frames = MulDiv(duration, fmt->nSamplesPerSec, 10000000);
> -    if(mode == AUDCLNT_SHAREMODE_EXCLUSIVE)
> -        stream->bufsize_frames -= stream->bufsize_frames % stream->period_frames;
> -
> -    if(!(stream->unit = get_audiounit(This->dataflow, This->adevid))){
> -        CoTaskMemFree(stream->fmt);
> -        HeapFree(GetProcessHeap(), 0, stream);
> -        LeaveCriticalSection(&g_sessions_lock);
> -        return AUDCLNT_E_DEVICE_INVALIDATED;
> -    }
> -
> -    hr = ca_setup_audiounit(This->dataflow, stream->unit, stream->fmt, &stream->dev_desc, &stream->converter);
> -    if(FAILED(hr)){
> -        CoTaskMemFree(stream->fmt);
> -        HeapFree(GetProcessHeap(), 0, stream);
> -        LeaveCriticalSection(&g_sessions_lock);
> -        return hr;
> -    }
> -
> -    if(This->dataflow == eCapture){
> -        AURenderCallbackStruct input;
> -
> -        memset(&input, 0, sizeof(input));
> -        input.inputProc = &ca_capture_cb;
> -        input.inputProcRefCon = This;
> -
> -        sc = AudioUnitSetProperty(stream->unit, kAudioOutputUnitProperty_SetInputCallback,
> -                kAudioUnitScope_Output, 1, &input, sizeof(input));
> -        if(sc != noErr){
> -            WARN("Couldn't set callback: %x\n", (int)sc);
> -            AudioConverterDispose(stream->converter);
> -            CoTaskMemFree(stream->fmt);
> -            HeapFree(GetProcessHeap(), 0, stream);
> -            LeaveCriticalSection(&g_sessions_lock);
> -            return osstatus_to_hresult(sc);
> -        }
> -    }else{
> -        AURenderCallbackStruct input;
> -
> -        memset(&input, 0, sizeof(input));
> -        input.inputProc = &ca_render_cb;
> -        input.inputProcRefCon = This;
> -
> -        sc = AudioUnitSetProperty(stream->unit, kAudioUnitProperty_SetRenderCallback,
> -                kAudioUnitScope_Input, 0, &input, sizeof(input));
> -        if(sc != noErr){
> -            WARN("Couldn't set callback: %x\n", (int)sc);
> -            CoTaskMemFree(stream->fmt);
> -            HeapFree(GetProcessHeap(), 0, stream);
> -            LeaveCriticalSection(&g_sessions_lock);
> -            return osstatus_to_hresult(sc);
> -        }
> -    }
> +    params.dev_id = This->adevid;
> +    params.flow = This->dataflow;
> +    params.share = mode;
> +    params.duration = duration;
> +    params.period = period;
> +    params.fmt = fmt;
>  
> -    sc = AudioUnitInitialize(stream->unit);
> -    if(sc != noErr){
> -        WARN("Couldn't initialize: %x\n", (int)sc);
> -        if(stream->converter)
> -            AudioConverterDispose(stream->converter);
> -        CoTaskMemFree(stream->fmt);
> -        HeapFree(GetProcessHeap(), 0, stream);
> -        LeaveCriticalSection(&g_sessions_lock);
> -        return osstatus_to_hresult(sc);
> -    }
> -
> -    /* we play audio continuously because AudioOutputUnitStart sometimes takes
> -     * a while to return */
> -    sc = AudioOutputUnitStart(stream->unit);
> -    if(sc != noErr){
> -        WARN("Unit failed to start: %x\n", (int)sc);
> -        if(stream->converter)
> -            AudioConverterDispose(stream->converter);
> -        CoTaskMemFree(stream->fmt);
> -        HeapFree(GetProcessHeap(), 0, stream);
> -        LeaveCriticalSection(&g_sessions_lock);
> -        return osstatus_to_hresult(sc);
> -    }
> -
> -    stream->local_buffer_size = stream->bufsize_frames * fmt->nBlockAlign;
> -    NtAllocateVirtualMemory(GetCurrentProcess(), (void **)&stream->local_buffer, 0,
> -                            &stream->local_buffer_size, MEM_COMMIT, PAGE_READWRITE);
> -    silence_buffer(stream, stream->local_buffer, stream->bufsize_frames);
> -
> -    if(This->dataflow == eCapture){
> -        stream->cap_bufsize_frames = MulDiv(duration, stream->dev_desc.mSampleRate, 10000000);
> -        stream->cap_buffer = HeapAlloc(GetProcessHeap(), 0, stream->cap_bufsize_frames * stream->fmt->nBlockAlign);
> -    }
> +    UNIX_CALL(create_stream, &params);
> +    if(FAILED(params.result)) goto end;
>  
> -    stream->share = mode;
>      This->flags = flags;
>      This->channel_count = fmt->nChannels;
>      This->period_ms = period / 10000;
>  
>      This->vols = HeapAlloc(GetProcessHeap(), 0, This->channel_count * sizeof(float));
>      if(!This->vols){
> -        CoTaskMemFree(stream->fmt);
> -        HeapFree(GetProcessHeap(), 0, stream);
> -        LeaveCriticalSection(&g_sessions_lock);
> -        return E_OUTOFMEMORY;
> +        params.result = E_OUTOFMEMORY;
> +        goto end;
>      }
>  
>      for(i = 0; i < This->channel_count; ++i)
>          This->vols[i] = 1.f;
>  
> -    hr = get_audio_session(sessionguid, This->parent, fmt->nChannels,
> -            &This->session);
> -    if(FAILED(hr)){
> -        CoTaskMemFree(stream->fmt);
> -        HeapFree(GetProcessHeap(), 0, stream);
> -        HeapFree(GetProcessHeap(), 0, This->vols);
> -        This->vols = NULL;
> -        LeaveCriticalSection(&g_sessions_lock);
> -        return E_INVALIDARG;
> -    }
> +    params.result = get_audio_session(sessionguid, This->parent, fmt->nChannels, &This->session);
> +    if(FAILED(params.result)) goto end;
>  
>      list_add_tail(&This->session->clients, &This->entry);
>  
> -    ca_setvol(This, stream, -1);
> +    ca_setvol(This, params.stream, -1);
>  
> -    This->stream = stream;
> +end:
> +    if(FAILED(params.result)){
> +        if(params.stream){
> +            release_params.stream = This->stream;
> +            UNIX_CALL(release_stream, &release_params);
> +        }
> +        HeapFree(GetProcessHeap(), 0, This->vols);
> +        This->vols = NULL;
> +    }else
> +        This->stream = params.stream;
>  
>      LeaveCriticalSection(&g_sessions_lock);
>  
> -    return S_OK;
> +    return params.result;
>  }
>  
>  static HRESULT WINAPI AudioClient_GetBufferSize(IAudioClient3 *iface,
> diff --git a/dlls/winecoreaudio.drv/unixlib.h b/dlls/winecoreaudio.drv/unixlib.h
> index 1b773b7f820..7ebdc0b7786 100644
> --- a/dlls/winecoreaudio.drv/unixlib.h
> +++ b/dlls/winecoreaudio.drv/unixlib.h
> @@ -57,9 +57,29 @@ struct get_endpoint_ids_params
>      unsigned int default_idx;
>  };
>  
> +struct create_stream_params
> +{
> +    DWORD dev_id;
> +    EDataFlow flow;
> +    AUDCLNT_SHAREMODE share;
> +    REFERENCE_TIME duration;
> +    REFERENCE_TIME period;
> +    const WAVEFORMATEX *fmt;
> +    HRESULT result;
> +    struct coreaudio_stream *stream;
> +};
> +
> +struct release_stream_params
> +{
> +    struct coreaudio_stream *stream;
> +    HRESULT result;
> +};
> +
>  enum unix_funcs
>  {
>      unix_get_endpoint_ids,
> +    unix_create_stream,
> +    unix_release_stream,
>  };
>  
>  extern unixlib_handle_t coreaudio_handle;
> -- 
> 2.23.0
> 
> 



More information about the wine-devel mailing list