Commit 4be80982 authored by Christian Kröner's avatar Christian Kröner

Fix sample rate sync on Mac output for low rates

parent 4d7f1f0c
...@@ -300,7 +300,7 @@ osx_output_set_channel_map(OSXOutput *oo) ...@@ -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,19 +325,26 @@ osx_output_sync_device_sample_rate(AudioDeviceID dev_id, AudioStreamBasicDescrip ...@@ -325,19 +325,26 @@ 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.
Float64 sample_rate = sample_rate_max;
Float64 rate;
if(requested_rate >= sample_rate_min) {
for (int f = 4; f > 0; f--) { for (int f = 4; f > 0; f--) {
Float64 rate = desc.mSampleRate / f; rate = requested_rate / f;
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
if (ranges[i].mMinimum <= rate if (ranges[i].mMinimum <= rate
&& rate <= ranges[i].mMaximum) { && rate <= ranges[i].mMaximum) {
...@@ -346,19 +353,42 @@ osx_output_sync_device_sample_rate(AudioDeviceID dev_id, AudioStreamBasicDescrip ...@@ -346,19 +353,42 @@ osx_output_sync_device_sample_rate(AudioDeviceID dev_id, AudioStreamBasicDescrip
} }
} }
} }
}
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) ...@@ -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) ...@@ -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) ...@@ -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;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment