From cb3183af6075e402b925bf00c4450bc4ad8acb14 Mon Sep 17 00:00:00 2001 From: dreamer Date: Sun, 22 Sep 2024 10:10:57 +0200 Subject: [PATCH] add DPF types; simplify midi docs --- docs/03.gen.dpf.md | 25 +++-- docs/04.midi.md | 260 ++------------------------------------------- 2 files changed, 25 insertions(+), 260 deletions(-) diff --git a/docs/03.gen.dpf.md b/docs/03.gen.dpf.md index 7ce5d0b4..22ce4fcf 100644 --- a/docs/03.gen.dpf.md +++ b/docs/03.gen.dpf.md @@ -22,21 +22,32 @@ Additionally you can use the special `[r __hv_dpf_bpm]` receiver to get the curr ## Parameter Types -In DPF a parameter can get an optional type configured. The default type is `float`. Other assignable types are `bool` - for toggling a value - and `trig` - for momentary signals. +In DPF a parameter can get an optional type configured. The default type is `float`. + +Other assignable types are `int` - or whole numbers, `bool` - for toggling a value, and `trig` - for momentary signals. ![dpf](img/docs_param_type.png) Using jinja the `v.attributes.type` can be evaluated for a specific string and different templating applied to the parameter. In DPF the extra types `bool` and `trig` result in the following plugin code: ```c++ - parameter.hints = kParameterIsAutomable | kParameterIsBoolean; + parameter.hints = kParameterIsInteger +// or + parameter.hints = kParameterIsBoolean; // or - parameter.hints = kParameterIsAutomable | kParameterIsTrigger; + parameter.hints = kParameterIsTrigger; ``` +Other special types can give additional information to the host: + +* `dB` - unit `dB` - min_value `-inf` label (assumes `0.0f`) +* `Hz` - unit `Hz` +* `log` - hints `kParameterIsLogarithmic` +* `log_hz` - unit `Hz` - hints `kParameterIsLogarithmic` + ## Metadata -An accomponying metadata.json file can be included to set additional plugin settings. +An accompanying metadata.json file can be included to set additional plugin settings. The `project` flag creates a `README.md` and `Makefile` in the root of the project output, but may conflict with other generators. @@ -55,7 +66,7 @@ Each of these are optional and have either a default value or are entirely optio "midi_input": 1, "midi_output": 0, "plugin_formats": [ - "lv2_dsp", + "lv2_sep", "vst2", "vst3", "clap", @@ -67,7 +78,7 @@ Each of these are optional and have either a default value or are entirely optio Other fields that the DPF metadata supports are: -* `port_groups` - If your plugin has more audio i/o that need to be grouped together. +* `port_groups` - If your plugin has more audio i/o that need to be grouped together or given Control Voltage status * `enumerators` - Configure a set of parameters that cycle over `: ` * `enable_ui` - Boolean that creates a generic GUI. Requires `dpf-widgets` on the same level as `dpf`. * `enable_modgui` - Boolean for use in MOD audio based systems. @@ -77,6 +88,8 @@ Other fields that the DPF metadata supports are: * `vst3_info` - String describing the VST3 plugin type. * `clap_info` - List of strings describing the CLAP plugin type. +The full type specification can be found [here](https://github.com/Wasted-Audio/hvcc/blob/develop/hvcc/generators/types/meta.py). + An example plugin that uses some of these extended metadata is [WSTD 3Q](https://github.com/Wasted-Audio/wstd-3q). ## Notes diff --git a/docs/04.midi.md b/docs/04.midi.md index 0f66212b..6599092e 100644 --- a/docs/04.midi.md +++ b/docs/04.midi.md @@ -1,6 +1,6 @@ -# MIDI I/O +# MIDI I/O -In PureData there are objects to handle interfacing with a machines MIDI device. +In PureData there are several objects to handle interfacing with MIDI. **heavy** doesn't provide cross-platform implementation for MIDI I/O as the requirements tend to change depending on the platform or framework being used. @@ -50,138 +50,11 @@ Here's the `DPF` implementation as an example. ## Handling MIDI Input -The MIDI input is called during the DPF `run()` loop where it receives `MidiEvent` messages: +The MIDI input is called during the DPF `run()` loop where it receives `MidiEvent` messages. -```cpp -#if DISTRHO_PLUGIN_WANT_MIDI_INPUT -// ------------------------------------------------------------------- -// Midi Input handler - -void {{class_name}}::handleMidiInput(uint32_t frames, const MidiEvent* midiEvents, uint32_t midiEventCount) -{ - // Realtime events - // TODO: Continue and Reset - - 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; - } - - 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++; - } - 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]; +[Source code (run loop)](https://github.com/Wasted-Audio/hvcc/blob/develop/hvcc/generators/c2dpf/templates/HeavyDPF.cpp#L201-L205) - // 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); - } - - if(mrtSet.find(status) != mrtSet.end()) - { - _context->sendMessageToReceiverV(HV_HASH_MIDIREALTIMEIN, 1000.0*timePos.frame/getSampleRate(), - "ff", (float) status); - } - - // 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; - } - } -} -#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) -{ - handleMidiInput(frames, midiEvents, midiEventCount); -#else -void {{class_name}}::run(const float** inputs, float** outputs, uint32_t frames) -{ -#endif - _context->process((float**)inputs, outputs, frames); -} -``` +[Source code (handleMidiInput)](https://github.com/Wasted-Audio/hvcc/blob/develop/hvcc/generators/c2dpf/templates/midiInput.cpp) ## Handling MIDI Output @@ -213,125 +86,4 @@ Pd does not have specific Note Off events, so velocity 0 is assumed to be Note O Bend assumes input values ranged `0 - 16383` for `[bendin]` (normal bend range), however as mentioned before `[bendout]` uses `-8192 to 8191` to stay compatible with pd-vanilla. -```cpp -#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT -// ------------------------------------------------------------------- -// Midi Send handler - -void {{class_name}}::handleMidiSend(uint32_t sendHash, const HvMessage *m) -{ - MidiEvent midiSendEvent; - midiSendEvent.frame = 0; - midiSendEvent.dataExt = nullptr; - - switch(sendHash){ - case HV_HASH_NOTEOUT: // __hv_noteout - { - uint8_t note = hv_msg_getFloat(m, 0); - uint8_t velocity = hv_msg_getFloat(m, 1); - uint8_t ch = hv_msg_getFloat(m, 2); - ch %= 16; // drop any pd "ports" - - midiSendEvent.size = 3; - if (velocity > 0){ - midiSendEvent.data[0] = 0x90 | ch; // noteon - } else { - midiSendEvent.data[0] = 0x80 | ch; // noteoff - } - midiSendEvent.data[1] = note; - midiSendEvent.data[2] = velocity; - - writeMidiEvent(midiSendEvent); - break; - } - case HV_HASH_CTLOUT: - { - uint8_t value = hv_msg_getFloat(m, 0); - uint8_t cc = hv_msg_getFloat(m, 1); - uint8_t ch = hv_msg_getFloat(m, 2); - ch %= 16; - - midiSendEvent.size = 3; - midiSendEvent.data[0] = 0xB0 | ch; // send CC - midiSendEvent.data[1] = cc; - midiSendEvent.data[2] = value; - - writeMidiEvent(midiSendEvent); - break; - } - case HV_HASH_PGMOUT: - { - uint8_t pgm = hv_msg_getFloat(m, 0); - uint8_t ch = hv_msg_getFloat(m, 1); - ch %= 16; - - midiSendEvent.size = 2; - midiSendEvent.data[0] = 0xC0 | ch; // send Program Change - midiSendEvent.data[1] = pgm; - - writeMidiEvent(midiSendEvent); - break; - } - case HV_HASH_TOUCHOUT: - { - uint8_t value = hv_msg_getFloat(m, 0); - uint8_t ch = hv_msg_getFloat(m, 1); - ch %= 16; - - midiSendEvent.size = 2; - midiSendEvent.data[0] = 0xD0 | ch; // send Touch - midiSendEvent.data[1] = value; - - writeMidiEvent(midiSendEvent); - break; - } - case HV_HASH_BENDOUT: - { - uint16_t value = hv_msg_getFloat(m, 0); - uint8_t lsb = value & 0x7F; - uint8_t msb = (value >> 7) & 0x7F; - uint8_t ch = hv_msg_getFloat(m, 1); - ch %= 16; - - midiSendEvent.size = 3; - midiSendEvent.data[0] = 0xE0 | ch; // send Bend - midiSendEvent.data[1] = lsb; - midiSendEvent.data[2] = msb; - - writeMidiEvent(midiSendEvent); - break; - } - case HV_HASH_MIDIOUT: // __hv_midiout - { - const uint8_t numElements = m->numElements; - if (numElements <=4 ) - { - for (int i = 0; i < numElements; ++i) - { - midiSendEvent.data[i] = hv_msg_getFloat(m, i); - } - } - else - { - printf("> we do not support sysex yet \n"); - break; - } - - // unsigned char* rawData = new unsigned char; - // for (int i = 0; i < numElements; ++i) { - // rawData[i] = (uint8_t) hv_msg_getFloat(m, i); - // printf("> data: %d \n", rawData[i]); - // } - - midiSendEvent.size = numElements; - // midiSendEvent.dataExt = (const uint8_t *) rawData; - - writeMidiEvent(midiSendEvent); - break; - } - default: - break; - } -} -#endif -``` +[Source code (handleMidiSend)](https://github.com/Wasted-Audio/hvcc/blob/develop/hvcc/generators/c2dpf/templates/midiOutput.cpp) \ No newline at end of file