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"
32 #define GET_COMPLETION_MSG(msg) \
33 mMsgSize = msg.getbsize(); \
35 mMsgData = (char*)World_Alloc(mWorld, mMsgSize); \
36 msg.getb(mMsgData, mMsgSize); \
39 void PerformCompletionMsg(World
*inWorld
, OSC_Packet
*inPacket
);
41 #define SEND_COMPLETION_MSG \
44 packet.mData = mMsgData; \
45 packet.mSize = mMsgSize; \
46 packet.mReplyAddr = mReplyAddress; \
47 PacketStatus status = PerformCompletionMsg(mWorld, packet); \
48 if (status == PacketScheduled) { \
54 void SndBuf_Init(SndBuf
*buf
);
55 void SndBuf_Init(SndBuf
*buf
)
67 char* allocAndRestrictPath(World
*mWorld
, const char* inPath
, const char* restrictBase
);
68 char* allocAndRestrictPath(World
*mWorld
, const char* inPath
, const char* restrictBase
){
69 char strbuf
[PATH_MAX
];
71 int remain
= PATH_MAX
;
73 // Ensure begins with the base
74 if(strncmp(inPath
, restrictBase
, strlen(restrictBase
)) != 0){
75 strcpy(strbuf
, restrictBase
);
76 offset
= strlen(restrictBase
);
78 if(inPath
[0]!='/' && strbuf
[strlen(strbuf
)-1]!='/'){
85 // Now copy string, but discard any ".." (which could be benign, but easy to abuse)
86 SC_StringParser
sp(inPath
, '/');
89 const char *token
= const_cast<char *>(sp
.NextToken());
90 tokenlen
= strlen(token
);
91 // now add the new token, then a slash, as long as token is neither dodgy nor overflows
92 if(strcmp(token
, "..")!=0 && remain
> tokenlen
){
93 strcpy(strbuf
+offset
, token
);
104 // Now we can make a long-term home for the string and return it
105 char* saferPath
= (char*)World_Alloc(mWorld
, strlen(strbuf
)+1);
106 strcpy(saferPath
, strbuf
);
110 SC_SequencedCommand::SC_SequencedCommand(World
*inWorld
, ReplyAddress
*inReplyAddress
)
111 : mNextStage(1), mWorld(inWorld
),
112 mMsgSize(0), mMsgData(0)
114 if (inReplyAddress
) mReplyAddress
= *inReplyAddress
;
115 else mReplyAddress
.mReplyFunc
= null_reply_func
;
118 SC_SequencedCommand::~SC_SequencedCommand()
120 if (mMsgData
) World_Free(mWorld
, mMsgData
);
123 int SC_SequencedCommand::Init(char* /*inData*/, int /*inSize*/)
128 void SC_SequencedCommand::SendDone(const char *inCommandName
)
130 ::SendDone(&mReplyAddress
, inCommandName
);
133 void SC_SequencedCommand::SendDoneWithIntValue(const char *inCommandName
, int value
)
135 ::SendDoneWithIntValue(&mReplyAddress
, inCommandName
, value
);
138 void SC_SequencedCommand::CallEveryStage()
140 switch (mNextStage
) {
141 case 1 : if (!Stage1()) break; mNextStage
++;
142 case 2 : if (!Stage2()) break; mNextStage
++;
143 case 3 : if (!Stage3()) break; mNextStage
++;
144 case 4 : Stage4(); break;
149 void DoSequencedCommand(FifoMsg
*inMsg
);
150 void DoSequencedCommand(FifoMsg
*inMsg
)
152 SC_SequencedCommand
*cmd
= (SC_SequencedCommand
*)inMsg
->mData
;
153 cmd
->CallNextStage();
156 void FreeSequencedCommand(FifoMsg
*inMsg
);
157 void FreeSequencedCommand(FifoMsg
*inMsg
)
159 SC_SequencedCommand
*cmd
= (SC_SequencedCommand
*)inMsg
->mData
;
164 void SC_SequencedCommand::CallNextStage()
166 bool sendAgain
= false;
169 int isRealTime
= mNextStage
& 1;
170 switch (mNextStage
) {
172 sendAgain
= Stage1(); // RT
175 sendAgain
= Stage2(); // NRT
178 sendAgain
= Stage3(); // RT
185 SC_AudioDriver
*driver
= AudioDriver(mWorld
);
187 msg
.Set(mWorld
, DoSequencedCommand
, 0, (void*)this);
188 // send this to next time.
191 driver
->SendMsgFromEngine(msg
);
194 driver
->SendMsgToEngine(msg
);
200 // can only be freed from RT.
201 msg
.Set(mWorld
, FreeSequencedCommand
, 0, (void*)this);
202 driver
->SendMsgToEngine(msg
);
207 void SC_SequencedCommand::Delete()
210 World_Free(mWorld
, this);
213 bool SC_SequencedCommand::Stage1()
218 bool SC_SequencedCommand::Stage2()
223 bool SC_SequencedCommand::Stage3()
228 void SC_SequencedCommand::Stage4()
233 ///////////////////////////////////////////////////////////////////////////
235 #include "sc_msg_iter.h"
238 SyncCmd::SyncCmd(World
*inWorld
, ReplyAddress
*inReplyAddress
)
239 : SC_SequencedCommand(inWorld
, inReplyAddress
)
243 int SyncCmd::Init(char *inData
, int inSize
)
245 sc_msg_iter
msg(inSize
, inData
);
250 void SyncCmd::CallDestructor()
255 bool SyncCmd::Stage2()
260 bool SyncCmd::Stage3()
265 void SyncCmd::Stage4()
267 small_scpacket packet
;
268 packet
.adds("/synced");
275 SendReply(&mReplyAddress
, packet
.data(), packet
.size());
278 ///////////////////////////////////////////////////////////////////////////
280 BufAllocCmd::BufAllocCmd(World
*inWorld
, ReplyAddress
*inReplyAddress
)
281 : SC_SequencedCommand(inWorld
, inReplyAddress
)
285 int BufAllocCmd::Init(char *inData
, int inSize
)
287 sc_msg_iter
msg(inSize
, inData
);
288 mBufIndex
= msg
.geti();
289 mNumFrames
= msg
.geti();
290 mNumChannels
= msg
.geti(1);
292 GET_COMPLETION_MSG(msg
);
297 void BufAllocCmd::CallDestructor()
299 this->~BufAllocCmd();
302 bool BufAllocCmd::Stage2()
304 SndBuf
*buf
= World_GetNRTBuf(mWorld
, mBufIndex
);
305 mFreeData
= buf
->data
;
306 bufAlloc(buf
, mNumChannels
, mNumFrames
, mWorld
->mFullRate
.mSampleRate
);
311 bool BufAllocCmd::Stage3()
313 SndBuf
* buf
= World_GetBuf(mWorld
, mBufIndex
);
315 mWorld
->mSndBufUpdates
[mBufIndex
].writes
++ ;
320 void BufAllocCmd::Stage4()
323 SendDoneWithIntValue("/b_alloc", mBufIndex
);
326 ///////////////////////////////////////////////////////////////////////////
328 #include "sc_msg_iter.h"
331 BufGenCmd::BufGenCmd(World
*inWorld
, ReplyAddress
*inReplyAddress
)
332 : SC_SequencedCommand(inWorld
, inReplyAddress
),
337 BufGenCmd::~BufGenCmd()
339 World_Free(mWorld
, mData
);
342 int BufGenCmd::Init(char *inData
, int inSize
)
345 mData
= (char*)World_Alloc(mWorld
, mSize
);
346 memcpy(mData
, inData
, mSize
);
348 sc_msg_iter
msg(mSize
, mData
);
349 mBufIndex
= msg
.geti();
351 int32
*genName
= msg
.gets4();
352 if (!genName
) return kSCErr_WrongArgType
;
354 mBufGen
= GetBufGen(genName
);
355 if (!mBufGen
) return kSCErr_BufGenNotFound
;
362 void BufGenCmd::CallDestructor()
367 bool BufGenCmd::Stage2()
369 SndBuf
*buf
= World_GetNRTBuf(mWorld
, mBufIndex
);
371 mFreeData
= buf
->data
;
372 (*mBufGen
->mBufGenFunc
)(mWorld
, buf
, &mMsg
);
373 if (buf
->data
== mFreeData
) mFreeData
= NULL
;
378 bool BufGenCmd::Stage3()
380 SndBuf
* buf
= World_GetBuf(mWorld
, mBufIndex
);
382 mWorld
->mSndBufUpdates
[mBufIndex
].writes
++ ;
386 void BufGenCmd::Stage4()
389 SendDoneWithIntValue("/b_gen", mBufIndex
);
393 ///////////////////////////////////////////////////////////////////////////
396 ///////////////////////////////////////////////////////////////////////////
399 BufFreeCmd::BufFreeCmd(World
*inWorld
, ReplyAddress
*inReplyAddress
)
400 : SC_SequencedCommand(inWorld
, inReplyAddress
)
404 int BufFreeCmd::Init(char *inData
, int inSize
)
406 sc_msg_iter
msg(inSize
, inData
);
407 mBufIndex
= msg
.geti();
409 GET_COMPLETION_MSG(msg
);
414 void BufFreeCmd::CallDestructor()
419 bool BufFreeCmd::Stage2()
421 SndBuf
*buf
= World_GetNRTBuf(mWorld
, mBufIndex
);
422 mFreeData
= buf
->data
;
423 #ifndef NO_LIBSNDFILE
424 if (buf
->sndfile
) sf_close(buf
->sndfile
);
430 bool BufFreeCmd::Stage3()
432 SndBuf
*buf
= World_GetBuf(mWorld
, mBufIndex
);
435 mWorld
->mSndBufUpdates
[mBufIndex
].writes
++ ;
441 void BufFreeCmd::Stage4()
444 SendDoneWithIntValue("/b_free", mBufIndex
);
447 ///////////////////////////////////////////////////////////////////////////
449 BufZeroCmd::BufZeroCmd(World
*inWorld
, ReplyAddress
*inReplyAddress
)
450 : SC_SequencedCommand(inWorld
, inReplyAddress
)
454 int BufZeroCmd::Init(char *inData
, int inSize
)
456 sc_msg_iter
msg(inSize
, inData
);
457 mBufIndex
= msg
.geti();
459 GET_COMPLETION_MSG(msg
);
464 void BufZeroCmd::CallDestructor()
469 bool BufZeroCmd::Stage2()
471 SndBuf
*buf
= World_GetNRTBuf(mWorld
, mBufIndex
);
472 memset(buf
->data
, 0, buf
->samples
* sizeof(float));
476 bool BufZeroCmd::Stage3()
478 mWorld
->mSndBufUpdates
[mBufIndex
].writes
++ ;
483 void BufZeroCmd::Stage4()
485 SendDoneWithIntValue("/b_zero", mBufIndex
);
488 ///////////////////////////////////////////////////////////////////////////
490 BufAllocReadCmd::BufAllocReadCmd(World
*inWorld
, ReplyAddress
*inReplyAddress
)
491 : SC_SequencedCommand(inWorld
, inReplyAddress
), mFreeData(0), mFilename(0)
495 int BufAllocReadCmd::Init(char *inData
, int inSize
)
497 sc_msg_iter
msg(inSize
, inData
);
498 mBufIndex
= msg
.geti();
500 const char *filename
= msg
.gets();
501 if (!filename
) return kSCErr_WrongArgType
;
503 if(mWorld
->mRestrictedPath
){
504 mFilename
= allocAndRestrictPath(mWorld
, filename
, mWorld
->mRestrictedPath
);
506 mFilename
= (char*)World_Alloc(mWorld
, strlen(filename
)+1);
507 strcpy(mFilename
, filename
);
510 mFileOffset
= msg
.geti();
511 mNumFrames
= msg
.geti();
513 GET_COMPLETION_MSG(msg
);
518 BufAllocReadCmd::~BufAllocReadCmd()
520 World_Free(mWorld
, mFilename
);
523 void BufAllocReadCmd::CallDestructor()
525 this->~BufAllocReadCmd();
528 bool BufAllocReadCmd::Stage2()
531 SendFailure(&mReplyAddress
, "/b_allocRead", "scsynth compiled without libsndfile\n");
532 scprintf("scsynth compiled without libsndfile\n");
535 SndBuf
*buf
= World_GetNRTBuf(mWorld
, mBufIndex
);
537 memset(&fileinfo
, 0, sizeof(fileinfo
));
538 SNDFILE
* sf
= sf_open(mFilename
, SFM_READ
, &fileinfo
);
541 sprintf(str
, "File '%s' could not be opened.\n", mFilename
);
542 SendFailureWithBufnum(&mReplyAddress
, "/b_allocRead", str
, mBufIndex
); //SendFailure(&mReplyAddress, "/b_allocRead", str);
546 if (mFileOffset
< 0) mFileOffset
= 0;
547 else if (mFileOffset
> fileinfo
.frames
) mFileOffset
= fileinfo
.frames
;
548 if (mNumFrames
<= 0 || mNumFrames
+ mFileOffset
> fileinfo
.frames
) mNumFrames
= fileinfo
.frames
- mFileOffset
;
551 mFreeData
= buf
->data
;
552 SCErr err
= bufAlloc(buf
, fileinfo
.channels
, mNumFrames
, fileinfo
.samplerate
);
555 sf_seek(sf
, mFileOffset
, SEEK_SET
);
556 sf_readf_float(sf
, buf
->data
, mNumFrames
);
566 bool BufAllocReadCmd::Stage3()
568 SndBuf
* buf
= World_GetBuf(mWorld
, mBufIndex
);
570 mWorld
->mSndBufUpdates
[mBufIndex
].writes
++ ;
576 void BufAllocReadCmd::Stage4()
579 SendDoneWithIntValue("/b_allocRead", mBufIndex
);
582 ///////////////////////////////////////////////////////////////////////////
584 BufReadCmd::BufReadCmd(World
*inWorld
, ReplyAddress
*inReplyAddress
)
585 : SC_SequencedCommand(inWorld
, inReplyAddress
),
590 int BufReadCmd::Init(char *inData
, int inSize
)
592 sc_msg_iter
msg(inSize
, inData
);
593 mBufIndex
= msg
.geti();
595 const char *filename
= msg
.gets();
596 if (!filename
) return kSCErr_WrongArgType
;
598 if(mWorld
->mRestrictedPath
){
599 mFilename
= allocAndRestrictPath(mWorld
, filename
, mWorld
->mRestrictedPath
);
601 mFilename
= (char*)World_Alloc(mWorld
, strlen(filename
)+1);
602 strcpy(mFilename
, filename
);
605 mFileOffset
= msg
.geti();
606 mNumFrames
= msg
.geti(-1);
607 mBufOffset
= msg
.geti();
608 mLeaveFileOpen
= msg
.geti();
610 GET_COMPLETION_MSG(msg
);
615 BufReadCmd::~BufReadCmd()
617 World_Free(mWorld
, mFilename
);
620 void BufReadCmd::CallDestructor()
625 bool BufReadCmd::Stage2()
628 SendFailure(&mReplyAddress
, "/b_read", "scsynth compiled without libsndfile\n");
629 scprintf("scsynth compiled without libsndfile\n");
634 SndBuf
*buf
= World_GetNRTBuf(mWorld
, mBufIndex
);
635 int framesToEnd
= buf
->frames
- mBufOffset
;
636 if (framesToEnd
<= 0) return true;
638 SNDFILE
* sf
= sf_open(mFilename
, SFM_READ
, &fileinfo
);
641 sprintf(str
, "File '%s' could not be opened.\n", mFilename
);
642 SendFailureWithBufnum(&mReplyAddress
, "/b_read", str
, mBufIndex
); //SendFailure(&mReplyAddress, "/b_read", str);
646 if (fileinfo
.channels
!= buf
->channels
) {
649 sprintf(str
, "channel mismatch. File'%s' has %d channels. Buffer has %d channels.\n", mFilename
, fileinfo
.channels
, buf
->channels
);
650 SendFailureWithBufnum(&mReplyAddress
, "/b_read", str
, mBufIndex
); //SendFailure(&mReplyAddress, "/b_read", str);
655 if (mFileOffset
< 0) mFileOffset
= 0;
656 else if (mFileOffset
> fileinfo
.frames
) mFileOffset
= fileinfo
.frames
;
657 if (mNumFrames
< 0 || mNumFrames
+ mFileOffset
> fileinfo
.frames
) mNumFrames
= fileinfo
.frames
- mFileOffset
;
659 if (mNumFrames
> framesToEnd
) mNumFrames
= framesToEnd
;
661 sf_seek(sf
, mFileOffset
, SEEK_SET
);
662 if (mNumFrames
> 0) {
663 sf_readf_float(sf
, buf
->data
+ (mBufOffset
* buf
->channels
), mNumFrames
);
667 sf_close(buf
->sndfile
);
669 if (mLeaveFileOpen
) {
676 mSampleRate
= (double)fileinfo
.samplerate
;
682 bool BufReadCmd::Stage3()
684 SndBuf
* buf
= World_GetBuf(mWorld
, mBufIndex
);
685 buf
->samplerate
= mSampleRate
;
686 if (mLeaveFileOpen
) buf
->mask
= buf
->mask1
= -1;
688 mWorld
->mSndBufUpdates
[mBufIndex
].writes
++ ;
693 void BufReadCmd::Stage4()
695 SendDoneWithIntValue("/b_read", mBufIndex
);
698 ///////////////////////////////////////////////////////////////////////////
700 SC_BufReadCommand::SC_BufReadCommand(World
* inWorld
, ReplyAddress
* inReplyAddress
)
701 : SC_SequencedCommand(inWorld
, inReplyAddress
),
706 SC_BufReadCommand::~SC_BufReadCommand()
710 void SC_BufReadCommand::InitChannels(sc_msg_iter
& msg
)
713 while (msg
.nextTag(0) == 'i') {
715 if (mNumChannels
<= kMaxNumChannels
) {
716 mChannels
[mNumChannels
++] = c
;
721 bool SC_BufReadCommand::CheckChannels(int inNumChannels
)
723 for (int i
=0; i
< mNumChannels
; ++i
) {
724 if (mChannels
[i
] >= inNumChannels
) {
731 void SC_BufReadCommand::CopyChannels(float* dst
, float* src
, size_t srcChannels
, size_t numFrames
)
733 for (int ci
=0; ci
< mNumChannels
; ++ci
) {
734 int c
= mChannels
[ci
];
735 if (c
>= 0 && c
< srcChannels
) {
736 for (size_t fi
=0; fi
< numFrames
; ++fi
) {
737 dst
[fi
*mNumChannels
+ci
] = src
[fi
*srcChannels
+c
];
740 for (size_t fi
=0; fi
< numFrames
; ++fi
) {
741 dst
[fi
*mNumChannels
+ci
] = 0.f
;
747 ///////////////////////////////////////////////////////////////////////////
749 BufAllocReadChannelCmd::BufAllocReadChannelCmd(World
*inWorld
, ReplyAddress
*inReplyAddress
)
750 : SC_BufReadCommand(inWorld
, inReplyAddress
),
751 mFreeData(0), mFilename(0)
755 int BufAllocReadChannelCmd::Init(char *inData
, int inSize
)
757 sc_msg_iter
msg(inSize
, inData
);
758 mBufIndex
= msg
.geti();
760 const char *filename
= msg
.gets();
761 if (!filename
) return kSCErr_WrongArgType
;
763 if(mWorld
->mRestrictedPath
){
764 mFilename
= allocAndRestrictPath(mWorld
, filename
, mWorld
->mRestrictedPath
);
766 mFilename
= (char*)World_Alloc(mWorld
, strlen(filename
)+1);
767 strcpy(mFilename
, filename
);
770 mFileOffset
= msg
.geti();
771 mNumFrames
= msg
.geti();
775 GET_COMPLETION_MSG(msg
);
780 BufAllocReadChannelCmd::~BufAllocReadChannelCmd()
782 World_Free(mWorld
, mFilename
);
785 void BufAllocReadChannelCmd::CallDestructor()
787 this->~BufAllocReadChannelCmd();
790 bool BufAllocReadChannelCmd::Stage2()
793 SendFailure(&mReplyAddress
, "/b_allocReadChannel", "scsynth compiled without libsndfile\n");
794 scprintf("scsynth compiled without libsndfile\n");
797 SndBuf
*buf
= World_GetNRTBuf(mWorld
, mBufIndex
);
800 memset(&fileinfo
, 0, sizeof(fileinfo
));
801 SNDFILE
* sf
= sf_open(mFilename
, SFM_READ
, &fileinfo
);
804 sprintf(str
, "File '%s' could not be opened.\n", mFilename
);
805 SendFailureWithBufnum(&mReplyAddress
, "/b_allocReadChannel", str
, mBufIndex
); //SendFailure(&mReplyAddress, "/b_allocRead", str);
809 if (mFileOffset
< 0) mFileOffset
= 0;
810 else if (mFileOffset
> fileinfo
.frames
) mFileOffset
= fileinfo
.frames
;
811 if (mNumFrames
<= 0 || mNumFrames
+ mFileOffset
> fileinfo
.frames
) mNumFrames
= fileinfo
.frames
- mFileOffset
;
813 if (mNumChannels
== 0) {
815 mFreeData
= buf
->data
;
816 SCErr err
= bufAlloc(buf
, fileinfo
.channels
, mNumFrames
, fileinfo
.samplerate
);
819 sf_seek(sf
, mFileOffset
, SEEK_SET
);
820 sf_readf_float(sf
, buf
->data
, mNumFrames
);
822 // verify channel indexes
823 if (!CheckChannels(fileinfo
.channels
)) {
824 const char* str
= "Channel index out of range.\n";
825 SendFailureWithBufnum(&mReplyAddress
, "/b_allocReadChannel", str
, mBufIndex
); //SendFailure(&mReplyAddress, "/b_allocRead", str);
831 mFreeData
= buf
->data
;
832 SCErr err
= bufAlloc(buf
, mNumChannels
, mNumFrames
, fileinfo
.samplerate
);
835 float* data
= (float*)malloc(mNumFrames
*fileinfo
.channels
*sizeof(float));
836 if (data
== 0) goto leave
;
837 // read some channels
838 sf_seek(sf
, mFileOffset
, SEEK_SET
);
839 sf_readf_float(sf
, data
, mNumFrames
);
840 CopyChannels(buf
->data
, data
, fileinfo
.channels
, mNumFrames
);
853 bool BufAllocReadChannelCmd::Stage3()
855 SndBuf
* buf
= World_GetBuf(mWorld
, mBufIndex
);
857 mWorld
->mSndBufUpdates
[mBufIndex
].writes
++ ;
863 void BufAllocReadChannelCmd::Stage4()
866 SendDoneWithIntValue("/b_allocReadChannel", mBufIndex
);
869 ///////////////////////////////////////////////////////////////////////////
871 BufReadChannelCmd::BufReadChannelCmd(World
*inWorld
, ReplyAddress
*inReplyAddress
)
872 : SC_BufReadCommand(inWorld
, inReplyAddress
),
877 int BufReadChannelCmd::Init(char *inData
, int inSize
)
879 sc_msg_iter
msg(inSize
, inData
);
880 mBufIndex
= msg
.geti();
882 const char *filename
= msg
.gets();
883 if (!filename
) return kSCErr_WrongArgType
;
885 if(mWorld
->mRestrictedPath
){
886 mFilename
= allocAndRestrictPath(mWorld
, filename
, mWorld
->mRestrictedPath
);
888 mFilename
= (char*)World_Alloc(mWorld
, strlen(filename
)+1);
889 strcpy(mFilename
, filename
);
892 mFileOffset
= msg
.geti();
893 mNumFrames
= msg
.geti(-1);
894 mBufOffset
= msg
.geti();
895 mLeaveFileOpen
= msg
.geti();
899 GET_COMPLETION_MSG(msg
);
904 BufReadChannelCmd::~BufReadChannelCmd()
906 World_Free(mWorld
, mFilename
);
909 void BufReadChannelCmd::CallDestructor()
911 this->~BufReadChannelCmd();
914 bool BufReadChannelCmd::Stage2()
917 SendFailure(&mReplyAddress
, "/b_readChannel", "scsynth compiled without libsndfile\n");
918 scprintf("scsynth compiled without libsndfile\n");
923 SndBuf
*buf
= World_GetNRTBuf(mWorld
, mBufIndex
);
924 int framesToEnd
= buf
->frames
- mBufOffset
;
925 if (framesToEnd
<= 0) return true;
927 SNDFILE
* sf
= sf_open(mFilename
, SFM_READ
, &fileinfo
);
930 sprintf(str
, "File '%s' could not be opened.\n", mFilename
);
931 SendFailureWithBufnum(&mReplyAddress
, "/b_readChannel", str
, mBufIndex
); //SendFailure(&mReplyAddress, "/b_read", str);
936 if (mNumChannels
> 0) {
937 // verify channel indexes
938 if (!( CheckChannels(fileinfo
.channels
)) ) { // nescivi: && CheckChannels(buf->channels) (should not check here for buf->channels)
939 const char* str
= "Channel index out of range.\n";
940 SendFailureWithBufnum(&mReplyAddress
, "/b_readChannel", str
, mBufIndex
); //SendFailure(&mReplyAddress, "/b_allocRead", str);
945 // nescivi: this also seems out of place: we want to read from a file with more channels than are in the buffer
946 // } else if (fileinfo.channels != buf->channels) {
949 // sprintf(str, "Channel mismatch. File '%s' has %d channels. Buffer has %d channels.\n",
950 // mFilename, fileinfo.channels, buf->channels);
951 // SendFailure(&mReplyAddress, "/b_read", str);
956 if (mFileOffset
< 0) mFileOffset
= 0;
957 else if (mFileOffset
> fileinfo
.frames
) mFileOffset
= fileinfo
.frames
;
958 if (mNumFrames
< 0 || mNumFrames
+ mFileOffset
> fileinfo
.frames
) mNumFrames
= fileinfo
.frames
- mFileOffset
;
959 if (mNumFrames
> framesToEnd
) mNumFrames
= framesToEnd
;
961 sf_seek(sf
, mFileOffset
, SEEK_SET
);
962 if (mNumFrames
> 0) {
963 if (mNumChannels
== 0) {
965 sf_readf_float(sf
, buf
->data
+ (mBufOffset
* buf
->channels
), mNumFrames
);
968 float* data
= (float*)malloc(mNumFrames
*fileinfo
.channels
*sizeof(float));
969 if (data
== 0) goto leave
;
970 // read some channels
971 sf_seek(sf
, mFileOffset
, SEEK_SET
);
972 sf_readf_float(sf
, data
, mNumFrames
);
973 CopyChannels(buf
->data
+ (mBufOffset
* mNumChannels
), data
, fileinfo
.channels
, mNumFrames
);
981 sf_close(buf
->sndfile
);
983 if (mLeaveFileOpen
) {
990 mSampleRate
= (double)fileinfo
.samplerate
;
996 bool BufReadChannelCmd::Stage3()
998 SndBuf
* buf
= World_GetBuf(mWorld
, mBufIndex
);
999 buf
->samplerate
= mSampleRate
;
1000 if (mLeaveFileOpen
) buf
->mask
= buf
->mask1
= -1;
1002 mWorld
->mSndBufUpdates
[mBufIndex
].writes
++ ;
1003 SEND_COMPLETION_MSG
;
1007 void BufReadChannelCmd::Stage4()
1009 SendDoneWithIntValue("/b_readChannel", mBufIndex
);
1012 ///////////////////////////////////////////////////////////////////////////
1014 BufWriteCmd::BufWriteCmd(World
*inWorld
, ReplyAddress
*inReplyAddress
)
1015 : SC_SequencedCommand(inWorld
, inReplyAddress
), mFilename(0)
1019 #ifdef NO_LIBSNDFILE
1025 int sndfileFormatInfoFromStrings(SF_INFO
*info
, const char *headerFormatString
, const char *sampleFormatString
);
1029 int BufWriteCmd::Init(char *inData
, int inSize
)
1031 #ifdef NO_LIBSNDFILE
1032 SendFailure(&mReplyAddress
, "/b_write", "scsynth compiled without libsndfile\n");
1033 scprintf("scsynth compiled without libsndfile\n");
1036 sc_msg_iter
msg(inSize
, inData
);
1037 mBufIndex
= msg
.geti();
1039 const char *filename
= msg
.gets();
1040 if (!filename
) return kSCErr_WrongArgType
;
1042 if(mWorld
->mRestrictedPath
){
1043 mFilename
= allocAndRestrictPath(mWorld
, filename
, mWorld
->mRestrictedPath
);
1045 mFilename
= (char*)World_Alloc(mWorld
, strlen(filename
)+1);
1046 strcpy(mFilename
, filename
);
1049 const char *headerFormatString
= msg
.gets("aiff");
1050 const char *sampleFormatString
= msg
.gets("int16");
1052 mNumFrames
= msg
.geti(-1);
1053 mBufOffset
= msg
.geti();
1054 mLeaveFileOpen
= msg
.geti();
1056 GET_COMPLETION_MSG(msg
);
1058 memset(&mFileInfo
, 0, sizeof(mFileInfo
));
1059 return sndfileFormatInfoFromStrings(&mFileInfo
, headerFormatString
, sampleFormatString
);
1063 BufWriteCmd::~BufWriteCmd()
1065 World_Free(mWorld
, mFilename
);
1068 void BufWriteCmd::CallDestructor()
1070 this->~BufWriteCmd();
1073 bool BufWriteCmd::Stage2()
1075 #ifdef NO_LIBSNDFILE
1078 SndBuf
*buf
= World_GetNRTBuf(mWorld
, mBufIndex
);
1079 int framesToEnd
= buf
->frames
- mBufOffset
;
1080 if (framesToEnd
< 0) framesToEnd
= 0;
1081 mFileInfo
.samplerate
= (int)buf
->samplerate
;
1082 mFileInfo
.channels
= buf
->channels
;
1084 SNDFILE
* sf
= sf_open(mFilename
, SFM_WRITE
, &mFileInfo
);
1088 sf_error_str(NULL
, sferr
, 256);
1089 sprintf(str
, "File '%s' could not be opened. '%s'\n", mFilename
, sferr
);
1090 SendFailureWithBufnum(&mReplyAddress
, "/b_write", str
, mBufIndex
); //SendFailure(&mReplyAddress, "/b_write", str);
1095 if (mNumFrames
< 0 || mNumFrames
> buf
->frames
) mNumFrames
= buf
->frames
;
1097 if (mNumFrames
> framesToEnd
) mNumFrames
= framesToEnd
;
1099 if (mNumFrames
> 0) {
1100 sf_writef_float(sf
, buf
->data
+ (mBufOffset
* buf
->channels
), mNumFrames
);
1104 sf_close(buf
->sndfile
);
1106 if (mLeaveFileOpen
) {
1117 bool BufWriteCmd::Stage3()
1119 SEND_COMPLETION_MSG
;
1123 void BufWriteCmd::Stage4()
1125 SendDoneWithIntValue("/b_write", mBufIndex
);
1128 ///////////////////////////////////////////////////////////////////////////
1130 BufCloseCmd::BufCloseCmd(World
*inWorld
, ReplyAddress
*inReplyAddress
)
1131 : SC_SequencedCommand(inWorld
, inReplyAddress
)
1135 int BufCloseCmd::Init(char *inData
, int inSize
)
1137 sc_msg_iter
msg(inSize
, inData
);
1138 mBufIndex
= msg
.geti();
1140 GET_COMPLETION_MSG(msg
);
1145 void BufCloseCmd::CallDestructor()
1147 this->~BufCloseCmd();
1150 bool BufCloseCmd::Stage2()
1152 #ifdef NO_LIBSNDFILE
1153 SendFailure(&mReplyAddress
, "/b_close", "scsynth compiled without libsndfile\n");
1154 scprintf("scsynth compiled without libsndfile\n");
1157 SndBuf
*buf
= World_GetNRTBuf(mWorld
, mBufIndex
);
1159 sf_close(buf
->sndfile
);
1166 bool BufCloseCmd::Stage3()
1168 SEND_COMPLETION_MSG
;
1172 void BufCloseCmd::Stage4()
1174 SendDoneWithIntValue("/b_close", mBufIndex
);
1177 ///////////////////////////////////////////////////////////////////////////
1179 AudioQuitCmd::AudioQuitCmd(World
*inWorld
, ReplyAddress
*inReplyAddress
)
1180 : SC_SequencedCommand(inWorld
, inReplyAddress
)
1184 void AudioQuitCmd::CallDestructor()
1186 this->~AudioQuitCmd();
1189 bool AudioQuitCmd::Stage2()
1191 mWorld
->hw
->mTerminating
= true;
1195 bool AudioQuitCmd::Stage3()
1197 #if SC_AUDIO_API == SC_AUDIO_API_AUDIOUNITS
1198 SendFailure(&mReplyAddress
, "/quit", "not allowed in AU host\n");
1199 scprintf("/quit : quit not allowed in AU host\n");
1206 void AudioQuitCmd::Stage4()
1209 mWorld
->hw
->mQuitProgram
->Release();
1212 ///////////////////////////////////////////////////////////////////////////
1214 AudioStatusCmd::AudioStatusCmd(World
*inWorld
, ReplyAddress
*inReplyAddress
)
1215 : SC_SequencedCommand(inWorld
, inReplyAddress
)
1219 void AudioStatusCmd::CallDestructor()
1221 this->~AudioStatusCmd();
1224 bool AudioStatusCmd::Stage2()
1226 // we stop replying to status requests after receiving /quit
1227 if (mWorld
->hw
->mTerminating
== true)
1230 small_scpacket packet
;
1231 packet
.adds("/status.reply");
1232 packet
.maketags(10);
1244 packet
.addi(1); // audio is always active now.
1245 packet
.addi(mWorld
->mNumUnits
);
1246 packet
.addi(mWorld
->mNumGraphs
);
1247 packet
.addi(mWorld
->mNumGroups
);
1248 packet
.addi(mWorld
->hw
->mGraphDefLib
->NumItems());
1250 SC_AudioDriver
*driver
= mWorld
->hw
->mAudioDriver
;
1251 packet
.addf(driver
->GetAvgCPU());
1252 packet
.addf(driver
->GetPeakCPU());
1253 packet
.addd(driver
->GetSampleRate());
1254 packet
.addd(driver
->GetActualSampleRate());
1256 SendReply(&mReplyAddress
, packet
.data(), packet
.size());
1261 ///////////////////////////////////////////////////////////////////////////
1263 NotifyCmd::NotifyCmd(World
*inWorld
, ReplyAddress
*inReplyAddress
)
1264 : SC_SequencedCommand(inWorld
, inReplyAddress
)
1268 int NotifyCmd::Init(char *inData
, int inSize
)
1270 sc_msg_iter
msg(inSize
, inData
);
1271 mOnOff
= msg
.geti();
1276 void NotifyCmd::CallDestructor()
1281 bool NotifyCmd::Stage2()
1283 HiddenWorld
*hw
= mWorld
->hw
;
1286 for (uint32 i
=0; i
<hw
->mNumUsers
; ++i
) {
1287 if (mReplyAddress
== hw
->mUsers
[i
]) {
1288 // already in table - don't fail though..
1289 SendFailure(&mReplyAddress
, "/notify", "notify: already registered\n");
1290 scprintf("/notify : already registered\n");
1295 // add reply address to user table
1296 if (hw
->mNumUsers
>= hw
->mMaxUsers
) {
1297 SendFailure(&mReplyAddress
, "/notify", "too many users\n");
1298 scprintf("too many users\n");
1302 hw
->mUsers
[hw
->mNumUsers
++] = mReplyAddress
;
1304 SendDone("/notify");
1306 for (uint32 i
=0; i
<hw
->mNumUsers
; ++i
) {
1307 if (mReplyAddress
== hw
->mUsers
[i
]) {
1309 hw
->mUsers
[i
] = hw
->mUsers
[--hw
->mNumUsers
];
1310 SendDone("/notify");
1315 SendFailure(&mReplyAddress
, "/notify", "not registered\n");
1316 scprintf("not registered\n");
1321 ///////////////////////////////////////////////////////////////////////////
1323 SendFailureCmd::SendFailureCmd(World
*inWorld
, ReplyAddress
*inReplyAddress
)
1324 : SC_SequencedCommand(inWorld
, inReplyAddress
), mCmdName(0), mErrString(0)
1328 SendFailureCmd::~SendFailureCmd()
1330 World_Free(mWorld
, mCmdName
);
1331 World_Free(mWorld
, mErrString
);
1335 void SendFailureCmd::InitSendFailureCmd(const char *inCmdName
, const char* inErrString
)
1337 mCmdName
= (char*)World_Alloc(mWorld
, strlen(inCmdName
)+1);
1338 strcpy(mCmdName
, inCmdName
);
1340 mErrString
= (char*)World_Alloc(mWorld
, strlen(inErrString
)+1);
1341 strcpy(mErrString
, inErrString
);
1344 void SendFailureCmd::CallDestructor()
1346 this->~SendFailureCmd();
1349 bool SendFailureCmd::Stage2()
1351 SendFailure(&mReplyAddress
, mCmdName
, mErrString
);
1355 ///////////////////////////////////////////////////////////////////////////
1357 RecvSynthDefCmd::RecvSynthDefCmd(World
*inWorld
, ReplyAddress
*inReplyAddress
)
1358 : SC_SequencedCommand(inWorld
, inReplyAddress
), mBuffer(0)
1362 int RecvSynthDefCmd::Init(char *inData
, int inSize
)
1364 sc_msg_iter
msg(inSize
, inData
);
1366 int size
= msg
.getbsize();
1367 if (!size
) throw kSCErr_WrongArgType
;
1369 mBuffer
= (char*)World_Alloc(mWorld
, size
);
1370 msg
.getb(mBuffer
, size
);
1372 GET_COMPLETION_MSG(msg
);
1378 RecvSynthDefCmd::~RecvSynthDefCmd()
1380 World_Free(mWorld
, mBuffer
);
1383 void RecvSynthDefCmd::CallDestructor()
1385 this->~RecvSynthDefCmd();
1388 bool RecvSynthDefCmd::Stage2()
1390 mDefs
= GraphDef_Recv(mWorld
, mBuffer
, mDefs
);
1395 bool RecvSynthDefCmd::Stage3()
1397 GraphDef_Define(mWorld
, mDefs
);
1398 SEND_COMPLETION_MSG
;
1402 void RecvSynthDefCmd::Stage4()
1404 SendDone("/d_recv");
1407 ///////////////////////////////////////////////////////////////////////////
1409 LoadSynthDefCmd::LoadSynthDefCmd(World
*inWorld
, ReplyAddress
*inReplyAddress
)
1410 : SC_SequencedCommand(inWorld
, inReplyAddress
), mFilename(0)
1414 int LoadSynthDefCmd::Init(char *inData
, int inSize
)
1416 sc_msg_iter
msg(inSize
, inData
);
1418 const char *filename
= msg
.gets();
1419 if (!filename
) return kSCErr_WrongArgType
;
1421 if(mWorld
->mRestrictedPath
){
1422 mFilename
= allocAndRestrictPath(mWorld
, filename
, mWorld
->mRestrictedPath
);
1424 mFilename
= (char*)World_Alloc(mWorld
, strlen(filename
)+1);
1425 strcpy(mFilename
, filename
);
1428 GET_COMPLETION_MSG(msg
);
1434 LoadSynthDefCmd::~LoadSynthDefCmd()
1436 World_Free(mWorld
, mFilename
);
1439 void LoadSynthDefCmd::CallDestructor()
1441 this->~LoadSynthDefCmd();
1444 bool LoadSynthDefCmd::Stage2()
1446 char* fname
= mFilename
;
1447 mDefs
= GraphDef_LoadGlob(mWorld
, fname
, mDefs
);
1451 bool LoadSynthDefCmd::Stage3()
1453 GraphDef_Define(mWorld
, mDefs
);
1454 SEND_COMPLETION_MSG
;
1458 void LoadSynthDefCmd::Stage4()
1460 SendDone("/d_load");
1463 ///////////////////////////////////////////////////////////////////////////
1465 LoadSynthDefDirCmd::LoadSynthDefDirCmd(World
*inWorld
, ReplyAddress
*inReplyAddress
)
1466 : SC_SequencedCommand(inWorld
, inReplyAddress
), mFilename(0)
1470 int LoadSynthDefDirCmd::Init(char *inData
, int inSize
)
1472 sc_msg_iter
msg(inSize
, inData
);
1474 const char *filename
= msg
.gets();
1475 if (!filename
) return kSCErr_WrongArgType
;
1477 if(mWorld
->mRestrictedPath
){
1478 mFilename
= allocAndRestrictPath(mWorld
, filename
, mWorld
->mRestrictedPath
);
1480 mFilename
= (char*)World_Alloc(mWorld
, strlen(filename
)+1);
1481 strcpy(mFilename
, filename
);
1484 GET_COMPLETION_MSG(msg
);
1490 LoadSynthDefDirCmd::~LoadSynthDefDirCmd()
1492 World_Free(mWorld
, mFilename
);
1495 void LoadSynthDefDirCmd::CallDestructor()
1497 this->~LoadSynthDefDirCmd();
1500 bool LoadSynthDefDirCmd::Stage2()
1502 mDefs
= GraphDef_LoadDir(mWorld
, mFilename
, mDefs
);
1507 bool LoadSynthDefDirCmd::Stage3()
1509 GraphDef_Define(mWorld
, mDefs
);
1510 SEND_COMPLETION_MSG
;
1514 void LoadSynthDefDirCmd::Stage4()
1516 SendDone("/d_loadDir");
1519 ///////////////////////////////////////////////////////////////////////////
1521 SendReplyCmd::SendReplyCmd(World
*inWorld
, ReplyAddress
*inReplyAddress
)
1522 : SC_SequencedCommand(inWorld
, inReplyAddress
)
1526 int SendReplyCmd::Init(char *inData
, int inSize
)
1529 mMsgData
= (char*)World_Alloc(mWorld
, mMsgSize
);
1530 memcpy(mMsgData
, inData
, inSize
);
1534 void SendReplyCmd::CallDestructor()
1536 this->~SendReplyCmd();
1539 bool SendReplyCmd::Stage2()
1541 SendReply(&mReplyAddress
, mMsgData
, mMsgSize
);
1545 ///////////////////////////////////////////////////////////////////////////
1547 int PerformAsynchronousCommand(
1550 const char* cmdName
,
1552 AsyncStageFn stage2
, // stage2 is non real time
1553 AsyncStageFn stage3
, // stage3 is real time - completion msg performed if stage3 returns true
1554 AsyncStageFn stage4
, // stage4 is non real time - sends done if stage4 returns true
1555 AsyncFreeFn cleanup
,
1556 int completionMsgSize
,
1557 void* completionMsgData
1560 void* space
= World_Alloc(inWorld
, sizeof(AsyncPlugInCmd
));
1561 AsyncPlugInCmd
*cmd
= new (space
) AsyncPlugInCmd(inWorld
, (ReplyAddress
*)replyAddr
,
1562 cmdName
, cmdData
, stage2
, stage3
, stage4
, cleanup
,
1563 completionMsgSize
, completionMsgData
);
1564 if (!cmd
) return kSCErr_Failed
;
1565 if (inWorld
->mRealTime
) cmd
->CallNextStage();
1566 else cmd
->CallEveryStage();
1570 ///////////////////////////////////////////////////////////////////////////
1572 AsyncPlugInCmd::AsyncPlugInCmd(World
*inWorld
, ReplyAddress
*inReplyAddress
,
1573 const char* cmdName
,
1575 AsyncStageFn stage2
, // stage2 is non real time
1576 AsyncStageFn stage3
, // stage3 is real time - completion msg performed if stage3 returns true
1577 AsyncStageFn stage4
, // stage4 is non real time - sends done if stage4 returns true
1578 AsyncFreeFn cleanup
, // cleanup is called in real time
1579 int completionMsgSize
,
1580 void* completionMsgData
)
1581 : SC_SequencedCommand(inWorld
, inReplyAddress
),
1582 mCmdName(cmdName
), mCmdData(cmdData
),
1583 mStage2(stage2
), mStage3(stage3
), mStage4(stage4
),
1586 if (completionMsgSize
&& completionMsgData
) {
1587 mMsgSize
= completionMsgSize
;
1588 mMsgData
= (char*)World_Alloc(mWorld
, mMsgSize
);
1589 memcpy(mMsgData
, completionMsgData
, mMsgSize
);
1593 AsyncPlugInCmd::~AsyncPlugInCmd()
1595 (mCleanup
)(mWorld
, mCmdData
);
1596 if (mMsgData
) World_Free(mWorld
, mMsgData
);
1599 void AsyncPlugInCmd::CallDestructor()
1601 this->~AsyncPlugInCmd();
1604 bool AsyncPlugInCmd::Stage2()
1606 bool result
= !mStage2
|| (mStage2
)(mWorld
, mCmdData
);
1610 bool AsyncPlugInCmd::Stage3()
1612 bool result
= !mStage3
|| (mStage3
)(mWorld
, mCmdData
);
1613 if (result
) SEND_COMPLETION_MSG
;
1617 void AsyncPlugInCmd::Stage4()
1619 bool result
= !mStage4
|| (mStage4
)(mWorld
, mCmdData
);
1620 if (result
&& mCmdName
&& mReplyAddress
.mReplyFunc
!= null_reply_func
) SendDone((char*)mCmdName
);