Browse Source

Workaround for CoreAudio devices that don't change sample rate correctly in 10.6; improvement to WASAPI error reporting

tags/2021-05-28
Julian Storer 15 years ago
parent
commit
b171ae8400
3 changed files with 125 additions and 175 deletions
  1. +62
    -87
      juce_amalgamated.cpp
  2. +57
    -87
      src/native/mac/juce_mac_CoreAudio.cpp
  3. +6
    -1
      src/native/windows/juce_win32_WASAPI.cpp

+ 62
- 87
juce_amalgamated.cpp View File

@@ -246871,7 +246871,6 @@ public:
defaultBufferSize = jmax (inputDevice->defaultBufferSize, outputDevice->defaultBufferSize);
sampleRates = inputDevice->rates;
sampleRates.removeValuesNotIn (outputDevice->rates);
jassert (sampleRates.size() > 0); // in and out devices don't share any common sample rates!
}
else
{
@@ -246945,6 +246944,12 @@ public:
close();
lastError = String::empty;

if (sampleRates.size() == 0 && inputDevice != 0 && outputDevice != 0)
{
lastError = "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;

@@ -263162,6 +263167,8 @@ public:
isSlaveDevice (false),
deviceID (id),
started (false),
sampleRate (0),
bufferSize (512),
audioBuffer (0),
numInputChans (0),
numOutputChans (0),
@@ -263173,24 +263180,16 @@ public:
inputChannelInfo (0),
outputChannelInfo (0)
{
sampleRate = 0;
bufferSize = 512;
jassert (deviceID != 0);

if (deviceID == 0)
{
error = TRANS("can't open device");
}
else
{
updateDetailsFromDevice();
updateDetailsFromDevice();

AudioObjectPropertyAddress pa;
pa.mSelector = kAudioObjectPropertySelectorWildcard;
pa.mScope = kAudioObjectPropertyScopeWildcard;
pa.mElement = kAudioObjectPropertyElementWildcard;
AudioObjectPropertyAddress pa;
pa.mSelector = kAudioObjectPropertySelectorWildcard;
pa.mScope = kAudioObjectPropertyScopeWildcard;
pa.mElement = kAudioObjectPropertyElementWildcard;

AudioObjectAddPropertyListener (deviceID, &pa, deviceListenerProc, this);
}
AudioObjectAddPropertyListener (deviceID, &pa, deviceListenerProc, this);
}

~CoreAudioInternal()
@@ -263203,13 +263202,13 @@ public:
AudioObjectRemovePropertyListener (deviceID, &pa, deviceListenerProc, this);

stop (false);
delete inputDevice;

juce_free (audioBuffer);
juce_free (tempInputBuffers);
juce_free (tempOutputBuffers);
juce_free (inputChannelInfo);
juce_free (outputChannelInfo);
delete inputDevice;
}

void allocateTempBuffers()
@@ -263551,7 +263550,7 @@ public:
double newSampleRate,
int bufferSizeSamples)
{
error = String::empty;
String error;
log ("CoreAudio reopen");
callbacksAllowed = false;
stopTimer();
@@ -263559,12 +263558,11 @@ public:
stop (false);

activeInputChans = inputChannels;
activeOutputChans = outputChannels;

activeInputChans.setRange (inChanNames.size(),
activeInputChans.getHighestBit() + 1 - inChanNames.size(),
false);

activeOutputChans = outputChannels;
activeOutputChans.setRange (outChanNames.size(),
activeOutputChans.getHighestBit() + 1 - outChanNames.size(),
false);
@@ -263573,52 +263571,49 @@ public:
numOutputChans = activeOutputChans.countNumberOfSetBits();

// set sample rate
Float64 sr = newSampleRate;
UInt32 size = sizeof (sr);

AudioObjectPropertyAddress pa;
pa.mSelector = kAudioDevicePropertyNominalSampleRate;
pa.mScope = kAudioObjectPropertyScopeWildcard;
pa.mElement = kAudioObjectPropertyElementMaster;
Float64 sr = newSampleRate;

OK (AudioObjectSetPropertyData (deviceID, &pa, 0, 0, size, &sr));

// change buffer size
UInt32 framesPerBuf = bufferSizeSamples;
size = sizeof (framesPerBuf);

pa.mSelector = kAudioDevicePropertyBufferFrameSize;
OK (AudioObjectSetPropertyData (deviceID, &pa, 0, 0, size, &framesPerBuf));

// wait for the changes to happen (on some devices)
int i = 30;
while (--i >= 0)
if (! OK (AudioObjectSetPropertyData (deviceID, &pa, 0, 0, sizeof (sr), &sr)))
{
updateDetailsFromDevice();

if (sampleRate == newSampleRate && bufferSizeSamples == bufferSize)
break;

Thread::sleep (100);
error = "Couldn't change sample rate";
}
else
{
// change buffer size
UInt32 framesPerBuf = bufferSizeSamples;
pa.mSelector = kAudioDevicePropertyBufferFrameSize;

if (i < 0)
error = "Couldn't change sample rate/buffer size";

if (sampleRates.size() == 0)
error = "Device has no available sample-rates";

if (bufferSizes.size() == 0)
error = "Device has no available buffer-sizes";
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 (inputDevice != 0 && error.isEmpty())
error = inputDevice->reopen (inputChannels,
outputChannels,
newSampleRate,
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;
}

@@ -263869,16 +263864,13 @@ public:
{
result = new CoreAudioInternal (devs[i]);

if (result->error.isEmpty())
{
const bool thisIsInput = inChanNames.size() > 0 && outChanNames.size() == 0;
const bool otherIsInput = result->inChanNames.size() > 0 && result->outChanNames.size() == 0;
const bool thisIsInput = inChanNames.size() > 0 && outChanNames.size() == 0;
const bool otherIsInput = result->inChanNames.size() > 0 && result->outChanNames.size() == 0;

if (thisIsInput != otherIsInput
|| (inChanNames.size() + outChanNames.size() == 0)
|| (result->inChanNames.size() + result->outChanNames.size()) == 0)
break;
}
if (thisIsInput != otherIsInput
|| (inChanNames.size() + outChanNames.size() == 0)
|| (result->inChanNames.size() + result->outChanNames.size()) == 0)
break;

deleteAndZero (result);
}
@@ -263893,7 +263885,6 @@ public:

juce_UseDebuggingNewOperator

String error;
int inputLatency, outputLatency;
BitArray activeInputChans, activeOutputChans;
StringArray inChanNames, outChanNames;
@@ -264024,34 +264015,17 @@ public:
jassert (inputDeviceId != 0);

device = new CoreAudioInternal (inputDeviceId);
lastError = device->error;

if (lastError.isNotEmpty())
deleteAndZero (device);
}
else
{
device = new CoreAudioInternal (outputDeviceId);
lastError = device->error;

if (lastError.isNotEmpty())
{
deleteAndZero (device);
}
else if (inputDeviceId != 0)
if (inputDeviceId != 0)
{
CoreAudioInternal* secondDevice = new CoreAudioInternal (inputDeviceId);
lastError = device->error;

if (lastError.isNotEmpty())
{
delete secondDevice;
}
else
{
device->inputDevice = secondDevice;
secondDevice->isSlaveDevice = true;
}
device->inputDevice = secondDevice;
secondDevice->isSlaveDevice = true;
}
}

@@ -264129,14 +264103,15 @@ public:
if (bufferSizeSamples <= 0)
bufferSizeSamples = getDefaultBufferSize();

internal->reopen (inputChannels, outputChannels, sampleRate, bufferSizeSamples);
lastError = internal->error;
lastError = internal->reopen (inputChannels, outputChannels, sampleRate, bufferSizeSamples);
isOpen_ = lastError.isEmpty();
return lastError;
}

void close()
{
isOpen_ = false;
internal->stop (false);
}

bool isOpen()


+ 57
- 87
src/native/mac/juce_mac_CoreAudio.cpp View File

@@ -75,6 +75,8 @@ public:
isSlaveDevice (false),
deviceID (id),
started (false),
sampleRate (0),
bufferSize (512),
audioBuffer (0),
numInputChans (0),
numOutputChans (0),
@@ -86,24 +88,16 @@ public:
inputChannelInfo (0),
outputChannelInfo (0)
{
sampleRate = 0;
bufferSize = 512;
jassert (deviceID != 0);

if (deviceID == 0)
{
error = TRANS("can't open device");
}
else
{
updateDetailsFromDevice();
updateDetailsFromDevice();

AudioObjectPropertyAddress pa;
pa.mSelector = kAudioObjectPropertySelectorWildcard;
pa.mScope = kAudioObjectPropertyScopeWildcard;
pa.mElement = kAudioObjectPropertyElementWildcard;
AudioObjectPropertyAddress pa;
pa.mSelector = kAudioObjectPropertySelectorWildcard;
pa.mScope = kAudioObjectPropertyScopeWildcard;
pa.mElement = kAudioObjectPropertyElementWildcard;

AudioObjectAddPropertyListener (deviceID, &pa, deviceListenerProc, this);
}
AudioObjectAddPropertyListener (deviceID, &pa, deviceListenerProc, this);
}

~CoreAudioInternal()
@@ -116,13 +110,13 @@ public:
AudioObjectRemovePropertyListener (deviceID, &pa, deviceListenerProc, this);

stop (false);
delete inputDevice;

juce_free (audioBuffer);
juce_free (tempInputBuffers);
juce_free (tempOutputBuffers);
juce_free (inputChannelInfo);
juce_free (outputChannelInfo);
delete inputDevice;
}

void allocateTempBuffers()
@@ -466,7 +460,7 @@ public:
double newSampleRate,
int bufferSizeSamples)
{
error = String::empty;
String error;
log ("CoreAudio reopen");
callbacksAllowed = false;
stopTimer();
@@ -474,12 +468,11 @@ public:
stop (false);

activeInputChans = inputChannels;
activeOutputChans = outputChannels;

activeInputChans.setRange (inChanNames.size(),
activeInputChans.getHighestBit() + 1 - inChanNames.size(),
false);

activeOutputChans = outputChannels;
activeOutputChans.setRange (outChanNames.size(),
activeOutputChans.getHighestBit() + 1 - outChanNames.size(),
false);
@@ -488,52 +481,49 @@ public:
numOutputChans = activeOutputChans.countNumberOfSetBits();

// set sample rate
Float64 sr = newSampleRate;
UInt32 size = sizeof (sr);

AudioObjectPropertyAddress pa;
pa.mSelector = kAudioDevicePropertyNominalSampleRate;
pa.mScope = kAudioObjectPropertyScopeWildcard;
pa.mElement = kAudioObjectPropertyElementMaster;
Float64 sr = newSampleRate;

OK (AudioObjectSetPropertyData (deviceID, &pa, 0, 0, size, &sr));

// change buffer size
UInt32 framesPerBuf = bufferSizeSamples;
size = sizeof (framesPerBuf);

pa.mSelector = kAudioDevicePropertyBufferFrameSize;
OK (AudioObjectSetPropertyData (deviceID, &pa, 0, 0, size, &framesPerBuf));

// wait for the changes to happen (on some devices)
int i = 30;
while (--i >= 0)
if (! OK (AudioObjectSetPropertyData (deviceID, &pa, 0, 0, sizeof (sr), &sr)))
{
updateDetailsFromDevice();

if (sampleRate == newSampleRate && bufferSizeSamples == bufferSize)
break;

Thread::sleep (100);
error = "Couldn't change sample rate";
}
else
{
// change buffer size
UInt32 framesPerBuf = bufferSizeSamples;
pa.mSelector = kAudioDevicePropertyBufferFrameSize;

if (i < 0)
error = "Couldn't change sample rate/buffer size";

if (sampleRates.size() == 0)
error = "Device has no available sample-rates";

if (bufferSizes.size() == 0)
error = "Device has no available buffer-sizes";

if (inputDevice != 0 && error.isEmpty())
error = inputDevice->reopen (inputChannels,
outputChannels,
newSampleRate,
bufferSizeSamples);
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;
}

@@ -784,16 +774,13 @@ public:
{
result = new CoreAudioInternal (devs[i]);

if (result->error.isEmpty())
{
const bool thisIsInput = inChanNames.size() > 0 && outChanNames.size() == 0;
const bool otherIsInput = result->inChanNames.size() > 0 && result->outChanNames.size() == 0;
const bool thisIsInput = inChanNames.size() > 0 && outChanNames.size() == 0;
const bool otherIsInput = result->inChanNames.size() > 0 && result->outChanNames.size() == 0;

if (thisIsInput != otherIsInput
|| (inChanNames.size() + outChanNames.size() == 0)
|| (result->inChanNames.size() + result->outChanNames.size()) == 0)
break;
}
if (thisIsInput != otherIsInput
|| (inChanNames.size() + outChanNames.size() == 0)
|| (result->inChanNames.size() + result->outChanNames.size()) == 0)
break;

deleteAndZero (result);
}
@@ -809,7 +796,6 @@ public:
//==============================================================================
juce_UseDebuggingNewOperator

String error;
int inputLatency, outputLatency;
BitArray activeInputChans, activeOutputChans;
StringArray inChanNames, outChanNames;
@@ -944,34 +930,17 @@ public:
jassert (inputDeviceId != 0);

device = new CoreAudioInternal (inputDeviceId);
lastError = device->error;

if (lastError.isNotEmpty())
deleteAndZero (device);
}
else
{
device = new CoreAudioInternal (outputDeviceId);
lastError = device->error;

if (lastError.isNotEmpty())
{
deleteAndZero (device);
}
else if (inputDeviceId != 0)
if (inputDeviceId != 0)
{
CoreAudioInternal* secondDevice = new CoreAudioInternal (inputDeviceId);
lastError = device->error;

if (lastError.isNotEmpty())
{
delete secondDevice;
}
else
{
device->inputDevice = secondDevice;
secondDevice->isSlaveDevice = true;
}
device->inputDevice = secondDevice;
secondDevice->isSlaveDevice = true;
}
}

@@ -1049,14 +1018,15 @@ public:
if (bufferSizeSamples <= 0)
bufferSizeSamples = getDefaultBufferSize();

internal->reopen (inputChannels, outputChannels, sampleRate, bufferSizeSamples);
lastError = internal->error;
lastError = internal->reopen (inputChannels, outputChannels, sampleRate, bufferSizeSamples);
isOpen_ = lastError.isEmpty();
return lastError;
}

void close()
{
isOpen_ = false;
internal->stop (false);
}

bool isOpen()


+ 6
- 1
src/native/windows/juce_win32_WASAPI.cpp View File

@@ -599,7 +599,6 @@ public:
defaultBufferSize = jmax (inputDevice->defaultBufferSize, outputDevice->defaultBufferSize);
sampleRates = inputDevice->rates;
sampleRates.removeValuesNotIn (outputDevice->rates);
jassert (sampleRates.size() > 0); // in and out devices don't share any common sample rates!
}
else
{
@@ -674,6 +673,12 @@ public:
close();
lastError = String::empty;
if (sampleRates.size() == 0 && inputDevice != 0 && outputDevice != 0)
{
lastError = "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;


Loading…
Cancel
Save