fmdcode.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  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. //fmdcode.c : decode .amb file to various speaker layouts
  21. #include <stdio.h>
  22. #include <stdlib.h>
  23. #include <string.h>
  24. #include <math.h>
  25. #include <memory.h>
  26. #include <sys/types.h>
  27. #include <sys/timeb.h>
  28. #include <portsf.h>
  29. #include "fmdcode.h"
  30. #ifdef unix
  31. /* in portsf.lib */
  32. extern int stricmp(const char *a, const char *b);
  33. #endif
  34. /*
  35. Channel order is WXYZ,RSTUV,KLMNOPQ
  36. The number of channels defines the order of the soundfield:
  37. 2 channel = W+Y = "Mid-Side"
  38. 3 channel = h = 1st order horizontal
  39. 4 channel = f = 1st order 3-D
  40. 5 channel = hh = 2nd order horizontal
  41. 6 channel = fh = 2nd order horizontal + 1st order height (formerly
  42. called 2.5 order)
  43. 7 channel = hhh = 3rd order horizontal
  44. 8 channel = fhh = 3rd order horizontal + 1st order height
  45. 9 channel = ff = 2nd order 3-D
  46. 11 channel = ffh = 3rd order horizontal + 2nd order height
  47. 16 channel = fff = 3rd order 3-D
  48. Horizontal Height Soundfield Number of Channels
  49. order order type channels
  50. 1 0 horizontal 3 WXY
  51. 1 1 full-sphere 4 WXYZ
  52. 2 0 horizontal 5 WXY....UV
  53. 2 1 mixed-order 6 WXYZ...UV
  54. 2 2 full-sphere 9 WXYZRSTUV
  55. 3 0 horizontal 7 WXY....UV.....PQ
  56. 3 1 mixed-order 8 WXYZ...UV.....PQ
  57. 3 2 mixed-order 11 WXYZRSTUV.....PQ
  58. 3 3 full-sphere 16 WXYZRSTUVKLMNOPQ
  59. */
  60. enum {ARG_PROGNAME, ARG_INFILE,ARG_OUTFILE, ARG_LAYOUT,ARG_NARGS};
  61. #define N_BFORMATS (10)
  62. enum {FM_MONO,FM_STEREO,FM_SQUARE,FM_QUAD,FM_PENT,DM_5_0,DM_5_1,FM_HEX,FM_OCT1,FM_OCT2,FM_CUBE,FM_QUADCUBE,FM_NLAYOUTS};
  63. //static const int bformats[N_BFORMATS] = {2,3,4,5,6,7,8,9,11,16};
  64. static const int layout_chans[] = {1,2,4,4,5,5,6,6,8,8,8,8};
  65. void usage(void)
  66. {
  67. printf(
  68. "usage: fmdcode [-x][-w] infile outfile layout\n"
  69. " -w : write plain WAVE outfile format\n"
  70. " (.wav default - use generic wavex format).\n"
  71. " -x : write std WAVEX speaker positions to header\n"
  72. " (applies to compatible layouts only; requires .wav extension).\n"
  73. " layout : one of the choices below.\n"
  74. " Output channel order is anticlockwise from centre front\n"
  75. " except where indicated.\n"
  76. " Layouts indicated with * are compatible with WAVEX speaker position order. \n"
  77. " Available speaker layouts:\n"
  78. " 1 : * mono (= W signal only)\n"
  79. " 2 : * stereo (quasi mid/side, = W +- Y)\n"
  80. " 3 : square\n"
  81. " 4 : * quad FL,FR,RL,RR order\n"
  82. " 5 : pentagon\n"
  83. " 6 : * 5.0 surround (WAVEX order)\n"
  84. " 7 : * 5.1 surround (WAVEX order, silent LFE)\n"
  85. " 8 : hexagon\n"
  86. " 9 : octagon 1 (front pair, 45deg)\n"
  87. " 10 : octagon 2 (front centre speaker)\n"
  88. " 11 : cube (as 3, low-high interleaved. Csound-compatible.)\n"
  89. " 12 : * cube (as 4, low quad followed by high quad).\n"
  90. " NOTE: no shelf filters or NF compensation used.\n");
  91. }
  92. int main(int argc,char *argv[])
  93. {
  94. int i,ifd, ofd;
  95. int layout,inorder = 1;
  96. int got,halfsec;
  97. unsigned int framepos;
  98. int inchans,outchans;
  99. int outsize;
  100. int write_speakerpositions = 0;
  101. MYLONG peaktime;
  102. psf_channelformat chformat = MC_STD;
  103. psf_format outformat;
  104. char *sfname;
  105. float *frame = NULL;
  106. fmhcopyfunc copyfunc;
  107. fmhdecodefunc decodefunc = NULL;
  108. int write_wavex = 1;
  109. ABFSAMPLE abfsample;
  110. PSF_PROPS props;
  111. PSF_CHPEAK *peaks = NULL;
  112. float abfframe[16];
  113. /* CDP version number */
  114. if(argc==2 && (stricmp(argv[1],"--version")==0)){
  115. printf("1.0b\n");
  116. return 0;
  117. }
  118. if(argc < 3){
  119. printf("\nCDP MCTOOLS: FMDCODE v 1.0beta: RWD,CDP 2009\n"
  120. "Plain multi-layout decoder for .amb files.\n"
  121. "Regular layouts use standard Furse-Malham in-phase coefficients.\n"
  122. "5.x surround coefficients (maxre) from David Moore.\n");
  123. usage();
  124. return 1;
  125. }
  126. while(argv[1][0] =='-'){
  127. switch(argv[1][1]){
  128. case('w'):
  129. write_wavex = 0;
  130. break;
  131. case 'x':
  132. write_speakerpositions = 1;
  133. break;
  134. default:
  135. fprintf(stderr,"fmdcode: error: illegal flag option %s\n",argv[1]);
  136. return 1;
  137. }
  138. argc--; argv++;
  139. }
  140. if(argc < ARG_NARGS){
  141. usage();
  142. return 1;
  143. }
  144. if(psf_init()) {
  145. printf("failed to init psfsys\n");
  146. exit(1);
  147. }
  148. sfname = argv[ARG_OUTFILE];
  149. layout = atoi(argv[ARG_LAYOUT]);
  150. if(layout < 1 || layout > FM_NLAYOUTS+1){
  151. printf("Unsupported layout type.\n");
  152. return 1;
  153. }
  154. ifd = psf_sndOpen(argv[ARG_INFILE],&props,0);
  155. if(ifd < 0){
  156. fprintf(stderr,"unable toopen infile %s\n",argv[ARG_INFILE]);
  157. return 1;
  158. }
  159. inchans = props.chans;
  160. if(inchans > 4) {
  161. inorder = 2;
  162. printf("%d-channel input: performing 2nd-order decode.\n",inchans);
  163. }
  164. outsize = psf_sndSize(ifd);
  165. if(outsize <= 0){
  166. fprintf(stderr,"fmdcode: infile is empty!\n");
  167. psf_sndClose(ifd);
  168. return 1;
  169. }
  170. switch(inchans){
  171. case 3:
  172. copyfunc = fmhcopy_3;
  173. break;
  174. case 4:
  175. copyfunc = fmhcopy_4;
  176. break;
  177. case 5:
  178. copyfunc = fmhcopy_5;
  179. break;
  180. case 6:
  181. copyfunc = fmhcopy_6;
  182. break;
  183. case 7:
  184. copyfunc = fmhcopy_7;
  185. break;
  186. case 8:
  187. copyfunc = fmhcopy_8;
  188. break;
  189. case 9:
  190. copyfunc = fmhcopy_9;
  191. break;
  192. case 11:
  193. copyfunc = fmhcopy_11;
  194. break;
  195. case 16:
  196. copyfunc = fmhcopy_16;
  197. break;
  198. default:
  199. printf("file has unsupported number of channels (%d)\n",inchans);
  200. psf_sndClose(ifd);
  201. return 1;
  202. }
  203. //FM_MONO,FM_STEREO,FM_SQUARE,FM_PENT,FM_SURR,FM_SURR6,FM_HEX,FM_OCT1,FM_OCT2,FM_CUBE
  204. switch(layout-1){
  205. case FM_MONO:
  206. printf("Decoding to Mono\n");
  207. decodefunc = fm_i1_mono;
  208. if(write_wavex && write_speakerpositions)
  209. chformat = MC_MONO;
  210. break;
  211. case FM_STEREO:
  212. printf("Decoding to Stereo\n");
  213. decodefunc = fm_i1_stereo;
  214. if(write_wavex && write_speakerpositions)
  215. chformat = MC_STEREO;
  216. break;
  217. case FM_SQUARE:
  218. printf("Decoding to Square\n");
  219. if(inorder == 1)
  220. decodefunc = fm_i1_square;
  221. else
  222. decodefunc = fm_i2_square;
  223. break;
  224. case FM_QUAD:
  225. printf("Decoding to quad surround (WAVEX order)\n");
  226. if(inorder == 1)
  227. decodefunc = fm_i1_quad;
  228. else
  229. decodefunc = fm_i2_quad;
  230. if(write_wavex && write_speakerpositions)
  231. chformat = MC_QUAD;
  232. break;
  233. case FM_PENT:
  234. printf("Decoding to pentagon\n");
  235. if(inorder==1)
  236. decodefunc = fm_i1_pent;
  237. else
  238. decodefunc = fm_i2_pent;
  239. break;
  240. case DM_5_0:
  241. printf("Decoding to 5.0 surround (David Moore)\n");
  242. if(inorder==1)
  243. decodefunc = dm_i1_surr;
  244. else
  245. decodefunc = dm_i2_surr;
  246. if(write_wavex && write_speakerpositions)
  247. chformat = MC_SURR_5_0;
  248. break;
  249. case DM_5_1:
  250. printf("Decoding to 5.1 surround (David Moore)\n");
  251. if(inorder==1)
  252. decodefunc = dm_i1_surr6;
  253. else
  254. decodefunc = dm_i2_surr6;
  255. if(write_wavex && write_speakerpositions)
  256. chformat = MC_DOLBY_5_1;
  257. break;
  258. case FM_HEX:
  259. printf("Decoding to Hexagon\n");
  260. if(inorder==1)
  261. decodefunc = fm_i1_hex;
  262. else
  263. decodefunc = fm_i2_hex;
  264. break;
  265. case FM_OCT1:
  266. printf("Decoding to Octagon 1\n");
  267. if(inorder==1)
  268. decodefunc = fm_i1_oct1;
  269. else
  270. decodefunc = fm_i2_oct1;
  271. break;
  272. case FM_OCT2:
  273. printf("Decoding to Octagon 2\n");
  274. if(inorder==1)
  275. decodefunc = fm_i1_oct2;
  276. else
  277. decodefunc = fm_i2_oct2;
  278. break;
  279. case FM_CUBE:
  280. printf("Decoding to Cube (FM interleaved)\n");
  281. if(inorder==1)
  282. decodefunc = fm_i1_cube;
  283. else
  284. decodefunc = fm_i2_cube;
  285. break;
  286. case FM_QUADCUBE:
  287. printf("Decoding to Octagon 1 (WAVEX order)\n");
  288. if(inorder==1)
  289. decodefunc = fm_i1_cubex;
  290. else
  291. decodefunc = fm_i2_cubex;
  292. if(write_wavex && write_speakerpositions)
  293. chformat = MC_CUBE;
  294. break;
  295. }
  296. outformat = psf_getFormatExt(sfname);
  297. if(outformat >= PSF_AIFF){
  298. if(write_speakerpositions)
  299. printf("Warning: -x requires .wav format\n");
  300. }
  301. outchans = layout_chans[layout-1];
  302. frame = malloc(sizeof(float) * outchans);
  303. if(frame==NULL){
  304. puts("No Memory!\n");
  305. return 1;
  306. }
  307. props.chformat = STDWAVE;
  308. props.chans = outchans;
  309. if(!is_legalsize(outsize,&props)){
  310. fprintf(stderr,"error: outfile size exceeds capacity of format.\n");
  311. return 1;
  312. }
  313. /*TODO: set speaker pos when we can */
  314. if(write_wavex){
  315. props.chformat = chformat;
  316. props.format = PSF_WAVE_EX;
  317. }
  318. ofd = psf_sndCreate(sfname,&props,0,0,PSF_CREATE_RDWR);
  319. if(ofd < 0){
  320. fprintf(stderr,"can't create outfile %s\n",sfname);
  321. psf_sndClose(ifd);
  322. return 1;
  323. }
  324. peaks = (PSF_CHPEAK*) malloc(sizeof(PSF_CHPEAK) * outchans);
  325. memset(peaks,0,sizeof(PSF_CHPEAK) * outchans);
  326. halfsec = props.srate / 2;
  327. framepos = 0;
  328. printf("\ndecoding:\n");
  329. while((got = psf_sndReadFloatFrames(ifd,abfframe,1))==1){
  330. memset(&abfsample,0,sizeof(ABFSAMPLE));
  331. copyfunc(&abfsample,abfframe);
  332. decodefunc(&abfsample,frame,1);
  333. if(0 > psf_sndWriteFloatFrames(ofd,frame,1)){
  334. fprintf(stderr,"error writing to outfile\n");
  335. psf_sndClose(ifd);
  336. psf_sndClose(ofd);
  337. return 1;
  338. }
  339. if((framepos % halfsec) == 0){
  340. printf("%.2lf secs\r",(double) framepos / (double) props.srate);
  341. fflush(stdout);
  342. }
  343. framepos++;
  344. }
  345. if(got != 0){
  346. fprintf(stderr,"warning: not all data was read\n");
  347. }
  348. printf("\n%.4lf secs\nWritten %d frames to %s\n",(double)framepos / (double) props.srate,framepos,sfname);
  349. if(psf_sndReadPeaks( ofd,peaks,&peaktime)){
  350. printf("PEAK values:\n");
  351. for(i=0; i < outchans; i++){
  352. double val, dbval;
  353. val = (double) peaks[i].val;
  354. if(val > 0.0){
  355. dbval = 20.0 * log10(val);
  356. printf("CH %d: %.6f (%.2lfdB) at frame %u:\t%.4f secs\n",i,
  357. val,dbval,(unsigned int)peaks[i].pos,(double)peaks[i].pos / (double) props.srate);
  358. }
  359. else{
  360. printf("CH %d: %.6f (-infdB) at frame %u:\t%.4f secs\n",i,
  361. val,(unsigned int)peaks[i].pos,(double)peaks[i].pos / (double) props.srate);
  362. }
  363. }
  364. }
  365. printf("\n");
  366. psf_sndClose(ifd);
  367. psf_sndClose(ofd);
  368. if(peaks){
  369. free(peaks);
  370. }
  371. psf_finish();
  372. return 0;
  373. }