| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,276 @@ | ||
| /* | ||
| ============================================================================== | ||
| This file is part of the JUCE library. | ||
| Copyright (c) 2022 - Raw Material Software Limited | ||
| JUCE is an open source library subject to commercial or open-source | ||
| licensing. | ||
| The code included in this file is provided under the terms of the ISC license | ||
| http://www.isc.org/downloads/software-support-policy/isc-license. Permission | ||
| To use, copy, modify, and/or distribute this software for any purpose with or | ||
| without fee is hereby granted provided that the above copyright notice and | ||
| this permission notice appear in all copies. | ||
| JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | ||
| EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | ||
| DISCLAIMED. | ||
| ============================================================================== | ||
| */ | ||
|
|
||
| namespace juce | ||
| { | ||
|
|
||
| //============================================================================== | ||
| /** | ||
| A subclass of AudioPlayHead can supply information about the position and | ||
| status of a moving play head during audio playback. | ||
| One of these can be supplied to an AudioProcessor object so that it can find | ||
| out about the position of the audio that it is rendering. | ||
| @see AudioProcessor::setPlayHead, AudioProcessor::getPlayHead | ||
| @tags{Audio} | ||
| */ | ||
| class JUCE_API AudioPlayHead | ||
| { | ||
| protected: | ||
| //============================================================================== | ||
| AudioPlayHead() = default; | ||
|
|
||
| public: | ||
| virtual ~AudioPlayHead() = default; | ||
|
|
||
| //============================================================================== | ||
| /** Frame rate types. */ | ||
| enum FrameRateType | ||
| { | ||
| fps23976 = 0, | ||
| fps24 = 1, | ||
| fps25 = 2, | ||
| fps2997 = 3, | ||
| fps30 = 4, | ||
| fps2997drop = 5, | ||
| fps30drop = 6, | ||
| fps60 = 7, | ||
| fps60drop = 8, | ||
| fpsUnknown = 99 | ||
| }; | ||
|
|
||
| /** More descriptive frame rate type. */ | ||
| class JUCE_API FrameRate | ||
| { | ||
| public: | ||
| /** Creates a frame rate with a base rate of 0. */ | ||
| FrameRate() = default; | ||
|
|
||
| /** Creates a FrameRate instance from a FrameRateType. */ | ||
| FrameRate (FrameRateType type) : FrameRate (fromType (type)) {} | ||
|
|
||
| /** Gets the FrameRateType that matches the state of this FrameRate. | ||
| Returns fpsUnknown if this FrameRate cannot be represented by any of the | ||
| other enum fields. | ||
| */ | ||
| FrameRateType getType() const | ||
| { | ||
| switch (base) | ||
| { | ||
| case 24: return pulldown ? fps23976 : fps24; | ||
| case 25: return fps25; | ||
| case 30: return pulldown ? (drop ? fps2997drop : fps2997) | ||
| : (drop ? fps30drop : fps30); | ||
| case 60: return drop ? fps60drop : fps60; | ||
| } | ||
|
|
||
| return fpsUnknown; | ||
| } | ||
|
|
||
| /** Returns the plain rate, without taking pulldown into account. */ | ||
| int getBaseRate() const { return base; } | ||
|
|
||
| /** Returns true if drop-frame timecode is in use. */ | ||
| bool isDrop() const { return drop; } | ||
|
|
||
| /** Returns true if the effective framerate is actually equal to the base rate divided by 1.001 */ | ||
| bool isPullDown() const { return pulldown; } | ||
|
|
||
| /** Returns the actual rate described by this object, taking pulldown into account. */ | ||
| double getEffectiveRate() const { return pulldown ? (double) base / 1.001 : (double) base; } | ||
|
|
||
| /** Returns a copy of this object with the specified base rate. */ | ||
| JUCE_NODISCARD FrameRate withBaseRate (int x) const { return with (&FrameRate::base, x); } | ||
|
|
||
| /** Returns a copy of this object with drop frames enabled or disabled, as specified. */ | ||
| JUCE_NODISCARD FrameRate withDrop (bool x = true) const { return with (&FrameRate::drop, x); } | ||
|
|
||
| /** Returns a copy of this object with pulldown enabled or disabled, as specified. */ | ||
| JUCE_NODISCARD FrameRate withPullDown (bool x = true) const { return with (&FrameRate::pulldown, x); } | ||
|
|
||
| /** Returns true if this instance is equal to other. */ | ||
| bool operator== (const FrameRate& other) const | ||
| { | ||
| const auto tie = [] (const FrameRate& x) { return std::tie (x.base, x.drop, x.pulldown); }; | ||
| return tie (*this) == tie (other); | ||
| } | ||
|
|
||
| /** Returns true if this instance is not equal to other. */ | ||
| bool operator!= (const FrameRate& other) const { return ! (*this == other); } | ||
|
|
||
| private: | ||
| static FrameRate fromType (FrameRateType type) | ||
| { | ||
| switch (type) | ||
| { | ||
| case fps23976: return FrameRate().withBaseRate (24).withPullDown(); | ||
| case fps24: return FrameRate().withBaseRate (24); | ||
| case fps25: return FrameRate().withBaseRate (25); | ||
| case fps2997: return FrameRate().withBaseRate (30).withPullDown(); | ||
| case fps30: return FrameRate().withBaseRate (30); | ||
| case fps2997drop: return FrameRate().withBaseRate (30).withDrop().withPullDown(); | ||
| case fps30drop: return FrameRate().withBaseRate (30).withDrop(); | ||
| case fps60: return FrameRate().withBaseRate (60); | ||
| case fps60drop: return FrameRate().withBaseRate (60).withDrop(); | ||
| case fpsUnknown: break; | ||
| } | ||
|
|
||
| return {}; | ||
| } | ||
|
|
||
| template <typename Member, typename Value> | ||
| FrameRate with (Member&& member, Value&& value) const | ||
| { | ||
| auto copy = *this; | ||
| copy.*member = std::forward<Value> (value); | ||
| return copy; | ||
| } | ||
|
|
||
| int base = 0; | ||
| bool drop = false, pulldown = false; | ||
| }; | ||
|
|
||
| //============================================================================== | ||
| /** This structure is filled-in by the AudioPlayHead::getCurrentPosition() method. | ||
| */ | ||
| struct JUCE_API CurrentPositionInfo | ||
| { | ||
| /** The tempo in BPM */ | ||
| double bpm = 120.0; | ||
|
|
||
| /** Time signature numerator, e.g. the 3 of a 3/4 time sig */ | ||
| int timeSigNumerator = 4; | ||
| /** Time signature denominator, e.g. the 4 of a 3/4 time sig */ | ||
| int timeSigDenominator = 4; | ||
|
|
||
| /** The current play position, in samples from the start of the timeline. */ | ||
| int64 timeInSamples = 0; | ||
| /** The current play position, in seconds from the start of the timeline. */ | ||
| double timeInSeconds = 0; | ||
|
|
||
| /** For timecode, the position of the start of the timeline, in seconds from 00:00:00:00. */ | ||
| double editOriginTime = 0; | ||
|
|
||
| /** The current play position, in units of quarter-notes. */ | ||
| double ppqPosition = 0; | ||
|
|
||
| /** The position of the start of the last bar, in units of quarter-notes. | ||
| This is the time from the start of the timeline to the start of the current | ||
| bar, in ppq units. | ||
| Note - this value may be unavailable on some hosts, e.g. Pro-Tools. If | ||
| it's not available, the value will be 0. | ||
| */ | ||
| double ppqPositionOfLastBarStart = 0; | ||
|
|
||
| /** The video frame rate, if applicable. */ | ||
| FrameRate frameRate = FrameRateType::fps23976; | ||
|
|
||
| /** True if the transport is currently playing. */ | ||
| bool isPlaying = false; | ||
|
|
||
| /** True if the transport is currently recording. | ||
| (When isRecording is true, then isPlaying will also be true). | ||
| */ | ||
| bool isRecording = false; | ||
|
|
||
| /** The current cycle start position in units of quarter-notes. | ||
| Note that not all hosts or plugin formats may provide this value. | ||
| @see isLooping | ||
| */ | ||
| double ppqLoopStart = 0; | ||
|
|
||
| /** The current cycle end position in units of quarter-notes. | ||
| Note that not all hosts or plugin formats may provide this value. | ||
| @see isLooping | ||
| */ | ||
| double ppqLoopEnd = 0; | ||
|
|
||
| /** True if the transport is currently looping. */ | ||
| bool isLooping = false; | ||
|
|
||
| //============================================================================== | ||
| bool operator== (const CurrentPositionInfo& other) const noexcept | ||
| { | ||
| const auto tie = [] (const CurrentPositionInfo& i) | ||
| { | ||
| return std::tie (i.timeInSamples, | ||
| i.ppqPosition, | ||
| i.editOriginTime, | ||
| i.ppqPositionOfLastBarStart, | ||
| i.frameRate, | ||
| i.isPlaying, | ||
| i.isRecording, | ||
| i.bpm, | ||
| i.timeSigNumerator, | ||
| i.timeSigDenominator, | ||
| i.ppqLoopStart, | ||
| i.ppqLoopEnd, | ||
| i.isLooping); | ||
| }; | ||
|
|
||
| return tie (*this) == tie (other); | ||
| } | ||
|
|
||
| bool operator!= (const CurrentPositionInfo& other) const noexcept | ||
| { | ||
| return ! operator== (other); | ||
| } | ||
|
|
||
| void resetToDefault() | ||
| { | ||
| *this = CurrentPositionInfo{}; | ||
| } | ||
| }; | ||
|
|
||
| //============================================================================== | ||
| /** Fills-in the given structure with details about the transport's | ||
| position at the start of the current processing block. If this method returns | ||
| false then the current play head position is not available and the given | ||
| structure will be undefined. | ||
| You can ONLY call this from your processBlock() method! Calling it at other | ||
| times will produce undefined behaviour, as the host may not have any context | ||
| in which a time would make sense, and some hosts will almost certainly have | ||
| multithreading issues if it's not called on the audio thread. | ||
| */ | ||
| virtual bool getCurrentPosition (CurrentPositionInfo& result) = 0; | ||
|
|
||
| /** Returns true if this object can control the transport. */ | ||
| virtual bool canControlTransport() { return false; } | ||
|
|
||
| /** Starts or stops the audio. */ | ||
| virtual void transportPlay (bool shouldStartPlaying) { ignoreUnused (shouldStartPlaying); } | ||
|
|
||
| /** Starts or stops recording the audio. */ | ||
| virtual void transportRecord (bool shouldStartRecording) { ignoreUnused (shouldStartRecording); } | ||
|
|
||
| /** Rewinds the audio. */ | ||
| virtual void transportRewind() {} | ||
| }; | ||
|
|
||
| } // namespace juce |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,99 @@ | ||
| /* | ||
| ============================================================================== | ||
| This file is part of the JUCE library. | ||
| Copyright (c) 2022 - Raw Material Software Limited | ||
| JUCE is an open source library subject to commercial or open-source | ||
| licensing. | ||
| The code included in this file is provided under the terms of the ISC license | ||
| http://www.isc.org/downloads/software-support-policy/isc-license. Permission | ||
| To use, copy, modify, and/or distribute this software for any purpose with or | ||
| without fee is hereby granted provided that the above copyright notice and | ||
| this permission notice appear in all copies. | ||
| JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | ||
| EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | ||
| DISCLAIMED. | ||
| ============================================================================== | ||
| */ | ||
|
|
||
| namespace juce | ||
| { | ||
|
|
||
| AudioProcessLoadMeasurer::AudioProcessLoadMeasurer() = default; | ||
| AudioProcessLoadMeasurer::~AudioProcessLoadMeasurer() = default; | ||
|
|
||
| void AudioProcessLoadMeasurer::reset() | ||
| { | ||
| reset (0, 0); | ||
| } | ||
|
|
||
| void AudioProcessLoadMeasurer::reset (double sampleRate, int blockSize) | ||
| { | ||
| const SpinLock::ScopedLockType lock (mutex); | ||
|
|
||
| cpuUsageProportion = 0; | ||
| xruns = 0; | ||
|
|
||
| samplesPerBlock = blockSize; | ||
| msPerSample = (sampleRate > 0.0 && blockSize > 0) ? 1000.0 / sampleRate : 0; | ||
| } | ||
|
|
||
| void AudioProcessLoadMeasurer::registerBlockRenderTime (double milliseconds) | ||
| { | ||
| const SpinLock::ScopedTryLockType lock (mutex); | ||
|
|
||
| if (lock.isLocked()) | ||
| registerRenderTimeLocked (milliseconds, samplesPerBlock); | ||
| } | ||
|
|
||
| void AudioProcessLoadMeasurer::registerRenderTime (double milliseconds, int numSamples) | ||
| { | ||
| const SpinLock::ScopedTryLockType lock (mutex); | ||
|
|
||
| if (lock.isLocked()) | ||
| registerRenderTimeLocked (milliseconds, numSamples); | ||
| } | ||
|
|
||
| void AudioProcessLoadMeasurer::registerRenderTimeLocked (double milliseconds, int numSamples) | ||
| { | ||
| if (msPerSample == 0) | ||
| return; | ||
|
|
||
| const auto maxMilliseconds = numSamples * msPerSample; | ||
| const auto usedProportion = milliseconds / maxMilliseconds; | ||
| const auto filterAmount = 0.2; | ||
| const auto proportion = cpuUsageProportion.load(); | ||
| cpuUsageProportion = proportion + filterAmount * (usedProportion - proportion); | ||
|
|
||
| if (milliseconds > maxMilliseconds) | ||
| ++xruns; | ||
| } | ||
|
|
||
| double AudioProcessLoadMeasurer::getLoadAsProportion() const { return jlimit (0.0, 1.0, cpuUsageProportion.load()); } | ||
| double AudioProcessLoadMeasurer::getLoadAsPercentage() const { return 100.0 * getLoadAsProportion(); } | ||
|
|
||
| int AudioProcessLoadMeasurer::getXRunCount() const { return xruns; } | ||
|
|
||
| AudioProcessLoadMeasurer::ScopedTimer::ScopedTimer (AudioProcessLoadMeasurer& p) | ||
| : ScopedTimer (p, p.samplesPerBlock) | ||
| { | ||
| } | ||
|
|
||
| AudioProcessLoadMeasurer::ScopedTimer::ScopedTimer (AudioProcessLoadMeasurer& p, int numSamplesInBlock) | ||
| : owner (p), startTime (Time::getMillisecondCounterHiRes()), samplesInBlock (numSamplesInBlock) | ||
| { | ||
| // numSamplesInBlock should never be zero. Did you remember to call AudioProcessLoadMeasurer::reset(), | ||
| // passing the expected samples per block? | ||
| jassert (numSamplesInBlock); | ||
| } | ||
|
|
||
| AudioProcessLoadMeasurer::ScopedTimer::~ScopedTimer() | ||
| { | ||
| owner.registerRenderTime (Time::getMillisecondCounterHiRes() - startTime, samplesInBlock); | ||
| } | ||
|
|
||
| } // namespace juce |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,109 @@ | ||
| /* | ||
| ============================================================================== | ||
| This file is part of the JUCE library. | ||
| Copyright (c) 2022 - Raw Material Software Limited | ||
| JUCE is an open source library subject to commercial or open-source | ||
| licensing. | ||
| The code included in this file is provided under the terms of the ISC license | ||
| http://www.isc.org/downloads/software-support-policy/isc-license. Permission | ||
| To use, copy, modify, and/or distribute this software for any purpose with or | ||
| without fee is hereby granted provided that the above copyright notice and | ||
| this permission notice appear in all copies. | ||
| JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | ||
| EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | ||
| DISCLAIMED. | ||
| ============================================================================== | ||
| */ | ||
|
|
||
| namespace juce | ||
| { | ||
|
|
||
| //============================================================================== | ||
| /** | ||
| Maintains an ongoing measurement of the proportion of time which is being | ||
| spent inside an audio callback. | ||
| @tags{Audio} | ||
| */ | ||
| class JUCE_API AudioProcessLoadMeasurer | ||
| { | ||
| public: | ||
| /** */ | ||
| AudioProcessLoadMeasurer(); | ||
|
|
||
| /** Destructor. */ | ||
| ~AudioProcessLoadMeasurer(); | ||
|
|
||
| //============================================================================== | ||
| /** Resets the state. */ | ||
| void reset(); | ||
|
|
||
| /** Resets the counter, in preparation for use with the given sample rate and block size. */ | ||
| void reset (double sampleRate, int blockSize); | ||
|
|
||
| /** Returns the current load as a proportion 0 to 1.0 */ | ||
| double getLoadAsProportion() const; | ||
|
|
||
| /** Returns the current load as a percentage 0 to 100.0 */ | ||
| double getLoadAsPercentage() const; | ||
|
|
||
| /** Returns the number of over- (or under-) runs recorded since the state was reset. */ | ||
| int getXRunCount() const; | ||
|
|
||
| //============================================================================== | ||
| /** This class measures the time between its construction and destruction and | ||
| adds it to an AudioProcessLoadMeasurer. | ||
| e.g. | ||
| @code | ||
| { | ||
| AudioProcessLoadMeasurer::ScopedTimer timer (myProcessLoadMeasurer); | ||
| myCallback->doTheCallback(); | ||
| } | ||
| @endcode | ||
| @tags{Audio} | ||
| */ | ||
| struct JUCE_API ScopedTimer | ||
| { | ||
| ScopedTimer (AudioProcessLoadMeasurer&); | ||
| ScopedTimer (AudioProcessLoadMeasurer&, int numSamplesInBlock); | ||
| ~ScopedTimer(); | ||
|
|
||
| private: | ||
| AudioProcessLoadMeasurer& owner; | ||
| double startTime; | ||
| int samplesInBlock; | ||
|
|
||
| JUCE_DECLARE_NON_COPYABLE (ScopedTimer) | ||
| }; | ||
|
|
||
| /** Can be called manually to add the time of a callback to the stats. | ||
| Normally you probably would never call this - it's simpler and more robust to | ||
| use a ScopedTimer to measure the time using an RAII pattern. | ||
| */ | ||
| void registerBlockRenderTime (double millisecondsTaken); | ||
|
|
||
| /** Can be called manually to add the time of a callback to the stats. | ||
| Normally you probably would never call this - it's simpler and more robust to | ||
| use a ScopedTimer to measure the time using an RAII pattern. | ||
| */ | ||
| void registerRenderTime (double millisecondsTaken, int numSamples); | ||
|
|
||
| private: | ||
| void registerRenderTimeLocked (double, int); | ||
|
|
||
| SpinLock mutex; | ||
| int samplesPerBlock = 0; | ||
| double msPerSample = 0; | ||
| std::atomic<double> cpuUsageProportion { 0 }; | ||
| std::atomic<int> xruns { 0 }; | ||
| }; | ||
|
|
||
|
|
||
| } // namespace juce |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,269 @@ | ||
| /* | ||
| ============================================================================== | ||
| This file is part of the JUCE library. | ||
| Copyright (c) 2022 - Raw Material Software Limited | ||
| JUCE is an open source library subject to commercial or open-source | ||
| licensing. | ||
| The code included in this file is provided under the terms of the ISC license | ||
| http://www.isc.org/downloads/software-support-policy/isc-license. Permission | ||
| To use, copy, modify, and/or distribute this software for any purpose with or | ||
| without fee is hereby granted provided that the above copyright notice and | ||
| this permission notice appear in all copies. | ||
| JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | ||
| EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | ||
| DISCLAIMED. | ||
| ============================================================================== | ||
| */ | ||
|
|
||
| namespace juce | ||
| { | ||
|
|
||
| #ifndef JUCE_SNAP_TO_ZERO | ||
| #if JUCE_INTEL | ||
| #define JUCE_SNAP_TO_ZERO(n) if (! (n < -1.0e-8f || n > 1.0e-8f)) n = 0; | ||
| #else | ||
| #define JUCE_SNAP_TO_ZERO(n) ignoreUnused (n) | ||
| #endif | ||
| #endif | ||
| class ScopedNoDenormals; | ||
|
|
||
| //============================================================================== | ||
| /** | ||
| A collection of simple vector operations on arrays of floating point numbers, | ||
| accelerated with SIMD instructions where possible, usually accessed from | ||
| the FloatVectorOperations class. | ||
| @code | ||
| float data[64]; | ||
| // The following two function calls are equivalent: | ||
| FloatVectorOperationsBase<float, int>::clear (data, 64); | ||
| FloatVectorOperations::clear (data, 64); | ||
| @endcode | ||
| @see FloatVectorOperations | ||
| @tags{Audio} | ||
| */ | ||
| template <typename FloatType, typename CountType> | ||
| struct FloatVectorOperationsBase | ||
| { | ||
| /** Clears a vector of floating point numbers. */ | ||
| static void JUCE_CALLTYPE clear (FloatType* dest, CountType numValues) noexcept; | ||
|
|
||
| /** Copies a repeated value into a vector of floating point numbers. */ | ||
| static void JUCE_CALLTYPE fill (FloatType* dest, FloatType valueToFill, CountType numValues) noexcept; | ||
|
|
||
| /** Copies a vector of floating point numbers. */ | ||
| static void JUCE_CALLTYPE copy (FloatType* dest, const FloatType* src, CountType numValues) noexcept; | ||
|
|
||
| /** Copies a vector of floating point numbers, multiplying each value by a given multiplier */ | ||
| static void JUCE_CALLTYPE copyWithMultiply (FloatType* dest, const FloatType* src, FloatType multiplier, CountType numValues) noexcept; | ||
|
|
||
| /** Adds a fixed value to the destination values. */ | ||
| static void JUCE_CALLTYPE add (FloatType* dest, FloatType amountToAdd, CountType numValues) noexcept; | ||
|
|
||
| /** Adds a fixed value to each source value and stores it in the destination array. */ | ||
| static void JUCE_CALLTYPE add (FloatType* dest, const FloatType* src, FloatType amount, CountType numValues) noexcept; | ||
|
|
||
| /** Adds the source values to the destination values. */ | ||
| static void JUCE_CALLTYPE add (FloatType* dest, const FloatType* src, CountType numValues) noexcept; | ||
|
|
||
| /** Adds each source1 value to the corresponding source2 value and stores the result in the destination array. */ | ||
| static void JUCE_CALLTYPE add (FloatType* dest, const FloatType* src1, const FloatType* src2, CountType num) noexcept; | ||
|
|
||
| /** Subtracts the source values from the destination values. */ | ||
| static void JUCE_CALLTYPE subtract (FloatType* dest, const FloatType* src, CountType numValues) noexcept; | ||
|
|
||
| /** Subtracts each source2 value from the corresponding source1 value and stores the result in the destination array. */ | ||
| static void JUCE_CALLTYPE subtract (FloatType* dest, const FloatType* src1, const FloatType* src2, CountType num) noexcept; | ||
|
|
||
| /** Multiplies each source value by the given multiplier, then adds it to the destination value. */ | ||
| static void JUCE_CALLTYPE addWithMultiply (FloatType* dest, const FloatType* src, FloatType multiplier, CountType numValues) noexcept; | ||
|
|
||
| /** Multiplies each source1 value by the corresponding source2 value, then adds it to the destination value. */ | ||
| static void JUCE_CALLTYPE addWithMultiply (FloatType* dest, const FloatType* src1, const FloatType* src2, CountType num) noexcept; | ||
|
|
||
| /** Multiplies each source value by the given multiplier, then subtracts it to the destination value. */ | ||
| static void JUCE_CALLTYPE subtractWithMultiply (FloatType* dest, const FloatType* src, FloatType multiplier, CountType numValues) noexcept; | ||
|
|
||
| /** Multiplies each source1 value by the corresponding source2 value, then subtracts it to the destination value. */ | ||
| static void JUCE_CALLTYPE subtractWithMultiply (FloatType* dest, const FloatType* src1, const FloatType* src2, CountType num) noexcept; | ||
|
|
||
| /** Multiplies the destination values by the source values. */ | ||
| static void JUCE_CALLTYPE multiply (FloatType* dest, const FloatType* src, CountType numValues) noexcept; | ||
|
|
||
| /** Multiplies each source1 value by the correspinding source2 value, then stores it in the destination array. */ | ||
| static void JUCE_CALLTYPE multiply (FloatType* dest, const FloatType* src1, const FloatType* src2, CountType numValues) noexcept; | ||
|
|
||
| /** Multiplies each of the destination values by a fixed multiplier. */ | ||
| static void JUCE_CALLTYPE multiply (FloatType* dest, FloatType multiplier, CountType numValues) noexcept; | ||
|
|
||
| /** Multiplies each of the source values by a fixed multiplier and stores the result in the destination array. */ | ||
| static void JUCE_CALLTYPE multiply (FloatType* dest, const FloatType* src, FloatType multiplier, CountType num) noexcept; | ||
|
|
||
| /** Copies a source vector to a destination, negating each value. */ | ||
| static void JUCE_CALLTYPE negate (FloatType* dest, const FloatType* src, CountType numValues) noexcept; | ||
|
|
||
| /** Copies a source vector to a destination, taking the absolute of each value. */ | ||
| static void JUCE_CALLTYPE abs (FloatType* dest, const FloatType* src, CountType numValues) noexcept; | ||
|
|
||
| /** Each element of dest will be the minimum of the corresponding element of the source array and the given comp value. */ | ||
| static void JUCE_CALLTYPE min (FloatType* dest, const FloatType* src, FloatType comp, CountType num) noexcept; | ||
|
|
||
| /** Each element of dest will be the minimum of the corresponding source1 and source2 values. */ | ||
| static void JUCE_CALLTYPE min (FloatType* dest, const FloatType* src1, const FloatType* src2, CountType num) noexcept; | ||
|
|
||
| /** Each element of dest will be the maximum of the corresponding element of the source array and the given comp value. */ | ||
| static void JUCE_CALLTYPE max (FloatType* dest, const FloatType* src, FloatType comp, CountType num) noexcept; | ||
|
|
||
| /** Each element of dest will be the maximum of the corresponding source1 and source2 values. */ | ||
| static void JUCE_CALLTYPE max (FloatType* dest, const FloatType* src1, const FloatType* src2, CountType num) noexcept; | ||
|
|
||
| /** Each element of dest is calculated by hard clipping the corresponding src element so that it is in the range specified by the arguments low and high. */ | ||
| static void JUCE_CALLTYPE clip (FloatType* dest, const FloatType* src, FloatType low, FloatType high, CountType num) noexcept; | ||
|
|
||
| /** Finds the minimum and maximum values in the given array. */ | ||
| static Range<FloatType> JUCE_CALLTYPE findMinAndMax (const FloatType* src, CountType numValues) noexcept; | ||
|
|
||
| /** Finds the minimum value in the given array. */ | ||
| static FloatType JUCE_CALLTYPE findMinimum (const FloatType* src, CountType numValues) noexcept; | ||
|
|
||
| /** Finds the maximum value in the given array. */ | ||
| static FloatType JUCE_CALLTYPE findMaximum (const FloatType* src, CountType numValues) noexcept; | ||
| }; | ||
|
|
||
| #if ! DOXYGEN | ||
| namespace detail | ||
| { | ||
|
|
||
| template <typename...> | ||
| struct NameForwarder; | ||
|
|
||
| template <typename Head> | ||
| struct NameForwarder<Head> : Head {}; | ||
|
|
||
| template <typename Head, typename... Tail> | ||
| struct NameForwarder<Head, Tail...> : Head, NameForwarder<Tail...> | ||
| { | ||
| using Head::clear; | ||
| using NameForwarder<Tail...>::clear; | ||
|
|
||
| using Head::fill; | ||
| using NameForwarder<Tail...>::fill; | ||
|
|
||
| using Head::copy; | ||
| using NameForwarder<Tail...>::copy; | ||
|
|
||
| using Head::copyWithMultiply; | ||
| using NameForwarder<Tail...>::copyWithMultiply; | ||
|
|
||
| using Head::add; | ||
| using NameForwarder<Tail...>::add; | ||
|
|
||
| using Head::subtract; | ||
| using NameForwarder<Tail...>::subtract; | ||
|
|
||
| using Head::addWithMultiply; | ||
| using NameForwarder<Tail...>::addWithMultiply; | ||
|
|
||
| using Head::subtractWithMultiply; | ||
| using NameForwarder<Tail...>::subtractWithMultiply; | ||
|
|
||
| using Head::multiply; | ||
| using NameForwarder<Tail...>::multiply; | ||
|
|
||
| using Head::negate; | ||
| using NameForwarder<Tail...>::negate; | ||
|
|
||
| using Head::abs; | ||
| using NameForwarder<Tail...>::abs; | ||
|
|
||
| using Head::min; | ||
| using NameForwarder<Tail...>::min; | ||
|
|
||
| using Head::max; | ||
| using NameForwarder<Tail...>::max; | ||
|
|
||
| using Head::clip; | ||
| using NameForwarder<Tail...>::clip; | ||
|
|
||
| using Head::findMinAndMax; | ||
| using NameForwarder<Tail...>::findMinAndMax; | ||
|
|
||
| using Head::findMinimum; | ||
| using NameForwarder<Tail...>::findMinimum; | ||
|
|
||
| using Head::findMaximum; | ||
| using NameForwarder<Tail...>::findMaximum; | ||
| }; | ||
|
|
||
| } // namespace detail | ||
| #endif | ||
|
|
||
| //============================================================================== | ||
| /** | ||
| A collection of simple vector operations on arrays of floating point numbers, | ||
| accelerated with SIMD instructions where possible and providing all methods | ||
| from FloatVectorOperationsBase. | ||
| @see FloatVectorOperationsBase | ||
| @tags{Audio} | ||
| */ | ||
| class JUCE_API FloatVectorOperations : public detail::NameForwarder<FloatVectorOperationsBase<float, int>, | ||
| FloatVectorOperationsBase<float, size_t>, | ||
| FloatVectorOperationsBase<double, int>, | ||
| FloatVectorOperationsBase<double, size_t>> | ||
| { | ||
| public: | ||
| static void JUCE_CALLTYPE convertFixedToFloat (float* dest, const int* src, float multiplier, int num) noexcept; | ||
|
|
||
| static void JUCE_CALLTYPE convertFixedToFloat (float* dest, const int* src, float multiplier, size_t num) noexcept; | ||
|
|
||
| /** This method enables or disables the SSE/NEON flush-to-zero mode. */ | ||
| static void JUCE_CALLTYPE enableFlushToZeroMode (bool shouldEnable) noexcept; | ||
|
|
||
| /** On Intel CPUs, this method enables the SSE flush-to-zero and denormalised-are-zero modes. | ||
| This effectively sets the DAZ and FZ bits of the MXCSR register. On arm CPUs this will | ||
| enable flush to zero mode. | ||
| It's a convenient thing to call before audio processing code where you really want to | ||
| avoid denormalisation performance hits. | ||
| */ | ||
| static void JUCE_CALLTYPE disableDenormalisedNumberSupport (bool shouldDisable = true) noexcept; | ||
|
|
||
| /** This method returns true if denormals are currently disabled. */ | ||
| static bool JUCE_CALLTYPE areDenormalsDisabled() noexcept; | ||
|
|
||
| private: | ||
| friend ScopedNoDenormals; | ||
|
|
||
| static intptr_t JUCE_CALLTYPE getFpStatusRegister() noexcept; | ||
| static void JUCE_CALLTYPE setFpStatusRegister (intptr_t) noexcept; | ||
| }; | ||
|
|
||
| //============================================================================== | ||
| /** | ||
| Helper class providing an RAII-based mechanism for temporarily disabling | ||
| denormals on your CPU. | ||
| @tags{Audio} | ||
| */ | ||
| class ScopedNoDenormals | ||
| { | ||
| public: | ||
| ScopedNoDenormals() noexcept; | ||
| ~ScopedNoDenormals() noexcept; | ||
|
|
||
| private: | ||
| #if JUCE_USE_SSE_INTRINSICS || (JUCE_USE_ARM_NEON || defined (__arm64__) || defined (__aarch64__)) | ||
| intptr_t fpsr; | ||
| #endif | ||
| }; | ||
|
|
||
| } // namespace juce |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,101 @@ | ||
| /* | ||
| ============================================================================== | ||
| This file is part of the JUCE library. | ||
| Copyright (c) 2022 - Raw Material Software Limited | ||
| JUCE is an open source library subject to commercial or open-source | ||
| licensing. | ||
| The code included in this file is provided under the terms of the ISC license | ||
| http://www.isc.org/downloads/software-support-policy/isc-license. Permission | ||
| To use, copy, modify, and/or distribute this software for any purpose with or | ||
| without fee is hereby granted provided that the above copyright notice and | ||
| this permission notice appear in all copies. | ||
| JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | ||
| EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | ||
| DISCLAIMED. | ||
| ============================================================================== | ||
| */ | ||
|
|
||
| #ifdef JUCE_AUDIO_BASICS_H_INCLUDED | ||
| /* When you add this cpp file to your project, you mustn't include it in a file where you've | ||
| already included any other headers - just put it inside a file on its own, possibly with your config | ||
| flags preceding it, but don't include anything else. That also includes avoiding any automatic prefix | ||
| header files that the compiler may be using. | ||
| */ | ||
| #error "Incorrect use of JUCE cpp file" | ||
| #endif | ||
|
|
||
| #include "juce_audio_basics.h" | ||
|
|
||
| #include <juce_core/containers/juce_Optional.h> | ||
|
|
||
| #if JUCE_MINGW && ! defined (alloca) | ||
| #define alloca __builtin_alloca | ||
| #endif | ||
|
|
||
| #if JUCE_USE_SSE_INTRINSICS | ||
| #include <emmintrin.h> | ||
| #endif | ||
|
|
||
| #ifndef JUCE_USE_VDSP_FRAMEWORK | ||
| #define JUCE_USE_VDSP_FRAMEWORK 1 | ||
| #endif | ||
|
|
||
| #if (JUCE_MAC || JUCE_IOS) && JUCE_USE_VDSP_FRAMEWORK | ||
| #include <Accelerate/Accelerate.h> | ||
| #else | ||
| #undef JUCE_USE_VDSP_FRAMEWORK | ||
| #endif | ||
|
|
||
| #if JUCE_USE_ARM_NEON | ||
| #include <arm_neon.h> | ||
| #endif | ||
|
|
||
| #include "buffers/juce_AudioDataConverters.cpp" | ||
| #include "buffers/juce_FloatVectorOperations.cpp" | ||
| #include "buffers/juce_AudioChannelSet.cpp" | ||
| #include "buffers/juce_AudioProcessLoadMeasurer.cpp" | ||
| #include "utilities/juce_IIRFilter.cpp" | ||
| #include "utilities/juce_LagrangeInterpolator.cpp" | ||
| #include "utilities/juce_WindowedSincInterpolator.cpp" | ||
| #include "utilities/juce_Interpolators.cpp" | ||
| #include "utilities/juce_SmoothedValue.cpp" | ||
| #include "midi/juce_MidiBuffer.cpp" | ||
| #include "midi/juce_MidiFile.cpp" | ||
| #include "midi/juce_MidiKeyboardState.cpp" | ||
| #include "midi/juce_MidiMessage.cpp" | ||
| #include "midi/juce_MidiMessageSequence.cpp" | ||
| #include "midi/juce_MidiRPN.cpp" | ||
| #include "mpe/juce_MPEValue.cpp" | ||
| #include "mpe/juce_MPENote.cpp" | ||
| #include "mpe/juce_MPEZoneLayout.cpp" | ||
| #include "mpe/juce_MPEInstrument.cpp" | ||
| #include "mpe/juce_MPEMessages.cpp" | ||
| #include "mpe/juce_MPESynthesiserBase.cpp" | ||
| #include "mpe/juce_MPESynthesiserVoice.cpp" | ||
| #include "mpe/juce_MPESynthesiser.cpp" | ||
| #include "mpe/juce_MPEUtils.cpp" | ||
| #include "sources/juce_BufferingAudioSource.cpp" | ||
| #include "sources/juce_ChannelRemappingAudioSource.cpp" | ||
| #include "sources/juce_IIRFilterAudioSource.cpp" | ||
| #include "sources/juce_MemoryAudioSource.cpp" | ||
| #include "sources/juce_MixerAudioSource.cpp" | ||
| #include "sources/juce_ResamplingAudioSource.cpp" | ||
| #include "sources/juce_ReverbAudioSource.cpp" | ||
| #include "sources/juce_ToneGeneratorAudioSource.cpp" | ||
| #include "synthesisers/juce_Synthesiser.cpp" | ||
|
|
||
| #include "midi/ump/juce_UMP.h" | ||
| #include "midi/ump/juce_UMPUtils.cpp" | ||
| #include "midi/ump/juce_UMPView.cpp" | ||
| #include "midi/ump/juce_UMPSysEx7.cpp" | ||
| #include "midi/ump/juce_UMPMidi1ToMidi2DefaultTranslator.cpp" | ||
|
|
||
| #if JUCE_UNIT_TESTS | ||
| #include "utilities/juce_ADSR_test.cpp" | ||
| #include "midi/ump/juce_UMP_test.cpp" | ||
| #endif |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,123 @@ | ||
| /* | ||
| ============================================================================== | ||
| This file is part of the JUCE library. | ||
| Copyright (c) 2022 - Raw Material Software Limited | ||
| JUCE is an open source library subject to commercial or open-source | ||
| licensing. | ||
| The code included in this file is provided under the terms of the ISC license | ||
| http://www.isc.org/downloads/software-support-policy/isc-license. Permission | ||
| To use, copy, modify, and/or distribute this software for any purpose with or | ||
| without fee is hereby granted provided that the above copyright notice and | ||
| this permission notice appear in all copies. | ||
| JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | ||
| EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | ||
| DISCLAIMED. | ||
| ============================================================================== | ||
| */ | ||
|
|
||
|
|
||
| /******************************************************************************* | ||
| The block below describes the properties of this module, and is read by | ||
| the Projucer to automatically generate project code that uses it. | ||
| For details about the syntax and how to create or use a module, see the | ||
| JUCE Module Format.md file. | ||
| BEGIN_JUCE_MODULE_DECLARATION | ||
| ID: juce_audio_basics | ||
| vendor: juce | ||
| version: 6.1.6 | ||
| name: JUCE audio and MIDI data classes | ||
| description: Classes for audio buffer manipulation, midi message handling, synthesis, etc. | ||
| website: http://www.juce.com/juce | ||
| license: ISC | ||
| minimumCppStandard: 14 | ||
| dependencies: juce_core | ||
| OSXFrameworks: Accelerate | ||
| iOSFrameworks: Accelerate | ||
| END_JUCE_MODULE_DECLARATION | ||
| *******************************************************************************/ | ||
|
|
||
|
|
||
| #pragma once | ||
| #define JUCE_AUDIO_BASICS_H_INCLUDED | ||
|
|
||
| #include <juce_core/juce_core.h> | ||
|
|
||
| //============================================================================== | ||
| #undef Complex // apparently some C libraries actually define these symbols (!) | ||
| #undef Factor | ||
|
|
||
| //============================================================================== | ||
| #if JUCE_MINGW && ! defined (__SSE2__) | ||
| #define JUCE_USE_SSE_INTRINSICS 0 | ||
| #endif | ||
|
|
||
| #ifndef JUCE_USE_SSE_INTRINSICS | ||
| #define JUCE_USE_SSE_INTRINSICS 1 | ||
| #endif | ||
|
|
||
| #if ! JUCE_INTEL | ||
| #undef JUCE_USE_SSE_INTRINSICS | ||
| #endif | ||
|
|
||
| #if __ARM_NEON__ && ! (JUCE_USE_VDSP_FRAMEWORK || defined (JUCE_USE_ARM_NEON)) | ||
| #define JUCE_USE_ARM_NEON 1 | ||
| #endif | ||
|
|
||
| #if TARGET_IPHONE_SIMULATOR | ||
| #ifdef JUCE_USE_ARM_NEON | ||
| #undef JUCE_USE_ARM_NEON | ||
| #endif | ||
| #define JUCE_USE_ARM_NEON 0 | ||
| #endif | ||
|
|
||
| //============================================================================== | ||
| #include "buffers/juce_AudioDataConverters.h" | ||
| #include "buffers/juce_FloatVectorOperations.h" | ||
| #include "buffers/juce_AudioSampleBuffer.h" | ||
| #include "buffers/juce_AudioChannelSet.h" | ||
| #include "buffers/juce_AudioProcessLoadMeasurer.h" | ||
| #include "utilities/juce_Decibels.h" | ||
| #include "utilities/juce_IIRFilter.h" | ||
| #include "utilities/juce_GenericInterpolator.h" | ||
| #include "utilities/juce_Interpolators.h" | ||
| #include "utilities/juce_SmoothedValue.h" | ||
| #include "utilities/juce_Reverb.h" | ||
| #include "utilities/juce_ADSR.h" | ||
| #include "midi/juce_MidiMessage.h" | ||
| #include "midi/juce_MidiBuffer.h" | ||
| #include "midi/juce_MidiMessageSequence.h" | ||
| #include "midi/juce_MidiFile.h" | ||
| #include "midi/juce_MidiKeyboardState.h" | ||
| #include "midi/juce_MidiRPN.h" | ||
| #include "mpe/juce_MPEValue.h" | ||
| #include "mpe/juce_MPENote.h" | ||
| #include "mpe/juce_MPEZoneLayout.h" | ||
| #include "mpe/juce_MPEInstrument.h" | ||
| #include "mpe/juce_MPEMessages.h" | ||
| #include "mpe/juce_MPESynthesiserBase.h" | ||
| #include "mpe/juce_MPESynthesiserVoice.h" | ||
| #include "mpe/juce_MPESynthesiser.h" | ||
| #include "mpe/juce_MPEUtils.h" | ||
| #include "sources/juce_AudioSource.h" | ||
| #include "sources/juce_PositionableAudioSource.h" | ||
| #include "sources/juce_BufferingAudioSource.h" | ||
| #include "sources/juce_ChannelRemappingAudioSource.h" | ||
| #include "sources/juce_IIRFilterAudioSource.h" | ||
| #include "sources/juce_MemoryAudioSource.h" | ||
| #include "sources/juce_MixerAudioSource.h" | ||
| #include "sources/juce_ResamplingAudioSource.h" | ||
| #include "sources/juce_ReverbAudioSource.h" | ||
| #include "sources/juce_ToneGeneratorAudioSource.h" | ||
| #include "synthesisers/juce_Synthesiser.h" | ||
| #include "audio_play_head/juce_AudioPlayHead.h" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| /* | ||
| ============================================================================== | ||
| This file is part of the JUCE library. | ||
| Copyright (c) 2022 - Raw Material Software Limited | ||
| JUCE is an open source library subject to commercial or open-source | ||
| licensing. | ||
| The code included in this file is provided under the terms of the ISC license | ||
| http://www.isc.org/downloads/software-support-policy/isc-license. Permission | ||
| To use, copy, modify, and/or distribute this software for any purpose with or | ||
| without fee is hereby granted provided that the above copyright notice and | ||
| this permission notice appear in all copies. | ||
| JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | ||
| EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | ||
| DISCLAIMED. | ||
| ============================================================================== | ||
| */ | ||
|
|
||
| #include "juce_audio_basics.cpp" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,320 @@ | ||
| /* | ||
| ============================================================================== | ||
| This file is part of the JUCE library. | ||
| Copyright (c) 2022 - Raw Material Software Limited | ||
| JUCE is an open source library subject to commercial or open-source | ||
| licensing. | ||
| The code included in this file is provided under the terms of the ISC license | ||
| http://www.isc.org/downloads/software-support-policy/isc-license. Permission | ||
| To use, copy, modify, and/or distribute this software for any purpose with or | ||
| without fee is hereby granted provided that the above copyright notice and | ||
| this permission notice appear in all copies. | ||
| JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | ||
| EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | ||
| DISCLAIMED. | ||
| ============================================================================== | ||
| */ | ||
|
|
||
| namespace juce | ||
| { | ||
|
|
||
| namespace MidiBufferHelpers | ||
| { | ||
| inline int getEventTime (const void* d) noexcept | ||
| { | ||
| return readUnaligned<int32> (d); | ||
| } | ||
|
|
||
| inline uint16 getEventDataSize (const void* d) noexcept | ||
| { | ||
| return readUnaligned<uint16> (static_cast<const char*> (d) + sizeof (int32)); | ||
| } | ||
|
|
||
| inline uint16 getEventTotalSize (const void* d) noexcept | ||
| { | ||
| return (uint16) (getEventDataSize (d) + sizeof (int32) + sizeof (uint16)); | ||
| } | ||
|
|
||
| static int findActualEventLength (const uint8* data, int maxBytes) noexcept | ||
| { | ||
| auto byte = (unsigned int) *data; | ||
|
|
||
| if (byte == 0xf0 || byte == 0xf7) | ||
| { | ||
| int i = 1; | ||
|
|
||
| while (i < maxBytes) | ||
| if (data[i++] == 0xf7) | ||
| break; | ||
|
|
||
| return i; | ||
| } | ||
|
|
||
| if (byte == 0xff) | ||
| { | ||
| if (maxBytes == 1) | ||
| return 1; | ||
|
|
||
| const auto var = MidiMessage::readVariableLengthValue (data + 1, maxBytes - 1); | ||
| return jmin (maxBytes, var.value + 2 + var.bytesUsed); | ||
| } | ||
|
|
||
| if (byte >= 0x80) | ||
| return jmin (maxBytes, MidiMessage::getMessageLengthFromFirstByte ((uint8) byte)); | ||
|
|
||
| return 0; | ||
| } | ||
|
|
||
| static uint8* findEventAfter (uint8* d, uint8* endData, int samplePosition) noexcept | ||
| { | ||
| while (d < endData && getEventTime (d) <= samplePosition) | ||
| d += getEventTotalSize (d); | ||
|
|
||
| return d; | ||
| } | ||
| } | ||
|
|
||
| //============================================================================== | ||
| MidiBufferIterator& MidiBufferIterator::operator++() noexcept | ||
| { | ||
| data += sizeof (int32) + sizeof (uint16) + size_t (MidiBufferHelpers::getEventDataSize (data)); | ||
| return *this; | ||
| } | ||
|
|
||
| MidiBufferIterator MidiBufferIterator::operator++ (int) noexcept | ||
| { | ||
| auto copy = *this; | ||
| ++(*this); | ||
| return copy; | ||
| } | ||
|
|
||
| MidiBufferIterator::reference MidiBufferIterator::operator*() const noexcept | ||
| { | ||
| return { data + sizeof (int32) + sizeof (uint16), | ||
| MidiBufferHelpers::getEventDataSize (data), | ||
| MidiBufferHelpers::getEventTime (data) }; | ||
| } | ||
|
|
||
| //============================================================================== | ||
| MidiBuffer::MidiBuffer (const MidiMessage& message) noexcept | ||
| { | ||
| addEvent (message, 0); | ||
| } | ||
|
|
||
| void MidiBuffer::swapWith (MidiBuffer& other) noexcept { data.swapWith (other.data); } | ||
| void MidiBuffer::clear() noexcept { data.clearQuick(); } | ||
| void MidiBuffer::ensureSize (size_t minimumNumBytes) { data.ensureStorageAllocated ((int) minimumNumBytes); } | ||
| bool MidiBuffer::isEmpty() const noexcept { return data.size() == 0; } | ||
|
|
||
| void MidiBuffer::clear (int startSample, int numSamples) | ||
| { | ||
| auto start = MidiBufferHelpers::findEventAfter (data.begin(), data.end(), startSample - 1); | ||
| auto end = MidiBufferHelpers::findEventAfter (start, data.end(), startSample + numSamples - 1); | ||
|
|
||
| data.removeRange ((int) (start - data.begin()), (int) (end - start)); | ||
| } | ||
|
|
||
| bool MidiBuffer::addEvent (const MidiMessage& m, int sampleNumber) | ||
| { | ||
| return addEvent (m.getRawData(), m.getRawDataSize(), sampleNumber); | ||
| } | ||
|
|
||
| bool MidiBuffer::addEvent (const void* newData, int maxBytes, int sampleNumber) | ||
| { | ||
| auto numBytes = MidiBufferHelpers::findActualEventLength (static_cast<const uint8*> (newData), maxBytes); | ||
|
|
||
| if (numBytes <= 0) | ||
| return true; | ||
|
|
||
| if (std::numeric_limits<uint16>::max() < numBytes) | ||
| { | ||
| // This method only supports messages smaller than (1 << 16) bytes | ||
| return false; | ||
| } | ||
|
|
||
| auto newItemSize = (size_t) numBytes + sizeof (int32) + sizeof (uint16); | ||
| auto offset = (int) (MidiBufferHelpers::findEventAfter (data.begin(), data.end(), sampleNumber) - data.begin()); | ||
|
|
||
| data.insertMultiple (offset, 0, (int) newItemSize); | ||
|
|
||
| auto* d = data.begin() + offset; | ||
| writeUnaligned<int32> (d, sampleNumber); | ||
| d += sizeof (int32); | ||
| writeUnaligned<uint16> (d, static_cast<uint16> (numBytes)); | ||
| d += sizeof (uint16); | ||
| memcpy (d, newData, (size_t) numBytes); | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| void MidiBuffer::addEvents (const MidiBuffer& otherBuffer, | ||
| int startSample, int numSamples, int sampleDeltaToAdd) | ||
| { | ||
| for (auto i = otherBuffer.findNextSamplePosition (startSample); i != otherBuffer.cend(); ++i) | ||
| { | ||
| const auto metadata = *i; | ||
|
|
||
| if (metadata.samplePosition >= startSample + numSamples && numSamples >= 0) | ||
| break; | ||
|
|
||
| addEvent (metadata.data, metadata.numBytes, metadata.samplePosition + sampleDeltaToAdd); | ||
| } | ||
| } | ||
|
|
||
| int MidiBuffer::getNumEvents() const noexcept | ||
| { | ||
| int n = 0; | ||
| auto end = data.end(); | ||
|
|
||
| for (auto d = data.begin(); d < end; ++n) | ||
| d += MidiBufferHelpers::getEventTotalSize (d); | ||
|
|
||
| return n; | ||
| } | ||
|
|
||
| int MidiBuffer::getFirstEventTime() const noexcept | ||
| { | ||
| return data.size() > 0 ? MidiBufferHelpers::getEventTime (data.begin()) : 0; | ||
| } | ||
|
|
||
| int MidiBuffer::getLastEventTime() const noexcept | ||
| { | ||
| if (data.size() == 0) | ||
| return 0; | ||
|
|
||
| auto endData = data.end(); | ||
|
|
||
| for (auto d = data.begin();;) | ||
| { | ||
| auto nextOne = d + MidiBufferHelpers::getEventTotalSize (d); | ||
|
|
||
| if (nextOne >= endData) | ||
| return MidiBufferHelpers::getEventTime (d); | ||
|
|
||
| d = nextOne; | ||
| } | ||
| } | ||
|
|
||
| MidiBufferIterator MidiBuffer::findNextSamplePosition (int samplePosition) const noexcept | ||
| { | ||
| return std::find_if (cbegin(), cend(), [&] (const MidiMessageMetadata& metadata) noexcept | ||
| { | ||
| return metadata.samplePosition >= samplePosition; | ||
| }); | ||
| } | ||
|
|
||
| //============================================================================== | ||
| JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") | ||
| JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996) | ||
|
|
||
| MidiBuffer::Iterator::Iterator (const MidiBuffer& b) noexcept | ||
| : buffer (b), iterator (b.data.begin()) | ||
| { | ||
| } | ||
|
|
||
| void MidiBuffer::Iterator::setNextSamplePosition (int samplePosition) noexcept | ||
| { | ||
| iterator = buffer.findNextSamplePosition (samplePosition); | ||
| } | ||
|
|
||
| bool MidiBuffer::Iterator::getNextEvent (const uint8*& midiData, int& numBytes, int& samplePosition) noexcept | ||
| { | ||
| if (iterator == buffer.cend()) | ||
| return false; | ||
|
|
||
| const auto metadata = *iterator++; | ||
| midiData = metadata.data; | ||
| numBytes = metadata.numBytes; | ||
| samplePosition = metadata.samplePosition; | ||
| return true; | ||
| } | ||
|
|
||
| bool MidiBuffer::Iterator::getNextEvent (MidiMessage& result, int& samplePosition) noexcept | ||
| { | ||
| if (iterator == buffer.cend()) | ||
| return false; | ||
|
|
||
| const auto metadata = *iterator++; | ||
| result = metadata.getMessage(); | ||
| samplePosition = metadata.samplePosition; | ||
| return true; | ||
| } | ||
|
|
||
| JUCE_END_IGNORE_WARNINGS_MSVC | ||
| JUCE_END_IGNORE_WARNINGS_GCC_LIKE | ||
|
|
||
| //============================================================================== | ||
| //============================================================================== | ||
| #if JUCE_UNIT_TESTS | ||
|
|
||
| struct MidiBufferTest : public UnitTest | ||
| { | ||
| MidiBufferTest() | ||
| : UnitTest ("MidiBuffer", UnitTestCategories::midi) | ||
| {} | ||
|
|
||
| void runTest() override | ||
| { | ||
| beginTest ("Clear messages"); | ||
| { | ||
| const auto message = MidiMessage::noteOn (1, 64, 0.5f); | ||
|
|
||
| const auto testBuffer = [&] | ||
| { | ||
| MidiBuffer buffer; | ||
| buffer.addEvent (message, 0); | ||
| buffer.addEvent (message, 10); | ||
| buffer.addEvent (message, 20); | ||
| buffer.addEvent (message, 30); | ||
| return buffer; | ||
| }(); | ||
|
|
||
| { | ||
| auto buffer = testBuffer; | ||
| buffer.clear (10, 0); | ||
| expectEquals (buffer.getNumEvents(), 4); | ||
| } | ||
|
|
||
| { | ||
| auto buffer = testBuffer; | ||
| buffer.clear (10, 1); | ||
| expectEquals (buffer.getNumEvents(), 3); | ||
| } | ||
|
|
||
| { | ||
| auto buffer = testBuffer; | ||
| buffer.clear (10, 10); | ||
| expectEquals (buffer.getNumEvents(), 3); | ||
| } | ||
|
|
||
| { | ||
| auto buffer = testBuffer; | ||
| buffer.clear (10, 20); | ||
| expectEquals (buffer.getNumEvents(), 2); | ||
| } | ||
|
|
||
| { | ||
| auto buffer = testBuffer; | ||
| buffer.clear (10, 30); | ||
| expectEquals (buffer.getNumEvents(), 1); | ||
| } | ||
|
|
||
| { | ||
| auto buffer = testBuffer; | ||
| buffer.clear (10, 300); | ||
| expectEquals (buffer.getNumEvents(), 1); | ||
| } | ||
| } | ||
| } | ||
| }; | ||
|
|
||
| static MidiBufferTest midiBufferTest; | ||
|
|
||
| #endif | ||
|
|
||
| } // namespace juce |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,345 @@ | ||
| /* | ||
| ============================================================================== | ||
| This file is part of the JUCE library. | ||
| Copyright (c) 2022 - Raw Material Software Limited | ||
| JUCE is an open source library subject to commercial or open-source | ||
| licensing. | ||
| The code included in this file is provided under the terms of the ISC license | ||
| http://www.isc.org/downloads/software-support-policy/isc-license. Permission | ||
| To use, copy, modify, and/or distribute this software for any purpose with or | ||
| without fee is hereby granted provided that the above copyright notice and | ||
| this permission notice appear in all copies. | ||
| JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | ||
| EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | ||
| DISCLAIMED. | ||
| ============================================================================== | ||
| */ | ||
|
|
||
| namespace juce | ||
| { | ||
|
|
||
| //============================================================================== | ||
| /** | ||
| A view of MIDI message data stored in a contiguous buffer. | ||
| Instances of this class do *not* own the midi data bytes that they point to. | ||
| Instead, they expect the midi data to live in a separate buffer that outlives | ||
| the MidiMessageMetadata instance. | ||
| @tags{Audio} | ||
| */ | ||
| struct MidiMessageMetadata final | ||
| { | ||
| MidiMessageMetadata() noexcept = default; | ||
|
|
||
| MidiMessageMetadata (const uint8* dataIn, int numBytesIn, int positionIn) noexcept | ||
| : data (dataIn), numBytes (numBytesIn), samplePosition (positionIn) | ||
| { | ||
| } | ||
|
|
||
| /** Constructs a new MidiMessage instance from the data that this object is viewing. | ||
| Note that MidiMessage owns its data storage, whereas MidiMessageMetadata does not. | ||
| */ | ||
| MidiMessage getMessage() const { return MidiMessage (data, numBytes, samplePosition); } | ||
|
|
||
| /** Pointer to the first byte of a MIDI message. */ | ||
| const uint8* data = nullptr; | ||
|
|
||
| /** The number of bytes in the MIDI message. */ | ||
| int numBytes = 0; | ||
|
|
||
| /** The MIDI message's timestamp. */ | ||
| int samplePosition = 0; | ||
| }; | ||
|
|
||
| //============================================================================== | ||
| /** | ||
| An iterator to move over contiguous raw MIDI data, which Allows iterating | ||
| over a MidiBuffer using C++11 range-for syntax. | ||
| In the following example, we log all three-byte messages in a midi buffer. | ||
| @code | ||
| void processBlock (AudioBuffer<float>&, MidiBuffer& midiBuffer) override | ||
| { | ||
| for (const MidiMessageMetadata metadata : midiBuffer) | ||
| if (metadata.numBytes == 3) | ||
| Logger::writeToLog (metadata.getMessage().getDescription()); | ||
| } | ||
| @endcode | ||
| @tags{Audio} | ||
| */ | ||
| class JUCE_API MidiBufferIterator | ||
| { | ||
| using Ptr = const uint8*; | ||
|
|
||
| public: | ||
| MidiBufferIterator() = default; | ||
|
|
||
| /** Constructs an iterator pointing at the message starting at the byte `dataIn`. | ||
| `dataIn` must point to the start of a valid MIDI message. If it does not, | ||
| calling other member functions on the iterator will result in undefined | ||
| behaviour. | ||
| */ | ||
| explicit MidiBufferIterator (const uint8* dataIn) noexcept | ||
| : data (dataIn) | ||
| { | ||
| } | ||
|
|
||
| using difference_type = std::iterator_traits<Ptr>::difference_type; | ||
| using value_type = MidiMessageMetadata; | ||
| using reference = MidiMessageMetadata; | ||
| using pointer = void; | ||
| using iterator_category = std::input_iterator_tag; | ||
|
|
||
| /** Make this iterator point to the next message in the buffer. */ | ||
| MidiBufferIterator& operator++() noexcept; | ||
|
|
||
| /** Create a copy of this object, make this iterator point to the next message in | ||
| the buffer, then return the copy. | ||
| */ | ||
| MidiBufferIterator operator++ (int) noexcept; | ||
|
|
||
| /** Return true if this iterator points to the same message as another | ||
| iterator instance, otherwise return false. | ||
| */ | ||
| bool operator== (const MidiBufferIterator& other) const noexcept { return data == other.data; } | ||
|
|
||
| /** Return false if this iterator points to the same message as another | ||
| iterator instance, otherwise returns true. | ||
| */ | ||
| bool operator!= (const MidiBufferIterator& other) const noexcept { return ! operator== (other); } | ||
|
|
||
| /** Return an instance of MidiMessageMetadata which describes the message to which | ||
| the iterator is currently pointing. | ||
| */ | ||
| reference operator*() const noexcept; | ||
|
|
||
| private: | ||
| Ptr data = nullptr; | ||
| }; | ||
|
|
||
| //============================================================================== | ||
| /** | ||
| Holds a sequence of time-stamped midi events. | ||
| Analogous to the AudioBuffer, this holds a set of midi events with | ||
| integer time-stamps. The buffer is kept sorted in order of the time-stamps. | ||
| If you're working with a sequence of midi events that may need to be manipulated | ||
| or read/written to a midi file, then MidiMessageSequence is probably a more | ||
| appropriate container. MidiBuffer is designed for lower-level streams of raw | ||
| midi data. | ||
| @see MidiMessage | ||
| @tags{Audio} | ||
| */ | ||
| class JUCE_API MidiBuffer | ||
| { | ||
| public: | ||
| //============================================================================== | ||
| /** Creates an empty MidiBuffer. */ | ||
| MidiBuffer() noexcept = default; | ||
|
|
||
| /** Creates a MidiBuffer containing a single midi message. */ | ||
| explicit MidiBuffer (const MidiMessage& message) noexcept; | ||
|
|
||
| //============================================================================== | ||
| /** Removes all events from the buffer. */ | ||
| void clear() noexcept; | ||
|
|
||
| /** Removes all events between two times from the buffer. | ||
| All events for which (start <= event position < start + numSamples) will | ||
| be removed. | ||
| */ | ||
| void clear (int start, int numSamples); | ||
|
|
||
| /** Returns true if the buffer is empty. | ||
| To actually retrieve the events, use a MidiBufferIterator object | ||
| */ | ||
| bool isEmpty() const noexcept; | ||
|
|
||
| /** Counts the number of events in the buffer. | ||
| This is actually quite a slow operation, as it has to iterate through all | ||
| the events, so you might prefer to call isEmpty() if that's all you need | ||
| to know. | ||
| */ | ||
| int getNumEvents() const noexcept; | ||
|
|
||
| /** Adds an event to the buffer. | ||
| The sample number will be used to determine the position of the event in | ||
| the buffer, which is always kept sorted. The MidiMessage's timestamp is | ||
| ignored. | ||
| If an event is added whose sample position is the same as one or more events | ||
| already in the buffer, the new event will be placed after the existing ones. | ||
| To retrieve events, use a MidiBufferIterator object. | ||
| Returns true on success, or false on failure. | ||
| */ | ||
| bool addEvent (const MidiMessage& midiMessage, int sampleNumber); | ||
|
|
||
| /** Adds an event to the buffer from raw midi data. | ||
| The sample number will be used to determine the position of the event in | ||
| the buffer, which is always kept sorted. | ||
| If an event is added whose sample position is the same as one or more events | ||
| already in the buffer, the new event will be placed after the existing ones. | ||
| The event data will be inspected to calculate the number of bytes in length that | ||
| the midi event really takes up, so maxBytesOfMidiData may be longer than the data | ||
| that actually gets stored. E.g. if you pass in a note-on and a length of 4 bytes, | ||
| it'll actually only store 3 bytes. If the midi data is invalid, it might not | ||
| add an event at all. | ||
| To retrieve events, use a MidiBufferIterator object. | ||
| Returns true on success, or false on failure. | ||
| */ | ||
| bool addEvent (const void* rawMidiData, | ||
| int maxBytesOfMidiData, | ||
| int sampleNumber); | ||
|
|
||
| /** Adds some events from another buffer to this one. | ||
| @param otherBuffer the buffer containing the events you want to add | ||
| @param startSample the lowest sample number in the source buffer for which | ||
| events should be added. Any source events whose timestamp is | ||
| less than this will be ignored | ||
| @param numSamples the valid range of samples from the source buffer for which | ||
| events should be added - i.e. events in the source buffer whose | ||
| timestamp is greater than or equal to (startSample + numSamples) | ||
| will be ignored. If this value is less than 0, all events after | ||
| startSample will be taken. | ||
| @param sampleDeltaToAdd a value which will be added to the source timestamps of the events | ||
| that are added to this buffer | ||
| */ | ||
| void addEvents (const MidiBuffer& otherBuffer, | ||
| int startSample, | ||
| int numSamples, | ||
| int sampleDeltaToAdd); | ||
|
|
||
| /** Returns the sample number of the first event in the buffer. | ||
| If the buffer's empty, this will just return 0. | ||
| */ | ||
| int getFirstEventTime() const noexcept; | ||
|
|
||
| /** Returns the sample number of the last event in the buffer. | ||
| If the buffer's empty, this will just return 0. | ||
| */ | ||
| int getLastEventTime() const noexcept; | ||
|
|
||
| //============================================================================== | ||
| /** Exchanges the contents of this buffer with another one. | ||
| This is a quick operation, because no memory allocating or copying is done, it | ||
| just swaps the internal state of the two buffers. | ||
| */ | ||
| void swapWith (MidiBuffer&) noexcept; | ||
|
|
||
| /** Preallocates some memory for the buffer to use. | ||
| This helps to avoid needing to reallocate space when the buffer has messages | ||
| added to it. | ||
| */ | ||
| void ensureSize (size_t minimumNumBytes); | ||
|
|
||
| /** Get a read-only iterator pointing to the beginning of this buffer. */ | ||
| MidiBufferIterator begin() const noexcept { return cbegin(); } | ||
|
|
||
| /** Get a read-only iterator pointing one past the end of this buffer. */ | ||
| MidiBufferIterator end() const noexcept { return cend(); } | ||
|
|
||
| /** Get a read-only iterator pointing to the beginning of this buffer. */ | ||
| MidiBufferIterator cbegin() const noexcept { return MidiBufferIterator (data.begin()); } | ||
|
|
||
| /** Get a read-only iterator pointing one past the end of this buffer. */ | ||
| MidiBufferIterator cend() const noexcept { return MidiBufferIterator (data.end()); } | ||
|
|
||
| /** Get an iterator pointing to the first event with a timestamp greater-than or | ||
| equal-to `samplePosition`. | ||
| */ | ||
| MidiBufferIterator findNextSamplePosition (int samplePosition) const noexcept; | ||
|
|
||
| //============================================================================== | ||
| #ifndef DOXYGEN | ||
| /** This class is now deprecated in favour of MidiBufferIterator. | ||
| Used to iterate through the events in a MidiBuffer. | ||
| Note that altering the buffer while an iterator is using it will produce | ||
| undefined behaviour. | ||
| @see MidiBuffer | ||
| */ | ||
| class [[deprecated]] JUCE_API Iterator | ||
| { | ||
| public: | ||
| //============================================================================== | ||
| /** Creates an Iterator for this MidiBuffer. */ | ||
| Iterator (const MidiBuffer& b) noexcept; | ||
|
|
||
| //============================================================================== | ||
| /** Repositions the iterator so that the next event retrieved will be the first | ||
| one whose sample position is at greater than or equal to the given position. | ||
| */ | ||
| void setNextSamplePosition (int samplePosition) noexcept; | ||
|
|
||
| /** Retrieves a copy of the next event from the buffer. | ||
| @param result on return, this will be the message. The MidiMessage's timestamp | ||
| is set to the same value as samplePosition. | ||
| @param samplePosition on return, this will be the position of the event, as a | ||
| sample index in the buffer | ||
| @returns true if an event was found, or false if the iterator has reached | ||
| the end of the buffer | ||
| */ | ||
| bool getNextEvent (MidiMessage& result, | ||
| int& samplePosition) noexcept; | ||
|
|
||
| /** Retrieves the next event from the buffer. | ||
| @param midiData on return, this pointer will be set to a block of data containing | ||
| the midi message. Note that to make it fast, this is a pointer | ||
| directly into the MidiBuffer's internal data, so is only valid | ||
| temporarily until the MidiBuffer is altered. | ||
| @param numBytesOfMidiData on return, this is the number of bytes of data used by the | ||
| midi message | ||
| @param samplePosition on return, this will be the position of the event, as a | ||
| sample index in the buffer | ||
| @returns true if an event was found, or false if the iterator has reached | ||
| the end of the buffer | ||
| */ | ||
| bool getNextEvent (const uint8* &midiData, | ||
| int& numBytesOfMidiData, | ||
| int& samplePosition) noexcept; | ||
|
|
||
| private: | ||
| //============================================================================== | ||
| const MidiBuffer& buffer; | ||
| MidiBufferIterator iterator; | ||
| }; | ||
| #endif | ||
|
|
||
| /** The raw data holding this buffer. | ||
| Obviously access to this data is provided at your own risk. Its internal format could | ||
| change in future, so don't write code that relies on it! | ||
| */ | ||
| Array<uint8> data; | ||
|
|
||
| private: | ||
| JUCE_LEAK_DETECTOR (MidiBuffer) | ||
| }; | ||
|
|
||
| } // namespace juce |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,188 @@ | ||
| /* | ||
| ============================================================================== | ||
| This file is part of the JUCE library. | ||
| Copyright (c) 2022 - Raw Material Software Limited | ||
| JUCE is an open source library subject to commercial or open-source | ||
| licensing. | ||
| The code included in this file is provided under the terms of the ISC license | ||
| http://www.isc.org/downloads/software-support-policy/isc-license. Permission | ||
| To use, copy, modify, and/or distribute this software for any purpose with or | ||
| without fee is hereby granted provided that the above copyright notice and | ||
| this permission notice appear in all copies. | ||
| JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | ||
| EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | ||
| DISCLAIMED. | ||
| ============================================================================== | ||
| */ | ||
|
|
||
| namespace juce | ||
| { | ||
|
|
||
| //============================================================================== | ||
| /** | ||
| Helper class that takes chunks of incoming midi bytes, packages them into | ||
| messages, and dispatches them to a midi callback. | ||
| @tags{Audio} | ||
| */ | ||
| class MidiDataConcatenator | ||
| { | ||
| public: | ||
| MidiDataConcatenator (int initialBufferSize) | ||
| : pendingSysexData ((size_t) initialBufferSize) | ||
| { | ||
| } | ||
|
|
||
| void reset() | ||
| { | ||
| currentMessageLen = 0; | ||
| pendingSysexSize = 0; | ||
| pendingSysexTime = 0; | ||
| } | ||
|
|
||
| template <typename UserDataType, typename CallbackType> | ||
| void pushMidiData (const void* inputData, int numBytes, double time, | ||
| UserDataType* input, CallbackType& callback) | ||
| { | ||
| auto d = static_cast<const uint8*> (inputData); | ||
|
|
||
| while (numBytes > 0) | ||
| { | ||
| auto nextByte = *d; | ||
|
|
||
| if (pendingSysexSize != 0 || nextByte == 0xf0) | ||
| { | ||
| processSysex (d, numBytes, time, input, callback); | ||
| currentMessageLen = 0; | ||
| continue; | ||
| } | ||
|
|
||
| ++d; | ||
| --numBytes; | ||
|
|
||
| if (isRealtimeMessage (nextByte)) | ||
| { | ||
| callback.handleIncomingMidiMessage (input, MidiMessage (nextByte, time)); | ||
| // These can be embedded in the middle of a normal message, so we won't | ||
| // reset the currentMessageLen here. | ||
| continue; | ||
| } | ||
|
|
||
| if (isInitialByte (nextByte)) | ||
| { | ||
| currentMessage[0] = nextByte; | ||
| currentMessageLen = 1; | ||
| } | ||
| else if (currentMessageLen > 0 && currentMessageLen < 3) | ||
| { | ||
| currentMessage[currentMessageLen++] = nextByte; | ||
| } | ||
| else | ||
| { | ||
| // message is too long or invalid MIDI - abandon it and start again with the next byte | ||
| currentMessageLen = 0; | ||
| continue; | ||
| } | ||
|
|
||
| auto expectedLength = MidiMessage::getMessageLengthFromFirstByte (currentMessage[0]); | ||
|
|
||
| if (expectedLength == currentMessageLen) | ||
| { | ||
| callback.handleIncomingMidiMessage (input, MidiMessage (currentMessage, expectedLength, time)); | ||
| currentMessageLen = 1; // reset, but leave the first byte to use as the running status byte | ||
| } | ||
| } | ||
| } | ||
|
|
||
| private: | ||
| template <typename UserDataType, typename CallbackType> | ||
| void processSysex (const uint8*& d, int& numBytes, double time, | ||
| UserDataType* input, CallbackType& callback) | ||
| { | ||
| if (*d == 0xf0) | ||
| { | ||
| pendingSysexSize = 0; | ||
| pendingSysexTime = time; | ||
| } | ||
|
|
||
| pendingSysexData.ensureSize ((size_t) (pendingSysexSize + numBytes), false); | ||
| auto totalMessage = static_cast<uint8*> (pendingSysexData.getData()); | ||
| auto dest = totalMessage + pendingSysexSize; | ||
|
|
||
| do | ||
| { | ||
| if (pendingSysexSize > 0 && isStatusByte (*d)) | ||
| { | ||
| if (*d == 0xf7) | ||
| { | ||
| *dest++ = *d++; | ||
| ++pendingSysexSize; | ||
| --numBytes; | ||
| break; | ||
| } | ||
|
|
||
| if (*d >= 0xfa || *d == 0xf8) | ||
| { | ||
| callback.handleIncomingMidiMessage (input, MidiMessage (*d, time)); | ||
| ++d; | ||
| --numBytes; | ||
| } | ||
| else | ||
| { | ||
| pendingSysexSize = 0; | ||
| int used = 0; | ||
| const MidiMessage m (d, numBytes, used, 0, time); | ||
|
|
||
| if (used > 0) | ||
| { | ||
| callback.handleIncomingMidiMessage (input, m); | ||
| numBytes -= used; | ||
| d += used; | ||
| } | ||
|
|
||
| break; | ||
| } | ||
| } | ||
| else | ||
| { | ||
| *dest++ = *d++; | ||
| ++pendingSysexSize; | ||
| --numBytes; | ||
| } | ||
| } | ||
| while (numBytes > 0); | ||
|
|
||
| if (pendingSysexSize > 0) | ||
| { | ||
| if (totalMessage [pendingSysexSize - 1] == 0xf7) | ||
| { | ||
| callback.handleIncomingMidiMessage (input, MidiMessage (totalMessage, pendingSysexSize, pendingSysexTime)); | ||
| pendingSysexSize = 0; | ||
| } | ||
| else | ||
| { | ||
| callback.handlePartialSysexMessage (input, totalMessage, pendingSysexSize, pendingSysexTime); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| static bool isRealtimeMessage (uint8 byte) { return byte >= 0xf8 && byte <= 0xfe; } | ||
| static bool isStatusByte (uint8 byte) { return byte >= 0x80; } | ||
| static bool isInitialByte (uint8 byte) { return isStatusByte (byte) && byte != 0xf7; } | ||
|
|
||
| uint8 currentMessage[3]; | ||
| int currentMessageLen = 0; | ||
|
|
||
| MemoryBlock pendingSysexData; | ||
| double pendingSysexTime = 0; | ||
| int pendingSysexSize = 0; | ||
|
|
||
| JUCE_DECLARE_NON_COPYABLE (MidiDataConcatenator) | ||
| }; | ||
|
|
||
| } // namespace juce |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,198 @@ | ||
| /* | ||
| ============================================================================== | ||
| This file is part of the JUCE library. | ||
| Copyright (c) 2022 - Raw Material Software Limited | ||
| JUCE is an open source library subject to commercial or open-source | ||
| licensing. | ||
| The code included in this file is provided under the terms of the ISC license | ||
| http://www.isc.org/downloads/software-support-policy/isc-license. Permission | ||
| To use, copy, modify, and/or distribute this software for any purpose with or | ||
| without fee is hereby granted provided that the above copyright notice and | ||
| this permission notice appear in all copies. | ||
| JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | ||
| EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | ||
| DISCLAIMED. | ||
| ============================================================================== | ||
| */ | ||
|
|
||
| namespace juce | ||
| { | ||
|
|
||
| //============================================================================== | ||
| /** | ||
| Reads/writes standard midi format files. | ||
| To read a midi file, create a MidiFile object and call its readFrom() method. You | ||
| can then get the individual midi tracks from it using the getTrack() method. | ||
| To write a file, create a MidiFile object, add some MidiMessageSequence objects | ||
| to it using the addTrack() method, and then call its writeTo() method to stream | ||
| it out. | ||
| @see MidiMessageSequence | ||
| @tags{Audio} | ||
| */ | ||
| class JUCE_API MidiFile | ||
| { | ||
| public: | ||
| //============================================================================== | ||
| /** Creates an empty MidiFile object. */ | ||
| MidiFile(); | ||
|
|
||
| /** Creates a copy of another MidiFile. */ | ||
| MidiFile (const MidiFile&); | ||
|
|
||
| /** Copies from another MidiFile object */ | ||
| MidiFile& operator= (const MidiFile&); | ||
|
|
||
| /** Creates a copy of another MidiFile. */ | ||
| MidiFile (MidiFile&&); | ||
|
|
||
| /** Copies from another MidiFile object */ | ||
| MidiFile& operator= (MidiFile&&); | ||
|
|
||
| //============================================================================== | ||
| /** Returns the number of tracks in the file. | ||
| @see getTrack, addTrack | ||
| */ | ||
| int getNumTracks() const noexcept; | ||
|
|
||
| /** Returns a pointer to one of the tracks in the file. | ||
| @returns a pointer to the track, or nullptr if the index is out-of-range | ||
| @see getNumTracks, addTrack | ||
| */ | ||
| const MidiMessageSequence* getTrack (int index) const noexcept; | ||
|
|
||
| /** Adds a midi track to the file. | ||
| This will make its own internal copy of the sequence that is passed-in. | ||
| @see getNumTracks, getTrack | ||
| */ | ||
| void addTrack (const MidiMessageSequence& trackSequence); | ||
|
|
||
| /** Removes all midi tracks from the file. | ||
| @see getNumTracks | ||
| */ | ||
| void clear(); | ||
|
|
||
| /** Returns the raw time format code that will be written to a stream. | ||
| After reading a midi file, this method will return the time-format that | ||
| was read from the file's header. It can be changed using the setTicksPerQuarterNote() | ||
| or setSmpteTimeFormat() methods. | ||
| If the value returned is positive, it indicates the number of midi ticks | ||
| per quarter-note - see setTicksPerQuarterNote(). | ||
| It it's negative, the upper byte indicates the frames-per-second (but negative), and | ||
| the lower byte is the number of ticks per frame - see setSmpteTimeFormat(). | ||
| */ | ||
| short getTimeFormat() const noexcept; | ||
|
|
||
| /** Sets the time format to use when this file is written to a stream. | ||
| If this is called, the file will be written as bars/beats using the | ||
| specified resolution, rather than SMPTE absolute times, as would be | ||
| used if setSmpteTimeFormat() had been called instead. | ||
| @param ticksPerQuarterNote e.g. 96, 960 | ||
| @see setSmpteTimeFormat | ||
| */ | ||
| void setTicksPerQuarterNote (int ticksPerQuarterNote) noexcept; | ||
|
|
||
| /** Sets the time format to use when this file is written to a stream. | ||
| If this is called, the file will be written using absolute times, rather | ||
| than bars/beats as would be the case if setTicksPerBeat() had been called | ||
| instead. | ||
| @param framesPerSecond must be 24, 25, 29 or 30 | ||
| @param subframeResolution the sub-second resolution, e.g. 4 (midi time code), | ||
| 8, 10, 80 (SMPTE bit resolution), or 100. For millisecond | ||
| timing, setSmpteTimeFormat (25, 40) | ||
| @see setTicksPerBeat | ||
| */ | ||
| void setSmpteTimeFormat (int framesPerSecond, | ||
| int subframeResolution) noexcept; | ||
|
|
||
| //============================================================================== | ||
| /** Makes a list of all the tempo-change meta-events from all tracks in the midi file. | ||
| Useful for finding the positions of all the tempo changes in a file. | ||
| @param tempoChangeEvents a list to which all the events will be added | ||
| */ | ||
| void findAllTempoEvents (MidiMessageSequence& tempoChangeEvents) const; | ||
|
|
||
| /** Makes a list of all the time-signature meta-events from all tracks in the midi file. | ||
| Useful for finding the positions of all the tempo changes in a file. | ||
| @param timeSigEvents a list to which all the events will be added | ||
| */ | ||
| void findAllTimeSigEvents (MidiMessageSequence& timeSigEvents) const; | ||
|
|
||
| /** Makes a list of all the key-signature meta-events from all tracks in the midi file. | ||
| @param keySigEvents a list to which all the events will be added | ||
| */ | ||
| void findAllKeySigEvents (MidiMessageSequence& keySigEvents) const; | ||
|
|
||
| /** Returns the latest timestamp in any of the tracks. | ||
| (Useful for finding the length of the file). | ||
| */ | ||
| double getLastTimestamp() const; | ||
|
|
||
| //============================================================================== | ||
| /** Reads a midi file format stream. | ||
| After calling this, you can get the tracks that were read from the file by using the | ||
| getNumTracks() and getTrack() methods. | ||
| The timestamps of the midi events in the tracks will represent their positions in | ||
| terms of midi ticks. To convert them to seconds, use the convertTimestampTicksToSeconds() | ||
| method. | ||
| @param sourceStream the source stream | ||
| @param createMatchingNoteOffs if true, any missing note-offs for previous note-ons will | ||
| be automatically added at the end of the file by calling | ||
| MidiMessageSequence::updateMatchedPairs on each track. | ||
| @param midiFileType if not nullptr, the integer at this address will be set | ||
| to 0, 1, or 2 depending on the type of the midi file | ||
| @returns true if the stream was read successfully | ||
| */ | ||
| bool readFrom (InputStream& sourceStream, | ||
| bool createMatchingNoteOffs = true, | ||
| int* midiFileType = nullptr); | ||
|
|
||
| /** Writes the midi tracks as a standard midi file. | ||
| The midiFileType value is written as the file's format type, which can be 0, 1 | ||
| or 2 - see the midi file spec for more info about that. | ||
| @param destStream the destination stream | ||
| @param midiFileType the type of midi file | ||
| @returns true if the operation succeeded. | ||
| */ | ||
| bool writeTo (OutputStream& destStream, int midiFileType = 1) const; | ||
|
|
||
| /** Converts the timestamp of all the midi events from midi ticks to seconds. | ||
| This will use the midi time format and tempo/time signature info in the | ||
| tracks to convert all the timestamps to absolute values in seconds. | ||
| */ | ||
| void convertTimestampTicksToSeconds(); | ||
|
|
||
| private: | ||
| //============================================================================== | ||
| OwnedArray<MidiMessageSequence> tracks; | ||
| short timeFormat; | ||
|
|
||
| void readNextTrack (const uint8*, int, bool); | ||
| bool writeTrack (OutputStream&, const MidiMessageSequence&) const; | ||
|
|
||
| JUCE_LEAK_DETECTOR (MidiFile) | ||
| }; | ||
|
|
||
| } // namespace juce |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,173 @@ | ||
| /* | ||
| ============================================================================== | ||
| This file is part of the JUCE library. | ||
| Copyright (c) 2022 - Raw Material Software Limited | ||
| JUCE is an open source library subject to commercial or open-source | ||
| licensing. | ||
| The code included in this file is provided under the terms of the ISC license | ||
| http://www.isc.org/downloads/software-support-policy/isc-license. Permission | ||
| To use, copy, modify, and/or distribute this software for any purpose with or | ||
| without fee is hereby granted provided that the above copyright notice and | ||
| this permission notice appear in all copies. | ||
| JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | ||
| EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | ||
| DISCLAIMED. | ||
| ============================================================================== | ||
| */ | ||
|
|
||
| namespace juce | ||
| { | ||
|
|
||
| MidiKeyboardState::MidiKeyboardState() | ||
| { | ||
| zerostruct (noteStates); | ||
| } | ||
|
|
||
| //============================================================================== | ||
| void MidiKeyboardState::reset() | ||
| { | ||
| const ScopedLock sl (lock); | ||
| zerostruct (noteStates); | ||
| eventsToAdd.clear(); | ||
| } | ||
|
|
||
| bool MidiKeyboardState::isNoteOn (const int midiChannel, const int n) const noexcept | ||
| { | ||
| jassert (midiChannel > 0 && midiChannel <= 16); | ||
|
|
||
| return isPositiveAndBelow (n, 128) | ||
| && (noteStates[n] & (1 << (midiChannel - 1))) != 0; | ||
| } | ||
|
|
||
| bool MidiKeyboardState::isNoteOnForChannels (const int midiChannelMask, const int n) const noexcept | ||
| { | ||
| return isPositiveAndBelow (n, 128) | ||
| && (noteStates[n] & midiChannelMask) != 0; | ||
| } | ||
|
|
||
| void MidiKeyboardState::noteOn (const int midiChannel, const int midiNoteNumber, const float velocity) | ||
| { | ||
| jassert (midiChannel > 0 && midiChannel <= 16); | ||
| jassert (isPositiveAndBelow (midiNoteNumber, 128)); | ||
|
|
||
| const ScopedLock sl (lock); | ||
|
|
||
| if (isPositiveAndBelow (midiNoteNumber, 128)) | ||
| { | ||
| const int timeNow = (int) Time::getMillisecondCounter(); | ||
| eventsToAdd.addEvent (MidiMessage::noteOn (midiChannel, midiNoteNumber, velocity), timeNow); | ||
| eventsToAdd.clear (0, timeNow - 500); | ||
|
|
||
| noteOnInternal (midiChannel, midiNoteNumber, velocity); | ||
| } | ||
| } | ||
|
|
||
| void MidiKeyboardState::noteOnInternal (const int midiChannel, const int midiNoteNumber, const float velocity) | ||
| { | ||
| if (isPositiveAndBelow (midiNoteNumber, 128)) | ||
| { | ||
| noteStates[midiNoteNumber] = static_cast<uint16> (noteStates[midiNoteNumber] | (1 << (midiChannel - 1))); | ||
| listeners.call ([&] (Listener& l) { l.handleNoteOn (this, midiChannel, midiNoteNumber, velocity); }); | ||
| } | ||
| } | ||
|
|
||
| void MidiKeyboardState::noteOff (const int midiChannel, const int midiNoteNumber, const float velocity) | ||
| { | ||
| const ScopedLock sl (lock); | ||
|
|
||
| if (isNoteOn (midiChannel, midiNoteNumber)) | ||
| { | ||
| const int timeNow = (int) Time::getMillisecondCounter(); | ||
| eventsToAdd.addEvent (MidiMessage::noteOff (midiChannel, midiNoteNumber), timeNow); | ||
| eventsToAdd.clear (0, timeNow - 500); | ||
|
|
||
| noteOffInternal (midiChannel, midiNoteNumber, velocity); | ||
| } | ||
| } | ||
|
|
||
| void MidiKeyboardState::noteOffInternal (const int midiChannel, const int midiNoteNumber, const float velocity) | ||
| { | ||
| if (isNoteOn (midiChannel, midiNoteNumber)) | ||
| { | ||
| noteStates[midiNoteNumber] = static_cast<uint16> (noteStates[midiNoteNumber] & ~(1 << (midiChannel - 1))); | ||
| listeners.call ([&] (Listener& l) { l.handleNoteOff (this, midiChannel, midiNoteNumber, velocity); }); | ||
| } | ||
| } | ||
|
|
||
| void MidiKeyboardState::allNotesOff (const int midiChannel) | ||
| { | ||
| const ScopedLock sl (lock); | ||
|
|
||
| if (midiChannel <= 0) | ||
| { | ||
| for (int i = 1; i <= 16; ++i) | ||
| allNotesOff (i); | ||
| } | ||
| else | ||
| { | ||
| for (int i = 0; i < 128; ++i) | ||
| noteOff (midiChannel, i, 0.0f); | ||
| } | ||
| } | ||
|
|
||
| void MidiKeyboardState::processNextMidiEvent (const MidiMessage& message) | ||
| { | ||
| if (message.isNoteOn()) | ||
| { | ||
| noteOnInternal (message.getChannel(), message.getNoteNumber(), message.getFloatVelocity()); | ||
| } | ||
| else if (message.isNoteOff()) | ||
| { | ||
| noteOffInternal (message.getChannel(), message.getNoteNumber(), message.getFloatVelocity()); | ||
| } | ||
| else if (message.isAllNotesOff()) | ||
| { | ||
| for (int i = 0; i < 128; ++i) | ||
| noteOffInternal (message.getChannel(), i, 0.0f); | ||
| } | ||
| } | ||
|
|
||
| void MidiKeyboardState::processNextMidiBuffer (MidiBuffer& buffer, | ||
| const int startSample, | ||
| const int numSamples, | ||
| const bool injectIndirectEvents) | ||
| { | ||
| const ScopedLock sl (lock); | ||
|
|
||
| for (const auto metadata : buffer) | ||
| processNextMidiEvent (metadata.getMessage()); | ||
|
|
||
| if (injectIndirectEvents) | ||
| { | ||
| const int firstEventToAdd = eventsToAdd.getFirstEventTime(); | ||
| const double scaleFactor = numSamples / (double) (eventsToAdd.getLastEventTime() + 1 - firstEventToAdd); | ||
|
|
||
| for (const auto metadata : eventsToAdd) | ||
| { | ||
| const auto pos = jlimit (0, numSamples - 1, roundToInt ((metadata.samplePosition - firstEventToAdd) * scaleFactor)); | ||
| buffer.addEvent (metadata.getMessage(), startSample + pos); | ||
| } | ||
| } | ||
|
|
||
| eventsToAdd.clear(); | ||
| } | ||
|
|
||
| //============================================================================== | ||
| void MidiKeyboardState::addListener (Listener* listener) | ||
| { | ||
| const ScopedLock sl (lock); | ||
| listeners.add (listener); | ||
| } | ||
|
|
||
| void MidiKeyboardState::removeListener (Listener* listener) | ||
| { | ||
| const ScopedLock sl (lock); | ||
| listeners.remove (listener); | ||
| } | ||
|
|
||
| } // namespace juce |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,195 @@ | ||
| /* | ||
| ============================================================================== | ||
| This file is part of the JUCE library. | ||
| Copyright (c) 2022 - Raw Material Software Limited | ||
| JUCE is an open source library subject to commercial or open-source | ||
| licensing. | ||
| The code included in this file is provided under the terms of the ISC license | ||
| http://www.isc.org/downloads/software-support-policy/isc-license. Permission | ||
| To use, copy, modify, and/or distribute this software for any purpose with or | ||
| without fee is hereby granted provided that the above copyright notice and | ||
| this permission notice appear in all copies. | ||
| JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER | ||
| EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE | ||
| DISCLAIMED. | ||
| ============================================================================== | ||
| */ | ||
|
|
||
| namespace juce | ||
| { | ||
|
|
||
| //============================================================================== | ||
| /** | ||
| Represents a piano keyboard, keeping track of which keys are currently pressed. | ||
| This object can parse a stream of midi events, using them to update its idea | ||
| of which keys are pressed for each individual midi channel. | ||
| When keys go up or down, it can broadcast these events to listener objects. | ||
| It also allows key up/down events to be triggered with its noteOn() and noteOff() | ||
| methods, and midi messages for these events will be merged into the | ||
| midi stream that gets processed by processNextMidiBuffer(). | ||
| @tags{Audio} | ||
| */ | ||
| class JUCE_API MidiKeyboardState | ||
| { | ||
| public: | ||
| //============================================================================== | ||
| MidiKeyboardState(); | ||
|
|
||
| //============================================================================== | ||
| /** Resets the state of the object. | ||
| All internal data for all the channels is reset, but no events are sent as a | ||
| result. | ||
| If you want to release any keys that are currently down, and to send out note-up | ||
| midi messages for this, use the allNotesOff() method instead. | ||
| */ | ||
| void reset(); | ||
|
|
||
| /** Returns true if the given midi key is currently held down for the given midi channel. | ||
| The channel number must be between 1 and 16. If you want to see if any notes are | ||
| on for a range of channels, use the isNoteOnForChannels() method. | ||
| */ | ||
| bool isNoteOn (int midiChannel, int midiNoteNumber) const noexcept; | ||
|
|
||
| /** Returns true if the given midi key is currently held down on any of a set of midi channels. | ||
| The channel mask has a bit set for each midi channel you want to test for - bit | ||
| 0 = midi channel 1, bit 1 = midi channel 2, etc. | ||
| If a note is on for at least one of the specified channels, this returns true. | ||
| */ | ||
| bool isNoteOnForChannels (int midiChannelMask, int midiNoteNumber) const noexcept; | ||
|
|
||
| /** Turns a specified note on. | ||
| This will cause a suitable midi note-on event to be injected into the midi buffer during the | ||
| next call to processNextMidiBuffer(). | ||
| It will also trigger a synchronous callback to the listeners to tell them that the key has | ||
| gone down. | ||
| */ | ||
| void noteOn (int midiChannel, int midiNoteNumber, float velocity); | ||
|
|
||
| /** Turns a specified note off. | ||
| This will cause a suitable midi note-off event to be injected into the midi buffer during the | ||
| next call to processNextMidiBuffer(). | ||
| It will also trigger a synchronous callback to the listeners to tell them that the key has | ||
| gone up. | ||
| But if the note isn't actually down for the given channel, this method will in fact do nothing. | ||
| */ | ||
| void noteOff (int midiChannel, int midiNoteNumber, float velocity); | ||
|
|
||
| /** This will turn off any currently-down notes for the given midi channel. | ||
| If you pass 0 for the midi channel, it will in fact turn off all notes on all channels. | ||
| Calling this method will make calls to noteOff(), so can trigger synchronous callbacks | ||
| and events being added to the midi stream. | ||
| */ | ||
| void allNotesOff (int midiChannel); | ||
|
|
||
| //============================================================================== | ||
| /** Looks at a key-up/down event and uses it to update the state of this object. | ||
| To process a buffer full of midi messages, use the processNextMidiBuffer() method | ||
| instead. | ||
| */ | ||
| void processNextMidiEvent (const MidiMessage& message); | ||
|
|
||
| /** Scans a midi stream for up/down events and adds its own events to it. | ||
| This will look for any up/down events and use them to update the internal state, | ||
| synchronously making suitable callbacks to the listeners. | ||
| If injectIndirectEvents is true, then midi events to produce the recent noteOn() | ||
| and noteOff() calls will be added into the buffer. | ||
| Only the section of the buffer whose timestamps are between startSample and | ||
| (startSample + numSamples) will be affected, and any events added will be placed | ||
| between these times. | ||
| If you're going to use this method, you'll need to keep calling it regularly for | ||
| it to work satisfactorily. | ||
| To process a single midi event at a time, use the processNextMidiEvent() method | ||
| instead. | ||
| */ | ||
| void processNextMidiBuffer (MidiBuffer& buffer, | ||
| int startSample, | ||
| int numSamples, | ||
| bool injectIndirectEvents); | ||
|
|
||
| //============================================================================== | ||
| /** Receives events from a MidiKeyboardState object. */ | ||
| class JUCE_API Listener | ||
| { | ||
| public: | ||
| //============================================================================== | ||
| virtual ~Listener() = default; | ||
|
|
||
| //============================================================================== | ||
| /** Called when one of the MidiKeyboardState's keys is pressed. | ||
| This will be called synchronously when the state is either processing a | ||
| buffer in its MidiKeyboardState::processNextMidiBuffer() method, or | ||
| when a note is being played with its MidiKeyboardState::noteOn() method. | ||
| Note that this callback could happen from an audio callback thread, so be | ||
| careful not to block, and avoid any UI activity in the callback. | ||
| */ | ||
| virtual void handleNoteOn (MidiKeyboardState* source, | ||
| int midiChannel, int midiNoteNumber, float velocity) = 0; | ||
|
|
||
| /** Called when one of the MidiKeyboardState's keys is released. | ||
| This will be called synchronously when the state is either processing a | ||
| buffer in its MidiKeyboardState::processNextMidiBuffer() method, or | ||
| when a note is being played with its MidiKeyboardState::noteOff() method. | ||
| Note that this callback could happen from an audio callback thread, so be | ||
| careful not to block, and avoid any UI activity in the callback. | ||
| */ | ||
| virtual void handleNoteOff (MidiKeyboardState* source, | ||
| int midiChannel, int midiNoteNumber, float velocity) = 0; | ||
| }; | ||
|
|
||
| /** Registers a listener for callbacks when keys go up or down. | ||
| @see removeListener | ||
| */ | ||
| void addListener (Listener* listener); | ||
|
|
||
| /** Deregisters a listener. | ||
| @see addListener | ||
| */ | ||
| void removeListener (Listener* listener); | ||
|
|
||
| private: | ||
| //============================================================================== | ||
| CriticalSection lock; | ||
| std::atomic<uint16> noteStates[128]; | ||
| MidiBuffer eventsToAdd; | ||
| ListenerList<Listener> listeners; | ||
|
|
||
| void noteOnInternal (int midiChannel, int midiNoteNumber, float velocity); | ||
| void noteOffInternal (int midiChannel, int midiNoteNumber, float velocity); | ||
|
|
||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiKeyboardState) | ||
| }; | ||
|
|
||
| using MidiKeyboardStateListener = MidiKeyboardState::Listener; | ||
|
|
||
| } // namespace juce |