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:
Max Kellermann 2011-12-24 15:55:35 +01:00
parent f208217412
commit 9179d9592d
1 changed files with 117 additions and 103 deletions

View File

@ -114,72 +114,6 @@ osx_output_finish(struct audio_output *ao)
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
osx_output_set_device(struct osx_output *oo, GError **error)
{
@ -269,65 +203,135 @@ done:
return ret;
}
static bool
osx_output_open(struct audio_output *ao, struct audio_format *audio_format, GError **error)
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 *)ao;
ComponentDescription desc;
Component comp;
AURenderCallbackStruct callback;
AudioStreamBasicDescription stream_description;
OSStatus status;
ComponentResult result;
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
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.componentSubType = od->component_subtype;
desc.componentSubType = oo->component_subtype;
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
desc.componentFlags = 0;
desc.componentFlagsMask = 0;
comp = FindNextComponent(NULL, &desc);
Component comp = FindNextComponent(NULL, &desc);
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");
return false;
}
status = OpenAComponent(comp, &od->au);
OSStatus status = OpenAComponent(comp, &oo->au);
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",
GetMacOSStatusCommentString(status));
return false;
}
status = AudioUnitInitialize(od->au);
if (status != noErr) {
CloseComponent(od->au);
g_set_error(error, osx_output_quark(), status,
"Unable to initialize OS X audio unit: %s",
GetMacOSStatusCommentString(status));
if (!osx_output_set_device(oo, error_r)) {
CloseComponent(oo->au);
return false;
}
if (!osx_output_set_device(od, error))
return false;
AURenderCallbackStruct callback;
callback.inputProc = osx_render;
callback.inputProcRefCon = od;
callback.inputProcRefCon = oo;
result = AudioUnitSetProperty(od->au,
kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Input, 0,
&callback, sizeof(callback));
ComponentResult result =
AudioUnitSetProperty(oo->au,
kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Input, 0,
&callback, sizeof(callback));
if (result != noErr) {
AudioUnitUninitialize(od->au);
CloseComponent(od->au);
g_set_error(error, osx_output_quark(), result,
g_set_error(error_r, osx_output_quark(), result,
"unable to set callback for OS X audio unit");
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.mFormatID = kAudioFormatLinearPCM;
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.mChannelsPerFrame = audio_format->channels;
result = AudioUnitSetProperty(od->au, kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input, 0,
&stream_description,
sizeof(stream_description));
ComponentResult result =
AudioUnitSetProperty(od->au, kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input, 0,
&stream_description,
sizeof(stream_description));
if (result != noErr) {
AudioUnitUninitialize(od->au);
CloseComponent(od->au);
g_set_error(error, osx_output_quark(), result,
"Unable to set format on OS X device");
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 */
od->buffer_size = (audio_format->sample_rate) *
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);
if (status != 0) {
AudioUnitUninitialize(od->au);
g_set_error(error, osx_output_quark(), status,
"unable to start audio output: %s",
GetMacOSStatusCommentString(status));
@ -427,6 +439,8 @@ const struct audio_output_plugin osx_output_plugin = {
.test_default_device = osx_output_test_default_device,
.init = osx_output_init,
.finish = osx_output_finish,
.enable = osx_output_enable,
.disable = osx_output_disable,
.open = osx_output_open,
.close = osx_output_close,
.play = osx_output_play,