]> Repos - portaudio/commitdiff
wasapi:
authordmitrykos <dmitrykos@0f58301d-fd10-0410-b4af-bbb618454e57>
Fri, 9 Jul 2010 23:57:51 +0000 (23:57 +0000)
committerdmitrykos <dmitrykos@0f58301d-fd10-0410-b4af-bbb618454e57>
Fri, 9 Jul 2010 23:57:51 +0000 (23:57 +0000)
 - fixed memory corruption if format is set to non-interlieved and mono <> stereo mini-mixer is used for to convert mono to stereo (conversion is done due to WASAPI limitation)
 - made WASAPI fully passing patest_wire.c (full-duplex combinations) for shared and exclusive modes
 - if WASAPI device failed to open due to unsupported format it will not crash top PA layer anymore
 - improved full-duplex mode for shared/exclusive (although exclusive full-duplex mode still produces audio glitches due to difference in input/output device buffer size due to unknown WASAPI reason, with shared mode all is ok)
 - exclusive mode will now work with 1 channel audio always, even if it is not supported by WASAPI natively (we use own mono <> stereo mini-mixer)

src/hostapi/wasapi/pa_win_wasapi.c

index f7e054fb8d5627d7a106a9ba2c920f19b57806b6..eadfd5694da17afaf1092a4158fbc2ca15de043e 100644 (file)
@@ -924,7 +924,7 @@ typedef enum EMixerDir { MIX_DIR__1TO2, MIX_DIR__2TO1 } EMixerDir;
        TYPE * __restrict end  = to + count;\\r
        while (to != end)\\r
        {\\r
-               *to ++ = (TYPE)((float)(from[0] + from[1]) * 0.5f);\\r
+               *to ++ = (TYPE)((float)(from[0] + from[1]) * 0.70710678118654752440084436210485f/*1/sqrt(2)*/);\\r
                from += 2;\\r
        }\r
 \r
@@ -948,7 +948,7 @@ static MixMonoToStereoF _GetMonoToStereoMixer(PaSampleFormat format, EMixerDir d
        switch (dir)\r
        {\r
        case MIX_DIR__1TO2:\r
-               switch (format)\r
+               switch (format & ~paNonInterleaved)\r
                {\r
                case paUInt8:   return _MixMonoToStereo_1TO2_8;\r
                case paInt16:   return _MixMonoToStereo_1TO2_16;\r
@@ -959,7 +959,7 @@ static MixMonoToStereoF _GetMonoToStereoMixer(PaSampleFormat format, EMixerDir d
                break;\r
 \r
        case MIX_DIR__2TO1:\r
-               switch (format)\r
+               switch (format & ~paNonInterleaved)\r
                {\r
                case paUInt8:   return _MixMonoToStereo_2TO1_8;\r
                case paInt16:   return _MixMonoToStereo_2TO1_16;\r
@@ -1505,7 +1505,7 @@ static void LogWAVEFORMATEXTENSIBLE(const WAVEFORMATEXTENSIBLE *in)
 }\r
 \r
 // ------------------------------------------------------------------------------------------\r
-static PaSampleFormat waveformatToPaFormat(const WAVEFORMATEXTENSIBLE *in)\r
+static PaSampleFormat WaveToPaFormat(const WAVEFORMATEXTENSIBLE *in)\r
 {\r
     const WAVEFORMATEX *old = (WAVEFORMATEX *)in;\r
 \r
@@ -1742,44 +1742,68 @@ static PaError GetClosestFormat(IAudioClient *myClient, double sampleRate,
        }\r
        else\r
        {\r
-#define FORMATTESTS 3\r
-static const int BestToWorst[FORMATTESTS] = { paFloat32, paInt24, paInt16 };\r
-\r
-               // try selecting suitable sample type\r
+               static const int BestToWorst[] = { paFloat32, paInt24, paInt16 };\r
                int i;\r
-               for (i = 0; i < FORMATTESTS; ++i)\r
+\r
+               // Try combination stereo and we will use built-in mono-stereo mixer then\r
+               if (params.channelCount == 1)\r
                {\r
-                       WAVEFORMATEXTENSIBLE ext = { 0 };\r
-                       wasapiFillWFEXT(&ext, BestToWorst[i], sampleRate, params.channelCount);\r
+                       WAVEFORMATEXTENSIBLE stereo = { 0 };\r
+\r
+                       PaStreamParameters stereo_params = params;\r
+                       stereo_params.channelCount = 2;\r
+\r
+                       MakeWaveFormatFromParams(&stereo, &stereo_params, sampleRate);\r
 \r
-                       hr = IAudioClient_IsFormatSupported(myClient, shareMode, &ext.Format, (shareMode == AUDCLNT_SHAREMODE_SHARED ? &sharedClosestMatch : NULL));\r
+                       hr = IAudioClient_IsFormatSupported(myClient, shareMode, &stereo.Format, (shareMode == AUDCLNT_SHAREMODE_SHARED ? &sharedClosestMatch : NULL));\r
                        if (hr == S_OK)\r
                        {\r
-                               memcpy(outWavex, &ext, sizeof(WAVEFORMATEXTENSIBLE));\r
-                               answer = paFormatIsSupported;\r
-                               break;\r
+                               memcpy(outWavex, &stereo, sizeof(WAVEFORMATEXTENSIBLE));\r
+                               CoTaskMemFree(sharedClosestMatch);\r
+                               return (answer = paFormatIsSupported);\r
                        }\r
-               }\r
 \r
-               if (answer != paFormatIsSupported)\r
-               {\r
-                       // If mono, then driver does not support 1 channel, we use internal workaround\r
-                       // of tiny software mixing functionality, e.g. we provide to user buffer 1 channel\r
-                       // but then mix into 2 for device buffer\r
-                       if (params.channelCount == 1)\r
+                       // Try selecting suitable sample type\r
+                       for (i = 0; i < STATIC_ARRAY_SIZE(BestToWorst); ++i)\r
                        {\r
-                               WAVEFORMATEXTENSIBLE stereo = { 0 };\r
-                               wasapiFillWFEXT(&stereo, params.sampleFormat, sampleRate, 2);\r
+                               WAVEFORMATEXTENSIBLE sample = { 0 };\r
 \r
-                               hr = IAudioClient_IsFormatSupported(myClient, shareMode, &stereo.Format, (shareMode == AUDCLNT_SHAREMODE_SHARED ? &sharedClosestMatch : NULL));\r
+                               PaStreamParameters sample_params = stereo_params;\r
+                               sample_params.sampleFormat = BestToWorst[i];\r
+\r
+                               MakeWaveFormatFromParams(&sample, &sample_params, sampleRate);\r
+\r
+                               hr = IAudioClient_IsFormatSupported(myClient, shareMode, &sample.Format, (shareMode == AUDCLNT_SHAREMODE_SHARED ? &sharedClosestMatch : NULL));\r
                                if (hr == S_OK)\r
                                {\r
-                                       memcpy(outWavex, &stereo, sizeof(WAVEFORMATEXTENSIBLE));\r
-                                       answer = paFormatIsSupported;\r
+                                       memcpy(outWavex, &sample, sizeof(WAVEFORMATEXTENSIBLE));\r
+                                       CoTaskMemFree(sharedClosestMatch);\r
+                                       return (answer = paFormatIsSupported);\r
                                }\r
                        }\r
                }\r
 \r
+               // Try selecting suitable sample type\r
+               for (i = 0; i < STATIC_ARRAY_SIZE(BestToWorst); ++i)\r
+               {\r
+                       WAVEFORMATEXTENSIBLE spfmt = { 0 };\r
+\r
+                       PaStreamParameters spfmt_params = params;\r
+                       spfmt_params.sampleFormat = BestToWorst[i];\r
+\r
+                       MakeWaveFormatFromParams(&spfmt, &spfmt_params, sampleRate);\r
+\r
+                       hr = IAudioClient_IsFormatSupported(myClient, shareMode, &spfmt.Format, (shareMode == AUDCLNT_SHAREMODE_SHARED ? &sharedClosestMatch : NULL));\r
+                       if (hr == S_OK)\r
+                       {\r
+                               memcpy(outWavex, &spfmt, sizeof(WAVEFORMATEXTENSIBLE));\r
+                               CoTaskMemFree(sharedClosestMatch);\r
+                               answer = paFormatIsSupported;\r
+                               break;\r
+                       }\r
+               }\r
+\r
+               // Nothing helped\r
                LogHostError(hr);\r
        }\r
 \r
@@ -1984,9 +2008,9 @@ static void _CalculateAlignedPeriod(PaWasapiSubStream *pSub, UINT32 *nFramesPerL
 }\r
 \r
 // ------------------------------------------------------------------------------------------\r
-static HRESULT CreateAudioClient(PaWasapiSubStream *pSub, PaWasapiDeviceInfo *pInfo,\r
-       const PaStreamParameters *params, UINT32 framesPerLatency, double sampleRate,\r
-       BOOL blocking, BOOL output, BOOL fullDuplex, PaError *pa_error)\r
+static HRESULT CreateAudioClient(PaWasapiStream *pStream, PaWasapiSubStream *pSub, \r
+       PaWasapiDeviceInfo *pInfo, const PaStreamParameters *params, UINT32 framesPerLatency, \r
+       double sampleRate, BOOL blocking, BOOL output, BOOL fullDuplex, PaError *pa_error)\r
 {\r
        PaError error;\r
     HRESULT hr;\r
@@ -2017,7 +2041,7 @@ static HRESULT CreateAudioClient(PaWasapiSubStream *pSub, PaWasapiDeviceInfo *pI
                goto done; // fail, format not supported\r
        }\r
 \r
-       // Check for Mono >> Stereo workaround\r
+       // Check for Mono <<>> Stereo workaround\r
        if ((params->channelCount == 1) && (pSub->wavex.Format.nChannels == 2))\r
        {\r
                if (blocking)\r
@@ -2027,7 +2051,7 @@ static HRESULT CreateAudioClient(PaWasapiSubStream *pSub, PaWasapiDeviceInfo *pI
                }\r
 \r
                // select mixer\r
-               pSub->monoMixer = _GetMonoToStereoMixer(params->sampleFormat, (pInfo->flow == eRender ? MIX_DIR__1TO2 : MIX_DIR__2TO1));\r
+               pSub->monoMixer = _GetMonoToStereoMixer(WaveToPaFormat(&pSub->wavex), (pInfo->flow == eRender ? MIX_DIR__1TO2 : MIX_DIR__2TO1));\r
                if (pSub->monoMixer == NULL)\r
                {\r
                        LogHostError(hr = AUDCLNT_E_UNSUPPORTED_FORMAT);\r
@@ -2074,7 +2098,7 @@ static HRESULT CreateAudioClient(PaWasapiSubStream *pSub, PaWasapiDeviceInfo *pI
                framesPerLatency = MakeFramesFromHns(pInfo->DefaultDevicePeriod, pSub->wavex.Format.nSamplesPerSec);\r
 \r
        // Calculate aligned period\r
-       _CalculateAlignedPeriod(pSub, &framesPerLatency, ALIGN_FWD);\r
+       _CalculateAlignedPeriod(pSub, &framesPerLatency, ALIGN_BWD);\r
 \r
        /*! Enforce min/max period for device in Shared mode to avoid bad audio quality.\r
         Avoid doing so for Exclusive mode as alignment will suffer.\r
@@ -2253,7 +2277,7 @@ static HRESULT CreateAudioClient(PaWasapiSubStream *pSub, PaWasapiDeviceInfo *pI
                        }\r
 \r
                        // Select mixer\r
-                       pSub->monoMixer = _GetMonoToStereoMixer(params->sampleFormat, (pInfo->flow == eRender ? MIX_DIR__1TO2 : MIX_DIR__2TO1));\r
+                       pSub->monoMixer = _GetMonoToStereoMixer(WaveToPaFormat(&pSub->wavex), (pInfo->flow == eRender ? MIX_DIR__1TO2 : MIX_DIR__2TO1));\r
                        if (pSub->monoMixer == NULL)\r
                        {\r
                                LogHostError(hr = AUDCLNT_E_UNSUPPORTED_FORMAT);\r
@@ -2411,21 +2435,17 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
                        stream->in.streamFlags = 0; // polling interface is implemented for full-duplex mode also\r
 \r
                // Create Audio client\r
-               hr = CreateAudioClient(&stream->in, info, inputParameters, framesPerBuffer/*framesPerLatency*/,\r
+               hr = CreateAudioClient(stream, &stream->in, info, inputParameters, framesPerBuffer/*framesPerLatency*/,\r
                        sampleRate, (streamCallback == NULL), FALSE, fullDuplex, &result);\r
         if (hr != S_OK)\r
                {\r
-            LogHostError(hr);\r
-                       if (hr != AUDCLNT_E_UNSUPPORTED_FORMAT)\r
-                               result = paInvalidDevice;\r
-\r
-                       LogPaError(result);\r
+                       LogPaError(result = paInvalidDevice);\r
                        goto error;\r
         }\r
                LogWAVEFORMATEXTENSIBLE(&stream->in.wavex);\r
 \r
                // Get closest format\r
-        hostInputSampleFormat = PaUtil_SelectClosestAvailableFormat( waveformatToPaFormat(&stream->in.wavex), inputSampleFormat );\r
+        hostInputSampleFormat = PaUtil_SelectClosestAvailableFormat( WaveToPaFormat(&stream->in.wavex), inputSampleFormat );\r
 \r
                // Create volume mgr\r
                stream->inVol = NULL;\r
@@ -2529,21 +2549,17 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
                        stream->out.streamFlags = 0; // polling interface is implemented for full-duplex mode also\r
 \r
                // Create Audio client\r
-               hr = CreateAudioClient(&stream->out, info, outputParameters, framesPerBuffer/*framesPerLatency*/,\r
+               hr = CreateAudioClient(stream, &stream->out, info, outputParameters, framesPerBuffer/*framesPerLatency*/,\r
                        sampleRate, (streamCallback == NULL), TRUE, fullDuplex, &result);\r
         if (hr != S_OK)\r
                {\r
-            LogHostError(hr);\r
-                       if (hr != AUDCLNT_E_UNSUPPORTED_FORMAT)\r
-                               result = paInvalidDevice;\r
-\r
-                       LogPaError(result);\r
+                       LogPaError(result = paInvalidDevice);\r
                        goto error;\r
         }\r
                LogWAVEFORMATEXTENSIBLE(&stream->out.wavex);\r
 \r
         // Get closest format\r
-        hostOutputSampleFormat = PaUtil_SelectClosestAvailableFormat( waveformatToPaFormat(&stream->out.wavex), outputSampleFormat );\r
+        hostOutputSampleFormat = PaUtil_SelectClosestAvailableFormat( WaveToPaFormat(&stream->out.wavex), outputSampleFormat );\r
 \r
                // Activate volume\r
                stream->outVol = NULL;\r
@@ -2651,6 +2667,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
                if (stream->in.period != stream->out.period)\r
                {\r
                        PRINT(("WASAPI: OpenStream: period discrepancy\n"));\r
+                       LogPaError(result = paBadIODeviceCombination);\r
                        goto error;\r
                }\r
 \r
@@ -3935,7 +3952,7 @@ PA_THREAD_FUNC ProcThreadPoll(void *param)
                while (WaitForSingleObject(stream->hCloseRequest, sleep_ms) == WAIT_TIMEOUT)\r
                {\r
                        UINT32 i_frames = 0, i_processed = 0;\r
-                       BYTE *i_data = NULL, *o_data = NULL;\r
+                       BYTE *i_data = NULL, *o_data = NULL, *o_data_host = NULL;\r
                        DWORD i_flags = 0;\r
                        UINT32 o_frames = 0;\r
 \r
@@ -3967,6 +3984,7 @@ PA_THREAD_FUNC ProcThreadPoll(void *param)
                                {\r
                                        // processed amount of i_frames\r
                                        i_processed = i_frames;\r
+                                       o_data_host = o_data;\r
 \r
                                        // convert output mono\r
                                        if (stream->out.monoMixer)\r
@@ -3976,7 +3994,17 @@ PA_THREAD_FUNC ProcThreadPoll(void *param)
                                                #undef __DIV_8\r
                                                // expand buffer (one way only for better performance due to no calls to realloc)\r
                                                if (mono_frames_size > stream->out.monoBufferSize)\r
+                                               {\r
                                                        stream->out.monoBuffer = realloc(stream->out.monoBuffer, (stream->out.monoBufferSize = mono_frames_size));\r
+                                                       if (stream->out.monoBuffer == NULL)\r
+                                                       {\r
+                                                               LogPaError(paInsufficientMemory);\r
+                                                               break;\r
+                                                       }\r
+                                               }\r
+\r
+                                               // replace buffer pointer\r
+                                               o_data = (BYTE *)stream->out.monoBuffer;\r
                                        }\r
 \r
                                        // convert input mono\r
@@ -3987,7 +4015,14 @@ PA_THREAD_FUNC ProcThreadPoll(void *param)
                                                #undef __DIV_8\r
                                                // expand buffer (one way only for better performance due to no calls to realloc)\r
                                                if (mono_frames_size > stream->in.monoBufferSize)\r
+                                               {\r
                                                        stream->in.monoBuffer = realloc(stream->in.monoBuffer, (stream->in.monoBufferSize = mono_frames_size));\r
+                                                       if (stream->in.monoBuffer == NULL)\r
+                                                       {\r
+                                                               LogPaError(paInsufficientMemory);\r
+                                                               break;\r
+                                                       }\r
+                                               }\r
 \r
                                                // mix 2 to 1 input channels\r
                                                stream->in.monoMixer(stream->in.monoBuffer, i_data, i_processed);\r
@@ -4001,7 +4036,7 @@ PA_THREAD_FUNC ProcThreadPoll(void *param)
 \r
                                        // mix 1 to 2 output channels\r
                                        if (stream->out.monoBuffer)\r
-                                               stream->out.monoMixer(o_data, stream->out.monoBuffer, o_processed);\r
+                                               stream->out.monoMixer(o_data_host, stream->out.monoBuffer, o_processed);\r
 \r
                                        // release host output buffer\r
                                        if ((hr = IAudioRenderClient_ReleaseBuffer(stream->rclient, o_processed, 0)) != S_OK)\r