// ================================ 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 (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];