DSP_EXPORT void processBlock(BlockData& data)
{
    // iterate on MIDI events
    for(uint i=0;i<data.inputMidiEvents.length;i++)
    {
        // forward all events (unchanged)
        data.outputMidiEvents.push(data.inputMidiEvents[i]);

        // add transposed Note On and Off events
        MidiEventType type=MidiEventUtils::getType(data.inputMidiEvents[i]);
        if(type==kMidiNoteOn || type==kMidiNoteOff)
        {
            // determine the index of the note in the major scale
            int absoluteNoteInScale=MidiEventUtils::getNote(data.inputMidiEvents[i])-key;
            int noteInScale=(absoluteNoteInScale%12);
            int noteIndexInScale=majorKeyNotes.find(noteInScale%12);
            for(uint j=0;j<offsets.length;j++)
            {
                if(offsets[j]!=0)
                {
                    int diff=0;

                    // if the note has been found, use our scale to find the appropriate interval
                    if(noteIndexInScale>=0)
                    {
                        int newNoteInScale=getRelativeNoteValueInScale(noteIndexInScale+offsets[j]);
                        diff=newNoteInScale-noteInScale;
                        // set proper octave
                        if(diff*offsets[j]<0)
                        {
                            diff+=12*sign(offsets[j]);
                        }
                        // handle case for more than one octave
                        diff+=(offsets[j]/7)*12;
                    }
                    else
                    {
                        // use predefined intervals (note is not in the scale anyway)
                        diff=predefinedIntervals[offsets[j]+9];
                    }

                    // create new note event if valid (not out of MIDI range)
                    int newNote=MidiEventUtils::getNote(data.inputMidiEvents[i])+diff;
                    if(newNote>=0 && newNote<128)
                    {
                        tempEvent=data.inputMidiEvents[i];
                        MidiEventUtils::setNote(tempEvent,newNote);
                        data.outputMidiEvents.push(tempEvent);
                    }
                }
            }
        }
    }
}
void handleMidiEvent(const MidiEvent& evt)
{
    switch(MidiEventUtils::getType(evt))
    {
        case kMidiNoteOn:
        {
            // find voice for the note on event (in case it is already here)
            // or use the first empty slot (note=-1)
            tempVoice.currentNote=MidiEventUtils::getNote(evt);
            int index=voices.find(tempVoice);
            if(index<0)
            {
                if(activeVoicesCount<voices.length)
                {
                    index=activeVoicesCount;
                    activeVoicesCount++;
                }
            }
            if(index>=0)
            {
                voices[index].NoteOn(evt);
            }
            break;
        }
        case kMidiNoteOff:
        {
            // find voice for the note off event
            tempVoice.currentNote=MidiEventUtils::getNote(evt);
            int index=voices.find(tempVoice);
            if(index>=0)
            {
                voices[index].NoteOff();
            }
            break;
        }
        case kMidiPitchWheel:
        {
            // update pitch for all voices
            currentPitchOffset=2*double(MidiEventUtils::getPitchWheelValue(evt))/8192.0;
            for(uint i=0;i<activeVoicesCount;i++)
            {
                if(voices[i].omega>0)
                {
                    voices[i].ForcePitch();
                }
            }
            break;
        }
        case kMidiControlChange:
        {
            // sustain pedal changed event
            if(MidiEventUtils::getCCNumber(evt)==64)
            {
                // if pedal is down
                bool down=(MidiEventUtils::getCCValue(evt)>=64);
                if(down!=pedalIsDown)
                {
                    pedalIsDown=down;
                    if(pedalIsDown==false)
                    {
                        for(uint i=0;i<activeVoicesCount;i++)
                        {
                            voices[i].PedalReleased();
                        }
                    }
                }
            }
        }
    }
}