Develop and Download Open Source Software

Browse Subversion Repository

Contents of /trunk/MD_package/MDAudio_MacOSX.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 196 - (show annotations) (download) (as text)
Wed Jun 24 14:51:57 2020 UTC (3 years, 9 months ago) by toshinagata1964
File MIME type: text/x-csrc
File size: 81390 byte(s)
The app does not start on non-Japanese system. Hopefully fixed.
1 /*
2 * MDAudio_MacOSX.c
3 * Alchemusica
4 *
5 * Created by Toshi Nagata on 08/01/06.
6 * Copyright 2008-2016 Toshi Nagata. All rights reserved.
7 *
8
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation version 2 of the License.
12
13 This program 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 General Public License for more details.
17 */
18
19 #include "MDHeaders.h"
20 #include "MDAudioUtility.h"
21
22 #include <unistd.h> /* For getcwd() */
23 #include <sys/param.h> /* For MAXPATHLEN */
24 //#include <CoreServices/CoreServices.h> /* Use Audio Component Services instead */
25 #include <AudioUnit/AudioComponent.h>
26 #include <AudioUnit/AudioUnit.h>
27 #include <AudioToolbox/AUGraph.h> /* for AUNode output */
28 #include <AudioToolbox/AUMIDIController.h> /* for routing MIDI to DLS synth */
29 #include <AudioToolbox/AudioConverter.h>
30 #include <AudioUnit/MusicDevice.h>
31
32 struct MDAudio {
33 /* Canonical audio format */
34 MDAudioFormat preferredFormat;
35
36 /* AUGraph and AUNodes */
37 AUGraph graph;
38 AUNode mixer, output;
39 /* AUNode synth, synth2, mixer, output, converter, splitter; */
40 /* int isInputRunning;
41 int isRunning; */
42
43 /* Audio Units */
44 AudioUnit mixerUnit, outputUnit;
45 /* AudioUnit inputUnit, outputUnit, mixerUnit, converterUnit;
46 MusicDeviceComponent musicDevice, musicDevice2;
47 AUMIDIControllerRef midiCon, midiCon2; */
48
49 /* Audio/Music device infos */
50 MDArray *inputDeviceInfos, *outputDeviceInfos;
51 MDArray *musicDeviceInfos, *effectDeviceInfos;
52
53 /* IO information (the mixer input/output) */
54 MDAudioIOStreamInfo ioStreamInfos[kMDAudioNumberOfStreams];
55 int isAudioThruEnabled;
56
57 /* Feeding audio from external device */
58 /* AudioBufferList *inputBufferList; */
59
60 /* Recording to file */
61 ExtAudioFileRef audioFile;
62 int isRecording;
63 UInt64 recordingStartTime;
64 UInt64 recordingDuration;
65
66 /* Play thru */
67 /* MDAudioDeviceInfo inputDeviceInfoCache, outputDeviceInfoCache;
68 MDSampleTime firstInputTime, firstOutputTime, inToOutSampleOffset; */
69
70 /* Audio through */
71 /* MDRingBuffer *ring;
72 int isAudioThruEnabled; */
73 };
74
75 struct MDAudio *gAudio;
76
77 #pragma mark ====== Internal Functions ======
78
79 int
80 MDAudioShowError(OSStatus sts, const char *file, int line)
81 {
82 if (sts != 0)
83 fprintf(stderr, "Error OSStatus = %d at %s:%d\n", (int)sts, file, line);
84 return (int)sts;
85 }
86
87 static int
88 sMDAudioCompareFormat(const AudioStreamBasicDescription *format1, const AudioStreamBasicDescription *format2)
89 {
90 if (format1->mSampleRate != format2->mSampleRate)
91 return 0;
92 if (format1->mFormatID != format2->mFormatID)
93 return 0;
94 if (format1->mFormatFlags != format2->mFormatFlags)
95 return 0;
96 if (format1->mBytesPerPacket != format2->mBytesPerPacket)
97 return 0;
98 if (format1->mFramesPerPacket != format2->mFramesPerPacket)
99 return 0;
100 if (format1->mBytesPerFrame != format2->mBytesPerFrame)
101 return 0;
102 if (format1->mChannelsPerFrame != format2->mChannelsPerFrame)
103 return 0;
104 if (format1->mBitsPerChannel != format2->mBitsPerChannel)
105 return 0;
106 return 1;
107 }
108
109 static void
110 sMDAudioReleaseMyBufferList(AudioBufferList *list)
111 {
112 UInt32 i;
113 if (list != NULL) {
114 for (i = 0; i < list->mNumberBuffers; i++) {
115 if (list->mBuffers[i].mData != NULL)
116 free(list->mBuffers[i].mData);
117 }
118 free(list);
119 }
120 }
121
122 static AudioBufferList *
123 sMDAudioAllocateMyBufferList(UInt32 channelsPerFrame, UInt32 bytesPerFrame, UInt32 numberOfFrames)
124 {
125 int i;
126 AudioBufferList *list;
127 list = (AudioBufferList *)calloc(1, sizeof(AudioBufferList) + channelsPerFrame * sizeof(AudioBuffer));
128 if (list == NULL)
129 return NULL;
130 list->mNumberBuffers = channelsPerFrame; /* Assumes non-interleaved stream */
131 for(i = 0; i < channelsPerFrame; i++) {
132 list->mBuffers[i].mNumberChannels = 1; /* Assumes non-interleaved stream */
133 list->mBuffers[i].mDataByteSize = numberOfFrames * bytesPerFrame;
134 list->mBuffers[i].mData = calloc(bytesPerFrame, numberOfFrames);
135 if (list->mBuffers[i].mData == NULL) {
136 sMDAudioReleaseMyBufferList(list);
137 return NULL;
138 }
139 }
140 return list;
141 }
142
143 /*
144 static void
145 sComputeThruOffset(MDAudioIOStreamInfo *info)
146 {
147 // The initial latency will at least be the saftey offset's of the devices + the buffer sizes
148
149 info->inToOutSampleOffset = 0;
150 // audio->inToOutSampleOffset = (MDSampleTime)(
151 // gAudio->inputDeviceInfoCache.safetyOffset +
152 // gAudio->inputDeviceInfoCache.bufferSizeFrames +
153 // gAudio->outputDeviceInfoCache.safetyOffset +
154 // gAudio->outputDeviceInfoCache.bufferSizeFrames);
155 }
156 */
157
158 static void
159 sMakeBufferSilent(AudioBufferList * ioData)
160 {
161 UInt32 i;
162 for (i = 0; i < ioData->mNumberBuffers; i++)
163 memset(ioData->mBuffers[i].mData, 0, ioData->mBuffers[i].mDataByteSize);
164 }
165
166 /* Callback proc for input from AUHAL and write to audio-through buffer */
167 static OSStatus
168 sMDAudioInputProc(void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData)
169 {
170 MDAudioIOStreamInfo *info = (MDAudioIOStreamInfo *)inRefCon;
171 OSStatus err = noErr;
172
173 // { static int count = 99; if (++count == 100) { fprintf(stderr, "sMDAudioInputProc called at %f, ioData->mNumberBuffers = %d\n", (double)inTimeStamp->mSampleTime, (info->bufferList == NULL ? 0 : (int)info->bufferList->mNumberBuffers)); count = 0; } }
174
175 if (info->firstInputTime < 0) {
176 info->firstInputTime = inTimeStamp->mSampleTime;
177 // fprintf(stderr, "firstInputTime = %f\n", (double)audio->firstInputTime);
178 }
179 // fprintf(stderr, "inputTimeStamp = %f\n", (double)inTimeStamp->mSampleTime);
180
181 /* Render into audio buffer */
182 err = AudioUnitRender(info->unit, ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, info->bufferList);
183 if (err != noErr) {
184 // fprintf(stderr, "AudioUnitRender() failed with error %d\n", (int)err);
185 return err;
186 }
187
188 /* Write to ring buffer */
189 err = MDRingBufferStore(info->ring, info->bufferList, inNumberFrames, (MDSampleTime)inTimeStamp->mSampleTime);
190 if (err != noErr) {
191 // fprintf(stderr, "MDRingBufferStore() failed with error %d\n", (int)err);
192 return err;
193 }
194
195 return noErr;
196 }
197
198 /* Callback proc for reading data from ring buffer and put into AUGraph input */
199 static OSStatus
200 sMDAudioPassProc(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList * ioData)
201 {
202 MDAudioIOStreamInfo *info = (MDAudioIOStreamInfo *)inRefCon;
203 OSStatus err = noErr;
204
205 // { static int count = 0; if (++count == 100) { fprintf(stderr, "sMDAudioPassProc called at %f, ioData->mNumberBuffers = %d\n", (double)inTimeStamp->mSampleTime, (int)ioData->mNumberBuffers); count = 0; } }
206
207 if (info->firstInputTime < 0) {
208 /* The input has not arrived yet */
209 sMakeBufferSilent(ioData);
210 return noErr;
211 }
212 /* if ((err = AudioDeviceGetCurrentTime(This->mInputDevice.mID, &inTS)) != 0) {
213 MakeBufferSilent(ioData);
214 return noErr;
215 }
216
217 CHECK_ERR(AudioDeviceGetCurrentTime(This->mOutputDevice.mID, &outTS)); */
218
219 // fprintf(stderr, "outputTimeStamp = %f\n", (double)inTimeStamp->mSampleTime);
220
221 // get Delta between the devices and add it to the offset
222 if (info->firstOutputTime < 0) {
223 info->firstOutputTime = inTimeStamp->mSampleTime;
224 info->inToOutSampleOffset = info->firstOutputTime - info->firstInputTime;
225 /* TODO: modify offset to account for the latency */
226 /* sComputeThruOffset(audio);
227 // Is this really correct??
228 if (delta < 0.0)
229 info->inToOutSampleOffset -= delta;
230 else
231 info->inToOutSampleOffset = -delta + info->inToOutSampleOffset; */
232 // fprintf(stderr, "offset = %f\n", (double)audio->inToOutSampleOffset);
233 sMakeBufferSilent(ioData);
234 return noErr;
235 }
236
237 // copy the data from the buffers
238 err = MDRingBufferFetch(info->ring, ioData, inNumberFrames, (MDSampleTime)inTimeStamp->mSampleTime - info->inToOutSampleOffset, false);
239 if (err != kMDRingBufferError_OK) {
240 MDSampleTime bufferStartTime, bufferEndTime;
241 // fprintf(stderr, "err = %d at line %d\n", (int)err, __LINE__);
242 sMakeBufferSilent(ioData);
243 if (err == 1 || err == -1) {
244 MDRingBufferGetTimeBounds(info->ring, &bufferStartTime, &bufferEndTime);
245 info->inToOutSampleOffset = inTimeStamp->mSampleTime - bufferStartTime;
246 // fprintf(stderr, "buffer = (%f,%f) offset = %f\n", (double)bufferStartTime, (double)bufferEndTime, (double)audio->inToOutSampleOffset);
247 } else {
248 info->firstInputTime = info->firstOutputTime = -1;
249 }
250 }
251
252 return noErr;
253 }
254
255 /* Callback proc for recording to audio file */
256 static OSStatus
257 sMDAudioRecordProc(void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData)
258 {
259 OSStatus err = noErr;
260 MDAudioIOStreamInfo *info = (MDAudioIOStreamInfo *)inRefCon;
261 MDSampleTime startTime, endTime, renderTime;
262
263 { static int count = 0; if (++count == 100) { dprintf(3, "sMDAudioRecordProc called at %f, ioData->mNumberBuffers = %d\n", (double)inTimeStamp->mSampleTime, (int)ioData->mNumberBuffers); count = 0; } }
264
265 #if 1
266 /* Sample time range to handle during this callback */
267 startTime = inTimeStamp->mSampleTime;
268 endTime = startTime + inNumberFrames;
269 if (info->firstInputTime < 0) {
270 info->firstInputTime = inTimeStamp->mSampleTime;
271 }
272
273 /* Fill the ring buffer until enough data is present in the ring buffer */
274 while ((renderTime = MDRingBufferEndTime(info->ring)) < endTime) {
275 AudioTimeStamp timeStamp = {0};
276 int i, n;
277 timeStamp.mSampleTime = renderTime;
278 timeStamp.mRateScalar = inTimeStamp->mRateScalar;
279 timeStamp.mFlags = kAudioTimeStampSampleTimeValid | kAudioTimeStampRateScalarValid;
280 n = info->bufferSizeFrames;
281 for (i = 0; i < info->bufferList->mNumberBuffers; i++)
282 info->bufferList->mBuffers[i].mDataByteSize = n * info->ring->bytesPerFrame;
283 err = AudioUnitRender(gAudio->mixerUnit, ioActionFlags, inTimeStamp, inBusNumber, n, info->bufferList);
284 if (err != noErr) {
285 dprintf(0, "AudioUnitRender() failed with error %d in sMDAudioRecordProc\n", (int)err);
286 return err;
287 }
288 /* Write to ring buffer */
289 err = MDRingBufferStore(info->ring, info->bufferList, n, renderTime);
290 if (err != noErr) {
291 dprintf(0, "MDRingBufferStore() failed with error %d in sMDAudioRecordProc\n", (int)err);
292 if (err < 0) {
293 /* Throw everything away from the ring buffer */
294 MDRingBufferSetTimeBounds(info->ring, startTime, startTime);
295 }
296 return err;
297 }
298 }
299
300 /* Fill ioData from the ring buffer */
301 err = MDRingBufferFetch(info->ring, ioData, inNumberFrames, startTime, false);
302 if (err != noErr) {
303 dprintf(0, "MDRingBufferFetch() failed with error %d in sMDAudioRecordProc\n", (int)err);
304 if (err < 0) {
305 /* Throw everything away from the ring buffer */
306 MDRingBufferSetTimeBounds(info->ring, startTime, startTime);
307 }
308 return err;
309 }
310
311 #else
312 /* Render into audio buffer */
313 err = AudioUnitRender(gAudio->mixerUnit, ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData);
314 if (err != noErr) {
315 dprintf(0, "AudioUnitRender() failed with error %d in sMDAudioRecordProc\n", (int)err);
316 return err;
317 }
318 #endif
319
320 /* Write to file */
321 if (gAudio->isRecording) {
322 if (gAudio->recordingStartTime == 0)
323 gAudio->recordingStartTime = inTimeStamp->mHostTime;
324 if (gAudio->recordingDuration == 0 || inTimeStamp->mHostTime < gAudio->recordingStartTime + gAudio->recordingDuration) {
325 err = ExtAudioFileWriteAsync(gAudio->audioFile, inNumberFrames, ioData);
326 if (err != noErr) {
327 dprintf(0, "ExtAudioFileWrite() failed with error %d in sMDAudioRecordProc\n", (int)err);
328 return err;
329 }
330 }
331 }
332
333 if (err != noErr) {
334 // fprintf(stderr, "sMDAudioRecordProc() failed with error %d\n", (int)err);
335 return err;
336 }
337
338 if (!gAudio->isAudioThruEnabled)
339 return 1; /* Discard the input data */
340
341 return noErr;
342 }
343
344 /* Callback to send MIDI events to Music Device */
345 static OSStatus
346 sMDAudioSendMIDIProc(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData)
347 {
348 MDAudioIOStreamInfo *ip = (MDAudioIOStreamInfo *)inRefCon;
349 int readOffset = ip->midiBufferReadOffset;
350 int writeOffset = ip->midiBufferWriteOffset;
351 int dataSize = (writeOffset + kMDAudioMaxMIDIBytesToSendPerDevice - readOffset) % kMDAudioMaxMIDIBytesToSendPerDevice;
352 int numChannelEvents = 0;
353 int readPos = readOffset;
354 if ((*ioActionFlags & kAudioUnitRenderAction_PreRender) != kAudioUnitRenderAction_PreRender)
355 return noErr; /* No action */
356 if (ip->requestFlush) {
357 /* Flush is requested: skip all unread bytes */
358 ip->requestFlush = 0;
359 ip->midiBufferReadOffset = ip->midiBufferWriteOffset;
360 return noErr;
361 }
362 while (readPos - readOffset < dataSize) {
363 UInt64 timeStamp = 0;
364 UInt32 offset;
365 int i, len;
366 unsigned char c;
367 for (i = 0; i < 8; i++) {
368 c = ip->midiBuffer[(readPos + i) % kMDAudioMaxMIDIBytesToSendPerDevice];
369 timeStamp += (((UInt64)c) << (i * 8));
370 }
371 if (timeStamp > inTimeStamp->mHostTime) {
372 offset = (UInt32)((double)(timeStamp - inTimeStamp->mHostTime) * (1.0 / ip->format.mSampleRate));
373 } else offset = 0;
374 if (offset >= inNumberFrames) {
375 /* This event is scheduled in the next or later frame, so it
376 should be processed in later callback */
377 break;
378 }
379 len = ip->midiBuffer[(readPos + 8) % kMDAudioMaxMIDIBytesToSendPerDevice];
380 c = ip->midiBuffer[(readPos + 9) % kMDAudioMaxMIDIBytesToSendPerDevice];
381 if (c == 0xff || c == 0xf0) {
382 /* System Exclusive: in this case, no channel events should be scheduled
383 in this callback session; otherwise, the scheduled channel events will be
384 sent _after_ sending sysex. (There is no mechanism to schedule a sysex
385 event to MusicDevice.)
386 So, if we already scheduled any channel events, then we stop processing
387 here and try to send sysex in the next session. */
388 if (numChannelEvents > 0)
389 break;
390 if (c == 0xff) {
391 if (ip->sysexData != NULL) {
392 MusicDeviceSysEx(ip->unit, ip->sysexData, ip->sysexLength);
393 ip->sysexData = NULL;
394 }
395 readPos += 10; /* 8 (timeStamp) + 1 (length) + 1 (0xff) */
396 } else {
397 static unsigned char tempBuffer[256];
398 for (i = 0; i < len; i++) {
399 tempBuffer[i] = ip->midiBuffer[(readPos + 9 + i) % kMDAudioMaxMIDIBytesToSendPerDevice];
400 }
401 MusicDeviceSysEx(ip->unit, tempBuffer, len);
402 /* printf("sysex %d\n", len); */
403 readPos += 9 + len;
404 }
405 } else {
406 unsigned char c2, c3;
407 c2 = c3 = 0;
408 if (len >= 2) {
409 c2 = ip->midiBuffer[(readPos + 10) % kMDAudioMaxMIDIBytesToSendPerDevice];
410 if (len >= 3) {
411 c3 = ip->midiBuffer[(readPos + 11) % kMDAudioMaxMIDIBytesToSendPerDevice];
412 }
413 }
414 MusicDeviceMIDIEvent(ip->unit, c, c2, c3, offset);
415 /* printf("%08x %02x %02x %02x %d\n", (UInt32)ip->unit, c, c2, c3, offset); */
416 readPos += 9 + len;
417 numChannelEvents++;
418 }
419 }
420 ip->midiBufferReadOffset = readPos % kMDAudioMaxMIDIBytesToSendPerDevice;
421 return noErr;
422 }
423
424 int
425 MDAudioScheduleMIDIToStream(MDAudioIOStreamInfo *ip, UInt64 timeStamp, int length, unsigned char *midiData, int isSysEx)
426 {
427 int readOffset, writeOffset, spaceSize;
428 int i, length2;
429 if (ip->midiBuffer == NULL)
430 return 0; /* Not active */
431 readOffset = ip->midiBufferReadOffset;
432 writeOffset = ip->midiBufferWriteOffset;
433 spaceSize = (readOffset + kMDAudioMaxMIDIBytesToSendPerDevice - 1 - writeOffset) % kMDAudioMaxMIDIBytesToSendPerDevice + 1;
434 if (isSysEx) {
435 /* Schedule sysex */
436 if (ip->sysexData != NULL)
437 return 1; /* Sysex is waiting: cannot schedule until this is done */
438 ip->sysexLength = length;
439 ip->sysexData = midiData;
440 return 0;
441 }
442 length2 = length + sizeof(timeStamp) + 1;
443 if (spaceSize <= length2)
444 return 1; /* Buffer overflow */
445 for (i = 0; i < length2; i++) {
446 unsigned char c;
447 if (i < sizeof(timeStamp))
448 c = (timeStamp >> (i * 8)) & 0xff;
449 else if (i == sizeof(timeStamp))
450 c = length & 0xff; /* Should be length <= 255 */
451 else
452 c = midiData[i - sizeof(timeStamp) - 1];
453 ip->midiBuffer[(writeOffset + i) % kMDAudioMaxMIDIBytesToSendPerDevice] = c;
454 }
455 ip->midiBufferWriteOffset = (writeOffset + length2) % kMDAudioMaxMIDIBytesToSendPerDevice;
456 return 0;
457 }
458
459 #pragma mark ====== Device information ======
460
461 static void
462 sMDAudioDeviceInfoDestructor(void *p)
463 {
464 MDAudioDeviceInfo *info = (MDAudioDeviceInfo *)p;
465 if (info->name != NULL)
466 free(info->name);
467 }
468
469 static int
470 sMDAudioDeviceCountChannels(MDAudioDeviceID deviceID, int isInput)
471 {
472 OSStatus err;
473 UInt32 propSize, i;
474 int result;
475 AudioBufferList *buflist;
476 AudioObjectPropertyAddress address;
477
478 address.mSelector = kAudioDevicePropertyStreamConfiguration;
479 address.mScope = (isInput ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput);
480 address.mElement = kAudioObjectPropertyElementMaster;
481
482 err = AudioObjectGetPropertyDataSize(deviceID, &address, 0, NULL, &propSize);
483
484 // err = AudioDeviceGetPropertyInfo(deviceID, 0, isInput, kAudioDevicePropertyStreamConfiguration, &propSize, NULL);
485 if (err != noErr)
486 return 0;
487
488 if (propSize == 0)
489 return 0;
490
491 buflist = (AudioBufferList *)malloc(propSize);
492 if (buflist == NULL)
493 return 0;
494
495 err = AudioObjectGetPropertyData(deviceID, &address, 0, NULL, &propSize, buflist);
496 // err = AudioDeviceGetProperty(deviceID, 0, isInput, kAudioDevicePropertyStreamConfiguration, &propSize, buflist);
497 result = 0;
498 if (err == noErr) {
499 for (i = 0; i < buflist->mNumberBuffers; ++i) {
500 result += buflist->mBuffers[i].mNumberChannels;
501 }
502 }
503
504 free(buflist);
505 return result;
506 }
507
508 static void
509 sMDAudioMusicDeviceInfoDestructor(void *p)
510 {
511 MDAudioMusicDeviceInfo *info = (MDAudioMusicDeviceInfo *)p;
512 if (info->name != NULL)
513 free(info->name);
514 }
515
516 static MDStatus
517 sMDAudioUpdateHardwareDeviceInfo(void)
518 {
519 UInt32 propsize;
520 MDStatus err = noErr;
521 int i, ndevs, isInput;
522 AudioDeviceID *devs;
523 MDArray *ary;
524
525 AudioObjectPropertyAddress address;
526
527 address.mSelector = kAudioHardwarePropertyDevices;
528 address.mScope = kAudioObjectPropertyScopeGlobal;
529 address.mElement = kAudioObjectPropertyElementMaster;
530
531 err = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &address, 0, NULL, &propsize);
532 if (err != noErr)
533 return err;
534 ndevs = propsize / sizeof(AudioDeviceID);
535 devs = (AudioDeviceID *)malloc(sizeof(AudioDeviceID) * ndevs);
536 if (devs == NULL)
537 return kMDErrorOutOfMemory;
538
539 err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &address, 0, NULL, &propsize, devs);
540 if (err != noErr)
541 goto exit;
542 if (gAudio->inputDeviceInfos == NULL) {
543 gAudio->inputDeviceInfos = MDArrayNewWithDestructor(sizeof(MDAudioDeviceInfo), sMDAudioDeviceInfoDestructor);
544 if (gAudio->inputDeviceInfos == NULL) {
545 err = kMDErrorOutOfMemory;
546 goto exit;
547 }
548 }
549 if (gAudio->outputDeviceInfos == NULL) {
550 gAudio->outputDeviceInfos = MDArrayNewWithDestructor(sizeof(MDAudioDeviceInfo), sMDAudioDeviceInfoDestructor);
551 if (gAudio->outputDeviceInfos == NULL) {
552 err = kMDErrorOutOfMemory;
553 goto exit;
554 }
555 }
556 for (isInput = 0; isInput < 2; isInput++) {
557 MDAudioDeviceInfo info, *ip;
558 ary = (isInput ? gAudio->inputDeviceInfos : gAudio->outputDeviceInfos);
559 /* Raise the internal flag for all registered deivces */
560 for (i = MDArrayCount(ary) - 1; i >= 0; i--) {
561 ip = MDArrayFetchPtr(ary, i);
562 ip->flags |= 1;
563 }
564 for (i = 0; i < ndevs; i++) {
565 int nchan = sMDAudioDeviceCountChannels(devs[i], isInput);
566 if (nchan == 0)
567 continue;
568 ip = MDAudioDeviceInfoForDeviceID(devs[i], isInput, NULL);
569 if (ip != NULL) {
570 /* Already known */
571 ip->nChannels = nchan;
572 ip->flags &= ~1;
573 continue;
574 } else {
575 /* Unknown device */
576 char buf[256];
577 UInt32 maxlen = sizeof(buf) - 1;
578 memset(&info, 0, sizeof(info));
579 info.deviceID = devs[i];
580 info.nChannels = sMDAudioDeviceCountChannels(devs[i], isInput);
581 address.mSelector = kAudioDevicePropertyDeviceName;
582 address.mScope = (isInput ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput);
583 err = AudioObjectGetPropertyData(devs[i], &address, 0, NULL, &maxlen, buf);
584 // err = AudioDeviceGetProperty(devs[i], 0, isInput, kAudioDevicePropertyDeviceName, &maxlen, buf);
585 if (err != noErr)
586 goto exit;
587 buf[maxlen] = 0;
588 info.name = strdup(buf);
589 MyAppCallback_startupMessage("Initializing %s...", info.name);
590 propsize = sizeof(UInt32);
591 address.mSelector = kAudioDevicePropertySafetyOffset;
592 if ((err = AudioObjectGetPropertyData(devs[i], &address, 0, NULL, &propsize, &info.safetyOffset)) != noErr)
593 goto exit;
594 propsize = sizeof(UInt32);
595 address.mSelector = kAudioDevicePropertyBufferFrameSize;
596 if ((err = AudioObjectGetPropertyData(devs[i], &address, 0, NULL, &propsize, &info.bufferSizeFrames)) != noErr)
597 goto exit;
598 propsize = sizeof(AudioStreamBasicDescription);
599 address.mSelector = kAudioDevicePropertyStreamFormat;
600 if ((err = AudioObjectGetPropertyData(devs[i], &address, 0, NULL, &propsize, &info.format)) != noErr)
601 goto exit;
602 if ((err = MDArrayInsert(ary, MDArrayCount(ary), 1, &info)) != kMDNoError)
603 goto exit;
604 }
605 }
606 /* Remove non-present devices */
607 for (i = MDArrayCount(ary) - 1; i >= 0; i--) {
608 ip = MDArrayFetchPtr(ary, i);
609 if (ip->flags & 1)
610 MDArrayDelete(ary, i, 1);
611 }
612 }
613
614 exit:
615 free(devs);
616 return err;
617 }
618
619 static MDStatus
620 sMDAudioUpdateSoftwareDeviceInfo(int music_or_effect)
621 {
622 AudioComponentDescription ccd, fcd;
623 AudioComponent cmp = NULL;
624 char *cName;
625 int n, i;
626 UInt32 propSize;
627 MDAudioMusicDeviceInfo info, *ip;
628 MDArray **basep;
629 OSStatus err;
630 MDStatus status = kMDNoError;
631
632 basep = (music_or_effect ? &(gAudio->musicDeviceInfos) : &(gAudio->effectDeviceInfos));
633 if (*basep == NULL) {
634 *basep = MDArrayNewWithDestructor(sizeof(MDAudioMusicDeviceInfo), sMDAudioMusicDeviceInfoDestructor);
635 if (*basep == NULL) {
636 return kMDErrorOutOfMemory;
637 }
638 }
639
640 memset(&fcd, 0, sizeof(fcd));
641 fcd.componentType = (music_or_effect ? kAudioUnitType_MusicDevice : kAudioUnitType_Effect);
642 n = 0;
643 while ((cmp = AudioComponentFindNext(cmp, &fcd)) != 0) {
644 AudioUnit unit;
645 CFStringRef nameRef;
646 CFIndex cflen;
647
648 /* Get the component information */
649 memset(&info, 0, sizeof(info));
650 err = AudioComponentGetDescription(cmp, &ccd);
651 if (err != noErr)
652 continue; /* Cannot get valid description */
653 info.code = (((UInt64)ccd.componentSubType) << 32) + ((UInt64)ccd.componentManufacturer);
654
655 /* Get the component name */
656 err = AudioComponentCopyName(cmp, &nameRef);
657 if (err != noErr)
658 continue; /* Cannot get valid name */
659 cflen = CFStringGetLength(nameRef);
660 if (cflen == 0)
661 continue; /* Empty device name */
662 cName = malloc(cflen * 4 + 1);
663 if (!CFStringGetCString(nameRef, cName, cflen * 4, kCFStringEncodingUTF8))
664 continue; /* Cannot get valid name */
665
666 info.name = cName;
667
668 for (i = 0; (ip = MDArrayFetchPtr(*basep, i)) != NULL; i++) {
669 if (ip->code == info.code && strncmp(ip->name, cName, cflen) == 0) {
670 free(info.name);
671 info.name = NULL;
672 break;
673 }
674 }
675 if (ip != NULL)
676 continue; /* This device is already known */
677
678 MyAppCallback_startupMessage("Loading %s...", info.name);
679
680 /* Get the audio output format */
681 err = AudioComponentInstanceNew(cmp, &unit);
682 if (err == noErr) {
683 propSize = sizeof(MDAudioFormat);
684 err = AudioUnitGetProperty(unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &info.format, &propSize);
685 info.acceptsCanonicalFormat = sMDAudioCompareFormat(&info.format, &gAudio->preferredFormat);
686 } else {
687 unit = NULL;
688 }
689 if (err == noErr) {
690 err = AudioUnitGetPropertyInfo(unit, kAudioUnitProperty_CocoaUI, kAudioUnitScope_Global, 0, &propSize, NULL);
691 if (err == noErr && propSize > 0)
692 info.hasCustomView = kMDAudioHasCocoaView;
693 else {
694 err = AudioUnitGetPropertyInfo(unit, kAudioUnitProperty_GetUIComponentList, kAudioUnitScope_Global, 0, &propSize, NULL);
695 if (err == noErr && propSize > 0)
696 info.hasCustomView = kMDAudioHasCarbonView;
697 else {
698 info.hasCustomView = 0;
699 err = noErr;
700 }
701 }
702 }
703 if (err == noErr) {
704 status = MDArrayInsert(*basep, MDArrayCount(*basep), 1, &info);
705 } else {
706 status = kMDErrorCannotSetupAudio;
707 }
708 if (unit != NULL)
709 AudioComponentInstanceDispose(unit);
710 if (status == kMDNoError)
711 n++;
712 else {
713 free(info.name);
714 info.name = NULL;
715 }
716 }
717 MyAppCallback_startupMessage("");
718 return status;
719 }
720
721 MDStatus
722 MDAudioUpdateDeviceInfo(void)
723 {
724 MDStatus err;
725 err = sMDAudioUpdateHardwareDeviceInfo();
726 if (err == kMDNoError)
727 err = sMDAudioUpdateSoftwareDeviceInfo(1); /* Music device */
728 if (err == kMDNoError)
729 err = sMDAudioUpdateSoftwareDeviceInfo(0); /* Effects */
730 return err;
731 }
732
733 int
734 MDAudioDeviceCountInfo(int isInput)
735 {
736 MDArray *ary = (isInput ? gAudio->inputDeviceInfos : gAudio->outputDeviceInfos);
737 if (ary == NULL)
738 return 0;
739 return MDArrayCount(ary);
740 }
741
742 MDAudioDeviceInfo *
743 MDAudioDeviceInfoAtIndex(int idx, int isInput)
744 {
745 MDArray *ary = (isInput ? gAudio->inputDeviceInfos : gAudio->outputDeviceInfos);
746 if (ary == NULL)
747 return NULL;
748 return MDArrayFetchPtr(ary, idx);
749 }
750
751 MDAudioDeviceInfo *
752 MDAudioDeviceInfoForDeviceID(int deviceID, int isInput, int *deviceIndex)
753 {
754 MDAudioDeviceInfo *ip;
755 int i;
756 MDArray *ary = (isInput ? gAudio->inputDeviceInfos : gAudio->outputDeviceInfos);
757 if (ary == NULL)
758 return NULL;
759 for (i = 0; (ip = MDArrayFetchPtr(ary, i)) != NULL; i++) {
760 if (ip->deviceID == deviceID) {
761 if (deviceIndex != NULL)
762 *deviceIndex = i;
763 return ip;
764 }
765 }
766 return NULL;
767 }
768
769 MDAudioDeviceInfo *
770 MDAudioDeviceInfoWithName(const char *name, int isInput, int *deviceIndex)
771 {
772 MDAudioDeviceInfo *ip;
773 int i;
774 MDArray *ary = (isInput ? gAudio->inputDeviceInfos : gAudio->outputDeviceInfos);
775 if (ary == NULL)
776 return NULL;
777 for (i = 0; (ip = MDArrayFetchPtr(ary, i)) != NULL; i++) {
778 if (strcmp(ip->name, name) == 0) {
779 if (deviceIndex != NULL)
780 *deviceIndex = i;
781 return ip;
782 }
783 }
784 return NULL;
785 }
786
787 int
788 MDAudioMusicDeviceCountInfo(void)
789 {
790 if (gAudio->musicDeviceInfos != NULL)
791 return MDArrayCount(gAudio->musicDeviceInfos);
792 else return 0;
793 }
794
795 MDAudioMusicDeviceInfo *
796 MDAudioMusicDeviceInfoAtIndex(int idx)
797 {
798 if (gAudio->musicDeviceInfos == NULL)
799 return NULL;
800 return MDArrayFetchPtr(gAudio->musicDeviceInfos, idx);
801 }
802
803 MDAudioMusicDeviceInfo *
804 MDAudioMusicDeviceInfoForCode(UInt64 code, int *outIndex)
805 {
806 MDAudioMusicDeviceInfo *ip;
807 int i;
808 if (gAudio->musicDeviceInfos == NULL)
809 return NULL;
810 for (i = 0; (ip = MDArrayFetchPtr(gAudio->musicDeviceInfos, i)) != NULL; i++) {
811 if (ip->code == code) {
812 if (outIndex != NULL)
813 *outIndex = i;
814 return ip;
815 }
816 }
817 if (outIndex != NULL)
818 *outIndex = -1;
819 return NULL;
820 }
821
822 int
823 MDAudioEffectDeviceCountInfo(void)
824 {
825 if (gAudio->effectDeviceInfos != NULL)
826 return MDArrayCount(gAudio->effectDeviceInfos);
827 else return 0;
828 }
829
830 MDAudioMusicDeviceInfo *
831 MDAudioEffectDeviceInfoAtIndex(int idx)
832 {
833 if (gAudio->effectDeviceInfos == NULL)
834 return NULL;
835 return MDArrayFetchPtr(gAudio->effectDeviceInfos, idx);
836 }
837
838 MDAudioMusicDeviceInfo *
839 MDAudioEffectDeviceInfoForCode(UInt64 code, int *outIndex)
840 {
841 MDAudioMusicDeviceInfo *ip;
842 int i;
843 if (gAudio->effectDeviceInfos == NULL)
844 return NULL;
845 for (i = 0; (ip = MDArrayFetchPtr(gAudio->effectDeviceInfos, i)) != NULL; i++) {
846 if (ip->code == code) {
847 if (outIndex != NULL)
848 *outIndex = i;
849 return ip;
850 }
851 }
852 if (outIndex != NULL)
853 *outIndex = -1;
854 return NULL;
855 }
856
857 MDAudioIOStreamInfo *
858 MDAudioGetIOStreamInfoAtIndex(int idx)
859 {
860 if (idx < 0 || idx >= kMDAudioNumberOfStreams)
861 return NULL;
862 return &(gAudio->ioStreamInfos[idx]);
863 }
864
865 #if 0
866 #pragma mark ====== Managing Audio Graph ======
867 #endif
868
869 /*
870 MDAudioIOStreamInfo *ip;
871 MDAudioEffectChain *cp;
872 MDAudioEffect *ep;
873
874 ip->unit
875 |
876 +- cp->converterUnit
877 | |--> [ ep->effect
878 | | |--> {ep->converterUnit --->} ]n--+-->ip->effectMixerUnit
879 | | (optional) | (optional) |
880 | |--> [ ep->effect | |
881 | | |--> {ep->converterUnit --->} ]n--+ |
882 | ... (optional) |
883 +- cp->converterUnit
884 | |--> [ ep->effect |
885 | | |--> {ep->converterUnit --->} ]n--+-->ip->effectMixerUnit
886 | | (optional) | (optional) |
887 | |--> [ ep->effect | |
888 | | |--> {ep->converterUnit --->} ]n--+ |
889 | ... (optional) |
890 | v
891 gAudio->mixer
892 v
893 gAudio->output
894 */
895
896 /* Disconnect the device output within the graph */
897 static int
898 sMDAudioDisconnectDeviceOutput(int streamIndex)
899 {
900 int i, result;
901 AURenderCallbackStruct callback;
902 MDAudioIOStreamInfo *ip = MDAudioGetIOStreamInfoAtIndex(streamIndex);
903 MDAudioEffectChain *cp;
904 if (ip == NULL)
905 return -1;
906 if (ip->deviceIndex >= kMDAudioMusicDeviceIndexOffset) {
907 /* Music Device */
908 CHECK_ERR(result, AudioUnitRemoveRenderNotify(ip->unit, sMDAudioSendMIDIProc, ip));
909 for (i = 0; i < ip->nchains; i++) {
910 cp = ip->chains + i;
911 if (cp->alive) {
912 CHECK_ERR(result, AUGraphDisconnectNodeInput(gAudio->graph, cp->converterNode, 0));
913 }
914 }
915 } else {
916 /* Audio Device */
917 /* Disable callback */
918 callback.inputProc = NULL;
919 callback.inputProcRefCon = NULL;
920 /* If effect chains are active, then remove the callback from
921 each effect chain; otherwise, from the mixer. */
922 for (i = 0; i < ip->nchains; i++) {
923 cp = ip->chains + i;
924 if (cp->alive) {
925 CHECK_ERR(result, AudioUnitSetProperty(cp->converterUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &callback, sizeof(AURenderCallbackStruct)));
926 }
927 }
928 /* Dispose the input AudioUnit */
929 // CHECK_ERR(result, AudioComponentInstanceDispose(ip->unit));
930 }
931 return kMDNoError;
932 exit:
933 return kMDErrorCannotSetupAudio;
934 }
935
936 MDStatus
937 MDAudioSelectIOStreamDevice(int idx, int deviceIndex)
938 {
939 int i, n, len;
940 OSStatus result = noErr;
941 MDAudioIOStreamInfo *ip;
942 MDAudioDeviceInfo *dp = NULL;
943 MDAudioMusicDeviceInfo *mp = NULL;
944 MDAudioEffectChain *cp;
945 MDStatus sts;
946 AudioDeviceID audioDeviceID;
947 AURenderCallbackStruct callback;
948 unsigned char midiSetupChanged = 0;
949
950 ip = MDAudioGetIOStreamInfoAtIndex(idx);
951 if (ip == NULL)
952 return kMDErrorCannotSetupAudio;
953
954 /* No change required? */
955 if (ip->deviceIndex == deviceIndex && ip->busIndex == idx)
956 return kMDNoError;
957
958 CHECK_ERR(result, AUGraphStop(gAudio->graph));
959
960 if (idx >= kMDAudioFirstIndexForOutputStream) {
961 /* Output stream */
962 dp = MDAudioDeviceInfoAtIndex(deviceIndex, 0);
963 ip->deviceIndex = -1; /* Will be overwritten later */
964 if (dp == NULL) {
965 /* Leave the node as it is, just disabling the audio thru */
966 gAudio->isAudioThruEnabled = 0;
967 ip->busIndex = -1;
968 } else {
969 if ((UInt64)(dp->deviceID) != ip->deviceID) {
970 /* Set the output device to the output unit */
971 audioDeviceID = dp->deviceID;
972 result = AudioUnitSetProperty(gAudio->outputUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &audioDeviceID, sizeof(AudioDeviceID));
973 if (result == noErr) {
974 MDAudioFormat format;
975 UInt32 propSize;
976 /* Initialize the audio unit */
977 //CHECK_ERR(result, AudioUnitInitialize(gAudio->outputUnit));
978 /* Set the preferred format of the output unit to the output side of the converter unit */
979 propSize = sizeof(MDAudioFormat);
980 CHECK_ERR(result, AudioUnitGetProperty(gAudio->outputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Global, 0, &format, &propSize));
981 gAudio->isAudioThruEnabled = 1;
982 ip->busIndex = idx;
983 ip->deviceID = (UInt64)(dp->deviceID);
984 ip->deviceIndex = deviceIndex;
985 /* Reallocate buffer list */
986 ip->bufferSizeFrames = dp->bufferSizeFrames; /* The buffer size of the underlying audio device; NOTE: dp must be alive until here! */
987 if (ip->bufferList != NULL)
988 sMDAudioReleaseMyBufferList(ip->bufferList);
989 ip->bufferList = sMDAudioAllocateMyBufferList(gAudio->preferredFormat.mChannelsPerFrame, gAudio->preferredFormat.mBytesPerFrame, ip->bufferSizeFrames);
990 /* Reallocate ring buffer */
991 if (ip->ring != NULL)
992 MDRingBufferRelease(ip->ring);
993 ip->ring = MDRingBufferNew();
994 MDRingBufferAllocate(ip->ring, gAudio->preferredFormat.mChannelsPerFrame, gAudio->preferredFormat.mBytesPerFrame, dp->bufferSizeFrames * 4);
995 ip->firstInputTime = ip->firstOutputTime = -1;
996 } else {
997 gAudio->isAudioThruEnabled = 0;
998 ip->busIndex = -1;
999 }
1000 } else {
1001 gAudio->isAudioThruEnabled = 1;
1002 ip->busIndex = idx;
1003 ip->deviceIndex = deviceIndex;
1004 }
1005 }
1006 } else {
1007 /* Input stream */
1008 UInt64 newDeviceID;
1009 AudioComponentDescription desc;
1010 MDAudioFormat format = {0};
1011 if (deviceIndex >= kMDAudioMusicDeviceIndexOffset) {
1012 mp = MDAudioMusicDeviceInfoAtIndex(deviceIndex - kMDAudioMusicDeviceIndexOffset);
1013 newDeviceID = (mp != NULL ? mp->code : kMDAudioMusicDeviceUnknown);
1014 if (mp != NULL)
1015 format = mp->format;
1016 } else {
1017 dp = MDAudioDeviceInfoAtIndex(deviceIndex, 1);
1018 newDeviceID = (dp != NULL ? (UInt64)(dp->deviceID) : kMDAudioMusicDeviceUnknown);
1019 if (dp != NULL)
1020 format = dp->format;
1021 }
1022
1023 if (ip->deviceID != kMDAudioMusicDeviceUnknown) {
1024 /* Disable the current input */
1025 CHECK_ERR(result, sMDAudioDisconnectDeviceOutput(idx));
1026 if (ip->deviceIndex >= kMDAudioMusicDeviceIndexOffset) {
1027 /* Remove from the graph */
1028 CHECK_ERR(result, AUGraphRemoveNode(gAudio->graph, ip->node));
1029 /* Dispose MIDI controller name and MIDI buffers */
1030 if (ip->midiControllerName != NULL) {
1031 free(ip->midiControllerName);
1032 ip->midiControllerName = NULL;
1033 }
1034 if (ip->midiBuffer != NULL) {
1035 free(ip->midiBuffer);
1036 ip->midiBuffer = NULL;
1037 }
1038 if (ip->bufferList != NULL) {
1039 sMDAudioReleaseMyBufferList(ip->bufferList);
1040 ip->bufferList = NULL;
1041 }
1042 if (ip->ring != NULL) {
1043 MDRingBufferDeallocate(ip->ring);
1044 ip->ring = NULL;
1045 }
1046 midiSetupChanged = 1;
1047 } else {
1048 /* Dispose the input AudioUnit */
1049 CHECK_ERR(result, AudioComponentInstanceDispose(ip->unit));
1050 }
1051 ip->deviceID = kMDAudioMusicDeviceUnknown;
1052 ip->node = 0;
1053 ip->unit = NULL;
1054 ip->deviceIndex = -1;
1055 ip->busIndex = -1;
1056 }
1057
1058 if (newDeviceID != kMDAudioMusicDeviceUnknown) {
1059 /* Enable the new input */
1060 /* Allocate the first converter unit if not present */
1061 if (ip->nchains == 0) {
1062 ip->chains = (MDAudioEffectChain *)calloc(sizeof(MDAudioEffectChain), 8);
1063 ip->nchains = 1;
1064 }
1065 if (ip->chains->converterUnit == NULL) {
1066 /* Create converter for the effector chain 0 */
1067 cp = ip->chains;
1068 desc.componentType = kAudioUnitType_FormatConverter;
1069 desc.componentSubType = kAudioUnitSubType_AUConverter;
1070 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
1071 desc.componentFlags = desc.componentFlagsMask = 0;
1072 CHECK_ERR(result, AUGraphAddNode(gAudio->graph, &desc, &cp->converterNode));
1073 /* Open component */
1074 CHECK_ERR(result, AUGraphOpen(gAudio->graph));
1075 CHECK_ERR(result, AUGraphNodeInfo(gAudio->graph, cp->converterNode, NULL, &cp->converterUnit));
1076 /* Set output format and connect to the mixer */
1077 CHECK_ERR(result, AudioUnitSetProperty(cp->converterUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &gAudio->preferredFormat, sizeof(AudioStreamBasicDescription)));
1078 CHECK_ERR(result, AUGraphConnectNodeInput(gAudio->graph, cp->converterNode, 0, gAudio->mixer, idx));
1079 }
1080 if (deviceIndex >= kMDAudioMusicDeviceIndexOffset) {
1081 /* Music Device */
1082 /* Create input node */
1083 desc.componentType = kAudioUnitType_MusicDevice;
1084 desc.componentSubType = (UInt32)(newDeviceID >> 32);
1085 desc.componentManufacturer = (UInt32)(newDeviceID);
1086 desc.componentFlags = desc.componentFlagsMask = 0;
1087 CHECK_ERR(result, AUGraphAddNode(gAudio->graph, &desc, &ip->node));
1088 /* Open component */
1089 CHECK_ERR(result, AUGraphOpen(gAudio->graph));
1090 CHECK_ERR(result, AUGraphNodeInfo(gAudio->graph, ip->node, NULL, &ip->unit));
1091 /* Connect the output to the converter unit(s) and set format */
1092 for (i = 0; i < ip->nchains; i++) {
1093 cp = ip->chains + i;
1094 result = AUGraphConnectNodeInput(gAudio->graph, ip->node, i, cp->converterNode, 0);
1095 cp->alive = (result == noErr);
1096 CHECK_ERR(result, AudioUnitSetProperty(cp->converterUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &format, sizeof(AudioStreamBasicDescription)));
1097 }
1098 /* Create the MIDI controller name */
1099 len = (int)strlen(mp->name) + 5;
1100 ip->midiControllerName = (char *)malloc(len);
1101 strcpy(ip->midiControllerName, mp->name);
1102 /* Check the duplicate */
1103 for (i = 0, n = 2; i < kMDAudioNumberOfInputStreams; i++) {
1104 MDAudioIOStreamInfo *ip2 = MDAudioGetIOStreamInfoAtIndex(i);
1105 if (ip == ip2 || ip2->midiControllerName == NULL)
1106 continue;
1107 if (strcmp(ip->midiControllerName, ip2->midiControllerName) == 0) {
1108 snprintf(ip->midiControllerName, len, "%s %d", mp->name, n);
1109 n++;
1110 i = -1;
1111 continue;
1112 }
1113 }
1114 /* Allocate buffer for MIDI scheduling */
1115 ip->midiBuffer = (unsigned char *)malloc(kMDAudioMIDIBufferSize);
1116 ip->midiBufferWriteOffset = 0;
1117 ip->midiBufferReadOffset = 0;
1118 /* Set render notify callback */
1119 CHECK_ERR(result, AudioUnitAddRenderNotify(ip->unit, sMDAudioSendMIDIProc, ip));
1120 midiSetupChanged = 1;
1121 } else {
1122 /* Audio Device */
1123 /* Create HAL input unit (not connected to AUGraph) */
1124 /* Cf. Apple Technical Note 2091 */
1125 AudioComponent comp;
1126 UInt32 unum;
1127 desc.componentType = kAudioUnitType_Output;
1128 desc.componentSubType = kAudioUnitSubType_HALOutput;
1129 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
1130 desc.componentFlags = 0;
1131 desc.componentFlagsMask = 0;
1132 comp = AudioComponentFindNext(NULL, &desc);
1133 if (comp == NULL)
1134 return kMDErrorCannotSetupAudio;
1135 CHECK_ERR(result, AudioComponentInstanceNew(comp, &ip->unit));
1136 /* Enable input */
1137 unum = 1;
1138 CHECK_ERR(result, AudioUnitSetProperty(ip->unit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &unum, sizeof(UInt32)));
1139 /* Disable output */
1140 unum = 0;
1141 CHECK_ERR(result, AudioUnitSetProperty(ip->unit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &unum, sizeof(UInt32)));
1142 /* Set the HAL AU output format to the canonical format */
1143 CHECK_ERR(result, AudioUnitSetProperty(ip->unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &gAudio->preferredFormat, sizeof(AudioStreamBasicDescription)));
1144 /* Set the AU callback function */
1145 callback.inputProc = sMDAudioInputProc;
1146 callback.inputProcRefCon = ip;
1147 CHECK_ERR(result, AudioUnitSetProperty(ip->unit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &callback, sizeof(AURenderCallbackStruct)));
1148 /* Connect the output to the converter unit(s) and set format */
1149 callback.inputProc = sMDAudioPassProc;
1150 callback.inputProcRefCon = ip;
1151 for (i = 0; i < ip->nchains; i++) {
1152 cp = ip->chains + i;
1153 result = AudioUnitSetProperty(cp->converterUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, i, &callback, sizeof(AURenderCallbackStruct));
1154 cp->alive = (result == noErr);
1155 CHECK_ERR(result, AudioUnitSetProperty(cp->converterUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &gAudio->preferredFormat, sizeof(AudioStreamBasicDescription)));
1156 }
1157 /* Set the input device */
1158 audioDeviceID = (AudioDeviceID)newDeviceID;
1159 CHECK_ERR(result, AudioUnitSetProperty(ip->unit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &audioDeviceID, sizeof(AudioDeviceID)));
1160 /* Reallocate buffer list */
1161 ip->bufferSizeFrames = dp->bufferSizeFrames; /* The buffer size of the underlying audio device; NOTE: dp must be alive until here! */
1162 ip->bufferList = sMDAudioAllocateMyBufferList(gAudio->preferredFormat.mChannelsPerFrame, gAudio->preferredFormat.mBytesPerFrame, ip->bufferSizeFrames);
1163
1164 /* Reallocate ring buffer */
1165 ip->ring = MDRingBufferNew();
1166 MDRingBufferAllocate(ip->ring, gAudio->preferredFormat.mChannelsPerFrame, gAudio->preferredFormat.mBytesPerFrame, ip->bufferSizeFrames * 20);
1167
1168 ip->firstInputTime = ip->firstOutputTime = -1;
1169 /* Initialize and start the AUHAL */
1170 CHECK_ERR(result, AudioUnitInitialize(ip->unit));
1171 CHECK_ERR(result, AudioOutputUnitStart(ip->unit));
1172 }
1173 ip->deviceIndex = deviceIndex;
1174 ip->busIndex = idx;
1175 ip->format = format;
1176 }
1177 }
1178 exit:
1179 sts = (result == noErr ? kMDNoError : kMDErrorCannotSetupAudio);
1180 if (sts == kMDNoError && midiSetupChanged)
1181 MDPlayerNotificationCallback();
1182 result = AUGraphStart(gAudio->graph);
1183 return (sts == kMDNoError && sts == noErr ? kMDNoError : kMDErrorCannotSetupAudio);
1184 }
1185
1186 MDStatus
1187 MDAudioGetIOStreamDevice(int idx, int *outDeviceIndex)
1188 {
1189 if (idx < 0 || idx >= kMDAudioNumberOfStreams) {
1190 if (outDeviceIndex != NULL)
1191 *outDeviceIndex = -1;
1192 return kMDErrorCannotSetupAudio;
1193 }
1194 if (idx >= kMDAudioFirstIndexForOutputStream) {
1195 if (!gAudio->isAudioThruEnabled) {
1196 /* Behave as if no device is set */
1197 *outDeviceIndex = -1;
1198 return kMDNoError;
1199 }
1200 }
1201 if (outDeviceIndex != NULL)
1202 *outDeviceIndex = gAudio->ioStreamInfos[idx].deviceIndex;
1203 return kMDNoError;
1204 }
1205
1206 #pragma mark ====== Audio Effects ======
1207
1208 MDStatus
1209 MDAudioAppendEffectChain(int streamIndex)
1210 {
1211 int sts, result;
1212 MDAudioIOStreamInfo *ip;
1213 MDAudioEffectChain *cp;
1214 MDAudioEffect *ep;
1215 AudioComponentDescription desc;
1216 AUNode node;
1217
1218 if (streamIndex < 0 || streamIndex >= kMDAudioNumberOfInputStreams)
1219 return -1; /* Invalid streamIndex */
1220 ip = MDAudioGetIOStreamInfoAtIndex(streamIndex);
1221 if (ip->nchains >= 64)
1222 return -2; /* Too many chains */
1223 if (ip->nchains % 8 == 0) {
1224 /* Allocate storage */
1225 void *p = realloc(ip->chains, sizeof(MDAudioEffectChain) * (ip->nchains + 8));
1226 if (p == NULL)
1227 return -3; /* Out of memory */
1228 ip->chains = p;
1229 }
1230 cp = ip->chains + ip->nchains;
1231 memset(cp, 0, sizeof(MDAudioEffectChain));
1232 ip->nchains++;
1233
1234 CHECK_ERR(result, AUGraphStop(gAudio->graph));
1235
1236 /* Create a converter (which is the entrance of the chain) */
1237 desc.componentType = kAudioUnitType_FormatConverter;
1238 desc.componentSubType = kAudioUnitSubType_AUConverter;
1239 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
1240 desc.componentFlags = desc.componentFlagsMask = 0;
1241 CHECK_ERR(result, AUGraphAddNode(gAudio->graph, &desc, &cp->converterNode));
1242 /* Open component */
1243 CHECK_ERR(result, AUGraphOpen(gAudio->graph));
1244 CHECK_ERR(result, AUGraphNodeInfo(gAudio->graph, cp->converterNode, NULL, &cp->converterUnit));
1245 /* Set output format */
1246 CHECK_ERR(result, AudioUnitSetProperty(cp->converterUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &gAudio->preferredFormat, sizeof(AudioStreamBasicDescription)));
1247 /* Create a mixer for this chain (when nchains == 2) */
1248 if (ip->nchains == 2) {
1249 desc.componentType = kAudioUnitType_Mixer;
1250 desc.componentSubType = kAudioUnitSubType_StereoMixer;
1251 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
1252 desc.componentFlags = desc.componentFlagsMask = 0;
1253 CHECK_ERR(result, AUGraphAddNode(gAudio->graph, &desc, &ip->effectMixerNode));
1254 CHECK_ERR(result, AUGraphOpen(gAudio->graph));
1255 CHECK_ERR(result, AUGraphNodeInfo(gAudio->graph, ip->effectMixerNode, NULL, &ip->effectMixerUnit));
1256 CHECK_ERR(result, AudioUnitSetProperty(ip->effectMixerUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Global, 0, &gAudio->preferredFormat, sizeof(AudioStreamBasicDescription)));
1257 /* Disconnect the last unit in chain 0 from gAudio->mixer and
1258 reconnect to the ip->effectMixerNode */
1259 CHECK_ERR(result, AUGraphDisconnectNodeInput(gAudio->graph, gAudio->mixer, streamIndex));
1260 if (ip->chains->neffects == 0)
1261 node = ip->chains->converterNode;
1262 else {
1263 /* Last effect entry */
1264 ep = ip->chains->effects + ip->chains->neffects - 1;
1265 if (ep->converterUnit != NULL)
1266 node = ep->converterNode;
1267 else node = ep->node;
1268 }
1269 CHECK_ERR(result, AUGraphConnectNodeInput(gAudio->graph, node, 0, ip->effectMixerNode, 0));
1270 /* Connect ip->effectMixerNode to gAudio->mixer */
1271 CHECK_ERR(result, AUGraphConnectNodeInput(gAudio->graph, ip->effectMixerNode, 0, gAudio->mixer, streamIndex));
1272 }
1273 /* Connect cp->converterNode to ip->effectMixerNode */
1274 CHECK_ERR(result, AUGraphConnectNodeInput(gAudio->graph, cp->converterNode, 0, ip->effectMixerNode, ip->nchains - 1));
1275 /* Connect ip->unit to cp->converterNode */
1276 if (ip->deviceIndex >= kMDAudioMusicDeviceIndexOffset) {
1277 /* Music device */
1278 UInt32 propSize, count;
1279 propSize = sizeof(UInt32);
1280 CHECK_ERR(result, AudioUnitGetProperty(ip->unit, kAudioUnitProperty_ElementCount, kAudioUnitScope_Output, ip->node, &count, &propSize));
1281 if (count >= ip->nchains) {
1282 AudioStreamBasicDescription format;
1283 cp->alive = 1;
1284 CHECK_ERR(result, AUGraphConnectNodeInput(gAudio->graph, ip->node, ip->nchains - 1, cp->converterNode, 0));
1285 propSize = sizeof(AudioStreamBasicDescription);
1286 CHECK_ERR(result, AudioUnitGetProperty(ip->unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, ip->nchains - 1, &format, &propSize));
1287 CHECK_ERR(result, AudioUnitSetProperty(cp->converterUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &format, propSize));
1288 } else cp->alive = 0;
1289 } else {
1290 /* HAL: we do not support more than one stereo bus (for now) */
1291 cp->alive = 0;
1292 }
1293
1294 exit:
1295 sts = (result == noErr ? kMDNoError : kMDErrorCannotSetupAudio);
1296 result = AUGraphStart(gAudio->graph);
1297 return (result == noErr && sts == kMDNoError ? 0 : kMDErrorCannotSetupAudio);
1298 }
1299
1300 MDStatus
1301 MDAudioRemoveLastEffectChain(int streamIndex)
1302 {
1303 int sts, result;
1304 MDAudioIOStreamInfo *ip;
1305 MDAudioEffectChain *cp;
1306 MDAudioEffect *ep;
1307 AUNode node;
1308
1309 if (streamIndex < 0 || streamIndex >= kMDAudioNumberOfInputStreams)
1310 return -1; /* Invalid streamIndex */
1311 ip = MDAudioGetIOStreamInfoAtIndex(streamIndex);
1312 if (ip->nchains <= 1)
1313 return -2; /* At least one chain should be present */
1314 cp = ip->chains + (ip->nchains - 1);
1315 if (cp->neffects > 0)
1316 return -3; /* The chain should be empty */
1317 ip->nchains--;
1318
1319 CHECK_ERR(result, AUGraphStop(gAudio->graph));
1320
1321 /* Disonnect cp->converterNode from ip->effectMixerNode */
1322 CHECK_ERR(result, AUGraphDisconnectNodeInput(gAudio->graph, ip->effectMixerNode, ip->nchains));
1323 /* Disonnect ip->unit from cp->converterNode */
1324 if (cp->alive != 0) {
1325 CHECK_ERR(result, AUGraphDisconnectNodeInput(gAudio->graph, cp->converterNode, 0));
1326 }
1327 /* Dispose cp->converterNode */
1328 CHECK_ERR(result, AUGraphRemoveNode(gAudio->graph, cp->converterNode));
1329 cp->converterNode = 0;
1330 cp->converterUnit = NULL;
1331
1332 /* Dispose the mixer for this chain (when nchains was 2) */
1333 if (ip->nchains == 1) {
1334 /* Disonnect ip->effectMixerNode from gAudio->mixer */
1335 CHECK_ERR(result, AUGraphDisconnectNodeInput(gAudio->graph, gAudio->mixer, streamIndex));
1336 /* Disconnect the last unit in chain 0 from ip->effectMixerNode and
1337 reconnect to gAudio->mixer */
1338 CHECK_ERR(result, AUGraphDisconnectNodeInput(gAudio->graph, ip->effectMixerNode, 0));
1339 if (ip->chains->neffects == 0)
1340 node = ip->chains->converterNode;
1341 else {
1342 /* Last effect entry */
1343 ep = ip->chains->effects + ip->chains->neffects - 1;
1344 if (ep->converterUnit != NULL)
1345 node = ep->converterNode;
1346 else node = ep->node;
1347 }
1348 CHECK_ERR(result, AUGraphConnectNodeInput(gAudio->graph, node, 0, gAudio->mixer, streamIndex));
1349 /* Dispose the mixer */
1350 CHECK_ERR(result, AUGraphRemoveNode(gAudio->graph, ip->effectMixerNode));
1351 ip->effectMixerNode = 0;
1352 ip->effectMixerUnit = NULL;
1353 }
1354
1355 exit:
1356 sts = (result == noErr ? kMDNoError : kMDErrorCannotSetupAudio);
1357 result = AUGraphStart(gAudio->graph);
1358 return (result == noErr && sts == kMDNoError ? 0 : kMDErrorCannotSetupAudio);
1359 }
1360
1361 MDStatus
1362 MDAudioChangeEffect(int streamIndex, int chainIndex, int effectIndex, int effectID, int insert)
1363 {
1364 int sts, result;
1365 MDAudioIOStreamInfo *ip;
1366 MDAudioEffectChain *cp;
1367 MDAudioEffect *ep;
1368 MDAudioMusicDeviceInfo *mp, *lastmp, *nextmp;
1369 AudioComponentDescription desc;
1370 AUNode lastNode, nextNode;
1371 int nextNodeBus;
1372
1373 if (streamIndex < 0 || streamIndex >= kMDAudioNumberOfInputStreams)
1374 return -1; /* Invalid streamIndex */
1375 ip = MDAudioGetIOStreamInfoAtIndex(streamIndex);
1376 if (chainIndex < 0 || chainIndex >= ip->nchains)
1377 return -2; /* Invalid chainIndex */
1378 mp = MDAudioEffectDeviceInfoAtIndex(effectID);
1379 if (mp == NULL)
1380 return -3; /* effectID out of range */
1381 cp = ip->chains + chainIndex;
1382 if (effectIndex < 0 || effectIndex > cp->neffects)
1383 return -4; /* Invalid effectIndex */
1384 if (effectIndex == cp->neffects && insert == 0)
1385 return -4; /* Invalid effectIndex */
1386 if (insert) {
1387 if (cp->neffects % 8 == 0) {
1388 /* Allocate storage */
1389 void *p = realloc(cp->effects, sizeof(MDAudioEffect) * (cp->neffects + 8));
1390 if (p == NULL)
1391 return -5; /* Out of memory */
1392 cp->effects = p;
1393 }
1394 ep = cp->effects + effectIndex;
1395 if (effectIndex < cp->neffects) {
1396 memmove(ep + 1, ep, sizeof(MDAudioEffect) * (cp->neffects - effectIndex));
1397 }
1398 cp->neffects++;
1399 memset(ep, 0, sizeof(MDAudioEffect));
1400 } else {
1401 ep = cp->effects + effectIndex;
1402 if (ep->effectDeviceIndex == effectID)
1403 return 0; /* Same effect: no action */
1404 }
1405
1406 CHECK_ERR(result, AUGraphStop(gAudio->graph));
1407
1408 /* Specify next nodes and disable the existing connection to it */
1409 if (effectIndex == cp->neffects - 1) {
1410 if (ip->effectMixerUnit != NULL) {
1411 nextNode = ip->effectMixerNode;
1412 nextNodeBus = chainIndex;
1413 } else {
1414 nextNode = gAudio->mixer;
1415 nextNodeBus = streamIndex;
1416 }
1417 } else {
1418 nextNode = ep[1].node;
1419 nextNodeBus = 0;
1420 }
1421 CHECK_ERR(result, AUGraphDisconnectNodeInput(gAudio->graph, nextNode, nextNodeBus));
1422
1423 if (!insert) {
1424 /* Disable the connection to the converter, if present */
1425 if (ep->converterUnit != NULL) {
1426 CHECK_ERR(result, AUGraphDisconnectNodeInput(gAudio->graph, ep->converterNode, 0));
1427 /* Note: the converter unit will be disposed later, if it is unnecessary */
1428 }
1429 /* Disable the existing connection to the present node */
1430 CHECK_ERR(result, AUGraphDisconnectNodeInput(gAudio->graph, ep->node, 0));
1431 /* Dispose the present node */
1432 CHECK_ERR(result, AUGraphRemoveNode(gAudio->graph, ep->node));
1433 ep->node = 0;
1434 ep->unit = NULL;
1435 }
1436
1437 /* Create a new effect node */
1438 desc.componentType = kAudioUnitType_Effect;
1439 desc.componentSubType = (UInt32)(mp->code >> 32);
1440 desc.componentManufacturer = (UInt32)(mp->code);
1441 desc.componentFlags = desc.componentFlagsMask = 0;
1442 CHECK_ERR(result, AUGraphAddNode(gAudio->graph, &desc, &ep->node));
1443 CHECK_ERR(result, AUGraphOpen(gAudio->graph));
1444 CHECK_ERR(result, AUGraphNodeInfo(gAudio->graph, ep->node, NULL, &ep->unit));
1445
1446 if (effectIndex == 0) {
1447 lastNode = cp->converterNode;
1448 lastmp = NULL;
1449 /* Set output format of the converter unit */
1450 CHECK_ERR(result, AudioUnitSetProperty(cp->converterUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &mp->format, sizeof(AudioStreamBasicDescription)));
1451 } else {
1452 lastmp = MDAudioEffectDeviceInfoAtIndex(ep[-1].effectDeviceIndex);
1453 if (sMDAudioCompareFormat(&mp->format, &lastmp->format)) {
1454 /* We do not need converter in the last effect block */
1455 if (ep[-1].converterUnit != NULL) {
1456 CHECK_ERR(result, AUGraphDisconnectNodeInput(gAudio->graph, ep[-1].converterNode, 0));
1457 CHECK_ERR(result, AUGraphRemoveNode(gAudio->graph, ep[-1].converterNode));
1458 ep[-1].converterNode = 0;
1459 ep[-1].converterUnit = NULL;
1460 }
1461 lastNode = ep[-1].node;
1462 } else {
1463 /* We do need converter */
1464 if (ep[-1].converterUnit == NULL) {
1465 /* Create ep->converterNode */
1466 desc.componentType = kAudioUnitType_FormatConverter;
1467 desc.componentSubType = kAudioUnitSubType_AUConverter;
1468 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
1469 desc.componentFlags = desc.componentFlagsMask = 0;
1470 CHECK_ERR(result, AUGraphAddNode(gAudio->graph, &desc, &ep[-1].converterNode));
1471 /* Open component */
1472 CHECK_ERR(result, AUGraphOpen(gAudio->graph));
1473 CHECK_ERR(result, AUGraphNodeInfo(gAudio->graph, ep[-1].converterNode, NULL, &ep[-1].converterUnit));
1474 /* Set input format */
1475 CHECK_ERR(result, AudioUnitSetProperty(ep[-1].converterUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &lastmp->format, sizeof(AudioStreamBasicDescription)));
1476 /* Reconnect effect->converter->next */
1477 CHECK_ERR(result, AUGraphConnectNodeInput(gAudio->graph, ep[-1].node, 0, ep[-1].converterNode, 0));
1478 }
1479 lastNode = ep[-1].converterNode;
1480 /* Set output format of the converter unit */
1481 CHECK_ERR(result, AudioUnitSetProperty(ep[-1].converterUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &mp->format, sizeof(AudioStreamBasicDescription)));
1482 }
1483 }
1484 /* Connect last node */
1485 CHECK_ERR(result, AUGraphConnectNodeInput(gAudio->graph, lastNode, 0, ep->node, 0));
1486 /* Do we need a converter after the effect? */
1487 if (effectIndex >= cp->neffects - 1) {
1488 nextmp = NULL;
1489 result = mp->acceptsCanonicalFormat;
1490 } else {
1491 nextmp = MDAudioEffectDeviceInfoAtIndex(ep[1].effectDeviceIndex);
1492 result = sMDAudioCompareFormat(&mp->format, &nextmp->format);
1493 }
1494 if (result == 0) {
1495 /* We do need a converter */
1496 if (ep->converterUnit == NULL) {
1497 desc.componentType = kAudioUnitType_FormatConverter;
1498 desc.componentSubType = kAudioUnitSubType_AUConverter;
1499 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
1500 desc.componentFlags = desc.componentFlagsMask = 0;
1501 CHECK_ERR(result, AUGraphAddNode(gAudio->graph, &desc, &ep->converterNode));
1502 /* Open component */
1503 CHECK_ERR(result, AUGraphOpen(gAudio->graph));
1504 CHECK_ERR(result, AUGraphNodeInfo(gAudio->graph, ep->converterNode, NULL, &ep->converterUnit));
1505 }
1506 /* Set output format */
1507 CHECK_ERR(result, AudioUnitSetProperty(ep->converterUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, (nextmp == NULL ? &gAudio->preferredFormat : &nextmp->format), sizeof(AudioStreamBasicDescription)));
1508 /* Set input format */
1509 CHECK_ERR(result, AudioUnitSetProperty(ep->converterUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &mp->format, sizeof(AudioStreamBasicDescription)));
1510 /* Connect effect to converter */
1511 CHECK_ERR(result, AUGraphConnectNodeInput(gAudio->graph, ep->node, 0, ep->converterNode, 0));
1512 /* Connect converter to the next node */
1513 CHECK_ERR(result, AUGraphConnectNodeInput(gAudio->graph, ep->converterNode, 0, nextNode, nextNodeBus));
1514 } else {
1515 /* Connect effect to the next node */
1516 CHECK_ERR(result, AUGraphConnectNodeInput(gAudio->graph, ep->node, 0, nextNode, nextNodeBus));
1517 /* Dispose the existing converter, as we do not need it */
1518 if (ep->converterUnit != NULL) {
1519 CHECK_ERR(result, AUGraphRemoveNode(gAudio->graph, ep->converterNode));
1520 ep->converterNode = 0;
1521 ep->converterUnit = NULL;
1522 }
1523 }
1524 ep->effectDeviceIndex = effectID;
1525 ep->name = strdup(mp->name);
1526
1527 exit:
1528 sts = (result == noErr ? kMDNoError : kMDErrorCannotSetupAudio);
1529 result = AUGraphStart(gAudio->graph);
1530 return (result == noErr && sts == kMDNoError ? 0 : kMDErrorCannotSetupAudio);
1531 }
1532
1533 MDStatus
1534 MDAudioRemoveEffect(int streamIndex, int chainIndex, int effectIndex)
1535 {
1536 int sts, result;
1537 MDAudioIOStreamInfo *ip;
1538 MDAudioEffectChain *cp;
1539 MDAudioEffect *ep;
1540 MDAudioMusicDeviceInfo *mp, *lastmp;
1541 AudioStreamBasicDescription *fmtp;
1542 AudioComponentDescription desc;
1543 AUNode lastNode, nextNode;
1544 int nextNodeBus;
1545
1546 if (streamIndex < 0 || streamIndex >= kMDAudioNumberOfInputStreams)
1547 return -1; /* Invalid streamIndex */
1548 ip = MDAudioGetIOStreamInfoAtIndex(streamIndex);
1549 if (chainIndex < 0 || chainIndex >= ip->nchains)
1550 return -2; /* Invalid chainIndex */
1551 cp = ip->chains + chainIndex;
1552 if (effectIndex < 0 || effectIndex >= cp->neffects)
1553 return -4; /* Invalid effectIndex */
1554 ep = cp->effects + effectIndex;
1555
1556 CHECK_ERR(result, AUGraphStop(gAudio->graph));
1557
1558 /* Specify next nodes and disable the existing connection to it */
1559 if (effectIndex == cp->neffects - 1) {
1560 mp = NULL;
1561 if (ip->effectMixerUnit != NULL) {
1562 nextNode = ip->effectMixerNode;
1563 nextNodeBus = chainIndex;
1564 } else {
1565 nextNode = gAudio->mixer;
1566 nextNodeBus = streamIndex;
1567 }
1568 fmtp = &gAudio->preferredFormat;
1569 } else {
1570 nextNode = ep[1].node;
1571 nextNodeBus = 0;
1572 mp = MDAudioEffectDeviceInfoAtIndex(ep[1].effectDeviceIndex);
1573 fmtp = &mp->format;
1574 }
1575 CHECK_ERR(result, AUGraphDisconnectNodeInput(gAudio->graph, nextNode, nextNodeBus));
1576
1577 /* Disable the connection to the converter, if present */
1578 if (ep->converterUnit != NULL) {
1579 CHECK_ERR(result, AUGraphDisconnectNodeInput(gAudio->graph, ep->converterNode, 0));
1580 CHECK_ERR(result, AUGraphRemoveNode(gAudio->graph, ep->converterNode));
1581 }
1582
1583 /* Disable the existing connection to the present node */
1584 CHECK_ERR(result, AUGraphDisconnectNodeInput(gAudio->graph, ep->node, 0));
1585 /* Dispose the present node */
1586 CHECK_ERR(result, AUGraphRemoveNode(gAudio->graph, ep->node));
1587
1588 /* Specify the last node */
1589 if (effectIndex == 0) {
1590 lastNode = cp->converterNode;
1591 lastmp = NULL;
1592 /* Set output format of the converter unit */
1593 CHECK_ERR(result, AudioUnitSetProperty(cp->converterUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, fmtp, sizeof(AudioStreamBasicDescription)));
1594 } else {
1595 lastmp = MDAudioEffectDeviceInfoAtIndex(ep[-1].effectDeviceIndex);
1596 if (sMDAudioCompareFormat(fmtp, &lastmp->format)) {
1597 /* We do not need converter in the last effect block */
1598 if (ep[-1].converterUnit != NULL) {
1599 CHECK_ERR(result, AUGraphDisconnectNodeInput(gAudio->graph, ep[-1].converterNode, 0));
1600 CHECK_ERR(result, AUGraphRemoveNode(gAudio->graph, ep[-1].converterNode));
1601 ep[-1].converterNode = 0;
1602 ep[-1].converterUnit = NULL;
1603 }
1604 lastNode = ep[-1].node;
1605 } else {
1606 /* We do need converter */
1607 if (ep[-1].converterUnit == NULL) {
1608 /* Create ep->converterNode */
1609 desc.componentType = kAudioUnitType_FormatConverter;
1610 desc.componentSubType = kAudioUnitSubType_AUConverter;
1611 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
1612 desc.componentFlags = desc.componentFlagsMask = 0;
1613 CHECK_ERR(result, AUGraphAddNode(gAudio->graph, &desc, &ep[-1].converterNode));
1614 /* Open component */
1615 CHECK_ERR(result, AUGraphOpen(gAudio->graph));
1616 CHECK_ERR(result, AUGraphNodeInfo(gAudio->graph, ep[-1].converterNode, NULL, &ep[-1].converterUnit));
1617 /* Set input format */
1618 CHECK_ERR(result, AudioUnitSetProperty(ep[-1].converterUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &lastmp->format, sizeof(AudioStreamBasicDescription)));
1619 /* Reconnect effect->converter->next */
1620 CHECK_ERR(result, AUGraphConnectNodeInput(gAudio->graph, ep[-1].node, 0, ep[-1].converterNode, 0));
1621 }
1622 lastNode = ep[-1].converterNode;
1623 /* Set output format of the converter unit */
1624 CHECK_ERR(result, AudioUnitSetProperty(ep[-1].converterUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, fmtp, sizeof(AudioStreamBasicDescription)));
1625 }
1626 }
1627 /* Connect last node */
1628 CHECK_ERR(result, AUGraphConnectNodeInput(gAudio->graph, lastNode, 0, nextNode, nextNodeBus));
1629
1630 exit:
1631 if (effectIndex < cp->neffects - 1) {
1632 memmove(ep, ep + 1, sizeof(MDAudioEffect) * (cp->neffects - effectIndex - 1));
1633 }
1634 cp->neffects--;
1635 sts = (result == noErr ? kMDNoError : kMDErrorCannotSetupAudio);
1636 result = AUGraphStart(gAudio->graph);
1637 return (result == noErr && sts == kMDNoError ? 0 : kMDErrorCannotSetupAudio);
1638 }
1639
1640 #if 0
1641 #pragma mark ====== Initialize Audio ======
1642 #endif
1643
1644 MDStatus
1645 MDAudioInitialize(void)
1646 {
1647 AudioComponentDescription desc;
1648 AURenderCallbackStruct callback;
1649 OSStatus err;
1650 UInt32 unum;
1651 int i;
1652
1653 if (gAudio != NULL)
1654 return kMDNoError;
1655 gAudio = (MDAudio *)malloc(sizeof(MDAudio));
1656 if (gAudio == NULL)
1657 return kMDErrorOutOfMemory;
1658 memset(gAudio, 0, sizeof(MDAudio));
1659
1660 /* The preferred audio format */
1661 MDAudioFormatSetCanonical(&gAudio->preferredFormat, 44100.0f, 2, 0);
1662
1663 /* Initialize IOStreamInfo */
1664 for (i = 0; i < kMDAudioNumberOfStreams; i++) {
1665 MDAudioIOStreamInfo *ip = &(gAudio->ioStreamInfos[i]);
1666 ip->deviceIndex = -1;
1667 ip->busIndex = -1;
1668 ip->pan = 0.5;
1669 ip->volume = 1.0;
1670 }
1671
1672 /* Load audio device info */
1673 MDAudioUpdateDeviceInfo();
1674
1675 MyAppCallback_startupMessage("Creating AudioUnit Graph...");
1676
1677 /* Create AUGraph */
1678 CHECK_ERR(err, NewAUGraph(&gAudio->graph));
1679
1680 /* Mixer */
1681 desc.componentType = kAudioUnitType_Mixer;
1682 desc.componentSubType = kAudioUnitSubType_StereoMixer;
1683 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
1684 desc.componentFlags = desc.componentFlagsMask = 0;
1685 CHECK_ERR(err, AUGraphAddNode(gAudio->graph, &desc, &gAudio->mixer));
1686
1687 /* Output */
1688 desc.componentType = kAudioUnitType_Output;
1689 desc.componentSubType = kAudioUnitSubType_DefaultOutput;
1690 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
1691 CHECK_ERR(err, AUGraphAddNode(gAudio->graph, &desc, &gAudio->output));
1692
1693 /* Open graph and load components */
1694 CHECK_ERR(err, AUGraphOpen(gAudio->graph));
1695 CHECK_ERR(err, AUGraphNodeInfo(gAudio->graph, gAudio->mixer, &desc, &gAudio->mixerUnit));
1696 CHECK_ERR(err, AUGraphNodeInfo(gAudio->graph, gAudio->output, &desc, &gAudio->outputUnit));
1697
1698 /* Set the canonical format to the mixer unit */
1699 CHECK_ERR(err, AudioUnitSetProperty(gAudio->mixerUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Global, 0, &gAudio->preferredFormat, sizeof(AudioStreamBasicDescription)));
1700
1701 /* Set the AU callback function for the output unit */
1702 /* (Read output from the converter and pass to the output _and_ record to the file) */
1703 callback.inputProc = sMDAudioRecordProc;
1704 callback.inputProcRefCon = &(gAudio->ioStreamInfos[kMDAudioFirstIndexForOutputStream]);
1705 CHECK_ERR(err, AudioUnitSetProperty(gAudio->outputUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &callback, sizeof(AURenderCallbackStruct)));
1706
1707 /* Enable metering for the stereo mixer */
1708 unum = 1;
1709 CHECK_ERR(err, AudioUnitSetProperty(gAudio->mixerUnit, kAudioUnitProperty_MeteringMode, kAudioUnitScope_Global, 0, &unum, sizeof(UInt32)));
1710
1711 CHECK_ERR(err, AUGraphInitialize(gAudio->graph));
1712 CHECK_ERR(err, AUGraphStart(gAudio->graph));
1713
1714 {
1715 int deviceIndex;
1716 MDStatus sts;
1717 UInt64 code;
1718 /* Open 2 instances of DLS synthesizer */
1719 MyAppCallback_startupMessage("Initializing Internal Synthesizer...");
1720 code = ((UInt64)kAudioUnitSubType_DLSSynth << 32) + (UInt64)kAudioUnitManufacturer_Apple;
1721 MDAudioMusicDeviceInfoForCode(code, &deviceIndex);
1722 if (deviceIndex >= 0) {
1723 sts = MDAudioSelectIOStreamDevice(0, deviceIndex + kMDAudioMusicDeviceIndexOffset);
1724 if (sts == 0)
1725 sts = MDAudioSelectIOStreamDevice(1, deviceIndex + kMDAudioMusicDeviceIndexOffset);
1726 if (sts != 0)
1727 return sts;
1728 }
1729 }
1730
1731 /* Set built-in output as the audio output */
1732 CHECK_ERR(err, MDAudioSelectIOStreamDevice(kMDAudioFirstIndexForOutputStream, 0));
1733
1734 return 0;
1735
1736 exit:
1737 return kMDErrorCannotSetupAudio;
1738 }
1739
1740 MDStatus
1741 MDAudioDispose(void)
1742 {
1743 int idx;
1744 OSStatus err;
1745 for (idx = 0; idx < kMDAudioNumberOfStreams; idx++)
1746 MDAudioSelectIOStreamDevice(idx, -1);
1747 CHECK_ERR(err, AUGraphStop(gAudio->graph));
1748 CHECK_ERR(err, AUGraphClose(gAudio->graph));
1749 gAudio->graph = NULL;
1750 return kMDNoError;
1751 exit:
1752 return kMDErrorCannotSetupAudio;
1753 }
1754
1755 #pragma mark ====== Start/Stop Audio input/output ======
1756
1757 MDStatus
1758 MDAudioGetMixerBusAttributes(int idx, float *outPan, float *outVolume, float *outAmpLeft, float *outAmpRight, float *outPeakLeft, float *outPeakRight)
1759 {
1760 OSStatus err;
1761 Float32 f32;
1762 int scope, mixerIndex;
1763 if (idx >= 0 && idx < kMDAudioNumberOfInputStreams) {
1764 mixerIndex = idx;
1765 scope = kAudioUnitScope_Input;
1766 } else if (idx >= kMDAudioFirstIndexForOutputStream && idx < kMDAudioNumberOfStreams) {
1767 mixerIndex = idx - kMDAudioFirstIndexForOutputStream;
1768 scope = kAudioUnitScope_Output;
1769 } else return kMDErrorCannotSetupAudio;
1770 if (scope == kAudioUnitScope_Input) {
1771 CHECK_ERR(err, AudioUnitGetParameter(gAudio->mixerUnit, kStereoMixerParam_Pan, scope, mixerIndex, &f32));
1772 } else f32 = 0.5f;
1773 gAudio->ioStreamInfos[idx].pan = f32;
1774 if (outPan != NULL)
1775 *outPan = f32;
1776 CHECK_ERR(err, AudioUnitGetParameter(gAudio->mixerUnit, kStereoMixerParam_Volume, scope, mixerIndex, &f32));
1777 gAudio->ioStreamInfos[idx].volume = f32;
1778 if (outVolume != NULL)
1779 *outVolume = f32;
1780 CHECK_ERR(err, AudioUnitGetParameter(gAudio->mixerUnit, kStereoMixerParam_PostAveragePower, scope, mixerIndex, &f32));
1781 if (outAmpLeft != NULL)
1782 *outAmpLeft = f32;
1783 CHECK_ERR(err, AudioUnitGetParameter(gAudio->mixerUnit, kStereoMixerParam_PostAveragePower + 1, scope, mixerIndex, &f32));
1784 if (outAmpRight != NULL)
1785 *outAmpRight = f32;
1786 CHECK_ERR(err, AudioUnitGetParameter(gAudio->mixerUnit, kStereoMixerParam_PostPeakHoldLevel, scope, mixerIndex, &f32));
1787 if (outPeakLeft != NULL)
1788 *outPeakLeft = f32;
1789 CHECK_ERR(err, AudioUnitGetParameter(gAudio->mixerUnit, kStereoMixerParam_PostPeakHoldLevel + 1, scope, mixerIndex, &f32));
1790 if (outPeakRight != NULL)
1791 *outPeakRight = f32;
1792 return kMDNoError;
1793 exit:
1794 return kMDErrorCannotSetupAudio;
1795 }
1796
1797 MDStatus
1798 MDAudioSetMixerVolume(int idx, float volume)
1799 {
1800 OSStatus err;
1801 Float32 f32 = volume;
1802 int scope, mixerIndex;
1803 if (idx >= 0 && idx < kMDAudioNumberOfInputStreams) {
1804 mixerIndex = idx;
1805 scope = kAudioUnitScope_Input;
1806 } else if (idx >= kMDAudioFirstIndexForOutputStream && idx < kMDAudioNumberOfStreams) {
1807 mixerIndex = idx - kMDAudioFirstIndexForOutputStream;
1808 scope = kAudioUnitScope_Output;
1809 } else return kMDErrorCannotSetupAudio;
1810 CHECK_ERR(err, AudioUnitSetParameter(gAudio->mixerUnit, kStereoMixerParam_Volume, scope, mixerIndex, f32, 0));
1811 gAudio->ioStreamInfos[idx].volume = volume;
1812 return kMDNoError;
1813 exit:
1814 return kMDErrorCannotSetupAudio;
1815 }
1816
1817 MDStatus
1818 MDAudioSetMixerPan(int idx, float pan)
1819 {
1820 OSStatus err;
1821 Float32 f32 = pan;
1822 int scope, mixerIndex;
1823 if (idx >= 0 && idx < kMDAudioNumberOfInputStreams) {
1824 mixerIndex = idx;
1825 scope = kAudioUnitScope_Input;
1826 } else if (idx >= kMDAudioFirstIndexForOutputStream && idx < kMDAudioNumberOfStreams) {
1827 mixerIndex = idx - kMDAudioFirstIndexForOutputStream;
1828 scope = kAudioUnitScope_Output;
1829 } else return kMDErrorCannotSetupAudio;
1830 CHECK_ERR(err, AudioUnitSetParameter(gAudio->mixerUnit, kStereoMixerParam_Pan, scope, mixerIndex, f32, 0));
1831 gAudio->ioStreamInfos[idx].pan = pan;
1832 return kMDNoError;
1833 exit:
1834 return kMDErrorCannotSetupAudio;
1835 }
1836
1837 #if 0
1838 MDStatus
1839 MDAudioStartInput(void)
1840 {
1841 OSStatus err;
1842 if (!gAudio->isInputRunning) {
1843 // return kMDErrorCannotSetupAudio;
1844 CHECK_ERR(err, AudioOutputUnitStart(gAudio->inputUnit));
1845 gAudio->isInputRunning = 1;
1846 gAudio->firstInputTime = -1;
1847 }
1848
1849 return kMDNoError;
1850 exit:
1851 return kMDErrorCannotSetupAudio;
1852 }
1853
1854 MDStatus
1855 MDAudioStopInput(void)
1856 {
1857 OSStatus err;
1858 if (!gAudio->isInputRunning) {
1859 // return kMDErrorCannotSetupAudio;
1860 CHECK_ERR(err, AudioOutputUnitStop(gAudio->inputUnit));
1861 gAudio->isInputRunning = 0;
1862 gAudio->firstInputTime = -1;
1863 }
1864 return kMDNoError;
1865 exit:
1866 return kMDErrorCannotSetupAudio;
1867 }
1868
1869 MDStatus
1870 MDAudioEnablePlayThru(int flag)
1871 {
1872 MDStatus sts;
1873 if (flag && !gAudio->isAudioThruEnabled) {
1874 /* if (!gAudio->isInputRunning) {
1875 if ((sts = MDAudioStartInput()) != kMDNoError)
1876 return sts;
1877 } */
1878 gAudio->isAudioThruEnabled = 1;
1879 } else if (!flag && gAudio->isAudioThruEnabled) {
1880 /* if (!gAudio->isRecording) {
1881 if ((sts = MDAudioStopInput()) != kMDNoError)
1882 return sts;
1883 } */
1884 gAudio->isAudioThruEnabled = 0;
1885 }
1886 return kMDNoError;
1887 }
1888
1889 int
1890 MDAudioIsPlayThruEnabled(void)
1891 {
1892 return gAudio->isAudioThruEnabled;
1893 }
1894
1895 int
1896 MDAudioGetInputVolumeAndAmplitudes(float *outVolume, float *outAmpLeft, float *outAmpRight, float *outPeakLeft, float *outPeakRight)
1897 {
1898 OSStatus err;
1899 Float32 f32;
1900 if (gAudio != NULL && gAudio->mixerUnit != NULL) {
1901 CHECK_ERR(err, AudioUnitGetParameter(gAudio->mixerUnit, kStereoMixerParam_Volume, kAudioUnitScope_Input, 2, &f32));
1902 if (outVolume != NULL)
1903 *outVolume = f32;
1904 CHECK_ERR(err, AudioUnitGetParameter(gAudio->mixerUnit, kStereoMixerParam_PostAveragePower, kAudioUnitScope_Input, 2, &f32));
1905 if (outAmpLeft != NULL)
1906 *outAmpLeft = f32;
1907 CHECK_ERR(err, AudioUnitGetParameter(gAudio->mixerUnit, kStereoMixerParam_PostAveragePower + 1, kAudioUnitScope_Input, 2, &f32));
1908 if (outAmpRight != NULL)
1909 *outAmpRight = f32;
1910 CHECK_ERR(err, AudioUnitGetParameter(gAudio->mixerUnit, kStereoMixerParam_PostPeakHoldLevel, kAudioUnitScope_Input, 2, &f32));
1911 if (outPeakLeft != NULL)
1912 *outPeakLeft = f32;
1913 CHECK_ERR(err, AudioUnitGetParameter(gAudio->mixerUnit, kStereoMixerParam_PostPeakHoldLevel + 1, kAudioUnitScope_Input, 2, &f32));
1914 if (outPeakRight != NULL)
1915 *outPeakRight = f32;
1916 }
1917 return kMDNoError;
1918 exit:
1919 return kMDErrorCannotSetupAudio;
1920 }
1921
1922 int
1923 MDAudioSetInputVolume(float volume)
1924 {
1925 OSStatus err;
1926 Float32 f32 = volume;
1927 if (gAudio != NULL && gAudio->mixerUnit != NULL) {
1928 CHECK_ERR(err, AudioUnitSetParameter(gAudio->mixerUnit, kStereoMixerParam_Volume, kAudioUnitScope_Input, 2, f32, 0));
1929 }
1930 return kMDNoError;
1931 exit:
1932 return kMDErrorCannotSetupAudio;
1933 }
1934 #endif
1935
1936 #pragma mark ====== Audio Recording ======
1937
1938 MDStatus
1939 MDAudioPrepareRecording(const char *filename, const MDAudioFormat *format, int audioFileType, UInt64 recordingDuration)
1940 {
1941 OSStatus err;
1942 /* FSRef parentDir; */
1943 /* CFStringRef filenameStr; */
1944 CFURLRef urlRef;
1945 /* const char *p; */
1946
1947 if (gAudio->isRecording)
1948 return kMDErrorCannotSetupAudio;
1949
1950 /* Prepare CFURLRef from the filename (assuming it is a full path) */
1951 urlRef = CFURLCreateFromFileSystemRepresentation(NULL, (const unsigned char *)filename, strlen(filename), 0);
1952
1953 /* Prepare FSRef/CFStringRef representation of the filename */
1954 /* if ((p = strrchr(filename, '/')) == NULL) {
1955 char buf[MAXPATHLEN];
1956 getcwd(buf, sizeof buf);
1957 err = FSPathMakeRef((unsigned char *)buf, &parentDir, NULL);
1958 if (err != noErr)
1959 return kMDErrorCannotSetupAudio;
1960 filenameStr = CFStringCreateWithCString(NULL, p, kCFStringEncodingUTF8);
1961 } else {
1962 char *pp;
1963 pp = malloc(p - filename + 1);
1964 strncpy(pp, filename, p - filename);
1965 pp[p - filename] = 0;
1966 err = FSPathMakeRef((unsigned char *)pp, &parentDir, NULL);
1967 free(pp);
1968 if (err != noErr)
1969 return kMDErrorCannotSetupAudio;
1970 filenameStr = CFStringCreateWithCString(NULL, p + 1, kCFStringEncodingUTF8);
1971 } */
1972
1973
1974 /* Create a new audio file */
1975
1976 err = ExtAudioFileCreateWithURL(urlRef, audioFileType, format, NULL, kAudioFileFlags_EraseFile, &(gAudio->audioFile));
1977 // err = ExtAudioFileCreateNew(&parentDir, filenameStr, audioFileType, format, NULL, &(gAudio->audioFile));
1978 if (err != noErr)
1979 return kMDErrorCannotSetupAudio;
1980 CFRelease(urlRef);
1981
1982 /* Set the client data format */
1983 err = ExtAudioFileSetProperty(gAudio->audioFile, kExtAudioFileProperty_ClientDataFormat, sizeof(AudioStreamBasicDescription), &gAudio->preferredFormat);
1984 if (err != noErr)
1985 return kMDErrorCannotSetupAudio;
1986
1987 /* Initialize AudioFile IO */
1988 err = ExtAudioFileWriteAsync(gAudio->audioFile, 0, NULL);
1989 if (err != noErr)
1990 return kMDErrorCannotSetupAudio;
1991
1992
1993
1994 /* // If we're recording from a mono source, setup a simple channel map to split to stereo
1995 if (fDeviceFormat.mChannelsPerFrame == 1 && fOutputFormat.mChannelsPerFrame == 2)
1996 {
1997 // Get the underlying AudioConverterRef
1998 UInt32 size = sizeof(AudioConverterRef);
1999 err = ExtAudioFileGetProperty(fOutputAudioFile, kExtAudioFileProperty_AudioConverter, &size, &conv);
2000 if (conv)
2001 {
2002 // This should be as large as the number of output channels,
2003 // each element specifies which input channel's data is routed to that output channel
2004 SInt32 channelMap[] = { 0, 0 };
2005 err = AudioConverterSetProperty(conv, kAudioConverterChannelMap, 2*sizeof(SInt32), channelMap);
2006 }
2007 }
2008 */
2009
2010 gAudio->recordingDuration = recordingDuration;
2011
2012 return kMDNoError;
2013 }
2014
2015 MDStatus
2016 MDAudioStartRecording(void)
2017 {
2018 /* MDStatus sts; */
2019 if (gAudio->isRecording)
2020 return kMDErrorCannotSetupAudio;
2021 /* if (sts != kMDNoError)
2022 return sts; */
2023 gAudio->recordingStartTime = 0; /* Set at the first call to the recording callback */
2024 gAudio->isRecording = 1;
2025 return kMDNoError;
2026 }
2027
2028 MDStatus
2029 MDAudioStopRecording(void)
2030 {
2031 OSStatus err;
2032 if (!gAudio->isRecording)
2033 return kMDErrorCannotProcessAudio;
2034 gAudio->isRecording = 0;
2035 CHECK_ERR(err, ExtAudioFileDispose(gAudio->audioFile));
2036 gAudio->audioFile = NULL;
2037 /* if (!gAudio->isAudioThruEnabled) {
2038 MDStatus sts = MDAudioStopInput();
2039 if (sts != kMDNoError)
2040 return sts;
2041 } */
2042 return kMDNoError;
2043 exit:
2044 return kMDErrorCannotProcessAudio;
2045 }
2046
2047 /*
2048 MDStatus
2049 MDAudioStop(void)
2050 {
2051 MDStatus status = kMDNoError;
2052 if (inAudio != NULL) {
2053 if (inAudio->isRecording)
2054 status = MDAudioStopRecording(inAudio);
2055 }
2056 return status;
2057 }
2058 */
2059
2060 int
2061 MDAudioIsRecording(void)
2062 {
2063 if (gAudio->isRecording)
2064 return 1;
2065 else return 0;
2066 }
2067
2068 #pragma mark ====== MDAudioFormat accessors ======
2069
2070 void
2071 MDAudioFormatSetCanonical(MDAudioFormat *fmt, float sampleRate, int nChannels, int interleaved)
2072 {
2073 if (sampleRate != 0.0)
2074 fmt->mSampleRate = sampleRate;
2075 fmt->mFormatID = kAudioFormatLinearPCM;
2076 fmt->mFormatFlags = kAudioFormatFlagsNativeFloatPacked;
2077 fmt->mBitsPerChannel = 32;
2078 fmt->mChannelsPerFrame = nChannels;
2079 fmt->mFramesPerPacket = 1;
2080 if (interleaved)
2081 fmt->mBytesPerPacket = fmt->mBytesPerFrame = nChannels * sizeof(Float32);
2082 else {
2083 fmt->mBytesPerPacket = fmt->mBytesPerFrame = sizeof(Float32);
2084 fmt->mFormatFlags |= kAudioFormatFlagIsNonInterleaved;
2085 }
2086 }
2087
2088 /* Not used yet
2089 MDAudioFormatSetSampleRate(MDAudioFormat *fmt, float sampleRate)
2090 { fmt->mSampleRate = sampleRate; }
2091
2092 float
2093 MDAudioFormatGetSampleRate(MDAudioFormat *fmt)
2094 { return fmt->mSampleRate; }
2095
2096 void
2097 MDAudioFormatSetFormatID(MDAudioFormat *fmt, UInt32 formatID)
2098 { fmt->mFormatID = formatID; }
2099
2100 UInt32
2101 MDAudioFormatGetFormatID(MDAudioFormat *fmt)
2102 { return fmt->formatID; }
2103 */
2104

Back to OSDN">Back to OSDN
ViewVC Help
Powered by ViewVC 1.1.26