pvthreads.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487
  1. /*
  2. * Copyright (c) 1983-2020 Richard Dobson and Composers Desktop Project Ltd
  3. * http://www.rwdobson.com
  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. /* pvthreads.c: thread functions for pvplay */
  21. /* Jan 2014: completed pvx support */
  22. #include "pvplay.h"
  23. #include <assert.h>
  24. extern int file_playing;
  25. extern psfdata *g_pdata;
  26. __inline void stereo_interls(const float *in_l,const float *in_r,float *out,long insize)
  27. {
  28. long i;
  29. const float *pfl_l,*pfl_r;
  30. float*pfl_o;
  31. pfl_o = out;
  32. pfl_l = in_l;
  33. pfl_r = in_r;
  34. for(i=insize;i;--i){
  35. *pfl_o++ = *pfl_l++;
  36. *pfl_o++ = *pfl_r++;
  37. }
  38. }
  39. /* soundfile */
  40. /* writes decoded data to ring buffer */
  41. /* size of element is full m/c frame */
  42. // TODO: unix thread should return void*
  43. #ifdef unix
  44. /*int*/void* threadFunctionReadFromRawFile(void* ptr)
  45. #else
  46. unsigned int __stdcall threadFunctionReadFromRawFile(void* ptr)
  47. #endif
  48. {
  49. psfdata* pdata = (psfdata*)ptr;
  50. /* Mark thread started */
  51. pdata->flag = 0;
  52. //printf("thread: from_frame = %d, to_frame = %d, current_Frame = %d\n",pdata->from_frame, pdata->to_frame, pdata->current_frame);
  53. while (1) {
  54. ring_buffer_size_t nElementsProcessed = 0;
  55. ring_buffer_size_t elementsInBuffer = PaUtil_GetRingBufferWriteAvailable(&pdata->ringbuf);
  56. if (elementsInBuffer >= pdata->ringbuf.bufferSize / 4) {
  57. void* ptr[2] = {NULL};
  58. ring_buffer_size_t sizes[2] = {0};
  59. /* By using PaUtil_GetRingBufferWriteRegions, we can write directly into the ring buffer */
  60. PaUtil_GetRingBufferWriteRegions(&pdata->ringbuf, elementsInBuffer, &ptr[0], &sizes[0], &ptr[1], &sizes[1]);
  61. if (file_playing) {
  62. ring_buffer_size_t itemsReadFromFile;
  63. int i,j;
  64. int framesread = 0;
  65. int sampswanted = 0;
  66. int sampsread = 0;
  67. // we work with frames, = constant across inchans and outchans
  68. itemsReadFromFile = 0;
  69. for(i = 0; i < 2 && (ptr[i] != NULL); i++) {
  70. // NB ringbuf is sized by m/c frames
  71. int frameswanted = sizes[i];
  72. pdata->current_frame = sndtellEx(pdata->ifd) / pdata->inchans;
  73. // read up to end frame if requested
  74. if(pdata->to_frame < pdata->current_frame + frameswanted)
  75. frameswanted = pdata->to_frame - pdata->current_frame;
  76. if(frameswanted > 0){
  77. sampswanted = frameswanted * pdata->inchans;
  78. sampsread = fgetfbufEx(pdata->inbuf,sampswanted,pdata->ifd,0);
  79. framesread = sampsread / pdata->inchans;
  80. if(framesread < 0){ // read error!
  81. printf("Error reading soundfile: %s ifd = %d\n", sferrstr(),pdata->ifd);
  82. pdata->flag = 1;
  83. file_playing = 0;
  84. break; // just out of for loop - need to return instead?
  85. }
  86. }
  87. if(framesread == 0){
  88. /* EOF. EITHER: finish, or rewind if looping playback*/
  89. if(pdata->play_looped){
  90. if(sndseekEx(pdata->ifd,pdata->from_frame * pdata->inchans,SEEK_SET) < 0){
  91. printf("Error looping soundfile\n");
  92. pdata->flag = 1;
  93. file_playing = 0;
  94. break;
  95. }
  96. // sizes[1] especially may well = 0
  97. if(frameswanted==0)
  98. break;
  99. sampswanted = frameswanted * pdata->inchans;
  100. sampsread = fgetfbufEx(pdata->inbuf,sampswanted,pdata->ifd,0);
  101. framesread = sampsread / pdata->inchans;
  102. if(framesread < 0){ // read error!
  103. printf("Error reading soundfile\n");
  104. pdata->flag = 1;
  105. file_playing = 0;
  106. break;
  107. }
  108. }
  109. else {
  110. // we must watch the ring buffer to make sure all data has been rendered,
  111. // over several callback blocks
  112. //printf("End of data. playing = %d:\n", file_playing);
  113. //printf("\telements remaining = %d\n",elementsInBuffer);
  114. if(elementsInBuffer == pdata->ringbuf.bufferSize) {
  115. pdata->flag = 1;
  116. break;
  117. }
  118. }
  119. }
  120. else {
  121. // ringbuf calcs always relative to outchans
  122. itemsReadFromFile += framesread;
  123. // now ready to apply decoding or other processing
  124. if(pdata->gain != 1.0){
  125. for(j = 0; j < framesread * pdata->inchans; j++)
  126. pdata->inbuf[j] = (float)(pdata->inbuf[j] * pdata->gain);
  127. }
  128. // no channel mapping in pvplay
  129. if(pdata->do_decode) {
  130. ABFSAMPLE abfsamp;
  131. for(j=0; j < framesread; j++){
  132. // single frame only
  133. pdata->copyfunc(&abfsamp,pdata->inbuf + (j * pdata->inchans));
  134. /* BIG TODO: update funcs to process large frame buffer! */
  135. /* NB: ring buffer is effectively defined as raw bytes */
  136. pdata->decodefunc(&abfsamp, (float*)(ptr[i]) + (j * pdata->outchans), 1);
  137. nElementsProcessed++;
  138. }
  139. }
  140. else { // inchans = outchans
  141. memcpy(ptr[i],pdata->inbuf,framesread * sizeof(float) * pdata->inchans);
  142. nElementsProcessed += framesread;
  143. }
  144. }
  145. }
  146. PaUtil_AdvanceRingBufferWriteIndex(&pdata->ringbuf, itemsReadFromFile);
  147. }
  148. else {
  149. // this code is activated on Ctrl-C. Can do immediate finish by setting flag
  150. // printf("file done\n");
  151. pdata->flag = 1;
  152. break;
  153. }
  154. }
  155. // else {
  156. // printf("ringbuf size = %d, elements remaining = %d, playing = %d\n",pdata->ringbuf.bufferSize,elementsInBuffer,file_playing);
  157. // }
  158. /* Sleep a little while...! */
  159. Pa_Sleep(10);
  160. }
  161. return 0;
  162. }
  163. /* versions for .ana file (always mono only) */
  164. #ifdef unix
  165. /*int*/void* threadFunctionReadFromAnalFile(void* ptr)
  166. #else
  167. unsigned int __stdcall threadFunctionReadFromAnalFile(void* ptr)
  168. #endif
  169. {
  170. psfdata* pdata = (psfdata*)ptr;
  171. /* Mark thread started */
  172. pdata->flag = 0;
  173. while (1) {
  174. ring_buffer_size_t nElementsProcessed = 0;
  175. ring_buffer_size_t elementsInBuffer = PaUtil_GetRingBufferWriteAvailable(&pdata->ringbuf);
  176. if (elementsInBuffer >= pdata->ringbuf.bufferSize / 4) {
  177. void* ptr[2] = {NULL};
  178. ring_buffer_size_t sizes[2] = {0};
  179. //read and analyse as many frames as resynth space is available for, into inbuf
  180. // then distribute to ringbuf sections as necessary
  181. //printf("from_frame = %d,to_frame = %d\n",pdata->from_frame,pdata->to_frame);
  182. int framestoget = elementsInBuffer / pdata->fftsize;
  183. //assert(framestoget); // must be at least one, or ring buffer is too small!
  184. if(framestoget==0)
  185. break;
  186. float *pbuf = pdata->inbuf;
  187. int i, got = 0;
  188. int elementstowrite = 0;
  189. if (file_playing) {
  190. /* find out where we are ... */
  191. long framepos1 = sndtellEx(pdata->ifd) / pdata->anal_framesize;
  192. for(i = 0;i < framestoget;i++){
  193. /* read one frame*/
  194. got = fgetfbufEx(pdata->analframe1,pdata->anal_framesize,pdata->ifd,0);
  195. if(got == -1) {
  196. fprintf(stderr,"\nError reading from sfile\n");
  197. file_playing = 0;
  198. break;
  199. }
  200. if(got != pdata->anal_framesize) {
  201. if(got > 0) {
  202. fprintf(stderr,"Infile error: incomplete analysis frame encountered\n");
  203. file_playing = 0;
  204. break;
  205. }
  206. }
  207. framepos1++;
  208. if(got && (framepos1 <= pdata->to_frame)) {
  209. long samps = pdata->pv_l->process_frame(pdata->analframe1,pbuf,PVOC_AMP_FREQ);
  210. elementstowrite += samps;
  211. pbuf += samps;
  212. }
  213. if (got==0 || framepos1 >= pdata->to_frame){
  214. if(pdata->play_looped) {
  215. long pos;
  216. pos = sndseekEx(pdata->ifd,pdata->from_frame * pdata->anal_framesize,SEEK_SET);
  217. if(pos < 0){
  218. fprintf(stderr,"Error rewinding frame loop.\n");
  219. file_playing = 0;
  220. break;
  221. }
  222. }
  223. else if(elementsInBuffer == pdata->ringbuf.bufferSize) {
  224. //printf("buffer empty!\n");
  225. pdata->flag = 1;
  226. break;
  227. }
  228. }
  229. }
  230. //apply gain to inbuf first, then copy to ringbuf
  231. if(pdata->gain != 1.0) {
  232. for(i=0;i < elementstowrite;i++)
  233. pdata->inbuf[i] = (float)(pdata->inbuf[i] * pdata->gain);
  234. }
  235. /* By using PaUtil_GetRingBufferWriteRegions, we can write directly into the ring buffer */
  236. PaUtil_GetRingBufferWriteRegions(&pdata->ringbuf, elementsInBuffer, &ptr[0], &sizes[0], &ptr[1], &sizes[1]);
  237. pbuf = pdata->inbuf;
  238. //printf("ring buffer sizes : %d:%d",(int)sizes[0], (int)sizes[1]);
  239. for(i = 0; i < 2; i++) {
  240. if(ptr[i]) {
  241. int frameswanted = sizes[i];
  242. memcpy(ptr[i],pbuf,frameswanted * sizeof(float) * pdata->inchans);
  243. pbuf += frameswanted * pdata->inchans;
  244. nElementsProcessed += frameswanted;
  245. }
  246. }
  247. if(elementstowrite)
  248. PaUtil_AdvanceRingBufferWriteIndex(&pdata->ringbuf, elementstowrite);
  249. } // file_playing
  250. else {
  251. // this code is activated on Ctrl-C. Can do immediate finish by setting flag
  252. // printf("file done\n");
  253. pdata->flag = 1;
  254. break;
  255. }
  256. }
  257. /* Sleep a little while... */
  258. Pa_Sleep(5);
  259. }
  260. return 0;
  261. }
  262. // version for pvx format, just mono and stereo supported for now
  263. #ifdef unix
  264. /*int*/void* threadFunctionReadFromPVXFile(void* ptr)
  265. #else
  266. unsigned int __stdcall threadFunctionReadFromPVXFile(void* ptr)
  267. #endif
  268. {
  269. psfdata* pdata = (psfdata*)ptr;
  270. /* Mark thread started */
  271. pdata->flag = 0;
  272. while (1) {
  273. ring_buffer_size_t nElementsProcessed = 0;
  274. ring_buffer_size_t elementsInBuffer = PaUtil_GetRingBufferWriteAvailable(&pdata->ringbuf);
  275. // we remember that an element is a frame (mono or stereo)
  276. if (elementsInBuffer >= pdata->ringbuf.bufferSize / 4) {
  277. void* ptr[2] = {NULL};
  278. ring_buffer_size_t sizes[2] = {0};
  279. //read and analyse as many frames as resynth space is available for, into inbuf
  280. // then distribute to ringbuf sections as necessary
  281. //printf("from_frame = %d,to_frame = %d\n",pdata->from_frame,pdata->to_frame);
  282. /* pvx : file framecount is of single frames - need enough space to synth a single mc analysis frame */
  283. /* but to_frame and from_frame are m/c frame count */
  284. int framestoget = (elementsInBuffer / pdata->fftsize);
  285. if(framestoget==0)
  286. break;
  287. int i, got = 0;
  288. int elementstowrite = 0;
  289. if(pdata->inchans==1) {
  290. float *pbuf = pdata->inbuf;
  291. if (file_playing) {
  292. /* find out where we are ...multi-chan frame count */
  293. long framepos = pvoc_framepos(pdata->pvfile) / pdata->inchans; // NB
  294. if(framepos < 0){
  295. fprintf(stderr,"\nError reading file frame position\n");
  296. file_playing = 0;
  297. break;
  298. }
  299. for(i = 0;i < framestoget;i++){
  300. /* read one frame*/
  301. got = pvoc_getframes(pdata->pvfile,pdata->analframe1,1);
  302. if(got == -1) {
  303. fprintf(stderr,"\nError reading from sfile\n");
  304. file_playing = 0;
  305. break;
  306. }
  307. framepos++;
  308. if(got && (framepos <= pdata->to_frame)) {
  309. long samps = pdata->pv_l->process_frame(pdata->analframe1,pbuf,PVOC_AMP_FREQ);
  310. elementstowrite += samps;
  311. pbuf += samps;
  312. }
  313. if (got==0 || framepos >= pdata->to_frame){
  314. if(pdata->play_looped) {
  315. if(pvoc_seek_mcframe(pdata->pvfile,pdata->from_frame,SEEK_SET)) {
  316. fprintf(stderr,"Error rewinding frame loop.\n");
  317. file_playing = 0;
  318. break;
  319. }
  320. }
  321. else if(elementsInBuffer == pdata->ringbuf.bufferSize) {
  322. //printf("buffer empty!\n");
  323. pdata->flag = 1;
  324. break;
  325. }
  326. }
  327. } // framestoget
  328. //apply gain to inbuf first, then copy to ringbuf
  329. if(pdata->gain != 1.0) {
  330. for(i=0;i < elementstowrite;i++)
  331. pdata->inbuf[i] = (float)(pdata->inbuf[i] * pdata->gain);
  332. }
  333. /* By using PaUtil_GetRingBufferWriteRegions, we can write directly into the ring buffer */
  334. PaUtil_GetRingBufferWriteRegions(&pdata->ringbuf, elementsInBuffer, &ptr[0], &sizes[0], &ptr[1], &sizes[1]);
  335. pbuf = pdata->inbuf;
  336. //printf("ring buffer sizes : %d:%d",(int)sizes[0], (int)sizes[1]);
  337. for(i = 0; i < 2; i++) {
  338. if(ptr[i]) {
  339. int frameswanted = sizes[i];
  340. memcpy(ptr[i],pbuf,frameswanted * sizeof(float) * pdata->inchans);
  341. pbuf += frameswanted * pdata->inchans;
  342. nElementsProcessed += frameswanted;
  343. }
  344. }
  345. if(elementstowrite)
  346. PaUtil_AdvanceRingBufferWriteIndex(&pdata->ringbuf, elementstowrite);
  347. } // file_playing
  348. else {
  349. // this code is activated on Ctrl-C. Can do immediate finish by setting flag
  350. // printf("file done\n");
  351. pdata->flag = 1;
  352. break;
  353. }
  354. } //inchans==1
  355. else if (pdata->inchans==2) {
  356. float *pbuf = pdata->outbuf_l;
  357. float *pbuf_r = pdata->outbuf_r;
  358. // framestoget is count of m/c analysis frames to read
  359. if(file_playing){
  360. /* find out where we are ...multi-chan frame count */
  361. long framepos = pvoc_framepos(pdata->pvfile) / pdata->inchans; // NB
  362. if(framepos < 0){
  363. fprintf(stderr,"\nError reading file frame position\n");
  364. file_playing = 0;
  365. break;
  366. }
  367. for(i = 0;i < framestoget;i++){
  368. /* read one stereo frame*/
  369. got = pvoc_getframes(pdata->pvfile,pdata->analframe1,1);
  370. if(got == -1) {
  371. fprintf(stderr,"\nError reading ch 1 from pvx file\n");
  372. file_playing = 0;
  373. break;
  374. }
  375. if(got){
  376. got = pvoc_getframes(pdata->pvfile,pdata->analframe2,1);
  377. if(got == -1) {
  378. fprintf(stderr,"\nError reading ch 2 from pvx file, frame %lu\n",framepos);
  379. file_playing = 0;
  380. break;
  381. }
  382. }
  383. framepos++;
  384. if(got && (framepos <= pdata->to_frame)) {
  385. // each call returns <overlap> samples
  386. long samps = pdata->pv_l->process_frame(pdata->analframe1,pbuf,PVOC_AMP_FREQ);
  387. pbuf += samps;
  388. samps = pdata->pv_r->process_frame(pdata->analframe2,pbuf_r,PVOC_AMP_FREQ);
  389. pbuf_r += samps;
  390. elementstowrite += samps; // element is a stereo sample frame
  391. }
  392. if (got==0 || framepos >= pdata->to_frame){
  393. if(pdata->play_looped) {
  394. //pos = sndseekEx(pdata->ifd,pdata->from_frame * pdata->anal_framesize,SEEK_SET);
  395. //if(pvoc_rewind(pdata->pvfile,1)){ /* 1 = skip empty frame */
  396. if(pvoc_seek_mcframe(pdata->pvfile,pdata->from_frame,SEEK_SET)) {
  397. fprintf(stderr,"Error rewinding frame loop.\n");
  398. file_playing = 0;
  399. break;
  400. }
  401. }
  402. else if(elementsInBuffer == pdata->ringbuf.bufferSize) {
  403. //printf("buffer empty!\n");
  404. pdata->flag = 1;
  405. break;
  406. }
  407. }
  408. } // framestogeet
  409. assert(elementstowrite <= elementsInBuffer);
  410. //apply gain to inbuf first, then copy to ringbuf
  411. pbuf = pdata->outbuf_l;
  412. pbuf_r = pdata->outbuf_r;
  413. stereo_interls(pbuf,pbuf_r,pdata->inbuf,elementstowrite);
  414. if(pdata->gain != 1.0) {
  415. for(i=0;i < elementstowrite * pdata->inchans;i++)
  416. pdata->inbuf[i] = (float)(pdata->inbuf[i] * pdata->gain);
  417. }
  418. /* By using PaUtil_GetRingBufferWriteRegions, we can write directly into the ring buffer */
  419. PaUtil_GetRingBufferWriteRegions(&pdata->ringbuf, elementsInBuffer, &ptr[0], &sizes[0], &ptr[1], &sizes[1]);
  420. pbuf = pdata->inbuf;
  421. //printf("ring buffer sizes : %d:%d",(int)sizes[0], (int)sizes[1]);
  422. for(i = 0; i < 2; i++) {
  423. if(ptr[i]) {
  424. int frameswanted = sizes[i];
  425. memcpy(ptr[i],pbuf,frameswanted * sizeof(float) * pdata->inchans);
  426. pbuf += frameswanted * pdata->inchans;
  427. nElementsProcessed += frameswanted;
  428. }
  429. }
  430. if(elementstowrite)
  431. PaUtil_AdvanceRingBufferWriteIndex(&pdata->ringbuf, elementstowrite);
  432. } // file_playing
  433. else {
  434. pdata->flag = 1;
  435. break;
  436. }
  437. }
  438. }
  439. /* Sleep a little while... */
  440. Pa_Sleep(5);
  441. }
  442. return 0;
  443. }