Well i want to implement sound volume control in my emu.
Im using 16 bit samples and when i take an APU sample i do: sample = GetApuOut() << 8. Then the sample goes to the buffer.
When i studied electronics i remember that there was 2 kind of potentiometers: logaritmic and linear and that logaritmic were better for sound volume.
Anyway my question is: wich is the best way to implement sound volume control?
Volume controls are most often implemented in a linear fashion: SignalOut = SignalIn × volume.
It is very simple, but doesn't particularly match to human perception of sound.
The point behind a logarithmic taper on a potentiometer is to make it such that an equal amount of rotation on the dial will cause an equal increase in perception of loudness.
The logical equivalent is SignalOut = SignalIn × 10^(VolumeInDecibels/20)
lidnariq wrote:
The logical equivalent is SignalOut = SignalIn × 10^(VolumeInDecibels/20)
And how do i get "VolumeInDecibles"??
The common logarithmic volume unit is the decibel (dB). An increase of 20 decibels represents a factor of 100 in power or 10 in amplitude.
Try this:
- In your emulator's GUI, offer a volume control ranging from -36 dB to +12 dB.
- Use this number to calculate an amplitude scale factor.
- Multiply each sample by the amplitude scale factor and then clip it so that it doesn't exceed the limits of sample values (usually -32000 to 32000) before writing it to the buffer. Clipping is necessary only when the amplitude_scale is greater than 1, which happens when volume is set greater than 0 dB.
This formula converts a decibel value to an amplitude scale factor:
Code:
float amplitude_scale = pow(1.122018454, decibelValue);
Volume-in-decibels would be a user-controlled setting.
Thank you very much.
At the biggining i didn't make a gui, i used accelerators for ascii "+" and "-" keys to test and it worked like a charm.
Now i when you click in the window while the emu is running a popup scroll bar appears that lets adjust the valume
I just have one more question:
Am i clipping well doint sample %= 32000; ????
The
% operator in C and several other languages performs wraparound, not clipping. Clipping is intended to
avoid implicit wraparound when writing out-of-range integers to an array. Clipping in C looks like this:
Code:
static inline int clip_sample(int sample_value) {
if (sample_value < -32000) return -32000;
if (sample_value > 32000) return 32000;
return sample_value;
}
You are my salvation Tepples...
And my final question:
When i get a raw nes sample i make: sample <<= 8; Is it well??
Sorry for being too noob, but i don't know to much about 16 bit PCM.
Left shifting by 8 behaves as multiplication by 256: always for unsigned integers, and usually for signed integers. If your input samples range from about -127 to +127, then multiplication by 256 is what you want.
tepples wrote:
If your input samples range from about -127 to +127, then multiplication by 256 is what you want.
No they are not -127 + 127, they are positive samples (unsigned) taken from the APU as is, so what do i do?
What's the current range of the samples from the APU? On the NES, not all channels have equal weight. It looks like you'll have to 1. scale the APU's output, 2. apply a DC killer (gentle high pass filter) to make it a well-behaved signed signal, and 3. apply the volume scaler described above.
The range is the dac 4 bit value taken from apu, since i take the samples every 40 cpu cc i avarage all the output and then divide it by 40 to form a sample.
Wich value should i scale with??
I don't know what what a DC Killer is.
Each of the 5 channels needs its own scale, as
they aren't all the same loudness. It's also better to take the sum of all 40 samples generated by the DAC during each sample period (a box filter), which reduces aliasing somewhat compared to keeping 1 sample and dropping 39.
There is some information here about the volumes of the APU channels and how to mix them:
http://wiki.nesdev.com/w/index.php/APU_MixerIf implementing expansion audio, there is a note about relative mix levels on each expansion audio page on the wiki.
rainwarrior wrote:
There is some information here about the volumes of the APU channels and how to mix them:
http://wiki.nesdev.com/w/index.php/APU_MixerIf implementing expansion audio, there is a note about relative mix levels on each expansion audio page on the wiki.
Tepples highlighted it....
Anyway i read what the wiki what says and im using the LUT for pulse, but always throw me float numbers and < 0... what is the scale factor... what do i do?
I will be more verbose:
Now im using pulses LUT and triangle, noid, dmc LUT.
I mix the sound from the two luts (as the wiki says), but how do i generate a correct 16 bit value for PC dsound sound out??
I have tested multipling by 32000 for expample and i can hear sound, but when i put volume at 100% it sound distorded (and im clippling as tepples says);
The formula on the wiki should be producing a floating point number that ranges from 0 to 1. (Are you using the wrong type?)
Some audio drivers actually accept a value in this range (for example, JACK).
To convert this to something you can directly use, if multiplying by 32767 doesn't work for you, you could adjust the formulae used to multiply the numerators by 32767 ahead of time.
lidnariq wrote:
The formula on the wiki should be producing a floating point number that ranges from 0 to 1. (Are you using the wrong type?)
Some audio drivers actually accept a value in this range (for example, JACK).
To convert this to something you can directly use, if multiplying by 32767 doesn't work for you, you could adjust the formulae used to multiply the numerators by 32767 ahead of time.
Im using the right type "float", if i send it directly to the dsound buffer i don't hear anything.
I already tested with 0x7FFF (32767) and sound at %100 of volume get distorded.
I will try the option of changing numerators...