viper.asm$EDA0 · palms.bin

viper

A vim-keyed NES chiptune tracker for the terminal that compiles songs to real NSF files, plays them through an emulated 2A03, and can rip music back out of NSFs and cartridges.

coverviper editing a phrase of stress_melodeath.vip, with neighbouring phrases shown dimmed above and below and a :gen euclid command previewing five DPCM hits before it runs.
viper editing a phrase of stress_melodeath.vip, with neighbouring phrases shown dimmed above and below and a :gen euclid command previewing five DPCM hits before it runs.
README.mdviper/index.mdx

A step sequencer for the five NES sound channels (two pulse, triangle, noise, DPCM) in a single Rust binary. You edit a tracker grid with vim motions, operators and text objects: dd deletes a row, dip deletes a phrase, . repeats, q/@ record and replay macros. Insert mode turns the bottom keyboard row into a chromatic piano.

Songs save as plain-text .vip files that round-trip losslessly, so they can be diffed, grepped, or written by hand.

Beyond the grid

  • Real NES output. viper compile links a song against a 6502 sound driver and produces an NSF or NSFe. :engine apu plays the song through an emulated 2A03 instead of the built-in ADSR synth.
  • Verification. viper render writes a deterministic mix, per-channel stems, trigger MIDI and a register-write log. viper verify diffs that log against another emulator; the bundled stress song matches FCEUX frame for frame.
  • Ripping. viper rip reads music back out of an NSF, a register dump, or a cartridge it boots itself. It recovers the row grid by simulating the driver’s row clock at candidate tempos, infers instruments from the envelopes, and reports which numbers were read and which were guessed.
  • Generators. :gen covers Euclidean rhythms, chord progressions, basslines, arpeggios, L-systems and cellular automata, all deterministic per seed. viper gen composes a whole song from a style directory.
  • Visualizer. Bars, scope, grid, orbit and sprite renderers in half-blocks. Sprite sheets can be loaded straight from a ROM’s CHR data, and sprite properties bind to audio sources through a small expression language.
  • Live mode. Play notes in real time, overdub, launch scenes on bar boundaries, mute channels.

Running it

Needs Rust 1.80+ (and ALSA headers on Linux). It boots with a demo song loaded.

git clone https://github.com/sadovsky/viper.git
cd viper
cargo run --release

A working sound driver ships in tests/fixtures/, so the compile, render and verify commands work out of the box.