ftp.nice.ch/pub/next/unix/audio/sndutil.1.3.s.tar.gz#/sndutil-1.3/sndproc/sndproc_ola.c

This is sndproc_ola.c in view mode; [Download] [Up]

/* Copyright 1988-1992, NeXT Inc.  All rights reserved. */
/* sndproc_ola.c - like sndproc0.c but with 50% overlap in frames.
 *
 *      This program shell is appropriate for a "transform coder"
 * 	with 50% overlap, such as used by the Dolby AC-2 system.
 *
 *	This program reads in a soundfile, applies a black box to
 *	the sound, one frame at a time, and writes a new soundfile.
 *      The frame advances by less than a whole frame on each step,
 *      and the overlapping output frames are added together.
 *
 *	Currently supports only SND_FORMAT_LINEAR_16.
 *	If the sound is multi-channel, only the first channel is filtered
 *	and the output sound is mono.
 *
 *	Usage:  sndproc_ola <inputSoundFile> <outputSoundFile>
 *
 *	The algorithm:
 *          You provide - currently just "clears the low-order bit"
 *	    of each sample.  This is an idempotent operation.  That
 *	    is, applying this program to its output will produce the
 *	    same samples exactly (so you can compare input and output
 *	    using cmp). 
 */

/* Here is a test shell script for this program.  It needs b.snd to exist.
   If you detect a bug, make the frame size very small (e.g. 2 or 4)
   and see if it still happens. */
/*
	sndproc_ola b.snd b2.snd
	sndproc_ola b2.snd b3.snd
	cmp b2.snd b3.snd
	od -i b3.snd | head | openfile
	od -i b2.snd | head | openfile
*/

#include <stdio.h>
#include <mach/mach_error.h>
#include <sound/sound.h>
#include <string.h>		/* bcopy */
#include <c.h>			/* MAX,MIN */

/* Constants */
#define	FRAME_SIZE 4096			/* one page of shorts */
#define	FRAME_STEP (FRAME_SIZE >> 1)	/* frame step size */
#define	OVERLAP_SIZE	(FRAME_SIZE-FRAME_STEP)

static void	processSound(SNDSoundStruct *inputSound,
			    SNDSoundStruct **outputSound);
static void	processFrame(int frameSize,short *inPtr,short *outPtr);
static void	addShortArrays(short *dest, short *source, int size);
static void	copyShortArrays(short *dest, short *source, int size);
static void	convertToMono(SNDSoundStruct **theSound);
static void	soundErrorExit();
static void	machErrorExit();

/* Static global variables */
static int	soundError;	/* used by soundErrorExit() macro */
static int	machError;	/* used by machErrorExit() macro */
static char	*programName;


/*
 * processFrame
 */
static void processFrame(int frameSize,short *inPtr,short *outPtr)
{
    int i;
    static int later = 0;
    if (later)
      for (i=0; i<frameSize; i++)
	*outPtr++ = (*inPtr++ >> 1); /* copy * 1/2 */
    else { /* all this is to enable "cmp" to compare input and output */
	for (i=0; i<FRAME_STEP; i++)
	  *outPtr++ = *inPtr++ & ~1;   /* clear low-order bit */
	for (i=0; i<(frameSize - FRAME_STEP); i++)
	  *outPtr++ = (*inPtr++ >> 1); /* copy * 1/2 */
	later++;
    }
}


/*
 * main
 */
void main(int argc, char *argv[])
{
    SNDSoundStruct	*inputSound, 		*outputSound;
    char		*inputSoundFile,	*outputSoundFile;
    long                nsamps;
    short               *ptr;

    /* Check arguments */
    programName = argv[0];
    if (argc != 3) {
        fprintf(stderr, "Usage: %s <inputSoundFile> <outputSoundFile>\n",
	        programName);
	exit(1);
    }
    inputSoundFile  = argv[1];
    outputSoundFile = argv[2];
    	
    /* Read sound file */
    if (soundError = SNDReadSoundfile(inputSoundFile, &inputSound))
	soundErrorExit();
    /* Abort if unsupported format */
    if (inputSound->dataFormat != SND_FORMAT_LINEAR_16) {
        fprintf(stderr, "%s: unsupported sound format: %d\n",
	    programName, inputSound->dataFormat);
	exit(1);
    }

    /* convert to host architecture before processing */
    ptr = (short *) ((char *) inputSound + inputSound->dataLocation);
    nsamps = inputSound->dataSize / (sizeof(short) * inputSound->channelCount);
    SNDSwapSoundToHost(ptr, ptr, nsamps, inputSound->channelCount,
		       inputSound->dataFormat);

    /* Convert to mono if required */
    if (inputSound->channelCount > 1) {
        fprintf(stderr, 
		"%s:  Warning: averaging channels "
		"of a %d-channel sound\n", 
		programName,inputSound->channelCount);
	convertToMono(&inputSound);
    }
    
    /* Filter by applying window in the frequency domain */
    processSound(inputSound, &outputSound);
    
    /* Write sound file */
    ptr = (short *) ((char *) outputSound + outputSound->dataLocation);
    nsamps = outputSound->dataSize / (sizeof(short)*outputSound->channelCount);
    SNDSwapHostToSound(ptr, ptr, nsamps, outputSound->channelCount,
		       outputSound->dataFormat);
    if (soundError = SNDWriteSoundfile(outputSoundFile, outputSound))
	soundErrorExit();
}


/*
 * processSound
 *	Apply a "black box" to the input sound
 *	in the frequency domain and write results to the output sound.
 */
static void	processSound(SNDSoundStruct *inputSound,
			    SNDSoundStruct **outputSound)
{
    int		size     = inputSound->dataSize;
    int		format   = inputSound->dataFormat;
    int		rate 	 = inputSound->samplingRate;
    int		channels = inputSound->channelCount;
    short 	*inPtr, *outPtr, *inFramePtr, *outFramePtr, *frameBuffer;
    int		numFrames, frameSize, sizeDone=0;
    
    /* Allocate a new sound (uses vm_allocate() => data is pre-zeroed) */
    if (soundError = SNDAlloc(outputSound, size, format, rate, channels, 4))
	soundErrorExit();
	
    size /= sizeof(short);
    inPtr  = (short *) ((char *) inputSound   + inputSound->dataLocation);
    outPtr = (short *) ((char *) *outputSound + (*outputSound)->dataLocation);

    /* Use vm_allocate() to get page alignment and initial zeros */
    machError = vm_allocate(task_self(),
			    (pointer_t *)&frameBuffer,
			    FRAME_SIZE * sizeof(short),1);
    if (machError != KERN_SUCCESS)
      machErrorExit();

    numFrames = (size + FRAME_STEP - 1) / FRAME_STEP;

    /* For each frame, download, filter, upload, and overlap-add into output */
    for (sizeDone=0, inFramePtr=inPtr, outFramePtr=outPtr; 
	 sizeDone < size; 
	 sizeDone+=FRAME_STEP, inFramePtr+=FRAME_STEP, 
	 outFramePtr+=FRAME_STEP)
    {
	frameSize = MIN(FRAME_SIZE,(size-sizeDone));
        processFrame(frameSize,inFramePtr,frameBuffer);
	addShortArrays(outFramePtr, frameBuffer, OVERLAP_SIZE);
	copyShortArrays(outFramePtr+OVERLAP_SIZE, 
			frameBuffer+OVERLAP_SIZE, FRAME_STEP);
    }

    /* All sound signals should start and end at 0 amp to avoid driver ramp */
    *outPtr = *(outPtr+size-1) = 0;
}

/* Add source array to dest array, results in dest */
static void	addShortArrays(short *dest, short *source, int size)
{
    int i;
    
    for (i = 0; i < size; i++)
        dest[i] += source[i];
}

/* Copy source array to dest array */
static void	copyShortArrays(short *dest, short *source, int size)
{
    bcopy((char *)source, (char *)dest, size * sizeof(short));
}

/* Convert multi-channel sound to mono by tossing extra channels */
static void	convertToMono(SNDSoundStruct **theSound)
{
    SNDSoundStruct *newSound;
    short 	*oldPtr, *newPtr;
    int		size     = (*theSound)->dataSize;
    int		format   = (*theSound)->dataFormat;
    int		rate 	 = (*theSound)->samplingRate;
    int		channels = (*theSound)->channelCount;
    int		newSize, i, j, k, s, scl;
    
    newSize = size/channels;
    if (soundError = SNDAlloc(&newSound, newSize, format, rate, 1, 4))
	soundErrorExit();
    oldPtr  = (short *) ((char *) *theSound + (*theSound)->dataLocation);
    newPtr  = (short *) ((char *) newSound + newSound->dataLocation);
#define SCALE_BITS 10
    scl = (1<<SCALE_BITS)/channels;
    for (i = 0, j = 0; i < newSize/sizeof(short); i++, j+=channels) {
	for (k=0, s=0; k<channels; k++)
	  s += (int)oldPtr[j+k];
	s *= scl;
	newPtr[i] = s >> SCALE_BITS;
    }
    if (soundError = SNDFree(*theSound))
	soundErrorExit();
    *theSound = newSound;
}


/* Sound error handling macro */
void soundErrorExit() {
    fprintf(stderr, "%s: sound error: %s\n", programName, 
	    SNDSoundError(soundError));
    exit(1);
}

/* Mach error handling macro */
void machErrorExit() {
    fprintf(stderr, "%s: mach error: %s\n", programName, 
	    mach_error_string(machError)); 
    exit(1); 
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.