#include "ratconf.h" #include "config_unix.h" #include "debug.h" #include "memory.h" #include "audio_types.h" #include "audio_fmt.h" #include "auddev_macosx.h" #include "util.h" #include #include #include #include #define BUFFERSIZEO 40000 int bpb = 640; enum { AUDIO_IN, AUDIO_OUT}; static audio_port_details_t iport = { AUDIO_IN, "audio_in"}; static audio_port_details_t oport = { AUDIO_OUT, "audio_out"}; struct Device { AudioUnit outputAU, inputAU; void *outputBuffer, *inputBuffer; UInt32 outputBufferSize, inputBufferSize; UInt32 outputBufferReadPosition, outputBufferWritePosition; UInt32 inputBufferReadPosition, inputBufferWritePosition; UInt32 availableInput; AudioBufferList *rawInputABL, *convertedInputABL, *convertedOutputABL; UInt32 rawInputABLSize, convertedInputABLSize, convertedOutputABLSize; AudioConverterRef inputConverter, outputConverter; AudioStreamBasicDescription inputConverterOutASBD, inputConverterInASBD; AudioStreamBasicDescription outputConverterOutASBD, outputConverterInASBD; }; typedef struct Device Device; Device *devices; /*static void printASBD(AudioStreamBasicDescription asbd) { union {UInt32 number; char letters[5];} format; memset(&format, 0, sizeof(format)); format.number = asbd.mFormatID; printf("Format: %s\n", format.letters); printf("Rate: %f\n", asbd.mSampleRate); printf("Flags: %u\n", asbd.mFormatFlags); printf("mBytesPerPacket: %u\n", asbd.mBytesPerPacket); printf("mFramesPerPacket: %u\n", asbd.mFramesPerPacket); printf("mBytesPerFrame: %u\n", asbd.mBytesPerFrame); printf("mChannelsPerFrame: %u\n", asbd.mChannelsPerFrame); printf("mBitsPerChannel: %u\n", asbd.mBitsPerChannel); printf("\n"); }*/ static AudioStreamBasicDescription makeASBD(const audio_format *format) { AudioStreamBasicDescription newASBD; memset(&newASBD, 0, sizeof(newASBD)); switch(format->encoding) { case DEV_PCMU: newASBD.mFormatID = kAudioFormatULaw; newASBD.mFormatFlags = kAudioFormatFlagsAreAllClear; break; case DEV_PCMA: newASBD.mFormatID = kAudioFormatALaw; newASBD.mFormatFlags = kAudioFormatFlagsAreAllClear; break; case DEV_S8: newASBD.mFormatID = kAudioFormatLinearPCM; newASBD.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked; break; case DEV_U8: newASBD.mFormatID = kAudioFormatLinearPCM; newASBD.mFormatFlags = kAudioFormatFlagIsPacked; break; case DEV_S16: newASBD.mFormatID = kAudioFormatLinearPCM; newASBD.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;// | kAudioFormatFlagIsBigEndian; #ifdef WORDS_BIGENDIAN newASBD.mFormatFlags |= kAudioFormatFlagIsBigEndian; #endif break; } newASBD.mSampleRate = format->sample_rate; newASBD.mChannelsPerFrame = format->channels; newASBD.mBitsPerChannel = format->bits_per_sample; newASBD.mBytesPerFrame = newASBD.mChannelsPerFrame * newASBD.mBitsPerChannel / 8; newASBD.mFramesPerPacket = 1; newASBD.mBytesPerPacket = newASBD.mBytesPerFrame; bpb = format->bytes_per_block; return newASBD; } static void resizeABLBuffers(AudioBufferList *abl, UInt32 *currentSize, UInt32 newSize) { if (*currentSize < newSize) { UInt32 i; if(*currentSize != 0) { for(i = 0; i < abl->mNumberBuffers; i++) { free(abl->mBuffers[i].mData); } } *currentSize = newSize; for(i = 0; i < abl->mNumberBuffers; i++) { //abl->mBuffers[i].mData = malloc(currentSize); abl->mBuffers[i].mData = calloc(1, *currentSize); abl->mBuffers[i].mDataByteSize = *currentSize; } } } static void makeABL(AudioBufferList **abl, const AudioStreamBasicDescription *asbd) { UInt32 buffers = 1; UInt32 channelsPerBuffer = 1; if(asbd->mFormatFlags & kAudioFormatFlagIsNonInterleaved) buffers = asbd->mChannelsPerFrame; else channelsPerBuffer = asbd->mChannelsPerFrame; UInt32 size = sizeof(AudioBufferList) + (buffers - 1) * sizeof(AudioBuffer); *abl = malloc(size); memset(*abl, 0, size); (*abl)->mNumberBuffers = buffers; int i; for(i = 0; i < buffers; i++) { AudioBuffer *buffer = &((*abl)->mBuffers[i]); buffer->mNumberChannels = channelsPerBuffer; buffer->mDataByteSize = 0; buffer->mData = NULL; } } static void freeABL(AudioBufferList *abl) { int i; for (i = 0; i < abl->mNumberBuffers; i++) { if (abl->mBuffers[i].mDataByteSize) { free(abl->mBuffers[i].mData); } } free(abl); } static OSStatus convertOutput( AudioConverterRef inAudioConverter, UInt32 *ioNumberDataPackets, AudioBufferList *ioData, AudioStreamPacketDescription **outDataPacketDescription, void *inUserData ) { UNUSED(inAudioConverter); UNUSED(outDataPacketDescription); Device *device = (Device*)inUserData; int i; UInt32 totalBytes = *ioNumberDataPackets * device->outputConverterInASBD.mBytesPerFrame; UInt32 write1 = min(totalBytes, device->outputBufferSize - device->outputBufferReadPosition); UInt32 write2 = totalBytes - write1; for(i = 0; i < ioData->mNumberBuffers; i++) { memcpy(device->convertedOutputABL->mBuffers[i].mData, device->outputBuffer + device->outputBufferReadPosition, write1); //memset(device->outputBuffer + device->outputBufferReadPosition, 0, write1); if(write2) { memcpy(device->convertedOutputABL->mBuffers[i].mData + write1, device->outputBuffer, write2); //memset(device->outputBuffer,0, write2); } ioData->mBuffers[i].mDataByteSize = totalBytes; ioData->mBuffers[i].mData = device->convertedOutputABL->mBuffers[i].mData; } if(write2) { device->outputBufferReadPosition = write2; } else { device->outputBufferReadPosition += write1; } return noErr; } static OSStatus renderOutput( void * inRefCon, AudioUnitRenderActionFlags * ioActionFlags, const AudioTimeStamp * inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList * ioData ) { UNUSED(ioActionFlags); UNUSED(inTimeStamp); UNUSED(inBusNumber); Device* device = (Device*)inRefCon; int i; for (i = 0; i < ioData->mNumberBuffers; i++) { memset(ioData->mBuffers[i].mData, 0, ioData->mBuffers[i].mDataByteSize); } resizeABLBuffers(device->convertedOutputABL, &device->convertedOutputABLSize, inNumberFrames * device->outputConverterOutASBD.mBytesPerPacket); OSStatus err = AudioConverterFillComplexBuffer(device->outputConverter, convertOutput, device, &inNumberFrames, ioData, NULL); return err; } static OSStatus convertInput( AudioConverterRef inAudioConverter, UInt32 *ioNumberDataPackets, AudioBufferList *ioData, AudioStreamPacketDescription **outDataPacketDescription, void *inUserData ) { UNUSED(inAudioConverter); UNUSED(outDataPacketDescription); Device *device = (Device*)inUserData; int i; for(i = 0; i < ioData->mNumberBuffers; i++) { ioData->mBuffers[i].mDataByteSize = *ioNumberDataPackets * device->inputConverterInASBD.mBytesPerPacket; ioData->mBuffers[i].mData = device->rawInputABL->mBuffers[i].mData; } return noErr; } static OSStatus renderInput( void * inRefCon, AudioUnitRenderActionFlags * ioActionFlags, const AudioTimeStamp * inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList * ioData ) { UNUSED(ioActionFlags); UNUSED(inTimeStamp); UNUSED(inBusNumber); UNUSED(ioData); static Float64 framesToConvert = 0; Device* device = (Device*)inRefCon; UInt32 inBytesPerPacket = device->inputConverterInASBD.mBytesPerPacket; UInt32 outBytesPerPacket = device->inputConverterOutASBD.mBytesPerPacket; resizeABLBuffers(device->rawInputABL, &device->rawInputABLSize, inNumberFrames * inBytesPerPacket); OSStatus err = AudioUnitRender(device->inputAU, ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, device->rawInputABL); if(err) { printf("output au render failed\n"); exit(1); } framesToConvert += inNumberFrames / device->inputConverterInASBD.mSampleRate * device->inputConverterOutASBD.mSampleRate; inNumberFrames = (UInt32)framesToConvert; framesToConvert -= inNumberFrames; resizeABLBuffers(device->convertedInputABL, &device->convertedInputABLSize, inNumberFrames * outBytesPerPacket); err = AudioConverterFillComplexBuffer(device->inputConverter, convertInput, device, &inNumberFrames, device->convertedInputABL, NULL); if (err) printf("output conversion failed\n"); UInt32 totalRead = inNumberFrames * outBytesPerPacket; device->availableInput += totalRead; UInt32 read1 = min(device->inputBufferSize - device->inputBufferWritePosition, totalRead); UInt32 read2 = totalRead - read1; memcpy(device->inputBuffer + device->inputBufferWritePosition, device->convertedInputABL->mBuffers[0].mData, read1); device->inputBufferWritePosition += read1; if(read2) { memcpy(device->inputBuffer, device->convertedInputABL->mBuffers[0].mData + read1, read2); device->inputBufferWritePosition = read2; } return err; } static OSStatus openDefaultOutput(AudioUnit *outputUnit) { ComponentDescription desc; Component component; desc.componentType = kAudioUnitType_Output; desc.componentSubType = kAudioUnitSubType_DefaultOutput; desc.componentManufacturer = kAudioUnitManufacturer_Apple; desc.componentFlags = 0; desc.componentFlagsMask = 0; component = FindNextComponent(NULL, &desc); if (component == NULL) exit (-1); return OpenAComponent(component, outputUnit); } static OSStatus openDefaultInput(AudioUnit *inputUnit) { ComponentDescription desc; Component component; desc.componentType = kAudioUnitType_Output; desc.componentSubType = kAudioUnitSubType_HALOutput; desc.componentManufacturer = kAudioUnitManufacturer_Apple; desc.componentFlags = 0; desc.componentFlagsMask = 0; component = FindNextComponent(NULL, &desc); if (component == NULL) exit (-1); return OpenAComponent(component, inputUnit); } int macosx_audio_init(void) { if(devices) free(devices); devices = calloc(macosx_audio_device_count(), sizeof(Device)); return (int)devices; } int macosx_audio_open(audio_desc_t ad, audio_format* ifmt, audio_format *ofmt) { OSStatus err; UInt32 size; Device *device = devices + ad; memset(device, 0, sizeof(*device)); AudioUnit outputAU; err = openDefaultOutput(&outputAU); if(err) return 0; device->outputConverterInASBD = makeASBD(ofmt); AudioStreamBasicDescription hardwareOutASBD; size = sizeof(hardwareOutASBD); err = AudioUnitGetProperty(outputAU, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &hardwareOutASBD, &size); if (err) return 0; size = sizeof(device->outputConverterOutASBD); err = AudioUnitGetProperty(outputAU, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &device->outputConverterOutASBD, &size); if (err) return 0; device->outputConverterOutASBD.mSampleRate = hardwareOutASBD.mSampleRate; size = sizeof(device->outputConverterOutASBD); err = AudioUnitSetProperty(outputAU, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &device->outputConverterOutASBD, size); if(err) return 0; device->outputAU = outputAU; device->outputBufferSize = BUFFERSIZEO; device->outputBuffer = malloc(device->outputBufferSize); memset(device->outputBuffer, 0, device->outputBufferSize); device->inputBufferSize = BUFFERSIZEO; device->inputBuffer = malloc(device->inputBufferSize); memset(device->inputBuffer, 0, device->inputBufferSize); device->availableInput = 0; makeABL(&device->convertedOutputABL, &device->outputConverterOutASBD); err = AudioConverterNew(&device->outputConverterInASBD, &device->outputConverterOutASBD, &device->outputConverter); if (err) return 0; AURenderCallbackStruct callback; callback.inputProc = renderOutput; callback.inputProcRefCon = device; AudioUnitSetProperty(outputAU, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &callback, sizeof(callback)); AudioUnitInitialize(outputAU); AudioOutputUnitStart(outputAU); AudioUnit inputAU; err = openDefaultInput(&inputAU); if(err) return 0; UInt32 io = 1; err = AudioUnitSetProperty(inputAU, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &io, sizeof(io)); if (err) return 0; io = 0; err = AudioUnitSetProperty(inputAU, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &io, sizeof(io)); if (err) return 0; AudioDeviceID inputID; size = sizeof(inputID); err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &size, &inputID); if (err) return 0; err = AudioUnitSetProperty(inputAU, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &inputID, sizeof(inputID)); if (err) return 0; size = sizeof(AudioStreamBasicDescription); err = AudioUnitGetProperty(inputAU, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &device->inputConverterInASBD, &size); if (err) return 0; device->inputConverterOutASBD = makeASBD(ifmt); err = AudioConverterNew(&device->inputConverterInASBD, &device->inputConverterOutASBD, &device->inputConverter); if (err) return 0; device->inputAU = inputAU; makeABL(&device->rawInputABL, &device->inputConverterInASBD); makeABL(&device->convertedInputABL, &device->inputConverterOutASBD); AURenderCallbackStruct inputCallback; inputCallback.inputProc = renderInput; inputCallback.inputProcRefCon = device; err = AudioUnitSetProperty(inputAU, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &inputCallback, sizeof(inputCallback)); if (err) return 0; err = AudioUnitInitialize(inputAU); if(err) return 0; err = AudioOutputUnitStart(inputAU); if(err) return 0; return 1; } void macosx_audio_close(audio_desc_t ad) { Device *device = devices + ad; AudioOutputUnitStop(device->outputAU); AudioUnitUninitialize(device->outputAU); //CloseComponent(device->outputAU); AudioOutputUnitStop(device->inputAU); AudioUnitUninitialize(device->inputAU); //CloseComponent(device->inputAU); //CloseComponent(device->inputConverter); //CloseComponent(device->outputConverter); free(device->outputBuffer); free(device->inputBuffer); freeABL(device->rawInputABL); freeABL(device->convertedInputABL); freeABL(device->convertedOutputABL); memset(device, 0, sizeof(*device)); } void macosx_audio_drain(audio_desc_t ad) { UNUSED(ad); macosx_audio_read(ad, NULL, 10000000); } int macosx_audio_duplex(audio_desc_t ad) { UNUSED(ad); return 1; } void macosx_audio_set_igain(audio_desc_t ad,int level) { UNUSED(ad); UNUSED(level); } void macosx_audio_set_ogain(audio_desc_t ad,int vol) { UNUSED(ad); UNUSED(vol); } int macosx_audio_get_igain(audio_desc_t ad) { UNUSED(ad); return 50; } int macosx_audio_get_ogain(audio_desc_t ad) { UNUSED(ad); return 50; } void macosx_audio_loopback(audio_desc_t ad, int gain) { UNUSED(ad); UNUSED(gain); } int macosx_audio_read(audio_desc_t ad, u_char *buf, int read_bytes) { Device *device = devices + ad; UInt32 available = device->availableInput;// / bpb * bpb; UInt32 totalRead = min(available, read_bytes); device->availableInput -= totalRead; UInt32 read1 = min(device->inputBufferSize - device->inputBufferReadPosition, totalRead); UInt32 read2 = totalRead - read1; memcpy(buf, device->inputBuffer + device->inputBufferReadPosition, read1); memset(device->inputBuffer + device->inputBufferReadPosition, 0, read1); device->inputBufferReadPosition += read1; if(read2) { memcpy(buf + read1, device->inputBuffer, read2); memset(device->inputBuffer, 0, read2); device->inputBufferReadPosition = read2; } return totalRead; } int macosx_audio_write(audio_desc_t ad, u_char* data, int write_bytes) { Device *device = devices + ad; UInt32 totalWrite = write_bytes; UInt32 write1 = min(device->outputBufferSize - device->outputBufferWritePosition, totalWrite); UInt32 write2 = totalWrite - write1; memcpy(device->outputBuffer + device->outputBufferWritePosition, data, write1); device->outputBufferWritePosition += write1; if(write2) { memcpy(device->outputBuffer, data + write1, write2); device->outputBufferWritePosition = write2; } return totalWrite; } void macosx_audio_non_block(audio_desc_t ad) { UNUSED(ad); } void macosx_audio_block(audio_desc_t ad) { UNUSED(ad); } void macosx_audio_oport_set(audio_desc_t ad, audio_port_t port) { UNUSED(ad); UNUSED(port); } audio_port_t macosx_audio_oport_get(audio_desc_t ad) { UNUSED(ad); return AUDIO_OUT; } int macosx_audio_oport_count(audio_desc_t ad) { UNUSED(ad); return 1; } const audio_port_details_t* macosx_audio_oport_details(audio_desc_t ad, int idx) { UNUSED(ad); UNUSED(idx); return &oport; } void macosx_audio_iport_set(audio_desc_t ad, audio_port_t port) { UNUSED(ad); UNUSED(port); } audio_port_t macosx_audio_iport_get(audio_desc_t ad) { UNUSED(ad); return AUDIO_IN; } int macosx_audio_iport_count(audio_desc_t ad) { UNUSED(ad); return 1; } const audio_port_details_t* macosx_audio_iport_details(audio_desc_t ad, int idx) { UNUSED(ad); UNUSED(idx); return &iport; } int macosx_audio_is_ready(audio_desc_t ad) { Device *device = devices + ad; return device->availableInput;// >= bpb * 1; } void macosx_audio_wait_for(audio_desc_t ad, int delay_ms) { struct timeval tv; UNUSED(ad); tv.tv_sec = 0; tv.tv_usec = delay_ms * 5;//1000; select(0, NULL, NULL, NULL, &tv); } int macosx_audio_supports(audio_desc_t ad, audio_format *fmt) { UNUSED(ad); UNUSED(fmt); return 1; } int macosx_audio_device_count(void) { return 1; } char *macosx_audio_device_name(audio_desc_t idx) { UNUSED(idx); char *name = "Default OSX Sound Device"; return name; }