ftp.nice.ch/pub/next/unix/audio/Record.s.tar.gz#/Record/record.c

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

// Record, a sampling rate conversion program by J. Laroche. January 1992
// Version 2.0

// Record makes it possible to record sounds from the AD64X converters at any
// arbitrary sampling rate. It uses a simple, real-time conversion algorithm
// to convert the sound to the desired sampling rate.
// Record can record either from analog (-a flag) or digital (-d flag) input.
// The simplest way to use record is to choose the final sampling rate, and
// let the software choose the AD64X sampling rate that makes the conversion
// the most easy. For example:
// record /tmp/soundfile.snd -S16000 
// will set the AD64X sampling rate to 32000 and downsample by a factor two.
// You can force the AD64X sampling rate using the -a flag (-al for 32000,
// -am for 44100 and -ah for 48000).
// When recording from digital input, the sampling rate is determined by the
// digital device connected to the AD64X. You NEED to specify its sampling 
// rate with the -dl -dm or -dh flags (same meaning as before).
// You can record only one channel using the -c flag: -cs for stereo. 
// -cr for right channel, -cl for left channel, and -c+ for mix.

#define DMASIZE 2048
#define MEMMAX 4000
#define WRITE_TAG -1
#define READ_TAG -2
#define	MAX_RECORD_LENGTH 10	// Max length in MBytes of recorded sound
#define	MAX_SR_TODISK 32000	// Max Sampling rate for mono direct to disk.

// 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.

#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 <signal.h>
#include <libc.h>

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

#define SR_HIGH 3
#define SR_MED 2
#define SR_LOW 1

#define DIGIT 0
#define ANALOG 1

#define LEFT 	0
#define RIGHT 	1
#define STEREO 	2
#define SUM 	3


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

static void recorded_data(void *arg, int tag, void *p, int nbytes)
{
static int lastTag = 0;

    if (lastTag++ != tag && tag != READ_TAG)
    {
	fprintf(stderr,"\nLost some samples! Exiting...\n");
	endrec=1;
	done = 1;
	read_count = 0;
	return;
    }
    read_data = (short *)p;
    read_count = nbytes;
    done = 1;
}

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

int max_order;
float resize = 0.9;
static port_t reply_port, read_port;

void main (int argc, char *argv[])
{
    static port_t dev_port, owner_port,cmd_port;
    int i, j, protocol;
    char a;
    kern_return_t k_err;
    SNDSoundStruct *converted;
    char *file[100];
    char error_string[200];
    SNDSoundStruct *dspStruct;
    snddriver_handlers_t handlers = { 0, 0, 0, read_completed, 0, 0, 0,
    					0, recorded_data};
    msg_header_t *reply_msg;
    int low_water = 48*1024;
    int high_water = 512*1024;
    float flength = 0;
    int length = 0;
    int stereo = 0;
    int verbose = 0;
    int real = 1;
    int BINARY = 1;
    int DISK = 0;
    int INPUT = ANALOG;
    int SWAPOUT = 0;
    int CHANNEL = STEREO;
    int SR = -1;
    int U = 0, D = 0, K = 0, Filter_length = 0;
    int para[4],rat[2];
    int	*filtre;
    float cut, thresh, offset;
    int S_rateIn = 0, S_rateOut, S=0;
    void cal_filtre();
    void fract();
    void usage();
    void usage2();
    void stoprec();
    int output_file;


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

    thresh = 0.003;
    offset=0.005;
    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 'h' : usage2();
		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 'L' : flength = atof(argv[i]+2); break ;
		case 'A' : DISK = 1; break ;
		case 'w' : resize = atof(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 'd' : INPUT = DIGIT;
		    if(argv[i][2] == 'l') SR = SR_LOW; /* SSI setup: Low*/
		    if(argv[i][2] == 'm') SR = SR_MED; /* SSI setup: Med*/
		    if(argv[i][2] == 'h') SR = SR_HIGH; break ; /* High */
		case 'a' : INPUT = ANALOG;
		    if(argv[i][2] == 'l') SR = SR_LOW; /* SSI setup: Low*/
		    if(argv[i][2] == 'm') SR = SR_MED; /* SSI setup: Med*/
		    if(argv[i][2] == 'h') SR = SR_HIGH; break ; /* High */
		case 's' : SWAPOUT = 1; break ;
		case 'b' : BINARY = 0; break ;
		case 'c' : switch(argv[i][2])
		{
		    case 'l' : CHANNEL = LEFT ; break ;
		    case 'r' : CHANNEL = RIGHT ; break ;
		    case 's' : CHANNEL = STEREO ; break ;
		    case '+' : CHANNEL = SUM ; break ;
		    default  : usage();
		} break ;
		default  : usage();
	    } break ;
	default : file[j++] = argv[i]; break ;
    }

    if(j > 1) Error(1,"Only one soundfile name, please");
    if(j == 0) usage();

    output_file = creat(file[0],0644);
    sprintf(error_string,"Couldn't write to file \"%s\", protection?path?ownership problem.", file[0]);
    Error((output_file == -1), error_string);

/////////////// 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);
    k_err =  snddriver_set_sndout_bufsize(dev_port,owner_port,2048);
    
    

    

/////////////// Calculate Sampling rate conversion param. //////////////////
    


    if(SR == -1)
    { 
	if(INPUT == DIGIT) SR = SR_HIGH;
	else if(INPUT == ANALOG && S != 0)
	{
	    int comp[3];
	    
	    i = 0;
	    fract((float)32000/(float)S,rat,thresh,0); 
		comp[0] = MAX(rat[0],rat[1]);	
	    fract((float)44100/(float)S,rat,thresh,0); 
		comp[1]= MAX(rat[0],rat[1]);
		if(comp[1] <  comp[0]) {i = 1 ; comp[0] = comp[1];}	
	    fract((float)48000/(float)S,rat,thresh,0); 
		comp[2]= MAX(rat[0],rat[1]);
		if(comp[2] <  comp[0]) i = 2 ;
	    
	    switch(i)
	    {
		case 0 : SR = SR_LOW ; break ;
		case 1 : SR = SR_MED ; break ;
		case 2 : SR = SR_HIGH ; break ;
	    }
	}
	else if(INPUT == ANALOG && S == 0) SR = SR_HIGH;
    }
    
    switch(SR)
    {
	case SR_HIGH :  S_rateIn = 48000; break;
	case SR_MED :  S_rateIn = 44100; break;
	case SR_LOW :  S_rateIn = 32000; break;
    }
    
    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. ///////////////
	
    stereo = (CHANNEL == STEREO);
    
    if(verbose)
    {
    printf("Recording from A/D64X:\nSource:\t\t%s \n",
    ((INPUT == DIGIT)?"Digital":"Analog"));
    printf("Sampling rate: \t%d Hz\n", S_rateOut);
    switch(CHANNEL)
    {
	case STEREO : printf("Mode: \t\tStereo\n"); break ;
	case LEFT : printf("Mode: \t\tMono left\n"); break ;
	case RIGHT : printf("Mode: \t\tMono right\n"); break ;
	case SUM : printf("Mode:\t\tMono left + right\n"); break ;
    }
    if(flength) printf("Length: \t%.2f seconds\n",flength);       
    printf("AD64X quartz: \t%d Hz\n",S_rateIn);
    if(SWAPOUT)
    printf("Swapping bytes\n");
    }


/////////////// Calculate FIR Filter characteristics //////////////////
    
    if(U == 1 && D == 1) K = 3;
    if(K == 0)
	K = MIN((int)(1800000/S_rateOut*(2-stereo)), MIN(MEMMAX/U,120));
    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,((0)?
			SNDDRIVER_STREAM_DSP_TO_SNDOUT_44 : 
			SNDDRIVER_STREAM_FROM_DSP),
			DMASIZE, 2, 
			low_water, high_water,
			&protocol, &read_port);
    Error(k_err,"Stream setup impossible?! ");

    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", "record.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. //////////////////
//
//	Int stereo:
//
//	Bits		0	1	2	3	4	5
//	stereo		1	0	1	-	-	-
//	mono left	0	1	0	-	-	-
//	mono right	0	0	0	-	-	-
//	mono sum	1	0	0	-	-	-
//	Sr = high	-	-	-	1	0	-
//	Sr = med	-	-	-	0	1	-
//	Sr = low	-	-	-	0	0	-
// 	Input Analog	-	-	-	-	-	0
// 	Input Digital	-	-	-	-	-	1
///////////////////////////////////////////////////////////////////////////

    
    stereo = (CHANNEL == STEREO ||  CHANNEL == SUM)
    	+ 2*(CHANNEL == LEFT) 
	+ 4*(CHANNEL == STEREO) 
	+ 8*(SR == SR_HIGH) 
	+ 16*(SR == SR_MED)
	+ 32*(INPUT == DIGIT) ;
    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));
    }
    if(U == 1 && D == 1)
    {
	filtre[0] = filtre[2] = 0;
	filtre[1] = 8388607;
    }
    else 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);



///////////////////////////// Start recording /////////////////////////////
    
    
    printf("\nHit return to start recording\n");
    scanf("%c",&a);
    if((DISK == 0) && (S_rateOut*(1+(CHANNEL==STEREO)) <= MAX_SR_TODISK)
    	&& flength > 10) 
    	DISK = 1;


///////// If recording is done in two times (recording, then writing) /////////

if(DISK == 0)
{
    if(flength != 0)
    {
    length = flength * S_rateOut * ((CHANNEL == STEREO)? 2 : 1);
    length = MIN(length, MAX_RECORD_LENGTH*1000000/2);
    k_err = snddriver_stream_start_reading(read_port,0, 
	length,READ_TAG,1,0,0,0,0,0, reply_port);
    }
    else
    {
    k_err = snddriver_stream_start_reading(read_port,0, 
	MAX_RECORD_LENGTH*1000000/2,READ_TAG,1,0,0,0,0,0, reply_port);
    Error(k_err,"Problem occurred during Stream Reading!");
    }
    k_err = snddriver_dsp_host_cmd(cmd_port,20,SNDDRIVER_LOW_PRIORITY);
    
    if(length == 0)
    {    
    	printf("\nHit return to stop recording \n");
	scanf("%c",&a);
    k_err = snddriver_stream_control(read_port, 0, SNDDRIVER_AWAIT_STREAM);
    }

    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);
	}
    SNDRelease(SND_ACCESS_OUT|SND_ACCESS_DSP,dev_port,owner_port);
    if(length == 0) length = read_count/sizeof(short);
 


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

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

    if(verbose) printf("Copying the sound...\n");
    if(BINARY)
    {
	write(output_file,read_data, length*sizeof(short));
    }
    else
    {
	k_err = SNDAlloc(&converted,10, SND_FORMAT_LINEAR_16, 
	((real == 0 && S != 0) ? S : S_rateOut), 
	((CHANNEL == STEREO) ? 2 : 1),4);
	Error(k_err,"Couldn't alloc sound");    
	converted->dataSize = length*sizeof(short);
	write(output_file,(char*) converted, sizeof(SNDSoundStruct));
	write(output_file,read_data, length*sizeof(short));
    }
    vm_deallocate(task_self(),(pointer_t)read_data,read_count);
}

/////////////////////// If recording is direct-to-disk //////////////////////

else
{
int BUFFERSIZE = 32*DMASIZE;
int receivedLength = 0;
int j;

    if(!flength) printf("\nHit ^C to stop recording \n");
    signal(SIGINT,  stoprec); /* To handel ^C */
    length = flength * S_rateOut * ((CHANNEL == STEREO)? 2 : 1);
    if(BINARY == 0)
    {
	k_err = SNDAlloc(&converted,10, SND_FORMAT_LINEAR_16, 
	((real == 0 && S != 0) ? S : S_rateOut), 
	((CHANNEL == STEREO) ? 2 : 1),4);
	Error(k_err,"Couldn't alloc sound");    
	converted->dataSize = length*sizeof(short);
	write(output_file,(char*) converted, sizeof(SNDSoundStruct));
    }
    
    for(j=0;j<8;j++)	// Get some headroom!
    k_err = snddriver_stream_start_reading(read_port,0, 
	BUFFERSIZE,j,1,0,0,0,0,0, reply_port);
    k_err = snddriver_dsp_host_cmd(cmd_port,20,SNDDRIVER_LOW_PRIORITY);
    
    i = 0;
    endrec = 0;
    reply_msg = (msg_header_t *)malloc(MSG_SIZE_MAX);
    while(endrec == 0)
    {
	k_err = snddriver_stream_start_reading(read_port,0, 
	    BUFFERSIZE,j++,1,0,0,0,0,0, reply_port);
    
	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(read_count == 0) break;
	receivedLength = read_count / sizeof(short);
	i += receivedLength;
	if(SWAPOUT && BINARY)
	{
	    swab((char*)read_data,(char*)read_data, receivedLength*sizeof(short));
	}
	if(length) fprintf(stderr,"\r%sCopying the sound: %d samples copied",
		((SWAPOUT && BINARY) ? "Swapping and " : ""), MIN(i,length));
	else fprintf(stderr,"\r%sCopying the sound: %d samples copied",
		((SWAPOUT && BINARY) ? "Swapping and " : ""), i);
	if(length != 0 && i>length)
	{
	write(output_file,read_data, (length%(receivedLength))*sizeof(short));
	vm_deallocate(task_self(),(pointer_t)read_data,
		(length%(receivedLength))*sizeof(short));
	endrec = 1;
	}
	else
	{
	write(output_file,read_data, receivedLength*sizeof(short));
	vm_deallocate(task_self(),(pointer_t)read_data,
		receivedLength*sizeof(short));
	}
    }
    if(length == 0 && BINARY == 0)
    {
	converted->dataSize = i*sizeof(short);
	lseek(output_file,0,L_SET);
	write(output_file,(char*) converted, sizeof(SNDSoundStruct));
    }
    printf("\n");
}
}





// 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 * resize / (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,thresh,warn) 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 max_order. Thresh is equal to 0.03, "the" auditory threshold for pitch sensation. When warn = 0, no warning is given.
*/ 

void fract(x,rat,thresh,warn)
float x;
int *rat, warn;
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
    {
    if(warn) 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);
	if(warn) 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:  record \t-[Svsbcda] sound  \n\t\t\
    -v:verbose: indicates conversion parameters\n\t\t\
    -S:Output sampling rate\n\t\t\
    -L:duration (in seconds) to be recorded (optional)\n\t\t\
    -a:Records samples from analog input (AD64X)\n\t\t\
    -dh:Records samples from digital 48kHz input (AD64X)\n\t\t\
    -dm:Records samples from digital 44.1kHz input (AD64X)\n\t\t\
    -dl:Records samples from digital 32kHz input (AD64X)\n\t\t\
    -s:Swaps output sound bytes (for PCs and DECs)\n\t\t\
    -b:Outputs a soundfile (with header)\n\t\t\
    -c:Channels: s:stereo, l:left only, r:right only, +:sum \n\t\t\
    -A:Forces direct-to-disk recording\n\t\t\
    -h:Obtain info on all the possible options\n\t\
Examples: \n\tTo record 100000 samples from analog input at 24kHz to a binary file: \n\t\trecord -S24000 -ah  file -L100000\n\t\
(if -L isn't specified, record asks you to start and stop)\n\t\
To record 100000 samples from digital 48kHz input\n\tand down-sample to 24kHz: \n\t\trecord -S24000 -dh file -L100000\n");
    exit(0);

}

void usage2()
{
    printf("Usage:  record \t-[UDSKtovsobcrda] sound  \n\t\t\
    -v:verbose: indicates conversion parameters\n\t\t\
    -S:Output sampling rate\n\t\t\
    -L:duration (in seconds) to be recorded (optional)\n\t\t\
    -a:Records samples from analog input (AD64X)\n\t\t\
    -dh:Records samples from digital 48kHz input (AD64X)\n\t\t\
    -dm:Records samples from digital 44.1kHz input (AD64X)\n\t\t\
    -dl:Records samples from digital 32kHz input (AD64X)\n\t\t\
    -s:Swaps output sound bytes (for PCs and DECs)\n\t\t\
    -b:Outputs a soundfile (with header)\n\t\t\
    -c:Channels: s:stereo, l:left only, r:right only, +:sum \n\t\t\
    -A:Forces direct-to-disk recording\n\t\t\
    -ah:Records samples from analog input (AD64X) at 48kHz \n\t\t\
    -am:Records samples from analog input (AD64X) at 44.1kHz \n\t\t\
    -al:Records samples from analog input (AD64X) at 32kHz \n\t\t\
    -w:Scaling factor (default 0.9) \n\t\t\
    -t:Sampling rate accuracy (default 0.003) \n\t\t\
    -o:Cut-off frequency offset (default 0.005) \n\t\t\
    -U:Up-sampling factor\n\t\t\
    -D:Down-sampling factor\n\t\t\
    -K:filter-Order\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");
    exit(0);

}

void stoprec()
{
// Attempts to handle ^C gracefully! release everything before exiting.

    snddriver_stream_control(read_port,0, SNDDRIVER_ABORT_STREAM);
    endrec = 1;
} 
   
    

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