daily pastebin goal
29%
SHARE
TWEET

kolibrimod (tiny .mod replayer)

8bitbubsy Sep 11th, 2016 (edited) 646 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // kolibrimod - <500 lines ProTracker .MOD replayer by 8bitbubsy - 16th of March 2019 (7:36PM) - remember to link winmm!
  2. // Missing effects: E3x (portamento type), E4x (vibrato type), E7x (tremolo type), E8x (Karplus-Strong)
  3. // Do not use this replayer with an audio rate lower than 32kHz! Mixer is single-step only (max Amiga rate = ~31kHz)
  4. // Note: This is not very optimized, the main focus is amount of code lines + okay accuracy
  5.  
  6. #include <stdio.h>
  7. #include <stdlib.h>
  8. #include <string.h>
  9. #include <stdint.h>
  10. #include <stdbool.h>
  11. #include <conio.h>   // _getch() */
  12. #include <math.h>    // powf(), sinf()
  13. #include <windows.h> // Sleep(), mixer stuff
  14.  
  15. #define MIX_BUF_NUM 4
  16. #define MIX_BUF_LEN 4096
  17. #define FRAC_BITS 14 /* 18.14 - don't change this! */
  18. #define MAX_SAMPLES_TO_MIX (int32_t)(((audioFreq * 2.5f) / 32.0f) + 0.5f))
  19. #define LERP8(s1, s2, f) { s2 -= s1; s2 *= (int32_t)(f); s1 <<= 8; s2 >>= (FRAC_BITS - 8); s1 += s2; }
  20. #define PERIOD2DELTA(x) (((x) <= 0) ? 0 : (((int32_t)(3546895.0f / roundf((x) * v->fFineTune)) << FRAC_BITS) / audioFreq))
  21. #define BPM2SAMPLES_PER_TICK(x) (int32_t)(((((audioFreq * 125.0f) / 50.0f) / (x))) + 0.5f)
  22. #define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
  23.  
  24. typedef struct
  25. {
  26.     int8_t *sampleData, *swapData, vol;
  27.     int16_t panL, panR;
  28.     bool loopFlag, swapLoopFlag;
  29.     int32_t sampleEnd, loopStart, swapSampleEnd, swapLoopStart, volL, volR;
  30.     uint32_t oldPos, pos, delta;
  31.     float fFineTune;
  32. } ptVoice_t;
  33.  
  34. typedef struct
  35. {
  36.     int8_t volume, fineTune, *invLoopPtr, *sampleData, *invLoopStartPtr, jmpRow, jmpCnt;
  37.     uint8_t note, efx, efxDat, sampleOffsetMem, sampleOffset, portaMem, vibDepth, vibSpeed;
  38.     uint8_t tremoloDepth, tremoloSpeed, vibPos, tremoloPos, invLoopSpeed, invLoopPos;
  39.     int16_t rawPeriod, period, toPeriod;
  40.     uint32_t invLoopLen, sampleLen, loopStart, loopLen;
  41. } ptChannel_t;
  42.  
  43. static volatile bool isMixing;
  44. static bool newPosFlag, loopFlag;
  45. static int8_t modSpeed, pattNum, modDelay, newModRow, modRow, *mixerBuffer, *sampleDataPointers[31];
  46. static uint8_t stereoSep, *modData, modOrder;
  47. static int16_t modTick;
  48. static int32_t samplesLeft, samplesPerTick, audioFreq, mixBufferL[MIX_BUF_LEN / 4], mixBufferR[MIX_BUF_LEN / 4];
  49. static const uint8_t invLoopTable[16] = { 0, 5, 6, 7, 8, 10, 11, 13, 16, 19, 22, 26, 32, 43, 64, 128 };
  50. static const int16_t periodTable[36] = {856,808,762,720,678,640,604,570,538,508,480,453,428,404,381,360,339,320,
  51.                                         302,285,269,254,240,226,214,202,190,180,170,160,151,143,135,127,120,113};
  52. static ptVoice_t ptVoice[4];
  53. static ptChannel_t ptChannel[4];
  54. static WAVEHDR waveBlocks[MIX_BUF_NUM];
  55. static HWAVEOUT hWaveOut;
  56. static WAVEFORMATEX wfx;
  57.  
  58. static void tickReplayer(void);
  59.  
  60. static void mixChannels(int16_t *audioOutPtr, int32_t numSamples)
  61. {
  62.     int32_t sample, sample2, i, j, pos, pos2;
  63.     ptVoice_t *v;
  64.  
  65.     memset(mixBufferL, 0, sizeof (int32_t) * numSamples);
  66.     memset(mixBufferR, 0, sizeof (int32_t) * numSamples);
  67.  
  68.     for (i = 0; i < 4; ++i)
  69.     {
  70.         v = &ptVoice[i];
  71.         if ((v->sampleData == NULL) || (v->sampleEnd <= 0)) continue; // voice is not active
  72.  
  73.         for (j = 0; j < numSamples; ++j)
  74.         {
  75.             pos = v->pos >> FRAC_BITS;
  76.  
  77.             pos2 = pos + 1;
  78.             if (pos2 >= v->sampleEnd)
  79.                 pos2 = v->loopFlag ? v->loopStart : (v->sampleEnd - 1);
  80.  
  81.             if (pos >= v->sampleEnd)
  82.             {
  83.                 if (v->swapData == NULL)
  84.                 {
  85.                     if (v->loopFlag) { pos = v->loopStart; } else { v->sampleData = NULL; break; }
  86.                 }
  87.                 else
  88.                 {
  89.                     // do sample swapping
  90.                     if (!v->swapLoopFlag) { v->sampleData = NULL; break; } // illegal swap
  91.                     v->sampleData = v->swapData;
  92.                     v->sampleEnd  = v->swapSampleEnd;
  93.                     v->loopStart  = v->swapLoopStart;
  94.                     v->loopFlag   = v->swapLoopFlag;
  95.                     v->swapData   = NULL; // turn off sample swap
  96.                     pos  = v->loopStart;
  97.                     pos2 = v->loopStart + 1;
  98.                 }
  99.             }
  100.  
  101.             v->pos &= ((1 << FRAC_BITS) - 1); // keep fractional part only
  102.             sample  = v->sampleData[pos];
  103.             sample2 = v->sampleData[pos2];
  104.             LERP8(sample, sample2, v->pos);
  105.             v->pos |= (pos << FRAC_BITS); // add updated position
  106.             v->pos += v->delta;
  107.             mixBufferL[j] += ((sample * v->volL) >> 16); // -32768..32767
  108.             mixBufferR[j] += ((sample * v->volR) >> 16); // -32768..32767
  109.         }
  110.     }
  111.  
  112.     for (i = 0; i < numSamples; ++i)
  113.     {
  114.         *audioOutPtr++ = (int16_t)(mixBufferL[i] >> 2); // shift back into -32768..32767
  115.         *audioOutPtr++ = (int16_t)(mixBufferR[i] >> 2); // shift back into -32768..32767
  116.     }
  117. }
  118.  
  119. static void CALLBACK waveOutProc(HWAVEOUT _hWaveOut, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
  120. {
  121.     int16_t *outputStream;
  122.     int32_t sampleBlock, samplesTodo;
  123.     WAVEHDR *waveBlockHeader;
  124.  
  125.     if (uMsg != MM_WOM_DONE) return;
  126.     waveBlockHeader = (WAVEHDR *)(dwParam1);
  127.     waveOutUnprepareHeader(_hWaveOut, waveBlockHeader, sizeof (WAVEHDR));
  128.  
  129.     if (!isMixing) return;
  130.     memcpy(waveBlockHeader->lpData, mixerBuffer, MIX_BUF_LEN);
  131.     waveOutPrepareHeader(_hWaveOut, waveBlockHeader, sizeof (WAVEHDR));
  132.     waveOutWrite(_hWaveOut, waveBlockHeader, sizeof (WAVEHDR));
  133.  
  134.     outputStream = (int16_t *)(mixerBuffer);
  135.     sampleBlock = MIX_BUF_LEN / 4; // /2 for stereo + /2 for 16-bit
  136.     while (sampleBlock)
  137.     {
  138.         samplesTodo = (sampleBlock < samplesLeft) ? sampleBlock : samplesLeft;
  139.         if (samplesTodo > 0)
  140.         {
  141.             mixChannels(outputStream, samplesTodo);
  142.             outputStream += (samplesTodo * 2);
  143.             sampleBlock  -=  samplesTodo;
  144.             samplesLeft  -=  samplesTodo;
  145.         }
  146.         else
  147.         {
  148.             tickReplayer();
  149.             samplesLeft = samplesPerTick;
  150.         }
  151.     }
  152. }
  153.  
  154. void kolibrimodLoad(FILE *in, int32_t audioOutputFreq, uint8_t audioStereoSeparation)
  155. {
  156.     uint8_t *ptr8, i;
  157.     int32_t modFilesize;
  158.  
  159.     if (modData != NULL) return;
  160.  
  161.     fseek(in, 0, SEEK_END);
  162.     modFilesize = ftell(in);
  163.     modData = (uint8_t *)(malloc(modFilesize));
  164.     rewind(in);
  165.     fread(modData, 1, modFilesize, in);
  166.  
  167.     // count number of patterns
  168.     for (i = 0; i < 128; ++i) if (modData[952 + i] > pattNum) pattNum = modData[952 + i];
  169.  
  170.     // set sample data pointers
  171.     ptr8 = &modData[1084 + (1024 * (pattNum + 1))];
  172.     for (i = 0; i < 31; ++i)
  173.     {
  174.         sampleDataPointers[i] = (int8_t *)(ptr8);
  175.         ptr8 += ((modData[42 + (30 * i)] << 8) | modData[(42 + (30 * i)) + 1]) * 2;
  176.     }
  177.  
  178.     memset(&wfx, 0, sizeof (wfx));
  179.     audioFreq = audioOutputFreq;
  180.     stereoSep = audioStereoSeparation;
  181.     wfx.nSamplesPerSec = audioFreq;
  182.     wfx.wBitsPerSample = 16;
  183.     wfx.nChannels = 2;
  184.     wfx.wFormatTag = WAVE_FORMAT_PCM;
  185.     wfx.nBlockAlign = (wfx.wBitsPerSample * wfx.nChannels) / 8;
  186.     wfx.nAvgBytesPerSec = wfx.nBlockAlign * wfx.nSamplesPerSec;
  187.     mixerBuffer = (int8_t  *)(calloc(MIX_BUF_LEN, 1));
  188.  
  189.     waveOutOpen(&hWaveOut, WAVE_MAPPER, &wfx, (DWORD_PTR)(waveOutProc), 0, CALLBACK_FUNCTION);
  190.     for (i = 0; i < MIX_BUF_NUM; ++i)
  191.     {
  192.         waveBlocks[i].dwBufferLength = MIX_BUF_LEN;
  193.         waveBlocks[i].lpData = (LPSTR)(calloc(MIX_BUF_LEN, 1));
  194.         waveOutPrepareHeader(hWaveOut, &waveBlocks[i], sizeof (WAVEHDR));
  195.         waveOutWrite(hWaveOut, &waveBlocks[i], sizeof (WAVEHDR));
  196.     }
  197. }
  198.  
  199. void kolibrimodPlay(void)
  200. {
  201.     uint8_t i;
  202.  
  203.     if (modData == NULL) return; // no module loaded
  204.  
  205.     memset(ptChannel, 0, sizeof (ptChannel));
  206.     memset(ptVoice,   0, sizeof (ptVoice));
  207.  
  208.     for (i = 0; i < 4; ++i) // set up channel pannings from stereo separation (0..1024)
  209.     {
  210.         ptVoice[i].panR = ((i==0)||(i==3) ? (128-((stereoSep*128)/100)) : (128+((stereoSep*128)/100))) << 2;
  211.         ptVoice[i].panL = (256 << 2) - ptVoice[i].panR;
  212.     }
  213.  
  214.     samplesPerTick = BPM2SAMPLES_PER_TICK(125);
  215.     modSpeed = 6;
  216.     modTick  = modSpeed - 1; // don't render a tick of silence
  217.     loopFlag = modRow = newModRow = newPosFlag = 0;
  218.     isMixing = modDelay = 1;
  219. }
  220.  
  221. void kolibrimodFree(void)
  222. {
  223.     uint8_t i;
  224.  
  225.     isMixing = 0;
  226.     for (i = 0; i < MIX_BUF_NUM; ++i) waveOutUnprepareHeader(hWaveOut, &waveBlocks[i], sizeof (WAVEHDR));
  227.  
  228.     for (i = 0; i < MIX_BUF_NUM; ++i)
  229.     {
  230.         while (waveBlocks[i].dwFlags & WHDR_PREPARED) SleepEx(1, 1); // wait
  231.         if (waveBlocks[i].lpData != NULL) free(waveBlocks[i].lpData); waveBlocks[i].lpData = NULL;
  232.     }
  233.  
  234.     waveOutReset(hWaveOut);
  235.     waveOutClose(hWaveOut);
  236.  
  237.     if (modData     != NULL) free(modData);     modData     = NULL;
  238.     if (mixerBuffer != NULL) free(mixerBuffer); mixerBuffer = NULL;
  239. }
  240.  
  241. static void triggerVoice(ptChannel_t *c, ptVoice_t *v)
  242. {
  243.     v->sampleData = c->sampleData;
  244.     v->loopStart  = c->loopStart;
  245.     v->loopFlag   = (c->loopStart + c->loopLen) > 2;
  246.     v->sampleEnd  = v->loopFlag ? (c->loopStart + c->loopLen) : c->sampleLen;
  247.     v->swapData   = NULL;
  248.     v->fFineTune  = powf(2.0f, c->fineTune * (1.0f / -(12.0f * 8.0f)));
  249.     v->oldPos     = (c->sampleOffset * 256) << FRAC_BITS; // 9xx sample offset
  250.     v->pos        = v->oldPos;
  251.     c->period     = c->toPeriod;
  252. }
  253.  
  254. static void portamento(ptChannel_t *c)
  255. {
  256.     if ((c->period <= 0) || (c->toPeriod <= 0)) return;
  257.  
  258.     if (c->period > c->toPeriod)
  259.     {
  260.         c->period -= c->portaMem;
  261.         if (c->period <= c->toPeriod) c->period = c->toPeriod;
  262.     }
  263.     else if (c->period < c->toPeriod)
  264.     {
  265.         c->period += c->portaMem;
  266.         if (c->period >= c->toPeriod) c->period = c->toPeriod;
  267.     }
  268. }
  269.  
  270. static void vibrato(ptChannel_t *c, ptVoice_t *v)
  271. {
  272.     if (c->period > 0)
  273.         v->delta = PERIOD2DELTA(CLAMP(c->period + (int32_t)(c->vibDepth*sinf(c->vibPos*(2.0f*3.1415926f/64.0f))), 113, 856));
  274.     if (modTick > 0) c->vibPos = (c->vibPos + c->vibSpeed) & 63;
  275. }
  276.  
  277. static void tremolo(ptChannel_t *c, ptVoice_t *v)
  278. {
  279.     v->vol = c->volume + (int8_t)(c->tremoloDepth * sinf(c->tremoloPos * (2.0f * 3.1415926f / 64.0f)));
  280.     v->vol = CLAMP(v->vol, 0, 64);
  281.     if (modTick > 0) c->tremoloPos = (c->tremoloPos + c->tremoloSpeed) & 63;
  282. }
  283.  
  284. static void volumeSlide(ptChannel_t *c)
  285. {
  286.     if (modTick > 0)
  287.         c->volume += (c->efxDat & 0xF0) ? (c->efxDat >> 4) : -(c->efxDat & 0x0F);
  288.     c->volume = CLAMP(c->volume, 0, 64);
  289. }
  290.  
  291. static void invertLoop(ptChannel_t *c)
  292. {
  293.     if ((c->invLoopPos += invLoopTable[c->invLoopSpeed]) < 128) return;
  294.     c->invLoopPos = 0;
  295.  
  296.     if (c->invLoopPtr == NULL) return;
  297.     if (++c->invLoopPtr >= (c->invLoopStartPtr + c->invLoopLen)) c->invLoopPtr = c->invLoopStartPtr;
  298.     *c->invLoopPtr = -1 - *c->invLoopPtr;
  299. }
  300.  
  301. static void checkEfx(void)
  302. {
  303.     int16_t i, period;
  304.     ptChannel_t *c;
  305.     ptVoice_t *v;
  306.  
  307.     for (i = 0; i < 4; ++i) // do effects
  308.     {
  309.         c = &ptChannel[i]; v = &ptVoice[i];
  310.  
  311.         if (modTick > 0) invertLoop(c);
  312.  
  313.         if (!((c->efx == 0) && (c->efxDat == 0)))
  314.         {
  315.             // these are handled on non-zero ticks only in ProTracker, but it sounds SO BAD! PT vibrato/tremolo sucks, let's do it better
  316.             if (c->efx == 0x4) { if (c->efxDat & 0x0F) c->vibDepth = (c->efxDat & 0x0F) * 2; if (c->efxDat & 0xF0) c->vibSpeed = c->efxDat >> 4; vibrato(c, v); }
  317.             if (c->efx == 0x7) { if (c->efxDat & 0x0F) c->tremoloDepth = (c->efxDat & 0x0F) * 4; if (c->efxDat & 0xF0) c->tremoloSpeed = c->efxDat >> 4; tremolo(c, v); }
  318.             if (c->efx == 0x6) { vibrato(c, v); volumeSlide(c); }
  319.  
  320.             if (modTick == 0)
  321.             {    // tick=0 effects
  322.                      if (c->efx == 0xB) { newPosFlag = 1; newModRow = 0; modOrder = c->efxDat - 1; }
  323.                 else if (c->efx == 0xC) c->volume = (c->efxDat > 64) ? 64 : c->efxDat;
  324.                 else if (c->efx == 0xD) { newPosFlag = 1; newModRow = ((c->efxDat >> 4) * 10) + (c->efxDat & 0x0F); if (newModRow > 63) newModRow = 0; }
  325.                 else if (c->efx == 0xE)
  326.                 {
  327.                          if ((c->efxDat & 0xF0) == 0x10) { if (c->period > 0) { c->period -= c->efxDat & 0x0F; if (c->period < 113) c->period = 113; } }
  328.                     else if ((c->efxDat & 0xF0) == 0x20) { if (c->period > 0) { c->period += c->efxDat & 0x0F; if (c->period > 856) c->period = 856; } }
  329.                     else if ((c->efxDat & 0xF0) == 0x50) c->fineTune = ((c->efxDat & 0x0F) >= 8) ? ((c->efxDat & 0x0F) - 16) : (c->efxDat & 0x0F);
  330.                     else if ((c->efxDat & 0xF0) == 0x60) { if (c->efxDat == 0x60) c->jmpRow = modRow; else { if (!c->jmpCnt) c->jmpCnt = c->efxDat & 0x0F; else if (!--c->jmpCnt) return; newModRow = c->jmpRow; loopFlag = 1; }}
  331.                     else if ((c->efxDat & 0xF0) == 0xA0) { c->volume += c->efxDat & 0x0F; if (c->volume > 64) c->volume = 64; }
  332.                     else if ((c->efxDat & 0xF0) == 0xB0) { c->volume -= c->efxDat & 0x0F; if (c->volume <  0) c->volume =  0; }
  333.                     else if ((c->efxDat & 0xF0) == 0xE0) modDelay = (c->efxDat & 0x0F) + 1;
  334.                     else if ((c->efxDat & 0xF0) == 0xF0) { c->invLoopSpeed = c->efxDat & 0x0F; invertLoop(c); }
  335.                 }
  336.                 else if (c->efx == 0xF) if (c->efxDat < 0x20) { if (c->efxDat) modSpeed = c->efxDat; } else { samplesPerTick = BPM2SAMPLES_PER_TICK(c->efxDat); }
  337.             }
  338.             else
  339.             {   // tick>0 effects
  340.                      if (c->efx == 0x1) { if (c->period > 0) { c->period -= c->efxDat; if (c->period < 113) c->period = 113; } }
  341.                 else if (c->efx == 0x2) { if (c->period > 0) { c->period += c->efxDat; if (c->period > 856) c->period = 856; } }
  342.                 else if (c->efx == 0x3) { if (c->efxDat) c->portaMem = c->efxDat; portamento(c); }
  343.                 else if (c->efx == 0x5) { portamento(c); volumeSlide(c); }
  344.                 else if (c->efx == 0xE) { if ((c->efxDat & 0xF0) == 0x90) { if (c->efxDat == 0x90) return; if (!(modTick % (c->efxDat & 0x0F))) v->pos = v->oldPos; }}
  345.                 else if (c->efx == 0xA) volumeSlide(c);
  346.             }
  347.         }
  348.  
  349.              if ((c->efx == 0xE) && (c->efxDat & 0xF0) == 0xC0) { if (modTick == (c->efxDat & 0x0F)) c->volume = 0; }
  350.         else if ((c->efx == 0xE) && (c->efxDat & 0xF0) == 0xD0) { if ((c->rawPeriod >= 113) && (modTick == (c->efxDat & 0x0F))) triggerVoice(c, v); }
  351.  
  352.         period = c->period;
  353.         if ((period > 0) && (c->efx == 0) && (c->efxDat != 0)) // do arpeggio here
  354.         {
  355.                  if ((modTick % 3) == 0) period = periodTable[c->note];
  356.             else if ((modTick % 3) == 1) period = periodTable[(c->note + (c->efxDat >>   4)) % 36];
  357.             else if ((modTick % 3) == 2) period = periodTable[(c->note + (c->efxDat & 0x0F)) % 36];
  358.         }
  359.  
  360.         if ((c->efx != 4) && (c->efx != 6) && (period > 0)) v->delta = PERIOD2DELTA(period); // set voice pitch
  361.         if (c->efx != 7) v->vol = c->volume; // set voice volume
  362.         v->volL = v->vol * v->panL;
  363.         v->volR = v->vol * v->panR;
  364.     }
  365. }
  366.  
  367. static void tickReplayer(void)
  368. {
  369.     uint8_t *pattPtr, *ptr8, i, note, smp;
  370.     int16_t period;
  371.     ptChannel_t *c;
  372.     ptVoice_t *v;
  373.  
  374.     if (++modTick >= (modSpeed * modDelay))
  375.     {
  376.         modTick  = 0;
  377.         modDelay = 1; // reset EEx (pattern delay) | 1 = no delay
  378.  
  379.         for (i = 0; i < 4; ++i) // read pattern data
  380.         {
  381.             c = &ptChannel[i]; v = &ptVoice[i];
  382.  
  383.             pattPtr = &modData[1084 + (modData[952 + modOrder] * 1024) + (modRow * 16) + (i * 4)];
  384.  
  385.             c->rawPeriod = period = (((pattPtr[0] & 0x0F) << 8) | pattPtr[1]);
  386.             c->efx = pattPtr[2] & 0x0F;
  387.             c->efxDat = pattPtr[3];
  388.  
  389.             smp = (pattPtr[0] & 0xF0) | (pattPtr[2] >> 4);
  390.             if ((smp >= 1) && (smp <= 31)) // sample found in pattern data
  391.             {
  392.                 ptr8 = &modData[42 + (30 * (smp - 1))];
  393.                 c->fineTune     = ((ptr8[2] & 0x0F) >= 8) ? ((ptr8[2] & 0x0F) - 16) : (ptr8[2] & 0x0F);
  394.                 c->volume       = (ptr8[3] > 64) ? 64 : ptr8[3];
  395.                 c->sampleLen    = ((ptr8[0] << 8) | ptr8[1]) * 2;
  396.                 c->loopStart    = ((ptr8[4] << 8) | ptr8[5]) * 2;
  397.                 c->loopLen      = ((ptr8[6] << 8) | ptr8[7]) * 2;
  398.                 c->sampleData   = sampleDataPointers[smp - 1];
  399.                 c->sampleOffset = 0;
  400.  
  401.                 if (!((c->efx == 0xE) && ((c->efxDat & 0xF0) == 0xD0))) // don't update voice during note delay
  402.                 {
  403.                     if (v->sampleData && !period)
  404.                     {   // do sample swap (if voice is active and no pattern note)
  405.                         v->swapData      = c->sampleData;
  406.                         v->swapLoopStart = c->loopStart;
  407.                         v->swapLoopFlag  = (c->loopStart + c->loopLen) > 2;
  408.                         v->swapSampleEnd = v->swapLoopFlag ? (c->loopStart + c->loopLen) : c->sampleLen;
  409.                     }
  410.                     else
  411.                     {   // set sample like normal
  412.                         v->sampleData = c->sampleData;
  413.                         v->loopStart  = c->loopStart;
  414.                         v->loopFlag   = (c->loopStart + c->loopLen) > 2;
  415.                         v->sampleEnd  = v->loopFlag ? (c->loopStart + c->loopLen) : c->sampleLen;
  416.                         v->swapData   = NULL;
  417.                     }
  418.                 }
  419.  
  420.                 c->invLoopLen = c->loopLen;
  421.                 c->invLoopPtr = c->invLoopStartPtr = c->sampleData + c->loopStart;
  422.             }
  423.  
  424.             if (c->efx == 0x9) // 9xx "sample offset" effect (must be handled here)
  425.             {
  426.                 if (c->efxDat) c->sampleOffsetMem = c->efxDat;
  427.                 c->sampleOffset = c->sampleOffsetMem;
  428.             }
  429.  
  430.             if (period > 0) // note found in pattern data
  431.             {
  432.                 if ((c->efx == 0xE) && ((c->efxDat & 0xF0) == 0x50))
  433.                     c->fineTune = ((c->efxDat & 0x0F) >= 8) ? ((c->efxDat & 0x0F) - 16) : (c->efxDat & 0x0F);
  434.  
  435.                 for (note = 0; note < 36; ++note) if (period >= periodTable[note]) break;
  436.  
  437.                 if (note < 36) // note was found in period table
  438.                 {
  439.                     c->note = note;
  440.                     c->toPeriod = periodTable[note];
  441.  
  442.                     if ((c->efx != 0x03) && (c->efx != 0x05) && !((c->efx == 0xE) && ((c->efxDat & 0xF0) == 0xD0)))
  443.                     {
  444.                         c->vibPos = c->tremoloPos = 0; // reset vibrato/tremolo position
  445.                         triggerVoice(c, v);
  446.                         v->delta = PERIOD2DELTA(c->period); // set initial voice pitch
  447.                     }
  448.                 }
  449.             }
  450.         }
  451.  
  452.         checkEfx();
  453.  
  454.         if (loopFlag) // handle pattern loop (E6x)
  455.         {
  456.             modRow = newModRow;
  457.             newPosFlag = loopFlag = newModRow = 0;
  458.         }
  459.         else if (++modRow >= 64) newPosFlag = 1; // increase row like normal
  460.     }
  461.     else checkEfx();
  462.  
  463.     if (newPosFlag) // handle position jump (Bxx), pattern break (Dxx) or end of pattern
  464.     {
  465.         modRow     = newModRow;
  466.         newPosFlag = newModRow = 0;
  467.         if (++modOrder >= modData[950]) modOrder = (modData[951] < modData[950]) ? modData[951] : 0; // song loop
  468.     }
  469. }
  470.  
  471. int main(int argc, char *argv[]) // please edit this for your own use!
  472. {
  473.     FILE *f;
  474.  
  475.     if (argc != 2)
  476.     {
  477.         printf("Usage: kolibrimod.exe <module>\n");
  478.         return (-1);
  479.     }
  480.  
  481.     f = fopen(argv[1], "rb");
  482.     if (f == NULL)
  483.     {
  484.         printf("ERROR: Can't open file!\n");
  485.         return (1);
  486.     }
  487.  
  488.     kolibrimodLoad(f, 44100, 19); // file, audio frequency, stereo separation (0..100%)
  489.     fclose(f);
  490.     kolibrimodPlay();
  491.  
  492.     printf("Playing at %dHz with %d%% stereo separation... Press any key to stop.\n", audioFreq, stereoSep);
  493.     while (!_getch()) Sleep(250);
  494.     kolibrimodFree();
  495.  
  496.     return (0);
  497. }
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
 
Top