[vorbis-dev] integer pcm decode patch
Jonathan Blow
jon at bolt-action.com
Wed Apr 19 18:36:49 PDT 2000
robert at moon.eorbit.net wrote:
> Finding a better place to do the int16_t conversion is the key solving this
> problem. Once solution that I looked into was having the mdct kick out
> int16_t values, but without having access to and understanding the original
> paper that the mdct code was based on I only made things worse.
Hi, I am using WinCVS and don't want to get cvs working on my linux machines
right now, and don't have patch on my win32 machine. So I am reading
straight from the patch file. I assume this is the code you are talking
about:
+ for(i=beginSl,t=beginSl;i<endSl;i++,t+=vi->channels)
+ pcm_int[t+j]+=vb->pcm[j][i]*32767.;
+ /* the remaining section */
+ for(;i<sizeW;i++,t+=vi->channels)
+ pcm_int[t+j]=vb->pcm[j][i]*32767.;
Here is my question: would it help to have a faster way to convert
from double to integer?
Being a game developer I possess some arcane knowledge about how to
do wacky things with IEEE-754 floating point numbers. A standard trick
that we do in games is to manipulate this format directly to squeeze out
the values we want. This does two things: it (a) eliminates floating
point multiplies, which are more expensive than adds on some processors,
and (b) it eliminates the _ftol or equivalent code that the compiler
sticks in when you go from any float size to integer (This stuff is there
to enforce the IEEE rounding semantics which, most of the time you are
converting float to int, you don't really care about... and it is this
stuff that usually takes all the time).
For example here is some code that converts from a double to a machine-word-
sized integer (without scaling the double)... uhh this is actually C++
so it has a reference and stuff, but it can be done without that:
----------------------------------------
static const double fix32_conv_factor = ((double)0x10000000) * 256.0 * 1.5;
static const double int_conv_factor = (fix32_conv_factor * (double)0x10000);
const int LOW_WORD_OFFSET = 0;
inline long iDOUBLE_TO_INT(double d) {
d += int_conv_factor;
const long *const &num = (long *)&d + LOW_WORD_OFFSET;
return *num;
}
----------------------------------------
The value of LOW_WORD_OFFSET changes depending on the architecture (on a
sparc, you want it to be 1).
Anyway to scale the number, you add a different value to the double
in the first line of iDOUBLE_TO_INT. So for example, if you want to
multiply the double by 32768, just use 'fix32_conv_factor' instead
of 'int_conv_factor':
----------------------------------------
inline long iDOUBLE_TO_INT(double d) {
d += fix32_conv_factor;
const long *const &num = (long *)&d + LOW_WORD_OFFSET;
return *num;
}
----------------------------------------
Assuming that your compiler (gcc?) is generating rounding code for your
cast, which it probably is, this is going to be hella faster.
If you want to know why this actually works, I have a book in progress
that talks about this stuff, I can forward you the draft chapters of
that.
-Jonathan.
--- >8 ----
List archives: http://www.xiph.org/archives/
Ogg project homepage: http://www.xiph.org/ogg/
More information about the Vorbis-dev
mailing list