bool OK (const OSStatus errorCode) const { if (errorCode == noErr) return true; const String errorMessage ("CoreAudio error: " + String::toHexString ((int) errorCode)); JUCE_COREAUDIOLOG (errorMessage); if (callback != nullptr) callback->audioDeviceError (errorMessage); return false; }
void timerCallback() { stopTimer(); JUCE_COREAUDIOLOG ("CoreAudio device changed callback"); const double oldSampleRate = sampleRate; const int oldBufferSize = bufferSize; updateDetailsFromDevice(); if (oldBufferSize != bufferSize || oldSampleRate != sampleRate) { callbacksAllowed = false; stop (false); updateDetailsFromDevice(); callbacksAllowed = true; } }
//============================================================================== String reopen (const BigInteger& inputChannels, const BigInteger& outputChannels, double newSampleRate, int bufferSizeSamples) { String error; JUCE_COREAUDIOLOG ("CoreAudio reopen"); callbacksAllowed = false; stopTimer(); stop (false); activeInputChans = inputChannels; activeInputChans.setRange (inChanNames.size(), activeInputChans.getHighestBit() + 1 - inChanNames.size(), false); activeOutputChans = outputChannels; activeOutputChans.setRange (outChanNames.size(), activeOutputChans.getHighestBit() + 1 - outChanNames.size(), false); numInputChans = activeInputChans.countNumberOfSetBits(); numOutputChans = activeOutputChans.countNumberOfSetBits(); // set sample rate AudioObjectPropertyAddress pa; pa.mSelector = kAudioDevicePropertyNominalSampleRate; pa.mScope = kAudioObjectPropertyScopeWildcard; pa.mElement = kAudioObjectPropertyElementMaster; Float64 sr = newSampleRate; if (! OK (AudioObjectSetPropertyData (deviceID, &pa, 0, 0, sizeof (sr), &sr))) { error = "Couldn't change sample rate"; } else { // change buffer size UInt32 framesPerBuf = (UInt32) bufferSizeSamples; pa.mSelector = kAudioDevicePropertyBufferFrameSize; if (! OK (AudioObjectSetPropertyData (deviceID, &pa, 0, 0, sizeof (framesPerBuf), &framesPerBuf))) { error = "Couldn't change buffer size"; } else { // Annoyingly, after changing the rate and buffer size, some devices fail to // correctly report their new settings until some random time in the future, so // after calling updateDetailsFromDevice, we need to manually bodge these values // to make sure we're using the correct numbers.. updateDetailsFromDevice(); sampleRate = newSampleRate; bufferSize = bufferSizeSamples; if (sampleRates.size() == 0) error = "Device has no available sample-rates"; else if (bufferSizes.size() == 0) error = "Device has no available buffer-sizes"; else if (inputDevice != 0) error = inputDevice->reopen (inputChannels, outputChannels, newSampleRate, bufferSizeSamples); } } callbacksAllowed = true; return error; }
void updateDetailsFromDevice() { stopTimer(); if (deviceID == 0) return; const ScopedLock sl (callbackLock); AudioObjectPropertyAddress pa; pa.mScope = kAudioObjectPropertyScopeWildcard; pa.mElement = kAudioObjectPropertyElementMaster; UInt32 isAlive; UInt32 size = sizeof (isAlive); pa.mSelector = kAudioDevicePropertyDeviceIsAlive; if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, 0, &size, &isAlive)) && isAlive == 0) return; Float64 sr; size = sizeof (sr); pa.mSelector = kAudioDevicePropertyNominalSampleRate; if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, 0, &size, &sr))) sampleRate = sr; UInt32 framesPerBuf; size = sizeof (framesPerBuf); pa.mSelector = kAudioDevicePropertyBufferFrameSize; if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, 0, &size, &framesPerBuf))) { bufferSize = (int) framesPerBuf; allocateTempBuffers(); } bufferSizes.clear(); pa.mSelector = kAudioDevicePropertyBufferFrameSizeRange; if (OK (AudioObjectGetPropertyDataSize (deviceID, &pa, 0, 0, &size))) { HeapBlock <AudioValueRange> ranges; ranges.calloc (size, 1); if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, 0, &size, ranges))) { bufferSizes.add ((int) (ranges[0].mMinimum + 15) & ~15); for (int i = 32; i < 2048; i += 32) { for (int j = size / (int) sizeof (AudioValueRange); --j >= 0;) { if (i >= ranges[j].mMinimum && i <= ranges[j].mMaximum) { bufferSizes.addIfNotAlreadyThere (i); break; } } } if (bufferSize > 0) bufferSizes.addIfNotAlreadyThere (bufferSize); } } if (bufferSizes.size() == 0 && bufferSize > 0) bufferSizes.add (bufferSize); sampleRates.clear(); const double possibleRates[] = { 44100.0, 48000.0, 88200.0, 96000.0, 176400.0, 192000.0 }; String rates; pa.mSelector = kAudioDevicePropertyAvailableNominalSampleRates; if (OK (AudioObjectGetPropertyDataSize (deviceID, &pa, 0, 0, &size))) { HeapBlock <AudioValueRange> ranges; ranges.calloc (size, 1); if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, 0, &size, ranges))) { for (int i = 0; i < numElementsInArray (possibleRates); ++i) { bool ok = false; for (int j = size / (int) sizeof (AudioValueRange); --j >= 0;) if (possibleRates[i] >= ranges[j].mMinimum - 2 && possibleRates[i] <= ranges[j].mMaximum + 2) ok = true; if (ok) { sampleRates.add (possibleRates[i]); rates << possibleRates[i] << ' '; } } } } if (sampleRates.size() == 0 && sampleRate > 0) { sampleRates.add (sampleRate); rates << sampleRate; } JUCE_COREAUDIOLOG ("sr: " + rates); inputLatency = 0; outputLatency = 0; UInt32 lat; size = sizeof (lat); pa.mSelector = kAudioDevicePropertyLatency; pa.mScope = kAudioDevicePropertyScopeInput; if (AudioObjectGetPropertyData (deviceID, &pa, 0, 0, &size, &lat) == noErr) inputLatency = (int) lat; pa.mScope = kAudioDevicePropertyScopeOutput; size = sizeof (lat); if (AudioObjectGetPropertyData (deviceID, &pa, 0, 0, &size, &lat) == noErr) outputLatency = (int) lat; JUCE_COREAUDIOLOG ("lat: " + String (inputLatency) + " " + String (outputLatency)); inChanNames.clear(); outChanNames.clear(); inputChannelInfo.calloc ((size_t) numInputChans + 2); numInputChannelInfos = 0; outputChannelInfo.calloc ((size_t) numOutputChans + 2); numOutputChannelInfos = 0; fillInChannelInfo (true); fillInChannelInfo (false); }