@@ -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

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

@@ -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
@@ -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
1,296 changes: 1,296 additions & 0 deletions libs/juce7/source/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.h

Large diffs are not rendered by default.

Large diffs are not rendered by default.

@@ -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
101 changes: 101 additions & 0 deletions libs/juce7/source/modules/juce_audio_basics/juce_audio_basics.cpp
@@ -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
123 changes: 123 additions & 0 deletions libs/juce7/source/modules/juce_audio_basics/juce_audio_basics.h
@@ -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"
23 changes: 23 additions & 0 deletions libs/juce7/source/modules/juce_audio_basics/juce_audio_basics.mm
@@ -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"
320 changes: 320 additions & 0 deletions libs/juce7/source/modules/juce_audio_basics/midi/juce_MidiBuffer.cpp
@@ -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
345 changes: 345 additions & 0 deletions libs/juce7/source/modules/juce_audio_basics/midi/juce_MidiBuffer.h
@@ -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
@@ -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
794 changes: 794 additions & 0 deletions libs/juce7/source/modules/juce_audio_basics/midi/juce_MidiFile.cpp

Large diffs are not rendered by default.

198 changes: 198 additions & 0 deletions libs/juce7/source/modules/juce_audio_basics/midi/juce_MidiFile.h
@@ -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
@@ -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
@@ -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