LV2 Toolkit  1.1.1
 All Classes Namespaces Functions Typedefs Enumerations Enumerator Groups Pages
synth.hpp
1 /*
2 
3  synth.hpp - Support file for writing LV2 tone generating plugins in C++
4 
5  Copyright (C) 2007 Lars Luthman <lars.luthman@gmail.com>
6 
7  This program is free software; you can redistribute it and/or modify
8  it under the terms of the GNU General Public License as published by
9  the Free Software Foundation; either version 3 of the License, or
10  (at your option) any later version.
11 
12  This program is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with this program; if not, write to the Free Software
19  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 01222-1307 USA
20 
21  */
24 #ifndef LVTK_LV2_SYNTH_HPP
25 #define LVTK_LV2_SYNTH_HPP
26 
27 #include <cmath>
28 #include <cstring>
29 #include <vector>
30 
31 #include <lvtk/plugin.hpp>
32 
33 namespace lvtk {
34 
35  using std::vector;
36 
38  static const unsigned char INVALID_KEY = 255;
39 
40 
43  static inline float key2hz(unsigned char key) {
44  return 8.1758 * std::pow(1.0594, key);
45  }
46 
47 
52  class Voice {
53  public:
54 
64  void on(unsigned char key, unsigned char velocity) { }
65 
70  void off(unsigned char velocity) { }
71 
76  unsigned char get_key() const { return lvtk::INVALID_KEY; }
77 
83  void render(uint32_t from, uint32_t to) { }
84 
89  void set_port_buffers(vector<void*>& ports) { m_ports = &ports; }
90 
91  protected:
92 
94  template <typename T> inline T*& p(uint32_t port) {
95  return reinterpret_cast<T*&>((*m_ports)[port]);
96  }
97 
99  float*& p(uint32_t port) {
100  return reinterpret_cast<float*&>((*m_ports)[port]);
101  }
102 
104  vector<void*>* m_ports;
105  };
106 
107 
181  template <class V, class D,
182  class Ext1 = end, class Ext2 = end, class Ext3 = end,
183  class Ext4 = end, class Ext5 = end, class Ext6 = end,
184  class Ext7 = end>
185  class Synth : public Plugin<D, URID<true>, Ext1, Ext2, Ext3, Ext4, Ext5, Ext6, Ext7>
186  {
187  public:
188 
190  typedef Plugin<D, URID<true>, Ext1, Ext2, Ext3, Ext4, Ext5, Ext6, Ext7>
191  Parent;
192 
199  Synth(uint32_t ports, uint32_t midi_input)
200  : Parent(ports),
201  m_midi_input (midi_input)
202  {
203  m_midi_type = Parent::map (LV2_MIDI__MidiEvent);
204  }
205 
206 
209  {
210  for (unsigned i = 0; i < m_voices.size(); ++i)
211  delete m_voices[i];
212  }
213 
224  unsigned
225  find_free_voice(unsigned char key, unsigned char velocity)
226  {
227  for (unsigned i = 0; i < m_voices.size(); ++i)
228  {
229  if (m_voices[i]->get_key() == lvtk::INVALID_KEY)
230  return i;
231  }
232 
233  return 0;
234  }
235 
236 
248  void
249  handle_midi (uint32_t size, unsigned char* data)
250  {
251  if (size != 3) return;
252  LV2_Midi_Message_Type type (lv2_midi_message_type (data));
253 
254  if (data[0] == 0x90)
255  {
256  unsigned voice =
257  static_cast<D*>(this)->find_free_voice(data[1], data[2]);
258  if (voice < m_voices.size())
259  m_voices[voice]->on(data[1], data[2]);
260  }
261  else if (data[0] == 0x80)
262  {
263  for (unsigned i = 0; i < m_voices.size(); ++i)
264  {
265  if (m_voices[i]->get_key() == data[1])
266  {
267  m_voices[i]->off(data[2]);
268  break;
269  }
270  }
271  }
272  }
273 
274  void
275  handle_atom_event (LV2_Atom_Event* ev) { }
276 
288  void
289  pre_process(uint32_t from, uint32_t to)
290  { }
291 
292 
303  void
304  post_process(uint32_t from, uint32_t to)
305  { }
306 
307 
312  void
313  run(uint32_t sample_count)
314  {
315  D* synth = static_cast<D*>(this);
316 
317  // Zero output buffers so voices can add to them
318  for (unsigned i = 0; i < m_audio_ports.size(); ++i)
319  std::memset(p(m_audio_ports[i]), 0, sizeof(float) * sample_count);
320 
321  // Make the port buffers available to the voices
322  for (unsigned i = 0; i < m_voices.size(); ++i)
323  m_voices[i]->set_port_buffers(Parent::m_ports);
324 
325  const LV2_Atom_Sequence* seq = p<LV2_Atom_Sequence> (m_midi_input);
326  uint32_t last_frame = 0;
327 
328  for (LV2_Atom_Event* ev = lv2_atom_sequence_begin (&seq->body);
329  !lv2_atom_sequence_is_end(&seq->body, seq->atom.size, ev);
330  ev = lv2_atom_sequence_next (ev))
331  {
332  synth->pre_process (last_frame, ev->time.frames);
333  for (uint32_t i = 0; i < m_voices.size(); ++i)
334  m_voices[i]->render (last_frame, ev->time.frames);
335  synth->post_process (last_frame, ev->time.frames);
336 
337  if (ev->body.type == m_midi_type)
338  synth->handle_midi (ev->body.size, (uint8_t*) LV2_ATOM_BODY (&ev->body));
339  else
340  synth->handle_atom_event (ev);
341 
342  last_frame = ev->time.frames;
343  }
344 
345  if (last_frame < sample_count)
346  {
347  synth->pre_process (last_frame, sample_count);
348  for (uint32_t i = 0; i < m_voices.size(); ++i)
349  m_voices[i]->render (last_frame, sample_count);
350  synth->post_process (last_frame, sample_count);
351  }
352  }
353 
354 
366  void
367  add_audio_outputs(uint32_t p1 = -1, uint32_t p2 = -1,
368  uint32_t p3 = -1, uint32_t p4 = -1,
369  uint32_t p5 = -1, uint32_t p6 = -1)
370  {
371  if (p1 == uint32_t(-1))
372  return;
373  m_audio_ports.push_back(p1);
374  if (p2 == uint32_t(-1))
375  return;
376  m_audio_ports.push_back(p2);
377  if (p3 == uint32_t(-1))
378  return;
379  m_audio_ports.push_back(p3);
380  if (p4 == uint32_t(-1))
381  return;
382  m_audio_ports.push_back(p4);
383  if (p5 == uint32_t(-1))
384  return;
385  m_audio_ports.push_back(p5);
386  if (p6 == uint32_t(-1))
387  return;
388  m_audio_ports.push_back(p6);
389  }
390 
391 
402  void add_voices(V* v01 = 0, V* v02 = 0, V* v03 = 0, V* v04 = 0, V* v05 = 0,
403  V* v06 = 0, V* v07 = 0, V* v08 = 0, V* v09 = 0, V* v10 = 0,
404  V* v11 = 0, V* v12 = 0, V* v13 = 0, V* v14 = 0, V* v15 = 0,
405  V* v16 = 0, V* v17 = 0, V* v18 = 0, V* v19 = 0, V* v20 = 0)
406  {
407  if (v01 == 0)
408  return;
409  m_voices.push_back(v01);
410  if (v02 == 0)
411  return;
412  m_voices.push_back(v02);
413  if (v03 == 0)
414  return;
415  m_voices.push_back(v03);
416  if (v04 == 0)
417  return;
418  m_voices.push_back(v04);
419  if (v05 == 0)
420  return;
421  m_voices.push_back(v05);
422  if (v06 == 0)
423  return;
424  m_voices.push_back(v06);
425  if (v07 == 0)
426  return;
427  m_voices.push_back(v07);
428  if (v08 == 0)
429  return;
430  m_voices.push_back(v08);
431  if (v09 == 0)
432  return;
433  m_voices.push_back(v09);
434  if (v10 == 0)
435  return;
436  m_voices.push_back(v10);
437  if (v11 == 0)
438  return;
439  m_voices.push_back(v11);
440  if (v12 == 0)
441  return;
442  m_voices.push_back(v12);
443  if (v13 == 0)
444  return;
445  m_voices.push_back(v13);
446  if (v14 == 0)
447  return;
448  m_voices.push_back(v14);
449  if (v15 == 0)
450  return;
451  m_voices.push_back(v15);
452  if (v16 == 0)
453  return;
454  m_voices.push_back(v16);
455  if (v17 == 0)
456  return;
457  m_voices.push_back(v17);
458  if (v18 == 0)
459  return;
460  m_voices.push_back(v18);
461  if (v19 == 0)
462  return;
463  m_voices.push_back(v19);
464  if (v20 == 0)
465  return;
466  m_voices.push_back(v20);
467  }
468  protected:
469 
473  template <typename T> T*&
474  p(uint32_t port) {
475  return reinterpret_cast<T*&>(Parent::m_ports[port]);
476  }
477 
479  float*&
480  p(uint32_t port) {
481  return reinterpret_cast<float*&>(Parent::m_ports[port]);
482  }
483 
485  vector<V*> m_voices;
486 
488  vector<uint32_t> m_audio_ports;
489 
491  uint32_t m_midi_input;
492 
494  LV2_URID m_midi_type;
495 
496  };
497 
498 }
499 
500 
501 #endif