SHARE
TWEET

kolibrimod (tiny .mod replayer)

8bitbubsy Sep 11th, 2016 (edited) 745 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /* kolibrimod - tiny 1..99ch .MOD replayer by 8bitbubsy - 5th of December 2019 (5:58PM) - remember to link winmm.lib!
  2. ** Missing effects: E3x (portamento type), E4x (vibrato type), E7x (tremolo type)
  3. ** Note: This is not very optimized, the main focus is amount of code lines + okay replayer accuracy */
  4.  
  5. #include <stdio.h>
  6. #include <stdlib.h>
  7. #include <string.h>
  8. #include <stdint.h>
  9. #include <stdbool.h>
  10. #include <conio.h> // _getch()
  11. #include <math.h> // powf(), sinf(), round()
  12. #include <windows.h> // Sleep(), mixer stuff
  13.  
  14. #define MIX_BUF_NUM 4
  15. #define MIX_BUF_LEN 4096
  16. #define MAX_VOICES 99
  17. #define MAX_NOTES 96
  18. #define FRAC_BITS 16 /* do not change! */
  19. #define PT_BASE_CLK 3546895 /* PAL */
  20. #define PT_MIN_PERIOD 113
  21. #define PT_MAX_PERIOD 856
  22. #define BASE_CLK 3579364 /* (8363*1712)/4 */
  23. #define MIN_PERIOD 28
  24. #define MAX_PERIOD 6848
  25. #define MIN_LOOP_LEN 4
  26. #define LERP8_TO_15(s1, s2, f) { s2 <<= 7; s1 <<= 7; s2 -= s1; s2 *= (int32_t)(f); s2 >>= FRAC_BITS; s1 += s2; }
  27. #define CONVERT_FINETUNE(x) (((x) >= 8) ? ((x) - 16) : (x))
  28. #define BPM2SAMPLES_PER_TICK(x) (int32_t)((((audioFreq * 2.5f) / (x))) + 0.5f)
  29. #define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
  30. #define CLAMP16(i) if ((int16_t)(i) != i) i = 0x7FFF ^ (i >> 31)
  31.  
  32. // we must round the new finetuned period to get correct .mod finetuned pitches
  33. #define PERIOD2DELTA(x) (((x) <= 0) ? 0 : (uint32_t)((((dBaseClk / round((x) * v->dPeriodMul)) * dHz2DeltaMul) + 0.5)))
  34.  
  35. typedef struct
  36. {
  37.     int8_t *sampleData, *swapData, vol;
  38.     int16_t panL, panR;
  39.     bool loopFlag, swapLoopFlag;
  40.     int32_t volL, volR;
  41.     uint32_t sampleEnd, loopStart, swapSampleEnd, swapLoopStart, oldPos, frac, delta, pos;
  42.     double dPeriodMul;
  43. } ptVoice_t;
  44.  
  45. typedef struct
  46. {
  47.     int8_t vol, ftune, *invLoopPtr, *sampleData, *invLoopStartPtr, jmpRow, jmpCnt;
  48.     uint8_t note, efx, efx2, portaMem, vibDepth, vibSpeed;
  49.     uint8_t tremoloDepth, tremoloSpeed, vibPos, tremoloPos, invLoopSpeed, invLoopPos;
  50.     int16_t rawPeriod, period, toPeriod;
  51.     uint32_t sampleOffsetMem, sampleOffset, invLoopLen, sampleLen, loopStart, loopLen;
  52. } ptChannel_t;
  53.  
  54. static volatile bool isMixing;
  55. static bool newPosFlag, patLoop, ptMode;
  56. static char songName[20 + 1];
  57. static int8_t modSpeed, pattNum, modDelayFactor, newRow, row, *mixerBuffer, *sampleDataPointers[31];
  58. static uint8_t *modData, order;
  59. static int16_t modTick, minPeriod, maxPeriod;
  60. static int32_t samplesLeft, pattSize, modChannels, samplesPerTick, audioFreq, mixBuffer[MIX_BUF_LEN / 2];
  61. static double dBaseClk, dHz2DeltaMul;
  62. static const uint8_t invLoopTable[16] = { 0, 5, 6, 7, 8, 10, 11, 13, 16, 19, 22, 26, 32, 43, 64, 128 };
  63. static const int16_t periods[MAX_NOTES] =
  64. {
  65.  6848,6464,6096,5760,5424,5120,4832,4560,4304,4064,3840,3624,3424,3232,3048,2880,2712,2560,2416,2280,2152,2032,1920,1812,
  66.  1712,1616,1524,1440,1356,1280,1208,1140,1076,1016, 960, 906, 856, 808, 762, 720, 678, 640, 604, 570, 538, 508, 480, 453,
  67.   428, 404, 381, 360, 339, 320, 302, 285, 269, 254, 240, 226, 214, 202, 190, 180, 170, 160, 151, 143, 135, 127, 120, 113,
  68.   107, 101,  95,  90,  85,  80,  75,  71,  67,  63,  60,  56, 53,  50,  47,  45,  42,  40,  37,  35,  33,  31,  30,  28
  69. };
  70. static ptVoice_t ptVoice[MAX_VOICES];
  71. static ptChannel_t ptChannel[MAX_VOICES];
  72. static WAVEHDR waveBlocks[MIX_BUF_NUM];
  73. static HWAVEOUT hWaveOut;
  74. static WAVEFORMATEX wfx;
  75.  
  76. static void tickReplayer(void);
  77.  
  78. // in: -128..127 (32-bit vars) - out: 15-bit -16384..16383 + spline overshoot
  79. #define CUBIC_SPLINE_3TAP(s1, s2, s3, f) \
  80. { \
  81.     int32_t s4; \
  82.     s2 <<= (8 - 1); \
  83.     s4 = ((s1 + s3) << (8 - 2)) - s2; \
  84.     s4 = ((s4 * (int32_t)(f)) >> (16 + 1)) + s2; \
  85.     s3 = (s1 + s3) << (8 - 2); \
  86.     s1 <<= (8 - 1); \
  87.     s3 = (s1 + s3) >> 1; \
  88.     s1 += ((s4 - s3) * (int32_t)(f)) >> (16 - 1); \
  89. } \
  90.  
  91. static void mixChannels(int16_t *audioOutPtr, uint32_t numSamples)
  92. {
  93.     int32_t sample;
  94.  
  95.     memset(mixBuffer, 0, numSamples * (sizeof (int32_t) * 2));
  96.  
  97.     for (int32_t i = 0; i < modChannels; i++)
  98.     {
  99.         ptVoice_t *v = &ptVoice[i];
  100.         if (v->sampleData == NULL || v->sampleEnd <= 0) continue; // voice is not active   
  101.         int32_t sample2, sample3, *mixPtr = mixBuffer;
  102.         uint32_t deltaHi = v->delta >> FRAC_BITS;
  103.         uint32_t deltaLo = v->delta & 0xFFFF;
  104.  
  105.         // this is pretty darn slow with all the branches (especially for many channels), but oh well.
  106.         // I would optimize this further if this project wasn't about number of lines in the code.
  107.         for (uint32_t j = 0; j < numSamples; j++)
  108.         {
  109.             uint32_t pos2 = v->pos + 1; // next sample for interpolation
  110.             uint32_t pos3 = v->pos + 2; // next next sample for interpolation
  111.             if (pos2 >= v->sampleEnd) pos2 = v->loopFlag ? v->loopStart + (v->sampleEnd - pos2) : (v->sampleEnd - 1);
  112.             if (pos3 >= v->sampleEnd) pos3 = v->loopFlag ? v->loopStart + (v->sampleEnd - pos3) : (v->sampleEnd - 1);
  113.  
  114.             if (v->pos >= v->sampleEnd)
  115.             {
  116.                 if (v->swapData == NULL)
  117.                 {
  118.                     if (v->loopFlag) { v->pos = v->loopStart; } else { v->sampleData = NULL; break; }
  119.                 }
  120.                 else
  121.                 {   // do sample swapping
  122.                     if (!v->swapLoopFlag) { v->sampleData = NULL; break; } // illegal swap
  123.                     v->sampleData = v->swapData;
  124.                     v->sampleEnd = v->swapSampleEnd;
  125.                     v->loopStart = v->swapLoopStart;
  126.                     v->loopFlag = v->swapLoopFlag;
  127.                     v->swapData = NULL; // turn off sample swap
  128.                     v->pos = v->loopStart;
  129.                     pos2 = v->loopStart + 1;
  130.                     pos3 = v->loopStart + 2;
  131.                 }
  132.             }
  133.  
  134.             sample = v->sampleData[v->pos];
  135.             sample2 = v->sampleData[pos2];
  136.             sample3 = v->sampleData[pos3];
  137.             CUBIC_SPLINE_3TAP(sample, sample2, sample3, v->frac); // sample is now -16384..16383
  138.             *mixPtr++ += (sample * v->volL) >> 16; // (-16384..16383 * 0..131072) >> 16 = -32768..32767
  139.             *mixPtr++ += (sample * v->volR) >> 16; // (-16384..16383 * 0..131072) >> 16 = -32768..32767
  140.             v->frac += deltaLo;
  141.             v->pos += deltaHi + (v->frac >> FRAC_BITS);
  142.             v->frac &= 0xFFFF;
  143.         }
  144.     }
  145.  
  146.     numSamples *= 2; // stereo
  147.     for (uint32_t i = 0; i < numSamples; i++)
  148.     {
  149.         sample = mixBuffer[i] >> 1;
  150.         CLAMP16(sample);
  151.         audioOutPtr[i] = (int16_t)(sample);
  152.     }
  153. }
  154.  
  155. static void CALLBACK waveOutProc(HWAVEOUT _hWaveOut, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
  156. {
  157.     int16_t *outputStream;
  158.     int32_t sampleBlock, samplesTodo;
  159.     WAVEHDR *waveBlockHeader;
  160.  
  161.     if (uMsg != MM_WOM_DONE) return;
  162.     waveBlockHeader = (WAVEHDR *)dwParam1;
  163.     waveOutUnprepareHeader(_hWaveOut, waveBlockHeader, sizeof (WAVEHDR));
  164.  
  165.     if (!isMixing) return;
  166.     memcpy(waveBlockHeader->lpData, mixerBuffer, MIX_BUF_LEN);
  167.     waveOutPrepareHeader(_hWaveOut, waveBlockHeader, sizeof (WAVEHDR));
  168.     waveOutWrite(_hWaveOut, waveBlockHeader, sizeof (WAVEHDR));
  169.  
  170.     outputStream = (int16_t *)mixerBuffer;
  171.     sampleBlock = MIX_BUF_LEN / 4; // /2 for stereo + /2 for 16-bit
  172.     while (sampleBlock)
  173.     {
  174.         samplesTodo = (sampleBlock < samplesLeft) ? sampleBlock : samplesLeft;
  175.         if (samplesTodo > 0)
  176.         {
  177.             mixChannels(outputStream, samplesTodo);
  178.             outputStream += (uint32_t)samplesTodo * 2;
  179.             sampleBlock -= samplesTodo;
  180.             samplesLeft -= samplesTodo;
  181.         }
  182.         else
  183.         {
  184.             tickReplayer();
  185.             samplesLeft = samplesPerTick;
  186.         }
  187.     }
  188.  
  189.     (void)dwParam2;
  190.     (void)dwInstance;
  191. }
  192.  
  193. bool getNumbersOfChannels(FILE *in)
  194. {
  195.     char magic[5]; magic[4] = '\0';
  196.     fseek(in, 1080, SEEK_SET);
  197.     fread(magic, 1, 4, in);
  198.     rewind(in);
  199.  
  200.     ptMode = false;
  201.     modChannels = 0;
  202.     dBaseClk = BASE_CLK;
  203.     minPeriod = MIN_PERIOD;
  204.     maxPeriod = MAX_PERIOD;
  205.  
  206.     if (!strcmp(magic, "M.K.") || !strcmp(magic, "M!K!")) // ProTracker/NoiseTracker
  207.     {
  208.         ptMode = true;
  209.         modChannels = 4;
  210.         dBaseClk = PT_BASE_CLK;
  211.         minPeriod = PT_MIN_PERIOD;
  212.         maxPeriod = PT_MAX_PERIOD;
  213.     }
  214.     else if (magic[1] == 'C' && magic[2] == 'H' && magic[3] == 'N') modChannels = magic[0] - '0'; // FT2/IT2/Others
  215.     else if (magic[2] == 'C' && magic[3] == 'H') modChannels = ((magic[0] - '0') * 10) + (magic[1] - '0'); // FT2/IT2/Others
  216.  
  217.     if (modChannels < 1 || modChannels > 99)
  218.         return false;
  219.  
  220.     pattSize = modChannels * (64 * 4);
  221.     return true;
  222. }
  223.  
  224. bool kolibrimodLoad(FILE *in, int32_t audioOutputFreq)
  225. {
  226.     uint8_t *ptr8;
  227.     int32_t modFilesize;
  228.  
  229.     if (modData != NULL) return false;
  230.     if (!getNumbersOfChannels(in)) return false;
  231.  
  232.     fseek(in, 0, SEEK_END);
  233.     modFilesize = ftell(in);
  234.     modData = (uint8_t *)malloc(modFilesize);
  235.     rewind(in);
  236.     fread(modData, 1, modFilesize, in);
  237.     memcpy(songName, modData, 20); songName[20] = '\0';
  238.  
  239.     // count number of patterns
  240.     for (uint8_t i = 0; i < 128; i++)
  241.         if (modData[952 + i] > pattNum) pattNum = modData[952 + i];
  242.  
  243.     // set sample data pointers
  244.     ptr8 = &modData[1084 + (pattSize * (pattNum + 1))];
  245.     for (uint8_t i = 0; i < 31; i++)
  246.     {
  247.         sampleDataPointers[i] = (int8_t *)ptr8;
  248.         ptr8 += ((modData[42 + (30 * i)] << 8) | modData[(42 + (30 * i)) + 1]) * 2;
  249.     }
  250.  
  251.     memset(&wfx, 0, sizeof (wfx));
  252.     audioFreq = audioOutputFreq;
  253.     wfx.nSamplesPerSec = audioFreq;
  254.     wfx.wBitsPerSample = 16;
  255.     wfx.nChannels = 2;
  256.     wfx.wFormatTag = WAVE_FORMAT_PCM;
  257.     wfx.nBlockAlign = (wfx.wBitsPerSample * wfx.nChannels) / 8;
  258.     wfx.nAvgBytesPerSec = wfx.nBlockAlign * wfx.nSamplesPerSec;
  259.     mixerBuffer = (int8_t *)calloc(MIX_BUF_LEN, 1);
  260.  
  261.     waveOutOpen(&hWaveOut, WAVE_MAPPER, &wfx, (DWORD_PTR)waveOutProc, 0, CALLBACK_FUNCTION);
  262.     for (uint8_t i = 0; i < MIX_BUF_NUM; i++)
  263.     {
  264.         waveBlocks[i].dwBufferLength = MIX_BUF_LEN;
  265.         waveBlocks[i].lpData = (LPSTR)calloc(MIX_BUF_LEN, 1);
  266.         waveOutPrepareHeader(hWaveOut, &waveBlocks[i], sizeof (WAVEHDR));
  267.         waveOutWrite(hWaveOut, &waveBlocks[i], sizeof (WAVEHDR));
  268.     }
  269.  
  270.     dHz2DeltaMul = (double)(1 << FRAC_BITS) / audioOutputFreq;
  271.     return true;
  272. }
  273.  
  274. bool kolibrimodPlay(void)
  275. {
  276.     if (modData == NULL) return false; // no module loaded
  277.  
  278.     memset(ptChannel, 0, sizeof (ptChannel));
  279.     memset(ptVoice, 0, sizeof (ptVoice));
  280.  
  281.     for (uint8_t i = 0; i < modChannels; i++) // set up initial LRRL channel pannings (0..2048)
  282.     {
  283.         if (ptMode)
  284.             ptVoice[i].panR = !((i + 1) & 2) ? (103 << 3) : (153 << 3); // 20% stereo separation in PT mode (LRRL)
  285.         else
  286.             ptVoice[i].panR = (128<<3); // always center in FT2/IT2 mode
  287.  
  288.         ptVoice[i].panL = (256 << 3) - ptVoice[i].panR;
  289.     }
  290.  
  291.     samplesPerTick = BPM2SAMPLES_PER_TICK(125);
  292.     order = 0;
  293.     modSpeed = 6;
  294.     modTick = modSpeed - 1; // don't render a tick of silence
  295.     row = newRow = 0;
  296.     modDelayFactor = 1; // 1 = no delay
  297.     newPosFlag = patLoop = false;
  298.     isMixing = true;
  299.  
  300.     return true;
  301. }
  302.  
  303. void kolibrimodFree(void)
  304. {
  305.     isMixing = false;
  306.     for (uint8_t i = 0; i < MIX_BUF_NUM; i++)
  307.         waveOutUnprepareHeader(hWaveOut, &waveBlocks[i], sizeof (WAVEHDR));
  308.  
  309.     for (uint8_t i = 0; i < MIX_BUF_NUM; i++)
  310.     {
  311.         while (waveBlocks[i].dwFlags & WHDR_PREPARED) SleepEx(1, 1); // wait
  312.         if (waveBlocks[i].lpData != NULL) free(waveBlocks[i].lpData); waveBlocks[i].lpData = NULL;
  313.     }
  314.  
  315.     waveOutReset(hWaveOut);
  316.     waveOutClose(hWaveOut);
  317.  
  318.     if (modData != NULL) free(modData); modData = NULL;
  319.     if (mixerBuffer != NULL) free(mixerBuffer); mixerBuffer = NULL;
  320. }
  321.  
  322. static void triggerVoice(ptChannel_t *c, ptVoice_t *v)
  323. {
  324.     v->sampleData = c->sampleData;
  325.     v->loopStart = c->loopStart;
  326.     v->loopFlag = c->loopLen >= MIN_LOOP_LEN;
  327.     v->sampleEnd = v->loopFlag ? (c->loopStart + c->loopLen) : c->sampleLen;
  328.     v->swapData = NULL;
  329.     v->dPeriodMul = exp2(c->ftune * (1.0 / -(12.0 * 8.0)));
  330.     v->frac = 0;
  331.     v->pos = v->oldPos = c->sampleOffset; // 9xx sample offset * 256
  332.     c->period = c->toPeriod;
  333. }
  334.  
  335. static void portamento(ptChannel_t *c)
  336. {
  337.     if (c->period == 0 || c->toPeriod == 0) return;
  338.  
  339.     if (c->period > c->toPeriod)
  340.     {
  341.         c->period -= c->portaMem;
  342.         if (c->period <= c->toPeriod) c->period = c->toPeriod;
  343.     }
  344.     else if (c->period < c->toPeriod)
  345.     {
  346.         c->period += c->portaMem;
  347.         if (c->period >= c->toPeriod) c->period = c->toPeriod;
  348.     }
  349. }
  350.  
  351. static void vibrato(ptChannel_t *c, ptVoice_t *v)
  352. {
  353.     if (c->period != 0)
  354.     {
  355.         int32_t newPeriod = c->period + (int32_t)(c->vibDepth * sinf(c->vibPos * (2.0f * 3.1415926f / 64.0f)));
  356.         if (newPeriod < 0) newPeriod = 0;
  357.         v->delta = PERIOD2DELTA(newPeriod);
  358.     }
  359.  
  360.     if (modTick > 0) c->vibPos = (c->vibPos + c->vibSpeed) & 63;
  361. }
  362.  
  363. static void tremolo(ptChannel_t *c, ptVoice_t *v)
  364. {
  365.     v->vol = c->vol + (int8_t)(c->tremoloDepth * sinf(c->tremoloPos * (2.0f * 3.1415926f / 64.0f)));
  366.     v->vol = CLAMP(v->vol, 0, 64);
  367.     if (modTick > 0) c->tremoloPos = (c->tremoloPos + c->tremoloSpeed) & 63;
  368. }
  369.  
  370. static void volumeSlide(ptChannel_t *c)
  371. {
  372.     if (modTick == 0) return;
  373.     c->vol += (c->efx2 & 0xF0) ? (c->efx2 >> 4) : -(c->efx2 & 0xF);
  374.     c->vol = CLAMP(c->vol, 0, 64);
  375. }
  376.  
  377. static void invertLoop(ptChannel_t *c) // ProTracker only
  378. {
  379.     if (!ptMode || (c->invLoopPos += invLoopTable[c->invLoopSpeed]) < 128) return;
  380.     c->invLoopPos = 0;
  381.  
  382.     if (c->invLoopPtr == NULL) return;
  383.     if (++c->invLoopPtr >= c->invLoopStartPtr+c->invLoopLen) c->invLoopPtr = c->invLoopStartPtr;
  384.     *c->invLoopPtr = -1 - *c->invLoopPtr;
  385. }
  386.  
  387. static void checkEfx(void)
  388. {
  389.     int16_t period;
  390.     ptChannel_t *c;
  391.     ptVoice_t *v;
  392.  
  393.     for (int16_t i = 0; i < modChannels; i++) // do effects
  394.     {
  395.         c = &ptChannel[i]; v = &ptVoice[i];
  396.         if (modTick > 0) invertLoop(c);
  397.  
  398.         if (c->efx != 0 || c->efx2 != 0)
  399.         {
  400.             if (c->efx == 0x4) { if (c->efx2 & 0xF) c->vibDepth = (c->efx2 & 0xF) * 2; if (c->efx2 & 0xF0) c->vibSpeed = c->efx2 >> 4; vibrato(c, v); }
  401.             if (c->efx == 0x7) { if (c->efx2 & 0xF) c->tremoloDepth = (c->efx2 & 0xF) * 4; if (c->efx2 & 0xF0) c->tremoloSpeed = c->efx2 >> 4; tremolo(c, v); }
  402.             if (c->efx == 0x6) { vibrato(c, v); volumeSlide(c); }
  403.  
  404.             if (modTick == 0)
  405.             {   // tick=0 effects
  406.                      if (c->efx == 0x8) { if (!ptMode) { v->panR = c->efx2 << 3; v->panL = 2048 - (c->efx2 << 3); } }
  407.                 else if (c->efx == 0xB) { newPosFlag = true; newRow = 0; order = c->efx2 - 1; }
  408.                 else if (c->efx == 0xC) { c->vol = (c->efx2 > 64) ? 64 : c->efx2; }
  409.                 else if (c->efx == 0xD) { newPosFlag = true; newRow = ((c->efx2 >> 4) * 10) + (c->efx2 & 0xF); if (newRow > 63) newRow = 0; }
  410.                 else if (c->efx == 0xE)
  411.                 {
  412.                          if ((c->efx2&0xF0)==0x10) { if (c->period > 0) { c->period -= c->efx2 & 0xF; if (c->period < minPeriod) c->period = minPeriod; } }
  413.                     else if ((c->efx2&0xF0)==0x20) { if (c->period > 0) { c->period += c->efx2 & 0xF; if (c->period > maxPeriod) c->period = maxPeriod; } }
  414.                     else if ((c->efx2&0xF0)==0x50) { c->ftune = CONVERT_FINETUNE(c->efx2 & 0xF); }
  415.                     else if ((c->efx2&0xF0)==0x60) { if (c->efx2==0x60) c->jmpRow = row; else { if (!c->jmpCnt) c->jmpCnt = c->efx2 & 0xF; else if (!--c->jmpCnt) return; newRow = c->jmpRow; patLoop = true; }}
  416.                     else if ((c->efx2&0xF0)==0xA0) { c->vol += c->efx2 & 0xF; if (c->vol > 64) c->vol = 64; }
  417.                     else if ((c->efx2&0xF0)==0xB0) { c->vol -= c->efx2 & 0xF; if (c->vol < 0) c->vol = 0; }
  418.                     else if ((c->efx2&0xF0)==0xE0) { modDelayFactor = (c->efx2 & 0xF) + 1; }
  419.                     else if ((c->efx2&0xF0)==0xF0) { c->invLoopSpeed = c->efx2 & 0xF; invertLoop(c); }
  420.                 }
  421.                 else if (c->efx == 0xF) if (c->efx2 < 0x20) { if (c->efx2) modSpeed = c->efx2; } else { samplesPerTick = BPM2SAMPLES_PER_TICK(c->efx2); }
  422.             }
  423.             else
  424.             {   // tick>0 effects
  425.                      if (c->efx == 0x1) { if (c->period > 0) { c->period -= c->efx2; if (c->period < minPeriod) c->period = minPeriod; } }
  426.                 else if (c->efx == 0x2) { if (c->period > 0) { c->period += c->efx2; if (c->period > maxPeriod) c->period = maxPeriod; } }
  427.                 else if (c->efx == 0x3) { if (c->efx2) c->portaMem = c->efx2; portamento(c); }
  428.                 else if (c->efx == 0x5) { portamento(c); volumeSlide(c); }
  429.                 else if (c->efx == 0xE) { if ((c->efx2 & 0xF0) == 0x90) { if (c->efx2 == 0x90) return; if (!(modTick % (c->efx2 & 0xF))) v->pos = v->oldPos; }}
  430.                 else if (c->efx == 0xA) { volumeSlide(c); }
  431.             }
  432.         }
  433.  
  434.              if ((c->efx == 0xE) && (c->efx2 & 0xF0) == 0xC0) { if (modTick == (c->efx2 & 0xF)) c->vol = 0; }
  435.         else if ((c->efx == 0xE) && (c->efx2 & 0xF0) == 0xD0) { if (c->period > 0 && modTick == (c->efx2 & 0xF)) triggerVoice(c, v); }
  436.  
  437.         period = c->period;
  438.         if (period > 0 && c->efx == 0 && c->efx2 != 0) // do arpeggio here
  439.         {
  440.             uint8_t arpTick = modTick % 3;
  441.                  if (arpTick == 0) period = periods[c->note];
  442.             else if (arpTick == 1) period = periods[(c->note + (c->efx2 >>  4)) % MAX_NOTES];
  443.             else if (arpTick == 2) period = periods[(c->note + (c->efx2 & 0xF)) % MAX_NOTES];
  444.         }
  445.  
  446.         if (c->efx != 4 && c->efx != 6 && period > 0) v->delta = PERIOD2DELTA(period); // set voice pitch
  447.         if (c->efx != 7) v->vol = c->vol; // set voice volume
  448.  
  449.         v->volL = v->vol * v->panL; // 0..64 * 0..2048 = 0..131072
  450.         v->volR = v->vol * v->panR; // 0..64 * 0..2048 = 0..131072
  451.     }
  452. }
  453.  
  454. static void tickReplayer(void)
  455. {
  456.     uint8_t *pattPtr, *ptr8, note, smp;
  457.     int16_t period;
  458.     ptChannel_t *c;
  459.     ptVoice_t *v;
  460.  
  461.     if (++modTick >= modSpeed*modDelayFactor)
  462.     {
  463.         modTick = 0;
  464.         modDelayFactor = 1; // reset EEx (pattern delay). 1 = no delay
  465.  
  466.         for (uint8_t i = 0; i < modChannels; i++) // read pattern data
  467.         {
  468.             c = &ptChannel[i]; v = &ptVoice[i];
  469.  
  470.             pattPtr = &modData[1084 + (modData[952 + order] * pattSize) + (row * (4 * modChannels)) + (i * 4)];
  471.  
  472.             c->rawPeriod = period = (((pattPtr[0] & 0xF) << 8) | pattPtr[1]);
  473.             c->efx = pattPtr[2] & 0xF;
  474.             c->efx2 = pattPtr[3];
  475.  
  476.             smp = (pattPtr[0] & 0xF0) | (pattPtr[2] >> 4);
  477.             if (smp >= 1 && smp <= 31) // sample found in pattern data
  478.             {
  479.                 ptr8 = &modData[42 + (30 * (smp - 1))];
  480.                 c->ftune = CONVERT_FINETUNE(ptr8[2] & 0xF);
  481.                 c->vol = (ptr8[3] > 64) ? 64 : ptr8[3];
  482.                 c->sampleLen = ((ptr8[0] << 8) | ptr8[1]) * 2;
  483.                 c->loopStart = ((ptr8[4] << 8) | ptr8[5]) * 2;
  484.                 c->loopLen = ((ptr8[6] << 8) | ptr8[7]) * 2;
  485.                 c->sampleData = sampleDataPointers[smp-1];
  486.                 c->sampleOffset = 0;
  487.  
  488.                 if (!(c->efx == 0xE && (c->efx2 & 0xF0) == 0xD0)) // don't update voice during note delay
  489.                 {
  490.                     if (ptMode && v->sampleData && !period)
  491.                     {   // do ProTracker sample swap (if voice is active and no pattern note)
  492.                         v->swapData = c->sampleData;
  493.                         v->swapLoopStart = c->loopStart;
  494.                         v->swapLoopFlag = (c->loopStart + c->loopLen) > 2;
  495.                         v->swapSampleEnd = v->swapLoopFlag ? (c->loopStart + c->loopLen) : c->sampleLen;
  496.                     }
  497.                     else
  498.                     {   // trigger sample the normal way
  499.                         v->sampleData = c->sampleData;
  500.                         v->loopStart = c->loopStart;
  501.                         v->loopFlag = c->loopLen >= MIN_LOOP_LEN;
  502.                         v->sampleEnd = v->loopFlag ? (c->loopStart + c->loopLen) : c->sampleLen;
  503.                         v->swapData = NULL;
  504.                     }
  505.                 }
  506.  
  507.                 c->invLoopLen = c->loopLen;
  508.                 c->invLoopPtr = c->invLoopStartPtr = c->sampleData + c->loopStart;
  509.             }
  510.  
  511.             if (c->efx == 0x9) // 9xx "sample offset" effect (must be handled here)
  512.             {
  513.                 if (c->efx2) c->sampleOffsetMem = c->efx2 << 8;
  514.                 c->sampleOffset = c->sampleOffsetMem;
  515.             }
  516.  
  517.             if (period > 0) // note found in pattern data
  518.             {
  519.                 if (c->efx == 0xE && (c->efx2 & 0xF0) == 0x50) // finetune command must be handled here as well
  520.                     c->ftune = CONVERT_FINETUNE(c->efx2 & 0xF);
  521.  
  522.                 for (note = 0; note < MAX_NOTES; note++)
  523.                     if (period >= periods[note]) break;
  524.  
  525.                 if (note < MAX_NOTES) // note was found in period table
  526.                 {
  527.                     c->note = note;
  528.                     c->toPeriod = periods[note];
  529.  
  530.                     if (c->efx != 0x03 && c->efx != 0x05 && !(c->efx != 0xE && (c->efx2 & 0xF0) == 0xD0))
  531.                     {
  532.                         c->vibPos = c->tremoloPos = 0; // reset vibrato/tremolo position
  533.                         triggerVoice(c, v);
  534.                         v->delta = PERIOD2DELTA(c->period); // set initial voice pitch
  535.                     }
  536.                 }
  537.             }
  538.         }
  539.  
  540.         checkEfx();
  541.  
  542.         if (patLoop) // handle pattern loop (E6x)
  543.         {
  544.             row = newRow;
  545.             newRow = 0;
  546.             newPosFlag = patLoop = false;
  547.         }
  548.         else if (++row >= 64) newPosFlag = true; // increase row like normal
  549.     }
  550.     else checkEfx();
  551.  
  552.     if (newPosFlag) // handle position jump (Bxx), pattern break (Dxx) or end of pattern
  553.     {
  554.         row = newRow;
  555.         newRow = 0;
  556.         newPosFlag = false;
  557.         if (++order >= modData[950]) order = (modData[951] < modData[950]) ? modData[951] : 0; // song loop
  558.     }
  559. }
  560.  
  561. int main(int argc, char *argv[]) // please edit this for your own use!
  562. {
  563.     FILE *f;
  564.  
  565.     if (argc != 2)
  566.     {
  567.         printf("Usage: kolibrimod.exe <module>\n");
  568.         return -1;
  569.     }
  570.  
  571.     f = fopen(argv[1], "rb");
  572.     if (f == NULL)
  573.     {
  574.         printf("ERROR: Can't open file!\n");
  575.         return 1;
  576.     }
  577.  
  578.     if (!kolibrimodLoad(f, 48000))
  579.     {
  580.         printf("ERROR: This .MOD is not supported!\n");
  581.         return 1;
  582.     }
  583.  
  584.     fclose(f);
  585.     kolibrimodPlay();
  586.  
  587.     printf("Playing \"%s\" (%dHz - %d channels)\nPress any key to stop...\n", songName, audioFreq, modChannels);
  588.     while (!_getch()) Sleep(250);
  589.     kolibrimodFree();
  590.  
  591.     return 0;
  592. }
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
Not a member of Pastebin yet?
Sign Up, it unlocks many cool features!
 
Top