VOGONS


First post, by superfury

User metadata
Rank l33t++
Rank
l33t++

Anyone knows what the correct 'volume' maximum values are for PC speaker, adlib, MIDI and Sound source? My application uses 16-bit samples atm. The PC speaker, adlib and Sound source range is currently set to generate values (sinus and derratives) in the range of -32768 to 32767. This applies to all three of them. The MIDI renderer simply takes it's samples from the Soundfont (.sf2 file) and applies it's volume according to the preset and instrument data.

Anyone knows what the correct volumes are for the PC speaker, adlib, MIDI and Sound source in the -32768 to 32767 range (this is linear range. My application uses Decibel range though (with 0.0 being silence and 1.0 being full volume(32767 range). Anything in between is mapped using the Decibel range 0.0 - 1.0(decibel) => 0 - 32767(linear)).

So can anyone tell me the correct decibel range (0.0 being silence and 1.0 being maximum volume using a decibel scale. So 2.0 sounds twice as loud as 1.0, while 0.0 being silence(exception to the rule. So effectively the linear range is a 10 to the power of the volume set(0.0-1.0+) with the exception of 0.0 being silence(instead of 0dB))) of these 'soundcards' (PC speaker isn't a sound card, the rest is)?

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 1 of 14, by Malvineous

User metadata
Rank Oldbie
Rank
Oldbie

I don't think there's any standard for maximum volumes. I've used machines which had quiet PC speakers, others with loud ones, and others with a volume control for the PC speaker. Most sound cards have a mixer onboard, so you can choose what level you want the maximum sample level to be for a specific audio source.

Personally I would choose to use the largest amount of dynamic range available, which would have everything -32768 to +32767. You can then run everything through a mixer (e.g. the one found on many Creative cards) and then reduce the volume if needed. If you instead generated the audio using a restricted range (e.g. -100 to +100) then amplifying it would probably result in poor sounding audio because most of the dynamic range is missing.

Just as an aside, the decibel scale is logarithmic, so 2.0 is not twice as loud as 1.0 (that would be linear), in order to double the volume you need to add 6dB.

Reply 2 of 14, by Jepael

User metadata
Rank Oldbie
Rank
Oldbie

Would you like to explain why you want to do it like that?
If you want, you can also explain how you have understood decibels, so we can also clear out any misunderstandings about decibels, because I have trouble following some of your logic.
The decibel range is not from 0 to 1 for 0% to 100%.
As Malvineous explained, the scale you explained is not logarithmic but linear, if you want 0=silence, 1.0=full scale, 2.0=double the full scale.
In decibel units, silence is minus infinty, but as 16-bit audio has 96.3dB range, so -90.3dB is enough to make it silent. -6.02dB is half the full scale, 0dB is exactly the full scale, and +6.02dB is double the full scale.
(sometimes instead of 6.02 the value is just rounded to 6 to assume ratio of 2).
So if you want to control volume from 0 to 200% with a slider going between 0.0 and 1.0, it's decibel range for controlling 16-bit audio needs to be -93 to +6 dB. If the slider has 100 steps, then sure you can limit the range from going between -94 and +6 decibels, but then you can't go over 6dB or under -94 dB, so you can't select 400% volume as that would be 12dB.

Adlib output should be 16-bit full scale so it needs no further gain or attenuation. Well at least OPL3 output is 16-bit linear PCM, but OPL2 output is kind of floating point with range equaling 16 bits and resolution of 10 bits.
So I'd use it directly, but of course music with small amount of channels or low carrier volume is not going to be very loud and does not fill the 16-bit full scale range, as 8 operators at full volume can almost fill the 16-bit range, and 9 operators will overflow/clip.

So in comparison, the 1-bit speaker output scaled to -32768..+32767 full scale range range would sound too loud.
On the other hand, most likely you want to scale the DSS 8-bit range -128..127 to full scale range as well.

If you mix together more than one full scale channels, they can overflow as the result does not fit into 16-bit full scale any more, so I don't know if your mixer (SDL) handles this for you or how does it work. On the other hand, mixing together three channels does not mean you have to divide the values by three to make them fit into full scale again, as it's not very likely that all three channels would be at their maximum value at the same instant.

Reply 3 of 14, by superfury

User metadata
Rank l33t++
Rank
l33t++
#define dB2factor(dB, fMaxLevelDB) pow(10, (((dB) - (fMaxLevelDB)) / 20))
#define factor2dB(factor, fMaxLevelDB) ((fMaxLevelDB) + (20 * log(factor)))

dB2factor is used in generating volume envelopes and setting mixer volume(with fMaxLevelDB set to 1) for each emulated hardware stream(s) (3 pc speaker channels(2 for testing), 1 adlib channel, 1 disney sound source channel and 24 midi channels) where each channel has it's own sample rate, volume, buffer size, rendering callback and output buffer. It's usually called with fMaxLeveldB=1)
factor2dB is used with soundfont/midi emulation to convert the sustain cB into a linear factor(so fMaxLevelDB is 100).

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 4 of 14, by gdjacobs

User metadata
Rank l33t++
Rank
l33t++

A bit of a refresher for everyone: dB in audio is a voltage reference. Specifically, Vout/Vin=10^(dBgain/20). 3 dB gain gives approximately (just under) sqrt(2) x the input voltage. Sound power at the speaker, assuming perfect speaker response, will be proportional to the square of the output voltage, therefore 3 dB gain is approximately double the volume. The reverse is also true. A 3 dB cut (-3 dB) will result in approximately half the output sound volume. Superfury's macro is correct and will agree with this.

I'm not aware of a standard for the output level of a consumer speaker drive, that would depend on the characteristics of the intended speakers themselves. Even THX standards are usually in terms of the end product -- SPL. RCA line output waveforms should conform to the customary output levels for either pro or consumer gear. For consumer gear, your reference level (0 dBV) is at 1Vp-p with nominal listening level being about -10dBV. The rated RCA input voltage on my Denon amp is a bit higher than this, but how much headroom you have is always dependent on the design of the preamp.

So, correctly designed consumer gear should output 1Vp-p max as a line output with a few hundred ohms impedance (470ohms for my Denon) giving approximately equal output to the power amp. This voltage level also corresponds with 0 dBV, your reference level. How well some of the early pc audio gear conformed to this is unknown. However, your internal mixing should conform with the standard by being able to take a coherent full scale sine wave as input on each of the mixed channels and output 1 Vp-p max with no clipping. A sound engineer would have to do this on the mixer board, but you can design it in.

As for your internal representation of dB, you are getting quite a bit of dynamic range for free by using fp for your intermediate representation. When you convert back to 16 or 24 bit samples for output, audio which is quieter than the output range of the sample size will automatically be truncated. 0 will be 0 bits, 1 will be 16 or 24 bits. So, this is good. I mean, your math could represent -150 dB or less, but the output conversion will properly truncate anything less than what can be represented by the output waveform.

All hail the Great Capacitor Brand Finder

Reply 5 of 14, by Jepael

User metadata
Rank Oldbie
Rank
Oldbie

That's exactly what I mean, because I don't understand why you have the fMaxLevelDB there, because that way you have already set yourself the reference level, the value that means no change in volume.
Decibels are about ratios, not absolute quantities. Normally, a value that does not change the volume (which means the ratio of volumes is 1.0) is 0dB, because 20*log(1.0)=0dB.

For example, if you set fMaxLevelDB to 100:
So to me, that tells your reference level is -100dB.
With that notation, you always attennuate by 100dB (divide by 100000 means -100dB), and then you need to set gain to 100dB (multiply by 100000 means +100dB) to have the original volume.

So in your notation, if fMaxLevelDB is 100, then your input dB value of:
106 means to double the volume (+6dB),
100 means to not change the volume (0dB),
94 means to halve volume (-6dB), and,
0 means to attennuate by 100dB (-100dB) which is yes more than enough to completely mute 16-bit audio, even 17-bit audio.

So even if you do use -100dB as your reference, it does not limit the dB volume range between 0 and 100, but if you limit to 0 and 100 yourself, you can't amplify the volume, only scale back to original ratio.

Even in soundfont specs, the notation 0dB means no attenuation, and silence means minus infinity, and for example the initial attenuation goes from 144dB attennuation (-144dB) to 0 (0dB). They also mention 96dB attenuation being enough for muting 16-bit audio. So in soundfont specs even if the values go from 0 to 144, it means 0dB is most loud and 144dB is most silent, so it is not gain (positive decibels) but attenuation (negative decibels mean positive attenuation).

Reply 6 of 14, by superfury

User metadata
Rank l33t++
Rank
l33t++

Well, the only place the value of 100 is used, is when converting the soundfont sustain volume(in centibels) to a linear factor to sustain. All other calculations use value of 1(to convert volume linear 0-1 to a decibel-like curve with the same range and of course to calculate the linear 100%(output volume factor) value(pc speaker volume factor to apply, as well as for each other sound channel for other hardware)). Also the adlib uses a different value as well(I believe it was about 48dB or something close, see the 'volume' register(the register containing bits for 1.5dB, 3, 6, 12 and 24dB attenuation)).

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 7 of 14, by gdjacobs

User metadata
Rank l33t++
Rank
l33t++
Jepael wrote:
That's exactly what I mean, because I don't understand why you have the fMaxLevelDB there, because that way you have already set […]
Show full quote

That's exactly what I mean, because I don't understand why you have the fMaxLevelDB there, because that way you have already set yourself the reference level, the value that means no change in volume.
Decibels are about ratios, not absolute quantities. Normally, a value that does not change the volume (which means the ratio of volumes is 1.0) is 0dB, because 20*log(1.0)=0dB.

For example, if you set fMaxLevelDB to 100:
So to me, that tells your reference level is -100dB.
With that notation, you always attennuate by 100dB (divide by 100000 means -100dB), and then you need to set gain to 100dB (multiply by 100000 means +100dB) to have the original volume.

So in your notation, if fMaxLevelDB is 100, then your input dB value of:
106 means to double the volume (+6dB),
100 means to not change the volume (0dB),
94 means to halve volume (-6dB), and,
0 means to attennuate by 100dB (-100dB) which is yes more than enough to completely mute 16-bit audio, even 17-bit audio.

So even if you do use -100dB as your reference, it does not limit the dB volume range between 0 and 100, but if you limit to 0 and 100 yourself, you can't amplify the volume, only scale back to original ratio.

Even in soundfont specs, the notation 0dB means no attenuation, and silence means minus infinity, and for example the initial attenuation goes from 144dB attennuation (-144dB) to 0 (0dB). They also mention 96dB attenuation being enough for muting 16-bit audio. So in soundfont specs even if the values go from 0 to 144, it means 0dB is most loud and 144dB is most silent, so it is not gain (positive decibels) but attenuation (negative decibels mean positive attenuation).

Small mistake here. 6 dB up is four times the volume, 6 dB down is 1/4 the volume.

superfury wrote:

Also the adlib uses a different value as well(I believe it was about 48dB or something close, see the 'volume' register(the register containing bits for 1.5dB, 3, 6, 12 and 24dB attenuation)).

The Adlib has 6 bits of volume adjustment, but maps it logarithmically to generate 48 dB of dynamic range. Adlib also references from 48 dB below max volume. Referenced to highest volume (hopefully 1 Vp-p on a real Adlib card), 0x00 on the volume register is -48 dB attenuation, 0x3F is 0 dB attenuation.

You can use whatever reference level you want, but I suggest you set your attenuation for mixing as a final step, and the simplest way to do that will be treating max output as 0 dB throughout.

All hail the Great Capacitor Brand Finder

Reply 8 of 14, by Jepael

User metadata
Rank Oldbie
Rank
Oldbie
superfury wrote:

Well, the only place the value of 100 is used, is when converting the soundfont sustain volume(in centibels) to a linear factor to sustain.

 

sustainVolEnv This is the decrease in level, expressed in centibels, to which the Volume Envelope value ramps during the decay p […]
Show full quote

sustainVolEnv
This is the decrease in level, expressed in centibels, to which the
Volume Envelope value ramps during the decay phase. For the Volume
Envelope, the sustain level is best expressed in centibels of attenuation
from full scale. A value of 0 indicates the sustain level is full level; this
implies a zero duration of decay phase regardless of decay time. A
positive value indicates a decay to the corresponding level. Values less
than zero are to be interpreted as zero; conventionally 1000 indicates
full attenuation. For example, a sustain level which corresponds to an
absolute value 12dB below of peak would be 120

So, in a SoundFont, 0 means no attenuation, 1000 means attenuation of 100dB which is same as gain of -100dB. I see now where you get that 100dB offset. I just understand the SoundFont way more natural for handling volume envelopes, other synths do it too. I mean the system where small number (zero) means max volume and large numbers mean low volume, not the other way around.

superfury wrote:

All other calculations use value of 1(to convert volume linear 0-1 to a decibel-like curve with the same range and of course to calculate the linear 100%(output volume factor) value(pc speaker volume factor to apply, as well as for each other sound channel for other hardware)).

I still dont get this part. Why would you want offset of -1 dB in your calculations? It's 0.98 in linear space. 0 dB = 1.0 linear. +1dB = 1.12 linear. 100% volume is 0dB, not -1dB, not +1dB.

superfury wrote:

Also the adlib uses a different value as well(I believe it was about 48dB or something close, see the 'volume' register(the register containing bits for 1.5dB, 3, 6, 12 and 24dB attenuation)).

All that matters is when it plays music with 8 channels at full volume, it can already fill 16-bit range and all 9 channels would be too loud to fit into 16-bit range. Does it matter how much you can attenuate the operator volume, the 0dB level is still the maximum. It does not mean you should amplify it by 48dB. It outputs audio which you can simply understand as 16-bit PCM like it was a wav file.

Reply 9 of 14, by superfury

User metadata
Rank l33t++
Rank
l33t++

With 48dB being full attenuation (so it's a linear factor of 1.0. So the linear curve of the value set by the register (0-48dB) is mapped to a workable multiply factor (0.0-1.0) to multiply the output samples with for the correct volume).

So effectively output of a sample is:
ChannelOutput = calculated4ksample * dB2factor(volenv,1.0) * dB2factor(channelvolume,48.0)

Although the channel volume dB2factor calculation is done during initialisation and saved into an lookup table to be used when multiplying.

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 10 of 14, by Jepael

User metadata
Rank Oldbie
Rank
Oldbie
gdjacobs wrote:

Small mistake here. 6 dB up is four times the volume, 6 dB down is 1/4 the volume.

Is it? The last time I checked, for voltages (and thus PCM audio), 20*log(2) was +6dB and 20*log(0.5) was -6dB. I think you are confusing it with decibels for powers where 10*log(4) is 6dB.

gdjacobs wrote:

0x00 on the volume register is -48 dB attenuation, 0x3F is 0 dB attenuation.

Also the last time I checked, 0x00 was -0dB (max volume) and 0x3F was -48dB (minimum volume).

superfury wrote:

With 48dB being full attenuation (so it's a linear factor of 1.0. So the linear curve of the value set by the register (0-48dB) is mapped to a workable multiply factor (0.0-1.0) to multiply the output samples with for the correct volume).

OK, now I know what is the problem.
See, something attenuated by 0dB has a factor of 1.0 so it is not attenuated by anything.
But something attenuated by 48dB is not 0!
Having a range of 48dB for volume means you can't attenuate more, it's just the minimum volume, it does not mean it is completely silenced.
If you have 48dB attenuation and put 10^(-48/20) into calculator, you get 0.00398, which is a factor to attenuate anything by 48dB, for example, full scale 16-bit audio with range of -32768 .. 32767 to a range of -130 .. +130. In the case of Adlib, one operator with range of about 4000 attenuated by 48dB is about 16. As you see, it is not zero.

Reply 11 of 14, by gdjacobs

User metadata
Rank l33t++
Rank
l33t++

Yes, mixing up dBV and dB SPL, but the ratio given by dBV does not measure volume. It is used for waveform amplitude. The actual sound volume is proportional to power. The actual loudness of -3 dB will be the same, but the ratio will be different depending on whether you're interested in PCM amplitude or SPL.

I was going from his description, but it sounds like even the ordering of the Adlib volume register is consistent with attenuation rather than amplification. This is good. Second, line level is 1V RMS (1.414 Vp-p). I was leading astray there.

That description of factor didn't make much sense to me either. You can't really remap a real number to dB scaling. It would no longer be a real number. The Adlib board can do this because it's essentially using the 6 bit register as an enumeration. In your case, go ahead and use 0 to 1 as the PCM envelope and convert to/from dB as needed. If you want to avoid some compute cost in conversion, precompute dB and create lookup tables.

All hail the Great Capacitor Brand Finder

Reply 12 of 14, by superfury

User metadata
Rank l33t++
Rank
l33t++

The adlib attenuation (the range of 48dB) is already a lookup table created during initialization. If I remember correctly most values are mapped normally, except sustain value 0xF.

	//Build the needed tables!
for (i = 0; i < (int)NUMITEMS(sustaintable); i++)
{
if (i==0xF) sustaintable[i] = (float)dB2factor(93, 93); //Full volume exception with all bits set!
else sustaintable[i] = (float)dB2factor((float)93-(float)(((i & 1) ? 3 : 0) + ((i & 2) ? 6 : 0) + ((i & 4) ? 12 : 0) + ((i & 8) ? 24 : 0)), 93); //Build a sustain table!
}

for (i = 0; i < (int)NUMITEMS(outputtable); i++)
{
outputtable[i] = (float)dB2factor((float)48 - (float)(
((i & 1) ? 0.75:0)+
((i&2)?1.5:0)+
((i&4)?3:0)+
((i&8)?6:0)+
((i&0x10)?12:0)+
((i&0x20)?24:0)
),48.0f);
}
Last edited by superfury on 2015-12-01, 22:07. Edited 1 time in total.

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 13 of 14, by Jepael

User metadata
Rank Oldbie
Rank
Oldbie
gdjacobs wrote:

Yes, mixing up dBV and dB SPL, but the ratio given by dBV does not measure volume. It is used for waveform amplitude. The actual sound volume is proportional to power. The actual loudness of -3 dB will be the same, but the ratio will be different depending on whether you're interested in PCM amplitude or SPL.

For the same load, if you double the voltage, you get quadruple the power, right?
For power, 10*log(4)=6dB. For voltage, 20*log(2)=6dB.
So the decibel ratio is same. You need 3dB more power to double the power so it sounds double as loud (doubles the SPL).
You also need 3dB more voltage to sound double as loud (double the SPL), even though that only requires 1.414 times more voltage amplitude to double the power.
Either way, we are working with PCM sample values that are about voltages, not powers, So yes, I could have been more clear that by volume here it really means amplitudes of PCM values or voltages they represent, not the actual sound pressure level, but also considering the context... Anyway, for those PCM values, doubling/halving is +/- 6dB which is what matters, and for making it twice/half as loud it is +/- 3dB.

gdjacobs wrote:

The Adlib board can do this because it's essentially using the 6 bit register as an enumeration. In your case, go ahead and use 0 to 1 as the PCM envelope and convert to/from dB as needed. If you want to avoid some compute cost in conversion, precompute dB and create lookup tables.

It's not used as an enumeration. It really uses everything related to amplitude (volumes, envelopes, even the waveforms) in log domain, so it can just sum together the attenuations, and finally convert this to linear PCM domain. If each operator would require just one multiplication per sample, it would have to perform about 900000 multiplications per second, which was just not possible to do cheaply.

But yes, it is completely weird to use 1 as the decibel offset. And completely weird to use a range of 0 to 1 in decibels for volume, even if it meant -1 to 0 decibels. 0 to 1 as linear is good.

Now, what could be useful is to convert some volume variable that is between 0 and 1 into decibels in between -100 to -0 dB , so where 0 is -100dB and 1 is -0dB.
In Superfury's code, that would be 0 to 100 dB amplification with 100 as the maxDB to have the 100dB attenuation there as well. And 1.03 would be twice as loud, and 1.06 would be twice the voltage.

The only problem is what he wants to avoid, 2 equals then 200dB, meaning +100dB gain or times 100000 linear, so even PCM values of +/- 1 with 100dB gain goes through the roof not fitting into 16-bit PCM values any more.
Perhaps a 100dB range, from -80 to +20, is good? -80dB divides by 10000, +20dB multiplies by 10.

Reply 14 of 14, by gdjacobs

User metadata
Rank l33t++
Rank
l33t++

Yay, we agree on terminology and math!

Pretty much the best you can hope for in real SNR will be 100-110 dB even with modern equipment. 100 dB dynamic range sounds fine to me. However, SACD aficionados with the golden ears might prefer the full 144 dB of 24 bit audio. Also, what about the option of normalizing the envelopes? Then apply master volume attenuation, attenuate the waveforms, and convert back to a linear range for waveform mixing and conversion to fixed point?

All hail the Great Capacitor Brand Finder