output/osx: allocate the device in enable()
Keep the device open as long as the output is enabled, but initialize it only when playback starts.
This commit is contained in:
@@ -114,72 +114,6 @@ osx_output_finish(struct audio_output *ao)
|
|||||||
g_free(od);
|
g_free(od);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
osx_output_cancel(struct audio_output *ao)
|
|
||||||
{
|
|
||||||
struct osx_output *od = (struct osx_output *)ao;
|
|
||||||
|
|
||||||
g_mutex_lock(od->mutex);
|
|
||||||
od->len = 0;
|
|
||||||
g_mutex_unlock(od->mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
osx_output_close(struct audio_output *ao)
|
|
||||||
{
|
|
||||||
struct osx_output *od = (struct osx_output *)ao;
|
|
||||||
|
|
||||||
AudioOutputUnitStop(od->au);
|
|
||||||
AudioUnitUninitialize(od->au);
|
|
||||||
CloseComponent(od->au);
|
|
||||||
}
|
|
||||||
|
|
||||||
static OSStatus
|
|
||||||
osx_render(void *vdata,
|
|
||||||
G_GNUC_UNUSED AudioUnitRenderActionFlags *io_action_flags,
|
|
||||||
G_GNUC_UNUSED const AudioTimeStamp *in_timestamp,
|
|
||||||
G_GNUC_UNUSED UInt32 in_bus_number,
|
|
||||||
G_GNUC_UNUSED UInt32 in_number_frames,
|
|
||||||
AudioBufferList *buffer_list)
|
|
||||||
{
|
|
||||||
struct osx_output *od = (struct osx_output *) vdata;
|
|
||||||
AudioBuffer *buffer = &buffer_list->mBuffers[0];
|
|
||||||
size_t buffer_size = buffer->mDataByteSize;
|
|
||||||
size_t bytes_to_copy;
|
|
||||||
size_t trailer_length;
|
|
||||||
size_t dest_pos = 0;
|
|
||||||
|
|
||||||
g_mutex_lock(od->mutex);
|
|
||||||
|
|
||||||
bytes_to_copy = MIN(od->len, buffer_size);
|
|
||||||
od->len -= bytes_to_copy;
|
|
||||||
|
|
||||||
trailer_length = od->buffer_size - od->pos;
|
|
||||||
if (bytes_to_copy > trailer_length) {
|
|
||||||
memcpy((unsigned char*)buffer->mData + dest_pos,
|
|
||||||
od->buffer + od->pos, trailer_length);
|
|
||||||
od->pos = 0;
|
|
||||||
dest_pos += trailer_length;
|
|
||||||
bytes_to_copy -= trailer_length;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy((unsigned char*)buffer->mData + dest_pos,
|
|
||||||
od->buffer + od->pos, bytes_to_copy);
|
|
||||||
od->pos += bytes_to_copy;
|
|
||||||
|
|
||||||
if (od->pos >= od->buffer_size)
|
|
||||||
od->pos = 0;
|
|
||||||
|
|
||||||
g_cond_signal(od->condition);
|
|
||||||
g_mutex_unlock(od->mutex);
|
|
||||||
|
|
||||||
if (bytes_to_copy < buffer_size)
|
|
||||||
memset((unsigned char*)buffer->mData + bytes_to_copy, 0,
|
|
||||||
buffer_size - bytes_to_copy);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
osx_output_set_device(struct osx_output *oo, GError **error)
|
osx_output_set_device(struct osx_output *oo, GError **error)
|
||||||
{
|
{
|
||||||
@@ -269,65 +203,135 @@ done:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static OSStatus
|
||||||
osx_output_open(struct audio_output *ao, struct audio_format *audio_format, GError **error)
|
osx_render(void *vdata,
|
||||||
|
G_GNUC_UNUSED AudioUnitRenderActionFlags *io_action_flags,
|
||||||
|
G_GNUC_UNUSED const AudioTimeStamp *in_timestamp,
|
||||||
|
G_GNUC_UNUSED UInt32 in_bus_number,
|
||||||
|
G_GNUC_UNUSED UInt32 in_number_frames,
|
||||||
|
AudioBufferList *buffer_list)
|
||||||
{
|
{
|
||||||
struct osx_output *od = (struct osx_output *)ao;
|
struct osx_output *od = (struct osx_output *) vdata;
|
||||||
ComponentDescription desc;
|
AudioBuffer *buffer = &buffer_list->mBuffers[0];
|
||||||
Component comp;
|
size_t buffer_size = buffer->mDataByteSize;
|
||||||
AURenderCallbackStruct callback;
|
size_t bytes_to_copy;
|
||||||
AudioStreamBasicDescription stream_description;
|
size_t trailer_length;
|
||||||
OSStatus status;
|
size_t dest_pos = 0;
|
||||||
ComponentResult result;
|
|
||||||
|
|
||||||
|
g_mutex_lock(od->mutex);
|
||||||
|
|
||||||
|
bytes_to_copy = MIN(od->len, buffer_size);
|
||||||
|
od->len -= bytes_to_copy;
|
||||||
|
|
||||||
|
trailer_length = od->buffer_size - od->pos;
|
||||||
|
if (bytes_to_copy > trailer_length) {
|
||||||
|
memcpy((unsigned char*)buffer->mData + dest_pos,
|
||||||
|
od->buffer + od->pos, trailer_length);
|
||||||
|
od->pos = 0;
|
||||||
|
dest_pos += trailer_length;
|
||||||
|
bytes_to_copy -= trailer_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy((unsigned char*)buffer->mData + dest_pos,
|
||||||
|
od->buffer + od->pos, bytes_to_copy);
|
||||||
|
od->pos += bytes_to_copy;
|
||||||
|
|
||||||
|
if (od->pos >= od->buffer_size)
|
||||||
|
od->pos = 0;
|
||||||
|
|
||||||
|
g_cond_signal(od->condition);
|
||||||
|
g_mutex_unlock(od->mutex);
|
||||||
|
|
||||||
|
if (bytes_to_copy < buffer_size)
|
||||||
|
memset((unsigned char*)buffer->mData + bytes_to_copy, 0,
|
||||||
|
buffer_size - bytes_to_copy);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
osx_output_enable(struct audio_output *ao, GError **error_r)
|
||||||
|
{
|
||||||
|
struct osx_output *oo = (struct osx_output *)ao;
|
||||||
|
|
||||||
|
ComponentDescription desc;
|
||||||
desc.componentType = kAudioUnitType_Output;
|
desc.componentType = kAudioUnitType_Output;
|
||||||
desc.componentSubType = od->component_subtype;
|
desc.componentSubType = oo->component_subtype;
|
||||||
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
|
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
|
||||||
desc.componentFlags = 0;
|
desc.componentFlags = 0;
|
||||||
desc.componentFlagsMask = 0;
|
desc.componentFlagsMask = 0;
|
||||||
|
|
||||||
comp = FindNextComponent(NULL, &desc);
|
Component comp = FindNextComponent(NULL, &desc);
|
||||||
if (comp == 0) {
|
if (comp == 0) {
|
||||||
g_set_error(error, osx_output_quark(), 0,
|
g_set_error(error_r, osx_output_quark(), 0,
|
||||||
"Error finding OS X component");
|
"Error finding OS X component");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
status = OpenAComponent(comp, &od->au);
|
OSStatus status = OpenAComponent(comp, &oo->au);
|
||||||
if (status != noErr) {
|
if (status != noErr) {
|
||||||
g_set_error(error, osx_output_quark(), status,
|
g_set_error(error_r, osx_output_quark(), status,
|
||||||
"Unable to open OS X component: %s",
|
"Unable to open OS X component: %s",
|
||||||
GetMacOSStatusCommentString(status));
|
GetMacOSStatusCommentString(status));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
status = AudioUnitInitialize(od->au);
|
if (!osx_output_set_device(oo, error_r)) {
|
||||||
if (status != noErr) {
|
CloseComponent(oo->au);
|
||||||
CloseComponent(od->au);
|
|
||||||
g_set_error(error, osx_output_quark(), status,
|
|
||||||
"Unable to initialize OS X audio unit: %s",
|
|
||||||
GetMacOSStatusCommentString(status));
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!osx_output_set_device(od, error))
|
AURenderCallbackStruct callback;
|
||||||
return false;
|
|
||||||
|
|
||||||
callback.inputProc = osx_render;
|
callback.inputProc = osx_render;
|
||||||
callback.inputProcRefCon = od;
|
callback.inputProcRefCon = oo;
|
||||||
|
|
||||||
result = AudioUnitSetProperty(od->au,
|
ComponentResult result =
|
||||||
|
AudioUnitSetProperty(oo->au,
|
||||||
kAudioUnitProperty_SetRenderCallback,
|
kAudioUnitProperty_SetRenderCallback,
|
||||||
kAudioUnitScope_Input, 0,
|
kAudioUnitScope_Input, 0,
|
||||||
&callback, sizeof(callback));
|
&callback, sizeof(callback));
|
||||||
if (result != noErr) {
|
if (result != noErr) {
|
||||||
AudioUnitUninitialize(od->au);
|
g_set_error(error_r, osx_output_quark(), result,
|
||||||
CloseComponent(od->au);
|
|
||||||
g_set_error(error, osx_output_quark(), result,
|
|
||||||
"unable to set callback for OS X audio unit");
|
"unable to set callback for OS X audio unit");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
osx_output_disable(struct audio_output *ao)
|
||||||
|
{
|
||||||
|
struct osx_output *oo = (struct osx_output *)ao;
|
||||||
|
|
||||||
|
CloseComponent(oo->au);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
osx_output_cancel(struct audio_output *ao)
|
||||||
|
{
|
||||||
|
struct osx_output *od = (struct osx_output *)ao;
|
||||||
|
|
||||||
|
g_mutex_lock(od->mutex);
|
||||||
|
od->len = 0;
|
||||||
|
g_mutex_unlock(od->mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
osx_output_close(struct audio_output *ao)
|
||||||
|
{
|
||||||
|
struct osx_output *od = (struct osx_output *)ao;
|
||||||
|
|
||||||
|
AudioOutputUnitStop(od->au);
|
||||||
|
AudioUnitUninitialize(od->au);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
osx_output_open(struct audio_output *ao, struct audio_format *audio_format, GError **error)
|
||||||
|
{
|
||||||
|
struct osx_output *od = (struct osx_output *)ao;
|
||||||
|
|
||||||
|
AudioStreamBasicDescription stream_description;
|
||||||
stream_description.mSampleRate = audio_format->sample_rate;
|
stream_description.mSampleRate = audio_format->sample_rate;
|
||||||
stream_description.mFormatID = kAudioFormatLinearPCM;
|
stream_description.mFormatID = kAudioFormatLinearPCM;
|
||||||
stream_description.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
|
stream_description.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
|
||||||
@@ -357,18 +361,25 @@ osx_output_open(struct audio_output *ao, struct audio_format *audio_format, GErr
|
|||||||
stream_description.mBytesPerFrame = stream_description.mBytesPerPacket;
|
stream_description.mBytesPerFrame = stream_description.mBytesPerPacket;
|
||||||
stream_description.mChannelsPerFrame = audio_format->channels;
|
stream_description.mChannelsPerFrame = audio_format->channels;
|
||||||
|
|
||||||
result = AudioUnitSetProperty(od->au, kAudioUnitProperty_StreamFormat,
|
ComponentResult result =
|
||||||
|
AudioUnitSetProperty(od->au, kAudioUnitProperty_StreamFormat,
|
||||||
kAudioUnitScope_Input, 0,
|
kAudioUnitScope_Input, 0,
|
||||||
&stream_description,
|
&stream_description,
|
||||||
sizeof(stream_description));
|
sizeof(stream_description));
|
||||||
if (result != noErr) {
|
if (result != noErr) {
|
||||||
AudioUnitUninitialize(od->au);
|
|
||||||
CloseComponent(od->au);
|
|
||||||
g_set_error(error, osx_output_quark(), result,
|
g_set_error(error, osx_output_quark(), result,
|
||||||
"Unable to set format on OS X device");
|
"Unable to set format on OS X device");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OSStatus status = AudioUnitInitialize(od->au);
|
||||||
|
if (status != noErr) {
|
||||||
|
g_set_error(error, osx_output_quark(), status,
|
||||||
|
"Unable to initialize OS X audio unit: %s",
|
||||||
|
GetMacOSStatusCommentString(status));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/* create a buffer of 1s */
|
/* create a buffer of 1s */
|
||||||
od->buffer_size = (audio_format->sample_rate) *
|
od->buffer_size = (audio_format->sample_rate) *
|
||||||
audio_format_frame_size(audio_format);
|
audio_format_frame_size(audio_format);
|
||||||
@@ -379,6 +390,7 @@ osx_output_open(struct audio_output *ao, struct audio_format *audio_format, GErr
|
|||||||
|
|
||||||
status = AudioOutputUnitStart(od->au);
|
status = AudioOutputUnitStart(od->au);
|
||||||
if (status != 0) {
|
if (status != 0) {
|
||||||
|
AudioUnitUninitialize(od->au);
|
||||||
g_set_error(error, osx_output_quark(), status,
|
g_set_error(error, osx_output_quark(), status,
|
||||||
"unable to start audio output: %s",
|
"unable to start audio output: %s",
|
||||||
GetMacOSStatusCommentString(status));
|
GetMacOSStatusCommentString(status));
|
||||||
@@ -427,6 +439,8 @@ const struct audio_output_plugin osx_output_plugin = {
|
|||||||
.test_default_device = osx_output_test_default_device,
|
.test_default_device = osx_output_test_default_device,
|
||||||
.init = osx_output_init,
|
.init = osx_output_init,
|
||||||
.finish = osx_output_finish,
|
.finish = osx_output_finish,
|
||||||
|
.enable = osx_output_enable,
|
||||||
|
.disable = osx_output_disable,
|
||||||
.open = osx_output_open,
|
.open = osx_output_open,
|
||||||
.close = osx_output_close,
|
.close = osx_output_close,
|
||||||
.play = osx_output_play,
|
.play = osx_output_play,
|
||||||
|
Reference in New Issue
Block a user