static bool coreaudio_init_format(struct coreaudio_data *ca) { AudioStreamBasicDescription desc; OSStatus stat; UInt32 size = sizeof(desc); stat = get_property(ca->unit, kAudioUnitProperty_StreamFormat, SCOPE_INPUT, BUS_INPUT, &desc, &size); if (!ca_success(stat, ca, "coreaudio_init_format", "get input format")) return false; /* Certain types of devices have no limit on channel count, and * there's no way to know the actual number of channels it's using, * so if we encounter this situation just force to stereo */ if (desc.mChannelsPerFrame > 8) { desc.mChannelsPerFrame = 2; desc.mBytesPerFrame = 2 * desc.mBitsPerChannel / 8; desc.mBytesPerPacket = desc.mFramesPerPacket * desc.mBytesPerFrame; } stat = set_property(ca->unit, kAudioUnitProperty_StreamFormat, SCOPE_OUTPUT, BUS_INPUT, &desc, size); if (!ca_success(stat, ca, "coreaudio_init_format", "set output format")) return false; if (desc.mFormatID != kAudioFormatLinearPCM) { ca_warn(ca, "coreaudio_init_format", "format is not PCM"); return false; } ca->format = convert_ca_format(desc.mFormatFlags, desc.mBitsPerChannel); if (ca->format == AUDIO_FORMAT_UNKNOWN) { ca_warn(ca, "coreaudio_init_format", "unknown format flags: " "%u, bits: %u", (unsigned int)desc.mFormatFlags, (unsigned int)desc.mBitsPerChannel); return false; } ca->sample_rate = (uint32_t)desc.mSampleRate; ca->speakers = convert_ca_speaker_layout(desc.mChannelsPerFrame); if (ca->speakers == SPEAKERS_UNKNOWN) { ca_warn(ca, "coreaudio_init_format", "unknown speaker layout: " "%u channels", (unsigned int)desc.mChannelsPerFrame); return false; } return true; }
static OSStatus input_callback( void *data, AudioUnitRenderActionFlags *action_flags, const AudioTimeStamp *ts_data, UInt32 bus_num, UInt32 frames, AudioBufferList *ignored_buffers) { struct coreaudio_data *ca = data; OSStatus stat; struct source_audio audio; stat = AudioUnitRender(ca->unit, action_flags, ts_data, bus_num, frames, ca->buf_list); if (!ca_success(stat, ca, "input_callback", "audio retrieval")) return noErr; for (UInt32 i = 0; i < ca->buf_list->mNumberBuffers; i++) audio.data[i] = ca->buf_list->mBuffers[i].mData; audio.frames = frames; audio.speakers = ca->speakers; audio.format = ca->format; audio.samples_per_sec = ca->sample_rate; audio.timestamp = ts_data->mHostTime; obs_source_output_audio(ca->source, &audio); UNUSED_PARAMETER(ignored_buffers); return noErr; }
static bool coreaudio_init_buffer(struct coreaudio_data *ca) { UInt32 buf_size = 0; UInt32 size = 0; UInt32 frames = 0; OSStatus stat; AudioObjectPropertyAddress addr = { kAudioDevicePropertyStreamConfiguration, kAudioDevicePropertyScopeInput, kAudioObjectPropertyElementMaster }; stat = AudioObjectGetPropertyDataSize(ca->device_id, &addr, 0, NULL, &buf_size); if (!ca_success(stat, ca, "coreaudio_init_buffer", "get list size")) return false; size = sizeof(frames); stat = get_property(ca->unit, kAudioDevicePropertyBufferFrameSize, SCOPE_GLOBAL, 0, &frames, &size); if (!ca_success(stat, ca, "coreaudio_init_buffer", "get frame size")) return false; /* ---------------------- */ ca->buf_list = bmalloc(buf_size); stat = AudioObjectGetPropertyData(ca->device_id, &addr, 0, NULL, &buf_size, ca->buf_list); if (!ca_success(stat, ca, "coreaudio_init_buffer", "allocate")) { bfree(ca->buf_list); ca->buf_list = NULL; return false; } for (UInt32 i = 0; i < ca->buf_list->mNumberBuffers; i++) { size = ca->buf_list->mBuffers[i].mDataByteSize; ca->buf_list->mBuffers[i].mData = bmalloc(size); } return true; }
static bool coreaudio_init_format(struct coreaudio_data *ca) { AudioStreamBasicDescription desc; OSStatus stat; UInt32 size = sizeof(desc); stat = get_property(ca->unit, kAudioUnitProperty_StreamFormat, SCOPE_INPUT, BUS_INPUT, &desc, &size); if (!ca_success(stat, ca, "coreaudio_init_format", "get input format")) return false; stat = set_property(ca->unit, kAudioUnitProperty_StreamFormat, SCOPE_OUTPUT, BUS_INPUT, &desc, size); if (!ca_success(stat, ca, "coreaudio_init_format", "set output format")) return false; if (desc.mFormatID != kAudioFormatLinearPCM) { ca_warn(ca, "coreaudio_init_format", "format is not PCM"); return false; } ca->format = convert_ca_format(desc.mFormatFlags, desc.mBitsPerChannel); if (ca->format == AUDIO_FORMAT_UNKNOWN) { ca_warn(ca, "coreaudio_init_format", "unknown format flags: " "%u, bits: %u", (unsigned int)desc.mFormatFlags, (unsigned int)desc.mBitsPerChannel); return false; } ca->sample_rate = (uint32_t)desc.mSampleRate; ca->speakers = convert_ca_speaker_layout(desc.mChannelsPerFrame); if (ca->speakers == SPEAKERS_UNKNOWN) { ca_warn(ca, "coreaudio_init_format", "unknown speaker layout: " "%u channels", (unsigned int)desc.mChannelsPerFrame); return false; } return true; }
static bool coreaudio_init_hooks(struct coreaudio_data *ca) { OSStatus stat; AURenderCallbackStruct callback_info = { .inputProc = input_callback, .inputProcRefCon = ca }; stat = add_listener(ca, kAudioDevicePropertyDeviceIsAlive); if (!ca_success(stat, ca, "coreaudio_init_hooks", "set disconnect callback")) return false; stat = add_listener(ca, PROPERTY_FORMATS); if (!ca_success(stat, ca, "coreaudio_init_hooks", "set format change callback")) return false; if (ca->default_device) { AudioObjectPropertyAddress addr = { PROPERTY_DEFAULT_DEVICE, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; stat = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &addr, notification_callback, ca); if (!ca_success(stat, ca, "coreaudio_init_hooks", "set device change callback")) return false; } stat = set_property(ca->unit, kAudioOutputUnitProperty_SetInputCallback, SCOPE_GLOBAL, 0, &callback_info, sizeof(callback_info)); if (!ca_success(stat, ca, "coreaudio_init_hooks", "set input callback")) return false; return true; }