Commit Graph

14 Commits

Author SHA1 Message Date
d9666c23b4 feat: Hermite resampling, sprite shift registers, controller open bus
Some checks failed
CI / rust (push) Has been cancelled
#3 audio.rs: replace linear interpolation with Catmull-Rom Hermite cubic.
  Stores prev_sample as p0 control point; m1=(p2-p0)/2, m2=(p2-p1)/2
  tangents give continuous first derivative across batch boundaries.

#4 ppu: add per-slot sprite shift registers (spr_shift_lo/hi, spr_x_counter,
  spr_attr_latch). load_sprite_shifters fetches pattern bytes with h-flip at
  dot 1 of each visible scanline. sprite_pixel_from_shifters replaces the
  per-pixel OAM scan; sprite-0 hit detection integrated into the shifter path.

#5 joypad.rs: format_controller_read now preserves bits 1-5,7 as open bus
  (!0x41 mask) instead of zeroing bits 1-4, matching NES hardware behaviour.
2026-03-15 11:30:14 +03:00
c77be7c84b feat(audio): non-linear APU mixing and mapper expansion audio (VRC6, FME-7, Namco163) 2026-03-15 11:17:37 +03:00
d94fbb894b fix(audio): fix DMC loop byte skip, add DC blocker, lazy cpal stream
Some checks failed
CI / rust (push) Has been cancelled
Three audio bugs fixed:

1. DMC loop mode skipped the last byte of each sample iteration.
   provide_dmc_dma_byte() was immediately setting dmc_dma_request on
   loop restart while the sample buffer was still full, causing the
   while-loop in clock_cpu_cycles to service a second DMA immediately
   and overwrite the valid buffer. Per NES hardware spec, the reader
   only fills an empty buffer — the request is now left to clock_dmc
   when the output unit actually empties the buffer into the shift
   register. Fixes intermittent clicking/crackling in games that use
   looped DMC samples (BGM, SFX).

2. Missing DC blocker (high-pass filter) in AudioMixer. The NES APU
   has a capacitor-coupled output stage that blocks DC bias. Without
   it, abrupt channel state changes (length counter expiry, sweep
   mute, triangle period < 2) produce DC steps that manifest as
   audible clicks. Added a one-pole IIR high-pass filter at ~5 Hz
   applied after the existing low-pass filter.

3. cpal stream was opened at application startup with
   BufferSize::Fixed(256), forcing PipeWire/PulseAudio to run the
   entire audio graph at a 5.3 ms quantum. This disrupted other audio
   applications (browsers, media players) even when no ROM was loaded.
   Fixed by: (a) creating the stream lazily on the first push_samples
   call so no device is touched until a ROM is running, and (b)
   switching to BufferSize::Default so the audio server chooses the
   quantum instead of the emulator imposing one. Ring buffer capacity
   increased from 1536 to 4096 samples to absorb larger server quanta.
2026-03-15 10:44:43 +03:00
d8f41bc2c9 fix(apu): correct frame counter timing, add LP filter, mute aliased triangle
- Fix frame counter running at 2× speed: clock_frame_counter now skips
  odd CPU cycles (APU cycle = CPU/2), so envelope, sweep, and length
  counters tick at the correct rate. Fixes sweep-driven whistle in Megaman II.

- Switch audio sampling to per-CPU-cycle granularity in
  run_until_frame_complete_with_audio to eliminate square-wave harmonic
  aliasing caused by sampling only once per instruction.

- Add IIR one-pole low-pass filter (~14 kHz) to AudioMixer to smooth
  abrupt level transitions (crackling) introduced by correct envelope timing.

- Mute triangle channel when timer_period < 2 (≥27 kHz), which aliases
  into the audible range at 48 kHz. Real NES RC circuit removes these
  ultrasonics; emulator must suppress them explicitly.

- Update all APU bus tests to use correct (doubled) CPU cycle counts.
2026-03-15 10:44:43 +03:00
d2be893cfe fix: stabilize desktop audio playback
Some checks failed
CI / rust (push) Has been cancelled
2026-03-13 19:20:33 +03:00
f86e3c2284 docs: add audio output design spec and implementation plan 2026-03-13 16:21:30 +03:00
e113c53fb7 feat(desktop): replace audio stub with cpal backend and volume slider
- CpalAudioSink writes to SPSC ring buffer, cpal callback reads
- Graceful fallback to silent operation on audio device errors
- Volume slider (0-100%) in header bar with speaker icon
- Ring buffer cleared on ROM load and reset
2026-03-13 16:21:25 +03:00
bddc144c27 feat: add lock-free SPSC ring buffer for audio streaming
AtomicU32-based storage avoids unsafe while maintaining SPSC guarantees.
Capacity: 4096 samples (~85ms at 48kHz). Exported from crate root.
2026-03-13 16:21:19 +03:00
5895344f6f feat(mixer): 5-channel APU mixing with linear approximation formula 2026-03-13 16:09:50 +03:00
e63b5783bd feat(apu): add ChannelOutputs struct and channel_outputs() method 2026-03-13 16:08:55 +03:00
49568a582b feat(apu): clock pulse/triangle/noise timers and sequencers 2026-03-13 16:07:57 +03:00
cd0a99a813 feat(apu): add timer/sequencer/LFSR fields for channel output tracking 2026-03-13 16:06:37 +03:00
6f81eb4b08 chore: ignore local worktrees 2026-03-13 15:06:29 +03:00
bdf23de8db Initial commit: NES emulator with GTK4 desktop frontend
Some checks failed
CI / rust (push) Has been cancelled
Full NES emulation: CPU, PPU, APU, 47 mappers, iNES/NES 2.0 parsing.
GTK4 desktop client with HeaderBar, pixel-perfect Cairo rendering,
drag-and-drop ROM loading, and keyboard shortcuts.
187 tests covering core emulation, mappers, and runtime.
2026-03-13 11:48:45 +03:00