Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix wrong waveform and random crackles in N163 audio #294

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from

Commits on Sep 15, 2024

  1. Properly initialize Namco163Audio

    Fixes first note with Zxx playing with wrong waveform.
    nyanpasu64 committed Sep 15, 2024
    Configuration menu
    Copy the full SHA
    d838080 View commit details
    Browse the repository at this point in the history

Commits on Sep 16, 2024

  1. Fix random crackles in N163 playback and WAV export

    ### Background
    
    N163 playback, like other chips, uses `Blip_Synth` and `Blip_Buffer` for
    resampling. Each sound chip has its own Blip_Synth, with an independent
    volume control and treble filter (a set of impulse responses).
    
    Chip emulators create audio by calling `Blip_Synth::update
    (time_clocks, amplitude)`. Blip_Synth calculates `delta = amplitude -
    previous amplitude`, then inserts an impulse of size `delta` into a
    `Blip_Buffer`. `Blip_Synth` performs resampling by converting
    `time_clocks` into fractional samples, then adding a fractionally
    delayed impulse to Blip_Buffer's difference array starting at `int
    (sample timestamp)`. (This introduces a few samples of delay, from the
    impulse start to center point.)
    
    Each frame lasts an integer number of clocks, but a fractional number of
    samples. At the end of a frame, `Blip_Buffer::read_samples()` finds the
    sample where the next frame begins, generates audio up to that sample
    using a running sum of Blip_Buffer's difference array (with optional
    highpass DC removal), then drops the returned samples (keeping the
    fractional part of completed samples, so the next frame's impulses
    begin midway through the new first sample of the buffer).
    
    ### Bug
    
    Dn-FT outputs audio from a global Blip_Buffer, `CMixer::BlipBuffer`. In
    order to apply (approximately emulated) audio filtering, `CN163` has
    its own Blip_Buffer, `CN163::m_BlipN163`.
    
    When we begin playback, we call `CMixer::ClearBuffer()` and
    `CN163::Reset()`, which both reset `Blip_Buffer::offset_` to 0. Due to
    how FamiTracker is designed, this can happen at random points
    mid-frame (rather than on boundaries).
    
    The bug in this case was that when FamiTracker called `CN163::Reset
    ()` without ending the previous frame normally, `CN163` failed to clear
    its "time since frame begin" field (`m_iTime`). Normally this would
    result in steps being delayed slightly for the next frame (or at
    extremely low tick rates, writing out of bounds of Blip_Buffer's
    difference array). But CN163 has its own Blip_Buffer, so each
    subsequent frame starts and stops at a different fractional sample than
    the global Blip_Buffer, resulting in different numbers of completed
    samples being generated each frame.
    
    These frames of incorrect length get mixed into the global Blip_Buffer
    when `CN163::EndFrame()` calls `Output.mix_samples_raw()`
    (where `Output` points to `CMixer::BlipBuffer`). Whenever a N163 frame
    doesn't match the length of its corresponding global frame, it can
    produce 1-sample overlaps or gaps at frame borders, resulting in audio
    crackling.
    
    ### Fix
    
    Make `CN163::Reset()` set `m_iTime = 0`, so the first N163 frame adds
    samples at the correct time, has the correct length, and all subsequent
    frames have the correct phase relative to the global Blip_Buffer.
    
    Does CN163 need to have a separate Blip_Buffer? It's complicated. It's
    used to perform a first-order lowpass filter at 12 kHz, but this is
    close enough to Nyquist (at the usual sampling rate of 44-48 kHz) to
    have noticeable frequency warping. I don't know if this is
    hardware-accurate or an attempt to filter out switching noise.
    nyanpasu64 committed Sep 16, 2024
    Configuration menu
    Copy the full SHA
    1caa313 View commit details
    Browse the repository at this point in the history