49#include <AudioUnit/AudioUnit.h>
50#include <Availability.h>
54#include <CoreAudio/AudioHardware.h>
70using namespace std::chrono;
76#define NO_DATA_STOP_SEC 2
77#define MOD_NAME "[CoreAudio play.] "
92 AudioUnitRenderActionFlags *inActionFlags,
93 const AudioTimeStamp *inTimeStamp,
94 UInt32 inBusNumber, UInt32 inNumFrames,
95 AudioBufferList *ioData);
98 AudioUnitRenderActionFlags *inActionFlags,
99 const AudioTimeStamp *inTimeStamp,
100 UInt32 inBusNumber, UInt32 inNumFrames,
101 AudioBufferList *ioData)
111 ioData->mBuffers[0].mDataByteSize = ret;
113 if(ret < write_bytes) {
118 ioData->mBuffers[0].mDataByteSize = ret;
153 " Core Audio - use fixed audio playback buffer instead of an adaptive one\n");
157 AudioStreamBasicDescription stream_desc;
159 OSStatus ret = noErr;
160 AURenderCallbackStruct renderStruct;
201 size =
sizeof(stream_desc);
202 ret = AudioUnitGetProperty(s->
auHALComponentInstance, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
203 0, &stream_desc, &size);
208 stream_desc.mSampleRate =
desc.sample_rate;
209 stream_desc.mFormatID = kAudioFormatLinearPCM;
210 stream_desc.mChannelsPerFrame =
desc.ch_count;
211 stream_desc.mBitsPerChannel =
desc.bps * 8;
212 stream_desc.mFormatFlags = kAudioFormatFlagIsSignedInteger|kAudioFormatFlagIsPacked;
213 stream_desc.mFramesPerPacket = 1;
214 s->
audio_packet_size = stream_desc.mBytesPerFrame = stream_desc.mBytesPerPacket = stream_desc.mFramesPerPacket *
desc.ch_count *
desc.bps;
216 ret = AudioUnitSetProperty(s->
auHALComponentInstance, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
217 0, &stream_desc,
sizeof(stream_desc));
224 renderStruct.inputProcRefCon = s;
226 kAudioUnitScope_Input, 0, &renderStruct,
sizeof(AURenderCallbackStruct));
258 propertyAddress.mSelector = kAudioDevicePropertyStreamConfiguration;
259 status = AudioObjectGetPropertyDataSize(*audioDevice, &propertyAddress, 0, NULL, &size);
260 if(kAudioHardwareNoError !=
status) {
265 AudioBufferList *bufferList =
static_cast<AudioBufferList *
>(malloc(size));
266 if(NULL == bufferList) {
270 status = AudioObjectGetPropertyData(*audioDevice, &propertyAddress, 0, NULL, &size, bufferList);
271 if(kAudioHardwareNoError !=
status || 0 == bufferList->mNumberBuffers) {
272 if(kAudioHardwareNoError !=
status)
283 AudioObjectPropertyAddress propertyAddress{};
284 propertyAddress.mSelector = kAudioHardwarePropertyDevices;
285 propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
287 propertyAddress.mSelector = kAudioDevicePropertyDeviceNameCFString;
288 CFStringRef deviceName = NULL;
289 UInt32 size =
sizeof(deviceName);
290 if (OSStatus ret = AudioObjectGetPropertyData(dev_id, &propertyAddress, 0, NULL, &size, &deviceName)) {
293 snprintf(namebuf, namebuf_len,
"CoreAudio device #%" PRIu32
" (unable to get name)", dev_id);
296 CFStringGetCString(deviceName, namebuf, namebuf_len, kCFStringEncodingMacRoman);
297 CFRelease(deviceName);
307 assert(dir == -1 || dir == 1);
310 snprintf((*available_devices)[0].
dev,
sizeof(*available_devices)[0].
dev,
312 snprintf((*available_devices)[0].
name,
313 sizeof(*available_devices)[0].
name,
"Default CoreAudio %s",
314 dir == -1 ?
"capture" :
"playback");
318 AudioDeviceID *dev_ids;
321 AudioObjectPropertyAddress propertyAddress;
323 propertyAddress.mSelector = kAudioHardwarePropertyDevices;
324 propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
326 ret = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &size);
331 dev_ids = (AudioDeviceID *) malloc(size);
332 dev_count = size /
sizeof(AudioDeviceID);
333 ret = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &size, dev_ids);
339 propertyAddress.mScope = dir == -1 ? kAudioObjectPropertyScopeInput : kAudioObjectPropertyScopeOutput;
340 for (
int i = 0; i < dev_count; ++i) {
350 (
char *) (*available_devices)[*
count - 1].
name);
351 snprintf((*available_devices)[*
count - 1].
dev,
sizeof (*available_devices)[*
count - 1].
dev,
352 ":%" PRIu32, dev_ids[i]);
374 for (
int i = 0; i <
count; ++i) {
375 if (strstr(devices[i].
name,
name) != NULL) {
376 return atoi(devices[i].
dev + 1);
390 cout <<
"Core Audio playback usage:\n";
391 col() <<
SBOLD(
SRED(
"\t-r coreaudio") <<
"[:<index>|:<name>] "
392 "[--param audio-buffer-len=<len_ms>] [--param audio-disable-adaptive-buffer]") <<
"\n";
393 col() <<
"where\n\t" <<
SBOLD(
"<name>") <<
" - device name substring (case sensitive)\n\n";
394 printf(
"Available CoreAudio playback devices:\n");
400 for (
int i = 0; i <
count; ++i) {
409 OSStatus ret = noErr;
411 AudioComponentDescription comp_desc;
412 AudioDeviceID device;
427 comp_desc.componentType = kAudioUnitType_Output;
432 comp_desc.componentSubType = kAudioUnitSubType_HALOutput;
436 comp_desc.componentManufacturer = kAudioUnitManufacturer_Apple;
437 comp_desc.componentFlags = 0;
438 comp_desc.componentFlagsMask = 0;
440 comp = AudioComponentFindNext(NULL, &comp_desc);
458 if (strcmp(cfg,
"help") == 0) {
463 if (strlen(cfg) > 0) {
466 }
catch (std::invalid_argument &e) {
468 if (device == UINT_MAX) {
471 "Wrong device index "
472 "or unrecognized name \"%s\"!\n",
478 AudioObjectPropertyAddress propertyAddress;
479 UInt32 size =
sizeof device;
480 propertyAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
481 propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
483 if ((ret = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &size, &device)) != noErr) {
488 char device_name[128];
495 "In case of problems, try \"--param audio-disable-adaptive-buffer\" "
500 kAudioOutputUnitProperty_CurrentDevice,
501 kAudioUnitScope_Global,
struct video_frame * frame
int void(** deleter)(void *))
This file contains definition of some common audio types.
struct audio_buffer_api audio_buffer_fns
struct audio_buffer * audio_buffer_init(int sample_rate, int bps, int ch_count, int suggested_latency_ms)
#define AUDIO_PLAYBACK_CTL_QUERY_FORMAT
#define AUDIO_PLAYBACK_ABI_VERSION
int color_printf(const char *format,...)
static OSStatus theRenderProc(void *inRefCon, AudioUnitRenderActionFlags *inActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumFrames, AudioBufferList *ioData)
static bool audio_play_ca_ctl(void *state, int request, void *data, size_t *len)
static const struct audio_playback_info aplay_coreaudio_info
constexpr int DEFAULT_BUFLEN_MS
void audio_ca_probe(struct device_info **available_devices, int *count, int dir)
static void audio_play_ca_put_frame(void *state, const struct audio_frame *frame)
AudioDeviceID audio_ca_get_device_by_name(const char *name, int dir)
void audio_ca_get_device_name(AudioDeviceID dev_id, size_t namebuf_len, char *namebuf)
static void audio_play_ca_done(void *state)
static bool is_requested_direction(AudioObjectPropertyAddress propertyAddress, AudioDeviceID *audioDevice)
static void audio_play_ca_help()
static void * audio_play_ca_init(const char *cfg)
static bool audio_play_ca_reconfigure(void *state, struct audio_desc desc)
static void audio_play_ca_probe(struct device_info **available_devices, int *count, void(**deleter)(void *))
void log_msg(int level, const char *format,...)
#define LOG_LEVEL_NOTICE
information that may be interesting
#define LOG_LEVEL_INFO
normal reporting
#define LOG_LEVEL_ERROR
general errors
#define LOG_LEVEL_WARNING
less severe errors
const char * get_commandline_param(const char *key)
#define ADD_TO_PARAM(param, doc)
@ LIBRARY_CLASS_AUDIO_PLAYBACK
#define REGISTER_MODULE(name, info, lclass, abi)
Registers module to global modules' registry.
#define kAudioObjectPropertyElementMain
const char * get_osstatus_str(OSStatus err)
struct audio_buffer_api ring_buffer_fns
struct ring_buffer * ring_buffer_init(int size)
int(* read)(void *buf, char *out, int max_len)
void(* write)(void *buf, const char *in, int len)
void(* destroy)(void *buf)
struct used to comunicate with audio capture/playback device
bool quiet
do not report buffer underruns if we do not receive data at all for a long period
AudioComponentInstance auHALComponentInstance
struct audio_buffer_api * buffer_fns
steady_clock::time_point last_audio_read