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
22 #include "SC_SequencedCommand.h"
23 #include "SC_CoreAudio.h"
24 #include "SC_Errors.h"
25 #include "scsynthsend.h"
26 #include "SC_Prototypes.h"
27 #include "SC_HiddenWorld.h"
29 #include "SC_DirUtils.h"
30 #include "SC_StringParser.h"
31 #include "../../common/SC_SndFileHelpers.hpp"
32 #include "SC_WorldOptions.h"
34 #define GET_COMPLETION_MSG(msg) \
35 mMsgSize = msg.getbsize(); \
37 mMsgData = (char*)World_Alloc(mWorld, mMsgSize); \
38 msg.getb(mMsgData, mMsgSize); \
41 void PerformCompletionMsg(World
*inWorld
, OSC_Packet
*inPacket
);
43 #define SEND_COMPLETION_MSG \
46 packet.mData = mMsgData; \
47 packet.mSize = mMsgSize; \
48 packet.mReplyAddr = mReplyAddress; \
49 PacketStatus status = PerformCompletionMsg(mWorld, packet); \
50 if (status == PacketScheduled) { \
56 void SndBuf_Init(SndBuf
*buf
);
57 void SndBuf_Init(SndBuf
*buf
)
69 char* allocAndRestrictPath(World
*mWorld
, const char* inPath
, const char* restrictBase
);
70 char* allocAndRestrictPath(World
*mWorld
, const char* inPath
, const char* restrictBase
){
71 char strbuf
[PATH_MAX
];
73 int remain
= PATH_MAX
;
75 // Ensure begins with the base
76 if(strncmp(inPath
, restrictBase
, strlen(restrictBase
)) != 0){
77 strcpy(strbuf
, restrictBase
);
78 offset
= strlen(restrictBase
);
80 if(inPath
[0]!='/' && strbuf
[strlen(strbuf
)-1]!='/'){
87 // Now copy string, but discard any ".." (which could be benign, but easy to abuse)
88 SC_StringParser
sp(inPath
, '/');
91 const char *token
= const_cast<char *>(sp
.NextToken());
92 tokenlen
= strlen(token
);
93 // now add the new token, then a slash, as long as token is neither dodgy nor overflows
94 if(strcmp(token
, "..")!=0 && remain
> tokenlen
){
95 strcpy(strbuf
+offset
, token
);
106 // Now we can make a long-term home for the string and return it
107 char* saferPath
= (char*)World_Alloc(mWorld
, strlen(strbuf
)+1);
108 strcpy(saferPath
, strbuf
);
112 SC_SequencedCommand::SC_SequencedCommand(World
*inWorld
, ReplyAddress
*inReplyAddress
)
113 : mNextStage(1), mWorld(inWorld
),
114 mMsgSize(0), mMsgData(0)
116 if (inReplyAddress
) mReplyAddress
= *inReplyAddress
;
117 else mReplyAddress
.mReplyFunc
= null_reply_func
;
120 SC_SequencedCommand::~SC_SequencedCommand()
122 if (mMsgData
) World_Free(mWorld
, mMsgData
);
125 int SC_SequencedCommand::Init(char* /*inData*/, int /*inSize*/)
130 void SC_SequencedCommand::SendDone(const char *inCommandName
)
132 ::SendDone(&mReplyAddress
, inCommandName
);
135 void SC_SequencedCommand::SendDoneWithIntValue(const char *inCommandName
, int value
)
137 ::SendDoneWithIntValue(&mReplyAddress
, inCommandName
, value
);
140 void SC_SequencedCommand::CallEveryStage()
142 switch (mNextStage
) {
143 case 1 : if (!Stage1()) break; mNextStage
++;
144 case 2 : if (!Stage2()) break; mNextStage
++;
145 case 3 : if (!Stage3()) break; mNextStage
++;
146 case 4 : Stage4(); break;
151 void DoSequencedCommand(FifoMsg
*inMsg
);
152 void DoSequencedCommand(FifoMsg
*inMsg
)
154 SC_SequencedCommand
*cmd
= (SC_SequencedCommand
*)inMsg
->mData
;
155 cmd
->CallNextStage();
158 void FreeSequencedCommand(FifoMsg
*inMsg
);
159 void FreeSequencedCommand(FifoMsg
*inMsg
)
161 SC_SequencedCommand
*cmd
= (SC_SequencedCommand
*)inMsg
->mData
;
166 void SC_SequencedCommand::CallNextStage()
168 bool sendAgain
= false;
171 int isRealTime
= mNextStage
& 1;
172 switch (mNextStage
) {
174 sendAgain
= Stage1(); // RT
177 sendAgain
= Stage2(); // NRT
180 sendAgain
= Stage3(); // RT
187 SC_AudioDriver
*driver
= AudioDriver(mWorld
);
189 msg
.Set(mWorld
, DoSequencedCommand
, 0, (void*)this);
190 // send this to next time.
193 driver
->SendMsgFromEngine(msg
);
196 driver
->SendMsgToEngine(msg
);
202 // can only be freed from RT.
203 msg
.Set(mWorld
, FreeSequencedCommand
, 0, (void*)this);
204 driver
->SendMsgToEngine(msg
);
209 void SC_SequencedCommand::Delete()
212 World_Free(mWorld
, this);
215 bool SC_SequencedCommand::Stage1()
220 bool SC_SequencedCommand::Stage2()
225 bool SC_SequencedCommand::Stage3()
230 void SC_SequencedCommand::Stage4()
235 ///////////////////////////////////////////////////////////////////////////
237 #include "sc_msg_iter.h"
240 SyncCmd::SyncCmd(World
*inWorld
, ReplyAddress
*inReplyAddress
)
241 : SC_SequencedCommand(inWorld
, inReplyAddress
)
245 int SyncCmd::Init(char *inData
, int inSize
)
247 sc_msg_iter
msg(inSize
, inData
);
252 void SyncCmd::CallDestructor()
257 bool SyncCmd::Stage2()
262 bool SyncCmd::Stage3()
267 void SyncCmd::Stage4()
269 small_scpacket packet
;
270 packet
.adds("/synced");
277 SendReply(&mReplyAddress
, packet
.data(), packet
.size());
280 ///////////////////////////////////////////////////////////////////////////
282 BufAllocCmd::BufAllocCmd(World
*inWorld
, ReplyAddress
*inReplyAddress
)
283 : SC_SequencedCommand(inWorld
, inReplyAddress
)
287 int BufAllocCmd::Init(char *inData
, int inSize
)
289 sc_msg_iter
msg(inSize
, inData
);
290 mBufIndex
= msg
.geti();
291 mNumFrames
= msg
.geti();
292 mNumChannels
= msg
.geti(1);
294 GET_COMPLETION_MSG(msg
);
299 void BufAllocCmd::CallDestructor()
301 this->~BufAllocCmd();
304 bool BufAllocCmd::Stage2()
306 SndBuf
*buf
= World_GetNRTBuf(mWorld
, mBufIndex
);
307 mFreeData
= buf
->data
;
308 bufAlloc(buf
, mNumChannels
, mNumFrames
, mWorld
->mFullRate
.mSampleRate
);
313 bool BufAllocCmd::Stage3()
315 SndBuf
* buf
= World_GetBuf(mWorld
, mBufIndex
);
317 mWorld
->mSndBufUpdates
[mBufIndex
].writes
++ ;
322 void BufAllocCmd::Stage4()
325 SendDoneWithIntValue("/b_alloc", mBufIndex
);
328 ///////////////////////////////////////////////////////////////////////////
330 #include "sc_msg_iter.h"
333 BufGenCmd::BufGenCmd(World
*inWorld
, ReplyAddress
*inReplyAddress
)
334 : SC_SequencedCommand(inWorld
, inReplyAddress
),
339 BufGenCmd::~BufGenCmd()
341 World_Free(mWorld
, mData
);
344 int BufGenCmd::Init(char *inData
, int inSize
)
347 mData
= (char*)World_Alloc(mWorld
, mSize
);
348 memcpy(mData
, inData
, mSize
);
350 sc_msg_iter
msg(mSize
, mData
);
351 mBufIndex
= msg
.geti();
353 int32
*genName
= msg
.gets4();
354 if (!genName
) return kSCErr_WrongArgType
;
356 mBufGen
= GetBufGen(genName
);
357 if (!mBufGen
) return kSCErr_BufGenNotFound
;
364 void BufGenCmd::CallDestructor()
369 bool BufGenCmd::Stage2()
371 SndBuf
*buf
= World_GetNRTBuf(mWorld
, mBufIndex
);
373 mFreeData
= buf
->data
;
374 (*mBufGen
->mBufGenFunc
)(mWorld
, buf
, &mMsg
);
375 if (buf
->data
== mFreeData
) mFreeData
= NULL
;
380 bool BufGenCmd::Stage3()
382 SndBuf
* buf
= World_GetBuf(mWorld
, mBufIndex
);
384 mWorld
->mSndBufUpdates
[mBufIndex
].writes
++ ;
388 void BufGenCmd::Stage4()
391 SendDoneWithIntValue("/b_gen", mBufIndex
);
395 ///////////////////////////////////////////////////////////////////////////
398 ///////////////////////////////////////////////////////////////////////////
401 BufFreeCmd::BufFreeCmd(World
*inWorld
, ReplyAddress
*inReplyAddress
)
402 : SC_SequencedCommand(inWorld
, inReplyAddress
)
406 int BufFreeCmd::Init(char *inData
, int inSize
)
408 sc_msg_iter
msg(inSize
, inData
);
409 mBufIndex
= msg
.geti();
411 GET_COMPLETION_MSG(msg
);
416 void BufFreeCmd::CallDestructor()
421 bool BufFreeCmd::Stage2()
423 SndBuf
*buf
= World_GetNRTBuf(mWorld
, mBufIndex
);
424 mFreeData
= buf
->data
;
425 #ifndef NO_LIBSNDFILE
426 if (buf
->sndfile
) sf_close(buf
->sndfile
);
432 bool BufFreeCmd::Stage3()
434 SndBuf
*buf
= World_GetBuf(mWorld
, mBufIndex
);
437 mWorld
->mSndBufUpdates
[mBufIndex
].writes
++ ;
443 void BufFreeCmd::Stage4()
446 SendDoneWithIntValue("/b_free", mBufIndex
);
449 ///////////////////////////////////////////////////////////////////////////
451 BufZeroCmd::BufZeroCmd(World
*inWorld
, ReplyAddress
*inReplyAddress
)
452 : SC_SequencedCommand(inWorld
, inReplyAddress
)
456 int BufZeroCmd::Init(char *inData
, int inSize
)
458 sc_msg_iter
msg(inSize
, inData
);
459 mBufIndex
= msg
.geti();
461 GET_COMPLETION_MSG(msg
);
466 void BufZeroCmd::CallDestructor()
471 bool BufZeroCmd::Stage2()
473 SndBuf
*buf
= World_GetNRTBuf(mWorld
, mBufIndex
);
474 memset(buf
->data
, 0, buf
->samples
* sizeof(float));
478 bool BufZeroCmd::Stage3()
480 mWorld
->mSndBufUpdates
[mBufIndex
].writes
++ ;
485 void BufZeroCmd::Stage4()
487 SendDoneWithIntValue("/b_zero", mBufIndex
);
490 ///////////////////////////////////////////////////////////////////////////
492 BufAllocReadCmd::BufAllocReadCmd(World
*inWorld
, ReplyAddress
*inReplyAddress
)
493 : SC_SequencedCommand(inWorld
, inReplyAddress
), mFreeData(0), mFilename(0)
497 int BufAllocReadCmd::Init(char *inData
, int inSize
)
499 sc_msg_iter
msg(inSize
, inData
);
500 mBufIndex
= msg
.geti();
502 const char *filename
= msg
.gets();
503 if (!filename
) return kSCErr_WrongArgType
;
505 if(mWorld
->mRestrictedPath
){
506 mFilename
= allocAndRestrictPath(mWorld
, filename
, mWorld
->mRestrictedPath
);
508 mFilename
= (char*)World_Alloc(mWorld
, strlen(filename
)+1);
509 strcpy(mFilename
, filename
);
512 mFileOffset
= msg
.geti();
513 mNumFrames
= msg
.geti();
515 GET_COMPLETION_MSG(msg
);
520 BufAllocReadCmd::~BufAllocReadCmd()
522 World_Free(mWorld
, mFilename
);
525 void BufAllocReadCmd::CallDestructor()
527 this->~BufAllocReadCmd();
530 bool BufAllocReadCmd::Stage2()
533 SendFailure(&mReplyAddress
, "/b_allocRead", "scsynth compiled without libsndfile\n");
534 scprintf("scsynth compiled without libsndfile\n");
537 SndBuf
*buf
= World_GetNRTBuf(mWorld
, mBufIndex
);
539 memset(&fileinfo
, 0, sizeof(fileinfo
));
540 SNDFILE
* sf
= sf_open(mFilename
, SFM_READ
, &fileinfo
);
543 sprintf(str
, "File '%s' could not be opened: %s\n", mFilename
, sf_strerror(NULL
));
544 SendFailureWithBufnum(&mReplyAddress
, "/b_allocRead", str
, mBufIndex
); //SendFailure(&mReplyAddress, "/b_allocRead", str);
548 if (mFileOffset
< 0) mFileOffset
= 0;
549 else if (mFileOffset
> fileinfo
.frames
) mFileOffset
= fileinfo
.frames
;
550 if (mNumFrames
<= 0 || mNumFrames
+ mFileOffset
> fileinfo
.frames
) mNumFrames
= fileinfo
.frames
- mFileOffset
;
553 mFreeData
= buf
->data
;
554 SCErr err
= bufAlloc(buf
, fileinfo
.channels
, mNumFrames
, fileinfo
.samplerate
);
557 sf_seek(sf
, mFileOffset
, SEEK_SET
);
558 sf_readf_float(sf
, buf
->data
, mNumFrames
);
568 bool BufAllocReadCmd::Stage3()
570 SndBuf
* buf
= World_GetBuf(mWorld
, mBufIndex
);
572 mWorld
->mSndBufUpdates
[mBufIndex
].writes
++ ;
578 void BufAllocReadCmd::Stage4()
581 SendDoneWithIntValue("/b_allocRead", mBufIndex
);
584 ///////////////////////////////////////////////////////////////////////////
586 BufReadCmd::BufReadCmd(World
*inWorld
, ReplyAddress
*inReplyAddress
)
587 : SC_SequencedCommand(inWorld
, inReplyAddress
),
592 int BufReadCmd::Init(char *inData
, int inSize
)
594 sc_msg_iter
msg(inSize
, inData
);
595 mBufIndex
= msg
.geti();
597 const char *filename
= msg
.gets();
598 if (!filename
) return kSCErr_WrongArgType
;
600 if(mWorld
->mRestrictedPath
){
601 mFilename
= allocAndRestrictPath(mWorld
, filename
, mWorld
->mRestrictedPath
);
603 mFilename
= (char*)World_Alloc(mWorld
, strlen(filename
)+1);
604 strcpy(mFilename
, filename
);
607 mFileOffset
= msg
.geti();
608 mNumFrames
= msg
.geti(-1);
609 mBufOffset
= msg
.geti();
610 mLeaveFileOpen
= msg
.geti();
612 GET_COMPLETION_MSG(msg
);
617 BufReadCmd::~BufReadCmd()
619 World_Free(mWorld
, mFilename
);
622 void BufReadCmd::CallDestructor()
627 bool BufReadCmd::Stage2()
630 SendFailure(&mReplyAddress
, "/b_read", "scsynth compiled without libsndfile\n");
631 scprintf("scsynth compiled without libsndfile\n");
636 SndBuf
*buf
= World_GetNRTBuf(mWorld
, mBufIndex
);
637 int framesToEnd
= buf
->frames
- mBufOffset
;
638 if (framesToEnd
<= 0) return true;
640 SNDFILE
* sf
= sf_open(mFilename
, SFM_READ
, &fileinfo
);
643 sprintf(str
, "File '%s' could not be opened: %s\n", mFilename
, sf_strerror(NULL
));
644 SendFailureWithBufnum(&mReplyAddress
, "/b_read", str
, mBufIndex
); //SendFailure(&mReplyAddress, "/b_read", str);
648 if (fileinfo
.channels
!= buf
->channels
) {
651 sprintf(str
, "Channel mismatch. File '%s' has %d channels. Buffer has %d channels.\n", mFilename
, fileinfo
.channels
, buf
->channels
);
652 SendFailureWithBufnum(&mReplyAddress
, "/b_read", str
, mBufIndex
); //SendFailure(&mReplyAddress, "/b_read", str);
657 if (mFileOffset
< 0) mFileOffset
= 0;
658 else if (mFileOffset
> fileinfo
.frames
) mFileOffset
= fileinfo
.frames
;
659 if (mNumFrames
< 0 || mNumFrames
+ mFileOffset
> fileinfo
.frames
) mNumFrames
= fileinfo
.frames
- mFileOffset
;
661 if (mNumFrames
> framesToEnd
) mNumFrames
= framesToEnd
;
663 sf_seek(sf
, mFileOffset
, SEEK_SET
);
664 if (mNumFrames
> 0) {
665 sf_readf_float(sf
, buf
->data
+ (mBufOffset
* buf
->channels
), mNumFrames
);
669 sf_close(buf
->sndfile
);
671 if (mLeaveFileOpen
) {
678 mSampleRate
= (double)fileinfo
.samplerate
;
684 bool BufReadCmd::Stage3()
686 SndBuf
* buf
= World_GetBuf(mWorld
, mBufIndex
);
687 buf
->samplerate
= mSampleRate
;
688 if (mLeaveFileOpen
) buf
->mask
= buf
->mask1
= -1;
690 mWorld
->mSndBufUpdates
[mBufIndex
].writes
++ ;
695 void BufReadCmd::Stage4()
697 SendDoneWithIntValue("/b_read", mBufIndex
);
700 ///////////////////////////////////////////////////////////////////////////
702 SC_BufReadCommand::SC_BufReadCommand(World
* inWorld
, ReplyAddress
* inReplyAddress
)
703 : SC_SequencedCommand(inWorld
, inReplyAddress
),
708 SC_BufReadCommand::~SC_BufReadCommand()
712 void SC_BufReadCommand::InitChannels(sc_msg_iter
& msg
)
715 while (msg
.nextTag(0) == 'i') {
717 if (mNumChannels
<= kMaxNumChannels
) {
718 mChannels
[mNumChannels
++] = c
;
723 bool SC_BufReadCommand::CheckChannels(int inNumChannels
)
725 for (int i
=0; i
< mNumChannels
; ++i
) {
726 if (mChannels
[i
] >= inNumChannels
) {
733 void SC_BufReadCommand::CopyChannels(float* dst
, float* src
, size_t srcChannels
, size_t numFrames
)
735 for (int ci
=0; ci
< mNumChannels
; ++ci
) {
736 int c
= mChannels
[ci
];
737 if (c
>= 0 && c
< srcChannels
) {
738 for (size_t fi
=0; fi
< numFrames
; ++fi
) {
739 dst
[fi
*mNumChannels
+ci
] = src
[fi
*srcChannels
+c
];
742 for (size_t fi
=0; fi
< numFrames
; ++fi
) {
743 dst
[fi
*mNumChannels
+ci
] = 0.f
;
749 ///////////////////////////////////////////////////////////////////////////
751 BufAllocReadChannelCmd::BufAllocReadChannelCmd(World
*inWorld
, ReplyAddress
*inReplyAddress
)
752 : SC_BufReadCommand(inWorld
, inReplyAddress
),
753 mFreeData(0), mFilename(0)
757 int BufAllocReadChannelCmd::Init(char *inData
, int inSize
)
759 sc_msg_iter
msg(inSize
, inData
);
760 mBufIndex
= msg
.geti();
762 const char *filename
= msg
.gets();
763 if (!filename
) return kSCErr_WrongArgType
;
765 if(mWorld
->mRestrictedPath
){
766 mFilename
= allocAndRestrictPath(mWorld
, filename
, mWorld
->mRestrictedPath
);
768 mFilename
= (char*)World_Alloc(mWorld
, strlen(filename
)+1);
769 strcpy(mFilename
, filename
);
772 mFileOffset
= msg
.geti();
773 mNumFrames
= msg
.geti();
777 GET_COMPLETION_MSG(msg
);
782 BufAllocReadChannelCmd::~BufAllocReadChannelCmd()
784 World_Free(mWorld
, mFilename
);
787 void BufAllocReadChannelCmd::CallDestructor()
789 this->~BufAllocReadChannelCmd();
792 bool BufAllocReadChannelCmd::Stage2()
795 SendFailure(&mReplyAddress
, "/b_allocReadChannel", "scsynth compiled without libsndfile\n");
796 scprintf("scsynth compiled without libsndfile\n");
799 SndBuf
*buf
= World_GetNRTBuf(mWorld
, mBufIndex
);
802 memset(&fileinfo
, 0, sizeof(fileinfo
));
803 SNDFILE
* sf
= sf_open(mFilename
, SFM_READ
, &fileinfo
);
806 sprintf(str
, "File '%s' could not be opened: %s\n", mFilename
, sf_strerror(NULL
));
807 SendFailureWithBufnum(&mReplyAddress
, "/b_allocReadChannel", str
, mBufIndex
); //SendFailure(&mReplyAddress, "/b_allocRead", str);
811 if (mFileOffset
< 0) mFileOffset
= 0;
812 else if (mFileOffset
> fileinfo
.frames
) mFileOffset
= fileinfo
.frames
;
813 if (mNumFrames
<= 0 || mNumFrames
+ mFileOffset
> fileinfo
.frames
) mNumFrames
= fileinfo
.frames
- mFileOffset
;
815 if (mNumChannels
== 0) {
817 mFreeData
= buf
->data
;
818 SCErr err
= bufAlloc(buf
, fileinfo
.channels
, mNumFrames
, fileinfo
.samplerate
);
821 sf_seek(sf
, mFileOffset
, SEEK_SET
);
822 sf_readf_float(sf
, buf
->data
, mNumFrames
);
824 // verify channel indexes
825 if (!CheckChannels(fileinfo
.channels
)) {
826 const char* str
= "Channel index out of range.\n";
827 SendFailureWithBufnum(&mReplyAddress
, "/b_allocReadChannel", str
, mBufIndex
); //SendFailure(&mReplyAddress, "/b_allocRead", str);
833 mFreeData
= buf
->data
;
834 SCErr err
= bufAlloc(buf
, mNumChannels
, mNumFrames
, fileinfo
.samplerate
);
837 float* data
= (float*)malloc(mNumFrames
*fileinfo
.channels
*sizeof(float));
838 if (data
== 0) goto leave
;
839 // read some channels
840 sf_seek(sf
, mFileOffset
, SEEK_SET
);
841 sf_readf_float(sf
, data
, mNumFrames
);
842 CopyChannels(buf
->data
, data
, fileinfo
.channels
, mNumFrames
);
855 bool BufAllocReadChannelCmd::Stage3()
857 SndBuf
* buf
= World_GetBuf(mWorld
, mBufIndex
);
859 mWorld
->mSndBufUpdates
[mBufIndex
].writes
++ ;
865 void BufAllocReadChannelCmd::Stage4()
868 SendDoneWithIntValue("/b_allocReadChannel", mBufIndex
);
871 ///////////////////////////////////////////////////////////////////////////
873 BufReadChannelCmd::BufReadChannelCmd(World
*inWorld
, ReplyAddress
*inReplyAddress
)
874 : SC_BufReadCommand(inWorld
, inReplyAddress
),
879 int BufReadChannelCmd::Init(char *inData
, int inSize
)
881 sc_msg_iter
msg(inSize
, inData
);
882 mBufIndex
= msg
.geti();
884 const char *filename
= msg
.gets();
885 if (!filename
) return kSCErr_WrongArgType
;
887 if(mWorld
->mRestrictedPath
){
888 mFilename
= allocAndRestrictPath(mWorld
, filename
, mWorld
->mRestrictedPath
);
890 mFilename
= (char*)World_Alloc(mWorld
, strlen(filename
)+1);
891 strcpy(mFilename
, filename
);
894 mFileOffset
= msg
.geti();
895 mNumFrames
= msg
.geti(-1);
896 mBufOffset
= msg
.geti();
897 mLeaveFileOpen
= msg
.geti();
901 GET_COMPLETION_MSG(msg
);
906 BufReadChannelCmd::~BufReadChannelCmd()
908 World_Free(mWorld
, mFilename
);
911 void BufReadChannelCmd::CallDestructor()
913 this->~BufReadChannelCmd();
916 bool BufReadChannelCmd::Stage2()
919 SendFailure(&mReplyAddress
, "/b_readChannel", "scsynth compiled without libsndfile\n");
920 scprintf("scsynth compiled without libsndfile\n");
925 SndBuf
*buf
= World_GetNRTBuf(mWorld
, mBufIndex
);
926 int framesToEnd
= buf
->frames
- mBufOffset
;
927 if (framesToEnd
<= 0) return true;
929 SNDFILE
* sf
= sf_open(mFilename
, SFM_READ
, &fileinfo
);
932 sprintf(str
, "File '%s' could not be opened: %s\n", mFilename
, sf_strerror(NULL
));
933 SendFailureWithBufnum(&mReplyAddress
, "/b_readChannel", str
, mBufIndex
); //SendFailure(&mReplyAddress, "/b_read", str);
938 if (mNumChannels
> 0) {
939 // verify channel indexes
940 if (!( CheckChannels(fileinfo
.channels
)) ) { // nescivi: && CheckChannels(buf->channels) (should not check here for buf->channels)
941 const char* str
= "Channel index out of range.\n";
942 SendFailureWithBufnum(&mReplyAddress
, "/b_readChannel", str
, mBufIndex
); //SendFailure(&mReplyAddress, "/b_allocRead", str);
947 // nescivi: this also seems out of place: we want to read from a file with more channels than are in the buffer
948 // } else if (fileinfo.channels != buf->channels) {
951 // sprintf(str, "Channel mismatch. File '%s' has %d channels. Buffer has %d channels.\n",
952 // mFilename, fileinfo.channels, buf->channels);
953 // SendFailure(&mReplyAddress, "/b_read", str);
958 if (mFileOffset
< 0) mFileOffset
= 0;
959 else if (mFileOffset
> fileinfo
.frames
) mFileOffset
= fileinfo
.frames
;
960 if (mNumFrames
< 0 || mNumFrames
+ mFileOffset
> fileinfo
.frames
) mNumFrames
= fileinfo
.frames
- mFileOffset
;
961 if (mNumFrames
> framesToEnd
) mNumFrames
= framesToEnd
;
963 sf_seek(sf
, mFileOffset
, SEEK_SET
);
964 if (mNumFrames
> 0) {
965 if (mNumChannels
== 0) {
967 sf_readf_float(sf
, buf
->data
+ (mBufOffset
* buf
->channels
), mNumFrames
);
970 float* data
= (float*)malloc(mNumFrames
*fileinfo
.channels
*sizeof(float));
971 if (data
== 0) goto leave
;
972 // read some channels
973 sf_seek(sf
, mFileOffset
, SEEK_SET
);
974 sf_readf_float(sf
, data
, mNumFrames
);
975 CopyChannels(buf
->data
+ (mBufOffset
* mNumChannels
), data
, fileinfo
.channels
, mNumFrames
);
983 sf_close(buf
->sndfile
);
985 if (mLeaveFileOpen
) {
992 mSampleRate
= (double)fileinfo
.samplerate
;
998 bool BufReadChannelCmd::Stage3()
1000 SndBuf
* buf
= World_GetBuf(mWorld
, mBufIndex
);
1001 buf
->samplerate
= mSampleRate
;
1002 if (mLeaveFileOpen
) buf
->mask
= buf
->mask1
= -1;
1004 mWorld
->mSndBufUpdates
[mBufIndex
].writes
++ ;
1005 SEND_COMPLETION_MSG
;
1009 void BufReadChannelCmd::Stage4()
1011 SendDoneWithIntValue("/b_readChannel", mBufIndex
);
1014 ///////////////////////////////////////////////////////////////////////////
1016 BufWriteCmd::BufWriteCmd(World
*inWorld
, ReplyAddress
*inReplyAddress
)
1017 : SC_SequencedCommand(inWorld
, inReplyAddress
), mFilename(0)
1021 #ifdef NO_LIBSNDFILE
1025 int BufWriteCmd::Init(char *inData
, int inSize
)
1027 #ifdef NO_LIBSNDFILE
1028 SendFailure(&mReplyAddress
, "/b_write", "scsynth compiled without libsndfile\n");
1029 scprintf("scsynth compiled without libsndfile\n");
1032 sc_msg_iter
msg(inSize
, inData
);
1033 mBufIndex
= msg
.geti();
1035 const char *filename
= msg
.gets();
1036 if (!filename
) return kSCErr_WrongArgType
;
1038 if(mWorld
->mRestrictedPath
){
1039 mFilename
= allocAndRestrictPath(mWorld
, filename
, mWorld
->mRestrictedPath
);
1041 mFilename
= (char*)World_Alloc(mWorld
, strlen(filename
)+1);
1042 strcpy(mFilename
, filename
);
1045 const char *headerFormatString
= msg
.gets("aiff");
1046 const char *sampleFormatString
= msg
.gets("int16");
1048 mNumFrames
= msg
.geti(-1);
1049 mBufOffset
= msg
.geti();
1050 mLeaveFileOpen
= msg
.geti();
1052 GET_COMPLETION_MSG(msg
);
1054 memset(&mFileInfo
, 0, sizeof(mFileInfo
));
1055 return sndfileFormatInfoFromStrings(&mFileInfo
, headerFormatString
, sampleFormatString
);
1059 BufWriteCmd::~BufWriteCmd()
1061 World_Free(mWorld
, mFilename
);
1064 void BufWriteCmd::CallDestructor()
1066 this->~BufWriteCmd();
1069 bool BufWriteCmd::Stage2()
1071 #ifdef NO_LIBSNDFILE
1074 SndBuf
*buf
= World_GetNRTBuf(mWorld
, mBufIndex
);
1075 int framesToEnd
= buf
->frames
- mBufOffset
;
1076 if (framesToEnd
< 0) framesToEnd
= 0;
1077 mFileInfo
.samplerate
= (int)buf
->samplerate
;
1078 mFileInfo
.channels
= buf
->channels
;
1080 SNDFILE
* sf
= sf_open(mFilename
, SFM_WRITE
, &mFileInfo
);
1083 sprintf(str
, "File '%s' could not be opened: %s\n", mFilename
, sf_strerror(NULL
));
1084 SendFailureWithBufnum(&mReplyAddress
, "/b_write", str
, mBufIndex
); //SendFailure(&mReplyAddress, "/b_write", str);
1089 if (mNumFrames
< 0 || mNumFrames
> buf
->frames
) mNumFrames
= buf
->frames
;
1091 if (mNumFrames
> framesToEnd
) mNumFrames
= framesToEnd
;
1093 sf_command(sf
, SFC_SET_CLIPPING
, NULL
, SF_TRUE
); // choose clipping rather than wraparound for integer-format files
1095 if (mNumFrames
> 0) {
1096 sf_writef_float(sf
, buf
->data
+ (mBufOffset
* buf
->channels
), mNumFrames
);
1100 sf_close(buf
->sndfile
);
1102 if (mLeaveFileOpen
) {
1113 bool BufWriteCmd::Stage3()
1115 SEND_COMPLETION_MSG
;
1119 void BufWriteCmd::Stage4()
1121 SendDoneWithIntValue("/b_write", mBufIndex
);
1124 ///////////////////////////////////////////////////////////////////////////
1126 BufCloseCmd::BufCloseCmd(World
*inWorld
, ReplyAddress
*inReplyAddress
)
1127 : SC_SequencedCommand(inWorld
, inReplyAddress
)
1131 int BufCloseCmd::Init(char *inData
, int inSize
)
1133 sc_msg_iter
msg(inSize
, inData
);
1134 mBufIndex
= msg
.geti();
1136 GET_COMPLETION_MSG(msg
);
1141 void BufCloseCmd::CallDestructor()
1143 this->~BufCloseCmd();
1146 bool BufCloseCmd::Stage2()
1148 #ifdef NO_LIBSNDFILE
1149 SendFailure(&mReplyAddress
, "/b_close", "scsynth compiled without libsndfile\n");
1150 scprintf("scsynth compiled without libsndfile\n");
1153 SndBuf
*buf
= World_GetNRTBuf(mWorld
, mBufIndex
);
1155 sf_close(buf
->sndfile
);
1162 bool BufCloseCmd::Stage3()
1164 SEND_COMPLETION_MSG
;
1168 void BufCloseCmd::Stage4()
1170 SendDoneWithIntValue("/b_close", mBufIndex
);
1173 ///////////////////////////////////////////////////////////////////////////
1175 AudioQuitCmd::AudioQuitCmd(World
*inWorld
, ReplyAddress
*inReplyAddress
)
1176 : SC_SequencedCommand(inWorld
, inReplyAddress
)
1180 void AudioQuitCmd::CallDestructor()
1182 this->~AudioQuitCmd();
1185 bool AudioQuitCmd::Stage2()
1187 mWorld
->hw
->mTerminating
= true;
1191 bool AudioQuitCmd::Stage3()
1193 #if SC_AUDIO_API == SC_AUDIO_API_AUDIOUNITS
1194 SendFailure(&mReplyAddress
, "/quit", "not allowed in AU host\n");
1195 scprintf("/quit : quit not allowed in AU host\n");
1198 if (mWorld
->hw
->mShmem
)
1199 mWorld
->hw
->mShmem
->disconnect();
1204 void AudioQuitCmd::Stage4()
1207 mWorld
->hw
->mQuitProgram
->Release();
1210 ///////////////////////////////////////////////////////////////////////////
1212 AudioStatusCmd::AudioStatusCmd(World
*inWorld
, ReplyAddress
*inReplyAddress
)
1213 : SC_SequencedCommand(inWorld
, inReplyAddress
)
1217 void AudioStatusCmd::CallDestructor()
1219 this->~AudioStatusCmd();
1222 bool AudioStatusCmd::Stage2()
1224 // we stop replying to status requests after receiving /quit
1225 if (mWorld
->hw
->mTerminating
== true)
1228 small_scpacket packet
;
1229 packet
.adds("/status.reply");
1230 packet
.maketags(10);
1242 packet
.addi(1); // audio is always active now.
1243 packet
.addi(mWorld
->mNumUnits
);
1244 packet
.addi(mWorld
->mNumGraphs
);
1245 packet
.addi(mWorld
->mNumGroups
);
1246 packet
.addi(mWorld
->hw
->mGraphDefLib
->NumItems());
1248 SC_AudioDriver
*driver
= mWorld
->hw
->mAudioDriver
;
1249 packet
.addf(driver
->GetAvgCPU());
1250 packet
.addf(driver
->GetPeakCPU());
1251 packet
.addd(driver
->GetSampleRate());
1252 packet
.addd(driver
->GetActualSampleRate());
1254 SendReply(&mReplyAddress
, packet
.data(), packet
.size());
1259 ///////////////////////////////////////////////////////////////////////////
1261 NotifyCmd::NotifyCmd(World
*inWorld
, ReplyAddress
*inReplyAddress
)
1262 : SC_SequencedCommand(inWorld
, inReplyAddress
)
1266 int NotifyCmd::Init(char *inData
, int inSize
)
1268 sc_msg_iter
msg(inSize
, inData
);
1269 mOnOff
= msg
.geti();
1274 void NotifyCmd::CallDestructor()
1279 bool NotifyCmd::Stage2()
1281 HiddenWorld
*hw
= mWorld
->hw
;
1284 for (uint32 i
=0; i
<hw
->mNumUsers
; ++i
) {
1285 if (mReplyAddress
== hw
->mUsers
[i
]) {
1286 // already in table - don't fail though..
1287 SendFailure(&mReplyAddress
, "/notify", "notify: already registered\n");
1288 scprintf("/notify : already registered\n");
1293 // add reply address to user table
1294 if (hw
->mNumUsers
>= hw
->mMaxUsers
) {
1295 SendFailure(&mReplyAddress
, "/notify", "too many users\n");
1296 scprintf("too many users\n");
1300 hw
->mUsers
[hw
->mNumUsers
++] = mReplyAddress
;
1302 SendDone("/notify");
1304 for (uint32 i
=0; i
<hw
->mNumUsers
; ++i
) {
1305 if (mReplyAddress
== hw
->mUsers
[i
]) {
1307 hw
->mUsers
[i
] = hw
->mUsers
[--hw
->mNumUsers
];
1308 SendDone("/notify");
1313 SendFailure(&mReplyAddress
, "/notify", "not registered\n");
1314 scprintf("not registered\n");
1319 ///////////////////////////////////////////////////////////////////////////
1321 SendFailureCmd::SendFailureCmd(World
*inWorld
, ReplyAddress
*inReplyAddress
)
1322 : SC_SequencedCommand(inWorld
, inReplyAddress
), mCmdName(0), mErrString(0)
1326 SendFailureCmd::~SendFailureCmd()
1328 World_Free(mWorld
, mCmdName
);
1329 World_Free(mWorld
, mErrString
);
1333 void SendFailureCmd::InitSendFailureCmd(const char *inCmdName
, const char* inErrString
)
1335 mCmdName
= (char*)World_Alloc(mWorld
, strlen(inCmdName
)+1);
1336 strcpy(mCmdName
, inCmdName
);
1338 mErrString
= (char*)World_Alloc(mWorld
, strlen(inErrString
)+1);
1339 strcpy(mErrString
, inErrString
);
1342 void SendFailureCmd::CallDestructor()
1344 this->~SendFailureCmd();
1347 bool SendFailureCmd::Stage2()
1349 SendFailure(&mReplyAddress
, mCmdName
, mErrString
);
1353 ///////////////////////////////////////////////////////////////////////////
1355 RecvSynthDefCmd::RecvSynthDefCmd(World
*inWorld
, ReplyAddress
*inReplyAddress
)
1356 : SC_SequencedCommand(inWorld
, inReplyAddress
), mBuffer(0)
1360 int RecvSynthDefCmd::Init(char *inData
, int inSize
)
1362 sc_msg_iter
msg(inSize
, inData
);
1364 int size
= msg
.getbsize();
1365 if (!size
) throw kSCErr_WrongArgType
;
1367 mBuffer
= (char*)World_Alloc(mWorld
, size
);
1368 msg
.getb(mBuffer
, size
);
1370 GET_COMPLETION_MSG(msg
);
1376 RecvSynthDefCmd::~RecvSynthDefCmd()
1378 World_Free(mWorld
, mBuffer
);
1381 void RecvSynthDefCmd::CallDestructor()
1383 this->~RecvSynthDefCmd();
1386 bool RecvSynthDefCmd::Stage2()
1388 mDefs
= GraphDef_Recv(mWorld
, mBuffer
, mDefs
);
1393 bool RecvSynthDefCmd::Stage3()
1395 GraphDef_Define(mWorld
, mDefs
);
1396 SEND_COMPLETION_MSG
;
1400 void RecvSynthDefCmd::Stage4()
1402 SendDone("/d_recv");
1405 ///////////////////////////////////////////////////////////////////////////
1407 LoadSynthDefCmd::LoadSynthDefCmd(World
*inWorld
, ReplyAddress
*inReplyAddress
)
1408 : SC_SequencedCommand(inWorld
, inReplyAddress
), mFilename(0)
1412 int LoadSynthDefCmd::Init(char *inData
, int inSize
)
1414 sc_msg_iter
msg(inSize
, inData
);
1416 const char *filename
= msg
.gets();
1417 if (!filename
) return kSCErr_WrongArgType
;
1419 if(mWorld
->mRestrictedPath
){
1420 mFilename
= allocAndRestrictPath(mWorld
, filename
, mWorld
->mRestrictedPath
);
1422 mFilename
= (char*)World_Alloc(mWorld
, strlen(filename
)+1);
1423 strcpy(mFilename
, filename
);
1426 GET_COMPLETION_MSG(msg
);
1432 LoadSynthDefCmd::~LoadSynthDefCmd()
1434 World_Free(mWorld
, mFilename
);
1437 void LoadSynthDefCmd::CallDestructor()
1439 this->~LoadSynthDefCmd();
1442 bool LoadSynthDefCmd::Stage2()
1444 char* fname
= mFilename
;
1445 mDefs
= GraphDef_LoadGlob(mWorld
, fname
, mDefs
);
1449 bool LoadSynthDefCmd::Stage3()
1451 GraphDef_Define(mWorld
, mDefs
);
1452 SEND_COMPLETION_MSG
;
1456 void LoadSynthDefCmd::Stage4()
1458 SendDone("/d_load");
1461 ///////////////////////////////////////////////////////////////////////////
1463 LoadSynthDefDirCmd::LoadSynthDefDirCmd(World
*inWorld
, ReplyAddress
*inReplyAddress
)
1464 : SC_SequencedCommand(inWorld
, inReplyAddress
), mFilename(0)
1468 int LoadSynthDefDirCmd::Init(char *inData
, int inSize
)
1470 sc_msg_iter
msg(inSize
, inData
);
1472 const char *filename
= msg
.gets();
1473 if (!filename
) return kSCErr_WrongArgType
;
1475 if(mWorld
->mRestrictedPath
){
1476 mFilename
= allocAndRestrictPath(mWorld
, filename
, mWorld
->mRestrictedPath
);
1478 mFilename
= (char*)World_Alloc(mWorld
, strlen(filename
)+1);
1479 strcpy(mFilename
, filename
);
1482 GET_COMPLETION_MSG(msg
);
1488 LoadSynthDefDirCmd::~LoadSynthDefDirCmd()
1490 World_Free(mWorld
, mFilename
);
1493 void LoadSynthDefDirCmd::CallDestructor()
1495 this->~LoadSynthDefDirCmd();
1498 bool LoadSynthDefDirCmd::Stage2()
1500 mDefs
= GraphDef_LoadDir(mWorld
, mFilename
, mDefs
);
1505 bool LoadSynthDefDirCmd::Stage3()
1507 GraphDef_Define(mWorld
, mDefs
);
1508 SEND_COMPLETION_MSG
;
1512 void LoadSynthDefDirCmd::Stage4()
1514 SendDone("/d_loadDir");
1517 ///////////////////////////////////////////////////////////////////////////
1519 SendReplyCmd::SendReplyCmd(World
*inWorld
, ReplyAddress
*inReplyAddress
)
1520 : SC_SequencedCommand(inWorld
, inReplyAddress
)
1524 int SendReplyCmd::Init(char *inData
, int inSize
)
1527 mMsgData
= (char*)World_Alloc(mWorld
, mMsgSize
);
1528 memcpy(mMsgData
, inData
, inSize
);
1532 void SendReplyCmd::CallDestructor()
1534 this->~SendReplyCmd();
1537 bool SendReplyCmd::Stage2()
1539 SendReply(&mReplyAddress
, mMsgData
, mMsgSize
);
1543 ///////////////////////////////////////////////////////////////////////////
1545 int PerformAsynchronousCommand(
1548 const char* cmdName
,
1550 AsyncStageFn stage2
, // stage2 is non real time
1551 AsyncStageFn stage3
, // stage3 is real time - completion msg performed if stage3 returns true
1552 AsyncStageFn stage4
, // stage4 is non real time - sends done if stage4 returns true
1553 AsyncFreeFn cleanup
,
1554 int completionMsgSize
,
1555 void* completionMsgData
1558 void* space
= World_Alloc(inWorld
, sizeof(AsyncPlugInCmd
));
1559 AsyncPlugInCmd
*cmd
= new (space
) AsyncPlugInCmd(inWorld
, (ReplyAddress
*)replyAddr
,
1560 cmdName
, cmdData
, stage2
, stage3
, stage4
, cleanup
,
1561 completionMsgSize
, completionMsgData
);
1562 if (!cmd
) return kSCErr_Failed
;
1563 if (inWorld
->mRealTime
) cmd
->CallNextStage();
1564 else cmd
->CallEveryStage();
1568 ///////////////////////////////////////////////////////////////////////////
1570 AsyncPlugInCmd::AsyncPlugInCmd(World
*inWorld
, ReplyAddress
*inReplyAddress
,
1571 const char* cmdName
,
1573 AsyncStageFn stage2
, // stage2 is non real time
1574 AsyncStageFn stage3
, // stage3 is real time - completion msg performed if stage3 returns true
1575 AsyncStageFn stage4
, // stage4 is non real time - sends done if stage4 returns true
1576 AsyncFreeFn cleanup
, // cleanup is called in real time
1577 int completionMsgSize
,
1578 void* completionMsgData
)
1579 : SC_SequencedCommand(inWorld
, inReplyAddress
),
1580 mCmdName(cmdName
), mCmdData(cmdData
),
1581 mStage2(stage2
), mStage3(stage3
), mStage4(stage4
),
1584 if (completionMsgSize
&& completionMsgData
) {
1585 mMsgSize
= completionMsgSize
;
1586 mMsgData
= (char*)World_Alloc(mWorld
, mMsgSize
);
1587 memcpy(mMsgData
, completionMsgData
, mMsgSize
);
1591 AsyncPlugInCmd::~AsyncPlugInCmd()
1593 (mCleanup
)(mWorld
, mCmdData
);
1594 if (mMsgData
) World_Free(mWorld
, mMsgData
);
1597 void AsyncPlugInCmd::CallDestructor()
1599 this->~AsyncPlugInCmd();
1602 bool AsyncPlugInCmd::Stage2()
1604 bool result
= !mStage2
|| (mStage2
)(mWorld
, mCmdData
);
1608 bool AsyncPlugInCmd::Stage3()
1610 bool result
= !mStage3
|| (mStage3
)(mWorld
, mCmdData
);
1611 if (result
) SEND_COMPLETION_MSG
;
1615 void AsyncPlugInCmd::Stage4()
1617 bool result
= !mStage4
|| (mStage4
)(mWorld
, mCmdData
);
1618 if (result
&& mCmdName
&& mReplyAddress
.mReplyFunc
!= null_reply_func
) SendDone((char*)mCmdName
);