|  | /*
 ==============================================================================
 This file is part of the JUCETICE project - Copyright 2008 by Lucio Asnaghi.
 JUCETICE is based around the JUCE library - "Jules' Utility Class Extensions"
 Copyright 2008 by Julian Storer.
 ------------------------------------------------------------------------------
 JUCE and JUCETICE can be redistributed and/or modified under the terms of
 the GNU Lesser General Public License, as published by the Free Software
 Foundation; either version 2 of the License, or (at your option) any later
 version.
 JUCE and JUCETICE are distributed in the hope that they 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 Lesser General Public License
 along with JUCE and JUCETICE; if not, visit www.gnu.org/licenses or write to
 Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 Boston, MA 02111-1307 USA
 ==============================================================================
   @author  rockhardbuns
   @tweaker Lucio Asnaghi
   @tweaker falkTX
 ==============================================================================
*/
#ifndef DISTRHO_VEX_ARP_HEADER_INCLUDED
#define DISTRHO_VEX_ARP_HEADER_INCLUDED
#include "VexArpSettings.h"
#ifndef CARLA_EXPORT
 #define CARLA_EXPORT
#endif
#ifdef CARLA_EXPORT
 #include "juce_audio_basics.h"
#else
 #include "../StandardHeader.h"
#endif
class VexArp
{
public:
    static const int kMaxNotes = 10;
    VexArp(const VexArpSettings* p)
        : arpSet(p),
          dead(true),
          notesPlaying(false),
          doSync(true),
          nextStep(0),
          sampleCount(0),
          sampleRate(44100)
    {
        meter[0] = 4;
        meter[1] = 8;
        meter[2] = 16;
        meter[3] = 32;
        for (int i = 0; i < kMaxNotes; ++i)
        {
            cKeysDown[i] = 0;
            cNotesToKill[i] = 0;
            cKeysVelocity[i] = 0;
        }
        outMidiBuffer.ensureSize(kMaxNotes+128);
    }
    void addNote(const char note, const char vel)
    {
        char tmp, tmpNote = note, tmpVel = vel;
        for (int i = 0; i < kMaxNotes; ++i)
        {
            if (note < cKeysDown[i] || cKeysDown[i] == 0)
            {
                tmp = cKeysDown[i];     cKeysDown[i]     = tmpNote; tmpNote = tmp;
                tmp = cKeysVelocity[i]; cKeysVelocity[i] = tmpVel;  tmpVel  = tmp;
            }
        }
        doSync = true;
    }
    void dropNote(const char note)
    {
        int i = 0;
        for (; i < kMaxNotes; ++i)
        {
            if (cKeysDown[i] == note)
                break;
        }
        if (i == kMaxNotes)
            return;
        for (; i < kMaxNotes-1; ++i)
        {
            cKeysDown[i] = cKeysDown[i+1];
            cKeysVelocity[i] = cKeysVelocity[i+1];
        }
        cKeysDown[kMaxNotes-1] = 0;
        cKeysVelocity[kMaxNotes-1] = 0;
    }
    void killThisNoteLater(const char note)
    {
        for (int i = 0; i < kMaxNotes; ++i)
        {
            if (cNotesToKill[i] == note)
                break;
            if (cNotesToKill[i] == 0)
            {
                cNotesToKill[i] = note;
                break;
            }
        }
    }
    void setSampleRate(const int srate)
    {
        sampleRate = srate;
    }
    const MidiBuffer& processMidi(MidiBuffer& inMidiBuffer, const bool isPlaying,
                                                            const double ppqPosition,
                                                            const double ppqPositionOfLastBarStart,
                                                            const double bpm,
                                                            const int numSamples)
    {
        const int timeSig = meter[arpSet->timeMode];
        // Loop though the midibuffer, take away note on/off, let the rest pass
        {
            outMidiBuffer.clear();
            MidiBuffer::Iterator inBufferIterator(inMidiBuffer);
            MidiMessage midiMessage(0xf4);
            int samplePosition;
            while (inBufferIterator.getNextEvent(midiMessage, samplePosition))
            {
                if (midiMessage.isNoteOn())
                    addNote(midiMessage.getNoteNumber(), midiMessage.getVelocity());
                else if (midiMessage.isNoteOff())
                    dropNote(midiMessage.getNoteNumber());
                else
                    outMidiBuffer.addEvent(midiMessage, samplePosition);
            }
        }
        const double beatsPerSec = (60.0 * double(sampleRate)) / bpm;
        // BarSync
        const unsigned int samplesPerStep = int(beatsPerSec * 4.0) / timeSig;
        if (isPlaying && arpSet->syncMode == 2) // bar sync
        {
            if (doSync)
            {
                //offset sample count
                sampleCount = int((ppqPosition - ppqPositionOfLastBarStart) * beatsPerSec);
                //offset step count
                nextStep = sampleCount / samplesPerStep;
                //Cycle the counts
                sampleCount = (sampleCount % samplesPerStep) + samplesPerStep - 10;
                nextStep = nextStep % arpSet->length;
                doSync = false;
            }
        }
        else
        {
            doSync = true;
        }
        //***************************
        if (cKeysDown[0])
        {   // Keys are down
            dead = false;
            sampleCount += numSamples;
            bool repeat = false;
            do
            {
                repeat = false;
                //***
                if (sampleCount >= samplesPerStep)
                {   //Play step
                    int offset = numSamples - (sampleCount - samplesPerStep);
                    bool doFail = true;
                    for (int i = 0; i < 5; ++i)
                    {
                        if((cKeysDown[i]!= 0) && (arpSet->grid[nextStep*5 + i]))
                        {   // we have a note to play
                            int vel;
                            switch (arpSet->velMode)
                            {
                            case 1:
                                vel = roundFloatToInt (arpSet->velocities[nextStep] * 127.0f);
                                break;
                            case 2:
                                vel = (int) cKeysVelocity[i];
                                break;
                            case 3:
                                vel = (int) cKeysVelocity[i] + roundFloatToInt (arpSet->velocities[nextStep] * 127.0f);
                                break;
                            default:
                                vel = 127;
                                break;
                            }
                            doFail = false;
                            notesPlaying = true;
                            killThisNoteLater(cKeysDown[i]);
                            outMidiBuffer.addEvent(MidiMessage::noteOn(1, cKeysDown[i], uint8(vel)), offset);
                        }
                    }
                    if (doFail)
                    {
                        switch (arpSet->failMode)
                        {
                        case 1: //normal
                            sampleCount -= samplesPerStep;
                            nextStep++;
                            nextStep = nextStep % arpSet->length;
                            break;
                        case 2: //skip one
                            //SampleCount -= SamplesPerStep;
                            nextStep++;
                            nextStep = nextStep % arpSet->length;
                            repeat = true;
                            break;
                        case 3: //skip two
                            //SampleCount -= SamplesPerStep;
                            nextStep += 2;
                            nextStep = nextStep % arpSet->length;
                            repeat = true;
                            break;
                        }
                    }
                    else
                    {
                        sampleCount -= samplesPerStep;
                        nextStep++;
                        nextStep = nextStep % arpSet->length; //Cycle the steps over pattern length
                    }
                }
            } while (repeat);
            //***
            unsigned int NoteLength = (samplesPerStep / 4) * 3;
            if ((sampleCount >= NoteLength) && notesPlaying)
            {   //Mute step
                int offset = numSamples - (sampleCount - NoteLength);
                //***
                for(int i = 0; i < kMaxNotes; ++i)
                {
                    if (cNotesToKill[i] != 0)
                    {   //do we have a note to kill?
                        outMidiBuffer.addEvent(MidiMessage::noteOff(1, cNotesToKill[i]), offset);
                        cNotesToKill[i] = 0;
                    }
                }
                notesPlaying = false;
            }
        //***
        }
        else if (! dead)
        {   //No keys pressed - kill 'em all
            for (int i = 0; i < kMaxNotes; ++i)
            {
                if (cNotesToKill[i] != 0)
                {   //do we have a note to kill?
                    outMidiBuffer.addEvent(MidiMessage::noteOff(1, cNotesToKill[i]), 0);
                    cNotesToKill[i] = 0;
                }
            }
            nextStep = 0;
            sampleCount = samplesPerStep;
            dead = true;
        }
        return outMidiBuffer;
    }
private:
    const VexArpSettings* arpSet;
    MidiBuffer outMidiBuffer;
    bool dead, notesPlaying, doSync; // doSync used only in bar-sync
    unsigned int nextStep;
    unsigned int sampleCount, sampleRate;
    int meter[4];
    char cKeysDown[kMaxNotes];
    char cKeysVelocity[kMaxNotes];
    char cNotesToKill[kMaxNotes];
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(VexArp)
};
#endif // DISTRHO_VEX_ARP_HEADER_INCLUDED
 |