/*============================================================================= CAChannelMapper.cpp $Log: CAChannelMapper.cpp,v $ Revision 1.3 2005/01/12 22:01:17 luke gcc 4.0 fixes (code reviewed by asynth) Revision 1.2 2004/09/30 21:07:42 jcm10 flat includes Revision 1.1 2004/01/14 00:08:09 dwyatt moved from Source/Tests/AudioFileUtility/Utility Revision 1.2 2003/08/23 04:53:24 dwyatt don't spew matrix mixer dumps Revision 1.1 2003/08/04 23:40:00 dwyatt initial checkin created Mon Jul 28 2003, Doug Wyatt Copyright (c) 2003 Apple Computer, Inc. All Rights Reserved $NoKeywords: $ =============================================================================*/ #include "CAChannelMapper.h" #if !defined(__COREAUDIO_USE_FLAT_INCLUDES__) #include #else #include #endif static void DefaultChannelLayout(CAAudioChannelLayout &layout, UInt32 nchannels) { // we really can't do a sensible downmix without valid source/destination channel layouts if (nchannels == 1) layout = CAAudioChannelLayout(kAudioChannelLayoutTag_Mono); else if (nchannels == 2) layout = CAAudioChannelLayout(kAudioChannelLayoutTag_Stereo); } CAChannelMapper::CAChannelMapper( const CAStreamBasicDescription &srcFormat, const CAStreamBasicDescription &destFormat, const CAAudioChannelLayout * inSrcLayout, const CAAudioChannelLayout * inDestLayout) : mSrcNChannels(srcFormat.mChannelsPerFrame), mDestNChannels(destFormat.mChannelsPerFrame) { if (inSrcLayout && inSrcLayout->IsValid()) mSrcLayout = *inSrcLayout; else DefaultChannelLayout(mSrcLayout, srcFormat.mChannelsPerFrame); if (inDestLayout && inDestLayout->IsValid()) mDestLayout = *inDestLayout; else DefaultChannelLayout(mDestLayout, destFormat.mChannelsPerFrame); } CAChannelMapper::~CAChannelMapper() { if (mMatrixMixer) CloseComponent(mMatrixMixer); } OSStatus CAChannelMapper::OpenMixer(double sampleRate) { CAComponent comp(kAudioUnitType_Mixer, kAudioUnitSubType_MatrixMixer, kAudioUnitManufacturer_Apple); OSStatus err; err = CAAudioUnit::Open(comp, mMatrixMixer); if (err) return err; CAStreamBasicDescription fmt; fmt.mSampleRate = sampleRate; UInt32 nbuses = 1; err = mMatrixMixer.SetProperty(kAudioUnitProperty_BusCount, kAudioUnitScope_Input, 0, &nbuses, sizeof(UInt32)); if (err) return err; err = mMatrixMixer.SetProperty(kAudioUnitProperty_BusCount, kAudioUnitScope_Output, 0, &nbuses, sizeof(UInt32)); if (err) return err; fmt.SetCanonical(mSrcNChannels, false); err = mMatrixMixer.SetFormat(kAudioUnitScope_Input, 0, fmt); if (err) return err; fmt.SetCanonical(mDestNChannels, false); err = mMatrixMixer.SetFormat(kAudioUnitScope_Output, 0, fmt); if (err) return err; // set render callback AURenderCallbackStruct input; input.inputProc = MixerInputProc; input.inputProcRefCon = this; err = mMatrixMixer.SetProperty( kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, 0, &input, sizeof(input)); err = mMatrixMixer.Initialize(); if (err) return err; return ResetMixer(); } OSStatus CAChannelMapper::ResetMixer() { int nin = mSrcNChannels, nout = mDestNChannels; int i, j; // set global, input and output volumes mMatrixMixer.SetParameter(kMatrixMixerParam_Volume, kAudioUnitScope_Global, 0xFFFFFFFF, 1.); for (i = 0; i < nout; ++i) { mMatrixMixer.SetParameter(kMatrixMixerParam_Volume, kAudioUnitScope_Global, 0xFFFF0000 | i, 1.); } for (i = 0; i < nin; ++i) { mMatrixMixer.SetParameter(kMatrixMixerParam_Volume, kAudioUnitScope_Global, (i<<16) | 0xFFFF, 1.); } // set crosspoint volumes for (i = 0; i < nin; ++i) { for (j = 0; j < nout; ++j) { mMatrixMixer.SetParameter(kMatrixMixerParam_Volume, kAudioUnitScope_Global, (i<<16) | j, 0.); } } return noErr; } OSStatus CAChannelMapper::ConfigureDownmix() { OSStatus err = ResetMixer(); if (err) return err; const AudioChannelLayout *layouts[] = { &mSrcLayout.Layout(), &mDestLayout.Layout() }; UInt32 propSize; err = AudioFormatGetPropertyInfo(kAudioFormatProperty_MatrixMixMap, sizeof(layouts), layouts, &propSize); if (err) return err; void *mixmap = malloc(propSize); err = AudioFormatGetProperty(kAudioFormatProperty_MatrixMixMap, sizeof(layouts), layouts, &propSize, mixmap); if (!err) { mMatrixMixer.SetParameter(kMatrixMixerParam_Volume, kAudioUnitScope_Global, 0xFFFFFFFF, 1.); int nin = mSrcNChannels, nout = mDestNChannels; int i, j; // set the crosspoint volumes Float32 *val = (Float32 *)mixmap; for (i = 0; i < nin; ++i) { for (j = 0; j < nout; ++j) { mMatrixMixer.SetParameter(kMatrixMixerParam_Volume, kAudioUnitScope_Global, (i<<16) | j, *val++); } } } free(mixmap); return noErr; } OSStatus CAChannelMapper::ConnectChannelToChannel(UInt32 inChannel, UInt32 outChannel) { return mMatrixMixer.SetParameter(kMatrixMixerParam_Volume, kAudioUnitScope_Global, (inChannel << 16) | outChannel, 1.); } OSStatus CAChannelMapper::Mix(const AudioBufferList *src, AudioBufferList *dest, UInt32 nFrames) { mMixInputBufferList = src; AudioUnitRenderActionFlags flags = 0; AudioTimeStamp ts; ts.mFlags = 0; return AudioUnitRender(mMatrixMixer.AU(), &flags, &ts, 0, nFrames, dest); } OSStatus CAChannelMapper::MixerInputProc( void * inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp * inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList * ioData) { CAChannelMapper *This = static_cast(inRefCon); const AudioBufferList *mixInputBufferList = This->mMixInputBufferList; UInt32 copySize = sizeof(UInt32) + (mixInputBufferList->mNumberBuffers * sizeof(AudioBuffer)); memcpy(ioData, mixInputBufferList, copySize); return noErr; }