Re: Native Device Formats
Re: Native Device Formats
- Subject: Re: Native Device Formats
- From: Brian Willoughby <email@hidden>
- Date: Sun, 8 Jun 2008 14:29:27 -0700
This is a very important issue to be mindful of, but it's perhaps not
as horribly difficult as you might imagine. It all stems from the
limitation of twos-complement integer formatting. Because we're
talking about binary numbers, the inclusion of a code for zero means
that you would have an odd number of values if both positive and
negative versions of every other number were included. But any
binary number always has an even number of codes, so twos-complement
sacrifices one single maximum positive value, such that there is one
single negative number which cannot be converted to the positive.
This sacrifice is made to make room for the zero code. In other
words, you can reach -1.0, but you can never reach +1.0, at least not
so long as we are stuck with DACs that use twos-complement integers
(and they all do).
The proper algorithm depends upon the type of audio material.
e.g. outside sampled versus processed versus generated.
For outside samples - e.g. from CD or from an A/D interface - you're
fine using the standard conversion and pretending that the range is
[-1.0, 1.0) ... the fact that +1.0 is never reached is immaterial.
The final conversion from float to integer will preserve the original
outside samples at whatever bit depth they originated.
For processed samples, particularly with limiters designed to avoid
clipping, the algorithm must be mindful that +1.0 cannot be
represented on the analog output. An advanced algorithm can
specifically allow -1.0 to pass without correcting for "clipping"
while smartly detecting any signal which approaches +1.0 and
dynamically adjusting the gain to keep the output away from a +1.0
value. Obviously, any negative below less than -1.0 would also be
gain-adjusted. The algorithm can assume 16-bit or 24-bit or allow
the user some choice, and the bit-depth ultimate determines how close
the signal can get to the +1.0 value.
For generated samples, you do have to be mindful not to simply call
the sin() function and expect a scaling of +1.0 to be free from
clipping. My approach is to use 32767.0/32768.0 as the scaling
factor, such that my generated samples can be played on any 24-bit or
16-bit DAC without clipping distortion. You would use 255.0/256.0
for an 8-bit DAC, and I'll leave it to the reader to calculate the
scaling factor for 24-bit. In the case of generated samples, Mikael
is basically right. But the key is that the same approach is not
necessarily appropriate for outside sample sources or processed audio.
All of the above is really just a lead-in to answering your final
question. We do not map [-1.0, 1.0] to [0x800000, 0x7FFFFF] for
several reasons. First of all, that mapping creates a negative DC
bias in the output signal of one-half LSB. More importantly, that
mapping would create serious digital quantization noise in even the
most simple case of sending 16-bit CD audio through floating point
processing. Finally, I would say that your suggested mapping is not
more "intuitive" but perhaps more "convenient" in a limited number of
circumstances. The standard mapping that we're using is perfect for
A/D and most processing, with the only exceptions being generated
samples and dynamics processing which attempts to prevent clipping.
The standard mapping is actually the more intuitive one for those
aware of the mathematics and binary representations.
Brian Willoughby
Sound Consulting
On Jun 8, 2008, at 08:21, Mikael Hakman wrote:
AudioConverter tells me that in order to get the lowest 24-bit value
(0x800000) I have to use -1.0 Float32 value (0xbf800000). In order to
get highest 24-bit value (0x7fffff) I have to use 0.99999988 Float32
value (0x3f7ffffe). This means that FS amplitude when using Float32
is 0.99999944 (0x3f7fffff), not 1.0.
This may have some significance when e.g. generating FS precision
test signals as in:
s = 0.99999944 * (sin (2 * PI * f * t) + 1.0) - 1.0
Omitting the scaling as in:
s = sin (2 * PI * f * t)
would result in a slight clipping on the positive side.
Could you please give some explanation why we are using this mapping
as compared to (more intuitive) mapping of [-1.0, 1.0] to [0x800000,
0x7fffff] which would allow using sin() function results directly as
input to CoreAudio?
Regards/Mikael
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Coreaudio-api mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden