LMMS
Loading...
Searching...
No Matches
DistrhoPluginNekobi.cpp
Go to the documentation of this file.
1/*
2 * DISTRHO Nekobi Plugin, based on Nekobee by Sean Bolton and others.
3 * Copyright (C) 2004 Sean Bolton and others
4 * Copyright (C) 2013-2015 Filipe Coelho <falktx@falktx.com>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of
9 * the License, or any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * For a full copy of the GNU General Public License see the LICENSE file.
17 */
18
19#include "DistrhoPluginNekobi.hpp"
20
21extern "C" {
22
27
28// -----------------------------------------------------------------------
29// mutual exclusion
30
32{
33 /* Attempt the mutex lock */
34 if (pthread_mutex_trylock(&synth->voicelist_mutex) != 0)
35 {
36 synth->voicelist_mutex_grab_failed = 1;
37 return false;
38 }
39
40 /* Clean up if a previous mutex grab failed */
41 if (synth->voicelist_mutex_grab_failed)
42 {
44 synth->voicelist_mutex_grab_failed = 0;
45 }
46
47 return true;
48}
49
51{
52 return (pthread_mutex_unlock(&synth->voicelist_mutex) == 0);
53}
54
55// -----------------------------------------------------------------------
56// nekobee_handle_raw_event
57
59{
60 if (size != 3)
61 return;
62
63 switch (data[0] & 0xf0)
64 {
65 case 0x80:
67 break;
68 case 0x90:
69 if (data[2] > 0)
71 else
72 nekobee_synth_note_off(synth, data[1], 64); /* shouldn't happen, but... */
73 break;
74 case 0xB0:
76 break;
77 default:
78 break;
79 }
80}
81
82} /* extern "C" */
83
85
86// -----------------------------------------------------------------------
87
88DistrhoPluginNekobi::DistrhoPluginNekobi()
89 : Plugin(paramCount, 0, 0) // 0 programs, 0 states
90{
92
93 // init synth
94 fSynth.sample_rate = getSampleRate();
95 fSynth.deltat = 1.0f / (float)getSampleRate();
96 fSynth.nugget_remains = 0;
97
98 fSynth.note_id = 0;
99 fSynth.polyphony = XSYNTH_DEFAULT_POLYPHONY;
100 fSynth.voices = XSYNTH_DEFAULT_POLYPHONY;
101 fSynth.monophonic = XSYNTH_MONO_MODE_ONCE;
102 fSynth.glide = 0;
103 fSynth.last_noteon_pitch = 0.0f;
104 fSynth.vcf_accent = 0.0f;
105 fSynth.vca_accent = 0.0f;
106
107 for (int i=0; i<8; ++i)
108 fSynth.held_keys[i] = -1;
109
110 fSynth.voice = nekobee_voice_new();
111 fSynth.voicelist_mutex_grab_failed = 0;
112 pthread_mutex_init(&fSynth.voicelist_mutex, nullptr);
113
114 fSynth.channel_pressure = 0;
115 fSynth.pitch_wheel_sensitivity = 0;
116 fSynth.pitch_wheel = 0;
117
118 for (int i=0; i<128; ++i)
119 {
120 fSynth.key_pressure[i] = 0;
121 fSynth.cc[i] = 0;
122 }
123 fSynth.cc[7] = 127; // full volume
124
125 fSynth.mod_wheel = 1.0f;
126 fSynth.pitch_bend = 1.0f;
127 fSynth.cc_volume = 1.0f;
128
129 // Default values
130 fParams.waveform = 0.0f;
131 fParams.tuning = 0.0f;
132 fParams.cutoff = 25.0f;
133 fParams.resonance = 25.0f;
134 fParams.envMod = 50.0f;
135 fParams.decay = 75.0f;
136 fParams.accent = 25.0f;
137 fParams.volume = 75.0f;
138
139 // Internal stuff
140 fSynth.waveform = 0.0f;
141 fSynth.tuning = 1.0f;
142 fSynth.cutoff = 5.0f;
143 fSynth.resonance = 0.8f;
144 fSynth.envmod = 0.3f;
145 fSynth.decay = 0.0002f;
146 fSynth.accent = 0.3f;
147 fSynth.volume = 0.75f;
148
149 // reset
150 deactivate();
151}
152
153DistrhoPluginNekobi::~DistrhoPluginNekobi()
154{
155 std::free(fSynth.voice);
156}
157
158// -----------------------------------------------------------------------
159// Init
160
161void DistrhoPluginNekobi::initParameter(uint32_t index, Parameter& parameter)
162{
163 switch (index)
164 {
165 case paramWaveform:
166 parameter.hints = kParameterIsAutomatable|kParameterIsInteger;
167 parameter.name = "Waveform";
168 parameter.symbol = "waveform";
169 parameter.ranges.def = 0.0f;
170 parameter.ranges.min = 0.0f;
171 parameter.ranges.max = 1.0f;
172 parameter.enumValues.count = 2;
173 parameter.enumValues.restrictedMode = true;
174 parameter.midiCC = 70; //Sound Variation
175 {
176 ParameterEnumerationValue* const enumValues = new ParameterEnumerationValue[2];
177 enumValues[0].value = 0.0f;
178 enumValues[0].label = "Square";
179 enumValues[1].value = 1.0f;
180 enumValues[1].label = "Triangle";
181 parameter.enumValues.values = enumValues;
182 }
183 break;
184 case paramTuning:
185 parameter.hints = kParameterIsAutomatable; // was 0.5 <-> 2.0, log
186 parameter.name = "Tuning";
187 parameter.symbol = "tuning";
188 parameter.ranges.def = 0.0f;
189 parameter.ranges.min = -12.0f;
190 parameter.ranges.max = 12.0f;
191 parameter.midiCC = 75;
192 break;
193 case paramCutoff:
194 parameter.hints = kParameterIsAutomatable; // modified x2.5
195 parameter.name = "Cutoff";
196 parameter.symbol = "cutoff";
197 parameter.unit = "%";
198 parameter.ranges.def = 25.0f;
199 parameter.ranges.min = 0.0f;
200 parameter.ranges.max = 100.0f;
201 parameter.midiCC = 74;
202 break;
203 case paramResonance:
204 parameter.hints = kParameterIsAutomatable; // modified x100
205 parameter.name = "VCF Resonance";
206 parameter.symbol = "resonance";
207 parameter.unit = "%";
208 parameter.ranges.def = 25.0f;
209 parameter.ranges.min = 0.0f;
210 parameter.ranges.max = 95.0f;
211 parameter.midiCC = 71;
212 break;
213 case paramEnvMod:
214 parameter.hints = kParameterIsAutomatable; // modified x100
215 parameter.name = "Env Mod";
216 parameter.symbol = "env_mod";
217 parameter.unit = "%";
218 parameter.ranges.def = 50.0f;
219 parameter.ranges.min = 0.0f;
220 parameter.ranges.max = 100.0f;
221 parameter.midiCC = 1; //Mod Wheel
222 break;
223 case paramDecay:
224 parameter.hints = kParameterIsAutomatable; // was 0.000009 <-> 0.0005, log
225 parameter.name = "Decay";
226 parameter.symbol = "decay";
227 parameter.unit = "%";
228 parameter.ranges.def = 75.0f;
229 parameter.ranges.min = 0.0f;
230 parameter.ranges.max = 100.0f;
231 parameter.midiCC = 72;
232 break;
233 case paramAccent:
234 parameter.hints = kParameterIsAutomatable; // modified x100
235 parameter.name = "Accent";
236 parameter.symbol = "accent";
237 parameter.unit = "%";
238 parameter.ranges.def = 25.0f;
239 parameter.ranges.min = 0.0f;
240 parameter.ranges.max = 100.0f;
241 parameter.midiCC = 76;
242 break;
243 case paramVolume:
244 parameter.hints = kParameterIsAutomatable; // modified x100
245 parameter.name = "Volume";
246 parameter.symbol = "volume";
247 parameter.unit = "%";
248 parameter.ranges.def = 75.0f;
249 parameter.ranges.min = 0.0f;
250 parameter.ranges.max = 100.0f;
251 parameter.midiCC = 7; //Volume
252 break;
253 }
254}
255
256// -----------------------------------------------------------------------
257// Internal data
258
259float DistrhoPluginNekobi::getParameterValue(uint32_t index) const
260{
261 switch (index)
262 {
263 case paramWaveform:
264 return fParams.waveform;
265 case paramTuning:
266 return fParams.tuning;
267 case paramCutoff:
268 return fParams.cutoff;
269 case paramResonance:
270 return fParams.resonance;
271 case paramEnvMod:
272 return fParams.envMod;
273 case paramDecay:
274 return fParams.decay;
275 case paramAccent:
276 return fParams.accent;
277 case paramVolume:
278 return fParams.volume;
279 }
280
281 return 0.0f;
282}
283
284void DistrhoPluginNekobi::setParameterValue(uint32_t index, float value)
285{
286 switch (index)
287 {
288 case paramWaveform:
289 fParams.waveform = value;
290 fSynth.waveform = value;
291 DISTRHO_SAFE_ASSERT(fSynth.waveform == 0.0f || fSynth.waveform == 1.0f);
292 break;
293 case paramTuning:
294 fParams.tuning = value;
295 fSynth.tuning = (value+12.0f)/24.0f * 1.5 + 0.5f; // FIXME: log?
296 DISTRHO_SAFE_ASSERT(fSynth.tuning >= 0.5f && fSynth.tuning <= 2.0f);
297 break;
298 case paramCutoff:
299 fParams.cutoff = value;
300 fSynth.cutoff = value/2.5f;
301 DISTRHO_SAFE_ASSERT(fSynth.cutoff >= 0.0f && fSynth.cutoff <= 40.0f);
302 break;
303 case paramResonance:
304 fParams.resonance = value;
305 fSynth.resonance = value/100.0f;
306 DISTRHO_SAFE_ASSERT(fSynth.resonance >= 0.0f && fSynth.resonance <= 0.95f);
307 break;
308 case paramEnvMod:
309 fParams.envMod = value;
310 fSynth.envmod = value/100.0f;
311 DISTRHO_SAFE_ASSERT(fSynth.envmod >= 0.0f && fSynth.envmod <= 1.0f);
312 break;
313 case paramDecay:
314 fParams.decay = value;
315 fSynth.decay = value/100.0f * 0.000491f + 0.000009f; // FIXME: log?
316 DISTRHO_SAFE_ASSERT(fSynth.decay >= 0.000009f && fSynth.decay <= 0.0005f);
317 break;
318 case paramAccent:
319 fParams.accent = value;
320 fSynth.accent = value/100.0f;
321 DISTRHO_SAFE_ASSERT(fSynth.accent >= 0.0f && fSynth.accent <= 1.0f);
322 break;
323 case paramVolume:
324 fParams.volume = value;
325 fSynth.volume = value/100.0f;
326 DISTRHO_SAFE_ASSERT(fSynth.volume >= 0.0f && fSynth.volume <= 1.0f);
327 break;
328 }
329}
330
331// -----------------------------------------------------------------------
332// Process
333
334void DistrhoPluginNekobi::activate()
335{
336 fSynth.nugget_remains = 0;
337 fSynth.note_id = 0;
338
339 if (fSynth.voice != nullptr)
341}
342
343void DistrhoPluginNekobi::deactivate()
344{
345 if (fSynth.voice != nullptr)
347}
348
349void DistrhoPluginNekobi::run(const float**, float** outputs, uint32_t frames, const MidiEvent* midiEvents, uint32_t midiEventCount)
350{
351 uint32_t framesDone = 0;
352 uint32_t curEventIndex = 0;
353 uint32_t burstSize;
354
355 float* out = outputs[0];
356
357 if (fSynth.voice == nullptr || ! dssp_voicelist_mutex_trylock(&fSynth))
358 {
359 std::memset(out, 0, sizeof(float)*frames);
360 return;
361 }
362
363 while (framesDone < frames)
364 {
365 if (fSynth.nugget_remains == 0)
366 fSynth.nugget_remains = XSYNTH_NUGGET_SIZE;
367
368 /* process any ready events */
369 while (curEventIndex < midiEventCount && framesDone == midiEvents[curEventIndex].frame)
370 {
371 if (midiEvents[curEventIndex].size > MidiEvent::kDataSize)
372 continue;
373
374 nekobee_handle_raw_event(&fSynth, midiEvents[curEventIndex].size, midiEvents[curEventIndex].data);
375 curEventIndex++;
376 }
377
378 /* calculate the sample count (burstSize) for the next nekobee_voice_render() call to be the smallest of:
379 * - control calculation quantization size (XSYNTH_NUGGET_SIZE, in samples)
380 * - the number of samples remaining in an already-begun nugget (synth->nugget_remains)
381 * - the number of samples until the next event is ready
382 * - the number of samples left in this run
383 */
384 burstSize = XSYNTH_NUGGET_SIZE;
385
386 /* we're still in the middle of a nugget, so reduce the burst size
387 * to end when the nugget ends */
388 if (fSynth.nugget_remains < burstSize)
389 burstSize = fSynth.nugget_remains;
390
391 /* reduce burst size to end when next event is ready */
392 if (curEventIndex < midiEventCount && midiEvents[curEventIndex].frame - framesDone < burstSize)
393 burstSize = midiEvents[curEventIndex].frame - framesDone;
394
395 /* reduce burst size to end at end of this run */
396 if (frames - framesDone < burstSize)
397 burstSize = frames - framesDone;
398
399 /* render the burst */
400 nekobee_synth_render_voices(&fSynth, out + framesDone, burstSize, (burstSize == fSynth.nugget_remains));
401 framesDone += burstSize;
402 fSynth.nugget_remains -= burstSize;
403 }
404
406}
407
408// -----------------------------------------------------------------------
409
411{
412 return new DistrhoPluginNekobi();
413}
414
415// -----------------------------------------------------------------------
416
#define END_NAMESPACE_DISTRHO
Definition DistrhoDefines.h:191
#define DISTRHO_SAFE_ASSERT(cond)
Definition DistrhoDefines.h:104
#define START_NAMESPACE_DISTRHO
Definition DistrhoDefines.h:190
Plugin * createPlugin()
Definition DistrhoPluginNekobi.cpp:410
bool dssp_voicelist_mutex_trylock(nekobee_synth_t *const synth)
Definition DistrhoPluginNekobi.cpp:31
void nekobee_handle_raw_event(nekobee_synth_t *const synth, const uint8_t size, const uint8_t *const data)
Definition DistrhoPluginNekobi.cpp:58
bool dssp_voicelist_mutex_unlock(nekobee_synth_t *const synth)
Definition DistrhoPluginNekobi.cpp:50
SYNTH_T * synth
Definition LocalZynAddSubFx.cpp:47
static void deactivate(LV2_Handle instance)
Definition bindings_test_plugin.c:128
Definition basics.h:174
register unsigned i
Definition inflate.c:1575
static PuglViewHint int value
Definition pugl.h:1708
virtual ASIOError getSampleRate(ASIOSampleRate *sampleRate)=0
JSAMPIMAGE data
Definition jpeglib.h:945
float out
Definition lilv_test.c:1461
unsigned int uint32_t
Definition mid.cpp:100
unsigned char uint8_t
Definition mid.cpp:98
#define XSYNTH_DEFAULT_POLYPHONY
Definition nekobee.h:75
void nekobee_synth_note_off(nekobee_synth_t *synth, unsigned char key, unsigned char rvelocity)
Definition nekobee_synth.c:65
void nekobee_synth_control_change(nekobee_synth_t *synth, unsigned int param, signed int value)
Definition nekobee_synth.c:143
void nekobee_synth_render_voices(nekobee_synth_t *synth, float *out, unsigned long sample_count, int do_control_update)
Definition nekobee_synth.c:201
void nekobee_synth_all_voices_off(nekobee_synth_t *synth)
Definition nekobee_synth.c:44
void nekobee_synth_note_on(nekobee_synth_t *synth, unsigned char key, unsigned char velocity)
Definition nekobee_synth.c:112
#define XSYNTH_MONO_MODE_ONCE
Definition nekobee_synth.h:36
struct _nekobee_synth_t nekobee_synth_t
Definition nekobee_types.h:26
nekobee_voice_t * nekobee_voice_new()
Definition nekobee_voice.c:41
#define XSYNTH_NUGGET_SIZE
Definition nekobee_voice.h:34
void nekobee_init_tables(void)
Definition nekobee_voice_render.c:38
Definition InMgr.h:16
ulg size
Definition extract.c:2350