void MidiKeyboardComponent::updateNoteUnderMouse (const Point<int>& pos)
{
    float mousePositionVelocity = 0.0f;
    const int newNote = (mouseDragging || isMouseOver())
                            ? xyToNote (pos, mousePositionVelocity) : -1;

    if (noteUnderMouse != newNote)
    {
        if (mouseDownNote >= 0)
        {
            state.noteOff (midiChannel, mouseDownNote);
            mouseDownNote = -1;
        }

        if (mouseDragging && newNote >= 0)
        {
            if (! useMousePositionForVelocity)
                mousePositionVelocity = 1.0f;

            state.noteOn (midiChannel, newNote, mousePositionVelocity * velocity);
            mouseDownNote = newNote;
        }

        repaintNote (noteUnderMouse);
        noteUnderMouse = newNote;
        repaintNote (noteUnderMouse);
    }
    else if (mouseDownNote >= 0 && ! mouseDragging)
    {
        state.noteOff (midiChannel, mouseDownNote);
        mouseDownNote = -1;
    }
}
void MidiKeyboardComponent::timerCallback()
{
    if (shouldCheckState)
    {
        shouldCheckState = false;

        for (int i = rangeStart; i <= rangeEnd; ++i)
        {
            bool isOn = state.isNoteOnForChannels (midiInChannelMask, i);

            if (keysCurrentlyDrawnDown[i] != isOn)
            {
                keysCurrentlyDrawnDown.setBit (i, isOn);
                repaintNote (i);
            }
        }
    }

    if (shouldCheckMousePos)
    {
        for (auto& ms : Desktop::getInstance().getMouseSources())
            if (ms.getComponentUnderMouse() == this || isParentOf (ms.getComponentUnderMouse()))
                updateNoteUnderMouse (getLocalPoint (nullptr, ms.getScreenPosition()), ms.isDragging(), ms.getIndex());
    }
}
void MidiKeyboardComponent::timerCallback()
{
    if (shouldCheckState)
    {
        shouldCheckState = false;

        for (int i = rangeStart; i <= rangeEnd; ++i)
        {
            if (keysCurrentlyDrawnDown[i] != state.isNoteOnForChannels (midiInChannelMask, i))
            {
                keysCurrentlyDrawnDown.setBit (i, state.isNoteOnForChannels (midiInChannelMask, i));
                repaintNote (i);
            }
        }
    }

    if (shouldCheckMousePos)
    {
        Desktop& desktop = Desktop::getInstance();

        for (int i = desktop.getNumMouseSources(); --i >= 0;)
        {
            MouseInputSource* source = desktop.getMouseSource (i);
            jassert (source != nullptr);
            updateNoteUnderMouse (getLocalPoint (nullptr, source->getScreenPosition()),
                                  source->isDragging(), source->getIndex());
        }
    }
}
void MidiKeyboardComponent::updateNoteUnderMouse (const Point<int>& pos, bool isDown, int fingerNum)
{
    float mousePositionVelocity = 0.0f;
    const int newNote = xyToNote (pos, mousePositionVelocity);
    const int oldNote = mouseOverNotes.getUnchecked (fingerNum);

    if (oldNote != newNote)
    {
        repaintNote (oldNote);
        repaintNote (newNote);
        mouseOverNotes.set (fingerNum, newNote);
    }

    int oldNoteDown = mouseDownNotes.getUnchecked (fingerNum);

    if (isDown)
    {
        if (newNote != oldNoteDown)
        {
            if (oldNoteDown >= 0)
            {
                mouseDownNotes.set (fingerNum, -1);

                if (! mouseDownNotes.contains (oldNoteDown))
                    state.noteOff (midiChannel, oldNoteDown);
            }

            if (newNote >= 0)
            {
                if (! useMousePositionForVelocity)
                    mousePositionVelocity = 1.0f;

                state.noteOn (midiChannel, newNote, mousePositionVelocity * velocity);
                mouseDownNotes.set (fingerNum, newNote);
            }
        }
    }
    else if (oldNoteDown >= 0)
    {
        mouseDownNotes.set (fingerNum, -1);

        if (! mouseDownNotes.contains (oldNoteDown))
            state.noteOff (midiChannel, oldNoteDown);
    }
}
void MidiKeyboardComponent::updateNoteUnderMouse (Point<float> pos, bool isDown, int fingerNum)
{
    float mousePositionVelocity = 0.0f;
    auto newNote = xyToNote (pos, mousePositionVelocity);
    auto oldNote = mouseOverNotes.getUnchecked (fingerNum);
    auto oldNoteDown = mouseDownNotes.getUnchecked (fingerNum);
    auto eventVelocity = useMousePositionForVelocity ? mousePositionVelocity * velocity : velocity;

    if (oldNote != newNote)
    {
        repaintNote (oldNote);
        repaintNote (newNote);
        mouseOverNotes.set (fingerNum, newNote);
    }

    if (isDown)
    {
        if (newNote != oldNoteDown)
        {
            if (oldNoteDown >= 0)
            {
                mouseDownNotes.set (fingerNum, -1);

                if (! mouseDownNotes.contains (oldNoteDown))
                    state.noteOff (midiChannel, oldNoteDown, eventVelocity);
            }

            if (newNote >= 0 && ! mouseDownNotes.contains (newNote))
            {
                state.noteOn (midiChannel, newNote, eventVelocity);
                mouseDownNotes.set (fingerNum, newNote);
            }
        }
    }
    else if (oldNoteDown >= 0)
    {
        mouseDownNotes.set (fingerNum, -1);

        if (! mouseDownNotes.contains (oldNoteDown))
            state.noteOff (midiChannel, oldNoteDown, eventVelocity);
    }
}
void MidiKeyboardComponent::handleAsyncUpdate()
{
    for (int i = rangeStart; i <= rangeEnd; ++i)
    {
        if (keysCurrentlyDrawnDown[i] != state.isNoteOnForChannels (midiInChannelMask, i))
        {
            keysCurrentlyDrawnDown.setBit (i, state.isNoteOnForChannels (midiInChannelMask, i));
            repaintNote (i);
        }
    }
}
void GuitarNeckComponent::handleAsyncUpdate()
{
	for (int s = 0; s<numStrings; s++)
	{
		for (int i = rangeStart+stringNote[s]; i <= rangeEnd+stringNote[s]; ++i)
		{
			if (state.isNoteOn (s+1, i))
			{
				repaintNote (currentlyFrettedFret[s]);
				currentlyFrettedFret[s] = i - stringNote[s];
				repaintNote (currentlyFrettedFret[s]);
			}
			else if (currentlyFrettedFret[s] == i - stringNote[s])
			{
				repaintNote (currentlyFrettedFret[s]);
				currentlyFrettedFret[s] = -1;
				repaintNote (currentlyFrettedFret[s]);
			}
		}
	}
}
void GuitarNeckComponent::updateNoteUnderMouse (const Point<int>& pos)
{
    float mousePositionVelocity = 0.0f;
    const FrettedNote newNote = (mouseDragging || isMouseOver())
                            ? xyToNote (pos, mousePositionVelocity) : FrettedNote();

    if (noteUnderMouse != newNote)
    {
        if (mouseDownNote.isValid())
        {
			//state.noteOff (mouseDownNote.string+1, getNote(mouseDownNote));
			currentlyFrettedFret[newNote.string]=-1;
			mouseDownNote.invalidate();
        }

		if (mouseDragging && newNote.isValid())
        {
            //if (! useMousePositionForVelocity)
                mousePositionVelocity = 1.0f;

			//state.noteOn (newNote.string+1, getNote(newNote), mousePositionVelocity * velocity);
			int oldFret = currentlyFrettedFret[newNote.string];
			currentlyFrettedFret[newNote.string] = newNote.fret;
			repaintNote(oldFret);
            mouseDownNote = newNote;
        }

		repaintNote (noteUnderMouse.fret);
        noteUnderMouse = newNote;
		repaintNote (noteUnderMouse.fret);
    }
	else if (mouseDownNote.isValid() && ! mouseDragging)
    {
		//state.noteOff (mouseDownNote.string+1, getNote(mouseDownNote));
		//currentlyFrettedFret[newNote.string]=-1;
		mouseDownNote.invalidate();
    }
}
void MidiKeyboardComponent::mouseDown (const MouseEvent& e)
{
    float mousePositionVelocity;
    const int newNote = xyToNote (e.getPosition(), mousePositionVelocity);
    mouseDragging = false;

    if (newNote >= 0 && mouseDownOnKey (newNote, e))
    {
        repaintNote (noteUnderMouse);
        noteUnderMouse = -1;
        mouseDragging = true;

        updateNoteUnderMouse (e.getPosition());
        startTimer (500);
    }
}