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 compressor.c
/* |
File: A-Law compressor.c |
Contains: Sample sound compression component to convert |
16-bit linear samples to ALaw-encoded data |
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 Compression 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) |
globals->sourceComponent = source; // remember our source component |
globals->sourceDataPtr = nil; // nothing read from source yet |
return (noErr); |
} |
/* ============================================================================== |
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 |
globals->thisComponent = *requested; |
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 compress |
some more data. It should first make sure it has some source data, then |
compress into an internal buffer and return that buffer to the Sound Manager. |
If the Sound Manager is requesting the data in reverse, you must compress |
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; |
short *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; |
inputBuffer = (short *)sourceDataPtr->buffer; // point at input buffer |
bytesToSkip = (samplesConverted / globals->compInfo.samplesPerPacket) * globals->compInfo.bytesPerFrame; |
sourceDataPtr->buffer += bytesToSkip; // skip over the source consumed |
sourceDataPtr->sampleCount -= samplesConverted; // this many samples will be compressed |
// Do the compression |
CompressALaw(inputBuffer, 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 compressed 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); |
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 |
globals->compInfo.recordSize = sizeof(CompressionInfo); |
result = GetCompressionInfo(fixedCompression, sourceDataPtr->format, |
sourceDataPtr->numChannels, sourceDataPtr->sampleSize, |
&globals->compInfo); |
globals->destCompInfo.recordSize = sizeof(CompressionInfo); |
globals->destCompInfo.format = globals->thisComponent.format; |
GetCompressorInfo(&globals->destCompInfo); // get compression info |
globals->destCompInfo.bytesPerFrame = globals->destCompInfo.bytesPerPacket * sourceDataPtr->numChannels; |
globals->outputFrames = globals->outputSamples / globals->compInfo.samplesPerPacket; |
return (result); |
} |
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
// Compressor-specific Methods |
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
// This routine initializes the compressor. |
void InitializeCompressor(SoundComponentGlobalsPtr globals) // initialize our compressor state |
{ |
#pragma unused (globals) |
// Here you should initialize any state used by your compression 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 compressed sample |
} |
//Code taken from "IMA Recommended Practices for Enhancing Digital Audio Compatibility in Multimedia Systems" |
inline short Normalize (short *value) { |
int numShiftBits, msb, nextmsb; |
numShiftBits = 0; |
msb = (*value & 0x8000) >> 15; |
nextmsb = (*value & 0x4000) >> 14; |
while (msb == nextmsb) { |
*value <<= 1; |
numShiftBits++; |
msb = (*value & 0x8000) >> 15; |
nextmsb = (*value & 0x4000) >> 14; |
} |
return (numShiftBits); |
} |
//Code taken from "IMA Recommended Practices for Enhancing Digital Audio Compatibility in Multimedia Systems" |
void CompressALaw(short *inbuf, Byte *outbuf, unsigned long framesToConvert, |
unsigned long numChannels) |
{ |
int i; |
short origSample, X, YYY, ZZZZ, numberOfShiftBits; |
unsigned char newSample; |
for (i = 0; i < framesToConvert*numChannels; i++) { // loop over all source frames |
origSample = *inbuf; // get ALaw sample |
inbuf ++; |
if (origSample == -32768) { //check boundary condition |
newSample = 0x2A; |
} else { |
X = (origSample & 0x8000) >> 8; //create the sign bit |
if (origSample & 0x8000) //abs(origSample) |
origSample = -origSample; |
if (origSample < 0x0100) { //check for zero segment |
ZZZZ = (origSample >> 4) & 0x000F; //if zero segment, shift down 4 to get position |
YYY = 0x0000; //create segment |
} else { //not zero segment |
numberOfShiftBits = Normalize (&origSample); |
ZZZZ = (origSample & 0x3C00) >> 10; //create position |
YYY = (7 - numberOfShiftBits) << 4; //create segment |
} |
newSample = X | YYY | ZZZZ; //combine sign bit, segment, and position |
newSample ^= 0xD5; //invert necessary bits |
} |
*outbuf = newSample; // output 16-bit sample |
outbuf ++; |
} |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-03-14