Change PG_03 discussion of rests to use Rest()
[supercollider.git] / server / supernova / sc / sc_synthdef.cpp
blob7913530d6661bd97f02ec11e098189d89ae82985
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 <fstream>
21 #include <sstream>
23 #include <boost/format.hpp>
24 #include <boost/integer/endian.hpp>
26 #include "sc_synthdef.hpp"
27 #include "sc_ugen_factory.hpp"
29 #include "utilities/sized_array.hpp"
30 #include "utilities/exists.hpp"
32 namespace nova
34 typedef boost::int16_t int16;
35 typedef boost::int32_t int32;
36 typedef boost::int8_t int8;
38 using boost::integer::big32_t;
39 using boost::integer::big16_t;
40 using boost::integer::big8_t;
42 using std::size_t;
44 namespace {
46 std::string read_pstring(const char *& buffer)
48 char str[256+1];
49 char name_size = *buffer++;
50 memcpy(str, buffer, name_size);
51 str[int(name_size)] = 0;
53 buffer += name_size;
54 return std::string(str);
57 float read_float(const char *& ptr)
59 big32_t data = *(big32_t*)ptr;
60 ptr += 4;
62 union {
63 int32_t i;
64 float f;
65 } cast;
67 cast.i = data;
68 return cast.f;
71 int8_t read_int8(const char *& ptr)
73 big8_t data = *(big8_t*)ptr;
74 ptr += 1;
75 return data;
78 int16_t read_int16(const char *& ptr)
80 big16_t data = *(big16_t*)ptr;
81 ptr += 2;
82 return data;
85 int32_t read_int32(const char *& ptr)
87 big32_t data = *(big32_t*)ptr;
88 ptr += 4;
89 return data;
92 } /* namespace */
94 std::vector<sc_synthdef> read_synthdefs(const char * buf_ptr)
96 /* int32 header = */ read_int32(buf_ptr);
97 /* int32 version = */ read_int32(buf_ptr);
99 int16 definition_count = read_int16(buf_ptr);
101 std::vector<sc_synthdef> ret;
103 for (int i = 0; i != definition_count; ++i) {
104 try {
105 sc_synthdef def(buf_ptr);
106 ret.push_back(def);
108 catch (std::runtime_error const & e) {
109 std::cerr << "Exception when reading synthdef: " << e.what() << std::endl;
112 return ret;
115 std::vector<sc_synthdef> read_synthdef_file(boost::filesystem::path const & filename)
117 using namespace std;
119 ifstream stream;
120 stream.open(filename.string().c_str());
122 if (!stream.is_open()) {
123 cout << "cannot open file " << filename << endl;
124 return std::vector<sc_synthdef>();
127 /* get length of file */
128 stream.seekg (0, ios::end);
129 size_t length = stream.tellg();
130 stream.seekg (0, ios::beg);
132 sized_array<char> buffer(length);
133 stream.read(buffer.c_array(), length);
134 stream.close();
136 return read_synthdefs(buffer.c_array());
139 sc_synthdef::unit_spec_t::unit_spec_t(const char *& buffer)
141 name = read_pstring(buffer);
142 rate = read_int8(buffer);
143 int16 inputs = read_int16(buffer);
144 int16 outputs = read_int16(buffer);
145 special_index = read_int16(buffer);
147 for (int i = 0; i != inputs; ++i) {
148 int16_t source = read_int16(buffer);
149 int16_t index = read_int16(buffer);
150 input_spec spec(source, index);
151 input_specs.push_back(spec);
154 for (int i = 0; i != outputs; ++i) {
155 char rate = read_int8(buffer);
156 output_specs.push_back(rate);
160 sc_synthdef::sc_synthdef(const char*& data)
162 read_synthdef(data);
165 void sc_synthdef::read_synthdef(const char *& ptr)
167 using namespace std;
169 /* read name */
170 name_ = read_pstring(ptr);
172 /* read constants */
173 int16_t consts = read_int16(ptr);
175 for (int i = 0; i != consts; ++i) {
176 float data = read_float(ptr);
177 constants.push_back(data);
180 /* read parameters */
181 int16_t pars = read_int16(ptr);
183 for (int i = 0; i != pars; ++i) {
184 float data = read_float(ptr);
185 parameters.push_back(data);
188 /* read parameter names */
189 int16_t par_names = read_int16(ptr);
191 for (int i = 0; i != par_names; ++i) {
192 string data = read_pstring(ptr);
193 int16_t index = read_int16(ptr);
195 parameter_map[data] = index;
198 int16_t ugens = read_int16(ptr);
199 graph.reserve(ugens);
201 for (int i = 0; i != ugens; ++i) {
202 unit_spec_t data(ptr);
203 graph.push_back(data);
206 prepare();
209 namespace
212 template <typename Alloc = std::allocator<int16_t> >
213 class buffer_allocator
215 std::vector<size_t, Alloc> buffers; /* index: buffer id, value: last reference */
217 public:
218 /** allocate buffer for current ugen
220 * reuse buffers, which are not used after the current ugen
222 int16_t allocate_buffer(size_t current_ugen)
224 for (size_t i = 0; i != buffers.size(); ++i) {
225 if (buffers[i] <= current_ugen)
226 return i;
228 buffers.push_back(current_ugen);
229 return buffers.size() - 1;
232 /** allocate buffer for current ugen
234 * reuse the buffers, which have been used before the current ugen
236 int16_t allocate_buffer_noalias(size_t current_ugen)
238 for (size_t i = 0; i != buffers.size(); ++i) {
239 if (buffers[i] < current_ugen)
240 return i;
242 buffers.push_back(current_ugen);
243 return buffers.size() - 1;
246 size_t buffer_count (void) const
248 return buffers.size();
251 void set_last_reference (int16_t buffer_id, size_t ugen_index)
253 buffers[buffer_id] = ugen_index;
257 } /* namespace */
259 void sc_synthdef::prepare(void)
261 memory_requirement_ = 0;
263 const size_t number_of_ugens = graph.size();
265 // store the last references to each output buffer inside a std::map for faster lookup
266 std::map<input_spec, size_t> last_buffer_references;
267 for (graph_t::const_reverse_iterator it = graph.rbegin(); it != graph.rend(); ++it) {
268 for (size_t i = 0; i != it->input_specs.size(); ++i) {
269 input_spec const & in_spec = it->input_specs[i];
270 if (!exists(last_buffer_references, in_spec)) {
271 size_t ugen_index = graph.size() - (it - graph.rbegin()) - 1;
272 last_buffer_references.insert(std::make_pair(in_spec, ugen_index));
277 buffer_allocator<> allocator;
279 for (size_t ugen_index = 0; ugen_index != number_of_ugens; ++ugen_index) {
280 unit_spec_t & current_ugen_spec = graph[ugen_index];
282 /* calc units are stored in an additional vector */
283 switch (current_ugen_spec.rate) {
284 case calc_BufRate:
285 case calc_FullRate:
286 calc_unit_indices.push_back(ugen_index);
289 memory_requirement_ += current_ugen_spec.memory_requirement();
291 current_ugen_spec.buffer_mapping.resize(current_ugen_spec.output_specs.size());
293 sc_ugen_def * ugen = sc_factory->find_ugen(current_ugen_spec.name);
294 if (unlikely(ugen == NULL)) {
295 /* we cannot prepare the synthdef, if the ugens are not installed */
296 boost::format frmt("Cannot load synth %1%: Unit generator %2% not installed");
297 frmt % name_.c_str() % current_ugen_spec.name.c_str();
299 throw std::runtime_error(frmt.str());
302 current_ugen_spec.prototype = ugen;
304 const bool can_alias = !ugen->cant_alias();
305 memory_requirement_ += ugen->memory_requirement();
307 for (size_t output_index = 0; output_index != current_ugen_spec.output_specs.size(); ++output_index) {
308 int16_t buffer_id;
309 if (current_ugen_spec.output_specs[output_index] == 2) {
310 /* find last reference to this buffer */
311 size_t last_ref = ugen_index;
312 input_spec this_input_spec(ugen_index, output_index);
314 if (exists(last_buffer_references, this_input_spec))
315 last_ref = last_buffer_references[this_input_spec];
317 buffer_id = can_alias ? allocator.allocate_buffer(ugen_index)
318 : allocator.allocate_buffer_noalias(ugen_index);
320 allocator.set_last_reference(buffer_id, last_ref);
322 else
323 buffer_id = -1;
324 current_ugen_spec.buffer_mapping[output_index] = buffer_id;
328 memory_requirement_ += (graph.size() + calc_unit_indices.size()) * sizeof(Unit*); // reserves space for units
330 // memory that is required to fill the sc_synth data structure
331 const size_t ctor_alloc_size = parameter_count() * (sizeof(float) + sizeof(int) + sizeof(float*))
332 + constants.size() * sizeof(Wire);
334 memory_requirement_ += ctor_alloc_size;
336 buffer_count = uint16_t(allocator.buffer_count());
340 std::string sc_synthdef::dump(void) const
342 using namespace std;
344 stringstream stream;
346 stream << "name " << name().c_str() << endl;
348 stream << "constant: " << endl;
349 for (uint i = 0; i != constants.size(); ++i)
350 stream << "\t" << constants[i] << endl;
352 stream << "parameters: " << endl;
353 for (uint i = 0; i != parameters.size(); ++i)
354 stream << "\t" << parameters[i] << endl;
356 stream << "parameter names: " << endl;
357 for (parameter_map_t::const_iterator it = parameter_map.begin();
358 it != parameter_map.end(); ++it)
359 stream << "\t" << it->first.c_str() << " " << it->second << endl;
361 stream << "ugens: " << endl;
362 for (uint i = 0; i != graph.size(); ++i) {
363 stream << "\t" << graph[i].name.c_str() << ": rate " << graph[i].rate
364 << endl;
365 stream << "\tinputs:" << endl;
366 for (uint j = 0; j != graph[i].input_specs.size(); ++j) {
367 stream << "\t\t" << graph[i].input_specs[j].source << " " << graph[i].input_specs[j].index << endl;
371 return stream.str();
375 } /* namespace nova */