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