From 37d57810f2652dae9d896516146bca033e39aa44 Mon Sep 17 00:00:00 2001 From: Fabian Renn-Giles Date: Wed, 21 Sep 2022 18:11:57 +0000 Subject: [PATCH] CoreAudio: Modernised code interacting with CoreAudio audio objects --- .../native/juce_mac_CoreAudio.cpp | 673 +++++++++--------- 1 file changed, 341 insertions(+), 332 deletions(-) diff --git a/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp b/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp index d7ccff64e0..66ea635cf8 100644 --- a/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp +++ b/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp @@ -40,6 +40,190 @@ constexpr auto juceAudioObjectPropertyElementMain = kAudioObjectPropertyElementMaster; #endif +//============================================================================== +class ManagedAudioBufferList : public AudioBufferList +{ +public: + struct Deleter + { + void operator() (ManagedAudioBufferList* p) const + { + if (p != nullptr) + p->~ManagedAudioBufferList(); + + delete[] reinterpret_cast (p); + } + }; + + using Ref = std::unique_ptr; + + //============================================================================== + static auto create (std::size_t numBuffers) + { + std::unique_ptr storage (new std::byte[storageSizeForNumBuffers (numBuffers)]); + return Ref { new (storage.release()) ManagedAudioBufferList (numBuffers) }; + } + + //============================================================================== + static std::size_t storageSizeForNumBuffers (std::size_t numBuffers) noexcept + { + return audioBufferListHeaderSize + (numBuffers * sizeof (::AudioBuffer)); + } + + static std::size_t numBuffersForStorageSize (std::size_t bytes) noexcept + { + bytes -= audioBufferListHeaderSize; + + // storage size ends between to buffers in AudioBufferList + jassert ((bytes % sizeof (::AudioBuffer)) == 0); + + return bytes / sizeof (::AudioBuffer); + } + +private: + // Do not call the base constructor here as this will zero-initialize the first buffer, + // for which no storage may be available though (when numBuffers == 0). + explicit ManagedAudioBufferList (std::size_t numBuffers) + { + mNumberBuffers = static_cast (numBuffers); + } + + static constexpr auto audioBufferListHeaderSize = sizeof (AudioBufferList) - sizeof (::AudioBuffer); + + JUCE_DECLARE_NON_COPYABLE (ManagedAudioBufferList) + JUCE_DECLARE_NON_MOVEABLE (ManagedAudioBufferList) +}; + +//============================================================================== +struct IgnoreUnused +{ + template + void operator() (Ts&&...) const {} +}; + +template +static auto getDataPtrAndSize (T& t) +{ + static_assert (std::is_pod_v); + return std::make_tuple (&t, (UInt32) sizeof (T)); +} + +static auto getDataPtrAndSize (ManagedAudioBufferList::Ref& t) +{ + const auto size = t.get() != nullptr + ? ManagedAudioBufferList::storageSizeForNumBuffers (t->mNumberBuffers) + : 0; + return std::make_tuple (t.get(), (UInt32) size); +} + +//============================================================================== +[[nodiscard]] static bool audioObjectHasProperty (AudioObjectID objectID, const AudioObjectPropertyAddress address) +{ + return objectID != kAudioObjectUnknown && AudioObjectHasProperty (objectID, &address); +} + +template +[[nodiscard]] static auto audioObjectGetProperty (AudioObjectID objectID, + const AudioObjectPropertyAddress address, + OnError&& onError = {}) +{ + using Result = std::conditional_t, ManagedAudioBufferList::Ref, std::optional>; + + if (! audioObjectHasProperty (objectID, address)) + return Result{}; + + auto result = [&] + { + if constexpr (std::is_same_v) + { + UInt32 size{}; + + if (auto status = AudioObjectGetPropertyDataSize (objectID, &address, 0, nullptr, &size); status != noErr) + { + onError (status); + return Result{}; + } + + return ManagedAudioBufferList::create (ManagedAudioBufferList::numBuffersForStorageSize (size)); + } + else + { + return T{}; + } + }(); + + auto [ptr, size] = getDataPtrAndSize (result); + + if (size == 0) + return Result{}; + + if (auto status = AudioObjectGetPropertyData (objectID, &address, 0, nullptr, &size, ptr); status != noErr) + { + onError (status); + return Result{}; + } + + return Result { std::move (result) }; +} + +template +static bool audioObjectSetProperty (AudioObjectID objectID, + const AudioObjectPropertyAddress address, + const T value, + OnError&& onError = {}) +{ + if (! audioObjectHasProperty (objectID, address)) + return false; + + Boolean isSettable = NO; + if (auto status = AudioObjectIsPropertySettable (objectID, &address, &isSettable); status != noErr) + { + onError (status); + return false; + } + + if (! isSettable) + return false; + + if (auto status = AudioObjectSetPropertyData (objectID, &address, 0, nullptr, static_cast (sizeof (T)), &value); status != noErr) + { + onError (status); + return false; + } + + return true; +} + +template +[[nodiscard]] static std::vector audioObjectGetProperties (AudioObjectID objectID, + const AudioObjectPropertyAddress address, + OnError&& onError = {}) +{ + if (! audioObjectHasProperty (objectID, address)) + return {}; + + UInt32 size{}; + + if (auto status = AudioObjectGetPropertyDataSize (objectID, &address, 0, nullptr, &size); status != noErr) + { + onError (status); + return {}; + } + + // If this is hit, the number of results is not integral, and the following + // AudioObjectGetPropertyData will probably write past the end of the result buffer. + jassert ((size % sizeof (T)) == 0); + std::vector result (size / sizeof (T)); + + if (auto status = AudioObjectGetPropertyData (objectID, &address, 0, nullptr, &size, result.data()); status != noErr) + { + onError (status); + return {}; + } + + return result; +} + //============================================================================== struct AsyncRestarter { @@ -49,91 +233,36 @@ struct AsyncRestarter struct SystemVol { - SystemVol (AudioObjectPropertySelector selector) noexcept - : outputDeviceID (kAudioObjectUnknown) - { - addr.mScope = kAudioObjectPropertyScopeGlobal; - addr.mElement = juceAudioObjectPropertyElementMain; - addr.mSelector = kAudioHardwarePropertyDefaultOutputDevice; - - if (AudioObjectHasProperty (kAudioObjectSystemObject, &addr)) - { - UInt32 deviceIDSize = sizeof (outputDeviceID); - OSStatus status = AudioObjectGetPropertyData (kAudioObjectSystemObject, &addr, 0, nullptr, &deviceIDSize, &outputDeviceID); - - if (status == noErr) - { - addr.mElement = juceAudioObjectPropertyElementMain; - addr.mSelector = selector; - addr.mScope = kAudioDevicePropertyScopeOutput; - - if (! AudioObjectHasProperty (outputDeviceID, &addr)) - outputDeviceID = kAudioObjectUnknown; - } - } - } + explicit SystemVol (AudioObjectPropertySelector selector) noexcept + : outputDeviceID (audioObjectGetProperty (kAudioObjectSystemObject, { kAudioHardwarePropertyDefaultOutputDevice, + kAudioObjectPropertyScopeGlobal, + juceAudioObjectPropertyElementMain }).value_or (kAudioObjectUnknown)), + addr { selector, kAudioDevicePropertyScopeOutput, juceAudioObjectPropertyElementMain } + {} float getGain() const noexcept { - Float32 gain = 0; - - if (outputDeviceID != kAudioObjectUnknown) - { - UInt32 size = sizeof (gain); - AudioObjectGetPropertyData (outputDeviceID, &addr, 0, nullptr, &size, &gain); - } - - return (float) gain; + return audioObjectGetProperty (outputDeviceID, addr).value_or (0.0f); } bool setGain (float gain) const noexcept { - if (outputDeviceID != kAudioObjectUnknown && canSetVolume()) - { - Float32 newVolume = gain; - UInt32 size = sizeof (newVolume); - - return AudioObjectSetPropertyData (outputDeviceID, &addr, 0, nullptr, size, &newVolume) == noErr; - } - - return false; + return audioObjectSetProperty (outputDeviceID, addr, static_cast (gain)); } bool isMuted() const noexcept { - UInt32 muted = 0; - - if (outputDeviceID != kAudioObjectUnknown) - { - UInt32 size = sizeof (muted); - AudioObjectGetPropertyData (outputDeviceID, &addr, 0, nullptr, &size, &muted); - } - - return muted != 0; + return audioObjectGetProperty (outputDeviceID, addr).value_or (0) != 0; } bool setMuted (bool mute) const noexcept { - if (outputDeviceID != kAudioObjectUnknown && canSetVolume()) - { - UInt32 newMute = mute ? 1 : 0; - UInt32 size = sizeof (newMute); - - return AudioObjectSetPropertyData (outputDeviceID, &addr, 0, nullptr, size, &newMute) == noErr; - } - - return false; + return audioObjectSetProperty (outputDeviceID, addr, static_cast (mute ? 1 : 0)); } private: AudioDeviceID outputDeviceID; AudioObjectPropertyAddress addr; - - bool canSetVolume() const noexcept - { - Boolean isSettable = NO; - return AudioObjectIsPropertySettable (outputDeviceID, &addr, &isSettable) == noErr && isSettable; - } }; JUCE_END_IGNORE_WARNINGS_GCC_LIKE @@ -162,6 +291,12 @@ class CoreAudioIODevice; class CoreAudioInternal : private Timer, private AsyncUpdater { +private: + // members with deduced return types need to be defined before they + // are used, so define it here. decltype doesn't help as you can't + // capture anything in lambdas inside a decltype context. + auto err2log() const { return [this] (OSStatus err) { OK (err); }; } + public: CoreAudioInternal (CoreAudioIODevice& d, AudioDeviceID id, bool input, bool output) : owner (d), @@ -225,53 +360,43 @@ public: { StringArray newNames; int chanNum = 0; - UInt32 size; + const auto propertyScope = input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; - AudioObjectPropertyAddress pa; - pa.mSelector = kAudioDevicePropertyStreamConfiguration; - pa.mScope = input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; - pa.mElement = juceAudioObjectPropertyElementMain; - - if (OK (AudioObjectGetPropertyDataSize (deviceID, &pa, 0, nullptr, &size))) + if (auto bufList = audioObjectGetProperty (deviceID, { kAudioDevicePropertyStreamConfiguration, + propertyScope, + juceAudioObjectPropertyElementMain }, err2log())) { - HeapBlock bufList; - bufList.calloc (size, 1); + const int numStreams = (int) bufList->mNumberBuffers; - if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, bufList))) + for (int i = 0; i < numStreams; ++i) { - const int numStreams = (int) bufList->mNumberBuffers; + auto& b = bufList->mBuffers[i]; - for (int i = 0; i < numStreams; ++i) + for (unsigned int j = 0; j < b.mNumberChannels; ++j) { - auto& b = bufList->mBuffers[i]; + String name; - for (unsigned int j = 0; j < b.mNumberChannels; ++j) + const auto propertyElement = static_cast (chanNum + 1); + + if (auto nameNSString = audioObjectGetProperty (deviceID, { kAudioObjectPropertyElementName, + propertyScope, + propertyElement }).value_or (nullptr)) { - String name; - NSString* nameNSString = nil; - size = sizeof (nameNSString); - - pa.mSelector = kAudioObjectPropertyElementName; - pa.mElement = (AudioObjectPropertyElement) chanNum + 1; - - if (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &nameNSString) == noErr) - { - name = nsStringToJuce (nameNSString); - [nameNSString release]; - } - - if ((input ? activeInputChans : activeOutputChans) [chanNum]) - { - CallbackDetailsForChannel info = { i, (int) j, (int) b.mNumberChannels }; - newChannelInfo.add (info); - } - - if (name.isEmpty()) - name << (input ? "Input " : "Output ") << (chanNum + 1); - - newNames.add (name); - ++chanNum; + name = nsStringToJuce (nameNSString); + [nameNSString release]; } + + if ((input ? activeInputChans : activeOutputChans)[chanNum]) + { + CallbackDetailsForChannel info = { i, (int) j, (int) b.mNumberChannels }; + newChannelInfo.add (info); + } + + if (name.isEmpty()) + name << (input ? "Input " : "Output ") << (chanNum + 1); + + newNames.add (name); + ++chanNum; } } } @@ -283,28 +408,20 @@ public: { Array newSampleRates; - AudioObjectPropertyAddress pa; - pa.mScope = kAudioObjectPropertyScopeWildcard; - pa.mElement = juceAudioObjectPropertyElementMain; - pa.mSelector = kAudioDevicePropertyAvailableNominalSampleRates; - UInt32 size = 0; - - if (OK (AudioObjectGetPropertyDataSize (deviceID, &pa, 0, nullptr, &size))) + if (auto ranges = audioObjectGetProperties (deviceID, + { kAudioObjectPropertyScopeWildcard, + juceAudioObjectPropertyElementMain, + kAudioDevicePropertyAvailableNominalSampleRates }, + err2log()); ! ranges.empty()) { - HeapBlock ranges; - ranges.calloc (size, 1); - - if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, ranges))) + for (const auto rate : SampleRateHelpers::getAllSampleRates()) { - for (const auto rate : SampleRateHelpers::getAllSampleRates()) + for (auto range = ranges.rbegin(); range != ranges.rend(); ++range) { - for (int j = size / (int) sizeof (AudioValueRange); --j >= 0;) + if (range->mMinimum - 2 <= rate && rate <= range->mMaximum + 2) { - if (ranges[j].mMinimum - 2 <= rate && rate <= ranges[j].mMaximum + 2) - { - newSampleRates.add (rate); - break; - } + newSampleRates.add (rate); + break; } } } @@ -325,36 +442,27 @@ public: { Array newBufferSizes; - AudioObjectPropertyAddress pa; - pa.mScope = kAudioObjectPropertyScopeWildcard; - pa.mElement = juceAudioObjectPropertyElementMain; - pa.mSelector = kAudioDevicePropertyBufferFrameSizeRange; - UInt32 size = 0; - - if (OK (AudioObjectGetPropertyDataSize (deviceID, &pa, 0, nullptr, &size))) + if (auto ranges = audioObjectGetProperties (deviceID, { kAudioObjectPropertyScopeWildcard, + juceAudioObjectPropertyElementMain, + kAudioDevicePropertyBufferFrameSizeRange }, + err2log()); ! ranges.empty()) { - HeapBlock ranges; - ranges.calloc (size, 1); + newBufferSizes.add ((int) (ranges[0].mMinimum + 15) & ~15); - if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, ranges))) + for (int i = 32; i <= 2048; i += 32) { - newBufferSizes.add ((int) (ranges[0].mMinimum + 15) & ~15); - - for (int i = 32; i <= 2048; i += 32) + for (auto range = ranges.rbegin(); range != ranges.rend(); ++range) { - for (int j = size / (int) sizeof (AudioValueRange); --j >= 0;) + if (i >= range->mMinimum && i <= range->mMaximum) { - if (i >= ranges[j].mMinimum && i <= ranges[j].mMaximum) - { - newBufferSizes.addIfNotAlreadyThere (i); - break; - } + newBufferSizes.addIfNotAlreadyThere (i); + break; } } - - if (bufferSize > 0) - newBufferSizes.addIfNotAlreadyThere (bufferSize); } + + if (bufferSize > 0) + newBufferSizes.addIfNotAlreadyThere (bufferSize); } if (newBufferSizes.isEmpty() && bufferSize > 0) @@ -365,63 +473,38 @@ public: int getLatencyFromDevice (AudioObjectPropertyScope scope) const { - UInt32 latency = 0; - UInt32 size = sizeof (latency); - AudioObjectPropertyAddress pa; - pa.mElement = juceAudioObjectPropertyElementMain; - pa.mSelector = kAudioDevicePropertyLatency; - pa.mScope = scope; - AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &latency); + const auto latency = audioObjectGetProperty (deviceID, { kAudioDevicePropertyLatency, + scope, + juceAudioObjectPropertyElementMain }).value_or (0); - UInt32 safetyOffset = 0; - size = sizeof (safetyOffset); - pa.mSelector = kAudioDevicePropertySafetyOffset; - AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &safetyOffset); + const auto safetyOffset = audioObjectGetProperty (deviceID, { kAudioDevicePropertySafetyOffset, + scope, + juceAudioObjectPropertyElementMain }).value_or (0); - return (int) (latency + safetyOffset); + return static_cast (latency + safetyOffset); } int getBitDepthFromDevice (AudioObjectPropertyScope scope) const { - AudioObjectPropertyAddress pa; - pa.mElement = juceAudioObjectPropertyElementMain; - pa.mSelector = kAudioStreamPropertyPhysicalFormat; - pa.mScope = scope; - - AudioStreamBasicDescription asbd; - UInt32 size = sizeof (asbd); - - if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &asbd))) - return (int) asbd.mBitsPerChannel; - - return 0; + return static_cast (audioObjectGetProperty (deviceID, { kAudioStreamPropertyPhysicalFormat, + scope, + juceAudioObjectPropertyElementMain }, err2log()) + .value_or (AudioStreamBasicDescription{}).mBitsPerChannel); } int getFrameSizeFromDevice() const { - AudioObjectPropertyAddress pa; - pa.mScope = kAudioObjectPropertyScopeWildcard; - pa.mElement = juceAudioObjectPropertyElementMain; - pa.mSelector = kAudioDevicePropertyBufferFrameSize; - - UInt32 framesPerBuf = (UInt32) bufferSize; - UInt32 size = sizeof (framesPerBuf); - AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &framesPerBuf); - return (int) framesPerBuf; + return static_cast (audioObjectGetProperty (deviceID, { kAudioDevicePropertyBufferFrameSize, + kAudioObjectPropertyScopeWildcard, + juceAudioObjectPropertyElementMain }).value_or (0)); } bool isDeviceAlive() const { - AudioObjectPropertyAddress pa; - pa.mScope = kAudioObjectPropertyScopeWildcard; - pa.mElement = juceAudioObjectPropertyElementMain; - pa.mSelector = kAudioDevicePropertyDeviceIsAlive; - - UInt32 isAlive = 0; - UInt32 size = sizeof (isAlive); return deviceID != 0 - && OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &isAlive)) - && isAlive != 0; + && audioObjectGetProperty (deviceID, { kAudioDevicePropertyDeviceIsAlive, + kAudioObjectPropertyScopeWildcard, + juceAudioObjectPropertyElementMain }, err2log()).value_or (0) != 0; } bool updateDetailsFromDevice() @@ -512,15 +595,16 @@ public: StringArray getSources (bool input) { StringArray s; - HeapBlock types; - auto num = getAllDataSourcesForDevice (deviceID, types); + auto types = audioObjectGetProperties (deviceID, { kAudioDevicePropertyDataSources, + kAudioObjectPropertyScopeWildcard, + juceAudioObjectPropertyElementMain }); - for (int i = 0; i < num; ++i) + for (auto type : types) { AudioValueTranslation avt; char buffer[256]; - avt.mInputData = &(types[i]); + avt.mInputData = &type; avt.mInputDataSize = sizeof (UInt32); avt.mOutputData = buffer; avt.mOutputDataSize = 256; @@ -541,66 +625,51 @@ public: int getCurrentSourceIndex (bool input) const { - OSType currentSourceID = 0; - UInt32 size = sizeof (currentSourceID); - int result = -1; - - AudioObjectPropertyAddress pa; - pa.mSelector = kAudioDevicePropertyDataSource; - pa.mScope = input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; - pa.mElement = juceAudioObjectPropertyElementMain; + const auto scope = input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; if (deviceID != 0) { - if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, ¤tSourceID))) + if (auto currentSourceID = audioObjectGetProperty (deviceID, { kAudioDevicePropertyDataSource, + scope, + juceAudioObjectPropertyElementMain }, err2log())) { - HeapBlock types; - auto num = getAllDataSourcesForDevice (deviceID, types); + auto types = audioObjectGetProperties (deviceID, { kAudioDevicePropertyDataSources, + kAudioObjectPropertyScopeWildcard, + juceAudioObjectPropertyElementMain }); - for (int i = 0; i < num; ++i) - { - if (types[num] == currentSourceID) - { - result = i; - break; - } - } + if (auto it = std::find (types.begin(), types.end(), *currentSourceID); it != types.end()) + return static_cast (std::distance (types.begin(), it)); } } - return result; + return -1; } void setCurrentSourceIndex (int index, bool input) { if (deviceID != 0) { - HeapBlock types; - auto num = getAllDataSourcesForDevice (deviceID, types); + auto types = audioObjectGetProperties (deviceID, { kAudioDevicePropertyDataSources, + kAudioObjectPropertyScopeWildcard, + juceAudioObjectPropertyElementMain }); - if (isPositiveAndBelow (index, num)) + if (isPositiveAndBelow (index, static_cast (types.size()))) { - AudioObjectPropertyAddress pa; - pa.mSelector = kAudioDevicePropertyDataSource; - pa.mScope = input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; - pa.mElement = juceAudioObjectPropertyElementMain; - - OSType typeId = types[index]; - - OK (AudioObjectSetPropertyData (deviceID, &pa, 0, nullptr, sizeof (typeId), &typeId)); + const auto scope = input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; + audioObjectSetProperty (deviceID, { kAudioDevicePropertyDataSource, + scope, + juceAudioObjectPropertyElementMain }, + types[static_cast (index)], err2log()); } } } double getNominalSampleRate() const { - AudioObjectPropertyAddress pa; - pa.mSelector = kAudioDevicePropertyNominalSampleRate; - pa.mScope = kAudioObjectPropertyScopeGlobal; - pa.mElement = juceAudioObjectPropertyElementMain; - Float64 sr = 0; - UInt32 size = (UInt32) sizeof (sr); - return OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &sr)) ? (double) sr : 0.0; + return static_cast (audioObjectGetProperty (deviceID, { kAudioDevicePropertyNominalSampleRate, + kAudioObjectPropertyScopeGlobal, + juceAudioObjectPropertyElementMain }, + err2log()).value_or (0.0)); } bool setNominalSampleRate (double newSampleRate) const @@ -608,12 +677,10 @@ public: if (std::abs (getNominalSampleRate() - newSampleRate) < 1.0) return true; - AudioObjectPropertyAddress pa; - pa.mSelector = kAudioDevicePropertyNominalSampleRate; - pa.mScope = kAudioObjectPropertyScopeGlobal; - pa.mElement = juceAudioObjectPropertyElementMain; - Float64 sr = newSampleRate; - return OK (AudioObjectSetPropertyData (deviceID, &pa, 0, nullptr, sizeof (sr), &sr)); + return audioObjectSetProperty (deviceID, { kAudioDevicePropertyNominalSampleRate, + kAudioObjectPropertyScopeGlobal, + juceAudioObjectPropertyElementMain }, + static_cast (newSampleRate), err2log()); } //============================================================================== @@ -649,14 +716,10 @@ public: } else { - // change buffer size - AudioObjectPropertyAddress pa; - pa.mSelector = kAudioDevicePropertyBufferFrameSize; - pa.mScope = kAudioObjectPropertyScopeGlobal; - pa.mElement = juceAudioObjectPropertyElementMain; - UInt32 framesPerBuf = (UInt32) bufferSizeSamples; - - if (! OK (AudioObjectSetPropertyData (deviceID, &pa, 0, nullptr, sizeof (framesPerBuf), &framesPerBuf))) + if (! audioObjectSetProperty (deviceID, { kAudioDevicePropertyBufferFrameSize, + kAudioObjectPropertyScopeGlobal, + juceAudioObjectPropertyElementMain }, + static_cast (bufferSizeSamples), err2log())) { updateDetailsFromDevice(); error = "Couldn't change buffer size"; @@ -772,7 +835,7 @@ public: { for (int i = numInputChans; --i >= 0;) { - auto& info = inputChannelInfo.getReference(i); + auto& info = inputChannelInfo.getReference (i); auto dest = tempInputBuffers[i]; auto src = ((const float*) inInputData->mBuffers[info.streamNum].mData) + info.dataOffsetSamples; auto stride = info.dataStrideSamples; @@ -981,26 +1044,6 @@ private: } //============================================================================== - static int getAllDataSourcesForDevice (AudioDeviceID deviceID, HeapBlock& types) - { - AudioObjectPropertyAddress pa; - pa.mSelector = kAudioDevicePropertyDataSources; - pa.mScope = kAudioObjectPropertyScopeWildcard; - pa.mElement = juceAudioObjectPropertyElementMain; - UInt32 size = 0; - - if (deviceID != 0 - && AudioObjectGetPropertyDataSize (deviceID, &pa, 0, nullptr, &size) == noErr) - { - types.calloc (size, 1); - - if (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, types) == noErr) - return size / (int) sizeof (OSType); - } - - return 0; - } - bool OK (const OSStatus errorCode) const { if (errorCode == noErr) @@ -1084,7 +1127,7 @@ public: int best = 0; for (int i = 0; best < 512 && i < internal->bufferSizes.size(); ++i) - best = internal->bufferSizes.getUnchecked(i); + best = internal->bufferSizes.getUnchecked (i); if (best == 0) best = 512; @@ -1418,7 +1461,7 @@ public: auto rates = getAvailableSampleRates(); for (int i = 0; i < rates.size() && sampleRate < 44100.0; ++i) - sampleRate = rates.getUnchecked(i); + sampleRate = rates.getUnchecked (i); } currentSampleRate = sampleRate; @@ -2185,46 +2228,30 @@ public: inputIds.clear(); outputIds.clear(); - UInt32 size; + auto audioDevices = audioObjectGetProperties (kAudioObjectSystemObject, { kAudioHardwarePropertyDevices, + kAudioObjectPropertyScopeWildcard, + juceAudioObjectPropertyElementMain }); - AudioObjectPropertyAddress pa; - pa.mSelector = kAudioHardwarePropertyDevices; - pa.mScope = kAudioObjectPropertyScopeWildcard; - pa.mElement = juceAudioObjectPropertyElementMain; - - if (AudioObjectGetPropertyDataSize (kAudioObjectSystemObject, &pa, 0, nullptr, &size) == noErr) + for (auto audioDevice : audioDevices) { - HeapBlock devs; - devs.calloc (size, 1); - - if (AudioObjectGetPropertyData (kAudioObjectSystemObject, &pa, 0, nullptr, &size, devs) == noErr) + if (auto name = audioObjectGetProperties (audioDevice, { kAudioDevicePropertyDeviceName, + kAudioObjectPropertyScopeWildcard, + juceAudioObjectPropertyElementMain }); ! name.empty()) { - auto num = (int) size / (int) sizeof (AudioDeviceID); + auto nameString = String::fromUTF8 (name.data(), (int) strlen (name.data())); + auto numIns = getNumChannels (audioDevice, true); + auto numOuts = getNumChannels (audioDevice, false); - for (int i = 0; i < num; ++i) + if (numIns > 0) { - char name[1024]; - size = sizeof (name); - pa.mSelector = kAudioDevicePropertyDeviceName; + inputDeviceNames.add (nameString); + inputIds.add (audioDevice); + } - if (AudioObjectGetPropertyData (devs[i], &pa, 0, nullptr, &size, name) == noErr) - { - auto nameString = String::fromUTF8 (name, (int) strlen (name)); - auto numIns = getNumChannels (devs[i], true); - auto numOuts = getNumChannels (devs[i], false); - - if (numIns > 0) - { - inputDeviceNames.add (nameString); - inputIds.add (devs[i]); - } - - if (numOuts > 0) - { - outputDeviceNames.add (nameString); - outputIds.add (devs[i]); - } - } + if (numOuts > 0) + { + outputDeviceNames.add (nameString); + outputIds.add (audioDevice); } } } @@ -2245,32 +2272,23 @@ public: { jassert (hasScanned); // need to call scanForDevices() before doing this - AudioDeviceID deviceID; - UInt32 size = sizeof (deviceID); - // if they're asking for any input channels at all, use the default input, so we // get the built-in mic rather than the built-in output with no inputs.. AudioObjectPropertyAddress pa; - pa.mSelector = forInput ? kAudioHardwarePropertyDefaultInputDevice - : kAudioHardwarePropertyDefaultOutputDevice; + auto selector = forInput ? kAudioHardwarePropertyDefaultInputDevice + : kAudioHardwarePropertyDefaultOutputDevice; pa.mScope = kAudioObjectPropertyScopeWildcard; pa.mElement = juceAudioObjectPropertyElementMain; - if (AudioObjectGetPropertyData (kAudioObjectSystemObject, &pa, 0, nullptr, &size, &deviceID) == noErr) + if (auto deviceID = audioObjectGetProperty (kAudioObjectSystemObject, { selector, + kAudioObjectPropertyScopeWildcard, + juceAudioObjectPropertyElementMain })) { - if (forInput) - { - for (int i = inputIds.size(); --i >= 0;) - if (inputIds[i] == deviceID) - return i; - } - else - { - for (int i = outputIds.size(); --i >= 0;) - if (outputIds[i] == deviceID) - return i; - } + auto& ids = forInput ? inputIds : outputIds; + + if (auto it = std::find (ids.begin(), ids.end(), deviceID); it != ids.end()) + return static_cast (std::distance (ids.begin(), it)); } return 0; @@ -2356,25 +2374,16 @@ private: static int getNumChannels (AudioDeviceID deviceID, bool input) { int total = 0; - UInt32 size; + auto scope = input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; - AudioObjectPropertyAddress pa; - pa.mSelector = kAudioDevicePropertyStreamConfiguration; - pa.mScope = input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; - pa.mElement = juceAudioObjectPropertyElementMain; - - if (AudioObjectGetPropertyDataSize (deviceID, &pa, 0, nullptr, &size) == noErr) + if (auto bufList = audioObjectGetProperty (deviceID, { kAudioDevicePropertyStreamConfiguration, + scope, + juceAudioObjectPropertyElementMain })) { - HeapBlock bufList; - bufList.calloc (size, 1); + auto numStreams = (int) bufList->mNumberBuffers; - if (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, bufList) == noErr) - { - auto numStreams = (int) bufList->mNumberBuffers; - - for (int i = 0; i < numStreams; ++i) - total += bufList->mBuffers[i].mNumberChannels; - } + for (int i = 0; i < numStreams; ++i) + total += bufList->mBuffers[i].mNumberChannels; } return total;