2 SuperCollider real time audio synthesis system
3 Copyright (c) 2002 James McCartney. All rights reserved.
4 http://www.audiosynth.com
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program 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. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 Output of frame index for DiskOut by Marije Baalman (nescivi), 2009
22 VDiskIn by Scott Wilson, 2008
26 #include "SC_SyncCondition.h"
27 #include "SC_PlugIn.h"
29 #include <sndfile-win.h>
36 static InterfaceTable
*ft
;
38 const int kMAXDISKCHANNELS
= 32;
55 struct DiskIn
: public Unit
62 struct DiskOut
: public Unit
67 uint32 m_framewritten
;
70 struct VDiskIn
: public Unit
72 float m_fbufnum
, m_pchRatio
, m_rBufSize
;
73 double m_framePos
, m_bufPos
;
78 //////////////////////////////////////////////////////////////////////////////////////////////////
83 void DiskIn_next(DiskIn
*unit
, int inNumSamples
);
84 void DiskIn_Ctor(DiskIn
* unit
);
86 void DiskOut_next(DiskOut
*unit
, int inNumSamples
);
87 void DiskOut_Ctor(DiskOut
* unit
);
89 void VDiskIn_next(VDiskIn
*unit
, int inNumSamples
);
90 void VDiskIn_first(VDiskIn
*unit
, int inNumSamples
);
91 void VDiskIn_Ctor(VDiskIn
* unit
);
94 //////////////////////////////////////////////////////////////////////////////////////////////////
118 void DiskIOMsg::Perform()
122 SndBuf
*buf
= World_GetNRTBuf(mWorld
, mBufNum
);
123 if (mPos
> buf
->frames
|| mPos
+ mFrames
> buf
->frames
|| buf
->channels
!= mChannels
) goto leave
;
128 count
= buf
->sndfile
? sf_readf_float(buf
->sndfile
, buf
->data
+ mPos
* buf
->channels
, mFrames
) : 0;
129 if (count
< mFrames
) {
130 memset(buf
->data
+ (mPos
+ count
) * buf
->channels
, 0, (mFrames
- count
) * buf
->channels
* sizeof(float));
131 World_GetBuf(mWorld
, mBufNum
)->mask
= mPos
+ count
;
132 // NOTE: Possible race condition above: The disk IO thread may write to the rt SndBuf
133 // while the stage3 of the sequenced commands copies the non-rt SndBuf struct to the rt buf.
134 // This only happens if the buffer is modified via an osc command.
135 // We can't use the non-rt SndBuf above since buf->mask won't be reflected to the rt buf.
138 case kDiskCmd_ReadLoop
:
140 memset(buf
->data
+ mPos
* buf
->channels
, 0, mFrames
* buf
->channels
* sizeof(float));
143 count
= sf_readf_float(buf
->sndfile
, buf
->data
+ mPos
* buf
->channels
, mFrames
);
144 while (mFrames
-= count
) {
145 sf_seek(buf
->sndfile
, 0, SEEK_SET
);
146 count
= sf_readf_float(buf
->sndfile
, buf
->data
+ (mPos
+ count
) * buf
->channels
, mFrames
);
149 case kDiskCmd_Write
:
150 //printf("kDiskCmd_Write %d %p\n", mBufNum, buf->sndfile);
151 if (!buf
->sndfile
) goto leave
;
152 count
= sf_writef_float(buf
->sndfile
, buf
->data
+ mPos
* buf
->channels
, mFrames
);
160 MsgFifoNoFree
<DiskIOMsg
, 256> gDiskFifo
;
161 SC_SyncCondition gDiskFifoHasData
;
163 void* disk_io_thread_func(void* arg
)
166 gDiskFifoHasData
.WaitEach();
174 //////////////////////////////////////////////////////////////////////////////////////////////////
176 #define MAXCHANNELS 32
178 #define SETUP_OUT(offset) \
179 if (unit->mNumOutputs != bufChannels) { \
180 ClearUnitOutputs(unit, inNumSamples); \
183 float *out[MAXCHANNELS]; \
184 for (uint32 i=0; i<bufChannels; ++i) out[i] = OUT(i+offset);
186 #define SETUP_IN(offset) \
187 if (unit->mNumInputs - (uint32)offset != bufChannels) { \
188 ClearUnitOutputs(unit, inNumSamples); \
191 float *in[MAXCHANNELS]; \
192 for (uint32 i=0; i<bufChannels; ++i) in[i] = IN(i+offset);
194 void DiskIn_Ctor(DiskIn
* unit
)
196 unit
->m_fbufnum
= -1.f
;
197 unit
->m_buf
= unit
->mWorld
->mSndBufs
;
198 unit
->m_framepos
= 0;
199 SETCALC(DiskIn_next
);
202 void DiskIn_next(DiskIn
*unit
, int inNumSamples
)
205 if (!bufData
|| ((bufFrames
& ((unit
->mWorld
->mBufLength
<<1) - 1)) != 0)) {
206 unit
->m_framepos
= 0;
207 ClearUnitOutputs(unit
, inNumSamples
);
212 if (unit
->m_framepos
>= bufFrames
) {
213 unit
->m_framepos
= 0;
216 bufData
+= unit
->m_framepos
* bufChannels
;
218 // buffer must be allocated as a multiple of 2*blocksize.
219 if (bufChannels
> 2) {
220 for (int j
=0; j
<inNumSamples
; ++j
) {
221 for (uint32 i
=0; i
<bufChannels
; ++i
) {
222 *out
[i
]++ = *bufData
++;
225 } else if (bufChannels
== 2) {
226 float *out0
= out
[0];
227 float *out1
= out
[1];
228 for (int j
=0; j
<inNumSamples
; ++j
) {
229 *out0
++ = *bufData
++;
230 *out1
++ = *bufData
++;
233 float *out0
= out
[0];
234 for (int j
=0; j
<inNumSamples
; ++j
) {
235 *out0
++ = *bufData
++;
238 if(unit
->m_buf
->mask1
>=0 && unit
->m_framepos
>=unit
->m_buf
->mask1
) unit
->mDone
= true;
239 unit
->m_framepos
+= inNumSamples
;
240 uint32 bufFrames2
= bufFrames
>> 1;
241 if (unit
->m_framepos
== bufFrames
) {
242 unit
->m_framepos
= 0;
244 } else if (unit
->m_framepos
== bufFrames2
) {
246 if (unit
->m_buf
->mask
>=0) unit
->m_buf
->mask1
= unit
->m_buf
->mask
;
247 if(unit
->mWorld
->mRealTime
){
248 // send a message to read
250 msg
.mWorld
= unit
->mWorld
;
251 msg
.mCommand
= (int)ZIN0(1) ? kDiskCmd_ReadLoop
: kDiskCmd_Read
;
252 msg
.mBufNum
= (int)fbufnum
;
253 msg
.mPos
= bufFrames2
- unit
->m_framepos
;
254 msg
.mFrames
= bufFrames2
;
255 msg
.mChannels
= bufChannels
;
256 gDiskFifo
.Write(msg
);
257 gDiskFifoHasData
.Signal();
259 SndBuf
*bufr
= World_GetNRTBuf(unit
->mWorld
, (int) fbufnum
);
260 uint32 mPos
= bufFrames2
- unit
->m_framepos
;
261 if (mPos
> (uint32
)bufr
->frames
|| mPos
+ bufFrames2
> (uint32
)bufr
->frames
|| (uint32
) bufr
->channels
!= bufChannels
) return;
264 if ((int)ZIN0(1)) { // loop
265 if (!bufr
->sndfile
) memset(bufr
->data
+ mPos
* bufr
->channels
, 0, bufFrames2
* bufr
->channels
* sizeof(float));
266 count
= sf_readf_float(bufr
->sndfile
, bufr
->data
+ mPos
* bufr
->channels
, bufFrames2
);
267 while (bufFrames2
-= count
) {
268 sf_seek(bufr
->sndfile
, 0, SEEK_SET
);
269 count
= sf_readf_float(bufr
->sndfile
, bufr
->data
+ (mPos
+ count
) * bufr
->channels
, bufFrames2
);
272 count
= bufr
->sndfile
? sf_readf_float(bufr
->sndfile
, bufr
->data
+ mPos
* bufr
->channels
, bufFrames2
) : 0;
273 if (count
< bufFrames2
) {
274 memset(bufr
->data
+ (mPos
+ count
) * bufr
->channels
, 0, (bufFrames2
- count
) * bufr
->channels
* sizeof(float));
275 unit
->m_buf
->mask
= mPos
+ count
;
282 ////////////////////////////////////////////////////////////////////////////////////////////////////////
284 void DiskOut_Ctor(DiskOut
* unit
)
286 unit
->m_fbufnum
= -1.f
;
287 unit
->m_buf
= unit
->mWorld
->mSndBufs
;
288 unit
->m_framepos
= 0;
289 unit
->m_framewritten
= 0;
290 SETCALC(DiskOut_next
);
294 void DiskOut_next(DiskOut
*unit
, int inNumSamples
)
298 if (!bufData
|| ((bufFrames
& ((unit
->mWorld
->mBufLength
<<1) - 1)) != 0)) {
299 unit
->m_framepos
= 0;
300 // unit->m_framewritten = 0;
306 uint32 framew
= unit
->m_framewritten
;
308 // printf("Start frames %i %i\n", unit->m_framewritten, framew );
310 if (unit
->m_framepos
>= bufFrames
) {
311 unit
->m_framepos
= 0;
314 bufData
+= unit
->m_framepos
* bufChannels
;
316 if (bufChannels
> 2) {
317 for (int j
=0; j
<inNumSamples
; ++j
) {
318 for (uint32 i
=0; i
<bufChannels
; ++i
) {
319 *bufData
++ = *in
[i
]++;
323 } else if (bufChannels
== 2) {
326 for (int j
=0; j
<inNumSamples
; ++j
) {
333 for (int j
=0; j
<inNumSamples
; ++j
) {
339 unit
->m_framepos
+= inNumSamples
;
340 // unit->m_framewritten += inNumSamples;
341 unit
->m_framewritten
= framew
;
343 // printf("frames %i %i\n", unit->m_framewritten, framew );
345 uint32 bufFrames2
= bufFrames
>> 1;
346 if (unit
->m_framepos
== bufFrames
) {
347 unit
->m_framepos
= 0;
349 } else if (unit
->m_framepos
== bufFrames2
) {
351 // send a message to write
353 msg
.mWorld
= unit
->mWorld
;
354 msg
.mCommand
= kDiskCmd_Write
;
355 msg
.mBufNum
= (int)fbufnum
;
356 msg
.mPos
= bufFrames2
- unit
->m_framepos
;
357 msg
.mFrames
= bufFrames2
;
358 msg
.mChannels
= bufChannels
;
359 //printf("sendMessage %d %d %d %d\n", msg.mBufNum, msg.mPos, msg.mFrames, msg.mChannels);
360 gDiskFifo
.Write(msg
);
361 gDiskFifoHasData
.Signal();
367 void VDiskIn_Ctor(VDiskIn
* unit
)
369 unit
->m_fbufnum
= -1.f
;
370 unit
->m_buf
= unit
->mWorld
->mSndBufs
;
371 unit
->m_framePos
= 0.;
373 unit
->m_pchRatio
= sc_max(IN0(1), 0.f
);
376 SETCALC(VDiskIn_first
); // should be first
379 // first time through, the FIRST sample doesn't need the interpolation... the buffer won't be filled 'correctly' for
380 // interpolation, so use the _first function to make this exception
382 void VDiskIn_first(VDiskIn
*unit
, int inNumSamples
)
385 SETCALC(VDiskIn_next
);
387 float a
, b
, c
, d
, oldBufPos
;
392 if (!bufData
|| ((bufFrames
& ((unit
->mWorld
->mBufLength
<<1) - 1)) != 0)) {
393 unit
->m_framePos
= 0.;
395 ClearUnitOutputs(unit
, inNumSamples
);
399 uint32 bufFrames2
= bufFrames
>> 1;
400 float fbufFrames2
= (float)bufFrames2
;
401 float fbufFrames
= (float)bufFrames
;
402 unit
->m_rBufSize
= 1. / bufFrames
;
406 float framePos
= unit
->m_framePos
;
407 float bufPos
= unit
->m_bufPos
; // where we are in the DiskIn buffer
408 float newPchRatio
= sc_max(IN0(1), 0.f
);
410 if ((newPchRatio
* inNumSamples
* unit
->m_rBufSize
) >= 0.5) {
411 printf("pitch ratio is greater then max allowed (see VDiskIn help)\n");
412 ClearUnitOutputs(unit
, inNumSamples
);
413 SETCALC(VDiskIn_first
);
417 float pchRatio
= unit
->m_pchRatio
;
418 float pchSlope
= CALCSLOPE(newPchRatio
, pchRatio
);
420 const float* tableInit
= bufData
;
422 for (uint32 i
= 0; i
< bufChannels
; i
++){
423 out
[i
][0] = bufData
[0 + i
];
426 pchRatio
+= pchSlope
;
427 framePos
+= pchRatio
;
430 for (int j
= 1; j
< inNumSamples
; j
++){
431 uint32 iBufPos
= (uint32
)bufPos
;
432 float frac
= bufPos
- (float)iBufPos
;
433 int table1
= iBufPos
* bufChannels
;
434 int table0
= table1
- bufChannels
;
435 int table2
= table1
+ bufChannels
;
436 int table3
= table2
+ bufChannels
;
437 while(table1
>= bufSamples
) table1
-= bufSamples
;
438 while(table0
< 0) table0
+= bufSamples
;
439 while(table2
>= bufSamples
) table2
-= bufSamples
;
440 while(table3
>= bufSamples
) table3
-= bufSamples
;
441 for (int i
= 0; i
< bufChannels
; i
++){
442 a
= bufData
[table0
+ i
];
443 b
= bufData
[table1
+ i
];
444 c
= bufData
[table2
+ i
];
445 d
= bufData
[table3
+ i
];
446 out
[i
][j
] = cubicinterp(frac
, a
, b
, c
, d
);
448 pchRatio
+= pchSlope
;
449 framePos
+= pchRatio
;
452 // the +1 is needed for the cubic interpolation... make SURE the old data isn't needed any more before
453 // setting up the new buffer
454 if ((oldBufPos
< (fbufFrames2
+ 1)) && ((bufPos
>= (fbufFrames2
+ 1)))){
457 if (bufPos
>= (fbufFrames
+ 1)){
459 bufPos
-= fbufFrames
;
462 if (unit
->m_buf
->mask1
>=0 && bufPos
>=unit
->m_buf
->mask1
) unit
->mDone
= true;
464 if (unit
->m_buf
->mask
>=0) unit
->m_buf
->mask1
= unit
->m_buf
->mask
;
466 if(unit
->mWorld
->mRealTime
) {
469 msg
.mWorld
= unit
->mWorld
;
470 msg
.mCommand
= (int)ZIN0(2) ? kDiskCmd_ReadLoop
: kDiskCmd_Read
;
471 msg
.mBufNum
= (int)fbufnum
;
473 if((uint32
)bufPos
>= bufFrames2
) thisPos
= 0; else thisPos
= bufFrames2
;
475 msg
.mFrames
= bufFrames2
;
476 msg
.mChannels
= bufChannels
;
477 gDiskFifo
.Write(msg
);
478 gDiskFifoHasData
.Signal();
482 // float outval = bufPos + sc_mod((float)(unit->m_count * bufFrames2), (float)buf->fileinfo.frames);
483 float outval
= bufPos
+ (float)(unit
->m_count
* bufFrames2
);
484 SendNodeReply(&unit
->mParent
->mNode
, (int)ZIN0(3), "/diskin", 1, &outval
);
488 SndBuf
*bufr
= World_GetNRTBuf(unit
->mWorld
, (int) fbufnum
);
490 if((uint32
)bufPos
>= bufFrames2
) mPos
= 0; else mPos
= bufFrames2
;
491 if (mPos
> (uint32
)bufr
->frames
|| mPos
+ bufFrames2
> (uint32
)bufr
->frames
|| (uint32
) bufr
->channels
!= bufChannels
) return;
494 if ((int)ZIN0(2)) { // loop
495 if (!bufr
->sndfile
) memset(bufr
->data
+ mPos
* bufr
->channels
, 0, bufFrames2
* bufr
->channels
* sizeof(float));
496 count
= sf_readf_float(bufr
->sndfile
, bufr
->data
+ mPos
* bufr
->channels
, bufFrames2
);
497 while (bufFrames2
-= count
) {
498 sf_seek(bufr
->sndfile
, 0, SEEK_SET
);
499 count
= sf_readf_float(bufr
->sndfile
, bufr
->data
+ (mPos
+ count
) * bufr
->channels
, bufFrames2
);
502 count
= bufr
->sndfile
? sf_readf_float(bufr
->sndfile
, bufr
->data
+ mPos
* bufr
->channels
, bufFrames2
) : 0;
503 if (count
< bufFrames2
) {
504 memset(bufr
->data
+ (mPos
+ count
) * bufr
->channels
, 0, (bufFrames2
- count
) * bufr
->channels
* sizeof(float));
505 unit
->m_buf
->mask
= mPos
+ count
;
512 unit
->m_framePos
= framePos
;
513 unit
->m_pchRatio
= pchRatio
;
514 unit
->m_bufPos
= bufPos
;
517 void VDiskIn_next(VDiskIn
*unit
, int inNumSamples
)
524 if (!bufData
|| ((bufFrames
& ((unit
->mWorld
->mBufLength
<<1) - 1)) != 0)) {
525 unit
->m_framePos
= 0.;
527 ClearUnitOutputs(unit
, inNumSamples
);
533 double framePos
= unit
->m_framePos
;
534 double bufPos
= unit
->m_bufPos
; // where we are in the DiskIn buffer
535 float newPchRatio
= sc_max(IN0(1), 0.f
);
536 if ((newPchRatio
* inNumSamples
* unit
->m_rBufSize
) >= 0.5) {
537 printf("pitch ratio is greater then max allowed (see VDiskIn help)\n");
538 ClearUnitOutputs(unit
, inNumSamples
);
542 float pchRatio
= unit
->m_pchRatio
;
543 float pchSlope
= CALCSLOPE(newPchRatio
, pchRatio
);
544 uint32 bufFrames2
= bufFrames
>> 1;
545 double fbufFrames2
= (double)bufFrames2
;
546 double fbufFrames
= (double)bufFrames
;
548 for (int j
= 0; j
< inNumSamples
; ++j
){
549 int32 iBufPos
= (int32
)bufPos
;
550 double frac
= bufPos
- (double)iBufPos
;
551 int table1
= iBufPos
* bufChannels
;
552 int table0
= table1
- bufChannels
;
553 int table2
= table1
+ bufChannels
;
554 int table3
= table2
+ bufChannels
;
555 while(table1
>= bufSamples
) table1
-= bufSamples
;
556 while(table0
< 0) table0
+= bufSamples
;
557 while(table2
>= bufSamples
) table2
-= bufSamples
;
558 while(table3
>= bufSamples
) table3
-= bufSamples
;
559 for (uint32 i
= 0; i
< bufChannels
; i
++){
560 a
= bufData
[table0
+ i
];
561 b
= bufData
[table1
+ i
];
562 c
= bufData
[table2
+ i
];
563 d
= bufData
[table3
+ i
];
564 out
[i
][j
] = cubicinterp(frac
, a
, b
, c
, d
);
566 pchRatio
+= pchSlope
;
567 framePos
+= pchRatio
;
571 if ((oldBufPos
< (fbufFrames2
+ 1)) && ((bufPos
>= (fbufFrames2
+ 1)))){
574 if (bufPos
>= (fbufFrames
+ 1)){
576 bufPos
-= fbufFrames
;
579 if (unit
->m_buf
->mask1
>=0 && bufPos
>=unit
->m_buf
->mask1
) unit
->mDone
= true;
581 if (unit
->m_buf
->mask
>=0) unit
->m_buf
->mask1
= unit
->m_buf
->mask
;
583 if(unit
->mWorld
->mRealTime
){
586 msg
.mWorld
= unit
->mWorld
;
587 msg
.mCommand
= (int)ZIN0(2) ? kDiskCmd_ReadLoop
: kDiskCmd_Read
;
588 msg
.mBufNum
= (int)fbufnum
;
590 if((uint32
)bufPos
>= bufFrames2
) thisPos
= 0; else thisPos
= bufFrames2
;
592 msg
.mFrames
= bufFrames2
;
593 msg
.mChannels
= bufChannels
;
594 gDiskFifo
.Write(msg
);
595 gDiskFifoHasData
.Signal();
599 // float outval = bufPos + sc_mod((float)(unit->m_count * bufFrames2), (float)buf->fileinfo.frames);
600 float outval
= bufPos
+ (float)(unit
->m_count
* bufFrames2
);
601 SendNodeReply(&unit
->mParent
->mNode
, (int)ZIN0(3), "/diskin", 1, &outval
);
605 SndBuf
*bufr
= World_GetNRTBuf(unit
->mWorld
, (int)fbufnum
);
607 if((uint32
)bufPos
>= bufFrames2
) mPos
= 0; else mPos
= bufFrames2
;
608 if (mPos
> (uint32
)bufr
->frames
|| mPos
+ bufFrames2
> (uint32
)bufr
->frames
|| (uint32
) bufr
->channels
!= bufChannels
) return;
611 if ((int)ZIN0(2)) { // loop
612 if (!bufr
->sndfile
) memset(bufr
->data
+ mPos
* bufr
->channels
, 0, bufFrames2
* bufr
->channels
* sizeof(float));
613 count
= sf_readf_float(bufr
->sndfile
, bufr
->data
+ mPos
* bufr
->channels
, bufFrames2
);
614 while (bufFrames2
-= count
) {
615 sf_seek(bufr
->sndfile
, 0, SEEK_SET
);
616 count
= sf_readf_float(bufr
->sndfile
, bufr
->data
+ (mPos
+ count
) * bufr
->channels
, bufFrames2
);
619 count
= bufr
->sndfile
? sf_readf_float(bufr
->sndfile
, bufr
->data
+ mPos
* bufr
->channels
, bufFrames2
) : 0;
620 if (count
< bufFrames2
) {
621 memset(bufr
->data
+ (mPos
+ count
) * bufr
->channels
, 0, (bufFrames2
- count
) * bufr
->channels
* sizeof(float));
622 unit
->m_buf
->mask
= mPos
+ count
;
631 unit
->m_framePos
= framePos
;
632 unit
->m_pchRatio
= pchRatio
;
633 unit
->m_bufPos
= bufPos
;
638 ////////////////////////////////////////////////////////////////////////////////////////////////////////
645 new(&gDiskFifo
) MsgFifoNoFree
<DiskIOMsg
, 256>();
646 new(&gDiskFifoHasData
) SC_SyncCondition();
649 pthread_t diskioThread
;
650 pthread_create (&diskioThread
, NULL
, disk_io_thread_func
, (void*)0);
652 DefineSimpleUnit(DiskIn
);
653 DefineSimpleUnit(DiskOut
);
654 DefineSimpleUnit(VDiskIn
);
657 //////////////////////////////////////////////////////////////////////////////////////////////////