Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Last updated: 2026-06-03

---

## Unreleased - 2026-06-03
## Unreleased - 2026-08-02

### Breaking changes

Expand All @@ -30,6 +30,7 @@ Last updated: 2026-06-03
- MMC5 also now has proper nonlinear mixing.
- Port VRC6 to new CSoundChip and NSFPlay emulation core (@Gumball2415 @eulyderg #325 #417)
- VRC6 sawtooth volume meter reads the register value rather than the actual output, the only practical difference being that the meter now displays volume level 1 correctly.
- N163 DC drifting fixed (@Nemo55aa #423)

### Bug fixes

Expand Down
20 changes: 19 additions & 1 deletion Source/Blip_Buffer/Blip_Buffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,10 @@ class Blip_Buffer {
public:
int last_amp = 0;
double delta_factor;
// Rounding error carried over from the previous delta. Without it, quantizing
// each delta independently breaks the telescoping property (sum of deltas ==
// amplitude) and lets a DC offset accumulate without bound.
mutable double delta_residual = 0.0;

void volume_unit( double );
Blip_Synth_Fast_();
Expand All @@ -182,6 +186,8 @@ class Blip_Buffer {
public:
int last_amp = 0;
double delta_factor;
// See Blip_Synth_Fast_::delta_residual.
mutable double delta_residual = 0.0;

void volume_unit( double );
Blip_Synth_( short* impulses, int width );
Expand Down Expand Up @@ -213,7 +219,7 @@ class Blip_Synth {
// Configure low-pass filter (see blip_buffer.txt)
void treble_eq( blip_eq_t const& eq ) { impl.treble_eq( eq ); }

void clear() { impl.last_amp = 0; }
void clear() { impl.last_amp = 0; impl.delta_residual = 0.0; }

/// Set the last-seen amplitude to `dc_amp` without outputting a step.
/// If Blip_Buffer currently has output level 0,
Expand Down Expand Up @@ -372,7 +378,19 @@ inline void Blip_Synth<quality>::offset_resampled( blip_resampled_time_t time,
// Fails if time is beyond end of Blip_Buffer, due to a bug in caller code or the
// need for a longer buffer as set by set_sample_rate().
assert( (blip_long) (time >> BLIP_BUFFER_ACCURACY) < blip_buf->buffer_size_ );

#if 0 // Nemo55aa 260802
delta = (int)((double)delta * impl.delta_factor);
#else
// Quantize with error feedback. Truncating each delta toward zero on its own
// biases the error against the sign of the delta, which on an asymmetric waveform
// integrates into an unbounded DC offset. Carrying the residual keeps the total
// error below one LSB forever.
double const scaled = (double)delta * impl.delta_factor + impl.delta_residual;
delta = (int) (scaled < 0 ? scaled - 0.5 : scaled + 0.5);
impl.delta_residual = scaled - (double)delta;
#endif // end Nemo55aa 260802

blip_long* BLIP_RESTRICT buf = blip_buf->buffer_ + (time >> BLIP_BUFFER_ACCURACY);
int phase = (int) (time >> (BLIP_BUFFER_ACCURACY - BLIP_PHASE_BITS) & (blip_res - 1));

Expand Down