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"
30 sc_synth::sc_synth(int node_id
, sc_synth_prototype_ptr
const & prototype
):
31 abstract_synth(node_id
, prototype
), trace(0), unit_buffers(0)
33 World
const & world
= sc_factory
->world
;
34 mNode
.mWorld
= &sc_factory
->world
;
35 rgen
.init((uint32_t)(uint64_t)this);
37 /* initialize sc wrapper class */
39 mSubsampleOffset
= world
.mSubsampleOffset
;
40 mSampleOffset
= world
.mSampleOffset
;
41 mLocalAudioBusUnit
= 0;
42 mLocalControlBusUnit
= 0;
49 sc_synthdef
const & synthdef
= *prototype
;
51 const size_t parameter_count
= synthdef
.parameter_count();
52 const size_t constants_count
= synthdef
.constants
.size();
54 /* we allocate one memory chunk */
55 const size_t wire_buffer_alignment
= 64; // align to cache line boundaries
56 const size_t alloc_size
= prototype
->memory_requirement();
58 const size_t sample_alloc_size
= world
.mBufLength
* synthdef
.buffer_count
59 + wire_buffer_alignment
/* for alignment */;
61 char * chunk
= (char*)rt_pool
.malloc(alloc_size
+ sample_alloc_size
*sizeof(sample
));
63 throw std::bad_alloc();
65 /* prepare controls */
66 mNumControls
= parameter_count
;
67 mControls
= (float*)chunk
; chunk
+= sizeof(float) * parameter_count
;
68 mControlRates
= (int*)chunk
; chunk
+= sizeof(int) * parameter_count
;
69 mMapControls
= (float**)chunk
; chunk
+= sizeof(float*) * parameter_count
;
71 /* initialize controls */
72 for (size_t i
= 0; i
!= parameter_count
; ++i
) {
73 mControls
[i
] = synthdef
.parameters
[i
]; /* initial parameters */
74 mMapControls
[i
] = &mControls
[i
]; /* map to control values */
75 mControlRates
[i
] = 0; /* init to 0*/
78 /* allocate constant wires */
79 mWire
= (Wire
*)chunk
; chunk
+= sizeof(Wire
) * constants_count
;
80 for (size_t i
= 0; i
!= synthdef
.constants
.size(); ++i
) {
81 Wire
* wire
= mWire
+ i
;
85 wire
->mScalarValue
= get_constant(i
);
88 unit_count
= prototype
->unit_count();
89 calc_unit_count
= prototype
->calc_unit_count();
90 units
= (Unit
**)chunk
; chunk
+= unit_count
* sizeof(Unit
*);
91 calc_units
= (Unit
**)chunk
; chunk
+= calc_unit_count
* sizeof(Unit
*);
92 unit_buffers
= (sample
*)chunk
; chunk
+= sample_alloc_size
*sizeof(sample
);
94 const int alignment_mask
= wire_buffer_alignment
- 1;
95 unit_buffers
= (sample
*) ((size_t(unit_buffers
) + alignment_mask
) & ~alignment_mask
); /* next aligned pointer */
97 /* allocate unit generators */
98 sc_factory
->allocate_ugens(synthdef
.graph
.size());
99 for (size_t i
= 0; i
!= synthdef
.graph
.size(); ++i
) {
100 sc_synthdef::unit_spec_t
const & spec
= synthdef
.graph
[i
];
101 units
[i
] = spec
.prototype
->construct(spec
, this, &sc_factory
->world
, chunk
);
104 for (size_t i
= 0; i
!= synthdef
.calc_unit_indices
.size(); ++i
) {
105 int32_t index
= synthdef
.calc_unit_indices
[i
];
106 calc_units
[i
] = units
[index
];
109 assert((char*)mControls
+ alloc_size
<= chunk
); // ensure the memory boundaries
115 void free_ugen(struct Unit
* unit
)
117 sc_ugen_def
* def
= reinterpret_cast<sc_ugen_def
*>(unit
->mUnitDef
);
123 sc_synth::~sc_synth(void)
125 std::for_each(units
, units
+ unit_count
, free_ugen
);
126 sc_factory
->free_ugens(unit_count
);
127 rt_pool
.free(mControls
);
130 void sc_synth::prepare(void)
132 for (size_t i
= 0; i
!= unit_count
; ++i
) {
133 struct Unit
* unit
= units
[i
];
134 sc_ugen_def
* def
= reinterpret_cast<sc_ugen_def
*>(unit
->mUnitDef
);
135 def
->initialize(unit
);
139 void sc_synth::set(slot_index_t slot_index
, sample val
)
141 if (slot_index
>= mNumControls
) {
142 log("argument number out of range\n");
146 mControlRates
[slot_index
] = 0;
147 mMapControls
[slot_index
] = &mControls
[slot_index
];
148 mControls
[slot_index
] = val
;
151 void sc_synth::set_control_array(slot_index_t slot_index
, size_t count
, sample
* val
)
153 if (slot_index
+count
>= mNumControls
)
155 for (size_t i
= 0; i
!= count
; ++i
)
156 set(slot_index
+i
, val
[i
]);
160 void sc_synth::map_control_bus_control (unsigned int slot_index
, int control_bus_index
)
162 if (slot_index
>= mNumControls
)
165 World
*world
= mNode
.mWorld
;
166 if (control_bus_index
< 0) {
167 mControlRates
[slot_index
] = 0;
168 mMapControls
[slot_index
] = mControls
+ slot_index
;
170 else if (uint32(control_bus_index
) < world
->mNumControlBusChannels
) {
171 mControlRates
[slot_index
] = 1;
172 mMapControls
[slot_index
] = world
->mControlBus
+ control_bus_index
;
176 void sc_synth::map_control_buses_control (unsigned int slot_index
, int control_bus_index
, int count
)
178 if (slot_index
>= mNumControls
)
181 int slots_to_set
= std::min(count
, int(mNumControls
- slot_index
));
183 for (int i
= 0; i
!= slots_to_set
; ++i
)
184 map_control_bus_control(slot_index
+i
, control_bus_index
+i
);
187 void sc_synth::map_control_bus_audio (unsigned int slot_index
, int audio_bus_index
)
189 if (slot_index
>= mNumControls
)
192 World
*world
= mNode
.mWorld
;
194 if (audio_bus_index
< 0) {
195 mControlRates
[slot_index
] = 0;
196 mMapControls
[slot_index
] = mControls
+ slot_index
;
197 } else if (uint(audio_bus_index
) < world
->mNumAudioBusChannels
) {
198 mControlRates
[slot_index
] = 2;
199 mMapControls
[slot_index
] = world
->mAudioBus
+ (audio_bus_index
* world
->mBufLength
);
203 void sc_synth::map_control_buses_audio (unsigned int slot_index
, int audio_bus_index
, int count
)
205 if (slot_index
>= mNumControls
)
208 int slots_to_set
= std::min(count
, int(mNumControls
- slot_index
));
210 for (int i
= 0; i
!= slots_to_set
; ++i
)
211 map_control_bus_audio(slot_index
+i
, audio_bus_index
+i
);
214 void sc_synth::apply_unit_cmd(const char * unit_cmd
, unsigned int unit_index
, struct sc_msg_iter
*args
)
216 Unit
* unit
= units
[unit_index
];
217 sc_ugen_def
* def
= reinterpret_cast<sc_ugen_def
*>(unit
->mUnitDef
);
219 def
->run_unit_command(unit_cmd
, unit
, args
);
222 void sc_synth::run(void)
227 extern spin_lock log_guard
;
229 #if defined(__GNUC__) && !defined(__APPLE__)
230 #define thread_local __thread
234 static thread_local
boost::array
<char, 32768> trace_scratchpad
;
236 static boost::array
<char, 32768> trace_scratchpad
;
239 struct scratchpad_printer
241 scratchpad_printer(char * str
):
242 string(str
), position(0)
247 void printf(const char *fmt
, ...)
250 va_start(vargs
, fmt
);
254 const char * data(void) const
259 bool shouldFlush(void) const
261 return position
+ 1024 > 32768;
267 string
[0] = '\0'; // zero-terminated
271 void printf(const char *fmt
, va_list vargs
)
273 position
+= vsprintf(string
+ position
, fmt
, vargs
);
280 void sc_synth::run_traced(void)
285 spin_lock::scoped_lock
lock (log_guard
);
288 scratchpad_printer
printer(trace_scratchpad
.data());
290 printer
.printf("\nTRACE %d %s #units: %d\n", id(), this->prototype_name(), calc_unit_count
);
292 for (size_t i
= 0; i
!= calc_unit_count
; ++i
) {
293 Unit
* unit
= calc_units
[i
];
295 sc_ugen_def
* def
= reinterpret_cast<sc_ugen_def
*>(unit
->mUnitDef
);
296 printer
.printf(" unit %zd %s\n in ", i
, def
->name());
297 for (uint16_t j
=0; j
!=unit
->mNumInputs
; ++j
) {
298 printer
.printf(" %g", unit
->mInBuf
[j
][0]);
299 if (printer
.shouldFlush()) {
301 spin_lock::scoped_lock
lock (log_guard
);
308 printer
.printf("\n");
310 (unit
->mCalcFunc
)(unit
, unit
->mBufLength
);
312 printer
.printf(" out");
313 for (int j
=0; j
<unit
->mNumOutputs
; ++j
) {
314 printer
.printf(" %g", unit
->mOutBuf
[j
][0]);
315 if (printer
.shouldFlush()) {
317 spin_lock::scoped_lock
lock (log_guard
);
323 printer
.printf("\n");
325 printer
.printf("\n");
328 spin_lock::scoped_lock
lock (log_guard
);
333 } /* namespace nova */