1 /*
2     SuperCollider real time audio synthesis system
3     Copyright (c) 2002 James McCartney. All rights reserved.
4     Copyright (c) 2009 Marije Baalman
5     Copyright (c) 2008 Scott Wilson
6     Copyright (c) 2012 Tim Blechmann
7     http://www.audiosynth.com
8 
9     This program is free software; you can redistribute it and/or modify
10     it under the terms of the GNU General Public License as published by
11     the Free Software Foundation; either version 2 of the License, or
12     (at your option) any later version.
13 
14     This program is distributed in the hope that it will be useful,
15     but WITHOUT ANY WARRANTY; without even the implied warranty of
16     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17     GNU General Public License for more details.
18 
19     You should have received a copy of the GNU General Public License
20     along with this program; if not, write to the Free Software
21     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
22 */
23 
24 #include "SC_SyncCondition.h"
25 #include "SC_PlugIn.h"
26 
27 #include <sndfile.h>
28 
29 #include <atomic>
30 #include <new>
31 #include <functional>
32 #include <SC_Lock.h>
33 
34 #include <boost/lockfree/queue.hpp>
35 #include <boost/lockfree/spsc_queue.hpp>
36 
37 static InterfaceTable* ft;
38 
39 enum { diskinIdle, diskinStartingEmpty, diskinStartingFull, diskinNormal, diskinLastBuffer, diskinEndSilence };
40 
41 enum { diskoutIdle, diskoutNormal };
42 
43 
44 struct DiskIn : public Unit {
45     float m_fbufnum;
46     SndBuf* m_buf;
47     uint32 m_framepos;
48 };
49 
50 struct DiskOut : public Unit {
51     float m_fbufnum;
52     SndBuf* m_buf;
53     uint32 m_framepos;
54     uint32 m_framewritten;
55 };
56 
57 struct VDiskIn : public Unit {
58     float m_fbufnum, m_pchRatio, m_rBufSize;
59     double m_framePos, m_bufPos;
60     uint32 m_count;
61     SndBuf* m_buf;
62     uint32 m_iFramePos, m_iBufPos;
63 };
64 
65 //////////////////////////////////////////////////////////////////////////////////////////////////
66 
67 extern "C" {
68 
69 void DiskIn_next(DiskIn* unit, int inNumSamples);
70 void DiskIn_Ctor(DiskIn* unit);
71 
72 void DiskOut_next(DiskOut* unit, int inNumSamples);
73 void DiskOut_Ctor(DiskOut* unit);
74 void DiskOut_Dtor(DiskOut* unit);
75 
76 void VDiskIn_next(VDiskIn* unit, int inNumSamples);
77 void VDiskIn_first(VDiskIn* unit, int inNumSamples);
78 void VDiskIn_next_rate1(VDiskIn* unit, int inNumSamples);
79 void VDiskIn_Ctor(VDiskIn* unit);
80 }
81 
82 //////////////////////////////////////////////////////////////////////////////////////////////////
83 
84 
85 enum {
86     kDiskCmd_Read,
87     kDiskCmd_Write,
88     kDiskCmd_ReadLoop,
89 };
90 
91 namespace {
92 
93 struct DiskIOMsg {
94     World* mWorld;
95     int16 mCommand;
96     int16 mChannels;
97     int32 mBufNum;
98     int32 mPos;
99     int32 mFrames;
100 
101     void Perform();
102 };
103 
Perform()104 void DiskIOMsg::Perform() {
105     NRTLock(mWorld);
106 
107     SndBuf* buf = World_GetNRTBuf(mWorld, mBufNum);
108     if (mPos > buf->frames || mPos + mFrames > buf->frames || buf->channels != mChannels)
109         goto leave;
110 
111     sf_count_t count;
112     switch (mCommand) {
113     case kDiskCmd_Read:
114         count = buf->sndfile ? sf_readf_float(buf->sndfile, buf->data + mPos * buf->channels, mFrames) : 0;
115         if (count < mFrames) {
116             memset(buf->data + (mPos + count) * buf->channels, 0, (mFrames - count) * buf->channels * sizeof(float));
117             World_GetBuf(mWorld, mBufNum)->mask = mPos + count;
118             // NOTE: Possible race condition above: The disk IO thread may write to the rt SndBuf
119             // while the stage3 of the sequenced commands copies the non-rt SndBuf struct to the rt buf.
120             // This only happens if the buffer is modified via an osc command.
121             // We can't use the non-rt SndBuf above since buf->mask won't be reflected to the rt buf.
122         }
123         break;
124     case kDiskCmd_ReadLoop:
125         if (!buf->sndfile) {
126             memset(buf->data + mPos * buf->channels, 0, mFrames * buf->channels * sizeof(float));
127             goto leave;
128         }
129         count = sf_readf_float(buf->sndfile, buf->data + mPos * buf->channels, mFrames);
130         while (mFrames -= count) {
131             sf_seek(buf->sndfile, 0, SEEK_SET);
132             count = sf_readf_float(buf->sndfile, buf->data + (mPos + count) * buf->channels, mFrames);
133         }
134         break;
135     case kDiskCmd_Write:
136         // printf("kDiskCmd_Write %d %p\n", mBufNum, buf->sndfile);
137         if (!buf->sndfile)
138             goto leave;
139         count = sf_writef_float(buf->sndfile, buf->data + mPos * buf->channels, mFrames);
140         break;
141     }
142 
143 leave:
144     NRTUnlock(mWorld);
145 }
146 
147 struct DiskIOThread {
148     SC_SyncCondition mDiskFifoHasData;
149 
150 #ifdef SUPERNOVA
151     boost::lockfree::queue<DiskIOMsg, boost::lockfree::capacity<256>> mDiskFifo;
152 #else
153     boost::lockfree::spsc_queue<DiskIOMsg, boost::lockfree::capacity<256>> mDiskFifo;
154 #endif
155 
156     std::atomic<bool> mRunning;
157     SC_Thread mThread;
158 
DiskIOThread__anon94d32c1b0411::DiskIOThread159     DiskIOThread(): mRunning(false) {}
160 
~DiskIOThread__anon94d32c1b0411::DiskIOThread161     ~DiskIOThread() {
162         if (mRunning) {
163             mRunning.store(false);
164             mDiskFifoHasData.Signal();
165             mThread.join();
166         }
167     }
168 
launchThread__anon94d32c1b0411::DiskIOThread169     void launchThread() {
170         using namespace std;
171         mRunning.store(true);
172 
173         mThread = thread(bind(&DiskIOThread::ioThreadFunc, this));
174     }
175 
Write__anon94d32c1b0411::DiskIOThread176     bool Write(DiskIOMsg& data) {
177         bool pushSucceeded = mDiskFifo.push(data);
178         if (pushSucceeded)
179             mDiskFifoHasData.Signal();
180         return pushSucceeded;
181     }
182 
ioThreadFunc__anon94d32c1b0411::DiskIOThread183     void ioThreadFunc() {
184         while (mRunning.load()) {
185             mDiskFifoHasData.WaitEach();
186 
187             DiskIOMsg msg;
188             bool popSucceeded = mDiskFifo.pop(msg);
189 
190             if (popSucceeded)
191                 msg.Perform();
192         }
193     }
194 };
195 
196 DiskIOThread* gDiskIO;
197 
198 }
199 
200 //////////////////////////////////////////////////////////////////////////////////////////////////
201 
202 #define SETUP_OUT(offset)                                                                                              \
203     if (unit->mNumOutputs != bufChannels) {                                                                            \
204         ClearUnitOutputs(unit, inNumSamples);                                                                          \
205         return;                                                                                                        \
206     }                                                                                                                  \
207     float* const* const out = &OUT(offset);
208 
209 #define SETUP_IN(offset)                                                                                               \
210     if (unit->mNumInputs - (uint32)offset != bufChannels) {                                                            \
211         ClearUnitOutputs(unit, inNumSamples);                                                                          \
212         return;                                                                                                        \
213     }                                                                                                                  \
214     const float* const* const in = &IN(offset);
215 
DiskIn_Ctor(DiskIn * unit)216 void DiskIn_Ctor(DiskIn* unit) {
217     unit->m_fbufnum = -1.f;
218     unit->m_buf = unit->mWorld->mSndBufs;
219     unit->m_framepos = 0;
220     SETCALC(DiskIn_next);
221     ClearUnitOutputs(unit, 1);
222 }
223 
DiskIn_next(DiskIn * unit,int inNumSamples)224 void DiskIn_next(DiskIn* unit, int inNumSamples) {
225     GET_BUF_SHARED
226     if (!bufData || ((bufFrames & ((unit->mWorld->mBufLength << 1) - 1)) != 0)) {
227         unit->m_framepos = 0;
228         ClearUnitOutputs(unit, inNumSamples);
229         return;
230     }
231     SETUP_OUT(0)
232 
233     if (unit->m_framepos >= bufFrames) {
234         unit->m_framepos = 0;
235     }
236 
237     bufData += unit->m_framepos * bufChannels;
238 
239     // buffer must be allocated as a multiple of 2*blocksize.
240     if (bufChannels > 2) {
241         for (int j = 0; j < inNumSamples; ++j) {
242             for (uint32 i = 0; i < bufChannels; ++i)
243                 out[i][j] = *bufData++;
244         }
245     } else if (bufChannels == 2) {
246         float* out0 = out[0];
247         float* out1 = out[1];
248         for (int j = 0; j < inNumSamples; ++j) {
249             out0[j] = *bufData++;
250             out1[j] = *bufData++;
251         }
252     } else {
253         float* out0 = out[0];
254         for (int j = 0; j < inNumSamples; ++j) {
255             out0[j] = *bufData++;
256         }
257     }
258     if (unit->m_buf->mask1 >= 0 && unit->m_framepos >= unit->m_buf->mask1)
259         unit->mDone = true;
260     unit->m_framepos += inNumSamples;
261     uint32 bufFrames2 = bufFrames >> 1;
262     if (unit->m_framepos == bufFrames) {
263         unit->m_framepos = 0;
264         goto sendMessage;
265     } else if (unit->m_framepos == bufFrames2) {
266     sendMessage:
267         if (unit->m_buf->mask >= 0)
268             unit->m_buf->mask1 = unit->m_buf->mask;
269         if (unit->mWorld->mRealTime) {
270             // send a message to read
271             DiskIOMsg msg;
272             msg.mWorld = unit->mWorld;
273             msg.mCommand = (int)ZIN0(1) ? kDiskCmd_ReadLoop : kDiskCmd_Read;
274             msg.mBufNum = (int)fbufnum;
275             msg.mPos = bufFrames2 - unit->m_framepos;
276             msg.mFrames = bufFrames2;
277             msg.mChannels = bufChannels;
278             gDiskIO->Write(msg);
279         } else {
280             SndBuf* bufr = World_GetNRTBuf(unit->mWorld, (int)fbufnum);
281             uint32 mPos = bufFrames2 - unit->m_framepos;
282             if (mPos > (uint32)bufr->frames || mPos + bufFrames2 > (uint32)bufr->frames
283                 || (uint32)bufr->channels != bufChannels)
284                 return;
285             sf_count_t count;
286 
287             if ((int)ZIN0(1)) { // loop
288                 if (!bufr->sndfile)
289                     memset(bufr->data + mPos * bufr->channels, 0, bufFrames2 * bufr->channels * sizeof(float));
290                 count = sf_readf_float(bufr->sndfile, bufr->data + mPos * bufr->channels, bufFrames2);
291                 while (bufFrames2 -= count) {
292                     sf_seek(bufr->sndfile, 0, SEEK_SET);
293                     count = sf_readf_float(bufr->sndfile, bufr->data + (mPos + count) * bufr->channels, bufFrames2);
294                 }
295             } else { // non-loop
296                 count =
297                     bufr->sndfile ? sf_readf_float(bufr->sndfile, bufr->data + mPos * bufr->channels, bufFrames2) : 0;
298                 if (count < bufFrames2) {
299                     memset(bufr->data + (mPos + count) * bufr->channels, 0,
300                            (bufFrames2 - count) * bufr->channels * sizeof(float));
301                     unit->m_buf->mask = mPos + count;
302                 }
303             }
304         }
305     }
306 }
307 
308 ////////////////////////////////////////////////////////////////////////////////////////////////////////
309 
DiskOut_Ctor(DiskOut * unit)310 void DiskOut_Ctor(DiskOut* unit) {
311     unit->m_fbufnum = -1.f;
312     unit->m_buf = unit->mWorld->mSndBufs;
313     unit->m_framepos = 0;
314     unit->m_framewritten = 0;
315     SETCALC(DiskOut_next);
316     ClearUnitOutputs(unit, 1);
317 }
318 
319 
DiskOut_next(DiskOut * unit,int inNumSamples)320 void DiskOut_next(DiskOut* unit, int inNumSamples) {
321     GET_BUF
322 
323     if (!bufData || ((bufFrames & ((unit->mWorld->mBufLength << 1) - 1)) != 0)) {
324         unit->m_framepos = 0;
325         //		unit->m_framewritten = 0;
326         return;
327     }
328     SETUP_IN(1)
329 
330     float* out = OUT(0);
331     uint32 framew = unit->m_framewritten;
332 
333     //	printf("Start frames %i %i\n", unit->m_framewritten, framew );
334 
335     if (unit->m_framepos >= bufFrames)
336         unit->m_framepos = 0;
337 
338     bufData += unit->m_framepos * bufChannels;
339 
340     if (bufChannels > 2) {
341         for (int j = 0; j < inNumSamples; ++j) {
342             for (uint32 i = 0; i < bufChannels; ++i)
343                 *bufData++ = in[i][j];
344 
345             out[j] = framew++;
346         }
347     } else if (bufChannels == 2) {
348         const float* in0 = in[0];
349         const float* in1 = in[1];
350         for (int j = 0; j < inNumSamples; ++j) {
351             *bufData++ = in0[j];
352             *bufData++ = in1[j];
353             out[j] = framew++;
354         }
355     } else {
356         const float* in0 = in[0];
357         for (int j = 0; j < inNumSamples; ++j) {
358             *bufData++ = in0[j];
359             out[j] = framew++;
360         }
361     }
362 
363     unit->m_framepos += inNumSamples;
364     //	unit->m_framewritten += inNumSamples;
365     unit->m_framewritten = framew;
366 
367     //	printf("frames %i %i\n", unit->m_framewritten, framew );
368 
369     uint32 bufFrames2 = bufFrames >> 1;
370     if (unit->m_framepos == bufFrames) {
371         unit->m_framepos = 0;
372         goto sendMessage;
373     } else if (unit->m_framepos == bufFrames2) {
374     sendMessage:
375         // send a message to write
376         DiskIOMsg msg;
377         msg.mWorld = unit->mWorld;
378         msg.mCommand = kDiskCmd_Write;
379         msg.mBufNum = (int)fbufnum;
380         msg.mPos = bufFrames2 - unit->m_framepos;
381         msg.mFrames = bufFrames2;
382         msg.mChannels = bufChannels;
383         // printf("sendMessage %d  %d %d %d\n", msg.mBufNum, msg.mPos, msg.mFrames, msg.mChannels);
384         gDiskIO->Write(msg);
385     }
386 }
387 
DiskOut_Dtor(DiskOut * unit)388 void DiskOut_Dtor(DiskOut* unit) {
389     GET_BUF
390 
391     uint32 framepos = unit->m_framepos;
392     uint32 bufFrames2 = bufFrames >> 1;
393     // check that we didn't just write
394     if (framepos != 0 && framepos != bufFrames2) {
395         // if not write the last chunk of samples
396         uint32 writeStart;
397         if (framepos > bufFrames2) {
398             writeStart = bufFrames2;
399         } else {
400             writeStart = 0;
401         }
402         DiskIOMsg msg;
403         msg.mWorld = unit->mWorld;
404         msg.mCommand = kDiskCmd_Write;
405         msg.mBufNum = (int)fbufnum;
406         msg.mPos = writeStart;
407         msg.mFrames = framepos - writeStart;
408         msg.mChannels = bufChannels;
409         // printf("sendMessage %d  %d %d %d\n", msg.mBufNum, msg.mPos, msg.mFrames, msg.mChannels);
410         gDiskIO->Write(msg);
411     }
412 }
413 
414 
VDiskIn_Ctor(VDiskIn * unit)415 void VDiskIn_Ctor(VDiskIn* unit) {
416     unit->m_fbufnum = -1.f;
417     unit->m_buf = unit->mWorld->mSndBufs;
418     unit->m_framePos = 0.;
419     unit->m_bufPos = 0.;
420     unit->m_pchRatio = sc_max(IN0(1), 0.f);
421     unit->m_count = 0;
422     unit->m_iFramePos = 0;
423     unit->m_iBufPos = 0;
424 
425     if (INRATE(1) == calc_ScalarRate && (unit->m_pchRatio == 1))
426         SETCALC(VDiskIn_next_rate1);
427     else
428         SETCALC(VDiskIn_first);
429 
430     ClearUnitOutputs(unit, 1);
431 }
432 
VDiskIn_request_buffer(VDiskIn * unit,float fbufnum,uint32 bufFrames2,uint32 bufChannels,double bufPos)433 static void VDiskIn_request_buffer(VDiskIn* unit, float fbufnum, uint32 bufFrames2, uint32 bufChannels, double bufPos) {
434     if (unit->m_buf->mask >= 0)
435         unit->m_buf->mask1 = unit->m_buf->mask;
436     unit->m_count++;
437     if (unit->mWorld->mRealTime) {
438         DiskIOMsg msg;
439         msg.mWorld = unit->mWorld;
440         msg.mCommand = (int)ZIN0(2) ? kDiskCmd_ReadLoop : kDiskCmd_Read;
441         msg.mBufNum = (int)fbufnum;
442         uint32 thisPos;
443         if ((uint32)bufPos >= bufFrames2)
444             thisPos = 0;
445         else
446             thisPos = bufFrames2;
447         msg.mPos = thisPos;
448         msg.mFrames = bufFrames2;
449         msg.mChannels = bufChannels;
450         gDiskIO->Write(msg);
451 
452         if ((int)ZIN0(3)) {
453             //	float outval = bufPos + sc_mod((float)(unit->m_count * bufFrames2), (float)buf->fileinfo.frames);
454             float outval = bufPos + (float)(unit->m_count * bufFrames2);
455             SendNodeReply(&unit->mParent->mNode, (int)ZIN0(3), "/diskin", 1, &outval);
456         }
457     } else {
458         SndBuf* bufr = World_GetNRTBuf(unit->mWorld, (int)fbufnum);
459         uint32 mPos;
460         if ((uint32)bufPos >= bufFrames2)
461             mPos = 0;
462         else
463             mPos = bufFrames2;
464         if (mPos > (uint32)bufr->frames || mPos + bufFrames2 > (uint32)bufr->frames
465             || (uint32)bufr->channels != bufChannels)
466             return;
467         sf_count_t count;
468 
469         if ((int)ZIN0(2)) { // loop
470             if (!bufr->sndfile)
471                 memset(bufr->data + mPos * bufr->channels, 0, bufFrames2 * bufr->channels * sizeof(float));
472             count = sf_readf_float(bufr->sndfile, bufr->data + mPos * bufr->channels, bufFrames2);
473             while (bufFrames2 -= count) {
474                 sf_seek(bufr->sndfile, 0, SEEK_SET);
475                 count = sf_readf_float(bufr->sndfile, bufr->data + (mPos + count) * bufr->channels, bufFrames2);
476             }
477         } else { // non-loop
478             count = bufr->sndfile ? sf_readf_float(bufr->sndfile, bufr->data + mPos * bufr->channels, bufFrames2) : 0;
479             if (count < bufFrames2) {
480                 memset(bufr->data + (mPos + count) * bufr->channels, 0,
481                        (bufFrames2 - count) * bufr->channels * sizeof(float));
482                 unit->m_buf->mask = mPos + count;
483             }
484         }
485     }
486 }
487 
488 // first time through, the FIRST sample doesn't need the interpolation... the buffer won't be filled 'correctly' for
489 // interpolation, so use the _first function to make this exception
VDiskIn_next_(VDiskIn * unit,int inNumSamples)490 template <bool First> static inline void VDiskIn_next_(VDiskIn* unit, int inNumSamples) {
491     bool test = false;
492 
493     GET_BUF_SHARED
494     if (!bufData || ((bufFrames & ((unit->mWorld->mBufLength << 1) - 1)) != 0)) {
495         unit->m_framePos = 0.;
496         unit->m_count = 0;
497         ClearUnitOutputs(unit, inNumSamples);
498         return;
499     }
500 
501     SETUP_OUT(0);
502     if (First)
503         unit->m_rBufSize = 1. / bufFrames;
504 
505     double framePos = unit->m_framePos;
506     double bufPos = unit->m_bufPos; // where we are in the DiskIn buffer
507     float newPchRatio = sc_max(IN0(1), 0.f);
508     if ((newPchRatio * inNumSamples * unit->m_rBufSize) >= 0.5) {
509         printf("pitch ratio is greater then max allowed (see VDiskIn help)\n");
510         ClearUnitOutputs(unit, inNumSamples);
511         return;
512     }
513 
514     float pchRatio = unit->m_pchRatio;
515     float pchSlope = CALCSLOPE(newPchRatio, pchRatio);
516     uint32 bufFrames2 = bufFrames >> 1;
517     double fbufFrames2 = (double)bufFrames2;
518     double fbufFrames = (double)bufFrames;
519 
520     if (First) {
521         for (uint32 i = 0; i < bufChannels; i++)
522             out[i][0] = bufData[0 + i];
523     }
524 
525     const int firstLoopSample = First ? 1 : 0;
526 
527     for (int j = firstLoopSample; j < inNumSamples; ++j) {
528         int32 iBufPos = (int32)bufPos;
529         double frac = bufPos - (double)iBufPos;
530         int table1 = iBufPos * bufChannels;
531         int table0 = table1 - bufChannels;
532         int table2 = table1 + bufChannels;
533         int table3 = table2 + bufChannels;
534         while (table1 >= bufSamples)
535             table1 -= bufSamples;
536         while (table0 < 0)
537             table0 += bufSamples;
538         while (table2 >= bufSamples)
539             table2 -= bufSamples;
540         while (table3 >= bufSamples)
541             table3 -= bufSamples;
542         for (uint32 i = 0; i < bufChannels; i++) {
543             float a, b, c, d;
544             a = bufData[table0 + i];
545             b = bufData[table1 + i];
546             c = bufData[table2 + i];
547             d = bufData[table3 + i];
548             out[i][j] = cubicinterp(frac, a, b, c, d);
549         };
550         pchRatio += pchSlope;
551         framePos += pchRatio;
552         const double oldBufPos = bufPos;
553         bufPos += pchRatio;
554 
555         // the +1 is needed for the cubic interpolation... make SURE the old data isn't needed any more before
556         // setting up the new buffer
557         if ((oldBufPos < (fbufFrames2 + 1)) && ((bufPos >= (fbufFrames2 + 1)))) {
558             test = true;
559         }
560         if (bufPos >= (fbufFrames + 1)) {
561             test = true;
562             bufPos -= fbufFrames;
563         }
564     }
565     if (unit->m_buf->mask1 >= 0 && bufPos >= unit->m_buf->mask1)
566         unit->mDone = true;
567     if (test)
568         VDiskIn_request_buffer(unit, fbufnum, bufFrames2, bufChannels, bufPos);
569 
570     unit->m_framePos = framePos;
571     unit->m_pchRatio = pchRatio;
572     unit->m_bufPos = bufPos;
573 
574     if (First)
575         SETCALC(VDiskIn_next);
576 }
577 
VDiskIn_first(VDiskIn * unit,int inNumSamples)578 void VDiskIn_first(VDiskIn* unit, int inNumSamples) { VDiskIn_next_<true>(unit, inNumSamples); }
579 
VDiskIn_next(VDiskIn * unit,int inNumSamples)580 void VDiskIn_next(VDiskIn* unit, int inNumSamples) { VDiskIn_next_<false>(unit, inNumSamples); }
581 
VDiskIn_next_rate1(VDiskIn * unit,int inNumSamples)582 void VDiskIn_next_rate1(VDiskIn* unit, int inNumSamples) {
583     bool test = false;
584 
585     GET_BUF_SHARED
586     if (!bufData || ((bufFrames & ((unit->mWorld->mBufLength << 1) - 1)) != 0)) {
587         unit->m_iFramePos = 0.;
588         unit->m_count = 0;
589         ClearUnitOutputs(unit, inNumSamples);
590         return;
591     }
592 
593     SETUP_OUT(0)
594 
595     uint32 framePos = unit->m_iFramePos;
596     uint32 bufPos = unit->m_iBufPos;
597 
598     uint32 bufFrames2 = bufFrames >> 1;
599 
600     for (int sample = 0; sample < inNumSamples; ++sample) {
601         int bufferIndex = bufPos * bufChannels;
602 
603         for (uint32 channel = 0; channel < bufChannels; channel++)
604             out[channel][sample] = bufData[bufferIndex + channel];
605 
606         const uint32 oldBufPos = bufPos;
607         bufPos += 1;
608         framePos += 1;
609 
610         if ((oldBufPos < bufFrames2) && (bufPos >= bufFrames2))
611             test = true;
612 
613         if (bufPos >= bufFrames) {
614             test = true;
615             bufPos -= bufFrames;
616         }
617     }
618     if (unit->m_buf->mask1 >= 0 && bufPos >= unit->m_buf->mask1)
619         unit->mDone = true;
620     if (test)
621         VDiskIn_request_buffer(unit, fbufnum, bufFrames2, bufChannels, bufPos);
622 
623     unit->m_iFramePos = framePos;
624     unit->m_iBufPos = bufPos;
625 }
626 
627 
628 ////////////////////////////////////////////////////////////////////////////////////////////////////////
unload(InterfaceTable * inTable)629 C_LINKAGE SC_API_EXPORT void unload(InterfaceTable* inTable) { delete gDiskIO; }
630 
PluginLoad(DiskIO)631 PluginLoad(DiskIO) {
632     ft = inTable;
633     gDiskIO = new DiskIOThread();
634     gDiskIO->launchThread();
635 
636     DefineSimpleUnit(DiskIn);
637     DefineDtorUnit(DiskOut);
638     DefineSimpleUnit(VDiskIn);
639 }
640 
641 //////////////////////////////////////////////////////////////////////////////////////////////////
642