njoin.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698
  1. /*
  2. * Copyright (c) 1983-2013 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. /* njoin.c: concantenate files with optional spacing */
  21. /* 12 Dec 2006 v 0.7: fixed bug in read_filelist: trap leading spaces on line */
  22. /* OCT 2009: v1.0 support tilde-prefixed path under unix */
  23. /* Jan 2010 v1.0.1 fixed bug processing lots of files and running out of portsf slots */
  24. /* Nov 2013 recognise MC_SURR_6_1 */
  25. /* Mar 2021 fix small file format type mismatch error (line 543 */
  26. #include <stdio.h>
  27. #include <stdlib.h>
  28. #include <math.h>
  29. #include <memory.h>
  30. #include <string.h>
  31. #include <assert.h>
  32. #ifdef unix
  33. #include <glob.h>
  34. #endif
  35. #include "portsf.h"
  36. #ifdef unix
  37. /* in portsf.lib */
  38. extern int stricmp(const char *a, const char *b);
  39. #endif
  40. #define F_MAXLEN (1024)
  41. /* For CERN will need to increase this a lot! */
  42. #define MAXFILES (512)
  43. enum {ARG_PROGNAME, ARG_FLIST, ARG_NARGS};
  44. enum {ARG_OUTFILE = ARG_NARGS};
  45. int read_filelist(FILE* ifp, char* p_flist[]);
  46. void cleanup(int ifd, int ofd,char* flist[], int nfiles);
  47. #ifdef unix
  48. char* CreatePathByExpandingTildePath(char* path)
  49. {
  50. glob_t globbuf;
  51. char **v;
  52. char *expandedPath = NULL, *result = NULL;
  53. assert(path != NULL);
  54. if (glob(path, GLOB_TILDE, NULL, &globbuf) == 0) //success
  55. {
  56. v = globbuf.gl_pathv; //list of matched pathnames
  57. expandedPath = v[0]; //number of matched pathnames, gl_pathc == 1
  58. result = (char*)calloc( strlen(expandedPath) + 1,sizeof(char)); //the extra char is for the null-termination
  59. if(result)
  60. strncpy(result, expandedPath, strlen(expandedPath) + 1); //copy the null-termination as well
  61. globfree(&globbuf);
  62. }
  63. return result;
  64. }
  65. #endif
  66. void
  67. usage()
  68. {
  69. fprintf(stderr,"\nCDP MCTOOLS: NJOIN v1.1.1 (c) RWD,CDP 2006,2010,2013,2021\n"
  70. "concatenate multiple files into a single file\n"
  71. "Usage: njoin [-sSECS | -SSECS][-cCUEFILE][-x] filelist.txt [outfile] \n"
  72. " filelist.txt: text file containing list of sfiles\n"
  73. " in order. One file per line. \n"
  74. " Channel spec (if present) must be the same,\n"
  75. " but files with no spec assumed compatible.\n"
  76. " -cCUEFILE : if outfile used, generate cue textfile as CUEFILE.\n"
  77. " -sSECS : separate files with silence of SECS seconds\n"
  78. " -SSECS : as above, but no silence before first file.\n"
  79. " Default: files are joined with no gap.\n"
  80. " -x : strict: allow only CD-compatible files:\n"
  81. " Must use sr=44100, minimum duration 4 secs.\n"
  82. " NB: Files must match sample rate and number of channels,\n"
  83. " but can have different sample types.\n"
  84. " Output sample format taken from file with highest precision.\n"
  85. " If no outfile given: program scans files and prints report.\n"
  86. #ifdef unix
  87. " Unix systems: ~/ notation for home dir supported for file paths.\n"
  88. #endif
  89. );
  90. }
  91. void cleanup(int ifd, int ofd,char* flist[], int nfiles)
  92. {
  93. int i;
  94. for(i=0;i < nfiles; i++) {
  95. if(flist[i])
  96. free(flist[i]);
  97. }
  98. if(ifd >=0)
  99. psf_sndClose(ifd);
  100. if(ofd >=0)
  101. psf_sndClose(ofd);
  102. }
  103. /*SHORT8,SHORT16,FLOAT32,INT_32,INT2424,INT2432,INT2024,INT_MASKED*/
  104. static int wordsize[] = {1,2,4,4,3,4,3};
  105. /*return 0 for props2 same or less, 1 for props2 higher*/
  106. int compare_precision(const PSF_PROPS* props1,const PSF_PROPS* props2)
  107. {
  108. int retval = 0;
  109. switch(props1->samptype){
  110. case(PSF_SAMP_8):
  111. if(props2->samptype != props1->samptype)
  112. retval = 1;
  113. break;
  114. case(PSF_SAMP_16):
  115. if(props2->samptype > props1->samptype)
  116. retval = 1;
  117. break;
  118. case(PSF_SAMP_IEEE_FLOAT):
  119. /* only higher prec is 32bit int */
  120. if(props2->samptype== PSF_SAMP_32)
  121. retval = 1;
  122. break;
  123. case(PSF_SAMP_32):
  124. /* nothing higher than this!*/
  125. break;
  126. case(PSF_SAMP_24):
  127. //case(INT2432): // NB illegal for almost all formats!
  128. //case(INT2024):
  129. if(props2->samptype == PSF_SAMP_IEEE_FLOAT || props2->samptype == PSF_SAMP_32)
  130. retval = 1;
  131. break;
  132. default:
  133. break;
  134. }
  135. return retval;
  136. }
  137. const char* stype_as_string(const PSF_PROPS* props)
  138. {
  139. const char* msg;
  140. switch(props->samptype){
  141. case(PSF_SAMP_8):
  142. msg = "8-bit";
  143. break;
  144. case(PSF_SAMP_16):
  145. msg = "16-bit";
  146. break;
  147. case(PSF_SAMP_IEEE_FLOAT):
  148. msg = "32-bit floats";
  149. break;
  150. case(PSF_SAMP_32):
  151. msg = "32-bit integer";
  152. break;
  153. case(PSF_SAMP_24):
  154. msg = "24-bit";
  155. break;
  156. default:
  157. msg = "unknown WAVE_EX sample size!";
  158. break;
  159. }
  160. return msg;
  161. }
  162. //STDWAVE,MC_STD,MC_MONO,MC_STEREO,MC_QUAD,MC_LCRS,MC_BFMT,MC_DOLBY_5_1,MC_WAVE_EX
  163. const char* chformat_as_string(const PSF_PROPS* props)
  164. {
  165. const char* msg;
  166. switch(props->chformat){
  167. case MC_STD:
  168. msg = "Generic WAVE-EX";
  169. break;
  170. case MC_MONO:
  171. msg = "WAVE_EX Mono";
  172. break;
  173. case MC_STEREO:
  174. msg = "WAVE_EX Stereo";
  175. break;
  176. case MC_QUAD:
  177. msg = "WAVE_EX Quad";
  178. break;
  179. case MC_LCRS:
  180. msg = "WAVE_EX LCRS Surround";
  181. break;
  182. case MC_BFMT:
  183. msg = "WAVE_EX B-Format";
  184. break;
  185. case MC_DOLBY_5_1:
  186. msg = "WAVE_EX Dolby 5.1";
  187. break;
  188. case MC_SURR_6_1:
  189. msg = "6.1 surround";
  190. break;
  191. case MC_SURR_5_0:
  192. msg = "5.0 surround";
  193. break;
  194. case MC_SURR_7_1:
  195. msg = "7.1 Surround";
  196. break;
  197. case MC_CUBE:
  198. msg = "Cube Surround";
  199. break;
  200. case MC_WAVE_EX:
  201. msg = "WAVE-EX Custom Multi-Channel";
  202. break;
  203. default: // STDWAVE
  204. msg = "Standard soundfile";
  205. break;
  206. }
  207. return msg;
  208. }
  209. int read_filelist(FILE* ifp, char* p_flist[])
  210. {
  211. char buf[F_MAXLEN];
  212. long len;
  213. int nfiles = 0;
  214. #ifdef _DEBUG
  215. assert(ifp);
  216. assert(p_flist);
  217. #endif
  218. if(ifp==NULL || p_flist == NULL)
  219. return -1;
  220. while (fgets(buf,F_MAXLEN-1,ifp)){
  221. char* pbuf = buf;
  222. while(*pbuf == ' ')
  223. pbuf++;
  224. len = strlen(pbuf);
  225. if(len > 1){ // line has at least a eol byte
  226. p_flist[nfiles] = malloc(len+1);
  227. strcpy(p_flist[nfiles],pbuf);
  228. if(p_flist[nfiles][len-1] == 0x0A)
  229. p_flist[nfiles][len-1] = '\0';
  230. nfiles++;
  231. }
  232. if(feof(ifp))
  233. break;
  234. if(ferror(ifp))
  235. return -1;
  236. }
  237. return nfiles;
  238. }
  239. int strict_check(PSF_PROPS* props,unsigned long dur)
  240. {
  241. int ret = 0;
  242. unsigned long mindur = 4 * 44100;
  243. if(props->srate==44100 && dur >= mindur && props->chans==2)
  244. ret = 1;
  245. return ret;
  246. }
  247. int main(int argc, char* argv[])
  248. {
  249. int i = 0,j,ofd = -1;
  250. int ifd = -1;
  251. char* flist[MAXFILES];
  252. char* cuefilename = NULL;
  253. FILE* cuefp = NULL;
  254. int num_infiles = 0;
  255. float *inframe = NULL;
  256. float*space_frame = NULL;
  257. PSF_PROPS inprops, thisinprops,outprops;
  258. PSF_CHPEAK *fpeaks = NULL;
  259. double space_secs = 0.0;
  260. long space_frames = 0;
  261. long thisdur = 0;
  262. double totaldur = 0.0;
  263. FILE* fp = NULL;
  264. int formatsrc = 0;
  265. unsigned int max_datachunk = 0xFFFFFFFFU - 1024U; /* check Ok for PEAK chunk */
  266. double maxdur;
  267. double blockdur = 0.25; /* match buffersize to srate, so we get tidy upodate msgs */
  268. long buflen,block_frames;
  269. unsigned long written;
  270. int error = 0;
  271. int do_process = 1;
  272. int have_s = 0, have_S = 0;
  273. int strict =0;
  274. int strict_failures = 0;
  275. char* fname;
  276. /* CDP version number */
  277. if(argc==2 && (stricmp(argv[1],"--version")==0)){
  278. printf("1.1.0\n");
  279. return 0;
  280. }
  281. if(argc < ARG_NARGS){
  282. fprintf(stderr,"njoin: insufficient arguemnts\n");
  283. usage();
  284. return 1;
  285. }
  286. while(argv[1][0] =='-'){
  287. switch(argv[1][1]){
  288. case('c'):
  289. if(argv[1][2]== '\0'){
  290. fprintf(stderr,"-c flag needs filename.\n");
  291. return 1;
  292. }
  293. cuefilename = &(argv[1][2]);
  294. break;
  295. case('s'):
  296. if(have_S){
  297. fprintf(stderr,"njoin: cannot have both -s and -S.\n");
  298. return 1;
  299. }
  300. space_secs = atof(&argv[1][2]);
  301. if(space_secs < 0.0){
  302. fprintf(stderr,"njoin: -tSECS cannot be negative!\n");
  303. return 1;
  304. }
  305. have_s = 1;
  306. break;
  307. case('S'):
  308. if(have_s){
  309. fprintf(stderr,"njoin: cannot have both -s and -S.\n");
  310. return 1;
  311. }
  312. space_secs = atof(&argv[1][2]);
  313. if(space_secs < 0.0){
  314. fprintf(stderr,"njoin: -tSECS cannot be negative!\n");
  315. return 1;
  316. }
  317. have_S = 1;
  318. break;
  319. case 'x':
  320. strict = 1;
  321. break;
  322. default:
  323. fprintf(stderr,"\nnjoin: illegal flag option %s",argv[1]);
  324. exit(1);
  325. }
  326. argc--; argv++;
  327. }
  328. if(argc < ARG_NARGS){
  329. fprintf(stderr,"njoin: insufficient arguemnts\n");
  330. usage();
  331. return 1;
  332. }
  333. if(argc==ARG_NARGS)
  334. do_process = 0;
  335. /********** READ filelist *********/
  336. fp = fopen(argv[ARG_FLIST],"r");
  337. if(fp==NULL){
  338. fprintf(stderr,"njoin: Unable to open input file %s\n",argv[ARG_FLIST]);
  339. return 1;
  340. }
  341. memset(flist,0,MAXFILES * sizeof(char*));
  342. num_infiles = read_filelist(fp, flist);
  343. if(num_infiles < 0){
  344. fprintf(stderr,"njoin: file error reading filelist %s\n",argv[ARG_FLIST]);
  345. fclose(fp);
  346. return 1;
  347. }
  348. if(num_infiles ==0){
  349. fprintf(stderr,"njoin: filelist is empty!\n");
  350. fclose(fp);
  351. return 1;
  352. }
  353. if(num_infiles ==1){
  354. fprintf(stderr,"njoin: only one file listed - nothing to do!\n");
  355. fclose(fp);
  356. return 1;
  357. }
  358. fclose(fp); fp = NULL;
  359. #ifdef _DEBUG
  360. fprintf(stderr, "file list contains %d files: \n",num_infiles);
  361. for(i=0;i < num_infiles; i++)
  362. fprintf(stderr,"%s\n",flist[i]);
  363. #endif
  364. /********* open and check all soundfiles ***********/
  365. fprintf(stderr,"checking files...\n");
  366. if(psf_init()){
  367. fprintf(stderr,"njoin: startup failure.\n");
  368. return 1;
  369. }
  370. i = 0;
  371. #ifdef unix
  372. fname = CreatePathByExpandingTildePath(flist[i]);
  373. /* must free pointer later */
  374. #else
  375. fname = flist[i];
  376. #endif
  377. //open first infile and get properties
  378. ifd = psf_sndOpen(fname,&inprops,0);
  379. if(ifd < 0){
  380. fprintf(stderr,"unable to open infile %s.\n",fname);
  381. cleanup(ifd,ofd,flist,num_infiles);
  382. return 1;
  383. }
  384. thisdur = psf_sndSize(ifd);
  385. if(strict){
  386. if(!strict_check(&inprops,thisdur)){
  387. fprintf(stderr,"Strict: file %s is not CD-compatible.\n",fname);
  388. strict_failures++;
  389. }
  390. }
  391. if(thisdur==0){
  392. fprintf(stderr,"WARNING: file 1 empty: %s\n",fname);
  393. }
  394. else {
  395. totaldur += (double) thisdur / inprops.srate;
  396. }
  397. psf_sndClose(ifd);
  398. #ifdef unix
  399. free(fname);
  400. #endif
  401. ifd = -1;
  402. /* scan firther files, find max precision */
  403. /* drop out if channel formats different */
  404. for(i=1; i < num_infiles; i++){
  405. #ifdef unix
  406. fname = CreatePathByExpandingTildePath(flist[i]);
  407. /* must free pointer later */
  408. #else
  409. fname = flist[i];
  410. #endif
  411. ifd = psf_sndOpen(fname,&thisinprops,0);
  412. if(ifd < 0){
  413. fprintf(stderr,"unable to open infile %s.\n",fname);
  414. cleanup(ifd,ofd,flist,num_infiles);
  415. exit(1);
  416. }
  417. thisdur = psf_sndSize(ifd);
  418. if(strict){
  419. if(!strict_check(&thisinprops,thisdur)){
  420. fprintf(stderr,"Strict: file %s is not CD-compatible.\n",fname);
  421. strict_failures++;
  422. }
  423. }
  424. if(inprops.chans != thisinprops.chans){
  425. fprintf(stderr,"njoin: channel mismatch in file %s",fname);
  426. cleanup(ifd,ofd,flist,num_infiles);
  427. #ifdef unix
  428. free(fname);
  429. #endif
  430. return 1;
  431. }
  432. if(inprops.srate != thisinprops.srate){
  433. fprintf(stderr,"njoin: sample rate mismatch in file %s",fname);
  434. cleanup(ifd,ofd,flist,num_infiles);
  435. #ifdef unix
  436. free(fname);
  437. #endif
  438. return 1;
  439. }
  440. /* allow old multichannel files to be compatible with everything! */
  441. if(! (inprops.chformat==(psf_channelformat)PSF_STDWAVE || thisinprops.chformat==(psf_channelformat)PSF_STDWAVE)){
  442. if(inprops.chformat != thisinprops.chformat){
  443. fprintf(stderr,"njoin: channel format mismatch in file %s",fname);
  444. cleanup(ifd,ofd,flist,num_infiles);
  445. #ifdef unix
  446. free(fname);
  447. #endif
  448. return 1;
  449. }
  450. }
  451. else {
  452. /* one file is generic: promote format if possible*/
  453. if(thisinprops.chformat > inprops.chformat)
  454. inprops.chformat = thisinprops.chformat;
  455. }
  456. /* compare wordlength precision */
  457. if(compare_precision(&inprops,&thisinprops)) {
  458. inprops = thisinprops;
  459. formatsrc = i;
  460. }
  461. thisdur = psf_sndSize(ifd);
  462. if(thisdur==0){
  463. fprintf(stderr,"WARNING: file %d empty: %s\n",i+1,fname);
  464. }
  465. else {
  466. totaldur += (double) thisdur / thisinprops.srate;
  467. }
  468. psf_sndClose(ifd);
  469. ifd = -1;
  470. #ifdef unix
  471. free(fname);
  472. #endif
  473. }
  474. if(strict_failures){
  475. fprintf(stderr,"Strict: %d files are CD-incompatible. Exiting.\n",strict_failures);
  476. return 1;
  477. }
  478. fprintf(stderr, "output format taken from file %d:\n\t%s\n",formatsrc+1,flist[formatsrc]);
  479. fprintf(stderr, "sample type: %s\n",stype_as_string(&inprops));
  480. fprintf(stderr,"channel format: %s\n",chformat_as_string(&inprops));
  481. maxdur = (double)(max_datachunk/ inprops.chans / wordsize[inprops.samptype]) / inprops.srate;
  482. /*TODO: make sure we allow for size of PEAK chunk */
  483. fprintf(stderr, "Max duration available for this format: %f secs.\n",maxdur);
  484. if(have_S)
  485. fprintf(stderr,"Total outfile length including spacing: %f secs\n", totaldur + (num_infiles-1) * space_secs);
  486. else
  487. fprintf(stderr,"Total outfile length including spacing: %f secs\n", totaldur + (num_infiles) * space_secs);
  488. if(do_process == 0){
  489. if(totaldur > maxdur)
  490. fprintf(stderr, "Error: total duration exceeds capacity of file format.\n");
  491. cleanup(ifd,ofd,flist,num_infiles);
  492. return 0;
  493. }
  494. if(totaldur > maxdur){
  495. fprintf(stderr, "Sorry! Total duration exceeds capacity of file format.\nProcess aborted.\n");
  496. cleanup(ifd,ofd,flist,num_infiles);
  497. return 1;
  498. }
  499. /* if here, OK! We can make the file */
  500. printf("processing files...\n");
  501. outprops = inprops;
  502. /* try to make a legal wave file! */
  503. if((outprops.chans > 2 || outprops.samptype > PSF_SAMP_IEEE_FLOAT)
  504. && (outprops.format==PSF_STDWAVE))
  505. outprops.chformat = /* PSF_WAVE_EX */ MC_STD; //RWD 10:03:21
  506. block_frames = (long)(blockdur * outprops.srate);
  507. buflen = block_frames * outprops.chans;
  508. inframe = malloc(buflen * sizeof(float));
  509. if(inframe==NULL){
  510. puts("No memory!\n");
  511. cleanup(ifd,ofd,flist,num_infiles);
  512. return 1;
  513. }
  514. //setup PEAK data
  515. fpeaks = (PSF_CHPEAK *) calloc(outprops.chans,sizeof(PSF_CHPEAK));
  516. if(fpeaks==NULL){
  517. puts("njoin: error: no memory for internal PEAK buffer\n");
  518. cleanup(ifd,ofd,flist,num_infiles);
  519. return 1;
  520. }
  521. if(cuefilename){
  522. cuefp = fopen(cuefilename, "w");
  523. if(cuefp == NULL){
  524. fprintf(stderr, "WARNING: unable to create cue file %s.\n",cuefilename);
  525. cuefilename = NULL;
  526. }
  527. }
  528. space_frame = calloc(sizeof(float),outprops.chans);
  529. ofd = psf_sndCreate(argv[ARG_OUTFILE],&outprops,0,0,PSF_CREATE_RDWR);
  530. if(ofd < 0){
  531. fprintf(stderr,"njoin: Cannot create outfile %s\n",argv[ARG_OUTFILE]);
  532. cleanup(ifd,ofd,flist,num_infiles);
  533. return 1;
  534. }
  535. fprintf(stderr, "generating outfile...\n");
  536. written = 0;
  537. if(cuefp) {
  538. //fprintf(cuefp,"FILE %s WAVE\n",snd_getfilename(ofd));
  539. fprintf(cuefp,"FILE %s WAVE\n",argv[ARG_OUTFILE]);
  540. fprintf(cuefp,"\tTRACK 01 AUDIO\n");
  541. fprintf(cuefp,"\t\tINDEX 01 00:00:00\n");
  542. }
  543. space_frames = (long) (space_secs * outprops.srate + 0.5);
  544. /* add leading space ? */
  545. if(have_s && space_frames > 0){
  546. for(j=0; j < space_frames; j++) {
  547. if(psf_sndWriteFloatFrames(ofd,space_frame,1) < 0){
  548. fprintf(stderr,"njoin: error writing outfile\n");
  549. cleanup(ifd,ofd,flist,num_infiles);
  550. free(inframe);
  551. free(fpeaks);
  552. return 1;
  553. }
  554. }
  555. written += space_frames;
  556. }
  557. for(i=0; i < num_infiles; i++){
  558. long got, put;
  559. PSF_PROPS fprops; // dummy - not needed
  560. #ifdef unix
  561. fname = CreatePathByExpandingTildePath(flist[i]);
  562. /* must free pointer later */
  563. /*RWD TODO: may fail with null return if file does not exist */
  564. #else
  565. fname = flist[i];
  566. #endif
  567. ifd = psf_sndOpen(fname,&fprops,0);
  568. if(ifd < 0){
  569. fprintf(stderr,"unable to open infile %s.\n",fname);
  570. error++;
  571. break;
  572. }
  573. do {
  574. got = psf_sndReadFloatFrames(ifd,inframe,block_frames);
  575. if(got < 0){
  576. fprintf(stderr,"njoin: error reading file %s\n",fname);
  577. error++;
  578. break;
  579. }
  580. put = psf_sndWriteFloatFrames(ofd,inframe,got);
  581. if(put != got){
  582. fprintf(stderr,"njoin: error writing outfile\n");
  583. error++;
  584. break;
  585. }
  586. written += got;
  587. fprintf(stderr,"%.2f\r",(double) written / outprops.srate);
  588. } while (got > 0);
  589. if(error)
  590. break;
  591. /* add space */
  592. if(i < num_infiles - 1){
  593. /* update cue file */
  594. if(cuefp){
  595. double pos = (double)(psf_sndTell(ofd)) / outprops.srate;
  596. int mins = (int)(pos / 60.0);
  597. int secs = (int)(pos - mins);
  598. int frames = (int) ((pos - (60.0 * mins + secs)) * 75.0);
  599. fprintf(cuefp,"\tTRACK %.2d AUDIO\n",i+2);
  600. fprintf(cuefp,"\t\tINDEX 01 %.2d:%.2d:%.2d\n",mins,secs,frames);
  601. }
  602. for(j=0; j < space_frames ; j++) {
  603. if(psf_sndWriteFloatFrames(ofd,space_frame,1) < 0){
  604. fprintf(stderr,"njoin: error writing outfile\n");
  605. error++;
  606. break;
  607. }
  608. }
  609. }
  610. if(error)
  611. break;
  612. written += space_frames;
  613. #ifdef unix
  614. free(fname);
  615. #endif
  616. /*RWD Jan 2010 MUST close the file or we may run out of portsf slots! */
  617. psf_sndClose(ifd);
  618. ifd = -1;
  619. }
  620. if(error){
  621. fprintf(stderr,"Error: Outfile incomplete.\n");
  622. // sndunlink(ofd);
  623. }
  624. else {
  625. fprintf(stderr, "Done.\nWritten %ld frames to outfile\n",written);
  626. if(psf_sndReadPeaks(ofd,fpeaks,NULL) > 0){
  627. long i;
  628. double peaktime;
  629. printf("PEAK information:\n");
  630. for(i=0;i < inprops.chans;i++){
  631. double val = fpeaks[i].val;
  632. peaktime = (double) fpeaks[i].pos / (double) inprops.srate;
  633. if(val==0.0)
  634. printf("CH %ld:\t%.4f at %.4f secs\n", i+1, val,peaktime);
  635. else
  636. printf("CH %ld:\t%.4f (%.2fdB) at %.4f secs\n", i+1, val, 20.0*log10(val),peaktime);
  637. }
  638. }
  639. }
  640. if(inframe)
  641. free(inframe);
  642. if(fpeaks)
  643. free(fpeaks);
  644. if(cuefp)
  645. fclose(cuefp);
  646. cleanup(ifd,ofd,flist,num_infiles);
  647. psf_finish();
  648. return 0;
  649. }