output/osx: migrate from class Error to C++ exceptions

Beware, this commit was not tested.  I don't have OS X, but I want to
prepare an API change.
This commit is contained in:
Max Kellermann 2016-11-09 11:43:11 +01:00
parent 10f62db9fd
commit df4616ae4a

View File

@ -21,7 +21,7 @@
#include "OSXOutputPlugin.hxx" #include "OSXOutputPlugin.hxx"
#include "../OutputAPI.hxx" #include "../OutputAPI.hxx"
#include "util/ScopeExit.hxx" #include "util/ScopeExit.hxx"
#include "util/Error.hxx" #include "util/RuntimeError.hxx"
#include "util/Domain.hxx" #include "util/Domain.hxx"
#include "thread/Mutex.hxx" #include "thread/Mutex.hxx"
#include "thread/Cond.hxx" #include "thread/Cond.hxx"
@ -52,8 +52,7 @@ struct OSXOutput {
boost::lockfree::spsc_queue<uint8_t> *ring_buffer; boost::lockfree::spsc_queue<uint8_t> *ring_buffer;
OSXOutput() OSXOutput(const ConfigBlock &block);
:base(osx_output_plugin) {}
}; };
static constexpr Domain osx_output_domain("osx_output"); static constexpr Domain osx_output_domain("osx_output");
@ -80,40 +79,34 @@ osx_output_test_default_device(void)
return true; return true;
} }
static void OSXOutput::OSXOutput(const ConfigBlock &block)
osx_output_configure(OSXOutput *oo, const ConfigBlock &block) :base(osx_output_plugin, block)
{ {
const char *device = block.GetBlockValue("device"); const char *device = block.GetBlockValue("device");
if (device == nullptr || 0 == strcmp(device, "default")) { if (device == nullptr || 0 == strcmp(device, "default")) {
oo->component_subtype = kAudioUnitSubType_DefaultOutput; component_subtype = kAudioUnitSubType_DefaultOutput;
oo->device_name = nullptr; device_name = nullptr;
} }
else if (0 == strcmp(device, "system")) { else if (0 == strcmp(device, "system")) {
oo->component_subtype = kAudioUnitSubType_SystemOutput; component_subtype = kAudioUnitSubType_SystemOutput;
oo->device_name = nullptr; device_name = nullptr;
} }
else { else {
oo->component_subtype = kAudioUnitSubType_HALOutput; component_subtype = kAudioUnitSubType_HALOutput;
/* XXX am I supposed to strdup() this? */ /* XXX am I supposed to strdup() this? */
oo->device_name = device; device_name = device;
} }
oo->channel_map = block.GetBlockValue("channel_map"); channel_map = block.GetBlockValue("channel_map");
oo->hog_device = block.GetBlockValue("hog_device", false); hog_device = block.GetBlockValue("hog_device", false);
oo->sync_sample_rate = block.GetBlockValue("sync_sample_rate", false); sync_sample_rate = block.GetBlockValue("sync_sample_rate", false);
} }
static AudioOutput * static AudioOutput *
osx_output_init(const ConfigBlock &block, Error &error) osx_output_init(const ConfigBlock &block, Error &)
{ {
OSXOutput *oo = new OSXOutput(); OSXOutput *oo = new OSXOutput(block);
if (!oo->base.Configure(block, error)) {
delete oo;
return nullptr;
}
osx_output_configure(oo, block);
AudioObjectPropertyAddress aopa = { AudioObjectPropertyAddress aopa = {
kAudioHardwarePropertyDefaultOutputDevice, kAudioHardwarePropertyDefaultOutputDevice,
@ -142,25 +135,21 @@ osx_output_finish(AudioOutput *ao)
delete oo; delete oo;
} }
static bool static void
osx_output_parse_channel_map( osx_output_parse_channel_map(
const char *device_name, const char *device_name,
const char *channel_map_str, const char *channel_map_str,
SInt32 channel_map[], SInt32 channel_map[],
UInt32 num_channels, UInt32 num_channels)
Error &error)
{ {
char *endptr; char *endptr;
unsigned int inserted_channels = 0; unsigned int inserted_channels = 0;
bool want_number = true; bool want_number = true;
while (*channel_map_str) { while (*channel_map_str) {
if (inserted_channels >= num_channels) { if (inserted_channels >= num_channels)
error.Format(osx_output_domain, throw FormatRuntimeError("%s: channel map contains more than %u entries or trailing garbage",
"%s: channel map contains more than %u entries or trailing garbage", device_name, num_channels);
device_name, num_channels);
return false;
}
if (!want_number && *channel_map_str == ',') { if (!want_number && *channel_map_str == ',') {
++channel_map_str; ++channel_map_str;
@ -172,12 +161,9 @@ osx_output_parse_channel_map(
(isdigit(*channel_map_str) || *channel_map_str == '-') (isdigit(*channel_map_str) || *channel_map_str == '-')
) { ) {
channel_map[inserted_channels] = strtol(channel_map_str, &endptr, 10); channel_map[inserted_channels] = strtol(channel_map_str, &endptr, 10);
if (channel_map[inserted_channels] < -1) { if (channel_map[inserted_channels] < -1)
error.Format(osx_output_domain, throw FormatRuntimeError("%s: channel map value %d not allowed (must be -1 or greater)",
"%s: channel map value %d not allowed (must be -1 or greater)",
device_name, channel_map[inserted_channels]);
return false;
}
channel_map_str = endptr; channel_map_str = endptr;
want_number = false; want_number = false;
FormatDebug(osx_output_domain, FormatDebug(osx_output_domain,
@ -187,24 +173,17 @@ osx_output_parse_channel_map(
continue; continue;
} }
error.Format(osx_output_domain, throw FormatRuntimeError("%s: invalid character '%c' in channel map",
"%s: invalid character '%c' in channel map", device_name, *channel_map_str);
device_name, *channel_map_str);
return false;
} }
if (inserted_channels < num_channels) { if (inserted_channels < num_channels)
error.Format(osx_output_domain, throw FormatRuntimeError("%s: channel map contains less than %u entries",
"%s: channel map contains less than %u entries", device_name, num_channels);
device_name, num_channels);
return false;
}
return true;
} }
static bool static void
osx_output_set_channel_map(OSXOutput *oo, Error &error) osx_output_set_channel_map(OSXOutput *oo)
{ {
AudioStreamBasicDescription desc; AudioStreamBasicDescription desc;
OSStatus status; OSStatus status;
@ -221,22 +200,16 @@ osx_output_set_channel_map(OSXOutput *oo, Error &error)
&size); &size);
if (status != noErr) { if (status != noErr) {
osx_os_status_to_cstring(status, errormsg, sizeof(errormsg)); osx_os_status_to_cstring(status, errormsg, sizeof(errormsg));
error.Format(osx_output_domain, status, throw FormatRuntimeError("%s: unable to get number of output device channels: %s",
"%s: unable to get number of output device channels: %s", oo->device_name, errormsg);
oo->device_name, errormsg);
return false;
} }
num_channels = desc.mChannelsPerFrame; num_channels = desc.mChannelsPerFrame;
std::unique_ptr<SInt32[]> channel_map(new SInt32[num_channels]); std::unique_ptr<SInt32[]> channel_map(new SInt32[num_channels]);
if (!osx_output_parse_channel_map(oo->device_name, osx_output_parse_channel_map(oo->device_name,
oo->channel_map, oo->channel_map,
channel_map.get(), channel_map.get(),
num_channels, num_channels));
error)
) {
return false;
}
size = num_channels * sizeof(SInt32); size = num_channels * sizeof(SInt32);
status = AudioUnitSetProperty(oo->au, status = AudioUnitSetProperty(oo->au,
@ -247,12 +220,8 @@ osx_output_set_channel_map(OSXOutput *oo, Error &error)
size); size);
if (status != noErr) { if (status != noErr) {
osx_os_status_to_cstring(status, errormsg, sizeof(errormsg)); osx_os_status_to_cstring(status, errormsg, sizeof(errormsg));
error.Format(osx_output_domain, status, throw FormatRuntimeError("%s: unable to set channel map: %s", oo->device_name, errormsg);
"%s: unable to set channel map: %s", oo->device_name, errormsg);
return false;
} }
return true;
} }
static void static void
@ -428,8 +397,8 @@ osx_output_hog_device(AudioDeviceID dev_id, bool hog)
} }
static bool static void
osx_output_set_device(OSXOutput *oo, Error &error) osx_output_set_device(OSXOutput *oo)
{ {
OSStatus status; OSStatus status;
UInt32 size, numdevices; UInt32 size, numdevices;
@ -445,17 +414,15 @@ osx_output_set_device(OSXOutput *oo, Error &error)
}; };
if (oo->component_subtype != kAudioUnitSubType_HALOutput) if (oo->component_subtype != kAudioUnitSubType_HALOutput)
return true; return;
/* how many audio devices are there? */ /* how many audio devices are there? */
propaddr = { kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; propaddr = { kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
status = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &propaddr, 0, nullptr, &size); status = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &propaddr, 0, nullptr, &size);
if (status != noErr) { if (status != noErr) {
osx_os_status_to_cstring(status, errormsg, sizeof(errormsg)); osx_os_status_to_cstring(status, errormsg, sizeof(errormsg));
error.Format(osx_output_domain, status, throw FormatRuntimeError("Unable to determine number of OS X audio devices: %s",
"Unable to determine number of OS X audio devices: %s", errormsg);
errormsg);
return false;
} }
/* what are the available audio device IDs? */ /* what are the available audio device IDs? */
@ -464,10 +431,8 @@ osx_output_set_device(OSXOutput *oo, Error &error)
status = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propaddr, 0, nullptr, &size, deviceids.get()); status = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propaddr, 0, nullptr, &size, deviceids.get());
if (status != noErr) { if (status != noErr) {
osx_os_status_to_cstring(status, errormsg, sizeof(errormsg)); osx_os_status_to_cstring(status, errormsg, sizeof(errormsg));
error.Format(osx_output_domain, status, throw FormatRuntimeError("Unable to determine OS X audio device IDs: %s",
"Unable to determine OS X audio device IDs: %s", errormsg);
errormsg);
return false;
} }
/* which audio device matches oo->device_name? */ /* which audio device matches oo->device_name? */
@ -477,18 +442,14 @@ osx_output_set_device(OSXOutput *oo, Error &error)
status = AudioObjectGetPropertyData(deviceids[i], &propaddr, 0, nullptr, &size, &cfname); status = AudioObjectGetPropertyData(deviceids[i], &propaddr, 0, nullptr, &size, &cfname);
if (status != noErr) { if (status != noErr) {
osx_os_status_to_cstring(status, errormsg, sizeof(errormsg)); osx_os_status_to_cstring(status, errormsg, sizeof(errormsg));
error.Format(osx_output_domain, status, throw FormatRuntimeError("Unable to determine OS X device name "
"Unable to determine OS X device name " "(device %u): %s",
"(device %u): %s", (unsigned int) deviceids[i],
(unsigned int) deviceids[i], errormsg);
errormsg);
return false;
} }
if (!CFStringGetCString(cfname, name, sizeof(name), kCFStringEncodingUTF8)) { if (!CFStringGetCString(cfname, name, sizeof(name), kCFStringEncodingUTF8))
error.Set(osx_output_domain, "Unable to convert device name from CFStringRef to char*"); throw std::runtime_error("Unable to convert device name from CFStringRef to char*");
return false;
}
if (strcmp(oo->device_name, name) == 0) { if (strcmp(oo->device_name, name) == 0) {
FormatDebug(osx_output_domain, FormatDebug(osx_output_domain,
@ -502,7 +463,7 @@ osx_output_set_device(OSXOutput *oo, Error &error)
"Found no audio device with name '%s' " "Found no audio device with name '%s' "
"(will use default audio device)", "(will use default audio device)",
oo->device_name); oo->device_name);
return true; return;
} }
status = AudioUnitSetProperty(oo->au, status = AudioUnitSetProperty(oo->au,
@ -513,10 +474,8 @@ osx_output_set_device(OSXOutput *oo, Error &error)
sizeof(AudioDeviceID)); sizeof(AudioDeviceID));
if (status != noErr) { if (status != noErr) {
osx_os_status_to_cstring(status, errormsg, sizeof(errormsg)); osx_os_status_to_cstring(status, errormsg, sizeof(errormsg));
error.Format(osx_output_domain, status, throw FormatRuntimeError("Unable to set OS X audio output device: %s",
"Unable to set OS X audio output device: %s", errormsg);
errormsg);
return false;
} }
oo->dev_id = deviceids[i]; oo->dev_id = deviceids[i];
@ -524,10 +483,8 @@ osx_output_set_device(OSXOutput *oo, Error &error)
"set OS X audio output device ID=%u, name=%s", "set OS X audio output device ID=%u, name=%s",
(unsigned)deviceids[i], name); (unsigned)deviceids[i], name);
if (oo->channel_map && !osx_output_set_channel_map(oo, error)) if (oo->channel_map)
return false; osx_output_set_channel_map(oo);
return true;
} }
@ -556,7 +513,7 @@ osx_render(void *vdata,
} }
static bool static bool
osx_output_enable(AudioOutput *ao, Error &error) osx_output_enable(AudioOutput *ao, Error &)
{ {
char errormsg[1024]; char errormsg[1024];
OSXOutput *oo = (OSXOutput *)ao; OSXOutput *oo = (OSXOutput *)ao;
@ -569,24 +526,21 @@ osx_output_enable(AudioOutput *ao, Error &error)
desc.componentFlagsMask = 0; desc.componentFlagsMask = 0;
AudioComponent comp = AudioComponentFindNext(nullptr, &desc); AudioComponent comp = AudioComponentFindNext(nullptr, &desc);
if (comp == 0) { if (comp == 0)
error.Set(osx_output_domain, throw std::runtime_error("Error finding OS X component");
"Error finding OS X component");
return false;
}
OSStatus status = AudioComponentInstanceNew(comp, &oo->au); OSStatus status = AudioComponentInstanceNew(comp, &oo->au);
if (status != noErr) { if (status != noErr) {
osx_os_status_to_cstring(status, errormsg, sizeof(errormsg)); osx_os_status_to_cstring(status, errormsg, sizeof(errormsg));
error.Format(osx_output_domain, status, throw FormatRuntimeError("Unable to open OS X component: %s",
"Unable to open OS X component: %s", errormsg);
errormsg);
return false;
} }
if (!osx_output_set_device(oo, error)) { try {
osx_output_set_device(oo);
} catch (...) {
AudioComponentInstanceDispose(oo->au); AudioComponentInstanceDispose(oo->au);
return false; throw;
} }
if (oo->hog_device) { if (oo->hog_device) {
@ -621,7 +575,7 @@ osx_output_close(AudioOutput *ao)
static bool static bool
osx_output_open(AudioOutput *ao, AudioFormat &audio_format, osx_output_open(AudioOutput *ao, AudioFormat &audio_format,
Error &error) Error &)
{ {
char errormsg[1024]; char errormsg[1024];
OSXOutput *od = (OSXOutput *)ao; OSXOutput *od = (OSXOutput *)ao;
@ -667,11 +621,8 @@ osx_output_open(AudioOutput *ao, AudioFormat &audio_format,
kAudioUnitScope_Input, 0, kAudioUnitScope_Input, 0,
&od->asbd, &od->asbd,
sizeof(od->asbd)); sizeof(od->asbd));
if (status != noErr) { if (status != noErr)
error.Set(osx_output_domain, status, throw std::runtime_error("Unable to set format on OS X device");
"Unable to set format on OS X device");
return false;
}
AURenderCallbackStruct callback; AURenderCallbackStruct callback;
callback.inputProc = osx_render; callback.inputProc = osx_render;
@ -684,28 +635,22 @@ osx_output_open(AudioOutput *ao, AudioFormat &audio_format,
&callback, sizeof(callback)); &callback, sizeof(callback));
if (status != noErr) { if (status != noErr) {
AudioComponentInstanceDispose(od->au); AudioComponentInstanceDispose(od->au);
error.Set(osx_output_domain, status, throw std::runtime_error("unable to set callback for OS X audio unit");
"unable to set callback for OS X audio unit");
return false;
} }
status = AudioUnitInitialize(od->au); status = AudioUnitInitialize(od->au);
if (status != noErr) { if (status != noErr) {
osx_os_status_to_cstring(status, errormsg, sizeof(errormsg)); osx_os_status_to_cstring(status, errormsg, sizeof(errormsg));
error.Format(osx_output_domain, status, throw FormatRuntimeError("Unable to initialize OS X audio unit: %s",
"Unable to initialize OS X audio unit: %s", errormsg);
errormsg);
return false;
} }
UInt32 buffer_frame_size = 1; UInt32 buffer_frame_size = 1;
status = osx_output_set_buffer_size(od->au, od->asbd, &buffer_frame_size); status = osx_output_set_buffer_size(od->au, od->asbd, &buffer_frame_size);
if (status != noErr) { if (status != noErr) {
osx_os_status_to_cstring(status, errormsg, sizeof(errormsg)); osx_os_status_to_cstring(status, errormsg, sizeof(errormsg));
error.Format(osx_output_domain, status, throw FormatRuntimeError("Unable to set frame size: %s",
"Unable to set frame size: %s", errormsg);
errormsg);
return false;
} }
od->ring_buffer = new boost::lockfree::spsc_queue<uint8_t>(buffer_frame_size); od->ring_buffer = new boost::lockfree::spsc_queue<uint8_t>(buffer_frame_size);
@ -714,10 +659,8 @@ osx_output_open(AudioOutput *ao, AudioFormat &audio_format,
if (status != 0) { if (status != 0) {
AudioUnitUninitialize(od->au); AudioUnitUninitialize(od->au);
osx_os_status_to_cstring(status, errormsg, sizeof(errormsg)); osx_os_status_to_cstring(status, errormsg, sizeof(errormsg));
error.Format(osx_output_domain, status, throw FormatRuntimeError("unable to start audio output: %s",
"unable to start audio output: %s", errormsg);
errormsg);
return false;
} }
return true; return true;