]> Repos - portaudio/commitdiff
wasapi:
authordmitrykos <dmitrykos@0f58301d-fd10-0410-b4af-bbb618454e57>
Mon, 12 Apr 2010 20:55:34 +0000 (20:55 +0000)
committerdmitrykos <dmitrykos@0f58301d-fd10-0410-b4af-bbb618454e57>
Mon, 12 Apr 2010 20:55:34 +0000 (20:55 +0000)
 - fixed crash in Pa_OpenStream call if WASAPI device did not open or something failed (happened on stream handle cleanup due to double attempt to free same pointer)
 - improved the way WASAPI device is opening, such error as AUDCLNT_E_BUFFER_SIZE_ERROR which comes due to incorrectly small device period is fixed for Vista (and all other, including future) systems as well, latency will be corrected to device minimum (usually 3ms).
 - if suggestedLatency is set to 0 in Pa_OpenStream call then WASAPI device will select device default latency (in Exclusive mode it is 10ms, in Shared 20ms)
 - improved on-screen logging of WASAPI devices available during WASAPI initialization phase

src/hostapi/wasapi/pa_win_wasapi.c

index d7f58f851a158a90ecf9d6e15db8401a33d6d56c..2e26f7671dfc2e6d883ac2beca88d4812b8751a5 100644 (file)
@@ -1048,7 +1048,8 @@ PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
             deviceInfo->structVersion = 2;\r
             deviceInfo->hostApi       = hostApiIndex;\r
 \r
-                       PA_DEBUG(("WASAPI: device i: %d\n", i));\r
+                       PA_DEBUG(("WASAPI: device idx: %02d\n", i));\r
+                       PA_DEBUG(("WASAPI: ---------------\n"));\r
 \r
             hr = IMMDeviceCollection_Item(pEndPoints, i, &paWasapi->devInfo[i].device);\r
             IF_FAILED_JUMP(hr, error);\r
@@ -1104,6 +1105,7 @@ PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
                                                _snprintf(deviceName, MAX_STR_LEN-1, "baddev%d", i);\r
                     deviceInfo->name = deviceName;\r
                     PropVariantClear(&value);\r
+                                       PA_DEBUG(("WASAPI:%d| name[%s]\n", i, deviceInfo->name));\r
                 }\r
 \r
                 // Default format\r
@@ -1132,7 +1134,7 @@ PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
                                        #else\r
                                                paWasapi->devInfo[i].formFactor = (EndpointFormFactor)value.uintVal;\r
                                        #endif\r
-                                       PA_DEBUG(("WASAPI: device[%s] form-factor: %d\n", deviceInfo->name, paWasapi->devInfo[i].formFactor));\r
+                                       PA_DEBUG(("WASAPI:%d| form-factor[%d]\n", i, paWasapi->devInfo[i].formFactor));\r
                     // cleanup\r
                     PropVariantClear(&value);\r
                 }\r
@@ -1182,30 +1184,31 @@ PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
             }\r
 \r
             // we can now fill in portaudio device data\r
-            deviceInfo->maxInputChannels  = 0;  //for now\r
-            deviceInfo->maxOutputChannels = 0;  //for now\r
+            deviceInfo->maxInputChannels  = 0;\r
+            deviceInfo->maxOutputChannels = 0;\r
+                       deviceInfo->defaultSampleRate = paWasapi->devInfo[i].DefaultFormat.Format.nSamplesPerSec;\r
             switch (paWasapi->devInfo[i].flow)\r
                        {\r
-            case eRender:\r
-                // WASAPI accepts exact channels count\r
+                       case eRender: {\r
                 deviceInfo->maxOutputChannels           = paWasapi->devInfo[i].DefaultFormat.Format.nChannels;\r
                 deviceInfo->defaultHighOutputLatency = nano100ToSeconds(paWasapi->devInfo[i].DefaultDevicePeriod);\r
                 deviceInfo->defaultLowOutputLatency  = nano100ToSeconds(paWasapi->devInfo[i].MinimumDevicePeriod);\r
-            break;\r
-            case eCapture:\r
-                // WASAPI accepts exact channels count\r
+                               PA_DEBUG(("WASAPI:%d| def.SR[%d] max.CH[%d] latency{hi[%f] lo[%f]}\n", i, (UINT32)deviceInfo->defaultSampleRate,\r
+                                       deviceInfo->maxOutputChannels, (float)deviceInfo->defaultHighOutputLatency, (float)deviceInfo->defaultLowOutputLatency));\r
+                               break;}\r
+                       case eCapture: {\r
                 deviceInfo->maxInputChannels           = paWasapi->devInfo[i].DefaultFormat.Format.nChannels;\r
                 deviceInfo->defaultHighInputLatency = nano100ToSeconds(paWasapi->devInfo[i].DefaultDevicePeriod);\r
                 deviceInfo->defaultLowInputLatency  = nano100ToSeconds(paWasapi->devInfo[i].MinimumDevicePeriod);\r
-            break;\r
+                               PA_DEBUG(("WASAPI:%d| def.SR[%d] max.CH[%d] latency{hi[%f] lo[%f]}\n", i, (UINT32)deviceInfo->defaultSampleRate,\r
+                                       deviceInfo->maxInputChannels, (float)deviceInfo->defaultHighInputLatency, (float)deviceInfo->defaultLowInputLatency));\r
+                               break; }\r
             default:\r
-                PRINT(("WASAPI: device %d bad Data FLow! \n",i));\r
-                goto error;\r
+                PRINT(("WASAPI:%d| bad Data Flow!\n", i));\r
+                //continue; // do not skip from list, allow to initialize\r
             break;\r
             }\r
 \r
-                       deviceInfo->defaultSampleRate = paWasapi->devInfo[i].DefaultFormat.Format.nSamplesPerSec;\r
-\r
             (*hostApi)->deviceInfos[i] = deviceInfo;\r
             ++(*hostApi)->info.deviceCount;\r
         }\r
@@ -1827,6 +1830,7 @@ static HRESULT CreateAudioClient(PaWasapiSubStream *pSubStream, PaWasapiDeviceIn
     HRESULT hr                                 = S_OK;\r
     UINT32 nFrames                             = 0;\r
     IAudioClient *pAudioClient = NULL;\r
+       double suggestedLatency     = 0.0;\r
 \r
     if (!pSubStream || !info || !params)\r
         return E_POINTER;\r
@@ -1846,23 +1850,34 @@ static HRESULT CreateAudioClient(PaWasapiSubStream *pSubStream, PaWasapiDeviceIn
        {\r
                if (pa_error)\r
                        (*pa_error) = error;\r
-               return AUDCLNT_E_UNSUPPORTED_FORMAT;\r
+\r
+               LogHostError(hr = AUDCLNT_E_UNSUPPORTED_FORMAT);\r
+               goto done; // fail, format not supported\r
        }\r
 \r
        // Check for Mono >> Stereo workaround\r
        if ((params->channelCount == 1) && (pSubStream->wavex.Format.nChannels == 2))\r
        {\r
                if (blocking)\r
-                       return AUDCLNT_E_UNSUPPORTED_FORMAT; // fail, blocking mode not supported\r
+               {\r
+                       LogHostError(hr = AUDCLNT_E_UNSUPPORTED_FORMAT);\r
+                       goto done; // fail, blocking mode not supported\r
+               }\r
 \r
                // select mixer\r
                pSubStream->monoMixer = _GetMonoToStereoMixer(params->sampleFormat);\r
                if (pSubStream->monoMixer == NULL)\r
-                       return AUDCLNT_E_UNSUPPORTED_FORMAT; // fail, no mixer for format\r
+               {\r
+                       LogHostError(hr = AUDCLNT_E_UNSUPPORTED_FORMAT);\r
+                       goto done; // fail, no mixer for format\r
+               }\r
        }\r
 \r
+       // Correct latency to default device period (in Exclusive mode this is 10ms) if user selected 0\r
+       suggestedLatency = (params->suggestedLatency < 0.001 ? nano100ToSeconds(info->DefaultDevicePeriod) : params->suggestedLatency);\r
+\r
        // Add latency frames\r
-       framesPerLatency += MakeFramesFromHns(SecondsTonano100(params->suggestedLatency), pSubStream->wavex.Format.nSamplesPerSec);\r
+       framesPerLatency += MakeFramesFromHns(SecondsTonano100(suggestedLatency), pSubStream->wavex.Format.nSamplesPerSec);\r
 \r
        // Align frames to HD Audio packet size of 128 bytes for Exclusive mode only.\r
        // Not aligning on Windows Vista will cause Event timeout, although Windows 7 will\r
@@ -1927,10 +1942,39 @@ static const REFERENCE_TIME MAX_BUFFER_POLL_DURATION  = 2000 * 10000;
                &pSubStream->wavex.Format,\r
         NULL);\r
 \r
+       // This would mean too low latency\r
+       if (hr == AUDCLNT_E_BUFFER_SIZE_ERROR)\r
+       {\r
+               PRINT(("WASAPI: CreateAudioClient: correcting buffer size to device minimum\n"));\r
+\r
+               // User was trying to set lowest possible, so set lowest device possible\r
+               pSubStream->period = info->MinimumDevicePeriod;\r
+\r
+        // Release the previous allocations\r
+        SAFE_RELEASE(pAudioClient);\r
+\r
+        // Create a new audio client\r
+        hr = IMMDevice_Activate(info->device, &pa_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient);\r
+       if (hr != S_OK)\r
+               {\r
+                       LogHostError(hr);\r
+                       goto done;\r
+               }\r
+\r
+               // Open the stream and associate it with an audio session\r
+               hr = IAudioClient_Initialize(pAudioClient,\r
+                       pSubStream->shareMode,\r
+                       streamFlags/*AUDCLNT_STREAMFLAGS_EVENTCALLBACK*/,\r
+                       pSubStream->period,\r
+                       (pSubStream->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? pSubStream->period : 0),\r
+                       &pSubStream->wavex.Format,\r
+                       NULL);\r
+       }\r
+\r
     // If the requested buffer size is not aligned...\r
     if (hr == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED)\r
     {\r
-               PRINT(("WASAPI: CreateAudioClient: aligning buffer size"));\r
+               PRINT(("WASAPI: CreateAudioClient: aligning buffer size\n"));\r
 \r
         // Get the next aligned frame\r
         hr = IAudioClient_GetBufferSize(pAudioClient, &nFrames);\r
@@ -1952,19 +1996,31 @@ static const REFERENCE_TIME MAX_BUFFER_POLL_DURATION  = 2000 * 10000;
                }\r
 \r
                // Get closest format\r
-               if (GetClosestFormat(pAudioClient, sampleRate, params, pSubStream->shareMode, &pSubStream->wavex) != paFormatIsSupported)\r
-                       return AUDCLNT_E_UNSUPPORTED_FORMAT;\r
+               if ((error = GetClosestFormat(pAudioClient, sampleRate, params, pSubStream->shareMode, &pSubStream->wavex)) != paFormatIsSupported)\r
+               {\r
+                       if (pa_error)\r
+                               (*pa_error) = error;\r
+\r
+                       LogHostError(hr = AUDCLNT_E_UNSUPPORTED_FORMAT); // fail, format not supported\r
+                       goto done;\r
+               }\r
 \r
                // Check for Mono >> Stereo workaround\r
                if ((params->channelCount == 1) && (pSubStream->wavex.Format.nChannels == 2))\r
                {\r
                        if (blocking)\r
-                               return AUDCLNT_E_UNSUPPORTED_FORMAT; // fail, blocking mode not supported\r
+                       {\r
+                               LogHostError(hr = AUDCLNT_E_UNSUPPORTED_FORMAT);\r
+                               goto done; // fail, blocking mode not supported\r
+                       }\r
 \r
                        // select mixer\r
                        pSubStream->monoMixer = _GetMonoToStereoMixer(params->sampleFormat);\r
                        if (pSubStream->monoMixer == NULL)\r
-                               return AUDCLNT_E_UNSUPPORTED_FORMAT; // fail, no mixer for format\r
+                       {\r
+                               LogHostError(hr = AUDCLNT_E_UNSUPPORTED_FORMAT);\r
+                               goto done; // fail, no mixer for format\r
+                       }\r
                }\r
 \r
                // Calculate period\r
@@ -2404,10 +2460,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
 error:\r
 \r
     if (stream != NULL)\r
-       {\r
                CloseStream(stream);\r
-        PaUtil_FreeMemory(stream);\r
-       }\r
 \r
     return result;\r
 }\r