Skip to content

Commit

Permalink
Merge branch 'more-vismod-stuff'
Browse files Browse the repository at this point in the history
  • Loading branch information
sjoerdvankreel committed Sep 21, 2024
2 parents 46b0d2a + 22e6736 commit 4f9a52e
Show file tree
Hide file tree
Showing 48 changed files with 1,110 additions and 585 deletions.
14 changes: 14 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
### September 21, 2024 - V1.9.3.

- Added free-running smooth noise as a new LFO type. Note - consecutive cycles are not smooth by themselves so you still need to use the LFO filter to get a real smooth signal.
- Bugfix in generating random numbers for the per-voice-random cv source (they weren't all that random).
- Breaking change: lfo noise generators become phase-based sampling functions to prevent drift.
* This causes small and possibly audible differences for the (free-running-) static and smooth lfos, sorry, but was needed to keep engine and ui in check.
* On the upside, they now correctly respond to phase offset.
- LFO graphs now respond to per-voice-seeded random generators.
- LFO and CV graphs now respond to free-running random generators.
- Envelope and CV graphs now respond to retriggered/multi-triggered envelopes.
- CV graphs now respond to on-note-global-lfo, on-voice-random-seed, MIDI key and velocity.
* Not! to MICI CC and CLAP modulation signals, maybe later.
- CV graphs are now animated in all cases, replaced phase indicators (because they were wrong) by a continuous moving signal.

### September 11, 2024 - V1.9.2.

- Bugfix: modulated params would be slow to visually react to user input.
Expand Down
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.2</string>
<string>1.9.3</string>
<key>CFBundleVersion</key>
<string>1.9.2</string>
<string>1.9.3</string>
<key>NSHumanReadableCopyright</key>
<string></string>
<key>NSHighResolutionCapable</key>
Expand Down
4 changes: 2 additions & 2 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.2</title>
<title>Firefly Synth 1.9.3</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.2</h1>
<h1>Firefly Synth 1.9.3</h1>
<h2>Module Overview</h2>
<table>
<tr>
Expand Down
4 changes: 3 additions & 1 deletion plugin_base/src/plugin_base/plugin_base/dsp/block/plugin.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,10 @@ struct plugin_block final {
float pitch_to_freq_with_tuning(float pitch);

void set_out_param(int param, int slot, double raw) const;

// this is meant for audio->ui communication
void push_modulation_output(modulation_output const& output)
{ modulation_outputs->push_back(output); }
{ assert(!graph); modulation_outputs->push_back(output); }

template <domain_type DomainType>
float normalized_to_raw_fast(int module_, int param_, float normalized) const;
Expand Down
110 changes: 110 additions & 0 deletions plugin_base/src/plugin_base/plugin_base/dsp/block/shared.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
#include <plugin_base/dsp/block/shared.hpp>

namespace plugin_base {

modulation_output
modulation_output::make_mod_out_voice_state(std::int8_t voice_index, bool is_active, std::uint32_t stream_time_low)
{
modulation_output result;
result.state.voice.is_active = is_active;
result.state.voice.voice_index = voice_index;
result.state.voice.stream_time_low = stream_time_low;
result.state.voice.event_type = out_event_voice_activation;
return result;
}

modulation_output
modulation_output::make_mod_output_cv_state(std::int8_t voice_index, std::uint8_t module_global, float position_normalized)
{
assert(voice_index >= -1);
assert(-1e-3 <= position_normalized && position_normalized <= 1 + 1e-3);
modulation_output result;
result.state.cv.voice_index = voice_index;
result.state.cv.module_global = module_global;
result.state.cv.event_type = out_event_cv_state;
result.state.cv.position_normalized = position_normalized;
return result;
}

modulation_output
modulation_output::make_mod_output_custom_state(std::int8_t voice_index, std::uint8_t module_global, std::uint8_t tag_custom, std::int32_t value_custom)
{
assert(voice_index >= -1);
modulation_output result;
result.state.custom.tag_custom = tag_custom;
result.state.custom.voice_index = voice_index;
result.state.custom.value_custom = value_custom;
result.state.custom.module_global = module_global;
result.state.custom.event_type = out_event_custom_state;
return result;
}

modulation_output
modulation_output::make_mod_output_param_state(std::int8_t voice_index, std::uint8_t module_global, std::uint16_t param_global, float value_normalized)
{
assert(voice_index >= -1);
assert(-1e-3 <= value_normalized && value_normalized <= 1 + 1e-3);
modulation_output result;
result.state.param.voice_index = voice_index;
result.state.param.param_global = param_global;
result.state.param.module_global = module_global;
result.state.param.event_type = out_event_param_state;
result.state.param.value_normalized = (std::uint16_t)(std::clamp(value_normalized, 0.0f, 1.0f) * std::numeric_limits<std::uint16_t>::max());
return result;
}

bool operator <
(timed_modulation_output const& l, timed_modulation_output const& r)
{
if (l.output.event_type() < r.output.event_type()) return true;
if (l.output.event_type() > r.output.event_type()) return false;
if (l.output.event_type() == out_event_voice_activation)
{
// this event *is* the timestamp
if (l.output.state.voice.voice_index < r.output.state.voice.voice_index) return true;
if (l.output.state.voice.voice_index > r.output.state.voice.voice_index) return false;
if (l.output.state.voice.stream_time_low < r.output.state.voice.stream_time_low) return true;
if (l.output.state.voice.stream_time_low > r.output.state.voice.stream_time_low) return false;
return false;
}

// for all others sort by timestamp first (0 for global) voice index second
if (l.output.event_type() == out_event_cv_state)
{
if (l.output.state.cv.module_global < r.output.state.cv.module_global) return true;
if (l.output.state.cv.module_global > r.output.state.cv.module_global) return false;
if (l.stream_time_low < r.stream_time_low) return true;
if (l.stream_time_low > r.stream_time_low) return false;
if (l.output.state.cv.voice_index < r.output.state.cv.voice_index) return true;
if (l.output.state.cv.voice_index > r.output.state.cv.voice_index) return false;
return false;
}
if (l.output.event_type() == out_event_custom_state)
{
if (l.output.state.custom.module_global < r.output.state.custom.module_global) return true;
if (l.output.state.custom.module_global > r.output.state.custom.module_global) return false;
if (l.stream_time_low < r.stream_time_low) return true;
if (l.stream_time_low > r.stream_time_low) return false;
if (l.output.state.custom.voice_index < r.output.state.custom.voice_index) return true;
if (l.output.state.custom.voice_index > r.output.state.custom.voice_index) return false;
if (l.output.state.custom.tag_custom < r.output.state.custom.tag_custom) return true;
if (l.output.state.custom.tag_custom > r.output.state.custom.tag_custom) return false;
return false;
}
if (l.output.event_type() == out_event_param_state)
{
if (l.output.state.param.module_global < r.output.state.param.module_global) return true;
if (l.output.state.param.module_global > r.output.state.param.module_global) return false;
if (l.output.state.param.param_global < r.output.state.param.param_global) return true;
if (l.output.state.param.param_global > r.output.state.param.param_global) return false;
if (l.stream_time_low < r.stream_time_low) return true;
if (l.stream_time_low > r.stream_time_low) return false;
if (l.output.state.param.voice_index < r.output.state.param.voice_index) return true;
if (l.output.state.param.voice_index > r.output.state.param.voice_index) return false;
return false;
}
assert(false);
return false;
}

}
114 changes: 35 additions & 79 deletions plugin_base/src/plugin_base/plugin_base/dsp/block/shared.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
#pragma once

#include <limits>
#include <cassert>
#include <cstdint>
#include <algorithm>

namespace plugin_base {

Expand All @@ -17,20 +20,17 @@ struct shared_block final {
float const* const* audio_in;
};

// once per block output data
// we send back from audio->gui
// so it can paint fancy stuff
// it was at one point modulation output only
// but now also voice start/end
// but i dont feel like renaming a gazillion occurrences
// also it must fit in sizeof(double) to simplify the
// vst3 implementation
// Once per block output data we send back from audio->gui so it can paint fancy stuff.
// It was at one point modulation output only but now also voice start/end.
// But i dont feel like renaming a gazillion occurrences.
// Also it must fit in sizeof(double) to simplify the vst3 implementation.

enum output_event_type
{
out_event_voice_activation,
out_event_cv_state,
out_event_param_state
out_event_param_state,
out_event_custom_state
};

struct mod_out_voice_state
Expand Down Expand Up @@ -64,11 +64,21 @@ struct mod_out_param_state
{ return std::clamp((float)value_normalized / std::numeric_limits<std::uint16_t>::max(), 0.0f, 1.0f); }
};

struct mod_out_custom_state
{
std::uint8_t event_type;
std::int8_t voice_index; // -1 for global
std::uint8_t module_global;
std::uint8_t tag_custom;
std::int32_t value_custom;
};

union mod_out_state
{
mod_out_cv_state cv;
mod_out_voice_state voice;
mod_out_param_state param;
mod_out_custom_state custom;
};

union modulation_output
Expand All @@ -77,81 +87,27 @@ union modulation_output
std::uint64_t packed;

// stuff overlaps, all should match
output_event_type event_type() const
{ return (output_event_type)state.cv.event_type; }

static modulation_output
make_mod_out_voice_state(std::uint8_t voice_index, bool is_active, std::uint32_t stream_time_low)
{
modulation_output result;
result.state.voice.is_active = is_active;
result.state.voice.voice_index = voice_index;
result.state.voice.stream_time_low = stream_time_low;
result.state.voice.event_type = out_event_voice_activation;
return result;
}
std::int8_t voice_index() const { return state.cv.voice_index; }
output_event_type event_type() const { return (output_event_type)state.cv.event_type; }

static modulation_output
make_mod_output_cv_state(std::int8_t voice_index, std::uint8_t module_global, float position_normalized)
{
assert(voice_index >= -1);
assert(-1e-3 <= position_normalized && position_normalized <= 1 + 1e-3);
modulation_output result;
result.state.cv.voice_index = voice_index;
result.state.cv.module_global = module_global;
result.state.cv.event_type = out_event_cv_state;
result.state.cv.position_normalized = position_normalized;
return result;
}

make_mod_out_voice_state(std::int8_t voice_index, bool is_active, std::uint32_t stream_time_low);
static modulation_output
make_mod_output_cv_state(std::int8_t voice_index, std::uint8_t module_global, float position_normalized);
static modulation_output
make_mod_output_custom_state(std::int8_t voice_index, std::uint8_t module_global, std::uint8_t tag_custom, std::int32_t value_custom);
static modulation_output
make_mod_output_param_state(std::int8_t voice_index, std::uint8_t module_global, std::uint16_t param_global, float value_normalized)
{
assert(voice_index >= -1);
assert(-1e-3 <= value_normalized && value_normalized <= 1 + 1e-3);
modulation_output result;
result.state.param.voice_index = voice_index;
result.state.param.param_global = param_global;
result.state.param.module_global = module_global;
result.state.param.event_type = out_event_param_state;
result.state.param.value_normalized = (std::uint16_t)(std::clamp(value_normalized, 0.0f, 1.0f) * std::numeric_limits<std::uint16_t>::max());
return result;
}
make_mod_output_param_state(std::int8_t voice_index, std::uint8_t module_global, std::uint16_t param_global, float value_normalized);
};

inline bool operator <
(modulation_output const& l, modulation_output const& r)
// with timestamp for voice, needed for sorting
struct timed_modulation_output
{
if (l.event_type() < r.event_type()) return true;
if (l.event_type() > r.event_type()) return false;
if (l.event_type() == out_event_voice_activation)
{
if (l.state.voice.voice_index < r.state.voice.voice_index) return true;
if (l.state.voice.voice_index > r.state.voice.voice_index) return false;
if (l.state.voice.stream_time_low < r.state.voice.stream_time_low) return true;
if (l.state.voice.stream_time_low > r.state.voice.stream_time_low) return false;
return false;
}
if (l.event_type() == out_event_cv_state)
{
if (l.state.cv.module_global < r.state.cv.module_global) return true;
if (l.state.cv.module_global > r.state.cv.module_global) return false;
if (l.state.cv.voice_index < r.state.cv.voice_index) return true;
if (l.state.cv.voice_index > r.state.cv.voice_index) return false;
return false;
}
if (l.event_type() == out_event_param_state)
{
if (l.state.param.module_global < r.state.param.module_global) return true;
if (l.state.param.module_global > r.state.param.module_global) return false;
if (l.state.param.param_global < r.state.param.param_global) return true;
if (l.state.param.param_global > r.state.param.param_global) return false;
if (l.state.param.voice_index < r.state.param.voice_index) return true;
if (l.state.param.voice_index > r.state.param.voice_index) return false;
return false;
}
assert(false);
return false;
}
modulation_output output;
std::uint32_t stream_time_low;
};

bool operator <
(timed_modulation_output const& l, timed_modulation_output const& r);

}
12 changes: 6 additions & 6 deletions plugin_base/src/plugin_base/plugin_base/dsp/engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,7 @@ plugin_engine::activate_modules()
{
plugin_block block(make_plugin_block(-1, -1, m, mi, engine_tuning_mode_no_tuning, 0, 0));
_input_engines[m][mi] = factory(*_state.desc().plugin, _sample_rate, _max_frame_count);
_input_engines[m][mi]->reset(&block);
_input_engines[m][mi]->reset_audio(&block);
}
}

Expand All @@ -403,7 +403,7 @@ plugin_engine::activate_modules()
{
plugin_block block(make_plugin_block(-1, -1, m, mi, engine_tuning_mode_no_tuning, 0, 0));
_output_engines[m][mi] = factory(*_state.desc().plugin, _sample_rate, _max_frame_count);
_output_engines[m][mi]->reset(&block);
_output_engines[m][mi]->reset_audio(&block);
}
}
}
Expand Down Expand Up @@ -533,7 +533,7 @@ plugin_engine::process_voice(int v, bool threaded)

double start_time = seconds_since_epoch();
_voice_module_process_duration_sec[v][m][mi] = start_time;
_voice_engines[v][m][mi]->process(block);
_voice_engines[v][m][mi]->process_audio(block);
_voice_module_process_duration_sec[v][m][mi] = seconds_since_epoch() - start_time;

// plugin completed its envelope
Expand Down Expand Up @@ -617,7 +617,7 @@ plugin_engine::activate_voice(
_voice_states[slot].sub_voice_index, _last_note_key, _last_note_channel));
plugin_block block(make_plugin_block(slot, _voice_states[slot].note_id_.channel, m, mi, tuning_mode, state.start_frame, state.end_frame));
block.voice = &voice_block;
_voice_engines[slot][m][mi]->reset(&block);
_voice_engines[slot][m][mi]->reset_audio(&block);
}
}

Expand Down Expand Up @@ -968,7 +968,7 @@ plugin_engine::process()
plugin_block block(make_plugin_block(-1, -1, m, mi, _current_block_tuning_mode, 0, frame_count));
double start_time = seconds_since_epoch();
_global_module_process_duration_sec[m][mi] = start_time;
_input_engines[m][mi]->process(block);
_input_engines[m][mi]->process_audio(block);
_global_module_process_duration_sec[m][mi] = seconds_since_epoch() - start_time;
}

Expand Down Expand Up @@ -1232,7 +1232,7 @@ plugin_engine::process()
block.out = &out_block;
double start_time = seconds_since_epoch();
_global_module_process_duration_sec[m][mi] = start_time;
_output_engines[m][mi]->process(block);
_output_engines[m][mi]->process_audio(block);
_global_module_process_duration_sec[m][mi] = seconds_since_epoch() - start_time;

// copy back output parameter values
Expand Down
Loading

0 comments on commit 4f9a52e

Please sign in to comment.