| @@ -12214,6 +12214,10 @@ const char* String::toUTF8() const | |||||
| mutableThis->text = StringHolder::makeUniqueWithSize (mutableThis->text, currentLen + 1 + utf8BytesNeeded / sizeof (juce_wchar)); | mutableThis->text = StringHolder::makeUniqueWithSize (mutableThis->text, currentLen + 1 + utf8BytesNeeded / sizeof (juce_wchar)); | ||||
| char* const otherCopy = reinterpret_cast <char*> (mutableThis->text + currentLen); | char* const otherCopy = reinterpret_cast <char*> (mutableThis->text + currentLen); | ||||
| #if JUCE_DEBUG // (This just avoids spurious warnings from valgrind about the uninitialised bytes at the end of the buffer..) | |||||
| *(juce_wchar*) (otherCopy + (utf8BytesNeeded & ~(sizeof (juce_wchar) - 1))) = 0; | |||||
| #endif | |||||
| copyToUTF8 (otherCopy, std::numeric_limits<int>::max()); | copyToUTF8 (otherCopy, std::numeric_limits<int>::max()); | ||||
| return otherCopy; | return otherCopy; | ||||
| @@ -23702,13 +23706,15 @@ END_JUCE_NAMESPACE | |||||
| BEGIN_JUCE_NAMESPACE | BEGIN_JUCE_NAMESPACE | ||||
| ResamplingAudioSource::ResamplingAudioSource (AudioSource* const inputSource, | ResamplingAudioSource::ResamplingAudioSource (AudioSource* const inputSource, | ||||
| const bool deleteInputWhenDeleted_) | |||||
| const bool deleteInputWhenDeleted_, | |||||
| const int numChannels_) | |||||
| : input (inputSource), | : input (inputSource), | ||||
| deleteInputWhenDeleted (deleteInputWhenDeleted_), | deleteInputWhenDeleted (deleteInputWhenDeleted_), | ||||
| ratio (1.0), | ratio (1.0), | ||||
| lastRatio (1.0), | lastRatio (1.0), | ||||
| buffer (2, 0), | |||||
| sampsInBuffer (0) | |||||
| buffer (numChannels_, 0), | |||||
| sampsInBuffer (0), | |||||
| numChannels (numChannels_) | |||||
| { | { | ||||
| jassert (input != 0); | jassert (input != 0); | ||||
| } | } | ||||
| @@ -23734,12 +23740,15 @@ void ResamplingAudioSource::prepareToPlay (int samplesPerBlockExpected, | |||||
| input->prepareToPlay (samplesPerBlockExpected, sampleRate); | input->prepareToPlay (samplesPerBlockExpected, sampleRate); | ||||
| buffer.setSize (2, roundToInt (samplesPerBlockExpected * ratio) + 32); | |||||
| buffer.setSize (numChannels, roundToInt (samplesPerBlockExpected * ratio) + 32); | |||||
| buffer.clear(); | buffer.clear(); | ||||
| sampsInBuffer = 0; | sampsInBuffer = 0; | ||||
| bufferPos = 0; | bufferPos = 0; | ||||
| subSampleOffset = 0.0; | subSampleOffset = 0.0; | ||||
| filterStates.calloc (numChannels); | |||||
| srcBuffers.calloc (numChannels); | |||||
| destBuffers.calloc (numChannels); | |||||
| createLowPass (ratio); | createLowPass (ratio); | ||||
| resetFilters(); | resetFilters(); | ||||
| } | } | ||||
| @@ -23747,7 +23756,7 @@ void ResamplingAudioSource::prepareToPlay (int samplesPerBlockExpected, | |||||
| void ResamplingAudioSource::releaseResources() | void ResamplingAudioSource::releaseResources() | ||||
| { | { | ||||
| input->releaseResources(); | input->releaseResources(); | ||||
| buffer.setSize (2, 0); | |||||
| buffer.setSize (numChannels, 0); | |||||
| } | } | ||||
| void ResamplingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& info) | void ResamplingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& info) | ||||
| @@ -23793,7 +23802,7 @@ void ResamplingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& inf | |||||
| { | { | ||||
| // for down-sampling, pre-apply the filter.. | // for down-sampling, pre-apply the filter.. | ||||
| for (int i = jmin (2, info.buffer->getNumChannels()); --i >= 0;) | |||||
| for (int i = jmin (numChannels, info.buffer->getNumChannels()); --i >= 0;) | |||||
| applyFilter (buffer.getSampleData (i, endOfBufferPos), numToDo, filterStates[i]); | applyFilter (buffer.getSampleData (i, endOfBufferPos), numToDo, filterStates[i]); | ||||
| } | } | ||||
| @@ -23801,23 +23810,20 @@ void ResamplingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& inf | |||||
| endOfBufferPos += numToDo; | endOfBufferPos += numToDo; | ||||
| } | } | ||||
| float* dl = info.buffer->getSampleData (0, info.startSample); | |||||
| float* dr = (info.buffer->getNumChannels() > 1) ? info.buffer->getSampleData (1, info.startSample) : 0; | |||||
| const float* const bl = buffer.getSampleData (0, 0); | |||||
| const float* const br = buffer.getSampleData (1, 0); | |||||
| for (int channel = 0; channel < numChannels; ++channel) | |||||
| { | |||||
| destBuffers[channel] = info.buffer->getSampleData (channel, info.startSample); | |||||
| srcBuffers[channel] = buffer.getSampleData (channel, 0); | |||||
| } | |||||
| int nextPos = (bufferPos + 1) % bufferSize; | int nextPos = (bufferPos + 1) % bufferSize; | ||||
| for (int m = info.numSamples; --m >= 0;) | for (int m = info.numSamples; --m >= 0;) | ||||
| { | { | ||||
| const float alpha = (float) subSampleOffset; | const float alpha = (float) subSampleOffset; | ||||
| const float invAlpha = 1.0f - alpha; | const float invAlpha = 1.0f - alpha; | ||||
| *dl++ = bl [bufferPos] * invAlpha + bl [nextPos] * alpha; | |||||
| if (dr != 0) | |||||
| *dr++ = br [bufferPos] * invAlpha + br [nextPos] * alpha; | |||||
| for (int channel = 0; channel < numChannels; ++channel) | |||||
| *destBuffers[channel]++ = srcBuffers[channel][bufferPos] * invAlpha + srcBuffers[channel][nextPos] * alpha; | |||||
| subSampleOffset += ratio; | subSampleOffset += ratio; | ||||
| @@ -23838,14 +23844,13 @@ void ResamplingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& inf | |||||
| if (ratio < 0.9999) | if (ratio < 0.9999) | ||||
| { | { | ||||
| // for up-sampling, apply the filter after transposing.. | // for up-sampling, apply the filter after transposing.. | ||||
| for (int i = jmin (2, info.buffer->getNumChannels()); --i >= 0;) | |||||
| for (int i = jmin (numChannels, info.buffer->getNumChannels()); --i >= 0;) | |||||
| applyFilter (info.buffer->getSampleData (i, info.startSample), info.numSamples, filterStates[i]); | applyFilter (info.buffer->getSampleData (i, info.startSample), info.numSamples, filterStates[i]); | ||||
| } | } | ||||
| else if (ratio <= 1.0001) | else if (ratio <= 1.0001) | ||||
| { | { | ||||
| // if the filter's not currently being applied, keep it stoked with the last couple of samples to avoid discontinuities | // if the filter's not currently being applied, keep it stoked with the last couple of samples to avoid discontinuities | ||||
| for (int i = jmin (2, info.buffer->getNumChannels()); --i >= 0;) | |||||
| for (int i = jmin (numChannels, info.buffer->getNumChannels()); --i >= 0;) | |||||
| { | { | ||||
| const float* const endOfBuffer = info.buffer->getSampleData (i, info.startSample + info.numSamples - 1); | const float* const endOfBuffer = info.buffer->getSampleData (i, info.startSample + info.numSamples - 1); | ||||
| FilterState& fs = filterStates[i]; | FilterState& fs = filterStates[i]; | ||||
| @@ -23904,7 +23909,7 @@ void ResamplingAudioSource::setFilterCoefficients (double c1, double c2, double | |||||
| void ResamplingAudioSource::resetFilters() | void ResamplingAudioSource::resetFilters() | ||||
| { | { | ||||
| zeromem (filterStates, sizeof (filterStates)); | |||||
| zeromem (filterStates, sizeof (FilterState) * numChannels); | |||||
| } | } | ||||
| void ResamplingAudioSource::applyFilter (float* samples, int num, FilterState& fs) | void ResamplingAudioSource::applyFilter (float* samples, int num, FilterState& fs) | ||||
| @@ -26166,7 +26171,7 @@ void AudioSampleBuffer::readFromAudioReader (AudioFormatReader* reader, | |||||
| const int numSamples, | const int numSamples, | ||||
| const int readerStartSample, | const int readerStartSample, | ||||
| const bool useLeftChan, | const bool useLeftChan, | ||||
| const bool useRightChan) throw() | |||||
| const bool useRightChan) | |||||
| { | { | ||||
| jassert (reader != 0); | jassert (reader != 0); | ||||
| jassert (startSample >= 0 && startSample + numSamples <= size); | jassert (startSample >= 0 && startSample + numSamples <= size); | ||||
| @@ -26223,62 +26228,46 @@ void AudioSampleBuffer::readFromAudioReader (AudioFormatReader* reader, | |||||
| void AudioSampleBuffer::writeToAudioWriter (AudioFormatWriter* writer, | void AudioSampleBuffer::writeToAudioWriter (AudioFormatWriter* writer, | ||||
| const int startSample, | const int startSample, | ||||
| const int numSamples) const throw() | |||||
| const int numSamples) const | |||||
| { | { | ||||
| jassert (startSample >= 0 && startSample + numSamples <= size); | |||||
| jassert (startSample >= 0 && startSample + numSamples <= size && numChannels > 0); | |||||
| if (numSamples > 0) | if (numSamples > 0) | ||||
| { | { | ||||
| int* chans [3]; | |||||
| HeapBlock<int> tempBuffer; | |||||
| HeapBlock<int*> chans (numChannels + 1); | |||||
| chans [numChannels] = 0; | |||||
| if (writer->isFloatingPoint()) | if (writer->isFloatingPoint()) | ||||
| { | { | ||||
| chans[0] = (int*) getSampleData (0, startSample); | |||||
| if (numChannels > 1) | |||||
| chans[1] = (int*) getSampleData (1, startSample); | |||||
| else | |||||
| chans[1] = 0; | |||||
| chans[2] = 0; | |||||
| writer->write ((const int**) chans, numSamples); | |||||
| for (int i = numChannels; --i >= 0;) | |||||
| chans[i] = (int*) channels[i] + startSample; | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| HeapBlock <int> tempBuffer (numSamples * 2); | |||||
| chans[0] = tempBuffer; | |||||
| if (numChannels > 1) | |||||
| chans[1] = chans[0] + numSamples; | |||||
| else | |||||
| chans[1] = 0; | |||||
| chans[2] = 0; | |||||
| tempBuffer.malloc (numSamples * numChannels); | |||||
| for (int j = 0; j < 2; ++j) | |||||
| for (int j = 0; j < numChannels; ++j) | |||||
| { | { | ||||
| int* const dest = chans[j]; | |||||
| int* const dest = tempBuffer + j * numSamples; | |||||
| const float* const src = channels[j] + startSample; | |||||
| chans[j] = dest; | |||||
| if (dest != 0) | |||||
| for (int i = 0; i < numSamples; ++i) | |||||
| { | { | ||||
| const float* const src = channels [j] + startSample; | |||||
| for (int i = 0; i < numSamples; ++i) | |||||
| { | |||||
| const double samp = src[i]; | |||||
| const double samp = src[i]; | |||||
| if (samp <= -1.0) | |||||
| dest[i] = std::numeric_limits<int>::min(); | |||||
| else if (samp >= 1.0) | |||||
| dest[i] = std::numeric_limits<int>::max(); | |||||
| else | |||||
| dest[i] = roundToInt (std::numeric_limits<int>::max() * samp); | |||||
| } | |||||
| if (samp <= -1.0) | |||||
| dest[i] = std::numeric_limits<int>::min(); | |||||
| else if (samp >= 1.0) | |||||
| dest[i] = std::numeric_limits<int>::max(); | |||||
| else | |||||
| dest[i] = roundToInt (std::numeric_limits<int>::max() * samp); | |||||
| } | } | ||||
| } | } | ||||
| writer->write ((const int**) chans, numSamples); | |||||
| } | } | ||||
| writer->write ((const int**) chans.getData(), numSamples); | |||||
| } | } | ||||
| } | } | ||||
| @@ -29683,6 +29672,7 @@ PluginListComponent::PluginListComponent (KnownPluginList& listToEdit, | |||||
| setSize (400, 600); | setSize (400, 600); | ||||
| list.addChangeListener (this); | list.addChangeListener (this); | ||||
| changeListenerCallback (0); | |||||
| } | } | ||||
| PluginListComponent::~PluginListComponent() | PluginListComponent::~PluginListComponent() | ||||
| @@ -50950,14 +50940,6 @@ public: | |||||
| return String::empty; | return String::empty; | ||||
| } | } | ||||
| juce_UseDebuggingNewOperator | |||||
| private: | |||||
| TableListBox& owner; | |||||
| int row; | |||||
| bool isSelected, isDragging, selectRowOnMouseUp; | |||||
| BigInteger columnsWithComponents; | |||||
| Component* findChildComponentForColumn (const int columnId) const | Component* findChildComponentForColumn (const int columnId) const | ||||
| { | { | ||||
| for (int i = getNumChildComponents(); --i >= 0;) | for (int i = getNumChildComponents(); --i >= 0;) | ||||
| @@ -50971,6 +50953,14 @@ private: | |||||
| return 0; | return 0; | ||||
| } | } | ||||
| juce_UseDebuggingNewOperator | |||||
| private: | |||||
| TableListBox& owner; | |||||
| int row; | |||||
| bool isSelected, isDragging, selectRowOnMouseUp; | |||||
| BigInteger columnsWithComponents; | |||||
| TableListRowComp (const TableListRowComp&); | TableListRowComp (const TableListRowComp&); | ||||
| TableListRowComp& operator= (const TableListRowComp&); | TableListRowComp& operator= (const TableListRowComp&); | ||||
| }; | }; | ||||
| @@ -51102,6 +51092,12 @@ const Rectangle<int> TableListBox::getCellPosition (const int columnId, | |||||
| headerCell.getWidth(), row.getHeight()); | headerCell.getWidth(), row.getHeight()); | ||||
| } | } | ||||
| Component* TableListBox::getCellComponent (int columnId, int rowNumber) const | |||||
| { | |||||
| TableListRowComp* const rowComp = dynamic_cast <TableListRowComp*> (getComponentForRowNumber (rowNumber)); | |||||
| return rowComp != 0 ? rowComp->findChildComponentForColumn (columnId) : 0; | |||||
| } | |||||
| void TableListBox::scrollToEnsureColumnIsOnscreen (const int columnId) | void TableListBox::scrollToEnsureColumnIsOnscreen (const int columnId) | ||||
| { | { | ||||
| ScrollBar* const scrollbar = getHorizontalScrollBar(); | ScrollBar* const scrollbar = getHorizontalScrollBar(); | ||||
| @@ -85775,10 +85771,10 @@ static const Point<float> findQuadraticSubdivisionPoint (double proportion, cons | |||||
| return mid1 + (mid2 - mid1) * proportion; | return mid1 + (mid2 - mid1) * proportion; | ||||
| } | } | ||||
| ValueTree DrawablePath::ValueTreeWrapper::Element::insertPoint (const Point<float>& targetPoint, RelativeCoordinate::NamedCoordinateFinder* nameFinder, UndoManager* undoManager) | |||||
| double DrawablePath::ValueTreeWrapper::Element::findProportionAlongLine (const Point<float>& targetPoint, RelativeCoordinate::NamedCoordinateFinder* nameFinder) const | |||||
| { | { | ||||
| ValueTree newTree; | |||||
| const Identifier i (state.getType()); | const Identifier i (state.getType()); | ||||
| double bestProp = 0; | |||||
| if (i == cubicToElement) | if (i == cubicToElement) | ||||
| { | { | ||||
| @@ -85786,7 +85782,6 @@ ValueTree DrawablePath::ValueTreeWrapper::Element::insertPoint (const Point<floa | |||||
| const Point<float> points[] = { rp1.resolve (nameFinder), rp2.resolve (nameFinder), rp3.resolve (nameFinder), rp4.resolve (nameFinder) }; | const Point<float> points[] = { rp1.resolve (nameFinder), rp2.resolve (nameFinder), rp3.resolve (nameFinder), rp4.resolve (nameFinder) }; | ||||
| double bestProp = 0; | |||||
| float bestDistance = std::numeric_limits<float>::max(); | float bestDistance = std::numeric_limits<float>::max(); | ||||
| for (int i = 110; --i >= 0;) | for (int i = 110; --i >= 0;) | ||||
| @@ -85801,6 +85796,48 @@ ValueTree DrawablePath::ValueTreeWrapper::Element::insertPoint (const Point<floa | |||||
| bestDistance = distance; | bestDistance = distance; | ||||
| } | } | ||||
| } | } | ||||
| } | |||||
| else if (i == quadraticToElement) | |||||
| { | |||||
| RelativePoint rp1 (getStartPoint()), rp2 (getControlPoint (0)), rp3 (getEndPoint()); | |||||
| const Point<float> points[] = { rp1.resolve (nameFinder), rp2.resolve (nameFinder), rp3.resolve (nameFinder) }; | |||||
| float bestDistance = std::numeric_limits<float>::max(); | |||||
| for (int i = 110; --i >= 0;) | |||||
| { | |||||
| double prop = i > 10 ? ((i - 10) / 100.0) : (bestProp + ((i - 5) / 1000.0)); | |||||
| const Point<float> centre (findQuadraticSubdivisionPoint (prop, points)); | |||||
| const float distance = centre.getDistanceFrom (targetPoint); | |||||
| if (distance < bestDistance) | |||||
| { | |||||
| bestProp = prop; | |||||
| bestDistance = distance; | |||||
| } | |||||
| } | |||||
| } | |||||
| else if (i == lineToElement) | |||||
| { | |||||
| RelativePoint rp1 (getStartPoint()), rp2 (getEndPoint()); | |||||
| const Line<float> line (rp1.resolve (nameFinder), rp2.resolve (nameFinder)); | |||||
| bestProp = line.findNearestProportionalPositionTo (targetPoint); | |||||
| } | |||||
| return bestProp; | |||||
| } | |||||
| ValueTree DrawablePath::ValueTreeWrapper::Element::insertPoint (const Point<float>& targetPoint, RelativeCoordinate::NamedCoordinateFinder* nameFinder, UndoManager* undoManager) | |||||
| { | |||||
| ValueTree newTree; | |||||
| const Identifier i (state.getType()); | |||||
| if (i == cubicToElement) | |||||
| { | |||||
| double bestProp = findProportionAlongLine (targetPoint, nameFinder); | |||||
| RelativePoint rp1 (getStartPoint()), rp2 (getControlPoint (0)), rp3 (getControlPoint (1)), rp4 (getEndPoint()); | |||||
| const Point<float> points[] = { rp1.resolve (nameFinder), rp2.resolve (nameFinder), rp3.resolve (nameFinder), rp4.resolve (nameFinder) }; | |||||
| const Point<float> mid1 (points[0] + (points[1] - points[0]) * bestProp), | const Point<float> mid1 (points[0] + (points[1] - points[0]) * bestProp), | ||||
| mid2 (points[1] + (points[2] - points[1]) * bestProp), | mid2 (points[1] + (points[2] - points[1]) * bestProp), | ||||
| @@ -85825,26 +85862,11 @@ ValueTree DrawablePath::ValueTreeWrapper::Element::insertPoint (const Point<floa | |||||
| } | } | ||||
| else if (i == quadraticToElement) | else if (i == quadraticToElement) | ||||
| { | { | ||||
| RelativePoint rp1 (getStartPoint()), rp2 (getControlPoint (0)), rp3 (getEndPoint()); | |||||
| double bestProp = findProportionAlongLine (targetPoint, nameFinder); | |||||
| RelativePoint rp1 (getStartPoint()), rp2 (getControlPoint (0)), rp3 (getEndPoint()); | |||||
| const Point<float> points[] = { rp1.resolve (nameFinder), rp2.resolve (nameFinder), rp3.resolve (nameFinder) }; | const Point<float> points[] = { rp1.resolve (nameFinder), rp2.resolve (nameFinder), rp3.resolve (nameFinder) }; | ||||
| double bestProp = 0; | |||||
| float bestDistance = std::numeric_limits<float>::max(); | |||||
| for (int i = 110; --i >= 0;) | |||||
| { | |||||
| double prop = i > 10 ? ((i - 10) / 100.0) : (bestProp + ((i - 5) / 1000.0)); | |||||
| const Point<float> centre (findQuadraticSubdivisionPoint (prop, points)); | |||||
| const float distance = centre.getDistanceFrom (targetPoint); | |||||
| if (distance < bestDistance) | |||||
| { | |||||
| bestProp = prop; | |||||
| bestDistance = distance; | |||||
| } | |||||
| } | |||||
| const Point<float> mid1 (points[0] + (points[1] - points[0]) * bestProp), | const Point<float> mid1 (points[0] + (points[1] - points[0]) * bestProp), | ||||
| mid2 (points[1] + (points[2] - points[1]) * bestProp); | mid2 (points[1] + (points[2] - points[1]) * bestProp); | ||||
| @@ -64,7 +64,7 @@ | |||||
| */ | */ | ||||
| #define JUCE_MAJOR_VERSION 1 | #define JUCE_MAJOR_VERSION 1 | ||||
| #define JUCE_MINOR_VERSION 52 | #define JUCE_MINOR_VERSION 52 | ||||
| #define JUCE_BUILDNUMBER 30 | |||||
| #define JUCE_BUILDNUMBER 31 | |||||
| /** Current Juce version number. | /** Current Juce version number. | ||||
| @@ -29669,7 +29669,7 @@ public: | |||||
| int numSamples, | int numSamples, | ||||
| int readerStartSample, | int readerStartSample, | ||||
| bool useReaderLeftChan, | bool useReaderLeftChan, | ||||
| bool useReaderRightChan) throw(); | |||||
| bool useReaderRightChan); | |||||
| /** Writes a section of this buffer to an audio writer. | /** Writes a section of this buffer to an audio writer. | ||||
| @@ -29680,7 +29680,7 @@ public: | |||||
| */ | */ | ||||
| void writeToAudioWriter (AudioFormatWriter* writer, | void writeToAudioWriter (AudioFormatWriter* writer, | ||||
| int startSample, | int startSample, | ||||
| int numSamples) const throw(); | |||||
| int numSamples) const; | |||||
| juce_UseDebuggingNewOperator | juce_UseDebuggingNewOperator | ||||
| @@ -31739,9 +31739,11 @@ public: | |||||
| @param inputSource the input source to read from | @param inputSource the input source to read from | ||||
| @param deleteInputWhenDeleted if true, the input source will be deleted when | @param deleteInputWhenDeleted if true, the input source will be deleted when | ||||
| this object is deleted | this object is deleted | ||||
| @param numChannels the number of channels to process | |||||
| */ | */ | ||||
| ResamplingAudioSource (AudioSource* const inputSource, | ResamplingAudioSource (AudioSource* const inputSource, | ||||
| const bool deleteInputWhenDeleted); | |||||
| const bool deleteInputWhenDeleted, | |||||
| int numChannels = 2); | |||||
| /** Destructor. */ | /** Destructor. */ | ||||
| ~ResamplingAudioSource(); | ~ResamplingAudioSource(); | ||||
| @@ -31777,6 +31779,8 @@ private: | |||||
| double subSampleOffset; | double subSampleOffset; | ||||
| double coefficients[6]; | double coefficients[6]; | ||||
| CriticalSection ratioLock; | CriticalSection ratioLock; | ||||
| const int numChannels; | |||||
| HeapBlock<float*> destBuffers, srcBuffers; | |||||
| void setFilterCoefficients (double c1, double c2, double c3, double c4, double c5, double c6); | void setFilterCoefficients (double c1, double c2, double c3, double c4, double c5, double c6); | ||||
| void createLowPass (const double proportionalRate); | void createLowPass (const double proportionalRate); | ||||
| @@ -31786,7 +31790,7 @@ private: | |||||
| double x1, x2, y1, y2; | double x1, x2, y1, y2; | ||||
| }; | }; | ||||
| FilterState filterStates[2]; | |||||
| HeapBlock<FilterState> filterStates; | |||||
| void resetFilters(); | void resetFilters(); | ||||
| void applyFilter (float* samples, int num, FilterState& fs); | void applyFilter (float* samples, int num, FilterState& fs); | ||||
| @@ -36664,6 +36668,9 @@ public: | |||||
| */ | */ | ||||
| void showEditor(); | void showEditor(); | ||||
| /** Pops up the combo box's list. */ | |||||
| void showPopup(); | |||||
| /** Registers a listener that will be called when the box's content changes. */ | /** Registers a listener that will be called when the box's content changes. */ | ||||
| void addListener (ComboBoxListener* listener) throw(); | void addListener (ComboBoxListener* listener) throw(); | ||||
| @@ -36773,8 +36780,6 @@ private: | |||||
| ScopedPointer<Label> label; | ScopedPointer<Label> label; | ||||
| String textWhenNothingSelected, noChoicesMessage; | String textWhenNothingSelected, noChoicesMessage; | ||||
| void showPopup(); | |||||
| ItemInfo* getItemForId (int itemId) const throw(); | ItemInfo* getItemForId (int itemId) const throw(); | ||||
| ItemInfo* getItemForIndex (int index) const throw(); | ItemInfo* getItemForIndex (int index) const throw(); | ||||
| @@ -47165,10 +47170,16 @@ public: | |||||
| If relativeToComponentTopLeft is false, the co-ords are relative to the | If relativeToComponentTopLeft is false, the co-ords are relative to the | ||||
| top-left of the table's top-left cell. | top-left of the table's top-left cell. | ||||
| */ | */ | ||||
| const Rectangle<int> getCellPosition (int columnId, | |||||
| int rowNumber, | |||||
| const Rectangle<int> getCellPosition (int columnId, int rowNumber, | |||||
| bool relativeToComponentTopLeft) const; | bool relativeToComponentTopLeft) const; | ||||
| /** Returns the component that currently represents a given cell. | |||||
| If the component for this cell is off-screen or if the position is out-of-range, | |||||
| this may return 0. | |||||
| @see getCellPosition | |||||
| */ | |||||
| Component* getCellComponent (int columnId, int rowNumber) const; | |||||
| /** Scrolls horizontally if necessary to make sure that a particular column is visible. | /** Scrolls horizontally if necessary to make sure that a particular column is visible. | ||||
| @see ListBox::scrollToEnsureRowIsOnscreen | @see ListBox::scrollToEnsureRowIsOnscreen | ||||
| @@ -59405,6 +59416,7 @@ public: | |||||
| void convertToPathBreak (UndoManager* undoManager); | void convertToPathBreak (UndoManager* undoManager); | ||||
| ValueTree insertPoint (const Point<float>& targetPoint, RelativeCoordinate::NamedCoordinateFinder* nameFinder, UndoManager* undoManager); | ValueTree insertPoint (const Point<float>& targetPoint, RelativeCoordinate::NamedCoordinateFinder* nameFinder, UndoManager* undoManager); | ||||
| void removePoint (UndoManager* undoManager); | void removePoint (UndoManager* undoManager); | ||||
| double findProportionAlongLine (const Point<float>& targetPoint, RelativeCoordinate::NamedCoordinateFinder* nameFinder) const; | |||||
| static const Identifier mode, startSubPathElement, closeSubPathElement, | static const Identifier mode, startSubPathElement, closeSubPathElement, | ||||
| lineToElement, quadraticToElement, cubicToElement; | lineToElement, quadraticToElement, cubicToElement; | ||||
| @@ -33,13 +33,15 @@ BEGIN_JUCE_NAMESPACE | |||||
| //============================================================================== | //============================================================================== | ||||
| ResamplingAudioSource::ResamplingAudioSource (AudioSource* const inputSource, | ResamplingAudioSource::ResamplingAudioSource (AudioSource* const inputSource, | ||||
| const bool deleteInputWhenDeleted_) | |||||
| const bool deleteInputWhenDeleted_, | |||||
| const int numChannels_) | |||||
| : input (inputSource), | : input (inputSource), | ||||
| deleteInputWhenDeleted (deleteInputWhenDeleted_), | deleteInputWhenDeleted (deleteInputWhenDeleted_), | ||||
| ratio (1.0), | ratio (1.0), | ||||
| lastRatio (1.0), | lastRatio (1.0), | ||||
| buffer (2, 0), | |||||
| sampsInBuffer (0) | |||||
| buffer (numChannels_, 0), | |||||
| sampsInBuffer (0), | |||||
| numChannels (numChannels_) | |||||
| { | { | ||||
| jassert (input != 0); | jassert (input != 0); | ||||
| } | } | ||||
| @@ -65,12 +67,15 @@ void ResamplingAudioSource::prepareToPlay (int samplesPerBlockExpected, | |||||
| input->prepareToPlay (samplesPerBlockExpected, sampleRate); | input->prepareToPlay (samplesPerBlockExpected, sampleRate); | ||||
| buffer.setSize (2, roundToInt (samplesPerBlockExpected * ratio) + 32); | |||||
| buffer.setSize (numChannels, roundToInt (samplesPerBlockExpected * ratio) + 32); | |||||
| buffer.clear(); | buffer.clear(); | ||||
| sampsInBuffer = 0; | sampsInBuffer = 0; | ||||
| bufferPos = 0; | bufferPos = 0; | ||||
| subSampleOffset = 0.0; | subSampleOffset = 0.0; | ||||
| filterStates.calloc (numChannels); | |||||
| srcBuffers.calloc (numChannels); | |||||
| destBuffers.calloc (numChannels); | |||||
| createLowPass (ratio); | createLowPass (ratio); | ||||
| resetFilters(); | resetFilters(); | ||||
| } | } | ||||
| @@ -78,7 +83,7 @@ void ResamplingAudioSource::prepareToPlay (int samplesPerBlockExpected, | |||||
| void ResamplingAudioSource::releaseResources() | void ResamplingAudioSource::releaseResources() | ||||
| { | { | ||||
| input->releaseResources(); | input->releaseResources(); | ||||
| buffer.setSize (2, 0); | |||||
| buffer.setSize (numChannels, 0); | |||||
| } | } | ||||
| void ResamplingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& info) | void ResamplingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& info) | ||||
| @@ -124,7 +129,7 @@ void ResamplingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& inf | |||||
| { | { | ||||
| // for down-sampling, pre-apply the filter.. | // for down-sampling, pre-apply the filter.. | ||||
| for (int i = jmin (2, info.buffer->getNumChannels()); --i >= 0;) | |||||
| for (int i = jmin (numChannels, info.buffer->getNumChannels()); --i >= 0;) | |||||
| applyFilter (buffer.getSampleData (i, endOfBufferPos), numToDo, filterStates[i]); | applyFilter (buffer.getSampleData (i, endOfBufferPos), numToDo, filterStates[i]); | ||||
| } | } | ||||
| @@ -132,23 +137,20 @@ void ResamplingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& inf | |||||
| endOfBufferPos += numToDo; | endOfBufferPos += numToDo; | ||||
| } | } | ||||
| float* dl = info.buffer->getSampleData (0, info.startSample); | |||||
| float* dr = (info.buffer->getNumChannels() > 1) ? info.buffer->getSampleData (1, info.startSample) : 0; | |||||
| const float* const bl = buffer.getSampleData (0, 0); | |||||
| const float* const br = buffer.getSampleData (1, 0); | |||||
| for (int channel = 0; channel < numChannels; ++channel) | |||||
| { | |||||
| destBuffers[channel] = info.buffer->getSampleData (channel, info.startSample); | |||||
| srcBuffers[channel] = buffer.getSampleData (channel, 0); | |||||
| } | |||||
| int nextPos = (bufferPos + 1) % bufferSize; | int nextPos = (bufferPos + 1) % bufferSize; | ||||
| for (int m = info.numSamples; --m >= 0;) | for (int m = info.numSamples; --m >= 0;) | ||||
| { | { | ||||
| const float alpha = (float) subSampleOffset; | const float alpha = (float) subSampleOffset; | ||||
| const float invAlpha = 1.0f - alpha; | const float invAlpha = 1.0f - alpha; | ||||
| *dl++ = bl [bufferPos] * invAlpha + bl [nextPos] * alpha; | |||||
| if (dr != 0) | |||||
| *dr++ = br [bufferPos] * invAlpha + br [nextPos] * alpha; | |||||
| for (int channel = 0; channel < numChannels; ++channel) | |||||
| *destBuffers[channel]++ = srcBuffers[channel][bufferPos] * invAlpha + srcBuffers[channel][nextPos] * alpha; | |||||
| subSampleOffset += ratio; | subSampleOffset += ratio; | ||||
| @@ -169,14 +171,13 @@ void ResamplingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& inf | |||||
| if (ratio < 0.9999) | if (ratio < 0.9999) | ||||
| { | { | ||||
| // for up-sampling, apply the filter after transposing.. | // for up-sampling, apply the filter after transposing.. | ||||
| for (int i = jmin (2, info.buffer->getNumChannels()); --i >= 0;) | |||||
| for (int i = jmin (numChannels, info.buffer->getNumChannels()); --i >= 0;) | |||||
| applyFilter (info.buffer->getSampleData (i, info.startSample), info.numSamples, filterStates[i]); | applyFilter (info.buffer->getSampleData (i, info.startSample), info.numSamples, filterStates[i]); | ||||
| } | } | ||||
| else if (ratio <= 1.0001) | else if (ratio <= 1.0001) | ||||
| { | { | ||||
| // if the filter's not currently being applied, keep it stoked with the last couple of samples to avoid discontinuities | // if the filter's not currently being applied, keep it stoked with the last couple of samples to avoid discontinuities | ||||
| for (int i = jmin (2, info.buffer->getNumChannels()); --i >= 0;) | |||||
| for (int i = jmin (numChannels, info.buffer->getNumChannels()); --i >= 0;) | |||||
| { | { | ||||
| const float* const endOfBuffer = info.buffer->getSampleData (i, info.startSample + info.numSamples - 1); | const float* const endOfBuffer = info.buffer->getSampleData (i, info.startSample + info.numSamples - 1); | ||||
| FilterState& fs = filterStates[i]; | FilterState& fs = filterStates[i]; | ||||
| @@ -235,7 +236,7 @@ void ResamplingAudioSource::setFilterCoefficients (double c1, double c2, double | |||||
| void ResamplingAudioSource::resetFilters() | void ResamplingAudioSource::resetFilters() | ||||
| { | { | ||||
| zeromem (filterStates, sizeof (filterStates)); | |||||
| zeromem (filterStates, sizeof (FilterState) * numChannels); | |||||
| } | } | ||||
| void ResamplingAudioSource::applyFilter (float* samples, int num, FilterState& fs) | void ResamplingAudioSource::applyFilter (float* samples, int num, FilterState& fs) | ||||
| @@ -29,6 +29,7 @@ | |||||
| #include "juce_AudioSource.h" | #include "juce_AudioSource.h" | ||||
| #include "../../threads/juce_CriticalSection.h" | #include "../../threads/juce_CriticalSection.h" | ||||
| //============================================================================== | //============================================================================== | ||||
| /** | /** | ||||
| A type of AudioSource that takes an input source and changes its sample rate. | A type of AudioSource that takes an input source and changes its sample rate. | ||||
| @@ -44,9 +45,11 @@ public: | |||||
| @param inputSource the input source to read from | @param inputSource the input source to read from | ||||
| @param deleteInputWhenDeleted if true, the input source will be deleted when | @param deleteInputWhenDeleted if true, the input source will be deleted when | ||||
| this object is deleted | this object is deleted | ||||
| @param numChannels the number of channels to process | |||||
| */ | */ | ||||
| ResamplingAudioSource (AudioSource* const inputSource, | ResamplingAudioSource (AudioSource* const inputSource, | ||||
| const bool deleteInputWhenDeleted); | |||||
| const bool deleteInputWhenDeleted, | |||||
| int numChannels = 2); | |||||
| /** Destructor. */ | /** Destructor. */ | ||||
| ~ResamplingAudioSource(); | ~ResamplingAudioSource(); | ||||
| @@ -84,6 +87,8 @@ private: | |||||
| double subSampleOffset; | double subSampleOffset; | ||||
| double coefficients[6]; | double coefficients[6]; | ||||
| CriticalSection ratioLock; | CriticalSection ratioLock; | ||||
| const int numChannels; | |||||
| HeapBlock<float*> destBuffers, srcBuffers; | |||||
| void setFilterCoefficients (double c1, double c2, double c3, double c4, double c5, double c6); | void setFilterCoefficients (double c1, double c2, double c3, double c4, double c5, double c6); | ||||
| void createLowPass (const double proportionalRate); | void createLowPass (const double proportionalRate); | ||||
| @@ -93,7 +98,7 @@ private: | |||||
| double x1, x2, y1, y2; | double x1, x2, y1, y2; | ||||
| }; | }; | ||||
| FilterState filterStates[2]; | |||||
| HeapBlock<FilterState> filterStates; | |||||
| void resetFilters(); | void resetFilters(); | ||||
| void applyFilter (float* samples, int num, FilterState& fs); | void applyFilter (float* samples, int num, FilterState& fs); | ||||
| @@ -574,7 +574,7 @@ void AudioSampleBuffer::readFromAudioReader (AudioFormatReader* reader, | |||||
| const int numSamples, | const int numSamples, | ||||
| const int readerStartSample, | const int readerStartSample, | ||||
| const bool useLeftChan, | const bool useLeftChan, | ||||
| const bool useRightChan) throw() | |||||
| const bool useRightChan) | |||||
| { | { | ||||
| jassert (reader != 0); | jassert (reader != 0); | ||||
| jassert (startSample >= 0 && startSample + numSamples <= size); | jassert (startSample >= 0 && startSample + numSamples <= size); | ||||
| @@ -631,62 +631,46 @@ void AudioSampleBuffer::readFromAudioReader (AudioFormatReader* reader, | |||||
| void AudioSampleBuffer::writeToAudioWriter (AudioFormatWriter* writer, | void AudioSampleBuffer::writeToAudioWriter (AudioFormatWriter* writer, | ||||
| const int startSample, | const int startSample, | ||||
| const int numSamples) const throw() | |||||
| const int numSamples) const | |||||
| { | { | ||||
| jassert (startSample >= 0 && startSample + numSamples <= size); | |||||
| jassert (startSample >= 0 && startSample + numSamples <= size && numChannels > 0); | |||||
| if (numSamples > 0) | if (numSamples > 0) | ||||
| { | { | ||||
| int* chans [3]; | |||||
| HeapBlock<int> tempBuffer; | |||||
| HeapBlock<int*> chans (numChannels + 1); | |||||
| chans [numChannels] = 0; | |||||
| if (writer->isFloatingPoint()) | if (writer->isFloatingPoint()) | ||||
| { | { | ||||
| chans[0] = (int*) getSampleData (0, startSample); | |||||
| if (numChannels > 1) | |||||
| chans[1] = (int*) getSampleData (1, startSample); | |||||
| else | |||||
| chans[1] = 0; | |||||
| chans[2] = 0; | |||||
| writer->write ((const int**) chans, numSamples); | |||||
| for (int i = numChannels; --i >= 0;) | |||||
| chans[i] = (int*) channels[i] + startSample; | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| HeapBlock <int> tempBuffer (numSamples * 2); | |||||
| chans[0] = tempBuffer; | |||||
| if (numChannels > 1) | |||||
| chans[1] = chans[0] + numSamples; | |||||
| else | |||||
| chans[1] = 0; | |||||
| tempBuffer.malloc (numSamples * numChannels); | |||||
| chans[2] = 0; | |||||
| for (int j = 0; j < 2; ++j) | |||||
| for (int j = 0; j < numChannels; ++j) | |||||
| { | { | ||||
| int* const dest = chans[j]; | |||||
| int* const dest = tempBuffer + j * numSamples; | |||||
| const float* const src = channels[j] + startSample; | |||||
| chans[j] = dest; | |||||
| if (dest != 0) | |||||
| for (int i = 0; i < numSamples; ++i) | |||||
| { | { | ||||
| const float* const src = channels [j] + startSample; | |||||
| for (int i = 0; i < numSamples; ++i) | |||||
| { | |||||
| const double samp = src[i]; | |||||
| if (samp <= -1.0) | |||||
| dest[i] = std::numeric_limits<int>::min(); | |||||
| else if (samp >= 1.0) | |||||
| dest[i] = std::numeric_limits<int>::max(); | |||||
| else | |||||
| dest[i] = roundToInt (std::numeric_limits<int>::max() * samp); | |||||
| } | |||||
| const double samp = src[i]; | |||||
| if (samp <= -1.0) | |||||
| dest[i] = std::numeric_limits<int>::min(); | |||||
| else if (samp >= 1.0) | |||||
| dest[i] = std::numeric_limits<int>::max(); | |||||
| else | |||||
| dest[i] = roundToInt (std::numeric_limits<int>::max() * samp); | |||||
| } | } | ||||
| } | } | ||||
| writer->write ((const int**) chans, numSamples); | |||||
| } | } | ||||
| writer->write ((const int**) chans.getData(), numSamples); | |||||
| } | } | ||||
| } | } | ||||
| @@ -407,7 +407,7 @@ public: | |||||
| int numSamples, | int numSamples, | ||||
| int readerStartSample, | int readerStartSample, | ||||
| bool useReaderLeftChan, | bool useReaderLeftChan, | ||||
| bool useReaderRightChan) throw(); | |||||
| bool useReaderRightChan); | |||||
| /** Writes a section of this buffer to an audio writer. | /** Writes a section of this buffer to an audio writer. | ||||
| @@ -418,7 +418,7 @@ public: | |||||
| */ | */ | ||||
| void writeToAudioWriter (AudioFormatWriter* writer, | void writeToAudioWriter (AudioFormatWriter* writer, | ||||
| int startSample, | int startSample, | ||||
| int numSamples) const throw(); | |||||
| int numSamples) const; | |||||
| //============================================================================== | //============================================================================== | ||||
| juce_UseDebuggingNewOperator | juce_UseDebuggingNewOperator | ||||
| @@ -53,6 +53,7 @@ PluginListComponent::PluginListComponent (KnownPluginList& listToEdit, | |||||
| setSize (400, 600); | setSize (400, 600); | ||||
| list.addChangeListener (this); | list.addChangeListener (this); | ||||
| changeListenerCallback (0); | |||||
| } | } | ||||
| PluginListComponent::~PluginListComponent() | PluginListComponent::~PluginListComponent() | ||||
| @@ -33,7 +33,7 @@ | |||||
| */ | */ | ||||
| #define JUCE_MAJOR_VERSION 1 | #define JUCE_MAJOR_VERSION 1 | ||||
| #define JUCE_MINOR_VERSION 52 | #define JUCE_MINOR_VERSION 52 | ||||
| #define JUCE_BUILDNUMBER 30 | |||||
| #define JUCE_BUILDNUMBER 31 | |||||
| /** Current Juce version number. | /** Current Juce version number. | ||||
| @@ -282,6 +282,9 @@ public: | |||||
| */ | */ | ||||
| void showEditor(); | void showEditor(); | ||||
| /** Pops up the combo box's list. */ | |||||
| void showPopup(); | |||||
| //============================================================================== | //============================================================================== | ||||
| /** Registers a listener that will be called when the box's content changes. */ | /** Registers a listener that will be called when the box's content changes. */ | ||||
| void addListener (ComboBoxListener* listener) throw(); | void addListener (ComboBoxListener* listener) throw(); | ||||
| @@ -399,8 +402,6 @@ private: | |||||
| ScopedPointer<Label> label; | ScopedPointer<Label> label; | ||||
| String textWhenNothingSelected, noChoicesMessage; | String textWhenNothingSelected, noChoicesMessage; | ||||
| void showPopup(); | |||||
| ItemInfo* getItemForId (int itemId) const throw(); | ItemInfo* getItemForId (int itemId) const throw(); | ||||
| ItemInfo* getItemForIndex (int index) const throw(); | ItemInfo* getItemForIndex (int index) const throw(); | ||||
| @@ -237,14 +237,6 @@ public: | |||||
| return String::empty; | return String::empty; | ||||
| } | } | ||||
| juce_UseDebuggingNewOperator | |||||
| private: | |||||
| TableListBox& owner; | |||||
| int row; | |||||
| bool isSelected, isDragging, selectRowOnMouseUp; | |||||
| BigInteger columnsWithComponents; | |||||
| Component* findChildComponentForColumn (const int columnId) const | Component* findChildComponentForColumn (const int columnId) const | ||||
| { | { | ||||
| for (int i = getNumChildComponents(); --i >= 0;) | for (int i = getNumChildComponents(); --i >= 0;) | ||||
| @@ -258,6 +250,14 @@ private: | |||||
| return 0; | return 0; | ||||
| } | } | ||||
| juce_UseDebuggingNewOperator | |||||
| private: | |||||
| TableListBox& owner; | |||||
| int row; | |||||
| bool isSelected, isDragging, selectRowOnMouseUp; | |||||
| BigInteger columnsWithComponents; | |||||
| TableListRowComp (const TableListRowComp&); | TableListRowComp (const TableListRowComp&); | ||||
| TableListRowComp& operator= (const TableListRowComp&); | TableListRowComp& operator= (const TableListRowComp&); | ||||
| }; | }; | ||||
| @@ -392,6 +392,12 @@ const Rectangle<int> TableListBox::getCellPosition (const int columnId, | |||||
| headerCell.getWidth(), row.getHeight()); | headerCell.getWidth(), row.getHeight()); | ||||
| } | } | ||||
| Component* TableListBox::getCellComponent (int columnId, int rowNumber) const | |||||
| { | |||||
| TableListRowComp* const rowComp = dynamic_cast <TableListRowComp*> (getComponentForRowNumber (rowNumber)); | |||||
| return rowComp != 0 ? rowComp->findChildComponentForColumn (columnId) : 0; | |||||
| } | |||||
| void TableListBox::scrollToEnsureColumnIsOnscreen (const int columnId) | void TableListBox::scrollToEnsureColumnIsOnscreen (const int columnId) | ||||
| { | { | ||||
| ScrollBar* const scrollbar = getHorizontalScrollBar(); | ScrollBar* const scrollbar = getHorizontalScrollBar(); | ||||
| @@ -269,10 +269,16 @@ public: | |||||
| If relativeToComponentTopLeft is false, the co-ords are relative to the | If relativeToComponentTopLeft is false, the co-ords are relative to the | ||||
| top-left of the table's top-left cell. | top-left of the table's top-left cell. | ||||
| */ | */ | ||||
| const Rectangle<int> getCellPosition (int columnId, | |||||
| int rowNumber, | |||||
| const Rectangle<int> getCellPosition (int columnId, int rowNumber, | |||||
| bool relativeToComponentTopLeft) const; | bool relativeToComponentTopLeft) const; | ||||
| /** Returns the component that currently represents a given cell. | |||||
| If the component for this cell is off-screen or if the position is out-of-range, | |||||
| this may return 0. | |||||
| @see getCellPosition | |||||
| */ | |||||
| Component* getCellComponent (int columnId, int rowNumber) const; | |||||
| /** Scrolls horizontally if necessary to make sure that a particular column is visible. | /** Scrolls horizontally if necessary to make sure that a particular column is visible. | ||||
| @see ListBox::scrollToEnsureRowIsOnscreen | @see ListBox::scrollToEnsureRowIsOnscreen | ||||
| @@ -473,10 +473,10 @@ static const Point<float> findQuadraticSubdivisionPoint (double proportion, cons | |||||
| return mid1 + (mid2 - mid1) * proportion; | return mid1 + (mid2 - mid1) * proportion; | ||||
| } | } | ||||
| ValueTree DrawablePath::ValueTreeWrapper::Element::insertPoint (const Point<float>& targetPoint, RelativeCoordinate::NamedCoordinateFinder* nameFinder, UndoManager* undoManager) | |||||
| double DrawablePath::ValueTreeWrapper::Element::findProportionAlongLine (const Point<float>& targetPoint, RelativeCoordinate::NamedCoordinateFinder* nameFinder) const | |||||
| { | { | ||||
| ValueTree newTree; | |||||
| const Identifier i (state.getType()); | const Identifier i (state.getType()); | ||||
| double bestProp = 0; | |||||
| if (i == cubicToElement) | if (i == cubicToElement) | ||||
| { | { | ||||
| @@ -484,7 +484,6 @@ ValueTree DrawablePath::ValueTreeWrapper::Element::insertPoint (const Point<floa | |||||
| const Point<float> points[] = { rp1.resolve (nameFinder), rp2.resolve (nameFinder), rp3.resolve (nameFinder), rp4.resolve (nameFinder) }; | const Point<float> points[] = { rp1.resolve (nameFinder), rp2.resolve (nameFinder), rp3.resolve (nameFinder), rp4.resolve (nameFinder) }; | ||||
| double bestProp = 0; | |||||
| float bestDistance = std::numeric_limits<float>::max(); | float bestDistance = std::numeric_limits<float>::max(); | ||||
| for (int i = 110; --i >= 0;) | for (int i = 110; --i >= 0;) | ||||
| @@ -499,6 +498,48 @@ ValueTree DrawablePath::ValueTreeWrapper::Element::insertPoint (const Point<floa | |||||
| bestDistance = distance; | bestDistance = distance; | ||||
| } | } | ||||
| } | } | ||||
| } | |||||
| else if (i == quadraticToElement) | |||||
| { | |||||
| RelativePoint rp1 (getStartPoint()), rp2 (getControlPoint (0)), rp3 (getEndPoint()); | |||||
| const Point<float> points[] = { rp1.resolve (nameFinder), rp2.resolve (nameFinder), rp3.resolve (nameFinder) }; | |||||
| float bestDistance = std::numeric_limits<float>::max(); | |||||
| for (int i = 110; --i >= 0;) | |||||
| { | |||||
| double prop = i > 10 ? ((i - 10) / 100.0) : (bestProp + ((i - 5) / 1000.0)); | |||||
| const Point<float> centre (findQuadraticSubdivisionPoint (prop, points)); | |||||
| const float distance = centre.getDistanceFrom (targetPoint); | |||||
| if (distance < bestDistance) | |||||
| { | |||||
| bestProp = prop; | |||||
| bestDistance = distance; | |||||
| } | |||||
| } | |||||
| } | |||||
| else if (i == lineToElement) | |||||
| { | |||||
| RelativePoint rp1 (getStartPoint()), rp2 (getEndPoint()); | |||||
| const Line<float> line (rp1.resolve (nameFinder), rp2.resolve (nameFinder)); | |||||
| bestProp = line.findNearestProportionalPositionTo (targetPoint); | |||||
| } | |||||
| return bestProp; | |||||
| } | |||||
| ValueTree DrawablePath::ValueTreeWrapper::Element::insertPoint (const Point<float>& targetPoint, RelativeCoordinate::NamedCoordinateFinder* nameFinder, UndoManager* undoManager) | |||||
| { | |||||
| ValueTree newTree; | |||||
| const Identifier i (state.getType()); | |||||
| if (i == cubicToElement) | |||||
| { | |||||
| double bestProp = findProportionAlongLine (targetPoint, nameFinder); | |||||
| RelativePoint rp1 (getStartPoint()), rp2 (getControlPoint (0)), rp3 (getControlPoint (1)), rp4 (getEndPoint()); | |||||
| const Point<float> points[] = { rp1.resolve (nameFinder), rp2.resolve (nameFinder), rp3.resolve (nameFinder), rp4.resolve (nameFinder) }; | |||||
| const Point<float> mid1 (points[0] + (points[1] - points[0]) * bestProp), | const Point<float> mid1 (points[0] + (points[1] - points[0]) * bestProp), | ||||
| mid2 (points[1] + (points[2] - points[1]) * bestProp), | mid2 (points[1] + (points[2] - points[1]) * bestProp), | ||||
| @@ -523,26 +564,11 @@ ValueTree DrawablePath::ValueTreeWrapper::Element::insertPoint (const Point<floa | |||||
| } | } | ||||
| else if (i == quadraticToElement) | else if (i == quadraticToElement) | ||||
| { | { | ||||
| RelativePoint rp1 (getStartPoint()), rp2 (getControlPoint (0)), rp3 (getEndPoint()); | |||||
| double bestProp = findProportionAlongLine (targetPoint, nameFinder); | |||||
| RelativePoint rp1 (getStartPoint()), rp2 (getControlPoint (0)), rp3 (getEndPoint()); | |||||
| const Point<float> points[] = { rp1.resolve (nameFinder), rp2.resolve (nameFinder), rp3.resolve (nameFinder) }; | const Point<float> points[] = { rp1.resolve (nameFinder), rp2.resolve (nameFinder), rp3.resolve (nameFinder) }; | ||||
| double bestProp = 0; | |||||
| float bestDistance = std::numeric_limits<float>::max(); | |||||
| for (int i = 110; --i >= 0;) | |||||
| { | |||||
| double prop = i > 10 ? ((i - 10) / 100.0) : (bestProp + ((i - 5) / 1000.0)); | |||||
| const Point<float> centre (findQuadraticSubdivisionPoint (prop, points)); | |||||
| const float distance = centre.getDistanceFrom (targetPoint); | |||||
| if (distance < bestDistance) | |||||
| { | |||||
| bestProp = prop; | |||||
| bestDistance = distance; | |||||
| } | |||||
| } | |||||
| const Point<float> mid1 (points[0] + (points[1] - points[0]) * bestProp), | const Point<float> mid1 (points[0] + (points[1] - points[0]) * bestProp), | ||||
| mid2 (points[1] + (points[2] - points[1]) * bestProp); | mid2 (points[1] + (points[2] - points[1]) * bestProp); | ||||
| @@ -176,6 +176,7 @@ public: | |||||
| void convertToPathBreak (UndoManager* undoManager); | void convertToPathBreak (UndoManager* undoManager); | ||||
| ValueTree insertPoint (const Point<float>& targetPoint, RelativeCoordinate::NamedCoordinateFinder* nameFinder, UndoManager* undoManager); | ValueTree insertPoint (const Point<float>& targetPoint, RelativeCoordinate::NamedCoordinateFinder* nameFinder, UndoManager* undoManager); | ||||
| void removePoint (UndoManager* undoManager); | void removePoint (UndoManager* undoManager); | ||||
| double findProportionAlongLine (const Point<float>& targetPoint, RelativeCoordinate::NamedCoordinateFinder* nameFinder) const; | |||||
| static const Identifier mode, startSubPathElement, closeSubPathElement, | static const Identifier mode, startSubPathElement, closeSubPathElement, | ||||
| lineToElement, quadraticToElement, cubicToElement; | lineToElement, quadraticToElement, cubicToElement; | ||||
| @@ -1928,6 +1928,10 @@ const char* String::toUTF8() const | |||||
| mutableThis->text = StringHolder::makeUniqueWithSize (mutableThis->text, currentLen + 1 + utf8BytesNeeded / sizeof (juce_wchar)); | mutableThis->text = StringHolder::makeUniqueWithSize (mutableThis->text, currentLen + 1 + utf8BytesNeeded / sizeof (juce_wchar)); | ||||
| char* const otherCopy = reinterpret_cast <char*> (mutableThis->text + currentLen); | char* const otherCopy = reinterpret_cast <char*> (mutableThis->text + currentLen); | ||||
| #if JUCE_DEBUG // (This just avoids spurious warnings from valgrind about the uninitialised bytes at the end of the buffer..) | |||||
| *(juce_wchar*) (otherCopy + (utf8BytesNeeded & ~(sizeof (juce_wchar) - 1))) = 0; | |||||
| #endif | |||||
| copyToUTF8 (otherCopy, std::numeric_limits<int>::max()); | copyToUTF8 (otherCopy, std::numeric_limits<int>::max()); | ||||
| return otherCopy; | return otherCopy; | ||||