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"
33 #define GET_COMPLETION_MSG(msg) \
34 mMsgSize = msg.getbsize(); \
36 mMsgData = (char*)World_Alloc(mWorld, mMsgSize); \
37 msg.getb(mMsgData, mMsgSize); \
40 void PerformCompletionMsg(World
*inWorld
, OSC_Packet
*inPacket
);
42 #define SEND_COMPLETION_MSG \
45 packet.mData = mMsgData; \
46 packet.mSize = mMsgSize; \
47 packet.mReplyAddr = mReplyAddress; \
48 PacketStatus status = PerformCompletionMsg(mWorld, packet); \
49 if (status == PacketScheduled) { \
55 void SndBuf_Init(SndBuf
*buf
);
56 void SndBuf_Init(SndBuf
*buf
)
68 char* allocAndRestrictPath(World
*mWorld
, const char* inPath
, const char* restrictBase
);
69 char* allocAndRestrictPath(World
*mWorld
, const char* inPath
, const char* restrictBase
){
70 char strbuf
[PATH_MAX
];
72 int remain
= PATH_MAX
;
74 // Ensure begins with the base
75 if(strncmp(inPath
, restrictBase
, strlen(restrictBase
)) != 0){
76 strcpy(strbuf
, restrictBase
);
77 offset
= strlen(restrictBase
);
79 if(inPath
[0]!='/' && strbuf
[strlen(strbuf
)-1]!='/'){
86 // Now copy string, but discard any ".." (which could be benign, but easy to abuse)
87 SC_StringParser
sp(inPath
, '/');
90 const char *token
= const_cast<char *>(sp
.NextToken());
91 tokenlen
= strlen(token
);
92 // now add the new token, then a slash, as long as token is neither dodgy nor overflows
93 if(strcmp(token
, "..")!=0 && remain
> tokenlen
){
94 strcpy(strbuf
+offset
, token
);
105 // Now we can make a long-term home for the string and return it
106 char* saferPath
= (char*)World_Alloc(mWorld
, strlen(strbuf
)+1);
107 strcpy(saferPath
, strbuf
);
111 SC_SequencedCommand::SC_SequencedCommand(World
*inWorld
, ReplyAddress
*inReplyAddress
)
112 : mNextStage(1), mWorld(inWorld
),
113 mMsgSize(0), mMsgData(0)
115 if (inReplyAddress
) mReplyAddress
= *inReplyAddress
;
116 else mReplyAddress
.mReplyFunc
= null_reply_func
;
119 SC_SequencedCommand::~SC_SequencedCommand()
121 if (mMsgData
) World_Free(mWorld
, mMsgData
);
124 int SC_SequencedCommand::Init(char* /*inData*/, int /*inSize*/)
129 void SC_SequencedCommand::SendDone(const char *inCommandName
)
131 ::SendDone(&mReplyAddress
, inCommandName
);
134 void SC_SequencedCommand::SendDoneWithIntValue(const char *inCommandName
, int value
)
136 ::SendDoneWithIntValue(&mReplyAddress
, inCommandName
, value
);
139 void SC_SequencedCommand::CallEveryStage()
141 switch (mNextStage
) {
142 case 1 : if (!Stage1()) break; mNextStage
++;
143 case 2 : if (!Stage2()) break; mNextStage
++;
144 case 3 : if (!Stage3()) break; mNextStage
++;
145 case 4 : Stage4(); break;
150 void DoSequencedCommand(FifoMsg
*inMsg
);
151 void DoSequencedCommand(FifoMsg
*inMsg
)
153 SC_SequencedCommand
*cmd
= (SC_SequencedCommand
*)inMsg
->mData
;
154 cmd
->CallNextStage();
157 void FreeSequencedCommand(FifoMsg
*inMsg
);
158 void FreeSequencedCommand(FifoMsg
*inMsg
)
160 SC_SequencedCommand
*cmd
= (SC_SequencedCommand
*)inMsg
->mData
;
165 void SC_SequencedCommand::CallNextStage()
167 bool sendAgain
= false;
170 int isRealTime
= mNextStage
& 1;
171 switch (mNextStage
) {
173 sendAgain
= Stage1(); // RT
176 sendAgain
= Stage2(); // NRT
179 sendAgain
= Stage3(); // RT
186 SC_AudioDriver
*driver
= AudioDriver(mWorld
);
188 msg
.Set(mWorld
, DoSequencedCommand
, 0, (void*)this);
189 // send this to next time.
192 driver
->SendMsgFromEngine(msg
);
195 driver
->SendMsgToEngine(msg
);
201 // can only be freed from RT.
202 msg
.Set(mWorld
, FreeSequencedCommand
, 0, (void*)this);
203 driver
->SendMsgToEngine(msg
);
208 void SC_SequencedCommand::Delete()
211 World_Free(mWorld
, this);
214 bool SC_SequencedCommand::Stage1()
219 bool SC_SequencedCommand::Stage2()
224 bool SC_SequencedCommand::Stage3()
229 void SC_SequencedCommand::Stage4()
234 ///////////////////////////////////////////////////////////////////////////
236 #include "sc_msg_iter.h"
239 SyncCmd::SyncCmd(World
*inWorld
, ReplyAddress
*inReplyAddress
)
240 : SC_SequencedCommand(inWorld
, inReplyAddress
)
244 int SyncCmd::Init(char *inData
, int inSize
)
246 sc_msg_iter
msg(inSize
, inData
);
251 void SyncCmd::CallDestructor()
256 bool SyncCmd::Stage2()
261 bool SyncCmd::Stage3()
266 void SyncCmd::Stage4()
268 small_scpacket packet
;
269 packet
.adds("/synced");
276 SendReply(&mReplyAddress
, packet
.data(), packet
.size());
279 ///////////////////////////////////////////////////////////////////////////
281 BufAllocCmd::BufAllocCmd(World
*inWorld
, ReplyAddress
*inReplyAddress
)
282 : SC_SequencedCommand(inWorld
, inReplyAddress
)
286 int BufAllocCmd::Init(char *inData
, int inSize
)
288 sc_msg_iter
msg(inSize
, inData
);
289 mBufIndex
= msg
.geti();
290 mNumFrames
= msg
.geti();
291 mNumChannels
= msg
.geti(1);
293 GET_COMPLETION_MSG(msg
);
298 void BufAllocCmd::CallDestructor()
300 this->~BufAllocCmd();
303 bool BufAllocCmd::Stage2()
305 SndBuf
*buf
= World_GetNRTBuf(mWorld
, mBufIndex
);
306 mFreeData
= buf
->data
;
307 bufAlloc(buf
, mNumChannels
, mNumFrames
, mWorld
->mFullRate
.mSampleRate
);
312 bool BufAllocCmd::Stage3()
314 SndBuf
* buf
= World_GetBuf(mWorld
, mBufIndex
);
316 mWorld
->mSndBufUpdates
[mBufIndex
].writes
++ ;
321 void BufAllocCmd::Stage4()
324 SendDoneWithIntValue("/b_alloc", mBufIndex
);
327 ///////////////////////////////////////////////////////////////////////////
329 #include "sc_msg_iter.h"
332 BufGenCmd::BufGenCmd(World
*inWorld
, ReplyAddress
*inReplyAddress
)
333 : SC_SequencedCommand(inWorld
, inReplyAddress
),
338 BufGenCmd::~BufGenCmd()
340 World_Free(mWorld
, mData
);
343 int BufGenCmd::Init(char *inData
, int inSize
)
346 mData
= (char*)World_Alloc(mWorld
, mSize
);
347 memcpy(mData
, inData
, mSize
);
349 sc_msg_iter
msg(mSize
, mData
);
350 mBufIndex
= msg
.geti();
352 int32
*genName
= msg
.gets4();
353 if (!genName
) return kSCErr_WrongArgType
;
355 mBufGen
= GetBufGen(genName
);
356 if (!mBufGen
) return kSCErr_BufGenNotFound
;
363 void BufGenCmd::CallDestructor()
368 bool BufGenCmd::Stage2()
370 SndBuf
*buf
= World_GetNRTBuf(mWorld
, mBufIndex
);
372 mFreeData
= buf
->data
;
373 (*mBufGen
->mBufGenFunc
)(mWorld
, buf
, &mMsg
);
374 if (buf
->data
== mFreeData
) mFreeData
= NULL
;
379 bool BufGenCmd::Stage3()
381 SndBuf
* buf
= World_GetBuf(mWorld
, mBufIndex
);
383 mWorld
->mSndBufUpdates
[mBufIndex
].writes
++ ;
387 void BufGenCmd::Stage4()
390 SendDoneWithIntValue("/b_gen", mBufIndex
);
394 ///////////////////////////////////////////////////////////////////////////
397 ///////////////////////////////////////////////////////////////////////////
400 BufFreeCmd::BufFreeCmd(World
*inWorld
, ReplyAddress
*inReplyAddress
)
401 : SC_SequencedCommand(inWorld
, inReplyAddress
)
405 int BufFreeCmd::Init(char *inData
, int inSize
)
407 sc_msg_iter
msg(inSize
, inData
);
408 mBufIndex
= msg
.geti();
410 GET_COMPLETION_MSG(msg
);
415 void BufFreeCmd::CallDestructor()
420 bool BufFreeCmd::Stage2()
422 SndBuf
*buf
= World_GetNRTBuf(mWorld
, mBufIndex
);
423 mFreeData
= buf
->data
;
424 #ifndef NO_LIBSNDFILE
425 if (buf
->sndfile
) sf_close(buf
->sndfile
);
431 bool BufFreeCmd::Stage3()
433 SndBuf
*buf
= World_GetBuf(mWorld
, mBufIndex
);
436 mWorld
->mSndBufUpdates
[mBufIndex
].writes
++ ;
442 void BufFreeCmd::Stage4()
445 SendDoneWithIntValue("/b_free", mBufIndex
);
448 ///////////////////////////////////////////////////////////////////////////
450 BufZeroCmd::BufZeroCmd(World
*inWorld
, ReplyAddress
*inReplyAddress
)
451 : SC_SequencedCommand(inWorld
, inReplyAddress
)
455 int BufZeroCmd::Init(char *inData
, int inSize
)
457 sc_msg_iter
msg(inSize
, inData
);
458 mBufIndex
= msg
.geti();
460 GET_COMPLETION_MSG(msg
);
465 void BufZeroCmd::CallDestructor()
470 bool BufZeroCmd::Stage2()
472 SndBuf
*buf
= World_GetNRTBuf(mWorld
, mBufIndex
);
473 memset(buf
->data
, 0, buf
->samples
* sizeof(float));
477 bool BufZeroCmd::Stage3()
479 mWorld
->mSndBufUpdates
[mBufIndex
].writes
++ ;
484 void BufZeroCmd::Stage4()
486 SendDoneWithIntValue("/b_zero", mBufIndex
);
489 ///////////////////////////////////////////////////////////////////////////
491 BufAllocReadCmd::BufAllocReadCmd(World
*inWorld
, ReplyAddress
*inReplyAddress
)
492 : SC_SequencedCommand(inWorld
, inReplyAddress
), mFreeData(0), mFilename(0)
496 int BufAllocReadCmd::Init(char *inData
, int inSize
)
498 sc_msg_iter
msg(inSize
, inData
);
499 mBufIndex
= msg
.geti();
501 const char *filename
= msg
.gets();
502 if (!filename
) return kSCErr_WrongArgType
;
504 if(mWorld
->mRestrictedPath
){
505 mFilename
= allocAndRestrictPath(mWorld
, filename
, mWorld
->mRestrictedPath
);
507 mFilename
= (char*)World_Alloc(mWorld
, strlen(filename
)+1);
508 strcpy(mFilename
, filename
);
511 mFileOffset
= msg
.geti();
512 mNumFrames
= msg
.geti();
514 GET_COMPLETION_MSG(msg
);
519 BufAllocReadCmd::~BufAllocReadCmd()
521 World_Free(mWorld
, mFilename
);
524 void BufAllocReadCmd::CallDestructor()
526 this->~BufAllocReadCmd();
529 bool BufAllocReadCmd::Stage2()
532 SendFailure(&mReplyAddress
, "/b_allocRead", "scsynth compiled without libsndfile\n");
533 scprintf("scsynth compiled without libsndfile\n");
536 SndBuf
*buf
= World_GetNRTBuf(mWorld
, mBufIndex
);
538 memset(&fileinfo
, 0, sizeof(fileinfo
));
539 SNDFILE
* sf
= sf_open(mFilename
, SFM_READ
, &fileinfo
);
542 sprintf(str
, "File '%s' could not be opened: %s\n", mFilename
, sf_strerror(NULL
));
543 SendFailureWithBufnum(&mReplyAddress
, "/b_allocRead", str
, mBufIndex
); //SendFailure(&mReplyAddress, "/b_allocRead", str);
547 if (mFileOffset
< 0) mFileOffset
= 0;
548 else if (mFileOffset
> fileinfo
.frames
) mFileOffset
= fileinfo
.frames
;
549 if (mNumFrames
<= 0 || mNumFrames
+ mFileOffset
> fileinfo
.frames
) mNumFrames
= fileinfo
.frames
- mFileOffset
;
552 mFreeData
= buf
->data
;
553 SCErr err
= bufAlloc(buf
, fileinfo
.channels
, mNumFrames
, fileinfo
.samplerate
);
556 sf_seek(sf
, mFileOffset
, SEEK_SET
);
557 sf_readf_float(sf
, buf
->data
, mNumFrames
);
567 bool BufAllocReadCmd::Stage3()
569 SndBuf
* buf
= World_GetBuf(mWorld
, mBufIndex
);
571 mWorld
->mSndBufUpdates
[mBufIndex
].writes
++ ;
577 void BufAllocReadCmd::Stage4()
580 SendDoneWithIntValue("/b_allocRead", mBufIndex
);
583 ///////////////////////////////////////////////////////////////////////////
585 BufReadCmd::BufReadCmd(World
*inWorld
, ReplyAddress
*inReplyAddress
)
586 : SC_SequencedCommand(inWorld
, inReplyAddress
),
591 int BufReadCmd::Init(char *inData
, int inSize
)
593 sc_msg_iter
msg(inSize
, inData
);
594 mBufIndex
= msg
.geti();
596 const char *filename
= msg
.gets();
597 if (!filename
) return kSCErr_WrongArgType
;
599 if(mWorld
->mRestrictedPath
){
600 mFilename
= allocAndRestrictPath(mWorld
, filename
, mWorld
->mRestrictedPath
);
602 mFilename
= (char*)World_Alloc(mWorld
, strlen(filename
)+1);
603 strcpy(mFilename
, filename
);
606 mFileOffset
= msg
.geti();
607 mNumFrames
= msg
.geti(-1);
608 mBufOffset
= msg
.geti();
609 mLeaveFileOpen
= msg
.geti();
611 GET_COMPLETION_MSG(msg
);
616 BufReadCmd::~BufReadCmd()
618 World_Free(mWorld
, mFilename
);
621 void BufReadCmd::CallDestructor()
626 bool BufReadCmd::Stage2()
629 SendFailure(&mReplyAddress
, "/b_read", "scsynth compiled without libsndfile\n");
630 scprintf("scsynth compiled without libsndfile\n");
635 SndBuf
*buf
= World_GetNRTBuf(mWorld
, mBufIndex
);
636 int framesToEnd
= buf
->frames
- mBufOffset
;
637 if (framesToEnd
<= 0) return true;
639 SNDFILE
* sf
= sf_open(mFilename
, SFM_READ
, &fileinfo
);
642 sprintf(str
, "File '%s' could not be opened: %s\n", mFilename
, sf_strerror(NULL
));
643 SendFailureWithBufnum(&mReplyAddress
, "/b_read", str
, mBufIndex
); //SendFailure(&mReplyAddress, "/b_read", str);
647 if (fileinfo
.channels
!= buf
->channels
) {
650 sprintf(str
, "Channel mismatch. File '%s' has %d channels. Buffer has %d channels.\n", mFilename
, fileinfo
.channels
, buf
->channels
);
651 SendFailureWithBufnum(&mReplyAddress
, "/b_read", str
, mBufIndex
); //SendFailure(&mReplyAddress, "/b_read", str);
656 if (mFileOffset
< 0) mFileOffset
= 0;
657 else if (mFileOffset
> fileinfo
.frames
) mFileOffset
= fileinfo
.frames
;
658 if (mNumFrames
< 0 || mNumFrames
+ mFileOffset
> fileinfo
.frames
) mNumFrames
= fileinfo
.frames
- mFileOffset
;
660 if (mNumFrames
> framesToEnd
) mNumFrames
= framesToEnd
;
662 sf_seek(sf
, mFileOffset
, SEEK_SET
);
663 if (mNumFrames
> 0) {
664 sf_readf_float(sf
, buf
->data
+ (mBufOffset
* buf
->channels
), mNumFrames
);
668 sf_close(buf
->sndfile
);
670 if (mLeaveFileOpen
) {
677 mSampleRate
= (double)fileinfo
.samplerate
;
683 bool BufReadCmd::Stage3()
685 SndBuf
* buf
= World_GetBuf(mWorld
, mBufIndex
);
686 buf
->samplerate
= mSampleRate
;
687 if (mLeaveFileOpen
) buf
->mask
= buf
->mask1
= -1;
689 mWorld
->mSndBufUpdates
[mBufIndex
].writes
++ ;
694 void BufReadCmd::Stage4()
696 SendDoneWithIntValue("/b_read", mBufIndex
);
699 ///////////////////////////////////////////////////////////////////////////
701 SC_BufReadCommand::SC_BufReadCommand(World
* inWorld
, ReplyAddress
* inReplyAddress
)
702 : SC_SequencedCommand(inWorld
, inReplyAddress
),
707 SC_BufReadCommand::~SC_BufReadCommand()
711 void SC_BufReadCommand::InitChannels(sc_msg_iter
& msg
)
714 while (msg
.nextTag(0) == 'i') {
716 if (mNumChannels
<= kMaxNumChannels
) {
717 mChannels
[mNumChannels
++] = c
;
722 bool SC_BufReadCommand::CheckChannels(int inNumChannels
)
724 for (int i
=0; i
< mNumChannels
; ++i
) {
725 if (mChannels
[i
] >= inNumChannels
) {
732 void SC_BufReadCommand::CopyChannels(float* dst
, float* src
, size_t srcChannels
, size_t numFrames
)
734 for (int ci
=0; ci
< mNumChannels
; ++ci
) {
735 int c
= mChannels
[ci
];
736 if (c
>= 0 && c
< srcChannels
) {
737 for (size_t fi
=0; fi
< numFrames
; ++fi
) {
738 dst
[fi
*mNumChannels
+ci
] = src
[fi
*srcChannels
+c
];
741 for (size_t fi
=0; fi
< numFrames
; ++fi
) {
742 dst
[fi
*mNumChannels
+ci
] = 0.f
;
748 ///////////////////////////////////////////////////////////////////////////
750 BufAllocReadChannelCmd::BufAllocReadChannelCmd(World
*inWorld
, ReplyAddress
*inReplyAddress
)
751 : SC_BufReadCommand(inWorld
, inReplyAddress
),
752 mFreeData(0), mFilename(0)
756 int BufAllocReadChannelCmd::Init(char *inData
, int inSize
)
758 sc_msg_iter
msg(inSize
, inData
);
759 mBufIndex
= msg
.geti();
761 const char *filename
= msg
.gets();
762 if (!filename
) return kSCErr_WrongArgType
;
764 if(mWorld
->mRestrictedPath
){
765 mFilename
= allocAndRestrictPath(mWorld
, filename
, mWorld
->mRestrictedPath
);
767 mFilename
= (char*)World_Alloc(mWorld
, strlen(filename
)+1);
768 strcpy(mFilename
, filename
);
771 mFileOffset
= msg
.geti();
772 mNumFrames
= msg
.geti();
776 GET_COMPLETION_MSG(msg
);
781 BufAllocReadChannelCmd::~BufAllocReadChannelCmd()
783 World_Free(mWorld
, mFilename
);
786 void BufAllocReadChannelCmd::CallDestructor()
788 this->~BufAllocReadChannelCmd();
791 bool BufAllocReadChannelCmd::Stage2()
794 SendFailure(&mReplyAddress
, "/b_allocReadChannel", "scsynth compiled without libsndfile\n");
795 scprintf("scsynth compiled without libsndfile\n");
798 SndBuf
*buf
= World_GetNRTBuf(mWorld
, mBufIndex
);
801 memset(&fileinfo
, 0, sizeof(fileinfo
));
802 SNDFILE
* sf
= sf_open(mFilename
, SFM_READ
, &fileinfo
);
805 sprintf(str
, "File '%s' could not be opened: %s\n", mFilename
, sf_strerror(NULL
));
806 SendFailureWithBufnum(&mReplyAddress
, "/b_allocReadChannel", str
, mBufIndex
); //SendFailure(&mReplyAddress, "/b_allocRead", str);
810 if (mFileOffset
< 0) mFileOffset
= 0;
811 else if (mFileOffset
> fileinfo
.frames
) mFileOffset
= fileinfo
.frames
;
812 if (mNumFrames
<= 0 || mNumFrames
+ mFileOffset
> fileinfo
.frames
) mNumFrames
= fileinfo
.frames
- mFileOffset
;
814 if (mNumChannels
== 0) {
816 mFreeData
= buf
->data
;
817 SCErr err
= bufAlloc(buf
, fileinfo
.channels
, mNumFrames
, fileinfo
.samplerate
);
820 sf_seek(sf
, mFileOffset
, SEEK_SET
);
821 sf_readf_float(sf
, buf
->data
, mNumFrames
);
823 // verify channel indexes
824 if (!CheckChannels(fileinfo
.channels
)) {
825 const char* str
= "Channel index out of range.\n";
826 SendFailureWithBufnum(&mReplyAddress
, "/b_allocReadChannel", str
, mBufIndex
); //SendFailure(&mReplyAddress, "/b_allocRead", str);
832 mFreeData
= buf
->data
;
833 SCErr err
= bufAlloc(buf
, mNumChannels
, mNumFrames
, fileinfo
.samplerate
);
836 float* data
= (float*)malloc(mNumFrames
*fileinfo
.channels
*sizeof(float));
837 if (data
== 0) goto leave
;
838 // read some channels
839 sf_seek(sf
, mFileOffset
, SEEK_SET
);
840 sf_readf_float(sf
, data
, mNumFrames
);
841 CopyChannels(buf
->data
, data
, fileinfo
.channels
, mNumFrames
);
854 bool BufAllocReadChannelCmd::Stage3()
856 SndBuf
* buf
= World_GetBuf(mWorld
, mBufIndex
);
858 mWorld
->mSndBufUpdates
[mBufIndex
].writes
++ ;
864 void BufAllocReadChannelCmd::Stage4()
867 SendDoneWithIntValue("/b_allocReadChannel", mBufIndex
);
870 ///////////////////////////////////////////////////////////////////////////
872 BufReadChannelCmd::BufReadChannelCmd(World
*inWorld
, ReplyAddress
*inReplyAddress
)
873 : SC_BufReadCommand(inWorld
, inReplyAddress
),
878 int BufReadChannelCmd::Init(char *inData
, int inSize
)
880 sc_msg_iter
msg(inSize
, inData
);
881 mBufIndex
= msg
.geti();
883 const char *filename
= msg
.gets();
884 if (!filename
) return kSCErr_WrongArgType
;
886 if(mWorld
->mRestrictedPath
){
887 mFilename
= allocAndRestrictPath(mWorld
, filename
, mWorld
->mRestrictedPath
);
889 mFilename
= (char*)World_Alloc(mWorld
, strlen(filename
)+1);
890 strcpy(mFilename
, filename
);
893 mFileOffset
= msg
.geti();
894 mNumFrames
= msg
.geti(-1);
895 mBufOffset
= msg
.geti();
896 mLeaveFileOpen
= msg
.geti();
900 GET_COMPLETION_MSG(msg
);
905 BufReadChannelCmd::~BufReadChannelCmd()
907 World_Free(mWorld
, mFilename
);
910 void BufReadChannelCmd::CallDestructor()
912 this->~BufReadChannelCmd();
915 bool BufReadChannelCmd::Stage2()
918 SendFailure(&mReplyAddress
, "/b_readChannel", "scsynth compiled without libsndfile\n");
919 scprintf("scsynth compiled without libsndfile\n");
924 SndBuf
*buf
= World_GetNRTBuf(mWorld
, mBufIndex
);
925 int framesToEnd
= buf
->frames
- mBufOffset
;
926 if (framesToEnd
<= 0) return true;
928 SNDFILE
* sf
= sf_open(mFilename
, SFM_READ
, &fileinfo
);
931 sprintf(str
, "File '%s' could not be opened: %s\n", mFilename
, sf_strerror(NULL
));
932 SendFailureWithBufnum(&mReplyAddress
, "/b_readChannel", str
, mBufIndex
); //SendFailure(&mReplyAddress, "/b_read", str);
937 if (mNumChannels
> 0) {
938 // verify channel indexes
939 if (!( CheckChannels(fileinfo
.channels
)) ) { // nescivi: && CheckChannels(buf->channels) (should not check here for buf->channels)
940 const char* str
= "Channel index out of range.\n";
941 SendFailureWithBufnum(&mReplyAddress
, "/b_readChannel", str
, mBufIndex
); //SendFailure(&mReplyAddress, "/b_allocRead", str);
946 // nescivi: this also seems out of place: we want to read from a file with more channels than are in the buffer
947 // } else if (fileinfo.channels != buf->channels) {
950 // sprintf(str, "Channel mismatch. File '%s' has %d channels. Buffer has %d channels.\n",
951 // mFilename, fileinfo.channels, buf->channels);
952 // SendFailure(&mReplyAddress, "/b_read", str);
957 if (mFileOffset
< 0) mFileOffset
= 0;
958 else if (mFileOffset
> fileinfo
.frames
) mFileOffset
= fileinfo
.frames
;
959 if (mNumFrames
< 0 || mNumFrames
+ mFileOffset
> fileinfo
.frames
) mNumFrames
= fileinfo
.frames
- mFileOffset
;
960 if (mNumFrames
> framesToEnd
) mNumFrames
= framesToEnd
;
962 sf_seek(sf
, mFileOffset
, SEEK_SET
);
963 if (mNumFrames
> 0) {
964 if (mNumChannels
== 0) {
966 sf_readf_float(sf
, buf
->data
+ (mBufOffset
* buf
->channels
), mNumFrames
);
969 float* data
= (float*)malloc(mNumFrames
*fileinfo
.channels
*sizeof(float));
970 if (data
== 0) goto leave
;
971 // read some channels
972 sf_seek(sf
, mFileOffset
, SEEK_SET
);
973 sf_readf_float(sf
, data
, mNumFrames
);
974 CopyChannels(buf
->data
+ (mBufOffset
* mNumChannels
), data
, fileinfo
.channels
, mNumFrames
);
982 sf_close(buf
->sndfile
);
984 if (mLeaveFileOpen
) {
991 mSampleRate
= (double)fileinfo
.samplerate
;
997 bool BufReadChannelCmd::Stage3()
999 SndBuf
* buf
= World_GetBuf(mWorld
, mBufIndex
);
1000 buf
->samplerate
= mSampleRate
;
1001 if (mLeaveFileOpen
) buf
->mask
= buf
->mask1
= -1;
1003 mWorld
->mSndBufUpdates
[mBufIndex
].writes
++ ;
1004 SEND_COMPLETION_MSG
;
1008 void BufReadChannelCmd::Stage4()
1010 SendDoneWithIntValue("/b_readChannel", mBufIndex
);
1013 ///////////////////////////////////////////////////////////////////////////
1015 BufWriteCmd::BufWriteCmd(World
*inWorld
, ReplyAddress
*inReplyAddress
)
1016 : SC_SequencedCommand(inWorld
, inReplyAddress
), mFilename(0)
1020 #ifdef NO_LIBSNDFILE
1024 int BufWriteCmd::Init(char *inData
, int inSize
)
1026 #ifdef NO_LIBSNDFILE
1027 SendFailure(&mReplyAddress
, "/b_write", "scsynth compiled without libsndfile\n");
1028 scprintf("scsynth compiled without libsndfile\n");
1031 sc_msg_iter
msg(inSize
, inData
);
1032 mBufIndex
= msg
.geti();
1034 const char *filename
= msg
.gets();
1035 if (!filename
) return kSCErr_WrongArgType
;
1037 if(mWorld
->mRestrictedPath
){
1038 mFilename
= allocAndRestrictPath(mWorld
, filename
, mWorld
->mRestrictedPath
);
1040 mFilename
= (char*)World_Alloc(mWorld
, strlen(filename
)+1);
1041 strcpy(mFilename
, filename
);
1044 const char *headerFormatString
= msg
.gets("aiff");
1045 const char *sampleFormatString
= msg
.gets("int16");
1047 mNumFrames
= msg
.geti(-1);
1048 mBufOffset
= msg
.geti();
1049 mLeaveFileOpen
= msg
.geti();
1051 GET_COMPLETION_MSG(msg
);
1053 memset(&mFileInfo
, 0, sizeof(mFileInfo
));
1054 return sndfileFormatInfoFromStrings(&mFileInfo
, headerFormatString
, sampleFormatString
);
1058 BufWriteCmd::~BufWriteCmd()
1060 World_Free(mWorld
, mFilename
);
1063 void BufWriteCmd::CallDestructor()
1065 this->~BufWriteCmd();
1068 bool BufWriteCmd::Stage2()
1070 #ifdef NO_LIBSNDFILE
1073 SndBuf
*buf
= World_GetNRTBuf(mWorld
, mBufIndex
);
1074 int framesToEnd
= buf
->frames
- mBufOffset
;
1075 if (framesToEnd
< 0) framesToEnd
= 0;
1076 mFileInfo
.samplerate
= (int)buf
->samplerate
;
1077 mFileInfo
.channels
= buf
->channels
;
1079 SNDFILE
* sf
= sf_open(mFilename
, SFM_WRITE
, &mFileInfo
);
1082 sprintf(str
, "File '%s' could not be opened: %s\n", mFilename
, sf_strerror(NULL
));
1083 SendFailureWithBufnum(&mReplyAddress
, "/b_write", str
, mBufIndex
); //SendFailure(&mReplyAddress, "/b_write", str);
1088 if (mNumFrames
< 0 || mNumFrames
> buf
->frames
) mNumFrames
= buf
->frames
;
1090 if (mNumFrames
> framesToEnd
) mNumFrames
= framesToEnd
;
1092 if (mNumFrames
> 0) {
1093 sf_writef_float(sf
, buf
->data
+ (mBufOffset
* buf
->channels
), mNumFrames
);
1097 sf_close(buf
->sndfile
);
1099 if (mLeaveFileOpen
) {
1110 bool BufWriteCmd::Stage3()
1112 SEND_COMPLETION_MSG
;
1116 void BufWriteCmd::Stage4()
1118 SendDoneWithIntValue("/b_write", mBufIndex
);
1121 ///////////////////////////////////////////////////////////////////////////
1123 BufCloseCmd::BufCloseCmd(World
*inWorld
, ReplyAddress
*inReplyAddress
)
1124 : SC_SequencedCommand(inWorld
, inReplyAddress
)
1128 int BufCloseCmd::Init(char *inData
, int inSize
)
1130 sc_msg_iter
msg(inSize
, inData
);
1131 mBufIndex
= msg
.geti();
1133 GET_COMPLETION_MSG(msg
);
1138 void BufCloseCmd::CallDestructor()
1140 this->~BufCloseCmd();
1143 bool BufCloseCmd::Stage2()
1145 #ifdef NO_LIBSNDFILE
1146 SendFailure(&mReplyAddress
, "/b_close", "scsynth compiled without libsndfile\n");
1147 scprintf("scsynth compiled without libsndfile\n");
1150 SndBuf
*buf
= World_GetNRTBuf(mWorld
, mBufIndex
);
1152 sf_close(buf
->sndfile
);
1159 bool BufCloseCmd::Stage3()
1161 SEND_COMPLETION_MSG
;
1165 void BufCloseCmd::Stage4()
1167 SendDoneWithIntValue("/b_close", mBufIndex
);
1170 ///////////////////////////////////////////////////////////////////////////
1172 AudioQuitCmd::AudioQuitCmd(World
*inWorld
, ReplyAddress
*inReplyAddress
)
1173 : SC_SequencedCommand(inWorld
, inReplyAddress
)
1177 void AudioQuitCmd::CallDestructor()
1179 this->~AudioQuitCmd();
1182 bool AudioQuitCmd::Stage2()
1184 mWorld
->hw
->mTerminating
= true;
1188 bool AudioQuitCmd::Stage3()
1190 #if SC_AUDIO_API == SC_AUDIO_API_AUDIOUNITS
1191 SendFailure(&mReplyAddress
, "/quit", "not allowed in AU host\n");
1192 scprintf("/quit : quit not allowed in AU host\n");
1195 if (mWorld
->hw
->mShmem
)
1196 mWorld
->hw
->mShmem
->disconnect();
1201 void AudioQuitCmd::Stage4()
1204 mWorld
->hw
->mQuitProgram
->Release();
1207 ///////////////////////////////////////////////////////////////////////////
1209 AudioStatusCmd::AudioStatusCmd(World
*inWorld
, ReplyAddress
*inReplyAddress
)
1210 : SC_SequencedCommand(inWorld
, inReplyAddress
)
1214 void AudioStatusCmd::CallDestructor()
1216 this->~AudioStatusCmd();
1219 bool AudioStatusCmd::Stage2()
1221 // we stop replying to status requests after receiving /quit
1222 if (mWorld
->hw
->mTerminating
== true)
1225 small_scpacket packet
;
1226 packet
.adds("/status.reply");
1227 packet
.maketags(10);
1239 packet
.addi(1); // audio is always active now.
1240 packet
.addi(mWorld
->mNumUnits
);
1241 packet
.addi(mWorld
->mNumGraphs
);
1242 packet
.addi(mWorld
->mNumGroups
);
1243 packet
.addi(mWorld
->hw
->mGraphDefLib
->NumItems());
1245 SC_AudioDriver
*driver
= mWorld
->hw
->mAudioDriver
;
1246 packet
.addf(driver
->GetAvgCPU());
1247 packet
.addf(driver
->GetPeakCPU());
1248 packet
.addd(driver
->GetSampleRate());
1249 packet
.addd(driver
->GetActualSampleRate());
1251 SendReply(&mReplyAddress
, packet
.data(), packet
.size());
1256 ///////////////////////////////////////////////////////////////////////////
1258 NotifyCmd::NotifyCmd(World
*inWorld
, ReplyAddress
*inReplyAddress
)
1259 : SC_SequencedCommand(inWorld
, inReplyAddress
)
1263 int NotifyCmd::Init(char *inData
, int inSize
)
1265 sc_msg_iter
msg(inSize
, inData
);
1266 mOnOff
= msg
.geti();
1271 void NotifyCmd::CallDestructor()
1276 bool NotifyCmd::Stage2()
1278 HiddenWorld
*hw
= mWorld
->hw
;
1281 for (uint32 i
=0; i
<hw
->mNumUsers
; ++i
) {
1282 if (mReplyAddress
== hw
->mUsers
[i
]) {
1283 // already in table - don't fail though..
1284 SendFailure(&mReplyAddress
, "/notify", "notify: already registered\n");
1285 scprintf("/notify : already registered\n");
1290 // add reply address to user table
1291 if (hw
->mNumUsers
>= hw
->mMaxUsers
) {
1292 SendFailure(&mReplyAddress
, "/notify", "too many users\n");
1293 scprintf("too many users\n");
1297 hw
->mUsers
[hw
->mNumUsers
++] = mReplyAddress
;
1299 SendDone("/notify");
1301 for (uint32 i
=0; i
<hw
->mNumUsers
; ++i
) {
1302 if (mReplyAddress
== hw
->mUsers
[i
]) {
1304 hw
->mUsers
[i
] = hw
->mUsers
[--hw
->mNumUsers
];
1305 SendDone("/notify");
1310 SendFailure(&mReplyAddress
, "/notify", "not registered\n");
1311 scprintf("not registered\n");
1316 ///////////////////////////////////////////////////////////////////////////
1318 SendFailureCmd::SendFailureCmd(World
*inWorld
, ReplyAddress
*inReplyAddress
)
1319 : SC_SequencedCommand(inWorld
, inReplyAddress
), mCmdName(0), mErrString(0)
1323 SendFailureCmd::~SendFailureCmd()
1325 World_Free(mWorld
, mCmdName
);
1326 World_Free(mWorld
, mErrString
);
1330 void SendFailureCmd::InitSendFailureCmd(const char *inCmdName
, const char* inErrString
)
1332 mCmdName
= (char*)World_Alloc(mWorld
, strlen(inCmdName
)+1);
1333 strcpy(mCmdName
, inCmdName
);
1335 mErrString
= (char*)World_Alloc(mWorld
, strlen(inErrString
)+1);
1336 strcpy(mErrString
, inErrString
);
1339 void SendFailureCmd::CallDestructor()
1341 this->~SendFailureCmd();
1344 bool SendFailureCmd::Stage2()
1346 SendFailure(&mReplyAddress
, mCmdName
, mErrString
);
1350 ///////////////////////////////////////////////////////////////////////////
1352 RecvSynthDefCmd::RecvSynthDefCmd(World
*inWorld
, ReplyAddress
*inReplyAddress
)
1353 : SC_SequencedCommand(inWorld
, inReplyAddress
), mBuffer(0)
1357 int RecvSynthDefCmd::Init(char *inData
, int inSize
)
1359 sc_msg_iter
msg(inSize
, inData
);
1361 int size
= msg
.getbsize();
1362 if (!size
) throw kSCErr_WrongArgType
;
1364 mBuffer
= (char*)World_Alloc(mWorld
, size
);
1365 msg
.getb(mBuffer
, size
);
1367 GET_COMPLETION_MSG(msg
);
1373 RecvSynthDefCmd::~RecvSynthDefCmd()
1375 World_Free(mWorld
, mBuffer
);
1378 void RecvSynthDefCmd::CallDestructor()
1380 this->~RecvSynthDefCmd();
1383 bool RecvSynthDefCmd::Stage2()
1385 mDefs
= GraphDef_Recv(mWorld
, mBuffer
, mDefs
);
1390 bool RecvSynthDefCmd::Stage3()
1392 GraphDef_Define(mWorld
, mDefs
);
1393 SEND_COMPLETION_MSG
;
1397 void RecvSynthDefCmd::Stage4()
1399 SendDone("/d_recv");
1402 ///////////////////////////////////////////////////////////////////////////
1404 LoadSynthDefCmd::LoadSynthDefCmd(World
*inWorld
, ReplyAddress
*inReplyAddress
)
1405 : SC_SequencedCommand(inWorld
, inReplyAddress
), mFilename(0)
1409 int LoadSynthDefCmd::Init(char *inData
, int inSize
)
1411 sc_msg_iter
msg(inSize
, inData
);
1413 const char *filename
= msg
.gets();
1414 if (!filename
) return kSCErr_WrongArgType
;
1416 if(mWorld
->mRestrictedPath
){
1417 mFilename
= allocAndRestrictPath(mWorld
, filename
, mWorld
->mRestrictedPath
);
1419 mFilename
= (char*)World_Alloc(mWorld
, strlen(filename
)+1);
1420 strcpy(mFilename
, filename
);
1423 GET_COMPLETION_MSG(msg
);
1429 LoadSynthDefCmd::~LoadSynthDefCmd()
1431 World_Free(mWorld
, mFilename
);
1434 void LoadSynthDefCmd::CallDestructor()
1436 this->~LoadSynthDefCmd();
1439 bool LoadSynthDefCmd::Stage2()
1441 char* fname
= mFilename
;
1442 mDefs
= GraphDef_LoadGlob(mWorld
, fname
, mDefs
);
1446 bool LoadSynthDefCmd::Stage3()
1448 GraphDef_Define(mWorld
, mDefs
);
1449 SEND_COMPLETION_MSG
;
1453 void LoadSynthDefCmd::Stage4()
1455 SendDone("/d_load");
1458 ///////////////////////////////////////////////////////////////////////////
1460 LoadSynthDefDirCmd::LoadSynthDefDirCmd(World
*inWorld
, ReplyAddress
*inReplyAddress
)
1461 : SC_SequencedCommand(inWorld
, inReplyAddress
), mFilename(0)
1465 int LoadSynthDefDirCmd::Init(char *inData
, int inSize
)
1467 sc_msg_iter
msg(inSize
, inData
);
1469 const char *filename
= msg
.gets();
1470 if (!filename
) return kSCErr_WrongArgType
;
1472 if(mWorld
->mRestrictedPath
){
1473 mFilename
= allocAndRestrictPath(mWorld
, filename
, mWorld
->mRestrictedPath
);
1475 mFilename
= (char*)World_Alloc(mWorld
, strlen(filename
)+1);
1476 strcpy(mFilename
, filename
);
1479 GET_COMPLETION_MSG(msg
);
1485 LoadSynthDefDirCmd::~LoadSynthDefDirCmd()
1487 World_Free(mWorld
, mFilename
);
1490 void LoadSynthDefDirCmd::CallDestructor()
1492 this->~LoadSynthDefDirCmd();
1495 bool LoadSynthDefDirCmd::Stage2()
1497 mDefs
= GraphDef_LoadDir(mWorld
, mFilename
, mDefs
);
1502 bool LoadSynthDefDirCmd::Stage3()
1504 GraphDef_Define(mWorld
, mDefs
);
1505 SEND_COMPLETION_MSG
;
1509 void LoadSynthDefDirCmd::Stage4()
1511 SendDone("/d_loadDir");
1514 ///////////////////////////////////////////////////////////////////////////
1516 SendReplyCmd::SendReplyCmd(World
*inWorld
, ReplyAddress
*inReplyAddress
)
1517 : SC_SequencedCommand(inWorld
, inReplyAddress
)
1521 int SendReplyCmd::Init(char *inData
, int inSize
)
1524 mMsgData
= (char*)World_Alloc(mWorld
, mMsgSize
);
1525 memcpy(mMsgData
, inData
, inSize
);
1529 void SendReplyCmd::CallDestructor()
1531 this->~SendReplyCmd();
1534 bool SendReplyCmd::Stage2()
1536 SendReply(&mReplyAddress
, mMsgData
, mMsgSize
);
1540 ///////////////////////////////////////////////////////////////////////////
1542 int PerformAsynchronousCommand(
1545 const char* cmdName
,
1547 AsyncStageFn stage2
, // stage2 is non real time
1548 AsyncStageFn stage3
, // stage3 is real time - completion msg performed if stage3 returns true
1549 AsyncStageFn stage4
, // stage4 is non real time - sends done if stage4 returns true
1550 AsyncFreeFn cleanup
,
1551 int completionMsgSize
,
1552 void* completionMsgData
1555 void* space
= World_Alloc(inWorld
, sizeof(AsyncPlugInCmd
));
1556 AsyncPlugInCmd
*cmd
= new (space
) AsyncPlugInCmd(inWorld
, (ReplyAddress
*)replyAddr
,
1557 cmdName
, cmdData
, stage2
, stage3
, stage4
, cleanup
,
1558 completionMsgSize
, completionMsgData
);
1559 if (!cmd
) return kSCErr_Failed
;
1560 if (inWorld
->mRealTime
) cmd
->CallNextStage();
1561 else cmd
->CallEveryStage();
1565 ///////////////////////////////////////////////////////////////////////////
1567 AsyncPlugInCmd::AsyncPlugInCmd(World
*inWorld
, ReplyAddress
*inReplyAddress
,
1568 const char* cmdName
,
1570 AsyncStageFn stage2
, // stage2 is non real time
1571 AsyncStageFn stage3
, // stage3 is real time - completion msg performed if stage3 returns true
1572 AsyncStageFn stage4
, // stage4 is non real time - sends done if stage4 returns true
1573 AsyncFreeFn cleanup
, // cleanup is called in real time
1574 int completionMsgSize
,
1575 void* completionMsgData
)
1576 : SC_SequencedCommand(inWorld
, inReplyAddress
),
1577 mCmdName(cmdName
), mCmdData(cmdData
),
1578 mStage2(stage2
), mStage3(stage3
), mStage4(stage4
),
1581 if (completionMsgSize
&& completionMsgData
) {
1582 mMsgSize
= completionMsgSize
;
1583 mMsgData
= (char*)World_Alloc(mWorld
, mMsgSize
);
1584 memcpy(mMsgData
, completionMsgData
, mMsgSize
);
1588 AsyncPlugInCmd::~AsyncPlugInCmd()
1590 (mCleanup
)(mWorld
, mCmdData
);
1591 if (mMsgData
) World_Free(mWorld
, mMsgData
);
1594 void AsyncPlugInCmd::CallDestructor()
1596 this->~AsyncPlugInCmd();
1599 bool AsyncPlugInCmd::Stage2()
1601 bool result
= !mStage2
|| (mStage2
)(mWorld
, mCmdData
);
1605 bool AsyncPlugInCmd::Stage3()
1607 bool result
= !mStage3
|| (mStage3
)(mWorld
, mCmdData
);
1608 if (result
) SEND_COMPLETION_MSG
;
1612 void AsyncPlugInCmd::Stage4()
1614 bool result
= !mStage4
|| (mStage4
)(mWorld
, mCmdData
);
1615 if (result
&& mCmdName
&& mReplyAddress
.mReplyFunc
!= null_reply_func
) SendDone((char*)mCmdName
);