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