Julius 4.2
|
00001 00036 /* 00037 * adin_mic_darwin_coreaudio.c 00038 * 00039 * adin microphone library for CoreAudio API 00040 * 00041 * by Masatomo Hashimoto <m.hashimoto@aist.go.jp> 00042 * 00043 * Tested on Mac OS X v10.3.9 and v10.4.1 00044 * 00045 */ 00046 00047 /* $Id: adin_mic_darwin_coreaudio.c,v 1.6 2009/12/08 01:49:21 sumomo Exp $ */ 00048 00049 #include <CoreAudio/CoreAudio.h> 00050 #include <AudioUnit/AudioUnit.h> 00051 #include <AudioUnit/AudioOutputUnit.h> 00052 #include <AudioToolbox/AudioConverter.h> 00053 #include <CoreServices/CoreServices.h> 00054 #include <pthread.h> 00055 #include <stdio.h> 00056 00057 #define DEVICE_NAME_LEN 128 00058 #define BUF_SAMPLES 4096 00059 00060 static UInt32 ConvQuality = kAudioConverterQuality_Medium; 00061 00062 typedef SInt16 Sample; 00063 static UInt32 BytesPerSample = sizeof(Sample); 00064 00065 #define BITS_PER_BYTE 8 00066 00067 static AudioDeviceID InputDeviceID; 00068 static AudioUnit InputUnit; 00069 static AudioConverterRef Converter; 00070 00071 static pthread_mutex_t MutexInput; 00072 static pthread_cond_t CondInput; 00073 00074 static bool CoreAudioRecordStarted = FALSE; 00075 static bool CoreAudioHasInputDevice = FALSE; 00076 static bool CoreAudioInit = FALSE; 00077 00078 static UInt32 NumSamplesAvailable = 0; 00079 00080 static UInt32 InputDeviceBufferSamples = 0; 00081 static UInt32 InputBytesPerPacket = 0; 00082 static UInt32 InputFramesPerPacket = 0; 00083 static UInt32 InputSamplesPerPacket = 0; 00084 static UInt32 OutputBitsPerChannel = 0; 00085 static UInt32 OutputBytesPerPacket = 0; 00086 static UInt32 OutputSamplesPerPacket = 0; 00087 00088 static AudioBufferList* BufList; 00089 static AudioBufferList BufListBackup; 00090 static AudioBufferList* BufListConverted; 00091 00092 static char deviceName[DEVICE_NAME_LEN]; 00093 00094 #ifndef boolean 00095 typedef unsigned char boolean; 00096 #endif 00097 00098 static void printStreamInfo(AudioStreamBasicDescription* desc) { 00099 jlog("Stat: adin_darwin: ----- details of stream -----\n"); 00100 jlog("Stat: adin_darwin: sample rate: %f\n", desc->mSampleRate); 00101 jlog("Stat: adin_darwin: format flags: %s%s%s%s%s%s%s\n", 00102 desc->mFormatFlags & kAudioFormatFlagIsFloat ? 00103 "[float]" : "", 00104 desc->mFormatFlags & kAudioFormatFlagIsBigEndian ? 00105 "[big endian]" : "", 00106 desc->mFormatFlags & kAudioFormatFlagIsSignedInteger ? 00107 "[signed integer]" : "", 00108 desc->mFormatFlags & kAudioFormatFlagIsPacked ? 00109 "[packed]" : "", 00110 desc->mFormatFlags & kAudioFormatFlagIsAlignedHigh ? 00111 "[aligned high]" : "", 00112 desc->mFormatFlags & kAudioFormatFlagIsNonInterleaved ? 00113 "[non interleaved]" : "", 00114 desc->mFormatFlags & kAudioFormatFlagsAreAllClear ? 00115 "[all clear]" : "" 00116 ); 00117 jlog("Stat: adin_darwin: bytes per packet: %d\n", desc->mBytesPerPacket); 00118 jlog("Stat: adin_darwin: frames per packet: %d\n", desc->mFramesPerPacket); 00119 jlog("Stat: adin_darwin: bytes per frame: %d\n", desc->mBytesPerFrame); 00120 jlog("Stat: adin_darwin: channels per frame: %d\n", desc->mChannelsPerFrame); 00121 jlog("Stat: adin_darwin: bits per channel: %d\n", desc->mBitsPerChannel); 00122 jlog("Stat: adin_darwin: -----------------------------------\n"); 00123 } 00124 00125 static void printAudioBuffer(AudioBuffer* buf) { 00126 int sz = buf->mDataByteSize / BytesPerSample; 00127 int i; 00128 Sample* p = (Sample*)(buf->mData); 00129 for (i = 0; i < sz; i++) { 00130 printf("%d ", p[i]); 00131 } 00132 } 00133 00134 static AudioBufferList* 00135 allocateAudioBufferList(UInt32 data_bytes, UInt32 nsamples, UInt32 nchan) { 00136 00137 AudioBufferList* bufl; 00138 00139 #ifdef DEBUG 00140 jlog("Stat: adin_darwin: allocateAudioBufferList: data_bytes:%d nsamples:%d nchan:%d\n", 00141 data_bytes, nsamples, nchan); 00142 #endif 00143 00144 bufl = (AudioBufferList*)(malloc(sizeof(AudioBufferList))); 00145 00146 if(bufl == NULL) { 00147 jlog("Erorr: adin_darwin: allocateAudioBufferList: failed\n"); 00148 return NULL; 00149 } 00150 00151 bufl->mNumberBuffers = nchan; 00152 00153 int i; 00154 for (i = 0; i < nchan; i++) { 00155 bufl->mBuffers[i].mNumberChannels = nchan; 00156 bufl->mBuffers[i].mDataByteSize = data_bytes * nsamples; 00157 bufl->mBuffers[i].mData = malloc(data_bytes * nsamples); 00158 00159 if(bufl->mBuffers[i].mData == NULL) { 00160 jlog("Erorr: adin_darwin: allocateAudioBufferList: malloc for mBuffers[%d] failed\n", i); 00161 return NULL; 00162 } 00163 } 00164 return bufl; 00165 } 00166 00167 /* gives input data for Converter */ 00168 static OSStatus 00169 ConvInputProc(AudioConverterRef inConv, 00170 UInt32* ioNumDataPackets, 00171 AudioBufferList* ioData, // to be filled 00172 AudioStreamPacketDescription** outDataPacketDesc, 00173 void* inUserData) 00174 { 00175 int i; 00176 UInt32 nPacketsRequired = *ioNumDataPackets; 00177 UInt32 nBytesProvided = 0; 00178 UInt32 nBytesRequired; 00179 UInt32 n; 00180 00181 pthread_mutex_lock(&MutexInput); 00182 00183 #ifdef DEBUG 00184 jlog("Stat: adin_darwin: ConvInputProc: required %d packets\n", nPacketsRequired); 00185 #endif 00186 00187 while(NumSamplesAvailable == 0){ 00188 pthread_cond_wait(&CondInput, &MutexInput); 00189 } 00190 00191 for(i = 0; i < BufList->mNumberBuffers; i++) { 00192 n = BufList->mBuffers[i].mDataByteSize; 00193 if (nBytesProvided != 0 && nBytesProvided != n) { 00194 jlog("Warning: adin_darwin: buffer size mismatch\n"); 00195 } 00196 nBytesProvided = n; 00197 } 00198 00199 #ifdef DEBUG 00200 jlog("Stat: adin_darwin: ConvInputProc: %d bytes in buffer\n", nBytesProvided); 00201 #endif 00202 00203 for(i = 0; i < BufList->mNumberBuffers; i++) { 00204 ioData->mBuffers[i].mNumberChannels = 00205 BufList->mBuffers[i].mNumberChannels; 00206 00207 nBytesRequired = nPacketsRequired * InputBytesPerPacket; 00208 00209 if(nBytesRequired < nBytesProvided) { 00210 ioData->mBuffers[i].mData = BufList->mBuffers[i].mData; 00211 ioData->mBuffers[i].mDataByteSize = nBytesRequired; 00212 BufList->mBuffers[i].mData += nBytesRequired; 00213 BufList->mBuffers[i].mDataByteSize = nBytesProvided - nBytesRequired; 00214 } else { 00215 ioData->mBuffers[i].mData = BufList->mBuffers[i].mData; 00216 ioData->mBuffers[i].mDataByteSize = nBytesProvided; 00217 00218 BufList->mBuffers[i].mData = BufListBackup.mBuffers[i].mData; 00219 00220 } 00221 00222 } 00223 00224 *ioNumDataPackets = ioData->mBuffers[0].mDataByteSize / InputBytesPerPacket; 00225 00226 #ifdef DEBUG 00227 jlog("Stat: adin_darwin: ConvInputProc: provided %d packets\n", *ioNumDataPackets); 00228 #endif 00229 00230 NumSamplesAvailable = 00231 nBytesProvided / BytesPerSample - *ioNumDataPackets * InputSamplesPerPacket; 00232 00233 #ifdef DEBUG 00234 jlog("Stat: adin_darwin: ConvInputProc: %d samples available\n", NumSamplesAvailable); 00235 #endif 00236 00237 pthread_mutex_unlock(&MutexInput); 00238 00239 return noErr; 00240 } 00241 00242 00243 /* called when input data are available (an AURenderCallback) */ 00244 static OSStatus 00245 InputProc(void* inRefCon, 00246 AudioUnitRenderActionFlags* ioActionFlags, 00247 const AudioTimeStamp* inTimeStamp, 00248 UInt32 inBusNumber, 00249 UInt32 inNumberFrames, 00250 AudioBufferList* ioData // null 00251 ) 00252 { 00253 OSStatus status = noErr; 00254 int i; 00255 00256 pthread_mutex_lock(&MutexInput); 00257 00258 if (NumSamplesAvailable == 0) { 00259 00260 status = AudioUnitRender(InputUnit, 00261 ioActionFlags, 00262 inTimeStamp, 00263 inBusNumber, 00264 inNumberFrames, 00265 BufList); 00266 NumSamplesAvailable = 00267 BufList->mBuffers[0].mDataByteSize / InputBytesPerPacket; 00268 00269 #ifdef DEBUG 00270 printAudioBuffer(BufList->mBuffers); 00271 #endif 00272 } 00273 00274 pthread_mutex_unlock(&MutexInput); 00275 00276 pthread_cond_signal(&CondInput); 00277 00278 /* 00279 jlog("Stat: adin_darwin: InputProc: %d bytes filled (BufList)\n", 00280 BufList->mBuffers[0].mDataByteSize); 00281 */ 00282 00283 return status; 00284 } 00285 00286 00287 /* initialize default sound device */ 00288 boolean adin_mic_standby(int sfreq, void* dummy) { 00289 OSStatus status; 00290 UInt32 propertySize; 00291 struct AudioStreamBasicDescription inDesc; 00292 int err; 00293 00294 jlog("Stat: adin_darwin: sample rate = %d\n", sfreq); 00295 00296 if (CoreAudioInit) 00297 return TRUE; 00298 00299 #ifdef MAC_OS_X_VERSION_10_6 00300 AudioComponent halout; 00301 AudioComponentDescription haloutDesc; 00302 #else 00303 Component halout; 00304 ComponentDescription haloutDesc; 00305 #endif 00306 00307 haloutDesc.componentType = kAudioUnitType_Output; 00308 haloutDesc.componentSubType = kAudioUnitSubType_HALOutput; 00309 haloutDesc.componentManufacturer = kAudioUnitManufacturer_Apple; 00310 haloutDesc.componentFlags = 0; 00311 haloutDesc.componentFlagsMask = 0; 00312 halout = FindNextComponent(NULL, &haloutDesc); 00313 00314 if(halout == NULL) { 00315 jlog("Error: adin_darwin: no HALOutput component found\n"); 00316 return FALSE; 00317 } 00318 00319 OpenAComponent(halout, &InputUnit); 00320 00321 UInt32 enableIO; 00322 00323 enableIO = 1; 00324 status = AudioUnitSetProperty(InputUnit, 00325 kAudioOutputUnitProperty_EnableIO, 00326 kAudioUnitScope_Input, 00327 1, 00328 &enableIO, 00329 sizeof(enableIO)); 00330 if (status != noErr) { 00331 jlog("Error: adin_darwin: cannot set InputUnit's EnableIO(Input)\n"); 00332 return FALSE; 00333 } 00334 00335 enableIO = 0; 00336 status = AudioUnitSetProperty(InputUnit, 00337 kAudioOutputUnitProperty_EnableIO, 00338 kAudioUnitScope_Output, 00339 0, 00340 &enableIO, 00341 sizeof(enableIO)); 00342 if (status != noErr) { 00343 jlog("Error: adin_darwin: cannot set InputUnit's EnableIO(Output)\n"); 00344 return FALSE; 00345 } 00346 00347 00348 /* get default input device */ 00349 propertySize = sizeof(InputDeviceID); 00350 status = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, 00351 &propertySize, 00352 &InputDeviceID); 00353 if (status != noErr) { 00354 jlog("Error: adin_darwin: cannot get default input device\n"); 00355 return FALSE; 00356 } 00357 00358 if (InputDeviceID == kAudioDeviceUnknown) { 00359 jlog("Error: adin_darwin: no available input device found\n"); 00360 return FALSE; 00361 00362 } else { 00363 00364 CoreAudioHasInputDevice = TRUE; 00365 00366 /* get input device name */ 00367 propertySize = sizeof(char) * DEVICE_NAME_LEN; 00368 status = AudioDeviceGetProperty(InputDeviceID, 00369 1, 00370 1, 00371 kAudioDevicePropertyDeviceName, 00372 &propertySize, 00373 deviceName); 00374 if (status != noErr) { 00375 jlog("Error: adin_darwin: cannot get device name\n"); 00376 return FALSE; 00377 } 00378 00379 status = AudioUnitSetProperty(InputUnit, 00380 kAudioOutputUnitProperty_CurrentDevice, 00381 kAudioUnitScope_Global, 00382 0, 00383 &InputDeviceID, 00384 sizeof(InputDeviceID)); 00385 00386 if (status != noErr) { 00387 jlog("Error: adin_darwin: cannot bind default input device to AudioUnit\n"); 00388 return FALSE; 00389 } 00390 00391 /* get input device's format */ 00392 propertySize = sizeof(inDesc); 00393 status = AudioDeviceGetProperty(InputDeviceID, 00394 1, 00395 1, 00396 kAudioDevicePropertyStreamFormat, 00397 &propertySize, 00398 &inDesc); 00399 if (status != noErr) { 00400 jlog("Error: adin_darwin: cannot get input device's stream format\n"); 00401 return FALSE; 00402 } 00403 00404 /* get input device's buffer frame size */ 00405 UInt32 bufferFrameSize; 00406 propertySize = sizeof(bufferFrameSize); 00407 status = AudioDeviceGetProperty(InputDeviceID, 00408 1, 00409 1, 00410 kAudioDevicePropertyBufferFrameSize, 00411 &propertySize, 00412 &bufferFrameSize); 00413 if (status != noErr) { 00414 jlog("Error: adin_darwin: cannot get input device's buffer frame size\n"); 00415 return FALSE; 00416 } 00417 00418 jlog("Stat: adin_darwin: using device \"%s\" for input\n", deviceName); 00419 jlog("Stat: adin_darwin: sample rate %f\n\t%ld channels\n\t%ld-bit sample\n", 00420 inDesc.mSampleRate, 00421 inDesc.mChannelsPerFrame, 00422 inDesc.mBitsPerChannel); 00423 00424 jlog("Stat: adin_darwin: %d buffer frames\n", bufferFrameSize); 00425 00426 00427 printStreamInfo(&inDesc); 00428 00429 UInt32 formatFlagEndian = 00430 inDesc.mFormatFlags & kAudioFormatFlagIsBigEndian; 00431 00432 inDesc.mFormatFlags = 00433 kAudioFormatFlagIsSignedInteger | 00434 kAudioFormatFlagIsPacked | 00435 formatFlagEndian; 00436 00437 inDesc.mBytesPerPacket = BytesPerSample; 00438 inDesc.mFramesPerPacket = 1; 00439 inDesc.mBytesPerFrame = BytesPerSample; 00440 inDesc.mChannelsPerFrame = 1; 00441 inDesc.mBitsPerChannel = BytesPerSample * BITS_PER_BYTE; 00442 00443 printStreamInfo(&inDesc); 00444 00445 propertySize = sizeof(inDesc); 00446 status = AudioUnitSetProperty(InputUnit, 00447 kAudioUnitProperty_StreamFormat, 00448 kAudioUnitScope_Output, 00449 1, 00450 &inDesc, 00451 propertySize 00452 ); 00453 if (status != noErr) { 00454 jlog("Error: adin_darwin: cannot set InputUnit's stream format\n"); 00455 return FALSE; 00456 } 00457 00458 InputBytesPerPacket = inDesc.mBytesPerPacket; 00459 InputFramesPerPacket = inDesc.mFramesPerPacket; 00460 InputSamplesPerPacket = InputBytesPerPacket / BytesPerSample; 00461 00462 InputDeviceBufferSamples = 00463 bufferFrameSize * InputSamplesPerPacket * InputFramesPerPacket; 00464 00465 jlog("Stat: adin_darwin: input device's buffer size (# of samples): %d\n", 00466 InputDeviceBufferSamples); 00467 00468 AudioStreamBasicDescription outDesc; 00469 outDesc.mSampleRate = sfreq; 00470 outDesc.mFormatID = kAudioFormatLinearPCM; 00471 outDesc.mFormatFlags = 00472 kAudioFormatFlagIsSignedInteger | 00473 kAudioFormatFlagIsPacked | 00474 formatFlagEndian; 00475 outDesc.mBytesPerPacket = BytesPerSample; 00476 outDesc.mFramesPerPacket = 1; 00477 outDesc.mBytesPerFrame = BytesPerSample; 00478 outDesc.mChannelsPerFrame = 1; 00479 outDesc.mBitsPerChannel = BytesPerSample * BITS_PER_BYTE; 00480 00481 printStreamInfo(&outDesc); 00482 00483 OutputBitsPerChannel = outDesc.mBitsPerChannel; 00484 OutputBytesPerPacket = outDesc.mBytesPerPacket; 00485 00486 OutputSamplesPerPacket = (OutputBitsPerChannel / BITS_PER_BYTE) / OutputBytesPerPacket; 00487 00488 status = AudioConverterNew(&inDesc, &outDesc, &Converter); 00489 if (status != noErr){ 00490 jlog("Error: adin_darwin: cannot create audio converter\n"); 00491 return FALSE; 00492 } 00493 00494 /* 00495 UInt32 nChan = inDesc.mChannelsPerFrame; 00496 int i; 00497 00498 if (inDesc.mFormatFlags & kAudioFormatFlagIsNonInterleaved && nChan > 1) { 00499 UInt32 chmap[nChan]; 00500 for (i = 0; i < nChan; i++) 00501 chmap[i] = 0; 00502 00503 status = AudioConverterSetProperty(Converter, 00504 kAudioConverterChannelMap, 00505 sizeof(chmap), chmap); 00506 if (status != noErr){ 00507 jlog("cannot set audio converter's channel map\n"); 00508 return FALSE; 00509 } 00510 } 00511 */ 00512 00513 status = 00514 AudioConverterSetProperty(Converter, 00515 kAudioConverterSampleRateConverterQuality, 00516 sizeof(ConvQuality), &ConvQuality); 00517 if (status != noErr){ 00518 jlog("Error: adin_darwin: cannot set audio converter quality\n"); 00519 return FALSE; 00520 } 00521 00522 00523 //jlog("Stat: adin_darwin: audio converter generated\n"); 00524 00525 /* allocate buffers */ 00526 BufList = allocateAudioBufferList(inDesc.mBitsPerChannel / BITS_PER_BYTE, 00527 InputDeviceBufferSamples, 1); 00528 if (BufList == NULL) return FALSE; 00529 00530 BufListBackup.mNumberBuffers = BufList->mNumberBuffers; 00531 00532 BufListBackup.mBuffers[0].mNumberChannels = 1; 00533 BufListBackup.mBuffers[0].mDataByteSize = 00534 BufList->mBuffers[0].mDataByteSize; 00535 BufListBackup.mBuffers[0].mData = BufList->mBuffers[0].mData; 00536 00537 BufListConverted = allocateAudioBufferList(BytesPerSample, BUF_SAMPLES, 1); 00538 if (BufListConverted == NULL) return FALSE; 00539 //jlog("Stat: adin_darwin: buffers allocated\n"); 00540 00541 err = pthread_mutex_init(&MutexInput, NULL); 00542 if (err) { 00543 jlog("Error: adin_darwin: cannot init mutex\n"); 00544 return FALSE; 00545 } 00546 err = pthread_cond_init(&CondInput, NULL); 00547 if (err) { 00548 jlog("Error: adin_darwin: cannot init condition variable\n"); 00549 return FALSE; 00550 } 00551 00552 /* register the callback */ 00553 AURenderCallbackStruct input; 00554 input.inputProc = InputProc; // an AURenderCallback 00555 input.inputProcRefCon = 0; 00556 AudioUnitSetProperty(InputUnit, 00557 kAudioOutputUnitProperty_SetInputCallback, 00558 kAudioUnitScope_Global, 00559 0, 00560 &input, 00561 sizeof(input)); 00562 00563 status = AudioUnitInitialize(InputUnit); 00564 if (status != noErr){ 00565 jlog("Error: adin_darwin: InputUnit initialize failed\n"); 00566 return FALSE; 00567 } 00568 00569 } 00570 00571 CoreAudioInit = TRUE; 00572 00573 jlog("Stat: adin_darwin: CoreAudio: initialized\n"); 00574 00575 return TRUE; 00576 } 00577 00578 boolean adin_mic_begin(char *pathname){ return TRUE; } 00579 boolean adin_mic_end(){ return TRUE; } 00580 00581 int adin_mic_read(void *buffer, int nsamples) { 00582 OSStatus status; 00583 00584 #ifdef DEBUG 00585 jlog("Stat: adin_darwin: read: %d samples required\n", nsamples); 00586 #endif 00587 00588 if (!CoreAudioHasInputDevice) 00589 return -1; 00590 00591 if (!CoreAudioRecordStarted) { 00592 status = AudioOutputUnitStart(InputUnit); 00593 CoreAudioRecordStarted = TRUE; 00594 } 00595 00596 UInt32 capacity = BUF_SAMPLES * OutputSamplesPerPacket; 00597 UInt32 npackets = nsamples * OutputSamplesPerPacket; 00598 00599 UInt32 numDataPacketsNeeded; 00600 00601 Sample* inputDataBuf = (Sample*)(BufListConverted->mBuffers[0].mData); 00602 00603 numDataPacketsNeeded = npackets < capacity ? npackets : capacity; 00604 00605 #ifdef DEBUG 00606 jlog("Stat: adin_darwin: numDataPacketsNeeded=%d\n", numDataPacketsNeeded); 00607 #endif 00608 00609 status = AudioConverterFillComplexBuffer(Converter, 00610 ConvInputProc, 00611 NULL, // user data 00612 &numDataPacketsNeeded, 00613 BufListConverted, 00614 NULL // packet description 00615 ); 00616 if (status != noErr) { 00617 jlog("Error: adin_darwin: AudioConverterFillComplexBuffer: failed\n"); 00618 return -1; 00619 } 00620 00621 #ifdef DEBUG 00622 jlog("Stat: adin_darwin: %d bytes filled (BufListConverted)\n", 00623 BufListConverted->mBuffers[0].mDataByteSize); 00624 #endif 00625 00626 int providedSamples = numDataPacketsNeeded / OutputSamplesPerPacket; 00627 00628 pthread_mutex_lock(&MutexInput); 00629 00630 #ifdef DEBUG 00631 jlog("Stat: adin_darwin: provided samples: %d\n", providedSamples); 00632 #endif 00633 00634 Sample* dst_data = (Sample*)buffer; 00635 00636 int i; 00637 00638 int count = 0; 00639 00640 for (i = 0; i < providedSamples; i++) { 00641 dst_data[i] = inputDataBuf[i]; 00642 if (dst_data[i] == 0) count++; 00643 } 00644 00645 //jlog("Stat: adin_darwin: %d zero samples\n", count); 00646 00647 00648 pthread_mutex_unlock(&MutexInput); 00649 00650 #ifdef DEBUG 00651 jlog("Stat: adindarwin: EXIT: %d samples provided\n", providedSamples); 00652 #endif 00653 00654 return providedSamples; 00655 } 00656 00657 void adin_mic_pause() { 00658 OSStatus status; 00659 00660 if (CoreAudioHasInputDevice && CoreAudioRecordStarted) { 00661 status = AudioOutputUnitStop(InputUnit); 00662 CoreAudioRecordStarted = FALSE; 00663 } 00664 return; 00665 } 00666 00674 char * 00675 adin_mic_input_name() 00676 { 00677 return(deviceName); 00678 }