|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731 |
- /*
- ==============================================================================
-
- This file is part of the JUCE library.
- Copyright (c) 2017 - ROLI Ltd.
-
- JUCE is an open source library subject to commercial or open-source
- licensing.
-
- The code included in this file is provided under the terms of the ISC license
- http://www.isc.org/downloads/software-support-policy/isc-license. Permission
- To use, copy, modify, and/or distribute this software for any purpose with or
- without fee is hereby granted provided that the above copyright notice and
- this permission notice appear in all copies.
-
- JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
- EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
- DISCLAIMED.
-
- ==============================================================================
- */
-
- namespace juce
- {
-
- #ifndef JUCE_WASAPI_LOGGING
- #define JUCE_WASAPI_LOGGING 0
- #endif
-
- //==============================================================================
- namespace WasapiClasses
- {
-
- void logFailure (HRESULT hr)
- {
- ignoreUnused (hr);
- jassert (hr != (HRESULT) 0x800401f0); // If you hit this, it means you're trying to call from
- // a thread which hasn't been initialised with CoInitialize().
-
- #if JUCE_WASAPI_LOGGING
- if (FAILED (hr))
- {
- const char* m = nullptr;
-
- switch (hr)
- {
- case E_POINTER: m = "E_POINTER"; break;
- case E_INVALIDARG: m = "E_INVALIDARG"; break;
- case E_NOINTERFACE: m = "E_NOINTERFACE"; break;
-
- #define JUCE_WASAPI_ERR(desc, n) \
- case MAKE_HRESULT(1, 0x889, n): m = #desc; break;
-
- JUCE_WASAPI_ERR (AUDCLNT_E_NOT_INITIALIZED, 0x001)
- JUCE_WASAPI_ERR (AUDCLNT_E_ALREADY_INITIALIZED, 0x002)
- JUCE_WASAPI_ERR (AUDCLNT_E_WRONG_ENDPOINT_TYPE, 0x003)
- JUCE_WASAPI_ERR (AUDCLNT_E_DEVICE_INVALIDATED, 0x004)
- JUCE_WASAPI_ERR (AUDCLNT_E_NOT_STOPPED, 0x005)
- JUCE_WASAPI_ERR (AUDCLNT_E_BUFFER_TOO_LARGE, 0x006)
- JUCE_WASAPI_ERR (AUDCLNT_E_OUT_OF_ORDER, 0x007)
- JUCE_WASAPI_ERR (AUDCLNT_E_UNSUPPORTED_FORMAT, 0x008)
- JUCE_WASAPI_ERR (AUDCLNT_E_INVALID_SIZE, 0x009)
- JUCE_WASAPI_ERR (AUDCLNT_E_DEVICE_IN_USE, 0x00a)
- JUCE_WASAPI_ERR (AUDCLNT_E_BUFFER_OPERATION_PENDING, 0x00b)
- JUCE_WASAPI_ERR (AUDCLNT_E_THREAD_NOT_REGISTERED, 0x00c)
- JUCE_WASAPI_ERR (AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED, 0x00e)
- JUCE_WASAPI_ERR (AUDCLNT_E_ENDPOINT_CREATE_FAILED, 0x00f)
- JUCE_WASAPI_ERR (AUDCLNT_E_SERVICE_NOT_RUNNING, 0x010)
- JUCE_WASAPI_ERR (AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED, 0x011)
- JUCE_WASAPI_ERR (AUDCLNT_E_EXCLUSIVE_MODE_ONLY, 0x012)
- JUCE_WASAPI_ERR (AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL, 0x013)
- JUCE_WASAPI_ERR (AUDCLNT_E_EVENTHANDLE_NOT_SET, 0x014)
- JUCE_WASAPI_ERR (AUDCLNT_E_INCORRECT_BUFFER_SIZE, 0x015)
- JUCE_WASAPI_ERR (AUDCLNT_E_BUFFER_SIZE_ERROR, 0x016)
- JUCE_WASAPI_ERR (AUDCLNT_E_CPUUSAGE_EXCEEDED, 0x017)
- JUCE_WASAPI_ERR (AUDCLNT_E_BUFFER_ERROR, 0x018)
- JUCE_WASAPI_ERR (AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED, 0x019)
- JUCE_WASAPI_ERR (AUDCLNT_E_INVALID_DEVICE_PERIOD, 0x020)
- default: break;
- }
-
- Logger::writeToLog ("WASAPI error: " + (m != nullptr ? String (m)
- : String::toHexString ((int) hr)));
- }
- #endif
- }
-
- #undef check
-
- bool check (HRESULT hr)
- {
- logFailure (hr);
- return SUCCEEDED (hr);
- }
-
- //==============================================================================
- }
-
- #if JUCE_MINGW
- struct PROPERTYKEY
- {
- GUID fmtid;
- DWORD pid;
- };
-
- WINOLEAPI PropVariantClear (PROPVARIANT*);
- #endif
-
- #if JUCE_MINGW && defined (KSDATAFORMAT_SUBTYPE_PCM)
- #undef KSDATAFORMAT_SUBTYPE_PCM
- #undef KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
- #endif
-
- #ifndef KSDATAFORMAT_SUBTYPE_PCM
- #define KSDATAFORMAT_SUBTYPE_PCM uuidFromString ("00000001-0000-0010-8000-00aa00389b71")
- #define KSDATAFORMAT_SUBTYPE_IEEE_FLOAT uuidFromString ("00000003-0000-0010-8000-00aa00389b71")
- #endif
-
- #define JUCE_IUNKNOWNCLASS(name, guid) JUCE_COMCLASS(name, guid) : public IUnknown
- #define JUCE_COMCALL virtual HRESULT STDMETHODCALLTYPE
-
- enum EDataFlow
- {
- eRender = 0,
- eCapture = (eRender + 1),
- eAll = (eCapture + 1)
- };
-
- enum
- {
- DEVICE_STATE_ACTIVE = 1
- };
-
- enum
- {
- AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY = 1,
- AUDCLNT_BUFFERFLAGS_SILENT = 2
- };
-
- JUCE_IUNKNOWNCLASS (IPropertyStore, "886d8eeb-8cf2-4446-8d02-cdba1dbdcf99")
- {
- JUCE_COMCALL GetCount (DWORD*) = 0;
- JUCE_COMCALL GetAt (DWORD, PROPERTYKEY*) = 0;
- JUCE_COMCALL GetValue (const PROPERTYKEY&, PROPVARIANT*) = 0;
- JUCE_COMCALL SetValue (const PROPERTYKEY&, const PROPVARIANT&) = 0;
- JUCE_COMCALL Commit() = 0;
- };
-
- JUCE_IUNKNOWNCLASS (IMMDevice, "D666063F-1587-4E43-81F1-B948E807363F")
- {
- JUCE_COMCALL Activate (REFIID, DWORD, PROPVARIANT*, void**) = 0;
- JUCE_COMCALL OpenPropertyStore (DWORD, IPropertyStore**) = 0;
- JUCE_COMCALL GetId (LPWSTR*) = 0;
- JUCE_COMCALL GetState (DWORD*) = 0;
- };
-
- JUCE_IUNKNOWNCLASS (IMMEndpoint, "1BE09788-6894-4089-8586-9A2A6C265AC5")
- {
- JUCE_COMCALL GetDataFlow (EDataFlow*) = 0;
- };
-
- struct IMMDeviceCollection : public IUnknown
- {
- JUCE_COMCALL GetCount (UINT*) = 0;
- JUCE_COMCALL Item (UINT, IMMDevice**) = 0;
- };
-
- enum ERole
- {
- eConsole = 0,
- eMultimedia = (eConsole + 1),
- eCommunications = (eMultimedia + 1)
- };
-
- JUCE_IUNKNOWNCLASS (IMMNotificationClient, "7991EEC9-7E89-4D85-8390-6C703CEC60C0")
- {
- JUCE_COMCALL OnDeviceStateChanged (LPCWSTR, DWORD) = 0;
- JUCE_COMCALL OnDeviceAdded (LPCWSTR) = 0;
- JUCE_COMCALL OnDeviceRemoved (LPCWSTR) = 0;
- JUCE_COMCALL OnDefaultDeviceChanged (EDataFlow, ERole, LPCWSTR) = 0;
- JUCE_COMCALL OnPropertyValueChanged (LPCWSTR, const PROPERTYKEY) = 0;
- };
-
- JUCE_IUNKNOWNCLASS (IMMDeviceEnumerator, "A95664D2-9614-4F35-A746-DE8DB63617E6")
- {
- JUCE_COMCALL EnumAudioEndpoints (EDataFlow, DWORD, IMMDeviceCollection**) = 0;
- JUCE_COMCALL GetDefaultAudioEndpoint (EDataFlow, ERole, IMMDevice**) = 0;
- JUCE_COMCALL GetDevice (LPCWSTR, IMMDevice**) = 0;
- JUCE_COMCALL RegisterEndpointNotificationCallback (IMMNotificationClient*) = 0;
- JUCE_COMCALL UnregisterEndpointNotificationCallback (IMMNotificationClient*) = 0;
- };
-
- JUCE_COMCLASS (MMDeviceEnumerator, "BCDE0395-E52F-467C-8E3D-C4579291692E");
-
- typedef LONGLONG REFERENCE_TIME;
-
- enum AVRT_PRIORITY
- {
- AVRT_PRIORITY_LOW = -1,
- AVRT_PRIORITY_NORMAL,
- AVRT_PRIORITY_HIGH,
- AVRT_PRIORITY_CRITICAL
- };
-
- enum AUDCLNT_SHAREMODE
- {
- AUDCLNT_SHAREMODE_SHARED,
- AUDCLNT_SHAREMODE_EXCLUSIVE
- };
-
- JUCE_IUNKNOWNCLASS (IAudioClient, "1CB9AD4C-DBFA-4c32-B178-C2F568A703B2")
- {
- JUCE_COMCALL Initialize (AUDCLNT_SHAREMODE, DWORD, REFERENCE_TIME, REFERENCE_TIME, const WAVEFORMATEX*, LPCGUID) = 0;
- JUCE_COMCALL GetBufferSize (UINT32*) = 0;
- JUCE_COMCALL GetStreamLatency (REFERENCE_TIME*) = 0;
- JUCE_COMCALL GetCurrentPadding (UINT32*) = 0;
- JUCE_COMCALL IsFormatSupported (AUDCLNT_SHAREMODE, const WAVEFORMATEX*, WAVEFORMATEX**) = 0;
- JUCE_COMCALL GetMixFormat (WAVEFORMATEX**) = 0;
- JUCE_COMCALL GetDevicePeriod (REFERENCE_TIME*, REFERENCE_TIME*) = 0;
- JUCE_COMCALL Start() = 0;
- JUCE_COMCALL Stop() = 0;
- JUCE_COMCALL Reset() = 0;
- JUCE_COMCALL SetEventHandle (HANDLE) = 0;
- JUCE_COMCALL GetService (REFIID, void**) = 0;
- };
-
- JUCE_IUNKNOWNCLASS (IAudioCaptureClient, "C8ADBD64-E71E-48a0-A4DE-185C395CD317")
- {
- JUCE_COMCALL GetBuffer (BYTE**, UINT32*, DWORD*, UINT64*, UINT64*) = 0;
- JUCE_COMCALL ReleaseBuffer (UINT32) = 0;
- JUCE_COMCALL GetNextPacketSize (UINT32*) = 0;
- };
-
- JUCE_IUNKNOWNCLASS (IAudioRenderClient, "F294ACFC-3146-4483-A7BF-ADDCA7C260E2")
- {
- JUCE_COMCALL GetBuffer (UINT32, BYTE**) = 0;
- JUCE_COMCALL ReleaseBuffer (UINT32, DWORD) = 0;
- };
-
- JUCE_IUNKNOWNCLASS (IAudioEndpointVolume, "5CDF2C82-841E-4546-9722-0CF74078229A")
- {
- JUCE_COMCALL RegisterControlChangeNotify (void*) = 0;
- JUCE_COMCALL UnregisterControlChangeNotify (void*) = 0;
- JUCE_COMCALL GetChannelCount (UINT*) = 0;
- JUCE_COMCALL SetMasterVolumeLevel (float, LPCGUID) = 0;
- JUCE_COMCALL SetMasterVolumeLevelScalar (float, LPCGUID) = 0;
- JUCE_COMCALL GetMasterVolumeLevel (float*) = 0;
- JUCE_COMCALL GetMasterVolumeLevelScalar (float*) = 0;
- JUCE_COMCALL SetChannelVolumeLevel (UINT, float, LPCGUID) = 0;
- JUCE_COMCALL SetChannelVolumeLevelScalar (UINT, float, LPCGUID) = 0;
- JUCE_COMCALL GetChannelVolumeLevel (UINT, float*) = 0;
- JUCE_COMCALL GetChannelVolumeLevelScalar (UINT, float*) = 0;
- JUCE_COMCALL SetMute (BOOL, LPCGUID) = 0;
- JUCE_COMCALL GetMute (BOOL*) = 0;
- JUCE_COMCALL GetVolumeStepInfo (UINT*, UINT*) = 0;
- JUCE_COMCALL VolumeStepUp (LPCGUID) = 0;
- JUCE_COMCALL VolumeStepDown (LPCGUID) = 0;
- JUCE_COMCALL QueryHardwareSupport (DWORD*) = 0;
- JUCE_COMCALL GetVolumeRange (float*, float*, float*) = 0;
- };
-
- enum AudioSessionDisconnectReason
- {
- DisconnectReasonDeviceRemoval = 0,
- DisconnectReasonServerShutdown = 1,
- DisconnectReasonFormatChanged = 2,
- DisconnectReasonSessionLogoff = 3,
- DisconnectReasonSessionDisconnected = 4,
- DisconnectReasonExclusiveModeOverride = 5
- };
-
- enum AudioSessionState
- {
- AudioSessionStateInactive = 0,
- AudioSessionStateActive = 1,
- AudioSessionStateExpired = 2
- };
-
- JUCE_IUNKNOWNCLASS (IAudioSessionEvents, "24918ACC-64B3-37C1-8CA9-74A66E9957A8")
- {
- JUCE_COMCALL OnDisplayNameChanged (LPCWSTR, LPCGUID) = 0;
- JUCE_COMCALL OnIconPathChanged (LPCWSTR, LPCGUID) = 0;
- JUCE_COMCALL OnSimpleVolumeChanged (float, BOOL, LPCGUID) = 0;
- JUCE_COMCALL OnChannelVolumeChanged (DWORD, float*, DWORD, LPCGUID) = 0;
- JUCE_COMCALL OnGroupingParamChanged (LPCGUID, LPCGUID) = 0;
- JUCE_COMCALL OnStateChanged (AudioSessionState) = 0;
- JUCE_COMCALL OnSessionDisconnected (AudioSessionDisconnectReason) = 0;
- };
-
- JUCE_IUNKNOWNCLASS (IAudioSessionControl, "F4B1A599-7266-4319-A8CA-E70ACB11E8CD")
- {
- JUCE_COMCALL GetState (AudioSessionState*) = 0;
- JUCE_COMCALL GetDisplayName (LPWSTR*) = 0;
- JUCE_COMCALL SetDisplayName (LPCWSTR, LPCGUID) = 0;
- JUCE_COMCALL GetIconPath (LPWSTR*) = 0;
- JUCE_COMCALL SetIconPath (LPCWSTR, LPCGUID) = 0;
- JUCE_COMCALL GetGroupingParam (GUID*) = 0;
- JUCE_COMCALL SetGroupingParam (LPCGUID, LPCGUID) = 0;
- JUCE_COMCALL RegisterAudioSessionNotification (IAudioSessionEvents*) = 0;
- JUCE_COMCALL UnregisterAudioSessionNotification (IAudioSessionEvents*) = 0;
- };
-
- #undef JUCE_COMCALL
- #undef JUCE_COMCLASS
- #undef JUCE_IUNKNOWNCLASS
-
- //==============================================================================
- namespace WasapiClasses
- {
-
- String getDeviceID (IMMDevice* const device)
- {
- String s;
- WCHAR* deviceId = nullptr;
-
- if (check (device->GetId (&deviceId)))
- {
- s = String (deviceId);
- CoTaskMemFree (deviceId);
- }
-
- return s;
- }
-
- EDataFlow getDataFlow (const ComSmartPtr<IMMDevice>& device)
- {
- EDataFlow flow = eRender;
- ComSmartPtr<IMMEndpoint> endPoint;
- if (check (device.QueryInterface (endPoint)))
- (void) check (endPoint->GetDataFlow (&flow));
-
- return flow;
- }
-
- int refTimeToSamples (const REFERENCE_TIME& t, const double sampleRate) noexcept
- {
- return roundToInt (sampleRate * ((double) t) * 0.0000001);
- }
-
- REFERENCE_TIME samplesToRefTime (const int numSamples, const double sampleRate) noexcept
- {
- return (REFERENCE_TIME) ((numSamples * 10000.0 * 1000.0 / sampleRate) + 0.5);
- }
-
- void copyWavFormat (WAVEFORMATEXTENSIBLE& dest, const WAVEFORMATEX* const src) noexcept
- {
- memcpy (&dest, src, src->wFormatTag == WAVE_FORMAT_EXTENSIBLE ? sizeof (WAVEFORMATEXTENSIBLE)
- : sizeof (WAVEFORMATEX));
- }
-
- //==============================================================================
- class WASAPIDeviceBase
- {
- public:
- WASAPIDeviceBase (const ComSmartPtr<IMMDevice>& d, const bool exclusiveMode)
- : device (d),
- sampleRate (0),
- defaultSampleRate (0),
- numChannels (0),
- actualNumChannels (0),
- minBufferSize (0),
- defaultBufferSize (0),
- latencySamples (0),
- useExclusiveMode (exclusiveMode),
- actualBufferSize (0),
- bytesPerSample (0),
- bytesPerFrame (0),
- sampleRateHasChanged (false),
- shouldClose (false)
- {
- clientEvent = CreateEvent (nullptr, false, false, nullptr);
-
- ComSmartPtr<IAudioClient> tempClient (createClient());
- if (tempClient == nullptr)
- return;
-
- REFERENCE_TIME defaultPeriod, minPeriod;
- if (! check (tempClient->GetDevicePeriod (&defaultPeriod, &minPeriod)))
- return;
-
- WAVEFORMATEX* mixFormat = nullptr;
- if (! check (tempClient->GetMixFormat (&mixFormat)))
- return;
-
- WAVEFORMATEXTENSIBLE format;
- copyWavFormat (format, mixFormat);
- CoTaskMemFree (mixFormat);
-
- actualNumChannels = numChannels = format.Format.nChannels;
- defaultSampleRate = format.Format.nSamplesPerSec;
- minBufferSize = refTimeToSamples (minPeriod, defaultSampleRate);
- defaultBufferSize = refTimeToSamples (defaultPeriod, defaultSampleRate);
- mixFormatChannelMask = format.dwChannelMask;
-
- rates.addUsingDefaultSort (defaultSampleRate);
-
- if (useExclusiveMode
- && findSupportedFormat (tempClient, defaultSampleRate, format.dwChannelMask, format))
- {
- // Got a format that is supported by the device so we can ask what sample rates are supported (in whatever format)
- }
-
- static const int ratesToTest[] = { 44100, 48000, 88200, 96000, 176400, 192000, 352800, 384000 };
-
- for (int i = 0; i < numElementsInArray (ratesToTest); ++i)
- {
- if (rates.contains (ratesToTest[i]))
- continue;
-
- format.Format.nSamplesPerSec = (DWORD) ratesToTest[i];
- format.Format.nAvgBytesPerSec = (DWORD) (format.Format.nSamplesPerSec * format.Format.nChannels * format.Format.wBitsPerSample / 8);
-
- if (SUCCEEDED (tempClient->IsFormatSupported (useExclusiveMode ? AUDCLNT_SHAREMODE_EXCLUSIVE
- : AUDCLNT_SHAREMODE_SHARED,
- (WAVEFORMATEX*) &format, 0)))
- if (! rates.contains (ratesToTest[i]))
- rates.addUsingDefaultSort (ratesToTest[i]);
- }
- }
-
- virtual ~WASAPIDeviceBase()
- {
- device = nullptr;
- CloseHandle (clientEvent);
- }
-
- bool isOk() const noexcept { return defaultBufferSize > 0 && defaultSampleRate > 0; }
-
- bool openClient (const double newSampleRate, const BigInteger& newChannels, const int bufferSizeSamples)
- {
- sampleRate = newSampleRate;
- channels = newChannels;
- channels.setRange (actualNumChannels, channels.getHighestBit() + 1 - actualNumChannels, false);
- numChannels = channels.getHighestBit() + 1;
-
- if (numChannels == 0)
- return true;
-
- client = createClient();
-
- if (client != nullptr
- && tryInitialisingWithBufferSize (bufferSizeSamples))
- {
- sampleRateHasChanged = false;
-
- channelMaps.clear();
- for (int i = 0; i <= channels.getHighestBit(); ++i)
- if (channels[i])
- channelMaps.add (i);
-
- REFERENCE_TIME latency;
- if (check (client->GetStreamLatency (&latency)))
- latencySamples = refTimeToSamples (latency, sampleRate);
-
- (void) check (client->GetBufferSize (&actualBufferSize));
-
- createSessionEventCallback();
-
- return check (client->SetEventHandle (clientEvent));
- }
-
- return false;
- }
-
- void closeClient()
- {
- if (client != nullptr)
- client->Stop();
-
- deleteSessionEventCallback();
- client = nullptr;
- ResetEvent (clientEvent);
- }
-
- void deviceSampleRateChanged()
- {
- sampleRateHasChanged = true;
- }
-
- void deviceBecameInactive()
- {
- shouldClose = true;
- }
-
- //==============================================================================
- ComSmartPtr<IMMDevice> device;
- ComSmartPtr<IAudioClient> client;
- double sampleRate, defaultSampleRate;
- int numChannels, actualNumChannels;
- int minBufferSize, defaultBufferSize, latencySamples;
- DWORD mixFormatChannelMask;
- const bool useExclusiveMode;
- Array<double> rates;
- HANDLE clientEvent;
- BigInteger channels;
- Array<int> channelMaps;
- UINT32 actualBufferSize;
- int bytesPerSample, bytesPerFrame;
- bool sampleRateHasChanged, shouldClose;
-
- virtual void updateFormat (bool isFloat) = 0;
-
- private:
- //==============================================================================
- class SessionEventCallback : public ComBaseClassHelper<IAudioSessionEvents>
- {
- public:
- SessionEventCallback (WASAPIDeviceBase& d) : owner (d) {}
-
- JUCE_COMRESULT OnDisplayNameChanged (LPCWSTR, LPCGUID) { return S_OK; }
- JUCE_COMRESULT OnIconPathChanged (LPCWSTR, LPCGUID) { return S_OK; }
- JUCE_COMRESULT OnSimpleVolumeChanged (float, BOOL, LPCGUID) { return S_OK; }
- JUCE_COMRESULT OnChannelVolumeChanged (DWORD, float*, DWORD, LPCGUID) { return S_OK; }
- JUCE_COMRESULT OnGroupingParamChanged (LPCGUID, LPCGUID) { return S_OK; }
- JUCE_COMRESULT OnStateChanged(AudioSessionState state)
- {
- if (state == AudioSessionStateInactive || state == AudioSessionStateExpired)
- owner.deviceBecameInactive();
-
- return S_OK;
- }
-
- JUCE_COMRESULT OnSessionDisconnected (AudioSessionDisconnectReason reason)
- {
- Logger::writeToLog("OnSessionDisconnected");
- if (reason == DisconnectReasonFormatChanged)
- owner.deviceSampleRateChanged();
-
- return S_OK;
- }
-
- private:
- WASAPIDeviceBase& owner;
-
- JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SessionEventCallback)
- };
-
- ComSmartPtr<IAudioSessionControl> audioSessionControl;
- ComSmartPtr<SessionEventCallback> sessionEventCallback;
-
- void createSessionEventCallback()
- {
- deleteSessionEventCallback();
- client->GetService (__uuidof (IAudioSessionControl),
- (void**) audioSessionControl.resetAndGetPointerAddress());
-
- if (audioSessionControl != nullptr)
- {
- sessionEventCallback = new SessionEventCallback (*this);
- audioSessionControl->RegisterAudioSessionNotification (sessionEventCallback);
- sessionEventCallback->Release(); // (required because ComBaseClassHelper objects are constructed with a ref count of 1)
- }
- }
-
- void deleteSessionEventCallback()
- {
- if (audioSessionControl != nullptr && sessionEventCallback != nullptr)
- audioSessionControl->UnregisterAudioSessionNotification (sessionEventCallback);
-
- audioSessionControl = nullptr;
- sessionEventCallback = nullptr;
- }
-
- //==============================================================================
- ComSmartPtr<IAudioClient> createClient()
- {
- ComSmartPtr<IAudioClient> newClient;
-
- if (device != nullptr)
- logFailure (device->Activate (__uuidof (IAudioClient), CLSCTX_INPROC_SERVER,
- nullptr, (void**) newClient.resetAndGetPointerAddress()));
-
- return newClient;
- }
-
- struct AudioSampleFormat
- {
- bool useFloat;
- int bitsPerSampleToTry;
- int bytesPerSampleContainer;
- };
-
- bool tryFormat (const AudioSampleFormat sampleFormat, IAudioClient* clientToUse, double newSampleRate,
- DWORD newMixFormatChannelMask, WAVEFORMATEXTENSIBLE& format) const
- {
- zerostruct (format);
-
- if (numChannels <= 2 && sampleFormat.bitsPerSampleToTry <= 16)
- {
- format.Format.wFormatTag = WAVE_FORMAT_PCM;
- }
- else
- {
- format.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
- format.Format.cbSize = sizeof (WAVEFORMATEXTENSIBLE) - sizeof (WAVEFORMATEX);
- }
-
- format.Format.nSamplesPerSec = (DWORD) newSampleRate;
- format.Format.nChannels = (WORD) numChannels;
- format.Format.wBitsPerSample = (WORD) (8 * sampleFormat.bytesPerSampleContainer);
- format.Samples.wValidBitsPerSample = (WORD) (sampleFormat.bitsPerSampleToTry);
- format.Format.nBlockAlign = (WORD) (format.Format.nChannels * format.Format.wBitsPerSample / 8);
- format.Format.nAvgBytesPerSec = (DWORD) (format.Format.nSamplesPerSec * format.Format.nBlockAlign);
- format.SubFormat = sampleFormat.useFloat ? KSDATAFORMAT_SUBTYPE_IEEE_FLOAT : KSDATAFORMAT_SUBTYPE_PCM;
- format.dwChannelMask = newMixFormatChannelMask;
-
- WAVEFORMATEXTENSIBLE* nearestFormat = nullptr;
-
- HRESULT hr = clientToUse->IsFormatSupported (useExclusiveMode ? AUDCLNT_SHAREMODE_EXCLUSIVE
- : AUDCLNT_SHAREMODE_SHARED,
- (WAVEFORMATEX*) &format,
- useExclusiveMode ? nullptr : (WAVEFORMATEX**) &nearestFormat);
- logFailure (hr);
-
- if (hr == S_FALSE && format.Format.nSamplesPerSec == nearestFormat->Format.nSamplesPerSec)
- {
- copyWavFormat (format, (const WAVEFORMATEX*) nearestFormat);
- hr = S_OK;
- }
-
- CoTaskMemFree (nearestFormat);
- return check (hr);
- }
-
- bool findSupportedFormat (IAudioClient* clientToUse, double newSampleRate,
- DWORD newMixFormatChannelMask, WAVEFORMATEXTENSIBLE& format) const
- {
- static const AudioSampleFormat formats[] =
- {
- { true, 32, 4 },
- { false, 32, 4 },
- { false, 24, 4 },
- { false, 24, 3 },
- { false, 20, 4 },
- { false, 20, 3 },
- { false, 16, 2 }
- };
-
- for (int i = 0; i < numElementsInArray (formats); ++i)
- if (tryFormat (formats[i], clientToUse, newSampleRate, newMixFormatChannelMask, format))
- return true;
-
- return false;
- }
-
- bool tryInitialisingWithBufferSize (const int bufferSizeSamples)
- {
- WAVEFORMATEXTENSIBLE format;
-
- if (findSupportedFormat (client, sampleRate, mixFormatChannelMask, format))
- {
- REFERENCE_TIME defaultPeriod = 0, minPeriod = 0;
-
- check (client->GetDevicePeriod (&defaultPeriod, &minPeriod));
-
- if (useExclusiveMode && bufferSizeSamples > 0)
- defaultPeriod = jmax (minPeriod, samplesToRefTime (bufferSizeSamples, format.Format.nSamplesPerSec));
-
- for (;;)
- {
- GUID session;
- HRESULT hr = client->Initialize (useExclusiveMode ? AUDCLNT_SHAREMODE_EXCLUSIVE : AUDCLNT_SHAREMODE_SHARED,
- 0x40000 /*AUDCLNT_STREAMFLAGS_EVENTCALLBACK*/,
- defaultPeriod, useExclusiveMode ? defaultPeriod : 0, (WAVEFORMATEX*) &format, &session);
-
- if (check (hr))
- {
- actualNumChannels = format.Format.nChannels;
- const bool isFloat = format.Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE && format.SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
- bytesPerSample = format.Format.wBitsPerSample / 8;
- bytesPerFrame = format.Format.nBlockAlign;
-
- updateFormat (isFloat);
- return true;
- }
-
- // Handle the "alignment dance" : http://msdn.microsoft.com/en-us/library/windows/desktop/dd370875(v=vs.85).aspx (see Remarks)
- if (hr != MAKE_HRESULT (1, 0x889, 0x19)) // AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED
- break;
-
- UINT32 numFrames = 0;
- if (! check (client->GetBufferSize (&numFrames)))
- break;
-
- // Recreate client
- client = nullptr;
- client = createClient();
-
- defaultPeriod = samplesToRefTime (numFrames, format.Format.nSamplesPerSec);
- }
- }
-
- return false;
- }
-
- JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WASAPIDeviceBase)
- };
-
- //==============================================================================
- class WASAPIInputDevice : public WASAPIDeviceBase
- {
- public:
- WASAPIInputDevice (const ComSmartPtr<IMMDevice>& d, const bool exclusiveMode)
- : WASAPIDeviceBase (d, exclusiveMode),
- reservoir (1, 1)
- {
- }
-
- ~WASAPIInputDevice()
- {
- close();
- }
-
- bool open (const double newSampleRate, const BigInteger& newChannels, int bufferSizeSamples)
- {
- return openClient (newSampleRate, newChannels, bufferSizeSamples)
- && (numChannels == 0 || check (client->GetService (__uuidof (IAudioCaptureClient),
- (void**) captureClient.resetAndGetPointerAddress())));
- }
-
- void close()
- {
- closeClient();
- captureClient = nullptr;
- reservoir.reset();
- reservoirReadPos = reservoirWritePos = 0;
- }
-
- template<class SourceType>
- void updateFormatWithType (SourceType*) noexcept
- {
- typedef AudioData::Pointer<AudioData::Float32, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::NonConst> NativeType;
- converter = new AudioData::ConverterInstance<AudioData::Pointer<SourceType, AudioData::LittleEndian, AudioData::Interleaved, AudioData::Const>, NativeType> (actualNumChannels, 1);
- }
-
- void updateFormat (bool isFloat) override
- {
- if (isFloat) updateFormatWithType ((AudioData::Float32*) nullptr);
- else if (bytesPerSample == 4) updateFormatWithType ((AudioData::Int32*) nullptr);
- else if (bytesPerSample == 3) updateFormatWithType ((AudioData::Int24*) nullptr);
- else updateFormatWithType ((AudioData::Int16*) nullptr);
- }
-
- bool start (const int userBufferSize)
- {
- reservoirSize = actualBufferSize + userBufferSize;
- reservoirMask = nextPowerOfTwo (reservoirSize) - 1;
- reservoir.setSize ((reservoirMask + 1) * bytesPerFrame, true);
- reservoirReadPos = reservoirWritePos = 0;
- xruns = 0;
-
- if (! check (client->Start()))
- return false;
-
- purgeInputBuffers();
- return true;
- }
-
- void purgeInputBuffers()
- {
- uint8* inputData;
- UINT32 numSamplesAvailable;
- DWORD flags;
-
- while (captureClient->GetBuffer (&inputData, &numSamplesAvailable, &flags, nullptr, nullptr)
- != MAKE_HRESULT (0, 0x889, 0x1) /* AUDCLNT_S_BUFFER_EMPTY */)
- captureClient->ReleaseBuffer (numSamplesAvailable);
- }
-
- int getNumSamplesInReservoir() const noexcept { return reservoirWritePos - reservoirReadPos; }
-
- void handleDeviceBuffer()
- {
- if (numChannels <= 0)
- return;
-
- uint8* inputData;
- UINT32 numSamplesAvailable;
- DWORD flags;
-
- while (check (captureClient->GetBuffer (&inputData, &numSamplesAvailable, &flags, nullptr, nullptr)) && numSamplesAvailable > 0)
- {
- if ((flags & AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0)
- xruns++;
-
- int samplesLeft = (int) numSamplesAvailable;
-
- while (samplesLeft > 0)
- {
- const int localWrite = reservoirWritePos & reservoirMask;
- const int samplesToDo = jmin (samplesLeft, reservoirMask + 1 - localWrite);
- const int samplesToDoBytes = samplesToDo * bytesPerFrame;
-
- void* reservoirPtr = addBytesToPointer (reservoir.getData(), localWrite * bytesPerFrame);
-
- if ((flags & AUDCLNT_BUFFERFLAGS_SILENT) != 0)
- zeromem (reservoirPtr, samplesToDoBytes);
- else
- memcpy (reservoirPtr, inputData, samplesToDoBytes);
-
- reservoirWritePos += samplesToDo;
- inputData += samplesToDoBytes;
- samplesLeft -= samplesToDo;
- }
-
- if (getNumSamplesInReservoir() > reservoirSize)
- reservoirReadPos = reservoirWritePos - reservoirSize;
-
- captureClient->ReleaseBuffer (numSamplesAvailable);
- }
- }
-
- void copyBuffersFromReservoir (float** destBuffers, int numDestBuffers, int bufferSize)
- {
- if ((numChannels <= 0 && bufferSize == 0) || reservoir.getSize() == 0)
- return;
-
- int offset = jmax (0, bufferSize - getNumSamplesInReservoir());
-
- if (offset > 0)
- {
- for (int i = 0; i < numDestBuffers; ++i)
- zeromem (destBuffers[i], offset * sizeof (float));
-
- bufferSize -= offset;
- reservoirReadPos -= offset / 2;
- }
-
- while (bufferSize > 0)
- {
- const int localRead = reservoirReadPos & reservoirMask;
-
- const int samplesToDo = jmin (bufferSize, getNumSamplesInReservoir(), reservoirMask + 1 - localRead);
- if (samplesToDo <= 0)
- break;
-
- const int reservoirOffset = localRead * bytesPerFrame;
-
- for (int i = 0; i < numDestBuffers; ++i)
- converter->convertSamples (destBuffers[i] + offset, 0, addBytesToPointer (reservoir.getData(), reservoirOffset), channelMaps.getUnchecked(i), samplesToDo);
-
- bufferSize -= samplesToDo;
- offset += samplesToDo;
- reservoirReadPos += samplesToDo;
- }
- }
-
- ComSmartPtr<IAudioCaptureClient> captureClient;
- MemoryBlock reservoir;
- int reservoirSize, reservoirMask, xruns;
- volatile int reservoirReadPos, reservoirWritePos;
-
- ScopedPointer<AudioData::Converter> converter;
-
- private:
- JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WASAPIInputDevice)
- };
-
- //==============================================================================
- class WASAPIOutputDevice : public WASAPIDeviceBase
- {
- public:
- WASAPIOutputDevice (const ComSmartPtr<IMMDevice>& d, const bool exclusiveMode)
- : WASAPIDeviceBase (d, exclusiveMode)
- {
- }
-
- ~WASAPIOutputDevice()
- {
- close();
- }
-
- bool open (const double newSampleRate, const BigInteger& newChannels, int bufferSizeSamples)
- {
- return openClient (newSampleRate, newChannels, bufferSizeSamples)
- && (numChannels == 0 || check (client->GetService (__uuidof (IAudioRenderClient),
- (void**) renderClient.resetAndGetPointerAddress())));
- }
-
- void close()
- {
- closeClient();
- renderClient = nullptr;
- }
-
- template<class DestType>
- void updateFormatWithType (DestType*)
- {
- typedef AudioData::Pointer<AudioData::Float32, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::Const> NativeType;
- converter = new AudioData::ConverterInstance<NativeType, AudioData::Pointer<DestType, AudioData::LittleEndian, AudioData::Interleaved, AudioData::NonConst> > (1, actualNumChannels);
- }
-
- void updateFormat (bool isFloat) override
- {
- if (isFloat) updateFormatWithType ((AudioData::Float32*) nullptr);
- else if (bytesPerSample == 4) updateFormatWithType ((AudioData::Int32*) nullptr);
- else if (bytesPerSample == 3) updateFormatWithType ((AudioData::Int24*) nullptr);
- else updateFormatWithType ((AudioData::Int16*) nullptr);
- }
-
- bool start()
- {
- int samplesToDo = getNumSamplesAvailableToCopy();
- uint8* outputData;
-
- if (check (renderClient->GetBuffer (samplesToDo, &outputData)))
- renderClient->ReleaseBuffer (samplesToDo, AUDCLNT_BUFFERFLAGS_SILENT);
-
- return check (client->Start());
- }
-
- int getNumSamplesAvailableToCopy() const
- {
- if (numChannels <= 0)
- return 0;
-
- if (! useExclusiveMode)
- {
- UINT32 padding = 0;
- if (check (client->GetCurrentPadding (&padding)))
- return actualBufferSize - (int) padding;
- }
-
- return actualBufferSize;
- }
-
- void copyBuffers (const float** const srcBuffers, const int numSrcBuffers, int bufferSize,
- WASAPIInputDevice* inputDevice, Thread& thread)
- {
- if (numChannels <= 0)
- return;
-
- int offset = 0;
-
- while (bufferSize > 0)
- {
- // This is needed in order not to drop any input data if the output device endpoint buffer was full
- if ((! useExclusiveMode) && inputDevice != nullptr
- && WaitForSingleObject (inputDevice->clientEvent, 0) == WAIT_OBJECT_0)
- inputDevice->handleDeviceBuffer();
-
- int samplesToDo = jmin (getNumSamplesAvailableToCopy(), bufferSize);
-
- if (samplesToDo == 0)
- {
- // This can ONLY occur in non-exclusive mode
- if (! thread.threadShouldExit() && WaitForSingleObject (clientEvent, 1000) == WAIT_OBJECT_0)
- continue;
-
- break;
- }
-
- if (useExclusiveMode && WaitForSingleObject (clientEvent, 1000) == WAIT_TIMEOUT)
- break;
-
- uint8* outputData = nullptr;
- if (check (renderClient->GetBuffer ((UINT32) samplesToDo, &outputData)))
- {
- for (int i = 0; i < numSrcBuffers; ++i)
- converter->convertSamples (outputData, channelMaps.getUnchecked(i), srcBuffers[i] + offset, 0, samplesToDo);
-
- renderClient->ReleaseBuffer ((UINT32) samplesToDo, 0);
- }
-
- bufferSize -= samplesToDo;
- offset += samplesToDo;
- }
- }
-
- ComSmartPtr<IAudioRenderClient> renderClient;
- ScopedPointer<AudioData::Converter> converter;
-
- private:
- JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WASAPIOutputDevice)
- };
-
- //==============================================================================
- class WASAPIAudioIODevice : public AudioIODevice,
- public Thread,
- private AsyncUpdater
- {
- public:
- WASAPIAudioIODevice (const String& deviceName,
- const String& typeName,
- const String& outputDeviceID,
- const String& inputDeviceID,
- const bool exclusiveMode)
- : AudioIODevice (deviceName, typeName),
- Thread ("Juce WASAPI"),
- outputDeviceId (outputDeviceID),
- inputDeviceId (inputDeviceID),
- useExclusiveMode (exclusiveMode),
- isOpen_ (false),
- isStarted (false),
- currentBufferSizeSamples (0),
- currentSampleRate (0),
- callback (nullptr),
- deviceBecameInactive (false)
- {
- }
-
- ~WASAPIAudioIODevice()
- {
- close();
- }
-
- bool initialise()
- {
- latencyIn = latencyOut = 0;
- Array<double> ratesIn, ratesOut;
-
- if (createDevices())
- {
- jassert (inputDevice != nullptr || outputDevice != nullptr);
-
- if (inputDevice != nullptr && outputDevice != nullptr)
- {
- defaultSampleRate = jmin (inputDevice->defaultSampleRate, outputDevice->defaultSampleRate);
- minBufferSize = jmin (inputDevice->minBufferSize, outputDevice->minBufferSize);
- defaultBufferSize = jmax (inputDevice->defaultBufferSize, outputDevice->defaultBufferSize);
- sampleRates = inputDevice->rates;
- sampleRates.removeValuesNotIn (outputDevice->rates);
- }
- else
- {
- WASAPIDeviceBase* d = inputDevice != nullptr ? static_cast<WASAPIDeviceBase*> (inputDevice)
- : static_cast<WASAPIDeviceBase*> (outputDevice);
- defaultSampleRate = d->defaultSampleRate;
- minBufferSize = d->minBufferSize;
- defaultBufferSize = d->defaultBufferSize;
- sampleRates = d->rates;
- }
-
- bufferSizes.addUsingDefaultSort (defaultBufferSize);
- if (minBufferSize != defaultBufferSize)
- bufferSizes.addUsingDefaultSort (minBufferSize);
-
- int n = 64;
- for (int i = 0; i < 40; ++i)
- {
- if (n >= minBufferSize && n <= 2048 && ! bufferSizes.contains (n))
- bufferSizes.addUsingDefaultSort (n);
-
- n += (n < 512) ? 32 : (n < 1024 ? 64 : 128);
- }
-
- return true;
- }
-
- return false;
- }
-
- StringArray getOutputChannelNames() override
- {
- StringArray outChannels;
-
- if (outputDevice != nullptr)
- for (int i = 1; i <= outputDevice->actualNumChannels; ++i)
- outChannels.add ("Output channel " + String (i));
-
- return outChannels;
- }
-
- StringArray getInputChannelNames() override
- {
- StringArray inChannels;
-
- if (inputDevice != nullptr)
- for (int i = 1; i <= inputDevice->actualNumChannels; ++i)
- inChannels.add ("Input channel " + String (i));
-
- return inChannels;
- }
-
- Array<double> getAvailableSampleRates() override { return sampleRates; }
- Array<int> getAvailableBufferSizes() override { return bufferSizes; }
- int getDefaultBufferSize() override { return defaultBufferSize; }
-
- int getCurrentBufferSizeSamples() override { return currentBufferSizeSamples; }
- double getCurrentSampleRate() override { return currentSampleRate; }
- int getCurrentBitDepth() override { return 32; }
- int getOutputLatencyInSamples() override { return latencyOut; }
- int getInputLatencyInSamples() override { return latencyIn; }
- BigInteger getActiveOutputChannels() const override { return outputDevice != nullptr ? outputDevice->channels : BigInteger(); }
- BigInteger getActiveInputChannels() const override { return inputDevice != nullptr ? inputDevice->channels : BigInteger(); }
- String getLastError() override { return lastError; }
- int getXRunCount () const noexcept override { return inputDevice != nullptr ? inputDevice->xruns : -1; }
-
- String open (const BigInteger& inputChannels, const BigInteger& outputChannels,
- double sampleRate, int bufferSizeSamples) override
- {
- close();
- lastError.clear();
-
- if (sampleRates.size() == 0 && inputDevice != nullptr && outputDevice != nullptr)
- {
- lastError = TRANS("The input and output devices don't share a common sample rate!");
- return lastError;
- }
-
- currentBufferSizeSamples = bufferSizeSamples <= 0 ? defaultBufferSize : jmax (bufferSizeSamples, minBufferSize);
- currentSampleRate = sampleRate > 0 ? sampleRate : defaultSampleRate;
- lastKnownInputChannels = inputChannels;
- lastKnownOutputChannels = outputChannels;
-
- if (inputDevice != nullptr && ! inputDevice->open (currentSampleRate, inputChannels, bufferSizeSamples))
- {
- lastError = TRANS("Couldn't open the input device!");
- return lastError;
- }
-
- if (outputDevice != nullptr && ! outputDevice->open (currentSampleRate, outputChannels, bufferSizeSamples))
- {
- close();
- lastError = TRANS("Couldn't open the output device!");
- return lastError;
- }
-
- if (useExclusiveMode)
- {
- // This is to make sure that the callback uses actualBufferSize in case of exclusive mode
- if (inputDevice != nullptr && outputDevice != nullptr && inputDevice->actualBufferSize != outputDevice->actualBufferSize)
- {
- close();
- lastError = TRANS("Couldn't open the output device (buffer size mismatch)");
- return lastError;
- }
-
- currentBufferSizeSamples = outputDevice != nullptr ? outputDevice->actualBufferSize
- : inputDevice->actualBufferSize;
- }
-
- if (inputDevice != nullptr) ResetEvent (inputDevice->clientEvent);
- if (outputDevice != nullptr) ResetEvent (outputDevice->clientEvent);
-
- deviceBecameInactive = false;
-
- startThread (8);
- Thread::sleep (5);
-
- if (inputDevice != nullptr && inputDevice->client != nullptr)
- {
- latencyIn = (int) (inputDevice->latencySamples + currentBufferSizeSamples);
-
- if (! inputDevice->start (currentBufferSizeSamples))
- {
- close();
- lastError = TRANS("Couldn't start the input device!");
- return lastError;
- }
- }
-
- if (outputDevice != nullptr && outputDevice->client != nullptr)
- {
- latencyOut = (int) (outputDevice->latencySamples + currentBufferSizeSamples);
-
- if (! outputDevice->start())
- {
- close();
- lastError = TRANS("Couldn't start the output device!");
- return lastError;
- }
- }
-
- isOpen_ = true;
- return lastError;
- }
-
- void close() override
- {
- stop();
- signalThreadShouldExit();
-
- if (inputDevice != nullptr) SetEvent (inputDevice->clientEvent);
- if (outputDevice != nullptr) SetEvent (outputDevice->clientEvent);
-
- stopThread (5000);
-
- if (inputDevice != nullptr) inputDevice->close();
- if (outputDevice != nullptr) outputDevice->close();
-
- isOpen_ = false;
- }
-
- bool isOpen() override { return isOpen_ && isThreadRunning(); }
- bool isPlaying() override { return isStarted && isOpen_ && isThreadRunning(); }
-
- void start (AudioIODeviceCallback* call) override
- {
- if (isOpen_ && call != nullptr && ! isStarted)
- {
- if (! isThreadRunning())
- {
- // something's gone wrong and the thread's stopped..
- isOpen_ = false;
- return;
- }
-
- call->audioDeviceAboutToStart (this);
-
- const ScopedLock sl (startStopLock);
- callback = call;
- isStarted = true;
- }
- }
-
- void stop() override
- {
- if (isStarted)
- {
- AudioIODeviceCallback* const callbackLocal = callback;
-
- {
- const ScopedLock sl (startStopLock);
- isStarted = false;
- }
-
- if (callbackLocal != nullptr)
- callbackLocal->audioDeviceStopped();
- }
- }
-
- void setMMThreadPriority()
- {
- DynamicLibrary dll ("avrt.dll");
- JUCE_LOAD_WINAPI_FUNCTION (dll, AvSetMmThreadCharacteristicsW, avSetMmThreadCharacteristics, HANDLE, (LPCWSTR, LPDWORD))
- JUCE_LOAD_WINAPI_FUNCTION (dll, AvSetMmThreadPriority, avSetMmThreadPriority, HANDLE, (HANDLE, AVRT_PRIORITY))
-
- if (avSetMmThreadCharacteristics != 0 && avSetMmThreadPriority != 0)
- {
- DWORD dummy = 0;
- HANDLE h = avSetMmThreadCharacteristics (L"Pro Audio", &dummy);
-
- if (h != 0)
- avSetMmThreadPriority (h, AVRT_PRIORITY_NORMAL);
- }
- }
-
- void run() override
- {
- setMMThreadPriority();
-
- const int bufferSize = currentBufferSizeSamples;
- const int numInputBuffers = getActiveInputChannels().countNumberOfSetBits();
- const int numOutputBuffers = getActiveOutputChannels().countNumberOfSetBits();
- bool sampleRateHasChanged = false;
-
- AudioSampleBuffer ins (jmax (1, numInputBuffers), bufferSize + 32);
- AudioSampleBuffer outs (jmax (1, numOutputBuffers), bufferSize + 32);
- float** const inputBuffers = ins.getArrayOfWritePointers();
- float** const outputBuffers = outs.getArrayOfWritePointers();
- ins.clear();
- outs.clear();
-
- while (! threadShouldExit())
- {
- if (outputDevice != nullptr && outputDevice->shouldClose)
- deviceBecameInactive = true;
-
- if (inputDevice != nullptr && ! deviceBecameInactive)
- {
- if (inputDevice->shouldClose)
- deviceBecameInactive = true;
-
- if (outputDevice == nullptr)
- {
- if (WaitForSingleObject (inputDevice->clientEvent, 1000) == WAIT_TIMEOUT)
- break;
-
- inputDevice->handleDeviceBuffer();
-
- if (inputDevice->getNumSamplesInReservoir() < bufferSize)
- continue;
- }
- else
- {
- if (useExclusiveMode && WaitForSingleObject (inputDevice->clientEvent, 0) == WAIT_OBJECT_0)
- inputDevice->handleDeviceBuffer();
- }
-
- inputDevice->copyBuffersFromReservoir (inputBuffers, numInputBuffers, bufferSize);
-
- if (inputDevice->sampleRateHasChanged)
- {
- sampleRateHasChanged = true;
- sampleRateChangedByOutput = false;
- }
- }
-
- if (! deviceBecameInactive)
- {
- const ScopedTryLock sl (startStopLock);
-
- if (sl.isLocked() && isStarted)
- callback->audioDeviceIOCallback (const_cast<const float**> (inputBuffers), numInputBuffers,
- outputBuffers, numOutputBuffers, bufferSize);
- else
- outs.clear();
- }
-
- if (outputDevice != nullptr && !deviceBecameInactive)
- {
- // Note that this function is handed the input device so it can check for the event and make sure
- // the input reservoir is filled up correctly even when bufferSize > device actualBufferSize
- outputDevice->copyBuffers (const_cast<const float**> (outputBuffers), numOutputBuffers, bufferSize, inputDevice, *this);
-
- if (outputDevice->sampleRateHasChanged)
- {
- sampleRateHasChanged = true;
- sampleRateChangedByOutput = true;
- }
- }
-
- if (sampleRateHasChanged || deviceBecameInactive)
- {
- triggerAsyncUpdate();
- break; // Quit the thread... will restart it later!
- }
- }
- }
-
- //==============================================================================
- String outputDeviceId, inputDeviceId;
- String lastError;
-
- private:
- // Device stats...
- ScopedPointer<WASAPIInputDevice> inputDevice;
- ScopedPointer<WASAPIOutputDevice> outputDevice;
- const bool useExclusiveMode;
- double defaultSampleRate;
- int minBufferSize, defaultBufferSize;
- int latencyIn, latencyOut;
- Array<double> sampleRates;
- Array<int> bufferSizes;
-
- // Active state...
- bool isOpen_, isStarted;
- int currentBufferSizeSamples;
- double currentSampleRate;
- bool sampleRateChangedByOutput, deviceBecameInactive;
-
- AudioIODeviceCallback* callback;
- CriticalSection startStopLock;
-
- BigInteger lastKnownInputChannels, lastKnownOutputChannels;
-
- //==============================================================================
- bool createDevices()
- {
- ComSmartPtr<IMMDeviceEnumerator> enumerator;
- if (! check (enumerator.CoCreateInstance (__uuidof (MMDeviceEnumerator))))
- return false;
-
- ComSmartPtr<IMMDeviceCollection> deviceCollection;
- if (! check (enumerator->EnumAudioEndpoints (eAll, DEVICE_STATE_ACTIVE, deviceCollection.resetAndGetPointerAddress())))
- return false;
-
- UINT32 numDevices = 0;
- if (! check (deviceCollection->GetCount (&numDevices)))
- return false;
-
- for (UINT32 i = 0; i < numDevices; ++i)
- {
- ComSmartPtr<IMMDevice> device;
- if (! check (deviceCollection->Item (i, device.resetAndGetPointerAddress())))
- continue;
-
- const String deviceId (getDeviceID (device));
- if (deviceId.isEmpty())
- continue;
-
- const EDataFlow flow = getDataFlow (device);
-
- if (deviceId == inputDeviceId && flow == eCapture)
- inputDevice = new WASAPIInputDevice (device, useExclusiveMode);
- else if (deviceId == outputDeviceId && flow == eRender)
- outputDevice = new WASAPIOutputDevice (device, useExclusiveMode);
- }
-
- return (outputDeviceId.isEmpty() || (outputDevice != nullptr && outputDevice->isOk()))
- && (inputDeviceId.isEmpty() || (inputDevice != nullptr && inputDevice->isOk()));
- }
-
- //==============================================================================
- void handleAsyncUpdate() override
- {
- stop();
-
- outputDevice = nullptr;
- inputDevice = nullptr;
-
- // sample rate change
- if (! deviceBecameInactive)
- {
- initialise();
-
- open (lastKnownInputChannels, lastKnownOutputChannels,
- getChangedSampleRate(), currentBufferSizeSamples);
-
- start (callback);
- }
- }
-
- double getChangedSampleRate() const
- {
- if (outputDevice != nullptr && sampleRateChangedByOutput)
- return outputDevice->defaultSampleRate;
-
- if (inputDevice != nullptr && ! sampleRateChangedByOutput)
- return inputDevice->defaultSampleRate;
-
- return 0.0;
- }
-
- //==============================================================================
- JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WASAPIAudioIODevice)
- };
-
-
- //==============================================================================
- class WASAPIAudioIODeviceType : public AudioIODeviceType,
- private DeviceChangeDetector
- {
- public:
- WASAPIAudioIODeviceType (bool exclusive)
- : AudioIODeviceType (exclusive ? "Windows Audio (Exclusive Mode)" : "Windows Audio"),
- DeviceChangeDetector (L"Windows Audio"),
- exclusiveMode (exclusive),
- hasScanned (false)
- {
- }
-
- ~WASAPIAudioIODeviceType()
- {
- if (notifyClient != nullptr)
- enumerator->UnregisterEndpointNotificationCallback (notifyClient);
- }
-
- //==============================================================================
- void scanForDevices()
- {
- hasScanned = true;
-
- outputDeviceNames.clear();
- inputDeviceNames.clear();
- outputDeviceIds.clear();
- inputDeviceIds.clear();
-
- scan (outputDeviceNames, inputDeviceNames,
- outputDeviceIds, inputDeviceIds);
- }
-
- StringArray getDeviceNames (bool wantInputNames) const
- {
- jassert (hasScanned); // need to call scanForDevices() before doing this
-
- return wantInputNames ? inputDeviceNames
- : outputDeviceNames;
- }
-
- int getDefaultDeviceIndex (bool /*forInput*/) const
- {
- jassert (hasScanned); // need to call scanForDevices() before doing this
- return 0;
- }
-
- int getIndexOfDevice (AudioIODevice* device, bool asInput) const
- {
- jassert (hasScanned); // need to call scanForDevices() before doing this
-
- if (WASAPIAudioIODevice* const d = dynamic_cast<WASAPIAudioIODevice*> (device))
- return asInput ? inputDeviceIds.indexOf (d->inputDeviceId)
- : outputDeviceIds.indexOf (d->outputDeviceId);
-
- return -1;
- }
-
- bool hasSeparateInputsAndOutputs() const { return true; }
-
- AudioIODevice* createDevice (const String& outputDeviceName,
- const String& inputDeviceName)
- {
- jassert (hasScanned); // need to call scanForDevices() before doing this
-
- ScopedPointer<WASAPIAudioIODevice> device;
-
- const int outputIndex = outputDeviceNames.indexOf (outputDeviceName);
- const int inputIndex = inputDeviceNames.indexOf (inputDeviceName);
-
- if (outputIndex >= 0 || inputIndex >= 0)
- {
- device = new WASAPIAudioIODevice (outputDeviceName.isNotEmpty() ? outputDeviceName
- : inputDeviceName,
- getTypeName(),
- outputDeviceIds [outputIndex],
- inputDeviceIds [inputIndex],
- exclusiveMode);
-
- if (! device->initialise())
- device = nullptr;
- }
-
- return device.release();
- }
-
- //==============================================================================
- StringArray outputDeviceNames, outputDeviceIds;
- StringArray inputDeviceNames, inputDeviceIds;
-
- private:
- bool exclusiveMode, hasScanned;
- ComSmartPtr<IMMDeviceEnumerator> enumerator;
-
- //==============================================================================
- class ChangeNotificationClient : public ComBaseClassHelper<IMMNotificationClient>
- {
- public:
- ChangeNotificationClient (WASAPIAudioIODeviceType& d)
- : ComBaseClassHelper<IMMNotificationClient> (0), device (d) {}
-
- HRESULT STDMETHODCALLTYPE OnDeviceAdded (LPCWSTR) { return notify(); }
- HRESULT STDMETHODCALLTYPE OnDeviceRemoved (LPCWSTR) { return notify(); }
- HRESULT STDMETHODCALLTYPE OnDeviceStateChanged(LPCWSTR, DWORD) { return notify(); }
- HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged (EDataFlow, ERole, LPCWSTR) { return notify(); }
- HRESULT STDMETHODCALLTYPE OnPropertyValueChanged (LPCWSTR, const PROPERTYKEY) { return notify(); }
-
- private:
- WASAPIAudioIODeviceType& device;
-
- HRESULT notify() { device.triggerAsyncDeviceChangeCallback(); return S_OK; }
-
- JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChangeNotificationClient)
- };
-
- ComSmartPtr<ChangeNotificationClient> notifyClient;
-
- //==============================================================================
- static String getDefaultEndpoint (IMMDeviceEnumerator* const enumerator, const bool forCapture)
- {
- String s;
- IMMDevice* dev = nullptr;
-
- if (check (enumerator->GetDefaultAudioEndpoint (forCapture ? eCapture : eRender,
- eMultimedia, &dev)))
- {
- WCHAR* deviceId = nullptr;
-
- if (check (dev->GetId (&deviceId)))
- {
- s = deviceId;
- CoTaskMemFree (deviceId);
- }
-
- dev->Release();
- }
-
- return s;
- }
-
- //==============================================================================
- void scan (StringArray& outDeviceNames,
- StringArray& inDeviceNames,
- StringArray& outDeviceIds,
- StringArray& inDeviceIds)
- {
- if (enumerator == nullptr)
- {
- if (! check (enumerator.CoCreateInstance (__uuidof (MMDeviceEnumerator))))
- return;
-
- notifyClient = new ChangeNotificationClient (*this);
- enumerator->RegisterEndpointNotificationCallback (notifyClient);
- }
-
- const String defaultRenderer (getDefaultEndpoint (enumerator, false));
- const String defaultCapture (getDefaultEndpoint (enumerator, true));
-
- ComSmartPtr<IMMDeviceCollection> deviceCollection;
- UINT32 numDevices = 0;
-
- if (! (check (enumerator->EnumAudioEndpoints (eAll, DEVICE_STATE_ACTIVE, deviceCollection.resetAndGetPointerAddress()))
- && check (deviceCollection->GetCount (&numDevices))))
- return;
-
- for (UINT32 i = 0; i < numDevices; ++i)
- {
- ComSmartPtr<IMMDevice> device;
- if (! check (deviceCollection->Item (i, device.resetAndGetPointerAddress())))
- continue;
-
- DWORD state = 0;
- if (! (check (device->GetState (&state)) && state == DEVICE_STATE_ACTIVE))
- continue;
-
- const String deviceId (getDeviceID (device));
- String name;
-
- {
- ComSmartPtr<IPropertyStore> properties;
- if (! check (device->OpenPropertyStore (STGM_READ, properties.resetAndGetPointerAddress())))
- continue;
-
- PROPVARIANT value;
- zerostruct (value);
-
- const PROPERTYKEY PKEY_Device_FriendlyName
- = { { 0xa45c254e, 0xdf1c, 0x4efd, { 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0 } }, 14 };
-
- if (check (properties->GetValue (PKEY_Device_FriendlyName, &value)))
- name = value.pwszVal;
-
- PropVariantClear (&value);
- }
-
- const EDataFlow flow = getDataFlow (device);
-
- if (flow == eRender)
- {
- const int index = (deviceId == defaultRenderer) ? 0 : -1;
- outDeviceIds.insert (index, deviceId);
- outDeviceNames.insert (index, name);
- }
- else if (flow == eCapture)
- {
- const int index = (deviceId == defaultCapture) ? 0 : -1;
- inDeviceIds.insert (index, deviceId);
- inDeviceNames.insert (index, name);
- }
- }
-
- inDeviceNames.appendNumbersToDuplicates (false, false);
- outDeviceNames.appendNumbersToDuplicates (false, false);
- }
-
- //==============================================================================
- void systemDeviceChanged() override
- {
- StringArray newOutNames, newInNames, newOutIds, newInIds;
- scan (newOutNames, newInNames, newOutIds, newInIds);
-
- if (newOutNames != outputDeviceNames
- || newInNames != inputDeviceNames
- || newOutIds != outputDeviceIds
- || newInIds != inputDeviceIds)
- {
- hasScanned = true;
- outputDeviceNames = newOutNames;
- inputDeviceNames = newInNames;
- outputDeviceIds = newOutIds;
- inputDeviceIds = newInIds;
- }
-
- callDeviceChangeListeners();
- }
-
- //==============================================================================
- JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WASAPIAudioIODeviceType)
- };
-
- //==============================================================================
- struct MMDeviceMasterVolume
- {
- MMDeviceMasterVolume()
- {
- ComSmartPtr<IMMDeviceEnumerator> enumerator;
- if (check (enumerator.CoCreateInstance (__uuidof (MMDeviceEnumerator))))
- {
- ComSmartPtr<IMMDevice> device;
- if (check (enumerator->GetDefaultAudioEndpoint (eRender, eConsole, device.resetAndGetPointerAddress())))
- check (device->Activate (__uuidof (IAudioEndpointVolume), CLSCTX_INPROC_SERVER, nullptr,
- (void**) endpointVolume.resetAndGetPointerAddress()));
- }
- }
-
- float getGain() const
- {
- float vol = 0.0f;
- if (endpointVolume != nullptr)
- check (endpointVolume->GetMasterVolumeLevelScalar (&vol));
-
- return vol;
- }
-
- bool setGain (float newGain) const
- {
- return endpointVolume != nullptr
- && check (endpointVolume->SetMasterVolumeLevelScalar (jlimit (0.0f, 1.0f, newGain), nullptr));
- }
-
- bool isMuted() const
- {
- BOOL mute = 0;
- return endpointVolume != nullptr
- && check (endpointVolume->GetMute (&mute)) && mute != 0;
- }
-
- bool setMuted (bool shouldMute) const
- {
- return endpointVolume != nullptr
- && check (endpointVolume->SetMute (shouldMute, nullptr));
- }
-
- ComSmartPtr<IAudioEndpointVolume> endpointVolume;
-
- JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MMDeviceMasterVolume)
- };
-
- }
-
- //==============================================================================
- AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_WASAPI (bool exclusiveMode)
- {
- #if ! JUCE_WASAPI_EXCLUSIVE
- if (exclusiveMode)
- return nullptr;
- #endif
-
- return SystemStats::getOperatingSystemType() >= SystemStats::WinVista
- ? new WasapiClasses::WASAPIAudioIODeviceType (exclusiveMode)
- : nullptr;
- }
-
- //==============================================================================
- #define JUCE_SYSTEMAUDIOVOL_IMPLEMENTED 1
- float JUCE_CALLTYPE SystemAudioVolume::getGain() { return WasapiClasses::MMDeviceMasterVolume().getGain(); }
- bool JUCE_CALLTYPE SystemAudioVolume::setGain (float gain) { return WasapiClasses::MMDeviceMasterVolume().setGain (gain); }
- bool JUCE_CALLTYPE SystemAudioVolume::isMuted() { return WasapiClasses::MMDeviceMasterVolume().isMuted(); }
- bool JUCE_CALLTYPE SystemAudioVolume::setMuted (bool mute) { return WasapiClasses::MMDeviceMasterVolume().setMuted (mute); }
-
- } // namespace juce
|