wiki wrote:
The mixer receives the current envelope volume except when
* The sequencer output is zero, or
* The sweep unit is silencing the channel, or
* The length counter is zero
- It's odd or I'm not understanding the sound output thing. If the length counter is not zero and the duty cycle value is zero, the mixer does not receive the current volume (envelope seems an exclusive thing, isn't that wrong?). On other side, if the length counter is not zero and the duty cycle is non-zero, the mixer would receive a negative volume, either volume setting or envelope.
- It makes to think that even an high-pass filter won't "recognize" the negative part of the duty cycle, but all zeroes!
Code:
the correctness: (" is a positive sample, _ is a negative sample, - is zero)
----_""____----
the wiki'ed output:
-----""--------
- Do you see my point? The same is valid for all other channels, like noise. I'm not getting correct tunes, as I said previously.
First, you're mixing two things. You've got the way the channels work, and a high-pass filter. These are independent and any problems with one are unrelated to the other. I'd suggest first getting the channel working correctly.
I'm not sure how the Wiki description can be made more clear. The mixer receives the current volume UNLESS ANY OF THE FOLLOWING ARE TRUE: sequencer output is zero, sweep unit is silencing channel, or length counter is zero. In those cases, the mixer receives 0.
- Here's what I understand, in a cleaner way: the mixer receives the current volume, either enveloped or normal setting. If the length counter is zero, the output is zero; else, if the duty sequencer is zero, the output is -volume, else +volume.
- By following the wiki description, the negative part is lost, since the output is zero if the duty sequencer is zero as well. As I said, I may be misunderstanding the high-pass filtering, but my APU output appears to have problems. For my best, generating signed samples for each channel results in a crystal clear sound, with no audible errors, and playing those tunes (MM6 intro) correctly.
here's pseudocode:
Code:
output = volume;
if( decay_is_enabled )
output = decay_volume;
if( length_counter_is_zero )
output = 0;
if( sweep_unit_is_silencing_channel )
output = 0;
if( duty_is_low )
output = 0;
Output( output );
You output 0-F. You never output negative anything. All output is positive.
EDIT: this is true of all channels. The NES does not output signed samples. All of it is unsigned.
Same for VRC6, Sunsoft5B, N106, FDS, MMC5. It's all unsigned.
VRC7 I outputs signed samples though (I'm pretty sure).
As Disch says, there are no negative values. If you want to understand high-pass filtering, I highly recommend writing a separate program to get it working. It can be just a simple main() that generates a tone, applies the filter, then writes to a wave file. That will eliminate everything else as a possible cause, allowing you to focus on the filtering code.
Disch wrote:
You output 0-F. You never output negative anything. All output is positive.
- Yup, I know. The problem is how I'm resampling the sound now with unsigned samples. It's all correct, just something wrong with the new resampling method, which I am considering "high pass".
- To be honest, the negative/positive sample output idea came from the old Nofrendo. It wasn't really an high-pass, but an effective way of "translating" the samples into signed ones. Let's say I'm trying a new exercise now. ^_^;;
Indeed, modifying the APU to generate positive and negative is a way to do high-pass at the source. It eliminates the DC offset, but affects sound in other ways. The Game Boy Advance for example does this for the original GB sound channels, whereas the CGB and DMG aren't signed.
blargg wrote:
Indeed, modifying the APU to generate positive and negative is a way to do high-pass at the source.
But it breaks anything that relies on
not doing the negative-positive, like a "hello" APU demo I wrote a while back. Should I dig that up, fix it to run on a PowerPak (it was written back when loopynes was considered Teh Shiznit), and then record the output from my NES?
- I'm lazy to record from my NES, but I will do eventually... ^_^;;
- Anyway, yes, the problem is how I'm resampling the sound.
Do you know another method of resampling, instead of adding all the generated samples (1 per CPU cycle) and DIV by the number of updates?
Zepper wrote:
Do you know another method of resampling, instead of adding all the generated samples (1 per CPU cycle) and DIV by the number of updates?
Downsampling involves a convolution with a low-pass filter and a decimation. You are currently using a box filter; a windowed sinc filter produces results with less aliasing. But that eats a lot of CPU time, so blargg came up with a different solution:
- Precompute a set of Heaviside step functions delayed by n samples, low-pass filtered, and decimated.
- Take the first difference of the generated samples (δ[t] = y[t] - y[t - 1]); on discrete audio hardware like that in the NES, most of these differences will be 0.
- Convolve each nonzero difference with a step function chosen based on where the difference falls relative to the output sample.
The library to do all this is called
blip-buf. Get it working in a test harness that just plays a single square wave, triangle wave, noise, or DPCM sample before you plug it into RockNES.
- Yes, that's the idea. I used that for triangle, DMC and VRC6 sawtooth for signed samples. It works nicely. The step 3, if I'm not offtopic, is something like output_dac -= output_dac >> 5 or 8. I can see the output using a sound editor.
- Anyway, well... the noise channel is being the problem. The others seem to be ok, but I believe they're not. I don't see a problem of generating square wave samples as -1,+1,-1,-1,-1,-1,-1, making 0 if the length counter is expired.
- Thanks.