| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,104 @@ | ||
| /* | ||
| ============================================================================== | ||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | ||
| Copyright 2004-11 by Raw Material Software Ltd. | ||
| ------------------------------------------------------------------------------ | ||
| JUCE can be redistributed and/or modified under the terms of the GNU General | ||
| Public License (Version 2), as published by the Free Software Foundation. | ||
| A copy of the license is included in the JUCE distribution, or can be found | ||
| online at www.gnu.org/licenses. | ||
| JUCE is distributed in the hope that it will be useful, but WITHOUT ANY | ||
| WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR | ||
| A PARTICULAR PURPOSE. See the GNU General Public License for more details. | ||
| ------------------------------------------------------------------------------ | ||
| To release a closed-source product which uses JUCE, commercial licenses are | ||
| available: visit www.rawmaterialsoftware.com/juce for more information. | ||
| ============================================================================== | ||
| */ | ||
|
|
||
| #ifndef __JUCE_DECIBELS_JUCEHEADER__ | ||
| #define __JUCE_DECIBELS_JUCEHEADER__ | ||
|
|
||
| //============================================================================== | ||
| /** | ||
| This class contains some helpful static methods for dealing with decibel values. | ||
| */ | ||
| class Decibels | ||
| { | ||
| public: | ||
| //============================================================================== | ||
| /** Converts a dBFS value to its equivalent gain level. | ||
| A gain of 1.0 = 0 dB, and lower gains map onto negative decibel values. Any | ||
| decibel value lower than minusInfinityDb will return a gain of 0. | ||
| */ | ||
| template <typename Type> | ||
| static Type decibelsToGain (const Type decibels, | ||
| const Type minusInfinityDb = (Type) defaultMinusInfinitydB) | ||
| { | ||
| return decibels > minusInfinityDb ? powf ((Type) 10.0, decibels * (Type) 0.05) | ||
| : Type(); | ||
| } | ||
|
|
||
| /** Converts a gain level into a dBFS value. | ||
| A gain of 1.0 = 0 dB, and lower gains map onto negative decibel values. | ||
| If the gain is 0 (or negative), then the method will return the value | ||
| provided as minusInfinityDb. | ||
| */ | ||
| template <typename Type> | ||
| static Type gainToDecibels (const Type gain, | ||
| const Type minusInfinityDb = (Type) defaultMinusInfinitydB) | ||
| { | ||
| return gain > Type() ? jmax (minusInfinityDb, (Type) std::log10 (gain) * (Type) 20.0) | ||
| : minusInfinityDb; | ||
| } | ||
|
|
||
| //============================================================================== | ||
| /** Converts a decibel reading to a string, with the 'dB' suffix. | ||
| If the decibel value is lower than minusInfinityDb, the return value will | ||
| be "-INF dB". | ||
| */ | ||
| template <typename Type> | ||
| static String toString (const Type decibels, | ||
| const int decimalPlaces = 2, | ||
| const Type minusInfinityDb = (Type) defaultMinusInfinitydB) | ||
| { | ||
| String s; | ||
|
|
||
| if (decibels <= minusInfinityDb) | ||
| { | ||
| s = "-INF dB"; | ||
| } | ||
| else | ||
| { | ||
| if (decibels >= Type()) | ||
| s << '+'; | ||
|
|
||
| s << String (decibels, decimalPlaces) << " dB"; | ||
| } | ||
|
|
||
| return s; | ||
| } | ||
|
|
||
|
|
||
| private: | ||
| //============================================================================== | ||
| enum | ||
| { | ||
| defaultMinusInfinitydB = -100 | ||
| }; | ||
|
|
||
| Decibels(); // This class can't be instantiated, it's just a holder for static methods.. | ||
| JUCE_DECLARE_NON_COPYABLE (Decibels) | ||
| }; | ||
|
|
||
|
|
||
| #endif // __JUCE_DECIBELS_JUCEHEADER__ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,252 @@ | ||
| /* | ||
| ============================================================================== | ||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | ||
| Copyright 2004-11 by Raw Material Software Ltd. | ||
| ------------------------------------------------------------------------------ | ||
| JUCE can be redistributed and/or modified under the terms of the GNU General | ||
| Public License (Version 2), as published by the Free Software Foundation. | ||
| A copy of the license is included in the JUCE distribution, or can be found | ||
| online at www.gnu.org/licenses. | ||
| JUCE is distributed in the hope that it will be useful, but WITHOUT ANY | ||
| WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR | ||
| A PARTICULAR PURPOSE. See the GNU General Public License for more details. | ||
| ------------------------------------------------------------------------------ | ||
| To release a closed-source product which uses JUCE, commercial licenses are | ||
| available: visit www.rawmaterialsoftware.com/juce for more information. | ||
| ============================================================================== | ||
| */ | ||
|
|
||
| #if JUCE_INTEL | ||
| #define JUCE_SNAP_TO_ZERO(n) if (! (n < -1.0e-8 || n > 1.0e-8)) n = 0; | ||
| #else | ||
| #define JUCE_SNAP_TO_ZERO(n) | ||
| #endif | ||
|
|
||
| //============================================================================== | ||
| IIRFilter::IIRFilter() | ||
| : active (false) | ||
| { | ||
| reset(); | ||
| } | ||
|
|
||
| IIRFilter::IIRFilter (const IIRFilter& other) | ||
| : active (other.active) | ||
| { | ||
| const ScopedLock sl (other.processLock); | ||
| memcpy (coefficients, other.coefficients, sizeof (coefficients)); | ||
| reset(); | ||
| } | ||
|
|
||
| IIRFilter::~IIRFilter() | ||
| { | ||
| } | ||
|
|
||
| //============================================================================== | ||
| void IIRFilter::reset() noexcept | ||
| { | ||
| const ScopedLock sl (processLock); | ||
|
|
||
| x1 = 0; | ||
| x2 = 0; | ||
| y1 = 0; | ||
| y2 = 0; | ||
| } | ||
|
|
||
| float IIRFilter::processSingleSampleRaw (const float in) noexcept | ||
| { | ||
| float out = coefficients[0] * in | ||
| + coefficients[1] * x1 | ||
| + coefficients[2] * x2 | ||
| - coefficients[4] * y1 | ||
| - coefficients[5] * y2; | ||
|
|
||
| JUCE_SNAP_TO_ZERO (out); | ||
|
|
||
| x2 = x1; | ||
| x1 = in; | ||
| y2 = y1; | ||
| y1 = out; | ||
|
|
||
| return out; | ||
| } | ||
|
|
||
| void IIRFilter::processSamples (float* const samples, | ||
| const int numSamples) noexcept | ||
| { | ||
| const ScopedLock sl (processLock); | ||
|
|
||
| if (active) | ||
| { | ||
| for (int i = 0; i < numSamples; ++i) | ||
| { | ||
| const float in = samples[i]; | ||
|
|
||
| float out = coefficients[0] * in | ||
| + coefficients[1] * x1 | ||
| + coefficients[2] * x2 | ||
| - coefficients[4] * y1 | ||
| - coefficients[5] * y2; | ||
|
|
||
| JUCE_SNAP_TO_ZERO (out); | ||
|
|
||
| x2 = x1; | ||
| x1 = in; | ||
| y2 = y1; | ||
| y1 = out; | ||
|
|
||
| samples[i] = out; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| //============================================================================== | ||
| void IIRFilter::makeLowPass (const double sampleRate, | ||
| const double frequency) noexcept | ||
| { | ||
| jassert (sampleRate > 0); | ||
|
|
||
| const double n = 1.0 / tan (double_Pi * frequency / sampleRate); | ||
| const double nSquared = n * n; | ||
| const double c1 = 1.0 / (1.0 + std::sqrt (2.0) * n + nSquared); | ||
|
|
||
| setCoefficients (c1, | ||
| c1 * 2.0f, | ||
| c1, | ||
| 1.0, | ||
| c1 * 2.0 * (1.0 - nSquared), | ||
| c1 * (1.0 - std::sqrt (2.0) * n + nSquared)); | ||
| } | ||
|
|
||
| void IIRFilter::makeHighPass (const double sampleRate, | ||
| const double frequency) noexcept | ||
| { | ||
| const double n = tan (double_Pi * frequency / sampleRate); | ||
| const double nSquared = n * n; | ||
| const double c1 = 1.0 / (1.0 + std::sqrt (2.0) * n + nSquared); | ||
|
|
||
| setCoefficients (c1, | ||
| c1 * -2.0f, | ||
| c1, | ||
| 1.0, | ||
| c1 * 2.0 * (nSquared - 1.0), | ||
| c1 * (1.0 - std::sqrt (2.0) * n + nSquared)); | ||
| } | ||
|
|
||
| void IIRFilter::makeLowShelf (const double sampleRate, | ||
| const double cutOffFrequency, | ||
| const double Q, | ||
| const float gainFactor) noexcept | ||
| { | ||
| jassert (sampleRate > 0); | ||
| jassert (Q > 0); | ||
|
|
||
| const double A = jmax (0.0f, gainFactor); | ||
| const double aminus1 = A - 1.0; | ||
| const double aplus1 = A + 1.0; | ||
| const double omega = (double_Pi * 2.0 * jmax (cutOffFrequency, 2.0)) / sampleRate; | ||
| const double coso = std::cos (omega); | ||
| const double beta = std::sin (omega) * std::sqrt (A) / Q; | ||
| const double aminus1TimesCoso = aminus1 * coso; | ||
|
|
||
| setCoefficients (A * (aplus1 - aminus1TimesCoso + beta), | ||
| A * 2.0 * (aminus1 - aplus1 * coso), | ||
| A * (aplus1 - aminus1TimesCoso - beta), | ||
| aplus1 + aminus1TimesCoso + beta, | ||
| -2.0 * (aminus1 + aplus1 * coso), | ||
| aplus1 + aminus1TimesCoso - beta); | ||
| } | ||
|
|
||
| void IIRFilter::makeHighShelf (const double sampleRate, | ||
| const double cutOffFrequency, | ||
| const double Q, | ||
| const float gainFactor) noexcept | ||
| { | ||
| jassert (sampleRate > 0); | ||
| jassert (Q > 0); | ||
|
|
||
| const double A = jmax (0.0f, gainFactor); | ||
| const double aminus1 = A - 1.0; | ||
| const double aplus1 = A + 1.0; | ||
| const double omega = (double_Pi * 2.0 * jmax (cutOffFrequency, 2.0)) / sampleRate; | ||
| const double coso = std::cos (omega); | ||
| const double beta = std::sin (omega) * std::sqrt (A) / Q; | ||
| const double aminus1TimesCoso = aminus1 * coso; | ||
|
|
||
| setCoefficients (A * (aplus1 + aminus1TimesCoso + beta), | ||
| A * -2.0 * (aminus1 + aplus1 * coso), | ||
| A * (aplus1 + aminus1TimesCoso - beta), | ||
| aplus1 - aminus1TimesCoso + beta, | ||
| 2.0 * (aminus1 - aplus1 * coso), | ||
| aplus1 - aminus1TimesCoso - beta); | ||
| } | ||
|
|
||
| void IIRFilter::makeBandPass (const double sampleRate, | ||
| const double centreFrequency, | ||
| const double Q, | ||
| const float gainFactor) noexcept | ||
| { | ||
| jassert (sampleRate > 0); | ||
| jassert (Q > 0); | ||
|
|
||
| const double A = jmax (0.0f, gainFactor); | ||
| const double omega = (double_Pi * 2.0 * jmax (centreFrequency, 2.0)) / sampleRate; | ||
| const double alpha = 0.5 * std::sin (omega) / Q; | ||
| const double c2 = -2.0 * std::cos (omega); | ||
| const double alphaTimesA = alpha * A; | ||
| const double alphaOverA = alpha / A; | ||
|
|
||
| setCoefficients (1.0 + alphaTimesA, | ||
| c2, | ||
| 1.0 - alphaTimesA, | ||
| 1.0 + alphaOverA, | ||
| c2, | ||
| 1.0 - alphaOverA); | ||
| } | ||
|
|
||
| void IIRFilter::makeInactive() noexcept | ||
| { | ||
| const ScopedLock sl (processLock); | ||
| active = false; | ||
| } | ||
|
|
||
| //============================================================================== | ||
| void IIRFilter::copyCoefficientsFrom (const IIRFilter& other) noexcept | ||
| { | ||
| const ScopedLock sl (processLock); | ||
|
|
||
| memcpy (coefficients, other.coefficients, sizeof (coefficients)); | ||
| active = other.active; | ||
| } | ||
|
|
||
| //============================================================================== | ||
| void IIRFilter::setCoefficients (double c1, double c2, double c3, | ||
| double c4, double c5, double c6) noexcept | ||
| { | ||
| const double a = 1.0 / c4; | ||
|
|
||
| c1 *= a; | ||
| c2 *= a; | ||
| c3 *= a; | ||
| c5 *= a; | ||
| c6 *= a; | ||
|
|
||
| const ScopedLock sl (processLock); | ||
|
|
||
| coefficients[0] = (float) c1; | ||
| coefficients[1] = (float) c2; | ||
| coefficients[2] = (float) c3; | ||
| coefficients[3] = (float) c4; | ||
| coefficients[4] = (float) c5; | ||
| coefficients[5] = (float) c6; | ||
|
|
||
| active = true; | ||
| } | ||
|
|
||
| #undef JUCE_SNAP_TO_ZERO |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,149 @@ | ||
| /* | ||
| ============================================================================== | ||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | ||
| Copyright 2004-11 by Raw Material Software Ltd. | ||
| ------------------------------------------------------------------------------ | ||
| JUCE can be redistributed and/or modified under the terms of the GNU General | ||
| Public License (Version 2), as published by the Free Software Foundation. | ||
| A copy of the license is included in the JUCE distribution, or can be found | ||
| online at www.gnu.org/licenses. | ||
| JUCE is distributed in the hope that it will be useful, but WITHOUT ANY | ||
| WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR | ||
| A PARTICULAR PURPOSE. See the GNU General Public License for more details. | ||
| ------------------------------------------------------------------------------ | ||
| To release a closed-source product which uses JUCE, commercial licenses are | ||
| available: visit www.rawmaterialsoftware.com/juce for more information. | ||
| ============================================================================== | ||
| */ | ||
|
|
||
| #ifndef __JUCE_IIRFILTER_JUCEHEADER__ | ||
| #define __JUCE_IIRFILTER_JUCEHEADER__ | ||
|
|
||
|
|
||
| //============================================================================== | ||
| /** | ||
| An IIR filter that can perform low, high, or band-pass filtering on an | ||
| audio signal. | ||
| @see IIRFilterAudioSource | ||
| */ | ||
| class JUCE_API IIRFilter | ||
| { | ||
| public: | ||
| //============================================================================== | ||
| /** Creates a filter. | ||
| Initially the filter is inactive, so will have no effect on samples that | ||
| you process with it. Use the appropriate method to turn it into the type | ||
| of filter needed. | ||
| */ | ||
| IIRFilter(); | ||
|
|
||
| /** Creates a copy of another filter. */ | ||
| IIRFilter (const IIRFilter& other); | ||
|
|
||
| /** Destructor. */ | ||
| ~IIRFilter(); | ||
|
|
||
| //============================================================================== | ||
| /** Resets the filter's processing pipeline, ready to start a new stream of data. | ||
| Note that this clears the processing state, but the type of filter and | ||
| its coefficients aren't changed. To put a filter into an inactive state, use | ||
| the makeInactive() method. | ||
| */ | ||
| void reset() noexcept; | ||
|
|
||
| /** Performs the filter operation on the given set of samples. | ||
| */ | ||
| void processSamples (float* samples, | ||
| int numSamples) noexcept; | ||
|
|
||
| /** Processes a single sample, without any locking or checking. | ||
| Use this if you need fast processing of a single value, but be aware that | ||
| this isn't thread-safe in the way that processSamples() is. | ||
| */ | ||
| float processSingleSampleRaw (float sample) noexcept; | ||
|
|
||
| //============================================================================== | ||
| /** Sets the filter up to act as a low-pass filter. | ||
| */ | ||
| void makeLowPass (double sampleRate, | ||
| double frequency) noexcept; | ||
|
|
||
| /** Sets the filter up to act as a high-pass filter. | ||
| */ | ||
| void makeHighPass (double sampleRate, | ||
| double frequency) noexcept; | ||
|
|
||
| //============================================================================== | ||
| /** Sets the filter up to act as a low-pass shelf filter with variable Q and gain. | ||
| The gain is a scale factor that the low frequencies are multiplied by, so values | ||
| greater than 1.0 will boost the low frequencies, values less than 1.0 will | ||
| attenuate them. | ||
| */ | ||
| void makeLowShelf (double sampleRate, | ||
| double cutOffFrequency, | ||
| double Q, | ||
| float gainFactor) noexcept; | ||
|
|
||
| /** Sets the filter up to act as a high-pass shelf filter with variable Q and gain. | ||
| The gain is a scale factor that the high frequencies are multiplied by, so values | ||
| greater than 1.0 will boost the high frequencies, values less than 1.0 will | ||
| attenuate them. | ||
| */ | ||
| void makeHighShelf (double sampleRate, | ||
| double cutOffFrequency, | ||
| double Q, | ||
| float gainFactor) noexcept; | ||
|
|
||
| /** Sets the filter up to act as a band pass filter centred around a | ||
| frequency, with a variable Q and gain. | ||
| The gain is a scale factor that the centre frequencies are multiplied by, so | ||
| values greater than 1.0 will boost the centre frequencies, values less than | ||
| 1.0 will attenuate them. | ||
| */ | ||
| void makeBandPass (double sampleRate, | ||
| double centreFrequency, | ||
| double Q, | ||
| float gainFactor) noexcept; | ||
|
|
||
| /** Clears the filter's coefficients so that it becomes inactive. | ||
| */ | ||
| void makeInactive() noexcept; | ||
|
|
||
| //============================================================================== | ||
| /** Makes this filter duplicate the set-up of another one. | ||
| */ | ||
| void copyCoefficientsFrom (const IIRFilter& other) noexcept; | ||
|
|
||
|
|
||
| protected: | ||
| //============================================================================== | ||
| CriticalSection processLock; | ||
|
|
||
| void setCoefficients (double c1, double c2, double c3, | ||
| double c4, double c5, double c6) noexcept; | ||
|
|
||
| bool active; | ||
| float coefficients[6]; | ||
| float x1, x2, y1, y2; | ||
|
|
||
| // (use the copyCoefficientsFrom() method instead of this operator) | ||
| IIRFilter& operator= (const IIRFilter&); | ||
| JUCE_LEAK_DETECTOR (IIRFilter) | ||
| }; | ||
|
|
||
|
|
||
| #endif // __JUCE_IIRFILTER_JUCEHEADER__ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,321 @@ | ||
| /* | ||
| ============================================================================== | ||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | ||
| Copyright 2004-11 by Raw Material Software Ltd. | ||
| ------------------------------------------------------------------------------ | ||
| JUCE can be redistributed and/or modified under the terms of the GNU General | ||
| Public License (Version 2), as published by the Free Software Foundation. | ||
| A copy of the license is included in the JUCE distribution, or can be found | ||
| online at www.gnu.org/licenses. | ||
| JUCE is distributed in the hope that it will be useful, but WITHOUT ANY | ||
| WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR | ||
| A PARTICULAR PURPOSE. See the GNU General Public License for more details. | ||
| ------------------------------------------------------------------------------ | ||
| To release a closed-source product which uses JUCE, commercial licenses are | ||
| available: visit www.rawmaterialsoftware.com/juce for more information. | ||
| ============================================================================== | ||
| */ | ||
|
|
||
| #ifndef __JUCE_REVERB_JUCEHEADER__ | ||
| #define __JUCE_REVERB_JUCEHEADER__ | ||
|
|
||
|
|
||
| //============================================================================== | ||
| /** | ||
| Performs a simple reverb effect on a stream of audio data. | ||
| This is a simple stereo reverb, based on the technique and tunings used in FreeVerb. | ||
| Use setSampleRate() to prepare it, and then call processStereo() or processMono() to | ||
| apply the reverb to your audio data. | ||
| @see ReverbAudioSource | ||
| */ | ||
| class Reverb | ||
| { | ||
| public: | ||
| //============================================================================== | ||
| Reverb() | ||
| { | ||
| setParameters (Parameters()); | ||
| setSampleRate (44100.0); | ||
| } | ||
|
|
||
| //============================================================================== | ||
| /** Holds the parameters being used by a Reverb object. */ | ||
| struct Parameters | ||
| { | ||
| Parameters() noexcept | ||
| : roomSize (0.5f), | ||
| damping (0.5f), | ||
| wetLevel (0.33f), | ||
| dryLevel (0.4f), | ||
| width (1.0f), | ||
| freezeMode (0) | ||
| {} | ||
|
|
||
| float roomSize; /**< Room size, 0 to 1.0, where 1.0 is big, 0 is small. */ | ||
| float damping; /**< Damping, 0 to 1.0, where 0 is not damped, 1.0 is fully damped. */ | ||
| float wetLevel; /**< Wet level, 0 to 1.0 */ | ||
| float dryLevel; /**< Dry level, 0 to 1.0 */ | ||
| float width; /**< Reverb width, 0 to 1.0, where 1.0 is very wide. */ | ||
| float freezeMode; /**< Freeze mode - values < 0.5 are "normal" mode, values > 0.5 | ||
| put the reverb into a continuous feedback loop. */ | ||
| }; | ||
|
|
||
| //============================================================================== | ||
| /** Returns the reverb's current parameters. */ | ||
| const Parameters& getParameters() const noexcept { return parameters; } | ||
|
|
||
| /** Applies a new set of parameters to the reverb. | ||
| Note that this doesn't attempt to lock the reverb, so if you call this in parallel with | ||
| the process method, you may get artifacts. | ||
| */ | ||
| void setParameters (const Parameters& newParams) | ||
| { | ||
| const float wetScaleFactor = 3.0f; | ||
| const float dryScaleFactor = 2.0f; | ||
|
|
||
| const float wet = newParams.wetLevel * wetScaleFactor; | ||
| wet1 = wet * (newParams.width * 0.5f + 0.5f); | ||
| wet2 = wet * (1.0f - newParams.width) * 0.5f; | ||
| dry = newParams.dryLevel * dryScaleFactor; | ||
| gain = isFrozen (newParams.freezeMode) ? 0.0f : 0.015f; | ||
| parameters = newParams; | ||
| shouldUpdateDamping = true; | ||
| } | ||
|
|
||
| //============================================================================== | ||
| /** Sets the sample rate that will be used for the reverb. | ||
| You must call this before the process methods, in order to tell it the correct sample rate. | ||
| */ | ||
| void setSampleRate (const double sampleRate) | ||
| { | ||
| jassert (sampleRate > 0); | ||
|
|
||
| static const short combTunings[] = { 1116, 1188, 1277, 1356, 1422, 1491, 1557, 1617 }; // (at 44100Hz) | ||
| static const short allPassTunings[] = { 556, 441, 341, 225 }; | ||
| const int stereoSpread = 23; | ||
| const int intSampleRate = (int) sampleRate; | ||
|
|
||
| for (int i = 0; i < numCombs; ++i) | ||
| { | ||
| comb[0][i].setSize ((intSampleRate * combTunings[i]) / 44100); | ||
| comb[1][i].setSize ((intSampleRate * (combTunings[i] + stereoSpread)) / 44100); | ||
| } | ||
|
|
||
| for (int i = 0; i < numAllPasses; ++i) | ||
| { | ||
| allPass[0][i].setSize ((intSampleRate * allPassTunings[i]) / 44100); | ||
| allPass[1][i].setSize ((intSampleRate * (allPassTunings[i] + stereoSpread)) / 44100); | ||
| } | ||
|
|
||
| shouldUpdateDamping = true; | ||
| } | ||
|
|
||
| /** Clears the reverb's buffers. */ | ||
| void reset() | ||
| { | ||
| for (int j = 0; j < numChannels; ++j) | ||
| { | ||
| for (int i = 0; i < numCombs; ++i) | ||
| comb[j][i].clear(); | ||
|
|
||
| for (int i = 0; i < numAllPasses; ++i) | ||
| allPass[j][i].clear(); | ||
| } | ||
| } | ||
|
|
||
| //============================================================================== | ||
| /** Applies the reverb to two stereo channels of audio data. */ | ||
| void processStereo (float* const left, float* const right, const int numSamples) noexcept | ||
| { | ||
| jassert (left != nullptr && right != nullptr); | ||
|
|
||
| if (shouldUpdateDamping) | ||
| updateDamping(); | ||
|
|
||
| for (int i = 0; i < numSamples; ++i) | ||
| { | ||
| const float input = (left[i] + right[i]) * gain; | ||
| float outL = 0, outR = 0; | ||
|
|
||
| for (int j = 0; j < numCombs; ++j) // accumulate the comb filters in parallel | ||
| { | ||
| outL += comb[0][j].process (input); | ||
| outR += comb[1][j].process (input); | ||
| } | ||
|
|
||
| for (int j = 0; j < numAllPasses; ++j) // run the allpass filters in series | ||
| { | ||
| outL = allPass[0][j].process (outL); | ||
| outR = allPass[1][j].process (outR); | ||
| } | ||
|
|
||
| left[i] = outL * wet1 + outR * wet2 + left[i] * dry; | ||
| right[i] = outR * wet1 + outL * wet2 + right[i] * dry; | ||
| } | ||
| } | ||
|
|
||
| /** Applies the reverb to a single mono channel of audio data. */ | ||
| void processMono (float* const samples, const int numSamples) noexcept | ||
| { | ||
| jassert (samples != nullptr); | ||
|
|
||
| if (shouldUpdateDamping) | ||
| updateDamping(); | ||
|
|
||
| for (int i = 0; i < numSamples; ++i) | ||
| { | ||
| const float input = samples[i] * gain; | ||
| float output = 0; | ||
|
|
||
| for (int j = 0; j < numCombs; ++j) // accumulate the comb filters in parallel | ||
| output += comb[0][j].process (input); | ||
|
|
||
| for (int j = 0; j < numAllPasses; ++j) // run the allpass filters in series | ||
| output = allPass[0][j].process (output); | ||
|
|
||
| samples[i] = output * wet1 + input * dry; | ||
| } | ||
| } | ||
|
|
||
| private: | ||
| //============================================================================== | ||
| Parameters parameters; | ||
|
|
||
| volatile bool shouldUpdateDamping; | ||
| float gain, wet1, wet2, dry; | ||
|
|
||
| inline static bool isFrozen (const float freezeMode) noexcept { return freezeMode >= 0.5f; } | ||
|
|
||
| void updateDamping() noexcept | ||
| { | ||
| const float roomScaleFactor = 0.28f; | ||
| const float roomOffset = 0.7f; | ||
| const float dampScaleFactor = 0.4f; | ||
|
|
||
| shouldUpdateDamping = false; | ||
|
|
||
| if (isFrozen (parameters.freezeMode)) | ||
| setDamping (0.0f, 1.0f); | ||
| else | ||
| setDamping (parameters.damping * dampScaleFactor, | ||
| parameters.roomSize * roomScaleFactor + roomOffset); | ||
| } | ||
|
|
||
| void setDamping (const float dampingToUse, const float roomSizeToUse) noexcept | ||
| { | ||
| for (int j = 0; j < numChannels; ++j) | ||
| for (int i = numCombs; --i >= 0;) | ||
| comb[j][i].setFeedbackAndDamp (roomSizeToUse, dampingToUse); | ||
| } | ||
|
|
||
| //============================================================================== | ||
| class CombFilter | ||
| { | ||
| public: | ||
| CombFilter() noexcept : bufferSize (0), bufferIndex (0) {} | ||
|
|
||
| void setSize (const int size) | ||
| { | ||
| if (size != bufferSize) | ||
| { | ||
| bufferIndex = 0; | ||
| buffer.malloc ((size_t) size); | ||
| bufferSize = size; | ||
| } | ||
|
|
||
| clear(); | ||
| } | ||
|
|
||
| void clear() noexcept | ||
| { | ||
| last = 0; | ||
| buffer.clear ((size_t) bufferSize); | ||
| } | ||
|
|
||
| void setFeedbackAndDamp (const float f, const float d) noexcept | ||
| { | ||
| damp1 = d; | ||
| damp2 = 1.0f - d; | ||
| feedback = f; | ||
| } | ||
|
|
||
| inline float process (const float input) noexcept | ||
| { | ||
| const float output = buffer [bufferIndex]; | ||
| last = (output * damp2) + (last * damp1); | ||
| JUCE_UNDENORMALISE (last); | ||
|
|
||
| float temp = input + (last * feedback); | ||
| JUCE_UNDENORMALISE (temp); | ||
| buffer [bufferIndex] = temp; | ||
| bufferIndex = (bufferIndex + 1) % bufferSize; | ||
| return output; | ||
| } | ||
|
|
||
| private: | ||
| HeapBlock<float> buffer; | ||
| int bufferSize, bufferIndex; | ||
| float feedback, last, damp1, damp2; | ||
|
|
||
| JUCE_DECLARE_NON_COPYABLE (CombFilter) | ||
| }; | ||
|
|
||
| //============================================================================== | ||
| class AllPassFilter | ||
| { | ||
| public: | ||
| AllPassFilter() noexcept : bufferSize (0), bufferIndex (0) {} | ||
|
|
||
| void setSize (const int size) | ||
| { | ||
| if (size != bufferSize) | ||
| { | ||
| bufferIndex = 0; | ||
| buffer.malloc ((size_t) size); | ||
| bufferSize = size; | ||
| } | ||
|
|
||
| clear(); | ||
| } | ||
|
|
||
| void clear() noexcept | ||
| { | ||
| buffer.clear ((size_t) bufferSize); | ||
| } | ||
|
|
||
| inline float process (const float input) noexcept | ||
| { | ||
| const float bufferedValue = buffer [bufferIndex]; | ||
| float temp = input + (bufferedValue * 0.5f); | ||
| JUCE_UNDENORMALISE (temp); | ||
| buffer [bufferIndex] = temp; | ||
| bufferIndex = (bufferIndex + 1) % bufferSize; | ||
| return bufferedValue - input; | ||
| } | ||
|
|
||
| private: | ||
| HeapBlock<float> buffer; | ||
| int bufferSize, bufferIndex; | ||
|
|
||
| JUCE_DECLARE_NON_COPYABLE (AllPassFilter) | ||
| }; | ||
|
|
||
| enum { numCombs = 8, numAllPasses = 4, numChannels = 2 }; | ||
|
|
||
| CombFilter comb [numChannels][numCombs]; | ||
| AllPassFilter allPass [numChannels][numAllPasses]; | ||
|
|
||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Reverb) | ||
| }; | ||
|
|
||
|
|
||
| #endif // __JUCE_REVERB_JUCEHEADER__ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| /* | ||
| ============================================================================== | ||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | ||
| Copyright 2004-11 by Raw Material Software Ltd. | ||
| ------------------------------------------------------------------------------ | ||
| JUCE can be redistributed and/or modified under the terms of the GNU General | ||
| Public License (Version 2), as published by the Free Software Foundation. | ||
| A copy of the license is included in the JUCE distribution, or can be found | ||
| online at www.gnu.org/licenses. | ||
| JUCE is distributed in the hope that it will be useful, but WITHOUT ANY | ||
| WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR | ||
| A PARTICULAR PURPOSE. See the GNU General Public License for more details. | ||
| ------------------------------------------------------------------------------ | ||
| To release a closed-source product which uses JUCE, commercial licenses are | ||
| available: visit www.rawmaterialsoftware.com/juce for more information. | ||
| ============================================================================== | ||
| */ | ||
|
|
||
| #if defined (__JUCE_AUDIO_BASICS_JUCEHEADER__) && ! JUCE_AMALGAMATED_INCLUDE | ||
| /* 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 | ||
|
|
||
| // Your project must contain an AppConfig.h file with your project-specific settings in it, | ||
| // and your header search path must make it accessible to the module's files. | ||
| #include "AppConfig.h" | ||
|
|
||
| #include "juce_audio_basics.h" | ||
|
|
||
| namespace juce | ||
| { | ||
|
|
||
| // START_AUTOINCLUDE buffers/*.cpp, effects/*.cpp, midi/*.cpp, sources/*.cpp, synthesisers/*.cpp | ||
| #include "buffers/juce_AudioDataConverters.cpp" | ||
| #include "buffers/juce_AudioSampleBuffer.cpp" | ||
| #include "effects/juce_IIRFilter.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 "sources/juce_BufferingAudioSource.cpp" | ||
| #include "sources/juce_ChannelRemappingAudioSource.cpp" | ||
| #include "sources/juce_IIRFilterAudioSource.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" | ||
| // END_AUTOINCLUDE | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,100 @@ | ||
| /* | ||
| ============================================================================== | ||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | ||
| Copyright 2004-11 by Raw Material Software Ltd. | ||
| ------------------------------------------------------------------------------ | ||
| JUCE can be redistributed and/or modified under the terms of the GNU General | ||
| Public License (Version 2), as published by the Free Software Foundation. | ||
| A copy of the license is included in the JUCE distribution, or can be found | ||
| online at www.gnu.org/licenses. | ||
| JUCE is distributed in the hope that it will be useful, but WITHOUT ANY | ||
| WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR | ||
| A PARTICULAR PURPOSE. See the GNU General Public License for more details. | ||
| ------------------------------------------------------------------------------ | ||
| To release a closed-source product which uses JUCE, commercial licenses are | ||
| available: visit www.rawmaterialsoftware.com/juce for more information. | ||
| ============================================================================== | ||
| */ | ||
|
|
||
| #ifndef __JUCE_AUDIO_BASICS_JUCEHEADER__ | ||
| #define __JUCE_AUDIO_BASICS_JUCEHEADER__ | ||
|
|
||
| #include "../juce_core/juce_core.h" | ||
|
|
||
| //============================================================================= | ||
| namespace juce | ||
| { | ||
|
|
||
| // START_AUTOINCLUDE buffers, effects, midi, sources, synthesisers | ||
| #ifndef __JUCE_AUDIODATACONVERTERS_JUCEHEADER__ | ||
| #include "buffers/juce_AudioDataConverters.h" | ||
| #endif | ||
| #ifndef __JUCE_AUDIOSAMPLEBUFFER_JUCEHEADER__ | ||
| #include "buffers/juce_AudioSampleBuffer.h" | ||
| #endif | ||
| #ifndef __JUCE_DECIBELS_JUCEHEADER__ | ||
| #include "effects/juce_Decibels.h" | ||
| #endif | ||
| #ifndef __JUCE_IIRFILTER_JUCEHEADER__ | ||
| #include "effects/juce_IIRFilter.h" | ||
| #endif | ||
| #ifndef __JUCE_REVERB_JUCEHEADER__ | ||
| #include "effects/juce_Reverb.h" | ||
| #endif | ||
| #ifndef __JUCE_MIDIBUFFER_JUCEHEADER__ | ||
| #include "midi/juce_MidiBuffer.h" | ||
| #endif | ||
| #ifndef __JUCE_MIDIFILE_JUCEHEADER__ | ||
| #include "midi/juce_MidiFile.h" | ||
| #endif | ||
| #ifndef __JUCE_MIDIKEYBOARDSTATE_JUCEHEADER__ | ||
| #include "midi/juce_MidiKeyboardState.h" | ||
| #endif | ||
| #ifndef __JUCE_MIDIMESSAGE_JUCEHEADER__ | ||
| #include "midi/juce_MidiMessage.h" | ||
| #endif | ||
| #ifndef __JUCE_MIDIMESSAGESEQUENCE_JUCEHEADER__ | ||
| #include "midi/juce_MidiMessageSequence.h" | ||
| #endif | ||
| #ifndef __JUCE_AUDIOSOURCE_JUCEHEADER__ | ||
| #include "sources/juce_AudioSource.h" | ||
| #endif | ||
| #ifndef __JUCE_BUFFERINGAUDIOSOURCE_JUCEHEADER__ | ||
| #include "sources/juce_BufferingAudioSource.h" | ||
| #endif | ||
| #ifndef __JUCE_CHANNELREMAPPINGAUDIOSOURCE_JUCEHEADER__ | ||
| #include "sources/juce_ChannelRemappingAudioSource.h" | ||
| #endif | ||
| #ifndef __JUCE_IIRFILTERAUDIOSOURCE_JUCEHEADER__ | ||
| #include "sources/juce_IIRFilterAudioSource.h" | ||
| #endif | ||
| #ifndef __JUCE_MIXERAUDIOSOURCE_JUCEHEADER__ | ||
| #include "sources/juce_MixerAudioSource.h" | ||
| #endif | ||
| #ifndef __JUCE_POSITIONABLEAUDIOSOURCE_JUCEHEADER__ | ||
| #include "sources/juce_PositionableAudioSource.h" | ||
| #endif | ||
| #ifndef __JUCE_RESAMPLINGAUDIOSOURCE_JUCEHEADER__ | ||
| #include "sources/juce_ResamplingAudioSource.h" | ||
| #endif | ||
| #ifndef __JUCE_REVERBAUDIOSOURCE_JUCEHEADER__ | ||
| #include "sources/juce_ReverbAudioSource.h" | ||
| #endif | ||
| #ifndef __JUCE_TONEGENERATORAUDIOSOURCE_JUCEHEADER__ | ||
| #include "sources/juce_ToneGeneratorAudioSource.h" | ||
| #endif | ||
| #ifndef __JUCE_SYNTHESISER_JUCEHEADER__ | ||
| #include "synthesisers/juce_Synthesiser.h" | ||
| #endif | ||
| // END_AUTOINCLUDE | ||
|
|
||
| } | ||
|
|
||
| #endif // __JUCE_AUDIO_BASICS_JUCEHEADER__ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| /* | ||
| ============================================================================== | ||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | ||
| Copyright 2004-11 by Raw Material Software Ltd. | ||
| ------------------------------------------------------------------------------ | ||
| JUCE can be redistributed and/or modified under the terms of the GNU General | ||
| Public License (Version 2), as published by the Free Software Foundation. | ||
| A copy of the license is included in the JUCE distribution, or can be found | ||
| online at www.gnu.org/licenses. | ||
| JUCE is distributed in the hope that it will be useful, but WITHOUT ANY | ||
| WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR | ||
| A PARTICULAR PURPOSE. See the GNU General Public License for more details. | ||
| ------------------------------------------------------------------------------ | ||
| To release a closed-source product which uses JUCE, commercial licenses are | ||
| available: visit www.rawmaterialsoftware.com/juce for more information. | ||
| ============================================================================== | ||
| */ | ||
|
|
||
| #include "juce_audio_basics.cpp" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| { | ||
| "id": "juce_audio_basics", | ||
| "name": "JUCE audio and midi data classes", | ||
| "version": "2.0.32", | ||
| "description": "Classes for audio buffer manipulation, midi message handling, synthesis, etc", | ||
| "website": "http://www.juce.com/juce", | ||
| "license": "GPL/Commercial", | ||
|
|
||
| "dependencies": [ { "id": "juce_core", "version": "matching" } ], | ||
|
|
||
| "include": "juce_audio_basics.h", | ||
|
|
||
| "compile": [ { "file": "juce_audio_basics.cpp", "target": "! xcode" }, | ||
| { "file": "juce_audio_basics.mm", "target": "xcode" } ], | ||
|
|
||
| "browse": [ "buffers/*", | ||
| "midi/*", | ||
| "effects/*", | ||
| "sources/*", | ||
| "synthesisers/*" ] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,290 @@ | ||
| /* | ||
| ============================================================================== | ||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | ||
| Copyright 2004-11 by Raw Material Software Ltd. | ||
| ------------------------------------------------------------------------------ | ||
| JUCE can be redistributed and/or modified under the terms of the GNU General | ||
| Public License (Version 2), as published by the Free Software Foundation. | ||
| A copy of the license is included in the JUCE distribution, or can be found | ||
| online at www.gnu.org/licenses. | ||
| JUCE is distributed in the hope that it will be useful, but WITHOUT ANY | ||
| WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR | ||
| A PARTICULAR PURPOSE. See the GNU General Public License for more details. | ||
| ------------------------------------------------------------------------------ | ||
| To release a closed-source product which uses JUCE, commercial licenses are | ||
| available: visit www.rawmaterialsoftware.com/juce for more information. | ||
| ============================================================================== | ||
| */ | ||
|
|
||
| namespace MidiBufferHelpers | ||
| { | ||
| inline int getEventTime (const void* const d) noexcept | ||
| { | ||
| return *static_cast <const int*> (d); | ||
| } | ||
|
|
||
| inline uint16 getEventDataSize (const void* const d) noexcept | ||
| { | ||
| return *reinterpret_cast <const uint16*> (static_cast <const char*> (d) + sizeof (int)); | ||
| } | ||
|
|
||
| inline uint16 getEventTotalSize (const void* const d) noexcept | ||
| { | ||
| return getEventDataSize (d) + sizeof (int) + sizeof (uint16); | ||
| } | ||
|
|
||
| static int findActualEventLength (const uint8* const data, const int maxBytes) noexcept | ||
| { | ||
| unsigned int byte = (unsigned int) *data; | ||
| int size = 0; | ||
|
|
||
| if (byte == 0xf0 || byte == 0xf7) | ||
| { | ||
| const uint8* d = data + 1; | ||
|
|
||
| while (d < data + maxBytes) | ||
| if (*d++ == 0xf7) | ||
| break; | ||
|
|
||
| size = (int) (d - data); | ||
| } | ||
| else if (byte == 0xff) | ||
| { | ||
| int n; | ||
| const int bytesLeft = MidiMessage::readVariableLengthVal (data + 1, n); | ||
| size = jmin (maxBytes, n + 2 + bytesLeft); | ||
| } | ||
| else if (byte >= 0x80) | ||
| { | ||
| size = jmin (maxBytes, MidiMessage::getMessageLengthFromFirstByte ((uint8) byte)); | ||
| } | ||
|
|
||
| return size; | ||
| } | ||
| } | ||
|
|
||
| //============================================================================== | ||
| MidiBuffer::MidiBuffer() noexcept | ||
| : bytesUsed (0) | ||
| { | ||
| } | ||
|
|
||
| MidiBuffer::MidiBuffer (const MidiMessage& message) noexcept | ||
| : bytesUsed (0) | ||
| { | ||
| addEvent (message, 0); | ||
| } | ||
|
|
||
| MidiBuffer::MidiBuffer (const MidiBuffer& other) noexcept | ||
| : data (other.data), | ||
| bytesUsed (other.bytesUsed) | ||
| { | ||
| } | ||
|
|
||
| MidiBuffer& MidiBuffer::operator= (const MidiBuffer& other) noexcept | ||
| { | ||
| bytesUsed = other.bytesUsed; | ||
| data = other.data; | ||
|
|
||
| return *this; | ||
| } | ||
|
|
||
| void MidiBuffer::swapWith (MidiBuffer& other) noexcept | ||
| { | ||
| data.swapWith (other.data); | ||
| std::swap (bytesUsed, other.bytesUsed); | ||
| } | ||
|
|
||
| MidiBuffer::~MidiBuffer() | ||
| { | ||
| } | ||
|
|
||
| inline uint8* MidiBuffer::getData() const noexcept | ||
| { | ||
| return static_cast <uint8*> (data.getData()); | ||
| } | ||
|
|
||
| void MidiBuffer::clear() noexcept | ||
| { | ||
| bytesUsed = 0; | ||
| } | ||
|
|
||
| void MidiBuffer::clear (const int startSample, const int numSamples) | ||
| { | ||
| uint8* const start = findEventAfter (getData(), startSample - 1); | ||
| uint8* const end = findEventAfter (start, startSample + numSamples - 1); | ||
|
|
||
| if (end > start) | ||
| { | ||
| const int bytesToMove = bytesUsed - (int) (end - getData()); | ||
|
|
||
| if (bytesToMove > 0) | ||
| memmove (start, end, (size_t) bytesToMove); | ||
|
|
||
| bytesUsed -= (int) (end - start); | ||
| } | ||
| } | ||
|
|
||
| void MidiBuffer::addEvent (const MidiMessage& m, const int sampleNumber) | ||
| { | ||
| addEvent (m.getRawData(), m.getRawDataSize(), sampleNumber); | ||
| } | ||
|
|
||
| void MidiBuffer::addEvent (const void* const newData, const int maxBytes, const int sampleNumber) | ||
| { | ||
| const int numBytes = MidiBufferHelpers::findActualEventLength (static_cast <const uint8*> (newData), maxBytes); | ||
|
|
||
| if (numBytes > 0) | ||
| { | ||
| size_t spaceNeeded = (size_t) bytesUsed + (size_t) numBytes + sizeof (int) + sizeof (uint16); | ||
| data.ensureSize ((spaceNeeded + spaceNeeded / 2 + 8) & ~(size_t) 7); | ||
|
|
||
| uint8* d = findEventAfter (getData(), sampleNumber); | ||
| const int bytesToMove = bytesUsed - (int) (d - getData()); | ||
|
|
||
| if (bytesToMove > 0) | ||
| memmove (d + numBytes + sizeof (int) + sizeof (uint16), d, (size_t) bytesToMove); | ||
|
|
||
| *reinterpret_cast <int*> (d) = sampleNumber; | ||
| d += sizeof (int); | ||
| *reinterpret_cast <uint16*> (d) = (uint16) numBytes; | ||
| d += sizeof (uint16); | ||
|
|
||
| memcpy (d, newData, (size_t) numBytes); | ||
|
|
||
| bytesUsed += sizeof (int) + sizeof (uint16) + (size_t) numBytes; | ||
| } | ||
| } | ||
|
|
||
| void MidiBuffer::addEvents (const MidiBuffer& otherBuffer, | ||
| const int startSample, | ||
| const int numSamples, | ||
| const int sampleDeltaToAdd) | ||
| { | ||
| Iterator i (otherBuffer); | ||
| i.setNextSamplePosition (startSample); | ||
|
|
||
| const uint8* eventData; | ||
| int eventSize, position; | ||
|
|
||
| while (i.getNextEvent (eventData, eventSize, position) | ||
| && (position < startSample + numSamples || numSamples < 0)) | ||
| { | ||
| addEvent (eventData, eventSize, position + sampleDeltaToAdd); | ||
| } | ||
| } | ||
|
|
||
| void MidiBuffer::ensureSize (size_t minimumNumBytes) | ||
| { | ||
| data.ensureSize (minimumNumBytes); | ||
| } | ||
|
|
||
| bool MidiBuffer::isEmpty() const noexcept | ||
| { | ||
| return bytesUsed == 0; | ||
| } | ||
|
|
||
| int MidiBuffer::getNumEvents() const noexcept | ||
| { | ||
| int n = 0; | ||
| const uint8* d = getData(); | ||
| const uint8* const end = d + bytesUsed; | ||
|
|
||
| while (d < end) | ||
| { | ||
| d += MidiBufferHelpers::getEventTotalSize (d); | ||
| ++n; | ||
| } | ||
|
|
||
| return n; | ||
| } | ||
|
|
||
| int MidiBuffer::getFirstEventTime() const noexcept | ||
| { | ||
| return bytesUsed > 0 ? MidiBufferHelpers::getEventTime (data.getData()) : 0; | ||
| } | ||
|
|
||
| int MidiBuffer::getLastEventTime() const noexcept | ||
| { | ||
| if (bytesUsed == 0) | ||
| return 0; | ||
|
|
||
| const uint8* d = getData(); | ||
| const uint8* const endData = d + bytesUsed; | ||
|
|
||
| for (;;) | ||
| { | ||
| const uint8* const nextOne = d + MidiBufferHelpers::getEventTotalSize (d); | ||
|
|
||
| if (nextOne >= endData) | ||
| return MidiBufferHelpers::getEventTime (d); | ||
|
|
||
| d = nextOne; | ||
| } | ||
| } | ||
|
|
||
| uint8* MidiBuffer::findEventAfter (uint8* d, const int samplePosition) const noexcept | ||
| { | ||
| const uint8* const endData = getData() + bytesUsed; | ||
|
|
||
| while (d < endData && MidiBufferHelpers::getEventTime (d) <= samplePosition) | ||
| d += MidiBufferHelpers::getEventTotalSize (d); | ||
|
|
||
| return d; | ||
| } | ||
|
|
||
| //============================================================================== | ||
| MidiBuffer::Iterator::Iterator (const MidiBuffer& buffer_) noexcept | ||
| : buffer (buffer_), | ||
| data (buffer_.getData()) | ||
| { | ||
| } | ||
|
|
||
| MidiBuffer::Iterator::~Iterator() noexcept | ||
| { | ||
| } | ||
|
|
||
| //============================================================================== | ||
| void MidiBuffer::Iterator::setNextSamplePosition (const int samplePosition) noexcept | ||
| { | ||
| data = buffer.getData(); | ||
| const uint8* dataEnd = data + buffer.bytesUsed; | ||
|
|
||
| while (data < dataEnd && MidiBufferHelpers::getEventTime (data) < samplePosition) | ||
| data += MidiBufferHelpers::getEventTotalSize (data); | ||
| } | ||
|
|
||
| bool MidiBuffer::Iterator::getNextEvent (const uint8* &midiData, int& numBytes, int& samplePosition) noexcept | ||
| { | ||
| if (data >= buffer.getData() + buffer.bytesUsed) | ||
| return false; | ||
|
|
||
| samplePosition = MidiBufferHelpers::getEventTime (data); | ||
| numBytes = MidiBufferHelpers::getEventDataSize (data); | ||
| data += sizeof (int) + sizeof (uint16); | ||
| midiData = data; | ||
| data += numBytes; | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| bool MidiBuffer::Iterator::getNextEvent (MidiMessage& result, int& samplePosition) noexcept | ||
| { | ||
| if (data >= buffer.getData() + buffer.bytesUsed) | ||
| return false; | ||
|
|
||
| samplePosition = MidiBufferHelpers::getEventTime (data); | ||
| const int numBytes = MidiBufferHelpers::getEventDataSize (data); | ||
| data += sizeof (int) + sizeof (uint16); | ||
| result = MidiMessage (data, numBytes, samplePosition); | ||
| data += numBytes; | ||
|
|
||
| return true; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,241 @@ | ||
| /* | ||
| ============================================================================== | ||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | ||
| Copyright 2004-11 by Raw Material Software Ltd. | ||
| ------------------------------------------------------------------------------ | ||
| JUCE can be redistributed and/or modified under the terms of the GNU General | ||
| Public License (Version 2), as published by the Free Software Foundation. | ||
| A copy of the license is included in the JUCE distribution, or can be found | ||
| online at www.gnu.org/licenses. | ||
| JUCE is distributed in the hope that it will be useful, but WITHOUT ANY | ||
| WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR | ||
| A PARTICULAR PURPOSE. See the GNU General Public License for more details. | ||
| ------------------------------------------------------------------------------ | ||
| To release a closed-source product which uses JUCE, commercial licenses are | ||
| available: visit www.rawmaterialsoftware.com/juce for more information. | ||
| ============================================================================== | ||
| */ | ||
|
|
||
| #ifndef __JUCE_MIDIBUFFER_JUCEHEADER__ | ||
| #define __JUCE_MIDIBUFFER_JUCEHEADER__ | ||
|
|
||
| #include "juce_MidiMessage.h" | ||
|
|
||
|
|
||
| //============================================================================== | ||
| /** | ||
| Holds a sequence of time-stamped midi events. | ||
| Analogous to the AudioSampleBuffer, 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 | ||
| */ | ||
| class JUCE_API MidiBuffer | ||
| { | ||
| public: | ||
| //============================================================================== | ||
| /** Creates an empty MidiBuffer. */ | ||
| MidiBuffer() noexcept; | ||
|
|
||
| /** Creates a MidiBuffer containing a single midi message. */ | ||
| explicit MidiBuffer (const MidiMessage& message) noexcept; | ||
|
|
||
| /** Creates a copy of another MidiBuffer. */ | ||
| MidiBuffer (const MidiBuffer& other) noexcept; | ||
|
|
||
| /** Makes a copy of another MidiBuffer. */ | ||
| MidiBuffer& operator= (const MidiBuffer& other) noexcept; | ||
|
|
||
| /** Destructor */ | ||
| ~MidiBuffer(); | ||
|
|
||
| //============================================================================== | ||
| /** 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 MidiBuffer::Iterator 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 MidiBuffer::Iterator object | ||
| */ | ||
| void 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 MidiBuffer::Iterator object | ||
| */ | ||
| void 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& other) 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); | ||
|
|
||
| //============================================================================== | ||
| /** | ||
| Used to iterate through the events in a MidiBuffer. | ||
| Note that altering the buffer while an iterator is using it isn't a | ||
| safe operation. | ||
| @see MidiBuffer | ||
| */ | ||
| class JUCE_API Iterator | ||
| { | ||
| public: | ||
| //============================================================================== | ||
| /** Creates an Iterator for this MidiBuffer. */ | ||
| Iterator (const MidiBuffer& buffer) noexcept; | ||
|
|
||
| /** Destructor. */ | ||
| ~Iterator() 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 not set) | ||
| @param samplePosition on return, this will be the position of the event | ||
| @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 | ||
| @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; | ||
| const uint8* data; | ||
|
|
||
| JUCE_DECLARE_NON_COPYABLE (Iterator) | ||
| }; | ||
|
|
||
| private: | ||
| //============================================================================== | ||
| friend class MidiBuffer::Iterator; | ||
| MemoryBlock data; | ||
| int bytesUsed; | ||
|
|
||
| uint8* getData() const noexcept; | ||
| uint8* findEventAfter (uint8*, int samplePosition) const noexcept; | ||
|
|
||
| JUCE_LEAK_DETECTOR (MidiBuffer) | ||
| }; | ||
|
|
||
|
|
||
| #endif // __JUCE_MIDIBUFFER_JUCEHEADER__ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,187 @@ | ||
| /* | ||
| ============================================================================== | ||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | ||
| Copyright 2004-11 by Raw Material Software Ltd. | ||
| ------------------------------------------------------------------------------ | ||
| JUCE can be redistributed and/or modified under the terms of the GNU General | ||
| Public License (Version 2), as published by the Free Software Foundation. | ||
| A copy of the license is included in the JUCE distribution, or can be found | ||
| online at www.gnu.org/licenses. | ||
| JUCE is distributed in the hope that it will be useful, but WITHOUT ANY | ||
| WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR | ||
| A PARTICULAR PURPOSE. See the GNU General Public License for more details. | ||
| ------------------------------------------------------------------------------ | ||
| To release a closed-source product which uses JUCE, commercial licenses are | ||
| available: visit www.rawmaterialsoftware.com/juce for more information. | ||
| ============================================================================== | ||
| */ | ||
|
|
||
| #ifndef __JUCE_MIDIFILE_JUCEHEADER__ | ||
| #define __JUCE_MIDIFILE_JUCEHEADER__ | ||
|
|
||
| #include "juce_MidiMessageSequence.h" | ||
|
|
||
|
|
||
| //============================================================================== | ||
| /** | ||
| 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 | ||
| */ | ||
| class JUCE_API MidiFile | ||
| { | ||
| public: | ||
| //============================================================================== | ||
| /** Creates an empty MidiFile object. | ||
| */ | ||
| MidiFile(); | ||
|
|
||
| /** Destructor. */ | ||
| ~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; | ||
|
|
||
| /** 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. | ||
| @returns true if the stream was read successfully | ||
| */ | ||
| bool readFrom (InputStream& sourceStream); | ||
|
|
||
| /** Writes the midi tracks as a standard midi file. | ||
| @returns true if the operation succeeded. | ||
| */ | ||
| bool writeTo (OutputStream& destStream); | ||
|
|
||
| /** 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* data, int size); | ||
| void writeTrack (OutputStream& mainOut, int trackNum); | ||
|
|
||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiFile) | ||
| }; | ||
|
|
||
|
|
||
| #endif // __JUCE_MIDIFILE_JUCEHEADER__ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,184 @@ | ||
| /* | ||
| ============================================================================== | ||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | ||
| Copyright 2004-11 by Raw Material Software Ltd. | ||
| ------------------------------------------------------------------------------ | ||
| JUCE can be redistributed and/or modified under the terms of the GNU General | ||
| Public License (Version 2), as published by the Free Software Foundation. | ||
| A copy of the license is included in the JUCE distribution, or can be found | ||
| online at www.gnu.org/licenses. | ||
| JUCE is distributed in the hope that it will be useful, but WITHOUT ANY | ||
| WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR | ||
| A PARTICULAR PURPOSE. See the GNU General Public License for more details. | ||
| ------------------------------------------------------------------------------ | ||
| To release a closed-source product which uses JUCE, commercial licenses are | ||
| available: visit www.rawmaterialsoftware.com/juce for more information. | ||
| ============================================================================== | ||
| */ | ||
|
|
||
| MidiKeyboardState::MidiKeyboardState() | ||
| { | ||
| zerostruct (noteStates); | ||
| } | ||
|
|
||
| MidiKeyboardState::~MidiKeyboardState() | ||
| { | ||
| } | ||
|
|
||
| //============================================================================== | ||
| 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, (int) 128) | ||
| && (noteStates[n] & (1 << (midiChannel - 1))) != 0; | ||
| } | ||
|
|
||
| bool MidiKeyboardState::isNoteOnForChannels (const int midiChannelMask, const int n) const noexcept | ||
| { | ||
| return isPositiveAndBelow (n, (int) 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, (int) 128)); | ||
|
|
||
| const ScopedLock sl (lock); | ||
|
|
||
| if (isPositiveAndBelow (midiNoteNumber, (int) 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, (int) 128)) | ||
| { | ||
| noteStates [midiNoteNumber] |= (1 << (midiChannel - 1)); | ||
|
|
||
| for (int i = listeners.size(); --i >= 0;) | ||
| listeners.getUnchecked(i)->handleNoteOn (this, midiChannel, midiNoteNumber, velocity); | ||
| } | ||
| } | ||
|
|
||
| void MidiKeyboardState::noteOff (const int midiChannel, const int midiNoteNumber) | ||
| { | ||
| 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); | ||
| } | ||
| } | ||
|
|
||
| void MidiKeyboardState::noteOffInternal (const int midiChannel, const int midiNoteNumber) | ||
| { | ||
| if (isNoteOn (midiChannel, midiNoteNumber)) | ||
| { | ||
| noteStates [midiNoteNumber] &= ~(1 << (midiChannel - 1)); | ||
|
|
||
| for (int i = listeners.size(); --i >= 0;) | ||
| listeners.getUnchecked(i)->handleNoteOff (this, midiChannel, midiNoteNumber); | ||
| } | ||
| } | ||
|
|
||
| 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); | ||
| } | ||
| } | ||
|
|
||
| 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()); | ||
| } | ||
| else if (message.isAllNotesOff()) | ||
| { | ||
| for (int i = 0; i < 128; ++i) | ||
| noteOffInternal (message.getChannel(), i); | ||
| } | ||
| } | ||
|
|
||
| void MidiKeyboardState::processNextMidiBuffer (MidiBuffer& buffer, | ||
| const int startSample, | ||
| const int numSamples, | ||
| const bool injectIndirectEvents) | ||
| { | ||
| MidiBuffer::Iterator i (buffer); | ||
| MidiMessage message (0xf4, 0.0); | ||
| int time; | ||
|
|
||
| const ScopedLock sl (lock); | ||
|
|
||
| while (i.getNextEvent (message, time)) | ||
| processNextMidiEvent (message); | ||
|
|
||
| if (injectIndirectEvents) | ||
| { | ||
| MidiBuffer::Iterator i2 (eventsToAdd); | ||
| const int firstEventToAdd = eventsToAdd.getFirstEventTime(); | ||
| const double scaleFactor = numSamples / (double) (eventsToAdd.getLastEventTime() + 1 - firstEventToAdd); | ||
|
|
||
| while (i2.getNextEvent (message, time)) | ||
| { | ||
| const int pos = jlimit (0, numSamples - 1, roundToInt ((time - firstEventToAdd) * scaleFactor)); | ||
| buffer.addEvent (message, startSample + pos); | ||
| } | ||
| } | ||
|
|
||
| eventsToAdd.clear(); | ||
| } | ||
|
|
||
| //============================================================================== | ||
| void MidiKeyboardState::addListener (MidiKeyboardStateListener* const listener) | ||
| { | ||
| const ScopedLock sl (lock); | ||
| listeners.addIfNotAlreadyThere (listener); | ||
| } | ||
|
|
||
| void MidiKeyboardState::removeListener (MidiKeyboardStateListener* const listener) | ||
| { | ||
| const ScopedLock sl (lock); | ||
| listeners.removeFirstMatchingValue (listener); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,209 @@ | ||
| /* | ||
| ============================================================================== | ||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | ||
| Copyright 2004-11 by Raw Material Software Ltd. | ||
| ------------------------------------------------------------------------------ | ||
| JUCE can be redistributed and/or modified under the terms of the GNU General | ||
| Public License (Version 2), as published by the Free Software Foundation. | ||
| A copy of the license is included in the JUCE distribution, or can be found | ||
| online at www.gnu.org/licenses. | ||
| JUCE is distributed in the hope that it will be useful, but WITHOUT ANY | ||
| WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR | ||
| A PARTICULAR PURPOSE. See the GNU General Public License for more details. | ||
| ------------------------------------------------------------------------------ | ||
| To release a closed-source product which uses JUCE, commercial licenses are | ||
| available: visit www.rawmaterialsoftware.com/juce for more information. | ||
| ============================================================================== | ||
| */ | ||
|
|
||
| #ifndef __JUCE_MIDIKEYBOARDSTATE_JUCEHEADER__ | ||
| #define __JUCE_MIDIKEYBOARDSTATE_JUCEHEADER__ | ||
|
|
||
| #include "juce_MidiBuffer.h" | ||
| class MidiKeyboardState; | ||
|
|
||
|
|
||
| //============================================================================== | ||
| /** | ||
| Receives events from a MidiKeyboardState object. | ||
| @see MidiKeyboardState | ||
| */ | ||
| class JUCE_API MidiKeyboardStateListener | ||
| { | ||
| public: | ||
| //============================================================================== | ||
| MidiKeyboardStateListener() noexcept {} | ||
| virtual ~MidiKeyboardStateListener() {} | ||
|
|
||
| //============================================================================== | ||
| /** 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) = 0; | ||
| }; | ||
|
|
||
|
|
||
| //============================================================================== | ||
| /** | ||
| 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 individiual 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(). | ||
| */ | ||
| class JUCE_API MidiKeyboardState | ||
| { | ||
| public: | ||
| //============================================================================== | ||
| MidiKeyboardState(); | ||
| ~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 acutally down for the given channel, this method will in fact do nothing. | ||
| */ | ||
| void noteOff (int midiChannel, int midiNoteNumber); | ||
|
|
||
| /** 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); | ||
|
|
||
| //============================================================================== | ||
| /** Registers a listener for callbacks when keys go up or down. | ||
| @see removeListener | ||
| */ | ||
| void addListener (MidiKeyboardStateListener* listener); | ||
|
|
||
| /** Deregisters a listener. | ||
| @see addListener | ||
| */ | ||
| void removeListener (MidiKeyboardStateListener* listener); | ||
|
|
||
| private: | ||
| //============================================================================== | ||
| CriticalSection lock; | ||
| uint16 noteStates [128]; | ||
| MidiBuffer eventsToAdd; | ||
| Array <MidiKeyboardStateListener*> listeners; | ||
|
|
||
| void noteOnInternal (int midiChannel, int midiNoteNumber, float velocity); | ||
| void noteOffInternal (int midiChannel, int midiNoteNumber); | ||
|
|
||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiKeyboardState) | ||
| }; | ||
|
|
||
|
|
||
| #endif // __JUCE_MIDIKEYBOARDSTATE_JUCEHEADER__ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,335 @@ | ||
| /* | ||
| ============================================================================== | ||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | ||
| Copyright 2004-11 by Raw Material Software Ltd. | ||
| ------------------------------------------------------------------------------ | ||
| JUCE can be redistributed and/or modified under the terms of the GNU General | ||
| Public License (Version 2), as published by the Free Software Foundation. | ||
| A copy of the license is included in the JUCE distribution, or can be found | ||
| online at www.gnu.org/licenses. | ||
| JUCE is distributed in the hope that it will be useful, but WITHOUT ANY | ||
| WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR | ||
| A PARTICULAR PURPOSE. See the GNU General Public License for more details. | ||
| ------------------------------------------------------------------------------ | ||
| To release a closed-source product which uses JUCE, commercial licenses are | ||
| available: visit www.rawmaterialsoftware.com/juce for more information. | ||
| ============================================================================== | ||
| */ | ||
|
|
||
| MidiMessageSequence::MidiMessageSequence() | ||
| { | ||
| } | ||
|
|
||
| MidiMessageSequence::MidiMessageSequence (const MidiMessageSequence& other) | ||
| { | ||
| list.ensureStorageAllocated (other.list.size()); | ||
|
|
||
| for (int i = 0; i < other.list.size(); ++i) | ||
| list.add (new MidiEventHolder (other.list.getUnchecked(i)->message)); | ||
| } | ||
|
|
||
| MidiMessageSequence& MidiMessageSequence::operator= (const MidiMessageSequence& other) | ||
| { | ||
| MidiMessageSequence otherCopy (other); | ||
| swapWith (otherCopy); | ||
| return *this; | ||
| } | ||
|
|
||
| void MidiMessageSequence::swapWith (MidiMessageSequence& other) noexcept | ||
| { | ||
| list.swapWithArray (other.list); | ||
| } | ||
|
|
||
| MidiMessageSequence::~MidiMessageSequence() | ||
| { | ||
| } | ||
|
|
||
| void MidiMessageSequence::clear() | ||
| { | ||
| list.clear(); | ||
| } | ||
|
|
||
| int MidiMessageSequence::getNumEvents() const | ||
| { | ||
| return list.size(); | ||
| } | ||
|
|
||
| MidiMessageSequence::MidiEventHolder* MidiMessageSequence::getEventPointer (const int index) const | ||
| { | ||
| return list [index]; | ||
| } | ||
|
|
||
| double MidiMessageSequence::getTimeOfMatchingKeyUp (const int index) const | ||
| { | ||
| if (const MidiEventHolder* const meh = list [index]) | ||
| if (meh->noteOffObject != nullptr) | ||
| return meh->noteOffObject->message.getTimeStamp(); | ||
|
|
||
| return 0.0; | ||
| } | ||
|
|
||
| int MidiMessageSequence::getIndexOfMatchingKeyUp (const int index) const | ||
| { | ||
| if (const MidiEventHolder* const meh = list [index]) | ||
| return list.indexOf (meh->noteOffObject); | ||
|
|
||
| return -1; | ||
| } | ||
|
|
||
| int MidiMessageSequence::getIndexOf (MidiEventHolder* const event) const | ||
| { | ||
| return list.indexOf (event); | ||
| } | ||
|
|
||
| int MidiMessageSequence::getNextIndexAtTime (const double timeStamp) const | ||
| { | ||
| const int numEvents = list.size(); | ||
|
|
||
| int i; | ||
| for (i = 0; i < numEvents; ++i) | ||
| if (list.getUnchecked(i)->message.getTimeStamp() >= timeStamp) | ||
| break; | ||
|
|
||
| return i; | ||
| } | ||
|
|
||
| //============================================================================== | ||
| double MidiMessageSequence::getStartTime() const | ||
| { | ||
| return getEventTime (0); | ||
| } | ||
|
|
||
| double MidiMessageSequence::getEndTime() const | ||
| { | ||
| return getEventTime (list.size() - 1); | ||
| } | ||
|
|
||
| double MidiMessageSequence::getEventTime (const int index) const | ||
| { | ||
| if (const MidiEventHolder* const meh = list [index]) | ||
| return meh->message.getTimeStamp(); | ||
|
|
||
| return 0.0; | ||
| } | ||
|
|
||
| //============================================================================== | ||
| MidiMessageSequence::MidiEventHolder* MidiMessageSequence::addEvent (const MidiMessage& newMessage, | ||
| double timeAdjustment) | ||
| { | ||
| MidiEventHolder* const newOne = new MidiEventHolder (newMessage); | ||
|
|
||
| timeAdjustment += newMessage.getTimeStamp(); | ||
| newOne->message.setTimeStamp (timeAdjustment); | ||
|
|
||
| int i; | ||
| for (i = list.size(); --i >= 0;) | ||
| if (list.getUnchecked(i)->message.getTimeStamp() <= timeAdjustment) | ||
| break; | ||
|
|
||
| list.insert (i + 1, newOne); | ||
| return newOne; | ||
| } | ||
|
|
||
| void MidiMessageSequence::deleteEvent (const int index, | ||
| const bool deleteMatchingNoteUp) | ||
| { | ||
| if (isPositiveAndBelow (index, list.size())) | ||
| { | ||
| if (deleteMatchingNoteUp) | ||
| deleteEvent (getIndexOfMatchingKeyUp (index), false); | ||
|
|
||
| list.remove (index); | ||
| } | ||
| } | ||
|
|
||
| struct MidiMessageSequenceSorter | ||
| { | ||
| static int compareElements (const MidiMessageSequence::MidiEventHolder* const first, | ||
| const MidiMessageSequence::MidiEventHolder* const second) noexcept | ||
| { | ||
| const double diff = first->message.getTimeStamp() - second->message.getTimeStamp(); | ||
| return (diff > 0) - (diff < 0); | ||
| } | ||
| }; | ||
|
|
||
| void MidiMessageSequence::addSequence (const MidiMessageSequence& other, | ||
| double timeAdjustment, | ||
| double firstAllowableTime, | ||
| double endOfAllowableDestTimes) | ||
| { | ||
| firstAllowableTime -= timeAdjustment; | ||
| endOfAllowableDestTimes -= timeAdjustment; | ||
|
|
||
| for (int i = 0; i < other.list.size(); ++i) | ||
| { | ||
| const MidiMessage& m = other.list.getUnchecked(i)->message; | ||
| const double t = m.getTimeStamp(); | ||
|
|
||
| if (t >= firstAllowableTime && t < endOfAllowableDestTimes) | ||
| { | ||
| MidiEventHolder* const newOne = new MidiEventHolder (m); | ||
| newOne->message.setTimeStamp (timeAdjustment + t); | ||
|
|
||
| list.add (newOne); | ||
| } | ||
| } | ||
|
|
||
| sort(); | ||
| } | ||
|
|
||
| //============================================================================== | ||
| void MidiMessageSequence::sort() | ||
| { | ||
| MidiMessageSequenceSorter sorter; | ||
| list.sort (sorter, true); | ||
| } | ||
|
|
||
| void MidiMessageSequence::updateMatchedPairs() | ||
| { | ||
| for (int i = 0; i < list.size(); ++i) | ||
| { | ||
| MidiEventHolder* const meh = list.getUnchecked(i); | ||
| const MidiMessage& m1 = meh->message; | ||
|
|
||
| if (m1.isNoteOn()) | ||
| { | ||
| meh->noteOffObject = nullptr; | ||
| const int note = m1.getNoteNumber(); | ||
| const int chan = m1.getChannel(); | ||
| const int len = list.size(); | ||
|
|
||
| for (int j = i + 1; j < len; ++j) | ||
| { | ||
| const MidiMessage& m = list.getUnchecked(j)->message; | ||
|
|
||
| if (m.getNoteNumber() == note && m.getChannel() == chan) | ||
| { | ||
| if (m.isNoteOff()) | ||
| { | ||
| meh->noteOffObject = list[j]; | ||
| break; | ||
| } | ||
| else if (m.isNoteOn()) | ||
| { | ||
| MidiEventHolder* const newEvent = new MidiEventHolder (MidiMessage::noteOff (chan, note)); | ||
| list.insert (j, newEvent); | ||
| newEvent->message.setTimeStamp (m.getTimeStamp()); | ||
| meh->noteOffObject = newEvent; | ||
| break; | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| void MidiMessageSequence::addTimeToMessages (const double delta) | ||
| { | ||
| for (int i = list.size(); --i >= 0;) | ||
| { | ||
| MidiMessage& mm = list.getUnchecked(i)->message; | ||
| mm.setTimeStamp (mm.getTimeStamp() + delta); | ||
| } | ||
| } | ||
|
|
||
| //============================================================================== | ||
| void MidiMessageSequence::extractMidiChannelMessages (const int channelNumberToExtract, | ||
| MidiMessageSequence& destSequence, | ||
| const bool alsoIncludeMetaEvents) const | ||
| { | ||
| for (int i = 0; i < list.size(); ++i) | ||
| { | ||
| const MidiMessage& mm = list.getUnchecked(i)->message; | ||
|
|
||
| if (mm.isForChannel (channelNumberToExtract) || (alsoIncludeMetaEvents && mm.isMetaEvent())) | ||
| destSequence.addEvent (mm); | ||
| } | ||
| } | ||
|
|
||
| void MidiMessageSequence::extractSysExMessages (MidiMessageSequence& destSequence) const | ||
| { | ||
| for (int i = 0; i < list.size(); ++i) | ||
| { | ||
| const MidiMessage& mm = list.getUnchecked(i)->message; | ||
|
|
||
| if (mm.isSysEx()) | ||
| destSequence.addEvent (mm); | ||
| } | ||
| } | ||
|
|
||
| void MidiMessageSequence::deleteMidiChannelMessages (const int channelNumberToRemove) | ||
| { | ||
| for (int i = list.size(); --i >= 0;) | ||
| if (list.getUnchecked(i)->message.isForChannel (channelNumberToRemove)) | ||
| list.remove(i); | ||
| } | ||
|
|
||
| void MidiMessageSequence::deleteSysExMessages() | ||
| { | ||
| for (int i = list.size(); --i >= 0;) | ||
| if (list.getUnchecked(i)->message.isSysEx()) | ||
| list.remove(i); | ||
| } | ||
|
|
||
| //============================================================================== | ||
| void MidiMessageSequence::createControllerUpdatesForTime (const int channelNumber, | ||
| const double time, | ||
| OwnedArray<MidiMessage>& dest) | ||
| { | ||
| bool doneProg = false; | ||
| bool donePitchWheel = false; | ||
| Array <int> doneControllers; | ||
| doneControllers.ensureStorageAllocated (32); | ||
|
|
||
| for (int i = list.size(); --i >= 0;) | ||
| { | ||
| const MidiMessage& mm = list.getUnchecked(i)->message; | ||
|
|
||
| if (mm.isForChannel (channelNumber) && mm.getTimeStamp() <= time) | ||
| { | ||
| if (mm.isProgramChange()) | ||
| { | ||
| if (! doneProg) | ||
| { | ||
| dest.add (new MidiMessage (mm, 0.0)); | ||
| doneProg = true; | ||
| } | ||
| } | ||
| else if (mm.isController()) | ||
| { | ||
| if (! doneControllers.contains (mm.getControllerNumber())) | ||
| { | ||
| dest.add (new MidiMessage (mm, 0.0)); | ||
| doneControllers.add (mm.getControllerNumber()); | ||
| } | ||
| } | ||
| else if (mm.isPitchWheel()) | ||
| { | ||
| if (! donePitchWheel) | ||
| { | ||
| dest.add (new MidiMessage (mm, 0.0)); | ||
| donePitchWheel = true; | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
|
|
||
| //============================================================================== | ||
| MidiMessageSequence::MidiEventHolder::MidiEventHolder (const MidiMessage& mm) | ||
| : message (mm), | ||
| noteOffObject (nullptr) | ||
| { | ||
| } | ||
|
|
||
| MidiMessageSequence::MidiEventHolder::~MidiEventHolder() | ||
| { | ||
| } |