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"
33 static InterfaceTable
*ft
;
35 const int kMAXDISKCHANNELS
= 32;
52 struct DiskIn
: public Unit
59 struct DiskOut
: public Unit
64 uint32 m_framewritten
;
67 struct VDiskIn
: public Unit
69 float m_fbufnum
, m_pchRatio
, m_rBufSize
;
70 double m_framePos
, m_bufPos
;
75 //////////////////////////////////////////////////////////////////////////////////////////////////
80 void DiskIn_next(DiskIn
*unit
, int inNumSamples
);
81 void DiskIn_Ctor(DiskIn
* unit
);
83 void DiskOut_next(DiskOut
*unit
, int inNumSamples
);
84 void DiskOut_Ctor(DiskOut
* unit
);
86 void VDiskIn_next(VDiskIn
*unit
, int inNumSamples
);
87 void VDiskIn_first(VDiskIn
*unit
, int inNumSamples
);
88 void VDiskIn_Ctor(VDiskIn
* unit
);
91 //////////////////////////////////////////////////////////////////////////////////////////////////
115 void DiskIOMsg::Perform()
119 SndBuf
*buf
= World_GetNRTBuf(mWorld
, mBufNum
);
120 if (mPos
> buf
->frames
|| mPos
+ mFrames
> buf
->frames
|| buf
->channels
!= mChannels
) goto leave
;
125 count
= buf
->sndfile
? sf_readf_float(buf
->sndfile
, buf
->data
+ mPos
* buf
->channels
, mFrames
) : 0;
126 if (count
< mFrames
) {
127 memset(buf
->data
+ (mPos
+ count
) * buf
->channels
, 0, (mFrames
- count
) * buf
->channels
* sizeof(float));
128 World_GetBuf(mWorld
, mBufNum
)->mask
= mPos
+ count
;
129 // NOTE: Possible race condition above: The disk IO thread may write to the rt SndBuf
130 // while the stage3 of the sequenced commands copies the non-rt SndBuf struct to the rt buf.
131 // This only happens if the buffer is modified via an osc command.
132 // We can't use the non-rt SndBuf above since buf->mask won't be reflected to the rt buf.
135 case kDiskCmd_ReadLoop
:
137 memset(buf
->data
+ mPos
* buf
->channels
, 0, mFrames
* buf
->channels
* sizeof(float));
140 count
= sf_readf_float(buf
->sndfile
, buf
->data
+ mPos
* buf
->channels
, mFrames
);
141 while (mFrames
-= count
) {
142 sf_seek(buf
->sndfile
, 0, SEEK_SET
);
143 count
= sf_readf_float(buf
->sndfile
, buf
->data
+ (mPos
+ count
) * buf
->channels
, mFrames
);
146 case kDiskCmd_Write
:
147 //printf("kDiskCmd_Write %d %p\n", mBufNum, buf->sndfile);
148 if (!buf
->sndfile
) goto leave
;
149 count
= sf_writef_float(buf
->sndfile
, buf
->data
+ mPos
* buf
->channels
, mFrames
);
157 MsgFifoNoFree
<DiskIOMsg
, 256> gDiskFifo
;
158 SC_SyncCondition gDiskFifoHasData
;
160 void* disk_io_thread_func(void* arg
)
163 gDiskFifoHasData
.WaitEach();
171 //////////////////////////////////////////////////////////////////////////////////////////////////
173 #define MAXCHANNELS 32
175 #define SETUP_OUT(offset) \
176 if (unit->mNumOutputs != bufChannels) { \
177 ClearUnitOutputs(unit, inNumSamples); \
180 float *out[MAXCHANNELS]; \
181 for (uint32 i=0; i<bufChannels; ++i) out[i] = OUT(i+offset);
183 #define SETUP_IN(offset) \
184 if (unit->mNumInputs - (uint32)offset != bufChannels) { \
185 ClearUnitOutputs(unit, inNumSamples); \
188 float *in[MAXCHANNELS]; \
189 for (uint32 i=0; i<bufChannels; ++i) in[i] = IN(i+offset);
191 void DiskIn_Ctor(DiskIn
* unit
)
193 unit
->m_fbufnum
= -1.f
;
194 unit
->m_buf
= unit
->mWorld
->mSndBufs
;
195 unit
->m_framepos
= 0;
196 SETCALC(DiskIn_next
);
199 void DiskIn_next(DiskIn
*unit
, int inNumSamples
)
202 if (!bufData
|| ((bufFrames
& ((unit
->mWorld
->mBufLength
<<1) - 1)) != 0)) {
203 unit
->m_framepos
= 0;
204 ClearUnitOutputs(unit
, inNumSamples
);
209 if (unit
->m_framepos
>= bufFrames
) {
210 unit
->m_framepos
= 0;
213 bufData
+= unit
->m_framepos
* bufChannels
;
215 // buffer must be allocated as a multiple of 2*blocksize.
216 if (bufChannels
> 2) {
217 for (int j
=0; j
<inNumSamples
; ++j
) {
218 for (uint32 i
=0; i
<bufChannels
; ++i
) {
219 *out
[i
]++ = *bufData
++;
222 } else if (bufChannels
== 2) {
223 float *out0
= out
[0];
224 float *out1
= out
[1];
225 for (int j
=0; j
<inNumSamples
; ++j
) {
226 *out0
++ = *bufData
++;
227 *out1
++ = *bufData
++;
230 float *out0
= out
[0];
231 for (int j
=0; j
<inNumSamples
; ++j
) {
232 *out0
++ = *bufData
++;
235 if(unit
->m_buf
->mask1
>=0 && unit
->m_framepos
>=unit
->m_buf
->mask1
) unit
->mDone
= true;
236 unit
->m_framepos
+= inNumSamples
;
237 uint32 bufFrames2
= bufFrames
>> 1;
238 if (unit
->m_framepos
== bufFrames
) {
239 unit
->m_framepos
= 0;
241 } else if (unit
->m_framepos
== bufFrames2
) {
243 if (unit
->m_buf
->mask
>=0) unit
->m_buf
->mask1
= unit
->m_buf
->mask
;
244 if(unit
->mWorld
->mRealTime
){
245 // send a message to read
247 msg
.mWorld
= unit
->mWorld
;
248 msg
.mCommand
= (int)ZIN0(1) ? kDiskCmd_ReadLoop
: kDiskCmd_Read
;
249 msg
.mBufNum
= (int)fbufnum
;
250 msg
.mPos
= bufFrames2
- unit
->m_framepos
;
251 msg
.mFrames
= bufFrames2
;
252 msg
.mChannels
= bufChannels
;
253 gDiskFifo
.Write(msg
);
254 gDiskFifoHasData
.Signal();
256 SndBuf
*bufr
= World_GetNRTBuf(unit
->mWorld
, (int) fbufnum
);
257 uint32 mPos
= bufFrames2
- unit
->m_framepos
;
258 if (mPos
> (uint32
)bufr
->frames
|| mPos
+ bufFrames2
> (uint32
)bufr
->frames
|| (uint32
) bufr
->channels
!= bufChannels
) return;
261 if ((int)ZIN0(1)) { // loop
262 if (!bufr
->sndfile
) memset(bufr
->data
+ mPos
* bufr
->channels
, 0, bufFrames2
* bufr
->channels
* sizeof(float));
263 count
= sf_readf_float(bufr
->sndfile
, bufr
->data
+ mPos
* bufr
->channels
, bufFrames2
);
264 while (bufFrames2
-= count
) {
265 sf_seek(bufr
->sndfile
, 0, SEEK_SET
);
266 count
= sf_readf_float(bufr
->sndfile
, bufr
->data
+ (mPos
+ count
) * bufr
->channels
, bufFrames2
);
269 count
= bufr
->sndfile
? sf_readf_float(bufr
->sndfile
, bufr
->data
+ mPos
* bufr
->channels
, bufFrames2
) : 0;
270 if (count
< bufFrames2
) {
271 memset(bufr
->data
+ (mPos
+ count
) * bufr
->channels
, 0, (bufFrames2
- count
) * bufr
->channels
* sizeof(float));
272 unit
->m_buf
->mask
= mPos
+ count
;
279 ////////////////////////////////////////////////////////////////////////////////////////////////////////
281 void DiskOut_Ctor(DiskOut
* unit
)
283 unit
->m_fbufnum
= -1.f
;
284 unit
->m_buf
= unit
->mWorld
->mSndBufs
;
285 unit
->m_framepos
= 0;
286 unit
->m_framewritten
= 0;
287 SETCALC(DiskOut_next
);
291 void DiskOut_next(DiskOut
*unit
, int inNumSamples
)
295 if (!bufData
|| ((bufFrames
& ((unit
->mWorld
->mBufLength
<<1) - 1)) != 0)) {
296 unit
->m_framepos
= 0;
297 // unit->m_framewritten = 0;
303 uint32 framew
= unit
->m_framewritten
;
305 // printf("Start frames %i %i\n", unit->m_framewritten, framew );
307 if (unit
->m_framepos
>= bufFrames
) {
308 unit
->m_framepos
= 0;
311 bufData
+= unit
->m_framepos
* bufChannels
;
313 if (bufChannels
> 2) {
314 for (int j
=0; j
<inNumSamples
; ++j
) {
315 for (uint32 i
=0; i
<bufChannels
; ++i
) {
316 *bufData
++ = *in
[i
]++;
320 } else if (bufChannels
== 2) {
323 for (int j
=0; j
<inNumSamples
; ++j
) {
330 for (int j
=0; j
<inNumSamples
; ++j
) {
336 unit
->m_framepos
+= inNumSamples
;
337 // unit->m_framewritten += inNumSamples;
338 unit
->m_framewritten
= framew
;
340 // printf("frames %i %i\n", unit->m_framewritten, framew );
342 uint32 bufFrames2
= bufFrames
>> 1;
343 if (unit
->m_framepos
== bufFrames
) {
344 unit
->m_framepos
= 0;
346 } else if (unit
->m_framepos
== bufFrames2
) {
348 // send a message to write
350 msg
.mWorld
= unit
->mWorld
;
351 msg
.mCommand
= kDiskCmd_Write
;
352 msg
.mBufNum
= (int)fbufnum
;
353 msg
.mPos
= bufFrames2
- unit
->m_framepos
;
354 msg
.mFrames
= bufFrames2
;
355 msg
.mChannels
= bufChannels
;
356 //printf("sendMessage %d %d %d %d\n", msg.mBufNum, msg.mPos, msg.mFrames, msg.mChannels);
357 gDiskFifo
.Write(msg
);
358 gDiskFifoHasData
.Signal();
364 void VDiskIn_Ctor(VDiskIn
* unit
)
366 unit
->m_fbufnum
= -1.f
;
367 unit
->m_buf
= unit
->mWorld
->mSndBufs
;
368 unit
->m_framePos
= 0.;
370 unit
->m_pchRatio
= sc_max(IN0(1), 0.f
);
373 SETCALC(VDiskIn_first
); // should be first
376 // first time through, the FIRST sample doesn't need the interpolation... the buffer won't be filled 'correctly' for
377 // interpolation, so use the _first function to make this exception
379 void VDiskIn_first(VDiskIn
*unit
, int inNumSamples
)
382 SETCALC(VDiskIn_next
);
384 float a
, b
, c
, d
, oldBufPos
;
389 if (!bufData
|| ((bufFrames
& ((unit
->mWorld
->mBufLength
<<1) - 1)) != 0)) {
390 unit
->m_framePos
= 0.;
392 ClearUnitOutputs(unit
, inNumSamples
);
396 uint32 bufFrames2
= bufFrames
>> 1;
397 float fbufFrames2
= (float)bufFrames2
;
398 float fbufFrames
= (float)bufFrames
;
399 unit
->m_rBufSize
= 1. / bufFrames
;
403 float framePos
= unit
->m_framePos
;
404 float bufPos
= unit
->m_bufPos
; // where we are in the DiskIn buffer
405 float newPchRatio
= sc_max(IN0(1), 0.f
);
407 if ((newPchRatio
* inNumSamples
* unit
->m_rBufSize
) >= 0.5) {
408 printf("pitch ratio is greater then max allowed (see VDiskIn help)\n");
409 ClearUnitOutputs(unit
, inNumSamples
);
410 SETCALC(VDiskIn_first
);
414 float pchRatio
= unit
->m_pchRatio
;
415 float pchSlope
= CALCSLOPE(newPchRatio
, pchRatio
);
417 for (uint32 i
= 0; i
< bufChannels
; i
++){
418 out
[i
][0] = bufData
[0 + i
];
421 pchRatio
+= pchSlope
;
422 framePos
+= pchRatio
;
425 for (int j
= 1; j
< inNumSamples
; j
++){
426 uint32 iBufPos
= (uint32
)bufPos
;
427 float frac
= bufPos
- (float)iBufPos
;
428 int table1
= iBufPos
* bufChannels
;
429 int table0
= table1
- bufChannels
;
430 int table2
= table1
+ bufChannels
;
431 int table3
= table2
+ bufChannels
;
432 while(table1
>= bufSamples
) table1
-= bufSamples
;
433 while(table0
< 0) table0
+= bufSamples
;
434 while(table2
>= bufSamples
) table2
-= bufSamples
;
435 while(table3
>= bufSamples
) table3
-= bufSamples
;
436 for (int i
= 0; i
< bufChannels
; i
++){
437 a
= bufData
[table0
+ i
];
438 b
= bufData
[table1
+ i
];
439 c
= bufData
[table2
+ i
];
440 d
= bufData
[table3
+ i
];
441 out
[i
][j
] = cubicinterp(frac
, a
, b
, c
, d
);
443 pchRatio
+= pchSlope
;
444 framePos
+= pchRatio
;
447 // the +1 is needed for the cubic interpolation... make SURE the old data isn't needed any more before
448 // setting up the new buffer
449 if ((oldBufPos
< (fbufFrames2
+ 1)) && ((bufPos
>= (fbufFrames2
+ 1)))){
452 if (bufPos
>= (fbufFrames
+ 1)){
454 bufPos
-= fbufFrames
;
457 if (unit
->m_buf
->mask1
>=0 && bufPos
>=unit
->m_buf
->mask1
) unit
->mDone
= true;
459 if (unit
->m_buf
->mask
>=0) unit
->m_buf
->mask1
= unit
->m_buf
->mask
;
461 if(unit
->mWorld
->mRealTime
) {
464 msg
.mWorld
= unit
->mWorld
;
465 msg
.mCommand
= (int)ZIN0(2) ? kDiskCmd_ReadLoop
: kDiskCmd_Read
;
466 msg
.mBufNum
= (int)fbufnum
;
468 if((uint32
)bufPos
>= bufFrames2
) thisPos
= 0; else thisPos
= bufFrames2
;
470 msg
.mFrames
= bufFrames2
;
471 msg
.mChannels
= bufChannels
;
472 gDiskFifo
.Write(msg
);
473 gDiskFifoHasData
.Signal();
477 // float outval = bufPos + sc_mod((float)(unit->m_count * bufFrames2), (float)buf->fileinfo.frames);
478 float outval
= bufPos
+ (float)(unit
->m_count
* bufFrames2
);
479 SendNodeReply(&unit
->mParent
->mNode
, (int)ZIN0(3), "/diskin", 1, &outval
);
483 SndBuf
*bufr
= World_GetNRTBuf(unit
->mWorld
, (int) fbufnum
);
485 if((uint32
)bufPos
>= bufFrames2
) mPos
= 0; else mPos
= bufFrames2
;
486 if (mPos
> (uint32
)bufr
->frames
|| mPos
+ bufFrames2
> (uint32
)bufr
->frames
|| (uint32
) bufr
->channels
!= bufChannels
) return;
489 if ((int)ZIN0(2)) { // loop
490 if (!bufr
->sndfile
) memset(bufr
->data
+ mPos
* bufr
->channels
, 0, bufFrames2
* bufr
->channels
* sizeof(float));
491 count
= sf_readf_float(bufr
->sndfile
, bufr
->data
+ mPos
* bufr
->channels
, bufFrames2
);
492 while (bufFrames2
-= count
) {
493 sf_seek(bufr
->sndfile
, 0, SEEK_SET
);
494 count
= sf_readf_float(bufr
->sndfile
, bufr
->data
+ (mPos
+ count
) * bufr
->channels
, bufFrames2
);
497 count
= bufr
->sndfile
? sf_readf_float(bufr
->sndfile
, bufr
->data
+ mPos
* bufr
->channels
, bufFrames2
) : 0;
498 if (count
< bufFrames2
) {
499 memset(bufr
->data
+ (mPos
+ count
) * bufr
->channels
, 0, (bufFrames2
- count
) * bufr
->channels
* sizeof(float));
500 unit
->m_buf
->mask
= mPos
+ count
;
507 unit
->m_framePos
= framePos
;
508 unit
->m_pchRatio
= pchRatio
;
509 unit
->m_bufPos
= bufPos
;
512 void VDiskIn_next(VDiskIn
*unit
, int inNumSamples
)
519 if (!bufData
|| ((bufFrames
& ((unit
->mWorld
->mBufLength
<<1) - 1)) != 0)) {
520 unit
->m_framePos
= 0.;
522 ClearUnitOutputs(unit
, inNumSamples
);
528 double framePos
= unit
->m_framePos
;
529 double bufPos
= unit
->m_bufPos
; // where we are in the DiskIn buffer
530 float newPchRatio
= sc_max(IN0(1), 0.f
);
531 if ((newPchRatio
* inNumSamples
* unit
->m_rBufSize
) >= 0.5) {
532 printf("pitch ratio is greater then max allowed (see VDiskIn help)\n");
533 ClearUnitOutputs(unit
, inNumSamples
);
537 float pchRatio
= unit
->m_pchRatio
;
538 float pchSlope
= CALCSLOPE(newPchRatio
, pchRatio
);
539 uint32 bufFrames2
= bufFrames
>> 1;
540 double fbufFrames2
= (double)bufFrames2
;
541 double fbufFrames
= (double)bufFrames
;
543 for (int j
= 0; j
< inNumSamples
; ++j
){
544 int32 iBufPos
= (int32
)bufPos
;
545 double frac
= bufPos
- (double)iBufPos
;
546 int table1
= iBufPos
* bufChannels
;
547 int table0
= table1
- bufChannels
;
548 int table2
= table1
+ bufChannels
;
549 int table3
= table2
+ bufChannels
;
550 while(table1
>= bufSamples
) table1
-= bufSamples
;
551 while(table0
< 0) table0
+= bufSamples
;
552 while(table2
>= bufSamples
) table2
-= bufSamples
;
553 while(table3
>= bufSamples
) table3
-= bufSamples
;
554 for (uint32 i
= 0; i
< bufChannels
; i
++){
555 a
= bufData
[table0
+ i
];
556 b
= bufData
[table1
+ i
];
557 c
= bufData
[table2
+ i
];
558 d
= bufData
[table3
+ i
];
559 out
[i
][j
] = cubicinterp(frac
, a
, b
, c
, d
);
561 pchRatio
+= pchSlope
;
562 framePos
+= pchRatio
;
566 if ((oldBufPos
< (fbufFrames2
+ 1)) && ((bufPos
>= (fbufFrames2
+ 1)))){
569 if (bufPos
>= (fbufFrames
+ 1)){
571 bufPos
-= fbufFrames
;
574 if (unit
->m_buf
->mask1
>=0 && bufPos
>=unit
->m_buf
->mask1
) unit
->mDone
= true;
576 if (unit
->m_buf
->mask
>=0) unit
->m_buf
->mask1
= unit
->m_buf
->mask
;
578 if(unit
->mWorld
->mRealTime
){
581 msg
.mWorld
= unit
->mWorld
;
582 msg
.mCommand
= (int)ZIN0(2) ? kDiskCmd_ReadLoop
: kDiskCmd_Read
;
583 msg
.mBufNum
= (int)fbufnum
;
585 if((uint32
)bufPos
>= bufFrames2
) thisPos
= 0; else thisPos
= bufFrames2
;
587 msg
.mFrames
= bufFrames2
;
588 msg
.mChannels
= bufChannels
;
589 gDiskFifo
.Write(msg
);
590 gDiskFifoHasData
.Signal();
594 // float outval = bufPos + sc_mod((float)(unit->m_count * bufFrames2), (float)buf->fileinfo.frames);
595 float outval
= bufPos
+ (float)(unit
->m_count
* bufFrames2
);
596 SendNodeReply(&unit
->mParent
->mNode
, (int)ZIN0(3), "/diskin", 1, &outval
);
600 SndBuf
*bufr
= World_GetNRTBuf(unit
->mWorld
, (int)fbufnum
);
602 if((uint32
)bufPos
>= bufFrames2
) mPos
= 0; else mPos
= bufFrames2
;
603 if (mPos
> (uint32
)bufr
->frames
|| mPos
+ bufFrames2
> (uint32
)bufr
->frames
|| (uint32
) bufr
->channels
!= bufChannels
) return;
606 if ((int)ZIN0(2)) { // loop
607 if (!bufr
->sndfile
) memset(bufr
->data
+ mPos
* bufr
->channels
, 0, bufFrames2
* bufr
->channels
* sizeof(float));
608 count
= sf_readf_float(bufr
->sndfile
, bufr
->data
+ mPos
* bufr
->channels
, bufFrames2
);
609 while (bufFrames2
-= count
) {
610 sf_seek(bufr
->sndfile
, 0, SEEK_SET
);
611 count
= sf_readf_float(bufr
->sndfile
, bufr
->data
+ (mPos
+ count
) * bufr
->channels
, bufFrames2
);
614 count
= bufr
->sndfile
? sf_readf_float(bufr
->sndfile
, bufr
->data
+ mPos
* bufr
->channels
, bufFrames2
) : 0;
615 if (count
< bufFrames2
) {
616 memset(bufr
->data
+ (mPos
+ count
) * bufr
->channels
, 0, (bufFrames2
- count
) * bufr
->channels
* sizeof(float));
617 unit
->m_buf
->mask
= mPos
+ count
;
626 unit
->m_framePos
= framePos
;
627 unit
->m_pchRatio
= pchRatio
;
628 unit
->m_bufPos
= bufPos
;
633 ////////////////////////////////////////////////////////////////////////////////////////////////////////
640 new(&gDiskFifo
) MsgFifoNoFree
<DiskIOMsg
, 256>();
641 new(&gDiskFifoHasData
) SC_SyncCondition();
644 pthread_t diskioThread
;
645 pthread_create (&diskioThread
, NULL
, disk_io_thread_func
, (void*)0);
647 DefineSimpleUnit(DiskIn
);
648 DefineSimpleUnit(DiskOut
);
649 DefineSimpleUnit(VDiskIn
);
652 //////////////////////////////////////////////////////////////////////////////////////////////////