root/trunk/SMAC/SMACIMAsdec.cpp

Revision 1, 15.6 kB (checked in by gbooker, 3 years ago)

Initial Import

Line 
1 /*      Copyright:      © Copyright 2004 Apple Computer, Inc. All rights reserved.
2
3         Disclaimer:     IMPORTANT:  This Apple software is supplied to you by Apple Computer, Inc.
4                         ("Apple") in consideration of your agreement to the following terms, and your
5                         use, installation, modification or redistribution of this Apple software
6                         constitutes acceptance of these terms.  If you do not agree with these terms,
7                         please do not use, install, modify or redistribute this Apple software.
8
9                         In consideration of your agreement to abide by the following terms, and subject
10                         to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs
11                         copyrights in this original Apple software (the "Apple Software"), to use,
12                         reproduce, modify and redistribute the Apple Software, with or without
13                         modifications, in source and/or binary forms; provided that if you redistribute
14                         the Apple Software in its entirety and without modifications, you must retain
15                         this notice and the following text and disclaimers in all such redistributions of
16                         the Apple Software.  Neither the name, trademarks, service marks or logos of
17                         Apple Computer, Inc. may be used to endorse or promote products derived from the
18                         Apple Software without specific prior written permission from Apple.  Except as
19                         expressly stated in this notice, no other rights or licenses, express or implied,
20                         are granted by Apple herein, including but not limited to any patent rights that
21                         may be infringed by your derivative works or by other works in which the Apple
22                         Software may be incorporated.
23
24                         The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO
25                         WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
26                         WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
27                         PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
28                         COMBINATION WITH YOUR PRODUCTS.
29
30                         IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
31                         CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
32                         GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33                         ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
34                         OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
35                         (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
36                         ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 */
38 /*=============================================================================
39         SMACIMAsdec.cpp
40
41 =============================================================================*/
42
43 //=============================================================================
44 //      Includes
45 //=============================================================================
46 #include <CAConditionalMacros.h>
47
48 #include <string.h>
49 #include "SMACIMAsdec.h"
50 #include "CADebugMacros.h"
51 #include "CAException.h"
52
53 enum
54 {
55         kIMAAudioGeneralFormatID                                = FOUR_CHAR_CODE('ima4'),
56         kIMAAudioGeneralVersion                                 = 0
57 };
58
59
60 //=============================================================================
61 //      SMACIMAsdec
62 //=============================================================================
63
64 SMACIMAsdec::SMACIMAsdec(ComponentInstance inSelf)
65 :
66         SMACsdec(inSelf)
67 {
68         // Defaults
69         mSampleRate = 44100;
70         mNumChannels = 2;
71 }
72
73 SMACIMAsdec::~SMACIMAsdec()
74 {
75 }
76
77 void    SMACIMAsdec::GetInfo(SoundSource inSourceID, OSType inSelector, void* outData)
78 {
79     switch(inSelector)
80         {
81                 case siCompressionChannels:
82                         *((short *)outData) = (short)mNumChannels;
83                         break;
84                        
85                 default:
86                         SMACsdec::GetInfo(inSourceID, inSelector, outData);
87                         break;
88         };
89 }
90
91 void    SMACIMAsdec::SetInfo(SoundSource inSourceID, OSType inSelector, void* inData)
92 {
93         switch(inSelector)
94         {
95                 // IMA does not care about the sample rate, but we'll just accept the data so if we're
96                 // asked what the sample rate is, we can tell whoever it is that asks.
97                 case siCompressionSampleRate:
98                         mSampleRate = (*((UnsignedFixed *)inData)) >> 16;
99                         break;
100
101                 // This really is the only setting IMA cares about
102                 case siCompressionChannels:
103                         mNumChannels = *(short*)inData;
104                         mOutputData.numChannels = mNumChannels;
105                         break;
106
107                 default:
108                         SMACsdec::SetInfo(inSourceID, inSelector, inData);
109                         break;
110         };
111 }
112
113 // This is the answer to a QuickTime call to GetInfo for siCompressionFactor
114 void    SMACIMAsdec::GetCompressionInfo(CompressionInfo& outCompressionInfo)
115 {
116         outCompressionInfo.recordSize = sizeof(CompressionInfo);
117         outCompressionInfo.format = kIMACompression;
118         outCompressionInfo.compressionID = fixedCompression;
119         outCompressionInfo.samplesPerPacket = kIMA4BlockSamples;
120         outCompressionInfo.bytesPerPacket = kIMA4BlockBytes;
121         outCompressionInfo.bytesPerSample = kIMA4BytesPerSample;
122         outCompressionInfo.bytesPerFrame = kIMA4BlockBytes;
123         outCompressionInfo.futureUse1 = 0;
124 }
125
126 // Not used by IMA, but more complex codecs will need to actually return something here.
127 // The decompression params are or contain the "MagicCookie" used by the codec
128 // A MagicCookie can just be a "wave chunk" --  a series of QuickTime atoms that
129 // specify the format and the appropriate configuration
130 void    SMACIMAsdec::GetDecompressionParams(void* outData)
131 {
132         //      We don't have Decompression params
133         Handle theDecompressionParams = NULL;
134         //      set the return value
135         *static_cast<Handle*>(outData) = theDecompressionParams;
136 }
137
138 // Normally, QuickTime will call this directly. In IMA it doesn't.
139 // The compression params are or contain the "MagicCookie" used by the codec
140 // When this is called we can initialize the codec as we will have all the necessary information
141 // to do so.
142 ComponentInstance       SMACIMAsdec::SetDecompressionParams(const void* inData)
143 {
144         AudioStreamBasicDescription theInputFormat = {mSampleRate, kIMAAudioGeneralFormatID, 0, kIMA4BlockBytes, kIMA4BlockSamples, 0, mNumChannels, 0, 0 };
145
146         //      use the format to locate a suitable decoder
147         Component theDecoderComponent = FindIMAAudioDecoderComponent(theInputFormat);
148         ThrowIf(theDecoderComponent == NULL, badFormat, "SMACIMAsdec::SetDecompressionParams: couldn't find a decoder");
149
150         ComponentInstance theDecoder = InitializeIMAAudioDecoder(theDecoderComponent, theInputFormat);
151         ThrowIf(theDecoder == NULL, badFormat, "SMACIMAsdec::SetDecompressionParams: couldn't initialize the decoder");
152        
153         return theDecoder;
154 }
155
156 Component       SMACIMAsdec::FindIMAAudioDecoderComponent(const AudioStreamBasicDescription& inFormat)
157 {
158         Component                               theDecoderComponent = NULL;
159         ComponentDescription    theDecoderDescription = { kAudioDecoderComponentType, inFormat.mFormatID, 0, 0, 0 };
160
161         // If a component has sub types, ala MPEG-4, you'll need to loop here and look at the sub-type 
162         theDecoderComponent = FindNextComponent(NULL, &theDecoderDescription);
163
164         return theDecoderComponent;
165 }
166
167 // Note: The decoder deals with CoreAudio floats while the encoder deals with SInt16s.
168 // This is primarily to illustrate how to deal with either format.
169 static AudioStreamBasicDescription*     FindNEFloatFormat(const AudioStreamBasicDescription* inFormatList, UInt32 inNumberFormats)
170 {
171         AudioStreamBasicDescription* theAnswer = NULL;
172        
173         // Decoders can have multiple output formats
174         while((inNumberFormats > 0) && (theAnswer == NULL))
175         {
176                 if((inFormatList->mFormatID == kAudioFormatLinearPCM) && ((inFormatList->mFormatFlags & kAudioFormatFlagsNativeFloatPacked) == kAudioFormatFlagsNativeFloatPacked) && (inFormatList->mBitsPerChannel == 32))
177                 {
178                         theAnswer = const_cast<AudioStreamBasicDescription*>(inFormatList);
179                 }
180                
181                 ++inFormatList;
182                 --inNumberFormats;
183         }
184        
185         return theAnswer;
186 }
187
188 ComponentInstance       SMACIMAsdec::InitializeIMAAudioDecoder(Component inDecoderComponent, const AudioStreamBasicDescription& inFormat)
189 {
190         UInt32 theSize;
191         ComponentInstance theDecoder = OpenComponent(inDecoderComponent);
192         ThrowIf(theDecoder == NULL, badComponentInstance, "SMACIMAsdec::InitializeIMAAudioDecoder: couldn't open the component");
193        
194         //      first, give the decoder the info we have
195         theSize = sizeof(AudioStreamBasicDescription);
196         ComponentResult theError = AudioCodecSetProperty(theDecoder, kAudioCodecPropertyCurrentInputFormat, theSize, &inFormat);
197         ThrowIfError(theError, (CAException)theError, "SMACIMAsdec::InitializeIMAAudioDecoder: got an error setting the input format");
198                
199         //      now find out what it can output
200         theError = AudioCodecGetPropertyInfo(theDecoder, kAudioCodecPropertySupportedOutputFormats, &theSize, NULL);
201         ThrowIfError(theError, (CAException)theError, "SMACIMAsdec::InitializeIMAAudioDecoder: got an error getting the available output format list size");
202        
203         UInt32 theNumberAvailableOutputFormats = theSize / sizeof(AudioStreamBasicDescription);
204         AudioStreamBasicDescription* theAvailableOutputFormats = new AudioStreamBasicDescription[theNumberAvailableOutputFormats];
205        
206         try
207         {
208                 theSize = theNumberAvailableOutputFormats * sizeof(AudioStreamBasicDescription);
209                 theError = AudioCodecGetProperty(theDecoder, kAudioCodecPropertySupportedOutputFormats, &theSize, theAvailableOutputFormats);
210                 ThrowIfError(theError, (CAException)theError, "SMACIMAsdec::InitializeIMAAudioDecoder: got an error getting the available output formats");
211        
212                 //      find an acceptable output format
213                 AudioStreamBasicDescription* theOutputFormat = FindNEFloatFormat(theAvailableOutputFormats, theNumberAvailableOutputFormats);
214                 ThrowIf(theOutputFormat == NULL, badFormat, "SMACIMAsdec::InitializeIMAAudioDecoder: couldn't find an acceptable output format");
215                
216                 // finish filling out the output format
217                 theOutputFormat->mSampleRate = inFormat.mSampleRate;
218                 theOutputFormat->mChannelsPerFrame = inFormat.mChannelsPerFrame;
219                 theOutputFormat->mBytesPerFrame = 4 * inFormat.mChannelsPerFrame;
220                 theOutputFormat->mFormatID = kAudioFormatLinearPCM;
221                 theOutputFormat->mFormatFlags = kAudioFormatFlagsNativeFloatPacked;
222                 theOutputFormat->mBytesPerPacket = 4 * inFormat.mChannelsPerFrame;
223                 theOutputFormat->mFramesPerPacket = 1;
224                 theOutputFormat->mBitsPerChannel = 32;
225
226                 //      tell the decoder about it
227                 theSize = sizeof(AudioStreamBasicDescription);
228                 theError = AudioCodecSetProperty(theDecoder, kAudioCodecPropertyCurrentOutputFormat, theSize, theOutputFormat);
229                 ThrowIfError(theError, (CAException)theError, "SMACIMAsdec::InitializeIMAAudioDecoder: got an error setting the output format");
230                
231                 delete[] theAvailableOutputFormats;
232                 theAvailableOutputFormats = NULL;
233         }
234         catch(...)
235         {
236                 delete[] theAvailableOutputFormats;
237                 throw;
238         }
239        
240         //      finally initialize the decoder
241         theError = AudioCodecInitialize(theDecoder, NULL, NULL, NULL, 0);
242         ThrowIfError(theError, (CAException)theError, "SMACIMAsdec::InitializeIMAAudioDecoder: got an error initializing the decoder");
243        
244         return theDecoder;
245 }
246
247 // Overrides the method in SMACsdec. Necessary due to the way IMA works. This will not be necessary
248 // for many codecs.
249 void    SMACIMAsdec::GetSourceData(SoundComponentData** outData)
250 {
251         //      initialize the return value
252         *outData = NULL;
253        
254         //      stuff as many packets as we can into the decoder
255         UInt32 theError = 0;
256         void*   theInputData = NULL;
257         UInt32  theInputDataByteSize = 0;
258         UInt32  theInputDataFrameSize = 0;
259 #if     TARGET_API_MAC_OSX
260         bool threadLocked;
261 #endif
262
263 #if !(TARGET_MAC_OS && IS_COMPILER_WORKING)
264         UInt32 totalFloats;
265 #endif
266 #if     TARGET_API_MAC_OSX
267         // Lock the thread. The last thing we want is to have our state change in the middle of
268         // a decode -- it will typically cause a crash
269         threadLocked = mThreadStateMutex->Lock();
270 #endif
271         //      prime things with the current packet
272         GetCurrentInputPacket(theInputData, theInputDataByteSize, theInputDataFrameSize);
273         // Trying to mimic Decompressor.c in this case
274     if((theInputData == NULL) || (theInputDataFrameSize == 0 && theInputDataByteSize == 0) )
275     {
276         // silence with non-zero duration.
277                 mOutputData.buffer = NULL;
278                 *outData = mSourceData;
279     }
280     else
281     {
282                 // On more complex decoders this isn't always necessary
283                 if (!mDecoder)
284                 {
285                         mNumChannels = mOutputData.numChannels = mSourceData->numChannels;
286                         mSampleRate = (mSourceData->sampleRate) >> 16;
287                         CreateIMADecoder();
288                 }
289                
290                 //      loop and stuff the decoder with packets
291                 bool isDone = false;
292                 while((theInputData != NULL) && (theInputDataByteSize != 0) && (!isDone))
293                 {
294                         //      give the current packet to the decoder
295                         UInt32  theNumberInputPackets = 1;
296                         UInt32  theNumberInputBytesConsumed = theInputDataByteSize;
297                         AudioStreamPacketDescription theInputPacketDescription = { 0, 0, theInputDataByteSize };
298                         theError = AudioCodecAppendInputData(mDecoder, theInputData, &theNumberInputBytesConsumed, &theNumberInputPackets, &theInputPacketDescription);
299                         ThrowIfError(theError, (CAException)theError, "SMACIMAsdec::GetSourceData: got an error from AudioCodecAppendInputData");
300                        
301                         //      we know that the decoder is full if it ever says that it didn't take any data from the input buffer
302                         if(theNumberInputBytesConsumed > 0 && theNumberInputBytesConsumed == theInputDataByteSize)
303                         {
304                                 //      consume the data the decoder used
305                                 ConsumeCurrentInputPacket(theInputDataByteSize, 0);
306                                
307                                 //      get the next packet
308                                 GetCurrentInputPacket(theInputData, theInputDataByteSize, theInputDataFrameSize);
309                         }
310                         else if (theNumberInputBytesConsumed > 0)
311                         {
312                                 ConsumeCurrentInputPacket(theNumberInputBytesConsumed, 0);
313                                 isDone = true;
314                         }
315                         else
316                         {
317                                 isDone = true;
318                         }
319                 }
320                 //      the decoder is as full of data as we can make it at this time,
321                 //      so pull out 1 packet of output data
322                 UInt32 theOutputDataByteSize = mPacketFrameSize * sizeof(float) * mOutputData.numChannels;
323                 UInt32 theNumberOutputFrames = mPacketFrameSize;
324                 UInt32 theDecoderStatus = kAudioCodecProduceOutputPacketFailure;
325                 theError = AudioCodecProduceOutputPackets(mDecoder, mFloatBuffer, &theOutputDataByteSize, &theNumberOutputFrames, NULL, &theDecoderStatus);
326                 ThrowIfError(theError, (CAException)theError, "SMACIMAsdec::GetSourceData: got an error from AudioCodecProduceOutputPackets");
327                
328                 //      set up the return values if we got a packet back from the decoder
329                 if(theNumberOutputFrames)
330                 {
331 // gcc 2.95 doesn't work right in this case, so we guard against it
332 #if TARGET_MAC_OS && IS_COMPILER_WORKING
333                         Float32ToNativeInt16(mHasAltiVec, mFloatBuffer, ((short *)mOutputBuffer), theOutputDataByteSize >> 2);
334 #else
335                         totalFloats = theOutputDataByteSize >> 2;
336                         for (unsigned int i = 0; i < totalFloats; i++)
337                         {
338                                 ((short *)mOutputBuffer)[i] = (SInt16)(32768.0 * mFloatBuffer[i]);
339                         }
340 #endif
341                         mOutputData.buffer = mOutputBuffer;
342                         if (mOutputData.numChannels == 2) // It's either 1 or 2 here in SMAC land
343                         {
344                                 mOutputData.sampleCount = (theOutputDataByteSize >> 3); // Yes, these really are frames.
345                         }
346                         else
347                         {
348                                 mOutputData.sampleCount = (theOutputDataByteSize >> 2); // mono
349                         }
350                         *outData = &mOutputData;
351                 }
352                 else
353                 {
354                         mOutputData.buffer = NULL;
355                         mOutputData.sampleCount = 0;
356                         *outData = &mOutputData;
357                 }
358     }
359 #if     TARGET_API_MAC_OSX
360         if(threadLocked)
361         {
362                 mThreadStateMutex->Unlock();
363         }
364 #endif
365 }
366
367 // Not necessary for more complex codecs as SetDecompressionParams() will be called directly by QuickTime
368 void    SMACIMAsdec::CreateIMADecoder()
369 {
370         mDecoder = SetDecompressionParams(NULL);
371         UInt32 theSize = sizeof(UInt32);
372         ComponentResult theError = AudioCodecGetProperty(mDecoder, kAudioCodecPropertyPacketFrameSize, &theSize, &mPacketFrameSize);
373         ThrowIfError(theError, (CAException)theError, "SMACIMAsdec::CreateIMADecoder: got an error from AudioCodecGetProperty");
374        
375         //      toss the old output buffer
376         delete[] mOutputBuffer;
377         delete[] mFloatBuffer;
378        
379         //      allocate enough space for 1 packet of data, since that's
380         //      that's all this component will produce per call to GetSourceData
381         //      note that this is going to be 16 bit integer so we need to allocate a separate
382         //      buffer to deal with the floating point data as well
383         mOutputBuffer = new Byte[mPacketFrameSize * sizeof(SInt16) * mOutputData.numChannels];
384         mFloatBuffer = new float[mPacketFrameSize * mOutputData.numChannels];
385 }
Note: See TracBrowser for help on using the browser.