/*
    AudioGeneratorMOD
    Audio output generator that plays Amiga MOD tracker files

    Copyright (C) 2017  Earle F. Philhower, III

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#define PGM_READ_UNALIGNED 0

#include "AudioGeneratorMOD.h"

/*
    Ported/hacked out from STELLARPLAYER by Ronen K.
    http://mobile4dev.blogspot.com/2012/11/stellaris-launchpad-mod-player.html
    A version exists in GitHub at https://github.com/steveway/stellarplayer
    and also at https://github.com/MikesModz/StellarPlayer
    Both which were themselves a port of the PIC32 MOD player
    https://www.youtube.com/watch?v=i3Yl0TISQBE (seems to no longer be available.)

    Most changes involved reducing memory usage by changing data structures,
    moving constants to PROGMEM and minor tweaks to allow non pow2 buffer sizes.
*/

#pragma GCC optimize ("O3")

#define NOTE(r, c) (Player.currentPattern.note8[r][c]==NONOTE8?NONOTE:8*Player.currentPattern.note8[r][c])

#ifndef min
#define min(X,Y) ((X) < (Y) ? (X) : (Y))
#endif

AudioGeneratorMOD::AudioGeneratorMOD() {
    sampleRate = 44100;
    fatBufferSize = 6 * 1024;
    stereoSeparation = 32;
    mixerTick = 0;
    usePAL = false;
    UpdateAmiga();
    running = false;
    file = NULL;
    output = NULL;
}

AudioGeneratorMOD::~AudioGeneratorMOD() {
    // Free any remaining buffers
    for (int i = 0; i < CHANNELS; i++) {
        FatBuffer.channels[i] = NULL;
    }
}

bool AudioGeneratorMOD::stop() {
    // We may be stopping because of allocation failures, so always deallocate
    for (int i = 0; i < CHANNELS; i++) {
        free(FatBuffer.channels[i]);
        FatBuffer.channels[i] = NULL;
    }

    if (running || ((file != NULL) && (file->isOpen() == true))) {
        output->flush();  //flush I2S output buffer, if the player was actually running before.
    }

    if (file) {
        file->close();
    }
    running = false;
    output->stop();
    return true;
}

bool AudioGeneratorMOD::loop() {
    if (!running) {
        goto done;    // Easy-peasy
    }

    // First, try and push in the stored sample.  If we can't, then punt and try later
    if (!output->ConsumeSample(lastSample)) {
        goto done;    // FIFO full, wait...
    }

    // Now advance enough times to fill the i2s buffer
    do {
        if (mixerTick == 0) {
            running = RunPlayer();
            if (!running) {
                stop();
                goto done;
            }
            mixerTick = Player.samplesPerTick;
        }
        GetSample(lastSample);
        mixerTick--;
    } while (output->ConsumeSample(lastSample));

done:
    file->loop();
    output->loop();

    // We'll be left with one sample still in our buffer because it couldn't fit in the FIFO
    return running;
}

bool AudioGeneratorMOD::begin(AudioFileSource *source, AudioOutput *out) {
    if (running) {
        stop();
    }

    if (!source) {
        return false;
    }
    file = source;
    if (!out) {
        return false;
    }
    output = out;

    if (!file->isOpen()) {
        return false;    // Can't read the file!
    }

    // Set the output values properly
    if (!output->SetRate(sampleRate)) {
        return false;
    }
    if (!output->SetChannels(2)) {
        return false;
    }
    if (!output->begin()) {
        return false;
    }

    UpdateAmiga();

    for (int i = 0; i < CHANNELS; i++) {
        FatBuffer.channels[i] = reinterpret_cast<uint8_t*>(calloc(fatBufferSize, 1));
        if (!FatBuffer.channels[i]) {
            stop();
            return false;
        }
    }
    if (!LoadMOD()) {
        stop();
        return false;
    }
    running = true;
    return true;
}

// Sorted Amiga periods
static const uint16_t amigaPeriods[296] PROGMEM = {
    907, 900, 894, 887, 881, 875, 868, 862, //  -8 to -1
    856, 850, 844, 838, 832, 826, 820, 814, // C-1 to +7
    808, 802, 796, 791, 785, 779, 774, 768, // C#1 to +7
    762, 757, 752, 746, 741, 736, 730, 725, // D-1 to +7
    720, 715, 709, 704, 699, 694, 689, 684, // D#1 to +7
    678, 675, 670, 665, 660, 655, 651, 646, // E-1 to +7
    640, 636, 632, 628, 623, 619, 614, 610, // F-1 to +7
    604, 601, 597, 592, 588, 584, 580, 575, // F#1 to +7
    570, 567, 563, 559, 555, 551, 547, 543, // G-1 to +7
    538, 535, 532, 528, 524, 520, 516, 513, // G#1 to +7
    508, 505, 502, 498, 494, 491, 487, 484, // A-1 to +7
    480, 477, 474, 470, 467, 463, 460, 457, // A#1 to +7
    453, 450, 447, 444, 441, 437, 434, 431, // B-1 to +7
    428, 425, 422, 419, 416, 413, 410, 407, // C-2 to +7
    404, 401, 398, 395, 392, 390, 387, 384, // C#2 to +7
    381, 379, 376, 373, 370, 368, 365, 363, // D-2 to +7
    360, 357, 355, 352, 350, 347, 345, 342, // D#2 to +7
    339, 337, 335, 332, 330, 328, 325, 323, // E-2 to +7
    320, 318, 316, 314, 312, 309, 307, 305, // F-2 to +7
    302, 300, 298, 296, 294, 292, 290, 288, // F#2 to +7
    285, 284, 282, 280, 278, 276, 274, 272, // G-2 to +7
    269, 268, 266, 264, 262, 260, 258, 256, // G#2 to +7
    254, 253, 251, 249, 247, 245, 244, 242, // A-2 to +7
    240, 238, 237, 235, 233, 232, 230, 228, // A#2 to +7
    226, 225, 223, 222, 220, 219, 217, 216, // B-2 to +7
    214, 212, 211, 209, 208, 206, 205, 203, // C-3 to +7
    202, 200, 199, 198, 196, 195, 193, 192, // C#3 to +7
    190, 189, 188, 187, 185, 184, 183, 181, // D-3 to +7
    180, 179, 177, 176, 175, 174, 172, 171, // D#3 to +7
    170, 169, 167, 166, 165, 164, 163, 161, // E-3 to +7
    160, 159, 158, 157, 156, 155, 154, 152, // F-3 to +7
    151, 150, 149, 148, 147, 146, 145, 144, // F#3 to +7
    143, 142, 141, 140, 139, 138, 137, 136, // G-3 to +7
    135, 134, 133, 132, 131, 130, 129, 128, // G#3 to +7
    127, 126, 125, 125, 123, 123, 122, 121, // A-3 to +7
    120, 119, 118, 118, 117, 116, 115, 114, // A#3 to +7
    113, 113, 112, 111, 110, 109, 109, 108  // B-3 to +7
};
#define ReadAmigaPeriods(a) (uint16_t)pgm_read_word(amigaPeriods + (a))

static const uint8_t sine[64] PROGMEM = {
    0,  24,  49,  74,  97, 120, 141, 161,
    180, 197, 212, 224, 235, 244, 250, 253,
    255, 253, 250, 244, 235, 224, 212, 197,
    180, 161, 141, 120,  97,  74,  49,  24
};
#define ReadSine(a) pgm_read_byte(sine + (a))


static inline uint16_t MakeWord(uint8_t h, uint8_t l) {
    return h << 8 | l;
}

bool AudioGeneratorMOD::LoadHeader() {
    uint8_t i;
    uint8_t temp[4];
    uint8_t junk[22];

    if (20 != file->read(/*Mod.name*/junk, 20)) {
        return false;    // Skip MOD name
    }
    for (i = 0; i < SAMPLES; i++) {
        if (22 != file->read(junk /*Mod.samples[i].name*/, 22)) {
            return false;    // Skip sample name
        }
        if (2 != file->read(temp, 2)) {
            return false;
        }
        Mod.samples[i].length = MakeWord(temp[0], temp[1]) * 2;
        if (1 != file->read(reinterpret_cast<uint8_t*>(&Mod.samples[i].fineTune), 1)) {
            return false;
        }
        if (Mod.samples[i].fineTune > 7) {
            Mod.samples[i].fineTune -= 16;
        }
        if (1 != file->read(&Mod.samples[i].volume, 1)) {
            return false;
        }
        if (2 != file->read(temp, 2)) {
            return false;
        }
        Mod.samples[i].loopBegin = MakeWord(temp[0], temp[1]) * 2;
        if (2 != file->read(temp, 2)) {
            return false;
        }
        Mod.samples[i].loopLength = MakeWord(temp[0], temp[1]) * 2;
        if (Mod.samples[i].loopBegin + Mod.samples[i].loopLength > Mod.samples[i].length) {
            Mod.samples[i].loopLength = Mod.samples[i].length - Mod.samples[i].loopBegin;
        }
    }

    if (1 != file->read(&Mod.songLength, 1)) {
        return false;
    }
    if (1 != file->read(temp, 1)) {
        return false;    // Discard this byte
    }

    Mod.numberOfPatterns = 0;
    for (i = 0; i < 128; i++) {
        if (1 != file->read(&Mod.order[i], 1)) {
            return false;
        }
        if (Mod.order[i] > Mod.numberOfPatterns) {
            Mod.numberOfPatterns = Mod.order[i];
        }
    }
    Mod.numberOfPatterns++;

    // Offset 1080
    if (4 != file->read(temp, 4)) {
        return false;
    };
    if (!strncmp(reinterpret_cast<const char*>(temp + 1), "CHN", 3)) {
        Mod.numberOfChannels = temp[0] - '0';
    } else if (!strncmp(reinterpret_cast<const char*>(temp + 2), "CH", 2)) {
        Mod.numberOfChannels = (temp[0] - '0') * 10 + temp[1] - '0';
    } else {
        Mod.numberOfChannels = 4;
    }

    if (Mod.numberOfChannels > CHANNELS) {
        audioLogger->printf("\nAudioGeneratorMOD::LoadHeader abort - too many channels (configured: %d, needed: %d)\n", CHANNELS, Mod.numberOfChannels);
        return (false);
    }

    return true;
}

void AudioGeneratorMOD::LoadSamples() {
    uint8_t i;
    uint32_t fileOffset = 1084 + Mod.numberOfPatterns * ROWS * Mod.numberOfChannels * 4 - 1;

    for (i = 0; i < SAMPLES; i++) {

        if (Mod.samples[i].length) {
            Mixer.sampleBegin[i] = fileOffset;
            Mixer.sampleEnd[i] = fileOffset + Mod.samples[i].length;
            if (Mod.samples[i].loopLength > 2) {
                Mixer.sampleloopBegin[i] = fileOffset + Mod.samples[i].loopBegin;
                Mixer.sampleLoopLength[i] = Mod.samples[i].loopLength;
                Mixer.sampleLoopEnd[i] = Mixer.sampleloopBegin[i] + Mixer.sampleLoopLength[i];
            } else {
                Mixer.sampleloopBegin[i] = 0;
                Mixer.sampleLoopLength[i] = 0;
                Mixer.sampleLoopEnd[i] = 0;
            }
            fileOffset += Mod.samples[i].length;
        }

    }

}

bool AudioGeneratorMOD::LoadPattern(uint8_t pattern) {
    uint8_t row;
    uint8_t channel;
    uint8_t i;
    uint8_t temp[4];
    uint16_t amigaPeriod;

    if (!file->seek(1084 + pattern * ROWS * Mod.numberOfChannels * 4, SEEK_SET)) {
        return false;
    }

    for (row = 0; row < ROWS; row++) {
        for (channel = 0; channel < Mod.numberOfChannels; channel++) {

            if (4 != file->read(temp, 4)) {
                return false;
            }

            Player.currentPattern.sampleNumber[row][channel] = (temp[0] & 0xF0) + (temp[2] >> 4);

            amigaPeriod = ((temp[0] & 0xF) << 8) + temp[1];
            //   Player.currentPattern.note[row][channel] = NONOTE;
            Player.currentPattern.note8[row][channel] = NONOTE8;
            for (i = 1; i < 37; i++)
                if (amigaPeriod > ReadAmigaPeriods(i * 8) - 3 &&
                        amigaPeriod < ReadAmigaPeriods(i * 8) + 3) {
                    Player.currentPattern.note8[row][channel] = i;
                }

            Player.currentPattern.effectNumber[row][channel] = temp[2] & 0xF;
            Player.currentPattern.effectParameter[row][channel] = temp[3];
        }
    }

    return true;
}

void AudioGeneratorMOD::Portamento(uint8_t channel) {
    if (Player.lastAmigaPeriod[channel] < Player.portamentoNote[channel]) {
        Player.lastAmigaPeriod[channel] += Player.portamentoSpeed[channel];
        if (Player.lastAmigaPeriod[channel] > Player.portamentoNote[channel]) {
            Player.lastAmigaPeriod[channel] = Player.portamentoNote[channel];
        }
    }
    if (Player.lastAmigaPeriod[channel] > Player.portamentoNote[channel]) {
        Player.lastAmigaPeriod[channel] -= Player.portamentoSpeed[channel];
        if (Player.lastAmigaPeriod[channel] < Player.portamentoNote[channel]) {
            Player.lastAmigaPeriod[channel] = Player.portamentoNote[channel];
        }
    }
    Mixer.channelFrequency[channel] = Player.amiga / Player.lastAmigaPeriod[channel];
}

void AudioGeneratorMOD::Vibrato(uint8_t channel) {
    uint16_t delta;
    uint16_t temp;

    temp = Player.vibratoPos[channel] & 31;

    switch (Player.waveControl[channel] & 3) {
    case 0:
        delta = ReadSine(temp);
        break;
    case 1:
        temp <<= 3;
        if (Player.vibratoPos[channel] < 0) {
            temp = 255 - temp;
        }
        delta = temp;
        break;
    case 2:
        delta = 255;
        break;
    case 3:
        delta = rand() & 255;
        break;
    }

    delta *= Player.vibratoDepth[channel];
    delta >>= 7;

    if (Player.vibratoPos[channel] >= 0) {
        Mixer.channelFrequency[channel] = Player.amiga / (Player.lastAmigaPeriod[channel] + delta);
    } else {
        Mixer.channelFrequency[channel] = Player.amiga / (Player.lastAmigaPeriod[channel] - delta);
    }

    Player.vibratoPos[channel] += Player.vibratoSpeed[channel];
    if (Player.vibratoPos[channel] > 31) {
        Player.vibratoPos[channel] -= 64;
    }
}

void AudioGeneratorMOD::Tremolo(uint8_t channel) {
    uint16_t delta;
    uint16_t temp;

    temp = Player.tremoloPos[channel] & 31;

    switch (Player.waveControl[channel] & 3) {
    case 0:
        delta = ReadSine(temp);
        break;
    case 1:
        temp <<= 3;
        if (Player.tremoloPos[channel] < 0) {
            temp = 255 - temp;
        }
        delta = temp;
        break;
    case 2:
        delta = 255;
        break;
    case 3:
        delta = rand() & 255;
        break;
    }

    delta *= Player.tremoloDepth[channel];
    delta >>= 6;

    if (Player.tremoloPos[channel] >= 0) {
        if (Player.volume[channel] + delta > 64) {
            delta = 64 - Player.volume[channel];
        }
        Mixer.channelVolume[channel] = Player.volume[channel] + delta;
    } else {
        if (Player.volume[channel] - delta < 0) {
            delta = Player.volume[channel];
        }
        Mixer.channelVolume[channel] = Player.volume[channel] - delta;
    }

    Player.tremoloPos[channel] += Player.tremoloSpeed[channel];
    if (Player.tremoloPos[channel] > 31) {
        Player.tremoloPos[channel] -= 64;
    }
}

bool AudioGeneratorMOD::ProcessRow() {
    bool jumpFlag;
    bool breakFlag;
    uint8_t channel;
    uint8_t sampleNumber;
    uint16_t note;
    uint8_t effectNumber;
    uint8_t effectParameter;
    uint8_t effectParameterX;
    uint8_t effectParameterY;
    uint16_t sampleOffset;

    if (!running) {
        return false;
    }

    Player.lastRow = Player.row++;
    jumpFlag = false;
    breakFlag = false;
    for (channel = 0; channel < Mod.numberOfChannels; channel++) {

        sampleNumber = Player.currentPattern.sampleNumber[Player.lastRow][channel];
        note = NOTE(Player.lastRow, channel);
        effectNumber = Player.currentPattern.effectNumber[Player.lastRow][channel];
        effectParameter = Player.currentPattern.effectParameter[Player.lastRow][channel];
        effectParameterX = effectParameter >> 4;
        effectParameterY = effectParameter & 0xF;
        sampleOffset = 0;

        if (sampleNumber) {
            Player.lastSampleNumber[channel] = sampleNumber - 1;
            if (!(effectNumber == 0xE && effectParameterX == NOTEDELAY)) {
                Player.volume[channel] = Mod.samples[Player.lastSampleNumber[channel]].volume;
            }
        }

        if (note != NONOTE) {
            Player.lastNote[channel] = note;
            Player.amigaPeriod[channel] = ReadAmigaPeriods(note + Mod.samples[Player.lastSampleNumber[channel]].fineTune);

            if (effectNumber != TONEPORTAMENTO && effectNumber != PORTAMENTOVOLUMESLIDE) {
                Player.lastAmigaPeriod[channel] = Player.amigaPeriod[channel];
            }

            if (!(Player.waveControl[channel] & 0x80)) {
                Player.vibratoPos[channel] = 0;
            }
            if (!(Player.waveControl[channel] & 0x08)) {
                Player.tremoloPos[channel] = 0;
            }
        }

        switch (effectNumber) {
        case TONEPORTAMENTO:
            if (effectParameter) {
                Player.portamentoSpeed[channel] = effectParameter;
            }
            Player.portamentoNote[channel] = Player.amigaPeriod[channel];
            note = NONOTE;
            break;

        case VIBRATO:
            if (effectParameterX) {
                Player.vibratoSpeed[channel] = effectParameterX;
            }
            if (effectParameterY) {
                Player.vibratoDepth[channel] = effectParameterY;
            }
            break;

        case PORTAMENTOVOLUMESLIDE:
            Player.portamentoNote[channel] = Player.amigaPeriod[channel];
            note = NONOTE;
            break;

        case TREMOLO:
            if (effectParameterX) {
                Player.tremoloSpeed[channel] = effectParameterX;
            }
            if (effectParameterY) {
                Player.tremoloDepth[channel] = effectParameterY;
            }
            break;

        case SETCHANNELPANNING:
            Mixer.channelPanning[channel] = effectParameter >> 1;
            break;

        case SETSAMPLEOFFSET:
            sampleOffset = effectParameter << 8;
            if (sampleOffset > Mod.samples[Player.lastSampleNumber[channel]].length) {
                sampleOffset = Mod.samples[Player.lastSampleNumber[channel]].length;
            }
            break;

        case JUMPTOORDER:
            Player.orderIndex = effectParameter;
            if (Player.orderIndex >= Mod.songLength) {
                Player.orderIndex = 0;
            }
            Player.row = 0;
            jumpFlag = true;
            break;

        case SETVOLUME:
            if (effectParameter > 64) {
                Player.volume[channel] = 64;
            } else {
                Player.volume[channel] = effectParameter;
            }
            break;

        case BREAKPATTERNTOROW:
            Player.row = effectParameterX * 10 + effectParameterY;
            if (Player.row >= ROWS) {
                Player.row = 0;
            }
            if (!jumpFlag && !breakFlag) {
                Player.orderIndex++;
                if (Player.orderIndex >= Mod.songLength) {
                    Player.orderIndex = 0;
                }
            }
            breakFlag = true;
            break;

        case 0xE:
            switch (effectParameterX) {
            case FINEPORTAMENTOUP:
                Player.lastAmigaPeriod[channel] -= effectParameterY;
                break;

            case FINEPORTAMENTODOWN:
                Player.lastAmigaPeriod[channel] += effectParameterY;
                break;

            case SETVIBRATOWAVEFORM:
                Player.waveControl[channel] &= 0xF0;
                Player.waveControl[channel] |= effectParameterY;
                break;

            case SETFINETUNE:
                Mod.samples[Player.lastSampleNumber[channel]].fineTune = effectParameterY;
                if (Mod.samples[Player.lastSampleNumber[channel]].fineTune > 7) {
                    Mod.samples[Player.lastSampleNumber[channel]].fineTune -= 16;
                }
                break;

            case PATTERNLOOP:
                if (effectParameterY) {
                    if (Player.patternLoopCount[channel]) {
                        Player.patternLoopCount[channel]--;
                    } else {
                        Player.patternLoopCount[channel] = effectParameterY;
                    }
                    if (Player.patternLoopCount[channel]) {
                        Player.row = Player.patternLoopRow[channel] - 1;
                    }
                } else {
                    Player.patternLoopRow[channel] = Player.row;
                }
                break;

            case SETTREMOLOWAVEFORM:
                Player.waveControl[channel] &= 0xF;
                Player.waveControl[channel] |= effectParameterY << 4;
                break;

            case FINEVOLUMESLIDEUP:
                Player.volume[channel] += effectParameterY;
                if (Player.volume[channel] > 64) {
                    Player.volume[channel] = 64;
                }
                break;

            case FINEVOLUMESLIDEDOWN:
                Player.volume[channel] -= effectParameterY;
                if (Player.volume[channel] < 0) {
                    Player.volume[channel] = 0;
                }
                break;

            case NOTECUT:
                note = NONOTE;
                break;

            case PATTERNDELAY:
                Player.patternDelay = effectParameterY;
                break;

            case INVERTLOOP:

                break;
            }
            break;

        case SETSPEED:
            if (effectParameter < 0x20) {
                Player.speed = effectParameter;
            } else {
                Player.samplesPerTick = sampleRate / (2 * effectParameter / 5);
            }
            break;
        }

        if (note != NONOTE || (Player.lastAmigaPeriod[channel] &&
                               effectNumber != VIBRATO && effectNumber != VIBRATOVOLUMESLIDE &&
                               !(effectNumber == 0xE && effectParameterX == NOTEDELAY))) {
            Mixer.channelFrequency[channel] = Player.amiga / Player.lastAmigaPeriod[channel];
        }

        if (note != NONOTE) {
            Mixer.channelSampleOffset[channel] = sampleOffset << FIXED_DIVIDER;
        }

        if (sampleNumber) {
            Mixer.channelSampleNumber[channel] = Player.lastSampleNumber[channel];
        }

        if (effectNumber != TREMOLO) {
            Mixer.channelVolume[channel] = Player.volume[channel];
        }

    }
    return true;
}

bool AudioGeneratorMOD::ProcessTick() {
    uint8_t channel;
    uint8_t sampleNumber;
    uint16_t note;
    uint8_t effectNumber;
    uint8_t effectParameter;
    uint8_t effectParameterX;
    uint8_t effectParameterY;
    uint16_t tempNote;

    if (!running) {
        return false;
    }

    for (channel = 0; channel < Mod.numberOfChannels; channel++) {

        if (Player.lastAmigaPeriod[channel]) {

            sampleNumber = Player.currentPattern.sampleNumber[Player.lastRow][channel];
            //   note = Player.currentPattern.note[Player.lastRow][channel];
            note = NOTE(Player.lastRow, channel);
            effectNumber = Player.currentPattern.effectNumber[Player.lastRow][channel];
            effectParameter = Player.currentPattern.effectParameter[Player.lastRow][channel];
            effectParameterX = effectParameter >> 4;
            effectParameterY = effectParameter & 0xF;

            switch (effectNumber) {
            case ARPEGGIO:
                if (effectParameter)
                    switch (Player.tick % 3) {
                    case 0:
                        Mixer.channelFrequency[channel] = Player.amiga / Player.lastAmigaPeriod[channel];
                        break;
                    case 1:
                        tempNote = Player.lastNote[channel] + effectParameterX * 8 + Mod.samples[Player.lastSampleNumber[channel]].fineTune;
                        if (tempNote < 296) {
                            Mixer.channelFrequency[channel] = Player.amiga / ReadAmigaPeriods(tempNote);
                        }
                        break;
                    case 2:
                        tempNote = Player.lastNote[channel] + effectParameterY * 8 + Mod.samples[Player.lastSampleNumber[channel]].fineTune;
                        if (tempNote < 296) {
                            Mixer.channelFrequency[channel] = Player.amiga / ReadAmigaPeriods(tempNote);
                        }
                        break;
                    }
                break;

            case PORTAMENTOUP:
                Player.lastAmigaPeriod[channel] -= effectParameter;
                if (Player.lastAmigaPeriod[channel] < 113) {
                    Player.lastAmigaPeriod[channel] = 113;
                }
                Mixer.channelFrequency[channel] = Player.amiga / Player.lastAmigaPeriod[channel];
                break;

            case PORTAMENTODOWN:
                Player.lastAmigaPeriod[channel] += effectParameter;
                if (Player.lastAmigaPeriod[channel] > 856) {
                    Player.lastAmigaPeriod[channel] = 856;
                }
                Mixer.channelFrequency[channel] = Player.amiga / Player.lastAmigaPeriod[channel];
                break;

            case TONEPORTAMENTO:
                Portamento(channel);
                break;

            case VIBRATO:
                Vibrato(channel);
                break;

            case PORTAMENTOVOLUMESLIDE:
                Portamento(channel);
                Player.volume[channel] += effectParameterX - effectParameterY;
                if (Player.volume[channel] < 0) {
                    Player.volume[channel] = 0;
                } else if (Player.volume[channel] > 64) {
                    Player.volume[channel] = 64;
                }
                Mixer.channelVolume[channel] = Player.volume[channel];
                break;

            case VIBRATOVOLUMESLIDE:
                Vibrato(channel);
                Player.volume[channel] += effectParameterX - effectParameterY;
                if (Player.volume[channel] < 0) {
                    Player.volume[channel] = 0;
                } else if (Player.volume[channel] > 64) {
                    Player.volume[channel] = 64;
                }
                Mixer.channelVolume[channel] = Player.volume[channel];
                break;

            case TREMOLO:
                Tremolo(channel);
                break;

            case VOLUMESLIDE:
                Player.volume[channel] += effectParameterX - effectParameterY;
                if (Player.volume[channel] < 0) {
                    Player.volume[channel] = 0;
                } else if (Player.volume[channel] > 64) {
                    Player.volume[channel] = 64;
                }
                Mixer.channelVolume[channel] = Player.volume[channel];
                break;

            case 0xE:
                switch (effectParameterX) {
                case RETRIGGERNOTE:
                    if (!effectParameterY) {
                        break;
                    }
                    if (!(Player.tick % effectParameterY)) {
                        Mixer.channelSampleOffset[channel] = 0;
                    }
                    break;

                case NOTECUT:
                    if (Player.tick == effectParameterY) {
                        Mixer.channelVolume[channel] = Player.volume[channel] = 0;
                    }
                    break;

                case NOTEDELAY:
                    if (Player.tick == effectParameterY) {
                        if (sampleNumber) {
                            Player.volume[channel] = Mod.samples[Player.lastSampleNumber[channel]].volume;
                        }
                        if (note != NONOTE) {
                            Mixer.channelSampleOffset[channel] = 0;
                        }
                        Mixer.channelFrequency[channel] = Player.amiga / Player.lastAmigaPeriod[channel];
                        Mixer.channelVolume[channel] = Player.volume[channel];
                    }
                    break;
                }
                break;
            }

        }

    }
    return true;
}

bool AudioGeneratorMOD::RunPlayer() {
    if (!running) {
        return false;
    }

    if (Player.tick == Player.speed) {
        Player.tick = 0;

        if (Player.row == ROWS) {
            Player.orderIndex++;
            if (Player.orderIndex == Mod.songLength) {
                //Player.orderIndex = 0;
                // No loop, just say we're done!
                return false;
            }
            Player.row = 0;
        }

        if (Player.patternDelay) {
            Player.patternDelay--;
        } else {
            if (Player.orderIndex != Player.oldOrderIndex)
                if (!LoadPattern(Mod.order[Player.orderIndex])) {
                    return false;
                }
            Player.oldOrderIndex = Player.orderIndex;
            if (!ProcessRow()) {
                return false;
            }
        }

    } else {
        if (!ProcessTick()) {
            return false;
        }
    }
    Player.tick++;
    return true;
}

void AudioGeneratorMOD::GetSample(int16_t sample[2]) {
    int32_t sumL;
    int32_t sumR;
    uint8_t channel;
    uint32_t samplePointer;
    int8_t current;
    int8_t next;
    int16_t out;
    int32_t out32;

    if (!running) {
        return;
    }

    sumL = 0;
    sumR = 0;
    for (channel = 0; channel < Mod.numberOfChannels; channel++) {

        if (!Mixer.channelFrequency[channel] ||
                !Mod.samples[Mixer.channelSampleNumber[channel]].length) {
            continue;
        }

        Mixer.channelSampleOffset[channel] += Mixer.channelFrequency[channel];

        if (!Mixer.channelVolume[channel]) {
            continue;
        }

        samplePointer = Mixer.sampleBegin[Mixer.channelSampleNumber[channel]] +
                        (Mixer.channelSampleOffset[channel] >> FIXED_DIVIDER);

        if (Mixer.sampleLoopLength[Mixer.channelSampleNumber[channel]]) {

            if (samplePointer >= Mixer.sampleLoopEnd[Mixer.channelSampleNumber[channel]]) {
                Mixer.channelSampleOffset[channel] -= Mixer.sampleLoopLength[Mixer.channelSampleNumber[channel]] << FIXED_DIVIDER;
                samplePointer -= Mixer.sampleLoopLength[Mixer.channelSampleNumber[channel]];
            }

        } else {

            if (samplePointer >= Mixer.sampleEnd[Mixer.channelSampleNumber[channel]]) {
                Mixer.channelFrequency[channel] = 0;
                samplePointer = Mixer.sampleEnd[Mixer.channelSampleNumber[channel]];
            }

        }

        if (samplePointer < FatBuffer.samplePointer[channel] ||
                samplePointer >= FatBuffer.samplePointer[channel] + fatBufferSize - 1 ||
                Mixer.channelSampleNumber[channel] != FatBuffer.channelSampleNumber[channel]) {

            uint32_t toRead = Mixer.sampleEnd[Mixer.channelSampleNumber[channel]] - samplePointer + 1;
            if (toRead > (uint32_t)fatBufferSize) {
                toRead  = fatBufferSize;
            }

            if (!file->seek(samplePointer, SEEK_SET)) {
                stop();
                return;
            }
            if (toRead != file->read(FatBuffer.channels[channel], toRead)) {
                stop();
                return;
            }

            FatBuffer.samplePointer[channel] = samplePointer;
            FatBuffer.channelSampleNumber[channel] = Mixer.channelSampleNumber[channel];
        }

        current = FatBuffer.channels[channel][(samplePointer - FatBuffer.samplePointer[channel]) /*& (FATBUFFERSIZE - 1)*/];
        next = FatBuffer.channels[channel][(samplePointer + 1 - FatBuffer.samplePointer[channel]) /*& (FATBUFFERSIZE - 1)*/];

        // preserve a few more bits from sample interpolation, by upscaling input values.
        // This does (slightly) reduce quantization noise in higher frequencies, typically above 8kHz.
        // Actually we could could even gain more bits, I was just not sure if more bits would cause overflows in other conputations.
        int16_t current16 = (int16_t) current << 2;
        int16_t next16    = (int16_t) next << 2;

        out = current16;

        // Integer linear interpolation - only works correctly in 16bit
        out += (next16 - current16) * (Mixer.channelSampleOffset[channel] & ((1 << FIXED_DIVIDER) - 1)) >> FIXED_DIVIDER;

        // Upscale to BITDEPTH, considering the we already gained two bits in the previous step
        out32 = (int32_t)out << (BITDEPTH - 10);

        // Channel volume
        out32 = out32 * Mixer.channelVolume[channel] >> 6;

        // Channel panning
        sumL += out32 * min(128 - Mixer.channelPanning[channel], 64) >> 6;
        sumR += out32 * min(Mixer.channelPanning[channel], 64) >> 6;
    }

    // Downscale to BITDEPTH - a bit faster because the compiler can replaced division by constants with proper "right shift" + correct handling of sign bit
    if (Mod.numberOfChannels <= 4) {
        // up to 4 channels
        sumL /= 4;
        sumR /= 4;
    } else {
        if (Mod.numberOfChannels <= 6) {
            // 5 or 6 channels - pre-multiply be 1.5, then divide by 8 -> same as division by 6
            sumL = (sumL + (sumL / 2)) / 8;
            sumR = (sumR + (sumR / 2)) / 8;
        } else {
            // 7,8, or more channels
            sumL /= 8;
            sumR /= 8;
        }
    }

    // clip samples to 16bit (with saturation in case of overflow)
    if (sumL <= INT16_MIN) {
        sumL = INT16_MIN;
    } else if (sumL >= INT16_MAX) {
        sumL = INT16_MAX;
    }
    if (sumR <= INT16_MIN) {
        sumR = INT16_MIN;
    } else if (sumR >= INT16_MAX) {
        sumR = INT16_MAX;
    }

    // Fill the sound buffer with signed values
    sample[AudioOutput::LEFTCHANNEL] = sumL;
    sample[AudioOutput::RIGHTCHANNEL] = sumR;
}

bool AudioGeneratorMOD::LoadMOD() {
    uint8_t channel;

    if (!LoadHeader()) {
        return false;
    }
    LoadSamples();

    Player.amiga = AMIGA;
    Player.samplesPerTick = sampleRate / (2 * 125 / 5); // Hz = 2 * BPM / 5
    Player.speed = 6;
    Player.tick = Player.speed;
    Player.row = 0;

    Player.orderIndex = 0;
    Player.oldOrderIndex = 0xFF;
    Player.patternDelay = 0;

    for (channel = 0; channel < Mod.numberOfChannels; channel++) {
        Player.patternLoopCount[channel] = 0;
        Player.patternLoopRow[channel] = 0;

        Player.lastAmigaPeriod[channel] = 0;

        Player.waveControl[channel] = 0;

        Player.vibratoSpeed[channel] = 0;
        Player.vibratoDepth[channel] = 0;
        Player.vibratoPos[channel] = 0;

        Player.tremoloSpeed[channel] = 0;
        Player.tremoloDepth[channel] = 0;
        Player.tremoloPos[channel] = 0;

        FatBuffer.samplePointer[channel] = 0;
        FatBuffer.channelSampleNumber[channel] = 0xFF;

        Mixer.channelSampleOffset[channel] = 0;
        Mixer.channelFrequency[channel] = 0;
        Mixer.channelVolume[channel] = 0;
        switch (channel % 4) {
        case 0:
        case 3:
            Mixer.channelPanning[channel] = stereoSeparation;
            break;
        default:
            Mixer.channelPanning[channel] = 128 - stereoSeparation;
        }
    }
    return true;
}

