Skip to content

Commit

Permalink
Merge branch 'and-yet-even-still-more-ui-stuff'
Browse files Browse the repository at this point in the history
  • Loading branch information
sjoerdvankreel committed Sep 5, 2024
2 parents 37e2e56 + 74169bf commit 94c2c72
Show file tree
Hide file tree
Showing 55 changed files with 8,247 additions and 613 deletions.
5 changes: 5 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
### September 5, 2024 - V1.9.1.

- Add full realtime visual modulation for the graphs (so they show what the audio engine is actually doing if you f.e. set LFO to filter freq).
- Also added the option to back down on realtime repaint (nothing / params only / graphs) since this is quite cpu expensive.

### August 29, 2024 - V1.9.0.

- Load/save/init/clear patch split out to separate buttons again.
Expand Down
3,970 changes: 3,970 additions & 0 deletions demos/host_test/visual_modulation_demo_no_automation_reaper_clap.rpp

Large diffs are not rendered by default.

3,315 changes: 3,315 additions & 0 deletions demos/host_test/visual_modulation_demo_no_automation_reaper_vst3.rpp

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions macos/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleShortVersionString</key>
<string>1.9.0</string>
<string>1.9.1</string>
<key>CFBundleVersion</key>
<string>1.9.0</string>
<string>1.9.1</string>
<key>NSHumanReadableCopyright</key>
<string></string>
<key>NSHighResolutionCapable</key>
Expand Down
12 changes: 12 additions & 0 deletions manual.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,18 @@ It's fully resizable by scaling (by dragging the bottom right corner) and also r
A knob with a circle in it or a slider with a small dot in it means it can be modulated by the CV matrices.<br/>
Hover over a parameter to see a more detailed description.

### Realtime visualization

By default the UI shows the realtime state of the audio engine for both graphs and parameters.<br/>
Since this is quite CPU expensive (sorry i didn't do fancy gpu offloading) there's an option to back down
on the amount of realtime repainting, see Visuals dropdown in the master section.

- Parameter modulation global or single active voice paints the exact audio engine state.
- Parameter modulation multiple active voices paints the minimum and maximum values across all active voices.
- Graph modulation global or single active voice paints the exact audio engine state.
- Graph modulation multiple active voices paints the most recent voice state.
- Graph modulation zero active voices paints the static automation state.

### Drag-and-drop to mod matrix support

- Drag any parameter that can act as a mod source (Aux N, Mod Wheel etc) by dragging the parameter label
Expand Down
27 changes: 22 additions & 5 deletions param_reference.html
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<html>
<head>
<title>Firefly Synth 1.9.0</title>
<title>Firefly Synth 1.9.1</title>
<style>th, td { padding: 3px; }a, a:visited { color: #666666; }.description { background:#EEEEEE; }h1 { font-size: 19px; color: black; }h2 { font-size: 17px; color: black; }h3 { font-size: 15px; color: black; }body { font-family: Verdana; color: black; }html { position: relative; max-width: 1024px; margin: auto; }tr td { width: auto; white-space: nowrap; } tr th { width: auto; white-space: nowrap; }table, th, td { font-size: 13px; border: 1px solid gray; border-collapse: collapse; text-align: left; }tr td:last-child { width: 100%; white-space: wrap; } tr th:last-child { width: 100%; white-space: wrap; }</style>
</head>
<body>
<h1>Firefly Synth 1.9.0</h1>
<h1>Firefly Synth 1.9.1</h1>
<h2>Module Overview</h2>
<table>
<tr>
Expand Down Expand Up @@ -261,7 +261,7 @@ <h3>Master</h3>
<td colspan='11' class='description'>Factory preset.</td>
</tr>
<tr>
<td rowspan='2'>MIDI Smoothing</td>
<td rowspan='2'>MIDI Smooth</td>
<td>Smoothing</td>
<td>Yes</td>
<td>Instance</td>
Expand All @@ -278,7 +278,7 @@ <h3>Master</h3>
<td colspan='11' class='description'>Smoothing MIDI controller changes.</td>
</tr>
<tr>
<td rowspan='2'>BPM Smoothing</td>
<td rowspan='2'>BPM Smooth</td>
<td>Smoothing</td>
<td>Yes</td>
<td>Instance</td>
Expand All @@ -295,7 +295,7 @@ <h3>Master</h3>
<td colspan='11' class='description'>Smoothing host BPM parameter changes. Affects tempo-synced delay lines.</td>
</tr>
<tr>
<td rowspan='2'>Automation Smoothing</td>
<td rowspan='2'>Automation Smooth</td>
<td>Smoothing</td>
<td>Yes</td>
<td>Instance</td>
Expand All @@ -311,6 +311,23 @@ <h3>Master</h3>
<tr>
<td colspan='11' class='description'>Smoothing automation parameter changes.</td>
</tr>
<tr>
<td rowspan='2'>Realtime Visuals</td>
<td>Visuals</td>
<td>Yes</td>
<td>Instance</td>
<td>1</td>
<td>Input</td>
<td>Block</td>
<td>None</td>
<td>None</td>
<td>Params And Graphs</td>
<td>Params And Graphs</td>
<td>N/A</td>
</tr>
<tr>
<td colspan='11' class='description'>Realtime visualization mode</td>
</tr>
</table>
<a name='4'/>
<h3>GCV-CV</h3>
Expand Down
68 changes: 37 additions & 31 deletions plugin_base/src/plugin_base.clap/plugin_base.clap/pb_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ pb_plugin::
{
PB_LOG_FUNC_ENTRY_EXIT();
stopTimer();
_gui_state.remove_any_listener(this);
_automation_state.remove_any_listener(this);
MTS_DeregisterClient(_mts_client);
}

Expand All @@ -77,38 +77,38 @@ _mts_client(MTS_RegisterClient()),
_desc(std::make_unique<plugin_desc>(topo, this)),
_splice_engine(_desc.get(), false, forward_thread_pool_voice_processor, this),
_extra_state(gui_extra_state_keyset(*_desc->plugin)),
_gui_state(_desc.get(), true),
_automation_state(_desc.get(), true),
_to_gui_events(std::make_unique<event_queue>(default_q_size)),
_to_audio_events(std::make_unique<event_queue>(default_q_size)),
_mod_indicator_queue(std::make_unique<mod_indicator_queue>(default_q_size))
_modulation_output_queue(std::make_unique<modulation_output_queue>(default_q_size))
{
PB_LOG_FUNC_ENTRY_EXIT();
_gui_state.add_any_listener(this);
_mod_indicator_states.reserve(default_q_size);
_automation_state.add_any_listener(this);
_modulation_outputs.reserve(default_q_size);
_block_automation_seen.resize(_splice_engine.state().desc().param_count);
}

void
pb_plugin::gui_param_begin_changes(int index)
{
_gui_state.begin_undo_region();
_automation_state.begin_undo_region();
push_to_audio(index, sync_event_type::begin_edit);
}

void
pb_plugin::gui_param_end_changes(int index)
{
push_to_audio(index, sync_event_type::end_edit);
_gui_state.end_undo_region("Change", _gui_state.desc().params[index]->full_name);
_automation_state.end_undo_region("Change", _automation_state.desc().params[index]->full_name);
}

void
pb_plugin::param_state_changed(int index, plain_value plain)
{
if(_inside_timer_callback) return;
if (_gui_state.desc().params[index]->param->dsp.direction == param_direction::output) return;
if (_automation_state.desc().params[index]->param->dsp.direction == param_direction::output) return;
push_to_audio(index, plain);
_gui_state.set_plain_at_index(index, plain);
_automation_state.set_plain_at_index(index, plain);
}

bool
Expand All @@ -118,7 +118,7 @@ pb_plugin::init() noexcept

// Need to start timer on the main thread.
// Constructor is not guaranteed to run there.
startTimerHz(60);
startTimerHz(20);
return true;
}

Expand All @@ -128,13 +128,13 @@ pb_plugin::timerCallback()
sync_event sevent;
_inside_timer_callback = true;
while (_to_gui_events->try_dequeue(sevent))
_gui_state.set_plain_at_index(sevent.index, sevent.plain);
_automation_state.set_plain_at_index(sevent.index, sevent.plain);

mod_indicator_state mostate;
_mod_indicator_states.clear();
while (_mod_indicator_queue->try_dequeue(mostate))
_mod_indicator_states.push_back(mostate);
if (_gui) _gui->mod_indicator_states_changed();
modulation_output mod_output;
_modulation_outputs.clear();
while (_modulation_output_queue->try_dequeue(mod_output))
_modulation_outputs.push_back(mod_output);
if (_gui) _gui->modulation_outputs_changed();

_inside_timer_callback = false;
}
Expand All @@ -148,7 +148,7 @@ pb_plugin::stateSave(clap_ostream const* stream) noexcept
// don't bother with that and just write byte-for-byte
int written = 1;
int total_written = 0;
std::vector<char> data(plugin_io_save_all_state(_gui_state, &_extra_state, false));
std::vector<char> data(plugin_io_save_all_state(_automation_state, &_extra_state, false));
while(written == 1 && total_written < data.size())
{
written = stream->write(stream, data.data() + total_written, 1);
Expand All @@ -172,15 +172,15 @@ pb_plugin::stateLoad(clap_istream const* stream) noexcept
data.push_back(byte);
} while(true);

_gui_state.begin_undo_region();
if (!plugin_io_load_all_state(data, _gui_state, &_extra_state, false).ok())
_automation_state.begin_undo_region();
if (!plugin_io_load_all_state(data, _automation_state, &_extra_state, false).ok())
{
_gui_state.discard_undo_region();
_automation_state.discard_undo_region();
return false;
}
for (int p = 0; p < _splice_engine.state().desc().param_count; p++)
gui_param_changed(p, _gui_state.get_plain_at_index(p));
_gui_state.discard_undo_region();
gui_param_changed(p, _automation_state.get_plain_at_index(p));
_automation_state.discard_undo_region();
_splice_engine.automation_state_dirty();
return true;
}
Expand Down Expand Up @@ -276,7 +276,7 @@ bool
pb_plugin::guiCreate(char const* api, bool is_floating) noexcept
{
PB_LOG_FUNC_ENTRY_EXIT();
_gui = std::make_unique<plugin_gui>(&_gui_state, &_extra_state, &_mod_indicator_states);
_gui = std::make_unique<plugin_gui>(&_automation_state, &_extra_state, &_modulation_outputs);
return true;
}

Expand Down Expand Up @@ -344,7 +344,9 @@ pb_plugin::push_to_gui(int index, clap_value clap)
e.index = index;
e.type = sync_event_type::value_changing;
e.plain = topo.domain.normalized_to_plain(clap_to_normalized(topo, clap));
_to_gui_events->enqueue(e);
bool enqueued = _to_gui_events->try_enqueue(e);
assert(enqueued);
(void)enqueued;
}

std::int32_t
Expand Down Expand Up @@ -373,8 +375,8 @@ pb_plugin::paramsValue(clap_id param_id, double* value) noexcept
// need to pull in any outstanding audio-to-ui-values from
// the queue before we can report the current value to the host!
timerCallback();
auto const& topo = *_gui_state.desc().param_at_tag(param_id).param;
auto normalized = _gui_state.get_normalized_at_tag(param_id);
auto const& topo = *_automation_state.desc().param_at_tag(param_id).param;
auto normalized = _automation_state.get_normalized_at_tag(param_id);
*value = normalized_to_clap(topo, normalized).value();
return true;
}
Expand Down Expand Up @@ -456,7 +458,8 @@ pb_plugin::paramsFlush(clap_input_events const* in, clap_output_events const* ou
auto normalized = clap_to_normalized(param, clap_value(event->value));
if (main_thread)
{
_gui_state.set_normalized_at_index(index, normalized);
_automation_state.set_normalized_at_index(index, normalized);
if(_gui) _gui->automation_state_changed(index, normalized);
push_to_audio(index, param.domain.normalized_to_plain(normalized));
} else
{
Expand Down Expand Up @@ -782,12 +785,15 @@ pb_plugin::process(clap_process const* process) noexcept
auto const& out_event = block.events.output_params[e];
to_gui_event.index = out_event.param;
to_gui_event.plain = _splice_engine.state().desc().normalized_to_plain_at_index(out_event.param, out_event.normalized);
_to_gui_events->enqueue(to_gui_event);
bool enqueued = _to_gui_events->try_enqueue(to_gui_event);
assert(enqueued);
(void)enqueued;
}

// modulation indicators
for (int e = 0; e < block.events.mod_indicator_states.size(); e++)
_mod_indicator_queue->enqueue(block.events.mod_indicator_states[e]);
// modulation outputs - dont check if it happened
// gui is written to deal with missing events, and there's a lot of them
for (int e = 0; e < block.events.modulation_outputs.size(); e++)
_modulation_output_queue->try_enqueue(block.events.modulation_outputs[e]);

_splice_engine.release_block();
return CLAP_PROCESS_CONTINUE;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public any_state_listener,
public gui_param_listener
{
typedef moodycamel::ReaderWriterQueue<sync_event, default_q_size> event_queue;
typedef moodycamel::ReaderWriterQueue<mod_indicator_state, default_q_size> mod_indicator_queue;
typedef moodycamel::ReaderWriterQueue<modulation_output, default_q_size> modulation_output_queue;

// MTS-ESP support
MTSClient* _mts_client = {};
Expand All @@ -45,14 +45,14 @@ public gui_param_listener
std::unique_ptr<plugin_desc> _desc;
plugin_splice_engine _splice_engine;
extra_state _extra_state;
plugin_state _gui_state = {};
plugin_state _automation_state = {};
std::atomic<bool> _is_active = {};
std::unique_ptr<plugin_gui> _gui = {};
std::vector<int> _block_automation_seen = {};
std::unique_ptr<event_queue> _to_gui_events = {};
std::unique_ptr<event_queue> _to_audio_events = {};
std::unique_ptr<mod_indicator_queue> _mod_indicator_queue = {};
std::vector<mod_indicator_state> _mod_indicator_states = {};
std::vector<modulation_output> _modulation_outputs = {};
std::unique_ptr<modulation_output_queue> _modulation_output_queue = {};

// see param_state_changed and timerCallback()
// and vst3 pb_controller _inside_set_param_normalized
Expand Down
26 changes: 13 additions & 13 deletions plugin_base/src/plugin_base.vst3/plugin_base.vst3/pb_component.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,10 @@ _splice_engine(_desc.get(), false, nullptr, nullptr)
}
}

// fetch mod indicator param tags
_mod_indicator_count_param_tag = stable_hash(mod_indicator_count_param_guid);
for (int i = 0; i < mod_indicator_output_param_count; i++)
_mod_indicator_param_tags[i] = stable_hash(mod_indicator_param_guids[i]);
// fetch mod output param tags
_mod_output_count_param_tag = stable_hash(modulation_output_count_param_guid);
for (int i = 0; i < modulation_output_param_count; i++)
_mod_output_param_tags[i] = stable_hash(modulation_output_param_guids[i]);
}

tresult PLUGIN_API
Expand Down Expand Up @@ -232,19 +232,19 @@ pb_component::process(ProcessData& data)
queue->addPoint(0, event.normalized.value(), unused_index);
}

// module modulation indicators
// module modulation outputs
unused_index = 0;
if (data.outputParameterChanges)
{
auto num_mod_indicators = block.events.mod_indicator_states.size();
num_mod_indicators = std::min((int)num_mod_indicators, mod_indicator_output_param_count);
queue = data.outputParameterChanges->addParameterData(_mod_indicator_count_param_tag, unused_index);
queue->addPoint(0, *reinterpret_cast<double*>(&num_mod_indicators), unused_index);
for (int e = 0; e < block.events.mod_indicator_states.size(); e++)
auto num_modulation_outputs = block.events.modulation_outputs.size();
num_modulation_outputs = std::min((int)num_modulation_outputs, modulation_output_param_count);
queue = data.outputParameterChanges->addParameterData(_mod_output_count_param_tag, unused_index);
queue->addPoint(0, *reinterpret_cast<double*>(&num_modulation_outputs), unused_index);
for (int e = 0; e < block.events.modulation_outputs.size(); e++)
{
auto const& this_mod_indicator_state = block.events.mod_indicator_states[e];
queue = data.outputParameterChanges->addParameterData(_mod_indicator_param_tags[e], unused_index);
queue->addPoint(0, *reinterpret_cast<double const*>(&this_mod_indicator_state.packed), unused_index);
auto const& this_modulation_outputs = block.events.modulation_outputs[e];
queue = data.outputParameterChanges->addParameterData(_mod_output_param_tags[e], unused_index);
queue->addPoint(0, *reinterpret_cast<double const*>(&this_modulation_outputs.packed), unused_index);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ public Steinberg::Vst::AudioEffect {
std::vector<float> _scratch_out_l;
std::vector<float> _scratch_out_r;

// for modulation indicators
int _mod_indicator_count_param_tag = -1;
std::array<int, mod_indicator_output_param_count> _mod_indicator_param_tags = {};
// for modulation outputs
int _mod_output_count_param_tag = -1;
std::array<int, modulation_output_param_count> _mod_output_param_tags = {};

public:
PB_PREVENT_ACCIDENTAL_COPY(pb_component);
Expand Down
Loading

0 comments on commit 94c2c72

Please sign in to comment.