|  | /*
  ==============================================================================
   This file is part of the JUCE library.
   Copyright (c) 2015 - ROLI Ltd.
   Permission is granted to use this software under the terms of either:
   a) the GPL v2 (or any later version)
   b) the Affero GPL v3
   Details of these licenses can be found at: www.gnu.org/licenses
   JUCE 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.
   ------------------------------------------------------------------------------
   To release a closed-source product which uses JUCE, commercial licenses are
   available: visit www.juce.com for more information.
  ==============================================================================
*/
MidiRPNDetector::MidiRPNDetector() noexcept
{
}
MidiRPNDetector::~MidiRPNDetector() noexcept
{
}
bool MidiRPNDetector::parseControllerMessage (int midiChannel,
                                              int controllerNumber,
                                              int controllerValue,
                                              MidiRPNMessage& result) noexcept
{
    jassert (midiChannel >= 1 && midiChannel <= 16);
    jassert (controllerNumber >= 0 && controllerNumber < 128);
    jassert (controllerValue >= 0 && controllerValue < 128);
    return states[midiChannel - 1].handleController (midiChannel, controllerNumber, controllerValue, result);
}
void MidiRPNDetector::reset() noexcept
{
    for (int i = 0; i < 16; ++i)
    {
        states[i].parameterMSB = 0xff;
        states[i].parameterLSB = 0xff;
        states[i].resetValue();
        states[i].isNRPN = false;
    }
}
//==============================================================================
MidiRPNDetector::ChannelState::ChannelState () noexcept
    : parameterMSB (0xff), parameterLSB (0xff), valueMSB (0xff), valueLSB (0xff), isNRPN (false)
{
}
bool MidiRPNDetector::ChannelState::handleController (int channel,
                                                      int controllerNumber,
                                                      int value,
                                                      MidiRPNMessage& result) noexcept
{
    switch (controllerNumber)
    {
        case 0x62:  parameterLSB = uint8 (value); resetValue(); isNRPN = true;  break;
        case 0x63:  parameterMSB = uint8 (value); resetValue(); isNRPN = true;  break;
        case 0x64:  parameterLSB = uint8 (value); resetValue(); isNRPN = false; break;
        case 0x65:  parameterMSB = uint8 (value); resetValue(); isNRPN = false; break;
        case 0x06:  valueMSB = uint8 (value); return sendIfReady (channel, result);
        case 0x26:  valueLSB = uint8 (value); break;
        default:  break;
    }
    return false;
}
void MidiRPNDetector::ChannelState::resetValue() noexcept
{
    valueMSB = 0xff;
    valueLSB = 0xff;
}
//==============================================================================
bool MidiRPNDetector::ChannelState::sendIfReady (int channel, MidiRPNMessage& result) noexcept
{
    if (parameterMSB < 0x80 && parameterLSB < 0x80)
    {
        if (valueMSB < 0x80)
        {
            result.channel = channel;
            result.parameterNumber = (parameterMSB << 7) + parameterLSB;
            result.isNRPN = isNRPN;
            if (valueLSB < 0x80)
            {
                result.value = (valueMSB << 7) + valueLSB;
                result.is14BitValue = true;
            }
            else
            {
                result.value = valueMSB;
                result.is14BitValue = false;
            }
            return true;
        }
    }
    return false;
}
//==============================================================================
MidiBuffer MidiRPNGenerator::generate (MidiRPNMessage message)
{
    return generate (message.channel,
                     message.parameterNumber,
                     message.value,
                     message.isNRPN,
                     message.is14BitValue);
}
MidiBuffer MidiRPNGenerator::generate (int midiChannel,
                                       int parameterNumber,
                                       int value,
                                       bool isNRPN,
                                       bool use14BitValue)
{
    jassert (midiChannel > 0 && midiChannel <= 16);
    jassert (parameterNumber >= 0 && parameterNumber < 16384);
    jassert (value >= 0 && value < (use14BitValue ? 16384 : 128));
    uint8 parameterLSB = uint8 (parameterNumber & 0x0000007f);
    uint8 parameterMSB = uint8 (parameterNumber >> 7);
    uint8 valueLSB = use14BitValue ? uint8 (value & 0x0000007f) : 0x00;
    uint8 valueMSB = use14BitValue ? uint8 (value >> 7) : uint8 (value);
    uint8 channelByte = uint8 (0xb0 + midiChannel - 1);
    MidiBuffer buffer;
    buffer.addEvent (MidiMessage (channelByte, isNRPN ? 0x62 : 0x64, parameterLSB),  0);
    buffer.addEvent (MidiMessage (channelByte, isNRPN ? 0x63 : 0x65, parameterMSB),  0);
    // sending the value LSB is optional, but must come before sending the value MSB:
    if (use14BitValue)
        buffer.addEvent (MidiMessage (channelByte, 0x26, valueLSB), 0);
    buffer.addEvent (MidiMessage (channelByte, 0x06, valueMSB), 0);
    return buffer;
}
//==============================================================================
//==============================================================================
#if JUCE_UNIT_TESTS
class MidiRPNDetectorTests   : public UnitTest
{
public:
    MidiRPNDetectorTests()  : UnitTest ("MidiRPNDetector class") {}
    void runTest() override
    {
        beginTest ("7-bit RPN");
        {
            MidiRPNDetector detector;
            MidiRPNMessage rpn;
            expect (! detector.parseControllerMessage (2, 101, 0,  rpn));
            expect (! detector.parseControllerMessage (2, 100, 7,  rpn));
            expect (detector.parseControllerMessage   (2, 6,   42, rpn));
            expectEquals (rpn.channel, 2);
            expectEquals (rpn.parameterNumber, 7);
            expectEquals (rpn.value, 42);
            expect (! rpn.isNRPN);
            expect (! rpn.is14BitValue);
        }
        beginTest ("14-bit RPN");
        {
            MidiRPNDetector detector;
            MidiRPNMessage rpn;
            expect (! detector.parseControllerMessage (1, 100, 44, rpn));
            expect (! detector.parseControllerMessage (1, 101, 2,  rpn));
            expect (! detector.parseControllerMessage (1, 38,  94, rpn));
            expect (detector.parseControllerMessage   (1, 6,   1,  rpn));
            expectEquals (rpn.channel, 1);
            expectEquals (rpn.parameterNumber, 300);
            expectEquals (rpn.value, 222);
            expect (! rpn.isNRPN);
            expect (rpn.is14BitValue);
        }
        beginTest ("RPNs on multiple channels simultaneously");
        {
            MidiRPNDetector detector;
            MidiRPNMessage rpn;
            expect (! detector.parseControllerMessage (1, 100, 44, rpn));
            expect (! detector.parseControllerMessage (2, 101, 0,  rpn));
            expect (! detector.parseControllerMessage (1, 101, 2,  rpn));
            expect (! detector.parseControllerMessage (2, 100, 7,  rpn));
            expect (! detector.parseControllerMessage (1, 38,  94, rpn));
            expect (detector.parseControllerMessage   (2, 6,   42, rpn));
            expectEquals (rpn.channel, 2);
            expectEquals (rpn.parameterNumber, 7);
            expectEquals (rpn.value, 42);
            expect (! rpn.isNRPN);
            expect (! rpn.is14BitValue);
            expect (detector.parseControllerMessage   (1, 6,   1,  rpn));
            expectEquals (rpn.channel, 1);
            expectEquals (rpn.parameterNumber, 300);
            expectEquals (rpn.value, 222);
            expect (! rpn.isNRPN);
            expect (rpn.is14BitValue);
        }
        beginTest ("14-bit RPN with value within 7-bit range");
        {
            MidiRPNDetector detector;
            MidiRPNMessage rpn;
            expect (! detector.parseControllerMessage (16, 100, 0 , rpn));
            expect (! detector.parseControllerMessage (16, 101, 0,  rpn));
            expect (! detector.parseControllerMessage (16, 38,  3,  rpn));
            expect (detector.parseControllerMessage   (16, 6,   0,  rpn));
            expectEquals (rpn.channel, 16);
            expectEquals (rpn.parameterNumber, 0);
            expectEquals (rpn.value, 3);
            expect (! rpn.isNRPN);
            expect (rpn.is14BitValue);
        }
        beginTest ("invalid RPN (wrong order)");
        {
            MidiRPNDetector detector;
            MidiRPNMessage rpn;
            expect (! detector.parseControllerMessage (2, 6,   42, rpn));
            expect (! detector.parseControllerMessage (2, 101, 0,  rpn));
            expect (! detector.parseControllerMessage (2, 100, 7,  rpn));
        }
        beginTest ("14-bit RPN interspersed with unrelated CC messages");
        {
            MidiRPNDetector detector;
            MidiRPNMessage rpn;
            expect (! detector.parseControllerMessage (16, 3,   80, rpn));
            expect (! detector.parseControllerMessage (16, 100, 0 , rpn));
            expect (! detector.parseControllerMessage (16, 4,   81, rpn));
            expect (! detector.parseControllerMessage (16, 101, 0,  rpn));
            expect (! detector.parseControllerMessage (16, 5,   82, rpn));
            expect (! detector.parseControllerMessage (16, 5,   83, rpn));
            expect (! detector.parseControllerMessage (16, 38,  3,  rpn));
            expect (! detector.parseControllerMessage (16, 4,   84, rpn));
            expect (! detector.parseControllerMessage (16, 3,   85, rpn));
            expect (detector.parseControllerMessage   (16, 6,   0,  rpn));
            expectEquals (rpn.channel, 16);
            expectEquals (rpn.parameterNumber, 0);
            expectEquals (rpn.value, 3);
            expect (! rpn.isNRPN);
            expect (rpn.is14BitValue);
        }
        beginTest ("14-bit NRPN");
        {
            MidiRPNDetector detector;
            MidiRPNMessage rpn;
            expect (! detector.parseControllerMessage (1, 98,  44, rpn));
            expect (! detector.parseControllerMessage (1, 99 , 2,  rpn));
            expect (! detector.parseControllerMessage (1, 38,  94, rpn));
            expect (detector.parseControllerMessage   (1, 6,   1,  rpn));
            expectEquals (rpn.channel, 1);
            expectEquals (rpn.parameterNumber, 300);
            expectEquals (rpn.value, 222);
            expect (rpn.isNRPN);
            expect (rpn.is14BitValue);
        }
        beginTest ("reset");
        {
            MidiRPNDetector detector;
            MidiRPNMessage rpn;
            expect (! detector.parseControllerMessage (2, 101, 0,  rpn));
            detector.reset();
            expect (! detector.parseControllerMessage (2, 100, 7,  rpn));
            expect (! detector.parseControllerMessage (2, 6,   42, rpn));
        }
    }
};
static MidiRPNDetectorTests MidiRPNDetectorUnitTests;
//==============================================================================
class MidiRPNGeneratorTests   : public UnitTest
{
public:
    MidiRPNGeneratorTests()  : UnitTest ("MidiRPNGenerator class") {}
    void runTest() override
    {
        beginTest ("generating RPN/NRPN");
        {
            {
                MidiBuffer buffer = MidiRPNGenerator::generate (1, 23, 1337, true, true);
                expectContainsRPN (buffer, 1, 23, 1337, true, true);
            }
            {
                MidiBuffer buffer = MidiRPNGenerator::generate (16, 101, 34, false, false);
                expectContainsRPN (buffer, 16, 101, 34, false, false);
            }
            {
                MidiRPNMessage message = { 16, 101, 34, false, false };
                MidiBuffer buffer = MidiRPNGenerator::generate (message);
                expectContainsRPN (buffer, message);
            }
        }
    }
private:
    //==============================================================================
    void expectContainsRPN (const MidiBuffer& midiBuffer,
                            int channel,
                            int parameterNumber,
                            int value,
                            bool isNRPN,
                            bool is14BitValue)
    {
        MidiRPNMessage expected = { channel, parameterNumber, value, isNRPN, is14BitValue };
        expectContainsRPN (midiBuffer, expected);
    }
    //==============================================================================
    void expectContainsRPN (const MidiBuffer& midiBuffer, MidiRPNMessage expected)
    {
        MidiBuffer::Iterator iter (midiBuffer);
        MidiMessage midiMessage;
        MidiRPNMessage result = MidiRPNMessage();
        MidiRPNDetector detector;
        int samplePosition; // not actually used, so no need to initialise.
        while (iter.getNextEvent (midiMessage, samplePosition))
        {
            if (detector.parseControllerMessage (midiMessage.getChannel(),
                                                 midiMessage.getControllerNumber(),
                                                 midiMessage.getControllerValue(),
                                                 result))
                break;
        }
        expectEquals (result.channel, expected.channel);
        expectEquals (result.parameterNumber, expected.parameterNumber);
        expectEquals (result.value, expected.value);
        expect (result.isNRPN == expected.isNRPN),
        expect (result.is14BitValue == expected.is14BitValue);
    }
};
static MidiRPNGeneratorTests MidiRPNGeneratorUnitTests;
#endif // JUCE_UNIT_TESTS
 |