Repository navigation
Releases
Release 2.3.0
🚀 What's New in 2.3.0
Gives drums and bass their body back under the NES-derived profiles. Profile value 2 becomes Famicom: the Nes character with the Famicom's output stage, which keeps the low end that the NES 440 Hz high-pass removes. Kick and snare keep the noise period they were authored with, and a new triangle kick brings the classic NES "thump". Legacy and Nes output stays byte-identical.
🎛️ Famicom Profile
SoundProfile::Famicom: ReplacesHybridat value 2. It has the same character asNes: linear default envelope, ideal saw, the four NES duty patterns, unipolar noise and the NES-weighted mix.- Famicom Output Stage: A single 37 Hz high-pass into the same Butterworth low-pass (min(14 kHz, 0.45 fs)), instead of the NES chain's 90 Hz → 440 Hz → LPF.
- At 65 Hz,
Nesattenuates −21.1 dB andFamicomonly −1.2 dB. - The 440 Hz section is bypassed with a unity pole, so
Nespays nothing extra per sample.
- At 65 Hz,
- Level Trim: Recalibrated with the crest-weighted heuristic: 31765 (−0.27 dB).
Wlands within 0.001 dB ofLegacy, and the RMS is now only 0.31 dB below it, instead of 2.4 dB.
🥁 Drums and Bass
- Kick and Snare Keep Their Body:
INSTR_KICKandINSTR_SNAREuseNoisePeriodMode::Note, so underNes/Famicomthey keep periods 60 and 15 instead of collapsing to the one-sample hiss of NES LUT index 0. - Authentic NES Hi-Hat:
INSTR_HIHATkeeps the profile default and takes NES LUT index 0, the bright hat NES games use. INSTR_KICK_TRI: A new triangle kick. On a TRIANGLE track it sweeps from 196 Hz (G3, the default octave) to 50 Hz in 45 ms, with a short decay. Double it with a NOISE hit on the same beat for the attack.
🔧 Changed
- Linear Default Envelope Everywhere: No profile defaults to the exponential curve any more. A note can still ask for
EnvelopeCurve::Exponential. - Band-Limited Saw Is Dormant: No profile selects the mipmapped saw now.
AudioOscMipLUT.hand its read path are kept until a decision is made: remove it, or expose it as a per-voice option. - Fixtures:
apu_famicom_canonicalandapu_famicom_wavetablereplace theapu_hybrid_*pair.
🧪 Testing & Tools
- New Coverage:
- The Famicom output-stage response at 8 kHz, 22.05 kHz, 44.1 kHz and 48 kHz.
- Low-end attenuation for
NesandFamicom. - Character parity between
NesandFamicom. - Drum periods under every profile.
- The triangle-kick sweep.
test_apu_hybrid_wavetablebecomestest_apu_famicom_saw.- 616 cases across 16 targets.
- Tools:
golden_genandprofiles_abacceptfamicom, withhybridkept as an alias. - Hardware Listening: The engine's
apu_profilesexample was built for esp32dev against this release and compared by ear on the board.
📌 Compatibility Notes
-
HybridIs Deprecated, Not Removed:SoundProfile::Hybridis an alias ofFamicom(same value 2). Existing code compiles with a deprecation warning. -
Profile 2 Sounds Different: Content authored for
Hybridnow gets:- a linear envelope;
- the ideal saw;
- the 37 Hz output stage;
- a lower trim: +1.90 dB → −0.27 dB.
Use
Nesfor the old NES output stage, or setEnvelopeCurve::Exponentialon a note for the exponential curve. -
Default Output Unchanged for
LegacyandNes: Their fixtures were not regenerated and reproduce byte for byte. -
Not Measured on Boards: Famicom's CPU cost was not measured on hardware. It is expected to match
Nes, because the per-sample work is identical. -
Consumer Pin:
- The engine's
^2.0.0range already resolves 2.3.0. - Code that names
SoundProfile::FamicomorINSTR_KICK_TRIneeds>= 2.3.0.
- The engine's
Release 2.2.0
🚀 What’s New in 2.2.0
Lets a note keep the noise period and the continuous pulse duty it authored, even when the Nes or Hybrid sound profile would otherwise overwrite them. Curated percussion and custom duty values stay expressible under any profile, and the default output remains byte-identical.
🔊 Note-Pinned Character
- Noise Period Under NES Profiles: Adds
InstrumentPreset::noisePeriodModewithNoisePeriodMode::Note.NesandHybridarm the NES period LUT, which used to retune a noise voice to a one-sample clock; a note can now keep its ownnoisePeriod, so a kick authored with a 60-sample body stays a kick instead of becoming white hiss. - Continuous Pulse Duty: Adds
InstrumentPreset::pulseDutyModewithPulseDutyMode::Continuous. A static duty is no longer snapped to one of the four NES patterns, so values a caller can already express, such as 30 % or 20 %, survive the profile. - Per Note, Not Per Slot: Both requests resolve on note-on and leave the slot clean, so the next note on that voice gets the profile character again. Neither writes a per-voice override.
🔧 Changed
- Preset Layout:
sizeof(InstrumentPreset)grows from 72 to 76 bytes, entirely from trailing padding.sizeof(AudioChannel),sizeof(AudioCommand),sizeof(AudioEvent)and bothsizeof(ApuCore)figures (13,344 host and 13,368 test build) are unchanged, because the two requests reach the character hook as parameters instead of being stored per voice.
🧪 Testing & Tools
- Character Regression Tests: Adds 13 cases covering the pinned period, the frequency-derived path, no leakage into the next note, a voice pin outranking the note, PULSE-only duty scope and
Legacyinertness.test_apu_coremoves from 210 to 223 cases. - Default-Path Matrix: A 15-scenario comparison against 2.1.0, covering three profiles, both duty and noise paths,
nesAccurateand two-note sequences, reproduces identical PCM hashes and identical slot state. - Independent Verification: A separate review of the change reported no blockers; the two defects it found are fixed before this release.
📌 Compatibility Notes
- Default Output Unchanged: With the new fields at their defaults,
Legacy,NesandHybridrender byte-identical PCM. None of the reference fixtures was regenerated. - Source Compatible, Rebuild Required: The fields are appended after
envelopeCurve, so existing 17-value positionalInstrumentPresetliterals keep their meaning. Consumers must rebuild against 2.2.0. - Voice Pins Still Win:
setVoiceNesNoiseLutMode()andsetVoicePulseDutyMode()outrank the note-level request, exactly as they already outrank an animated duty. - Consumer Pin: The engine's
^2.0.0range already resolves 2.2.0 with no manifest change. The Tool Suite needs>= 2.2.0to use the new fields.
Release 2.1.0
🚀 What’s New in 2.1.0
Introduces NES-inspired and Hybrid sound profiles, improving audio character with NES-style mixing, envelopes, pulse duty patterns and output filtering. This release also fixes two integer-rendering issues on no-FPU builds, reduces command queue memory usage and preserves the default Legacy output on FPU builds.
🔊 Sound Profiles
- New Sound Profiles: Adds
Legacy(default),NesandHybrid, selectable at runtime throughApuCore::setSoundProfile().Nesintroduces NES-style mixing and stepped pulse duty, whileHybridadds exponential envelopes and band-limited SAW synthesis. - NES-Style Output: Adds profile-specific filtering and level compensation for a more consistent listening experience, while preserving the original
Legacyrendering behavior. - Consistent Integer Rendering:
NesandHybriduse the same integer core across platforms, producing identical PCM output for equivalent inputs.
🔧 Changed
- Reduced Memory Usage: Lowers the default audio command queue capacity from 128 to 64, reducing
sizeof(ApuCore)on the host ABI from 22,416 to 13,344 bytes. - Configurable Legacy Float Core: Adds
PR32_APU_ENABLE_LEGACY_FLOATto optionally exclude the legacy floating-point renderer on FPU targets, reducing flash usage. - Engine Audio Integration: Updates the recommended render block sizes to 512 samples on FPU builds and 1024 on no-FPU builds, reducing processing wake-ups at the cost of increased command latency.
🐛 Fixed
- Integer DC Blocking: Fixes a persistent negative DC offset after voices are silenced on no-FPU builds.
- 100% Pulse Duty: Fixes pulse waves rendering as silence at
duty = 1.0on the integer rendering path.
🧪 Testing & Tools
- Audio Regression Tests: Adds immutable PCM reference fixtures for the
Nes,Hybridand integer rendering paths. - Audio Profiling Tools: Adds profile comparison, canonical audio rendering and host/firmware benchmarking tools.
📌 Compatibility Notes
Legacyremains the default profile and preserves its existing output on FPU builds.- On ESP32-C3,
NesandHybridare limited to 22,050 Hz with eight voices due to processing constraints. - The reduced command queue capacity may affect applications that depend on more than 63 pending commands.
- Applications using the integer path should revalidate audio levels and regenerate PCM references affected by the DC blocker fix or the new sound profiles.