Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /* kolibrimod - small 1..99ch .MOD replayer by 8bitbubsy - 26th of July 2021 - remember to link winmm.lib!
- ** x86/x86_64: Make sure to compile with SSE instructions enabled!
- ** License: BSD-3 clause
- */
- #define AUDIO_RATE 44100
- #define AUDIO_BUF_LEN 1024
- #include <assert.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <stdint.h>
- #include <stdbool.h>
- #include <conio.h> // _getch()
- #include <math.h> // powf()
- #include <windows.h> // Sleep(), mixer stuff
- #define AMIGA_STEREO_SEPARATION 20 /* percentage */
- #define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
- #define CLAMP16(i) if ((int16_t)(i) != i) i = 0x7FFF ^ (i >> 31)
- typedef struct
- {
- uint8_t note, sample, efx, param;
- } note_t;
- typedef struct
- {
- bool loopFlag;
- int8_t *data;
- uint8_t finetune, volume;
- uint32_t length, loopStart, loopLength;
- } sample_t;
- typedef struct
- {
- int8_t *sampleData, *swapData;
- int16_t pan; // 0..256
- bool loopFlag, swapLoopFlag;
- uint32_t sampleEnd, loopStart, loopLength, swapSampleEnd, swapLoopStart, swapLoopLength, oldPos, pos;
- float fVolL, fVolR, fFrac, fDelta;
- } voice_t;
- typedef struct
- {
- bool noteFlag, sampleLoopFlag;
- int8_t vol, volOut, finetune, *invLoopPtr, *sampleData, *invLoopStartPtr, E6Row, E6Cnt;
- uint8_t note, efx, efx2, portaMem, vibratoDepth, vibratoSpeed, glissandoMode, lastSample;
- uint8_t tremoloDepth, tremoloSpeed, vibratoPos, vibratoType, tremoloPos, tremoloType, invLoopSpeed, invLoopPos;
- uint16_t period, toPeriod, periodOut;
- int32_t sampleOffsetMem, sampleOffset, invLoopLen, sampleLength, sampleLoopStart, sampleLoopLength;
- } channel_t;
- static volatile bool isMixing;
- static bool newPosFlag, patLoop, ptMode;
- static char songName[20+1];
- static int8_t modSpeed, pattNum, tickExtendFactor, newRow, row, *mixerBuffer;
- static uint8_t modOrder, restartPos, orderNum, orders[128];
- static uint16_t modTick, minPeriod, maxPeriod, periodTab[16][96];
- static int32_t pattSize, channels, audioFreq;
- static int64_t tickSampleCounter64, samplesPerTick64, tickTable64[(255-32)+1];
- static note_t *patterns[128], *patternPtr;
- static sample_t samples[31];
- static float fMixBuffer[AUDIO_BUF_LEN*2], *fPeriod2Delta;
- static const uint8_t invLoopTab[16] = { 0, 5, 6, 7, 8, 10, 11, 13, 16, 19, 22, 26, 32, 43, 64, 128 };
- static const uint8_t arpTab[32] = { 0,1,2,0,1,2,0,1,2,0,1,2,0,1,2,0,1,2,0,1,2,0,1,2,0,1,2,0,1,2,0,1 };
- static const uint16_t loadPeriods[96] = // table for period->note on load (from FT2)
- {
- 6848, 6464, 6096, 5760, 5424, 5120, 4832, 4560, 4304, 4064, 3840, 3624,
- 3424, 3232, 3048, 2880, 2712, 2560, 2416, 2280, 2152, 2032, 1920, 1812,
- 1712, 1616, 1524, 1440, 1356, 1280, 1208, 1140, 1076, 1016, 960, 906,
- 856, 808, 762, 720, 678, 640, 604, 570, 538, 508, 480, 453,
- 428, 404, 381, 360, 339, 320, 302, 285, 269, 254, 240, 226,
- 214, 202, 190, 180, 170, 160, 151, 143, 135, 127, 120, 113,
- 107, 101, 95, 90, 85, 80, 75, 71, 67, 63, 60, 56,
- 53, 50, 47, 45, 42, 40, 37, 35, 33, 31, 30, 28
- };
- static const uint16_t ptPeriods[3*12] = // PT C-3..B-5 note->period table (values multiplied by 4)
- {
- 3424,3232,3048,2880,2712,2560,2416,2280,2152,2032,1920,1812,
- 1712,1616,1524,1440,1356,1280,1208,1140,1076,1016, 960, 904,
- 856, 808, 760, 720, 680, 640, 604, 572, 540, 508, 480, 452
- };
- static const uint16_t ft2Periods[8*12] = // FT2 C-0..B-7 note->period table (Amiga periods, some notes differ from PT)
- {
- 27392,25856,24384,23040,21696,20480,19328,18240,17216,16256,15360,14512,
- 13696,12928,12192,11520,10848,10240, 9664, 9120, 8608, 8128, 7680, 7256,
- 6848, 6464, 6096, 5760, 5424, 5120, 4832, 4560, 4304, 4064, 3840, 3628,
- 3424, 3232, 3048, 2880, 2712, 2560, 2416, 2280, 2152, 2032, 1920, 1814,
- 1712, 1616, 1524, 1440, 1356, 1280, 1208, 1140, 1076, 1016, 960, 907,
- 856, 808, 762, 720, 678, 640, 604, 570, 538, 508, 480, 453,
- 428, 404, 381, 360, 339, 320, 302, 285, 269, 254, 240, 227,
- 214, 202, 190, 180, 169, 160, 151, 142, 134, 127, 120, 113
- };
- static const uint8_t sinusTab[32] =
- {
- 0x00,0x18,0x31,0x4A,0x61,0x78,0x8D,0xA1,0xB4,0xC5,0xD4,0xE0,0xEB,0xF4,0xFA,0xFD,
- 0xFF,0xFD,0xFA,0xF4,0xEB,0xE0,0xD4,0xC5,0xB4,0xA1,0x8D,0x78,0x61,0x4A,0x31,0x18
- };
- static voice_t voice[100];
- static channel_t channel[100];
- static WAVEHDR waveBlocks[4];
- static HWAVEOUT hWaveOut;
- static WAVEFORMATEX wfx;
- static void tickReplayer(void);
- static bool generateTables(const int32_t periodClk)
- {
- fPeriod2Delta = (float *)malloc(((maxPeriod-minPeriod)+1) * sizeof (float));
- if (!fPeriod2Delta) return false;
- const int32_t noteOfs = ptMode ? (3*12) : 0;
- const uint16_t *note2Period = ptMode ? ptPeriods : ft2Periods;
- for (int32_t ftune = 0; ftune < 16; ftune++) // calculated finetuned period table
- {
- const double dMul = exp2(((ftune ^ 8) - 8) / -(12.0 * 8.0));
- for (int32_t note = 0; note < 96; note++)
- {
- periodTab[ftune][note] = UINT16_MAX;
- if (!(ptMode && (note < 3*12 || note >= (6*12))))
- periodTab[ftune][note] = (uint16_t)((note2Period[note-noteOfs] * dMul) + 0.5); // rounded
- }
- }
- for (int32_t p = minPeriod; p <= maxPeriod; p++) // calculate table for period -> delta
- fPeriod2Delta[p-minPeriod] = (float)(((double)periodClk / p) / (double)audioFreq);
- for (int32_t bpm = 32; bpm <= 255; bpm++) // calculate table for BPM -> "samples per tick"
- tickTable64[bpm-32] = (int64_t)(((double)audioFreq / (bpm / 2.5)) * (UINT32_MAX+1.0));
- return true;
- }
- static uint8_t periodToNote(uint16_t period, uint8_t ftune)
- {
- int32_t i;
- for (i = 0; i < 96; i++) if (period >= periodTab[ftune][i]) break;
- return (uint8_t)i;
- }
- // mixes one output sample w/ linear interpolation, and then increments the sampling pointer
- #define MIX_SMP \
- assert(smpPtr >= v->sampleData && smpPtr < v->sampleData+v->sampleEnd); \
- fSample = smpPtr[0]; \
- fSample2 = smpPtr[1]; \
- fSample += (fSample2-fSample) * v->fFrac; \
- *fCurMixPtr++ += fSample * v->fVolL; \
- *fCurMixPtr++ += fSample * v->fVolR; \
- v->fFrac += v->fDelta; \
- wholeSamples = (int32_t)v->fFrac; \
- smpPtr += wholeSamples; \
- v->fFrac -= wholeSamples; \
- static void mixChannels(float *fMixPtr, uint32_t numSamples)
- {
- voice_t *v = voice;
- for (int32_t i = 0; i < channels; i++, v++)
- {
- if (v->sampleData == NULL || v->sampleEnd <= 0 || v->fDelta <= 0.0f) continue; // voice is not active
- float fSample, fSample2, *fCurMixPtr = fMixPtr;
- int32_t wholeSamples;
- uint32_t samplesLeft = numSamples;
- while (samplesLeft)
- {
- const int32_t samplesUntilEnd = v->sampleEnd - v->pos;
- const float fSamplesToMix = (samplesUntilEnd - v->fFrac) / v->fDelta;
- uint32_t samplesToMix = (int32_t)fSamplesToMix + 1;
- if (samplesToMix > samplesLeft) samplesToMix = samplesLeft;
- samplesLeft -= samplesToMix;
- const int8_t *smpPtr = v->sampleData + v->pos;
- for (uint32_t j = 0; j < (samplesToMix & 7); j++)
- {
- MIX_SMP
- }
- samplesToMix >>= 3;
- for (uint32_t j = 0; j < samplesToMix; j++)
- {
- MIX_SMP MIX_SMP MIX_SMP MIX_SMP
- MIX_SMP MIX_SMP MIX_SMP MIX_SMP
- }
- v->pos = (uint32_t)(smpPtr - v->sampleData);
- if (v->pos >= v->sampleEnd) // end of sample?
- {
- if (v->swapData == NULL) // no sample swap to do
- {
- if (v->loopFlag)
- {
- do
- {
- v->pos -= v->loopLength;
- }
- while (v->pos >= v->sampleEnd);
- }
- else
- {
- v->sampleData = NULL;
- break;
- }
- }
- else // do sample swapping
- {
- if (!v->swapLoopFlag)
- {
- v->sampleData = NULL; // illegal swap, turn off voice
- break;
- }
- v->sampleData = v->swapData;
- v->sampleEnd = v->swapSampleEnd;
- v->loopStart = v->swapLoopStart;
- v->loopLength = v->swapLoopLength;
- v->loopFlag = v->swapLoopFlag;
- v->pos = v->loopStart;
- v->swapData = NULL; // current sample swap has finished
- }
- }
- }
- }
- }
- static void CALLBACK waveOutProc(HWAVEOUT _hWaveOut, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
- {
- if (uMsg != MM_WOM_DONE) return;
- WAVEHDR *waveBlockHeader = (WAVEHDR *)dwParam1;
- waveOutUnprepareHeader(_hWaveOut, waveBlockHeader, sizeof (WAVEHDR));
- if (!isMixing) return;
- memcpy(waveBlockHeader->lpData, mixerBuffer, AUDIO_BUF_LEN * 4);
- waveOutPrepareHeader(_hWaveOut, waveBlockHeader, sizeof (WAVEHDR));
- waveOutWrite(_hWaveOut, waveBlockHeader, sizeof (WAVEHDR));
- float *fMixBufferPtr = fMixBuffer;
- int32_t samplesLeft = AUDIO_BUF_LEN;
- while (samplesLeft > 0)
- {
- if (tickSampleCounter64 <= 0)
- {
- tickReplayer();
- tickSampleCounter64 += samplesPerTick64;
- }
- const int32_t remainingTick = (tickSampleCounter64 + UINT32_MAX) >> 32; // ceil rounding (upwards)
- int32_t samplesToMix = samplesLeft;
- if (samplesToMix > remainingTick) samplesToMix = remainingTick;
- mixChannels(fMixBufferPtr, samplesToMix);
- fMixBufferPtr += samplesToMix*2;
- samplesLeft -= samplesToMix;
- tickSampleCounter64 -= (int64_t)samplesToMix << 32;
- }
- fMixBufferPtr = fMixBuffer;
- int16_t *streamOut = (int16_t *)mixerBuffer;
- for (int32_t i = 0; i < AUDIO_BUF_LEN; i++)
- {
- int32_t smpL = (int32_t)(fMixBufferPtr[0] * (32768.0f / 2.0f));
- int32_t smpR = (int32_t)(fMixBufferPtr[1] * (32768.0f / 2.0f));
- CLAMP16(smpL);
- CLAMP16(smpR);
- *streamOut++ = (int16_t)smpL;
- *streamOut++ = (int16_t)smpR;
- *fMixBufferPtr++ = 0.0f;
- *fMixBufferPtr++ = 0.0f;
- }
- (void)dwParam2;
- (void)dwInstance;
- }
- #define ID(x) !strcmp(magic, x)
- static bool getNumbersOfChannels(FILE *in)
- {
- char magic[5]; magic[4] = '\0';
- fseek(in, 1080, SEEK_SET);
- fread(magic, 1, 4, in);
- rewind(in);
- ptMode = false;
- channels = 0;
- if (ID("M.K.") || ID("M!K!") || ID("NSMS") || ID("LARD") || ID("PATT")) { ptMode = true; channels = 4; } // ProTracker (or compatible)
- else if (magic[1] == 'C' && magic[2] == 'H' && magic[3] == 'N') channels = magic[0] - '0'; // FT2/others
- else if (magic[2] == 'C' && magic[3] == 'H') channels = ((magic[0] - '0') * 10) + (magic[1] - '0'); // FT2/others
- if (channels < 1 || channels > 99) return false;
- pattSize = channels * (64 * 4);
- return true;
- }
- static void freeData(void)
- {
- for (int32_t i = 0; i < 128; i++)
- {
- if (patterns[i] != NULL)
- {
- free(patterns[i]);
- patterns[i] = NULL;
- }
- }
- for (int32_t i = 0; i < 31; i++)
- {
- if (samples[i].data != NULL)
- {
- free(samples[i].data);
- samples[i].data = NULL;
- }
- }
- if (fPeriod2Delta != NULL)
- {
- free(fPeriod2Delta);
- fPeriod2Delta = NULL;
- }
- }
- bool kolibrimodLoad(FILE *in, int32_t audioOutputFreq)
- {
- freeData();
- if (!getNumbersOfChannels(in)) return false;
- rewind(in);
- fread(songName, 1, 20, in); songName[20] = '\0';
- fseek(in, 950, SEEK_SET);
- fread(&orderNum, 1, 1, in);
- fread(&restartPos, 1, 1, in);
- if (orderNum > 128) return false; // too many orders!
- // count number of patterns
- pattNum = 0;
- fseek(in, 952, SEEK_SET);
- for (uint8_t i = 0; i < 128; i++)
- {
- orders[i] = (uint8_t)fgetc(in);
- if (orders[i] > pattNum) pattNum = orders[i];
- }
- pattNum++;
- // read sample structs
- memset(samples, 0, sizeof (samples));
- fseek(in, 20, SEEK_SET);
- sample_t *smp = samples;
- for (int32_t i = 0; i < 31; i++, smp++)
- {
- fseek(in, 22, SEEK_CUR);
- uint8_t bytes[8]; fread(bytes, 1, 8, in);
- smp->finetune = bytes[2] & 0xF;
- smp->volume = (bytes[3] > 64) ? 64 : bytes[3];
- smp->length = ((bytes[0] << 8) | bytes[1]) * 2;
- smp->loopStart = ((bytes[4] << 8) | bytes[5]) * 2;
- smp->loopLength = ((bytes[6] << 8) | bytes[7]) * 2;
- smp->loopFlag = (smp->loopStart+smp->loopLength) > 2;
- }
- // load pattern data
- fseek(in, 1084, SEEK_SET);
- for (int32_t i = 0; i < pattNum; i++)
- {
- patterns[i] = (note_t *)malloc(pattSize);
- if (patterns[i] == NULL) { freeData(); return false; };
- note_t *p = patterns[i];
- for (int32_t j = 0; j < channels*64; j++, p++)
- {
- uint8_t bytes[4]; fread(bytes, 1, 4, in);
- uint16_t period = ((bytes[0] & 0x0F) << 8) | bytes[1];
- if (ptMode && period != 0 && (period < 113 || period > 856)) // scan for extended periods
- ptMode = false;
- int32_t k = 0; // convert period to note number (0..95, 96 = none/illegal)
- for (; k < 96; k++)
- if (period >= loadPeriods[k])
- break;
- p->note = k;
- p->sample = (bytes[0] & 0xF0) | (bytes[2] >> 4);
- p->efx = bytes[2] & 0xF;
- p->param = bytes[3];
- }
- }
- // load sample data
- fseek(in, 1084 + (pattSize * pattNum), SEEK_SET);
- smp = samples;
- for (int32_t i = 0; i < 31; i++, smp++)
- {
- if (smp->length == 0) continue;
- smp->data = (int8_t *)malloc(smp->length+1);
- if (smp->data == NULL) { freeData(); return false; };
- fread(smp->data, 1, smp->length, in);
- // fix end-sample for branchless interpolation
- const int32_t sampleEnd = smp->loopFlag ? (smp->loopStart+smp->loopLength) : smp->length;
- smp->data[sampleEnd] = smp->loopFlag ? smp->data[smp->loopStart] : 0;
- }
- // ProTracker settings (multiply by four to match FT2 period scale)
- int32_t periodClk = 3546895 * 4;
- minPeriod = 113 * 4;
- maxPeriod = 856 * 4;
- if (!ptMode) // set FT2 settings
- {
- periodClk = 14317456; // 8363*1712
- minPeriod = 1;
- maxPeriod = 32000-1;
- }
- memset(&wfx, 0, sizeof (wfx));
- audioFreq = audioOutputFreq;
- wfx.nSamplesPerSec = audioFreq;
- wfx.wBitsPerSample = 16;
- wfx.nChannels = 2;
- wfx.wFormatTag = WAVE_FORMAT_PCM;
- wfx.nBlockAlign = (wfx.wBitsPerSample * wfx.nChannels) / 8;
- wfx.nAvgBytesPerSec = wfx.nBlockAlign * wfx.nSamplesPerSec;
- mixerBuffer = (int8_t *)calloc(AUDIO_BUF_LEN*4, 1);
- waveOutOpen(&hWaveOut, WAVE_MAPPER, &wfx, (DWORD_PTR)waveOutProc, 0, CALLBACK_FUNCTION);
- for (int32_t i = 0; i < 4; i++)
- {
- waveBlocks[i].dwBufferLength = AUDIO_BUF_LEN*4;
- waveBlocks[i].lpData = (LPSTR)calloc(AUDIO_BUF_LEN*4, 1);
- waveOutPrepareHeader(hWaveOut, &waveBlocks[i], sizeof (WAVEHDR));
- waveOutWrite(hWaveOut, &waveBlocks[i], sizeof (WAVEHDR));
- }
- if (!generateTables(periodClk)) { freeData(); return false; }
- return true;
- }
- bool kolibrimodPlay(void)
- {
- memset(channel, 0, sizeof (channel));
- memset(voice, 0, sizeof (voice));
- for (int32_t i = 0; i < channels; i++) // set up initial LRRL channel pannings
- {
- voice[i].pan = 128; // center
- const int32_t panMod = (AMIGA_STEREO_SEPARATION * 128) / 100;
- if (ptMode)
- voice[i].pan = !((i + 1) & 2) ? (128-panMod) : (128+panMod); // Amiga panning - LRRL
- }
- modSpeed = 6;
- samplesPerTick64 = tickTable64[125-32];
- tickSampleCounter64 = 0; // zero tick sample counter so that it will instantly initiate a tick
- modTick = modSpeed-1; // don't render a tick of silence
- row = newRow = modOrder = 0;
- tickExtendFactor = 1; // 1 = no delay
- newPosFlag = patLoop = false;
- isMixing = true;
- patternPtr = patterns[orders[modOrder]];
- return true;
- }
- void kolibrimodFree(void)
- {
- isMixing = false;
- for (int32_t i = 0; i < 4; i++)
- waveOutUnprepareHeader(hWaveOut, &waveBlocks[i], sizeof (WAVEHDR));
- for (int32_t i = 0; i < 4; i++)
- {
- while (waveBlocks[i].dwFlags & WHDR_PREPARED) SleepEx(1, 1); // wait
- if (waveBlocks[i].lpData != NULL) free(waveBlocks[i].lpData); waveBlocks[i].lpData = NULL;
- }
- waveOutReset(hWaveOut);
- waveOutClose(hWaveOut);
- if (mixerBuffer != NULL)
- {
- free(mixerBuffer);
- mixerBuffer = NULL;
- }
- freeData();
- }
- static void triggerVoice(channel_t *c, voice_t *v)
- {
- v->sampleData = c->sampleData;
- v->loopStart = c->sampleLoopStart;
- v->loopFlag = c->sampleLoopFlag;
- v->sampleEnd = v->loopFlag ? (c->sampleLoopStart + c->sampleLoopLength) : c->sampleLength;
- v->loopLength = c->sampleLoopLength;
- v->swapData = NULL;
- v->fFrac = 0.0f;
- c->period = c->toPeriod;
- v->pos = v->oldPos = c->sampleOffset; // 9xx sample offset * 256
- if (v->pos >= v->sampleEnd)
- {
- if (v->loopFlag)
- v->pos = v->loopStart;
- else
- v->sampleData = NULL; // cut voice
- }
- }
- static void portamento(channel_t *c)
- {
- if (c->period == 0 || c->toPeriod == 0) return;
- if (c->period > c->toPeriod)
- {
- c->period -= c->portaMem*4;
- if ((int16_t)c->period <= c->toPeriod) c->period = c->toPeriod;
- }
- else if (c->period < c->toPeriod)
- {
- c->period += c->portaMem*4;
- if (c->period >= c->toPeriod) c->period = c->toPeriod;
- }
- c->periodOut = c->period;
- if (c->glissandoMode != 0) // semitones slide
- {
- const uint8_t note = periodToNote(c->periodOut, c->finetune);
- if (note < 96) c->periodOut = periodTab[c->finetune][note];
- }
- }
- static uint16_t getWaveform(channel_t *c, bool tremoloFlag)
- {
- const uint8_t type = tremoloFlag ? c->tremoloType : c->vibratoType;
- uint8_t pos = ((tremoloFlag ? c->tremoloPos : c->vibratoPos) >> 2) & 0x1F;
- uint8_t data;
- if (type == 0) { data = sinusTab[pos]; }
- else if (type == 1) { data = pos << 3; if (c->vibratoPos >= 128) data = ~data; } // PT2/FT2 bug: always uses vibrato pos
- else { data = 255; }
- return tremoloFlag ? ((data * c->tremoloDepth) >> 6) : ((data * c->vibratoDepth) >> (7-2));
- }
- static void vibrato(channel_t *c)
- {
- const uint16_t data = getWaveform(c, false);
- c->periodOut = c->period + ((c->vibratoPos < 128) ? data : -data);
- if (modTick > 0) c->vibratoPos += c->vibratoSpeed;
- }
- static void tremolo(channel_t *c)
- {
- const uint16_t data = getWaveform(c, true);
- c->volOut = c->vol + ((c->tremoloPos < 128) ? data : -data);
- c->volOut = CLAMP(c->volOut, 0, 64);
- if (modTick > 0) c->tremoloPos += c->tremoloSpeed;
- }
- static void volumeSlide(channel_t *c)
- {
- if (modTick == 0) return;
- c->vol += (c->efx2 & 0xF0) ? (c->efx2 >> 4) : -(c->efx2 & 0xF);
- c->vol = CLAMP(c->vol, 0, 64);
- }
- static void invertLoop(channel_t *c) // ProTracker only
- {
- if ((c->invLoopPos += invLoopTab[c->invLoopSpeed]) < 128) return;
- c->invLoopPos = 0;
- if (c->invLoopPtr == NULL) return;
- if (++c->invLoopPtr >= c->invLoopStartPtr+c->invLoopLen) c->invLoopPtr = c->invLoopStartPtr;
- *c->invLoopPtr = -1 - *c->invLoopPtr;
- }
- static void retrigNote(channel_t *c, voice_t *v) // E9x effect
- {
- if (c->efx2 > 0x90 && !(modTick == 0 && c->noteFlag) && !(modTick % (c->efx2 & 0xF)))
- {
- v->pos = v->oldPos;
- v->fFrac = 0.0f;
- }
- }
- static void checkEfx(void)
- {
- uint16_t period;
- channel_t *c = channel;
- voice_t *v = voice;
- for (int32_t i = 0; i < channels; i++, c++, v++) // do effects
- {
- if (ptMode && modTick > 0) invertLoop(c);
- if (c->efx != 0 || c->efx2 != 0)
- {
- if (c->efx == 0x4) { if (c->efx2&0xF) c->vibratoDepth = c->efx2&0xF; if (c->efx2&0xF0) c->vibratoSpeed = (c->efx2>>4)*4; vibrato(c); }
- if (c->efx == 0x7) { if (c->efx2&0xF) c->tremoloDepth = c->efx2&0xF; if (c->efx2&0xF0) c->tremoloSpeed = (c->efx2>>4)*4; tremolo(c); }
- if (c->efx == 0x6) { vibrato(c); volumeSlide(c); }
- if (modTick == 0)
- { // tick=0 effects
- if (c->efx == 0x8) { if (!ptMode) v->pan = c->efx2; }
- else if (c->efx == 0xB) { newPosFlag = true; newRow = 0; modOrder = c->efx2 - 1; }
- else if (c->efx == 0xC) { c->vol = (c->efx2 > 64) ? 64 : c->efx2; }
- else if (c->efx == 0xD) { newPosFlag = true; newRow = ((c->efx2 >> 4) * 10) + (c->efx2 & 0xF); if (newRow > 63) newRow = 0; }
- else if (c->efx == 0xE)
- {
- if ((c->efx2&0xF0)==0x10) { if (c->period > 0) { c->period -= (c->efx2 & 0xF)*4; if ((int16_t)c->period < minPeriod) c->period = minPeriod; } }
- else if ((c->efx2&0xF0)==0x20) { if (c->period > 0) { c->period += (c->efx2 & 0xF)*4; if ((int16_t)c->period > maxPeriod) c->period = maxPeriod; } }
- else if ((c->efx2&0xF0)==0x30) { c->glissandoMode = c->efx2 & 0xF; }
- else if ((c->efx2&0xF0)==0x40) { c->vibratoType = c->efx2 & 0xF; }
- else if ((c->efx2&0xF0)==0x50) { c->finetune = ptMode ? (c->efx2 & 0xF) : (c->efx2 - 8) & 0xF; }
- else if ((c->efx2&0xF0)==0x60) { if (c->efx2==0x60) c->E6Row = row; else { if (!c->E6Cnt) c->E6Cnt = c->efx2&0xF;
- else if (!--c->E6Cnt) return; newRow = c->E6Row; patLoop = true; }}
- else if ((c->efx2&0xF0)==0x70) { c->tremoloType = c->efx2 & 0xF; }
- else if ((c->efx2&0xF0)==0x90) retrigNote(c, v);
- else if ((c->efx2&0xF0)==0xA0) { c->vol += c->efx2 & 0xF; if (c->vol > 64) c->vol = 64; }
- else if ((c->efx2&0xF0)==0xB0) { c->vol -= c->efx2 & 0xF; if (c->vol < 0) c->vol = 0; }
- else if ((c->efx2&0xF0)==0xE0) { tickExtendFactor = (c->efx2 & 0xF) + 1; }
- else if ((c->efx2&0xF0)==0xF0) { if (ptMode) { c->invLoopSpeed = c->efx2 & 0xF; invertLoop(c); } }
- }
- else if (c->efx == 0xF) if (c->efx2 < 0x20) { if (c->efx2) modSpeed = c->efx2; } else { samplesPerTick64 = tickTable64[c->efx2-32]; }
- }
- else
- { // tick>0 effects
- if (c->efx == 0x1) { if (c->period > 0) { c->period -= c->efx2*4; if ((int16_t)c->period < minPeriod) c->period = minPeriod; } }
- else if (c->efx == 0x2) { if (c->period > 0) { c->period += c->efx2*4; if ((int16_t)c->period > maxPeriod) c->period = maxPeriod; } }
- else if (c->efx == 0x3) { if (c->efx2) c->portaMem = c->efx2; portamento(c); }
- else if (c->efx == 0x5) { portamento(c); volumeSlide(c); }
- else if (c->efx == 0xE) { if ((c->efx2 & 0xF0) == 0x90) retrigNote(c, v); }
- else if (c->efx == 0xA) { volumeSlide(c); }
- }
- }
- if ((c->efx == 0xE) && (c->efx2 & 0xF0) == 0xC0) { if (modTick == (c->efx2 & 0xF)) c->vol = 0; }
- else if ((c->efx == 0xE) && (c->efx2 & 0xF0) == 0xD0) { if (modTick == (c->efx2 & 0xF) && c->noteFlag) triggerVoice(c, v); }
- period = c->period;
- if (c->efx == 0 && c->efx2 != 0 && period > 0) // do arpeggio here
- {
- uint8_t note = periodToNote(period, c->finetune);
- if (note < 96)
- {
- const uint8_t arpTick = arpTab[modTick];
- if (arpTick == 1) note += c->efx2 >> 4;
- else if (arpTick == 2) note += c->efx2 & 0xF;
- if (note > 95) note = 95;
- period = periodTab[c->finetune][note];
- }
- }
- // vibrato and portamento modifies the output period directly
- if (c->efx != 4 && c->efx != 6 && c->efx != 3 && c->efx != 5) c->periodOut = period;
- if (c->periodOut == 0) v->fDelta = 0.0f; else v->fDelta = fPeriod2Delta[CLAMP(c->periodOut, minPeriod, maxPeriod)-minPeriod];
- if (c->efx != 7) c->volOut = c->vol; // tremolo modifies the output volume directly
- v->fVolL = (c->volOut * (256-v->pan)) * (1.0f / (64.0f * 256.0f * 128.0f)); // scale = vol(0..64) * pan(0..256) * 8BitSmpScale(128)
- v->fVolR = (c->volOut * v->pan ) * (1.0f / (64.0f * 256.0f * 128.0f));
- }
- }
- static void tickReplayer(void)
- {
- if (++modTick >= modSpeed*tickExtendFactor)
- {
- modTick = 0;
- tickExtendFactor = 1; // reset EEx (pattern delay). 1 = no delay
- channel_t *c = channel;
- voice_t *v = voice;
- for (uint8_t i = 0; i < channels; i++, c++, v++) // read pattern data
- {
- const note_t *note = &patternPtr[(row*channels) + i];
- c->noteFlag = note->note != 96;
- const uint8_t sample = note->sample;
- c->efx = note->efx;
- c->efx2 = note->param;
- const bool noteDelay = (c->efx == 0xE) && ((c->efx2 & 0xF0) == 0xD0);
- const bool porta = (c->efx == 0x3 || c->efx == 0x5);
- if (sample >= 1 && sample <= 31) // sample found in pattern data
- {
- sample_t *s = &samples[sample-1];
- c->finetune = s->finetune;
- c->volOut = c->vol = s->volume;
- c->sampleLength = s->length;
- c->sampleLoopStart = s->loopStart;
- c->sampleLoopLength = s->loopLength;
- c->sampleLoopFlag = s->loopFlag;
- c->sampleData = s->data;
- c->sampleOffset = 0;
- if (!ptMode) v->pan = 128; // reset panning (FT2 mode)
- if (sample != c->lastSample && ptMode && (v->sampleData != NULL) && (!c->noteFlag || porta))
- { // do ProTracker sample swap (if voice is active and no pattern note)
- v->swapData = c->sampleData;
- v->swapLoopStart = c->sampleLoopStart;
- v->swapLoopLength = c->sampleLoopLength;
- v->swapLoopFlag = c->sampleLoopFlag;
- v->swapSampleEnd = v->swapLoopFlag ? (c->sampleLoopStart + c->sampleLoopLength) : c->sampleLength;
- }
- c->invLoopLen = c->sampleLoopLength;
- c->invLoopPtr = c->invLoopStartPtr = &c->sampleData[c->sampleLoopStart];
- c->lastSample = sample;
- }
- if (c->efx == 0x9) // 9xx "set sample offset" efx must be handled here
- {
- if (c->efx2 > 0) c->sampleOffsetMem = c->efx2 << 8;
- c->sampleOffset = c->sampleOffsetMem;
- }
- if (c->noteFlag) // note found in pattern data
- {
- if (c->efx == 0xE && (c->efx2 & 0xF0) == 0x50) // set-finetune efx must be handled here as well
- c->finetune = ptMode ? (c->efx2 & 0xF) : (c->efx2 - 8) & 0xF;
- c->note = note->note;
- c->toPeriod = periodTab[c->finetune][c->note];
- // trigger sample if no portamento (3xx and 5xy) and no note delay
- if (c->efx != 0x03 && c->efx != 0x05 && !noteDelay)
- {
- c->vibratoPos = c->tremoloPos = 0; // reset vibrato/tremolo position
- triggerVoice(c, v);
- c->periodOut = c->period = c->toPeriod; // set initial period
- }
- }
- }
- checkEfx();
- if (patLoop) // handle pattern loop (E6x)
- {
- row = newRow;
- newRow = 0;
- newPosFlag = patLoop = false;
- }
- else if (++row >= 64) newPosFlag = true; // increase row like normal
- }
- else checkEfx();
- if (newPosFlag) // handle position jump (Bxx), pattern break (Dxx) or end of pattern
- {
- row = newRow;
- newRow = 0;
- newPosFlag = false;
- if (++modOrder >= orderNum)
- modOrder = (restartPos < orderNum) ? restartPos : 0; // loop (wrap) song
- patternPtr = patterns[orders[modOrder]];
- }
- }
- int main(int argc, char *argv[]) // please edit this for your own use!
- {
- #ifdef _DEBUG
- FILE *f = fopen("debug.mod", "rb");
- #else
- if (argc != 2)
- {
- printf("Usage: kolibrimod.exe <module>\n");
- return -1;
- }
- FILE *f = fopen(argv[1], "rb");
- #endif
- if (f == NULL)
- {
- printf("ERROR: Can't open file!\n");
- return 1;
- }
- if (!kolibrimodLoad(f, AUDIO_RATE))
- {
- printf("ERROR: This .MOD is not supported!\n");
- return 1;
- }
- fclose(f);
- kolibrimodPlay();
- printf("Playing \"%s\" (%dHz - %d channels)\nPress any key to stop...\n",
- songName, audioFreq, channels);
- while (!_getch()) Sleep(250);
- kolibrimodFree();
- return 0;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement