diff --git a/src/output/plugins/OSXOutputPlugin.cxx b/src/output/plugins/OSXOutputPlugin.cxx index db7a98e28..775e3f993 100644 --- a/src/output/plugins/OSXOutputPlugin.cxx +++ b/src/output/plugins/OSXOutputPlugin.cxx @@ -45,18 +45,19 @@ static constexpr unsigned MPD_OSX_BUFFER_TIME_MS = 100; static StringBuffer<64> -StreamDescriptionToString(const AudioStreamBasicDescription desc) { +StreamDescriptionToString(const AudioStreamBasicDescription desc) +{ // Only convert the lpcm formats (nothing else supported / used by MPD) assert(desc.mFormatID == kAudioFormatLinearPCM); - + return StringFormat<64>("%u channel %s %sinterleaved %u-bit %s %s (%uHz)", - desc.mChannelsPerFrame, - (desc.mFormatFlags & kAudioFormatFlagIsNonMixable) ? "" : "mixable", - (desc.mFormatFlags & kAudioFormatFlagIsNonInterleaved) ? "non-" : "", - desc.mBitsPerChannel, - (desc.mFormatFlags & kAudioFormatFlagIsFloat) ? "Float" : "SInt", - (desc.mFormatFlags & kAudioFormatFlagIsBigEndian) ? "BE" : "LE", - (UInt32)desc.mSampleRate); + desc.mChannelsPerFrame, + (desc.mFormatFlags & kAudioFormatFlagIsNonMixable) ? "" : "mixable", + (desc.mFormatFlags & kAudioFormatFlagIsNonInterleaved) ? "non-" : "", + desc.mBitsPerChannel, + (desc.mFormatFlags & kAudioFormatFlagIsFloat) ? "Float" : "SInt", + (desc.mFormatFlags & kAudioFormatFlagIsBigEndian) ? "BE" : "LE", + (UInt32)desc.mSampleRate); } @@ -107,7 +108,8 @@ private: static constexpr Domain osx_output_domain("osx_output"); static void -osx_os_status_to_cstring(OSStatus status, char *str, size_t size) { +osx_os_status_to_cstring(OSStatus status, char *str, size_t size) +{ CFErrorRef cferr = CFErrorCreate(nullptr, kCFErrorDomainOSStatus, status, nullptr); CFStringRef cfstr = CFErrorCopyDescription(cferr); if (!CFStringGetCString(cfstr, str, size, kCFStringEncodingUTF8)) { @@ -121,7 +123,7 @@ osx_os_status_to_cstring(OSStatus status, char *str, size_t size) { } static bool -osx_output_test_default_device(void) +osx_output_test_default_device() { /* on a Mac, this is always the default plugin, if nothing else is configured */ @@ -161,7 +163,7 @@ OSXOutput::Create(EventLoop &, const ConfigBlock &block) AudioObjectPropertyAddress aopa; AudioDeviceID dev_id = kAudioDeviceUnknown; UInt32 dev_id_size = sizeof(dev_id); - + if (oo->component_subtype == kAudioUnitSubType_SystemOutput) // get system output dev_id if configured aopa = { @@ -170,7 +172,8 @@ OSXOutput::Create(EventLoop &, const ConfigBlock &block) kAudioObjectPropertyElementMaster }; else - // fallback to default device initially (can still be changed by osx_output_set_device) + /* fallback to default device initially (can still be + changed by osx_output_set_device) */ aopa = { kAudioHardwarePropertyDefaultOutputDevice, kAudioObjectPropertyScopeOutput, @@ -216,7 +219,8 @@ OSXOutput::GetVolume() } void -OSXOutput::SetVolume(unsigned new_volume) { +OSXOutput::SetVolume(unsigned new_volume) +{ Float32 vol = new_volume / 100.0; AudioObjectPropertyAddress aopa = { .mSelector = kAudioHardwareServiceDeviceProperty_VirtualMasterVolume, @@ -240,11 +244,10 @@ OSXOutput::SetVolume(unsigned new_volume) { } static void -osx_output_parse_channel_map( - const char *device_name, - const char *channel_map_str, - SInt32 channel_map[], - UInt32 num_channels) +osx_output_parse_channel_map(const char *device_name, + const char *channel_map_str, + SInt32 channel_map[], + UInt32 num_channels) { char *endptr; unsigned int inserted_channels = 0; @@ -331,7 +334,8 @@ osx_output_set_channel_map(OSXOutput *oo) static float -osx_output_score_sample_rate(Float64 destination_rate, unsigned int source_rate) { +osx_output_score_sample_rate(Float64 destination_rate, unsigned source_rate) +{ float score = 0; double int_portion; double frac_portion = modf(source_rate / destination_rate, &int_portion); @@ -341,24 +345,27 @@ osx_output_score_sample_rate(Float64 destination_rate, unsigned int source_rate) score += (int_portion == 1.0) ? 500 : 0; if (source_rate == destination_rate) score += 1000; - else if(source_rate > destination_rate) + else if (source_rate > destination_rate) score += (int_portion > 1 && int_portion < 100) ? (100 - int_portion) / 100 * 100 : 0; else score += (int_portion > 1 && int_portion < 100) ? (100 + int_portion) / 100 * 100 : 0; - + return score; } static float -osx_output_score_format(const AudioStreamBasicDescription &format_desc, const AudioStreamBasicDescription &target_format) { +osx_output_score_format(const AudioStreamBasicDescription &format_desc, + const AudioStreamBasicDescription &target_format) +{ float score = 0; // Score only linear PCM formats (everything else MPD cannot use) if (format_desc.mFormatID == kAudioFormatLinearPCM) { - score += osx_output_score_sample_rate(format_desc.mSampleRate, target_format.mSampleRate); - + score += osx_output_score_sample_rate(format_desc.mSampleRate, + target_format.mSampleRate); + // Just choose the stream / format with the highest number of output channels score += format_desc.mChannelsPerFrame * 5; - + if (target_format.mFormatFlags == kLinearPCMFormatFlagIsFloat) { // for float, prefer the highest bitdepth we have if (format_desc.mBitsPerChannel >= 16) @@ -368,14 +375,16 @@ osx_output_score_format(const AudioStreamBasicDescription &format_desc, const Au score += 5; else if (format_desc.mBitsPerChannel > target_format.mBitsPerChannel) score += 1; - + } } + return score; } static Float64 -osx_output_set_device_format(AudioDeviceID dev_id, const AudioStreamBasicDescription &target_format) +osx_output_set_device_format(AudioDeviceID dev_id, + const AudioStreamBasicDescription &target_format) { AudioObjectPropertyAddress aopa = { kAudioDevicePropertyStreams, @@ -384,66 +393,69 @@ osx_output_set_device_format(AudioDeviceID dev_id, const AudioStreamBasicDescrip }; UInt32 property_size; - OSStatus err = AudioObjectGetPropertyDataSize(dev_id, &aopa, 0, NULL, &property_size); - if (err != noErr) { - throw FormatRuntimeError("Cannot get number of streams: %d\n", err); - } - - int n_streams = property_size / sizeof(AudioStreamID); - AudioStreamID streams[n_streams]; - err = AudioObjectGetPropertyData(dev_id, &aopa, 0, NULL, &property_size, streams); - if (err != noErr) { - throw FormatRuntimeError("Cannot get streams: %d\n", err); - } - + OSStatus err = AudioObjectGetPropertyDataSize(dev_id, &aopa, 0, NULL, &property_size); + if (err != noErr) + throw FormatRuntimeError("Cannot get number of streams: %d\n", err); + + int n_streams = property_size / sizeof(AudioStreamID); + AudioStreamID streams[n_streams]; + err = AudioObjectGetPropertyData(dev_id, &aopa, 0, NULL, &property_size, streams); + if (err != noErr) + throw FormatRuntimeError("Cannot get streams: %d\n", err); + bool format_found = false; int output_stream; AudioStreamBasicDescription output_format; - for (int i = 0; i < n_streams; i++) { - UInt32 direction; + for (int i = 0; i < n_streams; i++) { + UInt32 direction; AudioStreamID stream = streams[i]; - aopa.mSelector = kAudioStreamPropertyDirection; - property_size = sizeof(direction); - err = AudioObjectGetPropertyData(stream, - &aopa, - 0, - NULL, - &property_size, - &direction); - if (err != noErr) { - throw FormatRuntimeError("Cannot get streams direction: %d\n", err); - } - if (direction != 0) { + aopa.mSelector = kAudioStreamPropertyDirection; + property_size = sizeof(direction); + err = AudioObjectGetPropertyData(stream, + &aopa, + 0, + NULL, + &property_size, + &direction); + if (err != noErr) + throw FormatRuntimeError("Cannot get streams direction: %d\n", + err); + + if (direction != 0) continue; - } aopa.mSelector = kAudioStreamPropertyAvailablePhysicalFormats; - err = AudioObjectGetPropertyDataSize(stream, &aopa, 0, NULL, &property_size); + err = AudioObjectGetPropertyDataSize(stream, &aopa, 0, NULL, + &property_size); if (err != noErr) - throw FormatRuntimeError("Unable to get format size s for stream %d. Error = %s", streams[i], err); + throw FormatRuntimeError("Unable to get format size s for stream %d. Error = %s", + streams[i], err); - int format_count = property_size / sizeof(AudioStreamRangedDescription); + const size_t format_count = property_size / sizeof(AudioStreamRangedDescription); AudioStreamRangedDescription format_list[format_count]; err = AudioObjectGetPropertyData(stream, &aopa, 0, NULL, &property_size, format_list); if (err != noErr) - throw FormatRuntimeError("Unable to get available formats for stream %d. Error = %s", streams[i], err); + throw FormatRuntimeError("Unable to get available formats for stream %d. Error = %s", + streams[i], err); float output_score = 0; - for (int j = 0; j < format_count; j++) { + for (size_t j = 0; j < format_count; j++) { AudioStreamBasicDescription format_desc = format_list[j].mFormat; std::string format_string; - + // for devices with kAudioStreamAnyRate // we use the requested samplerate here if (format_desc.mSampleRate == kAudioStreamAnyRate) format_desc.mSampleRate = target_format.mSampleRate; float score = osx_output_score_format(format_desc, target_format); - + // print all (linear pcm) formats and their rating - if(score > 0.0) - FormatDebug(osx_output_domain, "Format: %s rated %f", StreamDescriptionToString(format_desc).c_str(), score); - + if (score > 0.0) + FormatDebug(osx_output_domain, + "Format: %s rated %f", + StreamDescriptionToString(format_desc).c_str(), score); + if (score > output_score) { output_score = score; output_format = format_desc; @@ -454,23 +466,24 @@ osx_output_set_device_format(AudioDeviceID dev_id, const AudioStreamBasicDescrip } if (format_found) { - aopa.mSelector = kAudioStreamPropertyPhysicalFormat; + aopa.mSelector = kAudioStreamPropertyPhysicalFormat; err = AudioObjectSetPropertyData(output_stream, - &aopa, - 0, - NULL, - sizeof(output_format), - &output_format); - if (err != noErr) { - throw FormatRuntimeError("Failed to change the stream format: %d\n", err); - } - } + &aopa, + 0, + NULL, + sizeof(output_format), + &output_format); + if (err != noErr) + throw FormatRuntimeError("Failed to change the stream format: %d\n", + err); + } return output_format.mSampleRate; } static OSStatus -osx_output_set_buffer_size(AudioUnit au, AudioStreamBasicDescription desc, UInt32 *frame_size) +osx_output_set_buffer_size(AudioUnit au, AudioStreamBasicDescription desc, + UInt32 *frame_size) { AudioValueRange value_range = {0, 0}; UInt32 property_size = sizeof(AudioValueRange); @@ -491,7 +504,7 @@ osx_output_set_buffer_size(AudioUnit au, AudioStreamBasicDescription desc, UInt3 &buffer_frame_size, sizeof(buffer_frame_size)); if (err != noErr) - FormatWarning(osx_output_domain, + FormatWarning(osx_output_domain, "Failed to set maximum buffer size: %d", err); @@ -503,7 +516,7 @@ osx_output_set_buffer_size(AudioUnit au, AudioStreamBasicDescription desc, UInt3 &buffer_frame_size, &property_size); if (err != noErr) { - FormatWarning(osx_output_domain, + FormatWarning(osx_output_domain, "Cannot get the buffer frame size: %d", err); return err; @@ -542,19 +555,21 @@ osx_output_hog_device(AudioDeviceID dev_id, bool hog) err); return; } + if (hog) { if (hog_pid != -1) { - FormatDebug(osx_output_domain, + FormatDebug(osx_output_domain, "Device is already hogged."); return; } } else { if (hog_pid != getpid()) { - FormatDebug(osx_output_domain, + FormatDebug(osx_output_domain, "Device is not owned by this process."); return; } } + hog_pid = hog ? getpid() : -1; size = sizeof(hog_pid); err = AudioObjectSetPropertyData(dev_id, @@ -568,9 +583,10 @@ osx_output_hog_device(AudioDeviceID dev_id, bool hog) "Cannot hog the device: %d", err); } else { - FormatDebug(osx_output_domain, - hog_pid == -1 ? "Device is unhogged" - : "Device is hogged"); + LogDebug(osx_output_domain, + hog_pid == -1 + ? "Device is unhogged" + : "Device is hogged"); } } @@ -595,8 +611,11 @@ osx_output_set_device(OSXOutput *oo) return; /* how many audio devices are there? */ - propaddr = { kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; - status = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &propaddr, 0, nullptr, &size); + propaddr = { kAudioHardwarePropertyDevices, + kAudioObjectPropertyScopeGlobal, + kAudioObjectPropertyElementMaster }; + status = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, + &propaddr, 0, nullptr, &size); if (status != noErr) { osx_os_status_to_cstring(status, errormsg, sizeof(errormsg)); throw FormatRuntimeError("Unable to determine number of OS X audio devices: %s", @@ -606,7 +625,9 @@ osx_output_set_device(OSXOutput *oo) /* what are the available audio device IDs? */ numdevices = size / sizeof(AudioDeviceID); std::unique_ptr deviceids(new AudioDeviceID[numdevices]); - status = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propaddr, 0, nullptr, &size, deviceids.get()); + status = AudioObjectGetPropertyData(kAudioObjectSystemObject, + &propaddr, 0, nullptr, + &size, deviceids.get()); if (status != noErr) { osx_os_status_to_cstring(status, errormsg, sizeof(errormsg)); throw FormatRuntimeError("Unable to determine OS X audio device IDs: %s", @@ -614,10 +635,14 @@ osx_output_set_device(OSXOutput *oo) } /* which audio device matches oo->device_name? */ - propaddr = { kAudioObjectPropertyName, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; + propaddr = { kAudioObjectPropertyName, + kAudioObjectPropertyScopeGlobal, + kAudioObjectPropertyElementMaster }; size = sizeof(CFStringRef); for (i = 0; i < numdevices; i++) { - status = AudioObjectGetPropertyData(deviceids[i], &propaddr, 0, nullptr, &size, &cfname); + status = AudioObjectGetPropertyData(deviceids[i], &propaddr, + 0, nullptr, + &size, &cfname); if (status != noErr) { osx_os_status_to_cstring(status, errormsg, sizeof(errormsg)); throw FormatRuntimeError("Unable to determine OS X device name " @@ -626,7 +651,8 @@ osx_output_set_device(OSXOutput *oo) errormsg); } - if (!CFStringGetCString(cfname, name, sizeof(name), kCFStringEncodingUTF8)) + if (!CFStringGetCString(cfname, name, sizeof(name), + kCFStringEncodingUTF8)) throw std::runtime_error("Unable to convert device name from CFStringRef to char*"); if (strcmp(oo->device_name, name) == 0) { @@ -636,10 +662,10 @@ osx_output_set_device(OSXOutput *oo) break; } } - if (i == numdevices) { - throw FormatRuntimeError("Found no audio device with name '%s' ", + + if (i == numdevices) + throw FormatRuntimeError("Found no audio device with name '%s' ", oo->device_name); - } status = AudioUnitSetProperty(oo->au, kAudioOutputUnitProperty_CurrentDevice, @@ -663,13 +689,13 @@ osx_output_set_device(OSXOutput *oo) } -/* - This function (the 'render callback' osx_render) is called by the - OS X audio subsystem (CoreAudio) to request audio data that will be - played by the audio hardware. This function has hard time constraints - so it cannot do IO (debug statements) or memory allocations. -*/ - +/** + * This function (the 'render callback' osx_render) is called by the + * OS X audio subsystem (CoreAudio) to request audio data that will be + * played by the audio hardware. This function has hard time + * constraints so it cannot do IO (debug statements) or memory + * allocations. + */ static OSStatus osx_render(void *vdata, gcc_unused AudioUnitRenderActionFlags *io_action_flags, @@ -684,7 +710,7 @@ osx_render(void *vdata, buffer_list->mBuffers[0].mDataByteSize = od->ring_buffer->pop((uint8_t *)buffer_list->mBuffers[0].mData, count); - return noErr; + return noErr; } void @@ -722,7 +748,7 @@ OSXOutput::Enable() #endif throw; } - + if (hog_device) osx_output_hog_device(dev_id, true); } @@ -794,7 +820,9 @@ OSXOutput::Open(AudioFormat &audio_format) Float64 sample_rate = osx_output_set_device_format(dev_id, asbd); #ifdef ENABLE_DSD - if(audio_format.format == SampleFormat::DSD && sample_rate != asbd.mSampleRate) { // fall back to PCM in case sample_rate cannot be synchronized + if (audio_format.format == SampleFormat::DSD && + sample_rate != asbd.mSampleRate) { + // fall back to PCM in case sample_rate cannot be synchronized params.dop = false; audio_format.format = SampleFormat::S32; asbd.mBitsPerChannel = 32; @@ -846,7 +874,7 @@ OSXOutput::Open(AudioFormat &audio_format) MPD_OSX_BUFFER_TIME_MS * audio_format.GetFrameSize() * audio_format.sample_rate / 1000); #ifdef ENABLE_DSD - if (dop_enabled) { + if (dop_enabled) { pcm_export->Open(audio_format.format, audio_format.channels, params); ring_buffer_size = std::max(buffer_frame_size, MPD_OSX_BUFFER_TIME_MS * pcm_export->GetFrameSize(audio_format) * asbd.mSampleRate / 1000); @@ -868,7 +896,7 @@ size_t OSXOutput::Play(const void *chunk, size_t size) { assert(size > 0); - if(pause) { + if (pause) { pause = false; OSStatus status = AudioOutputUnitStart(au); if (status != 0) { @@ -877,7 +905,7 @@ OSXOutput::Play(const void *chunk, size_t size) } } #ifdef ENABLE_DSD - if (dop_enabled) { + if (dop_enabled) { const auto e = pcm_export->Export({chunk, size}); /* the DoP (DSD over PCM) filter converts two frames at a time and ignores the last odd frame; if there @@ -902,9 +930,10 @@ OSXOutput::Delay() const noexcept ? std::chrono::steady_clock::duration::zero() : std::chrono::milliseconds(MPD_OSX_BUFFER_TIME_MS / 4); } - -bool OSXOutput::Pause() { - if(!pause) { + +bool OSXOutput::Pause() +{ + if (!pause) { pause = true; AudioOutputUnitStop(au); }