Skip to content

Commit

Permalink
user guide: Update settings editor for new LFO features
Browse files Browse the repository at this point in the history
- Updated winterjs' forms and utils.
- Modified waveforms.js to support new winterjs forms.
- Added LFO fields to the settings editor.
- Added LFO waveshape display to the settings editor.
  • Loading branch information
theacodes committed Aug 31, 2021
1 parent 3adf2cf commit 604b5c7
Show file tree
Hide file tree
Showing 8 changed files with 609 additions and 100 deletions.
4 changes: 2 additions & 2 deletions user_guide/docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -357,9 +357,9 @@ To configure the internal LFO, **hold** down the hard sync button. The module's
![LFO configuration knobs highlighted](images/tweak.png)

1. Moving the **chorus knob** (φ) will change the frequency of the LFO. The LEDs at the bottom of the module will pulse to show the LFO's frequency.
2. Moving the **pulse width** knob of each of the oscillators will enable or disable LFO routing to the oscillator's pulse width. Turning the knob counter-clockwise will disable the LFO, turning it clockwise will enable it. The LEDs near the oscillator will illuminate if the LFO is enabled. When the LFO is enabled, the pulse width knob will control the *intensity* of the pulse width modulation instead of directly setting the pulse width.
2. Moving the **pulse width** knob of each of the oscillators will enable or disable LFO routing to the oscillator's pulse width. When the LFO is routed to an oscillator, its pulse width knob will control the *intensity* of the pulse width modulation instead of directly setting the pulse width. Turning the knob counter-clockwise will disable the LFO routing, turning it clockwise will enable it. The LEDs near the oscillator will illuminate if the LFO routing is enabled.

You can also configure the values for these settings at boot-up using the [settings editor](#editing-module-settings).
By default, the internal LFO is a straightforward triangle wave. However, the [settings editor](#editing-module-settings) allows you to change the LFO's waveshape between triangle, sine, sawtooth, and square, as well as combine a *second* waveshape with the first to create interesting LFO effects.


## Editing module settings
Expand Down
11 changes: 8 additions & 3 deletions user_guide/docs/scripts/gem_settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import Struct from "./structy.js";

class GemSettings extends Struct {
static _pack_string = "HhHiiiiiiiiiiH??ii";
static _pack_string = "HhHiiiiiiiiiiH??iiiBBii";
static _fields = [
{ name: "adc_gain_corr", kind: "uint16", default: 2048 },
{ name: "adc_offset_corr", kind: "int16", default: 0 },
Expand All @@ -14,7 +14,7 @@ class GemSettings extends Struct {
{ name: "pollux_knob_min", kind: "fix16", default: -1.2 },
{ name: "pollux_knob_max", kind: "fix16", default: 1.2 },
{ name: "chorus_max_intensity", kind: "fix16", default: 0.05 },
{ name: "lfo_frequency", kind: "fix16", default: 0.2 },
{ name: "lfo_1_frequency", kind: "fix16", default: 0.2 },
{ name: "cv_offset_error", kind: "fix16", default: 0.0 },
{ name: "cv_gain_error", kind: "fix16", default: 1.0 },
{ name: "smooth_initial_gain", kind: "fix16", default: 0.1 },
Expand All @@ -24,9 +24,14 @@ class GemSettings extends Struct {
{ name: "pollux_lfo_pwm", kind: "bool", default: false },
{ name: "pitch_knob_nonlinearity", kind: "fix16", default: 0.6 },
{ name: "base_cv_offset", kind: "fix16", default: 1.0 },
{ name: "lfo_2_frequency_ratio", kind: "fix16", default: 2 },
{ name: "lfo_1_waveshape", kind: "uint8", default: 0 },
{ name: "lfo_2_waveshape", kind: "uint8", default: 0 },
{ name: "lfo_1_factor", kind: "fix16", default: 1 },
{ name: "lfo_2_factor", kind: "fix16", default: 0 },
];

static packed_size = 58;
static packed_size = 72;

constructor(values = {}) {
super(values);
Expand Down
87 changes: 79 additions & 8 deletions user_guide/docs/scripts/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import Gemini from "./gemini.js";
import GitHub from "./github.js";

const ui = {
settings_form: $e("settings_editor"),
settings_form_elem: $e("settings_editor"),
info_section: $e("info_section"),
settings_section: $e("settings_section"),
save_btn: $e("save_button"),
Expand All @@ -27,13 +27,14 @@ const ui = {
firmware_incompatible: $e("firmware_incompatible"),
serial_number: $e("serial_number"),
restore_adc_calibration_btn: $e("restore_adc_calibration"),
lfo_waveform_canvas: $e("lfo-waveform-canvas"),
};

const midi = new MIDI("Gemini");
const gemini = new Gemini(midi);
const settings = new GemSettings();
/* The lowest compatible firmware version is from June, 2021 */
const minimum_firmware_version = new Date(2021, 5, 1);
/* The lowest compatible firmware version */
const minimum_firmware_version = new Date(2021, 8, 31);
let gemini_firmware_version = null;
let gemini_serial_number = null;

Expand Down Expand Up @@ -75,7 +76,7 @@ async function restore_adc_calibration() {
settings.cv_gain_error = afe_calibration.gain_error;
settings.cv_offset_error = afe_calibration.offset_error;

forms.update_values(ui.settings_form);
ui.settings_form.update();
}

function check_firmware_version() {
Expand Down Expand Up @@ -166,7 +167,7 @@ $on(ui.connect_btn, "click", async function () {
return;
}

forms.update_values(ui.settings_form);
ui.settings_form.update();

ui.connect_btn.classList.remove("is-primary");
ui.connect_btn.classList.add("is-success");
Expand Down Expand Up @@ -217,11 +218,81 @@ $on(ui.allow_danger, "change", function () {
/*
Form data binding and display logic
*/
forms.bind(ui.settings_form, settings);
forms.bind_value_displays(ui.settings_form);
ui.settings_form = new forms.Form(ui.settings_form_elem, settings);
forms.bind_value_displays(ui.settings_form.elem);

new forms.ValueDisplay(
ui.settings_form["pollux_follower_threshold"],
ui.settings_form.elem["pollux_follower_threshold"],
(input) => ((input.valueAsNumber / 4096) * 6.0).toFixed(2),
"pollux_follower_threshold_value_display_volts"
);

/*
Helper to draw the LFO waveform.
*/
function draw_lfo_waveform() {
const get_waveform = (selection) => {
selection = parseInt(selection);
switch (selection) {
// Triangle
case 0:
return (phase, frequency) =>
-1.0 + Math.abs(-2.0 + ((phase * frequency) % 1.0) * 4);
// Sine
case 1:
return (phase, frequency) =>
Math.sin(2 * Math.PI * phase * frequency);
// Sawtooth
case 2:
return (phase, frequency) =>
-1.0 + ((phase * frequency) % 1.0) * 2;
// Square
case 3:
return (phase, frequency) =>
(phase * frequency) % 1.0 < 0.5 ? -1 : 1;
}
};

const freq = 3;
const lfo_1_f = get_waveform(settings.lfo_1_waveshape);
const lfo_1_a = settings.lfo_1_factor;
const lfo_2_f = get_waveform(settings.lfo_2_waveshape);
const lfo_2_a = settings.lfo_2_factor;
const lfo_2_r = settings.lfo_2_frequency_ratio;

const lfo_func = (phase) => {
return (
lfo_1_f(phase, freq) * lfo_1_a +
lfo_2_f(phase, freq * lfo_2_r) * lfo_2_a
);
};

const canvas = ui.lfo_waveform_canvas;
const ctx = canvas.getContext("2d");
const padding = canvas.height * 0.2;

ctx.clearRect(0, 0, canvas.width, canvas.height);

ctx.strokeStyle = "rgb(94, 64, 158)";
ctx.lineWidth = 10;
ctx.lineCap = "round";
ctx.lineJoin = "round";

ctx.beginPath();
for (let i = 0; i < 1.1; i += 1 / canvas.width) {
let val = lfo_func(0.25 + i);
val = (val + 1) / 2;
ctx.lineTo(
i * canvas.width,
padding + val * (canvas.height - padding * 2)
);
}
ctx.stroke();
}

draw_lfo_waveform();
for (let input of ui.settings_form.elem.querySelectorAll("[name*=lfo]")) {
input.addEventListener("input", () => {
window.requestAnimationFrame(draw_lfo_waveform);
});
}
8 changes: 4 additions & 4 deletions user_guide/docs/scripts/waveforms.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ const pulse_display = new WaveformDisplay($e("pulse"), (phase, offset) => {
return pulse_waveform(phase, offset, pulse_config.width);
});

forms.bind($e("pulse-form"), pulse_config);
new forms.Form($e("pulse-form"), pulse_config);
$on($e("pulse-form"), "input", () => pulse_display.update());

/* Sub waveform */
Expand Down Expand Up @@ -147,7 +147,7 @@ const mix_display = new WaveformDisplay($e("mix"), (phase, offset) => {
);
});

forms.bind($e("mix-form"), mix_config);
new forms.Form($e("mix-form"), mix_config);
$on($e("mix-form"), "input", () => mix_display.update());

/* Stacked oscillators. */
Expand All @@ -172,7 +172,7 @@ const stacked_display = new WaveformDisplay($e("stacked"), (phase, offset) => {
);
});

forms.bind($e("stacked-form"), stacked_config);
new forms.Form($e("stacked-form"), stacked_config);
$on($e("stacked-form"), "input", () => stacked_display.update());

/* Chorusing */
Expand All @@ -199,7 +199,7 @@ const chorusing_display = new WaveformDisplay(
}
);

forms.bind($e("chorusing-form"), chorusing_config);
new forms.Form($e("chorusing-form"), chorusing_config);
$on($e("chorusing-form"), "input", () => chorusing_display.update());

/* Non-linear tune */
Expand Down
Loading

0 comments on commit 604b5c7

Please sign in to comment.