Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
A-Law decompressor.c
/* |
File: A-Law decompressor.c |
Contains: Sample sound decompression component to convert |
ALaw-encoded data to 16-bit linear samples. |
Written by: Mark Cookson |
Copyright: Copyright © 1996-1999 by Apple Computer, Inc., All Rights Reserved. |
You may incorporate this Apple sample source code into your program(s) without |
restriction. This Apple sample source code has been provided "AS IS" and the |
responsibility for its operation is yours. You are not permitted to redistribute |
this Apple sample source code as "Apple sample source code" after having made |
changes. If you're going to re-distribute the source, we require that you make |
it clear in the source that the code was descended from Apple sample source |
code, but that you've made changes. |
Change History (most recent first): |
8/13/1999 Karl Groethe Updated for Metrowerks Codewarror Pro 2.1 |
*/ |
#include "ComponentDispatch.h" |
#include <Memory.h> |
#include <Errors.h> |
#include <SoundInput.h> |
#include <Components.h> |
#include <Gestalt.h> |
#include <Sound.h> |
#include <SoundComponents.h> |
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
// Standard Decompression Component Methods |
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
/* ============================================================================== |
SetSource |
This routine sets the component we should call to get more data. We must remember |
this component and also tell it the data format of our component requires. |
============================================================================== */ |
pascal ComponentResult __SoundComponentSetSource(SoundComponentGlobalsPtr globals, SoundSource sourceID, ComponentInstance source) |
{ |
#pragma unused (sourceID) |
SoundComponentDataPtr sourceSifter; |
globals->sourceComponent = source; // remember our source component |
globals->sourceDataPtr = nil; // nothing read from source yet |
// make sure we can get the source we need |
return (SoundComponentSetOutput(source, &globals->thisComponent, &sourceSifter)); |
} |
/* ============================================================================== |
SetOutput |
This routine sets the data format our component should output. If we can't output |
the requested format, we should return a pointer to the format we do support, |
and return an error, and the Sound Manager will attempt the conversion for us. |
============================================================================== */ |
pascal ComponentResult __SoundComponentSetOutput(SoundComponentGlobalsPtr globals, SoundComponentDataPtr requested, SoundComponentDataPtr *actual) |
{ |
globals->outputSamples = requested->sampleCount; // no. samples to output |
if (globals->outputSamples > kMaxOutputSamples) // too much for our buffer |
globals->outputSamples = kMaxOutputSamples; // only output what we can |
// make sure data format and sample sizes match |
if ((requested->format == kOutputSampleFormat) && // formats match |
(requested->sampleSize == kOutputSampleSize)) // sample sizes match |
{ |
return (noErr); // no problem outputting this format |
} |
else |
{ |
// If we can't output the requested format, the Sound Manager will make an attempt to convert our |
// format into something that can be used. In order for the Sound Manager to do this, we need to |
// tell it here the format we will be outputting, so it can setup the proper conversion. This is really |
// handy if your algorithm only outputs 16-bit data, but the Sound Manager is requesting 8-bit. In this |
// case, the Sound Manager will automatically convert your 16-bit data to 8-bit. |
*actual = &globals->thisComponent; // tell the Sound Manager what we will output |
return (paramErr); // force the Sound Manager to convert for us |
} |
} |
/* ============================================================================== |
GetSourceData |
This routine is called when the Sound Manager wants your component to decompress |
some more data. It should first make sure it has some source data, then |
decompress into an internal buffer and return that buffer to the Sound Manager. |
If the Sound Manager is requesting the data in reverse, you must decompress |
the data starting from the end of the source buffer, and the Sound Manager will |
reverse the samples for you. |
NOTE: This will most often be called at interrupt time. |
============================================================================== */ |
pascal ComponentResult __SoundComponentGetSourceData(SoundComponentGlobalsPtr globals, SoundComponentDataPtr *resultDataPtr) |
{ |
SoundComponentDataPtr sourceDataPtr; |
unsigned long samplesConverted, framesToConvert, bytesToSkip; |
ComponentResult result; |
Byte *inputBuffer; |
result = noErr; |
sourceDataPtr = globals->sourceDataPtr; // get pointer to source sound component |
if (sourceDataPtr == nil) // source buffer was flushed |
{ |
result = PrimeSource(globals); // start with all new source data |
} |
else if (sourceDataPtr->sampleCount == 0) // source buffer is empty |
{ |
result = SoundComponentGetSourceData(globals->sourceComponent, &globals->sourceDataPtr); // fill it up // continue where we left off |
} |
if (result != noErr) // bail if error |
return (result); |
sourceDataPtr = globals->sourceDataPtr; // get pointer to source sound component |
if ((sourceDataPtr->format == globals->thisComponent.format) || // input and output are same |
(sourceDataPtr->buffer == nil)) // or no source buffer |
{ |
globals->sourceDataPtr = nil; // get new source next time |
*resultDataPtr = sourceDataPtr; // pass source on down |
} |
else |
{ |
// convert the source samples into frames |
framesToConvert = sourceDataPtr->sampleCount / globals->compInfo.samplesPerPacket; |
if (framesToConvert) // source has some data for us |
{ |
if (framesToConvert > globals->outputFrames) |
framesToConvert = globals->outputFrames; // limited to size of output |
// convert frames back into samples to quantize the source correctly |
samplesConverted = framesToConvert * globals->compInfo.samplesPerPacket; |
// If the Sound Manager is going to play this sound backwards, we need to decompress |
// starting from the end of the buffer, and it will reverse the decompressed samples for us. |
inputBuffer = sourceDataPtr->buffer; // point at input buffer |
if (globals->reverse) |
{ |
bytesToSkip = sourceDataPtr->sampleCount - samplesConverted; // no. samples to skip |
bytesToSkip = (bytesToSkip / globals->compInfo.samplesPerPacket) * globals->compInfo.bytesPerFrame; // convert samples into bytes |
inputBuffer += bytesToSkip; // decompress from end of buffer |
} |
// If it is playing forward (the normal case), we just skip over the source samples that |
// will be decompressed below. |
else |
{ |
bytesToSkip = (samplesConverted / globals->compInfo.samplesPerPacket) * globals->compInfo.bytesPerFrame; |
sourceDataPtr->buffer += bytesToSkip; // skip over the source consumed |
} |
sourceDataPtr->sampleCount -= samplesConverted; // this many samples will be decompressed |
// Do the decompression |
DecompressALaw(inputBuffer, (short*)globals->buffer, framesToConvert, sourceDataPtr->numChannels); |
} |
else |
samplesConverted = 0; // no samples were converted |
globals->thisComponent.buffer = (Byte *) globals->buffer; // data in this buffer |
globals->thisComponent.sampleCount = samplesConverted; // return num. samples converted |
*resultDataPtr = &globals->thisComponent; // return description of decompressed data |
} |
return (result); |
} |
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
// This routine is used to prime the source. It gets the first load of data |
// from the source and intializes the output format and compression factors. |
ComponentResult PrimeSource(SoundComponentGlobalsPtr globals) |
{ |
ComponentResult result; |
SoundComponentDataPtr sourceDataPtr; |
// get data from source |
result = SoundComponentGetSourceData(globals->sourceComponent, &globals->sourceDataPtr); |
if (result != noErr) |
return (result); |
if (globals->sourceDataPtr == nil) |
return (paramErr); |
// copy source settings so we know what to output. Notice that we do not change the sampleSize |
// or format fields, since we always output 16-bit, twos-complement data in this example. |
sourceDataPtr = globals->sourceDataPtr; |
globals->thisComponent.flags = sourceDataPtr->flags; // copy flags unchanged |
globals->thisComponent.sampleRate = sourceDataPtr->sampleRate; // copy sample rate unchanged |
globals->thisComponent.numChannels = sourceDataPtr->numChannels; // copy numchannels unchanged |
// The Sound Manager sets the flags field of the sourceDataPtr to indicate if the |
// sound should be played backwards. If this bit is set, we just store it in the |
// globals so we know to decompress from the end of the buffer. |
if (sourceDataPtr->flags & kReverse) // source is to be played backwards |
globals->reverse = true; |
else |
globals->reverse = false; |
// Setup the the compression info, so that we can convert between samples, frames and bytes correctly. |
globals->compInfo.recordSize = sizeof(CompressionInfo); |
globals->compInfo.format = sourceDataPtr->format; |
GetCompressorInfo(&globals->compInfo); // get compression info |
globals->compInfo.bytesPerFrame = globals->compInfo.bytesPerPacket * sourceDataPtr->numChannels; |
globals->outputFrames = globals->outputSamples / globals->compInfo.samplesPerPacket; |
return (noErr); |
} |
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
// Compressor-specific Methods |
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
// This routine initializes the decompressor. |
void InitializeDecompressor(SoundComponentGlobalsPtr globals) // initialize our decompressor state |
{ |
#pragma unused (globals) |
// Here you should initialize any state used by your decompression alorithm (such as predictors) |
// to default values. This routine will be called whenever a new sound is started so you can set |
// up correctly. In our example, we do not have any state, so we have nothing to set up. |
} |
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
// This routine returns information about the compression ratios. |
void GetCompressorInfo(CompressionInfoPtr cp) |
{ |
if (cp->recordSize > sizeof(CompressionInfo)) // limit amount we return |
cp->recordSize = sizeof(CompressionInfo); |
cp->compressionID = fixedCompression; // must set this to fixedCompression |
cp->samplesPerPacket = 1; // no. samples in one compressed packet |
cp->bytesPerPacket = 1; // no. bytes in a packet |
cp->bytesPerSample = 2; // no. bytes in a decompressed sample |
} |
//Code taken from "IMA Recommended Practices for Enhancing Digital Audio Compatibility in Multimedia Systems" |
void DecompressALaw (Byte *inbuf, short * outbuf, unsigned long framesToConvert, unsigned long numChannels) |
{ |
int i; |
short X, YYY, newSample, tempValue; |
unsigned char origSample; |
for (i = 0; i < framesToConvert*numChannels; i++) { // loop over all source frames |
origSample = *inbuf; // get ALaw sample |
inbuf ++; |
tempValue = origSample ^ 0x00D5; |
X = (tempValue & 0x0080) >> 7; |
newSample = ((tempValue & 0x000F) << 1) | 0x0001; |
YYY = (tempValue & 0x0070) >> 4; |
if ((YYY - 1) == 0) { |
newSample |= 0x0020; |
} else if ((YYY - 1) > 0) { |
newSample |= 0x0020; |
newSample <<= (YYY - 1); |
} |
newSample <<= 3; |
if (X) { |
newSample = -newSample; |
} |
*outbuf = newSample; // output 16-bit sample |
outbuf ++; |
} |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-03-14