diff options
author | Chun-Min Chang <[email protected]> | 2018-07-18 14:36:53 -0700 |
---|---|---|
committer | Matthew Gregan <[email protected]> | 2018-07-19 09:36:53 +1200 |
commit | 4b7442ee48b3d5267d2d01bf8513bb205f2309ca (patch) | |
tree | d56c5a0b16cf59b8a87714edb9daca9c93bdb66f /src/cubeb_audiounit.cpp | |
parent | 6c4704304288c45d8198173ac4a6fe27d82b1e7c (diff) | |
download | cubeb-4b7442ee48b3d5267d2d01bf8513bb205f2309ca.tar.gz cubeb-4b7442ee48b3d5267d2d01bf8513bb205f2309ca.zip |
audiounit: Reduce duplicate code in audiounit_stream_get_current_device (#453)
* Add a test for cubeb_stream_get_current_device() and cubeb_stream_device_destroy()
* Reduce duplicates in audiounit_stream_get_current_device()
The audiounit_stream_get_current_device() will set the default device's name for input and output by the following steps:
1. Get default device's data, whose type is uint32
2. Convert the uint32 data into a string
3. Set the string into cubeb_device's name
Hence we can split it into functions to do the above steps:
i. audiounit_get_default_device_data(...)
Do the step 1
ii. allocate_and_convert_uint32_into_string(...)
Do the step 2 and 3. It will allocate memory for a string and put the converted string right there. (If we don't allocate and then set the values into the memory at the same time, we need to find a way to check the memory's boundary.)
They are called by a function named audiounit_get_default_device_name(...), with cubeb_device_type parameter to set the device name of input or output. And audiounit_get_default_device_name(...) will be used in audiounit_stream_get_current_device().
* Replace allocate_and_convert_uint32_into_string(...) by convert_uint32_into_string(...)
1. Rename allocate_and_convert_uint32_into_string(...) to convert_uint32_into_string(...)
2. Return a unique_ptr<char[]> from convert_uint32_into_string(...) pointing to an allocated memory
* Hard-coding the convertion from uint32 into string
This change is to make sure the conversion only ever needs to handle 4 bytes.
* Add more information to log when calling audiounit_stream_get_current_device(...)
We add a log with error message when we cannot get datasource data from the devices. However, we don't return an error code in this case since it's quite common when we try getting that data from USB devices. We will convert the datasource data into a string. If there is no data, the string is empty. Instead of logging when we cannot get the datasource data, it's better to log that the converted name is empty. It'll give more meaning (Users are more likely confused about what the empty datasource means.).
* Rename audiounit_get_default_device_data() to audiounit_get_default_device_datasource()
We should use an explicit name for this function since the same device will return different datasources. For example, the default input device on macbook pro will return "imic" if it uses the default internal microphone or "emic" when it uses an external microphone plugged in audio jack.
TODO: it's better to rename audiounit_stream_get_current_device to audiounit_stream_get_current_device_source. The reason is same as above.
Diffstat (limited to 'src/cubeb_audiounit.cpp')
-rw-r--r-- | src/cubeb_audiounit.cpp | 136 |
1 files changed, 73 insertions, 63 deletions
diff --git a/src/cubeb_audiounit.cpp b/src/cubeb_audiounit.cpp index 5a83098..d7cc468 100644 --- a/src/cubeb_audiounit.cpp +++ b/src/cubeb_audiounit.cpp @@ -2889,21 +2889,33 @@ int audiounit_stream_set_panning(cubeb_stream * stm, float panning) return CUBEB_OK; } -int audiounit_stream_get_current_device(cubeb_stream * stm, - cubeb_device ** const device) +unique_ptr<char[]> convert_uint32_into_string(UInt32 data) { -#if TARGET_OS_IPHONE - //TODO - return CUBEB_ERROR_NOT_SUPPORTED; -#else - OSStatus r; - UInt32 size; - UInt32 data; - char strdata[4]; - AudioDeviceID output_device_id; - AudioDeviceID input_device_id; + // Simply create an empty string if no data. + size_t size = data == 0 ? 0 : 4; // 4 bytes for uint32. + auto str = unique_ptr<char[]> { new char[size + 1] }; // + 1 for '\0'. + str[size] = '\0'; + if (size < 4) { + return str; + } + + // Reverse 0xWXYZ into 0xZYXW. + str[0] = (char)(data >> 24); + str[1] = (char)(data >> 16); + str[2] = (char)(data >> 8); + str[3] = (char)(data); + return str; +} + +int audiounit_get_default_device_datasource(cubeb_device_type type, + UInt32 * data) +{ + AudioDeviceID id = audiounit_get_default_device_id(type); + if (id == kAudioObjectUnknown) { + return CUBEB_ERROR; + } - AudioObjectPropertyAddress datasource_address = { + AudioObjectPropertyAddress datasource_address_output = { kAudioDevicePropertyDataSource, kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster @@ -2915,70 +2927,68 @@ int audiounit_stream_get_current_device(cubeb_stream * stm, kAudioObjectPropertyElementMaster }; - *device = NULL; - - output_device_id = audiounit_get_default_device_id(CUBEB_DEVICE_TYPE_OUTPUT); - if (output_device_id == kAudioObjectUnknown) { - return CUBEB_ERROR; + UInt32 size = sizeof(*data); + /* This fails with some USB headsets (e.g., Plantronic .Audio 628). */ + OSStatus r = AudioObjectGetPropertyData(id, + type == CUBEB_DEVICE_TYPE_INPUT ? + &datasource_address_input : + &datasource_address_output, + 0, NULL, &size, data); + if (r != noErr) { + *data = 0; } - *device = new cubeb_device; - if (!*device) { - return CUBEB_ERROR; - } - PodZero(*device, 1); + return CUBEB_OK; +} - size = sizeof(UInt32); - /* This fails with some USB headset, so simply return an empty string. */ - r = AudioObjectGetPropertyData(output_device_id, - &datasource_address, - 0, NULL, &size, &data); - if (r != noErr) { - size = 0; - data = 0; - } +int audiounit_get_default_device_name(cubeb_stream * stm, + cubeb_device * const device, + cubeb_device_type type) +{ + assert(stm); + assert(device); - (*device)->output_name = new char[size + 1]; - if (!(*device)->output_name) { - return CUBEB_ERROR; + UInt32 data; + int r = audiounit_get_default_device_datasource(type, &data); + if (r != CUBEB_OK) { + return r; } + char ** name = type == CUBEB_DEVICE_TYPE_INPUT ? + &device->input_name : &device->output_name; + *name = convert_uint32_into_string(data).release(); + if (!strlen(*name)) { // empty string. + LOG("(%p) name of %s device is empty!", stm, + type == CUBEB_DEVICE_TYPE_INPUT ? "input" : "output"); + } + return CUBEB_OK; +} - // Turn the four chars packed into a uint32 into a string - strdata[0] = (char)(data >> 24); - strdata[1] = (char)(data >> 16); - strdata[2] = (char)(data >> 8); - strdata[3] = (char)(data); - - memcpy((*device)->output_name, strdata, size); - (*device)->output_name[size] = '\0'; - input_device_id = audiounit_get_default_device_id(CUBEB_DEVICE_TYPE_INPUT); - if (input_device_id == kAudioObjectUnknown) { +int audiounit_stream_get_current_device(cubeb_stream * stm, + cubeb_device ** const device) +{ +#if TARGET_OS_IPHONE + //TODO + return CUBEB_ERROR_NOT_SUPPORTED; +#else + *device = new cubeb_device; + if (!*device) { return CUBEB_ERROR; } + PodZero(*device, 1); - size = sizeof(UInt32); - r = AudioObjectGetPropertyData(input_device_id, &datasource_address_input, 0, NULL, &size, &data); - if (r != noErr) { - LOG("(%p) Error when getting device !", stm); - size = 0; - data = 0; + int r = audiounit_get_default_device_name(stm, *device, + CUBEB_DEVICE_TYPE_OUTPUT); + if (r != CUBEB_OK) { + return r; } - (*device)->input_name = new char[size + 1]; - if (!(*device)->input_name) { - return CUBEB_ERROR; + r = audiounit_get_default_device_name(stm, *device, + CUBEB_DEVICE_TYPE_INPUT); + if (r != CUBEB_OK) { + return r; } - // Turn the four chars packed into a uint32 into a string - strdata[0] = (char)(data >> 24); - strdata[1] = (char)(data >> 16); - strdata[2] = (char)(data >> 8); - strdata[3] = (char)(data); - - memcpy((*device)->input_name, strdata, size); - (*device)->input_name[size] = '\0'; - return CUBEB_OK; #endif } |