1 /*
2 SuperCollider real time audio synthesis system
3 Copyright (c) 2002 James McCartney. All rights reserved.
4 http://www.audiosynth.com
5
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.
10
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.
15
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
19 */
20
21 #ifdef _WIN32
22 # include "SC_Win32Utils.h"
23 #endif
24
25 #ifdef __APPLE__
26 # include "../../common/SC_Apple.hpp"
27 #endif
28
29
30 #include "SC_World.h"
31 #include "SC_WorldOptions.h"
32 #include "SC_HiddenWorld.h"
33 #include "SC_InterfaceTable.h"
34 #include "SC_AllocPool.h"
35 #include "SC_GraphDef.h"
36 #include "SC_UnitDef.h"
37 #include "SC_BufGen.h"
38 #include "SC_Node.h"
39 #include "SC_CoreAudio.h"
40 #include "SC_Group.h"
41 #include "SC_Errors.h"
42 #include <stdio.h>
43 #include "SC_Prototypes.h"
44 #include "SC_Filesystem.hpp"
45 #include "SC_Lock.h"
46 #include "SC_Lib_Cintf.h"
47 #include "../../common/SC_SndFileHelpers.hpp"
48 #include "../../common/Samp.hpp"
49 #include "SC_StringParser.h"
50 #ifdef _WIN32
51 # include <direct.h>
52 #else
53 # include <sys/param.h>
54 #endif
55
56 #include "malloc_aligned.hpp"
57
58 #include <boost/predef/hardware.h>
59
60 #if BOOST_HW_SIMD_X86 >= BOOST_HW_SIMD_X86_SSE_VERSION
61 # include <xmmintrin.h>
62 #endif
63
64 // undefine the shadowed scfft functions
65 #undef scfft_create
66 #undef scfft_dofft
67 #undef scfft_doifft
68 #undef scfft_destroy
69
70 #if (_POSIX_MEMLOCK - 0) >= 200112L
71 # include <sys/resource.h>
72 # include <sys/mman.h>
73 #endif
74
75 #include "server_shm.hpp"
76
77 #include <boost/filesystem/path.hpp> // path
78
79 namespace bfs = boost::filesystem;
80
81 InterfaceTable gInterfaceTable;
82 PrintFunc gPrint = nullptr;
83
84 extern HashTable<struct UnitDef, Malloc>* gUnitDefLib;
85 extern HashTable<struct BufGen, Malloc>* gBufGenLib;
86 extern HashTable<PlugInCmd, Malloc>* gPlugInCmds;
87
88 extern "C" {
89
90 #ifdef NO_LIBSNDFILE
91 struct SF_INFO {};
92 #endif
93
94 bool SendMsgToEngine(World* inWorld, FifoMsg& inMsg);
95 bool SendMsgFromEngine(World* inWorld, FifoMsg& inMsg);
96 }
97
98 ////////////////////////////////////////////////////////////////////////////////
99
sc_malloc(size_t size)100 inline void* sc_malloc(size_t size) { return nova::malloc_aligned(size); }
101
sc_dbg_malloc(size_t size,const char * tag,int line)102 void* sc_dbg_malloc(size_t size, const char* tag, int line) {
103 void* ptr = sc_malloc(size);
104 fprintf(stderr, "sc_dbg_malloc [%s:%d] %p %zu\n", tag, line, ptr, size);
105 #if SC_MEMORY_ALIGNMENT > 1
106 if (((intptr_t)ptr % SC_MEMORY_ALIGNMENT) != 0) {
107 fprintf(stderr, "sc_dbg_malloc [%s:%d] %p %zu: memory alignment error\n", tag, line, ptr, size);
108 abort();
109 }
110 #endif
111 return ptr;
112 }
113
sc_free(void * ptr)114 inline void sc_free(void* ptr) { return nova::free_aligned(ptr); }
115
sc_dbg_free(void * ptr,const char * tag,int line)116 void sc_dbg_free(void* ptr, const char* tag, int line) {
117 fprintf(stderr, "sc_dbg_free [%s:%d]: %p\n", tag, line, ptr);
118 sc_free(ptr);
119 }
120
sc_zalloc(size_t n,size_t size)121 inline void* sc_zalloc(size_t n, size_t size) {
122 size *= n;
123 if (size) {
124 void* ptr = sc_malloc(size);
125 if (ptr) {
126 memset(ptr, 0, size);
127 return ptr;
128 }
129 }
130 return nullptr;
131 }
132
sc_dbg_zalloc(size_t n,size_t size,const char * tag,int line)133 void* sc_dbg_zalloc(size_t n, size_t size, const char* tag, int line) {
134 void* ptr = sc_zalloc(n, size);
135 fprintf(stderr, "sc_dbg_zalloc [%s:%d]: %p %zu %zu\n", tag, line, ptr, n, size);
136 return ptr;
137 }
138
139 #if SC_DEBUG_MEMORY
140 # define malloc_alig(size) sc_dbg_malloc((size), __FUNCTION__, __LINE__)
141 # define free_alig(ptr) sc_dbg_free((ptr), __FUNCTION__, __LINE__)
142 # define zalloc_(n, size) sc_dbg_zalloc((n), (size), __FUNCTION__, __LINE__)
143 #else
144 # define malloc_alig(size) sc_malloc((size))
145 # define free_alig(ptr) sc_free((ptr))
146 # define zalloc_(n, size) sc_zalloc((n), (size))
147 #endif // SC_DEBUG_MEMORY
148
zalloc(size_t n,size_t size)149 void* zalloc(size_t n, size_t size) { return zalloc_(n, size); }
150
zfree(void * ptr)151 void zfree(void* ptr) { return free_alig(ptr); }
152
153
154 ////////////////////////////////////////////////////////////////////////////////
155
156 // Set denormal FTZ mode on CPUs that need/support it.
sc_SetDenormalFlags()157 void sc_SetDenormalFlags() {
158 #if BOOST_HW_SIMD_X86 >= BOOST_HW_SIMD_X86_SSE_VERSION
159 _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON);
160 _mm_setcsr(_mm_getcsr() | 0x40); // DAZ
161 #endif
162 }
163
164 ////////////////////////////////////////////////////////////////////////////////
165
166 static bool getScopeBuffer(World* inWorld, int index, int channels, int maxFrames, ScopeBufferHnd& hnd);
167 static void pushScopeBuffer(World* inWorld, ScopeBufferHnd& hnd, int frames);
168 static void releaseScopeBuffer(World* inWorld, ScopeBufferHnd& hnd);
169
InterfaceTable_Init()170 void InterfaceTable_Init() {
171 InterfaceTable* ft = &gInterfaceTable;
172
173 ft->mSine = gSine;
174 ft->mCosecant = gInvSine;
175 ft->mSineSize = kSineSize;
176 ft->mSineWavetable = gSineWavetable;
177
178 ft->fPrint = &scprintf;
179
180 ft->fRanSeed = &server_timeseed;
181
182 ft->fNodeEnd = &Node_End;
183
184 ft->fDefineUnit = &UnitDef_Create;
185 ft->fDefineBufGen = &BufGen_Create;
186 ft->fClearUnitOutputs = &Unit_ZeroOutputs;
187
188 ft->fNRTAlloc = &malloc;
189 ft->fNRTRealloc = &realloc;
190 ft->fNRTFree = &free;
191
192 ft->fRTAlloc = &World_Alloc;
193 ft->fRTRealloc = &World_Realloc;
194 ft->fRTFree = &World_Free;
195
196 ft->fNodeRun = &Node_SetRun;
197
198 ft->fSendTrigger = &Node_SendTrigger;
199 ft->fSendNodeReply = &Node_SendReply;
200
201
202 ft->fDefineUnitCmd = &UnitDef_AddCmd;
203 ft->fDefinePlugInCmd = &PlugIn_DefineCmd;
204
205 ft->fSendMsgFromRT = &SendMsgFromEngine;
206 ft->fSendMsgToRT = &SendMsgToEngine;
207 #ifdef NO_LIBSNDFILE
208 ft->fSndFileFormatInfoFromStrings = NULL;
209 #else
210 ft->fSndFileFormatInfoFromStrings = &sndfileFormatInfoFromStrings;
211 #endif
212 ft->fGetNode = &World_GetNode;
213 ft->fGetGraph = &World_GetGraph;
214
215 ft->fNRTLock = &World_NRTLock;
216 ft->fNRTUnlock = &World_NRTUnlock;
217
218 ft->mUnused0 = false;
219
220 ft->fGroup_DeleteAll = &Group_DeleteAll;
221 ft->fDoneAction = &Unit_DoneAction;
222 ft->fDoAsynchronousCommand = &PerformAsynchronousCommand;
223 ft->fBufAlloc = &bufAlloc;
224
225 ft->fSCfftCreate = &scfft_create;
226 ft->fSCfftDestroy = &scfft_destroy;
227 ft->fSCfftDoFFT = &scfft_dofft;
228 ft->fSCfftDoIFFT = &scfft_doifft;
229
230 ft->fGetScopeBuffer = &getScopeBuffer;
231 ft->fPushScopeBuffer = &pushScopeBuffer;
232 ft->fReleaseScopeBuffer = &releaseScopeBuffer;
233 }
234
235 void initialize_library(const char* mUGensPluginPath);
236 void initializeScheduler();
237
238 static void World_LoadGraphDefs(World* world);
World_LoadGraphDefs(World * world)239 void World_LoadGraphDefs(World* world) {
240 GraphDef* list = nullptr;
241 using DirName = SC_Filesystem::DirName;
242
243 if (getenv("SC_SYNTHDEF_PATH")) {
244 if (world->mVerbosity > 0)
245 scprintf("Loading synthdefs from path: %s\n", getenv("SC_SYNTHDEF_PATH"));
246 SC_StringParser sp(getenv("SC_SYNTHDEF_PATH"), SC_STRPARSE_PATHDELIMITER);
247 while (!sp.AtEnd()) {
248 GraphDef* list = nullptr;
249 char* path = const_cast<char*>(sp.NextToken());
250 list = GraphDef_LoadDir(world, path, list);
251 GraphDef_Define(world, list);
252 }
253 } else {
254 bfs::path path = SC_Filesystem::instance().getDirectory(DirName::UserAppSupport) / "synthdefs";
255 if (world->mVerbosity > 0)
256 scprintf("Loading synthdefs from default path: %s\n", SC_Codecvt::path_to_utf8_str(path).c_str());
257 list = GraphDef_LoadDir(world, path, list);
258 GraphDef_Define(world, list);
259 }
260 }
261
262 namespace scsynth {
263 void startAsioThread();
264 void stopAsioThread();
265 bool asioThreadStarted();
266 }
267
268
World_New(WorldOptions * inOptions)269 World* World_New(WorldOptions* inOptions) {
270 #if (_POSIX_MEMLOCK - 0) >= 200112L
271 if (inOptions->mMemoryLocking && inOptions->mRealTime) {
272 bool lock_memory = false;
273
274 rlimit limit;
275
276 int failure = getrlimit(RLIMIT_MEMLOCK, &limit);
277 if (failure)
278 scprintf("getrlimit failure\n");
279 else {
280 if (limit.rlim_cur == RLIM_INFINITY and limit.rlim_max == RLIM_INFINITY)
281 lock_memory = true;
282 else
283 scprintf("memory locking disabled due to resource limiting\n");
284
285 if (lock_memory) {
286 if (mlockall(MCL_FUTURE) != -1)
287 scprintf("memory locking enabled.\n");
288 }
289 }
290 }
291 #endif
292
293 World* world = nullptr;
294
295 try {
296 static bool gLibInitted = false;
297 if (!gLibInitted) {
298 InterfaceTable_Init();
299 initialize_library(inOptions->mUGensPluginPath);
300 initializeScheduler();
301 gLibInitted = true;
302 }
303
304 world = (World*)zalloc(1, sizeof(World));
305
306 world->hw = (HiddenWorld*)zalloc(1, sizeof(HiddenWorld));
307
308 world->hw->mAllocPool = new AllocPool(malloc, free, inOptions->mRealTimeMemorySize * 1024, 0);
309 world->hw->mQuitProgram = new boost::sync::semaphore(0);
310 world->hw->mTerminating = false;
311
312 HiddenWorld* hw = world->hw;
313 hw->mGraphDefLib = new HashTable<struct GraphDef, Malloc>(&gMalloc, inOptions->mMaxGraphDefs, false);
314 hw->mNodeLib = new IntHashTable<Node, AllocPool>(hw->mAllocPool, inOptions->mMaxNodes, false);
315 hw->mUsers = new Clients();
316 hw->mMaxUsers = inOptions->mMaxLogins;
317 hw->mAvailableClientIDs = new ClientIDs();
318 for (int i = 0; i < hw->mMaxUsers; i++) {
319 hw->mAvailableClientIDs->push_back(i);
320 }
321 hw->mClientIDdict = new ClientIDDict();
322 hw->mHiddenID = -8;
323 hw->mRecentID = -8;
324
325
326 world->mNumUnits = 0;
327 world->mNumGraphs = 0;
328 world->mNumGroups = 0;
329
330 world->mBufCounter = 0;
331 world->mBufLength = inOptions->mBufLength;
332 world->mSampleOffset = 0;
333 world->mSubsampleOffset = 0.f;
334 world->mNumAudioBusChannels = inOptions->mNumAudioBusChannels;
335 world->mNumControlBusChannels = inOptions->mNumControlBusChannels;
336 world->mNumInputs = inOptions->mNumInputBusChannels;
337 world->mNumOutputs = inOptions->mNumOutputBusChannels;
338
339 world->mVerbosity = inOptions->mVerbosity;
340 world->mErrorNotification = 1; // i.e., 0x01 | 0x02
341 world->mLocalErrorNotification = 0;
342
343 if (inOptions->mSharedMemoryID) {
344 server_shared_memory_creator::cleanup(inOptions->mSharedMemoryID);
345 hw->mShmem =
346 new server_shared_memory_creator(inOptions->mSharedMemoryID, inOptions->mNumControlBusChannels);
347 world->mControlBus = hw->mShmem->get_control_busses();
348 } else {
349 hw->mShmem = nullptr;
350 world->mControlBus = (float*)zalloc(world->mNumControlBusChannels, sizeof(float));
351 }
352
353 world->mNumSharedControls = 0;
354 world->mSharedControls = inOptions->mSharedControls;
355
356 int numsamples = world->mBufLength * world->mNumAudioBusChannels;
357 world->mAudioBus = (float*)zalloc(numsamples, sizeof(float));
358
359 world->mAudioBusTouched = (int32*)zalloc(inOptions->mNumAudioBusChannels, sizeof(int32));
360 world->mControlBusTouched = (int32*)zalloc(inOptions->mNumControlBusChannels, sizeof(int32));
361
362 world->mNumSndBufs = inOptions->mNumBuffers;
363 world->mSndBufs = (SndBuf*)zalloc(world->mNumSndBufs, sizeof(SndBuf));
364 world->mSndBufsNonRealTimeMirror = (SndBuf*)zalloc(world->mNumSndBufs, sizeof(SndBuf));
365 world->mSndBufUpdates = (SndBufUpdates*)zalloc(world->mNumSndBufs, sizeof(SndBufUpdates));
366
367 GroupNodeDef_Init();
368
369 int err = Group_New(world, 0, &world->mTopGroup);
370 if (err)
371 throw err;
372
373 world->mRealTime = inOptions->mRealTime;
374
375 world->ft = &gInterfaceTable;
376
377 world->mNumRGens = inOptions->mNumRGens;
378 world->mRGen = new RGen[world->mNumRGens];
379 for (uint32 i = 0; i < world->mNumRGens; ++i) {
380 world->mRGen[i].init(server_timeseed());
381 }
382
383 world->mNRTLock = new SC_Lock();
384 world->mDriverLock = new SC_Lock();
385
386 if (inOptions->mPassword) {
387 strncpy(world->hw->mPassword, inOptions->mPassword, 31);
388 world->hw->mPassword[31] = 0;
389 } else {
390 world->hw->mPassword[0] = 0;
391 }
392
393 #ifdef __APPLE__
394 world->hw->mInputStreamsEnabled = inOptions->mInputStreamsEnabled;
395 world->hw->mOutputStreamsEnabled = inOptions->mOutputStreamsEnabled;
396 #endif
397 world->hw->mInDeviceName = inOptions->mInDeviceName;
398 world->hw->mOutDeviceName = inOptions->mOutDeviceName;
399 hw->mMaxWireBufs = inOptions->mMaxWireBufs;
400 hw->mWireBufSpace = nullptr;
401
402 world->mRendezvous = inOptions->mRendezvous;
403
404 world->mRestrictedPath = inOptions->mRestrictedPath;
405
406 sc_SetDenormalFlags();
407
408 if (world->mRealTime) {
409 hw->mAudioDriver = SC_NewAudioDriver(world);
410 hw->mAudioDriver->SetPreferredHardwareBufferFrameSize(inOptions->mPreferredHardwareBufferFrameSize);
411 hw->mAudioDriver->SetPreferredSampleRate(inOptions->mPreferredSampleRate);
412
413 if (inOptions->mLoadGraphDefs) {
414 World_LoadGraphDefs(world);
415 }
416
417 if (!hw->mAudioDriver->Setup()) {
418 scprintf("could not initialize audio.\n");
419 return nullptr;
420 }
421 if (!hw->mAudioDriver->Start()) {
422 scprintf("start audio failed.\n");
423 return nullptr;
424 }
425
426 #ifdef __APPLE__
427 SC::Apple::disableAppNap();
428 #endif
429
430
431 } else {
432 hw->mAudioDriver = nullptr;
433 }
434
435 if (!scsynth::asioThreadStarted()) {
436 scsynth::startAsioThread();
437 }
438
439 } catch (std::exception& exc) {
440 scprintf("Exception in World_New: %s\n", exc.what());
441 World_Cleanup(world, true);
442 return nullptr;
443 } catch (...) {
444 }
445 return world;
446 }
447
World_CopySndBuf(World * world,uint32 index,SndBuf * outBuf,bool onlyIfChanged,bool * outDidChange)448 int World_CopySndBuf(World* world, uint32 index, SndBuf* outBuf, bool onlyIfChanged, bool* outDidChange) {
449 if (index > world->mNumSndBufs)
450 return kSCErr_IndexOutOfRange;
451
452 SndBufUpdates* updates = world->mSndBufUpdates + index;
453 bool didChange = updates->reads != updates->writes;
454
455 if (!onlyIfChanged || didChange) {
456 reinterpret_cast<SC_Lock*>(world->mNRTLock)->lock();
457
458 SndBuf* buf = world->mSndBufsNonRealTimeMirror + index;
459
460 if (buf->data && buf->samples) {
461 uint32 bufSize = buf->samples * sizeof(float);
462 if (buf->samples != outBuf->samples) {
463 free_alig(outBuf->data);
464 outBuf->data = (float*)malloc_alig(bufSize);
465 }
466 memcpy(outBuf->data, buf->data, bufSize);
467 outBuf->channels = buf->channels;
468 outBuf->samples = buf->samples;
469 outBuf->frames = buf->frames;
470 outBuf->mask = buf->mask;
471 outBuf->mask1 = buf->mask1;
472 } else {
473 free_alig(outBuf->data);
474 outBuf->data = nullptr;
475 outBuf->channels = 0;
476 outBuf->samples = 0;
477 outBuf->frames = 0;
478 outBuf->mask = 0;
479 outBuf->mask1 = 0;
480 }
481
482 outBuf->samplerate = buf->samplerate;
483 outBuf->sampledur = buf->sampledur;
484 outBuf->coord = buf->coord;
485 outBuf->sndfile = nullptr;
486
487 updates->reads = updates->writes;
488
489 reinterpret_cast<SC_Lock*>(world->mNRTLock)->unlock();
490 }
491
492 if (outDidChange)
493 *outDidChange = didChange;
494
495 return kSCErr_None;
496 }
497
nextOSCPacket(FILE * file,OSC_Packet * packet,int64 & outTime)498 bool nextOSCPacket(FILE* file, OSC_Packet* packet, int64& outTime) {
499 int32 msglen;
500 if (fread(&msglen, 1, sizeof(int32), file) != sizeof(int32))
501 return true;
502 // msglen is in network byte order
503 msglen = OSCint((char*)&msglen);
504 if (msglen > 1073741824) {
505 throw std::runtime_error("OSC packet too long. > 2^30 bytes\n");
506 }
507 packet->mData = (char*)realloc((void*)packet->mData, (size_t)msglen);
508 if (!packet->mData)
509 throw std::runtime_error("nextOSCPacket: realloc failed...\n");
510
511 size_t read = fread(packet->mData, 1, msglen, file);
512 if (read != msglen)
513 throw std::runtime_error("nextOSCPacket: invalid read of OSC packet\n");
514
515 if (strcmp(packet->mData, "#bundle") != 0)
516 throw std::runtime_error("OSC packet not a bundle\n");
517
518 packet->mSize = msglen;
519
520 outTime = OSCtime(packet->mData + 8);
521 return false;
522 }
523
524 void PerformOSCBundle(World* inWorld, OSC_Packet* inPacket);
525
526 #ifndef NO_LIBSNDFILE
World_NonRealTimeSynthesis(struct World * world,WorldOptions * inOptions)527 void World_NonRealTimeSynthesis(struct World* world, WorldOptions* inOptions) {
528 if (inOptions->mLoadGraphDefs) {
529 World_LoadGraphDefs(world);
530 }
531 int bufLength = world->mBufLength;
532 int fileBufFrames = inOptions->mPreferredHardwareBufferFrameSize;
533 if (fileBufFrames <= 0)
534 fileBufFrames = 8192;
535 int bufMultiple = (fileBufFrames + bufLength - 1) / bufLength;
536 fileBufFrames = bufMultiple * bufLength;
537
538 // batch process non real time audio
539 if (!inOptions->mNonRealTimeOutputFilename)
540 throw std::runtime_error("Non real time output filename is NULL.\n");
541
542 SF_INFO inputFileInfo, outputFileInfo;
543 float* inputFileBuf = nullptr;
544 float* outputFileBuf = nullptr;
545 int numInputChannels = 0;
546 int numOutputChannels;
547
548 outputFileInfo.samplerate = inOptions->mPreferredSampleRate;
549 numOutputChannels = outputFileInfo.channels = world->mNumOutputs;
550 sndfileFormatInfoFromStrings(&outputFileInfo, inOptions->mNonRealTimeOutputHeaderFormat,
551 inOptions->mNonRealTimeOutputSampleFormat);
552
553 world->hw->mNRTOutputFile = sndfileOpenFromCStr(inOptions->mNonRealTimeOutputFilename, SFM_WRITE, &outputFileInfo);
554 sf_command(world->hw->mNRTOutputFile, SFC_SET_CLIPPING, nullptr, SF_TRUE);
555
556 if (!world->hw->mNRTOutputFile)
557 throw std::runtime_error("Couldn't open non real time output file.\n");
558
559 outputFileBuf = (float*)calloc(1, world->mNumOutputs * fileBufFrames * sizeof(float));
560
561 if (inOptions->mNonRealTimeInputFilename) {
562 world->hw->mNRTInputFile = sndfileOpenFromCStr(inOptions->mNonRealTimeInputFilename, SFM_READ, &inputFileInfo);
563 if (!world->hw->mNRTInputFile)
564 throw std::runtime_error("Couldn't open non real time input file.\n");
565
566 inputFileBuf = (float*)calloc(1, inputFileInfo.channels * fileBufFrames * sizeof(float));
567
568 if (world->mNumInputs != (uint32)inputFileInfo.channels)
569 scprintf("WARNING: input file channels didn't match number of inputs specified in options.\n");
570
571 numInputChannels = world->mNumInputs = inputFileInfo.channels; // force it.
572
573 if (inputFileInfo.samplerate != (int)inOptions->mPreferredSampleRate)
574 scprintf("WARNING: input file sample rate does not equal output sample rate.\n");
575
576 } else {
577 world->hw->mNRTInputFile = nullptr;
578 }
579
580 FILE* cmdFile;
581 if (inOptions->mNonRealTimeCmdFilename) {
582 # ifdef _WIN32
583 cmdFile = fopen(inOptions->mNonRealTimeCmdFilename, "rb");
584 # else
585 cmdFile = fopen(inOptions->mNonRealTimeCmdFilename, "r");
586 # endif
587 } else
588 cmdFile = stdin;
589 if (!cmdFile)
590 throw std::runtime_error("Couldn't open non real time command file.\n");
591
592 OSC_Packet packet;
593 memset(&packet, 0, sizeof(packet));
594 packet.mData = (char*)malloc(8192);
595 packet.mIsBundle = true;
596 packet.mReplyAddr.mReplyFunc = null_reply_func;
597
598 int64 schedTime;
599 if (nextOSCPacket(cmdFile, &packet, schedTime))
600 throw std::runtime_error("command file empty.\n");
601 int64 prevTime = schedTime;
602
603 World_SetSampleRate(world, inOptions->mPreferredSampleRate);
604 World_Start(world);
605
606 int64 oscTime = 0;
607 double oscToSeconds = 1. / pow(2., 32.);
608 double oscToSamples = inOptions->mPreferredSampleRate * oscToSeconds;
609 int64 oscInc = (int64)((double)bufLength / oscToSamples);
610
611 if (inOptions->mVerbosity >= 0) {
612 printf("start time %g\n", schedTime * oscToSeconds);
613 }
614
615 bool run = true;
616 int inBufStep = numInputChannels * bufLength;
617 int outBufStep = numOutputChannels * bufLength;
618 float* inputBuses = world->mAudioBus + world->mNumOutputs * bufLength;
619 float* outputBuses = world->mAudioBus;
620 int32* inputTouched = world->mAudioBusTouched + world->mNumOutputs;
621 int32* outputTouched = world->mAudioBusTouched;
622 for (; run;) {
623 int bufFramesCalculated = 0;
624 float* inBufPos = inputFileBuf;
625 float* outBufPos = outputFileBuf;
626
627 if (world->hw->mNRTInputFile) {
628 int framesRead = sf_readf_float(world->hw->mNRTInputFile, inputFileBuf, fileBufFrames);
629 if (framesRead < fileBufFrames) {
630 memset(inputFileBuf + framesRead * numInputChannels, 0,
631 (fileBufFrames - framesRead) * numInputChannels * sizeof(float));
632 }
633 }
634
635 for (int i = 0; i < bufMultiple && run; ++i) {
636 int bufCounter = world->mBufCounter;
637
638 // deinterleave input to input buses
639 if (inputFileBuf) {
640 float* inBus = inputBuses;
641 for (int j = 0; j < numInputChannels; ++j, inBus += bufLength) {
642 float* inFileBufPtr = inBufPos + j;
643 for (int k = 0; k < bufLength; ++k) {
644 inBus[k] = *inFileBufPtr;
645 inFileBufPtr += numInputChannels;
646 }
647 inputTouched[j] = bufCounter;
648 }
649 }
650
651 // execute ready commands
652 int64 nextTime = oscTime + oscInc;
653
654 while (schedTime <= nextTime) {
655 float diffTime = (float)(schedTime - oscTime) * oscToSamples + 0.5;
656 float diffTimeFloor = floor(diffTime);
657 world->mSampleOffset = (int)diffTimeFloor;
658 world->mSubsampleOffset = diffTime - diffTimeFloor;
659
660 if (world->mSampleOffset < 0)
661 world->mSampleOffset = 0;
662 else if (world->mSampleOffset >= bufLength)
663 world->mSampleOffset = bufLength - 1;
664
665
666 PerformOSCBundle(world, &packet);
667 if (nextOSCPacket(cmdFile, &packet, schedTime)) {
668 run = false;
669 break;
670 }
671 if (inOptions->mVerbosity >= 0) {
672 printf("nextOSCPacket %g\n", schedTime * oscToSeconds);
673 }
674 if (schedTime < prevTime) {
675 scprintf("ERROR: Packet time stamps out-of-order.\n");
676 run = false;
677 goto Bail;
678 }
679 prevTime = schedTime;
680 }
681
682 World_Run(world);
683
684 // interleave output to output buffer
685 float* outBus = outputBuses;
686 for (int j = 0; j < numOutputChannels; ++j, outBus += bufLength) {
687 float* outFileBufPtr = outBufPos + j;
688 if (outputTouched[j] == bufCounter) {
689 for (int k = 0; k < bufLength; ++k) {
690 *outFileBufPtr = outBus[k];
691 outFileBufPtr += numOutputChannels;
692 }
693 } else {
694 for (int k = 0; k < bufLength; ++k) {
695 *outFileBufPtr = 0.f;
696 outFileBufPtr += numOutputChannels;
697 }
698 }
699 }
700 bufFramesCalculated += bufLength;
701 inBufPos += inBufStep;
702 outBufPos += outBufStep;
703 world->mBufCounter++;
704 oscTime = nextTime;
705 }
706
707 Bail:
708 // write output
709 sf_writef_float(world->hw->mNRTOutputFile, outputFileBuf, bufFramesCalculated);
710 }
711
712 if (cmdFile != stdin)
713 fclose(cmdFile);
714 sf_close(world->hw->mNRTOutputFile);
715 world->hw->mNRTOutputFile = nullptr;
716
717 if (world->hw->mNRTInputFile) {
718 sf_close(world->hw->mNRTInputFile);
719 world->hw->mNRTInputFile = nullptr;
720 }
721
722 free(packet.mData);
723 World_Cleanup(world, true);
724 }
725 #endif // !NO_LIBSNDFILE
726
World_WaitForQuit(struct World * inWorld,bool unload_plugins)727 void World_WaitForQuit(struct World* inWorld, bool unload_plugins) {
728 try {
729 inWorld->hw->mQuitProgram->wait();
730 World_Cleanup(inWorld, unload_plugins);
731 } catch (std::exception& exc) {
732 scprintf("Exception in World_WaitForQuit: %s\n", exc.what());
733 } catch (...) {
734 }
735 }
736
World_SetSampleRate(World * inWorld,double inSampleRate)737 void World_SetSampleRate(World* inWorld, double inSampleRate) {
738 inWorld->mSampleRate = inSampleRate;
739 Rate_Init(&inWorld->mFullRate, inSampleRate, inWorld->mBufLength);
740 Rate_Init(&inWorld->mBufRate, inSampleRate / inWorld->mBufLength, 1);
741 }
742
743 ////////////////////////////////////////////////////////////////////////////////
744
World_Alloc(World * inWorld,size_t inByteSize)745 void* World_Alloc(World* inWorld, size_t inByteSize) { return inWorld->hw->mAllocPool->Alloc(inByteSize); }
746
World_Realloc(World * inWorld,void * inPtr,size_t inByteSize)747 void* World_Realloc(World* inWorld, void* inPtr, size_t inByteSize) {
748 return inWorld->hw->mAllocPool->Realloc(inPtr, inByteSize);
749 }
750
World_TotalFree(World * inWorld)751 size_t World_TotalFree(World* inWorld) { return inWorld->hw->mAllocPool->TotalFree(); }
752
World_LargestFreeChunk(World * inWorld)753 size_t World_LargestFreeChunk(World* inWorld) { return inWorld->hw->mAllocPool->LargestFreeChunk(); }
754
World_Free(World * inWorld,void * inPtr)755 void World_Free(World* inWorld, void* inPtr) { inWorld->hw->mAllocPool->Free(inPtr); }
756
757 ////////////////////////////////////////////////////////////////////////////////
758
GetKey(GraphDef * inGraphDef)759 int32* GetKey(GraphDef* inGraphDef) { return inGraphDef->mNodeDef.mName; }
760
GetHash(GraphDef * inGraphDef)761 int32 GetHash(GraphDef* inGraphDef) { return inGraphDef->mNodeDef.mHash; }
762
World_AddGraphDef(World * inWorld,GraphDef * inGraphDef)763 void World_AddGraphDef(World* inWorld, GraphDef* inGraphDef) {
764 bool added = inWorld->hw->mGraphDefLib->Add(inGraphDef);
765 if (!added)
766 scprintf(
767 "ERROR: Could not add SynthDef %s.\nTry adjusting ServerOptions:maxSynthDefs or the -d cmdline flag.\n",
768 (char*)inGraphDef->mNodeDef.mName);
769 for (uint32 i = 0; i < inGraphDef->mNumVariants; ++i) {
770 GraphDef* var = inGraphDef->mVariants + i;
771 added = inWorld->hw->mGraphDefLib->Add(var);
772 if (!added)
773 scprintf(
774 "ERROR: Could not add SynthDef %s.\nTry adjusting ServerOptions:maxSynthDefs or the -d cmdline flag.\n",
775 (char*)var->mNodeDef.mName);
776 }
777 }
778
World_RemoveGraphDef(World * inWorld,GraphDef * inGraphDef)779 void World_RemoveGraphDef(World* inWorld, GraphDef* inGraphDef) {
780 for (uint32 i = 0; i < inGraphDef->mNumVariants; ++i) {
781 GraphDef* var = inGraphDef->mVariants + i;
782 inWorld->hw->mGraphDefLib->Remove(var);
783 }
784 inWorld->hw->mGraphDefLib->Remove(inGraphDef);
785 }
786
World_FreeAllGraphDefs(World * inWorld)787 void World_FreeAllGraphDefs(World* inWorld) {
788 GrafDefTable* lib = inWorld->hw->mGraphDefLib;
789 int size = lib->TableSize();
790 for (int i = 0; i < size; ++i) {
791 GraphDef* def = lib->AtIndex(i);
792 if (def)
793 GraphDef_Free(def);
794 }
795 lib->MakeEmpty();
796 }
797
World_GetGraphDef(World * inWorld,int32 * inKey)798 GraphDef* World_GetGraphDef(World* inWorld, int32* inKey) { return inWorld->hw->mGraphDefLib->Get(inKey); }
799
800 ////////////////////////////////////////////////////////////////////////////////
801
GetKey(UnitDef * inUnitDef)802 int32* GetKey(UnitDef* inUnitDef) { return inUnitDef->mUnitDefName; }
803
GetHash(UnitDef * inUnitDef)804 int32 GetHash(UnitDef* inUnitDef) { return inUnitDef->mHash; }
805
AddUnitDef(UnitDef * inUnitDef)806 bool AddUnitDef(UnitDef* inUnitDef) { return gUnitDefLib->Add(inUnitDef); }
807
RemoveUnitDef(UnitDef * inUnitDef)808 bool RemoveUnitDef(UnitDef* inUnitDef) { return gUnitDefLib->Remove(inUnitDef); }
809
GetUnitDef(int32 * inKey)810 UnitDef* GetUnitDef(int32* inKey) { return gUnitDefLib->Get(inKey); }
811
812 ////////////////////////////////////////////////////////////////////////////////
813
GetKey(BufGen * inBufGen)814 int32* GetKey(BufGen* inBufGen) { return inBufGen->mBufGenName; }
815
GetHash(BufGen * inBufGen)816 int32 GetHash(BufGen* inBufGen) { return inBufGen->mHash; }
817
AddBufGen(BufGen * inBufGen)818 bool AddBufGen(BufGen* inBufGen) { return gBufGenLib->Add(inBufGen); }
819
RemoveBufGen(BufGen * inBufGen)820 bool RemoveBufGen(BufGen* inBufGen) { return gBufGenLib->Remove(inBufGen); }
821
GetBufGen(int32 * inKey)822 BufGen* GetBufGen(int32* inKey) { return gBufGenLib->Get(inKey); }
823
824 ////////////////////////////////////////////////////////////////////////////////
825
GetKey(PlugInCmd * inPlugInCmd)826 int32* GetKey(PlugInCmd* inPlugInCmd) { return inPlugInCmd->mCmdName; }
827
GetHash(PlugInCmd * inPlugInCmd)828 int32 GetHash(PlugInCmd* inPlugInCmd) { return inPlugInCmd->mHash; }
829
AddPlugInCmd(PlugInCmd * inPlugInCmd)830 bool AddPlugInCmd(PlugInCmd* inPlugInCmd) { return gPlugInCmds->Add(inPlugInCmd); }
831
RemovePlugInCmd(PlugInCmd * inPlugInCmd)832 bool RemovePlugInCmd(PlugInCmd* inPlugInCmd) { return gPlugInCmds->Remove(inPlugInCmd); }
833
GetPlugInCmd(int32 * inKey)834 PlugInCmd* GetPlugInCmd(int32* inKey) { return gPlugInCmds->Get(inKey); }
835
836 ////////////////////////////////////////////////////////////////////////////////
837
GetKey(Node * inNode)838 int32 GetKey(Node* inNode) { return inNode->mID; }
839
GetHash(Node * inNode)840 int32 GetHash(Node* inNode) { return inNode->mHash; }
841
World_AddNode(World * inWorld,Node * inNode)842 bool World_AddNode(World* inWorld, Node* inNode) { return inWorld->hw->mNodeLib->Add(inNode); }
843
World_RemoveNode(World * inWorld,Node * inNode)844 bool World_RemoveNode(World* inWorld, Node* inNode) { return inWorld->hw->mNodeLib->Remove(inNode); }
845
World_GetNode(World * inWorld,int32 inID)846 Node* World_GetNode(World* inWorld, int32 inID) {
847 if (inID == -1)
848 inID = inWorld->hw->mRecentID;
849 return inWorld->hw->mNodeLib->Get(inID);
850 }
851
World_GetGraph(World * inWorld,int32 inID)852 Graph* World_GetGraph(World* inWorld, int32 inID) {
853 if (inID == -1)
854 inID = inWorld->hw->mRecentID;
855 Node* node = World_GetNode(inWorld, inID);
856 if (!node)
857 return nullptr;
858 return node->mIsGroup ? nullptr : (Graph*)node;
859 }
860
World_GetGroup(World * inWorld,int32 inID)861 Group* World_GetGroup(World* inWorld, int32 inID) {
862 Node* node = World_GetNode(inWorld, inID);
863 if (!node)
864 return nullptr;
865 return node->mIsGroup ? (Group*)node : nullptr;
866 }
867
868 ////////////////////////////////////////////////////////////////////////////////
869
World_Run(World * inWorld)870 void World_Run(World* inWorld) {
871 // run top group
872 Node* node = (Node*)inWorld->mTopGroup;
873 (*node->mCalcFunc)(node);
874 }
875
World_Start(World * inWorld)876 void World_Start(World* inWorld) {
877 inWorld->mBufCounter = 0;
878 for (uint32 i = 0; i < inWorld->mNumAudioBusChannels; ++i)
879 inWorld->mAudioBusTouched[i] = -1;
880 for (uint32 i = 0; i < inWorld->mNumControlBusChannels; ++i)
881 inWorld->mControlBusTouched[i] = -1;
882
883 inWorld->hw->mWireBufSpace = (float*)malloc_alig(inWorld->hw->mMaxWireBufs * inWorld->mBufLength * sizeof(float));
884
885 inWorld->hw->mTriggers.MakeEmpty();
886 inWorld->hw->mNodeMsgs.MakeEmpty();
887 inWorld->hw->mNodeEnds.MakeEmpty();
888 inWorld->mRunning = true;
889 }
890
World_Cleanup(World * world,bool unload_plugins)891 void World_Cleanup(World* world, bool unload_plugins) {
892 if (!world)
893 return;
894
895 if (scsynth::asioThreadStarted()) {
896 scsynth::stopAsioThread();
897 }
898
899 if (unload_plugins)
900 deinitialize_library();
901
902 HiddenWorld* hw = world->hw;
903
904 if (hw && world->mRealTime)
905 hw->mAudioDriver->Stop();
906
907 world->mRunning = false;
908
909 if (world->mTopGroup)
910 Group_DeleteAll(world->mTopGroup);
911
912 reinterpret_cast<SC_Lock*>(world->mDriverLock)->lock();
913 if (hw) {
914 sc_free(hw->mWireBufSpace);
915 delete hw->mAudioDriver;
916 hw->mAudioDriver = nullptr;
917 }
918 delete reinterpret_cast<SC_Lock*>(world->mNRTLock);
919 reinterpret_cast<SC_Lock*>(world->mDriverLock)->unlock();
920 delete reinterpret_cast<SC_Lock*>(world->mDriverLock);
921 World_Free(world, world->mTopGroup);
922
923 for (uint32 i = 0; i < world->mNumSndBufs; ++i) {
924 SndBuf* nrtbuf = world->mSndBufsNonRealTimeMirror + i;
925 SndBuf* rtbuf = world->mSndBufs + i;
926
927 if (nrtbuf->data)
928 free_alig(nrtbuf->data);
929 if (rtbuf->data && rtbuf->data != nrtbuf->data)
930 free_alig(rtbuf->data);
931
932 #ifndef NO_LIBSNDFILE
933 if (nrtbuf->sndfile)
934 sf_close(nrtbuf->sndfile);
935 if (rtbuf->sndfile && rtbuf->sndfile != nrtbuf->sndfile)
936 sf_close(rtbuf->sndfile);
937 #endif
938 }
939
940 free_alig(world->mSndBufsNonRealTimeMirror);
941 free_alig(world->mSndBufs);
942
943 free_alig(world->mControlBusTouched);
944 free_alig(world->mAudioBusTouched);
945 if (hw->mShmem) {
946 delete hw->mShmem;
947 } else
948 free_alig(world->mControlBus);
949 free_alig(world->mAudioBus);
950 delete[] world->mRGen;
951 if (hw) {
952 #ifndef NO_LIBSNDFILE
953 if (hw->mNRTInputFile)
954 sf_close(hw->mNRTInputFile);
955 if (hw->mNRTOutputFile)
956 sf_close(hw->mNRTOutputFile);
957 if (hw->mNRTCmdFile)
958 fclose(hw->mNRTCmdFile);
959 #endif
960 delete hw->mUsers;
961 delete hw->mAvailableClientIDs;
962 delete hw->mClientIDdict;
963 delete hw->mNodeLib;
964 delete hw->mGraphDefLib;
965 delete hw->mQuitProgram;
966 delete hw->mAllocPool;
967 free_alig(hw);
968 }
969 free_alig(world);
970 }
971
972
World_NRTLock(World * world)973 void World_NRTLock(World* world) { reinterpret_cast<SC_Lock*>(world->mNRTLock)->lock(); }
974
World_NRTUnlock(World * world)975 void World_NRTUnlock(World* world) { reinterpret_cast<SC_Lock*>(world->mNRTLock)->unlock(); }
976
977 ////////////////////////////////////////////////////////////////////////////////
978
getScopeBuffer(World * inWorld,int index,int channels,int maxFrames,ScopeBufferHnd & hnd)979 bool getScopeBuffer(World* inWorld, int index, int channels, int maxFrames, ScopeBufferHnd& hnd) {
980 server_shared_memory_creator* shm = inWorld->hw->mShmem;
981
982 scope_buffer_writer writer = shm->get_scope_buffer_writer(index, channels, maxFrames);
983
984 if (writer.valid()) {
985 hnd.internalData = writer.buffer;
986 hnd.data = writer.data();
987 hnd.channels = channels;
988 hnd.maxFrames = maxFrames;
989 return true;
990 } else {
991 hnd.internalData = nullptr;
992 return false;
993 }
994 }
995
pushScopeBuffer(World * inWorld,ScopeBufferHnd & hnd,int frames)996 void pushScopeBuffer(World* inWorld, ScopeBufferHnd& hnd, int frames) {
997 scope_buffer_writer writer(reinterpret_cast<scope_buffer*>(hnd.internalData));
998 writer.push(frames);
999 hnd.data = writer.data();
1000 }
1001
releaseScopeBuffer(World * inWorld,ScopeBufferHnd & hnd)1002 void releaseScopeBuffer(World* inWorld, ScopeBufferHnd& hnd) {
1003 scope_buffer_writer writer(reinterpret_cast<scope_buffer*>(hnd.internalData));
1004 server_shared_memory_creator* shm = inWorld->hw->mShmem;
1005 shm->release_scope_buffer_writer(writer);
1006 }
1007
1008 ////////////////////////////////////////////////////////////////////////////////
1009
1010
BUFMASK(int32 x)1011 inline int32 BUFMASK(int32 x) { return (1 << (31 - CLZ(x))) - 1; }
1012
bufAlloc(SndBuf * buf,int numChannels,int numFrames,double sampleRate)1013 SCErr bufAlloc(SndBuf* buf, int numChannels, int numFrames, double sampleRate) {
1014 long numSamples = numFrames * numChannels;
1015 if (numSamples < 1)
1016 return kSCErr_Failed;
1017 buf->data = (float*)zalloc(numSamples, sizeof(float));
1018 if (!buf->data)
1019 return kSCErr_Failed;
1020
1021 buf->channels = numChannels;
1022 buf->frames = numFrames;
1023 buf->samples = numSamples;
1024 buf->mask = BUFMASK(numSamples); // for delay lines
1025 buf->mask1 = buf->mask - 1; // for oscillators
1026 buf->samplerate = sampleRate;
1027 buf->sampledur = 1. / sampleRate;
1028
1029 return kSCErr_None;
1030 }
1031
1032 #include "scsynthsend.h"
1033
Perform()1034 void TriggerMsg::Perform() {
1035 small_scpacket packet;
1036 packet.adds("/tr");
1037 packet.maketags(4);
1038 packet.addtag(',');
1039 packet.addtag('i');
1040 packet.addtag('i');
1041 packet.addtag('f');
1042 packet.addi(mNodeID);
1043 packet.addi(mTriggerID);
1044 packet.addf(mValue);
1045
1046 for (auto addr : *mWorld->hw->mUsers)
1047 SendReply(&addr, packet.data(), packet.size());
1048 }
1049
NodeReplyMsg_RTFree(FifoMsg * msg)1050 static void NodeReplyMsg_RTFree(FifoMsg* msg) {
1051 // scprintf("NodeReplyMsg_RTFree()\n");
1052 World_Free(msg->mWorld, msg->mData);
1053 }
1054
Perform()1055 void NodeReplyMsg::Perform() {
1056 small_scpacket packet;
1057 packet.adds(mCmdName, mCmdNameSize);
1058 packet.maketags(3 + mNumArgs);
1059 packet.addtag(',');
1060 packet.addtag('i');
1061 packet.addi(mNodeID);
1062 packet.addtag('i');
1063 packet.addi(mID);
1064 for (int i = 0; i < mNumArgs; ++i) {
1065 packet.addtag('f');
1066 packet.addf(mValues[i]);
1067 }
1068
1069 for (auto addr : *mWorld->hw->mUsers)
1070 SendReply(&addr, packet.data(), packet.size());
1071
1072 // Free memory in realtime thread
1073 FifoMsg msg;
1074 msg.Set(mWorld, NodeReplyMsg_RTFree, nullptr, mRTMemory);
1075 AudioDriver(mWorld)->SendMsgToEngine(msg);
1076 }
1077
1078
Perform()1079 void NodeEndMsg::Perform() {
1080 small_scpacket packet;
1081 switch (mState) {
1082 case kNode_Go:
1083 packet.adds("/n_go");
1084 break;
1085 case kNode_End:
1086 packet.adds("/n_end");
1087 break;
1088 case kNode_On:
1089 packet.adds("/n_on");
1090 break;
1091 case kNode_Off:
1092 packet.adds("/n_off");
1093 break;
1094 case kNode_Move:
1095 packet.adds("/n_move");
1096 break;
1097 case kNode_Info:
1098 packet.adds("/n_info");
1099 break;
1100 }
1101 if (mIsGroup) {
1102 packet.maketags(8);
1103 packet.addtag(',');
1104 packet.addtag('i');
1105 packet.addtag('i');
1106 packet.addtag('i');
1107 packet.addtag('i');
1108 packet.addtag('i');
1109 packet.addtag('i');
1110 packet.addtag('i');
1111 packet.addi(mNodeID);
1112 packet.addi(mGroupID);
1113 packet.addi(mPrevNodeID);
1114 packet.addi(mNextNodeID);
1115 packet.addi(mIsGroup);
1116 packet.addi(mHeadID);
1117 packet.addi(mTailID);
1118 } else {
1119 packet.maketags(6);
1120 packet.addtag(',');
1121 packet.addtag('i');
1122 packet.addtag('i');
1123 packet.addtag('i');
1124 packet.addtag('i');
1125 packet.addtag('i');
1126 packet.addi(mNodeID);
1127 packet.addi(mGroupID);
1128 packet.addi(mPrevNodeID);
1129 packet.addi(mNextNodeID);
1130 packet.addi(mIsGroup);
1131 }
1132
1133 for (auto addr : *mWorld->hw->mUsers)
1134 SendReply(&addr, packet.data(), packet.size());
1135 }
1136
Perform()1137 void DeleteGraphDefMsg::Perform() { GraphDef_Free(mDef); }
1138
1139 void NotifyNoArgs(World* inWorld, char* inString);
NotifyNoArgs(World * inWorld,char * inString)1140 void NotifyNoArgs(World* inWorld, char* inString) {
1141 small_scpacket packet;
1142 packet.adds(inString);
1143
1144 for (auto addr : *inWorld->hw->mUsers)
1145 SendReply(&addr, packet.data(), packet.size());
1146 }
1147
1148
SendMsgToEngine(World * inWorld,FifoMsg & inMsg)1149 bool SendMsgToEngine(World* inWorld, FifoMsg& inMsg) { return inWorld->hw->mAudioDriver->SendMsgToEngine(inMsg); }
1150
SendMsgFromEngine(World * inWorld,FifoMsg & inMsg)1151 bool SendMsgFromEngine(World* inWorld, FifoMsg& inMsg) { return inWorld->hw->mAudioDriver->SendMsgFromEngine(inMsg); }
1152
SetPrintFunc(PrintFunc func)1153 void SetPrintFunc(PrintFunc func) { gPrint = func; }
1154
1155
scprintf(const char * fmt,...)1156 int scprintf(const char* fmt, ...) {
1157 va_list vargs;
1158 va_start(vargs, fmt);
1159 if (gPrint)
1160 return (*gPrint)(fmt, vargs);
1161 else
1162 return vprintf(fmt, vargs);
1163 }
1164