1 //  prototype of a supercollider-synthdef-based synth prototype
2 //  Copyright (C) 2009 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 #pragma once
20 
21 #include <boost/intrusive/set.hpp>
22 #include <boost/intrusive/unordered_set.hpp>
23 #include <boost/checked_delete.hpp>
24 
25 #include "sc_synthdef.hpp"
26 #include "sc_synth.hpp"
27 #include "sc_plugin_interface.hpp"
28 
29 #include "SC_InterfaceTable.h"
30 #include "SC_Unit.h"
31 
32 #include "utilities/named_hash_entry.hpp"
33 
34 namespace nova {
35 namespace bi = boost::intrusive;
36 
37 struct sc_unitcmd_def : public named_hash_entry {
38     const UnitCmdFunc func;
39 
sc_unitcmd_defnova::sc_unitcmd_def40     sc_unitcmd_def(const char* cmd_name, UnitCmdFunc func): named_hash_entry(cmd_name), func(func) {}
41 
runnova::sc_unitcmd_def42     void run(Unit* unit, struct sc_msg_iter* args) { (func)(unit, args); }
43 };
44 
45 class sc_ugen_def : public aligned_class, public named_hash_entry {
46 private:
47     const size_t alloc_size;
48     const UnitCtorFunc ctor;
49     const UnitDtorFunc dtor;
50     const uint32_t flags;
51 
52     static const std::size_t unitcmd_set_bucket_count = 4;
53     typedef bi::unordered_set<sc_unitcmd_def, bi::constant_time_size<false>, bi::power_2_buckets<true>,
54                               bi::store_hash<true>>
55         unitcmd_set_type;
56 
57     unitcmd_set_type::bucket_type unitcmd_set_buckets[unitcmd_set_bucket_count];
58     unitcmd_set_type unitcmd_set;
59 
60 public:
sc_ugen_def(const char * inUnitClassName,size_t inAllocSize,UnitCtorFunc inCtor,UnitDtorFunc inDtor,uint32 inFlags)61     sc_ugen_def(const char* inUnitClassName, size_t inAllocSize, UnitCtorFunc inCtor, UnitDtorFunc inDtor,
62                 uint32 inFlags):
63         named_hash_entry(inUnitClassName),
64         alloc_size(inAllocSize),
65         ctor(inCtor),
66         dtor(inDtor),
67         flags(inFlags),
68         unitcmd_set(unitcmd_set_type::bucket_traits(unitcmd_set_buckets, unitcmd_set_bucket_count)) {}
69 
70     Unit* construct(sc_synthdef::unit_spec_t const& unit_spec, sc_synth* parent, int parentIndex, World* world,
71                     linear_allocator& allocator);
72 
initialize(Unit * unit)73     void initialize(Unit* unit) { (*ctor)(unit); }
74 
destruct(Unit * unit)75     void destruct(Unit* unit) {
76         if (dtor)
77             (*dtor)(unit);
78     }
79 
cant_alias(void) const80     bool cant_alias(void) const { return flags & kUnitDef_CantAliasInputsToOutputs; }
81 
memory_requirement(void) const82     std::size_t memory_requirement(void) const {
83         return alloc_size + 64; // overallocate to allow alignment
84     }
85 
86     bool add_command(const char* cmd_name, UnitCmdFunc func);
87     void run_unit_command(const char* cmd_name, Unit* unit, struct sc_msg_iter* args);
88 };
89 
90 struct sc_bufgen_def : public named_hash_entry {
91     const BufGenFunc func;
92 
sc_bufgen_defnova::sc_bufgen_def93     sc_bufgen_def(const char* name, BufGenFunc func): named_hash_entry(name), func(func) {}
94 
95     sample* run(World* world, uint32_t buffer_index, struct sc_msg_iter* args);
96 };
97 
98 struct sc_cmdplugin_def : public named_hash_entry {
99     const PlugInCmdFunc func;
100     void* user_data;
101 
sc_cmdplugin_defnova::sc_cmdplugin_def102     sc_cmdplugin_def(const char* name, PlugInCmdFunc func, void* user_data):
103         named_hash_entry(name),
104         func(func),
105         user_data(user_data) {}
106 
runnova::sc_cmdplugin_def107     void run(World* world, struct sc_msg_iter* args, void* replyAddr) { (func)(world, user_data, args, replyAddr); }
108 };
109 
110 class sc_plugin_container {
111     static const std::size_t ugen_set_bucket_count = 512;
112     typedef bi::unordered_set<sc_ugen_def, bi::constant_time_size<false>, bi::power_2_buckets<true>,
113                               bi::store_hash<true>>
114         ugen_set_type;
115 
116     static const std::size_t bufgen_set_bucket_count = 64;
117     typedef bi::unordered_set<sc_bufgen_def, bi::constant_time_size<false>, bi::power_2_buckets<true>,
118                               bi::store_hash<true>>
119         bufgen_set_type;
120 
121     static const std::size_t cmdplugin_set_bucket_count = 8;
122     typedef bi::unordered_set<sc_cmdplugin_def, bi::constant_time_size<false>, bi::power_2_buckets<true>,
123                               bi::store_hash<true>>
124         cmdplugin_set_type;
125 
126     ugen_set_type::bucket_type ugen_set_buckets[ugen_set_bucket_count];
127     ugen_set_type ugen_set;
128 
129     bufgen_set_type::bucket_type bufgen_set_buckets[bufgen_set_bucket_count];
130     bufgen_set_type bufgen_set;
131 
132     cmdplugin_set_type::bucket_type cmdplugin_set_buckets[cmdplugin_set_bucket_count];
133     cmdplugin_set_type cmdplugin_set;
134 
135 protected:
sc_plugin_container(void)136     sc_plugin_container(void):
137         ugen_set(ugen_set_type::bucket_traits(ugen_set_buckets, ugen_set_bucket_count)),
138         bufgen_set(bufgen_set_type::bucket_traits(bufgen_set_buckets, bufgen_set_bucket_count)),
139         cmdplugin_set(cmdplugin_set_type::bucket_traits(cmdplugin_set_buckets, cmdplugin_set_bucket_count)) {}
140 
~sc_plugin_container(void)141     ~sc_plugin_container(void) {
142         ugen_set.clear_and_dispose(boost::checked_delete<sc_ugen_def>);
143         bufgen_set.clear_and_dispose(boost::checked_delete<sc_bufgen_def>);
144         cmdplugin_set.clear_and_dispose(boost::checked_delete<sc_cmdplugin_def>);
145     }
146 
147 public:
148     void register_ugen(const char* inUnitClassName, size_t inAllocSize, UnitCtorFunc inCtor, UnitDtorFunc inDtor,
149                        uint32 inFlags);
150 
151     void register_bufgen(const char* name, BufGenFunc func);
152 
153     sc_ugen_def* find_ugen(symbol const& name);
154 
155     bool register_ugen_command_function(const char* ugen_name, const char* cmd_name, UnitCmdFunc);
156     bool register_cmd_plugin(const char* cmd_name, PlugInCmdFunc func, void* user_data);
157 
158     sample* run_bufgen(World* world, const char* name, uint32_t buffer_index, struct sc_msg_iter* args);
159     bool run_cmd_plugin(World* world, const char* name, struct sc_msg_iter* args, void* replyAddr);
160 };
161 
162 /** factory class for supercollider ugens
163  *
164  *  \todo do we need to take care of thread safety? */
165 class sc_ugen_factory : public sc_plugin_interface, public sc_plugin_container {
166 public:
167     sc_ugen_factory() = default;
168 
~sc_ugen_factory(void)169     ~sc_ugen_factory(void) { close_handles(); }
170 
171     /* @{ */
172     /** ugen count handling */
allocate_ugens(uint32_t count)173     void allocate_ugens(uint32_t count) { ugen_count_ += count; }
174 
free_ugens(uint32_t count)175     void free_ugens(uint32_t count) { ugen_count_ -= count; }
176 
ugen_count(void) const177     uint32_t ugen_count(void) const { return ugen_count_; }
178     /* @} */
179 
180     void load_plugin_folder(boost::filesystem::path const& path);
181     void load_plugin(boost::filesystem::path const& path);
182 
183 private:
184     void close_handles(void);
185 
186     uint32_t ugen_count_ = 0;
187     std::vector<void*> open_handles;
188 };
189 
190 extern std::unique_ptr<sc_ugen_factory> sc_factory;
191 
192 } /* namespace nova */
193