Potatoslicer — chiptune audio engine with tracker-style sequencer
Project description
Potatoslicer Sequencer — Audio Client
Chiptune-style audio engine and player. No audio files — all sound is generated procedurally from JSON song data.
Architecture
SoundSystem (facade)
→ PotatoslicerEngine
→ Sequencer (tracker-style pattern playback)
→ Synth (voice pool + instruments)
→ Voice (oscillator + envelope per note)
→ Mixer (sum + soft clip)
→ AudioDevice (sounddevice callback → speakers)
Voice Model
- Fixed pool of 20 voices
- Each
note_onallocates a voice from the pool - Chords allocate one voice per note in the chord
- Detune allocates an additional voice per note (±pitch offset)
- Voice stealing: inactive → released → oldest active
- A channel can own multiple voices (for chords)
note_off/---releases all voices on that channel
Signal Path
Oscillator (pulse/triangle/saw/noise)
→ ADSR Envelope
→ Voice output
→ Mixer (sum all active voices → tanh soft clip → master volume)
→ sounddevice callback → speakers
Quick Start
from potatoslicer.sound_system import SoundSystem
sound = SoundSystem()
sound.start()
sound.play_song('groove') # loads from songs/groove.json
sound.play_sfx('confirm') # trigger a preset sound effect
sound.stop()
Keyboard Controls (in installer)
M— toggle audio on/off- Audio state shown in top-right status bar
Song Format
Songs are JSON files in potatoslicer/songs/.
Example
{
"title": "My Song",
"tempo": 122,
"rows_per_beat": 4,
"order": [0, 0, 1, 1, 2, 3],
"instruments": {
"stab": {
"waveform": "pulse",
"duty_cycle": 0.25,
"attack": 0.001,
"decay": 0.04,
"sustain": 0.0,
"release": 0.03,
"volume": 0.13,
"detune": 0.003
},
"bass": {
"waveform": "triangle",
"attack": 0.001,
"decay": 0.05,
"sustain": 0.5,
"release": 0.04,
"volume": 0.52
}
},
"patterns": {
"0": [
[
{"channel": 0, "note": "C-1", "instrument": "kick"},
{"channel": 4, "note": ["D-4", "F-4", "A-4"], "instrument": "stab"}
],
[],
[{"channel": 2, "note": "C-5", "instrument": "hat"}],
[]
]
}
}
Top-Level Fields
| Field | Description |
|---|---|
title |
Song name (display only) |
tempo |
BPM (beats per minute) |
rows_per_beat |
Rows per beat. 4 = 16th note resolution |
order |
Pattern play sequence, loops when finished |
instruments |
Named instrument definitions |
patterns |
Numbered patterns, each a list of rows |
Instrument Fields
| Field | Default | Description |
|---|---|---|
waveform |
pulse |
pulse, square, triangle, saw, noise |
duty_cycle |
0.5 |
Pulse width (0.0–1.0). Only affects pulse waveform |
attack |
0.01 |
Seconds to reach full volume |
decay |
0.05 |
Seconds to drop from full to sustain |
sustain |
0.6 |
Hold level (0.0–1.0) |
release |
0.1 |
Seconds to fade after note off |
volume |
0.7 |
Output volume (0.0–1.0) |
vibrato_depth |
0.0 |
Pitch modulation depth (0.002 = subtle) |
vibrato_rate |
5.0 |
Vibrato speed in Hz |
detune |
0.0 |
Spawns detuned voice pair for thickness. 0.003–0.005 = subtle widening |
Pattern Events
Each row is a list of events (or empty [] for silence):
{"channel": 0, "note": "C-5", "instrument": "lead", "volume": 0.8}
| Field | Description |
|---|---|
channel |
Voice channel (0–6 music, 7 reserved for SFX) |
note |
Single note "C-5", chord ["C-4","E-4","G-4"], or "---" for note off |
instrument |
Instrument name |
volume |
Optional, 0.0–1.0 |
Chords
note can be a single string or an array of note strings:
{"channel": 4, "note": ["D-4", "F-4", "A-4"], "instrument": "stab"}
This triggers all notes simultaneously on the same channel. "---" releases all voices on the channel.
Voice cost: Each note in a chord uses one voice. With detune > 0, each note uses two voices. A 3-note chord with detune = 6 voices.
Note Format
C-5— C in octave 5C#4— C sharp in octave 4D#3— D sharp in octave 3---— note off (releases all voices on channel)
Timing
seconds_per_row = 60 / tempo / rows_per_beat
| Tempo | Rows/beat | Row duration |
|---|---|---|
| 120 | 4 | 125ms |
| 122 | 4 | 123ms |
| 140 | 4 | 107ms |
Current Song: groove.json
"Chorus Lift" at 122 BPM.
Structure: [0, 0, 1, 1, 2, 3, 1, 0] — 8 patterns, ~32 bars
| Pattern | Character | Description |
|---|---|---|
| 0 | Intro/groove | Kick + bass + hats, stab chord enters halfway |
| 1 | Main | Full groove with clap, stab chords (Dm, Bb) |
| 2 | Breakdown | Hats only + single stab hit |
| 3 | Build | Accelerating kicks, claps, stab chords |
Instruments: kick (triangle), hat (noise), clap (noise), bass (triangle), stab (detuned pulse chords)
SFX Presets
Defined in presets.py:
| Name | Sound | Use |
|---|---|---|
confirm |
High bleep | Button press, selection |
cancel |
Low tone | Back, cancel |
error |
Noise burst | Error state |
click |
Short tick | UI interaction |
deploy_start |
Ascending tone | Deploy begins |
deploy_done |
Sustained tone | Deploy success |
deploy_fail |
Descending buzz | Deploy failure |
Song Tool
CLI for editing songs.
cd potatoslicer-client-python
# Show song metadata
python -m potatoslicer.song_tool info groove
# Export to editable tracker text format
python -m potatoslicer.song_tool export groove
# Creates: groove_edit.txt
# Edit the file, then reimport
python -m potatoslicer.song_tool import groove_edit.txt
# Preview (plays for 10s, Ctrl-C to stop)
python -m potatoslicer.song_tool play groove
# Change tempo
python -m potatoslicer.song_tool tempo groove 130
Tracker Text Format
The export format is human-readable and editable:
TITLE: Chorus Lift
TEMPO: 122
ROWS_PER_BEAT: 4
ORDER: 0 0 1 1 2 3 1 0
INST kick: triangle 0.5 0.001 0.09 0.0 0.02 0.75
INST stab: pulse 0.25 0.001 0.04 0.0 0.03 0.13
PATTERN 0
000 | 0:C-1:kick 1:D-2:bass
001 | ---
002 | 2:C-5:hat
003 | ---
Event format: channel:note:instrument[:volume]
Dependencies
| Package | Required | Purpose |
|---|---|---|
numpy |
Yes | Sample generation |
sounddevice |
Optional | Audio output |
libportaudio2 |
Optional | System lib for sounddevice |
Audio is gracefully disabled if sounddevice/portaudio are missing.
Install all: make installer-deps
Waveform Reference
| Waveform | Character | Best for |
|---|---|---|
pulse |
Bright, buzzy | Leads, stabs, arps |
square |
Hollow | Leads (pulse duty=0.5) |
triangle |
Soft, warm | Bass, kicks |
saw |
Harsh, edgy | Bass, aggressive leads |
noise |
White noise | Hi-hats, snares, claps |
Pulse Width
duty_cycle |
Sound |
|---|---|
0.5 |
Square wave (most hollow) |
0.25 |
Classic lead |
0.125 |
Thin, nasal |
0.0625 |
Very thin, almost click |
Detune
Adding "detune": 0.003 to an instrument:
- Spawns a second voice per note, slightly pitch-shifted
- Creates stereo-like width and harmonic thickness
- Essential for chord stabs to sound full rather than thin
- Costs 1 extra voice per note (budget accordingly with 20-voice pool)
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file potatoslicer_client-0.1.0.tar.gz.
File metadata
- Download URL: potatoslicer_client-0.1.0.tar.gz
- Upload date:
- Size: 71.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ae0c0e4b94759e2d003dd56117d9d09cb550938c2b40bc81dbb9c0b05c1f84a6
|
|
| MD5 |
1854388f81dcf43b31f457294a7e20ee
|
|
| BLAKE2b-256 |
66a3a261b0a584cea924d5e6053864273254be71378a5862bc44cdbffa118bea
|
File details
Details for the file potatoslicer_client-0.1.0-py3-none-any.whl.
File metadata
- Download URL: potatoslicer_client-0.1.0-py3-none-any.whl
- Upload date:
- Size: 82.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6576057dc8622d44414256c904144eb682207b1742ca0084fe5ceec5cf8d788a
|
|
| MD5 |
25fd977dfd2e1c21b9d70637943a0505
|
|
| BLAKE2b-256 |
f593e3238b4c076c8aa9776dc5e7fe2abcca5a83c90b95fca64ec6da26dadce2
|