1 /* FluidSynth - A Software Synthesizer
2  *
3  * Copyright (C) 2003  Peter Hanappe and others.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public License
7  * as published by the Free Software Foundation; either version 2.1 of
8  * the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free
17  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18  * 02110-1301, USA
19  */
20 
21 /* fluid_coreaudio.c
22  *
23  * Driver for the Apple's CoreAudio on MacOS X
24  *
25  */
26 
27 #include "fluid_adriver.h"
28 #include "fluid_settings.h"
29 
30 /*
31  * !!! Make sure that no include above includes <netinet/tcp.h> !!!
32  * It #defines some macros that collide with enum definitions of OpenTransportProviders.h, which is included from OSServices.h, included from CoreServices.h
33  *
34  * https://trac.macports.org/ticket/36962
35  */
36 
37 #if COREAUDIO_SUPPORT
38 #include <CoreServices/CoreServices.h>
39 #include <CoreAudio/CoreAudioTypes.h>
40 #include <CoreAudio/AudioHardware.h>
41 #include <AudioUnit/AudioUnit.h>
42 
43 /*
44  * fluid_core_audio_driver_t
45  *
46  */
47 typedef struct
48 {
49     fluid_audio_driver_t driver;
50     AudioUnit outputUnit;
51     AudioStreamBasicDescription format;
52     fluid_audio_func_t callback;
53     void *data;
54     unsigned int buffer_size;
55     float *buffers[2];
56     double phase;
57 } fluid_core_audio_driver_t;
58 
59 
60 OSStatus fluid_core_audio_callback(void *data,
61                                    AudioUnitRenderActionFlags *ioActionFlags,
62                                    const AudioTimeStamp *inTimeStamp,
63                                    UInt32 inBusNumber,
64                                    UInt32 inNumberFrames,
65                                    AudioBufferList *ioData);
66 
67 
68 /**************************************************************
69  *
70  *        CoreAudio audio driver
71  *
72  */
73 
74 #define OK(x) (x == noErr)
75 
76 int
get_num_outputs(AudioDeviceID deviceID)77 get_num_outputs(AudioDeviceID deviceID)
78 {
79     int i, total = 0;
80     UInt32 size;
81     AudioObjectPropertyAddress pa;
82     pa.mSelector = kAudioDevicePropertyStreamConfiguration;
83     pa.mScope = kAudioDevicePropertyScopeOutput;
84     pa.mElement = kAudioObjectPropertyElementMaster;
85 
86     if(OK(AudioObjectGetPropertyDataSize(deviceID, &pa, 0, 0, &size)) && size > 0)
87     {
88         AudioBufferList *bufList = FLUID_MALLOC(size);
89 
90         if(bufList == NULL)
91         {
92             FLUID_LOG(FLUID_ERR, "Out of memory");
93             return 0;
94         }
95 
96         if(OK(AudioObjectGetPropertyData(deviceID, &pa, 0, 0, &size, bufList)))
97         {
98             int numStreams = bufList->mNumberBuffers;
99 
100             for(i = 0; i < numStreams; ++i)
101             {
102                 AudioBuffer b = bufList->mBuffers[i];
103                 total += b.mNumberChannels;
104             }
105         }
106 
107         FLUID_FREE(bufList);
108     }
109 
110     return total;
111 }
112 
113 void
fluid_core_audio_driver_settings(fluid_settings_t * settings)114 fluid_core_audio_driver_settings(fluid_settings_t *settings)
115 {
116     int i;
117     UInt32 size;
118     AudioObjectPropertyAddress pa;
119     pa.mSelector = kAudioHardwarePropertyDevices;
120     pa.mScope = kAudioObjectPropertyScopeWildcard;
121     pa.mElement = kAudioObjectPropertyElementMaster;
122 
123     fluid_settings_register_str(settings, "audio.coreaudio.device", "default", 0);
124     fluid_settings_add_option(settings, "audio.coreaudio.device", "default");
125 
126     if(OK(AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &pa, 0, 0, &size)))
127     {
128         int num = size / (int) sizeof(AudioDeviceID);
129         AudioDeviceID devs [num];
130 
131         if(OK(AudioObjectGetPropertyData(kAudioObjectSystemObject, &pa, 0, 0, &size, devs)))
132         {
133             for(i = 0; i < num; ++i)
134             {
135                 char name [1024];
136                 size = sizeof(name);
137                 pa.mSelector = kAudioDevicePropertyDeviceName;
138 
139                 if(OK(AudioObjectGetPropertyData(devs[i], &pa, 0, 0, &size, name)))
140                 {
141                     if(get_num_outputs(devs[i]) > 0)
142                     {
143                         fluid_settings_add_option(settings, "audio.coreaudio.device", name);
144                     }
145                 }
146             }
147         }
148     }
149 }
150 
151 /*
152  * new_fluid_core_audio_driver
153  */
154 fluid_audio_driver_t *
new_fluid_core_audio_driver(fluid_settings_t * settings,fluid_synth_t * synth)155 new_fluid_core_audio_driver(fluid_settings_t *settings, fluid_synth_t *synth)
156 {
157     return new_fluid_core_audio_driver2(settings,
158                                         NULL,
159                                         synth);
160 }
161 
162 /*
163  * new_fluid_core_audio_driver2
164  */
165 fluid_audio_driver_t *
new_fluid_core_audio_driver2(fluid_settings_t * settings,fluid_audio_func_t func,void * data)166 new_fluid_core_audio_driver2(fluid_settings_t *settings, fluid_audio_func_t func, void *data)
167 {
168     char *devname = NULL;
169     fluid_core_audio_driver_t *dev = NULL;
170     int period_size, periods;
171     double sample_rate;
172     OSStatus status;
173     UInt32 size;
174     int i;
175 
176     dev = FLUID_NEW(fluid_core_audio_driver_t);
177 
178     if(dev == NULL)
179     {
180         FLUID_LOG(FLUID_ERR, "Out of memory");
181         return NULL;
182     }
183 
184     FLUID_MEMSET(dev, 0, sizeof(fluid_core_audio_driver_t));
185 
186     dev->callback = func;
187     dev->data = data;
188 
189     // Open the default output unit
190 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1060
191     ComponentDescription desc;
192 #else
193     AudioComponentDescription desc;
194 #endif
195     desc.componentType = kAudioUnitType_Output;
196     desc.componentSubType = kAudioUnitSubType_HALOutput; //kAudioUnitSubType_DefaultOutput;
197     desc.componentManufacturer = kAudioUnitManufacturer_Apple;
198     desc.componentFlags = 0;
199     desc.componentFlagsMask = 0;
200 
201 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1060
202     Component comp = FindNextComponent(NULL, &desc);
203 #else
204     AudioComponent comp = AudioComponentFindNext(NULL, &desc);
205 #endif
206 
207     if(comp == NULL)
208     {
209         FLUID_LOG(FLUID_ERR, "Failed to get the default audio device");
210         goto error_recovery;
211     }
212 
213 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1060
214     status = OpenAComponent(comp, &dev->outputUnit);
215 #else
216     status = AudioComponentInstanceNew(comp, &dev->outputUnit);
217 #endif
218 
219     if(status != noErr)
220     {
221         FLUID_LOG(FLUID_ERR, "Failed to open the default audio device. Status=%ld\n", (long int)status);
222         goto error_recovery;
223     }
224 
225     // Set up a callback function to generate output
226     AURenderCallbackStruct render;
227     render.inputProc = fluid_core_audio_callback;
228     render.inputProcRefCon = (void *) dev;
229     status = AudioUnitSetProperty(dev->outputUnit,
230                                   kAudioUnitProperty_SetRenderCallback,
231                                   kAudioUnitScope_Input,
232                                   0,
233                                   &render,
234                                   sizeof(render));
235 
236     if(status != noErr)
237     {
238         FLUID_LOG(FLUID_ERR, "Error setting the audio callback. Status=%ld\n", (long int)status);
239         goto error_recovery;
240     }
241 
242     fluid_settings_getnum(settings, "synth.sample-rate", &sample_rate);
243     fluid_settings_getint(settings, "audio.periods", &periods);
244     fluid_settings_getint(settings, "audio.period-size", &period_size);
245 
246     /* get the selected device name. if none is specified, use NULL for the default device. */
247     if(fluid_settings_dupstr(settings, "audio.coreaudio.device", &devname) == FLUID_OK   /* alloc device name */
248             && devname && strlen(devname) > 0)
249     {
250         AudioObjectPropertyAddress pa;
251         pa.mSelector = kAudioHardwarePropertyDevices;
252         pa.mScope = kAudioObjectPropertyScopeWildcard;
253         pa.mElement = kAudioObjectPropertyElementMaster;
254 
255         if(OK(AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &pa, 0, 0, &size)))
256         {
257             int num = size / (int) sizeof(AudioDeviceID);
258             AudioDeviceID devs [num];
259 
260             if(OK(AudioObjectGetPropertyData(kAudioObjectSystemObject, &pa, 0, 0, &size, devs)))
261             {
262                 for(i = 0; i < num; ++i)
263                 {
264                     char name [1024];
265                     size = sizeof(name);
266                     pa.mSelector = kAudioDevicePropertyDeviceName;
267 
268                     if(OK(AudioObjectGetPropertyData(devs[i], &pa, 0, 0, &size, name)))
269                     {
270                         if(get_num_outputs(devs[i]) > 0 && FLUID_STRCASECMP(devname, name) == 0)
271                         {
272                             AudioDeviceID selectedID = devs[i];
273                             status = AudioUnitSetProperty(dev->outputUnit,
274                                                           kAudioOutputUnitProperty_CurrentDevice,
275                                                           kAudioUnitScope_Global,
276                                                           0,
277                                                           &selectedID,
278                                                           sizeof(AudioDeviceID));
279 
280                             if(status != noErr)
281                             {
282                                 FLUID_LOG(FLUID_ERR, "Error setting the selected output device. Status=%ld\n", (long int)status);
283                                 goto error_recovery;
284                             }
285                         }
286                     }
287                 }
288             }
289         }
290     }
291 
292     FLUID_FREE(devname);  /* free device name */
293 
294     dev->buffer_size = period_size * periods;
295 
296     // The DefaultOutputUnit should do any format conversions
297     // necessary from our format to the device's format.
298     dev->format.mSampleRate = sample_rate; // sample rate of the audio stream
299     dev->format.mFormatID = kAudioFormatLinearPCM; // encoding type of the audio stream
300     dev->format.mFormatFlags = kLinearPCMFormatFlagIsFloat;
301     dev->format.mBytesPerPacket = 2 * sizeof(float);
302     dev->format.mFramesPerPacket = 1;
303     dev->format.mBytesPerFrame = 2 * sizeof(float);
304     dev->format.mChannelsPerFrame = 2;
305     dev->format.mBitsPerChannel = 8 * sizeof(float);
306 
307     FLUID_LOG(FLUID_DBG, "mSampleRate %g", dev->format.mSampleRate);
308     FLUID_LOG(FLUID_DBG, "mFormatFlags %08X", dev->format.mFormatFlags);
309     FLUID_LOG(FLUID_DBG, "mBytesPerPacket %d", dev->format.mBytesPerPacket);
310     FLUID_LOG(FLUID_DBG, "mFramesPerPacket %d", dev->format.mFramesPerPacket);
311     FLUID_LOG(FLUID_DBG, "mChannelsPerFrame %d", dev->format.mChannelsPerFrame);
312     FLUID_LOG(FLUID_DBG, "mBytesPerFrame %d", dev->format.mBytesPerFrame);
313     FLUID_LOG(FLUID_DBG, "mBitsPerChannel %d", dev->format.mBitsPerChannel);
314 
315     status = AudioUnitSetProperty(dev->outputUnit,
316                                   kAudioUnitProperty_StreamFormat,
317                                   kAudioUnitScope_Input,
318                                   0,
319                                   &dev->format,
320                                   sizeof(AudioStreamBasicDescription));
321 
322     if(status != noErr)
323     {
324         FLUID_LOG(FLUID_ERR, "Error setting the audio format. Status=%ld\n", (long int)status);
325         goto error_recovery;
326     }
327 
328     status = AudioUnitSetProperty(dev->outputUnit,
329                                   kAudioUnitProperty_MaximumFramesPerSlice,
330                                   kAudioUnitScope_Input,
331                                   0,
332                                   &dev->buffer_size,
333                                   sizeof(unsigned int));
334 
335     if(status != noErr)
336     {
337         FLUID_LOG(FLUID_ERR, "Failed to set the MaximumFramesPerSlice. Status=%ld\n", (long int)status);
338         goto error_recovery;
339     }
340 
341     FLUID_LOG(FLUID_DBG, "MaximumFramesPerSlice = %d", dev->buffer_size);
342 
343     dev->buffers[0] = FLUID_ARRAY(float, dev->buffer_size);
344     dev->buffers[1] = FLUID_ARRAY(float, dev->buffer_size);
345 
346     if(dev->buffers[0] == NULL || dev->buffers[1] == NULL)
347     {
348         FLUID_LOG(FLUID_ERR, "Out of memory.");
349         goto error_recovery;
350     }
351 
352     // Initialize the audio unit
353     status = AudioUnitInitialize(dev->outputUnit);
354 
355     if(status != noErr)
356     {
357         FLUID_LOG(FLUID_ERR, "Error calling AudioUnitInitialize(). Status=%ld\n", (long int)status);
358         goto error_recovery;
359     }
360 
361     // Start the rendering
362     status = AudioOutputUnitStart(dev->outputUnit);
363 
364     if(status != noErr)
365     {
366         FLUID_LOG(FLUID_ERR, "Error calling AudioOutputUnitStart(). Status=%ld\n", (long int)status);
367         goto error_recovery;
368     }
369 
370     return (fluid_audio_driver_t *) dev;
371 
372 error_recovery:
373 
374     delete_fluid_core_audio_driver((fluid_audio_driver_t *) dev);
375     return NULL;
376 }
377 
378 /*
379  * delete_fluid_core_audio_driver
380  */
381 void
delete_fluid_core_audio_driver(fluid_audio_driver_t * p)382 delete_fluid_core_audio_driver(fluid_audio_driver_t *p)
383 {
384     fluid_core_audio_driver_t *dev = (fluid_core_audio_driver_t *) p;
385     fluid_return_if_fail(dev != NULL);
386 
387 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1060
388     CloseComponent(dev->outputUnit);
389 #else
390     AudioComponentInstanceDispose(dev->outputUnit);
391 #endif
392 
393     if(dev->buffers[0])
394     {
395         FLUID_FREE(dev->buffers[0]);
396     }
397 
398     if(dev->buffers[1])
399     {
400         FLUID_FREE(dev->buffers[1]);
401     }
402 
403     FLUID_FREE(dev);
404 }
405 
406 OSStatus
fluid_core_audio_callback(void * data,AudioUnitRenderActionFlags * ioActionFlags,const AudioTimeStamp * inTimeStamp,UInt32 inBusNumber,UInt32 inNumberFrames,AudioBufferList * ioData)407 fluid_core_audio_callback(void *data,
408                           AudioUnitRenderActionFlags *ioActionFlags,
409                           const AudioTimeStamp *inTimeStamp,
410                           UInt32 inBusNumber,
411                           UInt32 inNumberFrames,
412                           AudioBufferList *ioData)
413 {
414     int i, k;
415     fluid_core_audio_driver_t *dev = (fluid_core_audio_driver_t *) data;
416     int len = inNumberFrames;
417     float *buffer = ioData->mBuffers[0].mData;
418 
419     if(dev->callback)
420     {
421         float *left = dev->buffers[0];
422         float *right = dev->buffers[1];
423 
424         FLUID_MEMSET(left, 0, len * sizeof(float));
425         FLUID_MEMSET(right, 0, len * sizeof(float));
426 
427         (*dev->callback)(dev->data, len, 0, NULL, 2, dev->buffers);
428 
429         for(i = 0, k = 0; i < len; i++)
430         {
431             buffer[k++] = left[i];
432             buffer[k++] = right[i];
433         }
434     }
435     else
436         fluid_synth_write_float((fluid_synth_t *) dev->data, len, buffer, 0, 2,
437                                 buffer, 1, 2);
438 
439     return noErr;
440 }
441 
442 
443 #endif /* #if COREAUDIO_SUPPORT */
444