| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506 |
- /*
- * 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 <stdio.h>
- #include <stdlib.h>
- #include <math.h>
- #include <memory.h>
- #include <string.h>
- #include <ctype.h>
- #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;
- }
|