/* Copyright: © Copyright 2004 Apple Computer, Inc. All rights reserved. Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. ("Apple") in consideration of your agreement to the following terms, and your use, installation, modification or redistribution of this Apple software constitutes acceptance of these terms. If you do not agree with these terms, please do not use, install, modify or redistribute this Apple software. In consideration of your agreement to abide by the following terms, and subject to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs copyrights in this original Apple software (the "Apple Software"), to use, reproduce, modify and redistribute the Apple Software, with or without modifications, in source and/or binary forms; provided that if you redistribute the Apple Software in its entirety and without modifications, you must retain this notice and the following text and disclaimers in all such redistributions of the Apple Software. Neither the name, trademarks, service marks or logos of Apple Computer, Inc. may be used to endorse or promote products derived from the Apple Software without specific prior written permission from Apple. Except as expressly stated in this notice, no other rights or licenses, express or implied, are granted by Apple herein, including but not limited to any patent rights that may be infringed by your derivative works or by other works in which the Apple Software may be incorporated. The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /*============================================================================= SMACIMAsdec.cpp =============================================================================*/ //============================================================================= // Includes //============================================================================= #include #include #include "SMACIMAsdec.h" #include "CADebugMacros.h" #include "CAException.h" enum { kIMAAudioGeneralFormatID = FOUR_CHAR_CODE('ima4'), kIMAAudioGeneralVersion = 0 }; //============================================================================= // SMACIMAsdec //============================================================================= SMACIMAsdec::SMACIMAsdec(ComponentInstance inSelf) : SMACsdec(inSelf) { // Defaults mSampleRate = 44100; mNumChannels = 2; } SMACIMAsdec::~SMACIMAsdec() { } void SMACIMAsdec::GetInfo(SoundSource inSourceID, OSType inSelector, void* outData) { switch(inSelector) { case siCompressionChannels: *((short *)outData) = (short)mNumChannels; break; default: SMACsdec::GetInfo(inSourceID, inSelector, outData); break; }; } void SMACIMAsdec::SetInfo(SoundSource inSourceID, OSType inSelector, void* inData) { switch(inSelector) { // IMA does not care about the sample rate, but we'll just accept the data so if we're // asked what the sample rate is, we can tell whoever it is that asks. case siCompressionSampleRate: mSampleRate = (*((UnsignedFixed *)inData)) >> 16; break; // This really is the only setting IMA cares about case siCompressionChannels: mNumChannels = *(short*)inData; mOutputData.numChannels = mNumChannels; break; default: SMACsdec::SetInfo(inSourceID, inSelector, inData); break; }; } // This is the answer to a QuickTime call to GetInfo for siCompressionFactor void SMACIMAsdec::GetCompressionInfo(CompressionInfo& outCompressionInfo) { outCompressionInfo.recordSize = sizeof(CompressionInfo); outCompressionInfo.format = kIMACompression; outCompressionInfo.compressionID = fixedCompression; outCompressionInfo.samplesPerPacket = kIMA4BlockSamples; outCompressionInfo.bytesPerPacket = kIMA4BlockBytes; outCompressionInfo.bytesPerSample = kIMA4BytesPerSample; outCompressionInfo.bytesPerFrame = kIMA4BlockBytes; outCompressionInfo.futureUse1 = 0; } // Not used by IMA, but more complex codecs will need to actually return something here. // The decompression params are or contain the "MagicCookie" used by the codec // A MagicCookie can just be a "wave chunk" -- a series of QuickTime atoms that // specify the format and the appropriate configuration void SMACIMAsdec::GetDecompressionParams(void* outData) { // We don't have Decompression params Handle theDecompressionParams = NULL; // set the return value *static_cast(outData) = theDecompressionParams; } // Normally, QuickTime will call this directly. In IMA it doesn't. // The compression params are or contain the "MagicCookie" used by the codec // When this is called we can initialize the codec as we will have all the necessary information // to do so. ComponentInstance SMACIMAsdec::SetDecompressionParams(const void* inData) { AudioStreamBasicDescription theInputFormat = {mSampleRate, kIMAAudioGeneralFormatID, 0, kIMA4BlockBytes, kIMA4BlockSamples, 0, mNumChannels, 0, 0 }; // use the format to locate a suitable decoder Component theDecoderComponent = FindIMAAudioDecoderComponent(theInputFormat); ThrowIf(theDecoderComponent == NULL, badFormat, "SMACIMAsdec::SetDecompressionParams: couldn't find a decoder"); ComponentInstance theDecoder = InitializeIMAAudioDecoder(theDecoderComponent, theInputFormat); ThrowIf(theDecoder == NULL, badFormat, "SMACIMAsdec::SetDecompressionParams: couldn't initialize the decoder"); return theDecoder; } Component SMACIMAsdec::FindIMAAudioDecoderComponent(const AudioStreamBasicDescription& inFormat) { Component theDecoderComponent = NULL; ComponentDescription theDecoderDescription = { kAudioDecoderComponentType, inFormat.mFormatID, 0, 0, 0 }; // If a component has sub types, ala MPEG-4, you'll need to loop here and look at the sub-type theDecoderComponent = FindNextComponent(NULL, &theDecoderDescription); return theDecoderComponent; } // Note: The decoder deals with CoreAudio floats while the encoder deals with SInt16s. // This is primarily to illustrate how to deal with either format. static AudioStreamBasicDescription* FindNEFloatFormat(const AudioStreamBasicDescription* inFormatList, UInt32 inNumberFormats) { AudioStreamBasicDescription* theAnswer = NULL; // Decoders can have multiple output formats while((inNumberFormats > 0) && (theAnswer == NULL)) { if((inFormatList->mFormatID == kAudioFormatLinearPCM) && ((inFormatList->mFormatFlags & kAudioFormatFlagsNativeFloatPacked) == kAudioFormatFlagsNativeFloatPacked) && (inFormatList->mBitsPerChannel == 32)) { theAnswer = const_cast(inFormatList); } ++inFormatList; --inNumberFormats; } return theAnswer; } ComponentInstance SMACIMAsdec::InitializeIMAAudioDecoder(Component inDecoderComponent, const AudioStreamBasicDescription& inFormat) { UInt32 theSize; ComponentInstance theDecoder = OpenComponent(inDecoderComponent); ThrowIf(theDecoder == NULL, badComponentInstance, "SMACIMAsdec::InitializeIMAAudioDecoder: couldn't open the component"); // first, give the decoder the info we have theSize = sizeof(AudioStreamBasicDescription); ComponentResult theError = AudioCodecSetProperty(theDecoder, kAudioCodecPropertyCurrentInputFormat, theSize, &inFormat); ThrowIfError(theError, (CAException)theError, "SMACIMAsdec::InitializeIMAAudioDecoder: got an error setting the input format"); // now find out what it can output theError = AudioCodecGetPropertyInfo(theDecoder, kAudioCodecPropertySupportedOutputFormats, &theSize, NULL); ThrowIfError(theError, (CAException)theError, "SMACIMAsdec::InitializeIMAAudioDecoder: got an error getting the available output format list size"); UInt32 theNumberAvailableOutputFormats = theSize / sizeof(AudioStreamBasicDescription); AudioStreamBasicDescription* theAvailableOutputFormats = new AudioStreamBasicDescription[theNumberAvailableOutputFormats]; try { theSize = theNumberAvailableOutputFormats * sizeof(AudioStreamBasicDescription); theError = AudioCodecGetProperty(theDecoder, kAudioCodecPropertySupportedOutputFormats, &theSize, theAvailableOutputFormats); ThrowIfError(theError, (CAException)theError, "SMACIMAsdec::InitializeIMAAudioDecoder: got an error getting the available output formats"); // find an acceptable output format AudioStreamBasicDescription* theOutputFormat = FindNEFloatFormat(theAvailableOutputFormats, theNumberAvailableOutputFormats); ThrowIf(theOutputFormat == NULL, badFormat, "SMACIMAsdec::InitializeIMAAudioDecoder: couldn't find an acceptable output format"); // finish filling out the output format theOutputFormat->mSampleRate = inFormat.mSampleRate; theOutputFormat->mChannelsPerFrame = inFormat.mChannelsPerFrame; theOutputFormat->mBytesPerFrame = 4 * inFormat.mChannelsPerFrame; theOutputFormat->mFormatID = kAudioFormatLinearPCM; theOutputFormat->mFormatFlags = kAudioFormatFlagsNativeFloatPacked; theOutputFormat->mBytesPerPacket = 4 * inFormat.mChannelsPerFrame; theOutputFormat->mFramesPerPacket = 1; theOutputFormat->mBitsPerChannel = 32; // tell the decoder about it theSize = sizeof(AudioStreamBasicDescription); theError = AudioCodecSetProperty(theDecoder, kAudioCodecPropertyCurrentOutputFormat, theSize, theOutputFormat); ThrowIfError(theError, (CAException)theError, "SMACIMAsdec::InitializeIMAAudioDecoder: got an error setting the output format"); delete[] theAvailableOutputFormats; theAvailableOutputFormats = NULL; } catch(...) { delete[] theAvailableOutputFormats; throw; } // finally initialize the decoder theError = AudioCodecInitialize(theDecoder, NULL, NULL, NULL, 0); ThrowIfError(theError, (CAException)theError, "SMACIMAsdec::InitializeIMAAudioDecoder: got an error initializing the decoder"); return theDecoder; } // Overrides the method in SMACsdec. Necessary due to the way IMA works. This will not be necessary // for many codecs. void SMACIMAsdec::GetSourceData(SoundComponentData** outData) { // initialize the return value *outData = NULL; // stuff as many packets as we can into the decoder UInt32 theError = 0; void* theInputData = NULL; UInt32 theInputDataByteSize = 0; UInt32 theInputDataFrameSize = 0; #if TARGET_API_MAC_OSX bool threadLocked; #endif #if !(TARGET_MAC_OS && IS_COMPILER_WORKING) UInt32 totalFloats; #endif #if TARGET_API_MAC_OSX // Lock the thread. The last thing we want is to have our state change in the middle of // a decode -- it will typically cause a crash threadLocked = mThreadStateMutex->Lock(); #endif // prime things with the current packet GetCurrentInputPacket(theInputData, theInputDataByteSize, theInputDataFrameSize); // Trying to mimic Decompressor.c in this case if((theInputData == NULL) || (theInputDataFrameSize == 0 && theInputDataByteSize == 0) ) { // silence with non-zero duration. mOutputData.buffer = NULL; *outData = mSourceData; } else { // On more complex decoders this isn't always necessary if (!mDecoder) { mNumChannels = mOutputData.numChannels = mSourceData->numChannels; mSampleRate = (mSourceData->sampleRate) >> 16; CreateIMADecoder(); } // loop and stuff the decoder with packets bool isDone = false; while((theInputData != NULL) && (theInputDataByteSize != 0) && (!isDone)) { // give the current packet to the decoder UInt32 theNumberInputPackets = 1; UInt32 theNumberInputBytesConsumed = theInputDataByteSize; AudioStreamPacketDescription theInputPacketDescription = { 0, 0, theInputDataByteSize }; theError = AudioCodecAppendInputData(mDecoder, theInputData, &theNumberInputBytesConsumed, &theNumberInputPackets, &theInputPacketDescription); ThrowIfError(theError, (CAException)theError, "SMACIMAsdec::GetSourceData: got an error from AudioCodecAppendInputData"); // we know that the decoder is full if it ever says that it didn't take any data from the input buffer if(theNumberInputBytesConsumed > 0 && theNumberInputBytesConsumed == theInputDataByteSize) { // consume the data the decoder used ConsumeCurrentInputPacket(theInputDataByteSize, 0); // get the next packet GetCurrentInputPacket(theInputData, theInputDataByteSize, theInputDataFrameSize); } else if (theNumberInputBytesConsumed > 0) { ConsumeCurrentInputPacket(theNumberInputBytesConsumed, 0); isDone = true; } else { isDone = true; } } // the decoder is as full of data as we can make it at this time, // so pull out 1 packet of output data UInt32 theOutputDataByteSize = mPacketFrameSize * sizeof(float) * mOutputData.numChannels; UInt32 theNumberOutputFrames = mPacketFrameSize; UInt32 theDecoderStatus = kAudioCodecProduceOutputPacketFailure; theError = AudioCodecProduceOutputPackets(mDecoder, mFloatBuffer, &theOutputDataByteSize, &theNumberOutputFrames, NULL, &theDecoderStatus); ThrowIfError(theError, (CAException)theError, "SMACIMAsdec::GetSourceData: got an error from AudioCodecProduceOutputPackets"); // set up the return values if we got a packet back from the decoder if(theNumberOutputFrames) { // gcc 2.95 doesn't work right in this case, so we guard against it #if TARGET_MAC_OS && IS_COMPILER_WORKING Float32ToNativeInt16(mHasAltiVec, mFloatBuffer, ((short *)mOutputBuffer), theOutputDataByteSize >> 2); #else totalFloats = theOutputDataByteSize >> 2; for (unsigned int i = 0; i < totalFloats; i++) { ((short *)mOutputBuffer)[i] = (SInt16)(32768.0 * mFloatBuffer[i]); } #endif mOutputData.buffer = mOutputBuffer; if (mOutputData.numChannels == 2) // It's either 1 or 2 here in SMAC land { mOutputData.sampleCount = (theOutputDataByteSize >> 3); // Yes, these really are frames. } else { mOutputData.sampleCount = (theOutputDataByteSize >> 2); // mono } *outData = &mOutputData; } else { mOutputData.buffer = NULL; mOutputData.sampleCount = 0; *outData = &mOutputData; } } #if TARGET_API_MAC_OSX if(threadLocked) { mThreadStateMutex->Unlock(); } #endif } // Not necessary for more complex codecs as SetDecompressionParams() will be called directly by QuickTime void SMACIMAsdec::CreateIMADecoder() { mDecoder = SetDecompressionParams(NULL); UInt32 theSize = sizeof(UInt32); ComponentResult theError = AudioCodecGetProperty(mDecoder, kAudioCodecPropertyPacketFrameSize, &theSize, &mPacketFrameSize); ThrowIfError(theError, (CAException)theError, "SMACIMAsdec::CreateIMADecoder: got an error from AudioCodecGetProperty"); // toss the old output buffer delete[] mOutputBuffer; delete[] mFloatBuffer; // allocate enough space for 1 packet of data, since that's // that's all this component will produce per call to GetSourceData // note that this is going to be 16 bit integer so we need to allocate a separate // buffer to deal with the floating point data as well mOutputBuffer = new Byte[mPacketFrameSize * sizeof(SInt16) * mOutputData.numChannels]; mFloatBuffer = new float[mPacketFrameSize * mOutputData.numChannels]; }