Skip to content

Commit

Permalink
Feature/midirealtimein (Wasted-Audio#36)
Browse files Browse the repository at this point in the history
* enable [midirealtimein]

* host transport to midi-clock + message alignment fixes
  • Loading branch information
dromer authored Oct 30, 2021
1 parent eb9b8eb commit d39512e
Show file tree
Hide file tree
Showing 8 changed files with 306 additions and 152 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
CHANGELOG
=====

Next Release
-----

* Midi realtime messages
* Host transport to midi-rt (TODO: Continue and Reset)
* Midi message scheduling improvements

0.3.0
-----

Expand Down
183 changes: 118 additions & 65 deletions docs/04.midi.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@ Instead, it provides wrappers around these objects that route the data to specif

The following Pd objects are mapped to their corresponding heavy parameter and internal hash.

| Pd object | heavy param | heavy hash |
| --------- | -------------- | ---------- |
| [notein] | __hv_notein | 0x67E37CA3 |
| [ctlin] | __hv_ctlin | 0x41BE0f9C |
| [pgmin] | __hv_pgmin | 0x2E1EA03D |
| [touchin] | __hv_touchin | 0x553925BD |
| [bendin] | __hv_bendin | 0x3083F0F7 |
| [midiin] | __hv_midiin | 0x149631bE |
| Pd object | heavy param | heavy hash |
| ----------------- | ------------------- | ---------- |
| [notein] | __hv_notein | 0x67E37CA3 |
| [ctlin] | __hv_ctlin | 0x41BE0f9C |
| [pgmin] | __hv_pgmin | 0x2E1EA03D |
| [touchin] | __hv_touchin | 0x553925BD |
| [bendin] | __hv_bendin | 0x3083F0F7 |
| [midiin] | __hv_midiin | 0x149631bE |
| [midirealtimein] | __hv_midirealtimein | 0x6FFF0BCF |


## Outputs
Expand Down Expand Up @@ -52,83 +53,133 @@ The MIDI input is called during the DPF `run()` loop where it receives `MidiEven

```cpp
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
void {{class_name}}::handleMidiInput(uint32_t curEventIndex, const MidiEvent* midiEvents)
{
int status = midiEvents[curEventIndex].data[0];
int command = status & 0xF0;
int channel = status & 0x0F;
int data1 = midiEvents[curEventIndex].data[1];
int data2 = midiEvents[curEventIndex].data[2];
// -------------------------------------------------------------------
// Midi Input handler

// raw [midiin] messages
int dataSize = *(&midiEvents[curEventIndex].data + 1) - midiEvents[curEventIndex].data;
void {{class_name}}::handleMidiInput(uint32_t frames, const MidiEvent* midiEvents, uint32_t midiEventCount)
{
// Realtime events
// TODO: Continue and Reset

for (int i = 0; i < dataSize; ++i) {
_context->sendMessageToReceiverV(HV_HASH_MIDIIN, 0, "ff",
(float) midiEvents[curEventIndex].data[i],
(float) channel);
const TimePosition& timePos(getTimePosition());
const bool playing = timePos.playing;
if (playing != wasPlaying)
{
if (playing)
{
_context->sendMessageToReceiverV(HV_HASH_MIDIREALTIMEIN, 0,
"ff", (float) MIDI_RT_START);
} else {
_context->sendMessageToReceiverV(HV_HASH_MIDIREALTIMEIN, 0,
"ff", (float) MIDI_RT_STOP);
}
wasPlaying = playing;
}

// typical midi messages
switch (command) {
case 0x80: { // note off
_context->sendMessageToReceiverV(HV_HASH_NOTEIN, 0, "fff",
(float) data1, // pitch
(float) 0, // velocity
(float) channel);
break;
}
case 0x90: { // note on
_context->sendMessageToReceiverV(HV_HASH_NOTEIN, 0, "fff",
(float) data1, // pitch
(float) data2, // velocity
(float) channel);
break;
}
case 0xB0: { // control change
_context->sendMessageToReceiverV(HV_HASH_CTLIN, 0, "fff",
(float) data2, // value
(float) data1, // cc number
(float) channel);
break;
if (playing && timePos.bbt.valid)
{
float samplesPerBeat = 60 * getSampleRate() / timePos.bbt.beatsPerMinute;
float samplesPerTick = samplesPerBeat / 24.0;

int i = 1;
while (samplesProcessed > samplesPerTick)
{
_context->sendMessageToReceiverV(HV_HASH_MIDIREALTIMEIN, i * 1000.0*samplesPerTick/getSampleRate(),
"ff", (float) MIDI_RT_CLOCK);
samplesProcessed -= samplesPerTick;
i++;
}
case 0xC0: { // program change
_context->sendMessageToReceiverV(HV_HASH_PGMIN, 0, "ff",
(float) data1,
samplesProcessed += frames;
// printf("> ticks: %f - samples: %f \n", samplesPerTick, samplesProcessed);
}

// Midi events
for (uint32_t i=0; i < midiEventCount; ++i)
{
int status = midiEvents[i].data[0];
int command = status & 0xF0;
int channel = status & 0x0F;
int data1 = midiEvents[i].data[1];
int data2 = midiEvents[i].data[2];

// raw [midiin] messages
int dataSize = *(&midiEvents[i].data + 1) - midiEvents[i].data;

for (int i = 0; i < dataSize; ++i) {
_context->sendMessageToReceiverV(HV_HASH_MIDIIN, 1000.0*timePos.frame/getSampleRate(), "ff",
(float) midiEvents[i].data[i],
(float) channel);
break;
}
case 0xD0: { // aftertouch
_context->sendMessageToReceiverV(HV_HASH_TOUCHIN, 0, "ff",
(float) data1,
(float) channel);
break;

if(mrtSet.find(status) != mrtSet.end())
{
_context->sendMessageToReceiverV(HV_HASH_MIDIREALTIMEIN, 1000.0*timePos.frame/getSampleRate(),
"ff", (float) status);
}
case 0xE0: { // pitch bend
// combine 7bit lsb and msb into 32bit int
hv_uint32_t value = (((hv_uint32_t) data2) << 7) | ((hv_uint32_t) data1);
_context->sendMessageToReceiverV(HV_HASH_BENDIN, 0, "ff",
(float) value,
(float) channel);
break;

// typical midi messages
switch (command) {
case 0x80: { // note off
_context->sendMessageToReceiverV(HV_HASH_NOTEIN, 1000.0*timePos.frame/getSampleRate(), "fff",
(float) data1, // pitch
(float) 0, // velocity
(float) channel);
break;
}
case 0x90: { // note on
_context->sendMessageToReceiverV(HV_HASH_NOTEIN, 1000.0*timePos.frame/getSampleRate(), "fff",
(float) data1, // pitch
(float) data2, // velocity
(float) channel);
break;
}
case 0xB0: { // control change
_context->sendMessageToReceiverV(HV_HASH_CTLIN, 1000.0*timePos.frame/getSampleRate(), "fff",
(float) data2, // value
(float) data1, // cc number
(float) channel);
break;
}
case 0xC0: { // program change
_context->sendMessageToReceiverV(HV_HASH_PGMIN, 1000.0*timePos.frame/getSampleRate(), "ff",
(float) data1,
(float) channel);
break;
}
case 0xD0: { // aftertouch
_context->sendMessageToReceiverV(HV_HASH_TOUCHIN, 1000.0*timePos.frame/getSampleRate(), "ff",
(float) data1,
(float) channel);
break;
}
case 0xE0: { // pitch bend
// combine 7bit lsb and msb into 32bit int
hv_uint32_t value = (((hv_uint32_t) data2) << 7) | ((hv_uint32_t) data1);
_context->sendMessageToReceiverV(HV_HASH_BENDIN, 1000.0*timePos.frame/getSampleRate(), "ff",
(float) value,
(float) channel);
break;
}
default: break;
}
default: break;
}
}
#endif


// -------------------------------------------------------------------
// DPF Plugin run() loop

#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
void {{class_name}}::run(const float** inputs, float** outputs, uint32_t frames, const MidiEvent* midiEvents, uint32_t midiEventCount)
{
for (uint32_t i=0; i < midiEventCount; ++i)
{
handleMidiInput(i, midiEvents);
}
handleMidiInput(frames, midiEvents, midiEventCount);
#else
void {{class_name}}::run(const float** inputs, float** outputs, uint32_t frames)
{
#endif
_context->process((float**)inputs, outputs, frames);
}
```
## Handling MIDI Output
Expand Down Expand Up @@ -163,6 +214,9 @@ Bend assumes input values ranged `0 - 16383` for [bendin] (normal bend range), h
```cpp
#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
// -------------------------------------------------------------------
// Midi Send handler
void {{class_name}}::handleMidiSend(uint32_t sendHash, const HvMessage *m)
{
MidiEvent midiSendEvent;
Expand Down Expand Up @@ -279,5 +333,4 @@ void {{class_name}}::handleMidiSend(uint32_t sendHash, const HvMessage *m)
}
}
#endif

```
6 changes: 5 additions & 1 deletion examples/dpf/dpf_midi_thru.pd
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#N canvas 472 394 409 184 12;
#N canvas 559 367 576 125 12;
#X obj 22 19 notein;
#X obj 22 59 noteout;
#X obj 98 19 ctlin;
Expand All @@ -10,6 +10,8 @@
#X obj 318 19 bendin;
#X obj 318 69 bendout;
#X obj 318 44 - 8192;
#X obj 417 22 midirealtimein;
#X obj 417 59 midiout;
#X connect 0 0 1 0;
#X connect 0 1 1 1;
#X connect 0 2 1 2;
Expand All @@ -23,3 +25,5 @@
#X connect 8 0 10 0;
#X connect 8 1 9 1;
#X connect 10 0 9 0;
#X connect 11 0 12 0;
#X connect 11 1 12 1;
29 changes: 15 additions & 14 deletions hvcc/generators/c2dpf/templates/DistrhoPluginInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,23 @@

#pragma once

#define DISTRHO_PLUGIN_NAME "{{name}}"
#define DISTRHO_PLUGIN_NAME "{{name}}"
{% if meta.plugin_uri is defined %}
#define DISTRHO_PLUGIN_URI "{{meta.plugin_uri}}"
#define DISTRHO_PLUGIN_URI "{{meta.plugin_uri}}"
{% else %}
#define DISTRHO_PLUGIN_URI "http://wasted.audio/lv2/plugin/{{name}}"
#define DISTRHO_PLUGIN_URI "http://wasted.audio/lv2/plugin/{{name}}"
{% endif %}
#define DISTRHO_PLUGIN_NUM_INPUTS {{num_input_channels}}
#define DISTRHO_PLUGIN_NUM_OUTPUTS {{num_output_channels}}
#define DISTRHO_PLUGIN_IS_SYNTH {{1 if num_output_channels > 0 and meta.midi_input else 0}}
#define DISTRHO_PLUGIN_HAS_UI 0
#define DISTRHO_PLUGIN_IS_RT_SAFE 1
#define DISTRHO_PLUGIN_WANT_PROGRAMS 0
#define DISTRHO_PLUGIN_WANT_STATE 0
#define DISTRHO_PLUGIN_WANT_FULL_STATE 0
#define DISTRHO_PLUGIN_WANT_MIDI_INPUT {{meta.midi_input if meta.midi_input is defined else 1}}
#define DISTRHO_PLUGIN_WANT_MIDI_OUTPUT {{meta.midi_output if meta.midi_output is defined else 1}}
#define DISTRHO_PLUGIN_NUM_INPUTS {{num_input_channels}}
#define DISTRHO_PLUGIN_NUM_OUTPUTS {{num_output_channels}}
#define DISTRHO_PLUGIN_IS_SYNTH {{1 if num_output_channels > 0 and meta.midi_input else 0}}
#define DISTRHO_PLUGIN_HAS_UI 0
#define DISTRHO_PLUGIN_IS_RT_SAFE 1
#define DISTRHO_PLUGIN_WANT_PROGRAMS 0
#define DISTRHO_PLUGIN_WANT_STATE 0
#define DISTRHO_PLUGIN_WANT_TIMEPOS 1
#define DISTRHO_PLUGIN_WANT_FULL_STATE 0
#define DISTRHO_PLUGIN_WANT_MIDI_INPUT {{meta.midi_input if meta.midi_input is defined else 1}}
#define DISTRHO_PLUGIN_WANT_MIDI_OUTPUT {{meta.midi_output if meta.midi_output is defined else 1}}

// for level monitoring
#define DISTRHO_PLUGIN_WANT_DIRECT_ACCESS 0
#define DISTRHO_PLUGIN_WANT_DIRECT_ACCESS 0
Loading

0 comments on commit d39512e

Please sign in to comment.