interlx.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506
  1. /*
  2. * Copyright (c) 1983-2023 Richard Dobson and Composers Desktop Project Ltd
  3. * http://people.bath.ac.uk/masrwd
  4. * http://www.composersdesktop.com
  5. * This file is part of the CDP System.
  6. * The CDP System is free software; you can redistribute it
  7. * and/or modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 2.1 of the License, or (at your option) any later version.
  10. *
  11. * The CDP System is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  14. * See the GNU Lesser General Public License for more details.
  15. * You should have received a copy of the GNU Lesser General Public
  16. * License along with the CDP System; if not, write to the Free Software
  17. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  18. *
  19. */
  20. /*TODO: fix bug writing header when using -x with .amb extension (omits cbSize setting) */
  21. /*interlx.c */
  22. /* v1.3. nov 2005; support placeholder arg for silent channel */
  23. /* v 1.7 beta ; added surr 5.0; updated sfsys with bit-correct 24bit copy */
  24. /* v 1.8 March 2009 updated sfsys for AIFC int24 suport */
  25. /* OCT 2009 portsf(64) version. int24 aifc supported for reading. */
  26. /* v2.0.1 Jan 2010: Corrected usage message to refer to outfile. */
  27. /* Nov 2013 added MC_SURR_6_1 */
  28. #include <stdio.h>
  29. #include <stdlib.h>
  30. #include <math.h>
  31. #include <memory.h>
  32. #include <string.h>
  33. #include <ctype.h>
  34. #include "portsf.h"
  35. /*
  36. The number of channels defines the order of the soundfield:
  37. 3 channel = h = 1st order horizontal
  38. 4 channel = f = 1st order 3-D
  39. 5 channel = hh = 2nd order horizontal
  40. 6 channel = fh = 2nd order horizontal + 1st order height (formerly called 2.5 order)
  41. 7 channel = hhh = 3rd order horizontal
  42. 8 channel = fhh = 3rd order horizontal + 1st order height
  43. 9 channel = ff = 2nd order 3-D
  44. 11 channel = ffh = 3rd order horizontal + 2nd order height
  45. 16 channel = fff = 3rd order 3-D
  46. */
  47. #define N_BFORMATS (10)
  48. static const int bformats[N_BFORMATS] = {2,3,4,5,6,7,8,9,11,16};
  49. #define MAX_INFILES (16) //should keep most people happy! Won't get much more on a cmdline anyway
  50. enum {OPT_STD_WAVE,OPT_WAVEX_GENERIC,OPT_WAVEX,OPT_WAVEX_LCRS,OPT_WAVEX_SURROUND,OPT_WAVEX_BFORMAT,
  51. OPT_SURR_5_0,OPT_WAVEX_7_1,OPT_WAVEX_CUBE,OPT_WAVEX_6_1, OPT_MAXOPTS};
  52. #ifdef unix
  53. /* in portsf.lib */
  54. extern int stricmp(const char *a, const char *b);
  55. #endif
  56. #ifndef max
  57. #define max(x,y) ((x) > (y) ? (x) : (y))
  58. #endif
  59. void
  60. usage()
  61. {
  62. fprintf(stderr,"\nCDP MCTOOLS: INTERLX v2.1.0 (c) RWD,CDP 2009,2013\n"
  63. "Interleave mono or stereo files into a multi-channel file\n"
  64. "Usage: interlx [-tN] outfile infile1 infile2 [infile3...]\n"
  65. " Up to %d files may be interleaved.\n"
  66. " Output format is taken from infile1.\n"
  67. " Files must match sample rate and number of channels,\n"
  68. " but can have different sample types.\n"
  69. " To create a silent channel, for infile2 onwards,\n"
  70. " use 0 (zero) as filename. Infile1 must be a soundfile.\n"
  71. " NB: Speaker-positions in WAVE_EX infiles are ignored\n"
  72. " Note that the same infile can be listed multiple times,\n"
  73. " for example, to write a mono file as stereo, quad, etc.\n"
  74. " The .amb B-Format extension is supported: \n"
  75. " the program warns if channel count is anomalous.\n"
  76. " recognised Bformat channel counts: 3,4,5,6,7,8,9,11,16.\n"
  77. " -tN : write outfile format as type N\n"
  78. " Available formats:\n"
  79. " 0 : (default) standard soundfile (.wav, .aif, .afc, .aifc)\n"
  80. " 1 : generic WAVE_EX (no speaker assignments)\n"
  81. " 2 : WAVE_EX mono/stereo/quad(LF,RF,LR,RR) - total chans must match.\n"
  82. " 3 : WAVE_EX quad surround (L,C,R,S) - total chans must be 4.\n"
  83. " 4 : WAVE_EX 5.1 format surround - total chans must be 6.\n"
  84. " 5 : WAVE_EX Ambisonic B-format (W,X,Y,Z...) - extension .amb recommended.\n"
  85. " 6 : WAVE_EX 5.0 surround - total chans must be 5.\n"
  86. " 7 : WAVE_EX 7.1 Surround - total chans must be 8.\n"
  87. " 8 : WAVE_EX Cube Surround - total chans must be 8.\n"
  88. " 9 : WAVE_EX 6.1 surround (new in v 2.1.0) - total chans must be 7.\n"
  89. " in all cases: outfile has sample format of infile1\n"
  90. " NB: types 1 to %d are for WAV format only\n"
  91. ,MAX_INFILES,OPT_MAXOPTS-1);
  92. }
  93. void cleanup(int *sflist)
  94. {
  95. int i;
  96. for(i=0;i < MAX_INFILES; i++)
  97. if(sflist[i] >= 0)
  98. psf_sndClose(sflist[i]);
  99. psf_finish();
  100. }
  101. int main(int argc, char *argv[])
  102. {
  103. long outframesize,thissize;
  104. int i,ofd;
  105. int ifdlist[MAX_INFILES];
  106. //int force_stype = -1,out_stype;
  107. int force_wave = 0;
  108. int infilearg,inchans;
  109. int num_infiles = 0;
  110. int halfsec;
  111. MYLONG peaktime;
  112. float *outframe = NULL,*inframe = NULL;
  113. PSF_PROPS firstinprops,inprops;
  114. PSF_CHPEAK *fpeaks = NULL;
  115. int wave_type = -1;
  116. char *create_msg = NULL;
  117. psf_format informat = PSF_STDWAVE;
  118. char* p_dot = NULL; /* to find extension of outfile */
  119. for(i=0;i < MAX_INFILES;i++)
  120. ifdlist[i] = -1;
  121. /* CDP version number */
  122. if(argc==2 && (stricmp(argv[1],"--version")==0)){
  123. printf("2.0.1.\n");
  124. return 0;
  125. }
  126. if(argc < 4) {
  127. fprintf(stderr,"interlx: insufficient arguments\n");
  128. usage();
  129. exit(1);
  130. }
  131. while(argv[1][0] =='-'){
  132. switch(argv[1][1]){
  133. case('t'):
  134. if(argv[1][2]=='\0'){
  135. fprintf(stderr,"-t flag requires parameter\n");
  136. usage();
  137. exit(1);
  138. }
  139. wave_type = atoi(&(argv[1][2]));
  140. if((wave_type < 0) || wave_type >= OPT_MAXOPTS){
  141. fprintf(stderr,"wave type out of range\n");
  142. usage();
  143. exit(1);
  144. }
  145. force_wave = 1;
  146. break;
  147. default:
  148. fprintf(stderr,"\nabfpan: error: illegal flag option %s",argv[1]);
  149. exit(1);
  150. }
  151. argc--; argv++;
  152. }
  153. if(argc < 4){
  154. fprintf(stderr,"interlx error: at least two infiles required!\n");
  155. usage();
  156. exit(1);
  157. }
  158. if(psf_init()){
  159. printf("Startup failure.\n");
  160. return 1;
  161. }
  162. //open first infile and get properties
  163. ifdlist[0] = psf_sndOpen(argv[2],&firstinprops,0);
  164. if(ifdlist[0] < 0){
  165. fprintf(stderr,"unable to open infile %s\n",argv[2]);
  166. cleanup(ifdlist);
  167. return 1;
  168. }
  169. /* we don't know how to deal with speaker positions yet, so disregard these
  170. if(firstinprops.chformat > MC_STD){
  171. printf(stderr,"Warning,interlx: ignoring source file speaker positions\n");
  172. }
  173. */
  174. outframesize = psf_sndSize(ifdlist[0]);
  175. if(outframesize < 0){
  176. fprintf(stderr,"unable to read size of infile %s\n",argv[2]);
  177. cleanup(ifdlist);
  178. return 1;
  179. }
  180. inchans = firstinprops.chans;
  181. /*we can always allow more channels if someone really needs it! */
  182. if(!(inchans==1 || inchans==2)){
  183. fprintf(stderr,"interlx: error: infile %s has %d channels\n"
  184. "\t(only mono and stereo files can be used)\n",argv[2],inchans);
  185. cleanup(ifdlist);
  186. return 1;
  187. }
  188. num_infiles = 1;
  189. printf("interleaving %d-channel files,sample rate = %d\n",inchans,firstinprops.srate);
  190. infilearg = 3;
  191. while(argv[infilearg] != NULL){
  192. if(strcmp(argv[infilearg],"0")==0){
  193. ifdlist[num_infiles] = -1; // mark silent channel
  194. }
  195. else{
  196. if((ifdlist[num_infiles] = psf_sndOpen(argv[infilearg],&inprops,0)) < 0){
  197. fprintf(stderr,"cannot open infile %s\n",argv[infilearg]);
  198. cleanup(ifdlist);
  199. return 1;
  200. }
  201. if(inprops.chans != firstinprops.chans){
  202. fprintf(stderr,"interlx: error: channel mismatch from infile %s\n",argv[infilearg]);
  203. cleanup(ifdlist);
  204. return 1;
  205. }
  206. if(inprops.srate != firstinprops.srate){
  207. fprintf(stderr,"interlx: error: sample rate mismatch from infile %s\n",argv[infilearg]);
  208. cleanup(ifdlist);
  209. return 1;
  210. }
  211. thissize = psf_sndSize(ifdlist[num_infiles]);
  212. if(thissize < 0){
  213. fprintf(stderr,"unable to read size of infile %s\n",argv[infilearg]);
  214. cleanup(ifdlist);
  215. return 1;
  216. }
  217. outframesize = max(outframesize,thissize);
  218. }
  219. infilearg++;
  220. num_infiles++;
  221. if(num_infiles > MAX_INFILES){
  222. fprintf(stderr,"Sorry! too many infiles. Maximum accepted is %d.\n",MAX_INFILES);
  223. cleanup(ifdlist);
  224. exit(1);
  225. }
  226. }
  227. inframe = malloc(inchans * sizeof(float));
  228. if(inframe==NULL){
  229. puts("interlx: error: no memory for input buffer!\n");
  230. cleanup(ifdlist);
  231. return 1;
  232. }
  233. firstinprops.chans *= num_infiles;
  234. outframe = (float *) malloc(firstinprops.chans * sizeof(float));
  235. if(outframe==NULL){
  236. puts("\ninterlx: error: no memory for output buffer!\n");
  237. cleanup(ifdlist);
  238. return 1;
  239. }
  240. fpeaks = (PSF_CHPEAK *) calloc(firstinprops.chans,sizeof(PSF_CHPEAK));
  241. if(fpeaks==NULL){
  242. puts("interlx: error: no memory for internal PEAK buffer\n");
  243. cleanup(ifdlist);
  244. return 1;
  245. }
  246. if(force_wave){
  247. int i,matched;
  248. switch(wave_type){
  249. case(OPT_WAVEX_GENERIC):
  250. inprops.chformat = MC_STD;
  251. informat = PSF_WAVE_EX;
  252. create_msg = "creating STD WAVE_EX file";
  253. break;
  254. case(OPT_WAVEX):
  255. switch(firstinprops.chans){
  256. case(1):
  257. firstinprops.chformat = MC_MONO;
  258. informat = PSF_WAVE_EX;
  259. create_msg = "creating MONO WAVE_EX file";
  260. break;
  261. case(2):
  262. firstinprops.chformat = MC_STEREO;
  263. informat = PSF_WAVE_EX;
  264. create_msg = "creating STEREO WAVE_EX file";
  265. break;
  266. case(4):
  267. firstinprops.chformat = MC_QUAD;
  268. informat = PSF_WAVE_EX;
  269. create_msg = "creating QUAD WAVE_EX file";
  270. break;
  271. default:
  272. fprintf(stderr,"infile nchans incompatible with requested WAVE-EX format\n");
  273. usage();
  274. cleanup(ifdlist);
  275. return 1;
  276. }
  277. break;
  278. case(OPT_WAVEX_LCRS):
  279. if(firstinprops.chans != 4){
  280. fprintf(stderr,"result must have four channels\n");
  281. usage();
  282. cleanup(ifdlist);
  283. return 1;
  284. }
  285. firstinprops.chformat = MC_LCRS;
  286. informat = PSF_WAVE_EX;
  287. create_msg = "creating LCRS-surround WAVE_EX file";
  288. break;
  289. case(OPT_WAVEX_SURROUND):
  290. if(firstinprops.chans != 6){
  291. fprintf(stderr,"result must have six channels\n");
  292. usage();
  293. cleanup(ifdlist);
  294. exit(1);
  295. }
  296. firstinprops.chformat = MC_DOLBY_5_1;
  297. informat = PSF_WAVE_EX;
  298. create_msg = "creating 5.1 surround WAVE_EX file";
  299. break;
  300. case(OPT_SURR_5_0):
  301. if(firstinprops.chans != 5){
  302. fprintf(stderr,"result must have five channels.\n");
  303. usage();
  304. cleanup(ifdlist);
  305. return 1;
  306. }
  307. firstinprops.chformat = MC_SURR_5_0;
  308. informat = PSF_WAVE_EX;
  309. create_msg = "creating 5.0 surround WAVE_EX file";
  310. break;
  311. case(OPT_WAVEX_BFORMAT):
  312. matched = 0;
  313. for(i=0;i < N_BFORMATS;i++) {
  314. if(firstinprops.chans == bformats[i]){
  315. matched = 1;
  316. break;
  317. }
  318. }
  319. if(!matched){
  320. printf("WARNING: No Bformat definition for %d-channel file.\n",inprops.chans);
  321. }
  322. firstinprops.chformat = MC_BFMT;
  323. informat = PSF_WAVE_EX;
  324. create_msg = "creating AMBISONIC B-FORMAT WAVE_EX file";
  325. break;
  326. case OPT_WAVEX_7_1:
  327. if(firstinprops.chans != 8){
  328. fprintf(stderr,"result must have channels\n");
  329. usage();
  330. cleanup(ifdlist);
  331. return 1;
  332. }
  333. firstinprops.chformat = MC_SURR_7_1;
  334. informat = PSF_WAVE_EX;
  335. create_msg = "creating 7.1 surround WAVE_EX file";
  336. break;
  337. case OPT_WAVEX_CUBE:
  338. if(firstinprops.chans != 8){
  339. fprintf(stderr,"result must have channels\n");
  340. usage();
  341. cleanup(ifdlist);
  342. return 1;
  343. }
  344. firstinprops.chformat = MC_CUBE;
  345. informat = PSF_WAVE_EX;
  346. create_msg = "creating cube surround WAVE_EX file";
  347. break;
  348. case OPT_WAVEX_6_1:
  349. if(firstinprops.chans != 7){
  350. fprintf(stderr,"result must have channels\n");
  351. usage();
  352. cleanup(ifdlist);
  353. return 1;
  354. }
  355. firstinprops.chformat = MC_SURR_6_1;
  356. informat = PSF_WAVE_EX;
  357. create_msg = "creating 6.1 surround WAVE_EX file";
  358. break;
  359. default:
  360. inprops.chformat = STDWAVE;
  361. informat = PSF_STDWAVE;
  362. create_msg = "creating plain sound file";
  363. break;
  364. }
  365. }
  366. /* want to avoid WAVE_EX if just plain WAVE possible */
  367. firstinprops.format = informat;
  368. /*firstinprops.chformat = MC_STD;*/ /* RWD April 2006 do this here? */
  369. p_dot = strrchr(argv[1],'.');
  370. if(stricmp(++p_dot,"amb")==0) {
  371. int i;
  372. int matched = 0;
  373. firstinprops.chformat = MC_BFMT;
  374. for(i=0;i < N_BFORMATS;i++) {
  375. if(firstinprops.chans == bformats[i]){
  376. matched = 1;
  377. break;
  378. }
  379. }
  380. if(!matched)
  381. printf("\nWARNING: channel count %d unknown for BFormat.\n",firstinprops.chans);
  382. }
  383. if(!is_legalsize(outframesize,&firstinprops)){
  384. fprintf(stderr,"error: outfile size %ld exceeds capacity of format.\n",outframesize);
  385. return 1;
  386. }
  387. printf("\n%s: %d channels, %ld frames.\n",create_msg,firstinprops.chans,outframesize);
  388. ofd = psf_sndCreate(argv[1],&firstinprops,0,0,PSF_CREATE_RDWR);
  389. if(ofd < 0){
  390. fprintf(stderr,"interlx: error: unable to create outfile %s.\n",argv[1]);
  391. cleanup(ifdlist);
  392. return 1;
  393. }
  394. halfsec = firstinprops.srate / 2;
  395. for(i=0;i < outframesize; i++){ // frame loop
  396. float *p_framesamp,*p_filesamp;
  397. int j;
  398. p_framesamp = outframe;
  399. memset((char *)outframe,0,firstinprops.chans * sizeof(float));
  400. for(j=0;j < num_infiles;j++) { //file loop
  401. int k,got;
  402. memset((char *)inframe,0,inchans * sizeof(float));
  403. if(ifdlist[j] < 0){
  404. got = inchans; // placeholder - write silent channel
  405. }
  406. else{
  407. if((got = psf_sndReadFloatFrames(ifdlist[j],inframe,1)) < 0){
  408. fprintf(stderr,"interlx: error reading from infile %s\n",argv[2+j]);
  409. psf_sndClose(ofd);
  410. cleanup(ifdlist);
  411. return 1;
  412. }
  413. }
  414. if(got==1){
  415. p_filesamp = inframe;
  416. for(k=0;k < inchans;k++) //channel loop
  417. *p_framesamp++ = *p_filesamp++;
  418. }
  419. else
  420. p_framesamp += inchans;
  421. }
  422. if(psf_sndWriteFloatFrames(ofd,outframe,1) < 0){
  423. fprintf(stderr,"interlx: error writing to outfile\n");
  424. psf_sndClose(ofd);
  425. cleanup(ifdlist);
  426. return 1;
  427. }
  428. if(i % halfsec==0) {
  429. printf("%.2lf secs\r",(double)i / (double) firstinprops.srate);
  430. fflush(stdout);
  431. }
  432. }
  433. printf("%.4lf secs\nWritten %ld sample frames to %s\n",(double)outframesize / (double)firstinprops.srate,outframesize,argv[1]);
  434. if(psf_sndReadPeaks( ofd,fpeaks,&peaktime)){
  435. printf("PEAK data:\n");
  436. for(i = 0; i < firstinprops.chans; i++) {
  437. double val, dbval;
  438. val = (double) fpeaks[i].val;
  439. dbval = 20.0 * log10(val);
  440. printf("CH %d: %.6f (%.2lfdB) at frame %u:\t%.4f secs\n",i,
  441. val,dbval,(unsigned int) fpeaks[i].pos,(double)fpeaks[i].pos / (double) firstinprops.srate);
  442. }
  443. }
  444. printf("\n");
  445. psf_sndClose(ofd);
  446. free(inframe);
  447. free(outframe);
  448. if(fpeaks) {
  449. free(fpeaks);
  450. }
  451. cleanup(ifdlist);
  452. return 0;
  453. }