Skip to content
andrewfaderPublic

About

Yet Another NES Audio Plugin, a CLAP synth for chiptunes and retro sounds

Resources

Stars

4 stars

Watchers

1 watching

Forks

Repository files navigation

YANES — Yet Another NES Audio Plugin

YANES is a clean-room CLAP instrument for NES and other retro console, computer, and arcade sounds. It builds on Linux, Windows, and macOS and is designed for Bitwig Studio and other CLAP hosts. A custom editor is included on all three platforms (X11 on Linux, Win32, and Cocoa).

YANES editor showing the Voice controls

Screenshots of every editor page: Voice, Sequence, Synth, FM, Hardware, FX + TV, and Custom. Also see the preset browser.

What is implemented

YANES is a polyphonic multi-chip synthesizer. Its core oscillator set covers NES pulse, triangle, noise, and DPCM; VRC6, FDS, Namco 163, VRC7, and Sunsoft 5B expansions; Game Boy and Master System voices; Genesis PSG and YM2612 FM; AY-3-8910, POKEY, PC Engine, OPL2/OPL3, OPN/OPNA, OPM, SID, Konami SCC, Philips SAA1099, and Atari TIA. It also includes original morphing-wavetable, phase-distortion, additive, six-operator FM, digital-partial, SNES-style Gaussian-sample, Neo Geo-style FM bell, arcade feedback-FM, and vowel/formant-wavetable synthesis modes, plus stack modes that map MIDI channels to multi-voice chip layouts.

The NES DPCM voice additionally supports a sixteen-slot bank: mono or stereo 16-bit WAV files are converted to one-bit DPCM, while pre-encoded .ydmc data can be loaded directly. Slots map to consecutive MIDI notes and can be looped, trimmed, and saved in CLAP project state.

The SNES sample bank voice plays that same sixteen-slot bank as a real, key-tracked sampler through a clean-room model of the SNES S-DSP: an imported WAV is resampled to the S-DSP's native 32 kHz and BRR-encoded (the console's four predictor filters and block-aligned loop), and playback decodes the BRR and interpolates with the S-DSP's exact 512-entry 4-point Gaussian table — the soft, high-rolled-off character of real SNES samples. Raw .brr files load directly, including the two-byte loop-offset header that carries a sample's real mid-sample loop point; the BRR bytes persist in project state (format v17), so a reopened project decodes identically; an empty slot falls back to a built-in SNES-style tone so the voice is always playable. When this voice is selected its echo runs through the S-DSP's eight-tap echo FIR — the soft, high-rolled- off colour of the console's signature reverb — reusing the Effects rack's Echo time, feedback and mix (no extra control). The Gaussian table and BRR filter coefficients are fixed SNES hardware constants; the surrounding code is implemented from the documented hardware behaviour, not ported from an emulator. The voice decodes BRR on the fly the way the S-DSP voice does, carrying the predictor history through the loop point, and steps its replay rate on the console's 14-bit pitch register. YANES's clean-room S-DSP is checked against the reference (blargg's SPC_DSP, with its Gaussian interpolator on): every one of 69 real Donkey Kong Country cartridge samples matches at four pitches to within 0.996 correlation (median 0.9993; the test gate is 0.99). Pass -DYANES_SNES_SAMPLES=a.brr:b.brr to pin the suite to your own samples (see FURNACE_PARITY.md).

The current instrument provides 12.5%, 25%, 50%, and 75% band-limited pulse waves, the NES 32-step triangle waveform, and the 2A03's 32,767-step and 93-step noise LFSRs with all 16 timer periods. It includes 16-voice polyphony, velocity, attack/release, tuning, portamento, a pitch-bend range of up to 48 semitones, automation, and project-state persistence.

Expansion-audio oscillator models include VRC6 pulse and saw, Famicom Disk System wavetable, Namco 163 wavetable, VRC7-style two-operator FM, and Sunsoft 5B tone. Their Shape, FM ratio, and FM index parameters are exposed for automation. These are compact musical models of each chip's characteristic synthesis method, not register- or cycle-perfect emulators.

The plug-in also provides NES DPCM kick/snare synthesis, Game Boy pulse/wave/noise, Master System tone/noise, Genesis PSG, and a register-driven four-operator Genesis FM model through ymfm. The console stack modes route MIDI channels to hardware-style channels:

  • NES channels 1–5: pulse 1, pulse 2, triangle, noise, DPCM
  • Game Boy channels 1–4: pulse 1, pulse 2, wave, noise
  • SMS channels 1–4: tone 1, tone 2, tone 3, noise
  • Genesis channels 1–6: FM; 7–9: PSG tone; 10: PSG noise

Genesis FM voices now use the pinned BSD-3-Clause ymfm YM2612 core through real chip-register writes rather than YANES's earlier sine-network approximation. Pitch uses block/F-number quantization, and instrument setup drives operator multiplier, total level, key scale, attack, decay, sustain, release, AM, hardware LFO, algorithm, feedback, stereo, and key-on registers. The core runs at its native rate and is converted to the host rate. OPL2 and OPL3 modes now use the same register-driven path, including hardware frequency numbers, key-on, operator envelopes, waveforms, key scaling, channel connection, feedback, and OPL3 output routing. YM2203 OPN, YM2608 OPNA, and YM2151 OPM modes also use their corresponding ymfm devices and native clock rates. A neutral LFO initialization avoids the invalid maximum-PM startup trace caught by the non-silence regression test.

The FM operator section exposes attack, decay, sustain rate, sustain level, release, detune, key scaling, LFO rate, AM depth, and PM depth. Values are translated to each selected chip's actual register widths; unsupported combinations such as OPL detune are intentionally ignored rather than simulated outside the chip.

yanes-register-render renders timestamped hexadecimal register writes through ymfm at native YM2203, YM2608, YM2612, YM2151, YM3812, or YMF262 rates. This provides a deterministic bridge for comparing YANES against register logs and audio exported by Furnace without incorporating Furnace's GPL engine. yanes-audio-compare checks two 16-bit PCM renders and reports normalized correlation and RMS error. It resamples differing source rates and also reports 1024-frame energy-envelope correlation, which is stable across different chip-core phase and resampling implementations. An optional minimum envelope-correlation threshold is suitable for CI.

advertised_tests holds one proof per parameter (all 334) and one per preset (all 145) that it does what it advertises, measured from rendered audio in the advertised unit: Attack and Release in milliseconds, gains in dB, Transpose/Fine tune/bend/vibrato/sweep in semitones and cents, rates in Hz or steps per second, pulse duty in percent, FM index against the Bessel sideband ratio, table position against the sine/triangle/saw/square harmonic series, echo delay and feedback, DPCM playback against the 2A03 DMC period table, and so on. Each preset must select the chip its name says, have the character its name says (a bass sounds an octave or more down, a bell decays, a pad sustains, an echo repeats, a zap sweeps, a stack plays every one of its channels), and every setting its recipe makes must be audible in its sound. The tables are indexed by ID and sized to the parameter and preset lists, so the suite fails if a parameter or preset has no proof, no voice claim, or no character claim.

The 39-chip audio suite (tools/compare_furnace_audio.sh, fixtures under tests/furnace/) uses yanes-parity-compare: onset alignment, log-band spectrum, octave-folded YIN pitch, and envelope shape. A fixture only selects a voice and plays a note — duty, wavetable, noise settings, FM ratio and release all come from the plugin's own per-voice defaults, so a passing row means the sound a user gets from that voice matches the chip, not that the engine could be talked into it. Each of the 39 fixtures is also rendered an octave up and down as an untuned holdout; all 113 registered cases pass. yanes-parity-compare --self-test runs as its own CTest to keep the gate from drifting into something a wrong render could satisfy. Every fixture is also scored by envelope_oracle, which compares the two amplitude envelopes in dB (attack, decay, release and note end); its --self-test plants eight defects (a missing attack, a 3x slow attack, a missing decay, a 1.5x release, an over-long release, an ignored release, a note that never ends and a level far off) and requires each to fail, alongside six controls that must pass. Packaged Furnace 0.6.8.3 cannot load these INF2 modules; configure -DYANES_FURNACE_EXECUTABLE= to a git Furnace binary (CI builds the commit pinned in .github/workflows/ci.yml). With REAPER installed the same fixtures are also rendered through REAPER as a real CLAP host (reaper_envelope_oracle). See FURNACE_PARITY.md.

tools/compare_furnace.sh CHIP module.fur [minimum-correlation] performs the complete external-oracle workflow: Furnace per-system WAV and VGM export, register extraction, native ymfm replay, and aligned audio comparison. CHIP is one of ym2203, ym2608, ym2612, ym2151, ym3812, or ymf262. It uses temporary files and does not copy Furnace modules or audio into YANES. The bundled Furnace Equinox Intro demo produced 61,323 YM2612 writes and an envelope correlation of 0.940756 in the development environment; this observation is deliberately not a hardcoded universal threshold because Furnace core selection and module features can differ.

The Game Boy gates use a patched SameBoy checkout to run a .gb/.gbc image and capture both its reference audio and every APU register write. Configure with -DYANES_SAMEBOY_SRC=/path/to/SameBoy (CI does) and CMake builds yanes-gb-oracle and adds gb_envelope_oracle: a ROM generated in-tree (yanes-gb-envelope-rom, booted with a stub, so no commercial or Nintendo boot ROM is involved) plays every channel's envelope, length, sweep and volume behaviour, and each channel must match SameBoy in envelope shape, level (1.5 dB) and timbre. For a real game, run tools/test_gb_rom_parity.sh /path/to/game.gb (or build the capture side by hand with SAMEBOY_SRC=/path/to/SameBoy tools/build_gb_oracle.sh). yanes-gb-replay independently decodes that register stream through YANES's pulse, wave-RAM, and LFSR primitives and emits a mix plus four isolated channels, so the gate identifies the diverging voice rather than hiding it in a mix.

gb_wave_preset_parity (registered with the SameBoy lanes) covers the Game Boy wave set. For each preset, yanes-gb-wave-preset-parity checks the preset holds its game's 32 samples exactly, renders it holding C-4, and writes a script ROM that plays the same bytes on CH3 at the same register period; SameBoy's render of that ROM must match the preset in envelope, pitch and timbre (yanes-parity-compare, 0.9) and in each of the first 16 harmonics to within 1.5 dB. The required, small SameBoy instrumentation is in third_party/sameboy-apu-register-log.patch; no emulator source is incorporated into YANES.

The PC Engine equivalent uses tools/build_libretro_host.sh and a Beetle PCE checkout patched with third_party/beetle-pce-psg-register-log.patch. Run tools/test_pce_rom_parity.sh game.pce; the core's frame-local timestamps are paired with the frontend frame index, then yanes-pce-replay reconstructs all six HuC6280 channels from the real ROM's exact frequency, balance, wave-RAM, DDA, noise, and channel-1-to-channel-0 LFO writes. Register events retain sub-sample timing, and the replay models the original HuC6280's unipolar DAC, ultrasonic wave accumulator, and zero-divider noise special case. ROM comparisons use the comparator's rom mode: energy contour and spectrum remain mandatory, while single-note YIN and boundary-onset rules (undefined for polyphonic continuous music) are deliberately omitted.

NES uses the same libretro host with a Nestopia checkout patched by third_party/nestopia-apu-register-log.patch. Build it with NESTOPIA_SRC=/path/to/nestopia tools/build_nes_oracle.sh, then run tools/test_nes_rom_parity.sh game.nes; yanes-nes-replay cycle-steps the captured 2A03 pulse, triangle, noise, DMC, frame-counter, length, sweep, and envelope state before applying the published nonlinear mixer curves.

The Master System lane uses a Genesis Plus GX checkout patched with third_party/genesis-plus-gx-psg-register-log.patch: tools/test_sms_rom_parity.sh game.sms logs every SN76489 write while the core renders the game, and yanes-sms-replay plays that register stream through YANES's SN76489 model (attenuators start silent, as the chip powers up). Games that lean on the FM add-on are not pure-PSG comparisons and are left out.

All four real-ROM lanes can be enrolled in CTest with -DYANES_GB_ROM=..., -DYANES_PCE_ROM=..., -DYANES_NES_ROM=..., and -DYANES_SMS_ROM=.... The ROMs and patched cores stay external. The scripts reject silent captures and compare a smoothed musical-energy contour plus log-band spectrum, avoiding a misleading raw-wave correlation between emulators with different analog filters and reset phase.

tools/test_real_roms.sh /path/to/roms /path/to/build runs the 29-game regression matrix from nes/, gb/, pcengine/, and sms/ subdirectories. Build the replay tools, tools/build_gb_oracle.sh /path/to/build/yanes-gb-oracle, and tools/build_libretro_host.sh /path/to/build/yanes-libretro-host first. Missing ROMs count as failures. The runner records the later Start presses needed by Super Mario Land and Battletoads, and the Start/A sequence that registers a name in Zelda; an idle title or name-entry screen is not an audio parity test. Individual NES runs can set YANES_AUTO_A_FRAMES alongside YANES_AUTO_START_FRAMES to reproduce that input sequence.

The Game Boy oracle seeds power-on RAM identically for the mix and isolated passes. The gate requires all five register logs to match byte for byte before scoring audio, so different game execution cannot masquerade as a channel defect.

POKEY modes now clock distinct 4-, 5-, 9-, and 17-bit polynomial generators and provide eight AUDC-style tone/noise gating combinations. Fast clock selection and strict-mode 16-bit channel pairing expand the earlier single-LFSR model. SID modes now combine quantized 12-bit triangle, saw, pulse, and noise sources before filtering; the 6581 path adds reduced combined-waveform level, neighboring-bit bleed, a nonlinear cutoff curve, and stronger saturation, while the 8580 path is cleaner and more linear. These SID changes are clean-room approximations, not reSID integration.

Hardware controls include NTSC/PAL clocks, timer pitch quantization, a 15-step envelope, pitch sweep, and tracker-like arpeggio sequences. The optional console/TV section supplies adjustable sample-rate and bit-depth reduction, RF noise, 50/60 Hz hum, coupling/high-frequency filtering, speaker drive, and stereo width. Set Retro amount to zero for the clean chip output.

The Preset parameter supplies clean NES, chord lead, DPCM kit, Game Boy, SMS, Genesis FM, bedroom-CRT, and noisy-RF starting points without requiring a plugin-owned window.

The computer/arcade chip lab adds AY-3-8910/SSG tone, noise, and tone + noise, Atari POKEY tone and 17-bit polynomial noise, PC Engine 32-sample/5-bit wavetable sound, OPL2 two-operator FM, OPL3 four-operator FM, OPN/OPNA, and OPM. Stack modes provide useful channel layouts for PC-88, PC-98, X68000, Atari, PC Engine, and Sound Blaster OPL3. Yamaha FM modes (YM2612, OPL2, OPL3, OPN/OPNA, OPM) write registers through ymfm. The remaining chip names are compact musical models of each synthesis method, not register- or cycle-perfect emulators.

The PC Engine stack routes channels 1–4 to wavetable voices and channels 5–6 to its 18-bit noise generator. The PCE page gives each of the six channels its own wave RAM: a Wave picker chooses Default (the Shape-knob wave, or noise on channels 5–6), Drawn (that channel's 32 five-bit samples, painted in the lane below it), or one of fourteen built-in 5-bit tables (sine, triangle, saw, square, 25% and 12.5% pulse, organ, hollow, bell, sloped square, soft saw, half sine, double saw, buzz). Any picker but Default turns channels 5–6 into wavetable channels. The solo PC Engine wavetable voice plays channel 1's picker. AY-3-8910 tone + noise is the AY mixer with both enabled on one channel: the square only gets through while the shared 17-bit noise generator is high, and Noise period sets that generator's register. PC-98 routes channels 1–6 to FM, 7–9 to SSG, 10–15 to synthesized rhythm (kick, snare, tom, hats, cymbal), and 16 to the DPCM/sample bank used as an ADPCM stand-in. X68000 routes channels 1–8 to OPM and channel 9 to the same sample bank. Since MIDI has sixteen channels, the OPL3 stack exposes sixteen simultaneously addressable 2-operator parts rather than all eighteen hardware channels. The dedicated OPL3 four-operator mode programs a true 4-operator channel pair.

Four additional families are included because they add synthesis methods not already covered:

  • Commodore SID 6581/8580 combines saw/pulse synthesis with revision-dependent resonant nonlinear filtering. Chip cutoff and Chip resonance control that filter.
  • Konami SCC supplies five channels of 32-sample, 8-bit wavetable sound.
  • Philips SAA1099 supplies six tone channels, shared noise coloration, and the wide Game Blaster/SAM Coupé character.
  • Atari TIA supplies two channels of deliberately coarse polynomial tones.

A further set covers sample players, one-bit speakers, speech, and the later handhelds:

  • Amiga Paula plays 8-bit samples with no interpolation, each byte held for a whole period of the 3.55 MHz colour clock, through the A500's fixed 4.4 kHz RC lowpass. LED filter (the Noise mode switch) adds the 3.3 kHz Butterworth. It plays the sample bank key-tracked, or one of eight built-in 32-byte loops.
  • ZX Spectrum beeper, PC speaker, and Apple II speaker are one-bit: every voice meets at a single pin. The Spectrum ORs pin-pulse engines' fixed-length pulses together (Pulse duty sets 32, 64, 128 or 256 ticks of the engine's 895 kHz loop; the length is its only volume, so the envelope narrows it), the Apple II toggles the speaker for either voice and its cone relaxes within half a millisecond, and the PC speaker plays one PIT square at a time, arpeggiating chords at 60 Hz. Each machine's pitch is quantised to its own loop or divider.
  • TMS5220 LPC speech excites a ten-stage lattice with the chirp at 8 kHz. Its reflection coefficients are derived from vowel formants and quantised to the chip's 5/5/4/4/4/4/4/3/3/3 bits. Vowel sweeps oo–oh–ah–eh–ee and Talk rate makes it talk.
  • OKI MSM6295 ADPCM runs everything through 4-bit ADPCM at a fixed 7575 Hz, so nothing changes pitch: a loaded bank slot plays at its own speed, otherwise keys play a built-in arcade kit (nine drums and three LPC shouts), or shouts on every key with Kit layout above 0. Sounds play to their end like the chip's.
  • MSX YM2413 is the real OPLL through ymfm. OPLL instrument picks one of the fifteen ROM patches, or 0 for the user patch built from the FM controls. The MSX-MUSIC stack routes channels 1–6 to YM2413 melody, 7–11 to its rhythm section (bass drum, snare, tom, top cymbal, hi-hat; the played note tunes each drum), and 12–14 to the AY PSG. F-numbers follow MSX drivers' one-octave table (about 172–343 per block), which the metallic cymbal and hi-hat depend on.
  • Atari Lynx clocks a 12-bit LFSR whose tap set (LFSR taps) decides whether it loops as a buzzy tone or runs as noise, from Mikey's 1 MHz timer. Integrator accumulates the bits.
  • GBA DirectSound steps through 8-bit samples at the 13379 Hz software-mixer rate with no interpolation, and changes volume only once per video frame. It plays the bank or a built-in loop. The GBA six-channel stack adds the Game Boy's four PSG channels (1–4) to DirectSound A and B (5–6), and everything leaves through the GBA's 9-bit, 32768 Hz DAC.
  • Capcom QSound is the CPS-2 arcade board's sample DSP (Street Fighter Alpha, Darkstalkers, Marvel vs. Capcom). It steps 8-bit samples at its fixed 24038 Hz output rate with no interpolation, from a rate register with 1/4096-sample resolution, and mixes into one 16-bit word per output sample. It plays the bank or one of eight built-in instrument loops (Built-in loop: brass, strings, electric piano, slap bass, choir, reed, orchestra hit, pulse lead). Everything then goes through the chip's power-on Q1 output stage at centre pan: each side hears the dry signal plus a negative, lowpassed copy a few samples later, which halves the bass and spreads the two sides (the right runs two samples behind). Retro and rack effects process the mid of the two sides; their difference rejoins after. The chip's own echo is left to the echo controls. qsound_parity checks the voice and the Q1 stage against superctr's DL-1425 emulator (see FURNACE_PARITY.md).
  • Virtual Boy VSU (6-bit) and WonderSwan (4-bit) play 32-step wavetables from their 11-bit frequency registers, and each has a noise voice: a 15-bit LFSR with eight selectable taps (Noise tap), stepped from the frequency register. The Virtual Boy six-channel stack is five wavetable channels and noise; channel 5 runs the VSU's modulation table (Ch 5 mod depth, the FM index control). The WonderSwan four-channel stack is wave, wave or PCM voice (channel 2 plays its bank slot as 8-bit voice samples when one is loaded), wave with the hardware sweep (channel 3: Sweep depth is the register step, Sweep time its period), and noise.
  • AY-3-8910 envelope buzzer runs the AY's volume envelope at audio rate through its log DAC: falling or rising saws and triangles, optionally gated by the tone channel (in sync, an octave up, or slightly detuned).
  • Slap bass models a thumbed or popped electric bass string: stretched partials whose overtones die long before the fundamental, plus the fret click. Slap / pop sets how hard it is struck.

Original retro-digital synthesis

Nine additional methods broaden the instrument without copying factory ROMs or commercial presets:

  • Morphing wavetable moves continuously through sine-, triangle-, saw-, and pulse-derived tables. Table Position selects the region and Table Warp bends phase distribution.
  • Phase distortion reshapes oscillator phase around a movable breakpoint for sharp brass, hollow reed, and resonant mid-1980s digital timbres.
  • Harmonic additive sums twelve partials. Harmonic Tilt controls spectral rolloff and Table Position balances odd and even harmonics.
  • Six-operator FM supplies a compact 32-routing family with modulation index and brightness. It follows the six-operator/32-algorithm concept without reproducing factory voices.
  • Digital partial pair layers a generated transient with morphing-table and additive sustain components for late-1980s digital/analog-style patches.
  • SNES Gaussian sample models the S-DSP's soft, high-rolled-off tone with a built-in Gaussian character; Sample Tone opens it from a warm sub-flute to a brighter pluck.
  • Neo Geo FM bell rings with an irrational modulator ratio and a decaying index, like struck arcade-FM metal, while Arcade feedback FM makes the spiky-yet-rounded wave a self-feedback operator produces.
  • Vowel formant wavetable sweeps the two lowest vocal formants across "ah", "eh" and "ee" for a talking, choir-like colour; Table Warp widens or narrows the formant bandwidth.

Original presets include Vector Wavetable Pad, Phase-Distortion Brass, Additive Drawbars, Six-Operator Electric Piano, Digital Partial Strings, Neo Geo FM Bell, Arcade Spike Lead, Vowel Formant Pad, Theremin (a near-sine additive voice that glides between notes and adds hand vibrato after a quarter second), and Crystal (a custom wavetable of a sine stepped by a square at each half-cycle, struck and fading like a bell).

Six era-inspired keyboard modes extend that original section without copying factory ROMs or patch data: a deliberately compact two-operator Porta FM voice, detuned vintage analog poly, envelope-swept matrix brass, quantized early-digital ensemble, electromechanical tine model, and nonlinear ladder-style mono synth. The Porta FM mode follows the broad low-cost OPLL/portable- keyboard synthesis approach but is not an emulation of a specific PSR model. Matching original presets provide portable FM piano and organ, Japanese analog poly, American matrix brass, early sampler choir, suitcase tine piano, and classic ladder bass starting points.

The Retro Chip Drum Kit maps twelve synthesized percussion voices from MIDI note 36 through 47: swept sine kick, LFSR snare, PSG tom, metallic FM tom, closed and open noise hats, multi-burst clap, SID-style zap, POKEY metallic hit, dual-square cowbell, TIA click, and arcade noise burst. These are generated by the engine without sample ROMs. Shape changes their global character, pitch controls transpose the kit, velocity controls level, and every hit decays automatically for DAW piano-roll use.

Performance, layering, and retro rack

The Layer control can add an octave, fifth, sub-octave, stepped triangle, or noise component to any chip or synthesis mode. Layer Mix keeps the recipe usable as either subtle reinforcement or an obvious fake multi-channel stack. Vibrato supports direct automation, MIDI modulation wheel, and Bitwig modulation. MIDI pitch bend and mod wheel act independently on each channel; the bend range defaults to two semitones and is adjustable up to 48. CLAP note expressions provide sample-accurate per-note tuning, volume, brightness, and pressure. Bitwig transport tempo can sync the arpeggiator and echo. Eight programmable pitch steps support tracker-style riffs, and Strict Hardware mode chokes an existing voice when a hardware-stack channel is retriggered.

All 39 continuous parameters accept sample-accurate global CLAP parameter modulation, including pitch, vibrato, FM brightness/feedback, filters, envelopes, and effects. Modulation offsets are added to the current automated base value and clamped to the parameter range; the editor and project state retain the base value. Reset clears transient offsets. Stepped choices remain automatable, and per-note control uses the note expressions listed above.

The internal effects rack contains soft drive, a feedback echo, and stereo modulated chorus. These run alongside the existing console/TV section, allowing a clean chip source, a tracker-like fake echo, a worn stereo digital effect, or the full RF/television treatment without extra devices.

Technique presets demonstrate common chiptune arrangements with original settings: Envelope Bass Trick, Hyper Arpeggio Lead, Duty-Cycle Lead, Fake Echo Lead, Octave Power Bass, and Worn Chorus Pad. They are inspired by general tracker and cartridge-era techniques and contain no game samples or extracted instrument data.

The embedded editor opens at 1600 x 1050 and can be freely resized down to 800 x 525; text, controls, and hit-testing scale with the window. The SIZE button in the header steps through 50%, 60%, 75%, 90%, 100%, and 125% for hosts without a resize handle (click steps smaller and wraps, the wheel goes either way, right-click restores 100%), and the project remembers the size. Seven pages group controls by what you are adjusting — Voice, Sequence, Synth, FM, Hardware, FX + TV, and Custom — in collapsible cards, with the preset selector always in the header. Controls that do nothing for the current sound source stay in place but are dimmed, so the layout never jumps when the source changes.

  • Knobs: drag up or down (Shift for fine), double-click, Ctrl/Cmd-click, or right-click to reset, and use the wheel to nudge.
  • Choices: short lists are segmented buttons; long ones (sound source, preset, arpeggio, layer) open a pop-up list.
  • Step lanes (Sequence page): drag to paint pitch, duty, or cents steps, right-drag to draw a line, right-click a step to reset it, and click or drag the ruler to set how many steps play.
  • Page artwork: a live oscilloscope and spectrum, wavetable and filter previews, FM routing, and the channel mixer and DPCM slot tiles.

Every edit is a complete CLAP begin/value/end gesture, one per parameter touched. Host automation and Bitwig's native panel update the same parameter state and redraw the editor. Page and collapse changes animate briefly and the editor repaints only while something changes; on Linux it draws into a back buffer, so it never shows a half-painted frame. Linux uses X11/Xft (including under XWayland), Windows uses GDI, and macOS uses a flipped Cocoa view. yanes-ui-preview OUTPUT_DIR [WIDTH HEIGHT] renders every page to PNG offscreen for layout review. On Linux, build/frontend_tests --display-review /tmp/yanes-native-review checks the production editor at four sizes with repeated window lifecycles and saves PPM screenshots. It requires an X display; the ordinary CTest frontend checks remain headless. See the native UI and leak results.

The catalogue contains 85 sound sources and 145 factory presets (plus Manual). Every source has a factory starting preset, including the console stacks and noise voices. Recent recipes include the deep-bass set (Moog ladder, Oberheim SEM, Korg resonant, Moog lead), the 1980s synth families (Juno chorus strings, Prophet sync lead, Hoover rave stab, DX slap bass, CS-80 brass swell, SID PWM arp lead), the SNES voices (SNES soft bass, SNES bright sample, SNES sampler), and the Game Boy wave set, and the PC Engine wave-RAM and AY mixer recipes (PC Engine channel waves, PC Engine drawn lead, AY tone-noise rasp), and Capcom QSound brass.

The Game Boy wave set plays wave tables real games loaded into CH3, byte for byte. Each preset is named for its shape, with the game it came from in parentheses: rounded pulse (Pokemon Red), ramp saw, hollow notch and jagged buzz (Pokemon Crystal), square (Tetris Blast), slanted bass (Link's Awakening), double-hump (Kirby's Dream Land 2), thin triangle, bright plateau and shallow sine (Gargoyle's Quest), stepped sine lead and wobble buzz (Mega Man Xtreme), 25% pulse (Final Fantasy Adventure), pulse-train (Metroid II), narrow spike (Wario Land), trapezoid (Mega Man V) and quiet sine (Oracle of Ages). The tables were harvested by running each game under SameBoy (yanes-gb-wave-harvest logs every APU write while a seeded input schedule plays through menus and levels; tools/gb_wave_tables.py ranks the tables CH3 actually sounded). A table made the set only where its harmonic series differs from every other one's by more than 5 dB RMS, so tables that sound alike are folded together: Pokemon Trading Card Game and Donkey Kong Land play the same saw as the existing Game Boy wave preset, and the squares in Link's Awakening, Final Fantasy Adventure, Mega Man Xtreme and Tetris DX all match Tetris Blast's. Two plain shapes no game table came near stay as Game Boy wave sine bass and Game Boy wave organ. Choose Custom wavetable directly in the sound-source menu, or use the Custom wave switch to override another source. The drawing is always active when Custom wavetable itself is selected.

The Custom page lets you draw a repeating 32-sample, 4-bit waveform, matching Game Boy wave-RAM resolution. Enable Custom wave to replace any selected source's oscillator (including every part of a stack) with the drawing. Pitch, envelopes, layers, mixer and effects still apply; source-specific synthesis controls are dimmed. Drag to paint, right-drag for a straight line, and right-click a sample to reset it. Fast strokes fill intervening samples. The fixed-length ruler labels the samples. Every sample is automatable and saved with the project; older projects load with Custom wave off. Selecting a factory preset resets the drawing and switch. This is a creative oscillator override, not a claim that every original chip supported wave RAM.

The duty sequence steps a pulse voice's duty through up to eight steps on every note, like a tracker duty macro: looping or one-shot, at a free rate or locked to host tempo with the sync division. It applies to NES, Game Boy, and SID pulses and to the NES stack's two pulse channels. The cents sequence works the same way on fine pitch: up to eight steps of ±100 cents for detuned chirps, slides into a note, or a stepped chorus wobble, on every sound source, and it stacks on top of the arpeggio. Vibrato delay holds the vibrato off for up to three seconds after each note starts and then brings it in from zero phase, like a tracker's delayed vibrato; mod-wheel vibrato stays immediate. Pitch bend range sets the wheel's range from 0 to 48 semitones (default 2); selecting a preset keeps it. Parameter display text parses back, so typing a value a host shows (a sound-source name, "50%", "C2 (36)") works.

MIDI CC7 controls volume independently on each channel, including stack parts. The CLAP latency and tail extensions report zero processing latency and a release/echo-dependent tail so offline hosts do not truncate decays.

NES DPCM sample bank

Sample import feeds this one sixteen-slot bank, which two voices read in different ways: the NES DPCM modes play it as the one-bit delta stream below, and the SNES sample bank voice plays the same slots as BRR samples through the S-DSP model described above (a raw .brr loads directly, and an imported WAV is BRR-encoded as well as DPCM-encoded). yanes-dpcm converts a mono or stereo 16-bit PCM WAV to the one-bit delta stream used by the NES DPCM modes:

build/yanes-dpcm input.wav sample.ydmc
YANES_DPCM_BANK="$PWD/kick.ydmc:$PWD/snare.ydmc:$PWD/tom.ydmc" bitwig-studio

(On Windows the list separator is ;, as in PATH.)

WAV input is mixed to mono and converted at 16,744 Hz, the fastest NTSC 2A03 DPCM rate. The plug-in reads WAV or .ydmc bank entries during initialization, never on the audio thread. You can also middle-click a slot on the Hardware page to choose a mono/stereo 16-bit PCM WAV or .ydmc file, right-click it to clear it, and left-click it to toggle looping. Sample replacement uses immutable snapshots, so a sounding voice safely finishes with its original sample while a new voice receives the replacement. Once Bitwig saves the project, all sixteen bank slots are included in CLAP state, so reopening that project does not depend on the environment variable or original files. DPCM Base Key maps consecutive MIDI keys to slots; each file is limited to 1 MiB. Empty slots retain the generated, copyright-free kick/snare fallback. State versions 8 and 9 migrate their former single sample into slot one, while versions 10 through 16 retain all sixteen slots and receive defaults for newer mixer, loop, DAC, trim, and later controls. Version 17 adds the per-slot BRR samples the SNES sample-bank voice plays. Each slot can loop independently through the DPCM Loop Mask, and DPCM Initial Level exposes the 2A03 DAC starting value used before the first delta bit. DPCM Trim Start and Trim End provide normalized, non-destructive start/end boundaries shared by the bank; they are not per-sample loop points or a waveform editor. The Hardware page shows loaded slots in amber and looping slots in green. The graphical file chooser uses zenity on Linux, GetOpenFileName on Windows, and NSOpenPanel on macOS; the environment-variable workflow does not require a chooser.

The Hardware page includes a sixteen-channel stack mixer strip. Left-clicking a channel toggles mute and right-clicking toggles solo, with both masks exposed to Bitwig automation and stored in the project. MIDI sustain pedal, All Sound Off, and All Notes Off are handled for both generated and register-driven voices.

Build

YANES requires CMake 3.20 or newer and a C++20 compiler. Linux additionally requires X11 development headers, Xft, and pkg-config. On Debian or Ubuntu, install the system dependencies with:

sudo apt install build-essential cmake pkg-config libx11-dev libxft-dev

On Arch Linux (or an Arch-based distribution), install the equivalent dependencies with:

sudo pacman -S --needed base-devel cmake pkgconf libx11 libxft git

Then configure, build, and run the test suite:

cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j
ctest --test-dir build --output-on-failure

The same commands work from a Visual Studio developer shell on Windows and from a current Xcode command-line-tools environment on macOS. Use --config Release for a multi-configuration CMake generator. Cross-platform CI and release-promotion details are in docs/CROSS_PLATFORM_RELEASES.md.

When REAPER is installed, CMake registers reaper_clap_integration. The test creates an isolated REAPER profile, instantiates YANES, writes a MIDI drum passage and waveform automation, saves and recalls the project, renders it offline, and validates the WAV:

ctest --test-dir build --output-on-failure -R reaper_clap_integration

CMake downloads the small official CLAP headers and the pinned ymfm source used by the hardware FM models, so the first online configuration requires Git and network access. For an offline build, pass -DCLAP_ROOT=/path/to/clap -DYMFM_ROOT=/path/to/ymfm (a checkout of the commit named in CMakeLists.txt).

Review and CPU measurements

The September 2026 review and its validation limits are recorded in docs/REVIEW_2026-09-26.md. Hardware-FM pitch corrections make YM2612/OPNA play an octave higher and OPM a semitone lower than the buggy earlier implementation, so existing patches using those sources now follow the intended MIDI pitch. Parameter IDs and saved-state migration remain compatible.

The full history and real-ROM follow-up records all 32 commits, retained features, capture corrections, CLAP modulation, and the additive oscillator and published-ID checks.

build/yanes-benchmark build/YANES.clap prints per-source CPU measurements at 48 kHz, 128-frame blocks, and one or sixteen active MIDI voices. It excludes activation, note setup and warm-up; it is an informational throughput measurement, not a DAW dropout guarantee.

The nes_preset_gate test scans NES_ROM_DIR (default /mnt/crucial/roms/nes) and selects a filename-derived score for comparison against a separate local APU model. It restores the tonal 0.95 envelope/spectrum/pitch and onset/offset gate, prints audio comparison diagnostics, and saves rom_song.rpp, rom_extracted.wav, and yanes_extracted.wav under build/reaper_projects/. It does not run a ROM or extract game music. The directory-backed gate remains enabled when the ROM directory exists; CTest reports it as skipped when that optional directory is absent, so CI does not require private ROMs. An additional nes_oscillator_gate always runs a generated score and rejects deliberately wrong oscillator, duty, and octave renders at the unchanged 0.95 composite threshold. It does not replace or weaken the directory-backed gate or the real-ROM tests. CTest stores the REAPER/WAV exports in the configured build directory's reaper_projects/ folder. Actual ROM capture/replay remains in the optional *_rom_parity gates described above. Full ROM mixes use rom-mix comparison, which rejects silence without requiring ffmpeg; rom permits silent isolated channels that an excerpt does not use.

Install

Local build and install

After building from source, copy the compiled YANES.clap plug-in to your platform's standard CLAP directory:

Platform Built file location Copy destination
Linux build/YANES.clap ~/.clap/ (or /usr/lib/clap/)
Windows build\Release\YANES.clap C:\Program Files\Common Files\CLAP\
macOS build/YANES.clap (bundle) ~/Library/Audio/Plug-Ins/CLAP/

Linux:

mkdir -p ~/.clap
cp build/YANES.clap ~/.clap/

(Or system-wide: sudo cp build/YANES.clap /usr/lib/clap/)

macOS:

mkdir -p ~/Library/Audio/Plug-Ins/CLAP
cp -R build/YANES.clap ~/Library/Audio/Plug-Ins/CLAP/

(Note: on macOS, YANES.clap is a bundle directory, so copy recursively with -R. When using multi-configuration generators like Xcode, the bundle is located in build/Release/YANES.clap.)

Windows (PowerShell):

New-Item -ItemType Directory -Force -Path "$env:CommonProgramFiles\CLAP"
Copy-Item build\Release\YANES.clap "$env:CommonProgramFiles\CLAP\"

(Or from Command Prompt: if not exist "%COMMONPROGRAMFILES%\CLAP" mkdir "%COMMONPROGRAMFILES%\CLAP" followed by copy build\Release\YANES.clap "%COMMONPROGRAMFILES%\CLAP\")

Companion CLI tools (optional)

The build also creates command-line utilities (yanes-dpcm, yanes-register-render, yanes-audio-compare, yanes-parity-compare, yanes-vgm-extract, the four yanes-*-replay tools and yanes-compare-furnace). You can install them into your local PATH using CMake:

cmake --install build --prefix ~/.local

(Or system-wide: sudo cmake --install build --prefix /usr/local. The plug-in itself installs to <prefix>/lib/clap, so --prefix /usr places it in the standard /usr/lib/clap.)

Prebuilt binary

Download a prebuilt package from the Releases page:

Platform Package Copy YANES.clap to
Linux x86-64 YANES-<version>-linux-x64.tar.gz ~/.clap/ (or /usr/lib/clap/)
Windows x64 YANES-<version>-windows-x64.zip C:\Program Files\Common Files\CLAP\
macOS (Apple Silicon and Intel) YANES-<version>-macos-universal.zip ~/Library/Audio/Plug-Ins/CLAP/

On Linux, for example:

tar xzf YANES-*-linux-x64.tar.gz
mkdir -p ~/.clap
cp YANES-*-linux-x64/YANES.clap ~/.clap/

The Linux build needs only the X11 and Xft libraries that every desktop distribution ships. The Windows and macOS release packages are unsigned; if macOS refuses to load a downloaded plug-in, clear its quarantine flag:

xattr -dr com.apple.quarantine ~/Library/Audio/Plug-Ins/CLAP/YANES.clap

Pushing a v* tag (or a bare version tag) runs .github/workflows/release.yml, which builds and tests every platform and publishes the packages, named by the version without the v, together with a SHA256SUMS.txt of their checksums.

DAW setup

After installing, rescan plug-ins in your DAW:

  • Bitwig Studio: Open Settings > Locations > Plug-in Locations and click Rescan Plug-ins (or restart Bitwig).
  • REAPER: Open Preferences > Plug-ins > VST/CLAP and click Re-scan.

Then insert YANES onto a track from your host's instrument browser.

Scope and provenance

This project implements published chip behavior using clean-room code. The built-in DPCM drums are generated at runtime and contain no samples from commercial games. Imported material remains the user's responsibility.

License

This project is licensed under the YANES Source-Available License.

  • Personal & Evaluation Use: Free to view, compile, and use for personal, non-commercial, or evaluation purposes with attribution.
  • Notification: Users redistributing or adapting this project for public release must notify the author.
  • Commercial Use & Licensing: Commercial use, embedding, or commercial redistribution requires explicit prior authorization from the author. The author reserves the right to deny permission or require a negotiated licensing fee.

About

Yet Another NES Audio Plugin, a CLAP synth for chiptunes and retro sounds

Resources

Stars

4 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages