/* ============================================================================== 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. ============================================================================== */ namespace { void checkAndLimitZoneParameters (int minValue, int maxValue, int& valueToCheckAndLimit) noexcept { if (valueToCheckAndLimit < minValue || valueToCheckAndLimit > maxValue) { // if you hit this, one of the parameters you supplied for MPEZone // was not within the allowed range! // we fit this back into the allowed range here to maintain a valid // state for the zone, but probably the resulting zone is not what you //wanted it to be! jassertfalse; valueToCheckAndLimit = jlimit (minValue, maxValue, valueToCheckAndLimit); } } } //============================================================================== MPEZone::MPEZone (int masterChannel_, int numNoteChannels_, int perNotePitchbendRange_, int masterPitchbendRange_) noexcept : masterChannel (masterChannel_), numNoteChannels (numNoteChannels_), perNotePitchbendRange (perNotePitchbendRange_), masterPitchbendRange (masterPitchbendRange_) { checkAndLimitZoneParameters (1, 15, masterChannel); checkAndLimitZoneParameters (1, 16 - masterChannel, numNoteChannels); checkAndLimitZoneParameters (0, 96, perNotePitchbendRange); checkAndLimitZoneParameters (0, 96, masterPitchbendRange); } //============================================================================== int MPEZone::getMasterChannel() const noexcept { return masterChannel; } int MPEZone::getNumNoteChannels() const noexcept { return numNoteChannels; } int MPEZone::getFirstNoteChannel() const noexcept { return masterChannel + 1; } int MPEZone::getLastNoteChannel() const noexcept { return masterChannel + numNoteChannels; } Range MPEZone::getNoteChannelRange() const noexcept { return Range::withStartAndLength (getFirstNoteChannel(), getNumNoteChannels()); } bool MPEZone::isUsingChannel (int channel) const noexcept { jassert (channel > 0 && channel <= 16); return channel >= masterChannel && channel <= masterChannel + numNoteChannels; } bool MPEZone::isUsingChannelAsNoteChannel (int channel) const noexcept { jassert (channel > 0 && channel <= 16); return channel > masterChannel && channel <= masterChannel + numNoteChannels; } int MPEZone::getPerNotePitchbendRange() const noexcept { return perNotePitchbendRange; } int MPEZone::getMasterPitchbendRange() const noexcept { return masterPitchbendRange; } void MPEZone::setPerNotePitchbendRange (int rangeInSemitones) noexcept { checkAndLimitZoneParameters (0, 96, rangeInSemitones); perNotePitchbendRange = rangeInSemitones; } void MPEZone::setMasterPitchbendRange (int rangeInSemitones) noexcept { checkAndLimitZoneParameters (0, 96, rangeInSemitones); masterPitchbendRange = rangeInSemitones; } //============================================================================== bool MPEZone::overlapsWith (MPEZone other) const noexcept { if (masterChannel == other.masterChannel) return true; if (masterChannel > other.masterChannel) return other.overlapsWith (*this); return masterChannel + numNoteChannels >= other.masterChannel; } //============================================================================== bool MPEZone::truncateToFit (MPEZone other) noexcept { const int masterChannelDiff = other.masterChannel - masterChannel; // we need at least 2 channels to be left after truncation: // 1 master channel and 1 note channel. otherwise we can't truncate. if (masterChannelDiff < 2) return false; numNoteChannels = jmin (numNoteChannels, masterChannelDiff - 1); return true; } //============================================================================== bool MPEZone::operator== (const MPEZone& other) const noexcept { return masterChannel == other.masterChannel && numNoteChannels == other.numNoteChannels && perNotePitchbendRange == other.perNotePitchbendRange && masterPitchbendRange == other.masterPitchbendRange; } bool MPEZone::operator!= (const MPEZone& other) const noexcept { return ! operator== (other); } //============================================================================== //============================================================================== #if JUCE_UNIT_TESTS class MPEZoneTests : public UnitTest { public: MPEZoneTests() : UnitTest ("MPEZone class") {} void runTest() override { beginTest ("initialisation"); { { MPEZone zone (1, 10); expectEquals (zone.getMasterChannel(), 1); expectEquals (zone.getNumNoteChannels(), 10); expectEquals (zone.getFirstNoteChannel(), 2); expectEquals (zone.getLastNoteChannel(), 11); expectEquals (zone.getPerNotePitchbendRange(), 48); expectEquals (zone.getMasterPitchbendRange(), 2); expect (zone.isUsingChannel (1)); expect (zone.isUsingChannel (2)); expect (zone.isUsingChannel (10)); expect (zone.isUsingChannel (11)); expect (! zone.isUsingChannel (12)); expect (! zone.isUsingChannel (16)); expect (! zone.isUsingChannelAsNoteChannel (1)); expect (zone.isUsingChannelAsNoteChannel (2)); expect (zone.isUsingChannelAsNoteChannel (10)); expect (zone.isUsingChannelAsNoteChannel (11)); expect (! zone.isUsingChannelAsNoteChannel (12)); expect (! zone.isUsingChannelAsNoteChannel (16)); } { MPEZone zone (5, 4); expectEquals (zone.getMasterChannel(), 5); expectEquals (zone.getNumNoteChannels(), 4); expectEquals (zone.getFirstNoteChannel(), 6); expectEquals (zone.getLastNoteChannel(), 9); expectEquals (zone.getPerNotePitchbendRange(), 48); expectEquals (zone.getMasterPitchbendRange(), 2); expect (! zone.isUsingChannel (1)); expect (! zone.isUsingChannel (4)); expect (zone.isUsingChannel (5)); expect (zone.isUsingChannel (6)); expect (zone.isUsingChannel (8)); expect (zone.isUsingChannel (9)); expect (! zone.isUsingChannel (10)); expect (! zone.isUsingChannel (16)); expect (! zone.isUsingChannelAsNoteChannel (5)); expect (zone.isUsingChannelAsNoteChannel (6)); expect (zone.isUsingChannelAsNoteChannel (8)); expect (zone.isUsingChannelAsNoteChannel (9)); expect (! zone.isUsingChannelAsNoteChannel (10)); } } beginTest ("getNoteChannelRange"); { MPEZone zone (2, 10); Range noteChannelRange = zone.getNoteChannelRange(); expectEquals (noteChannelRange.getStart(), 3); expectEquals (noteChannelRange.getEnd(), 13); } beginTest ("setting master pitchbend range"); { MPEZone zone (1, 10); zone.setMasterPitchbendRange (96); expectEquals (zone.getMasterPitchbendRange(), 96); zone.setMasterPitchbendRange (0); expectEquals (zone.getMasterPitchbendRange(), 0); expectEquals (zone.getPerNotePitchbendRange(), 48); } beginTest ("setting per-note pitchbend range"); { MPEZone zone (1, 10); zone.setPerNotePitchbendRange (96); expectEquals (zone.getPerNotePitchbendRange(), 96); zone.setPerNotePitchbendRange (0); expectEquals (zone.getPerNotePitchbendRange(), 0); expectEquals (zone.getMasterPitchbendRange(), 2); } beginTest ("checking overlap"); { testOverlapsWith (1, 10, 1, 10, true); testOverlapsWith (1, 4, 6, 3, false); testOverlapsWith (1, 4, 8, 3, false); testOverlapsWith (2, 10, 2, 8, true); testOverlapsWith (1, 10, 3, 2, true); testOverlapsWith (3, 10, 5, 9, true); } beginTest ("truncating"); { testTruncateToFit (1, 10, 3, 10, true, 1, 1); testTruncateToFit (3, 10, 1, 10, false, 3, 10); testTruncateToFit (1, 10, 5, 8, true, 1, 3); testTruncateToFit (5, 8, 1, 10, false, 5, 8); testTruncateToFit (1, 10, 4, 3, true, 1, 2); testTruncateToFit (4, 3, 1, 10, false, 4, 3); testTruncateToFit (1, 3, 5, 3, true, 1, 3); testTruncateToFit (5, 3, 1, 3, false, 5, 3); testTruncateToFit (1, 3, 7, 3, true, 1, 3); testTruncateToFit (7, 3, 1, 3, false, 7, 3); testTruncateToFit (1, 10, 2, 10, false, 1, 10); testTruncateToFit (2, 10, 1, 10, false, 2, 10); } } private: //============================================================================== void testOverlapsWith (int masterChannelFirst, int numNoteChannelsFirst, int masterChannelSecond, int numNoteChannelsSecond, bool expectedRetVal) { MPEZone first (masterChannelFirst, numNoteChannelsFirst); MPEZone second (masterChannelSecond, numNoteChannelsSecond); expect (first.overlapsWith (second) == expectedRetVal); expect (second.overlapsWith (first) == expectedRetVal); } //============================================================================== void testTruncateToFit (int masterChannelFirst, int numNoteChannelsFirst, int masterChannelSecond, int numNoteChannelsSecond, bool expectedRetVal, int masterChannelFirstAfter, int numNoteChannelsFirstAfter) { MPEZone first (masterChannelFirst, numNoteChannelsFirst); MPEZone second (masterChannelSecond, numNoteChannelsSecond); expect (first.truncateToFit (second) == expectedRetVal); expectEquals (first.getMasterChannel(), masterChannelFirstAfter); expectEquals (first.getNumNoteChannels(), numNoteChannelsFirstAfter); } }; static MPEZoneTests MPEZoneUnitTests; #endif // JUCE_UNIT_TESTS