1 /* -*- c-basic-offset: 4 -*- vi:set ts=8 sts=4 sw=4: */
2
3 /* karplong.c
4
5 DSSI Soft Synth Interface
6 Constructed by Chris Cannam, Steve Harris and Sean Bolton
7
8 This is an example DSSI synth plugin written by Chris Cannam.
9
10 It implements the basic Karplus-Strong plucked-string synthesis
11 algorithm (Kevin Karplus & Alex Strong, "Digital Synthesis of
12 Plucked-String and Drum Timbres", Computer Music Journal 1983).
13
14 My belief is that this algorithm is no longer patented anywhere in
15 the world. The algorithm was protected by US patent no 4,649,783,
16 filed in 1984 and granted to Stanford University in 1987. This
17 patent expired in June 2004. The related European patent EP0124197
18 lapsed during the 1990s.
19
20 This also serves as an example of one way to structure a simple
21 DSSI plugin in C++.
22
23 This example file is placed in the public domain.
24 */
25
26 #include <stdlib.h>
27 #include <string.h>
28 #include <math.h>
29
30 #include "dssi.h"
31 #include "ladspa.h"
32
33 //#define DEBUG_KARPLONG 1
34
35 #ifdef DEBUG_KARPLONG
36 #include <iostream>
37 #endif
38
39 class Karplong
40 {
41 public:
42 static const DSSI_Descriptor *getDescriptor(unsigned long index);
43
44 private:
45 Karplong(int sampleRate);
46 ~Karplong();
47
48 enum {
49 OutputPort = 0,
50 Sustain = 1,
51 PortCount = 2
52 };
53
54 enum {
55 Notes = 128
56 };
57
58 static const char *const portNames[PortCount];
59 static const LADSPA_PortDescriptor ports[PortCount];
60 static const LADSPA_PortRangeHint hints[PortCount];
61 static const LADSPA_Properties properties;
62 static const LADSPA_Descriptor ladspaDescriptor;
63 static const DSSI_Descriptor dssiDescriptor;
64
65 static LADSPA_Handle instantiate(const LADSPA_Descriptor *, unsigned long);
66 static void connectPort(LADSPA_Handle, unsigned long, LADSPA_Data *);
67 static void activate(LADSPA_Handle);
68 static void run(LADSPA_Handle, unsigned long);
69 static void deactivate(LADSPA_Handle);
70 static void cleanup(LADSPA_Handle);
71 static const DSSI_Program_Descriptor *getProgram(LADSPA_Handle, unsigned long);
72 static void selectProgram(LADSPA_Handle, unsigned long, unsigned long);
73 static int getMidiController(LADSPA_Handle, unsigned long);
74 static void runSynth(LADSPA_Handle, unsigned long,
75 snd_seq_event_t *, unsigned long);
76
77 void runImpl(unsigned long, snd_seq_event_t *, unsigned long);
78 void addSamples(int, unsigned long, unsigned long);
79
80 float *m_output;
81 float *m_sustain;
82
83 int m_sampleRate;
84 long m_blockStart;
85
86 long m_ons[Notes];
87 long m_offs[Notes];
88 int m_velocities[Notes];
89 float *m_wavetable[Notes];
90 float m_sizes[Notes];
91 };
92
93 const char *const
94 Karplong::portNames[PortCount] =
95 {
96 "Output",
97 "Sustain (on/off)",
98 };
99
100 const LADSPA_PortDescriptor
101 Karplong::ports[PortCount] =
102 {
103 LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO,
104 LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL,
105 };
106
107 const LADSPA_PortRangeHint
108 Karplong::hints[PortCount] =
109 {
110 { 0, 0, 0 },
111 { LADSPA_HINT_DEFAULT_MINIMUM | LADSPA_HINT_INTEGER |
112 LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE, 0, 1 },
113 };
114
115 const LADSPA_Properties
116 Karplong::properties = LADSPA_PROPERTY_HARD_RT_CAPABLE;
117
118 const LADSPA_Descriptor
119 Karplong::ladspaDescriptor =
120 {
121 0, // "Unique" ID
122 "karplong", // Label
123 properties,
124 "Simple Karplus-Strong Plucked String Synth", // Name
125 "Chris Cannam", // Maker
126 "Public Domain", // Copyright
127 PortCount,
128 ports,
129 portNames,
130 hints,
131 0, // Implementation data
132 instantiate,
133 connectPort,
134 activate,
135 run,
136 0, // Run adding
137 0, // Set run adding gain
138 deactivate,
139 cleanup
140 };
141
142 const DSSI_Descriptor
143 Karplong::dssiDescriptor =
144 {
145 1, // DSSI API version
146 &ladspaDescriptor,
147 0, // Configure
148 0, // Get Program
149 0, // Select Program
150 getMidiController,
151 runSynth,
152 0, // Run synth adding
153 0, // Run multiple synths
154 0, // Run multiple synths adding
155 };
156
157
158 const DSSI_Descriptor *
getDescriptor(unsigned long index)159 Karplong::getDescriptor(unsigned long index)
160 {
161 if (index == 0) return &dssiDescriptor;
162 return 0;
163 }
164
Karplong(int sampleRate)165 Karplong::Karplong(int sampleRate) :
166 m_output(0),
167 m_sustain(0),
168 m_sampleRate(sampleRate),
169 m_blockStart(0)
170 {
171 for (int i = 0; i < Notes; ++i) {
172 float frequency = 440.0f * powf(2.0, (i - 69.0) / 12.0);
173 m_sizes[i] = m_sampleRate / frequency;
174 m_wavetable[i] = new float[int(m_sizes[i]) + 1];
175 }
176 }
177
~Karplong()178 Karplong::~Karplong()
179 {
180 for (int i = 0; i < Notes; ++i) {
181 delete[] m_wavetable[i];
182 }
183 }
184
185 LADSPA_Handle
instantiate(const LADSPA_Descriptor *,unsigned long rate)186 Karplong::instantiate(const LADSPA_Descriptor *, unsigned long rate)
187 {
188 Karplong *karplong = new Karplong(rate);
189 return karplong;
190 }
191
192 void
connectPort(LADSPA_Handle handle,unsigned long port,LADSPA_Data * location)193 Karplong::connectPort(LADSPA_Handle handle,
194 unsigned long port, LADSPA_Data *location)
195 {
196 Karplong *karplong = (Karplong *)handle;
197
198 float **ports[PortCount] = {
199 &karplong->m_output,
200 &karplong->m_sustain,
201 };
202
203 *ports[port] = (float *)location;
204 }
205
206 void
activate(LADSPA_Handle handle)207 Karplong::activate(LADSPA_Handle handle)
208 {
209 Karplong *karplong = (Karplong *)handle;
210
211 karplong->m_blockStart = 0;
212
213 for (size_t i = 0; i < Notes; ++i) {
214 karplong->m_ons[i] = -1;
215 karplong->m_offs[i] = -1;
216 karplong->m_velocities[i] = 0;
217 }
218 }
219
220 void
run(LADSPA_Handle handle,unsigned long samples)221 Karplong::run(LADSPA_Handle handle, unsigned long samples)
222 {
223 runSynth(handle, samples, 0, 0);
224 }
225
226 void
deactivate(LADSPA_Handle handle)227 Karplong::deactivate(LADSPA_Handle handle)
228 {
229 activate(handle); // both functions just reset the plugin
230 }
231
232 void
cleanup(LADSPA_Handle handle)233 Karplong::cleanup(LADSPA_Handle handle)
234 {
235 delete (Karplong *)handle;
236 }
237
238 int
getMidiController(LADSPA_Handle,unsigned long port)239 Karplong::getMidiController(LADSPA_Handle, unsigned long port)
240 {
241 int controllers[PortCount] = {
242 DSSI_NONE,
243 DSSI_CC(64)
244 };
245
246 return controllers[port];
247 }
248
249 void
runSynth(LADSPA_Handle handle,unsigned long samples,snd_seq_event_t * events,unsigned long eventCount)250 Karplong::runSynth(LADSPA_Handle handle, unsigned long samples,
251 snd_seq_event_t *events, unsigned long eventCount)
252 {
253 Karplong *karplong = (Karplong *)handle;
254
255 karplong->runImpl(samples, events, eventCount);
256 }
257
258 void
runImpl(unsigned long sampleCount,snd_seq_event_t * events,unsigned long eventCount)259 Karplong::runImpl(unsigned long sampleCount,
260 snd_seq_event_t *events,
261 unsigned long eventCount)
262 {
263 unsigned long pos;
264 unsigned long count;
265 unsigned long eventPos;
266 snd_seq_ev_note_t n;
267 int i;
268
269 for (pos = 0, eventPos = 0; pos < sampleCount; ) {
270
271 while (eventPos < eventCount &&
272 pos >= events[eventPos].time.tick) {
273
274 switch (events[eventPos].type) {
275
276 case SND_SEQ_EVENT_NOTEON:
277 n = events[eventPos].data.note;
278 if (n.velocity > 0) {
279 m_ons[n.note] = m_blockStart + events[eventPos].time.tick;
280 m_offs[n.note] = -1;
281 m_velocities[n.note] = n.velocity;
282 }
283 break;
284
285 case SND_SEQ_EVENT_NOTEOFF:
286 n = events[eventPos].data.note;
287 m_offs[n.note] = m_blockStart + events[eventPos].time.tick;
288 break;
289
290 default:
291 break;
292 }
293
294 ++eventPos;
295 }
296
297 count = sampleCount - pos;
298 if (eventPos < eventCount &&
299 events[eventPos].time.tick < sampleCount) {
300 count = events[eventPos].time.tick - pos;
301 }
302
303 for (i = 0; i < count; ++i) {
304 m_output[pos + i] = 0;
305 }
306
307 for (i = 0; i < Notes; ++i) {
308 if (m_ons[i] >= 0) {
309 addSamples(i, pos, count);
310 }
311 }
312
313 pos += count;
314 }
315
316 m_blockStart += sampleCount;
317 }
318
319 void
addSamples(int voice,unsigned long offset,unsigned long count)320 Karplong::addSamples(int voice, unsigned long offset, unsigned long count)
321 {
322 #ifdef DEBUG_KARPLONG
323 std::cerr << "Karplong::addSamples(" << voice << ", " << offset
324 << ", " << count << "): on " << m_ons[voice] << ", off "
325 << m_offs[voice] << ", size " << m_sizes[voice]
326 << ", start " << m_blockStart + offset << std::endl;
327 #endif
328
329 if (m_ons[voice] < 0) return;
330
331 unsigned long on = (unsigned long)(m_ons[voice]);
332 unsigned long start = m_blockStart + offset;
333
334 if (start < on) return;
335
336 if (start == on) {
337 for (size_t i = 0; i <= int(m_sizes[voice]); ++i) {
338 m_wavetable[voice][i] = (float(rand()) / float(RAND_MAX)) * 2 - 1;
339 }
340 }
341
342 size_t i, s;
343
344 float vgain = (float)(m_velocities[voice]) / 127.0f;
345
346 for (i = 0, s = start - on;
347 i < count;
348 ++i, ++s) {
349
350 float gain(vgain);
351
352 if ((!m_sustain || !*m_sustain) &&
353 m_offs[voice] >= 0 &&
354 (unsigned long)(m_offs[voice]) < i + start) {
355
356 unsigned long release = 1 + (0.01 * m_sampleRate);
357 unsigned long dist = i + start - m_offs[voice];
358
359 if (dist > release) {
360 m_ons[voice] = -1;
361 break;
362 }
363
364 gain = gain * float(release - dist) / float(release);
365 }
366
367 size_t sz = int(m_sizes[voice]);
368 bool decay = (s > sz);
369 size_t index = (s % int(sz));
370
371 float sample = m_wavetable[voice][index];
372
373 if (decay) {
374 if (index == 0) sample += m_wavetable[voice][sz - 1];
375 else sample += m_wavetable[voice][index - 1];
376 sample /= 2;
377 m_wavetable[voice][index] = sample;
378 }
379
380 m_output[offset + i] += gain * sample;
381 }
382 }
383
384 extern "C" {
385
ladspa_descriptor(unsigned long index)386 const LADSPA_Descriptor *ladspa_descriptor(unsigned long index)
387 {
388 return 0;
389 }
390
dssi_descriptor(unsigned long index)391 const DSSI_Descriptor *dssi_descriptor(unsigned long index)
392 {
393 return Karplong::getDescriptor(index);
394 }
395
396 }
397
398