Fix sample rate sync on Mac output for low rates
This commit is contained in:
parent
4d7f1f0c35
commit
4be80982a4
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue