Yes, you'd get a bit of a 50/60 Hz buzz when implementing the technique I suggested at face value, but wouldn't the square and noise channels overwhelm it in practice?
Well yeah.. if they're playing they'll dominate the sound, but the buzz would still be there... and even audible in many cases.
Or could it do the DC correction buzz in sub-frame, sub-DAC-quantum steps?
Not sure I follow.
I thought of just highpassing the DAC, but then you wouldn't get the nonlinearity in the signal, which some games use to control the volume of the triangle and noise channels.
Yeah you would... that's the beauty. Internally... the DMC is set to the value written... so everything it affects (including tri/noise volumes) is affected like normal. The only difference is the one sample generated at the time of the $4011 write needs to be adjusted.
When calculating the difference in sample output for your filter... you'd normally do something like:
dif = new_output - prev_output;
Instead, you could do:
dif = new_output - prev_output - dmc_adjust;
where 'dmc_adjust' is the output of the value written to $4011. So if $4011 was written with $7F... 'dmc_adjust' would be whatever value you output when the DMC is at level $7F. For all other times, dmc_adjust would be zero.
So for all purposes... the write to $4011 takes effect immediatly... and all the sideeffects and whatnot that come with the DMC's output being at a certain value take effect immediatly. The only difference is the pop isn't audible.
For example... say an NSF player inits with $00 at $4011, but the NSF writes to $7F first frame, then $7E second frame. Ordinarily it would output:
00->127->126 (00->127 creating a pop)
But working with differences and doing what I described, you'd output:
But as far as the NSF is concerned... the value written takes effect immediately.