1 // synth based on supercollider-style synthdef
2 // Copyright (C) 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.
21 #include <boost/bind.hpp>
23 #include "sc_synth.hpp"
24 #include "sc_ugen_factory.hpp"
25 #include "../server/server.hpp"
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 */
38 mSubsampleOffset
= world
.mSubsampleOffset
;
39 mSampleOffset
= world
.mSampleOffset
;
40 mLocalAudioBusUnit
= 0;
41 mLocalControlBusUnit
= 0;
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
;
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
116 void free_ugen(struct Unit
* unit
)
118 sc_ugen_def
* def
= reinterpret_cast<sc_ugen_def
*>(unit
->mUnitDef
);
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");
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
)
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
)
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
)
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
)
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
)
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)
232 extern spin_lock log_guard
;
234 #if defined(__GNUC__) && !defined(__APPLE__)
235 #define thread_local __thread
239 static thread_local
boost::array
<char, 32768> trace_scratchpad
;
241 static boost::array
<char, 32768> trace_scratchpad
;
244 struct scratchpad_printer
246 scratchpad_printer(char * str
):
247 string(str
), position(0)
252 void printf(const char *fmt
, ...)
255 va_start(vargs
, fmt
);
259 const char * data(void) const
264 bool shouldFlush(void) const
266 return position
+ 1024 > 32768;
272 string
[0] = '\0'; // zero-terminated
276 void printf(const char *fmt
, va_list vargs
)
278 position
+= vsprintf(string
+ position
, fmt
, vargs
);
285 void sc_synth::run_traced(void)
290 spin_lock::scoped_lock
lock (log_guard
);
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()) {
306 spin_lock::scoped_lock
lock (log_guard
);
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()) {
322 spin_lock::scoped_lock
lock (log_guard
);
328 printer
.printf("\n");
330 printer
.printf("\n");
333 spin_lock::scoped_lock
lock (log_guard
);
338 } /* namespace nova */