diff --git a/lib-src/portaudio-v19/include/pa_win_wasapi.h b/lib-src/portaudio-v19/include/pa_win_wasapi.h index 1fbb51c75..0c55f838c 100644 --- a/lib-src/portaudio-v19/include/pa_win_wasapi.h +++ b/lib-src/portaudio-v19/include/pa_win_wasapi.h @@ -272,6 +272,15 @@ int/*PaWasapiDeviceRole*/ PaWasapi_GetDeviceRole( PaDeviceIndex nDevice ); */ int PaWasapi_IsLoopback( PaDeviceIndex nDevice ); +/** Returns Windows device ID. + + @param nDevice device index. + + @return 0 = Not loopback, 1 = loopback, < 0 = PaErrorCode + if PortAudio is not initialized or an error is encountered. +*/ +const wchar_t *PaWasapi_GetInputDeviceID( PaStream* s ); +const wchar_t *PaWasapi_GetOutputDeviceID( PaStream* s ); /** Boost thread priority of calling thread (MMCSS). Use it for Blocking Interface only for thread which makes calls to Pa_WriteStream/Pa_ReadStream. diff --git a/lib-src/portaudio-v19/src/hostapi/wasapi/pa_win_wasapi.c b/lib-src/portaudio-v19/src/hostapi/wasapi/pa_win_wasapi.c index 0d52c073b..8057ac04b 100644 --- a/lib-src/portaudio-v19/src/hostapi/wasapi/pa_win_wasapi.c +++ b/lib-src/portaudio-v19/src/hostapi/wasapi/pa_win_wasapi.c @@ -1607,6 +1607,32 @@ int PaWasapi_IsLoopback( PaDeviceIndex nDevice ) return paWasapi->devInfo[ index ].loopBack; } +// ------------------------------------------------------------------------------------------ +const wchar_t *PaWasapi_GetInputDeviceID( PaStream* s ) +{ + PaWasapiStream *stream = (PaWasapiStream *)s; + if (stream == NULL) + return NULL; + + if (stream->in.params.device_info) + return stream->in.params.device_info->szDeviceID; + + return NULL; +} + +// ------------------------------------------------------------------------------------------ +const wchar_t *PaWasapi_GetOutputDeviceID( PaStream* s ) +{ + PaWasapiStream *stream = (PaWasapiStream *)s; + if (stream == NULL) + return NULL; + + if (stream->out.params.device_info) + return stream->out.params.device_info->szDeviceID; + + return NULL; +} + // ------------------------------------------------------------------------------------------ PaError PaWasapi_GetFramesPerHostBuffer( PaStream *pStream, unsigned int *nInput, unsigned int *nOutput ) { diff --git a/lib-src/portmixer/portaudio.patch b/lib-src/portmixer/portaudio.patch index 49f130640..a776cc1fe 100644 --- a/lib-src/portmixer/portaudio.patch +++ b/lib-src/portmixer/portaudio.patch @@ -341,3 +341,58 @@ diff -wruN portaudio/src/hostapi/oss/pa_unix_oss.c portaudio-v19/src/hostapi/oss + + return NULL; +} +diff -wruN orig/portaudio-v19/include/pa_win_wasapi.h portaudio-v19/include/pa_win_wasapi.h +--- orig/portaudio-v19/include/pa_win_wasapi.h 2013-09-23 23:44:18.192843200 -0500 ++++ portaudio-v19/include/pa_win_wasapi.h 2013-09-23 23:47:03.705310000 -0500 +@@ -272,6 +272,15 @@ + */ + int PaWasapi_IsLoopback( PaDeviceIndex nDevice ); + ++/** Returns Windows device ID. ++ ++ @param nDevice device index. ++ ++ @return 0 = Not loopback, 1 = loopback, < 0 = PaErrorCode ++ if PortAudio is not initialized or an error is encountered. ++*/ ++const wchar_t *PaWasapi_GetInputDeviceID( PaStream* s ); ++const wchar_t *PaWasapi_GetOutputDeviceID( PaStream* s ); + + /** Boost thread priority of calling thread (MMCSS). Use it for Blocking Interface only for thread + which makes calls to Pa_WriteStream/Pa_ReadStream. +diff -wruN orig/portaudio-v19/src/hostapi/wasapi/pa_win_wasapi.c portaudio-v19/src/hostapi/wasapi/pa_win_wasapi.c +--- orig/portaudio-v19/src/hostapi/wasapi/pa_win_wasapi.c 2013-09-23 23:44:15.266675900 -0500 ++++ portaudio-v19/src/hostapi/wasapi/pa_win_wasapi.c 2013-09-23 23:47:03.709310200 -0500 +@@ -1608,6 +1608,32 @@ + } + + // ------------------------------------------------------------------------------------------ ++const wchar_t *PaWasapi_GetInputDeviceID( PaStream* s ) ++{ ++ PaWasapiStream *stream = (PaWasapiStream *)s; ++ if (stream == NULL) ++ return NULL; ++ ++ if (stream->in.params.device_info) ++ return stream->in.params.device_info->szDeviceID; ++ ++ return NULL; ++} ++ ++// ------------------------------------------------------------------------------------------ ++const wchar_t *PaWasapi_GetOutputDeviceID( PaStream* s ) ++{ ++ PaWasapiStream *stream = (PaWasapiStream *)s; ++ if (stream == NULL) ++ return NULL; ++ ++ if (stream->out.params.device_info) ++ return stream->out.params.device_info->szDeviceID; ++ ++ return NULL; ++} ++ ++// ------------------------------------------------------------------------------------------ + PaError PaWasapi_GetFramesPerHostBuffer( PaStream *pStream, unsigned int *nInput, unsigned int *nOutput ) + { + PaWasapiStream *stream = (PaWasapiStream *)pStream; diff --git a/lib-src/portmixer/src/px_mixer.c b/lib-src/portmixer/src/px_mixer.c index fef8411d6..bb10e6de4 100644 --- a/lib-src/portmixer/src/px_mixer.c +++ b/lib-src/portmixer/src/px_mixer.c @@ -56,6 +56,10 @@ int OpenMixer_Win_MME(px_mixer *Px, int index); int OpenMixer_Win_DirectSound(px_mixer *Px, int index); #endif +#if defined(PX_USE_WIN_WASAPI) +int OpenMixer_Win_WASAPI(px_mixer *Px, int index); +#endif + #if defined(PX_USE_MAC_COREAUDIO) int OpenMixer_Mac_CoreAudio(px_mixer *Px, int index); #endif @@ -138,6 +142,12 @@ PxMixer *Px_OpenMixer(PaStream *pa_stream, int i) break; #endif +#if defined(PX_USE_WIN_WASAPI) + case paWASAPI: + good = OpenMixer_Win_WASAPI(Px, i); + break; +#endif + #if defined(PX_USE_MAC_COREAUDIO) case paCoreAudio: good = OpenMixer_Mac_CoreAudio(Px, i); @@ -155,6 +165,10 @@ PxMixer *Px_OpenMixer(PaStream *pa_stream, int i) good = OpenMixer_Linux_ALSA(Px, i); break; #endif + + default: + good = FALSE; + break; } if (!good) { diff --git a/lib-src/portmixer/src/px_win_endpoint.c b/lib-src/portmixer/src/px_win_endpoint.c index 3c4c2cf6d..f4eb56cd8 100644 --- a/lib-src/portmixer/src/px_win_endpoint.c +++ b/lib-src/portmixer/src/px_win_endpoint.c @@ -240,6 +240,80 @@ fail: return cleanup(Px); } +int open_ep_mixers_byid(px_mixer *Px, wchar_t *deviceIn, wchar_t *deviceOut) +{ + PxEPInfo *info; + IMMDeviceEnumerator *denum = NULL; + IMMDevice *device = NULL; + HRESULT hr; + MMRESULT res; + LPWSTR idStr; + size_t idLen; + + if (!initialize(Px)) { + goto fail; + } + + info = (PxEPInfo *) Px->info; + info->inputEP = NULL; + info->outputEP = NULL; + + // Create an audio endpoint device enumerator. + hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, + NULL, + CLSCTX_ALL, + &IID_IMMDeviceEnumerator, + &denum); + if (FAILED(hr)) { + goto fail; + } + + if (deviceIn) { + hr = IMMDeviceEnumerator_GetDevice(denum, deviceIn, &device); + if (SUCCEEDED(hr)) { + hr = IMMDevice_Activate(device, + &IID_IAudioEndpointVolume, + CLSCTX_ALL, + NULL, + &info->inputEP); + IUnknown_Release(device); + } + + if (FAILED(hr)) { + goto fail; + } + } + + if (deviceOut) { + hr = IMMDeviceEnumerator_GetDevice(denum, deviceOut, &device); + if (SUCCEEDED(hr)) { + hr = IMMDevice_Activate(device, + &IID_IAudioEndpointVolume, + CLSCTX_ALL, + NULL, + &info->outputEP); + IUnknown_Release(device); + } + + if (FAILED(hr)) { + goto fail; + } + } + + if (denum) { + IUnknown_Release(denum); + } + + return TRUE; + +fail: + if (denum) { + IUnknown_Release(denum); + } + + return cleanup(Px); +} + static int initialize(px_mixer *Px) { @@ -317,53 +391,15 @@ static const char *get_mixer_name(px_mixer *Px, int i) return NULL; } -static PxVolume VolumeFunction(HMIXEROBJ hMixer, DWORD controlID, PxVolume volume) -{ - MIXERCONTROLDETAILS details; - MMRESULT result; - MIXERCONTROLDETAILS_UNSIGNED value; - - if (hMixer == NULL) { - return -1.0; - } - - memset(&value, 0, sizeof(MIXERCONTROLDETAILS_UNSIGNED)); - - details.cbStruct = sizeof(MIXERCONTROLDETAILS); - details.dwControlID = controlID; - details.cChannels = 1; /* all channels */ - details.cMultipleItems = 0; - details.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED); - details.paDetails = &value; - - if (volume < 0.0) { - result = mixerGetControlDetails(hMixer, - &details, - MIXER_GETCONTROLDETAILSF_VALUE); - if (result != MMSYSERR_NOERROR) - return -1.0; - - return (PxVolume)(value.dwValue / 65535.0); - } - - value.dwValue = (unsigned short)(volume * 65535.0); - result = mixerSetControlDetails(hMixer, - &details, - MIXER_GETCONTROLDETAILSF_VALUE); - - if (result != MMSYSERR_NOERROR) - return -1.0; - - return 0.0; -} - static PxVolume get_master_volume(px_mixer *Px) { PxEPInfo *info = (PxEPInfo *)Px->info; float volume = 0.0; - IAudioEndpointVolume_GetMasterVolumeLevelScalar(info->outputEP, - &volume); + if (info->outputEP) { + IAudioEndpointVolume_GetMasterVolumeLevelScalar(info->outputEP, + &volume); + } return volume; } @@ -372,9 +408,11 @@ static void set_master_volume(px_mixer *Px, PxVolume volume) { PxEPInfo *info = (PxEPInfo *)Px->info; - IAudioEndpointVolume_SetMasterVolumeLevelScalar(info->outputEP, - volume, - NULL); + if (info->outputEP) { + IAudioEndpointVolume_SetMasterVolumeLevelScalar(info->outputEP, + volume, + NULL); + } return; } @@ -410,14 +448,14 @@ static int get_num_output_volumes(px_mixer *Px) { PxEPInfo *info = (PxEPInfo *)Px->info; - return 1; + return (info->outputEP ? 1 : 0); } static const char *get_output_volume_name(px_mixer *Px, int i) { PxEPInfo *info = (PxEPInfo *)Px->info; - return "PCM"; + return (info->outputEP ? "PCM" : NULL); } /* @@ -473,8 +511,10 @@ static PxVolume get_input_volume(px_mixer *Px) PxEPInfo *info = (PxEPInfo *)Px->info; float volume = 0.0; - IAudioEndpointVolume_GetMasterVolumeLevelScalar(info->inputEP, - &volume); + if (info->inputEP) { + IAudioEndpointVolume_GetMasterVolumeLevelScalar(info->inputEP, + &volume); + } return volume; } @@ -483,9 +523,11 @@ static void set_input_volume(px_mixer *Px, PxVolume volume) { PxEPInfo *info = (PxEPInfo *)Px->info; - IAudioEndpointVolume_SetMasterVolumeLevelScalar(info->inputEP, - volume, - NULL); + if (info->inputEP) { + IAudioEndpointVolume_SetMasterVolumeLevelScalar(info->inputEP, + volume, + NULL); + } return; } diff --git a/lib-src/portmixer/src/px_win_wasapi.c b/lib-src/portmixer/src/px_win_wasapi.c new file mode 100644 index 000000000..d5f5863a9 --- /dev/null +++ b/lib-src/portmixer/src/px_win_wasapi.c @@ -0,0 +1,80 @@ +/* + * PortMixer + * Windows DirectSound Implementation + * + * Copyright (c) 2002, 2006 + * + * Written by Dominic Mazzoni and Augustus Saunders + * and Leland Lucius + * + * PortMixer is intended to work side-by-side with PortAudio, + * the Portable Real-Time Audio Library by Ross Bencina and + * Phil Burk. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#define COBJMACROS + +#include + +#include "portaudio.h" +#include "pa_win_wasapi.h" + +#include "portmixer.h" +#include "px_mixer.h" +#include "px_win_common.h" + +#if defined(_DEBUG) +#include +static void dprintf(const char *format, ...) +{ + char buf[4096]; + va_list args; + int cnt; + + va_start(args, format); + cnt = _vsnprintf(buf, sizeof(buf) - 1, format, args); + va_end(args); + + if (cnt > 0) { + buf[cnt] = '\0'; + OutputDebugString(buf); + } +} +#endif + +int OpenMixer_Win_WASAPI(px_mixer *Px, int index) +{ + wchar_t *deviceIn = PaWasapi_GetInputDeviceID(Px->pa_stream); + wchar_t *deviceOut = PaWasapi_GetOutputDeviceID(Px->pa_stream); + int ret = FALSE; + + if (open_ep_mixers_byid(Px, deviceIn, deviceOut)) + { + ret = TRUE; + } + + return ret; +}