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.