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