1 //  prototype of a supercollider-synthdef-based synth prototype
2 //  Copyright (C) 2009-2013 Tim Blechmann
3 //
4 //  This program is free software; you can redistribute it and/or modify
5 //  it under the terms of the GNU General Public License as published by
6 //  the Free Software Foundation; either version 2 of the License, or
7 //  (at your option) any later version.
8 //
9 //  This program is distributed in the hope that it will be useful,
10 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
11 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 //  GNU General Public License for more details.
13 //
14 //  You should have received a copy of the GNU General Public License
15 //  along with this program; see the file COPYING.  If not, write to
16 //  the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 //  Boston, MA 02111-1307, USA.
18 
19 /* \todo for now we use dlopen, later we want to become cross-platform
20  */
21 
22 #ifdef DLOPEN
23 #    include <dlfcn.h>
24 #elif defined(_WIN32)
25 #    include "Windows.h"
26 #endif
27 
28 #include <boost/filesystem.hpp>
29 
30 #include "sc_ugen_factory.hpp"
31 
32 #include "SC_World.h"
33 #include "SC_Wire.h"
34 #include "ErrorMessage.hpp"
35 #include "SC_Filesystem.hpp" // SC_PLUGIN_EXT
36 
37 namespace nova {
38 
39 std::unique_ptr<sc_ugen_factory> sc_factory;
40 
construct(sc_synthdef::unit_spec_t const & unit_spec,sc_synth * parent,int parentIndex,World * world,linear_allocator & allocator)41 Unit* sc_ugen_def::construct(sc_synthdef::unit_spec_t const& unit_spec, sc_synth* parent, int parentIndex, World* world,
42                              linear_allocator& allocator) {
43     const int buffer_length = world->mBufLength;
44 
45     const size_t output_count = unit_spec.output_specs.size();
46 
47     /* size for wires and buffers */
48     uint8_t* chunk = allocator.alloc<uint8_t>(memory_requirement());
49     memset(chunk, 0, memory_requirement());
50 
51     Unit* unit = (Unit*)(std::uintptr_t(chunk + 63) & (~63)); // align on 64 byte boundary
52 
53     unit->mInBuf = allocator.alloc<float*>(unit_spec.input_specs.size());
54     unit->mOutBuf = allocator.alloc<float*>(unit_spec.output_specs.size());
55     unit->mInput = allocator.alloc<Wire*>(unit_spec.input_specs.size());
56     unit->mOutput = allocator.alloc<Wire*>(unit_spec.output_specs.size());
57 
58     unit->mNumInputs = unit_spec.input_specs.size();
59     unit->mNumOutputs = unit_spec.output_specs.size();
60 
61     /* initialize members */
62     unit->mCalcRate = unit_spec.rate;
63     unit->mSpecialIndex = unit_spec.special_index;
64     unit->mDone = false;
65     unit->mUnitDef = reinterpret_cast<struct UnitDef*>(this); /* we abuse this field to store our reference */
66     unit->mWorld = world;
67 
68     /* initialize members from synth */
69     unit->mParent = static_cast<Graph*>(parent);
70     unit->mParentIndex = parentIndex;
71     if (unit_spec.rate == 2)
72         unit->mRate = &world->mFullRate;
73     else
74         unit->mRate = &world->mBufRate;
75 
76     unit->mBufLength = unit->mRate->mBufLength;
77 
78     float* buffer_base = parent->unit_buffers;
79 
80     /* allocate buffers */
81     for (size_t i = 0; i != output_count; ++i) {
82         Wire* w = allocator.alloc<Wire>();
83 
84         w->mFromUnit = unit;
85         w->mCalcRate = unit->mCalcRate;
86 
87         w->mBuffer = nullptr;
88         w->mScalarValue = 0;
89 
90         if (unit->mCalcRate == 2) {
91             /* allocate a new buffer */
92             assert(unit_spec.buffer_mapping[i] >= 0);
93             std::size_t buffer_id = unit_spec.buffer_mapping[i];
94             unit->mOutBuf[i] = buffer_base + buffer_length * buffer_id;
95             w->mBuffer = unit->mOutBuf[i];
96         } else
97             unit->mOutBuf[i] = &w->mScalarValue;
98 
99         unit->mOutput[i] = w;
100     }
101 
102     /* prepare inputs */
103     for (size_t i = 0; i != unit_spec.input_specs.size(); ++i) {
104         int source = unit_spec.input_specs[i].source;
105         int index = unit_spec.input_specs[i].index;
106 
107         if (source == -1)
108             unit->mInput[i] = &unit->mParent->mWire[index];
109         else {
110             Unit* prev = parent->units[source];
111             unit->mInput[i] = prev->mOutput[index];
112         }
113 
114         if (unit->mInput[i]->mCalcRate == 2)
115             unit->mInBuf[i] = unit->mInput[i]->mBuffer;
116         else
117             unit->mInBuf[i] = &unit->mInput[i]->mScalarValue;
118     }
119 
120     return unit;
121 }
122 
add_command(const char * cmd_name,UnitCmdFunc func)123 bool sc_ugen_def::add_command(const char* cmd_name, UnitCmdFunc func) {
124     sc_unitcmd_def* def = new sc_unitcmd_def(cmd_name, func);
125     unitcmd_set.insert(*def);
126     return true;
127 }
128 
run_unit_command(const char * cmd_name,Unit * unit,struct sc_msg_iter * args)129 void sc_ugen_def::run_unit_command(const char* cmd_name, Unit* unit, struct sc_msg_iter* args) {
130     unitcmd_set_type::iterator it = unitcmd_set.find(cmd_name, named_hash_hash(), named_hash_equal());
131 
132     if (it != unitcmd_set.end()) {
133         it->run(unit, args);
134     } else {
135         std::cout << "can't find unit command: " << cmd_name << std::endl;
136     }
137 }
138 
run(World * world,uint32_t buffer_index,struct sc_msg_iter * args)139 sample* sc_bufgen_def::run(World* world, uint32_t buffer_index, struct sc_msg_iter* args) {
140     SndBuf* buf = World_GetNRTBuf(world, buffer_index);
141     sample* data = buf->data;
142 
143     (func)(world, buf, args);
144 
145     if (data == buf->data)
146         return nullptr;
147     else
148         return data;
149 }
150 
register_ugen(const char * inUnitClassName,size_t inAllocSize,UnitCtorFunc inCtor,UnitDtorFunc inDtor,uint32 inFlags)151 void sc_plugin_container::register_ugen(const char* inUnitClassName, size_t inAllocSize, UnitCtorFunc inCtor,
152                                         UnitDtorFunc inDtor, uint32 inFlags) {
153     sc_ugen_def* def = new sc_ugen_def(inUnitClassName, inAllocSize, inCtor, inDtor, inFlags);
154     ugen_set.insert(*def);
155 }
156 
register_bufgen(const char * name,BufGenFunc func)157 void sc_plugin_container::register_bufgen(const char* name, BufGenFunc func) {
158     sc_bufgen_def* def = new sc_bufgen_def(name, func);
159     bufgen_set.insert(*def);
160 }
161 
find_ugen(symbol const & name)162 sc_ugen_def* sc_plugin_container::find_ugen(symbol const& name) {
163     ugen_set_type::iterator it = ugen_set.find(name, named_hash_hash(), named_hash_equal());
164     if (it == ugen_set.end())
165         return nullptr;
166 
167     return &*it;
168 }
169 
register_ugen_command_function(const char * ugen_name,const char * cmd_name,UnitCmdFunc func)170 bool sc_plugin_container::register_ugen_command_function(const char* ugen_name, const char* cmd_name,
171                                                          UnitCmdFunc func) {
172     sc_ugen_def* def = find_ugen(symbol(ugen_name));
173     if (!def) {
174         std::cout << "unable to register ugen command: ugen '" << ugen_name << "' doesn't exist" << std::endl;
175         return false;
176     }
177     return def->add_command(cmd_name, func);
178 }
179 
register_cmd_plugin(const char * cmd_name,PlugInCmdFunc func,void * user_data)180 bool sc_plugin_container::register_cmd_plugin(const char* cmd_name, PlugInCmdFunc func, void* user_data) {
181     cmdplugin_set_type::iterator it = cmdplugin_set.find(cmd_name, named_hash_hash(), named_hash_equal());
182     if (it != cmdplugin_set.end()) {
183         std::cout << "cmd plugin already registered: " << cmd_name << std::endl;
184         return false;
185     }
186 
187     sc_cmdplugin_def* def = new sc_cmdplugin_def(cmd_name, func, user_data);
188     cmdplugin_set.insert(*def);
189 
190     return true;
191 }
192 
run_bufgen(World * world,const char * name,uint32_t buffer_index,struct sc_msg_iter * args)193 sample* sc_plugin_container::run_bufgen(World* world, const char* name, uint32_t buffer_index,
194                                         struct sc_msg_iter* args) {
195     bufgen_set_type::iterator it = bufgen_set.find(name, named_hash_hash(), named_hash_equal());
196     if (it == bufgen_set.end()) {
197         std::cout << "unable to find buffer generator: " << name << std::endl;
198         return nullptr;
199     }
200 
201     return it->run(world, buffer_index, args);
202 }
203 
204 
run_cmd_plugin(World * world,const char * name,struct sc_msg_iter * args,void * replyAddr)205 bool sc_plugin_container::run_cmd_plugin(World* world, const char* name, struct sc_msg_iter* args, void* replyAddr) {
206     cmdplugin_set_type::iterator it = cmdplugin_set.find(name, named_hash_hash(), named_hash_equal());
207     if (it == cmdplugin_set.end()) {
208         std::cout << "unable to find cmd plugin: " << name << std::endl;
209         return false;
210     }
211 
212     it->run(world, args, replyAddr);
213 
214     return true;
215 }
216 
217 
load_plugin_folder(boost::filesystem::path const & path)218 void sc_ugen_factory::load_plugin_folder(boost::filesystem::path const& path) {
219     using namespace boost::filesystem;
220 
221     directory_iterator end;
222 
223     if (!is_directory(path))
224         return;
225 
226     for (directory_iterator it(path); it != end; ++it) {
227         if (is_regular_file(it->status()))
228             load_plugin(it->path());
229         if (is_directory(it->status()))
230             load_plugin_folder(it->path());
231     }
232 }
233 
check_api_version(int (* api_version)(),std::string const & filename)234 static bool check_api_version(int (*api_version)(), std::string const& filename) {
235     using namespace std;
236     using namespace scsynth;
237 
238     if (api_version) {
239         int plugin_version = (*api_version)();
240         if (plugin_version == sc_api_version)
241             return true;
242         else
243             cout << ErrorMessage::apiVersionMismatch(filename, sc_api_version, plugin_version) << endl;
244     } else {
245         cout << ErrorMessage::apiVersionNotFound(filename) << endl;
246     }
247 
248     return false;
249 }
250 
251 #ifdef DLOPEN
load_plugin(boost::filesystem::path const & path)252 void sc_ugen_factory::load_plugin(boost::filesystem::path const& path) {
253     using namespace std;
254 
255     // Ignore files that don't have the extension of an SC plugin
256     if (path.extension() != SC_PLUGIN_EXT) {
257         return;
258     }
259 
260     void* handle = dlopen(path.string().c_str(), RTLD_NOW | RTLD_LOCAL);
261     if (handle == nullptr)
262         return;
263 
264     typedef int (*info_function)();
265 
266     info_function api_version = reinterpret_cast<info_function>(dlsym(handle, "api_version"));
267 
268     if (!check_api_version(api_version, path.string())) {
269         dlclose(handle);
270         return;
271     }
272 
273     info_function supernova_check = reinterpret_cast<info_function>(dlsym(handle, "server_type"));
274     if (!supernova_check || (*supernova_check)() == sc_server_scsynth) {
275         // silently ignore
276         dlclose(handle);
277         return;
278     }
279 
280     void* load_symbol = dlsym(handle, "load");
281     if (!load_symbol) {
282         cout << "Problem when loading plugin: \"load\" function undefined" << path << endl;
283         dlclose(handle);
284         return;
285     }
286 
287     open_handles.push_back(handle);
288     LoadPlugInFunc load_func = reinterpret_cast<LoadPlugInFunc>(load_symbol);
289     (*load_func)(&sc_interface);
290 
291     /* don't close the handle */
292     return;
293 }
294 
close_handles(void)295 void sc_ugen_factory::close_handles(void) {
296     for (void* handle : open_handles) {
297         void* ptr = dlsym(handle, "unload");
298         if (ptr) {
299             UnLoadPlugInFunc unloadFunc = (UnLoadPlugInFunc)ptr;
300             (*unloadFunc)();
301         }
302         dlclose(handle);
303     }
304 }
305 
306 #elif defined(_WIN32)
307 
load_plugin(boost::filesystem::path const & path)308 void sc_ugen_factory::load_plugin(boost::filesystem::path const& path) {
309     // Ignore files that don't have the extension of an SC plugin
310     if (path.extension() != SC_PLUGIN_EXT) {
311         return;
312     }
313 
314     // std::cout << "try open plugin: " << path << std::endl;
315     const char* filename = path.string().c_str();
316     HINSTANCE hinstance = LoadLibrary(path.string().c_str());
317     if (!hinstance) {
318         char* s;
319         DWORD lastErr = GetLastError();
320         FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
321                       nullptr, lastErr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (char*)&s, 0, NULL);
322 
323         std::cout << "Cannot open plugin: " << path << s << std::endl;
324         LocalFree(s);
325         return;
326     }
327 
328     typedef int (*info_function)();
329     info_function api_version = reinterpret_cast<info_function>(GetProcAddress(hinstance, "api_version"));
330 
331     if (!check_api_version(api_version, filename)) {
332         FreeLibrary(hinstance);
333         return;
334     }
335 
336     typedef int (*info_function)();
337     info_function server_type = reinterpret_cast<info_function>(GetProcAddress(hinstance, "server_type"));
338     if (!server_type) {
339         FreeLibrary(hinstance);
340         return;
341     }
342 
343     if ((*server_type)() != sc_server_supernova) {
344         FreeLibrary(hinstance);
345         return;
346     }
347 
348     void* ptr = (void*)GetProcAddress(hinstance, "load");
349     if (!ptr) {
350         char* s;
351         FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
352                       nullptr, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (char*)&s, 0, NULL);
353 
354         std::cout << "*** ERROR: GetProcAddress err " << s << std::endl;
355         LocalFree(s);
356 
357         FreeLibrary(hinstance);
358         return;
359     }
360     open_handles.push_back(hinstance);
361     LoadPlugInFunc loadFunc = (LoadPlugInFunc)ptr;
362     (*loadFunc)(&sc_interface);
363 
364     return;
365 }
366 
close_handles(void)367 void sc_ugen_factory::close_handles(void) {
368     for (void* ptrhinstance : open_handles) {
369         HINSTANCE hinstance = (HINSTANCE)ptrhinstance;
370         void* ptr = (void*)GetProcAddress(hinstance, "unload");
371         if (ptr) {
372             UnLoadPlugInFunc unloadFunc = (UnLoadPlugInFunc)ptr;
373             (*unloadFunc)();
374         }
375         FreeLibrary(hinstance);
376     }
377 }
378 #else
379 
380 #endif
381 
382 } /* namespace nova */
383