You've already forked torvalds-GuitarPedal
mirror of
https://github.com/torvalds/GuitarPedal.git
synced 2026-08-18 21:26:54 +00:00
'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>
187 lines
6.2 KiB
C
187 lines
6.2 KiB
C
//
|
|
// MIDI connection code
|
|
//
|
|
|
|
//
|
|
// MIDI Control Change (CC) constants
|
|
//
|
|
// Deep editing is SysEx - it names the effect and the pot explicitly, so
|
|
// nothing here needs to know about parameters. What is left is
|
|
// performance control coming in and status going out.
|
|
//
|
|
//
|
|
// Global bypass in and out, and - on value 126 - reboot to the
|
|
// bootloader.
|
|
//
|
|
// This number is frozen, and not because it is a good one. CC 20 is in
|
|
// the MSB half of the 14-bit controller range, so by the rule below it
|
|
// belongs up at 102 with the rest of what we invented. But 126 on this
|
|
// controller is how you get an enclosed pedal into programming mode, and
|
|
// some enclosures have no exposed BOOTSEL to fall back on. A pedal
|
|
// answers the number the firmware it is already running was built with,
|
|
// so moving it strands anything flashed before the move: the recovery
|
|
// path has to keep working on the old number, which means the old number
|
|
// is the only number.
|
|
//
|
|
#define MIDI_CC_GLOBAL_ENABLE 20
|
|
|
|
//
|
|
// Status out.
|
|
//
|
|
// Everything we made up lives in CC 102-119, which the spec leaves
|
|
// undefined. Below 64 is the 14-bit convention, where CC n+32 is the
|
|
// LSB of CC n - so 32 is Bank Select LSB and anything in 0-31 can be
|
|
// read as an MSB waiting for its other half. Where the standard already
|
|
// means what we mean, use the standard number instead: CC 7 is volume
|
|
// everywhere, and CC 11 is expression when that jack gets wired.
|
|
//
|
|
// The pedal has one LED and it can only say "something wants you". The
|
|
// host can do better than that, so these say what. Three CCs, split by
|
|
// what the answer is about rather than by which subsystem noticed:
|
|
//
|
|
// The global one carries what is not per-effect. A count rather than a
|
|
// flag for the dropped samples, because "once" and "constantly" are
|
|
// different problems and the LED cannot tell you which - see status.h.
|
|
//
|
|
// bits 0-4 samples dropped since the last report, to 31
|
|
// bit 5 the output clipped
|
|
// bit 6 effects[0], the front of the chain, wants attention
|
|
//
|
|
// The chain ones are one bit per routed effect in chain order, so the
|
|
// host can light the effect that is doing something instead of just
|
|
// reporting that something is. Two of them because a CC value is seven
|
|
// bits and a chain can hold fourteen.
|
|
//
|
|
#define MIDI_CC_STATUS_GLOBAL 102
|
|
#define MIDI_CC_STATUS_CHAIN_LO 103
|
|
#define MIDI_CC_STATUS_CHAIN_HI 104
|
|
|
|
#define STATUS_DROPPED_MASK 0x1f
|
|
#define STATUS_CLIPPED (1u << 5)
|
|
#define STATUS_FRONT_ATTN (1u << 6)
|
|
|
|
// How many effects fit in one of the chain CCs
|
|
#define STATUS_CHAIN_BITS 7
|
|
|
|
//
|
|
// USB-MIDI 1.0 packs everything into four bytes: a cable number in the
|
|
// high nibble of the first byte - always zero here - and a Code Index
|
|
// Number in the low nibble, then up to three bytes of the message
|
|
// itself. The CIN says what kind of message it is and, with it, how
|
|
// many of those three bytes are real. 0x0 and 0x1 are reserved and
|
|
// mean nothing to a host.
|
|
//
|
|
// Both directions of the hardware MIDI port need this, one to build a
|
|
// CIN and one to take it apart, so keep the two halves next to each
|
|
// other where they can be checked against one another.
|
|
//
|
|
|
|
// How many of the three data bytes a CIN actually carries
|
|
static inline int midi_cin_length(uint8_t cin)
|
|
{
|
|
switch (cin) {
|
|
case 0x5: // single-byte system common, or SysEx ending on one
|
|
case 0xF: // single byte
|
|
return 1;
|
|
case 0x2: // two-byte system common
|
|
case 0x6: // SysEx ending on two
|
|
case 0xC: // program change
|
|
case 0xD: // channel pressure
|
|
return 2;
|
|
case 0x3: // three-byte system common
|
|
case 0x4: // SysEx start or continue
|
|
case 0x7: // SysEx ending on three
|
|
case 0x8: // note off
|
|
case 0x9: // note on
|
|
case 0xA: // poly key pressure
|
|
case 0xB: // control change
|
|
case 0xE: // pitch bend
|
|
return 3;
|
|
default: // 0x0 and 0x1 are reserved
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// The CIN a status byte belongs in. Not for SysEx, whose CIN depends
|
|
// on where in the stream the packet falls rather than on any one byte.
|
|
static inline uint8_t midi_status_cin(uint8_t status)
|
|
{
|
|
if (status >= 0xF8) // real time
|
|
return 0xF;
|
|
if (status >= 0xF0) {
|
|
switch (status) {
|
|
case 0xF1: // MIDI time code
|
|
case 0xF3: // song select
|
|
return 0x2;
|
|
case 0xF2: // song position
|
|
return 0x3;
|
|
default: // tune request, and friends
|
|
return 0x5;
|
|
}
|
|
}
|
|
// Channel voice: the CIN is simply the top nibble
|
|
return status >> 4;
|
|
}
|
|
|
|
bool handle_midi_packet(const uint8_t packet[4]);
|
|
void usb_midi_poll(void);
|
|
bool usb_midi_write(const uint8_t packet[4]);
|
|
bool usb_midi_write_nb(const uint8_t packet[4]);
|
|
void uart_midi_write(const uint8_t packet[4]);
|
|
|
|
static inline void send_midi_cc(uint8_t cc, uint8_t val)
|
|
{
|
|
uint8_t packet[4] = { 0x0B, 0xB0, cc, val };
|
|
usb_midi_write(packet);
|
|
uart_midi_write(packet);
|
|
}
|
|
|
|
//
|
|
// The same, for something nobody is waiting on.
|
|
//
|
|
// Returns whether USB took it, so a caller that repeats itself anyway can
|
|
// simply not remember having sent it and say it again next time. A host
|
|
// that is not reading fills the transmit fifo and every blocking write
|
|
// into it costs MIDI_TX_TIMEOUT_MS, which for anything periodic is a
|
|
// stall the pedal inflicts on itself for no reader's benefit.
|
|
//
|
|
// The UART is written either way and is not part of the answer: it is a
|
|
// ring that drops when full and never waits, so there is nothing to
|
|
// report and nothing to retry.
|
|
//
|
|
static inline bool send_midi_cc_nb(uint8_t cc, uint8_t val)
|
|
{
|
|
uint8_t packet[4] = { 0x0B, 0xB0, cc, val };
|
|
uart_midi_write(packet);
|
|
return usb_midi_write_nb(packet);
|
|
}
|
|
|
|
static inline void send_midi_note_on(uint8_t ch, uint8_t note, uint8_t vel)
|
|
{
|
|
uint8_t packet[4] = { 0x09, 0x90 | (ch & 0x0F), note, vel };
|
|
usb_midi_write(packet);
|
|
uart_midi_write(packet);
|
|
}
|
|
|
|
static inline void send_midi_note_off(uint8_t ch, uint8_t note, uint8_t vel)
|
|
{
|
|
uint8_t packet[4] = { 0x08, 0x80 | (ch & 0x0F), note, vel };
|
|
usb_midi_write(packet);
|
|
uart_midi_write(packet);
|
|
}
|
|
|
|
static inline void send_midi_pitch_bend(uint8_t ch, int16_t bend)
|
|
{
|
|
uint16_t val = bend + 8192;
|
|
uint8_t packet[4] = { 0x0E, 0xE0 | (ch & 0x0F), val & 0x7F, (val >> 7) & 0x7F };
|
|
usb_midi_write(packet);
|
|
uart_midi_write(packet);
|
|
}
|
|
|
|
static inline void send_midi_channel_pressure(uint8_t ch, uint8_t pressure)
|
|
{
|
|
uint8_t packet[4] = { 0x0D, 0xD0 | (ch & 0x0F), pressure & 0x7F, 0 };
|
|
usb_midi_write(packet);
|
|
uart_midi_write(packet);
|
|
}
|