/* * Copyright (c) 1983-2023 Richard Dobson and Composers Desktop Project Ltd * http://people.bath.ac.uk/masrwd * http://www.composersdesktop.com * This file is part of the CDP System. * The CDP System is free software; you can redistribute it * and/or modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * The CDP System is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General Public * License along with the CDP System; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ /*TODO: fix bug writing header when using -x with .amb extension (omits cbSize setting) */ /*interlx.c */ /* v1.3. nov 2005; support placeholder arg for silent channel */ /* v 1.7 beta ; added surr 5.0; updated sfsys with bit-correct 24bit copy */ /* v 1.8 March 2009 updated sfsys for AIFC int24 suport */ /* OCT 2009 portsf(64) version. int24 aifc supported for reading. */ /* v2.0.1 Jan 2010: Corrected usage message to refer to outfile. */ /* Nov 2013 added MC_SURR_6_1 */ #include #include #include #include #include #include #include "portsf.h" /* The number of channels defines the order of the soundfield: 3 channel = h = 1st order horizontal 4 channel = f = 1st order 3-D 5 channel = hh = 2nd order horizontal 6 channel = fh = 2nd order horizontal + 1st order height (formerly called 2.5 order) 7 channel = hhh = 3rd order horizontal 8 channel = fhh = 3rd order horizontal + 1st order height 9 channel = ff = 2nd order 3-D 11 channel = ffh = 3rd order horizontal + 2nd order height 16 channel = fff = 3rd order 3-D */ #define N_BFORMATS (10) static const int bformats[N_BFORMATS] = {2,3,4,5,6,7,8,9,11,16}; #define MAX_INFILES (16) //should keep most people happy! Won't get much more on a cmdline anyway enum {OPT_STD_WAVE,OPT_WAVEX_GENERIC,OPT_WAVEX,OPT_WAVEX_LCRS,OPT_WAVEX_SURROUND,OPT_WAVEX_BFORMAT, OPT_SURR_5_0,OPT_WAVEX_7_1,OPT_WAVEX_CUBE,OPT_WAVEX_6_1, OPT_MAXOPTS}; #ifdef unix /* in portsf.lib */ extern int stricmp(const char *a, const char *b); #endif #ifndef max #define max(x,y) ((x) > (y) ? (x) : (y)) #endif void usage() { fprintf(stderr,"\nCDP MCTOOLS: INTERLX v2.1.0 (c) RWD,CDP 2009,2013\n" "Interleave mono or stereo files into a multi-channel file\n" "Usage: interlx [-tN] outfile infile1 infile2 [infile3...]\n" " Up to %d files may be interleaved.\n" " Output format is taken from infile1.\n" " Files must match sample rate and number of channels,\n" " but can have different sample types.\n" " To create a silent channel, for infile2 onwards,\n" " use 0 (zero) as filename. Infile1 must be a soundfile.\n" " NB: Speaker-positions in WAVE_EX infiles are ignored\n" " Note that the same infile can be listed multiple times,\n" " for example, to write a mono file as stereo, quad, etc.\n" " The .amb B-Format extension is supported: \n" " the program warns if channel count is anomalous.\n" " recognised Bformat channel counts: 3,4,5,6,7,8,9,11,16.\n" " -tN : write outfile format as type N\n" " Available formats:\n" " 0 : (default) standard soundfile (.wav, .aif, .afc, .aifc)\n" " 1 : generic WAVE_EX (no speaker assignments)\n" " 2 : WAVE_EX mono/stereo/quad(LF,RF,LR,RR) - total chans must match.\n" " 3 : WAVE_EX quad surround (L,C,R,S) - total chans must be 4.\n" " 4 : WAVE_EX 5.1 format surround - total chans must be 6.\n" " 5 : WAVE_EX Ambisonic B-format (W,X,Y,Z...) - extension .amb recommended.\n" " 6 : WAVE_EX 5.0 surround - total chans must be 5.\n" " 7 : WAVE_EX 7.1 Surround - total chans must be 8.\n" " 8 : WAVE_EX Cube Surround - total chans must be 8.\n" " 9 : WAVE_EX 6.1 surround (new in v 2.1.0) - total chans must be 7.\n" " in all cases: outfile has sample format of infile1\n" " NB: types 1 to %d are for WAV format only\n" ,MAX_INFILES,OPT_MAXOPTS-1); } void cleanup(int *sflist) { int i; for(i=0;i < MAX_INFILES; i++) if(sflist[i] >= 0) psf_sndClose(sflist[i]); psf_finish(); } int main(int argc, char *argv[]) { long outframesize,thissize; int i,ofd; int ifdlist[MAX_INFILES]; //int force_stype = -1,out_stype; int force_wave = 0; int infilearg,inchans; int num_infiles = 0; int halfsec; MYLONG peaktime; float *outframe = NULL,*inframe = NULL; PSF_PROPS firstinprops,inprops; PSF_CHPEAK *fpeaks = NULL; int wave_type = -1; char *create_msg = NULL; psf_format informat = PSF_STDWAVE; char* p_dot = NULL; /* to find extension of outfile */ for(i=0;i < MAX_INFILES;i++) ifdlist[i] = -1; /* CDP version number */ if(argc==2 && (stricmp(argv[1],"--version")==0)){ printf("2.0.1.\n"); return 0; } if(argc < 4) { fprintf(stderr,"interlx: insufficient arguments\n"); usage(); exit(1); } while(argv[1][0] =='-'){ switch(argv[1][1]){ case('t'): if(argv[1][2]=='\0'){ fprintf(stderr,"-t flag requires parameter\n"); usage(); exit(1); } wave_type = atoi(&(argv[1][2])); if((wave_type < 0) || wave_type >= OPT_MAXOPTS){ fprintf(stderr,"wave type out of range\n"); usage(); exit(1); } force_wave = 1; break; default: fprintf(stderr,"\nabfpan: error: illegal flag option %s",argv[1]); exit(1); } argc--; argv++; } if(argc < 4){ fprintf(stderr,"interlx error: at least two infiles required!\n"); usage(); exit(1); } if(psf_init()){ printf("Startup failure.\n"); return 1; } //open first infile and get properties ifdlist[0] = psf_sndOpen(argv[2],&firstinprops,0); if(ifdlist[0] < 0){ fprintf(stderr,"unable to open infile %s\n",argv[2]); cleanup(ifdlist); return 1; } /* we don't know how to deal with speaker positions yet, so disregard these if(firstinprops.chformat > MC_STD){ printf(stderr,"Warning,interlx: ignoring source file speaker positions\n"); } */ outframesize = psf_sndSize(ifdlist[0]); if(outframesize < 0){ fprintf(stderr,"unable to read size of infile %s\n",argv[2]); cleanup(ifdlist); return 1; } inchans = firstinprops.chans; /*we can always allow more channels if someone really needs it! */ if(!(inchans==1 || inchans==2)){ fprintf(stderr,"interlx: error: infile %s has %d channels\n" "\t(only mono and stereo files can be used)\n",argv[2],inchans); cleanup(ifdlist); return 1; } num_infiles = 1; printf("interleaving %d-channel files,sample rate = %d\n",inchans,firstinprops.srate); infilearg = 3; while(argv[infilearg] != NULL){ if(strcmp(argv[infilearg],"0")==0){ ifdlist[num_infiles] = -1; // mark silent channel } else{ if((ifdlist[num_infiles] = psf_sndOpen(argv[infilearg],&inprops,0)) < 0){ fprintf(stderr,"cannot open infile %s\n",argv[infilearg]); cleanup(ifdlist); return 1; } if(inprops.chans != firstinprops.chans){ fprintf(stderr,"interlx: error: channel mismatch from infile %s\n",argv[infilearg]); cleanup(ifdlist); return 1; } if(inprops.srate != firstinprops.srate){ fprintf(stderr,"interlx: error: sample rate mismatch from infile %s\n",argv[infilearg]); cleanup(ifdlist); return 1; } thissize = psf_sndSize(ifdlist[num_infiles]); if(thissize < 0){ fprintf(stderr,"unable to read size of infile %s\n",argv[infilearg]); cleanup(ifdlist); return 1; } outframesize = max(outframesize,thissize); } infilearg++; num_infiles++; if(num_infiles > MAX_INFILES){ fprintf(stderr,"Sorry! too many infiles. Maximum accepted is %d.\n",MAX_INFILES); cleanup(ifdlist); exit(1); } } inframe = malloc(inchans * sizeof(float)); if(inframe==NULL){ puts("interlx: error: no memory for input buffer!\n"); cleanup(ifdlist); return 1; } firstinprops.chans *= num_infiles; outframe = (float *) malloc(firstinprops.chans * sizeof(float)); if(outframe==NULL){ puts("\ninterlx: error: no memory for output buffer!\n"); cleanup(ifdlist); return 1; } fpeaks = (PSF_CHPEAK *) calloc(firstinprops.chans,sizeof(PSF_CHPEAK)); if(fpeaks==NULL){ puts("interlx: error: no memory for internal PEAK buffer\n"); cleanup(ifdlist); return 1; } if(force_wave){ int i,matched; switch(wave_type){ case(OPT_WAVEX_GENERIC): inprops.chformat = MC_STD; informat = PSF_WAVE_EX; create_msg = "creating STD WAVE_EX file"; break; case(OPT_WAVEX): switch(firstinprops.chans){ case(1): firstinprops.chformat = MC_MONO; informat = PSF_WAVE_EX; create_msg = "creating MONO WAVE_EX file"; break; case(2): firstinprops.chformat = MC_STEREO; informat = PSF_WAVE_EX; create_msg = "creating STEREO WAVE_EX file"; break; case(4): firstinprops.chformat = MC_QUAD; informat = PSF_WAVE_EX; create_msg = "creating QUAD WAVE_EX file"; break; default: fprintf(stderr,"infile nchans incompatible with requested WAVE-EX format\n"); usage(); cleanup(ifdlist); return 1; } break; case(OPT_WAVEX_LCRS): if(firstinprops.chans != 4){ fprintf(stderr,"result must have four channels\n"); usage(); cleanup(ifdlist); return 1; } firstinprops.chformat = MC_LCRS; informat = PSF_WAVE_EX; create_msg = "creating LCRS-surround WAVE_EX file"; break; case(OPT_WAVEX_SURROUND): if(firstinprops.chans != 6){ fprintf(stderr,"result must have six channels\n"); usage(); cleanup(ifdlist); exit(1); } firstinprops.chformat = MC_DOLBY_5_1; informat = PSF_WAVE_EX; create_msg = "creating 5.1 surround WAVE_EX file"; break; case(OPT_SURR_5_0): if(firstinprops.chans != 5){ fprintf(stderr,"result must have five channels.\n"); usage(); cleanup(ifdlist); return 1; } firstinprops.chformat = MC_SURR_5_0; informat = PSF_WAVE_EX; create_msg = "creating 5.0 surround WAVE_EX file"; break; case(OPT_WAVEX_BFORMAT): matched = 0; for(i=0;i < N_BFORMATS;i++) { if(firstinprops.chans == bformats[i]){ matched = 1; break; } } if(!matched){ printf("WARNING: No Bformat definition for %d-channel file.\n",inprops.chans); } firstinprops.chformat = MC_BFMT; informat = PSF_WAVE_EX; create_msg = "creating AMBISONIC B-FORMAT WAVE_EX file"; break; case OPT_WAVEX_7_1: if(firstinprops.chans != 8){ fprintf(stderr,"result must have channels\n"); usage(); cleanup(ifdlist); return 1; } firstinprops.chformat = MC_SURR_7_1; informat = PSF_WAVE_EX; create_msg = "creating 7.1 surround WAVE_EX file"; break; case OPT_WAVEX_CUBE: if(firstinprops.chans != 8){ fprintf(stderr,"result must have channels\n"); usage(); cleanup(ifdlist); return 1; } firstinprops.chformat = MC_CUBE; informat = PSF_WAVE_EX; create_msg = "creating cube surround WAVE_EX file"; break; case OPT_WAVEX_6_1: if(firstinprops.chans != 7){ fprintf(stderr,"result must have channels\n"); usage(); cleanup(ifdlist); return 1; } firstinprops.chformat = MC_SURR_6_1; informat = PSF_WAVE_EX; create_msg = "creating 6.1 surround WAVE_EX file"; break; default: inprops.chformat = STDWAVE; informat = PSF_STDWAVE; create_msg = "creating plain sound file"; break; } } /* want to avoid WAVE_EX if just plain WAVE possible */ firstinprops.format = informat; /*firstinprops.chformat = MC_STD;*/ /* RWD April 2006 do this here? */ p_dot = strrchr(argv[1],'.'); if(stricmp(++p_dot,"amb")==0) { int i; int matched = 0; firstinprops.chformat = MC_BFMT; for(i=0;i < N_BFORMATS;i++) { if(firstinprops.chans == bformats[i]){ matched = 1; break; } } if(!matched) printf("\nWARNING: channel count %d unknown for BFormat.\n",firstinprops.chans); } if(!is_legalsize(outframesize,&firstinprops)){ fprintf(stderr,"error: outfile size %ld exceeds capacity of format.\n",outframesize); return 1; } printf("\n%s: %d channels, %ld frames.\n",create_msg,firstinprops.chans,outframesize); ofd = psf_sndCreate(argv[1],&firstinprops,0,0,PSF_CREATE_RDWR); if(ofd < 0){ fprintf(stderr,"interlx: error: unable to create outfile %s.\n",argv[1]); cleanup(ifdlist); return 1; } halfsec = firstinprops.srate / 2; for(i=0;i < outframesize; i++){ // frame loop float *p_framesamp,*p_filesamp; int j; p_framesamp = outframe; memset((char *)outframe,0,firstinprops.chans * sizeof(float)); for(j=0;j < num_infiles;j++) { //file loop int k,got; memset((char *)inframe,0,inchans * sizeof(float)); if(ifdlist[j] < 0){ got = inchans; // placeholder - write silent channel } else{ if((got = psf_sndReadFloatFrames(ifdlist[j],inframe,1)) < 0){ fprintf(stderr,"interlx: error reading from infile %s\n",argv[2+j]); psf_sndClose(ofd); cleanup(ifdlist); return 1; } } if(got==1){ p_filesamp = inframe; for(k=0;k < inchans;k++) //channel loop *p_framesamp++ = *p_filesamp++; } else p_framesamp += inchans; } if(psf_sndWriteFloatFrames(ofd,outframe,1) < 0){ fprintf(stderr,"interlx: error writing to outfile\n"); psf_sndClose(ofd); cleanup(ifdlist); return 1; } if(i % halfsec==0) { printf("%.2lf secs\r",(double)i / (double) firstinprops.srate); fflush(stdout); } } printf("%.4lf secs\nWritten %ld sample frames to %s\n",(double)outframesize / (double)firstinprops.srate,outframesize,argv[1]); if(psf_sndReadPeaks( ofd,fpeaks,&peaktime)){ printf("PEAK data:\n"); for(i = 0; i < firstinprops.chans; i++) { double val, dbval; val = (double) fpeaks[i].val; dbval = 20.0 * log10(val); printf("CH %d: %.6f (%.2lfdB) at frame %u:\t%.4f secs\n",i, val,dbval,(unsigned int) fpeaks[i].pos,(double)fpeaks[i].pos / (double) firstinprops.srate); } } printf("\n"); psf_sndClose(ofd); free(inframe); free(outframe); if(fpeaks) { free(fpeaks); } cleanup(ifdlist); return 0; }