mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-02-05 03:50:07 +00:00
New method for TableListBox and ComboBox, made AudioSampleBuffer::writeToAudioWriter and ResamplingAudioSource handle multiple channels.
This commit is contained in:
parent
fad682c1e1
commit
e502d753d7
14 changed files with 261 additions and 192 deletions
|
|
@ -12214,6 +12214,10 @@ const char* String::toUTF8() const
|
|||
mutableThis->text = StringHolder::makeUniqueWithSize (mutableThis->text, currentLen + 1 + utf8BytesNeeded / sizeof (juce_wchar));
|
||||
|
||||
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());
|
||||
|
||||
return otherCopy;
|
||||
|
|
@ -23702,13 +23706,15 @@ END_JUCE_NAMESPACE
|
|||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
ResamplingAudioSource::ResamplingAudioSource (AudioSource* const inputSource,
|
||||
const bool deleteInputWhenDeleted_)
|
||||
const bool deleteInputWhenDeleted_,
|
||||
const int numChannels_)
|
||||
: input (inputSource),
|
||||
deleteInputWhenDeleted (deleteInputWhenDeleted_),
|
||||
ratio (1.0),
|
||||
lastRatio (1.0),
|
||||
buffer (2, 0),
|
||||
sampsInBuffer (0)
|
||||
buffer (numChannels_, 0),
|
||||
sampsInBuffer (0),
|
||||
numChannels (numChannels_)
|
||||
{
|
||||
jassert (input != 0);
|
||||
}
|
||||
|
|
@ -23734,12 +23740,15 @@ void ResamplingAudioSource::prepareToPlay (int samplesPerBlockExpected,
|
|||
|
||||
input->prepareToPlay (samplesPerBlockExpected, sampleRate);
|
||||
|
||||
buffer.setSize (2, roundToInt (samplesPerBlockExpected * ratio) + 32);
|
||||
buffer.setSize (numChannels, roundToInt (samplesPerBlockExpected * ratio) + 32);
|
||||
buffer.clear();
|
||||
sampsInBuffer = 0;
|
||||
bufferPos = 0;
|
||||
subSampleOffset = 0.0;
|
||||
|
||||
filterStates.calloc (numChannels);
|
||||
srcBuffers.calloc (numChannels);
|
||||
destBuffers.calloc (numChannels);
|
||||
createLowPass (ratio);
|
||||
resetFilters();
|
||||
}
|
||||
|
|
@ -23747,7 +23756,7 @@ void ResamplingAudioSource::prepareToPlay (int samplesPerBlockExpected,
|
|||
void ResamplingAudioSource::releaseResources()
|
||||
{
|
||||
input->releaseResources();
|
||||
buffer.setSize (2, 0);
|
||||
buffer.setSize (numChannels, 0);
|
||||
}
|
||||
|
||||
void ResamplingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& info)
|
||||
|
|
@ -23793,7 +23802,7 @@ void ResamplingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& inf
|
|||
{
|
||||
// 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]);
|
||||
}
|
||||
|
||||
|
|
@ -23801,23 +23810,20 @@ void ResamplingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& inf
|
|||
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;
|
||||
|
||||
for (int m = info.numSamples; --m >= 0;)
|
||||
{
|
||||
const float alpha = (float) subSampleOffset;
|
||||
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;
|
||||
|
||||
|
|
@ -23838,14 +23844,13 @@ void ResamplingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& inf
|
|||
if (ratio < 0.9999)
|
||||
{
|
||||
// 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]);
|
||||
}
|
||||
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
|
||||
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);
|
||||
FilterState& fs = filterStates[i];
|
||||
|
|
@ -23904,7 +23909,7 @@ void ResamplingAudioSource::setFilterCoefficients (double c1, double c2, double
|
|||
|
||||
void ResamplingAudioSource::resetFilters()
|
||||
{
|
||||
zeromem (filterStates, sizeof (filterStates));
|
||||
zeromem (filterStates, sizeof (FilterState) * numChannels);
|
||||
}
|
||||
|
||||
void ResamplingAudioSource::applyFilter (float* samples, int num, FilterState& fs)
|
||||
|
|
@ -26166,7 +26171,7 @@ void AudioSampleBuffer::readFromAudioReader (AudioFormatReader* reader,
|
|||
const int numSamples,
|
||||
const int readerStartSample,
|
||||
const bool useLeftChan,
|
||||
const bool useRightChan) throw()
|
||||
const bool useRightChan)
|
||||
{
|
||||
jassert (reader != 0);
|
||||
jassert (startSample >= 0 && startSample + numSamples <= size);
|
||||
|
|
@ -26223,62 +26228,46 @@ void AudioSampleBuffer::readFromAudioReader (AudioFormatReader* reader,
|
|||
|
||||
void AudioSampleBuffer::writeToAudioWriter (AudioFormatWriter* writer,
|
||||
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)
|
||||
{
|
||||
int* chans [3];
|
||||
HeapBlock<int> tempBuffer;
|
||||
HeapBlock<int*> chans (numChannels + 1);
|
||||
chans [numChannels] = 0;
|
||||
|
||||
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
|
||||
{
|
||||
HeapBlock <int> tempBuffer (numSamples * 2);
|
||||
chans[0] = tempBuffer;
|
||||
tempBuffer.malloc (numSamples * numChannels);
|
||||
|
||||
if (numChannels > 1)
|
||||
chans[1] = chans[0] + numSamples;
|
||||
else
|
||||
chans[1] = 0;
|
||||
|
||||
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;
|
||||
const double samp = src[i];
|
||||
|
||||
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);
|
||||
}
|
||||
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);
|
||||
list.addChangeListener (this);
|
||||
changeListenerCallback (0);
|
||||
}
|
||||
|
||||
PluginListComponent::~PluginListComponent()
|
||||
|
|
@ -50950,14 +50940,6 @@ public:
|
|||
return String::empty;
|
||||
}
|
||||
|
||||
juce_UseDebuggingNewOperator
|
||||
|
||||
private:
|
||||
TableListBox& owner;
|
||||
int row;
|
||||
bool isSelected, isDragging, selectRowOnMouseUp;
|
||||
BigInteger columnsWithComponents;
|
||||
|
||||
Component* findChildComponentForColumn (const int columnId) const
|
||||
{
|
||||
for (int i = getNumChildComponents(); --i >= 0;)
|
||||
|
|
@ -50971,6 +50953,14 @@ private:
|
|||
return 0;
|
||||
}
|
||||
|
||||
juce_UseDebuggingNewOperator
|
||||
|
||||
private:
|
||||
TableListBox& owner;
|
||||
int row;
|
||||
bool isSelected, isDragging, selectRowOnMouseUp;
|
||||
BigInteger columnsWithComponents;
|
||||
|
||||
TableListRowComp (const TableListRowComp&);
|
||||
TableListRowComp& operator= (const TableListRowComp&);
|
||||
};
|
||||
|
|
@ -51102,6 +51092,12 @@ const Rectangle<int> TableListBox::getCellPosition (const int columnId,
|
|||
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)
|
||||
{
|
||||
ScrollBar* const scrollbar = getHorizontalScrollBar();
|
||||
|
|
@ -85775,10 +85771,10 @@ static const Point<float> findQuadraticSubdivisionPoint (double proportion, cons
|
|||
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());
|
||||
double bestProp = 0;
|
||||
|
||||
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) };
|
||||
|
||||
double bestProp = 0;
|
||||
float bestDistance = std::numeric_limits<float>::max();
|
||||
|
||||
for (int i = 110; --i >= 0;)
|
||||
|
|
@ -85801,6 +85796,48 @@ ValueTree DrawablePath::ValueTreeWrapper::Element::insertPoint (const Point<floa
|
|||
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),
|
||||
mid2 (points[1] + (points[2] - points[1]) * bestProp),
|
||||
|
|
@ -85825,26 +85862,11 @@ ValueTree DrawablePath::ValueTreeWrapper::Element::insertPoint (const Point<floa
|
|||
}
|
||||
else if (i == quadraticToElement)
|
||||
{
|
||||
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) };
|
||||
|
||||
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),
|
||||
mid2 (points[1] + (points[2] - points[1]) * bestProp);
|
||||
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@
|
|||
*/
|
||||
#define JUCE_MAJOR_VERSION 1
|
||||
#define JUCE_MINOR_VERSION 52
|
||||
#define JUCE_BUILDNUMBER 30
|
||||
#define JUCE_BUILDNUMBER 31
|
||||
|
||||
/** Current Juce version number.
|
||||
|
||||
|
|
@ -29669,7 +29669,7 @@ public:
|
|||
int numSamples,
|
||||
int readerStartSample,
|
||||
bool useReaderLeftChan,
|
||||
bool useReaderRightChan) throw();
|
||||
bool useReaderRightChan);
|
||||
|
||||
/** Writes a section of this buffer to an audio writer.
|
||||
|
||||
|
|
@ -29680,7 +29680,7 @@ public:
|
|||
*/
|
||||
void writeToAudioWriter (AudioFormatWriter* writer,
|
||||
int startSample,
|
||||
int numSamples) const throw();
|
||||
int numSamples) const;
|
||||
|
||||
juce_UseDebuggingNewOperator
|
||||
|
||||
|
|
@ -31739,9 +31739,11 @@ public:
|
|||
@param inputSource the input source to read from
|
||||
@param deleteInputWhenDeleted if true, the input source will be deleted when
|
||||
this object is deleted
|
||||
@param numChannels the number of channels to process
|
||||
*/
|
||||
ResamplingAudioSource (AudioSource* const inputSource,
|
||||
const bool deleteInputWhenDeleted);
|
||||
const bool deleteInputWhenDeleted,
|
||||
int numChannels = 2);
|
||||
|
||||
/** Destructor. */
|
||||
~ResamplingAudioSource();
|
||||
|
|
@ -31777,6 +31779,8 @@ private:
|
|||
double subSampleOffset;
|
||||
double coefficients[6];
|
||||
CriticalSection ratioLock;
|
||||
const int numChannels;
|
||||
HeapBlock<float*> destBuffers, srcBuffers;
|
||||
|
||||
void setFilterCoefficients (double c1, double c2, double c3, double c4, double c5, double c6);
|
||||
void createLowPass (const double proportionalRate);
|
||||
|
|
@ -31786,7 +31790,7 @@ private:
|
|||
double x1, x2, y1, y2;
|
||||
};
|
||||
|
||||
FilterState filterStates[2];
|
||||
HeapBlock<FilterState> filterStates;
|
||||
void resetFilters();
|
||||
|
||||
void applyFilter (float* samples, int num, FilterState& fs);
|
||||
|
|
@ -36664,6 +36668,9 @@ public:
|
|||
*/
|
||||
void showEditor();
|
||||
|
||||
/** Pops up the combo box's list. */
|
||||
void showPopup();
|
||||
|
||||
/** Registers a listener that will be called when the box's content changes. */
|
||||
void addListener (ComboBoxListener* listener) throw();
|
||||
|
||||
|
|
@ -36773,8 +36780,6 @@ private:
|
|||
ScopedPointer<Label> label;
|
||||
String textWhenNothingSelected, noChoicesMessage;
|
||||
|
||||
void showPopup();
|
||||
|
||||
ItemInfo* getItemForId (int itemId) const throw();
|
||||
ItemInfo* getItemForIndex (int index) const throw();
|
||||
|
||||
|
|
@ -47165,10 +47170,16 @@ public:
|
|||
If relativeToComponentTopLeft is false, the co-ords are relative to the
|
||||
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;
|
||||
|
||||
/** 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.
|
||||
|
||||
@see ListBox::scrollToEnsureRowIsOnscreen
|
||||
|
|
@ -59405,6 +59416,7 @@ public:
|
|||
void convertToPathBreak (UndoManager* undoManager);
|
||||
ValueTree insertPoint (const Point<float>& targetPoint, RelativeCoordinate::NamedCoordinateFinder* nameFinder, UndoManager* undoManager);
|
||||
void removePoint (UndoManager* undoManager);
|
||||
double findProportionAlongLine (const Point<float>& targetPoint, RelativeCoordinate::NamedCoordinateFinder* nameFinder) const;
|
||||
|
||||
static const Identifier mode, startSubPathElement, closeSubPathElement,
|
||||
lineToElement, quadraticToElement, cubicToElement;
|
||||
|
|
|
|||
|
|
@ -33,13 +33,15 @@ BEGIN_JUCE_NAMESPACE
|
|||
|
||||
//==============================================================================
|
||||
ResamplingAudioSource::ResamplingAudioSource (AudioSource* const inputSource,
|
||||
const bool deleteInputWhenDeleted_)
|
||||
const bool deleteInputWhenDeleted_,
|
||||
const int numChannels_)
|
||||
: input (inputSource),
|
||||
deleteInputWhenDeleted (deleteInputWhenDeleted_),
|
||||
ratio (1.0),
|
||||
lastRatio (1.0),
|
||||
buffer (2, 0),
|
||||
sampsInBuffer (0)
|
||||
buffer (numChannels_, 0),
|
||||
sampsInBuffer (0),
|
||||
numChannels (numChannels_)
|
||||
{
|
||||
jassert (input != 0);
|
||||
}
|
||||
|
|
@ -65,12 +67,15 @@ void ResamplingAudioSource::prepareToPlay (int samplesPerBlockExpected,
|
|||
|
||||
input->prepareToPlay (samplesPerBlockExpected, sampleRate);
|
||||
|
||||
buffer.setSize (2, roundToInt (samplesPerBlockExpected * ratio) + 32);
|
||||
buffer.setSize (numChannels, roundToInt (samplesPerBlockExpected * ratio) + 32);
|
||||
buffer.clear();
|
||||
sampsInBuffer = 0;
|
||||
bufferPos = 0;
|
||||
subSampleOffset = 0.0;
|
||||
|
||||
filterStates.calloc (numChannels);
|
||||
srcBuffers.calloc (numChannels);
|
||||
destBuffers.calloc (numChannels);
|
||||
createLowPass (ratio);
|
||||
resetFilters();
|
||||
}
|
||||
|
|
@ -78,7 +83,7 @@ void ResamplingAudioSource::prepareToPlay (int samplesPerBlockExpected,
|
|||
void ResamplingAudioSource::releaseResources()
|
||||
{
|
||||
input->releaseResources();
|
||||
buffer.setSize (2, 0);
|
||||
buffer.setSize (numChannels, 0);
|
||||
}
|
||||
|
||||
void ResamplingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& info)
|
||||
|
|
@ -124,7 +129,7 @@ void ResamplingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& inf
|
|||
{
|
||||
// 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]);
|
||||
}
|
||||
|
||||
|
|
@ -132,23 +137,20 @@ void ResamplingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& inf
|
|||
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;
|
||||
|
||||
for (int m = info.numSamples; --m >= 0;)
|
||||
{
|
||||
const float alpha = (float) subSampleOffset;
|
||||
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;
|
||||
|
||||
|
|
@ -169,14 +171,13 @@ void ResamplingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& inf
|
|||
if (ratio < 0.9999)
|
||||
{
|
||||
// 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]);
|
||||
}
|
||||
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
|
||||
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);
|
||||
FilterState& fs = filterStates[i];
|
||||
|
|
@ -235,7 +236,7 @@ void ResamplingAudioSource::setFilterCoefficients (double c1, double c2, double
|
|||
|
||||
void ResamplingAudioSource::resetFilters()
|
||||
{
|
||||
zeromem (filterStates, sizeof (filterStates));
|
||||
zeromem (filterStates, sizeof (FilterState) * numChannels);
|
||||
}
|
||||
|
||||
void ResamplingAudioSource::applyFilter (float* samples, int num, FilterState& fs)
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
#include "juce_AudioSource.h"
|
||||
#include "../../threads/juce_CriticalSection.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
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 deleteInputWhenDeleted if true, the input source will be deleted when
|
||||
this object is deleted
|
||||
@param numChannels the number of channels to process
|
||||
*/
|
||||
ResamplingAudioSource (AudioSource* const inputSource,
|
||||
const bool deleteInputWhenDeleted);
|
||||
const bool deleteInputWhenDeleted,
|
||||
int numChannels = 2);
|
||||
|
||||
/** Destructor. */
|
||||
~ResamplingAudioSource();
|
||||
|
|
@ -84,6 +87,8 @@ private:
|
|||
double subSampleOffset;
|
||||
double coefficients[6];
|
||||
CriticalSection ratioLock;
|
||||
const int numChannels;
|
||||
HeapBlock<float*> destBuffers, srcBuffers;
|
||||
|
||||
void setFilterCoefficients (double c1, double c2, double c3, double c4, double c5, double c6);
|
||||
void createLowPass (const double proportionalRate);
|
||||
|
|
@ -93,7 +98,7 @@ private:
|
|||
double x1, x2, y1, y2;
|
||||
};
|
||||
|
||||
FilterState filterStates[2];
|
||||
HeapBlock<FilterState> filterStates;
|
||||
void resetFilters();
|
||||
|
||||
void applyFilter (float* samples, int num, FilterState& fs);
|
||||
|
|
|
|||
|
|
@ -574,7 +574,7 @@ void AudioSampleBuffer::readFromAudioReader (AudioFormatReader* reader,
|
|||
const int numSamples,
|
||||
const int readerStartSample,
|
||||
const bool useLeftChan,
|
||||
const bool useRightChan) throw()
|
||||
const bool useRightChan)
|
||||
{
|
||||
jassert (reader != 0);
|
||||
jassert (startSample >= 0 && startSample + numSamples <= size);
|
||||
|
|
@ -631,62 +631,46 @@ void AudioSampleBuffer::readFromAudioReader (AudioFormatReader* reader,
|
|||
|
||||
void AudioSampleBuffer::writeToAudioWriter (AudioFormatWriter* writer,
|
||||
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)
|
||||
{
|
||||
int* chans [3];
|
||||
HeapBlock<int> tempBuffer;
|
||||
HeapBlock<int*> chans (numChannels + 1);
|
||||
chans [numChannels] = 0;
|
||||
|
||||
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
|
||||
{
|
||||
HeapBlock <int> tempBuffer (numSamples * 2);
|
||||
chans[0] = tempBuffer;
|
||||
tempBuffer.malloc (numSamples * numChannels);
|
||||
|
||||
if (numChannels > 1)
|
||||
chans[1] = chans[0] + numSamples;
|
||||
else
|
||||
chans[1] = 0;
|
||||
|
||||
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;
|
||||
const double samp = src[i];
|
||||
|
||||
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);
|
||||
}
|
||||
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 readerStartSample,
|
||||
bool useReaderLeftChan,
|
||||
bool useReaderRightChan) throw();
|
||||
bool useReaderRightChan);
|
||||
|
||||
/** Writes a section of this buffer to an audio writer.
|
||||
|
||||
|
|
@ -418,7 +418,7 @@ public:
|
|||
*/
|
||||
void writeToAudioWriter (AudioFormatWriter* writer,
|
||||
int startSample,
|
||||
int numSamples) const throw();
|
||||
int numSamples) const;
|
||||
|
||||
//==============================================================================
|
||||
juce_UseDebuggingNewOperator
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ PluginListComponent::PluginListComponent (KnownPluginList& listToEdit,
|
|||
|
||||
setSize (400, 600);
|
||||
list.addChangeListener (this);
|
||||
changeListenerCallback (0);
|
||||
}
|
||||
|
||||
PluginListComponent::~PluginListComponent()
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@
|
|||
*/
|
||||
#define JUCE_MAJOR_VERSION 1
|
||||
#define JUCE_MINOR_VERSION 52
|
||||
#define JUCE_BUILDNUMBER 30
|
||||
#define JUCE_BUILDNUMBER 31
|
||||
|
||||
/** Current Juce version number.
|
||||
|
||||
|
|
|
|||
|
|
@ -282,6 +282,9 @@ public:
|
|||
*/
|
||||
void showEditor();
|
||||
|
||||
/** Pops up the combo box's list. */
|
||||
void showPopup();
|
||||
|
||||
//==============================================================================
|
||||
/** Registers a listener that will be called when the box's content changes. */
|
||||
void addListener (ComboBoxListener* listener) throw();
|
||||
|
|
@ -399,8 +402,6 @@ private:
|
|||
ScopedPointer<Label> label;
|
||||
String textWhenNothingSelected, noChoicesMessage;
|
||||
|
||||
void showPopup();
|
||||
|
||||
ItemInfo* getItemForId (int itemId) const throw();
|
||||
ItemInfo* getItemForIndex (int index) const throw();
|
||||
|
||||
|
|
|
|||
|
|
@ -237,14 +237,6 @@ public:
|
|||
return String::empty;
|
||||
}
|
||||
|
||||
juce_UseDebuggingNewOperator
|
||||
|
||||
private:
|
||||
TableListBox& owner;
|
||||
int row;
|
||||
bool isSelected, isDragging, selectRowOnMouseUp;
|
||||
BigInteger columnsWithComponents;
|
||||
|
||||
Component* findChildComponentForColumn (const int columnId) const
|
||||
{
|
||||
for (int i = getNumChildComponents(); --i >= 0;)
|
||||
|
|
@ -258,6 +250,14 @@ private:
|
|||
return 0;
|
||||
}
|
||||
|
||||
juce_UseDebuggingNewOperator
|
||||
|
||||
private:
|
||||
TableListBox& owner;
|
||||
int row;
|
||||
bool isSelected, isDragging, selectRowOnMouseUp;
|
||||
BigInteger columnsWithComponents;
|
||||
|
||||
TableListRowComp (const TableListRowComp&);
|
||||
TableListRowComp& operator= (const TableListRowComp&);
|
||||
};
|
||||
|
|
@ -392,6 +392,12 @@ const Rectangle<int> TableListBox::getCellPosition (const int columnId,
|
|||
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)
|
||||
{
|
||||
ScrollBar* const scrollbar = getHorizontalScrollBar();
|
||||
|
|
|
|||
|
|
@ -269,10 +269,16 @@ public:
|
|||
If relativeToComponentTopLeft is false, the co-ords are relative to the
|
||||
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;
|
||||
|
||||
/** 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.
|
||||
|
||||
@see ListBox::scrollToEnsureRowIsOnscreen
|
||||
|
|
|
|||
|
|
@ -473,10 +473,10 @@ static const Point<float> findQuadraticSubdivisionPoint (double proportion, cons
|
|||
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());
|
||||
double bestProp = 0;
|
||||
|
||||
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) };
|
||||
|
||||
double bestProp = 0;
|
||||
float bestDistance = std::numeric_limits<float>::max();
|
||||
|
||||
for (int i = 110; --i >= 0;)
|
||||
|
|
@ -499,6 +498,48 @@ ValueTree DrawablePath::ValueTreeWrapper::Element::insertPoint (const Point<floa
|
|||
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),
|
||||
mid2 (points[1] + (points[2] - points[1]) * bestProp),
|
||||
|
|
@ -523,26 +564,11 @@ ValueTree DrawablePath::ValueTreeWrapper::Element::insertPoint (const Point<floa
|
|||
}
|
||||
else if (i == quadraticToElement)
|
||||
{
|
||||
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) };
|
||||
|
||||
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),
|
||||
mid2 (points[1] + (points[2] - points[1]) * bestProp);
|
||||
|
||||
|
|
|
|||
|
|
@ -176,6 +176,7 @@ public:
|
|||
void convertToPathBreak (UndoManager* undoManager);
|
||||
ValueTree insertPoint (const Point<float>& targetPoint, RelativeCoordinate::NamedCoordinateFinder* nameFinder, UndoManager* undoManager);
|
||||
void removePoint (UndoManager* undoManager);
|
||||
double findProportionAlongLine (const Point<float>& targetPoint, RelativeCoordinate::NamedCoordinateFinder* nameFinder) const;
|
||||
|
||||
static const Identifier mode, startSubPathElement, closeSubPathElement,
|
||||
lineToElement, quadraticToElement, cubicToElement;
|
||||
|
|
|
|||
|
|
@ -1928,6 +1928,10 @@ const char* String::toUTF8() const
|
|||
mutableThis->text = StringHolder::makeUniqueWithSize (mutableThis->text, currentLen + 1 + utf8BytesNeeded / sizeof (juce_wchar));
|
||||
|
||||
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());
|
||||
|
||||
return otherCopy;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue