| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488 |
- /*
- * Copyright (c) 1983-2023 Richard Dobson and Composers Desktop Project Ltd
- * http://www.rwdobson.com
- * http://www.composersdesktop.com
- * This file is part of the CDP System.
- * The CDP System is free software; you can redistribute it
- * and/or modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * The CDP System is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU Lesser General Public License for more details.
- * You should have received a copy of the GNU Lesser General Public
- * License along with the CDP System; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- */
- /* pvthreads.c: thread functions for pvplay */
- /* Jan 2014: completed pvx support */
- #include "pvplay.h"
- #include <assert.h>
- extern int file_playing;
- extern psfdata *g_pdata;
- __inline void stereo_interls(const float *in_l,const float *in_r,float *out,long insize)
- {
- long i;
- const float *pfl_l,*pfl_r;
- float*pfl_o;
- pfl_o = out;
- pfl_l = in_l;
- pfl_r = in_r;
- for(i=insize;i;--i){
- *pfl_o++ = *pfl_l++;
- *pfl_o++ = *pfl_r++;
- }
- }
- /* soundfile */
- /* writes decoded data to ring buffer */
- /* size of element is full m/c frame */
- // TODO: unix thread should return void*
- #ifdef unix
- /*int*/void* threadFunctionReadFromRawFile(void* ptr)
- #else
- unsigned int __stdcall threadFunctionReadFromRawFile(void* ptr)
- #endif
- {
- psfdata* pdata = (psfdata*)ptr;
-
- /* Mark thread started */
- pdata->flag = 0;
- //printf("thread: from_frame = %d, to_frame = %d, current_Frame = %d\n",pdata->from_frame, pdata->to_frame, pdata->current_frame);
- while (1) {
- // ring_buffer_size_t nElementsProcessed = 0;
- ring_buffer_size_t elementsInBuffer = PaUtil_GetRingBufferWriteAvailable(&pdata->ringbuf);
-
- if (elementsInBuffer >= pdata->ringbuf.bufferSize / 4) {
- void* ptr[2] = {NULL};
- ring_buffer_size_t sizes[2] = {0};
-
- /* By using PaUtil_GetRingBufferWriteRegions, we can write directly into the ring buffer */
- PaUtil_GetRingBufferWriteRegions(&pdata->ringbuf, elementsInBuffer, &ptr[0], &sizes[0], &ptr[1], &sizes[1]);
-
- if (file_playing) {
- ring_buffer_size_t itemsReadFromFile;
- int i,j;
- int framesread = 0;
- int sampswanted = 0;
- int sampsread = 0;
- // we work with frames, = constant across inchans and outchans
- itemsReadFromFile = 0;
-
- for(i = 0; i < 2 && (ptr[i] != NULL); i++) {
- // NB ringbuf is sized by m/c frames
- int frameswanted = sizes[i];
-
- pdata->current_frame = sndtellEx(pdata->ifd) / pdata->inchans;
- // read up to end frame if requested
- if(pdata->to_frame < pdata->current_frame + frameswanted)
- frameswanted = pdata->to_frame - pdata->current_frame;
-
- if(frameswanted > 0){
- sampswanted = frameswanted * pdata->inchans;
- sampsread = fgetfbufEx(pdata->inbuf,sampswanted,pdata->ifd,0);
- framesread = sampsread / pdata->inchans;
-
- if(framesread < 0){ // read error!
- printf("Error reading soundfile: %s ifd = %d\n", sferrstr(),pdata->ifd);
- pdata->flag = 1;
- file_playing = 0;
- break; // just out of for loop - need to return instead?
- }
- }
- if(framesread == 0){
- /* EOF. EITHER: finish, or rewind if looping playback*/
- if(pdata->play_looped){
- if(sndseekEx(pdata->ifd,pdata->from_frame * pdata->inchans,SEEK_SET) < 0){
- printf("Error looping soundfile\n");
- pdata->flag = 1;
- file_playing = 0;
- break;
- }
- // sizes[1] especially may well = 0
- if(frameswanted==0)
- break;
- sampswanted = frameswanted * pdata->inchans;
- sampsread = fgetfbufEx(pdata->inbuf,sampswanted,pdata->ifd,0);
- framesread = sampsread / pdata->inchans;
- if(framesread < 0){ // read error!
- printf("Error reading soundfile\n");
- pdata->flag = 1;
- file_playing = 0;
- break;
- }
- }
- else {
- // we must watch the ring buffer to make sure all data has been rendered,
- // over several callback blocks
- //printf("End of data. playing = %d:\n", file_playing);
- //printf("\telements remaining = %d\n",elementsInBuffer);
- if(elementsInBuffer == pdata->ringbuf.bufferSize) {
- pdata->flag = 1;
- break;
- }
- }
- }
- else {
- // ringbuf calcs always relative to outchans
- itemsReadFromFile += framesread;
-
- // now ready to apply decoding or other processing
- if(pdata->gain != 1.0){
- for(j = 0; j < framesread * pdata->inchans; j++)
- pdata->inbuf[j] = (float)(pdata->inbuf[j] * pdata->gain);
- }
- // no channel mapping in pvplay
- if(pdata->do_decode) {
- ABFSAMPLE abfsamp;
-
- for(j=0; j < framesread; j++){
- // single frame only
- pdata->copyfunc(&abfsamp,pdata->inbuf + (j * pdata->inchans));
- /* BIG TODO: update funcs to process large frame buffer! */
- /* NB: ring buffer is effectively defined as raw bytes */
- pdata->decodefunc(&abfsamp, (float*)(ptr[i]) + (j * pdata->outchans), 1);
- // nElementsProcessed++;
- }
- }
- else { // inchans = outchans
- memcpy(ptr[i],pdata->inbuf,framesread * sizeof(float) * pdata->inchans);
- // nElementsProcessed += framesread;
- }
- }
- }
- PaUtil_AdvanceRingBufferWriteIndex(&pdata->ringbuf, itemsReadFromFile);
- }
- else {
- // this code is activated on Ctrl-C. Can do immediate finish by setting flag
- // printf("file done\n");
- pdata->flag = 1;
- break;
- }
- }
- // else {
- // printf("ringbuf size = %d, elements remaining = %d, playing = %d\n",pdata->ringbuf.bufferSize,elementsInBuffer,file_playing);
- // }
- /* Sleep a little while...! */
- Pa_Sleep(10);
- }
- return 0;
- }
- /* versions for .ana file (always mono only) */
- #ifdef unix
- /*int*/void* threadFunctionReadFromAnalFile(void* ptr)
- #else
- unsigned int __stdcall threadFunctionReadFromAnalFile(void* ptr)
- #endif
- {
- psfdata* pdata = (psfdata*)ptr;
-
- /* Mark thread started */
- pdata->flag = 0;
-
- while (1) {
- // ring_buffer_size_t nElementsProcessed = 0;
- ring_buffer_size_t elementsInBuffer = PaUtil_GetRingBufferWriteAvailable(&pdata->ringbuf);
-
- if (elementsInBuffer >= pdata->ringbuf.bufferSize / 4) {
- void* ptr[2] = {NULL};
- ring_buffer_size_t sizes[2] = {0};
-
- //read and analyse as many frames as resynth space is available for, into inbuf
- // then distribute to ringbuf sections as necessary
- //printf("from_frame = %d,to_frame = %d\n",pdata->from_frame,pdata->to_frame);
- int framestoget = elementsInBuffer / pdata->fftsize;
- //assert(framestoget); // must be at least one, or ring buffer is too small!
- if(framestoget==0)
- break;
- float *pbuf = pdata->inbuf;
- int i, got = 0;
- int elementstowrite = 0;
-
- if (file_playing) {
- /* find out where we are ... */
- // unsigned: we have to trust no error from sndtellEx!
- unsigned long framepos1 = (unsigned long) (sndtellEx(pdata->ifd) / pdata->anal_framesize);
-
- for(i = 0;i < framestoget;i++){
- /* read one frame*/
- got = fgetfbufEx(pdata->analframe1,pdata->anal_framesize,pdata->ifd,0);
- if(got == -1) {
- fprintf(stderr,"\nError reading from sfile\n");
- file_playing = 0;
- break;
- }
- if(got != pdata->anal_framesize) {
- if(got > 0) {
- fprintf(stderr,"Infile error: incomplete analysis frame encountered\n");
- file_playing = 0;
- break;
- }
- }
- framepos1++;
-
- if(got && (framepos1 <= pdata->to_frame)) {
- long samps = pdata->pv_l->process_frame(pdata->analframe1,pbuf,PVOC_AMP_FREQ);
- elementstowrite += samps;
- pbuf += samps;
- }
-
- if (got==0 || framepos1 >= pdata->to_frame){
- if(pdata->play_looped) {
- long pos;
- pos = sndseekEx(pdata->ifd,pdata->from_frame * pdata->anal_framesize,SEEK_SET);
- if(pos < 0){
- fprintf(stderr,"Error rewinding frame loop.\n");
- file_playing = 0;
- break;
- }
- }
- else if(elementsInBuffer == pdata->ringbuf.bufferSize) {
- //printf("buffer empty!\n");
- pdata->flag = 1;
- break;
- }
- }
- }
-
- //apply gain to inbuf first, then copy to ringbuf
- if(pdata->gain != 1.0) {
- for(i=0;i < elementstowrite;i++)
- pdata->inbuf[i] = (float)(pdata->inbuf[i] * pdata->gain);
- }
- /* By using PaUtil_GetRingBufferWriteRegions, we can write directly into the ring buffer */
- PaUtil_GetRingBufferWriteRegions(&pdata->ringbuf, elementsInBuffer, &ptr[0], &sizes[0], &ptr[1], &sizes[1]);
- pbuf = pdata->inbuf;
- //printf("ring buffer sizes : %d:%d",(int)sizes[0], (int)sizes[1]);
- for(i = 0; i < 2; i++) {
- if(ptr[i]) {
- int frameswanted = sizes[i];
- memcpy(ptr[i],pbuf,frameswanted * sizeof(float) * pdata->inchans);
- pbuf += frameswanted * pdata->inchans;
- // nElementsProcessed += frameswanted;
- }
- }
- if(elementstowrite)
- PaUtil_AdvanceRingBufferWriteIndex(&pdata->ringbuf, elementstowrite);
- } // file_playing
- else {
- // this code is activated on Ctrl-C. Can do immediate finish by setting flag
- // printf("file done\n");
- pdata->flag = 1;
- break;
- }
- }
- /* Sleep a little while... */
- Pa_Sleep(5);
- }
- return 0;
- }
- // version for pvx format, just mono and stereo supported for now
- #ifdef unix
- /*int*/void* threadFunctionReadFromPVXFile(void* ptr)
- #else
- unsigned int __stdcall threadFunctionReadFromPVXFile(void* ptr)
- #endif
- {
- psfdata* pdata = (psfdata*)ptr;
- /* Mark thread started */
- pdata->flag = 0;
- while (1) {
- // ring_buffer_size_t nElementsProcessed = 0;
- ring_buffer_size_t elementsInBuffer = PaUtil_GetRingBufferWriteAvailable(&pdata->ringbuf);
- // we remember that an element is a frame (mono or stereo)
- if (elementsInBuffer >= pdata->ringbuf.bufferSize / 4) {
- void* ptr[2] = {NULL};
- ring_buffer_size_t sizes[2] = {0};
- //read and analyse as many frames as resynth space is available for, into inbuf
- // then distribute to ringbuf sections as necessary
- //printf("from_frame = %d,to_frame = %d\n",pdata->from_frame,pdata->to_frame);
- /* pvx : file framecount is of single frames - need enough space to synth a single mc analysis frame */
- /* but to_frame and from_frame are m/c frame count */
- int framestoget = (elementsInBuffer / pdata->fftsize);
- if(framestoget==0)
- break;
- int i, got = 0;
- int elementstowrite = 0;
- if(pdata->inchans==1) {
- float *pbuf = pdata->inbuf;
- if (file_playing) {
- /* find out where we are ...multi-chan frame count */
- long framepos = pvoc_framepos(pdata->pvfile) / pdata->inchans; // NB
- if(framepos < 0){
- fprintf(stderr,"\nError reading file frame position\n");
- file_playing = 0;
- break;
- }
- for(i = 0;i < framestoget;i++){
- /* read one frame*/
- got = pvoc_getframes(pdata->pvfile,pdata->analframe1,1);
- if(got == -1) {
- fprintf(stderr,"\nError reading from sfile\n");
- file_playing = 0;
- break;
- }
- framepos++;
- if(got && (framepos <= (long) pdata->to_frame)) {
- long samps = pdata->pv_l->process_frame(pdata->analframe1,pbuf,PVOC_AMP_FREQ);
- elementstowrite += samps;
- pbuf += samps;
- }
- if (got==0 || framepos >= (long) pdata->to_frame){
- if(pdata->play_looped) {
- if(pvoc_seek_mcframe(pdata->pvfile,pdata->from_frame,SEEK_SET)) {
- fprintf(stderr,"Error rewinding frame loop.\n");
- file_playing = 0;
- break;
- }
- }
- else if(elementsInBuffer == pdata->ringbuf.bufferSize) {
- //printf("buffer empty!\n");
- pdata->flag = 1;
- break;
- }
- }
- } // framestoget
- //apply gain to inbuf first, then copy to ringbuf
- if(pdata->gain != 1.0) {
- for(i=0;i < elementstowrite;i++)
- pdata->inbuf[i] = (float)(pdata->inbuf[i] * pdata->gain);
- }
- /* By using PaUtil_GetRingBufferWriteRegions, we can write directly into the ring buffer */
- PaUtil_GetRingBufferWriteRegions(&pdata->ringbuf, elementsInBuffer, &ptr[0], &sizes[0], &ptr[1], &sizes[1]);
- pbuf = pdata->inbuf;
- //printf("ring buffer sizes : %d:%d",(int)sizes[0], (int)sizes[1]);
- for(i = 0; i < 2; i++) {
- if(ptr[i]) {
- int frameswanted = sizes[i];
- memcpy(ptr[i],pbuf,frameswanted * sizeof(float) * pdata->inchans);
- pbuf += frameswanted * pdata->inchans;
- // nElementsProcessed += frameswanted;
- }
- }
- if(elementstowrite)
- PaUtil_AdvanceRingBufferWriteIndex(&pdata->ringbuf, elementstowrite);
- } // file_playing
- else {
- // this code is activated on Ctrl-C. Can do immediate finish by setting flag
- // printf("file done\n");
- pdata->flag = 1;
- break;
- }
- } //inchans==1
- else if (pdata->inchans==2) {
- float *pbuf = pdata->outbuf_l;
- float *pbuf_r = pdata->outbuf_r;
- // framestoget is count of m/c analysis frames to read
- if(file_playing){
- /* find out where we are ...multi-chan frame count */
- long framepos = pvoc_framepos(pdata->pvfile) / pdata->inchans; // NB
- if(framepos < 0){
- fprintf(stderr,"\nError reading file frame position\n");
- file_playing = 0;
- break;
- }
- for(i = 0;i < framestoget;i++){
- /* read one stereo frame*/
- got = pvoc_getframes(pdata->pvfile,pdata->analframe1,1);
- if(got == -1) {
- fprintf(stderr,"\nError reading ch 1 from pvx file\n");
- file_playing = 0;
- break;
- }
- if(got){
- got = pvoc_getframes(pdata->pvfile,pdata->analframe2,1);
- if(got == -1) {
- fprintf(stderr,"\nError reading ch 2 from pvx file, frame %lu\n",framepos);
- file_playing = 0;
- break;
- }
- }
- framepos++;
- if(got && (framepos <= (long) pdata->to_frame)) {
- // each call returns <overlap> samples
- long samps = pdata->pv_l->process_frame(pdata->analframe1,pbuf,PVOC_AMP_FREQ);
- pbuf += samps;
- samps = pdata->pv_r->process_frame(pdata->analframe2,pbuf_r,PVOC_AMP_FREQ);
- pbuf_r += samps;
-
- elementstowrite += samps; // element is a stereo sample frame
- }
- if (got==0 || framepos >= (long) pdata->to_frame){
- if(pdata->play_looped) {
- //pos = sndseekEx(pdata->ifd,pdata->from_frame * pdata->anal_framesize,SEEK_SET);
- //if(pvoc_rewind(pdata->pvfile,1)){ /* 1 = skip empty frame */
- if(pvoc_seek_mcframe(pdata->pvfile,pdata->from_frame,SEEK_SET)) {
- fprintf(stderr,"Error rewinding frame loop.\n");
- file_playing = 0;
- break;
- }
- }
- else if(elementsInBuffer == pdata->ringbuf.bufferSize) {
- //printf("buffer empty!\n");
- pdata->flag = 1;
- break;
- }
- }
- } // framestogeet
- assert(elementstowrite <= elementsInBuffer);
- //apply gain to inbuf first, then copy to ringbuf
- pbuf = pdata->outbuf_l;
- pbuf_r = pdata->outbuf_r;
- stereo_interls(pbuf,pbuf_r,pdata->inbuf,elementstowrite);
-
- if(pdata->gain != 1.0) {
- for(i=0;i < elementstowrite * pdata->inchans;i++)
- pdata->inbuf[i] = (float)(pdata->inbuf[i] * pdata->gain);
- }
- /* By using PaUtil_GetRingBufferWriteRegions, we can write directly into the ring buffer */
- PaUtil_GetRingBufferWriteRegions(&pdata->ringbuf, elementsInBuffer, &ptr[0], &sizes[0], &ptr[1], &sizes[1]);
- pbuf = pdata->inbuf;
- //printf("ring buffer sizes : %d:%d",(int)sizes[0], (int)sizes[1]);
- for(i = 0; i < 2; i++) {
- if(ptr[i]) {
- int frameswanted = sizes[i];
- memcpy(ptr[i],pbuf,frameswanted * sizeof(float) * pdata->inchans);
- pbuf += frameswanted * pdata->inchans;
- // nElementsProcessed += frameswanted;
- }
- }
- if(elementstowrite)
- PaUtil_AdvanceRingBufferWriteIndex(&pdata->ringbuf, elementstowrite);
- } // file_playing
- else {
- pdata->flag = 1;
- break;
- }
- }
- }
- /* Sleep a little while... */
- Pa_Sleep(5);
- }
- return 0;
- }
|