abfpan2.cpp 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  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. //abfpan2.cpp : write mono wave file into ambisonic B-format
  21. // Dec 2005 support .amb extension, and 3ch output
  22. // OCT 2009 portsf version, with 2nd-order encoding
  23. #include <stdio.h>
  24. #include <stdlib.h>
  25. #include <string.h>
  26. #include <math.h>
  27. #include <memory.h>
  28. #include "portsf.h"
  29. #ifndef M_PI
  30. #define M_PI (3.141592654)
  31. #endif
  32. #ifndef TWOPI
  33. #define TWOPI (6.283185307)
  34. #endif
  35. #ifdef unix
  36. /* in portsf.lib */
  37. extern "C" {
  38. int stricmp(const char *a, const char *b);
  39. }
  40. #endif
  41. enum {W,X,Y,Z,R,S,T,U,V};
  42. enum {ARG_PROGNAME, ARG_INFILE, ARG_OUTFILE,ARG_STARTPOS,ARG_ENDPOS,ARG_NARGS};
  43. void usage()
  44. {
  45. fprintf(stderr,"\nCDP MCTOOLS V1.0.1 beta (c) RWD,CDP 2010"
  46. "\n\nABFPAN2: apply fixed or orbiting 2nd-order B-Format pan to infile\n");
  47. fprintf(stderr,"usage : abfpan2 [-gGAIN][-w] [-p[DEG]] infile outfile startpos endpos\n"
  48. " infile : mono source.\n"
  49. " outfile: 2nd order B-format output.\n"
  50. " 0.0 <= startpos <= 1.0 (0.0 and 1.0 = Centre Front).\n");
  51. fprintf(stderr," endpos : endpos < 0.0 gives anticlockwise rotation,\n"
  52. " endpos > 0.0 gives clockwise rotation.\n"
  53. " Units give number of revolutions, fraction gives final position\n"
  54. " Set endpos = startpos for fixed pan\n"
  55. " -gGAIN : scale infile amplitude by GAIN (GAIN > 0).\n"
  56. " -w : Write standard soundfile (wave, aiff)\n"
  57. " Default: WAVEX B-Format; use .amb extension.\n"
  58. " -p[DEG]: write full 9-channel (periphonic) B-Format file.\n"
  59. " Default: write 5-channel (2nd-order horizontal) file.\n"
  60. " DEG: optional fixed height argument (degrees).\n"
  61. " Range = -180 to +180,\n"
  62. " where -90 = nadir, +90 = zenith (directly above).\n"
  63. " Default: DEG=0; height channels (Z,R,S,T) will be empty.\n"
  64. " NB: this program does not create a decoded output.\n"
  65. " Use FMDCODE to decode to choice of speaker layouts.\n"
  66. );
  67. fprintf(stderr,"\n");
  68. }
  69. int main(int argc,char *argv[])
  70. {
  71. int i,got,ifd, ofd;
  72. long srate = 44100;
  73. long outchans = 5;
  74. long do_peri = 0;
  75. int write_wav = 1;
  76. MYLONG peaktime;
  77. long outsize;
  78. float this_samp;
  79. int half_sec;
  80. long total_frames;
  81. double d_srate;
  82. char *sfname;
  83. double angle_incr;
  84. double start_angle = 0.0;
  85. double startpos,endpos;
  86. double gain = 1.0;
  87. double degree=0.0,elevation = 0.0;
  88. float abfsample[9];
  89. float outframe[5];
  90. float *p_frame;
  91. PSF_PROPS props;
  92. PSF_CHPEAK *peaks = NULL;
  93. /* CDP version number */
  94. if(argc==2 && (stricmp(argv[1],"--version")==0)){
  95. printf("1.0.1b\n");
  96. return 0;
  97. }
  98. if(argc < 5){
  99. usage();
  100. return 1;
  101. }
  102. if(psf_init()){
  103. fprintf(stderr,"\nabfpan2: Startup failure");
  104. return 1;
  105. }
  106. while(argv[1][0] =='-'){
  107. switch(argv[1][1]){
  108. case 'g':
  109. if(argv[1][2] == '\0'){
  110. fprintf(stderr,"abfpan2 Error: -g flag requires a value.\n");
  111. return 1;
  112. }
  113. gain = atof(&argv[1][2]);
  114. if(gain <= 0.0){
  115. printf("abfpan2: gain value must be positive!\n");
  116. return 1;
  117. }
  118. break;
  119. case 'p':
  120. if(argv[1][2] != '\0'){
  121. degree = atof(&argv[1][2]);
  122. if(degree < -180.0 || degree > 180.0){
  123. fprintf(stderr,"-p: degree value out of range.\n");
  124. return 1;
  125. }
  126. elevation = degree * (M_PI / 180.0);
  127. }
  128. outchans = 9;
  129. do_peri = 1;
  130. break;
  131. case 'w':
  132. write_wav = 0;
  133. break;
  134. default:
  135. fprintf(stderr,"\nabfpan: error: illegal flag option %s",argv[1]);
  136. return 1;
  137. }
  138. argc--; argv++;
  139. }
  140. if(argc < ARG_NARGS ){
  141. usage();
  142. return 1;
  143. }
  144. sfname = argv[ARG_INFILE];
  145. startpos = atof(argv[ARG_STARTPOS]);
  146. if(startpos < 0.0 || startpos > 1.0){
  147. fprintf(stderr,"abfpan2: startpos %.4lf out of range: must be between 0.0 and 1.0\n",startpos);
  148. return 1;
  149. }
  150. endpos = atof(argv[ARG_ENDPOS]);
  151. ifd = psf_sndOpen(sfname,&props,0);
  152. if(ifd < 0){
  153. fprintf(stderr,"abfpan2: unable toopen infile %s.\n",sfname);
  154. return 1;
  155. }
  156. if(props.chans != 1){
  157. fprintf(stderr,"abfpan2: infile must be mono.\n");
  158. psf_sndClose(ifd);
  159. return 1;
  160. }
  161. outsize = psf_sndSize(ifd);
  162. if(outsize <= 0){
  163. fprintf(stderr,"abfpan2: infile is empty!\n");
  164. psf_sndClose(ifd);
  165. return 1;
  166. }
  167. srate = props.srate;
  168. props.chans = outchans;
  169. if(!is_legalsize(outsize,&props)){
  170. fprintf(stderr,"error: outfile size exceeds capacity of format.\n");
  171. return 1;
  172. }
  173. start_angle = - (TWOPI * startpos); //we think of positive as clockwise at cmdline!
  174. angle_incr = TWOPI / outsize;
  175. angle_incr *= (endpos - startpos);
  176. if(write_wav == 0)
  177. props.format = PSF_STDWAVE;
  178. else {
  179. printf("Writing B-Format file.\n");
  180. props.format = PSF_WAVE_EX;
  181. props.chformat = MC_BFMT;
  182. }
  183. peaks = (PSF_CHPEAK*) malloc(sizeof(PSF_CHPEAK) * outchans);
  184. memset(peaks,0,sizeof(PSF_CHPEAK) * outchans);
  185. ofd = psf_sndCreate(argv[ARG_OUTFILE],&props,0,0,PSF_CREATE_RDWR);
  186. if(ofd < 0){
  187. fprintf(stderr,"abfpan2: can't create outfile %s.\n",sfname);
  188. return 1;
  189. }
  190. half_sec = srate / 2;
  191. total_frames = 0;
  192. d_srate = (double)srate;
  193. //should make one 360deg rotate over duration
  194. //TODO: make lookup_sin and lookup_cos ugens, taking angle arg
  195. if(do_peri)
  196. p_frame = abfsample;
  197. else
  198. p_frame = outframe;
  199. while((got = psf_sndReadFloatFrames(ifd,&this_samp,1))==1){
  200. double x,y,z,xx,yy,zz;
  201. x = cos(start_angle);
  202. y = sin(start_angle);
  203. if(elevation ==0.0)
  204. z = 0.0;
  205. else
  206. z = sin(elevation);
  207. xx = x * x;
  208. yy = y * y;
  209. zz = z * z;
  210. this_samp *= gain;
  211. if(do_peri) {
  212. abfsample[W] = (float) (this_samp * 0.7071);
  213. abfsample[X] = (float) (this_samp * x);
  214. abfsample[Y] = (float) (this_samp * y);
  215. abfsample[Z] = (float) (this_samp * z);
  216. abfsample[R] = (float) (this_samp * (1.5 * zz - 0.5)); /// ????
  217. abfsample[S] = (float) (this_samp * (2.0 * z * x));
  218. abfsample[T] = (float) (this_samp * (2.0 * y * z));
  219. abfsample[U] = (float) (this_samp * (xx-yy));
  220. abfsample[V] = (float) (this_samp * (2.0 * x * y));
  221. }
  222. else{
  223. outframe[0] = (float) (this_samp * 0.7071);
  224. outframe[1] = (float) (this_samp * x);
  225. outframe[2] = (float) (this_samp * y);
  226. outframe[3] = (float) (this_samp * (xx-yy));
  227. outframe[4] = (float) (this_samp * (2.0 * x * y));
  228. }
  229. if(0 > psf_sndWriteFloatFrames(ofd, p_frame,1)){
  230. fprintf(stderr,"abfpan2: error writing frame %ld\n",total_frames);
  231. return 1;
  232. }
  233. start_angle -= angle_incr;
  234. total_frames++;
  235. if(total_frames % half_sec ==0) {
  236. printf("%.2lf secs\r",(double)total_frames / d_srate);
  237. fflush(stdout);
  238. }
  239. }
  240. if(got != 0){
  241. fprintf(stderr,"abfpan2: warning: not all data was read.\n");
  242. }
  243. printf("%.4lf secs\nWritten %ld frames to %s.\n",(double)total_frames / d_srate,total_frames, argv[ARG_OUTFILE]);
  244. if(psf_sndReadPeaks( ofd,peaks,&peaktime)){
  245. printf("PEAK values:\n");
  246. for(i=0; i < outchans; i++){
  247. double val, dbval;
  248. val = (double) peaks[i].val;
  249. if(val > 0.0){
  250. dbval = 20.0 * log10(val);
  251. printf("CH %d: %.6f (%.2lfdB) at frame %u:\t%.4f secs\n",i,
  252. val,dbval,(unsigned int) peaks[i].pos,(double)peaks[i].pos / (double) props.srate);
  253. }
  254. else{
  255. printf("CH %d: %.6f (-infdB) at frame %u:\t%.4f secs\n",i,
  256. val,(unsigned int)peaks[i].pos,(double)peaks[i].pos / (double) props.srate);
  257. }
  258. }
  259. }
  260. psf_sndClose(ifd);
  261. psf_sndClose(ofd);
  262. psf_finish();
  263. return 0;
  264. }