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 ++;
    }
}