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 alloc_size
= prototype
->memory_requirement();
57 const size_t sample_alloc_size
= world
.mBufLength
* synthdef
.buffer_count
58 + vec
<float>::size
* sizeof(float) /* for alignment */;
60 char * chunk
= (char*)rt_pool
.malloc(alloc_size
+ sample_alloc_size
*sizeof(sample
));
62 throw std::bad_alloc();
64 /* prepare controls */
65 mNumControls
= parameter_count
;
66 mControls
= (float*)chunk
; chunk
+= sizeof(float) * parameter_count
;
67 mControlRates
= (int*)chunk
; chunk
+= sizeof(int) * parameter_count
;
68 mMapControls
= (float**)chunk
; chunk
+= sizeof(float*) * parameter_count
;
70 /* initialize controls */
71 for (size_t i
= 0; i
!= parameter_count
; ++i
) {
72 mControls
[i
] = synthdef
.parameters
[i
]; /* initial parameters */
73 mMapControls
[i
] = &mControls
[i
]; /* map to control values */
74 mControlRates
[i
] = 0; /* init to 0*/
77 /* allocate constant wires */
78 mWire
= (Wire
*)chunk
; chunk
+= sizeof(Wire
) * constants_count
;
79 for (size_t i
= 0; i
!= synthdef
.constants
.size(); ++i
) {
80 Wire
* wire
= mWire
+ i
;
84 wire
->mScalarValue
= get_constant(i
);
87 unit_count
= prototype
->unit_count();
88 calc_unit_count
= prototype
->calc_unit_count();
89 units
= (Unit
**)chunk
; chunk
+= unit_count
* sizeof(Unit
*);
90 calc_units
= (Unit
**)chunk
; chunk
+= calc_unit_count
* sizeof(Unit
*);
91 unit_buffers
= (sample
*)chunk
; chunk
+= sample_alloc_size
*sizeof(sample
);
93 const int alignment_mask
= vec
<float>::size
* sizeof(float) - 1;
94 unit_buffers
= (sample
*) ((size_t(unit_buffers
) + alignment_mask
) & ~alignment_mask
); /* next aligned pointer */
96 /* allocate unit generators */
97 sc_factory
->allocate_ugens(synthdef
.graph
.size());
98 for (size_t i
= 0; i
!= synthdef
.graph
.size(); ++i
) {
99 sc_synthdef::unit_spec_t
const & spec
= synthdef
.graph
[i
];
100 units
[i
] = spec
.prototype
->construct(spec
, this, &sc_factory
->world
, chunk
);
103 for (size_t i
= 0; i
!= synthdef
.calc_unit_indices
.size(); ++i
) {
104 int32_t index
= synthdef
.calc_unit_indices
[i
];
105 calc_units
[i
] = units
[index
];
108 assert((char*)mControls
+ alloc_size
<= chunk
); // ensure the memory boundaries
114 void free_ugen(struct Unit
* unit
)
116 sc_ugen_def
* def
= reinterpret_cast<sc_ugen_def
*>(unit
->mUnitDef
);
122 sc_synth::~sc_synth(void)
124 std::for_each(units
, units
+ unit_count
, free_ugen
);
125 sc_factory
->free_ugens(unit_count
);
126 rt_pool
.free(mControls
);
129 void sc_synth::prepare(void)
131 for (size_t i
= 0; i
!= unit_count
; ++i
) {
132 struct Unit
* unit
= units
[i
];
133 sc_ugen_def
* def
= reinterpret_cast<sc_ugen_def
*>(unit
->mUnitDef
);
134 def
->initialize(unit
);
138 void sc_synth::set(slot_index_t slot_index
, sample val
)
140 if (slot_index
>= mNumControls
) {
141 log("argument number out of range\n");
145 mControlRates
[slot_index
] = 0;
146 mMapControls
[slot_index
] = &mControls
[slot_index
];
147 mControls
[slot_index
] = val
;
150 void sc_synth::set_control_array(slot_index_t slot_index
, size_t count
, sample
* val
)
152 if (slot_index
+count
>= mNumControls
)
154 for (size_t i
= 0; i
!= count
; ++i
)
155 set(slot_index
+i
, val
[i
]);
159 void sc_synth::map_control_bus_control (unsigned int slot_index
, int control_bus_index
)
161 if (slot_index
>= mNumControls
)
164 World
*world
= mNode
.mWorld
;
165 if (control_bus_index
< 0) {
166 mControlRates
[slot_index
] = 0;
167 mMapControls
[slot_index
] = mControls
+ slot_index
;
169 else if (uint32(control_bus_index
) < world
->mNumControlBusChannels
) {
170 mControlRates
[slot_index
] = 1;
171 mMapControls
[slot_index
] = world
->mControlBus
+ control_bus_index
;
175 void sc_synth::map_control_buses_control (unsigned int slot_index
, int control_bus_index
, int count
)
177 if (slot_index
>= mNumControls
)
180 int slots_to_set
= std::min(count
, int(mNumControls
- slot_index
));
182 for (int i
= 0; i
!= slots_to_set
; ++i
)
183 map_control_bus_control(slot_index
+i
, control_bus_index
+i
);
186 void sc_synth::map_control_bus_audio (unsigned int slot_index
, int audio_bus_index
)
188 if (slot_index
>= mNumControls
)
191 World
*world
= mNode
.mWorld
;
193 if (audio_bus_index
< 0) {
194 mControlRates
[slot_index
] = 0;
195 mMapControls
[slot_index
] = mControls
+ slot_index
;
196 } else if (uint(audio_bus_index
) < world
->mNumAudioBusChannels
) {
197 mControlRates
[slot_index
] = 2;
198 mMapControls
[slot_index
] = world
->mAudioBus
+ (audio_bus_index
* world
->mBufLength
);
202 void sc_synth::map_control_buses_audio (unsigned int slot_index
, int audio_bus_index
, int count
)
204 if (slot_index
>= mNumControls
)
207 int slots_to_set
= std::min(count
, int(mNumControls
- slot_index
));
209 for (int i
= 0; i
!= slots_to_set
; ++i
)
210 map_control_bus_audio(slot_index
+i
, audio_bus_index
+i
);
213 void sc_synth::apply_unit_cmd(const char * unit_cmd
, unsigned int unit_index
, struct sc_msg_iter
*args
)
215 Unit
* unit
= units
[unit_index
];
216 sc_ugen_def
* def
= reinterpret_cast<sc_ugen_def
*>(unit
->mUnitDef
);
218 def
->run_unit_command(unit_cmd
, unit
, args
);
221 void sc_synth::run(void)
226 extern spin_lock log_guard
;
228 #if defined(__GNUC__) && !defined(__APPLE__)
229 #define thread_local __thread
233 static thread_local
boost::array
<char, 32768> trace_scratchpad
;
235 static boost::array
<char, 32768> trace_scratchpad
;
238 struct scratchpad_printer
240 scratchpad_printer(char * str
):
241 string(str
), position(0)
246 void printf(const char *fmt
, ...)
249 va_start(vargs
, fmt
);
253 const char * data(void) const
258 bool shouldFlush(void) const
260 return position
+ 1024 > 32768;
266 string
[0] = '\0'; // zero-terminated
270 void printf(const char *fmt
, va_list vargs
)
272 position
+= vsprintf(string
+ position
, fmt
, vargs
);
279 void sc_synth::run_traced(void)
284 spin_lock::scoped_lock
lock (log_guard
);
287 scratchpad_printer
printer(trace_scratchpad
.data());
289 printer
.printf("\nTRACE %d %s #units: %d\n", id(), this->prototype_name(), calc_unit_count
);
291 for (size_t i
= 0; i
!= calc_unit_count
; ++i
) {
292 Unit
* unit
= calc_units
[i
];
294 sc_ugen_def
* def
= reinterpret_cast<sc_ugen_def
*>(unit
->mUnitDef
);
295 printer
.printf(" unit %zd %s\n in ", i
, def
->name());
296 for (uint16_t j
=0; j
!=unit
->mNumInputs
; ++j
) {
297 printer
.printf(" %g", unit
->mInBuf
[j
][0]);
298 if (printer
.shouldFlush()) {
300 spin_lock::scoped_lock
lock (log_guard
);
307 printer
.printf("\n");
309 (unit
->mCalcFunc
)(unit
, unit
->mBufLength
);
311 printer
.printf(" out");
312 for (int j
=0; j
<unit
->mNumOutputs
; ++j
) {
313 printer
.printf(" %g", unit
->mOutBuf
[j
][0]);
314 if (printer
.shouldFlush()) {
316 spin_lock::scoped_lock
lock (log_guard
);
322 printer
.printf("\n");
324 printer
.printf("\n");
327 spin_lock::scoped_lock
lock (log_guard
);
332 } /* namespace nova */