Fix sample rate sync on Mac output for low rates

This commit is contained in:
Christian Kröner 2018-03-07 21:32:21 +01:00
parent 4d7f1f0c35
commit 4be80982a4
1 changed files with 53 additions and 23 deletions

View File

@ -300,7 +300,7 @@ osx_output_set_channel_map(OSXOutput *oo)
} }
static Float64 static Float64
osx_output_sync_device_sample_rate(AudioDeviceID dev_id, AudioStreamBasicDescription desc) osx_output_sync_device_sample_rate(AudioDeviceID dev_id, Float64 requested_rate)
{ {
FormatDebug(osx_output_domain, "Syncing sample rate."); FormatDebug(osx_output_domain, "Syncing sample rate.");
AudioObjectPropertyAddress aopa = { AudioObjectPropertyAddress aopa = {
@ -325,40 +325,70 @@ osx_output_sync_device_sample_rate(AudioDeviceID dev_id, AudioStreamBasicDescrip
NULL, NULL,
&property_size, &property_size,
&ranges); &ranges);
// Get the maximum sample rate as fallback.
Float64 sample_rate = .0; // Get the maximum and minimum sample rates as fallback.
Float64 sample_rate_min = ranges[0].mMinimum;
Float64 sample_rate_max = ranges[0].mMaximum;
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
if (ranges[i].mMaximum > sample_rate) if (ranges[i].mMaximum > sample_rate_max)
sample_rate = ranges[i].mMaximum; sample_rate_max = ranges[i].mMaximum;
if( ranges[i].mMinimum < sample_rate_min)
sample_rate_min = ranges[i].mMinimum;
} }
// Now try to see if the device support our format sample rate. // Now try to see if the device support our format sample rate.
// For some high quality media samples, the frame rate may exceed // For some media samples, the frame rate may exceed device
// device capability. In this case, we let CoreAudio downsample // capability. In this case, we downsample or upsample
// by decimation with an integer factor ranging from 1 to 4. // with an integer factor ranging from 1 to 4.
for (int f = 4; f > 0; f--) { Float64 sample_rate = sample_rate_max;
Float64 rate = desc.mSampleRate / f; Float64 rate;
for (int i = 0; i < count; i++) { if(requested_rate >= sample_rate_min) {
if (ranges[i].mMinimum <= rate for (int f = 4; f > 0; f--) {
&& rate <= ranges[i].mMaximum) { rate = requested_rate / f;
sample_rate = rate; for (int i = 0; i < count; i++) {
break; if (ranges[i].mMinimum <= rate
&& rate <= ranges[i].mMaximum) {
sample_rate = rate;
break;
}
}
}
}
else {
sample_rate = sample_rate_min;
for (int f = 4; f > 1; f--) {
rate = requested_rate * f;
for (int i = 0; i < count; i++) {
if (ranges[i].mMinimum <= rate
&& rate <= ranges[i].mMaximum) {
sample_rate = rate;
break;
}
} }
} }
} }
aopa.mSelector = kAudioDevicePropertyNominalSampleRate, aopa.mSelector = kAudioDevicePropertyNominalSampleRate,
property_size = sizeof(sample_rate);
err = AudioObjectSetPropertyData(dev_id, err = AudioObjectSetPropertyData(dev_id,
&aopa, &aopa,
0, 0,
NULL, NULL,
sizeof(&desc.mSampleRate), property_size,
&sample_rate); &sample_rate);
if (err != noErr) { if (err != noErr) {
FormatWarning(osx_output_domain, FormatWarning(osx_output_domain,
"Failed to synchronize the sample rate: %d", "Failed to synchronize the sample rate: %d",
err); err);
// Something went wrong with synchronization, get current device sample_rate and return that
err = AudioObjectGetPropertyData(dev_id,
&aopa,
0,
NULL,
&property_size,
&sample_rate);
if(err != noErr)
throw std::runtime_error("Cannot get sample rate of macOS output device");
} else { } else {
FormatDebug(osx_output_domain, FormatDebug(osx_output_domain,
"Sample rate synced to %f Hz.", "Sample rate synced to %f Hz.",
@ -703,7 +733,7 @@ OSXOutput::Open(AudioFormat &audio_format)
|| params.dop // sample rate needs to be synchronized for DoP || params.dop // sample rate needs to be synchronized for DoP
#endif #endif
) )
sample_rate = osx_output_sync_device_sample_rate(dev_id, asbd); sample_rate = osx_output_sync_device_sample_rate(dev_id, asbd.mSampleRate);
#ifdef ENABLE_DSD #ifdef ENABLE_DSD
if(params.dop && (sample_rate != asbd.mSampleRate)) { // fall back to PCM in case sample_rate cannot be synchronized if(params.dop && (sample_rate != asbd.mSampleRate)) { // fall back to PCM in case sample_rate cannot be synchronized
@ -735,7 +765,7 @@ OSXOutput::Open(AudioFormat &audio_format)
&callback, sizeof(callback)); &callback, sizeof(callback));
if (status != noErr) { if (status != noErr) {
AudioComponentInstanceDispose(au); AudioComponentInstanceDispose(au);
throw std::runtime_error("unable to set callback for OS X audio unit"); throw std::runtime_error("Unable to set callback for OS X audio unit");
} }
status = AudioUnitInitialize(au); status = AudioUnitInitialize(au);
@ -762,7 +792,7 @@ OSXOutput::Open(AudioFormat &audio_format)
if (status != 0) { if (status != 0) {
AudioUnitUninitialize(au); AudioUnitUninitialize(au);
osx_os_status_to_cstring(status, errormsg, sizeof(errormsg)); osx_os_status_to_cstring(status, errormsg, sizeof(errormsg));
throw FormatRuntimeError("unable to start audio output: %s", throw FormatRuntimeError("Unable to start audio output: %s",
errormsg); errormsg);
} }
pause = false; pause = false;