-
Notifications
You must be signed in to change notification settings - Fork 3
EN MIDI
Menu path: Options → Audio → MIDI · Boards: RP2350 only (RP2040 has no MIDI) · Requires: nothing for the Software synth; a MIDI device on the board's MIDI_TX pin for the hardware modes; an instrument bank on SD for the GM.DLS wavetable (a
.dlsconverts right on the device)
The emulator can turn a ZX-Spectrum program's MIDI output into real notes. ZX software drives MIDI in two different ways, and pico-spec emulates both, plus two built-in synthesisers that need no external hardware:
-
AY — the bit-banged MIDI of
zx-midiplayer-style players. The program toggles AY register 14, bit 2 as a serial TX line; the emulator decodes that bit-banged 31250-baud UART frame and forwards each byte. -
ShamaZX — the ShamaZX MIDI interface, accessed through I/O ports
#A0CF(data) /#A1CF(status). The program writes MIDI bytes to the data port and polls the "transmitter busy" bit. -
Software — a small internal procedural synth (
SoftSynth): square/saw/triangle/ noise voices with ADSR and 8 presets. No sample data, no SD bank — works out of the box. -
GM.DLS Wavetable — a fixed-point General MIDI wavetable synth that plays a real
GM sound bank for far more realistic instruments. You supply the bank
(
gm_bank.bin, packed from agm.dls/ GUS set — see below).
In the AY and ShamaZX modes the decoded MIDI byte stream is sent out the board's MIDI_TX GPIO as a standard 31250-baud, 8-N-1 serial signal (the MIDI hardware standard), so you can wire a synth, a MIDI module (e.g. a SAM2695), or a Raspberry Pi running a soft-synth to it. The Software and GM.DLS modes ignore the pin entirely and mix their audio into the emulator's own output.
RP2040 boards have no MIDI at all — the whole feature is compiled out (
#if !PICO_RP2040). The MIDI row does not appear in the Audio menu on the ZERO and MURM (RP2040) boards.
Options → Audio → MIDI(Ext:P<pin>) ▸
├── OFF
├── AY decode AY reg-14 bit-2 bit-bang → MIDI_TX pin
├── ShamaZX #A0CF/#A1CF ports → MIDI_TX pin
├── Software MIDI ▸ internal procedural synth (no hardware)
│ └── Synth Preset ▸ GM / Piano / Chiptune / Strings / Rock / Organ / Music Box / Synth
└── GM.DLS Wavetable GM wavetable from gm_bank.bin on SD (no hardware)
The menu title shows the active external TX pin for this board, e.g.
MIDI(Ext:P22). Selecting Software MIDI opens the Synth Preset submenu;
selecting GM.DLS Wavetable loads/installs the bank (see below).
| Item | Values | Default | Description |
|---|---|---|---|
| MIDI | OFF / AY / ShamaZX / Software MIDI / GM.DLS Wavetable | OFF | MIDI mode. Stored in NVS (midi) |
| Synth Preset | GM / Piano / Chiptune / Strings / Rock / Organ / Music Box / Synth | GM | Voice set for the Software synth only. Stored in NVS (midipreset) |
- The mode is
Config::midi(0=OFF,1=AY,2=ShamaZX,3=Software,4=GM.DLS); the Software preset isConfig::midi_synth_preset(0=GM …7=Synth). Saved on change. - Changing the mode tears down the previous mode (
Midi::deinit()) and brings up the new one (Midi::init()) live — no reboot (the GM.DLS first install does reboot, see below). - The current MIDI state is also shown on the Emulator Info screen
(Hardware → Emulator Info):
Off,AY bitbang,ShamaZX,Software (<preset>), orGM.DLS (bank OK / no bank).
| AY | ShamaZX | Software | GM.DLS Wavetable | |
|---|---|---|---|---|
| ZX-side interface | AY reg 14, bit 2 |
#A0CF/#A1CF
|
#A0CF/#A1CF (+AY bit-bang) |
#A0CF/#A1CF (+AY bit-bang) |
| Uses MIDI_TX pin | yes (31250 8-N-1) | yes | no | no |
| Needs external hardware | yes | yes | no | no |
| Needs a bank on SD | no | no | no |
yes (gm_bank.bin) |
| Sound | external device | external device | procedural (chip-like) | real GM samples |
The hardware UART runs at the MIDI standard 31250 baud, 8-N-1. Sends are non-blocking:
a byte is dropped if the TX FIFO is full (the ShamaZX driver polls the busy bit at
#A1CF first, so this should not happen in practice).
The GM.DLS Wavetable mode plays a real General MIDI sound bank. The bank is not included — you provide it once. Two ways to get it in:
-
On the device (easiest): copy the raw
.dlsfile to SD and either open it from the F5 file browser, or pick "[+] Convert a .dls..." in the instrument-set picker — the device converts it itself (see below). No PC tools needed. -
On a PC: pack the
.dlsinto a.binbank withtools/dls_pack.pyand copy the result to SD.
The wavetable engine and the
dls_pack/gus_packconversion tools come from xrip/embedded-midi-synth — thanks to @xrip. See Credits & help.
- A General MIDI DLS bank — e.g. Microsoft's
gm.dls(shipped with Windows atC:\Windows\System32\drivers\gm.dls, ~3.4 MB), or an open GUS patch set (freepats). - An RP2350 board with ≥ 4 MB flash — the packed bank lives in a dedicated ~1.6 MB flash partition (or, on boards with PSRAM, loads straight into PSRAM). RP2040 boards have no MIDI at all.
- A PC with Python 3 only if you prefer the offline packer — the on-device converter needs nothing.
Licensing. Microsoft's
gm.dlsis copyrighted and not redistributable. Packing your own copy (from a Windows licence you own) for personal use on your own device is fine, but do not commitgm.dls/gm_bank.binto a repo or ship firmware containing it. For a freely shareable bank use freepats (open licence) instead.
The engine plays a compact packed .bin bank, not the raw .dls — but the conversion
can happen on the device itself. Copy the .dls to SD, then either:
-
open the
.dlsfrom the F5 file browser — it is converted and GM.DLS mode is enabled in one go; or - in Options → Audio → MIDI → GM.DLS Wavetable, pick the first row of the instrument-set picker, "[+] Convert a .dls...", and choose the file.
The converter streams the file (a progress percentage is shown), writes
<name>.bin into /.config/pico-spec/, and selects it. A bank too big for the flash
partition is rejected with a size message.
Alternatively convert offline with tools/dls_pack.py, packed at 31250 Hz
(pico-spec's audio rate — the engine does not resample, so the rate must match):
python3 tools/dls_pack.py gm.dls gm_bank.bin 31250
The result is ~1.6 MB (PCM stored as 8-bit µ-law). For a GUS / freepats patch set use
tools/gus_pack.py instead, pointing it at the TiMidity config:
python3 tools/gus_pack.py timidity.cfg gm_bank.bin 31250
Both packers are pure Python (no dependencies) and emit the same gm_bank.bin format.
Put the packed bank on the SD card, either at the root (e.g. /gm_bank.bin) or in
/.config/pico-spec/.
Instrument-set picker. Selecting GM.DLS Wavetable always opens an "Instrument set" picker: the first row is "[+] Convert a .dls..." (on-device conversion, see 1a), followed by every packed bank found on SD, the active one marked
[*]. Declining the install later reverts the selection — nothing changes until you confirm.
Options → Audio → MIDI → GM.DLS Wavetable, then pick a bank:
- Boards with PSRAM load the bank live — sound works immediately, no reboot.
-
Without PSRAM the bank must go into flash, which only happens at early boot:
confirm "Install this bank? (reboots)" and the device reboots and writes it during
the next boot — the screen stays off and the LED blinks for ~20-30 s (do not
power off). It then comes up with sound. Because the bank lives in its own flash
partition, later boots are instant (no SD read) and it survives firmware
updates (the partition is not part of the firmware
.uf2).
-
New bank: copy a different
gm_bank.binto SD and select GM.DLS again — if it differs from the flash copy, it reinstalls on the next boot. - Force reinstall (e.g. to recover a bad bank): select GM.DLS Wavetable when it is already loaded and answer Yes to "Reinstall DLS bank from SD?" — it reboots and rewrites from SD.
- Flash writing happens only at early boot, single-core (before video starts) — that is why the screen is off during the write; it avoids freezing the HDMI/video on core 1.
- An interrupted write is safe: the bank header is committed last, so a partial/aborted write is simply rejected on boot and you can reinstall.
- Instruments come from the bank, so the Synth Preset submenu does not apply to GM.DLS — it is only for the Software synth.
- If
gm_bank.binis missing/invalid, GM.DLS mode is silent and the info line showsGM.DLS (no bank). -
ALF cartridges no longer conflict. ALF TV Game cartridges now stream from SD
instead of occupying the bank's flash region, so GM.DLS and a loaded cartridge
coexist — no unload prompt. Only special ALF firmware builds that reclaim the
bank partition for the firmware itself (
NO_GM_DLS) drop the GM.DLS menu row entirely.
The external TX pin is fixed per board at build time (MIDI_TX_PIN). On RP2350 only an
even GPIO can be a UART TX (odd GPIOs are RX-only); the code auto-selects the UART
instance and funcsel from the pin number. (Applies to the AY / ShamaZX hardware
modes only — Software and GM.DLS do not use the pin.)
| Board | MCU | MIDI_TX GPIO | UART | Status |
|---|---|---|---|---|
| MURM2 | RP2350A | 22 | UART1 | OK — but shares the pin with Real sound-in (WAV): mutually exclusive (see below) |
| PICO_PC | RP2350A | 26 | UART1 | OK |
| ZERO2 | RP2350B | 22 | UART1 | OK |
| MURM-1 (RP2350 / MURM_P2) | RP2350 | 22 | UART1 | OK — but shares the pin with Real sound-in (WAV): mutually exclusive (see below) |
| PICO_DV | RP2350 | 21 | — |
BROKEN — GPIO 21 is odd (RX-only), so it cannot be a UART TX. Hardware MIDI does not work on PICO_DV. (When the debug-probe UART is enabled, the pin is set to 255 = disabled.) |
| ZERO | RP2040 | — | — | No MIDI (RP2040 — feature compiled out) |
| MURM (RP2040) | RP2040 | — | — | No MIDI (RP2040 — feature compiled out) |
On PICO_DV the Software and GM.DLS synths still work (they do not use a TX pin); only the AY and ShamaZX hardware-output modes are affected by the odd-pin bug.
See the full GPIO maps in Boards & pinout.
On MURM2 and MURM-1 (RP2350) the MIDI_TX pin (GPIO 22) is the same pin as LOAD_WAV_PIO — the GPIO used by Real sound-in (Storage → Tape → Real sound-in). They are mutually exclusive: only one can use the pin. If you enable a hardware MIDI mode (AY or ShamaZX) while Real sound-in is on, a warning appears:
MIDI and Real sound-in share GPIO 22. Disable one of them.
(The Software and GM.DLS synths do not use the pin, so they never conflict.)
MIDI uses the standard 31250-baud serial / current-loop signalling. Connect the board's MIDI_TX GPIO to the MIDI IN of your device. Both the Pico and most 3.3 V MIDI modules are 3.3 V logic; classic 5-pin DIN MIDI uses the usual opto-isolated current-loop circuit, while small UART-MIDI synth modules (e.g. SAM2695 breakouts) take the TX line directly. A common setup is a Raspberry Pi running a software synth fed from the Pico's TX pin. Full pinouts: Boards & pinout.
- Software synth (no hardware): set MIDI → Software MIDI, pick a preset, then run a ZX MIDI player. You should hear the procedural synth in the emulator's audio.
-
GM.DLS wavetable: copy a
.dlsto SD, set MIDI → GM.DLS Wavetable and use "[+] Convert a .dls..." (or pack a bank on a PC first). On PSRAM boards sound is immediate; otherwise wait for the one-time flash install (LED blinks ~20-30 s after the reboot). Then run a MIDI player — you should hear real GM instruments. - Hardware out: on a working board (MURM2 / PICO_PC / ZERO2 / MURM-1), set MIDI → AY or ShamaZX, wire MIDI_TX to a synth's MIDI IN, and run the same player.
- ShamaZX MIDI interface: https://zxpress.ru/article.php?id=9979
- General MIDI: https://en.wikipedia.org/wiki/General_MIDI
- freepats (open GM patch set): https://freepats.zenvoid.org/
- Boards & pinout: Boards & pinout
- Developer notes (MIDI UART TX constraint, per-board pins): CLAUDE.md
The GM.DLS Wavetable synth engine and the bank-conversion tools (dls_pack /
gus_pack) are based on xrip/embedded-midi-synth —
many thanks to @xrip for the engine and for permission to
use it in pico-spec.
Need help? Ask in the Murmulator Telegram chat: https://t.me/ZX_MURMULATOR/241767
Меню (RU)
Сквозные темы
Для разработчиков