1 // supercollider-style synthdef
2 // Copyright (C) 2008, 2009, 2010 Tim Blechmann
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.
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.
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"
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
;
47 std::string
read_pstring(const char *& buffer
)
50 char name_size
= *buffer
++;
51 memcpy(str
, buffer
, name_size
);
52 str
[int(name_size
)] = 0;
55 return std::string(str
);
58 float read_float(const char *& ptr
)
60 big32_t data
= *(big32_t
*)ptr
;
72 int8_t read_int8(const char *& ptr
)
74 big8_t data
= *(big8_t
*)ptr
;
79 int16_t read_int16(const char *& ptr
)
81 big16_t data
= *(big16_t
*)ptr
;
86 int32_t read_int32(const char *& ptr
)
88 big32_t data
= *(big32_t
*)ptr
;
93 int32_t read_int(const char *& ptr
, int size
)
96 return read_int32(ptr
);
99 return read_int16(ptr
);
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
) {
117 sc_synthdef
def(buf_ptr
, version
);
120 catch (std::runtime_error
const & e
) {
121 std::cerr
<< "Exception when reading synthdef: " << e
.what() << std::endl
;
127 std::vector
<sc_synthdef
> read_synthdef_file(boost::filesystem::path
const & filename
)
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
);
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
)
182 const int short_int_size
= (version
== 1) ? 16 : 32;
185 name_
= read_pstring(ptr
);
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
);
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 */
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
)
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
)
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
;
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
;
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
) {
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
) {
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
);
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
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
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
;
397 } /* namespace nova */