Fix scvim regsitry file for updated filename (thanks Carlo Capocasa)
[supercollider.git] / server / supernova / sc / sc_synthdef.cpp
blobfee52e6148fc8c927ed5d7d2b9d334bde751dce2
1 // supercollider-style synthdef
2 // Copyright (C) 2008, 2009, 2010 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.
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.
19 #include <iostream>
20 #include <iterator>
21 #include <fstream>
22 #include <sstream>
24 #include <boost/format.hpp>
25 #include <boost/integer/endian.hpp>
27 #include "sc_synthdef.hpp"
28 #include "sc_ugen_factory.hpp"
30 #include "utilities/sized_array.hpp"
31 #include "utilities/exists.hpp"
33 namespace nova
35 typedef boost::int16_t int16;
36 typedef boost::int32_t int32;
37 typedef boost::int8_t int8;
39 using boost::integer::big32_t;
40 using boost::integer::big16_t;
41 using boost::integer::big8_t;
43 using std::size_t;
45 namespace {
47 std::string read_pstring(const char *& buffer)
49 char str[256+1];
50 char name_size = *buffer++;
51 memcpy(str, buffer, name_size);
52 str[int(name_size)] = 0;
54 buffer += name_size;
55 return std::string(str);
58 float read_float(const char *& ptr)
60 big32_t data = *(big32_t*)ptr;
61 ptr += 4;
63 union {
64 int32_t i;
65 float f;
66 } cast;
68 cast.i = data;
69 return cast.f;
72 int8_t read_int8(const char *& ptr)
74 big8_t data = *(big8_t*)ptr;
75 ptr += 1;
76 return data;
79 int16_t read_int16(const char *& ptr)
81 big16_t data = *(big16_t*)ptr;
82 ptr += 2;
83 return data;
86 int32_t read_int32(const char *& ptr)
88 big32_t data = *(big32_t*)ptr;
89 ptr += 4;
90 return data;
93 int32_t read_int(const char *& ptr, int size)
95 if (size == 32)
96 return read_int32(ptr);
98 if (size == 16)
99 return read_int16(ptr);
100 assert(false);
101 return 0;
104 } /* namespace */
106 std::vector<sc_synthdef> read_synthdefs(const char * buf_ptr)
108 /* int32 header = */ read_int32(buf_ptr);
109 int32 version = read_int32(buf_ptr);
111 int16 definition_count = read_int16(buf_ptr);
113 std::vector<sc_synthdef> ret;
115 for (int i = 0; i != definition_count; ++i) {
116 try {
117 sc_synthdef def(buf_ptr, version);
118 ret.push_back(def);
120 catch (std::runtime_error const & e) {
121 std::cerr << "Exception when reading synthdef: " << e.what() << std::endl;
124 return ret;
127 std::vector<sc_synthdef> read_synthdef_file(boost::filesystem::path const & filename)
129 using namespace std;
131 ifstream stream;
132 stream.open(filename.string().c_str());
134 if (!stream.is_open()) {
135 cout << "cannot open file " << filename << endl;
136 return std::vector<sc_synthdef>();
139 /* get length of file */
140 stream.seekg (0, ios::end);
141 size_t length = stream.tellg();
142 stream.seekg (0, ios::beg);
144 sized_array<char> buffer(length);
145 stream.read(buffer.c_array(), length);
146 stream.close();
148 return read_synthdefs(buffer.c_array());
151 sc_synthdef::unit_spec_t::unit_spec_t(const char *& buffer, int version)
153 const int short_int_size = (version == 1) ? 16 : 32;
155 name = read_pstring(buffer);
156 rate = read_int8(buffer);
157 int32_t input_count = read_int(buffer, short_int_size);
158 int32_t output_count = read_int(buffer, short_int_size);
159 special_index = read_int16(buffer);
161 for (int i = 0; i != input_count; ++i) {
162 int16_t source = read_int(buffer, short_int_size);
163 int16_t index = read_int(buffer, short_int_size);
164 input_spec spec(source, index);
165 input_specs.push_back(spec);
168 for (int i = 0; i != output_count; ++i) {
169 char rate = read_int8(buffer);
170 output_specs.push_back(rate);
174 sc_synthdef::sc_synthdef(const char*& data, int version)
176 read_synthdef(data, version);
179 void sc_synthdef::read_synthdef(const char *& ptr, int version)
181 using namespace std;
182 const int short_int_size = (version == 1) ? 16 : 32;
184 /* read name */
185 name_ = read_pstring(ptr);
187 /* read constants */
188 int32_t constant_count = read_int(ptr, short_int_size);
190 for (int i = 0; i != constant_count; ++i) {
191 float data = read_float(ptr);
192 constants.push_back(data);
195 /* read parameters */
196 int32_t paramenter_count = read_int(ptr, short_int_size);
198 for (int i = 0; i != paramenter_count; ++i) {
199 float data = read_float(ptr);
200 parameters.push_back(data);
203 /* read parameter names */
204 int32_t parameter_names_count = read_int(ptr, short_int_size);
206 for (int i = 0; i != parameter_names_count; ++i) {
207 string data = read_pstring(ptr);
208 int32_t index = read_int(ptr, short_int_size);
210 parameter_map[data] = index;
213 int32_t ugen_count = read_int(ptr, short_int_size);
214 graph.reserve(ugen_count);
216 for (int i = 0; i != ugen_count; ++i) {
217 unit_spec_t data(ptr, version);
218 graph.push_back(data);
221 prepare();
224 namespace
227 template <typename Alloc = std::allocator<int16_t> >
228 class buffer_allocator
230 std::vector<size_t, Alloc> buffers; /* index: buffer id, value: last reference */
232 public:
233 /** allocate buffer for current ugen
235 * reuse buffers, which are not used after the current ugen
237 int16_t allocate_buffer(size_t current_ugen)
239 for (size_t i = 0; i != buffers.size(); ++i) {
240 if (buffers[i] <= current_ugen)
241 return i;
243 buffers.push_back(current_ugen);
244 return buffers.size() - 1;
247 /** allocate buffer for current ugen
249 * reuse the buffers, which have been used before the current ugen
251 int16_t allocate_buffer_noalias(size_t current_ugen)
253 for (size_t i = 0; i != buffers.size(); ++i) {
254 if (buffers[i] < current_ugen)
255 return i;
257 buffers.push_back(current_ugen);
258 return buffers.size() - 1;
261 size_t buffer_count (void) const
263 return buffers.size();
266 void set_last_reference (int16_t buffer_id, size_t ugen_index)
268 buffers[buffer_id] = ugen_index;
272 } /* namespace */
274 void sc_synthdef::prepare(void)
276 memory_requirement_ = 0;
278 const size_t number_of_ugens = graph.size();
280 // store the last references to each output buffer inside a std::map for faster lookup
281 std::map<input_spec, size_t> last_buffer_references;
283 for (graph_t::reverse_iterator it = graph.rbegin(); it != graph.rend(); ++it) {
284 for (size_t i = 0; i != it->input_specs.size(); ++i) {
285 input_spec const & in_spec = it->input_specs[i];
287 if (it->rate == calc_DemandRate) {
288 size_t ugen_index = number_of_ugens; // audio-inputs to demand-rate ugens will never be reused!
289 last_buffer_references[in_spec] = ugen_index;
290 } else {
291 if (!exists(last_buffer_references, in_spec)) {
292 size_t ugen_index = std::distance(it, graph.rend()) - 1;
293 last_buffer_references.insert(std::make_pair(in_spec, ugen_index));
299 buffer_allocator<> allocator;
301 for (size_t ugen_index = 0; ugen_index != number_of_ugens; ++ugen_index) {
302 unit_spec_t & current_ugen_spec = graph[ugen_index];
304 /* calc units are stored in an additional vector */
305 switch (current_ugen_spec.rate) {
306 case calc_BufRate:
307 case calc_FullRate:
308 calc_unit_indices.push_back(ugen_index);
311 memory_requirement_ += current_ugen_spec.memory_requirement();
313 current_ugen_spec.buffer_mapping.resize(current_ugen_spec.output_specs.size());
315 sc_ugen_def * ugen = sc_factory->find_ugen(current_ugen_spec.name);
316 if (unlikely(ugen == NULL)) {
317 /* we cannot prepare the synthdef, if the ugens are not installed */
318 boost::format frmt("Cannot load synth %1%: Unit generator %2% not installed");
319 frmt % name_.c_str() % current_ugen_spec.name.c_str();
321 throw std::runtime_error(frmt.str());
324 current_ugen_spec.prototype = ugen;
326 const bool can_alias = !ugen->cant_alias();
327 memory_requirement_ += ugen->memory_requirement();
329 for (size_t output_index = 0; output_index != current_ugen_spec.output_specs.size(); ++output_index) {
330 int16_t buffer_id;
331 if (current_ugen_spec.output_specs[output_index] == 2) {
332 /* find last reference to this buffer */
333 size_t last_ref = ugen_index;
334 input_spec this_input_spec(ugen_index, output_index);
336 if (exists(last_buffer_references, this_input_spec))
337 last_ref = last_buffer_references[this_input_spec];
339 buffer_id = can_alias ? allocator.allocate_buffer(ugen_index)
340 : allocator.allocate_buffer_noalias(ugen_index);
342 allocator.set_last_reference(buffer_id, last_ref);
344 else
345 buffer_id = -1;
346 current_ugen_spec.buffer_mapping[output_index] = buffer_id;
350 memory_requirement_ += (graph.size() + calc_unit_indices.size()) * sizeof(Unit*); // reserves space for units
352 // memory that is required to fill the sc_synth data structure
353 const size_t ctor_alloc_size = parameter_count() * (sizeof(float) + sizeof(int) + sizeof(float*))
354 + constants.size() * sizeof(Wire);
356 memory_requirement_ += ctor_alloc_size;
358 buffer_count = uint16_t(allocator.buffer_count());
362 std::string sc_synthdef::dump(void) const
364 using namespace std;
366 stringstream stream;
368 stream << "name " << name().c_str() << endl;
370 stream << "constant: " << endl;
371 for (uint i = 0; i != constants.size(); ++i)
372 stream << "\t" << constants[i] << endl;
374 stream << "parameters: " << endl;
375 for (uint i = 0; i != parameters.size(); ++i)
376 stream << "\t" << parameters[i] << endl;
378 stream << "parameter names: " << endl;
379 for (parameter_map_t::const_iterator it = parameter_map.begin();
380 it != parameter_map.end(); ++it)
381 stream << "\t" << it->first.c_str() << " " << it->second << endl;
383 stream << "ugens: " << endl;
384 for (uint i = 0; i != graph.size(); ++i) {
385 stream << "\t" << graph[i].name.c_str() << ": rate " << graph[i].rate
386 << endl;
387 stream << "\tinputs:" << endl;
388 for (uint j = 0; j != graph[i].input_specs.size(); ++j) {
389 stream << "\t\t" << graph[i].input_specs[j].source << " " << graph[i].input_specs[j].index << endl;
393 return stream.str();
397 } /* namespace nova */