|
From: <Do...@as...> - 2000-06-27 15:56:32
|
Something had been bugging me for a while about my understanding of mixing
and today I finally worked it out. This stuff directly fits in with what
Lourens has been doing so it should make an interesting read.
For some reason I got the idea that in the module code I had been examining
interpolation was a post process which doesn't make sense really, seeing as
interpolation is about creating points.
I decided to re-exam what was going on and figured it out well and truely
this time. What it does mean is that we 100% must go over the integer based
mixing now, we cannot stick with floating point, there is no advantage and
if it stays floating point our code will be extremely slow, I can easily see
losses of several hundread percent.
The reason?
Read this bit from an article I pointed some of you to on IRC.
------------- Begin extract from HUGI #19 -----------------
If you have ever used a tracker, you know that samples can have different
frequencies depending on the current note, and they can also have different
volumes. If the sound buffer we are going to fill has frequency F1, and a
sample has frequency F2, we can simply copy the sample into the buffer,
moving in the sample with step F2/F1, and scaling sample data by volume.
Example:
for (i=0; i<SoundBufferSize; i++) {
SoundBuffer[i] = Sample[SamplePos] * SampleVolume / 0x40;
SamplePos += F2/F1;
}
In the above example, I assume that sample volume varies between 0 (0%) and
0x40 (100%), like in typical modules.
The only problem in the above example is that SamplePos is moved by
floating-point step, as F2/F1 is a floating-point value. To solve this, we
can do simple linear interpolation, that is, separately add integral step
and fractional step, like this:
IntStep = long(F2/F1);
FracStep = long((F2<<32)/F1); // 64-bit / 32-bit division
for (i=0; i<SoundBufferSize; i++) {
SoundBuffer[i] = Sample[IntSamplePos] * SampleVolume >> 6;
FracSamplePos += FracStep;
IntSamplePos += IntStep + CY;
}
In the above example, integral step is added to integral sample position,
along with carry from fractional step addition.
-----------------------------------------------------------
By using fixed point arithmetic using two integer values we get a fractional
and integral part. The integral part represents the original data values and
each loop in which we are incrementing the fractional part is a new data
value we're creating, which in this case is duplicating the original byte.
At this point we can start to introduce the interpolation code to improve
the sound quality output.
The problem with floating point incrementing is that we don't get seperate
integer and fractional parts, we could extract them but that would be
extremely inefficient and unneeded when you consider how well the integer
technique works.
Also it doesn't take long before you realise how well this code could work
in assembler. The potential benefits are immense.
One final point and this is what I need you guys to help me on, see this
line of code:
IntSamplePos += IntStep + CY;
CY from what I can work out represents the carry flag, it would appear this
is something that isn't present in Windows compilers yet I believe you would
find it in Borland compilers. I could really do with finding or simulating
the carry counter (or maybe even the overflow flag) for my ANSI C++ part of
SUB-SONIC. Once this is done I can actually get on and write the asm code
and watch the CPU usage go down dramatically.
So I hope I've cleared those points up for you (and me) now. I will play
around with my mixing code a little more, it looks extremely ugly having two
sorts of mixing code in the same module with 50% of commented out.
Doug
|