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.
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"
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
;
46 std::string
read_pstring(const char *& buffer
)
49 char name_size
= *buffer
++;
50 memcpy(str
, buffer
, name_size
);
51 str
[int(name_size
)] = 0;
54 return std::string(str
);
57 float read_float(const char *& ptr
)
59 big32_t data
= *(big32_t
*)ptr
;
71 int8_t read_int8(const char *& ptr
)
73 big8_t data
= *(big8_t
*)ptr
;
78 int16_t read_int16(const char *& ptr
)
80 big16_t data
= *(big16_t
*)ptr
;
85 int32_t read_int32(const char *& ptr
)
87 big32_t data
= *(big32_t
*)ptr
;
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
) {
105 sc_synthdef
def(buf_ptr
);
108 catch (std::runtime_error
const & e
) {
109 std::cerr
<< "Exception when reading synthdef: " << e
.what() << std::endl
;
115 std::vector
<sc_synthdef
> read_synthdef_file(boost::filesystem::path
const & filename
)
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
);
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
)
165 void sc_synthdef::read_synthdef(const char *& ptr
)
170 name_
= read_pstring(ptr
);
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
);
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 */
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
)
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
)
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
;
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
) {
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
) {
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
);
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
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
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
;
375 } /* namespace nova */