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