1 /* AudioCore sound handler
2  *
3  * Copyright 2008 Chris Bagwell And Sundry Contributors
4  */
5 
6 #include "sox_i.h"
7 
8 #include <CoreAudio/CoreAudio.h>
9 #include <pthread.h>
10 
11 #define Buffactor 4
12 
13 typedef struct {
14   AudioDeviceID adid;
15   pthread_mutex_t mutex;
16   pthread_cond_t cond;
17   int device_started;
18   size_t bufsize;
19   size_t bufrd;
20   size_t bufwr;
21   size_t bufrdavail;
22   float *buf;
23 } priv_t;
24 
PlaybackIOProc(AudioDeviceID inDevice UNUSED,const AudioTimeStamp * inNow UNUSED,const AudioBufferList * inInputData UNUSED,const AudioTimeStamp * inInputTime UNUSED,AudioBufferList * outOutputData,const AudioTimeStamp * inOutputTime UNUSED,void * inClientData)25 static OSStatus PlaybackIOProc(AudioDeviceID inDevice UNUSED,
26                                const AudioTimeStamp *inNow UNUSED,
27                                const AudioBufferList *inInputData UNUSED,
28                                const AudioTimeStamp *inInputTime UNUSED,
29                                AudioBufferList *outOutputData,
30                                const AudioTimeStamp *inOutputTime UNUSED,
31                                void *inClientData)
32 {
33     priv_t *ac = (priv_t*)((sox_format_t*)inClientData)->priv;
34     AudioBuffer *buf;
35     size_t copylen, avail;
36 
37     pthread_mutex_lock(&ac->mutex);
38 
39     for(buf = outOutputData->mBuffers;
40         buf != outOutputData->mBuffers + outOutputData->mNumberBuffers;
41         buf++){
42 
43         copylen = buf->mDataByteSize / sizeof(float);
44         if(copylen > ac->bufrdavail)
45             copylen = ac->bufrdavail;
46 
47         avail = ac->bufsize - ac->bufrd;
48         if(buf->mData == NULL){
49             /*do nothing-hardware can't play audio*/
50         }else if(copylen > avail){
51             memcpy(buf->mData, ac->buf + ac->bufrd, avail * sizeof(float));
52             memcpy((float*)buf->mData + avail, ac->buf, (copylen - avail) * sizeof(float));
53         }else{
54             memcpy(buf->mData, ac->buf + ac->bufrd, copylen * sizeof(float));
55         }
56 
57         buf->mDataByteSize = copylen * sizeof(float);
58         ac->bufrd += copylen;
59         if(ac->bufrd >= ac->bufsize)
60             ac->bufrd -= ac->bufsize;
61         ac->bufrdavail -= copylen;
62     }
63 
64     pthread_cond_signal(&ac->cond);
65     pthread_mutex_unlock(&ac->mutex);
66 
67     return kAudioHardwareNoError;
68 }
69 
RecIOProc(AudioDeviceID inDevice UNUSED,const AudioTimeStamp * inNow UNUSED,const AudioBufferList * inInputData,const AudioTimeStamp * inInputTime UNUSED,AudioBufferList * outOutputData UNUSED,const AudioTimeStamp * inOutputTime UNUSED,void * inClientData)70 static OSStatus RecIOProc(AudioDeviceID inDevice UNUSED,
71                           const AudioTimeStamp *inNow UNUSED,
72                           const AudioBufferList *inInputData,
73                           const AudioTimeStamp *inInputTime UNUSED,
74                           AudioBufferList *outOutputData UNUSED,
75                           const AudioTimeStamp *inOutputTime UNUSED,
76                           void *inClientData)
77 {
78     priv_t *ac = (priv_t *)((sox_format_t*)inClientData)->priv;
79     AudioBuffer const *buf;
80     size_t nfree, copylen, avail;
81 
82     pthread_mutex_lock(&ac->mutex);
83 
84     for(buf = inInputData->mBuffers;
85         buf != inInputData->mBuffers + inInputData->mNumberBuffers;
86         buf++){
87 
88         if(buf->mData == NULL)
89             continue;
90 
91         copylen = buf->mDataByteSize / sizeof(float);
92         nfree = ac->bufsize - ac->bufrdavail - 1;
93         if(nfree == 0)
94             lsx_warn("coreaudio: unhandled buffer overrun.  Data discarded.");
95 
96         if(copylen > nfree)
97             copylen = nfree;
98 
99         avail = ac->bufsize - ac->bufwr;
100         if(copylen > avail){
101             memcpy(ac->buf + ac->bufwr, buf->mData, avail * sizeof(float));
102             memcpy(ac->buf, (float*)buf->mData + avail, (copylen - avail) * sizeof(float));
103         }else{
104             memcpy(ac->buf + ac->bufwr, buf->mData, copylen * sizeof(float));
105         }
106 
107         ac->bufwr += copylen;
108         if(ac->bufwr >= ac->bufsize)
109             ac->bufwr -= ac->bufsize;
110         ac->bufrdavail += copylen;
111     }
112 
113     pthread_cond_signal(&ac->cond);
114     pthread_mutex_unlock(&ac->mutex);
115 
116     return kAudioHardwareNoError;
117 }
118 
setup(sox_format_t * ft,int is_input)119 static int setup(sox_format_t *ft, int is_input)
120 {
121   priv_t *ac = (priv_t *)ft->priv;
122   OSStatus status;
123   UInt32 property_size;
124   struct AudioStreamBasicDescription stream_desc;
125   int32_t buf_size;
126   int rc;
127 
128   if (strncmp(ft->filename, "default", (size_t)7) == 0)
129   {
130       property_size = sizeof(ac->adid);
131       if (is_input)
132           status = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &property_size, &ac->adid);
133       else
134           status = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &property_size, &ac->adid);
135   }
136   else
137   {
138       Boolean is_writable;
139       status = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &property_size, &is_writable);
140 
141       if (status == noErr)
142       {
143           int device_count = property_size/sizeof(AudioDeviceID);
144           AudioDeviceID *devices;
145 
146           devices = malloc(property_size);
147               status = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &property_size, devices);
148 
149           if (status == noErr)
150           {
151               int i;
152               for (i = 0; i < device_count; i++)
153               {
154                   char name[256];
155                   status = AudioDeviceGetProperty(devices[i],0,false,kAudioDevicePropertyDeviceName,&property_size,&name);
156 
157                   lsx_report("Found Audio Device \"%s\"\n",name);
158 
159                   /* String returned from OS is truncated so only compare
160                    * as much as returned.
161                    */
162                   if (strncmp(name,ft->filename,strlen(name)) == 0)
163                   {
164                       ac->adid = devices[i];
165                       break;
166                   }
167               }
168           }
169           free(devices);
170       }
171   }
172 
173   if (status || ac->adid == kAudioDeviceUnknown)
174   {
175     lsx_fail_errno(ft, SOX_EPERM, "can not open audio device");
176     return SOX_EOF;
177   }
178 
179   /* Query device to get initial values */
180   property_size = sizeof(struct AudioStreamBasicDescription);
181   status = AudioDeviceGetProperty(ac->adid, 0, is_input,
182                                   kAudioDevicePropertyStreamFormat,
183                                   &property_size, &stream_desc);
184   if (status)
185   {
186     lsx_fail_errno(ft, SOX_EPERM, "can not get audio device properties");
187     return SOX_EOF;
188   }
189 
190   if (!(stream_desc.mFormatFlags & kLinearPCMFormatFlagIsFloat))
191   {
192     lsx_fail_errno(ft, SOX_EPERM, "audio device does not accept floats");
193     return SOX_EOF;
194   }
195 
196   /* OS X effectively only supports these values. */
197   ft->signal.channels = 2;
198   ft->signal.rate = 44100;
199   ft->encoding.bits_per_sample = 32;
200 
201   /* TODO: My limited experience with hardware can only get floats working
202    * withh a fixed sample rate and stereo.  I know that is a limitiation of
203    * audio device I have so this may not be standard operating orders.
204    * If some hardware supports setting sample rates and channel counts
205    * then should do that over resampling and mixing.
206    */
207 #if  0
208   stream_desc.mSampleRate = ft->signal.rate;
209   stream_desc.mChannelsPerFrame = ft->signal.channels;
210 
211   /* Write them back */
212   property_size = sizeof(struct AudioStreamBasicDescription);
213   status = AudioDeviceSetProperty(ac->adid, NULL, 0, is_input,
214                                   kAudioDevicePropertyStreamFormat,
215                                   property_size, &stream_desc);
216   if (status)
217   {
218     lsx_fail_errno(ft, SOX_EPERM, "can not set audio device properties");
219     return SOX_EOF;
220   }
221 
222   /* Query device to see if it worked */
223   property_size = sizeof(struct AudioStreamBasicDescription);
224   status = AudioDeviceGetProperty(ac->adid, 0, is_input,
225                                   kAudioDevicePropertyStreamFormat,
226                                   &property_size, &stream_desc);
227 
228   if (status)
229   {
230     lsx_fail_errno(ft, SOX_EPERM, "can not get audio device properties");
231     return SOX_EOF;
232   }
233 #endif
234 
235   if (stream_desc.mChannelsPerFrame != ft->signal.channels)
236   {
237     lsx_debug("audio device did not accept %d channels. Use %d channels instead.", (int)ft->signal.channels,
238               (int)stream_desc.mChannelsPerFrame);
239     ft->signal.channels = stream_desc.mChannelsPerFrame;
240   }
241 
242   if (stream_desc.mSampleRate != ft->signal.rate)
243   {
244     lsx_debug("audio device did not accept %d sample rate. Use %d instead.", (int)ft->signal.rate,
245               (int)stream_desc.mSampleRate);
246     ft->signal.rate = stream_desc.mSampleRate;
247   }
248 
249   ac->bufsize = sox_globals.bufsiz / sizeof(sox_sample_t) * Buffactor;
250   ac->bufrd = 0;
251   ac->bufwr = 0;
252   ac->bufrdavail = 0;
253   ac->buf = lsx_malloc(ac->bufsize * sizeof(float));
254 
255   buf_size = sox_globals.bufsiz / sizeof(sox_sample_t) * sizeof(float);
256   property_size = sizeof(buf_size);
257   status = AudioDeviceSetProperty(ac->adid, NULL, 0, is_input,
258                                   kAudioDevicePropertyBufferSize,
259                                   property_size, &buf_size);
260 
261   rc = pthread_mutex_init(&ac->mutex, NULL);
262   if (rc)
263   {
264     lsx_fail_errno(ft, SOX_EPERM, "failed initializing mutex");
265     return SOX_EOF;
266   }
267 
268   rc = pthread_cond_init(&ac->cond, NULL);
269   if (rc)
270   {
271     lsx_fail_errno(ft, SOX_EPERM, "failed initializing condition");
272     return SOX_EOF;
273   }
274 
275   ac->device_started = 0;
276 
277   /* Registers callback with the device without activating it. */
278   if (is_input)
279     status = AudioDeviceAddIOProc(ac->adid, RecIOProc, (void *)ft);
280   else
281     status = AudioDeviceAddIOProc(ac->adid, PlaybackIOProc, (void *)ft);
282 
283   return SOX_SUCCESS;
284 }
285 
startread(sox_format_t * ft)286 static int startread(sox_format_t *ft)
287 {
288     return setup(ft, 1);
289 }
290 
read_samples(sox_format_t * ft,sox_sample_t * buf,size_t nsamp)291 static size_t read_samples(sox_format_t *ft, sox_sample_t *buf, size_t nsamp)
292 {
293     priv_t *ac = (priv_t *)ft->priv;
294     size_t len;
295     SOX_SAMPLE_LOCALS;
296 
297     if (!ac->device_started) {
298         AudioDeviceStart(ac->adid, RecIOProc);
299         ac->device_started = 1;
300     }
301 
302     pthread_mutex_lock(&ac->mutex);
303 
304     /* Wait until input buffer has been filled by device driver */
305     while (ac->bufrdavail == 0)
306         pthread_cond_wait(&ac->cond, &ac->mutex);
307 
308     len = 0;
309     while(len < nsamp && ac->bufrdavail > 0){
310         buf[len] = SOX_FLOAT_32BIT_TO_SAMPLE(ac->buf[ac->bufrd], ft->clips);
311         len++;
312         ac->bufrd++;
313         if(ac->bufrd == ac->bufsize)
314             ac->bufrd = 0;
315         ac->bufrdavail--;
316     }
317 
318     pthread_mutex_unlock(&ac->mutex);
319 
320     return len;
321 }
322 
stopread(sox_format_t * ft)323 static int stopread(sox_format_t * ft)
324 {
325   priv_t *ac = (priv_t *)ft->priv;
326 
327   AudioDeviceStop(ac->adid, RecIOProc);
328   AudioDeviceRemoveIOProc(ac->adid, RecIOProc);
329   pthread_cond_destroy(&ac->cond);
330   pthread_mutex_destroy(&ac->mutex);
331   free(ac->buf);
332 
333   return SOX_SUCCESS;
334 }
335 
startwrite(sox_format_t * ft)336 static int startwrite(sox_format_t * ft)
337 {
338     return setup(ft, 0);
339 }
340 
write_samples(sox_format_t * ft,const sox_sample_t * buf,size_t nsamp)341 static size_t write_samples(sox_format_t *ft, const sox_sample_t *buf, size_t nsamp)
342 {
343     priv_t *ac = (priv_t *)ft->priv;
344     size_t i;
345 
346     SOX_SAMPLE_LOCALS;
347 
348     pthread_mutex_lock(&ac->mutex);
349 
350     /* Wait to start until mutex is locked to help prevent callback
351     * getting zero samples.
352     */
353     if(!ac->device_started){
354         if(AudioDeviceStart(ac->adid, PlaybackIOProc)){
355             pthread_mutex_unlock(&ac->mutex);
356             return SOX_EOF;
357         }
358         ac->device_started = 1;
359     }
360 
361     /* globals.bufsize is in samples
362     * buf_offset is in bytes
363     * buf_size is in bytes
364     */
365     for(i = 0; i < nsamp; i++){
366         while(ac->bufrdavail == ac->bufsize - 1)
367             pthread_cond_wait(&ac->cond, &ac->mutex);
368 
369         ac->buf[ac->bufwr] = SOX_SAMPLE_TO_FLOAT_32BIT(buf[i], ft->clips);
370         ac->bufwr++;
371         if(ac->bufwr == ac->bufsize)
372             ac->bufwr = 0;
373         ac->bufrdavail++;
374     }
375 
376     pthread_mutex_unlock(&ac->mutex);
377     return nsamp;
378 }
379 
380 
stopwrite(sox_format_t * ft)381 static int stopwrite(sox_format_t * ft)
382 {
383     priv_t *ac = (priv_t *)ft->priv;
384 
385     if(ac->device_started){
386         pthread_mutex_lock(&ac->mutex);
387 
388         while (ac->bufrdavail > 0)
389             pthread_cond_wait(&ac->cond, &ac->mutex);
390 
391         pthread_mutex_unlock(&ac->mutex);
392 
393         AudioDeviceStop(ac->adid, PlaybackIOProc);
394     }
395 
396     AudioDeviceRemoveIOProc(ac->adid, PlaybackIOProc);
397     pthread_cond_destroy(&ac->cond);
398     pthread_mutex_destroy(&ac->mutex);
399     free(ac->buf);
400 
401     return SOX_SUCCESS;
402 }
403 
LSX_FORMAT_HANDLER(coreaudio)404 LSX_FORMAT_HANDLER(coreaudio)
405 {
406   static char const *const names[] = { "coreaudio", NULL };
407   static unsigned const write_encodings[] = {
408     SOX_ENCODING_FLOAT, 32, 0,
409     0};
410   static sox_format_handler_t const handler = {SOX_LIB_VERSION_CODE,
411     "Mac AudioCore device driver",
412     names, SOX_FILE_DEVICE | SOX_FILE_NOSTDIO,
413     startread, read_samples, stopread,
414     startwrite, write_samples, stopwrite,
415     NULL, write_encodings, NULL, sizeof(priv_t)
416   };
417   return &handler;
418 }
419