Merge pull request #506 from andrewcsmith/patch-2
[supercollider.git] / server / supernova / sc / sc_synth.cpp
blob10e5b76dbaa223818f646e1ed55b8436ed5057c7
1 // synth based on supercollider-style synthdef
2 // Copyright (C) 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 <cstdio>
21 #include <boost/bind.hpp>
23 #include "sc_synth.hpp"
24 #include "sc_ugen_factory.hpp"
25 #include "../server/server.hpp"
27 namespace nova {
29 sc_synth::sc_synth(int node_id, sc_synth_definition_ptr const & prototype):
30 abstract_synth(node_id, prototype), trace(0), unit_buffers(0)
32 World const & world = sc_factory->world;
33 mNode.mWorld = &sc_factory->world;
34 rgen.init((uint32_t)(uint64_t)this);
36 /* initialize sc wrapper class */
37 mRGen = &rgen;
38 mSubsampleOffset = world.mSubsampleOffset;
39 mSampleOffset = world.mSampleOffset;
40 mLocalAudioBusUnit = 0;
41 mLocalControlBusUnit = 0;
43 localBufNum = 0;
44 localMaxBufNum = 0;
46 mNode.mID = node_id;
48 sc_synthdef const & synthdef = *prototype;
50 const size_t parameter_count = synthdef.parameter_count();
51 const size_t constants_count = synthdef.constants.size();
53 /* we allocate one memory chunk */
54 const size_t wire_buffer_alignment = 64 * 8; // align to cache line boundaries
55 const size_t alloc_size = prototype->memory_requirement();
57 const size_t sample_alloc_size = world.mBufLength * synthdef.buffer_count
58 + wire_buffer_alignment /* for alignment */;
60 char * raw_chunk = (char*)rt_pool.malloc(alloc_size + sample_alloc_size*sizeof(sample));
61 if (raw_chunk == NULL)
62 throw std::bad_alloc();
64 linear_allocator allocator(raw_chunk);
66 /* prepare controls */
67 mNumControls = parameter_count;
68 mControls = allocator.alloc<float>(parameter_count);
69 mControlRates = allocator.alloc<int>(parameter_count);
70 mMapControls = allocator.alloc<float*>(parameter_count);
72 /* initialize controls */
73 for (size_t i = 0; i != parameter_count; ++i) {
74 mControls[i] = synthdef.parameters[i]; /* initial parameters */
75 mMapControls[i] = &mControls[i]; /* map to control values */
76 mControlRates[i] = 0; /* init to 0*/
79 /* allocate constant wires */
80 mWire = allocator.alloc<Wire>(constants_count);
81 for (size_t i = 0; i != synthdef.constants.size(); ++i) {
82 Wire * wire = mWire + i;
83 wire->mFromUnit = 0;
84 wire->mCalcRate = 0;
85 wire->mBuffer = 0;
86 wire->mScalarValue = get_constant(i);
89 unit_count = prototype->unit_count();
90 calc_unit_count = prototype->calc_unit_count();
91 units = allocator.alloc<Unit*>(unit_count);
92 calc_units = allocator.alloc<Unit*>(calc_unit_count);
93 unit_buffers = allocator.alloc<sample>(sample_alloc_size);
95 const int alignment_mask = wire_buffer_alignment - 1;
96 unit_buffers = (sample*) ((intptr_t(unit_buffers) + alignment_mask) & ~alignment_mask); /* next aligned pointer */
98 /* allocate unit generators */
99 sc_factory->allocate_ugens(synthdef.graph.size());
100 for (size_t i = 0; i != synthdef.graph.size(); ++i) {
101 sc_synthdef::unit_spec_t const & spec = synthdef.graph[i];
102 units[i] = spec.prototype->construct(spec, this, &sc_factory->world, allocator);
105 for (size_t i = 0; i != synthdef.calc_unit_indices.size(); ++i) {
106 int32_t index = synthdef.calc_unit_indices[i];
107 calc_units[i] = units[index];
110 assert((char*)mControls + alloc_size <= allocator.alloc<char>()); // ensure the memory boundaries
113 namespace
116 void free_ugen(struct Unit * unit)
118 sc_ugen_def * def = reinterpret_cast<sc_ugen_def*>(unit->mUnitDef);
119 def->destruct(unit);
122 } /* namespace */
124 sc_synth::~sc_synth(void)
126 std::for_each(units, units + unit_count, free_ugen);
127 sc_factory->free_ugens(unit_count);
128 rt_pool.free(mControls);
131 void sc_synth::prepare(void)
133 for (size_t i = 0; i != unit_count; ++i) {
134 struct Unit * unit = units[i];
135 sc_ugen_def * def = reinterpret_cast<sc_ugen_def*>(unit->mUnitDef);
136 def->initialize(unit);
140 void sc_synth::set(slot_index_t slot_index, sample val)
142 if (slot_index >= mNumControls) {
143 log("argument number out of range\n");
144 return;
147 mControlRates[slot_index] = 0;
148 mMapControls[slot_index] = &mControls[slot_index];
149 mControls[slot_index] = val;
152 void sc_synth::set_control_array(slot_index_t slot_index, size_t count, sample * val)
154 if (slot_index+count >= mNumControls)
155 return;
156 for (size_t i = 0; i != count; ++i)
157 set(slot_index+i, val[i]);
160 float sc_synth::get(slot_index_t slot_index) const
162 return mControls[slot_index];
165 void sc_synth::map_control_bus_control (unsigned int slot_index, int control_bus_index)
167 if (slot_index >= mNumControls)
168 return;
170 World *world = mNode.mWorld;
171 if (control_bus_index < 0) {
172 mControlRates[slot_index] = 0;
173 mMapControls[slot_index] = mControls + slot_index;
175 else if (uint32(control_bus_index) < world->mNumControlBusChannels) {
176 mControlRates[slot_index] = 1;
177 mMapControls[slot_index] = world->mControlBus + control_bus_index;
181 void sc_synth::map_control_buses_control (unsigned int slot_index, int control_bus_index, int count)
183 if (slot_index >= mNumControls)
184 return;
186 int slots_to_set = std::min(count, int(mNumControls - slot_index));
188 for (int i = 0; i != slots_to_set; ++i)
189 map_control_bus_control(slot_index+i, control_bus_index+i);
192 void sc_synth::map_control_bus_audio (unsigned int slot_index, int audio_bus_index)
194 if (slot_index >= mNumControls)
195 return;
197 World *world = mNode.mWorld;
199 if (audio_bus_index < 0) {
200 mControlRates[slot_index] = 0;
201 mMapControls[slot_index] = mControls + slot_index;
202 } else if (uint(audio_bus_index) < world->mNumAudioBusChannels) {
203 mControlRates[slot_index] = 2;
204 mMapControls[slot_index] = world->mAudioBus + (audio_bus_index * world->mBufLength);
208 void sc_synth::map_control_buses_audio (unsigned int slot_index, int audio_bus_index, int count)
210 if (slot_index >= mNumControls)
211 return;
213 int slots_to_set = std::min(count, int(mNumControls - slot_index));
215 for (int i = 0; i != slots_to_set; ++i)
216 map_control_bus_audio(slot_index+i, audio_bus_index+i);
219 void sc_synth::apply_unit_cmd(const char * unit_cmd, unsigned int unit_index, struct sc_msg_iter *args)
221 Unit * unit = units[unit_index];
222 sc_ugen_def * def = reinterpret_cast<sc_ugen_def*>(unit->mUnitDef);
224 def->run_unit_command(unit_cmd, unit, args);
227 void sc_synth::run(void)
229 perform();
232 extern spin_lock log_guard;
234 #if defined(__GNUC__) && !defined(__APPLE__)
235 #define thread_local __thread
236 #endif
238 #ifdef thread_local
239 static thread_local boost::array<char, 32768> trace_scratchpad;
240 #else
241 static boost::array<char, 32768> trace_scratchpad;
242 #endif
244 struct scratchpad_printer
246 scratchpad_printer(char * str):
247 string(str), position(0)
249 clear();
252 void printf(const char *fmt, ...)
254 va_list vargs;
255 va_start(vargs, fmt);
256 printf(fmt, vargs);
259 const char * data(void) const
261 return string;
264 bool shouldFlush(void) const
266 return position + 1024 > 32768;
269 void clear(void)
271 position = 0;
272 string[0] = '\0'; // zero-terminated
275 private:
276 void printf(const char *fmt, va_list vargs)
278 position += vsprintf(string + position, fmt, vargs);
281 char * string;
282 int position;
285 void sc_synth::run_traced(void)
287 trace = 0;
289 #ifndef thread_local
290 spin_lock::scoped_lock lock (log_guard);
291 #endif
293 scratchpad_printer printer(trace_scratchpad.data());
295 printer.printf("\nTRACE %d %s #units: %d\n", id(), this->definition_name(), calc_unit_count);
297 for (size_t i = 0; i != calc_unit_count; ++i) {
298 Unit * unit = calc_units[i];
300 sc_ugen_def * def = reinterpret_cast<sc_ugen_def*>(unit->mUnitDef);
301 printer.printf(" unit %zd %s\n in ", i, def->name());
302 for (uint16_t j=0; j!=unit->mNumInputs; ++j) {
303 printer.printf(" %g", unit->mInBuf[j][0]);
304 if (printer.shouldFlush()) {
305 #ifdef thread_local
306 spin_lock::scoped_lock lock (log_guard);
307 #endif
308 log(printer.data());
309 printer.clear();
313 printer.printf("\n");
315 (unit->mCalcFunc)(unit, unit->mBufLength);
317 printer.printf(" out");
318 for (int j=0; j<unit->mNumOutputs; ++j) {
319 printer.printf(" %g", unit->mOutBuf[j][0]);
320 if (printer.shouldFlush()) {
321 #ifdef thread_local
322 spin_lock::scoped_lock lock (log_guard);
323 #endif
324 log(printer.data());
325 printer.clear();
328 printer.printf("\n");
330 printer.printf("\n");
332 #ifdef thread_local
333 spin_lock::scoped_lock lock (log_guard);
334 #endif
335 log(printer.data());
338 } /* namespace nova */