#define SAFE_CLOSE(h) if ((h) != NULL) { CloseHandle((h)); (h) = NULL; }\r
#define SAFE_RELEASE(punk) if ((punk) != NULL) { (punk)->lpVtbl->Release((punk)); (punk) = NULL; }\r
\r
+// Mixer function\r
+typedef void (*MixMonoToStereoF) (void *__to, void *__from, UINT32 count);\r
+\r
// AVRT is the new "multimedia schedulling stuff"\r
typedef BOOL (WINAPI *FAvRtCreateThreadOrderingGroup) (PHANDLE,PLARGE_INTEGER,GUID*,PLARGE_INTEGER);\r
typedef BOOL (WINAPI *FAvRtDeleteThreadOrderingGroup) (HANDLE);\r
// Used by blocking interface:\r
UINT32 prevTime; // time ms between calls of WriteStream\r
UINT32 prevSleep; // time ms to sleep from frames written in previous call\r
+\r
+ // Used for Mono >> Stereo workaround, if driver does not support it\r
+ // (in Exclusive mode WASAPI usually refuses to operate with Mono (1-ch)\r
+ void *monoBuffer; //!< pointer to buffer\r
+ UINT32 monoBufferSize; //!< buffer size in bytes\r
+ MixMonoToStereoF monoMixer; //!< pointer to mixer function\r
}\r
PaWasapiSubStream;\r
\r
return (IsWow64() && (GetWindowsVersion() == WINDOWS_VISTA_SERVER2008));\r
}\r
\r
+// ------------------------------------------------------------------------------------------\r
+#define _WASAPI_MONO_TO_STEREO_MIXER(TYPE)\\r
+ TYPE * __restrict to = __to;\\r
+ TYPE * __restrict from = __from;\\r
+ TYPE * __restrict end = from + count;\\r
+ while (from != end)\\r
+ {\\r
+ *to ++ = *from;\\r
+ *to ++ = *from;\\r
+ ++ from;\\r
+ }\r
+\r
+// ------------------------------------------------------------------------------------------\r
+static void _MixMonoToStereo_8(void *__to, void *__from, UINT32 count)\r
+{\r
+ _WASAPI_MONO_TO_STEREO_MIXER(BYTE);\r
+}\r
+\r
+// ------------------------------------------------------------------------------------------\r
+static void _MixMonoToStereo_16(void *__to, void *__from, UINT32 count)\r
+{\r
+ _WASAPI_MONO_TO_STEREO_MIXER(short);\r
+}\r
+\r
+// ------------------------------------------------------------------------------------------\r
+static void _MixMonoToStereo_24(void *__to, void *__from, UINT32 count)\r
+{\r
+#pragma pack(push, 1)\r
+ typedef struct wasapi_int24 { BYTE d[3]; } wasapi_int24; //<< 24 bit value, packed to 1 byte to avoid padding\r
+#pragma pack(pop)\r
+ _WASAPI_MONO_TO_STEREO_MIXER(wasapi_int24);\r
+}\r
+\r
+// ------------------------------------------------------------------------------------------\r
+static void _MixMonoToStereo_32(void *__to, void *__from, UINT32 count)\r
+{\r
+ _WASAPI_MONO_TO_STEREO_MIXER(int);\r
+}\r
+\r
+// ------------------------------------------------------------------------------------------\r
+static void _MixMonoToStereo_32f(void *__to, void *__from, UINT32 count)\r
+{\r
+ _WASAPI_MONO_TO_STEREO_MIXER(float);\r
+}\r
+\r
+// ------------------------------------------------------------------------------------------\r
+static MixMonoToStereoF _GetMonoToStereoMixer(PaSampleFormat format)\r
+{\r
+ switch (format)\r
+ {\r
+ case paUInt8: return _MixMonoToStereo_8;\r
+ case paInt16: return _MixMonoToStereo_16;\r
+ case paInt24: return _MixMonoToStereo_24;\r
+ case paInt32: return _MixMonoToStereo_32;\r
+ case paFloat32: return _MixMonoToStereo_32f;\r
+ }\r
+ return NULL;\r
+}\r
+\r
// ------------------------------------------------------------------------------------------\r
PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )\r
{\r
\r
SAFE_RELEASE(pEndPoints);\r
\r
- PRINT(("WASAPI: initialized ok"));\r
+ PRINT(("WASAPI: initialized ok\n"));\r
\r
return paNoError;\r
\r
error:\r
\r
- PRINT(("WASAPI: failed %s error[%d|%s]", __FUNCTION__, result, Pa_GetErrorText(result)));\r
+ PRINT(("WASAPI: failed %s error[%d|%s]\n", __FUNCTION__, result, Pa_GetErrorText(result)));\r
\r
SAFE_RELEASE(pEndPoints);\r
\r
}\r
else\r
{\r
- //it doesnt suggest anything?? ok lets show it the MENU!\r
- int i;\r
-\r
#define FORMATTESTS 3\r
-static const int BestToWorst[FORMATTESTS]={ paFloat32, paInt24, paInt16 };\r
+static const int BestToWorst[FORMATTESTS] = { paFloat32, paInt24, paInt16 };\r
\r
- //ok fun time as with pa_win_mme, we know only a refusal of the user-requested\r
- //sampleRate+num Channel is disastrous, as the portaudio buffer processor converts between anything\r
- //so lets only use the number\r
+ // try selecting suitable sample type\r
+ int i;\r
for (i = 0; i < FORMATTESTS; ++i)\r
{\r
WAVEFORMATEXTENSIBLE ext = { 0 };\r
- wasapiFillWFEXT(&ext,BestToWorst[i],sampleRate,params->channelCount);\r
+ wasapiFillWFEXT(&ext, BestToWorst[i], sampleRate, params->channelCount);\r
\r
hr = IAudioClient_IsFormatSupported(myClient, shareMode, &ext.Format, (shareMode == AUDCLNT_SHAREMODE_SHARED ? &sharedClosestMatch : NULL));\r
if (hr == S_OK)\r
{\r
- memcpy(outWavex,&ext,sizeof(WAVEFORMATEXTENSIBLE));\r
+ memcpy(outWavex, &ext, sizeof(WAVEFORMATEXTENSIBLE));\r
answer = paFormatIsSupported;\r
break;\r
}\r
\r
if (answer != paFormatIsSupported)\r
{\r
- // try MIX format?\r
- // why did it HAVE to come to this ....\r
- WAVEFORMATEX pcm16WaveFormat = { 0 };\r
- pcm16WaveFormat.wFormatTag = WAVE_FORMAT_PCM;\r
- pcm16WaveFormat.nChannels = 2;\r
- pcm16WaveFormat.nSamplesPerSec = (DWORD)sampleRate;\r
- pcm16WaveFormat.nBlockAlign = 4;\r
- pcm16WaveFormat.nAvgBytesPerSec = pcm16WaveFormat.nSamplesPerSec*pcm16WaveFormat.nBlockAlign;\r
- pcm16WaveFormat.wBitsPerSample = 16;\r
- pcm16WaveFormat.cbSize = 0;\r
-\r
- hr = IAudioClient_IsFormatSupported(myClient, shareMode, &pcm16WaveFormat, (shareMode == AUDCLNT_SHAREMODE_SHARED ? &sharedClosestMatch : NULL));\r
- if (hr == S_OK)\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 for 1 channel\r
+ // but then mix into 2 of device buffer\r
+ if (params->channelCount == 1)\r
{\r
- memcpy(outWavex,&pcm16WaveFormat,sizeof(WAVEFORMATEX));\r
- answer = paFormatIsSupported;\r
+ WAVEFORMATEXTENSIBLE stereo = { 0 };\r
+ wasapiFillWFEXT(&stereo, params->sampleFormat, sampleRate, 2);\r
+\r
+ hr = IAudioClient_IsFormatSupported(myClient, shareMode, &stereo.Format, (shareMode == AUDCLNT_SHAREMODE_SHARED ? &sharedClosestMatch : NULL));\r
+ if (hr == S_OK)\r
+ {\r
+ memcpy(outWavex, &stereo, sizeof(WAVEFORMATEXTENSIBLE));\r
+ answer = paFormatIsSupported;\r
+ }\r
}\r
}\r
\r
// ------------------------------------------------------------------------------------------\r
static HRESULT CreateAudioClient(PaWasapiSubStream *pSubStream, PaWasapiDeviceInfo *info,\r
const PaStreamParameters *params, UINT32 framesPerLatency, double sampleRate, UINT32 streamFlags,\r
- PaError *pa_error)\r
+ BOOL blocking, PaError *pa_error)\r
{\r
PaError error;\r
HRESULT hr = S_OK;\r
return AUDCLNT_E_UNSUPPORTED_FORMAT;\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
+ // 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
+\r
// Add latency frames\r
framesPerLatency += MakeFramesFromHns(SecondsTonano100(params->suggestedLatency), pSubStream->wavex.Format.nSamplesPerSec);\r
\r
if (GetClosestFormat(pAudioClient, sampleRate, params, pSubStream->shareMode, &pSubStream->wavex) != paFormatIsSupported)\r
return AUDCLNT_E_UNSUPPORTED_FORMAT;\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
+ // 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
+\r
// Calculate period\r
pSubStream->period = MakeHnsPeriod(nFrames, pSubStream->wavex.Format.nSamplesPerSec);\r
\r
\r
// Create Audio client\r
hr = CreateAudioClient(&stream->in, info, inputParameters, 0/*framesPerLatency*/,\r
- sampleRate, stream->in.streamFlags, &result);\r
+ sampleRate, stream->in.streamFlags, (streamCallback == NULL), &result);\r
if (hr != S_OK)\r
{\r
LogHostError(hr);\r
// Append buffer latency to interface latency in shared mode (see GetStreamLatency notes)\r
stream->in.latency_seconds += buffer_latency;\r
\r
- PRINT(("PaWASAPIOpenStream(input): framesPerHostCallback[ %d ] latency[ %.02fms ]\n", (UINT32)stream->in.framesPerHostCallback, (float)(stream->in.latency_seconds*1000.0f)));\r
+ PRINT(("PaWASAPIOpenStream(input): framesPerHostCallback[ %d ] latency[ %.02fms ] exclusive[ %s ] wow64_fix[ %s ]\n", (UINT32)stream->in.framesPerHostCallback, (float)(stream->in.latency_seconds*1000.0f), (stream->in.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? "YES" : "NO"), (paWasapi->useVistaWOW64Workaround ? "YES" : "NO")));\r
}\r
else\r
{\r
\r
// Create Audio client\r
hr = CreateAudioClient(&stream->out, info, outputParameters, 0/*framesPerLatency*/,\r
- sampleRate, stream->out.streamFlags, &result);\r
+ sampleRate, stream->out.streamFlags, (streamCallback == NULL), &result);\r
if (hr != S_OK)\r
{\r
LogHostError(hr);\r
// Append buffer latency to interface latency in shared mode (see GetStreamLatency notes)\r
stream->out.latency_seconds += buffer_latency;\r
\r
- PRINT(("PaWASAPIOpenStream(output): framesPerHostCallback[ %d ] latency[ %.02fms ]\n", (UINT32)stream->out.framesPerHostCallback, (float)(stream->out.latency_seconds*1000.0f)));\r
+ PRINT(("PaWASAPIOpenStream(output): framesPerHostCallback[ %d ] latency[ %.02fms ] exclusive[ %s ] wow64_fix[ %s ]\n", (UINT32)stream->out.framesPerHostCallback, (float)(stream->out.latency_seconds*1000.0f), (stream->out.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? "YES" : "NO"), (paWasapi->useVistaWOW64Workaround ? "YES" : "NO")));\r
}\r
else\r
{\r
SAFE_CLOSE(stream->hBlockingOpStreamRD);\r
SAFE_CLOSE(stream->hBlockingOpStreamWR);\r
\r
+ free(stream->in.monoBuffer);\r
+ free(stream->out.monoBuffer);\r
+\r
PaUtil_TerminateBufferProcessor(&stream->bufferProcessor);\r
PaUtil_TerminateStreamRepresentation(&stream->streamRepresentation);\r
PaUtil_FreeMemory(stream);\r
}\r
\r
// Process data\r
- processor[S_OUTPUT].processor(NULL, 0, data, frames, processor[S_OUTPUT].userData);\r
+ if (stream->out.monoMixer != NULL)\r
+ {\r
+#define __DIV_8(v) ((v) >> 3) //!< (v / 8)\r
+\r
+ // expand buffer (one way only for better performancedue to no calls to realloc)\r
+ UINT32 mono_frames_size = frames * __DIV_8(stream->out.wavex.Format.wBitsPerSample);\r
+ if (mono_frames_size > stream->out.monoBufferSize)\r
+ {\r
+ stream->out.monoBuffer = realloc(stream->out.monoBuffer, mono_frames_size);\r
+ }\r
+\r
+ // feed to user\r
+ processor[S_OUTPUT].processor(NULL, 0, (BYTE *)stream->out.monoBuffer, frames, processor[S_OUTPUT].userData);\r
+\r
+ // mix 1 to 2 channels\r
+ stream->out.monoMixer(data, stream->out.monoBuffer, frames);\r
+\r
+#undef __DIV_8\r
+ }\r
+ else\r
+ {\r
+ processor[S_OUTPUT].processor(NULL, 0, data, frames, processor[S_OUTPUT].userData);\r
+ }\r
\r
// Release buffer\r
if ((hr = IAudioRenderClient_ReleaseBuffer(stream->rclient, frames, 0)) != S_OK)\r
// if (flags & AUDCLNT_BUFFERFLAGS_SILENT)\r
// data = NULL;\r
\r
- // Process data\r
- processor[S_INPUT].processor(data, frames, NULL, 0, processor[S_INPUT].userData);\r
+ // Process data\r
+ if (stream->in.monoMixer != NULL)\r
+ {\r
+#define __DIV_8(v) ((v) >> 3) //!< (v / 8)\r
+\r
+ // expand buffer (one way only for better performancedue to no calls to realloc)\r
+ UINT32 mono_frames_size = frames * __DIV_8(stream->in.wavex.Format.wBitsPerSample);\r
+ if (mono_frames_size > stream->in.monoBufferSize)\r
+ {\r
+ stream->in.monoBuffer = realloc(stream->in.monoBuffer, mono_frames_size);\r
+ }\r
+\r
+ // feed to user\r
+ processor[S_INPUT].processor((BYTE *)stream->in.monoBuffer, frames, NULL, 0, processor[S_INPUT].userData);\r
+\r
+ // mix 1 to 2 channels\r
+ stream->in.monoMixer(data, stream->in.monoBuffer, frames);\r
+\r
+#undef __DIV_8\r
+ }\r
+ else\r
+ {\r
+ processor[S_INPUT].processor(data, frames, NULL, 0, processor[S_INPUT].userData);\r
+ }\r
\r
// Release buffer\r
if ((hr = IAudioCaptureClient_ReleaseBuffer(stream->cclient, frames)) != S_OK)\r