Rename paula.h to r1.0/paula.h
This commit is contained in:
+178
@@ -0,0 +1,178 @@
|
||||
// ========================= Paula Class (Paula Emulator) =======================
|
||||
|
||||
class Paula
|
||||
{
|
||||
public:
|
||||
static const sInt FIR_WIDTH = 512; // Finite Impulse Response (FIR) filter width
|
||||
sF32 FIRMem[2 * FIR_WIDTH + 1]; // FIR memory (1025), one dimensional array
|
||||
|
||||
struct Voice // Start Voice Structure
|
||||
{
|
||||
private:
|
||||
sInt Pos; // position ?
|
||||
sInt PWMCnt, DivCnt; // Pulse Width Modulation, pwm division count?
|
||||
sIntFlt Cur; // current ?
|
||||
|
||||
public:
|
||||
sS8 *Sample; // audio channel data (sample) location
|
||||
sInt SampleLen; // audio channel data (sample) length
|
||||
sInt LoopLen; // loop length
|
||||
sInt Period; // 124 .. 65535 (audio channel period (rate))
|
||||
sInt Volume; // 0 .. 64 AUDxVOL
|
||||
|
||||
Voice ()
|
||||
: Period (65535), Volume (0), Sample (0), Pos (0), PWMCnt (0), DivCnt (0), LoopLen (1)
|
||||
{
|
||||
Cur.F32 = 0;
|
||||
} // voice constructor ( initailization list - zero everything)
|
||||
|
||||
public:
|
||||
void Render (sF32 *buffer, sInt samples) // define render function
|
||||
{
|
||||
if (!Sample) // return if no samples... i think
|
||||
return;
|
||||
|
||||
sU8 *smp = (sU8 *)Sample;
|
||||
for (sInt i = 0; i < samples; i++)
|
||||
{
|
||||
if (!DivCnt)
|
||||
{ // todo: use a fake d/a table for this
|
||||
Cur.U32 = ((smp[Pos] ^ 0x80) << 15) | 0x40000000; // smp[pos] XOR 0x80 << 15 OR 4000 0000
|
||||
Cur.F32 -= 3.0f;
|
||||
if (++Pos == SampleLen)
|
||||
Pos -= LoopLen;
|
||||
DivCnt = Period;
|
||||
}
|
||||
if (PWMCnt < Volume)
|
||||
buffer[i] += Cur.F32; // PWM counter
|
||||
PWMCnt = (PWMCnt + 1) & 0x3f; // 0x3f = 63
|
||||
DivCnt--;
|
||||
}
|
||||
} // end render function
|
||||
public:
|
||||
void Trigger (sS8 *smp, sInt sl, sInt ll, sInt offs = 0) // define trigger function (trigger voice data)
|
||||
{
|
||||
Sample = smp; // sample
|
||||
SampleLen = sl; // sample length
|
||||
LoopLen = ll; // looplength
|
||||
Pos = sMin (offs, SampleLen - 1); // offset
|
||||
} // end trigger function
|
||||
// }; // end voice structure
|
||||
// Voice V[4];
|
||||
} V[4]; // create array of instance of voice structure
|
||||
|
||||
// --
|
||||
// rendering in paula freq
|
||||
static const sInt RBSIZE = 4096; // ring buffer (aka circular buffer) size
|
||||
sF32 RingBuf[2 * RBSIZE];
|
||||
sInt WritePos; // write position
|
||||
sInt ReadPos; // read position
|
||||
sF32 ReadFrac; // fraction?
|
||||
public:
|
||||
void CalcFrag (sF32 *out, sInt samples) // i believe this function transfers
|
||||
// samples into ring buffer
|
||||
{
|
||||
sZeroMem (out, sizeof (sF32) * samples); // zero-out mem
|
||||
sZeroMem (out + RBSIZE, sizeof (sF32) * samples);
|
||||
for (sInt i = 0; i < 4; i++) // four voices(0 - 3)
|
||||
{
|
||||
if (i == 1 || i == 2)
|
||||
V[i].Render (out + RBSIZE, samples);
|
||||
else
|
||||
V[i].Render (out, samples);
|
||||
}
|
||||
}
|
||||
|
||||
// =================================== Calc
|
||||
public:
|
||||
void Calc ()
|
||||
{
|
||||
sInt RealReadPos = ReadPos - FIR_WIDTH - 1;
|
||||
sInt samples = (RealReadPos - WritePos) & (RBSIZE - 1);
|
||||
|
||||
sInt todo = sMin (samples, RBSIZE - WritePos);
|
||||
CalcFrag (RingBuf + WritePos, todo);
|
||||
if (todo < samples)
|
||||
{
|
||||
WritePos = 0;
|
||||
todo = samples - todo;
|
||||
CalcFrag (RingBuf, todo);
|
||||
}
|
||||
WritePos += todo;
|
||||
}; // Calc end
|
||||
|
||||
// =================== rendering in output freq P->Render
|
||||
public:
|
||||
sF32 MasterVolume; // master volume
|
||||
sF32 MasterSeparation; // master stereo separation
|
||||
|
||||
void Render (sF32 *outbuf, sInt samples) // iutput buffer
|
||||
{
|
||||
const sF32 step = sF32 (PAULARATE) / sF32 (OUTRATE);// ratio paula/output rate step (3740000/48000 = 77.92)
|
||||
const sF32 pan = 0.5f + 0.5f * MasterSeparation; // audio panning (50% each left/right) (0.5 + 0.5 * 0.5 = 0.75)
|
||||
const sF32 vm0 = MasterVolume * sFSqrt (pan); // master volume 0
|
||||
const sF32 vm1 = MasterVolume * sFSqrt (1 - pan); // master volume 1
|
||||
|
||||
for (sInt s = 0; s < samples; s++)
|
||||
{
|
||||
sInt ReadEnd = ReadPos + FIR_WIDTH + 1;
|
||||
if (WritePos < ReadPos)
|
||||
ReadEnd -= RBSIZE;
|
||||
if (ReadEnd > WritePos)
|
||||
Calc (); // call calc() - render in paula rate
|
||||
sF32 outl0 = 0, outl1 = 0; // out left
|
||||
sF32 outr0 = 0, outr1 = 0; // out right
|
||||
|
||||
sInt offs
|
||||
= (ReadPos - FIR_WIDTH - 1) & (RBSIZE - 1); // offset [this needs optimization. SSE would
|
||||
// come to mind. (streaming SMID extensions)]
|
||||
sF32 vl = RingBuf[offs];
|
||||
sF32 vr = RingBuf[offs + RBSIZE];
|
||||
for (sInt i = 1; i < 2 * FIR_WIDTH - 1; i++)
|
||||
{
|
||||
sF32 w = FIRMem[i]; // w = FIRMem[i]
|
||||
outl0 += vl * w; // outl0 = outl0 + (vl * w)
|
||||
outr0 += vr * w; // outr0 = outr0 + (vl * w)
|
||||
offs = (offs + 1) & (RBSIZE - 1);
|
||||
vl = RingBuf[offs];
|
||||
vr = RingBuf[offs + RBSIZE];
|
||||
outl1 += vl * w;
|
||||
outr1 += vr * w;
|
||||
}
|
||||
sF32 outl = sLerp (outl0, outl1, ReadFrac); // output left
|
||||
sF32 outr = sLerp (outr0, outr1, ReadFrac); // output right
|
||||
*outbuf++ = vm0 * outl + vm1 * outr;
|
||||
*outbuf++ = vm1 * outl + vm0 * outr;
|
||||
|
||||
ReadFrac += step;
|
||||
sInt rfi = sInt (ReadFrac);
|
||||
ReadPos = (ReadPos + rfi) & (RBSIZE - 1);
|
||||
ReadFrac -= rfi;
|
||||
}
|
||||
} // Render end
|
||||
|
||||
// --
|
||||
public:
|
||||
Paula () // paula constructor
|
||||
{
|
||||
// make Finite Impulse Response (FIR) table (for low pass filter?)
|
||||
sF32 *FIRTable = FIRMem + FIR_WIDTH; // FIR table size
|
||||
sF32 yscale = sF32 (OUTRATE) / sF32 (PAULARATE); // Y scale
|
||||
sF32 xscale = sFPi * yscale; // X scale
|
||||
for (sInt i = -FIR_WIDTH; i <= FIR_WIDTH; i++) // windowed-sinc FIR filter (product of sinc & window function)
|
||||
FIRTable[i]
|
||||
= yscale * sFSinc (sF32 (i) * xscale)
|
||||
* sFHamming (sF32 (i)
|
||||
/ sF32 (FIR_WIDTH
|
||||
- 1)); // Firtable = (yscale) * (sinc(i) * xscale) * hamming(i) / (fir_width-1)
|
||||
|
||||
sZeroMem (RingBuf, sizeof (RingBuf));
|
||||
ReadPos = 0;
|
||||
ReadFrac = 0;
|
||||
WritePos = FIR_WIDTH; // reset ring buffer
|
||||
|
||||
MasterVolume = 0.66f; // master volume 66%
|
||||
MasterSeparation = 0.5f; // stereo seperation 50:50
|
||||
// FltBuf = 0;
|
||||
} // Paula Constructor end
|
||||
};
|
||||
Reference in New Issue
Block a user