1 /*
2   rtauhal.c:
3 
4   Copyright (C) 2011 Victor Lazzarini
5 
6   This file is part of Csound.
7 
8   The Csound Library is free software; you can redistribute it
9   and/or modify it under the terms of the GNU Lesser General Public
10   License as published by the Free Software Foundation; either
11   version 2.1 of the License, or (at your option) any later version.
12 
13   Csound is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU Lesser General Public License for more details.
17 
18   You should have received a copy of the GNU Lesser General Public
19   License along with Csound; if not, write to the Free Software
20   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21   02110-1301 USA
22 */
23 
24 #include <AudioUnit/AudioUnit.h>
25 #include <CoreAudio/AudioHardware.h>
26 #include <CoreFoundation/CoreFoundation.h>
27 #include <unistd.h>
28 #include <stdint.h>
29 #include "csdl.h"
30 #include "soundio.h"
31 
32 /* Modified from BSD sources for strlcpy */
33 /*
34  * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
35  *
36  * Permission to use, copy, modify, and distribute this software for any
37  * purpose with or without fee is hereby granted, provided that the above
38  * copyright notice and this permission notice appear in all copies.
39  */
40 /* modifed for speed -- JPff */
41 char *
strNcpy(char * dst,const char * src,size_t siz)42 strNcpy(char *dst, const char *src, size_t siz)
43 {
44     char *d = dst;
45     const char *s = src;
46     size_t n = siz;
47 
48     /* Copy as many bytes as will fit or until NULL */
49     if (n != 0) {
50       while (--n != 0) {
51         if ((*d++ = *s++) == '\0')
52           break;
53       }
54     }
55 
56     /* Not enough room in dst, add NUL */
57     if (n == 0) {
58       if (siz != 0)
59         *d = '\0';                /* NUL-terminate dst */
60 
61       //while (*s++) ;
62     }
63     return dst;        /* count does not include NUL */
64 }
65 
66 #if !defined(MAC_OS_X_VERSION_10_6)
67 /* the API was changed for 10.6, these make it backwards compatible  */
68 typedef ComponentInstance AudioComponentInstance;
69 typedef Component AudioComponent;
70 typedef ComponentDescription AudioComponentDescription;
71 #define AudioComponentFindNext FindNextComponent
72 #define AudioComponentInstanceNew OpenAComponent
73 #define  AudioComponentInstanceDispose CloseComponent
74 typedef float AudioUnitSampleType;
75 #endif
76 
77 
78 typedef struct {
79   char name[128];
80   int outchannels;
81   int inchannels;
82   int indevnum;
83   int outdevnum;
84 } Device_Info;
85 
86 
87 typedef struct csdata_ {
88   AudioDeviceID dev;
89   AudioStreamBasicDescription format;
90   int         inBufSamples;
91   int         outBufSamples;
92   int         currentInputIndex;
93   int         currentOutputIndex;
94   MYFLT       *inputBuffer;
95   MYFLT       *outputBuffer;
96   csRtAudioParams *inParm;
97   csRtAudioParams *outParm;
98   int onchnls, inchnls;
99   AudioComponentInstance outunit;
100   AudioComponentInstance inunit;
101   CSOUND *csound;
102   AudioBufferList *inputdata;
103   int disp;
104   AudioDeviceID defdevin;
105   AudioDeviceID defdevout;
106   int devnos;
107   int devin;
108   int devout;
109   void *incb;
110   void *outcb;
111 } csdata;
112 
113 
114 OSStatus  Csound_Input(void *inRefCon,
115                        AudioUnitRenderActionFlags *ioActionFlags,
116                        const AudioTimeStamp *inTimeStamp,
117                        UInt32 inBusNumber,
118                        UInt32 inNumberFrames,
119                        AudioBufferList *ioData);
120 
121 OSStatus  Csound_Render(void *inRefCon,
122                         AudioUnitRenderActionFlags *ioActionFlags,
123                         const AudioTimeStamp *inTimeStamp,
124                         UInt32 dump,
125                         UInt32 inNumberFrames,
126                         AudioBufferList *ioData);
127 
DAC_channels(CSOUND * csound,int chans)128 static void DAC_channels(CSOUND *csound, int chans){
129     int *dachans = (int *) csound->QueryGlobalVariable(csound, "_DAC_CHANNELS_");
130     if (dachans == NULL) {
131       if (csound->CreateGlobalVariable(csound, "_DAC_CHANNELS_",
132                                        sizeof(int)) != 0)
133         return;
134       dachans = (int *) csound->QueryGlobalVariable(csound, "_DAC_CHANNELS_");
135       *dachans = chans;
136     }
137 }
138 
ADC_channels(CSOUND * csound,int chans)139 static void ADC_channels(CSOUND *csound, int chans){
140     int *dachans = (int *) csound->QueryGlobalVariable(csound, "_ADC_CHANNELS_");
141     if (dachans == NULL) {
142       if (csound->CreateGlobalVariable(csound, "_ADC_CHANNELS_",
143                                        sizeof(int)) != 0)
144         return;
145       dachans = (int *) csound->QueryGlobalVariable(csound, "_ADC_CHANNELS_");
146       *dachans = chans;
147     }
148 }
149 
AuHAL_open(CSOUND * csound,const csRtAudioParams * parm,csdata * cdata,int isInput)150 int AuHAL_open(CSOUND *csound, const csRtAudioParams * parm,
151                csdata *cdata, int isInput)
152 {
153     UInt32  psize, devnum, devnos;
154     AudioDeviceID dev;
155     AudioDeviceID *sysdevs;
156     AudioStreamBasicDescription format;
157     int     i;
158     Device_Info *devinfo;
159     UInt32  bufframes, nchnls;
160     int devouts = 0, devins = 0;
161     double srate;
162     UInt32 enableIO, maxFPS;
163     AudioComponent HALOutput;
164     AudioComponentInstance *aunit;
165     AudioComponentDescription cd = {kAudioUnitType_Output,
166                                     kAudioUnitSubType_HALOutput,
167                                     kAudioUnitManufacturer_Apple, 0, 0};
168     AudioObjectPropertyAddress prop = {
169       kAudioObjectPropertyName,
170       kAudioObjectPropertyScopeGlobal,
171       kAudioObjectPropertyElementMaster
172     };
173     CFStringRef devName;
174     CFStringEncoding defaultEncoding = CFStringGetSystemEncoding();
175 
176 
177 
178     prop.mSelector = (isInput ?
179                       kAudioHardwarePropertyDefaultInputDevice :
180                       kAudioHardwarePropertyDefaultOutputDevice);
181 
182     psize = sizeof(AudioDeviceID);
183     AudioObjectGetPropertyData(kAudioObjectSystemObject,
184                                &prop, 0, NULL, &psize, &dev);
185 
186     if(isInput) cdata->defdevin = dev;
187     else cdata->defdevout = dev;
188 
189     prop.mSelector = kAudioHardwarePropertyDevices;
190     AudioObjectGetPropertyDataSize(kAudioObjectSystemObject,
191                                    &prop, 0, NULL, &psize);
192     devnos = psize / sizeof(AudioDeviceID);
193     sysdevs = (AudioDeviceID *) csound->Malloc(csound,psize);
194     devinfo = (Device_Info *) csound->Malloc(csound,devnos*sizeof(Device_Info));
195     AudioObjectGetPropertyData(kAudioObjectSystemObject,
196                                &prop, 0, NULL, &psize, sysdevs);
197 
198     cdata->devnos = devnos;
199     for (i = 0; (unsigned int) i < devnos; i++) {
200       AudioBufferList *b;
201       int devchannels, k, n;
202       int numlists;
203       psize = sizeof(CFStringRef);
204       prop.mScope = kAudioObjectPropertyScopeGlobal;
205       prop.mSelector = kAudioObjectPropertyName;
206       AudioObjectGetPropertyData(sysdevs[i],
207                                  &prop, 0, NULL, &psize, &devName);
208       if(CFStringGetCStringPtr(devName, defaultEncoding))
209         strNcpy(devinfo[i].name,
210                 CFStringGetCStringPtr(devName, defaultEncoding), 127);
211       else
212         strNcpy(devinfo[i].name, "unnamed device", 127);
213       CFRelease(devName);
214 
215       devchannels = 0;
216       prop.mScope = kAudioDevicePropertyScopeInput;
217       prop.mSelector =  kAudioDevicePropertyStreamConfiguration;
218       AudioObjectGetPropertyDataSize(sysdevs[i],
219                                      &prop, 0, NULL, &psize);
220       b = (AudioBufferList *) csound->Malloc(csound,psize);
221       numlists = psize / sizeof(AudioBufferList);
222       AudioObjectGetPropertyData(sysdevs[i],
223                                  &prop, 0, NULL, &psize, b);
224       for(n=0; n < numlists; n++){
225         for(k=0; (unsigned int) k < b[n].mNumberBuffers; k++)
226           devchannels += b[n].mBuffers[k].mNumberChannels;
227       }
228       devinfo[i].inchannels = devchannels;
229       if(devchannels) {
230         devins++;
231         devinfo[i].indevnum = devins;
232       } else devinfo[i].indevnum = -1;
233       csound->Free(csound,b);
234 
235       devchannels = 0;
236       prop.mScope = kAudioDevicePropertyScopeOutput;
237       AudioObjectGetPropertyDataSize(sysdevs[i],
238                                      &prop, 0, NULL, &psize);
239       b = (AudioBufferList *) csound->Malloc(csound,psize);
240       numlists = psize /sizeof(AudioBufferList);
241       AudioObjectGetPropertyData(sysdevs[i],
242                                  &prop, 0, NULL, &psize, b);
243       for(n=0; n < numlists; n++){
244         for(k=0; (unsigned int) k < b[n].mNumberBuffers; k++)
245           devchannels += b[n].mBuffers[k].mNumberChannels;
246       }
247       devinfo[i].outchannels = devchannels;
248       if(devchannels) {
249         devouts++;
250         devinfo[i].outdevnum = devouts;
251       } else devinfo[i].outdevnum = -1;
252       csound->Free(csound,b);
253     }
254 
255 
256     if(cdata->disp)
257       csound->Message(csound,
258          "==========================================================\n");
259 
260     if (isInput)
261       csound->Message(csound,
262                       Str("AuHAL Module: found %d input device(s):\n"), devins);
263     else csound->Message(csound,
264                          Str("AuHAL Module: found %d output device(s):\n"),
265                          devouts);
266 
267     for (i = 0; (unsigned int)  i < devnos; i++) {
268       if (isInput) {
269         if(devinfo[i].inchannels) {
270           csound->Message(csound, Str("%d: %s (%d channels)\n"),
271                           devinfo[i].indevnum, devinfo[i].name,
272                           devinfo[i].inchannels);
273         }
274       }
275       else {
276         if(devinfo[i].outchannels)
277           csound->Message(csound, Str("%d: %s (%d channels)\n"),
278                           devinfo[i].outdevnum, devinfo[i].name,
279                           devinfo[i].outchannels);
280       }
281     }
282 
283     if (parm->devName != NULL) devnum = atoi(parm->devName);
284     else devnum = parm->devNum;
285 
286     if (devnum > 0 && devnum < 1024) {
287       int CoreAudioDev = -1;
288       prop.mSelector = kAudioHardwarePropertyDevices;
289       if (isInput) {
290         for(i=0; (unsigned int)  i < devnos; i++) {
291           if((unsigned int) devinfo[i].indevnum == devnum) {
292             CoreAudioDev = i;
293             break;
294           }
295         }
296         if (LIKELY(CoreAudioDev >= 0)) {
297           prop.mSelector = kAudioHardwarePropertyDefaultInputDevice;
298           dev  = sysdevs[CoreAudioDev];
299           AudioObjectSetPropertyData(kAudioObjectSystemObject, &prop,
300                                      0, NULL, sizeof(AudioDeviceID), &dev);
301         }
302         else csound->Warning(csound, Str("requested device %d out of range"),
303                              devnum);
304 
305       }
306       else {
307         prop.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
308         for(i=0;(unsigned int)  i < devnos; i++) {
309           if((unsigned int) devinfo[i].outdevnum == devnum)  CoreAudioDev = i;
310         }
311         if (LIKELY(CoreAudioDev >= 0)) {
312           dev  = sysdevs[CoreAudioDev];
313           AudioObjectSetPropertyData(kAudioObjectSystemObject, &prop,
314                                      0, NULL, sizeof(AudioDeviceID), &dev);
315 
316         }
317         else csound->Warning(csound, Str("requested device %d (%s) out of range"),
318                              devnum, devinfo[CoreAudioDev].name);
319       }
320     }
321 
322     for(i=0; (unsigned int)  i < devnos; i++) {
323       if(sysdevs[i] == dev){
324         if(isInput) {
325           if(devinfo[i].inchannels < parm->nChannels) {
326             csound->ErrorMsg(csound,
327                              Str(" *** CoreAudio: Device has not enough"
328                                  " inputs (%d, requested %d)\n"),
329                              devinfo[i].inchannels, parm->nChannels);
330             return -1;
331           }
332           ADC_channels(csound, devinfo[i].inchannels);
333         }
334         else {
335           if(devinfo[i].outchannels < parm->nChannels) {
336             csound->ErrorMsg(csound,
337                              Str(" *** CoreAudio: Device has not enough"
338                                  " outputs (%d, requested %d)\n"),
339                              devinfo[i].outchannels, parm->nChannels);
340             return -1;
341           }
342           DAC_channels(csound, devinfo[i].outchannels);
343         }
344       }
345     }
346     csound->Free(csound,sysdevs);
347     csound->Free(csound,devinfo);
348 
349     psize = sizeof(CFStringRef);
350     prop.mSelector = kAudioObjectPropertyName;
351     AudioObjectGetPropertyData(dev,
352                                &prop, 0, NULL, &psize, &devName);
353     if(isInput)
354       csound->Message(csound, Str("selected input device: %s\n"),
355                       CFStringGetCStringPtr(devName, defaultEncoding));
356 
357     else
358       csound->Message(csound, Str("selected output device: %s\n"),
359                       CFStringGetCStringPtr(devName, defaultEncoding));
360 
361     CFRelease(devName);
362 
363     srate = csound->GetSr(csound);
364     if(!isInput){
365       nchnls =cdata->onchnls = parm->nChannels;
366       bufframes = csound->GetOutputBufferSize(csound)/nchnls;
367     }
368     else {
369       nchnls = cdata->inchnls = parm->nChannels;
370       bufframes = csound->GetInputBufferSize(csound)/nchnls;
371     }
372 
373     /* although the SR is set in the stream properties,
374        we also need to set the device to match */
375      double sr;
376     prop.mSelector = kAudioDevicePropertyNominalSampleRate;
377     if(!isInput){
378       AudioObjectGetPropertyData(dev, &prop, 0, NULL, &psize, &sr);
379       csound->system_sr(csound, sr);
380     }
381 
382     psize = sizeof(double);
383     AudioObjectSetPropertyData(dev, &prop, 0, NULL, psize, &srate);
384     AudioObjectGetPropertyData(dev, &prop, 0, NULL, &psize, &sr);
385 
386     if(srate < 0)
387       srate  =  csound->system_sr(csound, sr);
388     if(UNLIKELY(sr != srate)) {
389       csound->Warning(csound,
390                       Str("Attempted to set device SR, tried %.1f, got %.1f\n"),
391                       srate, sr);
392     }
393 
394     HALOutput = AudioComponentFindNext(NULL, &cd);
395     if (isInput) {
396       AudioComponentInstanceNew(HALOutput, &(cdata->inunit));
397       enableIO = 1;
398       AudioUnitSetProperty(cdata->inunit, kAudioOutputUnitProperty_EnableIO,
399                            kAudioUnitScope_Input, 1, &enableIO, sizeof(enableIO));
400       enableIO = 0;
401       AudioUnitSetProperty(cdata->inunit, kAudioOutputUnitProperty_EnableIO,
402                            kAudioUnitScope_Output, 0, &enableIO, sizeof(enableIO));
403       psize = sizeof(AudioDeviceID);
404       /* for input, select device AFTER enabling IO */
405       AudioUnitSetProperty(cdata->inunit,kAudioOutputUnitProperty_CurrentDevice,
406                            kAudioUnitScope_Global, isInput, &dev, psize);
407       aunit = &(cdata->inunit);
408     }
409     else {
410       AudioComponentInstanceNew(HALOutput, &(cdata->outunit));
411       psize = sizeof(AudioDeviceID);
412       /* for output, select device BEFORE enabling IO */
413       AudioUnitSetProperty(cdata->outunit, kAudioOutputUnitProperty_CurrentDevice,
414                            kAudioUnitScope_Global, isInput, &dev, psize);
415       enableIO = 1;
416       AudioUnitSetProperty(cdata->outunit, kAudioOutputUnitProperty_EnableIO,
417                            kAudioUnitScope_Output, 0, &enableIO, sizeof(enableIO));
418       enableIO = 0;
419       AudioUnitSetProperty(cdata->outunit, kAudioOutputUnitProperty_EnableIO,
420                            kAudioUnitScope_Input, 1, &enableIO, sizeof(enableIO));
421       aunit = &(cdata->outunit);
422     }
423     /* now set the buffer size */
424     psize = sizeof(AudioDeviceID);
425     AudioUnitGetProperty(*aunit, kAudioOutputUnitProperty_CurrentDevice,
426                          kAudioUnitScope_Global, isInput, &dev, &psize);
427     prop.mSelector = kAudioDevicePropertyBufferFrameSize;
428     psize = 4;
429     AudioObjectSetPropertyData(dev, &prop, 0, NULL, psize, &bufframes);
430     psize = sizeof(maxFPS);
431     AudioUnitGetProperty(*aunit, kAudioUnitProperty_MaximumFramesPerSlice,
432                          kAudioUnitScope_Global, isInput, &maxFPS, &psize);
433     AudioUnitSetProperty(*aunit, kAudioUnitProperty_MaximumFramesPerSlice,
434                          kAudioUnitScope_Global, isInput, &bufframes,
435                          sizeof(UInt32));
436     /* set the stream properties */
437     psize = sizeof(AudioStreamBasicDescription);
438     AudioUnitGetProperty(*aunit, kAudioUnitProperty_StreamFormat,
439                          (isInput ? kAudioUnitScope_Output : kAudioUnitScope_Input),
440                          isInput, &format, &psize);
441     format.mSampleRate    = srate;
442     format.mFormatID =  kAudioFormatLinearPCM;
443     format.mFormatFlags = kAudioFormatFlagIsFloat  |
444                           kAudioFormatFlagIsPacked |
445                           kLinearPCMFormatFlagIsNonInterleaved;
446     format.mBytesPerPacket = sizeof(Float32);
447     format.mFramesPerPacket = 1;
448     format.mBytesPerFrame = sizeof(Float32);
449     format.mChannelsPerFrame = nchnls;
450     format.mBitsPerChannel = sizeof(Float32)*8;
451     AudioUnitSetProperty(*aunit, kAudioUnitProperty_StreamFormat,
452                          (isInput ? kAudioUnitScope_Output : kAudioUnitScope_Input),
453                          isInput, &format,
454                          sizeof(AudioStreamBasicDescription));
455     /* set the callbacks and open the device */
456     if(!isInput) {
457       AURenderCallbackStruct output;
458       output.inputProc = Csound_Render;
459       output.inputProcRefCon = cdata;
460       AudioUnitSetProperty(*aunit, kAudioUnitProperty_SetRenderCallback,
461                            kAudioUnitScope_Input, isInput, &output, sizeof(output));
462       AudioUnitInitialize(*aunit);
463       AudioOutputUnitStart(*aunit);
464 
465       csound->Message(csound,
466                       Str("***** AuHAL module: output device open with %d "
467                           "buffer frames\n"),
468                           bufframes);
469       cdata->disp = 0;
470     }
471     else {
472       AURenderCallbackStruct input;
473       AudioBufferList *CAInputData =
474         (AudioBufferList*)csound->Malloc(csound,sizeof(UInt32)
475                                  + cdata->inchnls * sizeof(AudioBuffer));
476       CAInputData->mNumberBuffers = cdata->inchnls;
477       for (i = 0; i < cdata->inchnls; i++) {
478         CAInputData->mBuffers[i].mNumberChannels = 1;
479         CAInputData->mBuffers[i].mDataByteSize =
480           bufframes * sizeof(Float32);
481         CAInputData->mBuffers[i].mData =
482           csound->Calloc(csound,bufframes* sizeof(Float32));
483       }
484       cdata->inputdata = CAInputData;
485 
486       input.inputProc = Csound_Input;
487       input.inputProcRefCon = cdata;
488       AudioUnitSetProperty(*aunit, kAudioOutputUnitProperty_SetInputCallback,
489                            kAudioUnitScope_Input, isInput, &input, sizeof(input));
490       AudioUnitInitialize(*aunit);
491       AudioOutputUnitStart(*aunit);
492       csound->Message(csound,
493                       Str("***** AuHAL module: input device open with "
494                           "%d buffer frames\n"),
495                       (int) bufframes);
496     }
497     if(!cdata->disp)
498       csound->Message(csound,
499               "==========================================================\n");
500 
501     cdata->disp = 0;
502     return 0;
503 
504 }
505 
listDevices(CSOUND * csound,CS_AUDIODEVICE * list,int isOutput)506 int listDevices(CSOUND *csound, CS_AUDIODEVICE *list, int isOutput){
507     UInt32  psize, devnos;
508     AudioDeviceID *sysdevs;
509     Device_Info *devinfo;
510     int     i;
511     int devouts = 0, devins = 0;
512 
513     AudioObjectPropertyAddress prop = {
514       kAudioObjectPropertyName,
515       kAudioObjectPropertyScopeGlobal,
516       kAudioObjectPropertyElementMaster
517     };
518     CFStringRef devName;
519     CFStringEncoding defaultEncoding = CFStringGetSystemEncoding();
520     prop.mSelector = kAudioHardwarePropertyDevices;
521     AudioObjectGetPropertyDataSize(kAudioObjectSystemObject,
522                                    &prop, 0, NULL, &psize);
523     devnos = psize / sizeof(AudioDeviceID);
524     sysdevs = (AudioDeviceID *) csound->Malloc(csound,psize);
525     devinfo = (Device_Info *) csound->Malloc(csound,devnos*sizeof*devinfo);
526     AudioObjectGetPropertyData(kAudioObjectSystemObject,
527                                &prop, 0, NULL, &psize, sysdevs);
528 
529     for (i = 0; (unsigned int) i < devnos; i++) {
530       AudioBufferList *b;
531       int devchannels, k, n;
532       int numlists;
533       psize = sizeof(CFStringRef);
534       prop.mScope = kAudioObjectPropertyScopeGlobal;
535       prop.mSelector = kAudioObjectPropertyName;
536       AudioObjectGetPropertyData(sysdevs[i],
537                                  &prop, 0, NULL, &psize, &devName);
538       memset(devinfo[i].name,0,128);
539       if(CFStringGetCStringPtr(devName, defaultEncoding) != NULL)
540         strNcpy(devinfo[i].name,
541                 CFStringGetCStringPtr(devName, defaultEncoding),127);
542       CFRelease(devName);
543 
544       devchannels = 0;
545       prop.mScope = kAudioDevicePropertyScopeInput;
546       prop.mSelector =  kAudioDevicePropertyStreamConfiguration;
547       AudioObjectGetPropertyDataSize(sysdevs[i],
548                                      &prop, 0, NULL, &psize);
549       b = (AudioBufferList *) csound->Malloc(csound,psize);
550       numlists = psize / sizeof(AudioBufferList);
551       AudioObjectGetPropertyData(sysdevs[i],
552                                  &prop, 0, NULL, &psize, b);
553       for(n=0; n < numlists; n++){
554         for(k=0; (unsigned int)  k < b[n].mNumberBuffers; k++)
555           devchannels += b[n].mBuffers[k].mNumberChannels;
556       }
557       devinfo[i].inchannels = devchannels;
558       if(devchannels) {
559         devins++;
560         devinfo[i].indevnum = devins;
561       } else devinfo[i].indevnum = -1;
562       csound->Free(csound,b);
563 
564       devchannels = 0;
565       prop.mScope = kAudioDevicePropertyScopeOutput;
566       AudioObjectGetPropertyDataSize(sysdevs[i],
567                                      &prop, 0, NULL, &psize);
568       b = (AudioBufferList *) csound->Malloc(csound,psize);
569       numlists = psize /sizeof(AudioBufferList);
570       AudioObjectGetPropertyData(sysdevs[i],
571                                  &prop, 0, NULL, &psize, b);
572       for(n=0; n < numlists; n++){
573         for(k=0; (unsigned int) k < b[n].mNumberBuffers; k++)
574           devchannels += b[n].mBuffers[k].mNumberChannels;
575       }
576       devinfo[i].outchannels = devchannels;
577       if(devchannels) {
578         devouts++;
579         devinfo[i].outdevnum = devouts;
580       } else devinfo[i].outdevnum = -1;
581       csound->Free(csound,b);
582     }
583     if(list==NULL){
584       return (isOutput ? devouts : devins);
585     } else {
586 
587       char tmp[64], *s;
588       int n=0, i;
589 
590       if ((s = (char*) csound->QueryGlobalVariable(csound, "_RTAUDIO")) == NULL)
591         return 0;
592 
593       if(!isOutput){
594         for(i=0; (unsigned int)  i < devnos; i++) {
595           if(devinfo[i].inchannels) {
596             strNcpy(list[n].device_name,  devinfo[i].name, 63);
597             snprintf(tmp, 64, "adc%d", devinfo[i].indevnum);
598             strNcpy(list[n].device_id, tmp, 63);
599             strNcpy(list[n].rt_module, s, 63);
600             list[n].max_nchnls = devinfo[i].inchannels;
601             list[n].isOutput = 0;
602             n++;
603           }
604         }
605         return n;
606       } else {
607         for(i=0;(unsigned int) i < devnos; i++){
608           if(devinfo[i].outchannels) {
609             strNcpy(list[n].device_name,  devinfo[i].name, 63);
610             snprintf(tmp, 64, "dac%d", devinfo[i].outdevnum);
611             strNcpy(list[n].device_id, tmp, 63);
612             strNcpy(list[n].rt_module, s, 63);
613             list[n].max_nchnls = devinfo[i].outchannels;
614             list[n].isOutput = 1;
615             n++;
616           }
617         }
618         return n;
619       }
620     }
621 }
622 
623 /* open for audio input */
recopen_(CSOUND * csound,const csRtAudioParams * parm)624 static int recopen_(CSOUND *csound, const csRtAudioParams * parm)
625 {
626     csdata  *cdata;
627     void **recordata = csound->GetRtRecordUserData(csound);
628     if (*(csound->GetRtRecordUserData(csound)) != NULL)
629       return 0;
630 
631     /* allocate structure */
632 
633     if(*(csound->GetRtPlayUserData(csound) )!= NULL)
634        cdata = (csdata *) *(csound->GetRtPlayUserData(csound));
635     else {
636        cdata = (csdata *) csound->Calloc(csound, sizeof(csdata));
637       cdata->disp = 1;
638     }
639 
640     cdata->inunit = NULL;
641     *recordata = (void *) cdata;
642     cdata->inParm =  (csRtAudioParams *) parm;
643     cdata->csound = cdata->csound;
644     cdata->inputBuffer =
645       (MYFLT *) csound->Calloc(csound,
646                                csound->GetInputBufferSize(csound)* sizeof(MYFLT));
647     cdata->incb =
648       csound->CreateCircularBuffer(csound,
649                                    parm->bufSamp_HW*parm->nChannels, sizeof(MYFLT));
650     int ret = AuHAL_open(csound, parm, cdata, 1);
651     return ret;
652 }
653 
654 /* open for audio output */
playopen_(CSOUND * csound,const csRtAudioParams * parm)655 static int playopen_(CSOUND *csound, const csRtAudioParams * parm)
656 {
657     csdata  *cdata;
658     void    **playdata = csound->GetRtPlayUserData(csound);
659 
660     if(*(csound->GetRtRecordUserData(csound)) != NULL)
661       cdata = (csdata *) *(csound->GetRtRecordUserData(csound));
662     else {
663       cdata = (csdata *) csound->Calloc(csound, sizeof(csdata));
664       cdata->disp = 1;
665     }
666     cdata->outunit = NULL;
667     *playdata = (void *) cdata;
668     cdata->outParm =  (csRtAudioParams *) parm;
669     cdata->csound = csound;
670     cdata->outputBuffer =
671       (MYFLT *) csound->Calloc(csound,
672                                csound->GetOutputBufferSize(csound)* sizeof(MYFLT));
673     memset(cdata->outputBuffer, 0,
674            csound->GetOutputBufferSize(csound)*sizeof(MYFLT));
675     cdata->outcb =
676       csound->CreateCircularBuffer(csound,
677                                    parm->bufSamp_HW*parm->nChannels, sizeof(MYFLT));
678     return AuHAL_open(csound, parm,cdata,0);
679 }
680 
Csound_Input(void * inRefCon,AudioUnitRenderActionFlags * ioActionFlags,const AudioTimeStamp * inTimeStamp,UInt32 inBusNumber,UInt32 inNumberFrames,AudioBufferList * ioData)681 OSStatus  Csound_Input(void *inRefCon,
682                        AudioUnitRenderActionFlags *ioActionFlags,
683                        const AudioTimeStamp *inTimeStamp,
684                        UInt32 inBusNumber,
685                        UInt32 inNumberFrames,
686                        AudioBufferList *ioData)
687 {
688     csdata *cdata = (csdata *) inRefCon;
689     CSOUND *csound = cdata->csound;
690     int inchnls = cdata->inchnls;
691     MYFLT *inputBuffer = cdata->inputBuffer;
692     int j,k;
693     Float32 *buffer;
694     int n = inNumberFrames*inchnls;
695     int l;
696     IGN(ioData);
697 
698     AudioUnitRender(cdata->inunit, ioActionFlags, inTimeStamp, inBusNumber,
699                     inNumberFrames, cdata->inputdata);
700     for (k = 0; k < inchnls; k++){
701       buffer = (Float32 *) cdata->inputdata->mBuffers[k].mData;
702       for(j=0; (unsigned int) j < inNumberFrames; j++){
703         inputBuffer[j*inchnls+k] = buffer[j];
704       }
705     }
706     l = csound->WriteCircularBuffer(csound, cdata->incb,inputBuffer,n);
707     return 0;
708 }
709 
710 #define MICROS 1000000
rtrecord_(CSOUND * csound,MYFLT * inbuff_,int nbytes)711 static int rtrecord_(CSOUND *csound, MYFLT *inbuff_, int nbytes)
712 {
713     csdata  *cdata;
714     int n = nbytes/sizeof(MYFLT);
715     int m = 0, l, w = n;
716     MYFLT sr = csound->GetSr(csound);
717     cdata = (csdata *) *(csound->GetRtRecordUserData(csound));
718     do{
719       l = csound->ReadCircularBuffer(csound,cdata->incb,&inbuff_[m],n);
720       m += l;
721       n -= l;
722       if(n) usleep(MICROS*w/sr);
723     } while(n);
724     return nbytes;
725 }
726 
Csound_Render(void * inRefCon,AudioUnitRenderActionFlags * ioActionFlags,const AudioTimeStamp * inTimeStamp,UInt32 inBusNumber,UInt32 inNumberFrames,AudioBufferList * ioData)727 OSStatus  Csound_Render(void *inRefCon,
728                         AudioUnitRenderActionFlags *ioActionFlags,
729                         const AudioTimeStamp *inTimeStamp,
730                         UInt32 inBusNumber,
731                         UInt32 inNumberFrames,
732                         AudioBufferList *ioData)
733 {
734     csdata *cdata = (csdata *) inRefCon;
735     CSOUND *csound = cdata->csound;
736     int onchnls = cdata->onchnls;
737     MYFLT *outputBuffer = cdata->outputBuffer;
738     int j,k;
739     Float32 *buffer;
740     int n = inNumberFrames*onchnls;
741     IGN(ioActionFlags);
742     IGN(inTimeStamp);
743     IGN(inBusNumber);
744 
745     n = csound->ReadCircularBuffer(csound,cdata->outcb,outputBuffer,n);
746     for (k = 0; k < onchnls; k++) {
747       buffer = (Float32 *) ioData->mBuffers[k].mData;
748       for(j=0; (unsigned int) j < inNumberFrames; j++){
749         buffer[j] = (Float32) outputBuffer[j*onchnls+k] ;
750         outputBuffer[j*onchnls+k] = FL(0.0);
751       }
752     }
753     return 0;
754 }
755 
rtplay_(CSOUND * csound,const MYFLT * outbuff_,int nbytes)756 static void rtplay_(CSOUND *csound, const MYFLT *outbuff_, int nbytes)
757 {
758     csdata  *cdata;
759     int n = nbytes/sizeof(MYFLT);
760     int m = 0, l, w = n;
761     MYFLT sr = csound->GetSr(csound);
762     cdata = (csdata *) *(csound->GetRtPlayUserData(csound));
763     do {
764       l = csound->WriteCircularBuffer(csound, cdata->outcb,&outbuff_[m],n);
765       m += l;
766       n -= l;
767       if(n) usleep(MICROS*w/sr);
768     } while(n);
769 }
770 
771 /* close the I/O device entirely  */
772 /* called only when both complete */
773 
rtclose_(CSOUND * csound)774 static void rtclose_(CSOUND *csound)
775 {
776     csdata *cdata;
777     cdata = (csdata *) *(csound->GetRtRecordUserData(csound));
778     if(cdata == NULL)
779       cdata = (csdata *) *(csound->GetRtPlayUserData(csound));
780 
781     if (cdata != NULL) {
782       usleep(1000*csound->GetOutputBufferSize(csound)/
783              (csound->GetSr(csound)*csound->GetNchnls(csound)));
784 
785       if(cdata->inunit != NULL){
786         AudioOutputUnitStop(cdata->inunit);
787         AudioUnitUninitialize(cdata->inunit);
788         AudioComponentInstanceDispose(cdata->inunit);
789       }
790 
791       if(cdata->outunit != NULL){
792         AudioOutputUnitStop(cdata->outunit);
793         AudioUnitUninitialize(cdata->outunit);
794         AudioComponentInstanceDispose(cdata->outunit);
795       }
796 
797       if (cdata->outputBuffer != NULL) {
798         csound->Free(csound,cdata->outputBuffer);
799         cdata->outputBuffer = NULL;
800       }
801       if (cdata->inputBuffer != NULL) {
802         csound->Free(csound,cdata->inputBuffer);
803         cdata->inputBuffer = NULL;
804       }
805 
806       *(csound->GetRtRecordUserData(csound)) = NULL;
807       *(csound->GetRtPlayUserData(csound)) = NULL;
808 
809       if(cdata->inputdata) {
810         int i;
811         for (i = 0; i < cdata->inchnls; i++)
812           csound->Free(csound,cdata->inputdata->mBuffers[i].mData);
813         csound->Free(csound,cdata->inputdata);
814       }
815 
816       if(cdata->defdevin) {
817         AudioObjectPropertyAddress prop = {
818           kAudioHardwarePropertyDefaultInputDevice,
819           kAudioObjectPropertyScopeGlobal,
820           kAudioObjectPropertyElementMaster
821         };
822         UInt32 psize = sizeof(AudioDeviceID);
823         AudioObjectSetPropertyData(kAudioObjectSystemObject,
824                                    &prop, 0, NULL, psize, &cdata->defdevin);
825       }
826       if(cdata->defdevout) {
827         AudioObjectPropertyAddress prop = {
828           kAudioHardwarePropertyDefaultOutputDevice,
829           kAudioObjectPropertyScopeGlobal,
830           kAudioObjectPropertyElementMaster
831         };
832         UInt32 psize = sizeof(AudioDeviceID);
833         AudioObjectSetPropertyData(kAudioObjectSystemObject,
834                                    &prop, 0, NULL, psize, &cdata->defdevout);
835       }
836       csound->DestroyCircularBuffer(csound, cdata->incb);
837       csound->DestroyCircularBuffer(csound, cdata->outcb);
838       csound->Free(csound,cdata);
839       csound->Message(csound, "%s", Str("AuHAL module: device closed\n"));
840     }
841 }
842 
csoundModuleInit(CSOUND * csound)843 int csoundModuleInit(CSOUND *csound)
844 {
845     char   *drv;
846     csound->module_list_add(csound, "auhal", "audio");
847     drv = (char *) csound->QueryGlobalVariable(csound, "_RTAUDIO");
848     if (drv == NULL)
849       return 0;
850     if (!(strcmp(drv, "auhal") == 0 || strcmp(drv, "AuHal") == 0 ||
851           strcmp(drv, "AUHAL") == 0 ||
852           strcmp(drv, "coreaudio") == 0 || strcmp(drv, "CoreAudio") == 0 ||
853           strcmp(drv, "COREAUDIO") == 0))
854       return 0;
855     //if (csound->oparms->msglevel & 0x400)
856     csound->Message(csound, "%s", Str("rtaudio: coreaaudio-AuHAL module enabled\n"));
857     csound->SetPlayopenCallback(csound, playopen_);
858     csound->SetRecopenCallback(csound, recopen_);
859     csound->SetRtplayCallback(csound, rtplay_);
860     csound->SetRtrecordCallback(csound, rtrecord_);
861     csound->SetRtcloseCallback(csound, rtclose_);
862     csound->SetAudioDeviceListCallback(csound, listDevices);
863     return 0;
864 }
865 
csoundModuleCreate(CSOUND * csound)866 int csoundModuleCreate(CSOUND *csound)
867 {
868     IGN(csound);
869     return 0;
870 }
871