0
mirror of https://github.com/torvalds/GuitarPedal.git synced 2026-08-18 21:26:54 +00:00
Files
torvalds-GuitarPedal/WebMIDI/index.html
Linus Torvalds 0c1b9c3db3 Split Software/ into the four things it actually was
'Software' was the directory everything that was not KiCad ended up in,
which stopped describing anything a while ago - Validation and the web
app are software too.  Worse, it put the shared parts inside the
firmware, where they read as the firmware's own.

They are not.  Effects/ has three consumers built from it: the firmware,
Validation's bench, and the web app's controls, all generated from the
same POT: comments by gen_effects.py.  Audio/ has two - the bench
compiles the same biquads, the same envelope followers and the same
single_sample(), which is the whole reason a measurement on a
workstation says anything about the pedal.  Neither belongs under
Firmware/, so neither is under it any more:

  Effects/    one file per effect
  Audio/      the DSP they are built from, and the audio loop
  Firmware/   the rest of what runs on the pedal, and the submodules
  WebMIDI/    the web app
  scripts/    what the build runs
  Validation/ unchanged
  Hardware/, Documentation/, Images/

CMakeLists.txt and the wrapper Makefile move to the top with them,
because the build now consumes four of those directories and generates
into a fifth.  board.local and build/ come along; MIDI_CC_MAP.md is
generated into Documentation/ rather than into the old Software/ root.

scripts/ goes with the build rather than staying under the firmware,
because six of the ten had nothing to do with the firmware: gen_effects.py
reads Effects/ and writes to three different places, pow2/log2/quarter_sine
generate Audio/'s tables, check-readme.py compares Effects/ against the
README, and server.py serves the web app.  Four of them are invoked from
Validation, which was reaching into Firmware/ for tooling - the same
burying this commit is undoing.  The four that really are about the
firmware are ELF checks the top-level build drives anyway, and a second
scripts directory would only be a second place to look.

C includes say "Audio/foo.h" and the generated map says
"Effects/bar.h", with the repository root on the include path for both
the firmware and the bench.  Spelling the directory out rather than
relying on a bare name is what keeps Audio/cycles.h shimmable: a quoted
include searches the including file's own directory first.

The submodules are renamed as well as moved.  git mv updates their paths
but leaves the section names, and 'Software/pico-sdk' surviving in
.gitmodules would be the word this commit removes, still load-bearing.
That meant the nested modules under pico-sdk too - six .git files
pointing into .git/modules/Software - which is why 'git submodule update
--init --recursive' is worth running once after pulling this.

Verified rather than assumed: a clean configure and build, make check
(failing only on the missing-eeprom case it already failed on),
check-effects, all four analysis pages reproducing every series and
drawing every chart, and a flash to the board that still measures a
routed reverb where it did before.

One latent bug fell out of it.  bench/coeff declared only quarter_sine.h
of the three generated math tables, and Audio/util.h includes pow2.h and
log2.h as well - so building that target with an empty gen/ could never
have worked.  'make bench' builds bench/bench first, which generates all
three, so it stayed hidden until this rebuilt everything from nothing.

Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2026-08-11 13:48:26 -07:00

214 lines
9.6 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>RP2350 Pedal Controller</title>
<link rel="manifest" href="manifest.json">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<link rel="apple-touch-icon" href="icon-192.png">
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="app-container">
<header class="glass-panel app-header">
<!-- Everything the header says, as opposed to everything it does.
None of it is worth a line of its own: the title is a device
name you already know, and the faults are absent nearly
always. Together they are one small line under the controls,
and the colour is what you actually read in the moment. -->
<div class="header-status">
<h1 id="app-title" class="title-disconnected">RP2350 Pedal</h1>
<div id="pedal-status" class="pedal-status hidden" role="status" aria-live="polite">
<span id="status-clip" class="status-clip hidden">CLIP</span>
<span id="status-drop" class="status-drop hidden">DROP</span>
<!-- Not a transient like the other two, but the same channel:
something is wrong and you want to know now. -->
<span id="status-board" class="status-board hidden" title="">BOARD</span>
<!-- Old but working hardware: worth noticing, not worth
alarming anybody about, so muted rather than red. -->
<span id="status-early" class="status-early hidden" title="">EARLY BOARD</span>
</div>
</div>
<div class="header-controls">
<div class="burger-wrap">
<button id="burger-btn" class="action-btn" title="Menu"
aria-haspopup="true" aria-expanded="false"></button>
<div id="global-menu" class="menu-dropdown hidden" role="menu">
<!-- The scene labels are filled in from the picker, so it
always says which one it is about to overwrite. -->
<button id="global-save-scene-btn" role="menuitem">💾&nbsp;&nbsp;Save Scene</button>
<button id="global-load-scene-btn" role="menuitem">📂&nbsp;&nbsp;Load Scene</button>
<button id="global-unroute-btn" role="menuitem">&nbsp;&nbsp;Unroute All Effects</button>
<hr>
<button id="update-app-btn" role="menuitem">⬇️&nbsp;&nbsp;Update App</button>
<button id="open-bindings-btn" role="menuitem">🎛️&nbsp;&nbsp;Physical Controls…</button>
<button id="open-settings-btn" role="menuitem">🔌&nbsp;&nbsp;MIDI Connection…</button>
<hr>
<button id="global-program-btn" class="danger-item" role="menuitem">⚠️&nbsp;&nbsp;Reboot to Programming Mode</button>
</div>
</div>
<!-- Which scene you're on is worth seeing without opening
anything. Acting on it is not: Save overwrites the eeprom
and Load throws away what you have, so both sit behind the
menu where you can't reach them by accident. -->
<div class="header-scenes">
<select id="global-scene-select" title="Scene">
<!-- Options injected via JS -->
</select>
</div>
<button id="tuner-btn" class="action-btn" title="Tuner Mode">🎵</button>
<label class="switch" title="Global Enable">
<input type="checkbox" id="global-enable">
<span class="slider round"></span>
</label>
</div>
</header>
<!-- Tuner Panel -->
<div id="tuner-panel" class="glass-panel hidden">
<div class="tuner-header">
<h2>Tuner</h2>
<div style="display: flex; gap: 15px; align-items: center;">
<label style="color: var(--text-muted); display: flex; align-items: center; gap: 8px; cursor: pointer;">
<input type="checkbox" id="tuner-synth-toggle"> Play Tones
</label>
<button id="close-tuner-btn" class="action-btn" title="Exit Tuner"></button>
</div>
</div>
<div class="tuner-chromatic-display">
<div class="tuner-note" id="tuner-chromatic-note">--</div>
<div class="tuner-cents" id="tuner-chromatic-cents"></div>
<div class="tuner-vol" id="tuner-chromatic-vol">V: 0</div>
</div>
<div class="tuner-meter-container">
<div class="tuner-meter-center"></div>
<div id="tuner-needle" class="tuner-needle"></div>
</div>
<div class="tuner-strings" id="tuner-poly-strings">
<!-- Strings dynamically added here -->
</div>
</div>
<main id="effects-container">
<!-- Effect cards populated dynamically -->
</main>
<!-- Panel Backdrop -->
<div id="panel-backdrop" class="hidden"></div>
<!-- Reached from the menu. Set once and then left alone, which is
why it isn't in the menu itself. -->
<div id="settings-panel" class="glass-panel dialog-panel hidden" role="dialog" aria-modal="true">
<div class="panel-header">
<h2>MIDI Connection</h2>
<button id="close-settings" class="action-btn" title="Close"></button>
</div>
<div class="dialog-body">
<div class="menu-section">
<h3>Devices</h3>
<div class="menu-field">
<label for="midi-input-select">Input</label>
<select id="midi-input-select" class="menu-select"></select>
</div>
<div class="menu-field">
<label for="midi-output-select">Output</label>
<select id="midi-output-select" class="menu-select"></select>
</div>
</div>
<!-- What is on the other end. Here because this is the dialog
you open when something is not working, and "the pedal is
running firmware from before the change you are looking for"
is high on the list of reasons. -->
<div class="menu-section">
<h3>Pedal</h3>
<p class="menu-hint" id="identity-info">Not connected.</p>
</div>
<div class="menu-section">
<h3>Channel</h3>
<!-- The same value as the Settings card's "MIDI Ch" pot, and
the same control underneath - see bindMidiChannel(). It
is repeated here because this is where you come looking
when Control Change stops working, and the channel is the
usual reason. -->
<div class="menu-field">
<select id="midi-channel-select" class="menu-select"></select>
</div>
<p class="menu-hint">The pedal filters Control Change and Program
Change by this, so bypass, the tuner and scene changes need it to
match. Parameter edits are SysEx and work either way — which is
how this dialog can still fix it when it is wrong.</p>
</div>
</div>
</div>
<!-- The pedal has one knob and one footswitch and no way at all to
say what either of them is for. This is where that gets said,
and it is the only place it is written down. -->
<div id="bindings-panel" class="glass-panel dialog-panel hidden" role="dialog" aria-modal="true">
<div class="panel-header">
<h2>Physical Controls</h2>
<button id="close-bindings" class="action-btn" title="Close"></button>
</div>
<div class="dialog-body">
<div class="menu-section">
<div id="bindings-rows"></div>
<p class="menu-hint" id="bindings-hint">Not connected.</p>
</div>
</div>
</div>
<!-- Active Pot Panel -->
<div id="active-pot-panel" class="glass-panel bottom-panel hidden">
<div class="panel-header">
<div class="active-pot-info">
<span id="active-pot-title">Effect - Pot</span>
<span id="active-pot-value" class="pot-value">0</span>
</div>
<button id="close-active-pot" class="action-btn" title="Close"></button>
</div>
<div class="active-pot-slider-container">
<input type="range" id="active-pot-slider" class="large-slider" min="0" max="120" value="60">
</div>
<!-- The knob is the one control you can reach without the app, so
the shortcut to point it here belongs next to the thing it
would be pointed at. -->
<div class="active-pot-knob">
<button id="assign-knob-btn" class="action-btn">Assign knob</button>
<span id="knob-target-hint" class="menu-hint"></span>
</div>
</div>
</div>
<script src="effects.js"></script>
<script src="app.js"></script>
<script>
// Keep the service worker away from local development. Unregistering
// one does not stop it controlling the page - the active worker stays
// in charge of already-controlled clients until they all go away - so
// a stale app.js can happily survive several reloads and an "Update
// App" or three. The PWA only matters for the deployed copy anyway.
//
// IS_LOCAL_DEV comes from app.js, which is loaded above.
if ('serviceWorker' in navigator) {
if (IS_LOCAL_DEV) {
// And clean up after any worker registered here previously
navigator.serviceWorker.getRegistrations()
.then(regs => regs.forEach(reg => reg.unregister()));
} else {
window.addEventListener('load', () => {
navigator.serviceWorker.register('./service-worker.js')
.then(registration => {
console.log('ServiceWorker registration successful');
})
.catch(err => {
console.log('ServiceWorker registration failed: ', err);
});
});
}
}
</script>
</body>
</html>