ftp.nice.ch/pub/next/unix/audio/Srconv.s.tar.gz#/Srconv/srconv.c

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

// Srconv, a sampling rate conversion program by J. Laroche. January 1992
// Version 3.0

// Added to 1.0: additional useful features for converting to or from binary 
// files (no header), selecting right or left channel, swaping octects for 
// compatibility with PCs and DEC machines...
// Also, "fixed" a problem with large factor conversions (when the cut-off
// frequency gets negative!)

// Added to 2.0: DMA to DSP. The transfer is now faster and doesn't block
// the NeXT while doing the conversion (like 1.0 and 2.0 used to). Also added:
// conversion from stereo to mix, choice of filter length according to the
// sampling rate conversion (when playing), general optimizing and rewriting.
// The DSP file is now loaded from the Mach_O segment: srconv will work 
// anywhere.
// When reading from a binary file, the file is mapped into memory, so the 
// access is faster, and playing starts almost as soon as you hit return.


#import <sound/sound.h>
#import <sound/sounddriver.h>
#import <streams/streams.h>
#include <sys/file.h>
#include <defaults.h>
#import <mach.h>
#import <math.h>
#import <stdio.h>
#include <strings.h>
#include <libc.h>

#define Error(A,B) if((A)) {fprintf(stderr,"%s\n",B); exit(0);}


//#define DMASIZE 1024
//#define MEMMAX 6000
#define DMASIZE 2048
#define MEMMAX 4000
#define WRITE_TAG 1
#define READ_TAG 2

// MEMMAX depends on how much memory you have on your DSP.
// These are default values. If you have more memory than 8Kwords, 
// you should add the difference (in words) to MEMMAX, which would enable
// longer filters, and therefore higher ratios. (MEMMAX is the amount of
// DSP memory dedicated to the filter coefficients.) offset is the offset
// value for cutoff frequency. It also depends on the length of the filter.


static int done;
static short *read_data;
static int read_count;

static void recorded_data(void *arg, int tag, void *p, int nbytes)
{
    if(tag != READ_TAG) return;
    read_data = (short *)p;
    read_count = nbytes;
    done = 1;
}

static void read_completed(void *arg, int tag)
{
    done = 2;
}

static void play_underrun(void *arg, int tag)
{
    printf("The driver is running behind... aborting \n");
    done = 3;
}

int max_order;



void main (int argc, char *argv[])
{
    static port_t dev_port, owner_port,cmd_port;
    static port_t reply_port, read_port, write_port;
    int i, j, protocol;
    kern_return_t k_err;
    SNDSoundStruct *sound;
    SNDSoundStruct *converted;
    char *file[10];
    SNDSoundStruct *dspStruct;
    snddriver_handlers_t handlers = { 0, 0, 0, read_completed, 0, 0, 0,
    					play_underrun, recorded_data};
    msg_header_t *reply_msg;
    NXStream *mapfile = 0;
    int low_water = 48*1024;
    int high_water = 512*1024;
    short *location;
    int length;
    int stereo = 0;
    int PLAY = 1;
    int verbose = 0;
    int real = 1;
    int BINARY = 0;
    int BINARYIN = 0;
    int SWAPIN = 0;
    int SWAPOUT = 0;
    int CHANNEL = 2;
    int STEREO = 0;
    int U = 0, D = 0, K = 0, Filter_length = 0;
    int para[4],rat[2];
    int	*filtre;
    float cut, thresh, offset;
    int S_rateIn, S_rateOut, S=0;
    short *foo;
    int nb_iteration,inc,start;
    void cal_filtre();
    void fract();
    void usage();
    int testfile();


/////////////// Initialize parameters, and scan input line //////////////////

    thresh = 0.003;
    offset=0.02;
    max_order = (int) (MEMMAX / 150);
    
    for(i=1,j=0;i<argc;i++)
    switch(argv[i][0])
    {
	case '-' : switch(argv[i][1])
	    {
		case 'v' : verbose = 1; break ;
		case 'U' : U = atoi(argv[i]+2); break ;
		case 'D' : D = atoi(argv[i]+2); break ;
		case 'K' : K = atoi(argv[i]+2); break ;
		case 'S' : S = atoi(argv[i]+2); break ;
		case 't' : thresh = atof(argv[i]+2); break ;
		case 'o' : offset = atof(argv[i]+2); break ;
		case 'r' : real = 0; break ;
		case 's' : 
		    if(argv[i][2] == 'i') SWAPIN = 1;
		    if(argv[i][2] == 'o') SWAPOUT = 1; break ;
		case 'b' : BINARY = 1; break ;
		case 'c' : switch(argv[i][2])
		{
		    case 'l' : CHANNEL = 0 ; break ;
		    case 'r' : CHANNEL = 1 ; break ;
		    case 's' : CHANNEL = 2 ; break ;
		    case '+' : CHANNEL = 3 ; break ;
		    default  : usage();
		} break ;
		default  : usage();
	    } break ;
	default : file[j++] = argv[i]; break ;
    }

    if(j == 2) PLAY = 0;
    if(PLAY == 1 && j == 0) usage();


/////////////// Initialize Hardware, and read sound file //////////////////
        
    k_err = SNDAcquire(SND_ACCESS_OUT|SND_ACCESS_DSP,0,0,0,
    	NULL_NEGOTIATION_FUN,0,&dev_port,&owner_port); 
    Error(k_err,"DSP Busy, or access to device denied\n");
    k_err = SNDReset(SND_ACCESS_OUT|SND_ACCESS_DSP,dev_port,owner_port); 
    Error(k_err,"Problem during reset\n");
    
    k_err = snddriver_get_dsp_cmd_port(dev_port,owner_port,&cmd_port);

	// Here's an important setting. It appears that this size make the 
	// whole thing work. others don't. I don't know why, but it has
	// something to do with the ramp created by the snd driver.
    k_err =  snddriver_set_sndout_bufsize(dev_port,owner_port,2048);
    Error(k_err,"Buf Size  ");
    
    

    k_err = SNDReadSoundfile(file[0], &sound);
    if(k_err)
    {
	mapfile = NXMapFile((const char*) file[0], NX_READONLY);
	Error((mapfile == 0),"Couldn't locate/read file");
	if (verbose) printf("converting from a binary file ...\n");
	NXSeek(mapfile, 0, NX_FROMEND);
	length = NXTell(mapfile)/sizeof(short);
	NXSeek(mapfile, 0, NX_FROMSTART);
	k_err = SNDAlloc(&sound,0,SND_FORMAT_LINEAR_16,
	((S==0) ? 44100 : S),1,4);
	S=0;
	Error(k_err,"SND Allocation");
	SNDGetDataPointer(sound,(char**)&location,&i,&i);
	sound->dataSize = length*sizeof(short);
	BINARYIN = 1;
    }
    else
    k_err = SNDGetDataPointer(sound,(char**)&location,&length,&i);
    
    Error(sound->dataFormat != SND_FORMAT_LINEAR_16,
    "Bad format: I need 16 bits linear");
    if(PLAY && S) {sound->samplingRate = S; S = 0 ;}
    

/////////////// Calculate Sampling rate conversion param. //////////////////
    
    S_rateIn = sound->samplingRate;
    if(S==0 && U == 0 && D == 0 && PLAY) S = 44100;
    if(S!=0) 
    {
	fract((float)S_rateIn/(float)S,rat,thresh);
	D = rat[0];
	U = rat[1];
    }
    if(U==0) U = 1;
    if(D==0) D = 1;
    if(U > 100 || D > 100)
	Error(1,"Unable to convert: values of U or D too large.\n");
    if(U > max_order || D > max_order)
	printf("Warning! U and D should not be larger than %d.\n", max_order);
    
    S_rateOut = (int) floor((float) S_rateIn * (float)U / (float)D);



///////////// Examine sound file, set stereo conversion param. ///////////////
	
    if(CHANNEL != 2 && sound->channelCount == 2)
    {
	if(verbose) printf("Converting to a mono sound...\n");
	inc = 2; start = CHANNEL;
	if(CHANNEL == 3) start = 0;
	sound->channelCount = 1;
	sound->dataSize = sound->dataSize/2;
	length /= 2;
    }
    else
    {inc = 1 ; start = 1; CHANNEL = 0;}
    
    stereo = (sound->channelCount == 2);
    
    if(verbose) printf("%s a %s sound, %d samples\n",
    	((PLAY)?"Playing":"Converting"),((stereo)?"stereo":"mono"), length);
    
    if(verbose && !PLAY) printf("From %d Hz to %d Hz\n",S_rateIn,S_rateOut);
    if(verbose && PLAY) printf("Sound sampling rate: %d Hz\n",S_rateIn);


/////////////// Calculate FIR Filter characteristics //////////////////
    
    if(PLAY && K == 0)
	K = MIN((int)(40*(2-stereo)*U/D), MIN(MEMMAX/U,80));

    else if(K == 0) K = (int) MEMMAX / U;
    K = MIN(K,250);    
    if(verbose) 
    printf("Filter Length: %d,  Up-factor %d,  Down-factor %d\n",K,U,D);
    
    Filter_length = (((K*U) % 2)? K*U : K*U+1);

    para[0] = U;
    para[1] = D;
    para[2] = K-1;
    para[3] = Filter_length;
 
 
    
/////////////// Initialize sound and dsp driver //////////////////
    
        
    protocol = SNDDRIVER_DSP_PROTO_RAW;
    k_err = snddriver_stream_setup(dev_port, owner_port,
			SNDDRIVER_DMA_STREAM_TO_DSP,
			DMASIZE, 2, 
			low_water, high_water,
			&protocol, &write_port);
    Error(k_err,"Stream  ");
    k_err = snddriver_stream_setup(dev_port, owner_port,((PLAY)?
			SNDDRIVER_STREAM_DSP_TO_SNDOUT_44 : 
			SNDDRIVER_STREAM_FROM_DSP),
			DMASIZE, 2, 
			low_water, high_water,
			&protocol, &read_port);
    Error(k_err,"Stream  ");

    k_err = snddriver_dsp_protocol(dev_port, owner_port, protocol);   
    k_err = port_allocate(task_self(),&reply_port);



/////////////// Get DSP program and boot DSP. //////////////////
    
    dspStruct = (SNDSoundStruct *)getsectdata("__SND", "srconv.snd",&i);
    k_err = SNDBootDSP(dev_port, owner_port, dspStruct);
    Error(k_err,"Can't boot DSP!");




/////////////// Send stereo param and filter coeff to DSP. //////////////////
    
    STEREO = (((stereo) ? 1 : 0) + 2*((PLAY) ? 1 : 0));
    k_err = snddriver_dsp_write(cmd_port,&STEREO,1,sizeof(int),
   						SNDDRIVER_LOW_PRIORITY);
    
    filtre = (int*) calloc(Filter_length,sizeof(int));
    cut = 3.14159265 * (1/(float) MAX(U,D) - offset);
    if(cut <= 0) 
    {
	printf("Problem with the filter's cut-off frequency because U or D \
are too large, \nyou might get aliasing\n");
	cut = 3.14159265 * (1/(float) MAX(U,D));
    }
    cal_filtre(filtre, Filter_length, cut, U);
    
    k_err = snddriver_dsp_write(cmd_port,para,4,sizeof(int),
   						SNDDRIVER_LOW_PRIORITY);
    k_err = snddriver_dsp_write(cmd_port,filtre, Filter_length,sizeof(int),
   						SNDDRIVER_LOW_PRIORITY);



/////////////// Allocate virtual memory for DMA_IN //////////////////
    

    vm_allocate(task_self(),(vm_address_t *)(&foo),8*2*vm_page_size,TRUE);
    Error(k_err,"VM Allocation  ");
    
	
    nb_iteration = length/vm_page_size/8;
    


////////////// Send DMA buffers, while doing stereo conversion ////////////////
    
    if(!PLAY)
    {
	k_err = snddriver_stream_start_reading(read_port,0, 
	    length*U/D,READ_TAG,1,0,0,0,0,0, reply_port);
    }
    
    for(i=0,location = location + start ;i<nb_iteration;i++)
    {
    int max = 8*vm_page_size;
    
	if(BINARYIN)
	{
	k_err = NXRead(mapfile, (void*)foo, max*sizeof(short));
	}
	else
	{
	    if(CHANNEL == 3)
	    for(j=0;j<max;j++,location++)
		foo[j] = *(location) + *(++location);
	    else
	    for(j=0;j<max;j++,location += inc)
		foo[j] = *(location);
	}
	if(SWAPIN) swab((char*)foo,(char*)foo, 8*vm_page_size*sizeof(short));
	
	k_err = snddriver_stream_start_writing(write_port,(void *)foo,
	    8*vm_page_size,WRITE_TAG,0,0,0,0,0,0,0,0, reply_port);
	Error(k_err,"start writing  ");
    }
    
    i = length - nb_iteration * 8 *vm_page_size;
    if(BINARYIN)
    {
    k_err = NXRead(mapfile, (void*)foo, i*sizeof(short));
    }
    else
    {
	if(CHANNEL == 3)
	for(j=0;j<i;j++,location++)
	    foo[j] = *(location) + *(++location);
	else
	for(j=0;j<i;j++,location += inc)
	    foo[j] = *(location);
    }
    if(SWAPIN) swab((char*)foo,(char*)foo, 8*vm_page_size*sizeof(short));

    for(j = i;j<8*vm_page_size;j++,location += inc) 
	foo[j] = 0;
		
    if(PLAY)
    k_err = snddriver_stream_start_writing(write_port,(void *)foo,
	MIN((int)(i/DMASIZE + 1) * DMASIZE, 8*vm_page_size)
	 ,WRITE_TAG,0,0,0,1,0,0,0,0, reply_port);
    else
    k_err = snddriver_stream_start_writing(write_port,(void *)foo,
	8*vm_page_size,WRITE_TAG,0,0,0,1,0,0,0,0, reply_port);
    Error(k_err,"start writing  ");
    
    
/////////////// Wait until calculations completed ////////////////
    
    reply_msg = (msg_header_t *)malloc(MSG_SIZE_MAX);
    done = 0;

    while (done != 1) 
	{
	    reply_msg->msg_size = MSG_SIZE_MAX;
	    reply_msg->msg_local_port = reply_port;
	    k_err = msg_receive(reply_msg, MSG_OPTION_NONE, 0);
	    k_err = snddriver_reply_handler(reply_msg,&handlers);
	    if(PLAY && done == 2) { done = 1; usleep(250000); }
	}
    SNDRelease(SND_ACCESS_OUT|SND_ACCESS_DSP,dev_port,owner_port); 
    vm_deallocate(task_self(),(pointer_t)foo, 8*2*vm_page_size);
    if(PLAY) exit(0);


/////////////// Do some processing on the received data ////////////////

    if(SWAPOUT && BINARY)
    {
	swab((char*)read_data,(char*)read_data, length*U/D*sizeof(short));
	if(verbose) printf("Swapping octets...\n");
    }
    
    
/////////////// Write the sound into the output file ////////////////

    if(verbose) printf("Copying the sound...\n");
    if(BINARY)
    {
	int output_file;
	output_file = creat(file[1],0644);
	write(output_file,read_data, length*U/D*sizeof(short));
    }
    else
    {
	k_err = SNDAlloc(&converted,2*length*U/D, sound->dataFormat, 
	((real == 0 && S != 0) ? S : S_rateOut), sound->channelCount,4);
	Error(k_err,"SND Allocation");    
	k_err = SNDGetDataPointer(converted,(char**)&location,&i,&i);
	    
	for(i=0;i<length*U/D;i++)
	    *location++ = *read_data++;
	k_err = SNDWriteSoundfile(file[1], converted);
	Error(k_err,"Write Sound");
    }
    vm_deallocate(task_self(),(pointer_t)read_data,read_count);
}




// Calculates FIR low-pass filter by windowing a sinc with a hamming window,
// and normalizing.

void cal_filtre(filtre, order, freq_cut, P)
int *filtre;
int order;
float freq_cut;
int P;
{
    int i;
    float scaler;
    float aux;
    for(i=1,scaler = 0;i<order/2;i++)
    {
        aux = (sin(freq_cut * i) / freq_cut / i 
	* (0.54 + 0.46 * cos(6.28318530*i/(order-2))));
	filtre[order/2+i] = filtre[order/2-i] = 8388607 * aux ;
	scaler += 2 * aux;
   }
    scaler = P * 0.9 / (1 + scaler);
    filtre[order/2] = 8388607 * scaler;
    for(i=1;i<order/2;i++)
        filtre[order/2+i] = filtre[order/2-i] = filtre[order/2-i]*scaler;
}


/*  fract(x,rat) returns in rat a ratio that best approximates the real
x with an relative error less than thresh, making sure U and D are less than
100. Thresh is equal to 0.03, the auditory threshold for pitch sensation.
*/ 

void fract(x,rat,thresh)
float x;
int *rat;
float thresh;

{
int i=0,j;
float error = 1;
float reste;
int *A, *p, *q;
int prev_p = 1, prev_q = 1;

A = (int*) calloc(20,sizeof(int));
p = (int*) calloc(20,sizeof(int));
q = (int*) calloc(20,sizeof(int));

reste = x;

while(error/x > thresh)
{

    A[i] = floor(reste);
    reste = 1 / (reste - A[i]);
    for(j=i, p[i]=1, q[i]=A[i]; j>0; j--)
    {
	p[j-1] = q[j];
	q[j-1] = A[j-1] * q[j] + p[j];
    }
    error = fabs((float)q[0]/(float)p[0] - x);
    if(p[0] < max_order && q[0] < max_order)
    {
	prev_p = p[0];
	prev_q = q[0];
    }
    else
    {
    printf("Warning, difficult conversion: ideal factors U = %d, D = %d,\n",
	p[0],q[0]);
	p[0] = prev_p;
	q[0] = prev_q;
	error = fabs((float)q[0]/(float)p[0] - x);
	printf("Actual factors: U = %d, D = %d, \
Resulting Sampling rate error: %.2f %%\n",p[0],q[0],100*error/x);
	break;
    }
    i++;
}
rat[0] = q[0];
rat[1] = p[0];
}
        
void usage()
{
    printf("Usage:  srconv \t-[UDSKtovsosibcr] sound1 [sound2] \n\t\t\
    -S:Output sampling rate, Input sampling rate if playing\n\t\t\
    -U:Up-sampling factor\n\t\t\
    -D:Down-sampling factor\n\t\t\
    -K:filter-Order\n\t\t\
    -t:Sampling rate accuracy (default 0.003) \n\t\t\
    -o:Cut-off frequency offset (default 0.02) \n\t\t\
    -v:verbose: indicates conversion parameters\n\t\t\
    -so: outputs swaped octets (for PCs and DECs)\n\t\t\
    -si: swaps octets on input (for PCs and DECs)\n\t\t\
    -b:binary: outputs a regular binary file (no header)\n\t\t\
    -c:channels: s:stereo, l:left only, r:right only, +:sum \n\t\t\
    -r:forces the header of the output sound to the sampling \n\t\t\
    rate you specified instead of the approximated value\n\t\
Example: \n\tTo convert: \tsrconv -S48000 file.snd out.snd\n\t\
    \t\tsrconv -U7 -D2 file.snd out.snd\n\t\
To play: \tsrconv file.snd\n");
    exit(0);

}

    
    

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