Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
tree: a4fca2c402
Fetching contributors…

Cannot retrieve contributors at this time

1622 lines (1323 sloc) 37.666 kb
/*
SuperCollider real time audio synthesis system
Copyright (c) 2002 James McCartney. All rights reserved.
http://www.audiosynth.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "SC_SequencedCommand.h"
#include "SC_CoreAudio.h"
#include "SC_Errors.h"
#include "scsynthsend.h"
#include "SC_Prototypes.h"
#include "SC_HiddenWorld.h"
#include "SC_Sem.h"
#include "SC_DirUtils.h"
#include "SC_StringParser.h"
#include "../../common/SC_SndFileHelpers.hpp"
#include "SC_WorldOptions.h"
#define GET_COMPLETION_MSG(msg) \
mMsgSize = msg.getbsize(); \
if (mMsgSize) { \
mMsgData = (char*)World_Alloc(mWorld, mMsgSize); \
msg.getb(mMsgData, mMsgSize); \
}
void PerformCompletionMsg(World *inWorld, OSC_Packet *inPacket);
#define SEND_COMPLETION_MSG \
if (mMsgSize) { \
OSC_Packet packet; \
packet.mData = mMsgData; \
packet.mSize = mMsgSize; \
packet.mReplyAddr = mReplyAddress; \
PacketStatus status = PerformCompletionMsg(mWorld, packet); \
if (status == PacketScheduled) { \
mMsgSize = 0; \
mMsgData = 0; \
} \
}
void SndBuf_Init(SndBuf *buf);
void SndBuf_Init(SndBuf *buf)
{
buf->data = 0;
buf->channels = 0;
buf->samples = 0;
buf->frames = 0;
buf->mask = 0;
buf->mask1 = 0;
buf->coord = 0;
buf->sndfile = 0;
}
char* allocAndRestrictPath(World *mWorld, const char* inPath, const char* restrictBase);
char* allocAndRestrictPath(World *mWorld, const char* inPath, const char* restrictBase){
char strbuf[PATH_MAX];
int offset = 0;
int remain = PATH_MAX;
// Ensure begins with the base
if(strncmp(inPath, restrictBase, strlen(restrictBase)) != 0){
strcpy(strbuf, restrictBase);
offset = strlen(restrictBase);
remain -= offset;
if(inPath[0]!='/' && strbuf[strlen(strbuf)-1]!='/'){
strbuf[offset] = '/';
++offset;
--remain;
}
}
// Now copy string, but discard any ".." (which could be benign, but easy to abuse)
SC_StringParser sp(inPath, '/');
size_t tokenlen;
while (!sp.AtEnd()) {
const char *token = const_cast<char *>(sp.NextToken());
tokenlen = strlen(token);
// now add the new token, then a slash, as long as token is neither dodgy nor overflows
if(strcmp(token, "..")!=0 && remain > tokenlen){
strcpy(strbuf+offset, token);
offset += tokenlen;
remain -= tokenlen;
if(!sp.AtEnd()) {
strbuf[offset] = '/';
++offset;
--remain;
}
}
}
// Now we can make a long-term home for the string and return it
char* saferPath = (char*)World_Alloc(mWorld, strlen(strbuf)+1);
strcpy(saferPath, strbuf);
return saferPath;
}
SC_SequencedCommand::SC_SequencedCommand(World *inWorld, ReplyAddress *inReplyAddress)
: mNextStage(1), mWorld(inWorld),
mMsgSize(0), mMsgData(0)
{
if (inReplyAddress) mReplyAddress = *inReplyAddress;
else mReplyAddress.mReplyFunc = null_reply_func;
}
SC_SequencedCommand::~SC_SequencedCommand()
{
if (mMsgData) World_Free(mWorld, mMsgData);
}
int SC_SequencedCommand::Init(char* /*inData*/, int /*inSize*/)
{
return kSCErr_None;
}
void SC_SequencedCommand::SendDone(const char *inCommandName)
{
::SendDone(&mReplyAddress, inCommandName);
};
void SC_SequencedCommand::SendDoneWithIntValue(const char *inCommandName, int value)
{
::SendDoneWithIntValue(&mReplyAddress, inCommandName, value);
};
void SC_SequencedCommand::CallEveryStage()
{
switch (mNextStage) {
case 1 : if (!Stage1()) break; mNextStage++;
case 2 : if (!Stage2()) break; mNextStage++;
case 3 : if (!Stage3()) break; mNextStage++;
case 4 : Stage4(); break;
}
Delete();
}
void DoSequencedCommand(FifoMsg *inMsg);
void DoSequencedCommand(FifoMsg *inMsg)
{
SC_SequencedCommand *cmd = (SC_SequencedCommand*)inMsg->mData;
cmd->CallNextStage();
}
void FreeSequencedCommand(FifoMsg *inMsg);
void FreeSequencedCommand(FifoMsg *inMsg)
{
SC_SequencedCommand *cmd = (SC_SequencedCommand*)inMsg->mData;
cmd->Delete();
}
void SC_SequencedCommand::CallNextStage()
{
bool sendAgain = false;
FifoMsg msg;
int isRealTime = mNextStage & 1;
switch (mNextStage) {
case 1 :
sendAgain = Stage1(); // RT
break;
case 2 :
sendAgain = Stage2(); // NRT
break;
case 3 :
sendAgain = Stage3(); // RT
break;
case 4 :
Stage4(); // NRT
break;
}
mNextStage++;
SC_AudioDriver *driver = AudioDriver(mWorld);
if (sendAgain) {
msg.Set(mWorld, DoSequencedCommand, 0, (void*)this);
// send this to next time.
if (isRealTime) {
// send to NRT
driver->SendMsgFromEngine(msg);
} else {
// send to RT
driver->SendMsgToEngine(msg);
}
} else {
if (isRealTime) {
Delete();
} else {
// can only be freed from RT.
msg.Set(mWorld, FreeSequencedCommand, 0, (void*)this);
driver->SendMsgToEngine(msg);
}
}
}
void SC_SequencedCommand::Delete()
{
CallDestructor();
World_Free(mWorld, this);
}
bool SC_SequencedCommand::Stage1()
{
return true;
}
bool SC_SequencedCommand::Stage2()
{
return false;
}
bool SC_SequencedCommand::Stage3()
{
return false;
}
void SC_SequencedCommand::Stage4()
{
}
///////////////////////////////////////////////////////////////////////////
#include "sc_msg_iter.h"
#include <string.h>
SyncCmd::SyncCmd(World *inWorld, ReplyAddress *inReplyAddress)
: SC_SequencedCommand(inWorld, inReplyAddress)
{
}
int SyncCmd::Init(char *inData, int inSize)
{
sc_msg_iter msg(inSize, inData);
mID = msg.geti();
return kSCErr_None;
}
void SyncCmd::CallDestructor()
{
this->~SyncCmd();
}
bool SyncCmd::Stage2()
{
return true;
}
bool SyncCmd::Stage3()
{
return true;
}
void SyncCmd::Stage4()
{
small_scpacket packet;
packet.adds("/synced");
packet.maketags(2);
packet.addtag(',');
packet.addtag('i');
packet.addi(mID);
SendReply(&mReplyAddress, packet.data(), packet.size());
}
///////////////////////////////////////////////////////////////////////////
BufAllocCmd::BufAllocCmd(World *inWorld, ReplyAddress *inReplyAddress)
: SC_SequencedCommand(inWorld, inReplyAddress)
{
}
int BufAllocCmd::Init(char *inData, int inSize)
{
sc_msg_iter msg(inSize, inData);
mBufIndex = msg.geti();
mNumFrames = msg.geti();
mNumChannels = msg.geti(1);
GET_COMPLETION_MSG(msg);
return kSCErr_None;
}
void BufAllocCmd::CallDestructor()
{
this->~BufAllocCmd();
}
bool BufAllocCmd::Stage2()
{
SndBuf *buf = World_GetNRTBuf(mWorld, mBufIndex);
mFreeData = buf->data;
bufAlloc(buf, mNumChannels, mNumFrames, mWorld->mFullRate.mSampleRate);
mSndBuf = *buf;
return true;
}
bool BufAllocCmd::Stage3()
{
SndBuf* buf = World_GetBuf(mWorld, mBufIndex);
*buf = mSndBuf;
mWorld->mSndBufUpdates[mBufIndex].writes ++ ;
SEND_COMPLETION_MSG;
return true;
}
void BufAllocCmd::Stage4()
{
zfree(mFreeData);
SendDoneWithIntValue("/b_alloc", mBufIndex);
}
///////////////////////////////////////////////////////////////////////////
#include "sc_msg_iter.h"
#include <string.h>
BufGenCmd::BufGenCmd(World *inWorld, ReplyAddress *inReplyAddress)
: SC_SequencedCommand(inWorld, inReplyAddress),
mData(0)
{
}
BufGenCmd::~BufGenCmd()
{
World_Free(mWorld, mData);
}
int BufGenCmd::Init(char *inData, int inSize)
{
mSize = inSize;
mData = (char*)World_Alloc(mWorld, mSize);
memcpy(mData, inData, mSize);
sc_msg_iter msg(mSize, mData);
mBufIndex = msg.geti();
int32 *genName = msg.gets4();
if (!genName) return kSCErr_WrongArgType;
mBufGen = GetBufGen(genName);
if (!mBufGen) return kSCErr_BufGenNotFound;
mMsg = msg;
return kSCErr_None;
}
void BufGenCmd::CallDestructor()
{
this->~BufGenCmd();
}
bool BufGenCmd::Stage2()
{
SndBuf *buf = World_GetNRTBuf(mWorld, mBufIndex);
mFreeData = buf->data;
(*mBufGen->mBufGenFunc)(mWorld, buf, &mMsg);
if (buf->data == mFreeData) mFreeData = NULL;
mSndBuf = *buf;
return true;
}
bool BufGenCmd::Stage3()
{
SndBuf* buf = World_GetBuf(mWorld, mBufIndex);
*buf = mSndBuf;
mWorld->mSndBufUpdates[mBufIndex].writes ++ ;
return true;
}
void BufGenCmd::Stage4()
{
zfree(mFreeData);
SendDoneWithIntValue("/b_gen", mBufIndex);
}
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
BufFreeCmd::BufFreeCmd(World *inWorld, ReplyAddress *inReplyAddress)
: SC_SequencedCommand(inWorld, inReplyAddress)
{
}
int BufFreeCmd::Init(char *inData, int inSize)
{
sc_msg_iter msg(inSize, inData);
mBufIndex = msg.geti();
GET_COMPLETION_MSG(msg);
return kSCErr_None;
}
void BufFreeCmd::CallDestructor()
{
this->~BufFreeCmd();
}
bool BufFreeCmd::Stage2()
{
SndBuf *buf = World_GetNRTBuf(mWorld, mBufIndex);
mFreeData = buf->data;
#ifndef NO_LIBSNDFILE
if (buf->sndfile) sf_close(buf->sndfile);
#endif
SndBuf_Init(buf);
return true;
}
bool BufFreeCmd::Stage3()
{
SndBuf *buf = World_GetBuf(mWorld, mBufIndex);
SndBuf_Init(buf);
mWorld->mSndBufUpdates[mBufIndex].writes ++ ;
SEND_COMPLETION_MSG;
return true;
}
void BufFreeCmd::Stage4()
{
zfree(mFreeData);
SendDoneWithIntValue("/b_free", mBufIndex);
}
///////////////////////////////////////////////////////////////////////////
BufZeroCmd::BufZeroCmd(World *inWorld, ReplyAddress *inReplyAddress)
: SC_SequencedCommand(inWorld, inReplyAddress)
{
}
int BufZeroCmd::Init(char *inData, int inSize)
{
sc_msg_iter msg(inSize, inData);
mBufIndex = msg.geti();
GET_COMPLETION_MSG(msg);
return kSCErr_None;
}
void BufZeroCmd::CallDestructor()
{
this->~BufZeroCmd();
}
bool BufZeroCmd::Stage2()
{
SndBuf *buf = World_GetNRTBuf(mWorld, mBufIndex);
memset(buf->data, 0, buf->samples * sizeof(float));
return true;
}
bool BufZeroCmd::Stage3()
{
mWorld->mSndBufUpdates[mBufIndex].writes ++ ;
SEND_COMPLETION_MSG;
return true;
}
void BufZeroCmd::Stage4()
{
SendDoneWithIntValue("/b_zero", mBufIndex);
}
///////////////////////////////////////////////////////////////////////////
BufAllocReadCmd::BufAllocReadCmd(World *inWorld, ReplyAddress *inReplyAddress)
: SC_SequencedCommand(inWorld, inReplyAddress), mFreeData(0), mFilename(0)
{
}
int BufAllocReadCmd::Init(char *inData, int inSize)
{
sc_msg_iter msg(inSize, inData);
mBufIndex = msg.geti();
const char *filename = msg.gets();
if (!filename) return kSCErr_WrongArgType;
if(mWorld->mRestrictedPath){
mFilename = allocAndRestrictPath(mWorld, filename, mWorld->mRestrictedPath);
}else{
mFilename = (char*)World_Alloc(mWorld, strlen(filename)+1);
strcpy(mFilename, filename);
}
mFileOffset = msg.geti();
mNumFrames = msg.geti();
GET_COMPLETION_MSG(msg);
return kSCErr_None;
}
BufAllocReadCmd::~BufAllocReadCmd()
{
World_Free(mWorld, mFilename);
}
void BufAllocReadCmd::CallDestructor()
{
this->~BufAllocReadCmd();
}
bool BufAllocReadCmd::Stage2()
{
#ifdef NO_LIBSNDFILE
SendFailure(&mReplyAddress, "/b_allocRead", "scsynth compiled without libsndfile\n");
scprintf("scsynth compiled without libsndfile\n");
return false;
#else
SndBuf *buf = World_GetNRTBuf(mWorld, mBufIndex);
SF_INFO fileinfo;
memset(&fileinfo, 0, sizeof(fileinfo));
SNDFILE* sf = sf_open(mFilename, SFM_READ, &fileinfo);
if (!sf) {
char str[512];
sprintf(str, "File '%s' could not be opened: %s\n", mFilename, sf_strerror(NULL));
SendFailureWithBufnum(&mReplyAddress, "/b_allocRead", str, mBufIndex); //SendFailure(&mReplyAddress, "/b_allocRead", str);
scprintf(str);
return false;
}
if (mFileOffset < 0) mFileOffset = 0;
else if (mFileOffset > fileinfo.frames) mFileOffset = fileinfo.frames;
if (mNumFrames <= 0 || mNumFrames + mFileOffset > fileinfo.frames) mNumFrames = fileinfo.frames - mFileOffset;
// alloc data size
mFreeData = buf->data;
SCErr err = bufAlloc(buf, fileinfo.channels, mNumFrames, fileinfo.samplerate);
if (err) goto leave;
sf_seek(sf, mFileOffset, SEEK_SET);
sf_readf_float(sf, buf->data, mNumFrames);
leave:
mSndBuf = *buf;
sf_close(sf);
return true;
#endif
}
bool BufAllocReadCmd::Stage3()
{
SndBuf* buf = World_GetBuf(mWorld, mBufIndex);
*buf = mSndBuf;
mWorld->mSndBufUpdates[mBufIndex].writes ++ ;
SEND_COMPLETION_MSG;
return true;
}
void BufAllocReadCmd::Stage4()
{
zfree(mFreeData);
SendDoneWithIntValue("/b_allocRead", mBufIndex);
}
///////////////////////////////////////////////////////////////////////////
BufReadCmd::BufReadCmd(World *inWorld, ReplyAddress *inReplyAddress)
: SC_SequencedCommand(inWorld, inReplyAddress),
mFilename(0)
{
}
int BufReadCmd::Init(char *inData, int inSize)
{
sc_msg_iter msg(inSize, inData);
mBufIndex = msg.geti();
const char *filename = msg.gets();
if (!filename) return kSCErr_WrongArgType;
if(mWorld->mRestrictedPath){
mFilename = allocAndRestrictPath(mWorld, filename, mWorld->mRestrictedPath);
}else{
mFilename = (char*)World_Alloc(mWorld, strlen(filename)+1);
strcpy(mFilename, filename);
}
mFileOffset = msg.geti();
mNumFrames = msg.geti(-1);
mBufOffset = msg.geti();
mLeaveFileOpen = msg.geti();
GET_COMPLETION_MSG(msg);
return kSCErr_None;
}
BufReadCmd::~BufReadCmd()
{
World_Free(mWorld, mFilename);
}
void BufReadCmd::CallDestructor()
{
this->~BufReadCmd();
}
bool BufReadCmd::Stage2()
{
#ifdef NO_LIBSNDFILE
SendFailure(&mReplyAddress, "/b_read", "scsynth compiled without libsndfile\n");
scprintf("scsynth compiled without libsndfile\n");
return false;
#else
SF_INFO fileinfo;
SndBuf *buf = World_GetNRTBuf(mWorld, mBufIndex);
int framesToEnd = buf->frames - mBufOffset;
if (framesToEnd <= 0) return true;
SNDFILE* sf = sf_open(mFilename, SFM_READ, &fileinfo);
if (!sf) {
char str[512];
sprintf(str, "File '%s' could not be opened: %s\n", mFilename, sf_strerror(NULL));
SendFailureWithBufnum(&mReplyAddress, "/b_read", str, mBufIndex); //SendFailure(&mReplyAddress, "/b_read", str);
scprintf(str);
return false;
}
if (fileinfo.channels != buf->channels) {
char str[512];
sf_close(sf);
sprintf(str, "Channel mismatch. File '%s' has %d channels. Buffer has %d channels.\n", mFilename, fileinfo.channels, buf->channels);
SendFailureWithBufnum(&mReplyAddress, "/b_read", str, mBufIndex); //SendFailure(&mReplyAddress, "/b_read", str);
scprintf(str);
return false;
}
if (mFileOffset < 0) mFileOffset = 0;
else if (mFileOffset > fileinfo.frames) mFileOffset = fileinfo.frames;
if (mNumFrames < 0 || mNumFrames + mFileOffset > fileinfo.frames) mNumFrames = fileinfo.frames - mFileOffset;
if (mNumFrames > framesToEnd) mNumFrames = framesToEnd;
sf_seek(sf, mFileOffset, SEEK_SET);
if (mNumFrames > 0) {
sf_readf_float(sf, buf->data + (mBufOffset * buf->channels), mNumFrames);
}
if(buf->sndfile)
sf_close(buf->sndfile);
if (mLeaveFileOpen) {
buf->sndfile = sf;
} else {
sf_close(sf);
buf->sndfile = 0;
}
mSampleRate = (double)fileinfo.samplerate;
return true;
#endif
}
bool BufReadCmd::Stage3()
{
SndBuf* buf = World_GetBuf(mWorld, mBufIndex);
buf->samplerate = mSampleRate;
if (mLeaveFileOpen) buf->mask = buf->mask1 = -1;
mWorld->mSndBufUpdates[mBufIndex].writes ++ ;
SEND_COMPLETION_MSG;
return true;
}
void BufReadCmd::Stage4()
{
SendDoneWithIntValue("/b_read", mBufIndex);
}
///////////////////////////////////////////////////////////////////////////
SC_BufReadCommand::SC_BufReadCommand(World* inWorld, ReplyAddress* inReplyAddress)
: SC_SequencedCommand(inWorld, inReplyAddress),
mNumChannels(0)
{
}
SC_BufReadCommand::~SC_BufReadCommand()
{
}
void SC_BufReadCommand::InitChannels(sc_msg_iter& msg)
{
mNumChannels = 0;
while (msg.nextTag(0) == 'i') {
int c = msg.geti();
if (mNumChannels <= kMaxNumChannels) {
mChannels[mNumChannels++] = c;
}
}
}
bool SC_BufReadCommand::CheckChannels(int inNumChannels)
{
for (int i=0; i < mNumChannels; ++i) {
if (mChannels[i] >= inNumChannels) {
return false;
}
}
return true;
}
void SC_BufReadCommand::CopyChannels(float* dst, float* src, size_t srcChannels, size_t numFrames)
{
for (int ci=0; ci < mNumChannels; ++ci) {
int c = mChannels[ci];
if (c >= 0 && c < srcChannels) {
for (size_t fi=0; fi < numFrames; ++fi) {
dst[fi*mNumChannels+ci] = src[fi*srcChannels+c];
}
} else {
for (size_t fi=0; fi < numFrames; ++fi) {
dst[fi*mNumChannels+ci] = 0.f;
}
}
}
}
///////////////////////////////////////////////////////////////////////////
BufAllocReadChannelCmd::BufAllocReadChannelCmd(World *inWorld, ReplyAddress *inReplyAddress)
: SC_BufReadCommand(inWorld, inReplyAddress),
mFreeData(0), mFilename(0)
{
}
int BufAllocReadChannelCmd::Init(char *inData, int inSize)
{
sc_msg_iter msg(inSize, inData);
mBufIndex = msg.geti();
const char *filename = msg.gets();
if (!filename) return kSCErr_WrongArgType;
if(mWorld->mRestrictedPath){
mFilename = allocAndRestrictPath(mWorld, filename, mWorld->mRestrictedPath);
}else{
mFilename = (char*)World_Alloc(mWorld, strlen(filename)+1);
strcpy(mFilename, filename);
}
mFileOffset = msg.geti();
mNumFrames = msg.geti();
InitChannels(msg);
GET_COMPLETION_MSG(msg);
return kSCErr_None;
}
BufAllocReadChannelCmd::~BufAllocReadChannelCmd()
{
World_Free(mWorld, mFilename);
}
void BufAllocReadChannelCmd::CallDestructor()
{
this->~BufAllocReadChannelCmd();
}
bool BufAllocReadChannelCmd::Stage2()
{
#ifdef NO_LIBSNDFILE
SendFailure(&mReplyAddress, "/b_allocReadChannel", "scsynth compiled without libsndfile\n");
scprintf("scsynth compiled without libsndfile\n");
return false;
#else
SndBuf *buf = World_GetNRTBuf(mWorld, mBufIndex);
SF_INFO fileinfo;
memset(&fileinfo, 0, sizeof(fileinfo));
SNDFILE* sf = sf_open(mFilename, SFM_READ, &fileinfo);
if (!sf) {
char str[512];
sprintf(str, "File '%s' could not be opened: %s\n", mFilename, sf_strerror(NULL));
SendFailureWithBufnum(&mReplyAddress, "/b_allocReadChannel", str, mBufIndex); //SendFailure(&mReplyAddress, "/b_allocRead", str);
scprintf(str);
return false;
}
if (mFileOffset < 0) mFileOffset = 0;
else if (mFileOffset > fileinfo.frames) mFileOffset = fileinfo.frames;
if (mNumFrames <= 0 || mNumFrames + mFileOffset > fileinfo.frames) mNumFrames = fileinfo.frames - mFileOffset;
if (mNumChannels == 0) {
// alloc data size
mFreeData = buf->data;
SCErr err = bufAlloc(buf, fileinfo.channels, mNumFrames, fileinfo.samplerate);
if (err) goto leave;
// read all channels
sf_seek(sf, mFileOffset, SEEK_SET);
sf_readf_float(sf, buf->data, mNumFrames);
} else {
// verify channel indexes
if (!CheckChannels(fileinfo.channels)) {
const char* str = "Channel index out of range.\n";
SendFailureWithBufnum(&mReplyAddress, "/b_allocReadChannel", str, mBufIndex); //SendFailure(&mReplyAddress, "/b_allocRead", str);
scprintf(str);
sf_close(sf);
return false;
}
// alloc data size
mFreeData = buf->data;
SCErr err = bufAlloc(buf, mNumChannels, mNumFrames, fileinfo.samplerate);
if (err) goto leave;
// alloc temp buffer
float* data = (float*)malloc(mNumFrames*fileinfo.channels*sizeof(float));
if (data == 0) goto leave;
// read some channels
sf_seek(sf, mFileOffset, SEEK_SET);
sf_readf_float(sf, data, mNumFrames);
CopyChannels(buf->data, data, fileinfo.channels, mNumFrames);
// free temp buffer
free(data);
}
leave:
mSndBuf = *buf;
sf_close(sf);
return true;
#endif
}
bool BufAllocReadChannelCmd::Stage3()
{
SndBuf* buf = World_GetBuf(mWorld, mBufIndex);
*buf = mSndBuf;
mWorld->mSndBufUpdates[mBufIndex].writes ++ ;
SEND_COMPLETION_MSG;
return true;
}
void BufAllocReadChannelCmd::Stage4()
{
zfree(mFreeData);
SendDoneWithIntValue("/b_allocReadChannel", mBufIndex);
}
///////////////////////////////////////////////////////////////////////////
BufReadChannelCmd::BufReadChannelCmd(World *inWorld, ReplyAddress *inReplyAddress)
: SC_BufReadCommand(inWorld, inReplyAddress),
mFilename(0)
{
}
int BufReadChannelCmd::Init(char *inData, int inSize)
{
sc_msg_iter msg(inSize, inData);
mBufIndex = msg.geti();
const char *filename = msg.gets();
if (!filename) return kSCErr_WrongArgType;
if(mWorld->mRestrictedPath){
mFilename = allocAndRestrictPath(mWorld, filename, mWorld->mRestrictedPath);
}else{
mFilename = (char*)World_Alloc(mWorld, strlen(filename)+1);
strcpy(mFilename, filename);
}
mFileOffset = msg.geti();
mNumFrames = msg.geti(-1);
mBufOffset = msg.geti();
mLeaveFileOpen = msg.geti();
InitChannels(msg);
GET_COMPLETION_MSG(msg);
return kSCErr_None;
}
BufReadChannelCmd::~BufReadChannelCmd()
{
World_Free(mWorld, mFilename);
}
void BufReadChannelCmd::CallDestructor()
{
this->~BufReadChannelCmd();
}
bool BufReadChannelCmd::Stage2()
{
#ifdef NO_LIBSNDFILE
SendFailure(&mReplyAddress, "/b_readChannel", "scsynth compiled without libsndfile\n");
scprintf("scsynth compiled without libsndfile\n");
return false;
#else
SF_INFO fileinfo;
SndBuf *buf = World_GetNRTBuf(mWorld, mBufIndex);
int framesToEnd = buf->frames - mBufOffset;
if (framesToEnd <= 0) return true;
SNDFILE* sf = sf_open(mFilename, SFM_READ, &fileinfo);
if (!sf) {
char str[512];
sprintf(str, "File '%s' could not be opened: %s\n", mFilename, sf_strerror(NULL));
SendFailureWithBufnum(&mReplyAddress, "/b_readChannel", str, mBufIndex); //SendFailure(&mReplyAddress, "/b_read", str);
scprintf(str);
return false;
}
if (mNumChannels > 0) {
// verify channel indexes
if (!( CheckChannels(fileinfo.channels)) ) { // nescivi: && CheckChannels(buf->channels) (should not check here for buf->channels)
const char* str = "Channel index out of range.\n";
SendFailureWithBufnum(&mReplyAddress, "/b_readChannel", str, mBufIndex); //SendFailure(&mReplyAddress, "/b_allocRead", str);
scprintf(str);
sf_close(sf);
return false;
}
// nescivi: this also seems out of place: we want to read from a file with more channels than are in the buffer
// } else if (fileinfo.channels != buf->channels) {
// char str[256];
// sf_close(sf);
// sprintf(str, "Channel mismatch. File '%s' has %d channels. Buffer has %d channels.\n",
// mFilename, fileinfo.channels, buf->channels);
// SendFailure(&mReplyAddress, "/b_read", str);
// scprintf(str);
// return false;
}
if (mFileOffset < 0) mFileOffset = 0;
else if (mFileOffset > fileinfo.frames) mFileOffset = fileinfo.frames;
if (mNumFrames < 0 || mNumFrames + mFileOffset > fileinfo.frames) mNumFrames = fileinfo.frames - mFileOffset;
if (mNumFrames > framesToEnd) mNumFrames = framesToEnd;
sf_seek(sf, mFileOffset, SEEK_SET);
if (mNumFrames > 0) {
if (mNumChannels == 0) {
// read all channels
sf_readf_float(sf, buf->data + (mBufOffset * buf->channels), mNumFrames);
} else {
// alloc temp buffer
float* data = (float*)malloc(mNumFrames*fileinfo.channels*sizeof(float));
if (data == 0) goto leave;
// read some channels
sf_seek(sf, mFileOffset, SEEK_SET);
sf_readf_float(sf, data, mNumFrames);
CopyChannels(buf->data + (mBufOffset * mNumChannels), data, fileinfo.channels, mNumFrames);
// free temp buffer
free(data);
}
}
leave:
if(buf->sndfile)
sf_close(buf->sndfile);
if (mLeaveFileOpen) {
buf->sndfile = sf;
} else {
sf_close(sf);
buf->sndfile = 0;
}
mSampleRate = (double)fileinfo.samplerate;
return true;
#endif
}
bool BufReadChannelCmd::Stage3()
{
SndBuf* buf = World_GetBuf(mWorld, mBufIndex);
buf->samplerate = mSampleRate;
if (mLeaveFileOpen) buf->mask = buf->mask1 = -1;
mWorld->mSndBufUpdates[mBufIndex].writes ++ ;
SEND_COMPLETION_MSG;
return true;
}
void BufReadChannelCmd::Stage4()
{
SendDoneWithIntValue("/b_readChannel", mBufIndex);
}
///////////////////////////////////////////////////////////////////////////
BufWriteCmd::BufWriteCmd(World *inWorld, ReplyAddress *inReplyAddress)
: SC_SequencedCommand(inWorld, inReplyAddress), mFilename(0)
{
}
#ifdef NO_LIBSNDFILE
struct SF_INFO {};
#endif
int BufWriteCmd::Init(char *inData, int inSize)
{
#ifdef NO_LIBSNDFILE
SendFailure(&mReplyAddress, "/b_write", "scsynth compiled without libsndfile\n");
scprintf("scsynth compiled without libsndfile\n");
return false;
#else
sc_msg_iter msg(inSize, inData);
mBufIndex = msg.geti();
const char *filename = msg.gets();
if (!filename) return kSCErr_WrongArgType;
if(mWorld->mRestrictedPath){
mFilename = allocAndRestrictPath(mWorld, filename, mWorld->mRestrictedPath);
}else{
mFilename = (char*)World_Alloc(mWorld, strlen(filename)+1);
strcpy(mFilename, filename);
}
const char *headerFormatString = msg.gets("aiff");
const char *sampleFormatString = msg.gets("int16");
mNumFrames = msg.geti(-1);
mBufOffset = msg.geti();
mLeaveFileOpen = msg.geti();
GET_COMPLETION_MSG(msg);
memset(&mFileInfo, 0, sizeof(mFileInfo));
return sndfileFormatInfoFromStrings(&mFileInfo, headerFormatString, sampleFormatString);
#endif
}
BufWriteCmd::~BufWriteCmd()
{
World_Free(mWorld, mFilename);
}
void BufWriteCmd::CallDestructor()
{
this->~BufWriteCmd();
}
bool BufWriteCmd::Stage2()
{
#ifdef NO_LIBSNDFILE
return false;
#else
SndBuf *buf = World_GetNRTBuf(mWorld, mBufIndex);
int framesToEnd = buf->frames - mBufOffset;
if (framesToEnd < 0) framesToEnd = 0;
mFileInfo.samplerate = (int)buf->samplerate;
mFileInfo.channels = buf->channels;
SNDFILE* sf = sf_open(mFilename, SFM_WRITE, &mFileInfo);
if (!sf) {
char str[512];
sprintf(str, "File '%s' could not be opened: %s\n", mFilename, sf_strerror(NULL));
SendFailureWithBufnum(&mReplyAddress, "/b_write", str, mBufIndex); //SendFailure(&mReplyAddress, "/b_write", str);
scprintf(str);
return false;
}
if (mNumFrames < 0 || mNumFrames > buf->frames) mNumFrames = buf->frames;
if (mNumFrames > framesToEnd) mNumFrames = framesToEnd;
sf_command(sf, SFC_SET_CLIPPING, NULL, SF_TRUE); // choose clipping rather than wraparound for integer-format files
if (mNumFrames > 0) {
sf_writef_float(sf, buf->data + (mBufOffset * buf->channels), mNumFrames);
}
if(buf->sndfile)
sf_close(buf->sndfile);
if (mLeaveFileOpen) {
buf->sndfile = sf;
} else {
sf_close(sf);
buf->sndfile = 0;
}
return true;
#endif
}
bool BufWriteCmd::Stage3()
{
SEND_COMPLETION_MSG;
return true;
}
void BufWriteCmd::Stage4()
{
SendDoneWithIntValue("/b_write", mBufIndex);
}
///////////////////////////////////////////////////////////////////////////
BufCloseCmd::BufCloseCmd(World *inWorld, ReplyAddress *inReplyAddress)
: SC_SequencedCommand(inWorld, inReplyAddress)
{
}
int BufCloseCmd::Init(char *inData, int inSize)
{
sc_msg_iter msg(inSize, inData);
mBufIndex = msg.geti();
GET_COMPLETION_MSG(msg);
return kSCErr_None;
}
void BufCloseCmd::CallDestructor()
{
this->~BufCloseCmd();
}
bool BufCloseCmd::Stage2()
{
#ifdef NO_LIBSNDFILE
SendFailure(&mReplyAddress, "/b_close", "scsynth compiled without libsndfile\n");
scprintf("scsynth compiled without libsndfile\n");
return false;
#else
SndBuf *buf = World_GetNRTBuf(mWorld, mBufIndex);
if (buf->sndfile) {
sf_close(buf->sndfile);
buf->sndfile = 0;
}
return true;
#endif
}
bool BufCloseCmd::Stage3()
{
SEND_COMPLETION_MSG;
return true;
}
void BufCloseCmd::Stage4()
{
SendDoneWithIntValue("/b_close", mBufIndex);
}
///////////////////////////////////////////////////////////////////////////
AudioQuitCmd::AudioQuitCmd(World *inWorld, ReplyAddress *inReplyAddress)
: SC_SequencedCommand(inWorld, inReplyAddress)
{
}
void AudioQuitCmd::CallDestructor()
{
this->~AudioQuitCmd();
}
bool AudioQuitCmd::Stage2()
{
mWorld->hw->mTerminating = true;
return true;
}
bool AudioQuitCmd::Stage3()
{
#if SC_AUDIO_API == SC_AUDIO_API_AUDIOUNITS
SendFailure(&mReplyAddress, "/quit", "not allowed in AU host\n");
scprintf("/quit : quit not allowed in AU host\n");
return false;
#else
if (mWorld->hw->mShmem)
mWorld->hw->mShmem->disconnect();
return true;
#endif
}
void AudioQuitCmd::Stage4()
{
SendDone("/quit");
mWorld->hw->mQuitProgram->Release();
}
///////////////////////////////////////////////////////////////////////////
AudioStatusCmd::AudioStatusCmd(World *inWorld, ReplyAddress *inReplyAddress)
: SC_SequencedCommand(inWorld, inReplyAddress)
{
}
void AudioStatusCmd::CallDestructor()
{
this->~AudioStatusCmd();
}
bool AudioStatusCmd::Stage2()
{
// we stop replying to status requests after receiving /quit
if (mWorld->hw->mTerminating == true)
return false;
small_scpacket packet;
packet.adds("/status.reply");
packet.maketags(10);
packet.addtag(',');
packet.addtag('i');
packet.addtag('i');
packet.addtag('i');
packet.addtag('i');
packet.addtag('i');
packet.addtag('f');
packet.addtag('f');
packet.addtag('d');
packet.addtag('d');
packet.addi(1); // audio is always active now.
packet.addi(mWorld->mNumUnits);
packet.addi(mWorld->mNumGraphs);
packet.addi(mWorld->mNumGroups);
packet.addi(mWorld->hw->mGraphDefLib->NumItems());
SC_AudioDriver *driver = mWorld->hw->mAudioDriver;
packet.addf(driver->GetAvgCPU());
packet.addf(driver->GetPeakCPU());
packet.addd(driver->GetSampleRate());
packet.addd(driver->GetActualSampleRate());
SendReply(&mReplyAddress, packet.data(), packet.size());
return false;
}
///////////////////////////////////////////////////////////////////////////
NotifyCmd::NotifyCmd(World *inWorld, ReplyAddress *inReplyAddress)
: SC_SequencedCommand(inWorld, inReplyAddress)
{
}
int NotifyCmd::Init(char *inData, int inSize)
{
sc_msg_iter msg(inSize, inData);
mOnOff = msg.geti();
return kSCErr_None;
}
void NotifyCmd::CallDestructor()
{
this->~NotifyCmd();
}
bool NotifyCmd::Stage2()
{
HiddenWorld *hw = mWorld->hw;
if (mOnOff) {
for (uint32 i=0; i<hw->mNumUsers; ++i) {
if (mReplyAddress == hw->mUsers[i]) {
// already in table - don't fail though..
SendFailure(&mReplyAddress, "/notify", "notify: already registered\n");
scprintf("/notify : already registered\n");
return false;
}
}
// add reply address to user table
if (hw->mNumUsers >= hw->mMaxUsers) {
SendFailure(&mReplyAddress, "/notify", "too many users\n");
scprintf("too many users\n");
return false;
}
hw->mUsers[hw->mNumUsers++] = mReplyAddress;
SendDone("/notify");
} else {
for (uint32 i=0; i<hw->mNumUsers; ++i) {
if (mReplyAddress == hw->mUsers[i]) {
// remove from list
hw->mUsers[i] = hw->mUsers[--hw->mNumUsers];
SendDone("/notify");
return false;
}
}
SendFailure(&mReplyAddress, "/notify", "not registered\n");
scprintf("not registered\n");
}
return false;
}
///////////////////////////////////////////////////////////////////////////
SendFailureCmd::SendFailureCmd(World *inWorld, ReplyAddress *inReplyAddress)
: SC_SequencedCommand(inWorld, inReplyAddress), mCmdName(0), mErrString(0)
{
}
SendFailureCmd::~SendFailureCmd()
{
World_Free(mWorld, mCmdName);
World_Free(mWorld, mErrString);
}
void SendFailureCmd::InitSendFailureCmd(const char *inCmdName, const char* inErrString)
{
mCmdName = (char*)World_Alloc(mWorld, strlen(inCmdName)+1);
strcpy(mCmdName, inCmdName);
mErrString = (char*)World_Alloc(mWorld, strlen(inErrString)+1);
strcpy(mErrString, inErrString);
}
void SendFailureCmd::CallDestructor()
{
this->~SendFailureCmd();
}
bool SendFailureCmd::Stage2()
{
SendFailure(&mReplyAddress, mCmdName, mErrString);
return false;
}
///////////////////////////////////////////////////////////////////////////
RecvSynthDefCmd::RecvSynthDefCmd(World *inWorld, ReplyAddress *inReplyAddress)
: SC_SequencedCommand(inWorld, inReplyAddress), mBuffer(0)
{
}
int RecvSynthDefCmd::Init(char *inData, int inSize)
{
sc_msg_iter msg(inSize, inData);
int size = msg.getbsize();
if (!size) throw kSCErr_WrongArgType;
mBuffer = (char*)World_Alloc(mWorld, size);
msg.getb(mBuffer, size);
GET_COMPLETION_MSG(msg);
mDefs = 0;
return kSCErr_None;
}
RecvSynthDefCmd::~RecvSynthDefCmd()
{
World_Free(mWorld, mBuffer);
}
void RecvSynthDefCmd::CallDestructor()
{
this->~RecvSynthDefCmd();
}
bool RecvSynthDefCmd::Stage2()
{
mDefs = GraphDef_Recv(mWorld, mBuffer, mDefs);
return true;
}
bool RecvSynthDefCmd::Stage3()
{
GraphDef_Define(mWorld, mDefs);
SEND_COMPLETION_MSG;
return true;
}
void RecvSynthDefCmd::Stage4()
{
SendDone("/d_recv");
}
///////////////////////////////////////////////////////////////////////////
LoadSynthDefCmd::LoadSynthDefCmd(World *inWorld, ReplyAddress *inReplyAddress)
: SC_SequencedCommand(inWorld, inReplyAddress), mFilename(0)
{
}
int LoadSynthDefCmd::Init(char *inData, int inSize)
{
sc_msg_iter msg(inSize, inData);
const char *filename = msg.gets();
if (!filename) return kSCErr_WrongArgType;
if(mWorld->mRestrictedPath){
mFilename = allocAndRestrictPath(mWorld, filename, mWorld->mRestrictedPath);
}else{
mFilename = (char*)World_Alloc(mWorld, strlen(filename)+1);
strcpy(mFilename, filename);
}
GET_COMPLETION_MSG(msg);
mDefs = 0;
return kSCErr_None;
}
LoadSynthDefCmd::~LoadSynthDefCmd()
{
World_Free(mWorld, mFilename);
}
void LoadSynthDefCmd::CallDestructor()
{
this->~LoadSynthDefCmd();
}
bool LoadSynthDefCmd::Stage2()
{
char* fname = mFilename;
mDefs = GraphDef_LoadGlob(mWorld, fname, mDefs);
return true;
}
bool LoadSynthDefCmd::Stage3()
{
GraphDef_Define(mWorld, mDefs);
SEND_COMPLETION_MSG;
return true;
}
void LoadSynthDefCmd::Stage4()
{
SendDone("/d_load");
}
///////////////////////////////////////////////////////////////////////////
LoadSynthDefDirCmd::LoadSynthDefDirCmd(World *inWorld, ReplyAddress *inReplyAddress)
: SC_SequencedCommand(inWorld, inReplyAddress), mFilename(0)
{
}
int LoadSynthDefDirCmd::Init(char *inData, int inSize)
{
sc_msg_iter msg(inSize, inData);
const char *filename = msg.gets();
if (!filename) return kSCErr_WrongArgType;
if(mWorld->mRestrictedPath){
mFilename = allocAndRestrictPath(mWorld, filename, mWorld->mRestrictedPath);
}else{
mFilename = (char*)World_Alloc(mWorld, strlen(filename)+1);
strcpy(mFilename, filename);
}
GET_COMPLETION_MSG(msg);
mDefs = 0;
return kSCErr_None;
}
LoadSynthDefDirCmd::~LoadSynthDefDirCmd()
{
World_Free(mWorld, mFilename);
}
void LoadSynthDefDirCmd::CallDestructor()
{
this->~LoadSynthDefDirCmd();
}
bool LoadSynthDefDirCmd::Stage2()
{
mDefs = GraphDef_LoadDir(mWorld, mFilename, mDefs);
return true;
}
bool LoadSynthDefDirCmd::Stage3()
{
GraphDef_Define(mWorld, mDefs);
SEND_COMPLETION_MSG;
return true;
}
void LoadSynthDefDirCmd::Stage4()
{
SendDone("/d_loadDir");
}
///////////////////////////////////////////////////////////////////////////
SendReplyCmd::SendReplyCmd(World *inWorld, ReplyAddress *inReplyAddress)
: SC_SequencedCommand(inWorld, inReplyAddress)
{
}
int SendReplyCmd::Init(char *inData, int inSize)
{
mMsgSize = inSize;
mMsgData = (char*)World_Alloc(mWorld, mMsgSize);
memcpy(mMsgData, inData, inSize);
return kSCErr_None;
}
void SendReplyCmd::CallDestructor()
{
this->~SendReplyCmd();
}
bool SendReplyCmd::Stage2()
{
SendReply(&mReplyAddress, mMsgData, mMsgSize);
return false;
}
///////////////////////////////////////////////////////////////////////////
int PerformAsynchronousCommand(
World *inWorld,
void* replyAddr,
const char* cmdName,
void *cmdData,
AsyncStageFn stage2, // stage2 is non real time
AsyncStageFn stage3, // stage3 is real time - completion msg performed if stage3 returns true
AsyncStageFn stage4, // stage4 is non real time - sends done if stage4 returns true
AsyncFreeFn cleanup,
int completionMsgSize,
void* completionMsgData
)
{
void* space = World_Alloc(inWorld, sizeof(AsyncPlugInCmd));
AsyncPlugInCmd *cmd = new (space) AsyncPlugInCmd(inWorld, (ReplyAddress*)replyAddr,
cmdName, cmdData, stage2, stage3, stage4, cleanup,
completionMsgSize, completionMsgData);
if (!cmd) return kSCErr_Failed;
if (inWorld->mRealTime) cmd->CallNextStage();
else cmd->CallEveryStage();
return kSCErr_None;
}
///////////////////////////////////////////////////////////////////////////
AsyncPlugInCmd::AsyncPlugInCmd(World *inWorld, ReplyAddress *inReplyAddress,
const char* cmdName,
void *cmdData,
AsyncStageFn stage2, // stage2 is non real time
AsyncStageFn stage3, // stage3 is real time - completion msg performed if stage3 returns true
AsyncStageFn stage4, // stage4 is non real time - sends done if stage4 returns true
AsyncFreeFn cleanup, // cleanup is called in real time
int completionMsgSize,
void* completionMsgData)
: SC_SequencedCommand(inWorld, inReplyAddress),
mCmdName(cmdName), mCmdData(cmdData),
mStage2(stage2), mStage3(stage3), mStage4(stage4),
mCleanup(cleanup)
{
if (completionMsgSize && completionMsgData) {
mMsgSize = completionMsgSize;
mMsgData = (char*)World_Alloc(mWorld, mMsgSize);
memcpy(mMsgData, completionMsgData, mMsgSize);
}
}
AsyncPlugInCmd::~AsyncPlugInCmd()
{
(mCleanup)(mWorld, mCmdData);
if (mMsgData) World_Free(mWorld, mMsgData);
}
void AsyncPlugInCmd::CallDestructor()
{
this->~AsyncPlugInCmd();
}
bool AsyncPlugInCmd::Stage2()
{
bool result = !mStage2 || (mStage2)(mWorld, mCmdData);
return result;
}
bool AsyncPlugInCmd::Stage3()
{
bool result = !mStage3 || (mStage3)(mWorld, mCmdData);
if (result) SEND_COMPLETION_MSG;
return result;
}
void AsyncPlugInCmd::Stage4()
{
bool result = !mStage4 || (mStage4)(mWorld, mCmdData);
if (result && mCmdName && mReplyAddress.mReplyFunc != null_reply_func) SendDone((char*)mCmdName);
}
Jump to Line
Something went wrong with that request. Please try again.