Merge pull request #506 from andrewcsmith/patch-2
[supercollider.git] / server / scsynth / SC_SequencedCommand.cpp
blob47b1e621f8836a9843ddbaabd71eb9a68db8501c
1 /*
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"
28 #include "SC_Sem.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(); \
36 if (mMsgSize) { \
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 \
44 if (mMsgSize) { \
45 OSC_Packet packet; \
46 packet.mData = mMsgData; \
47 packet.mSize = mMsgSize; \
48 packet.mReplyAddr = mReplyAddress; \
49 PacketStatus status = PerformCompletionMsg(mWorld, packet); \
50 if (status == PacketScheduled) { \
51 mMsgSize = 0; \
52 mMsgData = 0; \
53 } \
56 void SndBuf_Init(SndBuf *buf);
57 void SndBuf_Init(SndBuf *buf)
59 buf->data = 0;
60 buf->channels = 0;
61 buf->samples = 0;
62 buf->frames = 0;
63 buf->mask = 0;
64 buf->mask1 = 0;
65 buf->coord = 0;
66 buf->sndfile = 0;
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];
72 int offset = 0;
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);
79 remain -= offset;
80 if(inPath[0]!='/' && strbuf[strlen(strbuf)-1]!='/'){
81 strbuf[offset] = '/';
82 ++offset;
83 --remain;
87 // Now copy string, but discard any ".." (which could be benign, but easy to abuse)
88 SC_StringParser sp(inPath, '/');
89 size_t tokenlen;
90 while (!sp.AtEnd()) {
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);
96 offset += tokenlen;
97 remain -= tokenlen;
98 if(!sp.AtEnd()) {
99 strbuf[offset] = '/';
100 ++offset;
101 --remain;
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);
109 return saferPath;
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*/)
127 return kSCErr_None;
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;
148 Delete();
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;
162 cmd->Delete();
166 void SC_SequencedCommand::CallNextStage()
168 bool sendAgain = false;
169 FifoMsg msg;
171 int isRealTime = mNextStage & 1;
172 switch (mNextStage) {
173 case 1 :
174 sendAgain = Stage1(); // RT
175 break;
176 case 2 :
177 sendAgain = Stage2(); // NRT
178 break;
179 case 3 :
180 sendAgain = Stage3(); // RT
181 break;
182 case 4 :
183 Stage4(); // NRT
184 break;
186 mNextStage++;
187 SC_AudioDriver *driver = AudioDriver(mWorld);
188 if (sendAgain) {
189 msg.Set(mWorld, DoSequencedCommand, 0, (void*)this);
190 // send this to next time.
191 if (isRealTime) {
192 // send to NRT
193 driver->SendMsgFromEngine(msg);
194 } else {
195 // send to RT
196 driver->SendMsgToEngine(msg);
198 } else {
199 if (isRealTime) {
200 Delete();
201 } else {
202 // can only be freed from RT.
203 msg.Set(mWorld, FreeSequencedCommand, 0, (void*)this);
204 driver->SendMsgToEngine(msg);
209 void SC_SequencedCommand::Delete()
211 CallDestructor();
212 World_Free(mWorld, this);
215 bool SC_SequencedCommand::Stage1()
217 return true;
220 bool SC_SequencedCommand::Stage2()
222 return false;
225 bool SC_SequencedCommand::Stage3()
227 return false;
230 void SC_SequencedCommand::Stage4()
235 ///////////////////////////////////////////////////////////////////////////
237 #include "sc_msg_iter.h"
238 #include <string.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);
248 mID = msg.geti();
249 return kSCErr_None;
252 void SyncCmd::CallDestructor()
254 this->~SyncCmd();
257 bool SyncCmd::Stage2()
259 return true;
262 bool SyncCmd::Stage3()
264 return true;
267 void SyncCmd::Stage4()
269 small_scpacket packet;
270 packet.adds("/synced");
271 packet.maketags(2);
272 packet.addtag(',');
273 packet.addtag('i');
275 packet.addi(mID);
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);
296 return kSCErr_None;
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);
309 mSndBuf = *buf;
310 return true;
313 bool BufAllocCmd::Stage3()
315 SndBuf* buf = World_GetBuf(mWorld, mBufIndex);
316 *buf = mSndBuf;
317 mWorld->mSndBufUpdates[mBufIndex].writes ++ ;
318 SEND_COMPLETION_MSG;
319 return true;
322 void BufAllocCmd::Stage4()
324 zfree(mFreeData);
325 SendDoneWithIntValue("/b_alloc", mBufIndex);
328 ///////////////////////////////////////////////////////////////////////////
330 #include "sc_msg_iter.h"
331 #include <string.h>
333 BufGenCmd::BufGenCmd(World *inWorld, ReplyAddress *inReplyAddress)
334 : SC_SequencedCommand(inWorld, inReplyAddress),
335 mData(0)
339 BufGenCmd::~BufGenCmd()
341 World_Free(mWorld, mData);
344 int BufGenCmd::Init(char *inData, int inSize)
346 mSize = 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;
359 mMsg = msg;
361 return kSCErr_None;
364 void BufGenCmd::CallDestructor()
366 this->~BufGenCmd();
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;
376 mSndBuf = *buf;
377 return true;
380 bool BufGenCmd::Stage3()
382 SndBuf* buf = World_GetBuf(mWorld, mBufIndex);
383 *buf = mSndBuf;
384 mWorld->mSndBufUpdates[mBufIndex].writes ++ ;
385 return true;
388 void BufGenCmd::Stage4()
390 zfree(mFreeData);
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);
413 return kSCErr_None;
416 void BufFreeCmd::CallDestructor()
418 this->~BufFreeCmd();
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);
427 #endif
428 SndBuf_Init(buf);
429 return true;
432 bool BufFreeCmd::Stage3()
434 SndBuf *buf = World_GetBuf(mWorld, mBufIndex);
436 SndBuf_Init(buf);
437 mWorld->mSndBufUpdates[mBufIndex].writes ++ ;
438 SEND_COMPLETION_MSG;
440 return true;
443 void BufFreeCmd::Stage4()
445 zfree(mFreeData);
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);
463 return kSCErr_None;
466 void BufZeroCmd::CallDestructor()
468 this->~BufZeroCmd();
471 bool BufZeroCmd::Stage2()
473 SndBuf *buf = World_GetNRTBuf(mWorld, mBufIndex);
474 memset(buf->data, 0, buf->samples * sizeof(float));
475 return true;
478 bool BufZeroCmd::Stage3()
480 mWorld->mSndBufUpdates[mBufIndex].writes ++ ;
481 SEND_COMPLETION_MSG;
482 return true;
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);
507 }else{
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);
517 return kSCErr_None;
520 BufAllocReadCmd::~BufAllocReadCmd()
522 World_Free(mWorld, mFilename);
525 void BufAllocReadCmd::CallDestructor()
527 this->~BufAllocReadCmd();
530 bool BufAllocReadCmd::Stage2()
532 #ifdef NO_LIBSNDFILE
533 SendFailure(&mReplyAddress, "/b_allocRead", "scsynth compiled without libsndfile\n");
534 scprintf("scsynth compiled without libsndfile\n");
535 return false;
536 #else
537 SndBuf *buf = World_GetNRTBuf(mWorld, mBufIndex);
538 SF_INFO fileinfo;
539 memset(&fileinfo, 0, sizeof(fileinfo));
540 SNDFILE* sf = sf_open(mFilename, SFM_READ, &fileinfo);
541 if (!sf) {
542 char str[512];
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);
545 scprintf(str);
546 return false;
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;
552 // alloc data size
553 mFreeData = buf->data;
554 SCErr err = bufAlloc(buf, fileinfo.channels, mNumFrames, fileinfo.samplerate);
555 if (err) goto leave;
557 sf_seek(sf, mFileOffset, SEEK_SET);
558 sf_readf_float(sf, buf->data, mNumFrames);
560 leave:
561 mSndBuf = *buf;
562 sf_close(sf);
564 return true;
565 #endif
568 bool BufAllocReadCmd::Stage3()
570 SndBuf* buf = World_GetBuf(mWorld, mBufIndex);
571 *buf = mSndBuf;
572 mWorld->mSndBufUpdates[mBufIndex].writes ++ ;
573 SEND_COMPLETION_MSG;
575 return true;
578 void BufAllocReadCmd::Stage4()
580 zfree(mFreeData);
581 SendDoneWithIntValue("/b_allocRead", mBufIndex);
584 ///////////////////////////////////////////////////////////////////////////
586 BufReadCmd::BufReadCmd(World *inWorld, ReplyAddress *inReplyAddress)
587 : SC_SequencedCommand(inWorld, inReplyAddress),
588 mFilename(0)
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);
602 }else{
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);
614 return kSCErr_None;
617 BufReadCmd::~BufReadCmd()
619 World_Free(mWorld, mFilename);
622 void BufReadCmd::CallDestructor()
624 this->~BufReadCmd();
627 bool BufReadCmd::Stage2()
629 #ifdef NO_LIBSNDFILE
630 SendFailure(&mReplyAddress, "/b_read", "scsynth compiled without libsndfile\n");
631 scprintf("scsynth compiled without libsndfile\n");
632 return false;
633 #else
634 SF_INFO fileinfo;
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);
641 if (!sf) {
642 char str[512];
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);
645 scprintf(str);
646 return false;
648 if (fileinfo.channels != buf->channels) {
649 char str[512];
650 sf_close(sf);
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);
653 scprintf(str);
654 return false;
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);
668 if(buf->sndfile)
669 sf_close(buf->sndfile);
671 if (mLeaveFileOpen) {
672 buf->sndfile = sf;
673 } else {
674 sf_close(sf);
675 buf->sndfile = 0;
678 mSampleRate = (double)fileinfo.samplerate;
680 return true;
681 #endif
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 ++ ;
691 SEND_COMPLETION_MSG;
692 return true;
695 void BufReadCmd::Stage4()
697 SendDoneWithIntValue("/b_read", mBufIndex);
700 ///////////////////////////////////////////////////////////////////////////
702 SC_BufReadCommand::SC_BufReadCommand(World* inWorld, ReplyAddress* inReplyAddress)
703 : SC_SequencedCommand(inWorld, inReplyAddress),
704 mNumChannels(0)
708 SC_BufReadCommand::~SC_BufReadCommand()
712 void SC_BufReadCommand::InitChannels(sc_msg_iter& msg)
714 mNumChannels = 0;
715 while (msg.nextTag(0) == 'i') {
716 int c = msg.geti();
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) {
727 return false;
730 return true;
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];
741 } else {
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);
767 }else{
768 mFilename = (char*)World_Alloc(mWorld, strlen(filename)+1);
769 strcpy(mFilename, filename);
772 mFileOffset = msg.geti();
773 mNumFrames = msg.geti();
775 InitChannels(msg);
777 GET_COMPLETION_MSG(msg);
779 return kSCErr_None;
782 BufAllocReadChannelCmd::~BufAllocReadChannelCmd()
784 World_Free(mWorld, mFilename);
787 void BufAllocReadChannelCmd::CallDestructor()
789 this->~BufAllocReadChannelCmd();
792 bool BufAllocReadChannelCmd::Stage2()
794 #ifdef NO_LIBSNDFILE
795 SendFailure(&mReplyAddress, "/b_allocReadChannel", "scsynth compiled without libsndfile\n");
796 scprintf("scsynth compiled without libsndfile\n");
797 return false;
798 #else
799 SndBuf *buf = World_GetNRTBuf(mWorld, mBufIndex);
801 SF_INFO fileinfo;
802 memset(&fileinfo, 0, sizeof(fileinfo));
803 SNDFILE* sf = sf_open(mFilename, SFM_READ, &fileinfo);
804 if (!sf) {
805 char str[512];
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);
808 scprintf(str);
809 return false;
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) {
816 // alloc data size
817 mFreeData = buf->data;
818 SCErr err = bufAlloc(buf, fileinfo.channels, mNumFrames, fileinfo.samplerate);
819 if (err) goto leave;
820 // read all channels
821 sf_seek(sf, mFileOffset, SEEK_SET);
822 sf_readf_float(sf, buf->data, mNumFrames);
823 } else {
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);
828 scprintf(str);
829 sf_close(sf);
830 return false;
832 // alloc data size
833 mFreeData = buf->data;
834 SCErr err = bufAlloc(buf, mNumChannels, mNumFrames, fileinfo.samplerate);
835 if (err) goto leave;
836 // alloc temp buffer
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);
843 // free temp buffer
844 free(data);
847 leave:
848 mSndBuf = *buf;
849 sf_close(sf);
851 return true;
852 #endif
855 bool BufAllocReadChannelCmd::Stage3()
857 SndBuf* buf = World_GetBuf(mWorld, mBufIndex);
858 *buf = mSndBuf;
859 mWorld->mSndBufUpdates[mBufIndex].writes ++ ;
860 SEND_COMPLETION_MSG;
862 return true;
865 void BufAllocReadChannelCmd::Stage4()
867 zfree(mFreeData);
868 SendDoneWithIntValue("/b_allocReadChannel", mBufIndex);
871 ///////////////////////////////////////////////////////////////////////////
873 BufReadChannelCmd::BufReadChannelCmd(World *inWorld, ReplyAddress *inReplyAddress)
874 : SC_BufReadCommand(inWorld, inReplyAddress),
875 mFilename(0)
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);
889 }else{
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();
899 InitChannels(msg);
901 GET_COMPLETION_MSG(msg);
903 return kSCErr_None;
906 BufReadChannelCmd::~BufReadChannelCmd()
908 World_Free(mWorld, mFilename);
911 void BufReadChannelCmd::CallDestructor()
913 this->~BufReadChannelCmd();
916 bool BufReadChannelCmd::Stage2()
918 #ifdef NO_LIBSNDFILE
919 SendFailure(&mReplyAddress, "/b_readChannel", "scsynth compiled without libsndfile\n");
920 scprintf("scsynth compiled without libsndfile\n");
921 return false;
922 #else
923 SF_INFO fileinfo;
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);
930 if (!sf) {
931 char str[512];
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);
934 scprintf(str);
935 return false;
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);
943 scprintf(str);
944 sf_close(sf);
945 return false;
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) {
949 // char str[256];
950 // sf_close(sf);
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);
954 // scprintf(str);
955 // return false;
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) {
966 // read all channels
967 sf_readf_float(sf, buf->data + (mBufOffset * buf->channels), mNumFrames);
968 } else {
969 // alloc temp buffer
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);
976 // free temp buffer
977 free(data);
981 leave:
982 if(buf->sndfile)
983 sf_close(buf->sndfile);
985 if (mLeaveFileOpen) {
986 buf->sndfile = sf;
987 } else {
988 sf_close(sf);
989 buf->sndfile = 0;
992 mSampleRate = (double)fileinfo.samplerate;
994 return true;
995 #endif
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;
1006 return true;
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
1022 struct SF_INFO {};
1023 #endif
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");
1030 return false;
1031 #else
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);
1040 }else{
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);
1056 #endif
1059 BufWriteCmd::~BufWriteCmd()
1061 World_Free(mWorld, mFilename);
1064 void BufWriteCmd::CallDestructor()
1066 this->~BufWriteCmd();
1069 bool BufWriteCmd::Stage2()
1071 #ifdef NO_LIBSNDFILE
1072 return false;
1073 #else
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);
1081 if (!sf) {
1082 char str[512];
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);
1085 scprintf(str);
1086 return false;
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);
1099 if(buf->sndfile)
1100 sf_close(buf->sndfile);
1102 if (mLeaveFileOpen) {
1103 buf->sndfile = sf;
1104 } else {
1105 sf_close(sf);
1106 buf->sndfile = 0;
1109 return true;
1110 #endif
1113 bool BufWriteCmd::Stage3()
1115 SEND_COMPLETION_MSG;
1116 return true;
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);
1138 return kSCErr_None;
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");
1151 return false;
1152 #else
1153 SndBuf *buf = World_GetNRTBuf(mWorld, mBufIndex);
1154 if (buf->sndfile) {
1155 sf_close(buf->sndfile);
1156 buf->sndfile = 0;
1158 return true;
1159 #endif
1162 bool BufCloseCmd::Stage3()
1164 SEND_COMPLETION_MSG;
1165 return true;
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;
1188 return 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");
1196 return false;
1197 #else
1198 if (mWorld->hw->mShmem)
1199 mWorld->hw->mShmem->disconnect();
1200 return true;
1201 #endif
1204 void AudioQuitCmd::Stage4()
1206 SendDone("/quit");
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)
1226 return false;
1228 small_scpacket packet;
1229 packet.adds("/status.reply");
1230 packet.maketags(10);
1231 packet.addtag(',');
1232 packet.addtag('i');
1233 packet.addtag('i');
1234 packet.addtag('i');
1235 packet.addtag('i');
1236 packet.addtag('i');
1237 packet.addtag('f');
1238 packet.addtag('f');
1239 packet.addtag('d');
1240 packet.addtag('d');
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());
1256 return false;
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();
1271 return kSCErr_None;
1274 void NotifyCmd::CallDestructor()
1276 this->~NotifyCmd();
1279 bool NotifyCmd::Stage2()
1281 HiddenWorld *hw = mWorld->hw;
1283 if (mOnOff) {
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");
1289 return false;
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");
1297 return false;
1300 hw->mUsers[hw->mNumUsers++] = mReplyAddress;
1302 SendDone("/notify");
1303 } else {
1304 for (uint32 i=0; i<hw->mNumUsers; ++i) {
1305 if (mReplyAddress == hw->mUsers[i]) {
1306 // remove from list
1307 hw->mUsers[i] = hw->mUsers[--hw->mNumUsers];
1308 SendDone("/notify");
1309 return false;
1313 SendFailure(&mReplyAddress, "/notify", "not registered\n");
1314 scprintf("not registered\n");
1316 return false;
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);
1350 return false;
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);
1372 mDefs = 0;
1373 return kSCErr_None;
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);
1390 return true;
1393 bool RecvSynthDefCmd::Stage3()
1395 GraphDef_Define(mWorld, mDefs);
1396 SEND_COMPLETION_MSG;
1397 return true;
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);
1421 }else{
1422 mFilename = (char*)World_Alloc(mWorld, strlen(filename)+1);
1423 strcpy(mFilename, filename);
1426 GET_COMPLETION_MSG(msg);
1428 mDefs = 0;
1429 return kSCErr_None;
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);
1446 return true;
1449 bool LoadSynthDefCmd::Stage3()
1451 GraphDef_Define(mWorld, mDefs);
1452 SEND_COMPLETION_MSG;
1453 return true;
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);
1477 }else{
1478 mFilename = (char*)World_Alloc(mWorld, strlen(filename)+1);
1479 strcpy(mFilename, filename);
1482 GET_COMPLETION_MSG(msg);
1484 mDefs = 0;
1485 return kSCErr_None;
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);
1502 return true;
1505 bool LoadSynthDefDirCmd::Stage3()
1507 GraphDef_Define(mWorld, mDefs);
1508 SEND_COMPLETION_MSG;
1509 return true;
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)
1526 mMsgSize = inSize;
1527 mMsgData = (char*)World_Alloc(mWorld, mMsgSize);
1528 memcpy(mMsgData, inData, inSize);
1529 return kSCErr_None;
1532 void SendReplyCmd::CallDestructor()
1534 this->~SendReplyCmd();
1537 bool SendReplyCmd::Stage2()
1539 SendReply(&mReplyAddress, mMsgData, mMsgSize);
1540 return false;
1543 ///////////////////////////////////////////////////////////////////////////
1545 int PerformAsynchronousCommand(
1546 World *inWorld,
1547 void* replyAddr,
1548 const char* cmdName,
1549 void *cmdData,
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();
1565 return kSCErr_None;
1568 ///////////////////////////////////////////////////////////////////////////
1570 AsyncPlugInCmd::AsyncPlugInCmd(World *inWorld, ReplyAddress *inReplyAddress,
1571 const char* cmdName,
1572 void *cmdData,
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),
1582 mCleanup(cleanup)
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);
1605 return result;
1608 bool AsyncPlugInCmd::Stage3()
1610 bool result = !mStage3 || (mStage3)(mWorld, mCmdData);
1611 if (result) SEND_COMPLETION_MSG;
1612 return result;
1615 void AsyncPlugInCmd::Stage4()
1617 bool result = !mStage4 || (mStage4)(mWorld, mCmdData);
1618 if (result && mCmdName && mReplyAddress.mReplyFunc != null_reply_func) SendDone((char*)mCmdName);