pvthreads.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488
  1. /*
  2. * Copyright (c) 1983-2023 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. // unsigned: we have to trust no error from sndtellEx!
  192. unsigned long framepos1 = (unsigned long) (sndtellEx(pdata->ifd) / pdata->anal_framesize);
  193. for(i = 0;i < framestoget;i++){
  194. /* read one frame*/
  195. got = fgetfbufEx(pdata->analframe1,pdata->anal_framesize,pdata->ifd,0);
  196. if(got == -1) {
  197. fprintf(stderr,"\nError reading from sfile\n");
  198. file_playing = 0;
  199. break;
  200. }
  201. if(got != pdata->anal_framesize) {
  202. if(got > 0) {
  203. fprintf(stderr,"Infile error: incomplete analysis frame encountered\n");
  204. file_playing = 0;
  205. break;
  206. }
  207. }
  208. framepos1++;
  209. if(got && (framepos1 <= pdata->to_frame)) {
  210. long samps = pdata->pv_l->process_frame(pdata->analframe1,pbuf,PVOC_AMP_FREQ);
  211. elementstowrite += samps;
  212. pbuf += samps;
  213. }
  214. if (got==0 || framepos1 >= pdata->to_frame){
  215. if(pdata->play_looped) {
  216. long pos;
  217. pos = sndseekEx(pdata->ifd,pdata->from_frame * pdata->anal_framesize,SEEK_SET);
  218. if(pos < 0){
  219. fprintf(stderr,"Error rewinding frame loop.\n");
  220. file_playing = 0;
  221. break;
  222. }
  223. }
  224. else if(elementsInBuffer == pdata->ringbuf.bufferSize) {
  225. //printf("buffer empty!\n");
  226. pdata->flag = 1;
  227. break;
  228. }
  229. }
  230. }
  231. //apply gain to inbuf first, then copy to ringbuf
  232. if(pdata->gain != 1.0) {
  233. for(i=0;i < elementstowrite;i++)
  234. pdata->inbuf[i] = (float)(pdata->inbuf[i] * pdata->gain);
  235. }
  236. /* By using PaUtil_GetRingBufferWriteRegions, we can write directly into the ring buffer */
  237. PaUtil_GetRingBufferWriteRegions(&pdata->ringbuf, elementsInBuffer, &ptr[0], &sizes[0], &ptr[1], &sizes[1]);
  238. pbuf = pdata->inbuf;
  239. //printf("ring buffer sizes : %d:%d",(int)sizes[0], (int)sizes[1]);
  240. for(i = 0; i < 2; i++) {
  241. if(ptr[i]) {
  242. int frameswanted = sizes[i];
  243. memcpy(ptr[i],pbuf,frameswanted * sizeof(float) * pdata->inchans);
  244. pbuf += frameswanted * pdata->inchans;
  245. // nElementsProcessed += frameswanted;
  246. }
  247. }
  248. if(elementstowrite)
  249. PaUtil_AdvanceRingBufferWriteIndex(&pdata->ringbuf, elementstowrite);
  250. } // file_playing
  251. else {
  252. // this code is activated on Ctrl-C. Can do immediate finish by setting flag
  253. // printf("file done\n");
  254. pdata->flag = 1;
  255. break;
  256. }
  257. }
  258. /* Sleep a little while... */
  259. Pa_Sleep(5);
  260. }
  261. return 0;
  262. }
  263. // version for pvx format, just mono and stereo supported for now
  264. #ifdef unix
  265. /*int*/void* threadFunctionReadFromPVXFile(void* ptr)
  266. #else
  267. unsigned int __stdcall threadFunctionReadFromPVXFile(void* ptr)
  268. #endif
  269. {
  270. psfdata* pdata = (psfdata*)ptr;
  271. /* Mark thread started */
  272. pdata->flag = 0;
  273. while (1) {
  274. // ring_buffer_size_t nElementsProcessed = 0;
  275. ring_buffer_size_t elementsInBuffer = PaUtil_GetRingBufferWriteAvailable(&pdata->ringbuf);
  276. // we remember that an element is a frame (mono or stereo)
  277. if (elementsInBuffer >= pdata->ringbuf.bufferSize / 4) {
  278. void* ptr[2] = {NULL};
  279. ring_buffer_size_t sizes[2] = {0};
  280. //read and analyse as many frames as resynth space is available for, into inbuf
  281. // then distribute to ringbuf sections as necessary
  282. //printf("from_frame = %d,to_frame = %d\n",pdata->from_frame,pdata->to_frame);
  283. /* pvx : file framecount is of single frames - need enough space to synth a single mc analysis frame */
  284. /* but to_frame and from_frame are m/c frame count */
  285. int framestoget = (elementsInBuffer / pdata->fftsize);
  286. if(framestoget==0)
  287. break;
  288. int i, got = 0;
  289. int elementstowrite = 0;
  290. if(pdata->inchans==1) {
  291. float *pbuf = pdata->inbuf;
  292. if (file_playing) {
  293. /* find out where we are ...multi-chan frame count */
  294. long framepos = pvoc_framepos(pdata->pvfile) / pdata->inchans; // NB
  295. if(framepos < 0){
  296. fprintf(stderr,"\nError reading file frame position\n");
  297. file_playing = 0;
  298. break;
  299. }
  300. for(i = 0;i < framestoget;i++){
  301. /* read one frame*/
  302. got = pvoc_getframes(pdata->pvfile,pdata->analframe1,1);
  303. if(got == -1) {
  304. fprintf(stderr,"\nError reading from sfile\n");
  305. file_playing = 0;
  306. break;
  307. }
  308. framepos++;
  309. if(got && (framepos <= (long) pdata->to_frame)) {
  310. long samps = pdata->pv_l->process_frame(pdata->analframe1,pbuf,PVOC_AMP_FREQ);
  311. elementstowrite += samps;
  312. pbuf += samps;
  313. }
  314. if (got==0 || framepos >= (long) pdata->to_frame){
  315. if(pdata->play_looped) {
  316. if(pvoc_seek_mcframe(pdata->pvfile,pdata->from_frame,SEEK_SET)) {
  317. fprintf(stderr,"Error rewinding frame loop.\n");
  318. file_playing = 0;
  319. break;
  320. }
  321. }
  322. else if(elementsInBuffer == pdata->ringbuf.bufferSize) {
  323. //printf("buffer empty!\n");
  324. pdata->flag = 1;
  325. break;
  326. }
  327. }
  328. } // framestoget
  329. //apply gain to inbuf first, then copy to ringbuf
  330. if(pdata->gain != 1.0) {
  331. for(i=0;i < elementstowrite;i++)
  332. pdata->inbuf[i] = (float)(pdata->inbuf[i] * pdata->gain);
  333. }
  334. /* By using PaUtil_GetRingBufferWriteRegions, we can write directly into the ring buffer */
  335. PaUtil_GetRingBufferWriteRegions(&pdata->ringbuf, elementsInBuffer, &ptr[0], &sizes[0], &ptr[1], &sizes[1]);
  336. pbuf = pdata->inbuf;
  337. //printf("ring buffer sizes : %d:%d",(int)sizes[0], (int)sizes[1]);
  338. for(i = 0; i < 2; i++) {
  339. if(ptr[i]) {
  340. int frameswanted = sizes[i];
  341. memcpy(ptr[i],pbuf,frameswanted * sizeof(float) * pdata->inchans);
  342. pbuf += frameswanted * pdata->inchans;
  343. // nElementsProcessed += frameswanted;
  344. }
  345. }
  346. if(elementstowrite)
  347. PaUtil_AdvanceRingBufferWriteIndex(&pdata->ringbuf, elementstowrite);
  348. } // file_playing
  349. else {
  350. // this code is activated on Ctrl-C. Can do immediate finish by setting flag
  351. // printf("file done\n");
  352. pdata->flag = 1;
  353. break;
  354. }
  355. } //inchans==1
  356. else if (pdata->inchans==2) {
  357. float *pbuf = pdata->outbuf_l;
  358. float *pbuf_r = pdata->outbuf_r;
  359. // framestoget is count of m/c analysis frames to read
  360. if(file_playing){
  361. /* find out where we are ...multi-chan frame count */
  362. long framepos = pvoc_framepos(pdata->pvfile) / pdata->inchans; // NB
  363. if(framepos < 0){
  364. fprintf(stderr,"\nError reading file frame position\n");
  365. file_playing = 0;
  366. break;
  367. }
  368. for(i = 0;i < framestoget;i++){
  369. /* read one stereo frame*/
  370. got = pvoc_getframes(pdata->pvfile,pdata->analframe1,1);
  371. if(got == -1) {
  372. fprintf(stderr,"\nError reading ch 1 from pvx file\n");
  373. file_playing = 0;
  374. break;
  375. }
  376. if(got){
  377. got = pvoc_getframes(pdata->pvfile,pdata->analframe2,1);
  378. if(got == -1) {
  379. fprintf(stderr,"\nError reading ch 2 from pvx file, frame %lu\n",framepos);
  380. file_playing = 0;
  381. break;
  382. }
  383. }
  384. framepos++;
  385. if(got && (framepos <= (long) pdata->to_frame)) {
  386. // each call returns <overlap> samples
  387. long samps = pdata->pv_l->process_frame(pdata->analframe1,pbuf,PVOC_AMP_FREQ);
  388. pbuf += samps;
  389. samps = pdata->pv_r->process_frame(pdata->analframe2,pbuf_r,PVOC_AMP_FREQ);
  390. pbuf_r += samps;
  391. elementstowrite += samps; // element is a stereo sample frame
  392. }
  393. if (got==0 || framepos >= (long) pdata->to_frame){
  394. if(pdata->play_looped) {
  395. //pos = sndseekEx(pdata->ifd,pdata->from_frame * pdata->anal_framesize,SEEK_SET);
  396. //if(pvoc_rewind(pdata->pvfile,1)){ /* 1 = skip empty frame */
  397. if(pvoc_seek_mcframe(pdata->pvfile,pdata->from_frame,SEEK_SET)) {
  398. fprintf(stderr,"Error rewinding frame loop.\n");
  399. file_playing = 0;
  400. break;
  401. }
  402. }
  403. else if(elementsInBuffer == pdata->ringbuf.bufferSize) {
  404. //printf("buffer empty!\n");
  405. pdata->flag = 1;
  406. break;
  407. }
  408. }
  409. } // framestogeet
  410. assert(elementstowrite <= elementsInBuffer);
  411. //apply gain to inbuf first, then copy to ringbuf
  412. pbuf = pdata->outbuf_l;
  413. pbuf_r = pdata->outbuf_r;
  414. stereo_interls(pbuf,pbuf_r,pdata->inbuf,elementstowrite);
  415. if(pdata->gain != 1.0) {
  416. for(i=0;i < elementstowrite * pdata->inchans;i++)
  417. pdata->inbuf[i] = (float)(pdata->inbuf[i] * pdata->gain);
  418. }
  419. /* By using PaUtil_GetRingBufferWriteRegions, we can write directly into the ring buffer */
  420. PaUtil_GetRingBufferWriteRegions(&pdata->ringbuf, elementsInBuffer, &ptr[0], &sizes[0], &ptr[1], &sizes[1]);
  421. pbuf = pdata->inbuf;
  422. //printf("ring buffer sizes : %d:%d",(int)sizes[0], (int)sizes[1]);
  423. for(i = 0; i < 2; i++) {
  424. if(ptr[i]) {
  425. int frameswanted = sizes[i];
  426. memcpy(ptr[i],pbuf,frameswanted * sizeof(float) * pdata->inchans);
  427. pbuf += frameswanted * pdata->inchans;
  428. // nElementsProcessed += frameswanted;
  429. }
  430. }
  431. if(elementstowrite)
  432. PaUtil_AdvanceRingBufferWriteIndex(&pdata->ringbuf, elementstowrite);
  433. } // file_playing
  434. else {
  435. pdata->flag = 1;
  436. break;
  437. }
  438. }
  439. }
  440. /* Sleep a little while... */
  441. Pa_Sleep(5);
  442. }
  443. return 0;
  444. }