mirror of
https://github.com/Ryujinx/libsoundio.git
synced 2024-12-22 21:55:36 +00:00
CoreAudio: ability to open an input device
This commit is contained in:
parent
48d927d8e3
commit
29d55f59f7
|
@ -3,6 +3,9 @@
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
|
static const int OUTPUT_ELEMENT = 0;
|
||||||
|
static const int INPUT_ELEMENT = 1;
|
||||||
|
|
||||||
static AudioObjectPropertyAddress device_listen_props[] = {
|
static AudioObjectPropertyAddress device_listen_props[] = {
|
||||||
{
|
{
|
||||||
kAudioDevicePropertyDeviceHasChanged,
|
kAudioDevicePropertyDeviceHasChanged,
|
||||||
|
@ -826,7 +829,7 @@ static void device_thread_run(void *arg) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static OSStatus on_device_overload(AudioObjectID in_object_id, UInt32 in_number_addresses,
|
static OSStatus on_outstream_device_overload(AudioObjectID in_object_id, UInt32 in_number_addresses,
|
||||||
const AudioObjectPropertyAddress in_addresses[], void *in_client_data)
|
const AudioObjectPropertyAddress in_addresses[], void *in_client_data)
|
||||||
{
|
{
|
||||||
SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)in_client_data;
|
SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)in_client_data;
|
||||||
|
@ -847,11 +850,11 @@ static void outstream_destroy_ca(struct SoundIoPrivate *si, struct SoundIoOutStr
|
||||||
kAudioObjectPropertyScopeGlobal,
|
kAudioObjectPropertyScopeGlobal,
|
||||||
kAudioObjectPropertyElementMaster
|
kAudioObjectPropertyElementMaster
|
||||||
};
|
};
|
||||||
AudioObjectRemovePropertyListener(dca->device_id, &prop_address, on_device_overload, os);
|
AudioObjectRemovePropertyListener(dca->device_id, &prop_address, on_outstream_device_overload, os);
|
||||||
|
|
||||||
if (osca->output_instance) {
|
if (osca->instance) {
|
||||||
AudioOutputUnitStop(osca->output_instance);
|
AudioOutputUnitStop(osca->instance);
|
||||||
AudioComponentInstanceDispose(osca->output_instance);
|
AudioComponentInstanceDispose(osca->instance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -887,26 +890,24 @@ static int outstream_open_ca(struct SoundIoPrivate *si, struct SoundIoOutStreamP
|
||||||
outstream->buffer_duration,
|
outstream->buffer_duration,
|
||||||
device->buffer_duration_max);
|
device->buffer_duration_max);
|
||||||
|
|
||||||
AudioComponentDescription desc;
|
AudioComponentDescription desc = {0};
|
||||||
desc.componentType = kAudioUnitType_Output;
|
desc.componentType = kAudioUnitType_Output;
|
||||||
desc.componentSubType = kAudioUnitSubType_HALOutput;
|
desc.componentSubType = kAudioUnitSubType_HALOutput;
|
||||||
desc.componentFlags = 0;
|
|
||||||
desc.componentFlagsMask = 0;
|
|
||||||
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
|
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
|
||||||
|
|
||||||
AudioComponent output_comp = AudioComponentFindNext(nullptr, &desc);
|
AudioComponent component = AudioComponentFindNext(nullptr, &desc);
|
||||||
if (!output_comp) {
|
if (!component) {
|
||||||
outstream_destroy_ca(si, os);
|
outstream_destroy_ca(si, os);
|
||||||
return SoundIoErrorOpeningDevice;
|
return SoundIoErrorOpeningDevice;
|
||||||
}
|
}
|
||||||
|
|
||||||
OSStatus os_err;
|
OSStatus os_err;
|
||||||
if ((os_err = AudioComponentInstanceNew(output_comp, &osca->output_instance))) {
|
if ((os_err = AudioComponentInstanceNew(component, &osca->instance))) {
|
||||||
outstream_destroy_ca(si, os);
|
outstream_destroy_ca(si, os);
|
||||||
return SoundIoErrorOpeningDevice;
|
return SoundIoErrorOpeningDevice;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((os_err = AudioUnitInitialize(osca->output_instance))) {
|
if ((os_err = AudioUnitInitialize(osca->instance))) {
|
||||||
outstream_destroy_ca(si, os);
|
outstream_destroy_ca(si, os);
|
||||||
return SoundIoErrorOpeningDevice;
|
return SoundIoErrorOpeningDevice;
|
||||||
}
|
}
|
||||||
|
@ -921,23 +922,23 @@ static int outstream_open_ca(struct SoundIoPrivate *si, struct SoundIoOutStreamP
|
||||||
format.mChannelsPerFrame = outstream->layout.channel_count;
|
format.mChannelsPerFrame = outstream->layout.channel_count;
|
||||||
format.mBitsPerChannel = 32;
|
format.mBitsPerChannel = 32;
|
||||||
|
|
||||||
if ((os_err = AudioUnitSetProperty(osca->output_instance, kAudioOutputUnitProperty_CurrentDevice,
|
if ((os_err = AudioUnitSetProperty(osca->instance, kAudioOutputUnitProperty_CurrentDevice,
|
||||||
kAudioUnitScope_Input, 0, &dca->device_id, sizeof(AudioDeviceID))))
|
kAudioUnitScope_Input, OUTPUT_ELEMENT, &dca->device_id, sizeof(AudioDeviceID))))
|
||||||
{
|
{
|
||||||
outstream_destroy_ca(si, os);
|
outstream_destroy_ca(si, os);
|
||||||
return SoundIoErrorOpeningDevice;
|
return SoundIoErrorOpeningDevice;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((os_err = AudioUnitSetProperty(osca->output_instance, kAudioUnitProperty_StreamFormat,
|
if ((os_err = AudioUnitSetProperty(osca->instance, kAudioUnitProperty_StreamFormat,
|
||||||
kAudioUnitScope_Input, 0, &format, sizeof(AudioStreamBasicDescription))))
|
kAudioUnitScope_Input, OUTPUT_ELEMENT, &format, sizeof(AudioStreamBasicDescription))))
|
||||||
{
|
{
|
||||||
outstream_destroy_ca(si, os);
|
outstream_destroy_ca(si, os);
|
||||||
return SoundIoErrorOpeningDevice;
|
return SoundIoErrorOpeningDevice;
|
||||||
}
|
}
|
||||||
|
|
||||||
AURenderCallbackStruct render_callback = {write_callback_ca, os};
|
AURenderCallbackStruct render_callback = {write_callback_ca, os};
|
||||||
if ((os_err = AudioUnitSetProperty(osca->output_instance, kAudioUnitProperty_SetRenderCallback,
|
if ((os_err = AudioUnitSetProperty(osca->instance, kAudioUnitProperty_SetRenderCallback,
|
||||||
kAudioUnitScope_Input, 0, &render_callback, sizeof(AURenderCallbackStruct))))
|
kAudioUnitScope_Input, OUTPUT_ELEMENT, &render_callback, sizeof(AURenderCallbackStruct))))
|
||||||
{
|
{
|
||||||
outstream_destroy_ca(si, os);
|
outstream_destroy_ca(si, os);
|
||||||
return SoundIoErrorOpeningDevice;
|
return SoundIoErrorOpeningDevice;
|
||||||
|
@ -946,7 +947,7 @@ static int outstream_open_ca(struct SoundIoPrivate *si, struct SoundIoOutStreamP
|
||||||
AudioObjectPropertyAddress prop_address = {
|
AudioObjectPropertyAddress prop_address = {
|
||||||
kAudioDevicePropertyBufferFrameSize,
|
kAudioDevicePropertyBufferFrameSize,
|
||||||
kAudioObjectPropertyScopeInput,
|
kAudioObjectPropertyScopeInput,
|
||||||
kAudioObjectPropertyElementMaster
|
OUTPUT_ELEMENT
|
||||||
};
|
};
|
||||||
UInt32 buffer_frame_size = outstream->buffer_duration * outstream->sample_rate;
|
UInt32 buffer_frame_size = outstream->buffer_duration * outstream->sample_rate;
|
||||||
if ((os_err = AudioObjectSetPropertyData(dca->device_id, &prop_address,
|
if ((os_err = AudioObjectSetPropertyData(dca->device_id, &prop_address,
|
||||||
|
@ -958,8 +959,9 @@ static int outstream_open_ca(struct SoundIoPrivate *si, struct SoundIoOutStreamP
|
||||||
|
|
||||||
prop_address.mSelector = kAudioDeviceProcessorOverload;
|
prop_address.mSelector = kAudioDeviceProcessorOverload;
|
||||||
prop_address.mScope = kAudioObjectPropertyScopeGlobal;
|
prop_address.mScope = kAudioObjectPropertyScopeGlobal;
|
||||||
|
prop_address.mElement = OUTPUT_ELEMENT;
|
||||||
if ((os_err = AudioObjectAddPropertyListener(dca->device_id, &prop_address,
|
if ((os_err = AudioObjectAddPropertyListener(dca->device_id, &prop_address,
|
||||||
on_device_overload, os)))
|
on_outstream_device_overload, os)))
|
||||||
{
|
{
|
||||||
outstream_destroy_ca(si, os);
|
outstream_destroy_ca(si, os);
|
||||||
return SoundIoErrorOpeningDevice;
|
return SoundIoErrorOpeningDevice;
|
||||||
|
@ -972,11 +974,11 @@ static int outstream_pause_ca(struct SoundIoPrivate *si, struct SoundIoOutStream
|
||||||
SoundIoOutStreamCoreAudio *osca = &os->backend_data.coreaudio;
|
SoundIoOutStreamCoreAudio *osca = &os->backend_data.coreaudio;
|
||||||
OSStatus os_err;
|
OSStatus os_err;
|
||||||
if (pause) {
|
if (pause) {
|
||||||
if ((os_err = AudioOutputUnitStop(osca->output_instance))) {
|
if ((os_err = AudioOutputUnitStop(osca->instance))) {
|
||||||
return SoundIoErrorStreaming;
|
return SoundIoErrorStreaming;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if ((os_err = AudioOutputUnitStart(osca->output_instance))) {
|
if ((os_err = AudioOutputUnitStart(osca->instance))) {
|
||||||
return SoundIoErrorStreaming;
|
return SoundIoErrorStreaming;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1026,17 +1028,183 @@ static int outstream_clear_buffer_ca(struct SoundIoPrivate *si, struct SoundIoOu
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static OSStatus on_instream_device_overload(AudioObjectID in_object_id, UInt32 in_number_addresses,
|
||||||
static int instream_open_ca(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is) {
|
const AudioObjectPropertyAddress in_addresses[], void *in_client_data)
|
||||||
soundio_panic("TODO open");
|
{
|
||||||
|
//SoundIoInStreamPrivate *os = (SoundIoInStreamPrivate *)in_client_data;
|
||||||
|
//SoundIoInStream *instream = &os->pub;
|
||||||
|
fprintf(stderr, "TODO overflow\n");
|
||||||
|
//instream->underflow_callback(instream);
|
||||||
|
return noErr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void instream_destroy_ca(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is) {
|
static void instream_destroy_ca(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is) {
|
||||||
soundio_panic("TODO destroy");
|
SoundIoInStreamCoreAudio *isca = &is->backend_data.coreaudio;
|
||||||
|
SoundIoInStream *instream = &is->pub;
|
||||||
|
SoundIoDevice *device = instream->device;
|
||||||
|
SoundIoDevicePrivate *dev = (SoundIoDevicePrivate *)device;
|
||||||
|
SoundIoDeviceCoreAudio *dca = &dev->backend_data.coreaudio;
|
||||||
|
|
||||||
|
AudioObjectPropertyAddress prop_address = {
|
||||||
|
kAudioDeviceProcessorOverload,
|
||||||
|
kAudioObjectPropertyScopeGlobal,
|
||||||
|
kAudioObjectPropertyElementMaster
|
||||||
|
};
|
||||||
|
AudioObjectRemovePropertyListener(dca->device_id, &prop_address, on_instream_device_overload, is);
|
||||||
|
|
||||||
|
if (isca->instance) {
|
||||||
|
AudioOutputUnitStop(isca->instance);
|
||||||
|
AudioComponentInstanceDispose(isca->instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static OSStatus read_callback_ca(void *userdata, AudioUnitRenderActionFlags *io_action_flags,
|
||||||
|
const AudioTimeStamp *in_time_stamp, UInt32 in_bus_number, UInt32 in_number_frames,
|
||||||
|
AudioBufferList *io_data)
|
||||||
|
{
|
||||||
|
SoundIoInStreamPrivate *is = (SoundIoInStreamPrivate *) userdata;
|
||||||
|
SoundIoInStream *instream = &is->pub;
|
||||||
|
SoundIoInStreamCoreAudio *isca = &is->backend_data.coreaudio;
|
||||||
|
|
||||||
|
isca->io_data = io_data;
|
||||||
|
isca->buffer_index = 0;
|
||||||
|
isca->frames_left = in_number_frames;
|
||||||
|
instream->read_callback(instream, isca->frames_left, isca->frames_left);
|
||||||
|
isca->io_data = nullptr;
|
||||||
|
|
||||||
|
return noErr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int instream_open_ca(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is) {
|
||||||
|
SoundIoInStreamCoreAudio *isca = &is->backend_data.coreaudio;
|
||||||
|
SoundIoInStream *instream = &is->pub;
|
||||||
|
SoundIoDevice *device = instream->device;
|
||||||
|
SoundIoDevicePrivate *dev = (SoundIoDevicePrivate *)device;
|
||||||
|
SoundIoDeviceCoreAudio *dca = &dev->backend_data.coreaudio;
|
||||||
|
|
||||||
|
if (instream->buffer_duration == 0.0)
|
||||||
|
instream->buffer_duration = device->buffer_duration_current;
|
||||||
|
|
||||||
|
instream->buffer_duration = clamp(
|
||||||
|
device->buffer_duration_min,
|
||||||
|
instream->buffer_duration,
|
||||||
|
device->buffer_duration_max);
|
||||||
|
|
||||||
|
AudioComponentDescription desc = {0};
|
||||||
|
desc.componentType = kAudioUnitType_Output;
|
||||||
|
desc.componentSubType = kAudioUnitSubType_HALOutput;
|
||||||
|
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
|
||||||
|
|
||||||
|
AudioComponent component = AudioComponentFindNext(nullptr, &desc);
|
||||||
|
if (!component) {
|
||||||
|
instream_destroy_ca(si, is);
|
||||||
|
return SoundIoErrorOpeningDevice;
|
||||||
|
}
|
||||||
|
|
||||||
|
OSStatus os_err;
|
||||||
|
if ((os_err = AudioComponentInstanceNew(component, &isca->instance))) {
|
||||||
|
instream_destroy_ca(si, is);
|
||||||
|
return SoundIoErrorOpeningDevice;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((os_err = AudioUnitInitialize(isca->instance))) {
|
||||||
|
instream_destroy_ca(si, is);
|
||||||
|
return SoundIoErrorOpeningDevice;
|
||||||
|
}
|
||||||
|
|
||||||
|
UInt32 enable_io = 1;
|
||||||
|
if ((os_err = AudioUnitSetProperty(isca->instance, kAudioOutputUnitProperty_EnableIO,
|
||||||
|
kAudioUnitScope_Input, INPUT_ELEMENT, &enable_io, sizeof(UInt32))))
|
||||||
|
{
|
||||||
|
instream_destroy_ca(si, is);
|
||||||
|
return SoundIoErrorOpeningDevice;
|
||||||
|
}
|
||||||
|
|
||||||
|
enable_io = 0;
|
||||||
|
if ((os_err = AudioUnitSetProperty(isca->instance, kAudioOutputUnitProperty_EnableIO,
|
||||||
|
kAudioUnitScope_Output, OUTPUT_ELEMENT, &enable_io, sizeof(UInt32))))
|
||||||
|
{
|
||||||
|
instream_destroy_ca(si, is);
|
||||||
|
return SoundIoErrorOpeningDevice;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((os_err = AudioUnitSetProperty(isca->instance, kAudioOutputUnitProperty_CurrentDevice,
|
||||||
|
kAudioUnitScope_Output, INPUT_ELEMENT, &dca->device_id, sizeof(AudioDeviceID))))
|
||||||
|
{
|
||||||
|
instream_destroy_ca(si, is);
|
||||||
|
return SoundIoErrorOpeningDevice;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
AudioStreamBasicDescription format = {0};
|
||||||
|
format.mSampleRate = instream->sample_rate;
|
||||||
|
format.mFormatID = kAudioFormatLinearPCM;
|
||||||
|
format.mFormatFlags = kAudioFormatFlagIsFloat;
|
||||||
|
format.mBytesPerPacket = instream->bytes_per_frame;
|
||||||
|
format.mFramesPerPacket = 1;
|
||||||
|
format.mBytesPerFrame = instream->bytes_per_frame;
|
||||||
|
format.mChannelsPerFrame = instream->layout.channel_count;
|
||||||
|
format.mBitsPerChannel = 32;
|
||||||
|
|
||||||
|
if ((os_err = AudioUnitSetProperty(isca->instance, kAudioUnitProperty_StreamFormat,
|
||||||
|
kAudioUnitScope_Output, INPUT_ELEMENT, &format, sizeof(AudioStreamBasicDescription))))
|
||||||
|
{
|
||||||
|
instream_destroy_ca(si, is);
|
||||||
|
return SoundIoErrorOpeningDevice;
|
||||||
|
}
|
||||||
|
|
||||||
|
AURenderCallbackStruct input_callback = {read_callback_ca, is};
|
||||||
|
if ((os_err = AudioUnitSetProperty(isca->instance, kAudioOutputUnitProperty_SetInputCallback,
|
||||||
|
kAudioUnitScope_Output, INPUT_ELEMENT, &input_callback, sizeof(AURenderCallbackStruct))))
|
||||||
|
{
|
||||||
|
instream_destroy_ca(si, is);
|
||||||
|
return SoundIoErrorOpeningDevice;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
AudioObjectPropertyAddress prop_address = {
|
||||||
|
kAudioDevicePropertyBufferFrameSize,
|
||||||
|
kAudioObjectPropertyScopeOutput,
|
||||||
|
INPUT_ELEMENT
|
||||||
|
};
|
||||||
|
UInt32 buffer_frame_size = instream->buffer_duration * instream->sample_rate;
|
||||||
|
if ((os_err = AudioObjectSetPropertyData(dca->device_id, &prop_address,
|
||||||
|
0, nullptr, sizeof(UInt32), &buffer_frame_size)))
|
||||||
|
{
|
||||||
|
instream_destroy_ca(si, is);
|
||||||
|
return SoundIoErrorOpeningDevice;
|
||||||
|
}
|
||||||
|
|
||||||
|
prop_address.mSelector = kAudioDeviceProcessorOverload;
|
||||||
|
prop_address.mScope = kAudioObjectPropertyScopeGlobal;
|
||||||
|
if ((os_err = AudioObjectAddPropertyListener(dca->device_id, &prop_address,
|
||||||
|
on_instream_device_overload, is)))
|
||||||
|
{
|
||||||
|
instream_destroy_ca(si, is);
|
||||||
|
return SoundIoErrorOpeningDevice;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int instream_pause_ca(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is, bool pause) {
|
||||||
|
SoundIoInStreamCoreAudio *isca = &is->backend_data.coreaudio;
|
||||||
|
OSStatus os_err;
|
||||||
|
if (pause) {
|
||||||
|
if ((os_err = AudioOutputUnitStop(isca->instance))) {
|
||||||
|
return SoundIoErrorStreaming;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ((os_err = AudioOutputUnitStart(isca->instance))) {
|
||||||
|
return SoundIoErrorStreaming;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int instream_start_ca(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is) {
|
static int instream_start_ca(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is) {
|
||||||
soundio_panic("TODO start");
|
return instream_pause_ca(si, is, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int instream_begin_read_ca(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is,
|
static int instream_begin_read_ca(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is,
|
||||||
|
@ -1049,9 +1217,6 @@ static int instream_end_read_ca(struct SoundIoPrivate *si, struct SoundIoInStrea
|
||||||
soundio_panic("TODO end read");
|
soundio_panic("TODO end read");
|
||||||
}
|
}
|
||||||
|
|
||||||
static int instream_pause_ca(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is, bool pause) {
|
|
||||||
soundio_panic("TODO pause");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Possible errors:
|
// Possible errors:
|
||||||
|
|
|
@ -42,7 +42,7 @@ struct SoundIoCoreAudio {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SoundIoOutStreamCoreAudio {
|
struct SoundIoOutStreamCoreAudio {
|
||||||
AudioComponentInstance output_instance;
|
AudioComponentInstance instance;
|
||||||
AudioBufferList *io_data;
|
AudioBufferList *io_data;
|
||||||
int buffer_index;
|
int buffer_index;
|
||||||
int frames_left;
|
int frames_left;
|
||||||
|
@ -51,6 +51,10 @@ struct SoundIoOutStreamCoreAudio {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SoundIoInStreamCoreAudio {
|
struct SoundIoInStreamCoreAudio {
|
||||||
|
AudioComponentInstance instance;
|
||||||
|
AudioBufferList *io_data;
|
||||||
|
int buffer_index;
|
||||||
|
int frames_left;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in a new issue