Initial commit
bulk data dump
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
## build TinyMOD
|
||||
## jbs - paragonsoft
|
||||
|
||||
all: tinymod
|
||||
|
||||
tinymod: tinymod.cpp
|
||||
## g++ -o tinymod tinymod.cpp
|
||||
g++ -o tinymod `pkg-config --libs alsa` tinymod.cpp -lm -L . -l:libportaudio.a
|
||||
clean:
|
||||
rm tinymod
|
||||
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
+612
@@ -0,0 +1,612 @@
|
||||
// ================================ Define ModPlayer Class (MOD Player) ===============================================
|
||||
|
||||
class ModPlayer // ModPlayer class
|
||||
{
|
||||
private:
|
||||
Paula *P; // create instance of Paula
|
||||
|
||||
static sInt BasePTable[5 * 12 + 1]; // base period table (5 octaves x 12 semitones + extra 1)
|
||||
static sInt PTable[16][60]; // period table (16 period tables x 60 semitones (5 x 12)
|
||||
static sInt VibTable[3][15][64]; // vibrato table (vib waveform, vib amplitude, vib position)
|
||||
|
||||
// ** tick related
|
||||
sInt Speed; // speed
|
||||
sInt TickRate; // tick rate
|
||||
sInt TRCounter; // tick rate counter
|
||||
|
||||
sInt CurTick; // current tick
|
||||
sInt CurRow; // current row
|
||||
sInt CurPos; // current song position
|
||||
sInt Delay; // delay?
|
||||
|
||||
// ** sample related
|
||||
sS8 *SData[32]; // sample data
|
||||
sInt SampleCount; // sample count
|
||||
sInt ChannelCount; // channel count
|
||||
|
||||
// ** pattern related
|
||||
sU8 PatternList[128]; // pattern list
|
||||
sInt PositionCount; // position count
|
||||
sInt PatternCount; // pattern count
|
||||
|
||||
// ################################################ SAMPLE STRUCTURE ################################################
|
||||
private:
|
||||
struct Sample
|
||||
{
|
||||
char Name[22]; // sample name
|
||||
sU16 Length; // sample length
|
||||
sS8 Finetune; // sample finetune
|
||||
sU8 Volume; // sample volume
|
||||
sU16 LoopStart; // sample loop start position
|
||||
sU16 LoopLen; // sample loop length
|
||||
|
||||
// ************ PREPARE ************
|
||||
void Prepare () // take care on endianness issues
|
||||
{
|
||||
sSwapEndian (Length); // swap high & low bytes
|
||||
sSwapEndian (LoopStart); // swap high & low bytes
|
||||
sSwapEndian (LoopLen); // swap high & low bytes
|
||||
Finetune &= 0x0f;
|
||||
if (Finetune >= 8) Finetune -= 16; // sample finetune is between -8 & 7
|
||||
}
|
||||
} *Samples; // Sample structure end
|
||||
// Sample *Samples;
|
||||
|
||||
// ################################################ PATTERN STRUCTURE ################################################
|
||||
private:
|
||||
struct Pattern // pattern structure
|
||||
{
|
||||
struct Event
|
||||
{
|
||||
sInt Sample; // sample
|
||||
sInt Note; // note
|
||||
sInt FX; // effect
|
||||
sInt FXParm; // effect paramater
|
||||
} Events[64][4];
|
||||
|
||||
// ************ PATTERN CONSTRUCTOR ************
|
||||
Pattern ()
|
||||
{
|
||||
sZeroMem (this, sizeof (Pattern)); // pattern constructor
|
||||
}
|
||||
|
||||
// ************ LOAD ************
|
||||
void Load (sU8 *ptr) // Load start
|
||||
{
|
||||
for (sInt row = 0; row < 64; row++)
|
||||
for (sInt ch = 0; ch < 4; ch++)
|
||||
{
|
||||
Event &e = Events[row][ch];
|
||||
e.Sample = (ptr[0] & 0xf0) | (ptr[2] >> 4); // sample
|
||||
e.FX = ptr[2] & 0x0f; // effect
|
||||
e.FXParm = ptr[3]; // effect paramater
|
||||
|
||||
e.Note = 0; // note
|
||||
sInt period = (sInt (ptr[0] & 0x0f) << 8) | ptr[1];
|
||||
sInt bestd = sAbs (period - BasePTable[0]);
|
||||
if (period)
|
||||
for (sInt i = 1; i <= 60; i++)
|
||||
{
|
||||
sInt d = sAbs (period - BasePTable[i]);
|
||||
if (d < bestd)
|
||||
{
|
||||
bestd = d;
|
||||
e.Note = i;
|
||||
}
|
||||
} // period
|
||||
|
||||
ptr += 4;
|
||||
}
|
||||
} // Load end
|
||||
} Patterns[128]; // Pattern structure end
|
||||
// Pattern Patterns[128]; // patterns
|
||||
|
||||
// ################################################ CHANNEL STRUCTURE ################################################
|
||||
private:
|
||||
struct Chan // Channel Structure start
|
||||
{
|
||||
sInt Note; // note
|
||||
sInt Period; // period
|
||||
sInt Sample; // sample
|
||||
sInt FineTune; // fine tune
|
||||
sInt Volume; // volume
|
||||
sInt FXBuf[16]; // effects buffer
|
||||
sInt FXBuf14[16]; // effects buffer (command 14 - extend)
|
||||
sInt LoopStart; // loop start
|
||||
sInt LoopCount; // loop count
|
||||
sInt RetrigCount; // retrigger count
|
||||
sInt VibWave; // vibrato waveform
|
||||
sInt VibRetr; // vibrato retrigger
|
||||
sInt VibPos; // vibrato position
|
||||
sInt VibAmpl; // vibrato amplitude
|
||||
sInt VibSpeed; // vibrato speed
|
||||
sInt TremWave; // tremolo waveform
|
||||
sInt TremRetr; // tremolo retrigger
|
||||
sInt TremPos; // tremolo position
|
||||
sInt TremAmpl; // tremolo amplitude
|
||||
sInt TremSpeed; // tremolo speed
|
||||
|
||||
// ************ CONSTRUCTOR ************
|
||||
Chan () { sZeroMem (this, sizeof (Chan)); } // channel constructor
|
||||
|
||||
// ************ GET PERIOD ************
|
||||
sInt GetPeriod (sInt offs = 0, sInt fineoffs = 0) // Get Period
|
||||
{
|
||||
sInt ft = FineTune + fineoffs; // fintune offset
|
||||
while (ft > 7)
|
||||
{
|
||||
offs++; // offset
|
||||
ft -= 16;
|
||||
}
|
||||
while (ft < -8)
|
||||
{
|
||||
offs--;
|
||||
ft += 16;
|
||||
}
|
||||
return Note ? (PTable[ft & 0x0f][sClamp (Note + offs - 1, 0, 59)]) : 0;
|
||||
}
|
||||
|
||||
// ************ SET PERIOD ************
|
||||
void SetPeriod (sInt offs = 0, sInt fineoffs = 0) // Set Period
|
||||
{
|
||||
if (Note)
|
||||
Period = GetPeriod (offs, fineoffs);
|
||||
}
|
||||
} Chans[4]; // Channel Structure end
|
||||
|
||||
// ************ CALCULATE TICK RATE ************
|
||||
private:
|
||||
void CalcTickRate (sInt bpm) // calculate tick rate start
|
||||
{
|
||||
TickRate = (125 * OUTRATE) / (bpm * OUTFPS);
|
||||
} // calculate tick rate end
|
||||
|
||||
// ************ TRIGGER NOTE ************
|
||||
private:
|
||||
void TrigNote (sInt ch, const Pattern::Event &e) // trigger note (channel, event)
|
||||
{
|
||||
Chan &c = Chans[ch]; // channel
|
||||
Paula::Voice &v = P->V[ch]; // Paula::Voice &v = P->[ch]; P is instance of Paula class,
|
||||
// v is voice, ch is channel - reference (alias)
|
||||
const Sample &s = Samples[c.Sample];
|
||||
sInt offset = 0;
|
||||
|
||||
if (e.FX == 9)
|
||||
offset = c.FXBuf[9] << 8;
|
||||
if (e.FX != 3 && e.FX != 5)
|
||||
{
|
||||
c.SetPeriod ();
|
||||
if (s.LoopLen > 1)
|
||||
v.Trigger (SData[c.Sample], 2 * (s.LoopStart + s.LoopLen), 2 * s.LoopLen, offset);
|
||||
else
|
||||
v.Trigger (SData[c.Sample], v.SampleLen = 2 * s.Length, 1, offset);
|
||||
if (!c.VibRetr)
|
||||
c.VibPos = 0;
|
||||
if (!c.TremRetr)
|
||||
c.TremPos = 0;
|
||||
}
|
||||
} // Trigger note end
|
||||
|
||||
// ************ RESET ************
|
||||
private:
|
||||
void Reset () // reset function
|
||||
{
|
||||
CalcTickRate (125); // default tick rate = 125
|
||||
Speed = 6; // default speed = 6
|
||||
TRCounter = 0; // tick rate counter = 0
|
||||
CurTick = 0; // current tick = 0
|
||||
CurRow = 0; // current row = 0
|
||||
CurPos = 0; // current song position = 0
|
||||
Delay = 0; // delay = 0
|
||||
}
|
||||
|
||||
|
||||
// ************ TICK ************
|
||||
private:
|
||||
void Tick () // start tick routine, cycle (50Hz, 20ms)
|
||||
{
|
||||
const Pattern &p = Patterns[PatternList[CurPos]];
|
||||
const Pattern::Event *re = p.Events[CurRow];
|
||||
for (sInt ch = 0; ch < 4; ch++)
|
||||
{
|
||||
const Pattern::Event &e = re[ch];
|
||||
Paula::Voice &v = P->V[ch];
|
||||
Chan &c = Chans[ch];
|
||||
const sInt fxpl = e.FXParm & 0x0f; // pattern list
|
||||
sInt TremVol = 0;
|
||||
if (!CurTick)
|
||||
{
|
||||
if (e.Sample)
|
||||
{
|
||||
c.Sample = e.Sample;
|
||||
c.FineTune = Samples[c.Sample].Finetune;
|
||||
c.Volume = Samples[c.Sample].Volume;
|
||||
}
|
||||
|
||||
if (e.FXParm)
|
||||
c.FXBuf[e.FX] = e.FXParm;
|
||||
|
||||
if (e.Note && (e.FX != 14 || ((e.FXParm >> 4) != 13)))
|
||||
{
|
||||
c.Note = e.Note;
|
||||
TrigNote (ch, e);
|
||||
}
|
||||
|
||||
switch (e.FX)
|
||||
{
|
||||
case 4: // vibrato (4) / vibrato + volume slide (6)
|
||||
case 6:
|
||||
|
||||
if (c.FXBuf[4] & 0x0f)
|
||||
c.VibAmpl = c.FXBuf[4] & 0x0f;
|
||||
if (c.FXBuf[4] & 0xf0)
|
||||
c.VibSpeed = c.FXBuf[4] >> 4;
|
||||
c.SetPeriod (0,
|
||||
VibTable[c.VibWave][(c.VibAmpl) - 1][c.VibPos]);
|
||||
break;
|
||||
|
||||
case 7: // tremolo (7)
|
||||
if (c.FXBuf[7] & 0x0f)
|
||||
c.TremAmpl = c.FXBuf[7] & 0x0f;
|
||||
if (c.FXBuf[7] & 0xf0)
|
||||
c.TremSpeed = c.FXBuf[7] >> 4;
|
||||
TremVol = VibTable[c.TremWave][(c.TremAmpl) - 1][c.TremPos];
|
||||
break;
|
||||
|
||||
case 12: // set volume (C)
|
||||
c.Volume = sClamp (e.FXParm, 0, 64);
|
||||
break;
|
||||
|
||||
case 14: // special (Exx)
|
||||
// (E0X - turn filter on/off),
|
||||
// (E1x - porta up, fine),
|
||||
// (E2x - porta down, fine),
|
||||
// (E3x - glissando control),
|
||||
// (E4x - vibratio waveform),
|
||||
// (E5x - set finetune),
|
||||
// (E6x - pattern loop),
|
||||
// (E7x - tremolo waveform),
|
||||
// (E8x - not implemented),
|
||||
// (E9x - retrigger note),
|
||||
// (EAx - volume slide up, fine),
|
||||
// (EBx - volume slide down, fine),
|
||||
// (ECx - cut note),
|
||||
// (EDx - delay note),
|
||||
// (EEx - pattern delay),
|
||||
// (EFx - not implemented)
|
||||
if (fxpl)
|
||||
c.FXBuf14[e.FXParm >> 4] = fxpl;
|
||||
switch (e.FXParm >> 4)
|
||||
{
|
||||
case 0: // set filter (0x)
|
||||
break;
|
||||
|
||||
case 1: // fineslide up (1x)
|
||||
c.Period = sMax (113, c.Period - c.FXBuf14[1]);
|
||||
break;
|
||||
|
||||
case 2: // slide down (2x)
|
||||
c.Period = sMin (856, c.Period + c.FXBuf14[2]);
|
||||
break;
|
||||
|
||||
case 3: // set glissando sucks! (0/1)
|
||||
break;
|
||||
|
||||
case 4: // set vib waveform (1/2)
|
||||
c.VibWave = fxpl & 3;
|
||||
if (c.VibWave == 3)
|
||||
c.VibWave = 0;
|
||||
c.VibRetr = fxpl & 4;
|
||||
break;
|
||||
|
||||
case 5: // set finetune
|
||||
c.FineTune = fxpl;
|
||||
if (c.FineTune >= 8)
|
||||
c.FineTune -= 16;
|
||||
break;
|
||||
|
||||
case 7: // set tremolo (1/2)
|
||||
c.TremWave = fxpl & 3;
|
||||
if (c.TremWave == 3)
|
||||
c.TremWave = 0;
|
||||
c.TremRetr = fxpl & 4;
|
||||
break;
|
||||
|
||||
case 9: // retrigger
|
||||
if (c.FXBuf14[9] && !e.Note)
|
||||
TrigNote (ch, e);
|
||||
c.RetrigCount = 0;
|
||||
break;
|
||||
|
||||
case 10: // fine volslide up
|
||||
c.Volume = sMin (c.Volume + c.FXBuf14[10], 64);
|
||||
break;
|
||||
|
||||
case 11: // fine volslide down;
|
||||
c.Volume = sMax (c.Volume - c.FXBuf14[11], 0);
|
||||
break;
|
||||
|
||||
case 14: // delay pattern
|
||||
Delay = c.FXBuf14[14];
|
||||
break;
|
||||
|
||||
case 15: // invert loop (WTF)
|
||||
break;
|
||||
|
||||
}
|
||||
break; // case 14 end
|
||||
|
||||
case 15: // set speed (F)
|
||||
if (e.FXParm)
|
||||
if (e.FXParm <= 32)
|
||||
Speed = e.FXParm;
|
||||
else
|
||||
CalcTickRate (e.FXParm);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (e.FX)
|
||||
{
|
||||
case 0: // arpeggio (or normal play)
|
||||
if (e.FXParm)
|
||||
{
|
||||
sInt no = 0;
|
||||
switch (CurTick % 3)
|
||||
{
|
||||
case 1:
|
||||
no = e.FXParm >> 4;
|
||||
break;
|
||||
case 2:
|
||||
no = e.FXParm & 0x0f;
|
||||
break;
|
||||
}
|
||||
c.SetPeriod (no);
|
||||
}
|
||||
break;
|
||||
|
||||
case 1: // slide up
|
||||
c.Period = sMax (113, c.Period - c.FXBuf[1]);
|
||||
break;
|
||||
|
||||
case 2: // slide down
|
||||
c.Period = sMin (856, c.Period + c.FXBuf[2]);
|
||||
break;
|
||||
|
||||
case 5: // Tone Portamento + volume slide slide
|
||||
if (c.FXBuf[5] & 0xf0)
|
||||
c.Volume = sMin (c.Volume + (c.FXBuf[5] >> 4), 0x40);
|
||||
else
|
||||
c.Volume = sMax (c.Volume - (c.FXBuf[5] & 0x0f), 0);
|
||||
// no break!
|
||||
case 3: // tone portamento (slide to note) slide speed
|
||||
{
|
||||
sInt np = c.GetPeriod ();
|
||||
if (c.Period > np)
|
||||
c.Period = sMax (c.Period - c.FXBuf[3], np);
|
||||
else if (c.Period < np)
|
||||
c.Period = sMin (c.Period + c.FXBuf[3], np);
|
||||
}
|
||||
break;
|
||||
|
||||
case 6: // vibrato plus volslide
|
||||
if (c.FXBuf[6] & 0xf0)
|
||||
c.Volume = sMin (c.Volume + (c.FXBuf[6] >> 4), 0x40);
|
||||
else
|
||||
c.Volume = sMax (c.Volume - (c.FXBuf[6] & 0x0f), 0);
|
||||
// no break!
|
||||
case 4: // vibrato (speed + depth)
|
||||
c.SetPeriod (0, VibTable[c.VibWave][c.VibAmpl - 1][c.VibPos]);
|
||||
c.VibPos = (c.VibPos + c.VibSpeed) & 0x3f;
|
||||
break;
|
||||
|
||||
case 7: // tremolo (rate + depth)
|
||||
TremVol = VibTable[c.TremWave][c.TremAmpl - 1][c.TremPos];
|
||||
c.TremPos = (c.TremPos + c.TremSpeed) & 0x3f;
|
||||
break;
|
||||
|
||||
case 10: // volume slide
|
||||
if (c.FXBuf[10] & 0xf0)
|
||||
c.Volume = sMin (c.Volume + (c.FXBuf[10] >> 4), 0x40);
|
||||
else
|
||||
c.Volume = sMax (c.Volume - (c.FXBuf[10] & 0x0f), 0);
|
||||
break;
|
||||
|
||||
case 11: // position jump
|
||||
if (CurTick == Speed - 1)
|
||||
{
|
||||
CurRow = -1;
|
||||
CurPos = e.FXParm;
|
||||
}
|
||||
break;
|
||||
|
||||
case 13: // pattern break
|
||||
if (CurTick == Speed - 1)
|
||||
{
|
||||
CurPos++;
|
||||
CurRow = (10 * (e.FXParm >> 4) + (e.FXParm & 0x0f)) - 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case 14: // set filter (special)
|
||||
switch (e.FXParm >> 4)
|
||||
{
|
||||
case 6: // loop pattern
|
||||
if (!fxpl) // loop start
|
||||
c.LoopStart = CurRow;
|
||||
else if (CurTick == Speed - 1)
|
||||
{
|
||||
if (c.LoopCount < fxpl)
|
||||
{
|
||||
CurRow = c.LoopStart - 1;
|
||||
c.LoopCount++;
|
||||
}
|
||||
else
|
||||
c.LoopCount = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case 9: // set sample offset (re-trigger)
|
||||
if (++c.RetrigCount == c.FXBuf14[9])
|
||||
{
|
||||
c.RetrigCount = 0;
|
||||
TrigNote (ch, e);
|
||||
}
|
||||
break;
|
||||
|
||||
case 12: // set volume
|
||||
if (CurTick == c.FXBuf14[12])
|
||||
c.Volume = 0;
|
||||
break;
|
||||
|
||||
case 13: // pattern break
|
||||
if (CurTick == c.FXBuf14[13])
|
||||
TrigNote (ch, e);
|
||||
break;
|
||||
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
v.Volume = sClamp (c.Volume + TremVol, 0, 64);
|
||||
v.Period = c.Period;
|
||||
}
|
||||
|
||||
CurTick++;
|
||||
if (CurTick >= Speed * (Delay + 1))
|
||||
{
|
||||
CurTick = 0;
|
||||
CurRow++;
|
||||
Delay = 0;
|
||||
}
|
||||
if (CurRow >= 64)
|
||||
{
|
||||
CurRow = 0;
|
||||
CurPos++;
|
||||
}
|
||||
if (CurPos >= PositionCount)
|
||||
CurPos = 0;
|
||||
}; // end tick routine
|
||||
|
||||
// ************ MODPLAYER CONSTRUCTOR ************
|
||||
public:
|
||||
char Name[21]; // song name
|
||||
|
||||
ModPlayer (Paula *p, sU8 *moddata) : P (p) // ModPlayer constructor (paula object and MOD data)
|
||||
{
|
||||
for (sInt ft = 0; ft < 16; ft++) // calc ptable (period table) - finetune
|
||||
{
|
||||
sInt rft = -((ft >= 8) ? ft - 16 : ft);
|
||||
sF32 fac = sFPow (2.0f, sF32 (rft) / (12.0f * 16.0f));
|
||||
for (sInt i = 0; i < 60; i++)
|
||||
PTable[ft][i] = sInt (sF32 (BasePTable[i]) * fac + 0.5f);
|
||||
}
|
||||
|
||||
for (sInt ampl = 0; ampl < 15; ampl++) // calc vibtable - vibrato amplitude
|
||||
{
|
||||
sF32 scale = ampl + 1.5f;
|
||||
sF32 shift = 0;
|
||||
for (sInt x = 0; x < 64; x++)
|
||||
{
|
||||
VibTable[0][ampl][x] = sInt (scale * sFSin (x * sFPi / 32.0f) + shift);
|
||||
VibTable[1][ampl][x] = sInt (scale * ((63 - x) / 31.5f - 1.0f) + shift);
|
||||
VibTable[2][ampl][x] = sInt (scale * ((x < 32) ? 1 : -1) + shift);
|
||||
}
|
||||
}
|
||||
// == "load" the mod
|
||||
memcpy (Name, moddata, 20); // copy from source to destination
|
||||
Name[20] = 0;
|
||||
moddata += 20; // begining of sample data
|
||||
|
||||
SampleCount = 32; // 32 samples (default was 16)
|
||||
ChannelCount = 4; // 4 channels
|
||||
Samples = (Sample *)(moddata - sizeof (Sample));
|
||||
moddata += 15 * sizeof (Sample); // number of positions
|
||||
sU32 &tag = *(sU32 *)(moddata + 130 + 16 * sizeof (Sample)); // get tag info (treat result as unsigned 32 then dereference)
|
||||
switch (tag) // magic number / signature / i.d. / tag string
|
||||
{
|
||||
case '.K.M': // Michael Kleps (M.K.)
|
||||
case '4TLF': // Startrekker 4 channel (fairlight) (FLT4)
|
||||
case '!K!M': // more than 100 patterns (M!K!)
|
||||
SampleCount = 32; // 32 samples if M.K. FLT4, M!K!
|
||||
break;
|
||||
}
|
||||
if (SampleCount > 16)
|
||||
moddata += (SampleCount - 16) * sizeof (Sample); // moddata=moddata+(Sampl.......
|
||||
|
||||
for (sInt i = 1; i < SampleCount; i++)
|
||||
Samples[i].Prepare ();
|
||||
|
||||
PositionCount = *moddata;
|
||||
moddata += 2; // + skip unused byte
|
||||
memcpy (PatternList, moddata, 128);
|
||||
moddata += 128;
|
||||
if (SampleCount > 15)
|
||||
moddata += 4; // skip tag
|
||||
|
||||
PatternCount = 0;
|
||||
for (sInt i = 0; i < 128; i++)
|
||||
PatternCount = sClamp (PatternCount, PatternList[i] + 1, 128);
|
||||
|
||||
for (sInt i = 0; i < PatternCount; i++)
|
||||
{
|
||||
Patterns[i].Load (moddata);
|
||||
moddata += 1024;
|
||||
}
|
||||
|
||||
sZeroMem (SData, sizeof (SData)); // zap memory?
|
||||
for (sInt i = 1; i < SampleCount; i++)
|
||||
{
|
||||
SData[i] = (sS8 *)moddata;
|
||||
moddata += 2 * Samples[i].Length;
|
||||
}
|
||||
|
||||
Reset ();
|
||||
} // ModPlayer constructor end
|
||||
|
||||
// ************ RENDER output************
|
||||
sU32 Render (sF32 *buf, sU32 len) // Render paramaters (pointer to buffer and length of buffer)
|
||||
{
|
||||
while (len)
|
||||
{
|
||||
sInt todo = sMin<sInt> (len, TRCounter); // sMin function using template
|
||||
|
||||
if (todo)
|
||||
{
|
||||
P->Render (buf, todo); // paula buffer and todo
|
||||
buf += 2 * todo;
|
||||
len -= todo;
|
||||
TRCounter -= todo; // tick rate counter
|
||||
}
|
||||
else
|
||||
{
|
||||
Tick ();
|
||||
TRCounter = TickRate;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
} // Render end
|
||||
|
||||
// ************ CALLBACK FUNCTION ************
|
||||
static sU32 __stdcall RenderProxy (void *parm, sF32 *buf, sU32 len) // (modplayer is parm)
|
||||
{
|
||||
return ((ModPlayer *)parm)->Render (buf, len); // typecast void pointer (parm) into modplayer object and access 'Render' member.
|
||||
} // (returns buffer to the audio out)
|
||||
};
|
||||
|
||||
// ************ PERIOD TABLE ************
|
||||
|
||||
sInt ModPlayer::BasePTable[61] = // scope resolution (belongs to ModPlayer class)
|
||||
{
|
||||
0, // finetune = 0
|
||||
1712, 1616, 1525, 1440, 1357, 1281, 1209, 1141, 1077, 1017, 961, 907, // C-0 to B-0 (octave 0)
|
||||
856, 808, 762, 720, 678, 640, 604, 570, 538, 508, 480, 453, // C-1 to B-1 (octave 1)
|
||||
428, 404, 381, 360, 339, 320, 302, 285, 269, 254, 240, 226, // C-2 to B-2 (octave 2)
|
||||
214, 202, 190, 180, 170, 160, 151, 143, 135, 127, 120, 113, // C-3 to B-3 (octave 3)
|
||||
107, 101, 95, 90, 85, 80, 76, 71, 67, 64, 60, 57, // C-4 to B-4 (octave 4)
|
||||
};
|
||||
|
||||
sInt ModPlayer::PTable[16][60];
|
||||
sInt ModPlayer::VibTable[3][15][64];
|
||||
@@ -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
|
||||
};
|
||||
+1228
File diff suppressed because it is too large
Load Diff
+247
@@ -0,0 +1,247 @@
|
||||
/*
|
||||
|
||||
TinyMOD
|
||||
Written by Tammo "kb" Hinrichs in 2007
|
||||
This source code is hereby placed into the public domain. Use, distribute,
|
||||
modify, misappropriate and generally abuse it as you wish. Giving credits
|
||||
would be nice of course.
|
||||
|
||||
This player includes an Amiga Paula chip "emulation" that faithfully recreates
|
||||
how it sounds when a sample is resampled using a master clock of 3.5 MHz. Yes,
|
||||
rendering at this rate and downsampling to the usual 48KHz takes quite a bit
|
||||
of CPU. Feel free to replace this part with some conventional mixing routines
|
||||
if authenticity isn't your goal and you really need Protracker MOD support
|
||||
for any other reason...
|
||||
|
||||
The code should be pretty portable, all OS/platform dependent stuff is
|
||||
at the top. Code for testing is at the bottom.
|
||||
|
||||
You'll need some kind of sound output that calls back the player providing
|
||||
it a stereo interleaved single float buffer to write into (0dB=1.0).
|
||||
|
||||
Changelog:
|
||||
|
||||
2024-03-09:
|
||||
* modifications by Jason Bou-Samra
|
||||
* changes to main() routine to make executable from Linux command line interface
|
||||
* modularised paula and modplayer classes into seperate files
|
||||
* added sound output using port audio
|
||||
* sprinkled comments throughout source code, and general source formatting
|
||||
* created makefile
|
||||
|
||||
2007-12-07:
|
||||
* fixed 40x and 4x0 vibrato effects (jogeir - tiny tunes)
|
||||
* fixed pattern loop (olof gustafsson - pinball illusions)
|
||||
* fixed fine volslide down (olof gustafsson - pinball illusions)
|
||||
* included some external header files
|
||||
* cleanups
|
||||
|
||||
2007-12-06: first "release". Note to self: Don't post stuff on pouet.net when drunk.
|
||||
|
||||
*/
|
||||
|
||||
// TO COMPILE: g++ -o tinymod `pkg-config --libs alsa` tinymod.cpp -lm -L . -l:libportaudio.a
|
||||
|
||||
// =================== system dependent stuff starts here =====================
|
||||
// Multicharacter literal
|
||||
#pragma GCC diagnostic ignored "-Wmultichar"
|
||||
#pragma intrinsic(memset, sqrt, sin, cos, atan, powf) // memory set, square root, SINe, COSine, Arc TAngent, power of
|
||||
|
||||
|
||||
// ### defines
|
||||
#define __stdcall
|
||||
#define __cdecl
|
||||
|
||||
#define NUM_SECONDS (1000)
|
||||
#define SAMPLE_RATE (96000)
|
||||
#define FRAMES_PER_BUFFER (0x10000)
|
||||
|
||||
#define cls() printf("\033[H\033[J") // control chars to clear screen
|
||||
|
||||
// ### includes
|
||||
#include <inttypes.h> // fixed size integer types (part of c standard library)
|
||||
#include <math.h> // basic mathematical operations (part of c standard library) - mostly floating point
|
||||
#include <stdio.h> // standard I/O library
|
||||
#include <string.h> // string handling (part of c standard library)
|
||||
#include <unistd.h> // (unix standard)
|
||||
#include <fcntl.h> // (file control options) - used by open()
|
||||
#include <sys/stat.h> // needed by stat function (get file status)
|
||||
#include <cstdint> // defines set of integeral types
|
||||
#include <cstdio> // defines set of C standard I/O types
|
||||
#include "portaudio.h" // port audio
|
||||
#include "types.h" // various type definitions
|
||||
#include "paula.h" // Amiga "Paula" sound hardware emulator
|
||||
#include "modplayer.h" // MOD play routine
|
||||
|
||||
// ## declerations
|
||||
void error(PaError err1); // declare port audio error handling function
|
||||
|
||||
// ***********************************************
|
||||
// ** handle Command Line Interface (CLI) stuff **
|
||||
// ***********************************************
|
||||
int __cdecl main (int argc, const char **argv) // main start
|
||||
{
|
||||
// freopen("warning.log", "w", stderr); // surpress console messages
|
||||
const char* filename = argv[1]; // only one argument and command
|
||||
|
||||
if (argc != 2){ // if less than 2 arguments passed on command line, display usage
|
||||
printf("Usage: tinymod [<mod name>|OPTION]\n\
|
||||
tinymod --help for help\n");
|
||||
return 1;
|
||||
} // display usage, then exit
|
||||
|
||||
if(!strcmp(filename, "--about")) {
|
||||
printf("TinyMOD\n"); return 0;
|
||||
} // display about, then exit
|
||||
|
||||
if(!strcmp (filename, "--help")) {
|
||||
printf("Usage: tinymod [<mod name>|OPTION]\n\n\
|
||||
An Amiga MOD file player that tries to replicate the authentic sound\n\
|
||||
charateristics of an Amiga via software emulation of the Paula chip.\n\n\
|
||||
OPTIONS\n\
|
||||
--about displays about message\n\
|
||||
--help displays this help message\n"); return 0;
|
||||
} // display help, then exit
|
||||
|
||||
// **********************
|
||||
// ** load MOD file **
|
||||
// **********************
|
||||
static sU8 mod[4 * 1024 * 1024]; // 4MB unsigned character array to hold MOD file
|
||||
|
||||
FILE* fh = fopen(filename, "rb");
|
||||
if (!fh) {
|
||||
perror("fopen");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
struct stat sb;
|
||||
if (stat(filename, &sb) == -1) {
|
||||
perror("stat");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
fread(mod, sb.st_size, 1, fh);
|
||||
fclose(fh);
|
||||
|
||||
// **********************
|
||||
// ** port audio setup **
|
||||
// **********************
|
||||
PaStreamParameters outputParameters;
|
||||
PaStream *stream;
|
||||
PaError err;
|
||||
|
||||
float buffer[FRAMES_PER_BUFFER][2]; // stereo output buffer
|
||||
int left_phase = 0;
|
||||
int right_phase = 0;
|
||||
int left_inc = 1;
|
||||
int right_inc = 1; // higher pitch so we can distinguish left and right.
|
||||
int i, j, k; // for indexing
|
||||
int bufferCount;
|
||||
static const int BUFFERLEN = 0x10000; // buffer length
|
||||
sInt nwrite = 0x10000; // number of samples
|
||||
sF32 mixbuffer[BUFFERLEN]; // sample buffer
|
||||
|
||||
err = Pa_Initialize(); // initialize
|
||||
if( err != paNoError ) error(err);
|
||||
|
||||
outputParameters.device = Pa_GetDefaultOutputDevice(); // default output device
|
||||
if (outputParameters.device == paNoDevice) {
|
||||
fprintf(stderr,"Error: No default output device.\n");
|
||||
error(err);
|
||||
}
|
||||
outputParameters.channelCount = 2; // stereo output
|
||||
outputParameters.sampleFormat = paFloat32; // 32 bit floating point output
|
||||
outputParameters.suggestedLatency = 0.050; // Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
|
||||
outputParameters.hostApiSpecificStreamInfo = NULL;
|
||||
|
||||
err = Pa_OpenStream(
|
||||
&stream,
|
||||
NULL, // no input
|
||||
&outputParameters,
|
||||
SAMPLE_RATE,
|
||||
FRAMES_PER_BUFFER,
|
||||
paClipOff, // we won't output out of range samples so don't bother clipping them
|
||||
NULL, // no callback, use blocking API
|
||||
NULL ); // no callback, so no callback userData
|
||||
if( err != paNoError ) error(err);
|
||||
|
||||
err = Pa_StartStream( stream );
|
||||
if( err != paNoError ) error(err);
|
||||
|
||||
// *********************
|
||||
// ** play setup/loop **
|
||||
// *********************
|
||||
Paula P; // instantiate paula
|
||||
ModPlayer player (&P, mod); // instantiate paramaterised constructior ModPlayer
|
||||
cls(); // clear screen
|
||||
printf("TinyMOD\n");
|
||||
printf ("currently playing: %s\n", player.Name); // display details of MOD being played
|
||||
printf("Playing for %d seconds.\n", NUM_SECONDS );
|
||||
printf("^C to stop\n");
|
||||
// printf("PortAudio: SR = %d, BufSize = %d\n", SAMPLE_RATE, FRAMES_PER_BUFFER);
|
||||
|
||||
bufferCount = ((NUM_SECONDS * SAMPLE_RATE) / FRAMES_PER_BUFFER); // determine buffer loads
|
||||
|
||||
for( i=0; i < bufferCount; i++ ) // countdown buffers
|
||||
{
|
||||
player.RenderProxy(&player, mixbuffer, nwrite/2); // MOD player
|
||||
for( j=0; j < FRAMES_PER_BUFFER; j++ ) // copy samples to buffer
|
||||
{
|
||||
buffer[j][0] = mixbuffer[left_phase]; // left
|
||||
buffer[j][1] = mixbuffer[right_phase]; // right
|
||||
left_phase += left_inc;
|
||||
right_phase += right_inc;
|
||||
if( left_phase >= FRAMES_PER_BUFFER ) left_phase -= FRAMES_PER_BUFFER;
|
||||
if( right_phase >= FRAMES_PER_BUFFER ) right_phase -= FRAMES_PER_BUFFER;
|
||||
}
|
||||
|
||||
// left_phase = right_phase = 0;
|
||||
|
||||
err = Pa_WriteStream( stream, buffer, FRAMES_PER_BUFFER ); // transfer buffer to stream
|
||||
if( err != paNoError ) error(err);
|
||||
} // keep going
|
||||
|
||||
// *************************
|
||||
// ** port audio shutdown **
|
||||
// *************************
|
||||
err = Pa_StopStream( stream );
|
||||
if( err != paNoError ) error(err);
|
||||
|
||||
// ++left_inc;
|
||||
// ++right_inc;
|
||||
|
||||
Pa_Sleep( 1000 ); // mainly for test purposes so we can hear sound
|
||||
|
||||
err = Pa_CloseStream( stream );
|
||||
if( err != paNoError ) error(err);
|
||||
|
||||
Pa_Terminate();
|
||||
printf("Bye bye!.\n");
|
||||
|
||||
return err; // that's all folks, all done
|
||||
}
|
||||
|
||||
// **********************
|
||||
// ** hidden message **
|
||||
// **********************
|
||||
char author[] = { "tinymod" };
|
||||
const char* text = "09/03/2023";
|
||||
|
||||
// *******************************
|
||||
// ** error handling routine **
|
||||
// *******************************
|
||||
void error(PaError err1)
|
||||
{
|
||||
fprintf( stderr, "An error occured while using the portaudio stream\n" );
|
||||
fprintf( stderr, "Error number: %d\n", err1 );
|
||||
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err1 ) );
|
||||
// Print more information about the error.
|
||||
if( err1 == paUnanticipatedHostError )
|
||||
{
|
||||
const PaHostErrorInfo *hostErrorInfo = Pa_GetLastHostErrorInfo();
|
||||
fprintf( stderr, "Host API error = #%ld, hostApiType = %d\n", hostErrorInfo->errorCode, hostErrorInfo->hostApiType );
|
||||
fprintf( stderr, "Host API error = %s\n", hostErrorInfo->errorText );
|
||||
}
|
||||
Pa_Terminate();
|
||||
exit(1);
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
typedef int sInt; // signed Integer
|
||||
typedef unsigned int sUInt; // unsigned Integer
|
||||
|
||||
typedef sInt sBool; // signed Bool
|
||||
typedef char sChar; // signed Character
|
||||
|
||||
typedef signed char sS8; // 8 bit signed int
|
||||
typedef signed short sS16; // 16 bit signed int
|
||||
typedef signed long sS32; // 32 bit signed int
|
||||
typedef int64_t sS64; // 64 bit signed int (long long) (signed)
|
||||
|
||||
typedef unsigned char sU8; // 8 bit unsigned int
|
||||
typedef unsigned short sU16; // 16 bit unsigned int
|
||||
typedef unsigned long sU32; // 32 bit unsigned int
|
||||
typedef uint64_t sU64; // 64 bit unsigned int (long long) (unsigned)
|
||||
|
||||
typedef float sF32; // 32 bit signed float
|
||||
typedef double sF64; // 64 bit signed float
|
||||
|
||||
// #define _CRT_SECURE_NO_DEPRECATE // disable C Runtime Library deprecation warnings
|
||||
|
||||
inline void sZeroMem(void *dest, sInt size)
|
||||
{ memset(dest, 0, size);} // declare & define function to zero memory
|
||||
|
||||
inline sF32 sFSqrt(sF32 x)
|
||||
{ return sqrtf(x); } // 32 bit signed float square root
|
||||
|
||||
inline sF32 sFSin(sF32 x)
|
||||
{ return sinf(x); } // 32 bit signed float sine
|
||||
|
||||
inline sF32 sFCos(sF32 x)
|
||||
{ return cosf(x); } // 32 bit signed float cosine
|
||||
|
||||
inline sF32 sFAtan(sF32 x)
|
||||
{ return atanf(x); } // 32 bit signed float arc tangent
|
||||
|
||||
inline sF32 sFPow(sF32 b, sF32 e)
|
||||
{ return powf(b, e); } // 32 bit signed float base to the power exponent
|
||||
|
||||
inline void sSwapEndian(sU16 &v)
|
||||
{ v = ((v & 0xff) << 8) | (v >> 8); } // endian swap
|
||||
|
||||
const sF32 sFPi = 4 * sFAtan(1); // signed floating Pi
|
||||
|
||||
// =================== system dependent stuff ends here ========================
|
||||
|
||||
template <typename T> inline T sMin(const T a, const T b)
|
||||
{ return (a < b) ? a : b; } // min function, return smallest
|
||||
|
||||
template <typename T> inline T sMax(const T a, const T b)
|
||||
{ return (a > b) ? a : b; } // max function, return largest
|
||||
|
||||
template <typename T> inline T sClamp(const T x, const T min, const T max)
|
||||
{ return sMax(min, sMin(max, x)); } // variable clamp
|
||||
|
||||
template <typename T> T sSqr(T v)
|
||||
{ return v * v; } // square root
|
||||
|
||||
template <typename T> T sLerp(T a, T b, sF32 f)
|
||||
{ return a + f * (b - a); } // linear interpolation
|
||||
|
||||
template <typename T> T sAbs(T x)
|
||||
{ return abs(x); } // absolute value
|
||||
|
||||
inline sF32 sFSinc(sF32 x)
|
||||
{ return x ? sFSin(x) / x : 1; } // low pass filter: (sinc function) sin(x)/x or 1
|
||||
|
||||
inline sF32 sFHamming(sF32 x)
|
||||
{ return (x > -1 && x < 1) ? sSqr(sFCos(x * sFPi / 2)) : 0; } // hamming window: cos(X * PI / 2)^2 or 0
|
||||
|
||||
union sIntFlt {
|
||||
sU32 U32;
|
||||
sF32 F32;
|
||||
}; // union integer/float
|
||||
|
||||
const sInt PAULARATE = 3740000; // approx. pal timing amiga paula rate (3.546895MHz DAC base clock)
|
||||
const sInt OUTRATE = 48000; // approx. pal timing output rate (48Khz)
|
||||
const sInt OUTFPS = 50; // approx. pal timing frames per second (50Hz - PAL)
|
||||
Reference in New Issue
Block a user