tdelaymain.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688
  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. //tdelay.cpp
  21. //mono/stereo tapped delay line
  22. //RWD,CDP 1998
  23. //VERSION: initial, 0.01
  24. //usage: tdelay [-f] infile outfile feedback tapfile.txt [trailertime]
  25. // -f : floatsam output
  26. // feedback (double) - obvious...
  27. // tapfile.txt lines of at least two values: time amplitude [pan]
  28. // times must be increasing, non-identical
  29. // amplitude +- 1.0
  30. // pan -1.0--1.0: -1 = hard Left, 1= hard Right, 0.0 = Centre
  31. #include <stdio.h>
  32. #include <stdlib.h>
  33. #include <math.h>
  34. #include <string.h>
  35. extern "C" {
  36. #include <sfsys.h>
  37. }
  38. #include <props.h>
  39. #include "reverberator.h"
  40. #include <vector>
  41. #include <algorithm>
  42. using namespace std;
  43. typedef struct stereo_tap {
  44. double time;
  45. double amp;
  46. double pan;
  47. } STAP ;
  48. void usage(void);
  49. #define ROOT2 (1.4142136)
  50. enum pan_direction {SIGNAL_TO_LEFT,SIGNAL_TO_RIGHT};
  51. enum args {INFILE=1,OUTFILE,TAPGAIN,FEEDBACK,MIX,TAPFILE,TRTIME};
  52. void pancalc(double position,double *leftgain,double *rightgain);
  53. const char* cdp_version = "5.0.0";
  54. int main(int argc, char **argv)
  55. {
  56. int ifd,ofd,inchans;
  57. float feedback = 0.0f;
  58. float dryfac = 0.0f,wetfac = 1.0f;
  59. double trailertime = 0.0;
  60. double current_time = 0.0;
  61. deltap *l_taps = 0,*r_taps = 0;
  62. tapdelay *l_dline = 0, *r_dline = 0;
  63. unsigned int i,nchans = 1,ntaps = 0,trailersamps = 0;
  64. bool is_stereo = false;
  65. bool floatsams_wanted = false;
  66. bool mix_input = true;
  67. SFPROPS props;
  68. FILE *tapfile = 0;
  69. double sample_duration,tapgain;
  70. double max_lgain = 1.0,max_rgain= 1.0;
  71. float max_gain = 1.0f;
  72. double sr;
  73. CHPEAK *peaks;
  74. if((argc==2) && strcmp(argv[1],"--version")==0) {
  75. fprintf(stdout,"%s\n",cdp_version);
  76. fflush(stdout);
  77. return 0;
  78. }
  79. if(argc < TRTIME){
  80. usage();
  81. exit(1);
  82. }
  83. if(sflinit("tdelay")){
  84. sfperror("tdelay: initialisation");
  85. exit(1);
  86. }
  87. while(argv[1][0] == '-'){
  88. switch(argv[1][1]){
  89. //case('s'):
  90. // is_stereo = true;
  91. // break;
  92. case('f'):
  93. floatsams_wanted = true;
  94. break;
  95. default:
  96. fprintf(stderr,"\nillegal flag option %s",argv[1]);
  97. usage();
  98. exit(1);
  99. break;
  100. }
  101. argv++;
  102. argc--;
  103. }
  104. if(argc < TRTIME){
  105. usage();
  106. exit(1);
  107. }
  108. if((ifd = sndopenEx(argv[INFILE],0,CDP_OPEN_RDONLY)) < 0) {
  109. fprintf(stderr,"reverb: cannot open input file %s: %s\n", argv[1],rsferrstr);
  110. exit(1);
  111. }
  112. if(!snd_headread(ifd,&props)){
  113. fprintf(stderr,"\nerror reading infile header: %s\n",rsferrstr);
  114. sndcloseEx(ifd);
  115. exit(1);
  116. }
  117. if(props.type != wt_wave){
  118. fprintf(stderr,"\ninfile is not a waveform file");
  119. sndcloseEx(ifd);
  120. exit(1);
  121. }
  122. inchans = props.chans;
  123. if(inchans > 2){
  124. fprintf(stderr,"\nSorry - cannot process files with more than two channels!");
  125. sndcloseEx(ifd);
  126. exit(1);
  127. }
  128. sample_duration = 1.0 / props.srate;
  129. if(floatsams_wanted)
  130. props.samptype = FLOAT32;
  131. tapgain = atof(argv[TAPGAIN]);
  132. if(tapgain <= 0.0){
  133. fprintf(stderr,"\nError: tapgain must be > 0.0\n");
  134. exit(1);
  135. }
  136. feedback = (float) atof(argv[FEEDBACK]);
  137. if(feedback < -1.0f || feedback > 1.0f){
  138. fprintf(stderr,"\nfeedback out of range -1.0 to +1.0");
  139. usage();
  140. sndcloseEx(ifd);
  141. exit(1);
  142. }
  143. dryfac = (float) atof(argv[MIX]);
  144. if(dryfac < 0.0f || dryfac >= 1.0f){
  145. fprintf(stderr,"\nmix value %s out of range",argv[4]);
  146. usage();
  147. sndcloseEx(ifd);
  148. exit(1);
  149. }
  150. wetfac = 1.0f - dryfac;
  151. if(argc==TRTIME+1){
  152. trailertime = atof(argv[TRTIME]);
  153. if(trailertime < 0.0){
  154. fprintf(stderr,"\ntrailertime must be >= 0.0");
  155. exit(1);
  156. }
  157. trailersamps = (unsigned int) (trailertime * props.srate);
  158. }
  159. tapfile = fopen(argv[TAPFILE],"r");
  160. if(!tapfile){
  161. fprintf(stderr,"\nunable to open tapfile %s",argv[TAPFILE]);
  162. usage();
  163. sndcloseEx(ifd);
  164. exit(1);
  165. }
  166. //read all lines, into taps array
  167. vector<STAP> ltaps;
  168. STAP thistap;
  169. double this_time,this_amp,this_pan;
  170. char line[255];
  171. int linecount = 1;
  172. this_time = 0.0; this_amp = 0.0;
  173. bool read_error = false;
  174. vector<STAP>::iterator I;
  175. //RWD TODO: take a tap at time = 0.0 as spec of direct sound : amp and pan
  176. while(fgets(line,255,tapfile) != NULL){
  177. this_pan = 0.0;
  178. int got;
  179. if(line[0] == '\n' || line[0]== ';'){ //allow comment lines; this does not check for leading white-space...
  180. linecount++;
  181. continue;
  182. }
  183. if((got = sscanf(line,"%lf%lf%lf",&this_time,&this_amp,&this_pan)) < 2){
  184. fprintf(stderr,"\nerror in tapfile: line %d: insufficient parameters",linecount);
  185. read_error = true;
  186. break;
  187. }
  188. //time= 0 no good for a taptime!
  189. if(this_time < 0.0){
  190. fprintf(stderr,"\nerror in tapfile: line %d: bad time value",linecount);
  191. read_error = true;
  192. break;
  193. }
  194. if(this_time < sample_duration) { //non-zero time must be at least one sample on!
  195. fprintf(stderr,"\nWARNING: very small taptime %.4lf treated as zero - ignoring mix value",this_time);
  196. this_time = 0.0;
  197. mix_input = false;
  198. }
  199. if(current_time != 0.0 && this_time==current_time){
  200. fprintf(stderr,"\nWARNING: duplicate time in line %d: ignored",linecount);
  201. linecount++;
  202. continue;
  203. }
  204. if(this_time < current_time){
  205. fprintf(stderr,"\nerror in tapfile: time out of sequence: line %d",linecount);
  206. read_error = true;
  207. break;
  208. }
  209. current_time = this_time;
  210. thistap.time = this_time;
  211. if(this_amp < 0.0 || this_amp > 1.0){
  212. fprintf(stderr,"\nerror in tapfile: line %d: bad amplitude value",linecount);
  213. read_error = true;
  214. break;
  215. }
  216. thistap.amp = this_amp;
  217. if(got==3){
  218. thistap.pan = this_pan;
  219. is_stereo = true;
  220. nchans = 2;
  221. }
  222. else
  223. thistap.pan = 0.0;
  224. ltaps.push_back(thistap);
  225. linecount++;
  226. }
  227. if(read_error){
  228. sndcloseEx(ifd);
  229. exit(1);
  230. }
  231. ntaps = ltaps.size();
  232. if(ntaps==0){
  233. printf("\nfile contains no data!");
  234. sndcloseEx(ifd);
  235. exit(1);
  236. }
  237. if(!mix_input)
  238. printf("\nzero taptime used: taking input mix control from tapfile");
  239. #ifdef _DEBUG
  240. printf("\nread %d taps",ntaps);
  241. for(I = ltaps.begin(); I != ltaps.end();I++)
  242. printf("\n%.4lf\t%.4lf\t%.4lf",I->time,I->amp,I->pan); //or whatever.....
  243. printf("\nfeedback = %.4f",feedback);
  244. printf("\ndryfac = %.4f, wetfac = %.4f",dryfac,wetfac);
  245. #endif
  246. if(is_stereo)
  247. printf("\ncreating stereo outfile");
  248. else
  249. printf("\ncreating mono outfile");
  250. l_taps = new deltap[ntaps];
  251. if(is_stereo){
  252. r_taps = new deltap[ntaps];
  253. //and create stereo delay-lines
  254. int j;
  255. for(j = 0,I = ltaps.begin(); I != ltaps.end();j++,I++){
  256. //printf("\n%.4lf\t%.4lf\t%.4lf",I->time,I->amp,I->pan); //or whatever.....
  257. double l_gain,r_gain;
  258. pancalc(I->pan,&l_gain,&r_gain);
  259. l_taps[j].pos = r_taps[j].pos = I->time;
  260. l_taps[j].val = I->amp * l_gain;
  261. r_taps[j].val = I->amp * r_gain;
  262. }
  263. }
  264. //else create a mono delayline
  265. else {
  266. int j;
  267. for(j = 0,I = ltaps.begin(); I != ltaps.end();j++,I++){
  268. l_taps[j].pos = I->time;
  269. l_taps[j].val = I->amp;
  270. }
  271. }
  272. l_dline = new tapdelay(l_taps,ntaps,props.srate);
  273. if(!l_dline->create()){
  274. fputs("\nunable to create delay line - probably no memory",stderr);
  275. delete [] l_taps;
  276. sndcloseEx(ifd);
  277. exit(1);
  278. }
  279. max_lgain = l_dline->get_maxgain();
  280. if(is_stereo){
  281. r_dline = new tapdelay(r_taps,ntaps,props.srate);
  282. if(!r_dline->create()){
  283. fputs("\nunable to create stereo delay line - probably no memory",stderr);
  284. delete [] l_taps;
  285. delete [] r_taps;
  286. delete l_dline;
  287. sndcloseEx(ifd);
  288. exit(1);
  289. }
  290. max_rgain = r_dline->get_maxgain();
  291. }
  292. if(is_stereo)
  293. max_gain = (float) min(max_lgain,max_rgain);
  294. else
  295. max_gain = (float) max_lgain;
  296. /* recale max_gain by user param, eg:*/
  297. max_gain *= (float) tapgain;
  298. l_dline->set_gain((double)max_gain);
  299. if(is_stereo)
  300. r_dline->set_gain((double)max_gain);
  301. props.chans = nchans;
  302. peaks = (CHPEAK *) malloc(sizeof(CHPEAK) * nchans);
  303. if(peaks==NULL){
  304. fputs("No memory for PEAK data",stderr);
  305. exit(1);
  306. }
  307. for(i=0; i < nchans; i++){
  308. peaks[i].value = 0.0f;
  309. peaks[i].position = 0;
  310. }
  311. //OK, now open outfile!
  312. ofd = sndcreat_ex(argv[OUTFILE], -1,&props,SFILE_CDP,CDP_CREATE_NORMAL);
  313. //ofd = sndcreat_formatted(argv[OUTFILE],-1,outsamp_type,nchans,props.srate,CDP_CREATE_NORMAL);
  314. if(ofd < 0){
  315. printf("\nunable to open outfile %s",argv[OUTFILE]);
  316. delete [] l_taps;
  317. delete l_dline;
  318. if(r_taps)
  319. delete [] r_taps;
  320. sndcloseEx(ifd);
  321. exit(1);
  322. }
  323. printf("\n");
  324. float l_op = 0.0f;
  325. float r_op = 0.0f;
  326. float l_out = 0.0f;
  327. float r_out = 0.0f;
  328. long outsamps = 0;
  329. long step = (long) (0.25 * props.srate);
  330. //RWD.10.98 BIG TODO: optimize all this!
  331. sr = (double) props.srate;
  332. if(is_stereo){
  333. for(;;){
  334. int rv;
  335. float ip,l_ip,r_ip;
  336. if((rv = fgetfloatEx(&l_ip,ifd,0)) < 0){
  337. fprintf(stderr,"\nerror reading infile data");
  338. delete [] l_taps;
  339. delete l_dline;
  340. if(r_taps)
  341. delete [] r_taps;
  342. sndcloseEx(ifd);
  343. exit(1);
  344. }
  345. if(!rv)
  346. break;
  347. ip = l_ip;
  348. if(inchans==2){
  349. //mix stereo ip to mono for delay purposers
  350. if((rv = fgetfloatEx(&r_ip,ifd,0)) < 0){
  351. fprintf(stderr,"\nerror reading infile data");
  352. delete [] l_taps;
  353. delete l_dline;
  354. if(r_taps)
  355. delete [] r_taps;
  356. sndcloseEx(ifd);
  357. exit(1);
  358. }
  359. if(!rv)
  360. break;
  361. ip = (l_ip + r_ip) * 0.5f;
  362. }
  363. if(feedback != 0.0f) {
  364. l_op = l_dline->tick(ip + (l_op * feedback));
  365. r_op = r_dline->tick(ip + (r_op * feedback));
  366. }
  367. else{
  368. l_op = l_dline->tick(ip);
  369. r_op = r_dline->tick(ip);
  370. }
  371. //mix mono or stereo input with panned output:
  372. if(mix_input){
  373. l_out = l_op * wetfac + l_ip * dryfac;
  374. r_out = r_op * wetfac;
  375. if(inchans==2)
  376. r_out += r_ip * dryfac;
  377. else
  378. r_out += l_ip * dryfac;
  379. }
  380. else{
  381. l_out = l_op;
  382. r_out = r_op;
  383. }
  384. /* rescale */
  385. l_out *= max_gain;
  386. r_out *= max_gain;
  387. if((float)fabs((double)l_out) > peaks[0].value) {
  388. peaks[0].value = (float)fabs((double)l_out);
  389. peaks[0].position = outsamps;
  390. }
  391. if((float)fabs((double)r_out) > peaks[1].value) {
  392. peaks[1].value = (float)fabs((double)r_out);
  393. peaks[1].position = outsamps;
  394. }
  395. if(fputfloatEx(&l_out,ofd) < 1){
  396. fprintf(stderr,"\nerror writing to outfile: %s",sferrstr());
  397. delete [] l_taps;
  398. delete l_dline;
  399. if(r_taps)
  400. delete [] r_taps;
  401. sndcloseEx(ifd);
  402. exit(1);
  403. }
  404. if(fputfloatEx(&r_out,ofd) < 1){
  405. fprintf(stderr,"\nerror writing to outfile: %s",sferrstr());
  406. delete [] l_taps;
  407. delete l_dline;
  408. if(r_taps)
  409. delete [] r_taps;
  410. sndcloseEx(ifd);
  411. exit(1);
  412. }
  413. outsamps++;
  414. if((outsamps % step) == 0)
  415. //inform(step,props.srate);
  416. fprintf(stdout,"%.2f\r",(double)outsamps/sr);
  417. }
  418. for(i = 0; i < trailersamps;i++){
  419. if(feedback != 0.0f){
  420. l_op = l_dline->tick(feedback * l_op);
  421. r_op = r_dline->tick(feedback * r_op);
  422. }
  423. else {
  424. l_op = l_dline->tick(0.0f);
  425. r_op = r_dline->tick(0.0f);
  426. }
  427. if(mix_input){
  428. l_out = l_op * wetfac;
  429. r_out = r_op * wetfac;
  430. }
  431. else {
  432. l_out = l_op;
  433. r_out = r_op;
  434. }
  435. /* rescale */
  436. l_out *= max_gain;
  437. r_out *= max_gain;
  438. if((float)fabs((double)l_out) > peaks[0].value) {
  439. peaks[0].value = (float)fabs((double)l_out);
  440. peaks[0].position = outsamps;
  441. }
  442. if((float)fabs((double)r_out) > peaks[1].value) {
  443. peaks[1].value = (float)fabs((double)r_out);
  444. peaks[1].position = outsamps;
  445. }
  446. if(fputfloatEx(&l_out,ofd) < 1){
  447. fprintf(stderr,"\nerror writing to outfile: %s",sferrstr());
  448. delete [] l_taps;
  449. delete l_dline;
  450. if(r_taps)
  451. delete [] r_taps;
  452. sndcloseEx(ifd);
  453. exit(1);
  454. }
  455. if(fputfloatEx(&r_out,ofd) < 1){
  456. fprintf(stderr,"\nerror writing to outfile: %s",sferrstr());
  457. delete [] l_taps;
  458. delete l_dline;
  459. if(r_taps)
  460. delete [] r_taps;
  461. sndcloseEx(ifd);
  462. exit(1);
  463. }
  464. outsamps++;
  465. if((outsamps % step) == 0)
  466. //inform(step,props.srate);
  467. fprintf(stdout,"%.2f\r",(double)outsamps/sr);
  468. }
  469. }
  470. else{
  471. for(;;){
  472. int rv;
  473. float ip,l_ip,r_ip;
  474. if((rv = fgetfloatEx(&l_ip,ifd,0)) < 0){
  475. fprintf(stderr,"\nerror reading infile data");
  476. delete [] l_taps;
  477. delete l_dline;
  478. sndcloseEx(ifd);
  479. exit(1);
  480. }
  481. if(!rv)
  482. break;
  483. if(inchans==2){
  484. //mix stereo ip to mono for delay purposers
  485. if((rv = fgetfloatEx(&r_ip,ifd,0)) < 0){
  486. fprintf(stderr,"\nerror reading infile data");
  487. delete [] l_taps;
  488. delete l_dline;
  489. sndcloseEx(ifd);
  490. exit(1);
  491. }
  492. if(!rv)
  493. break;
  494. ip = (l_ip + r_ip) * 0.5f;
  495. }
  496. else
  497. ip = l_ip;
  498. if(feedback != 0.0f)
  499. l_op = l_dline->tick(ip + l_op * feedback);
  500. else
  501. l_op = l_dline->tick(ip);
  502. if(mix_input)
  503. l_out = l_op * wetfac + l_ip * dryfac;
  504. else
  505. l_out = l_op;
  506. /* rescale */
  507. l_out *= max_gain;
  508. if((float)fabs((double)l_out) > peaks[0].value) {
  509. peaks[0].value = (float)fabs((double)l_out);
  510. peaks[0].position = outsamps;
  511. }
  512. if(fputfloatEx(&l_out,ofd) < 1){
  513. fprintf(stderr,"\nerror writing to outfile");
  514. delete [] l_taps;
  515. delete l_dline;
  516. sndcloseEx(ifd);
  517. exit(1);
  518. }
  519. outsamps++;
  520. if((outsamps % step) == 0)
  521. //inform(step,props.srate);
  522. fprintf(stdout,"%.2f\r",(double)outsamps/sr);
  523. }
  524. for(i = 0; i < trailersamps;i++){
  525. l_op = l_dline->tick(feedback * l_op);
  526. if(mix_input)
  527. l_out = l_op * wetfac;
  528. else
  529. l_out = l_op;
  530. if((float)fabs((double)l_out) > peaks[0].value) {
  531. peaks[0].value = (float)fabs((double)l_out);
  532. peaks[0].position = outsamps;
  533. }
  534. if(fputfloatEx(&l_out,ofd) < 1){
  535. fprintf(stderr,"\nerror writing to outfile");
  536. delete [] l_taps;
  537. delete l_dline;
  538. sndcloseEx(ifd);
  539. exit(1);
  540. }
  541. outsamps++;
  542. if((outsamps % step) == 0)
  543. //inform(step,props.srate);
  544. fprintf(stdout,"%.2f\r",(double)outsamps/sr);
  545. }
  546. }
  547. fprintf(stdout,"%.2f\n",(double)outsamps/sr);
  548. printf("\nPEAK data:\n");
  549. for(i=0;i < nchans;i++)
  550. printf("Channel %u: %.4f at frame %d: %.4lf secs\n",i+1,
  551. peaks[i].value,peaks[i].position, (double) peaks[i].position / (double)props.srate);
  552. sndputpeaks(ofd,nchans,peaks);
  553. if(sndcloseEx(ofd) < 0){
  554. fprintf(stderr,"\nwarning: error closing outfile");
  555. }
  556. delete [] l_taps;
  557. delete l_dline;
  558. if(is_stereo) {
  559. delete [] r_taps;
  560. delete r_dline;
  561. }
  562. sndcloseEx(ifd);
  563. return 0;
  564. }
  565. void usage(void)
  566. {
  567. printf("\n******* STEREO MULTI-TAPPED DELAY WITH PANNING v1.0 1998 : CDP Release 4 ******\n");
  568. printf("\nusage:\n\ttapdelay [-f] infile outfile tapgain feedback mix taps.txt [trailtime]\n");
  569. printf("\n\t-f\t\twrite floating-point outfile");
  570. printf("\n\t\t\t(default: outfile has same format as infile)");
  571. printf("\n\ttapgain\t\tgain factor applied to output from delayline");
  572. printf("\n\t\t\t(tapgain > 0.0, typ 0.25)");
  573. printf("\n\tfeedback\trange: -1 <= feeedback <= 1.0");
  574. printf("\n\tmix\t\tproportion of source mixed with delay output.\n\t\t\trange: 0.0 <= mix < 1.0");
  575. printf("\n\ttrailtime\textra time in seconds (beyond infile dur) ");
  576. printf("\n\t\t\t for delays to play out.");
  577. printf("\n\tStereo inputs are mixed to mono at input to the delay line.\n");
  578. printf("\n\ttaps.txt consists of list of taps in the format:");
  579. printf("\n\ttime amp [pan]");
  580. printf("\n\n\tAll values are floating-point, one tap per line.");
  581. printf("\n\tTimes (seconds) must be increasing. Duplicate times are ignored.");
  582. printf("\n\tA zero time (no delay) overrides the mix parameter,");
  583. printf("\n\t and determines the level and pan of the (mono) input.");
  584. printf("\n\tAmp values must be in the range 0.0 to 1.0");
  585. printf("\n\tEmpty lines are permitted, as are lines starting with a semni-colon,");
  586. printf("\n\t which may be used for comments.");
  587. printf("\n\tIf a pan value is used in any line, outfile will be stereo.");
  588. printf("\n\tPan values are nominally in the range -1 to +1: 0 = centre (mono)");
  589. printf("\n\t within which constant-power panning is used.");
  590. printf("\n\tValues beyond these limits result in attenuation according to the");
  591. printf("\n\t inverse-square law, to suggest distance beyond the speakers.\n");
  592. }
  593. //TW's constant-power pan routine, even does inv-square reductions beyond the speakers
  594. void pancalc(double position,double *leftgain,double *rightgain)
  595. {
  596. int dirflag;
  597. double temp;
  598. double relpos;
  599. double reldist, invsquare;
  600. if(position < 0.0)
  601. dirflag = SIGNAL_TO_LEFT; /* signal on left */
  602. else
  603. dirflag = SIGNAL_TO_RIGHT;
  604. if(position < 0)
  605. relpos = -position;
  606. else
  607. relpos = position;
  608. if(relpos <= 1.0){ /* between the speakers */
  609. temp = 1.0 + (relpos * relpos);
  610. reldist = ROOT2 / sqrt(temp);
  611. temp = (position + 1.0) / 2.0;
  612. *rightgain = temp * reldist;
  613. *leftgain = (1.0 - temp ) * reldist;
  614. } else { /* outside the speakers */
  615. temp = (relpos * relpos) + 1.0; /*RWD: NB same in both cases! */
  616. reldist = sqrt(temp) / ROOT2; /* relative distance to source */
  617. invsquare = 1.0 / (reldist * reldist);
  618. if(dirflag == SIGNAL_TO_LEFT){
  619. *leftgain = invsquare;
  620. *rightgain = 0.0;
  621. } else { /* SIGNAL_TO_RIGHT */
  622. *rightgain = invsquare;
  623. *leftgain = 0;
  624. }
  625. }
  626. }