diff --git a/source/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.cpp b/source/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.cpp index 944140f71..3addc5a19 100644 --- a/source/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.cpp +++ b/source/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.cpp @@ -46,8 +46,15 @@ AudioSampleBuffer::AudioSampleBuffer (const AudioSampleBuffer& other) noexcept { allocateData(); - for (int i = 0; i < numChannels; ++i) - FloatVectorOperations::copy (channels[i], other.channels[i], size); + if (other.isClear) + { + clear(); + } + else + { + for (int i = 0; i < numChannels; ++i) + FloatVectorOperations::copy (channels[i], other.channels[i], size); + } } } @@ -66,6 +73,7 @@ void AudioSampleBuffer::allocateData() } channels [numChannels] = nullptr; + isClear = false; } AudioSampleBuffer::AudioSampleBuffer (float* const* dataToReferTo, @@ -85,7 +93,8 @@ AudioSampleBuffer::AudioSampleBuffer (float* const* dataToReferTo, const int numSamples) noexcept : numChannels (numChans), size (numSamples), - allocatedBytes (0) + allocatedBytes (0), + isClear (false) { jassert (numChans > 0); allocateChannels (dataToReferTo, startSample); @@ -104,6 +113,7 @@ void AudioSampleBuffer::setDataToReferTo (float** dataToReferTo, size = newNumSamples; allocateChannels (dataToReferTo, 0); + jassert (! isClear); } void AudioSampleBuffer::allocateChannels (float* const* const dataToReferTo, int offset) @@ -128,6 +138,7 @@ void AudioSampleBuffer::allocateChannels (float* const* const dataToReferTo, int } channels [numChannels] = nullptr; + isClear = false; } AudioSampleBuffer& AudioSampleBuffer::operator= (const AudioSampleBuffer& other) noexcept @@ -136,8 +147,15 @@ AudioSampleBuffer& AudioSampleBuffer::operator= (const AudioSampleBuffer& other) { setSize (other.getNumChannels(), other.getNumSamples(), false, false, false); - for (int i = 0; i < numChannels; ++i) - FloatVectorOperations::copy (channels[i], other.channels[i], size); + if (other.isClear) + { + clear(); + } + else + { + for (int i = 0; i < numChannels; ++i) + FloatVectorOperations::copy (channels[i], other.channels[i], size); + } } return *this; @@ -165,13 +183,13 @@ void AudioSampleBuffer::setSize (const int newNumChannels, if (keepExistingContent) { - HeapBlock newData; - newData.allocate (newTotalBytes, clearExtraSpace); + HeapBlock newData; + newData.allocate (newTotalBytes, clearExtraSpace || isClear); const size_t numSamplesToCopy = (size_t) jmin (newNumSamples, size); - float** const newChannels = reinterpret_cast (newData.getData()); - float* newChan = reinterpret_cast (newData + channelListSize); + float** const newChannels = reinterpret_cast (newData.getData()); + float* newChan = reinterpret_cast (newData + channelListSize); for (int j = 0; j < newNumChannels; ++j) { @@ -179,9 +197,12 @@ void AudioSampleBuffer::setSize (const int newNumChannels, newChan += allocatedSamplesPerChannel; } - const int numChansToCopy = jmin (numChannels, newNumChannels); - for (int i = 0; i < numChansToCopy; ++i) - FloatVectorOperations::copy (newChannels[i], channels[i], (int) numSamplesToCopy); + if (! isClear) + { + const int numChansToCopy = jmin (numChannels, newNumChannels); + for (int i = 0; i < numChansToCopy; ++i) + FloatVectorOperations::copy (newChannels[i], channels[i], (int) numSamplesToCopy); + } allocatedData.swapWith (newData); allocatedBytes = newTotalBytes; @@ -191,17 +212,17 @@ void AudioSampleBuffer::setSize (const int newNumChannels, { if (avoidReallocating && allocatedBytes >= newTotalBytes) { - if (clearExtraSpace) + if (clearExtraSpace || isClear) allocatedData.clear (newTotalBytes); } else { allocatedBytes = newTotalBytes; - allocatedData.allocate (newTotalBytes, clearExtraSpace); - channels = reinterpret_cast (allocatedData.getData()); + allocatedData.allocate (newTotalBytes, clearExtraSpace || isClear); + channels = reinterpret_cast (allocatedData.getData()); } - float* chan = reinterpret_cast (allocatedData + channelListSize); + float* chan = reinterpret_cast (allocatedData + channelListSize); for (int i = 0; i < newNumChannels; ++i) { channels[i] = chan; @@ -217,8 +238,13 @@ void AudioSampleBuffer::setSize (const int newNumChannels, void AudioSampleBuffer::clear() noexcept { - for (int i = 0; i < numChannels; ++i) - FloatVectorOperations::clear (channels[i], size); + if (! isClear) + { + for (int i = 0; i < numChannels; ++i) + FloatVectorOperations::clear (channels[i], size); + + isClear = true; + } } void AudioSampleBuffer::clear (const int startSample, @@ -226,8 +252,14 @@ void AudioSampleBuffer::clear (const int startSample, { jassert (startSample >= 0 && startSample + numSamples <= size); - for (int i = 0; i < numChannels; ++i) - FloatVectorOperations::clear (channels[i] + startSample, numSamples); + if (! isClear) + { + if (startSample == 0 && numSamples == size) + isClear = true; + + for (int i = 0; i < numChannels; ++i) + FloatVectorOperations::clear (channels[i] + startSample, numSamples); + } } void AudioSampleBuffer::clear (const int channel, @@ -237,7 +269,31 @@ void AudioSampleBuffer::clear (const int channel, jassert (isPositiveAndBelow (channel, numChannels)); jassert (startSample >= 0 && startSample + numSamples <= size); - FloatVectorOperations::clear (channels [channel] + startSample, numSamples); + if (! isClear) + FloatVectorOperations::clear (channels [channel] + startSample, numSamples); +} + +float AudioSampleBuffer::getSample (int channel, int index) const noexcept +{ + jassert (isPositiveAndBelow (channel, numChannels)); + jassert (isPositiveAndBelow (index, size)); + return *(channels [channel] + index); +} + +void AudioSampleBuffer::setSample (int channel, int index, float newValue) noexcept +{ + jassert (isPositiveAndBelow (channel, numChannels)); + jassert (isPositiveAndBelow (index, size)); + *(channels [channel] + index) = newValue; + isClear = false; +} + +void AudioSampleBuffer::addSample (int channel, int index, float valueToAdd) noexcept +{ + jassert (isPositiveAndBelow (channel, numChannels)); + jassert (isPositiveAndBelow (index, size)); + *(channels [channel] + index) += valueToAdd; + isClear = false; } void AudioSampleBuffer::applyGain (const int channel, @@ -248,7 +304,7 @@ void AudioSampleBuffer::applyGain (const int channel, jassert (isPositiveAndBelow (channel, numChannels)); jassert (startSample >= 0 && startSample + numSamples <= size); - if (gain != 1.0f) + if (gain != 1.0f && ! isClear) { float* const d = channels [channel] + startSample; @@ -265,22 +321,25 @@ void AudioSampleBuffer::applyGainRamp (const int channel, float startGain, float endGain) noexcept { - if (startGain == endGain) + if (! isClear) { - applyGain (channel, startSample, numSamples, startGain); - } - else - { - jassert (isPositiveAndBelow (channel, numChannels)); - jassert (startSample >= 0 && startSample + numSamples <= size); + if (startGain == endGain) + { + applyGain (channel, startSample, numSamples, startGain); + } + else + { + jassert (isPositiveAndBelow (channel, numChannels)); + jassert (startSample >= 0 && startSample + numSamples <= size); - const float increment = (endGain - startGain) / numSamples; - float* d = channels [channel] + startSample; + const float increment = (endGain - startGain) / numSamples; + float* d = channels [channel] + startSample; - while (--numSamples >= 0) - { - *d++ *= startGain; - startGain += increment; + while (--numSamples >= 0) + { + *d++ *= startGain; + startGain += increment; + } } } } @@ -317,15 +376,27 @@ void AudioSampleBuffer::addFrom (const int destChannel, jassert (isPositiveAndBelow (sourceChannel, source.numChannels)); jassert (sourceStartSample >= 0 && sourceStartSample + numSamples <= source.size); - if (gain != 0.0f && numSamples > 0) + if (gain != 0.0f && numSamples > 0 && ! source.isClear) { float* const d = channels [destChannel] + destStartSample; const float* const s = source.channels [sourceChannel] + sourceStartSample; - if (gain != 1.0f) - FloatVectorOperations::addWithMultiply (d, s, gain, numSamples); + if (isClear) + { + isClear = false; + + if (gain != 1.0f) + FloatVectorOperations::copyWithMultiply (d, s, gain, numSamples); + else + FloatVectorOperations::copy (d, s, numSamples); + } else - FloatVectorOperations::add (d, s, numSamples); + { + if (gain != 1.0f) + FloatVectorOperations::addWithMultiply (d, s, gain, numSamples); + else + FloatVectorOperations::add (d, s, numSamples); + } } } @@ -343,10 +414,22 @@ void AudioSampleBuffer::addFrom (const int destChannel, { float* const d = channels [destChannel] + destStartSample; - if (gain != 1.0f) - FloatVectorOperations::addWithMultiply (d, source, gain, numSamples); + if (isClear) + { + isClear = false; + + if (gain != 1.0f) + FloatVectorOperations::copyWithMultiply (d, source, gain, numSamples); + else + FloatVectorOperations::copy (d, source, numSamples); + } else - FloatVectorOperations::add (d, source, numSamples); + { + if (gain != 1.0f) + FloatVectorOperations::addWithMultiply (d, source, gain, numSamples); + else + FloatVectorOperations::add (d, source, numSamples); + } } } @@ -369,6 +452,7 @@ void AudioSampleBuffer::addFromWithRamp (const int destChannel, { if (numSamples > 0 && (startGain != 0.0f || endGain != 0.0f)) { + isClear = false; const float increment = (endGain - startGain) / numSamples; float* d = channels [destChannel] + destStartSample; @@ -395,9 +479,20 @@ void AudioSampleBuffer::copyFrom (const int destChannel, jassert (sourceStartSample >= 0 && sourceStartSample + numSamples <= source.size); if (numSamples > 0) - FloatVectorOperations::copy (channels [destChannel] + destStartSample, - source.channels [sourceChannel] + sourceStartSample, - numSamples); + { + if (source.isClear) + { + if (! isClear) + FloatVectorOperations::clear (channels [destChannel] + destStartSample, numSamples); + } + else + { + isClear = false; + FloatVectorOperations::copy (channels [destChannel] + destStartSample, + source.channels [sourceChannel] + sourceStartSample, + numSamples); + } + } } void AudioSampleBuffer::copyFrom (const int destChannel, @@ -410,7 +505,10 @@ void AudioSampleBuffer::copyFrom (const int destChannel, jassert (source != nullptr); if (numSamples > 0) + { + isClear = false; FloatVectorOperations::copy (channels [destChannel] + destStartSample, source, numSamples); + } } void AudioSampleBuffer::copyFrom (const int destChannel, @@ -425,17 +523,24 @@ void AudioSampleBuffer::copyFrom (const int destChannel, if (numSamples > 0) { - float* d = channels [destChannel] + destStartSample; + float* const d = channels [destChannel] + destStartSample; if (gain != 1.0f) { if (gain == 0) - FloatVectorOperations::clear (d, numSamples); + { + if (! isClear) + FloatVectorOperations::clear (d, numSamples); + } else + { + isClear = false; FloatVectorOperations::copyWithMultiply (d, source, gain, numSamples); + } } else { + isClear = false; FloatVectorOperations::copy (d, source, numSamples); } } @@ -460,6 +565,7 @@ void AudioSampleBuffer::copyFromWithRamp (const int destChannel, { if (numSamples > 0 && (startGain != 0.0f || endGain != 0.0f)) { + isClear = false; const float increment = (endGain - startGain) / numSamples; float* d = channels [destChannel] + destStartSample; @@ -477,8 +583,9 @@ void AudioSampleBuffer::reverse (int channel, int startSample, int numSamples) c jassert (isPositiveAndBelow (channel, numChannels)); jassert (startSample >= 0 && startSample + numSamples <= size); - std::reverse (channels[channel] + startSample, - channels[channel] + startSample + numSamples); + if (! isClear) + std::reverse (channels[channel] + startSample, + channels[channel] + startSample + numSamples); } void AudioSampleBuffer::reverse (int startSample, int numSamples) const noexcept @@ -487,17 +594,17 @@ void AudioSampleBuffer::reverse (int startSample, int numSamples) const noexcept reverse (i, startSample, numSamples); } -void AudioSampleBuffer::findMinMax (const int channel, - const int startSample, - int numSamples, - float& minVal, - float& maxVal) const noexcept +Range AudioSampleBuffer::findMinMax (const int channel, + const int startSample, + int numSamples) const noexcept { jassert (isPositiveAndBelow (channel, numChannels)); jassert (startSample >= 0 && startSample + numSamples <= size); - FloatVectorOperations::findMinAndMax (channels [channel] + startSample, - numSamples, minVal, maxVal); + if (isClear) + return Range(); + + return FloatVectorOperations::findMinAndMax (channels [channel] + startSample, numSamples); } float AudioSampleBuffer::getMagnitude (const int channel, @@ -507,18 +614,21 @@ float AudioSampleBuffer::getMagnitude (const int channel, jassert (isPositiveAndBelow (channel, numChannels)); jassert (startSample >= 0 && startSample + numSamples <= size); - float mn, mx; - findMinMax (channel, startSample, numSamples, mn, mx); + if (isClear) + return 0.0f; + + const Range r (findMinMax (channel, startSample, numSamples)); - return jmax (mn, -mn, mx, -mx); + return jmax (r.getStart(), -r.getStart(), r.getEnd(), -r.getEnd()); } float AudioSampleBuffer::getMagnitude (int startSample, int numSamples) const noexcept { float mag = 0.0f; - for (int i = 0; i < numChannels; ++i) - mag = jmax (mag, getMagnitude (i, startSample, numSamples)); + if (! isClear) + for (int i = 0; i < numChannels; ++i) + mag = jmax (mag, getMagnitude (i, startSample, numSamples)); return mag; } @@ -530,7 +640,7 @@ float AudioSampleBuffer::getRMSLevel (const int channel, jassert (isPositiveAndBelow (channel, numChannels)); jassert (startSample >= 0 && startSample + numSamples <= size); - if (numSamples <= 0 || channel < 0 || channel >= numChannels) + if (numSamples <= 0 || channel < 0 || channel >= numChannels || isClear) return 0.0f; const float* const data = channels [channel] + startSample; diff --git a/source/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.h b/source/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.h index cd71c3da2..2674ad048 100644 --- a/source/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.h +++ b/source/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.h @@ -116,27 +116,58 @@ public: */ int getNumSamples() const noexcept { return size; } - /** Returns a pointer one of the buffer's channels. + /** Returns a pointer to an array of read-only samples in one of the buffer's channels. For speed, this doesn't check whether the channel number is out of range, so be careful when using it! + If you need to write to the data, do NOT call this method and const_cast the + result! Instead, you must call getWritePointer so that the buffer knows you're + planning on modifying the data. */ - float* getSampleData (const int channelNumber) const noexcept + const float* getReadPointer (int channelNumber) const noexcept { jassert (isPositiveAndBelow (channelNumber, numChannels)); return channels [channelNumber]; } - /** Returns a pointer to a sample in one of the buffer's channels. + /** Returns a pointer to an array of read-only samples in one of the buffer's channels. + For speed, this doesn't check whether the channel number or index are out of range, + so be careful when using it! + If you need to write to the data, do NOT call this method and const_cast the + result! Instead, you must call getWritePointer so that the buffer knows you're + planning on modifying the data. + */ + const float* getReadPointer (int channelNumber, int sampleIndex) const noexcept + { + jassert (isPositiveAndBelow (channelNumber, numChannels)); + jassert (isPositiveAndBelow (sampleIndex, size)); + return channels [channelNumber] + sampleIndex; + } - For speed, this doesn't check whether the channel and sample number - are out-of-range, so be careful when using it! + /** Returns a writeable pointer to one of the buffer's channels. + For speed, this doesn't check whether the channel number is out of range, + so be careful when using it! + Note that if you're not planning on writing to the data, you should always + use getReadPointer instead. */ - float* getSampleData (const int channelNumber, - const int sampleOffset) const noexcept + float* getWritePointer (int channelNumber) noexcept { jassert (isPositiveAndBelow (channelNumber, numChannels)); - jassert (isPositiveAndBelow (sampleOffset, size)); - return channels [channelNumber] + sampleOffset; + isClear = false; + return channels [channelNumber]; + } + + /** Returns a writeable pointer to one of the buffer's channels. + For speed, this doesn't check whether the channel number or index are out of range, + so be careful when using it! + Note that if you're not planning on writing to the data, you should + use getReadPointer instead. + */ + float* getWritePointer (int channelNumber, int sampleIndex) noexcept + { + jassert (isPositiveAndBelow (channelNumber, numChannels)); + jassert (isPositiveAndBelow (sampleIndex, size)); + isClear = false; + return channels [channelNumber] + sampleIndex; } /** Returns an array of pointers to the channels in the buffer. @@ -144,7 +175,14 @@ public: Don't modify any of the pointers that are returned, and bear in mind that these will become invalid if the buffer is resized. */ - float** getArrayOfChannels() const noexcept { return channels; } + const float** getArrayOfReadPointers() const noexcept { return const_cast (channels); } + + /** Returns an array of pointers to the channels in the buffer. + + Don't modify any of the pointers that are returned, and bear in mind that + these will become invalid if the buffer is resized. + */ + float** getArrayOfWritePointers() noexcept { isClear = false; return channels; } //============================================================================== /** Changes the buffer's size or number of channels. @@ -216,6 +254,36 @@ public: int startSample, int numSamples) noexcept; + /** Returns true if the buffer has been entirely cleared. + Note that this does not actually measure the contents of the buffer - it simply + returns a flag that is set when the buffer is cleared, and which is reset whenever + functions like getWritePointer() are invoked. That means the method does not take + any time, but it may return false negatives when in fact the buffer is still empty. + */ + bool hasBeenCleared() const noexcept { return isClear; } + + //============================================================================== + /** Returns a sample from the buffer. + The channel and index are not checked - they are expected to be in-range. If not, + an assertion will be thrown, but in a release build, you're into 'undefined behaviour' + territory. + */ + float getSample (int channel, int sampleIndex) const noexcept; + + /** Sets a sample in the buffer. + The channel and index are not checked - they are expected to be in-range. If not, + an assertion will be thrown, but in a release build, you're into 'undefined behaviour' + territory. + */ + void setSample (int destChannel, int destSample, float newValue) noexcept; + + /** Adds a value to a sample in the buffer. + The channel and index are not checked - they are expected to be in-range. If not, + an assertion will be thrown, but in a release build, you're into 'undefined behaviour' + territory. + */ + void addSample (int destChannel, int destSample, float valueToAdd) noexcept; + /** Applies a gain multiple to a region of one channel. For speed, this doesn't check whether the channel and sample number @@ -392,19 +460,15 @@ public: float endGain) noexcept; - /** Finds the highest and lowest sample values in a given range. + /** Returns a Range indicating the lowest and highest sample values in a given section. @param channel the channel to read from @param startSample the start sample within the channel @param numSamples the number of samples to check - @param minVal on return, the lowest value that was found - @param maxVal on return, the highest value that was found */ - void findMinMax (int channel, - int startSample, - int numSamples, - float& minVal, - float& maxVal) const noexcept; + Range findMinMax (int channel, + int startSample, + int numSamples) const noexcept; /** Finds the highest absolute sample value within a region of a channel. */ float getMagnitude (int channel, @@ -426,13 +490,27 @@ public: /** Reverses a part of the buffer. */ void reverse (int startSample, int numSamples) const noexcept; + //============================================================================== + #ifndef DOXYGEN + // Note that these methods have now been replaced by getReadPointer() and getWritePointer() + JUCE_DEPRECATED (const float* getSampleData (int channel) const) { return getReadPointer (channel); } + JUCE_DEPRECATED (const float* getSampleData (int channel, int index) const) { return getReadPointer (channel, index); } + JUCE_DEPRECATED (float* getSampleData (int channel)) { return getWritePointer (channel); } + JUCE_DEPRECATED (float* getSampleData (int channel, int index)) { return getWritePointer (channel, index); } + + // These have been replaced by getArrayOfReadPointers() and getArrayOfWritePointers() + JUCE_DEPRECATED (const float** getArrayOfChannels() const) { return getArrayOfReadPointers(); } + JUCE_DEPRECATED (float** getArrayOfChannels()) { return getArrayOfWritePointers(); } + #endif + private: //============================================================================== int numChannels, size; size_t allocatedBytes; float** channels; - HeapBlock allocatedData; + HeapBlock allocatedData; float* preallocatedChannelSpace [32]; + bool isClear; void allocateData(); void allocateChannels (float* const* dataToReferTo, int offset); diff --git a/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp b/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp index 9dda414fd..bc9234d5f 100644 --- a/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp +++ b/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp @@ -24,9 +24,8 @@ namespace FloatVectorHelpers { - - #define JUCE_INCREMENT_SRC_DEST dest += 4; src += 4; - #define JUCE_INCREMENT_DEST dest += 4; + #define JUCE_INCREMENT_SRC_DEST dest += (16 / sizeof (*dest)); src += (16 / sizeof (*dest)); + #define JUCE_INCREMENT_DEST dest += (16 / sizeof (*dest)); #if JUCE_USE_SSE_INTRINSICS static bool sse2Present = false; @@ -45,186 +44,317 @@ namespace FloatVectorHelpers return (((pointer_sized_int) p) & 15) == 0; } - static inline float findMinimumOrMaximum (const float* src, int num, const bool isMinimum) noexcept + struct BasicOps32 { - const int numLongOps = num / 4; - - if (numLongOps > 1 && FloatVectorHelpers::isSSE2Available()) - { - __m128 val; - - #define JUCE_MINIMUMMAXIMUM_SSE_LOOP(loadOp, minMaxOp) \ - val = loadOp (src); \ - src += 4; \ - for (int i = 1; i < numLongOps; ++i) \ - { \ - const __m128 s = loadOp (src); \ - val = minMaxOp (val, s); \ - src += 4; \ - } + typedef float Type; + typedef __m128 ParallelType; + enum { numParallel = 4 }; + + static forcedinline ParallelType load1 (Type v) noexcept { return _mm_load1_ps (&v); } + static forcedinline ParallelType loadA (const Type* v) noexcept { return _mm_load_ps (v); } + static forcedinline ParallelType loadU (const Type* v) noexcept { return _mm_loadu_ps (v); } + static forcedinline void storeA (Type* dest, ParallelType a) noexcept { return _mm_store_ps (dest, a); } + static forcedinline void storeU (Type* dest, ParallelType a) noexcept { return _mm_storeu_ps (dest, a); } + + static forcedinline ParallelType add (ParallelType a, ParallelType b) noexcept { return _mm_add_ps (a, b); } + static forcedinline ParallelType sub (ParallelType a, ParallelType b) noexcept { return _mm_sub_ps (a, b); } + static forcedinline ParallelType mul (ParallelType a, ParallelType b) noexcept { return _mm_mul_ps (a, b); } + static forcedinline ParallelType max (ParallelType a, ParallelType b) noexcept { return _mm_max_ps (a, b); } + static forcedinline ParallelType min (ParallelType a, ParallelType b) noexcept { return _mm_min_ps (a, b); } + + static forcedinline Type max (ParallelType a) noexcept { Type v[numParallel]; storeU (v, a); return jmax (v[0], v[1], v[2], v[3]); } + static forcedinline Type min (ParallelType a) noexcept { Type v[numParallel]; storeU (v, a); return jmin (v[0], v[1], v[2], v[3]); } + }; + + struct BasicOps64 + { + typedef double Type; + typedef __m128d ParallelType; + enum { numParallel = 2 }; + + static forcedinline ParallelType load1 (Type v) noexcept { return _mm_load1_pd (&v); } + static forcedinline ParallelType loadA (const Type* v) noexcept { return _mm_load_pd (v); } + static forcedinline ParallelType loadU (const Type* v) noexcept { return _mm_loadu_pd (v); } + static forcedinline void storeA (Type* dest, ParallelType a) noexcept { return _mm_store_pd (dest, a); } + static forcedinline void storeU (Type* dest, ParallelType a) noexcept { return _mm_storeu_pd (dest, a); } + + static forcedinline ParallelType add (ParallelType a, ParallelType b) noexcept { return _mm_add_pd (a, b); } + static forcedinline ParallelType sub (ParallelType a, ParallelType b) noexcept { return _mm_sub_pd (a, b); } + static forcedinline ParallelType mul (ParallelType a, ParallelType b) noexcept { return _mm_mul_pd (a, b); } + static forcedinline ParallelType max (ParallelType a, ParallelType b) noexcept { return _mm_max_pd (a, b); } + static forcedinline ParallelType min (ParallelType a, ParallelType b) noexcept { return _mm_min_pd (a, b); } + + static forcedinline Type max (ParallelType a) noexcept { Type v[numParallel]; storeU (v, a); return jmax (v[0], v[1]); } + static forcedinline Type min (ParallelType a) noexcept { Type v[numParallel]; storeU (v, a); return jmin (v[0], v[1]); } + }; + + #define JUCE_BEGIN_VEC_OP \ + typedef FloatVectorHelpers::ModeType::Mode Mode; \ + if (FloatVectorHelpers::isSSE2Available()) \ + { \ + const int numLongOps = num / Mode::numParallel; - if (isMinimum) - { - if (FloatVectorHelpers::isAligned (src)) { JUCE_MINIMUMMAXIMUM_SSE_LOOP (_mm_load_ps, _mm_min_ps) } - else { JUCE_MINIMUMMAXIMUM_SSE_LOOP (_mm_loadu_ps, _mm_min_ps) } - } - else - { - if (FloatVectorHelpers::isAligned (src)) { JUCE_MINIMUMMAXIMUM_SSE_LOOP (_mm_load_ps, _mm_max_ps) } - else { JUCE_MINIMUMMAXIMUM_SSE_LOOP (_mm_loadu_ps,_mm_max_ps) } - } + #define JUCE_FINISH_VEC_OP(normalOp) \ + num &= (Mode::numParallel - 1); \ + if (num == 0) return; \ + } \ + for (int i = 0; i < num; ++i) normalOp; - float localVal; + #define JUCE_PERFORM_VEC_OP_DEST(normalOp, vecOp, locals, setupOp) \ + JUCE_BEGIN_VEC_OP \ + setupOp \ + if (FloatVectorHelpers::isAligned (dest)) JUCE_VEC_LOOP (vecOp, dummy, Mode::loadA, Mode::storeA, locals, JUCE_INCREMENT_DEST) \ + else JUCE_VEC_LOOP (vecOp, dummy, Mode::loadU, Mode::storeU, locals, JUCE_INCREMENT_DEST) \ + JUCE_FINISH_VEC_OP (normalOp) - { - float vals[4]; - _mm_storeu_ps (vals, val); + #define JUCE_PERFORM_VEC_OP_SRC_DEST(normalOp, vecOp, locals, increment, setupOp) \ + JUCE_BEGIN_VEC_OP \ + setupOp \ + if (FloatVectorHelpers::isAligned (dest)) \ + { \ + if (FloatVectorHelpers::isAligned (src)) JUCE_VEC_LOOP (vecOp, Mode::loadA, Mode::loadA, Mode::storeA, locals, increment) \ + else JUCE_VEC_LOOP (vecOp, Mode::loadU, Mode::loadA, Mode::storeA, locals, increment) \ + }\ + else \ + { \ + if (FloatVectorHelpers::isAligned (src)) JUCE_VEC_LOOP (vecOp, Mode::loadA, Mode::loadU, Mode::storeU, locals, increment) \ + else JUCE_VEC_LOOP (vecOp, Mode::loadU, Mode::loadU, Mode::storeU, locals, increment) \ + } \ + JUCE_FINISH_VEC_OP (normalOp) - localVal = isMinimum ? jmin (vals[0], vals[1], vals[2], vals[3]) - : jmax (vals[0], vals[1], vals[2], vals[3]); - } + //============================================================================== + #elif JUCE_USE_ARM_NEON - num &= 3; + struct BasicOps32 + { + typedef float Type; + typedef float32x4_t ParallelType; + enum { numParallel = 4 }; + + static forcedinline ParallelType load1 (Type v) noexcept { return vld1q_dup_f32 (&v); } + static forcedinline ParallelType loadA (const Type* v) noexcept { return vld1q_f32 (v); } + static forcedinline ParallelType loadU (const Type* v) noexcept { return vld1q_f32 (v); } + static forcedinline void storeA (Type* dest, ParallelType a) noexcept { return vst1q_f32 (dest, a); } + static forcedinline void storeU (Type* dest, ParallelType a) noexcept { return vst1q_f32 (dest, a); } + + static forcedinline ParallelType add (ParallelType a, ParallelType b) noexcept { return vaddq_f32 (a, b); } + static forcedinline ParallelType sub (ParallelType a, ParallelType b) noexcept { return vsubq_f32 (a, b); } + static forcedinline ParallelType mul (ParallelType a, ParallelType b) noexcept { return vmulq_f32 (a, b); } + static forcedinline ParallelType max (ParallelType a, ParallelType b) noexcept { return vmaxq_f32 (a, b); } + static forcedinline ParallelType min (ParallelType a, ParallelType b) noexcept { return vminq_f32 (a, b); } + + static forcedinline Type max (ParallelType a) noexcept { Type v[numParallel]; storeU (v, a); return jmax (v[0], v[1], v[2], v[3]); } + static forcedinline Type min (ParallelType a) noexcept { Type v[numParallel]; storeU (v, a); return jmin (v[0], v[1], v[2], v[3]); } + }; + + struct BasicOps64 + { + typedef double Type; + typedef double ParallelType; + enum { numParallel = 1 }; + + static forcedinline ParallelType load1 (Type v) noexcept { return v; } + static forcedinline ParallelType loadA (const Type* v) noexcept { return *v; } + static forcedinline ParallelType loadU (const Type* v) noexcept { return *v; } + static forcedinline void storeA (Type* dest, ParallelType a) noexcept { return *dest = a; } + static forcedinline void storeU (Type* dest, ParallelType a) noexcept { return *dest = a; } + + static forcedinline ParallelType add (ParallelType a, ParallelType b) noexcept { return a + b; } + static forcedinline ParallelType sub (ParallelType a, ParallelType b) noexcept { return a - b; } + static forcedinline ParallelType mul (ParallelType a, ParallelType b) noexcept { return a * b; } + static forcedinline ParallelType max (ParallelType a, ParallelType b) noexcept { return jmax (a, b); } + static forcedinline ParallelType min (ParallelType a, ParallelType b) noexcept { return jmin (a, b); } + + static forcedinline Type max (ParallelType a) noexcept { return a; } + static forcedinline Type min (ParallelType a) noexcept { return a; } + }; + + #define JUCE_BEGIN_VEC_OP \ + typedef FloatVectorHelpers::ModeType::Mode Mode; \ + if (Mode::numParallel > 1) \ + { \ + const int numLongOps = num / Mode::numParallel; - for (int i = 0; i < num; ++i) - localVal = isMinimum ? jmin (localVal, src[i]) - : jmax (localVal, src[i]); + #define JUCE_FINISH_VEC_OP(normalOp) \ + num &= (Mode::numParallel - 1); \ + if (num == 0) return; \ + } \ + for (int i = 0; i < num; ++i) normalOp; - return localVal; - } + #define JUCE_PERFORM_VEC_OP_DEST(normalOp, vecOp, locals, setupOp) \ + JUCE_BEGIN_VEC_OP \ + setupOp \ + JUCE_VEC_LOOP (vecOp, dummy, Mode::loadU, Mode::storeU, locals, JUCE_INCREMENT_DEST) \ + JUCE_FINISH_VEC_OP (normalOp) - return isMinimum ? juce::findMinimum (src, num) - : juce::findMaximum (src, num); - } + #define JUCE_PERFORM_VEC_OP_SRC_DEST(normalOp, vecOp, locals, increment, setupOp) \ + JUCE_BEGIN_VEC_OP \ + setupOp \ + JUCE_VEC_LOOP (vecOp, Mode::loadU, Mode::loadU, Mode::storeU, locals, increment) \ + JUCE_FINISH_VEC_OP (normalOp) - #define JUCE_BEGIN_SSE_OP \ - if (FloatVectorHelpers::isSSE2Available()) \ - { \ - const int numLongOps = num / 4; + //============================================================================== + #else + #define JUCE_PERFORM_VEC_OP_DEST(normalOp, vecOp, locals, setupOp) \ + for (int i = 0; i < num; ++i) normalOp; - #define JUCE_FINISH_SSE_OP(normalOp) \ - num &= 3; \ - if (num == 0) return; \ - } \ + #define JUCE_PERFORM_VEC_OP_SRC_DEST(normalOp, vecOp, locals, increment, setupOp) \ for (int i = 0; i < num; ++i) normalOp; - #define JUCE_SSE_LOOP(sseOp, srcLoad, dstLoad, dstStore, locals, increment) \ + #endif + + //============================================================================== + #define JUCE_VEC_LOOP(vecOp, srcLoad, dstLoad, dstStore, locals, increment) \ for (int i = 0; i < numLongOps; ++i) \ { \ locals (srcLoad, dstLoad); \ - dstStore (dest, sseOp); \ + dstStore (dest, vecOp); \ increment; \ } #define JUCE_LOAD_NONE(srcLoad, dstLoad) - #define JUCE_LOAD_DEST(srcLoad, dstLoad) const __m128 d = dstLoad (dest); - #define JUCE_LOAD_SRC(srcLoad, dstLoad) const __m128 s = srcLoad (src); - #define JUCE_LOAD_SRC_DEST(srcLoad, dstLoad) const __m128 d = dstLoad (dest); const __m128 s = srcLoad (src); - - #define JUCE_PERFORM_SSE_OP_DEST(normalOp, sseOp, locals) \ - JUCE_BEGIN_SSE_OP \ - if (FloatVectorHelpers::isAligned (dest)) JUCE_SSE_LOOP (sseOp, dummy, _mm_load_ps, _mm_store_ps, locals, JUCE_INCREMENT_DEST) \ - else JUCE_SSE_LOOP (sseOp, dummy, _mm_loadu_ps, _mm_storeu_ps, locals, JUCE_INCREMENT_DEST) \ - JUCE_FINISH_SSE_OP (normalOp) - - #define JUCE_PERFORM_SSE_OP_SRC_DEST(normalOp, sseOp, locals, increment) \ - JUCE_BEGIN_SSE_OP \ - if (FloatVectorHelpers::isAligned (dest)) \ - { \ - if (FloatVectorHelpers::isAligned (src)) JUCE_SSE_LOOP (sseOp, _mm_load_ps, _mm_load_ps, _mm_store_ps, locals, increment) \ - else JUCE_SSE_LOOP (sseOp, _mm_loadu_ps, _mm_load_ps, _mm_store_ps, locals, increment) \ - }\ - else \ - { \ - if (FloatVectorHelpers::isAligned (src)) JUCE_SSE_LOOP (sseOp, _mm_load_ps, _mm_loadu_ps, _mm_storeu_ps, locals, increment) \ - else JUCE_SSE_LOOP (sseOp, _mm_loadu_ps, _mm_loadu_ps, _mm_storeu_ps, locals, increment) \ - } \ - JUCE_FINISH_SSE_OP (normalOp) - + #define JUCE_LOAD_DEST(srcLoad, dstLoad) const Mode::ParallelType d = dstLoad (dest); + #define JUCE_LOAD_SRC(srcLoad, dstLoad) const Mode::ParallelType s = srcLoad (src); + #define JUCE_LOAD_SRC_DEST(srcLoad, dstLoad) const Mode::ParallelType d = dstLoad (dest), s = srcLoad (src); - //============================================================================== - #elif JUCE_USE_ARM_NEON + #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON + template struct ModeType { typedef BasicOps32 Mode; }; + template<> struct ModeType<8> { typedef BasicOps64 Mode; }; - static inline float findMinimumOrMaximum (const float* src, int num, const bool isMinimum) noexcept + template + struct MinMax { - const int numLongOps = num / 4; + typedef typename Mode::Type Type; + typedef typename Mode::ParallelType ParallelType; - if (numLongOps > 1) + static Type findMinOrMax (const Type* src, int num, const bool isMinimum) noexcept { - float32x4_t val; - - #define JUCE_MINIMUMMAXIMUM_NEON_LOOP(loadOp, minMaxOp) \ - val = loadOp (src); \ - src += 4; \ - for (int i = 1; i < numLongOps; ++i) \ - { \ - const float32x4_t s = loadOp (src); \ - val = minMaxOp (val, s); \ - src += 4; \ + const int numLongOps = num / Mode::numParallel; + + #if JUCE_USE_SSE_INTRINSICS + if (numLongOps > 1 && isSSE2Available()) + #else + if (numLongOps > 1) + #endif + { + ParallelType val; + + #if ! JUCE_USE_ARM_NEON + if (isAligned (src)) + { + val = Mode::loadA (src); + + if (isMinimum) + { + for (int i = 1; i < numLongOps; ++i) + { + src += Mode::numParallel; + val = Mode::min (val, Mode::loadA (src)); + } + } + else + { + for (int i = 1; i < numLongOps; ++i) + { + src += Mode::numParallel; + val = Mode::max (val, Mode::loadA (src)); + } + } + } + else + #endif + { + val = Mode::loadU (src); + + if (isMinimum) + { + for (int i = 1; i < numLongOps; ++i) + { + src += Mode::numParallel; + val = Mode::min (val, Mode::loadU (src)); + } + } + else + { + for (int i = 1; i < numLongOps; ++i) + { + src += Mode::numParallel; + val = Mode::max (val, Mode::loadU (src)); + } + } } - if (isMinimum) { JUCE_MINIMUMMAXIMUM_NEON_LOOP (vld1q_f32, vminq_f32) } - else { JUCE_MINIMUMMAXIMUM_NEON_LOOP (vld1q_f32, vmaxq_f32) } + Type result = isMinimum ? Mode::min (val) + : Mode::max (val); - float localVal; + num &= (Mode::numParallel - 1); - { - float vals[4]; - vst1q_f32 (vals, val); + for (int i = 0; i < num; ++i) + result = isMinimum ? jmin (result, src[i]) + : jmax (result, src[i]); - localVal = isMinimum ? jmin (vals[0], vals[1], vals[2], vals[3]) - : jmax (vals[0], vals[1], vals[2], vals[3]); + return result; } - num &= 3; - - for (int i = 0; i < num; ++i) - localVal = isMinimum ? jmin (localVal, src[i]) - : jmax (localVal, src[i]); - - return localVal; + return isMinimum ? juce::findMinimum (src, num) + : juce::findMaximum (src, num); } - return isMinimum ? juce::findMinimum (src, num) - : juce::findMaximum (src, num); - } - - #define JUCE_BEGIN_NEON_OP \ - const int numLongOps = num / 4; - - #define JUCE_FINISH_NEON_OP(normalOp) \ - num &= 3; \ - if (num == 0) return; \ - for (int i = 0; i < num; ++i) normalOp; + static Range findMinAndMax (const Type* src, int num) noexcept + { + const int numLongOps = num / Mode::numParallel; - #define JUCE_NEON_LOOP(neonOp, srcLoad, dstLoad, dstStore, locals, increment) \ - for (int i = 0; i < numLongOps; ++i) \ - { \ - locals (srcLoad, dstLoad); \ - dstStore (dest, neonOp); \ - increment; \ - } + #if JUCE_USE_SSE_INTRINSICS + if (numLongOps > 1 && isSSE2Available()) + #else + if (numLongOps > 1) + #endif + { + ParallelType mn, mx; + + #if ! JUCE_USE_ARM_NEON + if (isAligned (src)) + { + mn = Mode::loadA (src); + mx = mn; + + for (int i = 1; i < numLongOps; ++i) + { + src += Mode::numParallel; + const ParallelType v = Mode::loadA (src); + mn = Mode::min (mn, v); + mx = Mode::max (mx, v); + } + } + else + #endif + { + mn = Mode::loadU (src); + mx = mn; + + for (int i = 1; i < numLongOps; ++i) + { + src += Mode::numParallel; + const ParallelType v = Mode::loadU (src); + mn = Mode::min (mn, v); + mx = Mode::max (mx, v); + } + } - #define JUCE_LOAD_NONE(srcLoad, dstLoad) - #define JUCE_LOAD_DEST(srcLoad, dstLoad) const float32x4_t d = dstLoad (dest); - #define JUCE_LOAD_SRC(srcLoad, dstLoad) const float32x4_t s = srcLoad (src); - #define JUCE_LOAD_SRC_DEST(srcLoad, dstLoad) const float32x4_t d = dstLoad (dest); const float32x4_t s = srcLoad (src); + Range result (Mode::min (mn), + Mode::max (mx)); - #define JUCE_PERFORM_NEON_OP_DEST(normalOp, neonOp, locals) \ - JUCE_BEGIN_NEON_OP \ - JUCE_NEON_LOOP (neonOp, dummy, vld1q_f32, vst1q_f32, locals, JUCE_INCREMENT_DEST) \ - JUCE_FINISH_NEON_OP (normalOp) + num &= 3; + for (int i = 0; i < num; ++i) + result = result.getUnionWith (src[i]); - #define JUCE_PERFORM_NEON_OP_SRC_DEST(normalOp, neonOp, locals) \ - JUCE_BEGIN_NEON_OP \ - JUCE_NEON_LOOP (neonOp, vld1q_f32, vld1q_f32, vst1q_f32, locals, JUCE_INCREMENT_SRC_DEST) \ - JUCE_FINISH_NEON_OP (normalOp) + return result; + } - //============================================================================== - #else - #define JUCE_PERFORM_SSE_OP_DEST(normalOp, unused1, unused2) for (int i = 0; i < num; ++i) normalOp; - #define JUCE_PERFORM_SSE_OP_SRC_DEST(normalOp, sseOp, locals, increment) for (int i = 0; i < num; ++i) normalOp; - #endif + return Range::findMinAndMax (src, num); + } + }; + #endif } //============================================================================== @@ -237,18 +367,32 @@ void JUCE_CALLTYPE FloatVectorOperations::clear (float* dest, int num) noexcept #endif } +void JUCE_CALLTYPE FloatVectorOperations::clear (double* dest, int num) noexcept +{ + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vclrD (dest, 1, (size_t) num); + #else + zeromem (dest, num * sizeof (double)); + #endif +} + void JUCE_CALLTYPE FloatVectorOperations::fill (float* dest, float valueToFill, int num) noexcept { #if JUCE_USE_VDSP_FRAMEWORK vDSP_vfill (&valueToFill, dest, 1, (size_t) num); - #elif JUCE_USE_ARM_NEON - const float32x4_t val = vld1q_dup_f32 (&valueToFill); - JUCE_PERFORM_NEON_OP_DEST (dest[i] = valueToFill, val, JUCE_LOAD_NONE) #else - #if JUCE_USE_SSE_INTRINSICS - const __m128 val = _mm_load1_ps (&valueToFill); - #endif - JUCE_PERFORM_SSE_OP_DEST (dest[i] = valueToFill, val, JUCE_LOAD_NONE) + JUCE_PERFORM_VEC_OP_DEST (dest[i] = valueToFill, val, JUCE_LOAD_NONE, + const Mode::ParallelType val = Mode::load1 (valueToFill);) + #endif +} + +void JUCE_CALLTYPE FloatVectorOperations::fill (double* dest, double valueToFill, int num) noexcept +{ + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vfillD (&valueToFill, dest, 1, (size_t) num); + #else + JUCE_PERFORM_VEC_OP_DEST (dest[i] = valueToFill, val, JUCE_LOAD_NONE, + const Mode::ParallelType val = Mode::load1 (valueToFill);) #endif } @@ -257,42 +401,60 @@ void JUCE_CALLTYPE FloatVectorOperations::copy (float* dest, const float* src, i memcpy (dest, src, (size_t) num * sizeof (float)); } +void JUCE_CALLTYPE FloatVectorOperations::copy (double* dest, const double* src, int num) noexcept +{ + memcpy (dest, src, (size_t) num * sizeof (double)); +} + void JUCE_CALLTYPE FloatVectorOperations::copyWithMultiply (float* dest, const float* src, float multiplier, int num) noexcept { #if JUCE_USE_VDSP_FRAMEWORK vDSP_vsmul (src, 1, &multiplier, dest, 1, num); - #elif JUCE_USE_ARM_NEON - JUCE_PERFORM_NEON_OP_SRC_DEST (dest[i] += src[i], vmulq_n_f32(s, multiplier), JUCE_LOAD_SRC) #else - #if JUCE_USE_SSE_INTRINSICS - const __m128 mult = _mm_load1_ps (&multiplier); - #endif - JUCE_PERFORM_SSE_OP_SRC_DEST (dest[i] = src[i] * multiplier, _mm_mul_ps (mult, s), - JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST) + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] * multiplier, Mode::mul (mult, s), + JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, + const Mode::ParallelType mult = Mode::load1 (multiplier);) #endif } -void JUCE_CALLTYPE FloatVectorOperations::add (float* dest, float amount, int num) noexcept +void JUCE_CALLTYPE FloatVectorOperations::copyWithMultiply (double* dest, const double* src, double multiplier, int num) noexcept { - #if JUCE_USE_ARM_NEON - const float32x4_t amountToAdd = vld1q_dup_f32(&amount); - JUCE_PERFORM_NEON_OP_DEST (dest[i] += amount, vaddq_f32 (d, amountToAdd), JUCE_LOAD_DEST) + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vsmulD (src, 1, &multiplier, dest, 1, num); #else - #if JUCE_USE_SSE_INTRINSICS - const __m128 amountToAdd = _mm_load1_ps (&amount); - #endif - JUCE_PERFORM_SSE_OP_DEST (dest[i] += amount, _mm_add_ps (d, amountToAdd), JUCE_LOAD_DEST) + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] * multiplier, Mode::mul (mult, s), + JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, + const Mode::ParallelType mult = Mode::load1 (multiplier);) #endif } +void JUCE_CALLTYPE FloatVectorOperations::add (float* dest, float amount, int num) noexcept +{ + JUCE_PERFORM_VEC_OP_DEST (dest[i] += amount, Mode::add (d, amountToAdd), JUCE_LOAD_DEST, + const Mode::ParallelType amountToAdd = Mode::load1 (amount);) +} + +void JUCE_CALLTYPE FloatVectorOperations::add (double* dest, double amount, int num) noexcept +{ + JUCE_PERFORM_VEC_OP_DEST (dest[i] += amount, Mode::add (d, amountToAdd), JUCE_LOAD_DEST, + const Mode::ParallelType amountToAdd = Mode::load1 (amount);) +} + void JUCE_CALLTYPE FloatVectorOperations::add (float* dest, const float* src, int num) noexcept { #if JUCE_USE_VDSP_FRAMEWORK vDSP_vadd (src, 1, dest, 1, dest, 1, num); - #elif JUCE_USE_ARM_NEON - JUCE_PERFORM_NEON_OP_SRC_DEST (dest[i] += src[i], vaddq_f32 (d, s), JUCE_LOAD_SRC_DEST) #else - JUCE_PERFORM_SSE_OP_SRC_DEST (dest[i] += src[i], _mm_add_ps (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST) + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] += src[i], Mode::add (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, ) + #endif +} + +void JUCE_CALLTYPE FloatVectorOperations::add (double* dest, const double* src, int num) noexcept +{ + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vaddD (src, 1, dest, 1, dest, 1, num); + #else + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] += src[i], Mode::add (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, ) #endif } @@ -300,39 +462,49 @@ void JUCE_CALLTYPE FloatVectorOperations::subtract (float* dest, const float* sr { #if JUCE_USE_VDSP_FRAMEWORK vDSP_vsub (src, 1, dest, 1, dest, 1, num); - #elif JUCE_USE_ARM_NEON - JUCE_PERFORM_NEON_OP_SRC_DEST (dest[i] -= src[i], vsubq_f32 (d, s), JUCE_LOAD_SRC_DEST) #else - JUCE_PERFORM_SSE_OP_SRC_DEST (dest[i] -= src[i], _mm_sub_ps (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST) + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] -= src[i], Mode::sub (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, ) #endif } -void JUCE_CALLTYPE FloatVectorOperations::addWithMultiply (float* dest, const float* src, float multiplier, int num) noexcept +void JUCE_CALLTYPE FloatVectorOperations::subtract (double* dest, const double* src, int num) noexcept { - #if JUCE_USE_ARM_NEON - JUCE_PERFORM_NEON_OP_SRC_DEST (dest[i] += src[i] * multiplier, - vmlaq_n_f32 (d, s, multiplier), - JUCE_LOAD_SRC_DEST) + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vsubD (src, 1, dest, 1, dest, 1, num); #else - #if JUCE_USE_SSE_INTRINSICS - const __m128 mult = _mm_load1_ps (&multiplier); - #endif - - JUCE_PERFORM_SSE_OP_SRC_DEST (dest[i] += src[i] * multiplier, - _mm_add_ps (d, _mm_mul_ps (mult, s)), - JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST) + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] -= src[i], Mode::sub (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, ) #endif +} + +void JUCE_CALLTYPE FloatVectorOperations::addWithMultiply (float* dest, const float* src, float multiplier, int num) noexcept +{ + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] += src[i] * multiplier, Mode::add (d, Mode::mul (mult, s)), + JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, + const Mode::ParallelType mult = Mode::load1 (multiplier);) +} +void JUCE_CALLTYPE FloatVectorOperations::addWithMultiply (double* dest, const double* src, double multiplier, int num) noexcept +{ + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] += src[i] * multiplier, Mode::add (d, Mode::mul (mult, s)), + JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, + const Mode::ParallelType mult = Mode::load1 (multiplier);) } void JUCE_CALLTYPE FloatVectorOperations::multiply (float* dest, const float* src, int num) noexcept { #if JUCE_USE_VDSP_FRAMEWORK vDSP_vmul (src, 1, dest, 1, dest, 1, num); - #elif JUCE_USE_ARM_NEON - JUCE_PERFORM_NEON_OP_SRC_DEST (dest[i] *= src[i], vmulq_f32 (d, s), JUCE_LOAD_SRC_DEST) #else - JUCE_PERFORM_SSE_OP_SRC_DEST (dest[i] *= src[i], _mm_mul_ps (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST) + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] *= src[i], Mode::mul (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, ) + #endif +} + +void JUCE_CALLTYPE FloatVectorOperations::multiply (double* dest, const double* src, int num) noexcept +{ + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vmulD (src, 1, dest, 1, dest, 1, num); + #else + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] *= src[i], Mode::mul (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, ) #endif } @@ -340,13 +512,19 @@ void JUCE_CALLTYPE FloatVectorOperations::multiply (float* dest, float multiplie { #if JUCE_USE_VDSP_FRAMEWORK vDSP_vsmul (dest, 1, &multiplier, dest, 1, num); - #elif JUCE_USE_ARM_NEON - JUCE_PERFORM_NEON_OP_DEST (dest[i] *= multiplier, vmulq_n_f32 (d, multiplier), JUCE_LOAD_DEST) #else - #if JUCE_USE_SSE_INTRINSICS - const __m128 mult = _mm_load1_ps (&multiplier); - #endif - JUCE_PERFORM_SSE_OP_DEST (dest[i] *= multiplier, _mm_mul_ps (d, mult), JUCE_LOAD_DEST) + JUCE_PERFORM_VEC_OP_DEST (dest[i] *= multiplier, Mode::mul (d, mult), JUCE_LOAD_DEST, + const Mode::ParallelType mult = Mode::load1 (multiplier);) + #endif +} + +void JUCE_CALLTYPE FloatVectorOperations::multiply (double* dest, double multiplier, int num) noexcept +{ + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vsmulD (dest, 1, &multiplier, dest, 1, num); + #else + JUCE_PERFORM_VEC_OP_DEST (dest[i] *= multiplier, Mode::mul (d, mult), JUCE_LOAD_DEST, + const Mode::ParallelType mult = Mode::load1 (multiplier);) #endif } @@ -359,6 +537,15 @@ void FloatVectorOperations::negate (float* dest, const float* src, int num) noex #endif } +void FloatVectorOperations::negate (double* dest, const double* src, int num) noexcept +{ + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vnegD ((double*) src, 1, dest, 1, (vDSP_Length) num); + #else + copyWithMultiply (dest, src, -1.0f, num); + #endif +} + void JUCE_CALLTYPE FloatVectorOperations::convertFixedToFloat (float* dest, const int* src, float multiplier, int num) noexcept { #if JUCE_USE_ARM_NEON @@ -366,118 +553,44 @@ void JUCE_CALLTYPE FloatVectorOperations::convertFixedToFloat (float* dest, cons vmulq_n_f32 (vcvtq_f32_s32 (vld1q_s32 (src)), multiplier), JUCE_LOAD_NONE) #else - #if JUCE_USE_SSE_INTRINSICS - const __m128 mult = _mm_load1_ps (&multiplier); - #endif - - JUCE_PERFORM_SSE_OP_SRC_DEST (dest[i] = src[i] * multiplier, - _mm_mul_ps (mult, _mm_cvtepi32_ps (_mm_loadu_si128 ((const __m128i*) src))), - JUCE_LOAD_NONE, JUCE_INCREMENT_SRC_DEST) + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] * multiplier, + Mode::mul (mult, _mm_cvtepi32_ps (_mm_loadu_si128 ((const __m128i*) src))), + JUCE_LOAD_NONE, JUCE_INCREMENT_SRC_DEST, + const Mode::ParallelType mult = Mode::load1 (multiplier);) #endif } -void JUCE_CALLTYPE FloatVectorOperations::findMinAndMax (const float* src, int num, float& minResult, float& maxResult) noexcept +Range JUCE_CALLTYPE FloatVectorOperations::findMinAndMax (const float* src, int num) noexcept { - #if JUCE_USE_SSE_INTRINSICS - const int numLongOps = num / 4; - - if (numLongOps > 1 && FloatVectorHelpers::isSSE2Available()) - { - __m128 mn, mx; - - #define JUCE_MINMAX_SSE_LOOP(loadOp) \ - mn = loadOp (src); \ - mx = mn; \ - src += 4; \ - for (int i = 1; i < numLongOps; ++i) \ - { \ - const __m128 s = loadOp (src); \ - mn = _mm_min_ps (mn, s); \ - mx = _mm_max_ps (mx, s); \ - src += 4; \ - } - - if (FloatVectorHelpers::isAligned (src)) { JUCE_MINMAX_SSE_LOOP (_mm_load_ps) } - else { JUCE_MINMAX_SSE_LOOP (_mm_loadu_ps) } - - float localMin, localMax; - - { - float mns[4], mxs[4]; - _mm_storeu_ps (mns, mn); - _mm_storeu_ps (mxs, mx); - - localMin = jmin (mns[0], mns[1], mns[2], mns[3]); - localMax = jmax (mxs[0], mxs[1], mxs[2], mxs[3]); - } - - num &= 3; - - for (int i = 0; i < num; ++i) - { - const float s = src[i]; - localMin = jmin (localMin, s); - localMax = jmax (localMax, s); - } - - minResult = localMin; - maxResult = localMax; - return; - } - #elif JUCE_USE_ARM_NEON - const int numLongOps = num / 4; - - if (numLongOps > 1) - { - float32x4_t mn, mx; - - #define JUCE_MINMAX_NEON_LOOP(loadOp) \ - mn = loadOp (src); \ - mx = mn; \ - src += 4; \ - for (int i = 1; i < numLongOps; ++i) \ - { \ - const float32x4_t s = loadOp (src); \ - mn = vminq_f32 (mn, s); \ - mx = vmaxq_f32 (mx, s); \ - src += 4; \ - } - - JUCE_MINMAX_NEON_LOOP (vld1q_f32); - - float localMin, localMax; - - { - float mns[4], mxs[4]; - vst1q_f32 (mns, mn); - vst1q_f32 (mxs, mx); - - localMin = jmin (mns[0], mns[1], mns[2], mns[3]); - localMax = jmax (mxs[0], mxs[1], mxs[2], mxs[3]); - } - - num &= 3; - - for (int i = 0; i < num; ++i) - { - const float s = src[i]; - localMin = jmin (localMin, s); - localMax = jmax (localMax, s); - } - - minResult = localMin; - maxResult = localMax; - return; - } + #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON + return FloatVectorHelpers::MinMax::findMinAndMax (src, num); + #else + return Range::findMinAndMax (src, num); #endif +} - juce::findMinAndMax (src, num, minResult, maxResult); +Range JUCE_CALLTYPE FloatVectorOperations::findMinAndMax (const double* src, int num) noexcept +{ + #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON + return FloatVectorHelpers::MinMax::findMinAndMax (src, num); + #else + return Range::findMinAndMax (src, num); + #endif } float JUCE_CALLTYPE FloatVectorOperations::findMinimum (const float* src, int num) noexcept { #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON - return FloatVectorHelpers::findMinimumOrMaximum (src, num, true); + return FloatVectorHelpers::MinMax::findMinOrMax (src, num, true); + #else + return juce::findMinimum (src, num); + #endif +} + +double JUCE_CALLTYPE FloatVectorOperations::findMinimum (const double* src, int num) noexcept +{ + #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON + return FloatVectorHelpers::MinMax::findMinOrMax (src, num, true); #else return juce::findMinimum (src, num); #endif @@ -486,7 +599,16 @@ float JUCE_CALLTYPE FloatVectorOperations::findMinimum (const float* src, int nu float JUCE_CALLTYPE FloatVectorOperations::findMaximum (const float* src, int num) noexcept { #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON - return FloatVectorHelpers::findMinimumOrMaximum (src, num, false); + return FloatVectorHelpers::MinMax::findMinOrMax (src, num, false); + #else + return juce::findMaximum (src, num); + #endif +} + +double JUCE_CALLTYPE FloatVectorOperations::findMaximum (const double* src, int num) noexcept +{ + #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON + return FloatVectorHelpers::MinMax::findMinOrMax (src, num, false); #else return juce::findMaximum (src, num); #endif @@ -510,116 +632,137 @@ class FloatVectorOperationsTests : public UnitTest public: FloatVectorOperationsTests() : UnitTest ("FloatVectorOperations") {} - void runTest() + template + struct TestRunner { - beginTest ("FloatVectorOperations"); - - for (int i = 100; --i >= 0;) + static void runTest (UnitTest& u, Random random) { - const int num = getRandom().nextInt (500) + 1; + const int range = random.nextBool() ? 500 : 10; + const int num = random.nextInt (range) + 1; - HeapBlock buffer1 (num + 16), buffer2 (num + 16); + HeapBlock buffer1 (num + 16), buffer2 (num + 16); HeapBlock buffer3 (num + 16); #if JUCE_ARM - float* const data1 = buffer1; - float* const data2 = buffer2; + ValueType* const data1 = buffer1; + ValueType* const data2 = buffer2; int* const int1 = buffer3; #else - float* const data1 = addBytesToPointer (buffer1.getData(), getRandom().nextInt (16)); - float* const data2 = addBytesToPointer (buffer2.getData(), getRandom().nextInt (16)); - int* const int1 = addBytesToPointer (buffer3.getData(), getRandom().nextInt (16)); + ValueType* const data1 = addBytesToPointer (buffer1.getData(), random.nextInt (16)); + ValueType* const data2 = addBytesToPointer (buffer2.getData(), random.nextInt (16)); + int* const int1 = addBytesToPointer (buffer3.getData(), random.nextInt (16)); #endif - fillRandomly (data1, num); - fillRandomly (data2, num); + fillRandomly (random, data1, num); + fillRandomly (random, data2, num); - float mn1, mx1, mn2, mx2; - FloatVectorOperations::findMinAndMax (data1, num, mn1, mx1); - juce::findMinAndMax (data1, num, mn2, mx2); - expect (mn1 == mn2); - expect (mx1 == mx2); + Range minMax1 (FloatVectorOperations::findMinAndMax (data1, num)); + Range minMax2 (Range::findMinAndMax (data1, num)); + u.expect (minMax1 == minMax2); - expect (FloatVectorOperations::findMinimum (data1, num) == juce::findMinimum (data1, num)); - expect (FloatVectorOperations::findMaximum (data1, num) == juce::findMaximum (data1, num)); + u.expect (valuesMatch (FloatVectorOperations::findMinimum (data1, num), juce::findMinimum (data1, num))); + u.expect (valuesMatch (FloatVectorOperations::findMaximum (data1, num), juce::findMaximum (data1, num))); - expect (FloatVectorOperations::findMinimum (data2, num) == juce::findMinimum (data2, num)); - expect (FloatVectorOperations::findMaximum (data2, num) == juce::findMaximum (data2, num)); + u.expect (valuesMatch (FloatVectorOperations::findMinimum (data2, num), juce::findMinimum (data2, num))); + u.expect (valuesMatch (FloatVectorOperations::findMaximum (data2, num), juce::findMaximum (data2, num))); FloatVectorOperations::clear (data1, num); - expect (areAllValuesEqual (data1, num, 0)); + u.expect (areAllValuesEqual (data1, num, 0)); - FloatVectorOperations::fill (data1, 2.0f, num); - expect (areAllValuesEqual (data1, num, 2.0f)); + FloatVectorOperations::fill (data1, (ValueType) 2, num); + u.expect (areAllValuesEqual (data1, num, (ValueType) 2)); - FloatVectorOperations::add (data1, 2.0f, num); - expect (areAllValuesEqual (data1, num, 4.0f)); + FloatVectorOperations::add (data1, (ValueType) 2, num); + u.expect (areAllValuesEqual (data1, num, (ValueType) 4)); FloatVectorOperations::copy (data2, data1, num); - expect (areAllValuesEqual (data2, num, 4.0f)); + u.expect (areAllValuesEqual (data2, num, (ValueType) 4)); FloatVectorOperations::add (data2, data1, num); - expect (areAllValuesEqual (data2, num, 8.0f)); + u.expect (areAllValuesEqual (data2, num, (ValueType) 8)); - FloatVectorOperations::copyWithMultiply (data2, data1, 4.0f, num); - expect (areAllValuesEqual (data2, num, 16.0f)); + FloatVectorOperations::copyWithMultiply (data2, data1, (ValueType) 4, num); + u.expect (areAllValuesEqual (data2, num, (ValueType) 16)); - FloatVectorOperations::addWithMultiply (data2, data1, 4.0f, num); - expect (areAllValuesEqual (data2, num, 32.0f)); + FloatVectorOperations::addWithMultiply (data2, data1, (ValueType) 4, num); + u.expect (areAllValuesEqual (data2, num, (ValueType) 32)); - FloatVectorOperations::multiply (data1, 2.0f, num); - expect (areAllValuesEqual (data1, num, 8.0f)); + FloatVectorOperations::multiply (data1, (ValueType) 2, num); + u.expect (areAllValuesEqual (data1, num, (ValueType) 8)); FloatVectorOperations::multiply (data1, data2, num); - expect (areAllValuesEqual (data1, num, 256.0f)); + u.expect (areAllValuesEqual (data1, num, (ValueType) 256)); FloatVectorOperations::negate (data2, data1, num); - expect (areAllValuesEqual (data2, num, -256.0f)); + u.expect (areAllValuesEqual (data2, num, (ValueType) -256)); FloatVectorOperations::subtract (data1, data2, num); - expect (areAllValuesEqual (data1, num, 512.0f)); + u.expect (areAllValuesEqual (data1, num, (ValueType) 512)); + + fillRandomly (random, int1, num); + doConversionTest (u, data1, data2, int1, num); + } - fillRandomly (int1, num); + static void doConversionTest (UnitTest& u, float* data1, float* data2, int* const int1, int num) + { FloatVectorOperations::convertFixedToFloat (data1, int1, 2.0f, num); convertFixed (data2, int1, 2.0f, num); - expect (buffersMatch (data1, data2, num)); + u.expect (buffersMatch (data1, data2, num)); } - } - void fillRandomly (float* d, int num) - { - while (--num >= 0) - *d++ = getRandom().nextFloat() * 1000.0f; - } + static void doConversionTest (UnitTest&, double*, double*, int*, int) {} - void fillRandomly (int* d, int num) - { - while (--num >= 0) - *d++ = getRandom().nextInt(); - } + static void fillRandomly (Random& random, ValueType* d, int num) + { + while (--num >= 0) + *d++ = (ValueType) (random.nextDouble() * 1000.0); + } - static void convertFixed (float* d, const int* s, float multiplier, int num) - { - while (--num >= 0) - *d++ = *s++ * multiplier; - } + static void fillRandomly (Random& random, int* d, int num) + { + while (--num >= 0) + *d++ = random.nextInt(); + } - static bool areAllValuesEqual (const float* d, int num, float target) - { - while (--num >= 0) - if (*d++ != target) - return false; + static void convertFixed (float* d, const int* s, ValueType multiplier, int num) + { + while (--num >= 0) + *d++ = *s++ * multiplier; + } - return true; - } + static bool areAllValuesEqual (const ValueType* d, int num, ValueType target) + { + while (--num >= 0) + if (*d++ != target) + return false; - static bool buffersMatch (const float* d1, const float* d2, int num) + return true; + } + + static bool buffersMatch (const ValueType* d1, const ValueType* d2, int num) + { + while (--num >= 0) + if (! valuesMatch (*d1++, *d2++)) + return false; + + return true; + } + + static bool valuesMatch (ValueType v1, ValueType v2) + { + return std::abs (v1 - v2) < std::numeric_limits::epsilon(); + } + }; + + void runTest() { - while (--num >= 0) - if (std::abs (*d1++ - *d2++) > std::numeric_limits::epsilon()) - return false; + beginTest ("FloatVectorOperations"); - return true; + for (int i = 1000; --i >= 0;) + { + TestRunner::runTest (*this, getRandom()); + TestRunner::runTest (*this, getRandom()); + } } }; diff --git a/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.h b/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.h index 6ffcfab37..54821d28c 100644 --- a/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.h +++ b/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.h @@ -38,48 +38,90 @@ public: /** Clears a vector of floats. */ static void JUCE_CALLTYPE clear (float* dest, int numValues) noexcept; + /** Clears a vector of doubles. */ + static void JUCE_CALLTYPE clear (double* dest, int numValues) noexcept; + /** Copies a repeated value into a vector of floats. */ static void JUCE_CALLTYPE fill (float* dest, float valueToFill, int numValues) noexcept; + /** Copies a repeated value into a vector of doubles. */ + static void JUCE_CALLTYPE fill (double* dest, double valueToFill, int numValues) noexcept; + /** Copies a vector of floats. */ static void JUCE_CALLTYPE copy (float* dest, const float* src, int numValues) noexcept; + /** Copies a vector of doubles. */ + static void JUCE_CALLTYPE copy (double* dest, const double* src, int numValues) noexcept; + /** Copies a vector of floats, multiplying each value by a given multiplier */ static void JUCE_CALLTYPE copyWithMultiply (float* dest, const float* src, float multiplier, int numValues) noexcept; + /** Copies a vector of doubles, multiplying each value by a given multiplier */ + static void JUCE_CALLTYPE copyWithMultiply (double* dest, const double* src, double multiplier, int numValues) noexcept; + + /** Adds a fixed value to the destination values. */ + static void JUCE_CALLTYPE add (float* dest, float amountToAdd, int numValues) noexcept; + /** Adds a fixed value to the destination values. */ - static void JUCE_CALLTYPE add (float* dest, float amount, int numValues) noexcept; + static void JUCE_CALLTYPE add (double* dest, double amountToAdd, int numValues) noexcept; /** Adds the source values to the destination values. */ static void JUCE_CALLTYPE add (float* dest, const float* src, int numValues) noexcept; + /** Adds the source values to the destination values. */ + static void JUCE_CALLTYPE add (double* dest, const double* src, int numValues) noexcept; + /** Subtracts the source values from the destination values. */ static void JUCE_CALLTYPE subtract (float* dest, const float* src, int numValues) noexcept; + /** Subtracts the source values from the destination values. */ + static void JUCE_CALLTYPE subtract (double* dest, const double* src, int numValues) noexcept; + /** Multiplies each source value by the given multiplier, then adds it to the destination value. */ static void JUCE_CALLTYPE addWithMultiply (float* dest, const float* src, float multiplier, int numValues) noexcept; + /** Multiplies each source value by the given multiplier, then adds it to the destination value. */ + static void JUCE_CALLTYPE addWithMultiply (double* dest, const double* src, double multiplier, int numValues) noexcept; + /** Multiplies the destination values by the source values. */ static void JUCE_CALLTYPE multiply (float* dest, const float* src, int numValues) noexcept; + /** Multiplies the destination values by the source values. */ + static void JUCE_CALLTYPE multiply (double* dest, const double* src, int numValues) noexcept; + /** Multiplies each of the destination values by a fixed multiplier. */ static void JUCE_CALLTYPE multiply (float* dest, float multiplier, int numValues) noexcept; + /** Multiplies each of the destination values by a fixed multiplier. */ + static void JUCE_CALLTYPE multiply (double* dest, double multiplier, int numValues) noexcept; + /** Copies a source vector to a destination, negating each value. */ static void JUCE_CALLTYPE negate (float* dest, const float* src, int numValues) noexcept; + /** Copies a source vector to a destination, negating each value. */ + static void JUCE_CALLTYPE negate (double* dest, const double* src, int numValues) noexcept; + /** Converts a stream of integers to floats, multiplying each one by the given multiplier. */ static void JUCE_CALLTYPE convertFixedToFloat (float* dest, const int* src, float multiplier, int numValues) noexcept; /** Finds the miniumum and maximum values in the given array. */ - static void JUCE_CALLTYPE findMinAndMax (const float* src, int numValues, float& minResult, float& maxResult) noexcept; + static Range JUCE_CALLTYPE findMinAndMax (const float* src, int numValues) noexcept; + + /** Finds the miniumum and maximum values in the given array. */ + static Range JUCE_CALLTYPE findMinAndMax (const double* src, int numValues) noexcept; /** Finds the miniumum value in the given array. */ static float JUCE_CALLTYPE findMinimum (const float* src, int numValues) noexcept; + /** Finds the miniumum value in the given array. */ + static double JUCE_CALLTYPE findMinimum (const double* src, int numValues) noexcept; + /** Finds the maximum value in the given array. */ static float JUCE_CALLTYPE findMaximum (const float* src, int numValues) noexcept; + /** Finds the maximum value in the given array. */ + static double JUCE_CALLTYPE findMaximum (const double* src, int numValues) noexcept; + /** On Intel CPUs, this method enables or disables the SSE flush-to-zero mode. Effectively, this is a wrapper around a call to _MM_SET_FLUSH_ZERO_MODE */ diff --git a/source/modules/juce_audio_basics/juce_audio_basics.cpp b/source/modules/juce_audio_basics/juce_audio_basics.cpp index d8e5b9b1f..0e13f7d8a 100644 --- a/source/modules/juce_audio_basics/juce_audio_basics.cpp +++ b/source/modules/juce_audio_basics/juce_audio_basics.cpp @@ -36,6 +36,10 @@ #include "AppConfig.h" #include "juce_audio_basics.h" +#if JUCE_MINGW && ! defined (__SSE2__) + #define JUCE_USE_SSE_INTRINSICS 0 +#endif + #ifndef JUCE_USE_SSE_INTRINSICS #define JUCE_USE_SSE_INTRINSICS 1 #endif diff --git a/source/modules/juce_audio_basics/midi/juce_MidiFile.cpp b/source/modules/juce_audio_basics/midi/juce_MidiFile.cpp index 7928bcef5..e798cc0c6 100644 --- a/source/modules/juce_audio_basics/midi/juce_MidiFile.cpp +++ b/source/modules/juce_audio_basics/midi/juce_MidiFile.cpp @@ -350,11 +350,13 @@ void MidiFile::convertTimestampTicksToSeconds() } //============================================================================== -bool MidiFile::writeTo (OutputStream& out) +bool MidiFile::writeTo (OutputStream& out, int midiFileType) { + jassert (midiFileType >= 0 && midiFileType <= 2); + out.writeIntBigEndian ((int) ByteOrder::bigEndianInt ("MThd")); out.writeIntBigEndian (6); - out.writeShortBigEndian (1); // type + out.writeShortBigEndian ((short) midiFileType); out.writeShortBigEndian ((short) tracks.size()); out.writeShortBigEndian (timeFormat); diff --git a/source/modules/juce_audio_basics/midi/juce_MidiFile.h b/source/modules/juce_audio_basics/midi/juce_MidiFile.h index 6bdc784ec..2a6993714 100644 --- a/source/modules/juce_audio_basics/midi/juce_MidiFile.h +++ b/source/modules/juce_audio_basics/midi/juce_MidiFile.h @@ -151,9 +151,11 @@ public: bool readFrom (InputStream& sourceStream); /** 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. @returns true if the operation succeeded. */ - bool writeTo (OutputStream& destStream); + bool writeTo (OutputStream& destStream, int midiFileType = 1); /** Converts the timestamp of all the midi events from midi ticks to seconds. diff --git a/source/modules/juce_audio_basics/midi/juce_MidiMessage.cpp b/source/modules/juce_audio_basics/midi/juce_MidiMessage.cpp index 6f6c37475..18733a30d 100644 --- a/source/modules/juce_audio_basics/midi/juce_MidiMessage.cpp +++ b/source/modules/juce_audio_basics/midi/juce_MidiMessage.cpp @@ -152,7 +152,8 @@ MidiMessage::MidiMessage (const MidiMessage& other, const double newTimeStamp) } } -MidiMessage::MidiMessage (const void* srcData, int sz, int& numBytesUsed, const uint8 lastStatusByte, double t) +MidiMessage::MidiMessage (const void* srcData, int sz, int& numBytesUsed, const uint8 lastStatusByte, + double t, bool sysexHasEmbeddedLength) : timeStamp (t) { const uint8* src = static_cast (srcData); @@ -175,7 +176,7 @@ MidiMessage::MidiMessage (const void* srcData, int sz, int& numBytesUsed, const if (byte == 0xf0) { const uint8* d = src; - bool haveReadAllLengthBytes = false; + bool haveReadAllLengthBytes = ! sysexHasEmbeddedLength; int numVariableLengthSysexBytes = 0; while (d < src + sz) diff --git a/source/modules/juce_audio_basics/midi/juce_MidiMessage.h b/source/modules/juce_audio_basics/midi/juce_MidiMessage.h index 4694c550f..9002aa033 100644 --- a/source/modules/juce_audio_basics/midi/juce_MidiMessage.h +++ b/source/modules/juce_audio_basics/midi/juce_MidiMessage.h @@ -82,10 +82,14 @@ public: has in fact been dropped. @param timeStamp the time to give the midi message - this value doesn't use any particular units, so will be application-specific + @param sysexHasEmbeddedLength when reading sysexes, this flag indicates whether + to expect the data to begin with a variable-length field + indicating its size */ MidiMessage (const void* data, int maxBytesToUse, int& numBytesUsed, uint8 lastStatusByte, - double timeStamp = 0); + double timeStamp = 0, + bool sysexHasEmbeddedLength = true); /** Creates an active-sense message. Since the MidiMessage has to contain a valid message, this default constructor @@ -94,10 +98,10 @@ public: MidiMessage() noexcept; /** Creates a copy of another midi message. */ - MidiMessage (const MidiMessage& other); + MidiMessage (const MidiMessage&); /** Creates a copy of another midi message, with a different timestamp. */ - MidiMessage (const MidiMessage& other, double newTimeStamp); + MidiMessage (const MidiMessage&, double newTimeStamp); /** Destructor. */ ~MidiMessage(); @@ -106,8 +110,8 @@ public: MidiMessage& operator= (const MidiMessage& other); #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS - MidiMessage (MidiMessage&& other) noexcept; - MidiMessage& operator= (MidiMessage&& other) noexcept; + MidiMessage (MidiMessage&&) noexcept; + MidiMessage& operator= (MidiMessage&&) noexcept; #endif //============================================================================== diff --git a/source/modules/juce_audio_basics/sources/juce_IIRFilterAudioSource.cpp b/source/modules/juce_audio_basics/sources/juce_IIRFilterAudioSource.cpp index 712a24b9d..08e87f0c1 100644 --- a/source/modules/juce_audio_basics/sources/juce_IIRFilterAudioSource.cpp +++ b/source/modules/juce_audio_basics/sources/juce_IIRFilterAudioSource.cpp @@ -72,6 +72,6 @@ void IIRFilterAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& buff for (int i = 0; i < numChannels; ++i) iirFilters.getUnchecked(i) - ->processSamples (bufferToFill.buffer->getSampleData (i, bufferToFill.startSample), + ->processSamples (bufferToFill.buffer->getWritePointer (i, bufferToFill.startSample), bufferToFill.numSamples); } diff --git a/source/modules/juce_audio_basics/sources/juce_ResamplingAudioSource.cpp b/source/modules/juce_audio_basics/sources/juce_ResamplingAudioSource.cpp index 4f8c4cf25..a48ed43e1 100644 --- a/source/modules/juce_audio_basics/sources/juce_ResamplingAudioSource.cpp +++ b/source/modules/juce_audio_basics/sources/juce_ResamplingAudioSource.cpp @@ -121,7 +121,7 @@ void ResamplingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& inf // for down-sampling, pre-apply the filter.. for (int i = channelsToProcess; --i >= 0;) - applyFilter (buffer.getSampleData (i, endOfBufferPos), numToDo, filterStates[i]); + applyFilter (buffer.getWritePointer (i, endOfBufferPos), numToDo, filterStates[i]); } sampsInBuffer += numToDo; @@ -130,8 +130,8 @@ void ResamplingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& inf for (int channel = 0; channel < channelsToProcess; ++channel) { - destBuffers[channel] = info.buffer->getSampleData (channel, info.startSample); - srcBuffers[channel] = buffer.getSampleData (channel, 0); + destBuffers[channel] = info.buffer->getWritePointer (channel, info.startSample); + srcBuffers[channel] = buffer.getReadPointer (channel); } int nextPos = (bufferPos + 1) % bufferSize; @@ -163,14 +163,14 @@ void ResamplingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& inf { // for up-sampling, apply the filter after transposing.. for (int i = channelsToProcess; --i >= 0;) - applyFilter (info.buffer->getSampleData (i, info.startSample), info.numSamples, filterStates[i]); + applyFilter (info.buffer->getWritePointer (i, info.startSample), info.numSamples, filterStates[i]); } else if (localRatio <= 1.0001 && info.numSamples > 0) { // if the filter's not currently being applied, keep it stoked with the last couple of samples to avoid discontinuities for (int i = channelsToProcess; --i >= 0;) { - const float* const endOfBuffer = info.buffer->getSampleData (i, info.startSample + info.numSamples - 1); + const float* const endOfBuffer = info.buffer->getReadPointer (i, info.startSample + info.numSamples - 1); FilterState& fs = filterStates[i]; if (info.numSamples > 1) diff --git a/source/modules/juce_audio_basics/sources/juce_ResamplingAudioSource.h b/source/modules/juce_audio_basics/sources/juce_ResamplingAudioSource.h index 76e79ef15..20886e68b 100644 --- a/source/modules/juce_audio_basics/sources/juce_ResamplingAudioSource.h +++ b/source/modules/juce_audio_basics/sources/juce_ResamplingAudioSource.h @@ -81,7 +81,8 @@ private: double coefficients[6]; SpinLock ratioLock; const int numChannels; - HeapBlock destBuffers, srcBuffers; + HeapBlock destBuffers; + HeapBlock srcBuffers; void setFilterCoefficients (double c1, double c2, double c3, double c4, double c5, double c6); void createLowPass (double proportionalRate); diff --git a/source/modules/juce_audio_basics/sources/juce_ReverbAudioSource.cpp b/source/modules/juce_audio_basics/sources/juce_ReverbAudioSource.cpp index fc38e6054..d63007537 100644 --- a/source/modules/juce_audio_basics/sources/juce_ReverbAudioSource.cpp +++ b/source/modules/juce_audio_basics/sources/juce_ReverbAudioSource.cpp @@ -48,12 +48,12 @@ void ReverbAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& bufferT if (! bypass) { - float* const firstChannel = bufferToFill.buffer->getSampleData (0, bufferToFill.startSample); + float* const firstChannel = bufferToFill.buffer->getWritePointer (0, bufferToFill.startSample); if (bufferToFill.buffer->getNumChannels() > 1) { reverb.processStereo (firstChannel, - bufferToFill.buffer->getSampleData (1, bufferToFill.startSample), + bufferToFill.buffer->getWritePointer (1, bufferToFill.startSample), bufferToFill.numSamples); } else diff --git a/source/modules/juce_audio_basics/sources/juce_ToneGeneratorAudioSource.cpp b/source/modules/juce_audio_basics/sources/juce_ToneGeneratorAudioSource.cpp index 7f5b9393f..a0e04eff2 100644 --- a/source/modules/juce_audio_basics/sources/juce_ToneGeneratorAudioSource.cpp +++ b/source/modules/juce_audio_basics/sources/juce_ToneGeneratorAudioSource.cpp @@ -70,6 +70,6 @@ void ToneGeneratorAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& currentPhase += phasePerSample; for (int j = info.buffer->getNumChannels(); --j >= 0;) - *info.buffer->getSampleData (j, info.startSample + i) = sample; + info.buffer->setSample (j, info.startSample + i, sample); } } diff --git a/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp b/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp index 1aa248bb0..2f0b13bd3 100644 --- a/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp +++ b/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp @@ -58,6 +58,11 @@ void SynthesiserVoice::clearCurrentNote() void SynthesiserVoice::aftertouchChanged (int) {} +bool SynthesiserVoice::wasStartedBefore (const SynthesiserVoice& other) const noexcept +{ + return noteOnTime < other.noteOnTime; +} + //============================================================================== Synthesiser::Synthesiser() : sampleRate (0), @@ -407,12 +412,11 @@ void Synthesiser::handleSoftPedal (int midiChannel, bool /*isDown*/) } //============================================================================== -SynthesiserVoice* Synthesiser::findFreeVoice (SynthesiserSound* soundToPlay, - const bool stealIfNoneAvailable) const +SynthesiserVoice* Synthesiser::findFreeVoice (SynthesiserSound* soundToPlay, const bool stealIfNoneAvailable) const { const ScopedLock sl (lock); - for (int i = voices.size(); --i >= 0;) + for (int i = 0; i < voices.size(); ++i) { SynthesiserVoice* const voice = voices.getUnchecked (i); @@ -421,22 +425,25 @@ SynthesiserVoice* Synthesiser::findFreeVoice (SynthesiserSound* soundToPlay, } if (stealIfNoneAvailable) - { - // currently this just steals the one that's been playing the longest, but could be made a bit smarter.. - SynthesiserVoice* oldest = nullptr; + return findVoiceToSteal (soundToPlay); - for (int i = voices.size(); --i >= 0;) - { - SynthesiserVoice* const voice = voices.getUnchecked (i); + return nullptr; +} - if (voice->canPlaySound (soundToPlay) - && (oldest == nullptr || oldest->noteOnTime > voice->noteOnTime)) - oldest = voice; - } +SynthesiserVoice* Synthesiser::findVoiceToSteal (SynthesiserSound* soundToPlay) const +{ + // currently this just steals the one that's been playing the longest, but could be made a bit smarter.. + SynthesiserVoice* oldest = nullptr; - jassert (oldest != nullptr); - return oldest; + for (int i = 0; i < voices.size(); ++i) + { + SynthesiserVoice* const voice = voices.getUnchecked (i); + + if (voice->canPlaySound (soundToPlay) + && (oldest == nullptr || voice->wasStartedBefore (*oldest))) + oldest = voice; } - return nullptr; + jassert (oldest != nullptr); + return oldest; } diff --git a/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h b/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h index 7202b4b28..069253ba1 100644 --- a/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h +++ b/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h @@ -199,6 +199,9 @@ public: /** Returns true if the sostenuto pedal is currently active for this voice. */ bool isSostenutoPedalDown() const noexcept { return sostenutoPedalDown; } + /** Returns true if this voice started playing its current note before the other voice did. */ + bool wasStartedBefore (const SynthesiserVoice& other) const noexcept; + protected: //============================================================================== /** Returns the current target sample rate at which rendering is being done. @@ -481,6 +484,12 @@ protected: virtual SynthesiserVoice* findFreeVoice (SynthesiserSound* soundToPlay, const bool stealIfNoneAvailable) const; + /** Chooses a voice that is most suitable for being re-used. + The default method returns the one that has been playing for the longest, but + you may want to override this and do something more cunning instead. + */ + virtual SynthesiserVoice* findVoiceToSteal (SynthesiserSound* soundToPlay) const; + /** Starts a specified voice playing a particular sound. You'll probably never need to call this, it's used internally by noteOn(), but diff --git a/source/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp b/source/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp index 262469cdc..f954d276b 100644 --- a/source/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp +++ b/source/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp @@ -728,7 +728,7 @@ void AudioDeviceManager::audioDeviceIOCallbackInt (const float** inputChannelDat callbacks.getUnchecked(0)->audioDeviceIOCallback (inputChannelData, numInputChannels, outputChannelData, numOutputChannels, numSamples); - float** const tempChans = tempBuffer.getArrayOfChannels(); + float** const tempChans = tempBuffer.getArrayOfWritePointers(); for (int i = callbacks.size(); --i > 0;) { @@ -757,7 +757,7 @@ void AudioDeviceManager::audioDeviceIOCallbackInt (const float** inputChannelDat if (testSound != nullptr) { const int numSamps = jmin (numSamples, testSound->getNumSamples() - testSoundPosition); - const float* const src = testSound->getSampleData (0, testSoundPosition); + const float* const src = testSound->getReadPointer (0, testSoundPosition); for (int i = 0; i < numOutputChannels; ++i) for (int j = 0; j < numSamps; ++j) @@ -951,16 +951,15 @@ void AudioDeviceManager::playTestSound() const double sampleRate = currentAudioDevice->getCurrentSampleRate(); const int soundLength = (int) sampleRate; - AudioSampleBuffer* const newSound = new AudioSampleBuffer (1, soundLength); - float* samples = newSound->getSampleData (0); - const double frequency = 440.0; const float amplitude = 0.5f; const double phasePerSample = double_Pi * 2.0 / (sampleRate / frequency); + AudioSampleBuffer* const newSound = new AudioSampleBuffer (1, soundLength); + for (int i = 0; i < soundLength; ++i) - samples[i] = amplitude * (float) std::sin (i * phasePerSample); + newSound->setSample (0, i, amplitude * (float) std::sin (i * phasePerSample)); newSound->applyGainRamp (0, 0, soundLength / 10, 0.0f, 1.0f); newSound->applyGainRamp (0, soundLength - soundLength / 4, soundLength / 4, 1.0f, 0.0f); diff --git a/source/modules/juce_audio_devices/native/juce_android_Audio.cpp b/source/modules/juce_audio_devices/native/juce_android_Audio.cpp index 2e3cd7e93..80711da9e 100644 --- a/source/modules/juce_audio_devices/native/juce_android_Audio.cpp +++ b/source/modules/juce_audio_devices/native/juce_android_Audio.cpp @@ -304,7 +304,7 @@ public: for (int chan = 0; chan < inputChannelBuffer.getNumChannels(); ++chan) { - AudioData::Pointer d (inputChannelBuffer.getSampleData (chan)); + AudioData::Pointer d (inputChannelBuffer.getWritePointer (chan)); if (chan < numDeviceInputChannels) { @@ -328,8 +328,8 @@ public: if (callback != nullptr) { - callback->audioDeviceIOCallback ((const float**) inputChannelBuffer.getArrayOfChannels(), numClientInputChannels, - outputChannelBuffer.getArrayOfChannels(), numClientOutputChannels, + callback->audioDeviceIOCallback (inputChannelBuffer.getArrayOfReadPointers(), numClientInputChannels, + outputChannelBuffer.getArrayOfWritePointers(), numClientOutputChannels, actualBufferSize); } else @@ -349,7 +349,7 @@ public: { AudioData::Pointer d (dest + chan, numDeviceOutputChannels); - const float* const sourceChanData = outputChannelBuffer.getSampleData (jmin (chan, outputChannelBuffer.getNumChannels() - 1)); + const float* const sourceChanData = outputChannelBuffer.getReadPointer (jmin (chan, outputChannelBuffer.getNumChannels() - 1)); AudioData::Pointer s (sourceChanData); d.convertSamples (s, actualBufferSize); } diff --git a/source/modules/juce_audio_devices/native/juce_android_OpenSL.cpp b/source/modules/juce_audio_devices/native/juce_android_OpenSL.cpp index eb60dcc75..842b42e53 100644 --- a/source/modules/juce_audio_devices/native/juce_android_OpenSL.cpp +++ b/source/modules/juce_audio_devices/native/juce_android_OpenSL.cpp @@ -179,10 +179,8 @@ public: if (callback != nullptr) { - callback->audioDeviceIOCallback (numInputChannels > 0 ? (const float**) inputBuffer.getArrayOfChannels() : nullptr, - numInputChannels, - numOutputChannels > 0 ? outputBuffer.getArrayOfChannels() : nullptr, - numOutputChannels, + callback->audioDeviceIOCallback (numInputChannels > 0 ? inputBuffer.getArrayOfReadPointers() : nullptr, numInputChannels, + numOutputChannels > 0 ? outputBuffer.getArrayOfWritePointers() : nullptr, numOutputChannels, actualBufferSize); } else @@ -404,7 +402,7 @@ private: typedef AudioData::Pointer SrcSampleType; DstSampleType dstData (destBuffer + i, bufferList.numChannels); - SrcSampleType srcData (buffer.getSampleData (i, offset)); + SrcSampleType srcData (buffer.getReadPointer (i, offset)); dstData.convertSamples (srcData, bufferList.numSamples); } @@ -522,7 +520,7 @@ private: typedef AudioData::Pointer DstSampleType; typedef AudioData::Pointer SrcSampleType; - DstSampleType dstData (buffer.getSampleData (i, offset)); + DstSampleType dstData (buffer.getWritePointer (i, offset)); SrcSampleType srcData (srcBuffer + i, bufferList.numChannels); dstData.convertSamples (srcData, bufferList.numSamples); } diff --git a/source/modules/juce_audio_devices/native/juce_ios_Audio.cpp b/source/modules/juce_audio_devices/native/juce_ios_Audio.cpp index f20ea444f..6016a5142 100644 --- a/source/modules/juce_audio_devices/native/juce_ios_Audio.cpp +++ b/source/modules/juce_audio_devices/native/juce_ios_Audio.cpp @@ -227,10 +227,10 @@ private: zeromem (outputChannels, sizeof (outputChannels)); for (int i = 0; i < numInputChannels; ++i) - inputChannels[i] = floatData.getSampleData (i); + inputChannels[i] = floatData.getWritePointer (i); for (int i = 0; i < numOutputChannels; ++i) - outputChannels[i] = floatData.getSampleData (i + numInputChannels); + outputChannels[i] = floatData.getWritePointer (i + numInputChannels); } } diff --git a/source/modules/juce_audio_devices/native/juce_linux_ALSA.cpp b/source/modules/juce_audio_devices/native/juce_linux_ALSA.cpp index 9bc9a2649..9bc7ecf4b 100644 --- a/source/modules/juce_audio_devices/native/juce_linux_ALSA.cpp +++ b/source/modules/juce_audio_devices/native/juce_linux_ALSA.cpp @@ -311,7 +311,7 @@ public: bool writeToOutputDevice (AudioSampleBuffer& outputChannelBuffer, const int numSamples) { jassert (numChannelsRunning <= outputChannelBuffer.getNumChannels()); - float** const data = outputChannelBuffer.getArrayOfChannels(); + float* const* const data = outputChannelBuffer.getArrayOfWritePointers(); snd_pcm_sframes_t numDone = 0; if (isInterleaved) @@ -343,7 +343,7 @@ public: bool readFromInputDevice (AudioSampleBuffer& inputChannelBuffer, const int numSamples) { jassert (numChannelsRunning <= inputChannelBuffer.getNumChannels()); - float** const data = inputChannelBuffer.getArrayOfChannels(); + float* const* const data = inputChannelBuffer.getArrayOfWritePointers(); if (isInterleaved) { @@ -497,7 +497,7 @@ public: { if (inputChannels[i]) { - inputChannelDataForCallback.add (inputChannelBuffer.getSampleData (i)); + inputChannelDataForCallback.add (inputChannelBuffer.getReadPointer (i)); currentInputChans.setBit (i); } } @@ -516,7 +516,7 @@ public: { if (outputChannels[i]) { - outputChannelDataForCallback.add (outputChannelBuffer.getSampleData (i)); + outputChannelDataForCallback.add (outputChannelBuffer.getWritePointer (i)); currentOutputChans.setBit (i); } } @@ -666,7 +666,7 @@ public: if (callback != nullptr) { - callback->audioDeviceIOCallback ((const float**) inputChannelDataForCallback.getRawDataPointer(), + callback->audioDeviceIOCallback (inputChannelDataForCallback.getRawDataPointer(), inputChannelDataForCallback.size(), outputChannelDataForCallback.getRawDataPointer(), outputChannelDataForCallback.size(), @@ -736,7 +736,8 @@ private: CriticalSection callbackLock; AudioSampleBuffer inputChannelBuffer, outputChannelBuffer; - Array inputChannelDataForCallback, outputChannelDataForCallback; + Array inputChannelDataForCallback; + Array outputChannelDataForCallback; unsigned int minChansOut, maxChansOut; unsigned int minChansIn, maxChansIn; diff --git a/source/modules/juce_audio_devices/native/juce_mac_AudioCDBurner.mm b/source/modules/juce_audio_devices/native/juce_mac_AudioCDBurner.mm index 137d44380..8dd7bfad1 100644 --- a/source/modules/juce_audio_devices/native/juce_mac_AudioCDBurner.mm +++ b/source/modules/juce_audio_devices/native/juce_mac_AudioCDBurner.mm @@ -129,9 +129,9 @@ private: typedef AudioData::Pointer SourceSampleFormat; CDSampleFormat left (buffer, 2); - left.convertSamples (SourceSampleFormat (tempBuffer.getSampleData (0)), numSamples); + left.convertSamples (SourceSampleFormat (tempBuffer.getReadPointer (0)), numSamples); CDSampleFormat right (buffer + 2, 2); - right.convertSamples (SourceSampleFormat (tempBuffer.getSampleData (1)), numSamples); + right.convertSamples (SourceSampleFormat (tempBuffer.getReadPointer (1)), numSamples); source->readPosition += numSamples; } diff --git a/source/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp b/source/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp index 74e7a6c22..9fb77ddb2 100644 --- a/source/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp +++ b/source/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp @@ -1319,14 +1319,15 @@ private: AudioSampleBuffer buffer (fifos.getNumChannels(), numSamples); buffer.clear(); - Array inputChans, outputChans; + Array inputChans; + Array outputChans; for (int i = 0; i < devices.size(); ++i) { DeviceWrapper& d = *devices.getUnchecked(i); - for (int j = 0; j < d.numInputChans; ++j) inputChans.add (buffer.getSampleData (d.inputIndex + j)); - for (int j = 0; j < d.numOutputChans; ++j) outputChans.add (buffer.getSampleData (d.outputIndex + j)); + for (int j = 0; j < d.numInputChans; ++j) inputChans.add (buffer.getReadPointer (d.inputIndex + j)); + for (int j = 0; j < d.numOutputChans; ++j) outputChans.add (buffer.getWritePointer (d.outputIndex + j)); } const int numInputChans = inputChans.size(); @@ -1535,8 +1536,8 @@ private: for (int i = 0; i < numInputChans; ++i) { const int index = inputIndex + i; - float* const dest = destBuffer.getSampleData (index); - const float* const src = owner.fifos.getSampleData (index); + float* const dest = destBuffer.getWritePointer (index); + const float* const src = owner.fifos.getReadPointer (index); if (size1 > 0) FloatVectorOperations::copy (dest, src + start1, size1); if (size2 > 0) FloatVectorOperations::copy (dest + size1, src + start2, size2); @@ -1561,8 +1562,8 @@ private: for (int i = 0; i < numOutputChans; ++i) { const int index = outputIndex + i; - float* const dest = owner.fifos.getSampleData (index); - const float* const src = srcBuffer.getSampleData (index); + float* const dest = owner.fifos.getWritePointer (index); + const float* const src = srcBuffer.getReadPointer (index); if (size1 > 0) FloatVectorOperations::copy (dest + start1, src, size1); if (size2 > 0) FloatVectorOperations::copy (dest + start2, src + size1, size2); @@ -1590,7 +1591,7 @@ private: for (int i = 0; i < numInputChannels; ++i) { - float* const dest = buf.getSampleData (inputIndex + i); + float* const dest = buf.getWritePointer (inputIndex + i); const float* const src = inputChannelData[i]; if (size1 > 0) FloatVectorOperations::copy (dest + start1, src, size1); @@ -1622,7 +1623,7 @@ private: for (int i = 0; i < numOutputChannels; ++i) { float* const dest = outputChannelData[i]; - const float* const src = buf.getSampleData (outputIndex + i); + const float* const src = buf.getReadPointer (outputIndex + i); if (size1 > 0) FloatVectorOperations::copy (dest, src + start1, size1); if (size2 > 0) FloatVectorOperations::copy (dest + size1, src + start2, size2); diff --git a/source/modules/juce_audio_devices/native/juce_win32_AudioCDBurner.cpp b/source/modules/juce_audio_devices/native/juce_win32_AudioCDBurner.cpp index e17079791..3c3befb80 100644 --- a/source/modules/juce_audio_devices/native/juce_win32_AudioCDBurner.cpp +++ b/source/modules/juce_audio_devices/native/juce_win32_AudioCDBurner.cpp @@ -391,9 +391,9 @@ bool AudioCDBurner::addAudioTrack (AudioSource* audioSource, int numSamples) AudioData::NonInterleaved, AudioData::Const> SourceSampleFormat; CDSampleFormat left (buffer, 2); - left.convertSamples (SourceSampleFormat (sourceBuffer.getSampleData (0)), samplesPerBlock); + left.convertSamples (SourceSampleFormat (sourceBuffer.getReadPointer (0)), samplesPerBlock); CDSampleFormat right (buffer + 2, 2); - right.convertSamples (SourceSampleFormat (sourceBuffer.getSampleData (1)), samplesPerBlock); + right.convertSamples (SourceSampleFormat (sourceBuffer.getReadPointer (1)), samplesPerBlock); hr = pimpl->redbook->AddAudioTrackBlocks (buffer, bytesPerBlock); diff --git a/source/modules/juce_audio_devices/native/juce_win32_DirectSound.cpp b/source/modules/juce_audio_devices/native/juce_win32_DirectSound.cpp index 3eb0757b3..d04902b62 100644 --- a/source/modules/juce_audio_devices/native/juce_win32_DirectSound.cpp +++ b/source/modules/juce_audio_devices/native/juce_win32_DirectSound.cpp @@ -998,10 +998,8 @@ public: if (isStarted) { - callback->audioDeviceIOCallback (const_cast (inputBuffers.getArrayOfChannels()), - inputBuffers.getNumChannels(), - outputBuffers.getArrayOfChannels(), - outputBuffers.getNumChannels(), + callback->audioDeviceIOCallback (inputBuffers.getArrayOfReadPointers(), inputBuffers.getNumChannels(), + outputBuffers.getArrayOfWritePointers(), outputBuffers.getNumChannels(), bufferSizeSamples); } else @@ -1105,13 +1103,8 @@ String DSoundAudioIODevice::openDevice (const BigInteger& inputChannels, for (int i = 0; i <= enabledInputs.getHighestBit(); i += 2) { - float* left = nullptr; - if (enabledInputs[i]) - left = inputBuffers.getSampleData (numIns++); - - float* right = nullptr; - if (enabledInputs[i + 1]) - right = inputBuffers.getSampleData (numIns++); + float* left = enabledInputs[i] ? inputBuffers.getWritePointer (numIns++) : nullptr; + float* right = enabledInputs[i + 1] ? inputBuffers.getWritePointer (numIns++) : nullptr; if (left != nullptr || right != nullptr) inChans.add (new DSoundInternalInChannel (dlh.inputDeviceNames [inputDeviceIndex], @@ -1131,13 +1124,8 @@ String DSoundAudioIODevice::openDevice (const BigInteger& inputChannels, for (int i = 0; i <= enabledOutputs.getHighestBit(); i += 2) { - float* left = nullptr; - if (enabledOutputs[i]) - left = outputBuffers.getSampleData (numOuts++); - - float* right = nullptr; - if (enabledOutputs[i + 1]) - right = outputBuffers.getSampleData (numOuts++); + float* left = enabledOutputs[i] ? outputBuffers.getWritePointer (numOuts++) : nullptr; + float* right = enabledOutputs[i + 1] ? outputBuffers.getWritePointer (numOuts++) : nullptr; if (left != nullptr || right != nullptr) outChans.add (new DSoundInternalOutChannel (dlh.outputDeviceNames[outputDeviceIndex], diff --git a/source/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp b/source/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp index b60cdd400..24f2d6231 100644 --- a/source/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp +++ b/source/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp @@ -632,7 +632,7 @@ public: { closeClient(); captureClient = nullptr; - reservoir.setSize (0); + reservoir.reset(); } template @@ -1063,8 +1063,8 @@ public: AudioSampleBuffer ins (jmax (1, numInputBuffers), bufferSize + 32); AudioSampleBuffer outs (jmax (1, numOutputBuffers), bufferSize + 32); - float** const inputBuffers = ins.getArrayOfChannels(); - float** const outputBuffers = outs.getArrayOfChannels(); + float** const inputBuffers = ins.getArrayOfWritePointers(); + float** const outputBuffers = outs.getArrayOfWritePointers(); ins.clear(); while (! threadShouldExit()) diff --git a/source/modules/juce_audio_devices/sources/juce_AudioSourcePlayer.cpp b/source/modules/juce_audio_devices/sources/juce_AudioSourcePlayer.cpp index ecfccb2f3..45b2a3ad6 100644 --- a/source/modules/juce_audio_devices/sources/juce_AudioSourcePlayer.cpp +++ b/source/modules/juce_audio_devices/sources/juce_AudioSourcePlayer.cpp @@ -115,7 +115,7 @@ void AudioSourcePlayer::audioDeviceIOCallback (const float** inputChannelData, for (int i = numOutputs; i < numInputs; ++i) { - channels[numActiveChans] = tempBuffer.getSampleData (i - numOutputs, 0); + channels[numActiveChans] = tempBuffer.getWritePointer (i - numOutputs); memcpy (channels[numActiveChans], inputChans[i], sizeof (float) * (size_t) numSamples); ++numActiveChans; } diff --git a/source/modules/juce_audio_formats/codecs/juce_FlacAudioFormat.cpp b/source/modules/juce_audio_formats/codecs/juce_FlacAudioFormat.cpp index 11e96e6c3..99c9fe105 100644 --- a/source/modules/juce_audio_formats/codecs/juce_FlacAudioFormat.cpp +++ b/source/modules/juce_audio_formats/codecs/juce_FlacAudioFormat.cpp @@ -61,6 +61,7 @@ namespace FlacNamespace #define FLAC__HAS_X86INTRIN 1 #endif + #undef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS 1 #define flac_max jmax #define flac_min jmin @@ -175,7 +176,7 @@ public: for (int i = jmin (numDestChannels, reservoir.getNumChannels()); --i >= 0;) if (destSamples[i] != nullptr) memcpy (destSamples[i] + startOffsetInDestBuffer, - reservoir.getSampleData (i, (int) (startSampleInFile - reservoirStart)), + reservoir.getReadPointer (i, (int) (startSampleInFile - reservoirStart)), sizeof (int) * (size_t) num); startOffsetInDestBuffer += num; @@ -242,7 +243,7 @@ public: if (src != nullptr) { - int* const dest = reinterpret_cast (reservoir.getSampleData(i)); + int* const dest = reinterpret_cast (reservoir.getWritePointer(i)); for (int j = 0; j < numSamples; ++j) dest[j] = src[j] << bitsToShift; diff --git a/source/modules/juce_audio_formats/codecs/juce_MP3AudioFormat.cpp b/source/modules/juce_audio_formats/codecs/juce_MP3AudioFormat.cpp index 8a7033a4b..219c93f70 100644 --- a/source/modules/juce_audio_formats/codecs/juce_MP3AudioFormat.cpp +++ b/source/modules/juce_audio_formats/codecs/juce_MP3AudioFormat.cpp @@ -516,12 +516,21 @@ struct MP3Frame { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160 } } }; - switch (layer) + if (bitrateIndex == 0) { - case 1: frameSize = (((frameSizes[lsf][0][bitrateIndex] * 12000) / getFrequency() + padding) * 4) - 4; break; - case 2: frameSize = (frameSizes[lsf][1][bitrateIndex] * 144000) / getFrequency() + (padding - 4); break; - case 3: frameSize = (bitrateIndex == 0) ? 0 : ((frameSizes[lsf][2][bitrateIndex] * 144000) / (getFrequency() << lsf) + (padding - 4)); break; - default: break; + jassertfalse; // This means the file is using "free format". Apparently very few decoders + // support this mode, and this one certainly doesn't handle it correctly! + frameSize = 0; + } + else + { + switch (layer) + { + case 1: frameSize = (((frameSizes[lsf][0][bitrateIndex] * 12000) / getFrequency() + padding) * 4) - 4; break; + case 2: frameSize = (frameSizes[lsf][1][bitrateIndex] * 144000) / getFrequency() + (padding - 4); break; + case 3: frameSize = (bitrateIndex == 0) ? 0 : ((frameSizes[lsf][2][bitrateIndex] * 144000) / (getFrequency() << lsf) + (padding - 4)); break; + default: break; + } } } @@ -1451,7 +1460,7 @@ struct MP3Stream bufferPointer = bufferSpace[bufferSpaceIndex] + 512; bitIndex = 0; - if (lastFrameSize == -1) + if (lastFrameSize < 0) return 1; } @@ -1513,8 +1522,14 @@ struct MP3Stream else { const int nextFrameOffset = scanForNextFrameHeader (true); + + wasFreeFormat = isFreeFormat; + if (nextFrameOffset < 0) + { + lastFrameSize = frameSize; return result; + } frameSize = nextFrameOffset + sideInfoSize + dataSize; lastFrameSizeNoPadding = frameSize - frame.padding; diff --git a/source/modules/juce_audio_formats/codecs/juce_OggVorbisAudioFormat.cpp b/source/modules/juce_audio_formats/codecs/juce_OggVorbisAudioFormat.cpp index b6932902b..e6303dc81 100644 --- a/source/modules/juce_audio_formats/codecs/juce_OggVorbisAudioFormat.cpp +++ b/source/modules/juce_audio_formats/codecs/juce_OggVorbisAudioFormat.cpp @@ -173,7 +173,7 @@ public: for (int i = jmin (numDestChannels, reservoir.getNumChannels()); --i >= 0;) if (destSamples[i] != nullptr) memcpy (destSamples[i] + startOffsetInDestBuffer, - reservoir.getSampleData (i, (int) (startSampleInFile - reservoirStart)), + reservoir.getReadPointer (i, (int) (startSampleInFile - reservoirStart)), sizeof (float) * (size_t) numToUse); startSampleInFile += numToUse; @@ -210,11 +210,7 @@ public: jassert (samps <= numToRead); for (int i = jmin ((int) numChannels, reservoir.getNumChannels()); --i >= 0;) - { - memcpy (reservoir.getSampleData (i, offset), - dataIn[i], - sizeof (float) * (size_t) samps); - } + memcpy (reservoir.getWritePointer (i, offset), dataIn[i], sizeof (float) * (size_t) samps); numToRead -= samps; offset += samps; diff --git a/source/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp b/source/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp index 859049ea3..058ff733f 100644 --- a/source/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp +++ b/source/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp @@ -983,11 +983,13 @@ private: switch (numChannels) { case 1: return 0; - case 2: return 1 + 2; // SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT - case 5: return 1 + 2 + 4 + 16 + 32; // SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT - case 6: return 1 + 2 + 4 + 8 + 16 + 32; // SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT - case 7: return 1 + 2 + 4 + 16 + 32 + 512 + 1024; // SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT - case 8: return 1 + 2 + 4 + 8 + 16 + 32 + 512 + 1024; // SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT + case 2: return 1 + 2; // SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT + case 3: return 1 + 2 + 4; // SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER + case 4: return 1 + 2 + 16 + 32; // SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT + case 5: return 1 + 2 + 4 + 16 + 32; // SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT + case 6: return 1 + 2 + 4 + 8 + 16 + 32; // SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT + case 7: return 1 + 2 + 4 + 16 + 32 + 512 + 1024; // SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT + case 8: return 1 + 2 + 4 + 8 + 16 + 32 + 512 + 1024; // SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT default: break; } diff --git a/source/modules/juce_audio_formats/codecs/juce_WindowsMediaAudioFormat.cpp b/source/modules/juce_audio_formats/codecs/juce_WindowsMediaAudioFormat.cpp index a4d45ea8a..11eec0fcb 100644 --- a/source/modules/juce_audio_formats/codecs/juce_WindowsMediaAudioFormat.cpp +++ b/source/modules/juce_audio_formats/codecs/juce_WindowsMediaAudioFormat.cpp @@ -344,6 +344,7 @@ Array WindowsMediaAudioFormat::getPossibleBitDepths() { return Array getPossibleBitDepths() override; bool canDoStereo() override; bool canDoMono() override; + bool isCompressed() override; //============================================================================== AudioFormatReader* createReaderFor (InputStream*, bool deleteStreamIfOpeningFails) override; diff --git a/source/modules/juce_audio_formats/format/juce_AudioFormatReader.cpp b/source/modules/juce_audio_formats/format/juce_AudioFormatReader.cpp index 1d2c845b1..501e33921 100644 --- a/source/modules/juce_audio_formats/format/juce_AudioFormatReader.cpp +++ b/source/modules/juce_audio_formats/format/juce_AudioFormatReader.cpp @@ -106,7 +106,7 @@ static void readChannels (AudioFormatReader& reader, const int64 readerStartSample, const int numTargetChannels) { for (int j = 0; j < numTargetChannels; ++j) - chans[j] = reinterpret_cast (buffer->getSampleData (j, startSample)); + chans[j] = reinterpret_cast (buffer->getWritePointer (j, startSample)); chans[numTargetChannels] = nullptr; reader.read (chans, numTargetChannels, readerStartSample, numSamples, true); @@ -128,8 +128,8 @@ void AudioFormatReader::read (AudioSampleBuffer* buffer, if (numTargetChannels <= 2) { - int* const dest0 = reinterpret_cast (buffer->getSampleData (0, startSample)); - int* const dest1 = reinterpret_cast (numTargetChannels > 1 ? buffer->getSampleData (1, startSample) : nullptr); + int* const dest0 = reinterpret_cast (buffer->getWritePointer (0, startSample)); + int* const dest1 = reinterpret_cast (numTargetChannels > 1 ? buffer->getWritePointer (1, startSample) : nullptr); int* chans[3]; if (useReaderLeftChan == useReaderRightChan) @@ -168,36 +168,35 @@ void AudioFormatReader::read (AudioSampleBuffer* buffer, if (! usesFloatingPointData) for (int j = 0; j < numTargetChannels; ++j) - if (float* const d = buffer->getSampleData (j, startSample)) + if (float* const d = buffer->getWritePointer (j, startSample)) FloatVectorOperations::convertFixedToFloat (d, reinterpret_cast (d), 1.0f / 0x7fffffff, numSamples); } } template -static inline void getChannelMinAndMax (SampleType* channel, const int numSamples, SampleType& mn, SampleType& mx) +static Range getChannelMinAndMax (SampleType* channel, int numSamples) noexcept { - findMinAndMax (channel, numSamples, mn, mx); + return Range::findMinAndMax (channel, numSamples); } -static inline void getChannelMinAndMax (float* channel, const int numSamples, float& mn, float& mx) +static Range getChannelMinAndMax (float* channel, int numSamples) noexcept { - FloatVectorOperations::findMinAndMax (channel, numSamples, mn, mx); + return FloatVectorOperations::findMinAndMax (channel, numSamples); } template static void getStereoMinAndMax (SampleType* const* channels, const int numChannels, const int numSamples, SampleType& lmin, SampleType& lmax, SampleType& rmin, SampleType& rmax) { - SampleType bufMin, bufMax; - getChannelMinAndMax (channels[0], numSamples, bufMin, bufMax); - lmax = jmax (lmax, bufMax); - lmin = jmin (lmin, bufMin); + Range range (getChannelMinAndMax (channels[0], numSamples)); + lmax = jmax (lmax, range.getEnd()); + lmin = jmin (lmin, range.getStart()); if (numChannels > 1) { - getChannelMinAndMax (channels[1], numSamples, bufMin, bufMax); - rmax = jmax (rmax, bufMax); - rmin = jmin (rmin, bufMin); + range = getChannelMinAndMax (channels[1], numSamples); + rmax = jmax (rmax, range.getEnd()); + rmin = jmin (rmin, range.getStart()); } else { @@ -222,7 +221,7 @@ void AudioFormatReader::readMaxLevels (int64 startSampleInFile, int64 numSamples const int bufferSize = (int) jmin (numSamples, (int64) 4096); AudioSampleBuffer tempSampleBuffer ((int) numChannels, bufferSize); - float** const floatBuffer = tempSampleBuffer.getArrayOfChannels(); + float* const* const floatBuffer = tempSampleBuffer.getArrayOfWritePointers(); int* const* intBuffer = reinterpret_cast (floatBuffer); if (usesFloatingPointData) diff --git a/source/modules/juce_audio_formats/format/juce_AudioFormatWriter.cpp b/source/modules/juce_audio_formats/format/juce_AudioFormatWriter.cpp index 9ca6756c6..cd1ffc9a7 100644 --- a/source/modules/juce_audio_formats/format/juce_AudioFormatWriter.cpp +++ b/source/modules/juce_audio_formats/format/juce_AudioFormatWriter.cpp @@ -68,7 +68,7 @@ bool AudioFormatWriter::writeFromAudioReader (AudioFormatReader& reader, int* buffers [128] = { 0 }; for (int i = tempBuffer.getNumChannels(); --i >= 0;) - buffers[i] = reinterpret_cast (tempBuffer.getSampleData (i, 0)); + buffers[i] = reinterpret_cast (tempBuffer.getWritePointer (i, 0)); if (numSamplesToRead < 0) numSamplesToRead = reader.lengthInSamples; @@ -170,13 +170,13 @@ bool AudioFormatWriter::writeFromAudioSampleBuffer (const AudioSampleBuffer& sou jassert (startSample >= 0 && startSample + numSamples <= source.getNumSamples() && numSourceChannels > 0); if (startSample == 0) - return writeFromFloatArrays (source.getArrayOfChannels(), numSourceChannels, numSamples); + return writeFromFloatArrays (source.getArrayOfReadPointers(), numSourceChannels, numSamples); const float* chans [256]; jassert ((int) numChannels < numElementsInArray (chans)); for (int i = 0; i < numSourceChannels; ++i) - chans[i] = source.getSampleData (i, startSample); + chans[i] = source.getReadPointer (i, startSample); chans[numSourceChannels] = nullptr; diff --git a/source/modules/juce_audio_formats/format/juce_BufferingAudioFormatReader.cpp b/source/modules/juce_audio_formats/format/juce_BufferingAudioFormatReader.cpp index fe20524c5..ecfe1d457 100644 --- a/source/modules/juce_audio_formats/format/juce_BufferingAudioFormatReader.cpp +++ b/source/modules/juce_audio_formats/format/juce_BufferingAudioFormatReader.cpp @@ -77,7 +77,7 @@ bool BufferingAudioReader::readSamples (int** destSamples, int numDestChannels, dest += startOffsetInDestBuffer; if (j < (int) numChannels) - FloatVectorOperations::copy (dest, block->buffer.getSampleData (j, offset), numToDo); + FloatVectorOperations::copy (dest, block->buffer.getReadPointer (j, offset), numToDo); else FloatVectorOperations::clear (dest, numToDo); } diff --git a/source/modules/juce_audio_formats/sampler/juce_Sampler.cpp b/source/modules/juce_audio_formats/sampler/juce_Sampler.cpp index fb51dcff2..9dbe7b3c5 100644 --- a/source/modules/juce_audio_formats/sampler/juce_Sampler.cpp +++ b/source/modules/juce_audio_formats/sampler/juce_Sampler.cpp @@ -154,12 +154,12 @@ void SamplerVoice::renderNextBlock (AudioSampleBuffer& outputBuffer, int startSa { if (const SamplerSound* const playingSound = static_cast (getCurrentlyPlayingSound().get())) { - const float* const inL = playingSound->data->getSampleData (0, 0); + const float* const inL = playingSound->data->getReadPointer (0); const float* const inR = playingSound->data->getNumChannels() > 1 - ? playingSound->data->getSampleData (1, 0) : nullptr; + ? playingSound->data->getReadPointer (1) : nullptr; - float* outL = outputBuffer.getSampleData (0, startSample); - float* outR = outputBuffer.getNumChannels() > 1 ? outputBuffer.getSampleData (1, startSample) : nullptr; + float* outL = outputBuffer.getWritePointer (0, startSample); + float* outR = outputBuffer.getNumChannels() > 1 ? outputBuffer.getWritePointer (1, startSample) : nullptr; while (--numSamples >= 0) { diff --git a/source/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm b/source/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm index cf2efcbeb..c9fafe2c8 100644 --- a/source/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm +++ b/source/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm @@ -530,7 +530,7 @@ public: { abl->mBuffers[j].mNumberChannels = 1; abl->mBuffers[j].mDataByteSize = sizeof (float) * numSamples; - abl->mBuffers[j].mData = buffer.getSampleData (i * numOutputBusChannels + j, 0); + abl->mBuffers[j].mData = buffer.getWritePointer (i * numOutputBusChannels + j); } } @@ -1050,7 +1050,7 @@ private: if (bufferChannel < currentBuffer->getNumChannels()) { memcpy (ioData->mBuffers[i].mData, - currentBuffer->getSampleData (bufferChannel, 0), + currentBuffer->getReadPointer (bufferChannel), sizeof (float) * inNumberFrames); } else diff --git a/source/modules/juce_audio_processors/format_types/juce_LADSPAPluginFormat.cpp b/source/modules/juce_audio_processors/format_types/juce_LADSPAPluginFormat.cpp index 05aa7e0df..592c2f6c9 100644 --- a/source/modules/juce_audio_processors/format_types/juce_LADSPAPluginFormat.cpp +++ b/source/modules/juce_audio_processors/format_types/juce_LADSPAPluginFormat.cpp @@ -294,13 +294,13 @@ public: { for (int i = 0; i < inputs.size(); ++i) plugin->connect_port (handle, inputs[i], - i < buffer.getNumChannels() ? buffer.getSampleData (i) : nullptr); + i < buffer.getNumChannels() ? buffer.getWritePointer (i) : nullptr); if (plugin->run != nullptr) { for (int i = 0; i < outputs.size(); ++i) plugin->connect_port (handle, outputs.getUnchecked(i), - i < buffer.getNumChannels() ? buffer.getSampleData (i) : nullptr); + i < buffer.getNumChannels() ? buffer.getWritePointer (i) : nullptr); plugin->run (handle, numSamples); return; @@ -312,7 +312,7 @@ public: tempBuffer.clear(); for (int i = 0; i < outputs.size(); ++i) - plugin->connect_port (handle, outputs.getUnchecked(i), tempBuffer.getSampleData (i)); + plugin->connect_port (handle, outputs.getUnchecked(i), tempBuffer.getWritePointer (i)); plugin->run_adding (handle, numSamples); diff --git a/source/modules/juce_audio_processors/format_types/juce_VST3Common.h b/source/modules/juce_audio_processors/format_types/juce_VST3Common.h index 2c575f314..d66a7598d 100644 --- a/source/modules/juce_audio_processors/format_types/juce_VST3Common.h +++ b/source/modules/juce_audio_processors/format_types/juce_VST3Common.h @@ -339,7 +339,7 @@ namespace VST3BufferExchange */ void associateBufferTo (Steinberg::Vst::AudioBusBuffers& vstBuffers, Bus& bus, - const AudioSampleBuffer& buffer, + AudioSampleBuffer& buffer, int numChannels, int channelStartOffset, int sampleOffset = 0) noexcept { @@ -349,7 +349,7 @@ namespace VST3BufferExchange bus.clearQuick(); for (int i = channelStartOffset; i < channelEnd; ++i) - bus.add (buffer.getSampleData (i, sampleOffset)); + bus.add (buffer.getWritePointer (i, sampleOffset)); vstBuffers.channelBuffers32 = bus.getRawDataPointer(); vstBuffers.numChannels = numChannels; diff --git a/source/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp b/source/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp index bdf3f7592..5f2d4f561 100644 --- a/source/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp +++ b/source/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp @@ -175,9 +175,9 @@ static void setStateForAllBussesOfType (Vst::IComponent* component, //============================================================================== /** Assigns a complete AudioSampleBuffer's channels to an AudioBusBuffers' */ -static void associateWholeBufferTo (Vst::AudioBusBuffers& vstBuffers, const AudioSampleBuffer& buffer) noexcept +static void associateWholeBufferTo (Vst::AudioBusBuffers& vstBuffers, AudioSampleBuffer& buffer) noexcept { - vstBuffers.channelBuffers32 = buffer.getArrayOfChannels(); + vstBuffers.channelBuffers32 = buffer.getArrayOfWritePointers(); vstBuffers.numChannels = buffer.getNumChannels(); vstBuffers.silenceFlags = 0; } @@ -224,6 +224,8 @@ static void toProcessContext (Vst::ProcessContext& context, AudioPlayHead* playH } break; + case AudioPlayHead::fpsUnknown: break; + default: jassertfalse; break; // New frame rate? } @@ -1634,7 +1636,11 @@ public: bool hasEditor() const override { - ComSmartPtr view (tryCreatingView()); //N.B.: Must use a ComSmartPtr to not delete the view from the plugin permanently! + // (if possible, avoid creating a second instance of the editor, because that crashes some plugins) + if (getActiveEditor() != nullptr) + return true; + + ComSmartPtr view (tryCreatingView()); return view != nullptr; } @@ -1752,7 +1758,7 @@ private: //============================================================================== VST3ModuleHandle::Ptr module; - friend class VST3HostContext; + friend VST3HostContext; ComSmartPtr host; // Information objects: diff --git a/source/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp b/source/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp index 3d29db048..c6084e39a 100644 --- a/source/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp +++ b/source/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp @@ -1062,17 +1062,17 @@ public: if ((effect->flags & effFlagsCanReplacing) != 0) { - effect->processReplacing (effect, buffer.getArrayOfChannels(), buffer.getArrayOfChannels(), numSamples); + effect->processReplacing (effect, buffer.getArrayOfWritePointers(), buffer.getArrayOfWritePointers(), numSamples); } else { tempBuffer.setSize (effect->numOutputs, numSamples); tempBuffer.clear(); - effect->process (effect, buffer.getArrayOfChannels(), tempBuffer.getArrayOfChannels(), numSamples); + effect->process (effect, buffer.getArrayOfWritePointers(), tempBuffer.getArrayOfWritePointers(), numSamples); for (int i = effect->numOutputs; --i >= 0;) - buffer.copyFrom (i, 0, tempBuffer.getSampleData (i), numSamples); + buffer.copyFrom (i, 0, tempBuffer.getReadPointer (i), numSamples); } } else diff --git a/source/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.cpp b/source/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.cpp index c1f192902..0dd5a14fa 100644 --- a/source/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.cpp +++ b/source/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.cpp @@ -175,7 +175,7 @@ public: void perform (AudioSampleBuffer& sharedBufferChans, const OwnedArray &, const int numSamples) { - float* data = sharedBufferChans.getSampleData (channel, 0); + float* data = sharedBufferChans.getWritePointer (channel, 0); for (int i = numSamples; --i >= 0;) { @@ -219,7 +219,7 @@ public: void perform (AudioSampleBuffer& sharedBufferChans, const OwnedArray & sharedMidiBuffers, const int numSamples) { for (int i = totalChans; --i >= 0;) - channels[i] = sharedBufferChans.getSampleData (audioChannelsToUse.getUnchecked (i), 0); + channels[i] = sharedBufferChans.getWritePointer (audioChannelsToUse.getUnchecked (i), 0); AudioSampleBuffer buffer (channels, totalChans, numSamples); @@ -230,8 +230,8 @@ public: AudioProcessor* const processor; private: - Array audioChannelsToUse; - HeapBlock channels; + Array audioChannelsToUse; + HeapBlock channels; int totalChans; int midiBufferToUse; diff --git a/source/modules/juce_core/files/juce_File.cpp b/source/modules/juce_core/files/juce_File.cpp index eff4af43e..8f80a5a01 100644 --- a/source/modules/juce_core/files/juce_File.cpp +++ b/source/modules/juce_core/files/juce_File.cpp @@ -754,7 +754,7 @@ String File::createLegalPathName (const String& original) String s (original); String start; - if (s[1] == ':') + if (s.isNotEmpty() && s[1] == ':') { start = s.substring (0, 2); s = s.substring (2); diff --git a/source/modules/juce_core/maths/juce_Range.h b/source/modules/juce_core/maths/juce_Range.h index 56678c1b9..0aa9be46e 100644 --- a/source/modules/juce_core/maths/juce_Range.h +++ b/source/modules/juce_core/maths/juce_Range.h @@ -34,6 +34,9 @@ /** A general-purpose range object, that simply represents any linear range with a start and end point. + Note that when checking whether values fall within the range, the start value is + considered to be inclusive, and the end of the range exclusive. + The templated parameter is expected to be a primitive integer or floating point type, though class types could also be used if they behave in a number-like way. */ @@ -210,7 +213,10 @@ public: return jlimit (start, end, value); } - /** Returns true if the given range lies entirely inside this range. */ + /** Returns true if the given range lies entirely inside this range. + When making this comparison, the start value is considered to be inclusive, + and the end of the range exclusive. + */ bool contains (Range other) const noexcept { return start <= other.start && end >= other.end; @@ -237,6 +243,13 @@ public: jmax (end, other.end)); } + /** Returns the smallest range that contains both this one and the given value. */ + Range getUnionWith (const ValueType valueToInclude) const noexcept + { + return Range (jmin (valueToInclude, start), + jmax (valueToInclude, end)); + } + /** Returns a given range, after moving it forwards or backwards to fit it within this range. @@ -255,6 +268,26 @@ public: : rangeToConstrain.movedToStartAt (jlimit (start, end - otherLen, rangeToConstrain.getStart())); } + /** Scans an array of values for its min and max, and returns these as a Range. */ + static Range findMinAndMax (const ValueType* values, int numValues) noexcept + { + if (numValues <= 0) + return Range(); + + const ValueType first (*values++); + Range r (first, first); + + while (--numValues > 0) // (> 0 rather than >= 0 because we've already taken the first sample) + { + const ValueType v (*values++); + + if (r.end < v) r.end = v; + if (v < r.start) r.start = v; + } + + return r; + } + private: //============================================================================== ValueType start, end; diff --git a/source/modules/juce_core/memory/juce_MemoryBlock.cpp b/source/modules/juce_core/memory/juce_MemoryBlock.cpp index f3be0f03d..a82703713 100644 --- a/source/modules/juce_core/memory/juce_MemoryBlock.cpp +++ b/source/modules/juce_core/memory/juce_MemoryBlock.cpp @@ -127,8 +127,7 @@ void MemoryBlock::setSize (const size_t newSize, const bool initialiseToZero) { if (newSize <= 0) { - data.free(); - size = 0; + reset(); } else { @@ -149,6 +148,12 @@ void MemoryBlock::setSize (const size_t newSize, const bool initialiseToZero) } } +void MemoryBlock::reset() +{ + data.free(); + size = 0; +} + void MemoryBlock::ensureSize (const size_t minimumSize, const bool initialiseToZero) { if (size < minimumSize) diff --git a/source/modules/juce_core/memory/juce_MemoryBlock.h b/source/modules/juce_core/memory/juce_MemoryBlock.h index 801a9e1f5..30bb3cab2 100644 --- a/source/modules/juce_core/memory/juce_MemoryBlock.h +++ b/source/modules/juce_core/memory/juce_MemoryBlock.h @@ -108,9 +108,9 @@ public: /** Resizes the memory block. - This will try to keep as much of the block's current content as it can, - and can optionally be made to clear any new space that gets allocated at - the end of the block. + Any data that is present in both the old and new sizes will be retained. + When enlarging the block, the new space that is allocated at the end can either be + cleared, or left uninitialised. @param newSize the new desired size for the block @param initialiseNewSpaceToZero if the block gets enlarged, this determines @@ -133,6 +133,9 @@ public: void ensureSize (const size_t minimumSize, bool initialiseNewSpaceToZero = false); + /** Frees all the blocks data, setting its size to 0. */ + void reset(); + //============================================================================== /** Fills the entire memory block with a repeated byte value. This is handy for clearing a block of memory to zero. diff --git a/source/modules/juce_core/network/juce_MACAddress.h b/source/modules/juce_core/network/juce_MACAddress.h index 635613171..67e119ec7 100644 --- a/source/modules/juce_core/network/juce_MACAddress.h +++ b/source/modules/juce_core/network/juce_MACAddress.h @@ -32,12 +32,7 @@ //============================================================================== /** - A wrapper for a streaming (TCP) socket. - - This allows low-level use of sockets; for an easier-to-use messaging layer on top of - sockets, you could also try the InterprocessConnection class. - - @see DatagramSocket, InterprocessConnection, InterprocessConnectionServer + Represents a MAC network card adapter address ID. */ class JUCE_API MACAddress { diff --git a/source/modules/juce_core/network/juce_URL.cpp b/source/modules/juce_core/network/juce_URL.cpp index c957e91a6..496b00b92 100644 --- a/source/modules/juce_core/network/juce_URL.cpp +++ b/source/modules/juce_core/network/juce_URL.cpp @@ -75,8 +75,7 @@ URL::URL (const URL& other) postData (other.postData), parameterNames (other.parameterNames), parameterValues (other.parameterValues), - filesToUpload (other.filesToUpload), - mimeTypes (other.mimeTypes) + filesToUpload (other.filesToUpload) { } @@ -87,7 +86,6 @@ URL& URL::operator= (const URL& other) parameterNames = other.parameterNames; parameterValues = other.parameterValues; filesToUpload = other.filesToUpload; - mimeTypes = other.mimeTypes; return *this; } @@ -98,8 +96,7 @@ bool URL::operator== (const URL& other) const && postData == other.postData && parameterNames == other.parameterNames && parameterValues == other.parameterValues - && filesToUpload == other.filesToUpload - && mimeTypes == other.mimeTypes; + && filesToUpload == other.filesToUpload; } bool URL::operator!= (const URL& other) const @@ -156,62 +153,6 @@ namespace URLHelpers return url.indexOfChar (findStartOfNetLocation (url), '/') + 1; } - static void createHeadersAndPostData (const URL& url, String& headers, MemoryBlock& postData) - { - MemoryOutputStream data (postData, false); - - if (url.getFilesToUpload().size() > 0) - { - // need to upload some files, so do it as multi-part... - const String boundary (String::toHexString (Random::getSystemRandom().nextInt64())); - - headers << "Content-Type: multipart/form-data; boundary=" << boundary << "\r\n"; - - data << "--" << boundary; - - for (int i = 0; i < url.getParameterNames().size(); ++i) - { - data << "\r\nContent-Disposition: form-data; name=\"" - << url.getParameterNames() [i] - << "\"\r\n\r\n" - << url.getParameterValues() [i] - << "\r\n--" - << boundary; - } - - for (int i = 0; i < url.getFilesToUpload().size(); ++i) - { - const File file (url.getFilesToUpload().getAllValues() [i]); - const String paramName (url.getFilesToUpload().getAllKeys() [i]); - - data << "\r\nContent-Disposition: form-data; name=\"" << paramName - << "\"; filename=\"" << file.getFileName() << "\"\r\n"; - - const String mimeType (url.getMimeTypesOfUploadFiles() - .getValue (paramName, String())); - - if (mimeType.isNotEmpty()) - data << "Content-Type: " << mimeType << "\r\n"; - - data << "Content-Transfer-Encoding: binary\r\n\r\n" - << file << "\r\n--" << boundary; - } - - data << "--\r\n"; - } - else - { - data << getMangledParameters (url) - << url.getPostData(); - - // if the user-supplied headers didn't contain a content-type, add one now.. - if (! headers.containsIgnoreCase ("Content-Type")) - headers << "Content-Type: application/x-www-form-urlencoded\r\n"; - - headers << "Content-length: " << (int) data.getDataSize() << "\r\n"; - } - } - static void concatenatePaths (String& path, const String& suffix) { if (! path.endsWithChar ('/')) @@ -296,6 +237,64 @@ URL URL::getChildURL (const String& subPath) const return u; } +void URL::createHeadersAndPostData (String& headers, MemoryBlock& headersAndPostData) const +{ + MemoryOutputStream data (headersAndPostData, false); + + data << URLHelpers::getMangledParameters (*this); + + if (filesToUpload.size() > 0) + { + // (this doesn't currently support mixing custom post-data with uploads..) + jassert (postData.isEmpty()); + + const String boundary (String::toHexString (Random::getSystemRandom().nextInt64())); + + headers << "Content-Type: multipart/form-data; boundary=" << boundary << "\r\n"; + + data << "--" << boundary; + + for (int i = 0; i < parameterNames.size(); ++i) + { + data << "\r\nContent-Disposition: form-data; name=\"" << parameterNames[i] + << "\"\r\n\r\n" << parameterValues[i] + << "\r\n--" << boundary; + } + + for (int i = 0; i < filesToUpload.size(); ++i) + { + const Upload& f = *filesToUpload.getObjectPointerUnchecked(i); + + data << "\r\nContent-Disposition: form-data; name=\"" << f.parameterName + << "\"; filename=\"" << f.filename << "\"\r\n"; + + if (f.mimeType.isNotEmpty()) + data << "Content-Type: " << f.mimeType << "\r\n"; + + data << "Content-Transfer-Encoding: binary\r\n\r\n"; + + if (f.data != nullptr) + data << *f.data; + else + data << f.file; + + data << "\r\n--" << boundary; + } + + data << "--\r\n"; + } + else + { + data << postData; + + // if the user-supplied headers didn't contain a content-type, add one now.. + if (! headers.containsIgnoreCase ("Content-Type")) + headers << "Content-Type: application/x-www-form-urlencoded\r\n"; + + headers << "Content-length: " << (int) data.getDataSize() << "\r\n"; + } +} + //============================================================================== bool URL::isProbablyAWebsiteURL (const String& possibleURL) { @@ -339,7 +338,7 @@ InputStream* URL::createInputStream (const bool usePostCommand, headers << "\r\n"; if (usePostCommand) - URLHelpers::createHeadersAndPostData (*this, headers, headersAndPostData); + createHeadersAndPostData (headers, headersAndPostData); if (! headers.endsWithChar ('\n')) headers << "\r\n"; @@ -394,33 +393,44 @@ URL URL::withParameter (const String& parameterName, return u; } -URL URL::withFileToUpload (const String& parameterName, - const File& fileToUpload, - const String& mimeType) const +URL URL::withPOSTData (const String& newPostData) const { - jassert (mimeType.isNotEmpty()); // You need to supply a mime type! - URL u (*this); - u.filesToUpload.set (parameterName, fileToUpload.getFullPathName()); - u.mimeTypes.set (parameterName, mimeType); + u.postData = newPostData; return u; } -URL URL::withPOSTData (const String& newPostData) const +URL::Upload::Upload (const String& param, const String& name, + const String& mime, const File& f, MemoryBlock* mb) + : parameterName (param), filename (name), mimeType (mime), file (f), data (mb) +{ + jassert (mimeType.isNotEmpty()); // You need to supply a mime type! +} + +URL URL::withUpload (Upload* const f) const { URL u (*this); - u.postData = newPostData; + + for (int i = u.filesToUpload.size(); --i >= 0;) + if (u.filesToUpload.getObjectPointerUnchecked(i)->parameterName == f->parameterName) + u.filesToUpload.remove (i); + + u.filesToUpload.add (f); return u; } -const StringPairArray& URL::getFilesToUpload() const +URL URL::withFileToUpload (const String& parameterName, const File& fileToUpload, + const String& mimeType) const { - return filesToUpload; + return withUpload (new Upload (parameterName, fileToUpload.getFileName(), + mimeType, fileToUpload, nullptr)); } -const StringPairArray& URL::getMimeTypesOfUploadFiles() const +URL URL::withDataToUpload (const String& parameterName, const String& filename, + const MemoryBlock& fileContentToUpload, const String& mimeType) const { - return mimeTypes; + return withUpload (new Upload (parameterName, filename, mimeType, File(), + new MemoryBlock (fileContentToUpload))); } //============================================================================== diff --git a/source/modules/juce_core/network/juce_URL.h b/source/modules/juce_core/network/juce_URL.h index ce7676b27..85b365741 100644 --- a/source/modules/juce_core/network/juce_URL.h +++ b/source/modules/juce_core/network/juce_URL.h @@ -137,18 +137,35 @@ public: URL withParameter (const String& parameterName, const String& parameterValue) const; - /** Returns a copy of this URl, with a file-upload type parameter added to it. + /** Returns a copy of this URL, with a file-upload type parameter added to it. When performing a POST where one of your parameters is a binary file, this lets you specify the file. Note that the filename is stored, but the file itself won't actually be read - until this URL is later used to create a network input stream. + until this URL is later used to create a network input stream. If you want to + upload data from memory, use withDataToUpload(). + + @see withDataToUpload */ URL withFileToUpload (const String& parameterName, const File& fileToUpload, const String& mimeType) const; + /** Returns a copy of this URL, with a file-upload type parameter added to it. + + When performing a POST where one of your parameters is a binary file, this + lets you specify the file content. + Note that the filename parameter should not be a full path, it's just the + last part of the filename. + + @see withFileToUpload + */ + URL withDataToUpload (const String& parameterName, + const String& filename, + const MemoryBlock& fileContentToUpload, + const String& mimeType) const; + /** Returns an array of the names of all the URL's parameters. E.g. for the url "www.fish.com?type=haddock&amount=some+fish", this array would @@ -175,17 +192,6 @@ public: */ const StringArray& getParameterValues() const noexcept { return parameterValues; } - /** Returns the set of files that should be uploaded as part of a POST operation. - - This is the set of files that were added to the URL with the withFileToUpload() - method. - */ - const StringPairArray& getFilesToUpload() const; - - /** Returns the set of mime types associated with each of the upload files. - */ - const StringPairArray& getMimeTypesOfUploadFiles() const; - /** Returns a copy of this URL, with a block of data to send as the POST data. If you're setting the POST data, be careful not to have any parameters set @@ -343,10 +349,23 @@ private: //============================================================================== String url, postData; StringArray parameterNames, parameterValues; - StringPairArray filesToUpload, mimeTypes; + + struct Upload : public ReferenceCountedObject + { + Upload (const String&, const String&, const String&, const File&, MemoryBlock*); + String parameterName, filename, mimeType; + File file; + ScopedPointer data; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Upload) + }; + + ReferenceCountedArray filesToUpload; URL (const String&, int); void addParameter (const String&, const String&); + void createHeadersAndPostData (String&, MemoryBlock&) const; + URL withUpload (Upload*) const; JUCE_LEAK_DETECTOR (URL) }; diff --git a/source/modules/juce_core/system/juce_SystemStats.h b/source/modules/juce_core/system/juce_SystemStats.h index 2a8b267e4..d9132f5af 100644 --- a/source/modules/juce_core/system/juce_SystemStats.h +++ b/source/modules/juce_core/system/juce_SystemStats.h @@ -58,6 +58,7 @@ public: MacOSX_10_6 = 0x1006, MacOSX_10_7 = 0x1007, MacOSX_10_8 = 0x1008, + MacOSX_10_9 = 0x1009, Win2000 = 0x4105, WinXP = 0x4106, diff --git a/source/modules/juce_core/threads/juce_ReadWriteLock.cpp b/source/modules/juce_core/threads/juce_ReadWriteLock.cpp index 7db6e921d..a0821b440 100644 --- a/source/modules/juce_core/threads/juce_ReadWriteLock.cpp +++ b/source/modules/juce_core/threads/juce_ReadWriteLock.cpp @@ -105,18 +105,8 @@ void ReadWriteLock::enterWrite() const noexcept const Thread::ThreadID threadId = Thread::getCurrentThreadId(); const SpinLock::ScopedLockType sl (accessLock); - for (;;) + while (! tryEnterWriteInternal (threadId)) { - if (readerThreads.size() + numWriters == 0 - || threadId == writerThreadId - || (readerThreads.size() == 1 - && readerThreads.getReference(0).threadID == threadId)) - { - writerThreadId = threadId; - ++numWriters; - break; - } - ++numWaitingWriters; accessLock.exit(); waitEvent.wait (100); @@ -127,13 +117,15 @@ void ReadWriteLock::enterWrite() const noexcept bool ReadWriteLock::tryEnterWrite() const noexcept { - const Thread::ThreadID threadId = Thread::getCurrentThreadId(); const SpinLock::ScopedLockType sl (accessLock); + return tryEnterWriteInternal (Thread::getCurrentThreadId()); +} +bool ReadWriteLock::tryEnterWriteInternal (Thread::ThreadID threadId) const noexcept +{ if (readerThreads.size() + numWriters == 0 || threadId == writerThreadId - || (readerThreads.size() == 1 - && readerThreads.getReference(0).threadID == threadId)) + || (readerThreads.size() == 1 && readerThreads.getReference(0).threadID == threadId)) { writerThreadId = threadId; ++numWriters; diff --git a/source/modules/juce_core/threads/juce_ReadWriteLock.h b/source/modules/juce_core/threads/juce_ReadWriteLock.h index 47bcf0e16..c41d2580a 100644 --- a/source/modules/juce_core/threads/juce_ReadWriteLock.h +++ b/source/modules/juce_core/threads/juce_ReadWriteLock.h @@ -143,6 +143,8 @@ private: mutable Array readerThreads; + bool tryEnterWriteInternal (Thread::ThreadID) const noexcept; + JUCE_DECLARE_NON_COPYABLE (ReadWriteLock) }; diff --git a/source/modules/juce_data_structures/values/juce_Value.h b/source/modules/juce_data_structures/values/juce_Value.h index efc05830a..5b23e1aed 100644 --- a/source/modules/juce_data_structures/values/juce_Value.h +++ b/source/modules/juce_data_structures/values/juce_Value.h @@ -75,8 +75,7 @@ public: operator var() const; /** Returns the value as a string. - - This is alternative to writing things like "myValue.getValue().toString()". + This is a shortcut for "myValue.getValue().toString()". */ String toString() const; diff --git a/source/modules/juce_events/interprocess/juce_ConnectedChildProcess.cpp b/source/modules/juce_events/interprocess/juce_ConnectedChildProcess.cpp index 79356f7c2..926798c8a 100644 --- a/source/modules/juce_events/interprocess/juce_ConnectedChildProcess.cpp +++ b/source/modules/juce_events/interprocess/juce_ConnectedChildProcess.cpp @@ -77,7 +77,7 @@ private: } } - JUCE_DECLARE_NON_COPYABLE (ChildProcessPingThread) + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChildProcessPingThread) }; //============================================================================== diff --git a/source/modules/juce_events/messages/juce_MessageManager.h b/source/modules/juce_events/messages/juce_MessageManager.h index 86dd62ebe..d50c35c58 100644 --- a/source/modules/juce_events/messages/juce_MessageManager.h +++ b/source/modules/juce_events/messages/juce_MessageManager.h @@ -128,7 +128,7 @@ public: */ Thread::ThreadID getCurrentMessageThread() const noexcept { return messageThreadId; } - /** Returns true if the caller thread has currenltly got the message manager locked. + /** Returns true if the caller thread has currently got the message manager locked. see the MessageManagerLock class for more info about this. diff --git a/source/modules/juce_events/timers/juce_Timer.cpp b/source/modules/juce_events/timers/juce_Timer.cpp index e0bbadb02..6b7343a55 100644 --- a/source/modules/juce_events/timers/juce_Timer.cpp +++ b/source/modules/juce_events/timers/juce_Timer.cpp @@ -295,20 +295,12 @@ Timer::TimerThread* Timer::TimerThread::instance = nullptr; Timer::TimerThread::LockType Timer::TimerThread::lock; //============================================================================== -#if JUCE_DEBUG -static SortedSet activeTimers; -#endif - Timer::Timer() noexcept : countdownMs (0), periodMs (0), previous (nullptr), next (nullptr) { - #if JUCE_DEBUG - const TimerThread::LockType::ScopedLockType sl (TimerThread::lock); - activeTimers.add (this); - #endif } Timer::Timer (const Timer&) noexcept @@ -317,30 +309,17 @@ Timer::Timer (const Timer&) noexcept previous (nullptr), next (nullptr) { - #if JUCE_DEBUG - const TimerThread::LockType::ScopedLockType sl (TimerThread::lock); - activeTimers.add (this); - #endif } Timer::~Timer() { stopTimer(); - - #if JUCE_DEBUG - activeTimers.removeValue (this); - #endif } void Timer::startTimer (const int interval) noexcept { const TimerThread::LockType::ScopedLockType sl (TimerThread::lock); - #if JUCE_DEBUG - // this isn't a valid object! Your timer might be a dangling pointer or something.. - jassert (activeTimers.contains (this)); - #endif - if (periodMs == 0) { countdownMs = interval; @@ -357,11 +336,6 @@ void Timer::stopTimer() noexcept { const TimerThread::LockType::ScopedLockType sl (TimerThread::lock); - #if JUCE_DEBUG - // this isn't a valid object! Your timer might be a dangling pointer or something.. - jassert (activeTimers.contains (this)); - #endif - if (periodMs > 0) { TimerThread::remove (this); diff --git a/source/modules/juce_graphics/fonts/juce_CustomTypeface.h b/source/modules/juce_graphics/fonts/juce_CustomTypeface.h index fa1bb790b..0acc4f173 100644 --- a/source/modules/juce_graphics/fonts/juce_CustomTypeface.h +++ b/source/modules/juce_graphics/fonts/juce_CustomTypeface.h @@ -36,6 +36,11 @@ If you want to create a copy of a native face, you can use addGlyphsFromOtherTypeface() to copy glyphs into this face. + NOTE! For most people this class is almost certainly NOT the right tool to use! + If what you want to do is to embed a font into your exe, then your best plan is + probably to embed your TTF/OTF font file into your binary using the Introjucer, + and then call Typeface::createSystemTypefaceFor() to load it from memory. + @see Typeface, Font */ class JUCE_API CustomTypeface : public Typeface @@ -108,6 +113,10 @@ public: /** Saves this typeface as a Juce-formatted font file. A CustomTypeface can be created to reload the data that is written - see the CustomTypeface constructor. + + NOTE! Since this class was written, support was added for loading real font files from + memory, so for most people, using Typeface::createSystemTypefaceFor() to load a real font + is more appropriate than using this class to store it in a proprietory format. */ bool writeToStream (OutputStream& outputStream); diff --git a/source/modules/juce_graphics/geometry/juce_Path.cpp b/source/modules/juce_graphics/geometry/juce_Path.cpp index 9997b5094..210844aab 100644 --- a/source/modules/juce_graphics/geometry/juce_Path.cpp +++ b/source/modules/juce_graphics/geometry/juce_Path.cpp @@ -254,6 +254,11 @@ Rectangle Path::getBoundsTransformed (const AffineTransform& transform) c } //============================================================================== +void Path::preallocateSpace (int numExtraCoordsToMakeSpaceFor) +{ + data.ensureAllocatedSize ((int) numElements + numExtraCoordsToMakeSpaceFor); +} + void Path::startNewSubPath (const float x, const float y) { JUCE_CHECK_COORDS_ARE_VALID (x, y); @@ -263,7 +268,7 @@ void Path::startNewSubPath (const float x, const float y) else bounds.extend (x, y); - data.ensureAllocatedSize ((int) numElements + 3); + preallocateSpace (3); data.elements [numElements++] = moveMarker; data.elements [numElements++] = x; @@ -282,7 +287,7 @@ void Path::lineTo (const float x, const float y) if (numElements == 0) startNewSubPath (0, 0); - data.ensureAllocatedSize ((int) numElements + 3); + preallocateSpace (3); data.elements [numElements++] = lineMarker; data.elements [numElements++] = x; @@ -305,7 +310,7 @@ void Path::quadraticTo (const float x1, const float y1, if (numElements == 0) startNewSubPath (0, 0); - data.ensureAllocatedSize ((int) numElements + 5); + preallocateSpace (5); data.elements [numElements++] = quadMarker; data.elements [numElements++] = x1; @@ -334,7 +339,7 @@ void Path::cubicTo (const float x1, const float y1, if (numElements == 0) startNewSubPath (0, 0); - data.ensureAllocatedSize ((int) numElements + 7); + preallocateSpace (7); data.elements [numElements++] = cubicMarker; data.elements [numElements++] = x1; @@ -362,7 +367,7 @@ void Path::closeSubPath() if (numElements > 0 && data.elements [numElements - 1] != closeSubPathMarker) { - data.ensureAllocatedSize ((int) numElements + 1); + preallocateSpace (1); data.elements [numElements++] = closeSubPathMarker; } } @@ -399,7 +404,7 @@ void Path::addRectangle (const float x, const float y, if (w < 0) std::swap (x1, x2); if (h < 0) std::swap (y1, y2); - data.ensureAllocatedSize ((int) numElements + 13); + preallocateSpace (13); if (numElements == 0) { diff --git a/source/modules/juce_graphics/geometry/juce_Path.h b/source/modules/juce_graphics/geometry/juce_Path.h index f94da19fe..7e364aa7d 100644 --- a/source/modules/juce_graphics/geometry/juce_Path.h +++ b/source/modules/juce_graphics/geometry/juce_Path.h @@ -560,6 +560,18 @@ public: */ void swapWithPath (Path&) noexcept; + //============================================================================== + /** Preallocates enough space for adding the given number of coordinates to the path. + If you're about to add a large number of lines or curves to the path, it can make + the task much more efficient to call this first and avoid costly reallocations + as the structure grows. + The actual value to pass is a bit tricky to calculate because the space required + depends on what you're adding - e.g. each lineTo() or startNewSubPath() will + require 3 coords (x, y and a type marker). Each quadraticTo() will need 5, and + a cubicTo() will require 7. Closing a sub-path will require 1. + */ + void preallocateSpace (int numExtraCoordsToMakeSpaceFor); + //============================================================================== /** Applies a 2D transform to all the vertices in the path. @@ -742,12 +754,11 @@ public: */ void restoreFromString (StringRef stringVersion); - private: //============================================================================== friend class PathFlatteningIterator; friend class Path::Iterator; - ArrayAllocationBase data; + ArrayAllocationBase data; size_t numElements; struct PathBounds diff --git a/source/modules/juce_graphics/native/juce_RenderingHelpers.h b/source/modules/juce_graphics/native/juce_RenderingHelpers.h index 10fb9dc64..f15734af8 100644 --- a/source/modules/juce_graphics/native/juce_RenderingHelpers.h +++ b/source/modules/juce_graphics/native/juce_RenderingHelpers.h @@ -164,53 +164,16 @@ public: //============================================================================== void drawGlyph (RenderTargetType& target, const Font& font, const int glyphNumber, Point pos) { - ++accessCounter; - CachedGlyphType* glyph = nullptr; - - const ScopedReadLock srl (lock); - - for (int i = glyphs.size(); --i >= 0;) - { - CachedGlyphType* const g = glyphs.getUnchecked (i); - - if (g->glyph == glyphNumber && g->font == font) - { - glyph = g; - ++hits; - break; - } - } - - if (glyph == nullptr) + if (ReferenceCountedObjectPtr glyph = findOrCreateGlyph (font, glyphNumber)) { - ++misses; - const ScopedWriteLock swl (lock); - - if (hits.value + misses.value > glyphs.size() * 16) - { - if (misses.value * 2 > hits.value) - addNewGlyphSlots (32); - - hits.set (0); - misses.set (0); - glyph = glyphs.getLast(); - } - else - { - glyph = findLeastRecentlyUsedGlyph(); - } - - jassert (glyph != nullptr); - glyph->generate (font, glyphNumber); + glyph->lastAccessCount = ++accessCounter; + glyph->draw (target, pos); } - - glyph->lastAccessCount = accessCounter.value; - glyph->draw (target, pos); } void reset() { - const ScopedWriteLock swl (lock); + const ScopedLock sl (lock); glyphs.clear(); addNewGlyphSlots (120); hits.set (0); @@ -219,9 +182,54 @@ public: private: friend struct ContainerDeletePolicy; - OwnedArray glyphs; + ReferenceCountedArray glyphs; Atomic accessCounter, hits, misses; - ReadWriteLock lock; + CriticalSection lock; + + ReferenceCountedObjectPtr findOrCreateGlyph (const Font& font, int glyphNumber) + { + const ScopedLock sl (lock); + + if (CachedGlyphType* g = findExistingGlyph (font, glyphNumber)) + { + ++hits; + return g; + } + + ++misses; + CachedGlyphType* g = getGlyphForReuse(); + jassert (g != nullptr); + g->generate (font, glyphNumber); + return g; + } + + CachedGlyphType* findExistingGlyph (const Font& font, int glyphNumber) const + { + for (int i = 0; i < glyphs.size(); ++i) + { + CachedGlyphType* const g = glyphs.getUnchecked (i); + + if (g->glyph == glyphNumber && g->font == font) + return g; + } + + return nullptr; + } + + CachedGlyphType* getGlyphForReuse() + { + if (hits.value + misses.value > glyphs.size() * 16) + { + if (misses.value * 2 > hits.value) + addNewGlyphSlots (32); + + hits.set (0); + misses.set (0); + return glyphs.getLast(); + } + + return findLeastRecentlyUsedGlyph(); + } void addNewGlyphSlots (int num) { @@ -240,7 +248,8 @@ private: { CachedGlyphType* const glyph = glyphs.getUnchecked(i); - if (glyph->lastAccessCount <= oldestCounter) + if (glyph->lastAccessCount <= oldestCounter + && glyph->getReferenceCount() == 1) { oldestCounter = glyph->lastAccessCount; oldest = glyph; @@ -262,7 +271,7 @@ private: //============================================================================== /** Caches a glyph as an edge-table. */ template -class CachedGlyphEdgeTable +class CachedGlyphEdgeTable : public ReferenceCountedObject { public: CachedGlyphEdgeTable() : glyph (0), lastAccessCount (0) {} diff --git a/source/modules/juce_graphics/native/juce_win32_Fonts.cpp b/source/modules/juce_graphics/native/juce_win32_Fonts.cpp index a5448f395..090ac9ff2 100644 --- a/source/modules/juce_graphics/native/juce_win32_Fonts.cpp +++ b/source/modules/juce_graphics/native/juce_win32_Fonts.cpp @@ -120,7 +120,8 @@ namespace TTFNameExtractor for (int i = 0; i < (int) ByteOrder::swapIfLittleEndian (offsetTable.numTables); ++i) { - TableDirectory tableDirectory = { 0 }; + TableDirectory tableDirectory; + zerostruct (tableDirectory); input.read (&tableDirectory, sizeof (tableDirectory)); if (String (tableDirectory.tag, sizeof (tableDirectory.tag)).equalsIgnoreCase ("name")) diff --git a/source/modules/juce_gui_basics/buttons/juce_Button.h b/source/modules/juce_gui_basics/buttons/juce_Button.h index 26613548b..aef6c9eb5 100644 --- a/source/modules/juce_gui_basics/buttons/juce_Button.h +++ b/source/modules/juce_gui_basics/buttons/juce_Button.h @@ -360,10 +360,12 @@ public: { virtual ~LookAndFeelMethods() {} - virtual void drawButtonBackground (Graphics&, Button& button, const Colour& backgroundColour, + virtual void drawButtonBackground (Graphics&, Button&, const Colour& backgroundColour, bool isMouseOverButton, bool isButtonDown) = 0; - virtual Font getTextButtonFont (TextButton& button) = 0; + virtual Font getTextButtonFont (TextButton&) = 0; + + virtual void changeTextButtonWidthToFitText (TextButton&, int newHeight) = 0; /** Draws the text for a TextButton. */ virtual void drawButtonText (Graphics&, TextButton&, bool isMouseOverButton, bool isButtonDown) = 0; diff --git a/source/modules/juce_gui_basics/buttons/juce_HyperlinkButton.cpp b/source/modules/juce_gui_basics/buttons/juce_HyperlinkButton.cpp index d5d09ebee..8274f4c12 100644 --- a/source/modules/juce_gui_basics/buttons/juce_HyperlinkButton.cpp +++ b/source/modules/juce_gui_basics/buttons/juce_HyperlinkButton.cpp @@ -74,7 +74,7 @@ Font HyperlinkButton::getFontToUse() const void HyperlinkButton::changeWidthToFitText() { - setSize (getFontToUse().getStringWidth (getName()) + 6, getHeight()); + setSize (getFontToUse().getStringWidth (getButtonText()) + 6, getHeight()); } void HyperlinkButton::colourChanged() diff --git a/source/modules/juce_gui_basics/buttons/juce_TextButton.cpp b/source/modules/juce_gui_basics/buttons/juce_TextButton.cpp index eafffd416..e2b885e41 100644 --- a/source/modules/juce_gui_basics/buttons/juce_TextButton.cpp +++ b/source/modules/juce_gui_basics/buttons/juce_TextButton.cpp @@ -22,6 +22,10 @@ ============================================================================== */ +TextButton::TextButton() : Button (String()) +{ +} + TextButton::TextButton (const String& name, const String& toolTip) : Button (name) { @@ -32,21 +36,15 @@ TextButton::~TextButton() { } -void TextButton::paintButton (Graphics& g, - bool isMouseOverButton, - bool isButtonDown) +void TextButton::paintButton (Graphics& g, bool isMouseOverButton, bool isButtonDown) { LookAndFeel& lf = getLookAndFeel(); lf.drawButtonBackground (g, *this, - findColour (getToggleState() ? buttonOnColourId - : buttonColourId), - isMouseOverButton, - isButtonDown); - - lf.drawButtonText (g, *this, - isMouseOverButton, - isButtonDown); + findColour (getToggleState() ? buttonOnColourId : buttonColourId), + isMouseOverButton, isButtonDown); + + lf.drawButtonText (g, *this, isMouseOverButton, isButtonDown); } void TextButton::colourChanged() @@ -61,9 +59,5 @@ Font TextButton::getFont() void TextButton::changeWidthToFitText (const int newHeight) { - if (newHeight >= 0) - setSize (jmax (1, getWidth()), newHeight); - - setSize (getFont().getStringWidth (getButtonText()) + getHeight(), - getHeight()); + getLookAndFeel().changeTextButtonWidthToFitText (*this, newHeight); } diff --git a/source/modules/juce_gui_basics/buttons/juce_TextButton.h b/source/modules/juce_gui_basics/buttons/juce_TextButton.h index 67c0ae48e..f630cbb88 100644 --- a/source/modules/juce_gui_basics/buttons/juce_TextButton.h +++ b/source/modules/juce_gui_basics/buttons/juce_TextButton.h @@ -37,17 +37,18 @@ class JUCE_API TextButton : public Button { public: //============================================================================== - /** Creates a TextButton. + /** Creates a TextButton. */ + TextButton(); + /** Creates a TextButton. @param buttonName the text to put in the button (the component's name is also initially set to this string, but these can be changed later using the setName() and setButtonText() methods) @param toolTip an optional string to use as a toolip - @see Button */ - TextButton (const String& buttonName = String::empty, - const String& toolTip = String::empty); + explicit TextButton (const String& buttonName, + const String& toolTip = String::empty); /** Destructor. */ ~TextButton(); @@ -74,19 +75,18 @@ public: //============================================================================== /** Resizes the button to fit neatly around its current text. - If newHeight is >= 0, the button's height will be changed to this value. If it's less than zero, its height will be unaffected. */ void changeWidthToFitText (int newHeight = -1); /** This can be overridden to use different fonts than the default one. - Note that you'll need to set the font's size appropriately, too. */ virtual Font getFont(); -protected: + + //============================================================================== /** @internal */ void paintButton (Graphics&, bool isMouseOverButton, bool isButtonDown) override; /** @internal */ diff --git a/source/modules/juce_gui_basics/drawables/juce_SVGParser.cpp b/source/modules/juce_gui_basics/drawables/juce_SVGParser.cpp index 198bfdbaa..b301a9d54 100644 --- a/source/modules/juce_gui_basics/drawables/juce_SVGParser.cpp +++ b/source/modules/juce_gui_basics/drawables/juce_SVGParser.cpp @@ -69,13 +69,15 @@ public: if (newState.width <= 0) newState.width = 100; if (newState.height <= 0) newState.height = 100; + Point viewboxXY; + if (xml->hasAttribute ("viewBox")) { const String viewBoxAtt (xml->getStringAttribute ("viewBox")); String::CharPointerType viewParams (viewBoxAtt.getCharPointer()); - Point vxy, vwh; + Point vwh; - if (parseCoords (viewParams, vxy, true) + if (parseCoords (viewParams, viewboxXY, true) && parseCoords (viewParams, vwh, true) && vwh.x > 0 && vwh.y > 0) @@ -105,7 +107,7 @@ public: } newState.transform = RectanglePlacement (placementFlags) - .getTransformToFit (Rectangle (vxy.x, vxy.y, vwh.x, vwh.y), + .getTransformToFit (Rectangle (viewboxXY.x, viewboxXY.y, vwh.x, vwh.y), Rectangle (newState.width, newState.height)) .followedBy (newState.transform); } @@ -118,7 +120,10 @@ public: newState.parseSubElements (xml, *drawable); - drawable->setContentArea (RelativeRectangle (Rectangle (newState.viewBoxW, newState.viewBoxH))); + drawable->setContentArea (RelativeRectangle (RelativeCoordinate (viewboxXY.x), + RelativeCoordinate (viewboxXY.x + newState.viewBoxW), + RelativeCoordinate (viewboxXY.y), + RelativeCoordinate (viewboxXY.y + newState.viewBoxH))); drawable->resetBoundingBoxToContentArea(); return drawable; diff --git a/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.cpp b/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.cpp index 85045f7b2..f4212595c 100644 --- a/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.cpp +++ b/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.cpp @@ -243,6 +243,16 @@ Font LookAndFeel_V2::getTextButtonFont (TextButton& button) return button.getFont(); } +void LookAndFeel_V2::changeTextButtonWidthToFitText (TextButton& b, int newHeight) +{ + if (newHeight >= 0) + b.setSize (jmax (1, b.getWidth()), newHeight); + else + newHeight = b.getHeight(); + + b.setSize (getTextButtonFont (b).getStringWidth (b.getButtonText()) + newHeight, newHeight); +} + void LookAndFeel_V2::drawButtonText (Graphics& g, TextButton& button, bool /*isMouseOverButton*/, bool /*isButtonDown*/) { Font font (getTextButtonFont (button)); diff --git a/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.h b/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.h index 8079e5a13..8115ec32a 100644 --- a/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.h +++ b/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.h @@ -46,6 +46,7 @@ public: void drawButtonText (Graphics&, TextButton& button, bool isMouseOverButton, bool isButtonDown) override; + void changeTextButtonWidthToFitText (TextButton&, int newHeight) override; void drawToggleButton (Graphics&, ToggleButton& button, bool isMouseOverButton, bool isButtonDown) override; diff --git a/source/modules/juce_gui_basics/native/juce_linux_FileChooser.cpp b/source/modules/juce_gui_basics/native/juce_linux_FileChooser.cpp index d7a5083d8..a1097be26 100644 --- a/source/modules/juce_gui_basics/native/juce_linux_FileChooser.cpp +++ b/source/modules/juce_gui_basics/native/juce_linux_FileChooser.cpp @@ -98,7 +98,6 @@ void FileChooser::showPlatformDialog (Array& results, args.add (startPath); args.add (filters.replaceCharacter (';', ' ')); - args.add ("2>/dev/null"); } else { @@ -132,6 +131,8 @@ void FileChooser::showPlatformDialog (Array& results, args.add ("--filename=" + file.getFileName()); } + args.add ("2>/dev/null"); + ChildProcess child; if (child.start (args, ChildProcess::wantStdOut)) diff --git a/source/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm b/source/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm index 94eed9f4d..866c0335d 100644 --- a/source/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm +++ b/source/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm @@ -141,7 +141,7 @@ public: [window setHasShadow: ((windowStyleFlags & windowHasDropShadow) != 0)]; if (component.isAlwaysOnTop()) - [window setLevel: NSFloatingWindowLevel]; + setAlwaysOnTop (true); [window setContentView: view]; [window setAutodisplay: YES]; @@ -202,7 +202,9 @@ public: { if (shouldBeVisible) { + ++insideToFrontCall; [window orderFront: nil]; + --insideToFrontCall; handleBroughtToFront(); } else @@ -439,8 +441,10 @@ public: bool setAlwaysOnTop (bool alwaysOnTop) override { if (! isSharedWindow) - [window setLevel: alwaysOnTop ? NSFloatingWindowLevel + [window setLevel: alwaysOnTop ? ((getStyleFlags() & windowIsTemporary) != 0 ? NSPopUpMenuWindowLevel + : NSFloatingWindowLevel) : NSNormalWindowLevel]; + return true; } diff --git a/source/modules/juce_gui_basics/native/juce_win32_Windowing.cpp b/source/modules/juce_gui_basics/native/juce_win32_Windowing.cpp index 9b3682049..7ea3fe44d 100644 --- a/source/modules/juce_gui_basics/native/juce_win32_Windowing.cpp +++ b/source/modules/juce_gui_basics/native/juce_win32_Windowing.cpp @@ -102,17 +102,38 @@ bool Desktop::canUseSemiTransparentWindows() noexcept #endif +#ifndef MONITOR_DPI_TYPE + enum Monitor_DPI_Type + { + MDT_Effective_DPI = 0, + MDT_Angular_DPI = 1, + MDT_Raw_DPI = 2, + MDT_Default = MDT_Effective_DPI + }; + + enum Process_DPI_Awareness + { + Process_DPI_Unaware = 0, + Process_System_DPI_Aware = 1, + Process_Per_Monitor_DPI_Aware = 2 + }; +#endif + typedef BOOL (WINAPI* RegisterTouchWindowFunc) (HWND, ULONG); typedef BOOL (WINAPI* GetTouchInputInfoFunc) (HTOUCHINPUT, UINT, TOUCHINPUT*, int); typedef BOOL (WINAPI* CloseTouchInputHandleFunc) (HTOUCHINPUT); typedef BOOL (WINAPI* GetGestureInfoFunc) (HGESTUREINFO, GESTUREINFO*); typedef BOOL (WINAPI* SetProcessDPIAwareFunc)(); +typedef BOOL (WINAPI* SetProcessDPIAwarenessFunc) (Process_DPI_Awareness); +typedef HRESULT (WINAPI* GetDPIForMonitorFunc) (HMONITOR, Monitor_DPI_Type, UINT*, UINT*); -static RegisterTouchWindowFunc registerTouchWindow = nullptr; -static GetTouchInputInfoFunc getTouchInputInfo = nullptr; -static CloseTouchInputHandleFunc closeTouchInputHandle = nullptr; -static GetGestureInfoFunc getGestureInfo = nullptr; -static SetProcessDPIAwareFunc setProcessDPIAware = nullptr; +static RegisterTouchWindowFunc registerTouchWindow = nullptr; +static GetTouchInputInfoFunc getTouchInputInfo = nullptr; +static CloseTouchInputHandleFunc closeTouchInputHandle = nullptr; +static GetGestureInfoFunc getGestureInfo = nullptr; +static SetProcessDPIAwareFunc setProcessDPIAware = nullptr; +static SetProcessDPIAwarenessFunc setProcessDPIAwareness = nullptr; +static GetDPIForMonitorFunc getDPIForMonitor = nullptr; static bool hasCheckedForMultiTouch = false; @@ -158,17 +179,33 @@ static void setDPIAwareness() { if (JUCEApplicationBase::isStandaloneApp()) { - if (setProcessDPIAware == nullptr) + if (setProcessDPIAwareness == nullptr) { - setProcessDPIAware = (SetProcessDPIAwareFunc) getUser32Function ("SetProcessDPIAware"); + HMODULE shcoreModule = GetModuleHandleA ("SHCore.dll"); + + if (shcoreModule != 0) + { + setProcessDPIAwareness = (SetProcessDPIAwarenessFunc) GetProcAddress (shcoreModule, "SetProcessDpiAwareness"); + getDPIForMonitor = (GetDPIForMonitorFunc) GetProcAddress (shcoreModule, "GetDpiForMonitor"); + + if (setProcessDPIAwareness != nullptr && getDPIForMonitor != nullptr +// && SUCCEEDED (setProcessDPIAwareness (Process_Per_Monitor_DPI_Aware))) + && SUCCEEDED (setProcessDPIAwareness (Process_System_DPI_Aware))) // (keep using this mode temporarily..) + return; + } - if (setProcessDPIAware != nullptr) - setProcessDPIAware(); + if (setProcessDPIAware == nullptr) + { + setProcessDPIAware = (SetProcessDPIAwareFunc) getUser32Function ("SetProcessDPIAware"); + + if (setProcessDPIAware != nullptr) + setProcessDPIAware(); + } } } } -static double getDPI() +static double getGlobalDPI() { setDPIAwareness(); @@ -181,7 +218,7 @@ static double getDPI() double Desktop::getDefaultMasterScale() { - return JUCEApplicationBase::isStandaloneApp() ? getDPI() / 96.0 + return JUCEApplicationBase::isStandaloneApp() ? getGlobalDPI() / 96.0 : 1.0; } @@ -1076,6 +1113,20 @@ public: JUCE_DECLARE_NON_COPYABLE (JuceDropTarget) }; + #if JUCE_MODULE_AVAILABLE_juce_audio_plugin_client + static bool offerKeyMessageToJUCEWindow (MSG& m) + { + if (m.message == WM_KEYDOWN || m.message == WM_KEYUP) + if (Component::getCurrentlyFocusedComponent() != nullptr) + if (HWNDComponentPeer* h = getOwnerOfWindow (m.hwnd)) + if (m.message == WM_KEYDOWN ? h->doKeyDown (m.wParam) + : h->doKeyUp (m.wParam)) + return true; + + return false; + } + #endif + private: HWND hwnd, parentToAddTo; ScopedPointer shadower; @@ -1973,28 +2024,25 @@ private: used = handleKeyPress (extendedKeyModifier | (int) key, 0) || used; break; - case VK_ADD: - case VK_SUBTRACT: - case VK_MULTIPLY: - case VK_DIVIDE: - case VK_SEPARATOR: - case VK_DECIMAL: - used = handleKeyUpOrDown (true); - break; - default: used = handleKeyUpOrDown (true); { MSG msg; - if (! PeekMessage (&msg, hwnd, WM_CHAR, WM_DEADCHAR, PM_NOREMOVE)) { // if there isn't a WM_CHAR or WM_DEADCHAR message pending, we need to // manually generate the key-press event that matches this key-down. + const UINT keyChar = MapVirtualKey ((UINT) key, 2); + const UINT scanCode = MapVirtualKey ((UINT) key, 0); + BYTE keyState[256]; + GetKeyboardState (keyState); + + WCHAR text[16] = { 0 }; + if (ToUnicode ((UINT) key, scanCode, keyState, text, 8, 0) != 1) + text[0] = 0; - const UINT keyChar = MapVirtualKey ((UINT) key, 2); - used = handleKeyPress ((int) LOWORD (keyChar), 0) || used; + used = handleKeyPress ((int) LOWORD (keyChar), (juce_wchar) text[0]) || used; } } @@ -2246,6 +2294,10 @@ private: } } + void handleDPIChange() // happens when a window moves to a screen with a different DPI. + { + } + //============================================================================== public: static LRESULT CALLBACK windowProc (HWND h, UINT message, WPARAM wParam, LPARAM lParam) @@ -2488,6 +2540,10 @@ private: doSettingChange(); break; + case 0x2e0: // WM_DPICHANGED + handleDPIChange(); + break; + case WM_INITMENU: initialiseSysMenu ((HMENU) wParam); break; @@ -2890,6 +2946,10 @@ bool KeyPress::isKeyCurrentlyDown (const int keyCode) return HWNDComponentPeer::isKeyDown (k); } +#if JUCE_MODULE_AVAILABLE_juce_audio_plugin_client +bool offerKeyMessageToJUCEWindow (MSG& m) { return HWNDComponentPeer::offerKeyMessageToJUCEWindow (m); } +#endif + //============================================================================== bool JUCE_CALLTYPE Process::isForegroundProcess() { @@ -3183,9 +3243,11 @@ void Desktop::setKioskComponent (Component* kioskModeComponent, bool enableOrDis //============================================================================== struct MonitorInfo { - MonitorInfo (Rectangle rect, bool main) noexcept : isMain (main), bounds (rect) {} + MonitorInfo (Rectangle rect, bool main, double d) noexcept + : bounds (rect), dpi (d), isMain (main) {} Rectangle bounds; + double dpi; bool isMain; }; @@ -3195,7 +3257,17 @@ static BOOL CALLBACK enumMonitorsProc (HMONITOR hm, HDC, LPRECT r, LPARAM userIn info.cbSize = sizeof (info); GetMonitorInfo (hm, &info); const bool isMain = (info.dwFlags & 1 /* MONITORINFOF_PRIMARY */) != 0; - ((Array*) userInfo)->add (MonitorInfo (rectangleFromRECT (*r), isMain)); + double dpi = 0; + + if (getDPIForMonitor != nullptr) + { + UINT dpiX = 0, dpiY = 0; + + if (SUCCEEDED (getDPIForMonitor (hm, MDT_Default, &dpiX, &dpiY))) + dpi = (dpiX + dpiY) / 2.0; + } + + ((Array*) userInfo)->add (MonitorInfo (rectangleFromRECT (*r), isMain, dpi)); return TRUE; } @@ -3207,8 +3279,10 @@ void Desktop::Displays::findDisplays (float masterScale) Array monitors; EnumDisplayMonitors (0, 0, &enumMonitorsProc, (LPARAM) &monitors); + const double globalDPI = getGlobalDPI(); + if (monitors.size() == 0) - monitors.add (MonitorInfo (rectangleFromRECT (getWindowRect (GetDesktopWindow())), true)); + monitors.add (MonitorInfo (rectangleFromRECT (getWindowRect (GetDesktopWindow())), true, globalDPI)); // make sure the first in the list is the main monitor for (int i = 1; i < monitors.size(); ++i) @@ -3218,15 +3292,22 @@ void Desktop::Displays::findDisplays (float masterScale) RECT workArea; SystemParametersInfo (SPI_GETWORKAREA, 0, &workArea, 0); - const double dpi = getDPI(); // (this has only one value for all monitors) - for (int i = 0; i < monitors.size(); ++i) { Display d; d.userArea = d.totalArea = monitors.getReference(i).bounds / masterScale; d.isMain = monitors.getReference(i).isMain; - d.scale = masterScale; - d.dpi = dpi; + d.dpi = monitors.getReference(i).dpi; + + if (d.dpi == 0) + { + d.scale = masterScale; + d.dpi = globalDPI; + } + else + { + d.scale = d.dpi / 96.0; + } if (d.isMain) d.userArea = d.userArea.getIntersection (rectangleFromRECT (workArea) / masterScale); diff --git a/source/modules/juce_gui_basics/widgets/juce_Slider.cpp b/source/modules/juce_gui_basics/widgets/juce_Slider.cpp index 1a1a73af2..c519a784e 100644 --- a/source/modules/juce_gui_basics/widgets/juce_Slider.cpp +++ b/source/modules/juce_gui_basics/widgets/juce_Slider.cpp @@ -892,7 +892,7 @@ public: if (isAbsoluteDragMode (e.mods) || (maximum - minimum) / sliderRegionSize < interval) { - dragMode = notDragging; + dragMode = absoluteDrag; handleAbsoluteDrag (e); } else diff --git a/source/modules/juce_gui_basics/windows/juce_CallOutBox.cpp b/source/modules/juce_gui_basics/windows/juce_CallOutBox.cpp index d9f4cf28d..9b877fee5 100644 --- a/source/modules/juce_gui_basics/windows/juce_CallOutBox.cpp +++ b/source/modules/juce_gui_basics/windows/juce_CallOutBox.cpp @@ -23,7 +23,7 @@ */ CallOutBox::CallOutBox (Component& c, const Rectangle& area, Component* const parent) - : borderSpace (20), arrowSize (16.0f), content (c) + : arrowSize (16.0f), content (c) { addAndMakeVisible (content); @@ -86,10 +86,14 @@ CallOutBox& CallOutBox::launchAsynchronously (Component* content, const Rectangl void CallOutBox::setArrowSize (const float newSize) { arrowSize = newSize; - borderSpace = jmax (20, (int) arrowSize); refreshPath(); } +int CallOutBox::getBorderSize() const noexcept +{ + return jmax (20, (int) arrowSize); +} + void CallOutBox::paint (Graphics& g) { getLookAndFeel().drawCallOutBoxBackground (*this, g, outline, background); @@ -97,6 +101,7 @@ void CallOutBox::paint (Graphics& g) void CallOutBox::resized() { + const int borderSpace = getBorderSize(); content.setTopLeftPosition (borderSpace, borderSpace); refreshPath(); } @@ -168,6 +173,8 @@ void CallOutBox::updatePosition (const Rectangle& newAreaToPointTo, const R targetArea = newAreaToPointTo; availableArea = newAreaToFitIn; + const int borderSpace = getBorderSize(); + Rectangle newBounds (content.getWidth() + borderSpace * 2, content.getHeight() + borderSpace * 2); diff --git a/source/modules/juce_gui_basics/windows/juce_CallOutBox.h b/source/modules/juce_gui_basics/windows/juce_CallOutBox.h index 51be2f3e8..a87a52cfb 100644 --- a/source/modules/juce_gui_basics/windows/juce_CallOutBox.h +++ b/source/modules/juce_gui_basics/windows/juce_CallOutBox.h @@ -149,10 +149,11 @@ public: bool keyPressed (const KeyPress&) override; /** @internal */ void handleCommandMessage (int) override; + /** @internal */ + int getBorderSize() const noexcept; private: //============================================================================== - int borderSpace; float arrowSize; Component& content; Path outline; diff --git a/source/modules/juce_gui_extra/native/juce_android_WebBrowserComponent.cpp b/source/modules/juce_gui_extra/native/juce_android_WebBrowserComponent.cpp index dcf786d73..d449bae36 100644 --- a/source/modules/juce_gui_extra/native/juce_android_WebBrowserComponent.cpp +++ b/source/modules/juce_gui_extra/native/juce_android_WebBrowserComponent.cpp @@ -41,17 +41,17 @@ void WebBrowserComponent::goToURL (const String& url, { lastURL = url; - lastHeaders.clear(); if (headers != nullptr) lastHeaders = *headers; + else + lastHeaders.clear(); - lastPostData.setSize (0); if (postData != nullptr) lastPostData = *postData; + else + lastPostData.reset(); blankPageShown = false; - - } void WebBrowserComponent::stop() diff --git a/source/modules/juce_gui_extra/native/juce_linux_WebBrowserComponent.cpp b/source/modules/juce_gui_extra/native/juce_linux_WebBrowserComponent.cpp index 1a36b7407..5aafe3a6a 100644 --- a/source/modules/juce_gui_extra/native/juce_linux_WebBrowserComponent.cpp +++ b/source/modules/juce_gui_extra/native/juce_linux_WebBrowserComponent.cpp @@ -46,17 +46,17 @@ void WebBrowserComponent::goToURL (const String& url, { lastURL = url; - lastHeaders.clear(); if (headers != nullptr) lastHeaders = *headers; + else + lastHeaders.clear(); - lastPostData.setSize (0); if (postData != nullptr) lastPostData = *postData; + else + lastPostData.reset(); blankPageShown = false; - - } void WebBrowserComponent::stop() diff --git a/source/modules/juce_gui_extra/native/juce_mac_WebBrowserComponent.mm b/source/modules/juce_gui_extra/native/juce_mac_WebBrowserComponent.mm index 286ff804b..46d8da7be 100644 --- a/source/modules/juce_gui_extra/native/juce_mac_WebBrowserComponent.mm +++ b/source/modules/juce_gui_extra/native/juce_mac_WebBrowserComponent.mm @@ -265,13 +265,15 @@ void WebBrowserComponent::goToURL (const String& url, { lastURL = url; - lastHeaders.clear(); if (headers != nullptr) lastHeaders = *headers; + else + lastHeaders.clear(); - lastPostData.setSize (0); if (postData != nullptr) lastPostData = *postData; + else + lastPostData.reset(); blankPageShown = false; diff --git a/source/modules/juce_gui_extra/native/juce_win32_WebBrowserComponent.cpp b/source/modules/juce_gui_extra/native/juce_win32_WebBrowserComponent.cpp index 0a5b30723..fdc6be7b3 100644 --- a/source/modules/juce_gui_extra/native/juce_win32_WebBrowserComponent.cpp +++ b/source/modules/juce_gui_extra/native/juce_win32_WebBrowserComponent.cpp @@ -214,13 +214,15 @@ void WebBrowserComponent::goToURL (const String& url, { lastURL = url; - lastHeaders.clear(); if (headers != nullptr) lastHeaders = *headers; + else + lastHeaders.clear(); - lastPostData.setSize (0); if (postData != nullptr) lastPostData = *postData; + else + lastPostData.reset(); blankPageShown = false;