2 * Copyright (C) 2015 Robin Gareus <robin@gareus.org>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 #include "coreaudio_pcmio.h"
23 static OSStatus hardwarePropertyChangeCallback (AudioHardwarePropertyID inPropertyID, void* arg) {
24 if (inPropertyID == kAudioHardwarePropertyDevices) {
25 CoreAudioPCM * self = static_cast<CoreAudioPCM*>(arg);
26 self->hwPropertyChange();
31 CoreAudioPCM::CoreAudioPCM ()
34 , _inputAudioBufferList (0)
36 , _capture_channels (0)
37 , _playback_channels (0)
40 , _process_callback (0)
45 #ifdef COREAUDIO_108 // TODO
46 CFRunLoopRef theRunLoop = NULL;
47 AudioObjectPropertyAddress property = { kAudioHardwarePropertyRunLoop, kAudioObjectPropertyScopeGlobal, kAudioHardwarePropertyDevices };
48 AudioObjectSetPropertyData (kAudioObjectSystemObject, &property, 0, NULL, sizeof(CFRunLoopRef), &theRunLoop);
50 AudioHardwareAddPropertyListener (kAudioHardwarePropertyDevices, hardwarePropertyChangeCallback, this);
53 CoreAudioPCM::~CoreAudioPCM ()
61 AudioHardwareRemovePropertyListener(kAudioHardwarePropertyDevices, hardwarePropertyChangeCallback);
62 free(_inputAudioBufferList);
67 CoreAudioPCM::hwPropertyChange() {
68 printf("hardwarePropertyChangeCallback\n");
73 CoreAudioPCM::discover() {
77 // TODO trymutex lock.
80 delete _deviceIDs; _deviceIDs = 0;
81 free(_device_ins); _device_ins = 0;
82 free(_device_outs); _device_outs = 0;
87 AudioObjectPropertyAddress propertyAddress;
88 propertyAddress.mSelector = kAudioHardwarePropertyDevices;
89 propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
90 propertyAddress.mElement = kAudioObjectPropertyElementMaster;
91 err = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &propSize);
93 err = AudioHardwareGetPropertyInfo (kAudioHardwarePropertyDevices, &propSize, NULL);
96 _numDevices = propSize / sizeof (AudioDeviceID);
97 propSize = _numDevices * sizeof (AudioDeviceID);
99 _deviceIDs = new AudioDeviceID[_numDevices];
100 _device_ins = (uint32_t*) calloc(_numDevices, sizeof(uint32_t));
101 _device_outs = (uint32_t*) calloc(_numDevices, sizeof(uint32_t));
104 propertyAddress.mSelector = kAudioHardwarePropertyDevices;
105 err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &propSize, _deviceIDs);
107 err = AudioHardwareGetProperty (kAudioHardwarePropertyDevices, &propSize, _deviceIDs);
110 for (size_t deviceIndex = 0; deviceIndex < _numDevices; deviceIndex++) {
114 propertyAddress.mSelector = kAudioDevicePropertyDeviceName;
115 propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
116 err = AudioObjectGetPropertyData(_deviceIDs[deviceIndex], &propertyAddress, 0, NULL, &propSize, deviceName);
118 err = AudioDeviceGetProperty(_deviceIDs[deviceIndex], 0, 0, kAudioDevicePropertyDeviceName, &propSize, deviceName);
121 if (kAudioHardwareNoError != err) {
122 fprintf(stderr, "device name query failed: %i\n", err);
127 UInt32 outputChannelCount = 0;
128 UInt32 inputChannelCount = 0;
129 AudioBufferList *bufferList = NULL;
131 /* query number of inputs */
134 propertyAddress.mSelector = kAudioDevicePropertyStreamConfiguration;
135 propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
136 err = AudioObjectGetPropertyDataSize(_deviceIDs[deviceIndex], &propertyAddress, 0, NULL, &size);
137 if (kAudioHardwareNoError != err) {
138 fprintf(stderr, "kAudioDevicePropertyStreamConfiguration failed: %i\n", err);
142 bufferList = (AudioBufferList *)(malloc(size));
144 if (!bufferList) { fprintf(stderr, "OUT OF MEMORY\n"); break; }
146 err = AudioObjectGetPropertyData(_deviceIDs[deviceIndex], &propertyAddress, 0, NULL, &size, bufferList);
149 err = AudioDeviceGetPropertyInfo (_deviceIDs[deviceIndex], 0, AUHAL_OUTPUT_ELEMENT, kAudioDevicePropertyStreamConfiguration, &propSize, NULL);
150 if (kAudioHardwareNoError != err) {
151 fprintf(stderr, "kAudioDevicePropertyStreamConfiguration failed: %i\n", err);
154 bufferList = (AudioBufferList *)(malloc(size));
156 if (!bufferList) { fprintf(stderr, "OUT OF MEMORY\n"); break; }
158 bufferList->mNumberBuffers = 0;
159 err = AudioDeviceGetProperty(_deviceIDs[deviceIndex], 0, AUHAL_OUTPUT_ELEMENT, kAudioDevicePropertyStreamConfiguration, &size, bufferList);
161 if(kAudioHardwareNoError != err) {
162 fprintf(stderr, "kAudioDevicePropertyStreamConfiguration failed: %i\n", err);
167 for(UInt32 j = 0; j < bufferList->mNumberBuffers; ++j) {
168 outputChannelCount += bufferList->mBuffers[j].mNumberChannels;
173 /* query number of inputs */
176 propertyAddress.mSelector = kAudioDevicePropertyStreamConfiguration;
177 propertyAddress.mScope = kAudioDevicePropertyScopeInput;
178 err = AudioObjectGetPropertyDataSize(_deviceIDs[deviceIndex], &propertyAddress, 0, NULL, &size);
179 if (kAudioHardwareNoError != err) {
180 fprintf(stderr, "kAudioDevicePropertyStreamConfiguration failed: %i\n", err);
184 bufferList = (AudioBufferList *)(malloc(size));
186 if (!bufferList) { fprintf(stderr, "OUT OF MEMORY\n"); break; }
188 err = AudioObjectGetPropertyData(_deviceIDs[deviceIndex], &propertyAddress, 0, NULL, &size, bufferList);
190 err = AudioDeviceGetPropertyInfo (_deviceIDs[deviceIndex], 0, AUHAL_INPUT_ELEMENT, kAudioDevicePropertyStreamConfiguration, &propSize, NULL);
191 if (kAudioHardwareNoError != err) {
192 fprintf(stderr, "kAudioDevicePropertyStreamConfiguration failed: %i\n", err);
195 bufferList = (AudioBufferList *)(malloc(size));
197 if (!bufferList) { fprintf(stderr, "OUT OF MEMORY\n"); break; }
199 bufferList->mNumberBuffers = 0;
200 err = AudioDeviceGetProperty(_deviceIDs[deviceIndex], 0, AUHAL_INPUT_ELEMENT, kAudioDevicePropertyStreamConfiguration, &size, bufferList);
202 if(kAudioHardwareNoError != err) {
203 fprintf(stderr, "kAudioDevicePropertyStreamConfiguration failed: %i\n", err);
208 for(UInt32 j = 0; j < bufferList->mNumberBuffers; ++j) {
209 inputChannelCount += bufferList->mBuffers[j].mNumberChannels;
216 std::string dn = deviceName;
217 _device_ins[deviceIndex] = inputChannelCount;
218 _device_outs[deviceIndex] = outputChannelCount;
219 printf("CoreAudio Device: #%ld '%s' in:%d out:%d\n", deviceIndex, deviceName, inputChannelCount, outputChannelCount);
220 if (outputChannelCount > 0 && inputChannelCount > 0) {
221 _devices.insert (std::pair<size_t, std::string> (deviceIndex, dn));
228 CoreAudioPCM::pcm_stop ()
230 printf("CoreAudioPCM::pcm_stop\n");
233 AudioOutputUnitStop(_auhal);
234 AudioUnitUninitialize(_auhal);
236 AudioComponentInstanceDispose(_auhal);
238 CloseComponent(_auhal);
242 _capture_channels = 0;
243 _playback_channels = 0;
245 free(_inputAudioBufferList);
246 _inputAudioBufferList = 0;
249 _process_callback = 0;
253 static void PrintStreamDesc (AudioStreamBasicDescription *inDesc)
255 printf ("- - - - - - - - - - - - - - - - - - - -\n");
256 printf (" Sample Rate:%f", inDesc->mSampleRate);
257 printf (" Format ID:%.*s\n", (int)sizeof(inDesc->mFormatID), (char*)&inDesc->mFormatID);
258 printf (" Format Flags:%X\n", inDesc->mFormatFlags);
259 printf (" Bytes per Packet:%d\n", inDesc->mBytesPerPacket);
260 printf (" Frames per Packet:%d\n", inDesc->mFramesPerPacket);
261 printf (" Bytes per Frame:%d\n", inDesc->mBytesPerFrame);
262 printf (" Channels per Frame:%d\n", inDesc->mChannelsPerFrame);
263 printf (" Bits per Channel:%d\n", inDesc->mBitsPerChannel);
264 printf ("- - - - - - - - - - - - - - - - - - - -\n");
268 static OSStatus render_callback_ptr (
270 AudioUnitRenderActionFlags* ioActionFlags,
271 const AudioTimeStamp* inTimeStamp,
273 UInt32 inNumberFrames,
274 AudioBufferList* ioData)
276 CoreAudioPCM * d = static_cast<CoreAudioPCM*> (inRefCon);
277 return d->render_callback(ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData);
283 CoreAudioPCM::pcm_start (
284 uint32_t device_id_in, uint32_t device_id_out,
285 uint32_t sample_rate, uint32_t samples_per_period,
286 int (process_callback (void*)), void *process_arg)
292 if (device_id_out >= _numDevices || device_id_in >= _numDevices) {
296 _process_callback = process_callback;
297 _process_arg = process_arg;
298 _max_samples_per_period = samples_per_period;
299 _cur_samples_per_period = 0;
303 AudioStreamBasicDescription srcFormat, dstFormat;
305 AudioComponentDescription cd = {kAudioUnitType_Output, kAudioUnitSubType_HALOutput, kAudioUnitManufacturer_Apple, 0, 0};
306 AudioComponent HALOutput = AudioComponentFindNext(NULL, &cd);
307 if (!HALOutput) { goto error; }
309 err = AudioComponentInstanceNew(HALOutput, &_auhal);
310 if (err != noErr) { goto error; }
312 err = AudioUnitInitialize(_auhal);
313 if (err != noErr) { goto error; }
316 err = AudioUnitSetProperty(_auhal, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, AUHAL_INPUT_ELEMENT, &enableIO, sizeof(enableIO));
317 if (err != noErr) { goto error; }
319 err = AudioUnitSetProperty(_auhal, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, AUHAL_OUTPUT_ELEMENT, &enableIO, sizeof(enableIO));
320 if (err != noErr) { goto error; }
322 err = AudioUnitSetProperty(_auhal, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, AUHAL_OUTPUT_ELEMENT, &_deviceIDs[device_id_out], sizeof(AudioDeviceID));
323 if (err != noErr) { goto error; }
325 err = AudioUnitSetProperty(_auhal, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, AUHAL_INPUT_ELEMENT, &_deviceIDs[device_id_in], sizeof(AudioDeviceID));
326 if (err != noErr) { goto error; }
329 err = AudioUnitSetProperty(_auhal, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, AUHAL_INPUT_ELEMENT, (UInt32*)&_max_samples_per_period, sizeof(UInt32));
330 if (err != noErr) { goto error; }
331 err = AudioUnitSetProperty(_auhal, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, AUHAL_OUTPUT_ELEMENT, (UInt32*)&_max_samples_per_period, sizeof(UInt32));
332 if (err != noErr) { goto error; }
336 srcFormat.mSampleRate = sample_rate;
337 srcFormat.mFormatID = kAudioFormatLinearPCM;
338 srcFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kLinearPCMFormatFlagIsNonInterleaved;
339 srcFormat.mBytesPerPacket = sizeof(float);
340 srcFormat.mFramesPerPacket = 1;
341 srcFormat.mBytesPerFrame = sizeof(float);
342 srcFormat.mChannelsPerFrame = _device_ins[device_id_in];
343 srcFormat.mBitsPerChannel = 32;
345 err = AudioUnitSetProperty(_auhal, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, AUHAL_INPUT_ELEMENT, &srcFormat, sizeof(AudioStreamBasicDescription));
346 if (err != noErr) { goto error; }
348 dstFormat.mSampleRate = sample_rate;
349 dstFormat.mFormatID = kAudioFormatLinearPCM;
350 dstFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kLinearPCMFormatFlagIsNonInterleaved;
351 dstFormat.mBytesPerPacket = sizeof(float);
352 dstFormat.mFramesPerPacket = 1;
353 dstFormat.mBytesPerFrame = sizeof(float);
354 dstFormat.mChannelsPerFrame = _device_outs[device_id_out];
355 dstFormat.mBitsPerChannel = 32;
357 err = AudioUnitSetProperty(_auhal, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, AUHAL_OUTPUT_ELEMENT, &dstFormat, sizeof(AudioStreamBasicDescription));
358 if (err != noErr) { goto error; }
361 size = sizeof(AudioStreamBasicDescription);
362 err = AudioUnitGetProperty(_auhal, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, AUHAL_INPUT_ELEMENT, &srcFormat, &size);
363 if (err != noErr) { goto error; }
364 _capture_channels = srcFormat.mChannelsPerFrame;
366 PrintStreamDesc(&srcFormat);
369 size = sizeof(AudioStreamBasicDescription);
370 err = AudioUnitGetProperty(_auhal, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, AUHAL_OUTPUT_ELEMENT, &dstFormat, &size);
371 if (err != noErr) { goto error; }
372 _playback_channels = dstFormat.mChannelsPerFrame;
375 PrintStreamDesc(&dstFormat);
378 _inputAudioBufferList = (AudioBufferList*)malloc(sizeof(UInt32) + _capture_channels * sizeof(AudioBuffer));
381 AURenderCallbackStruct renderCallback;
382 memset (&renderCallback, 0, sizeof (renderCallback));
383 renderCallback.inputProc = render_callback_ptr;
384 renderCallback.inputProcRefCon = this;
385 err = AudioUnitSetProperty(_auhal,
386 kAudioUnitProperty_SetRenderCallback,
387 kAudioUnitScope_Output, AUHAL_OUTPUT_ELEMENT,
388 &renderCallback, sizeof (renderCallback));
389 if (err != noErr) { goto error; }
391 printf("SETUP OK..\n");
393 if (AudioOutputUnitStart(_auhal) == noErr) {
394 printf("Coreaudio Started..\n");
407 CoreAudioPCM::render_callback (
408 AudioUnitRenderActionFlags* ioActionFlags,
409 const AudioTimeStamp* inTimeStamp,
411 UInt32 inNumberFrames,
412 AudioBufferList* ioData)
416 assert(_max_samples_per_period >= inNumberFrames);
417 assert(ioData->mNumberBuffers = _playback_channels);
419 _cur_samples_per_period = inNumberFrames;
422 _inputAudioBufferList->mNumberBuffers = _capture_channels;
423 for (int i = 0; i < _capture_channels; ++i) {
424 _inputAudioBufferList->mBuffers[i].mNumberChannels = 1;
425 _inputAudioBufferList->mBuffers[i].mDataByteSize = inNumberFrames * sizeof(float);
426 _inputAudioBufferList->mBuffers[i].mData = NULL;
429 retVal = AudioUnitRender(_auhal, ioActionFlags, inTimeStamp, AUHAL_INPUT_ELEMENT, inNumberFrames, _inputAudioBufferList);
431 if (retVal != kAudioHardwareNoError) {
432 char *rv = (char*)&retVal;
433 printf("ERR %c%c%c%c\n", rv[0], rv[1], rv[2], rv[3]);
434 if (_error_callback) {
435 _error_callback(_error_arg);
440 _outputAudioBufferList = ioData;
446 if (_process_callback) {
447 rv = _process_callback(_process_arg);
454 for (int i = 0; i < ioData->mNumberBuffers; ++i) {
455 float* ob = (float*) ioData->mBuffers[i].mData;
456 memset(ob, 0, sizeof(float) * inNumberFrames);
463 CoreAudioPCM::get_capture_channel (uint32_t chn, float *input, uint32_t n_samples)
465 if (!_in_process || chn > _capture_channels || n_samples > _cur_samples_per_period) {
468 assert(_inputAudioBufferList->mNumberBuffers > chn);
469 memcpy((void*)input, (void*)_inputAudioBufferList->mBuffers[chn].mData, sizeof(float) * n_samples);
474 CoreAudioPCM::set_playback_channel (uint32_t chn, const float *output, uint32_t n_samples)
476 if (!_in_process || chn > _playback_channels || n_samples > _cur_samples_per_period) {
480 assert(_outputAudioBufferList->mNumberBuffers > chn);
481 memcpy((void*)_outputAudioBufferList->mBuffers[chn].mData, (void*)output, sizeof(float) * n_samples);