1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-06-15 15:49:36 +02:00

Merge pull request #923 from Paul-Licameli/Review-of-dithering

Review of dithering
This commit is contained in:
Paul Licameli 2021-05-27 11:40:28 -04:00 committed by GitHub
commit 428506ea2f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 305 additions and 192 deletions

View File

@ -3622,11 +3622,9 @@ static void DoSoftwarePlaythrough(const void *inputBuffer,
{ {
for (unsigned int i=0; i < inputChannels; i++) { for (unsigned int i=0; i < inputChannels; i++) {
samplePtr inputPtr = ((samplePtr)inputBuffer) + (i * SAMPLE_SIZE(inputFormat)); samplePtr inputPtr = ((samplePtr)inputBuffer) + (i * SAMPLE_SIZE(inputFormat));
samplePtr outputPtr = ((samplePtr)outputBuffer) + (i * SAMPLE_SIZE(floatSample));
CopySamples(inputPtr, inputFormat, SamplesToFloats(inputPtr, inputFormat,
(samplePtr)outputPtr, floatSample, outputBuffer + i, len, inputChannels, 2);
len, true, inputChannels, 2);
} }
// One mono input channel goes to both output channels... // One mono input channel goes to both output channels...
@ -4411,9 +4409,8 @@ int AudioIoCallback::AudioCallback(const void *inputBuffer, void *outputBuffer,
inputSamples = (float *) inputBuffer; inputSamples = (float *) inputBuffer;
} }
else { else {
CopySamples((samplePtr)inputBuffer, mCaptureFormat, SamplesToFloats(reinterpret_cast<constSamplePtr>(inputBuffer),
(samplePtr)tempFloats, floatSample, mCaptureFormat, tempFloats, framesPerBuffer * numCaptureChannels);
framesPerBuffer * numCaptureChannels);
inputSamples = tempFloats; inputSamples = tempFloats;
} }

View File

@ -602,7 +602,7 @@ void FrequencyPlotDialog::GetAudio()
mDataLen = dataLen.as_size_t(); mDataLen = dataLen.as_size_t();
mData = Floats{ mDataLen }; mData = Floats{ mDataLen };
// Don't allow throw for bad reads // Don't allow throw for bad reads
track->Get((samplePtr)mData.get(), floatSample, start, mDataLen, track->GetFloats(mData.get(), start, mDataLen,
fillZero, false); fillZero, false);
} }
else { else {
@ -617,7 +617,7 @@ void FrequencyPlotDialog::GetAudio()
auto start = track->TimeToLongSamples(selectedRegion.t0()); auto start = track->TimeToLongSamples(selectedRegion.t0());
Floats buffer2{ mDataLen }; Floats buffer2{ mDataLen };
// Again, stop exceptions // Again, stop exceptions
track->Get((samplePtr)buffer2.get(), floatSample, start, mDataLen, track->GetFloats(buffer2.get(), start, mDataLen,
fillZero, false); fillZero, false);
for (size_t i = 0; i < mDataLen; i++) for (size_t i = 0; i < mDataLen; i++)
mData[i] += buffer2[i]; mData[i] += buffer2[i];

View File

@ -460,7 +460,8 @@ size_t Mixer::MixVariableRates(int *channelFlags, WaveTrackCache &cache,
// Nothing to do if past end of play interval // Nothing to do if past end of play interval
if (getLen > 0) { if (getLen > 0) {
if (backwards) { if (backwards) {
auto results = cache.Get(floatSample, *pos - (getLen - 1), getLen, mMayThrow); auto results =
cache.GetFloats(*pos - (getLen - 1), getLen, mMayThrow);
if (results) if (results)
memcpy(&queue[*queueLen], results, sizeof(float) * getLen); memcpy(&queue[*queueLen], results, sizeof(float) * getLen);
else else
@ -472,7 +473,7 @@ size_t Mixer::MixVariableRates(int *channelFlags, WaveTrackCache &cache,
*pos -= getLen; *pos -= getLen;
} }
else { else {
auto results = cache.Get(floatSample, *pos, getLen, mMayThrow); auto results = cache.GetFloats(*pos, getLen, mMayThrow);
if (results) if (results)
memcpy(&queue[*queueLen], results, sizeof(float) * getLen); memcpy(&queue[*queueLen], results, sizeof(float) * getLen);
else else
@ -589,7 +590,7 @@ size_t Mixer::MixSameRate(int *channelFlags, WaveTrackCache &cache,
); );
if (backwards) { if (backwards) {
auto results = cache.Get(floatSample, *pos - (slen - 1), slen, mMayThrow); auto results = cache.GetFloats(*pos - (slen - 1), slen, mMayThrow);
if (results) if (results)
memcpy(mFloatBuffer.get(), results, sizeof(float) * slen); memcpy(mFloatBuffer.get(), results, sizeof(float) * slen);
else else
@ -602,7 +603,7 @@ size_t Mixer::MixSameRate(int *channelFlags, WaveTrackCache &cache,
*pos -= slen; *pos -= slen;
} }
else { else {
auto results = cache.Get(floatSample, *pos, slen, mMayThrow); auto results = cache.GetFloats(*pos, slen, mMayThrow);
if (results) if (results)
memcpy(mFloatBuffer.get(), results, sizeof(float) * slen); memcpy(mFloatBuffer.get(), results, sizeof(float) * slen);
else else
@ -691,7 +692,7 @@ size_t Mixer::Process(size_t maxToProcess)
mBuffer[0].ptr() + (c * SAMPLE_SIZE(mFormat)), mBuffer[0].ptr() + (c * SAMPLE_SIZE(mFormat)),
mFormat, mFormat,
maxOut, maxOut,
mHighQuality, mHighQuality ? gHighQualityDither : gLowQualityDither,
mNumChannels, mNumChannels,
mNumChannels); mNumChannels);
} }
@ -703,7 +704,7 @@ size_t Mixer::Process(size_t maxToProcess)
mBuffer[c].ptr(), mBuffer[c].ptr(),
mFormat, mFormat,
maxOut, maxOut,
mHighQuality); mHighQuality ? gHighQualityDither : gLowQualityDither);
} }
} }
// MB: this doesn't take warping into account, replaced with code based on mSamplePos // MB: this doesn't take warping into account, replaced with code based on mSamplePos

View File

@ -610,8 +610,8 @@ void MixerTrackCluster::UpdateMeter(const double t0, const double t1)
Floats tempFloatsArray{ nFrames }; Floats tempFloatsArray{ nFrames };
decltype(tempFloatsArray) meterFloatsArray; decltype(tempFloatsArray) meterFloatsArray;
// Don't throw on read error in this drawing update routine // Don't throw on read error in this drawing update routine
bool bSuccess = pTrack->Get((samplePtr)tempFloatsArray.get(), bool bSuccess = pTrack->GetFloats(tempFloatsArray.get(),
floatSample, startSample, nFrames, fillZero, false); startSample, nFrames, fillZero, false);
if (bSuccess) if (bSuccess)
{ {
// We always pass a stereo sample array to the meter, as it shows 2 channels. // We always pass a stereo sample array to the meter, as it shows 2 channels.
@ -625,8 +625,8 @@ void MixerTrackCluster::UpdateMeter(const double t0, const double t1)
if (GetRight()) if (GetRight())
// Again, don't throw // Again, don't throw
bSuccess = GetRight()->Get((samplePtr)tempFloatsArray.get(), bSuccess = GetRight()->GetFloats(tempFloatsArray.get(),
floatSample, startSample, nFrames, fillZero, false); startSample, nFrames, fillZero, false);
if (bSuccess) if (bSuccess)
// Interleave right channel, or duplicate same signal for "right" channel in mono case. // Interleave right channel, or duplicate same signal for "right" channel in mono case.

View File

@ -85,7 +85,7 @@ size_t RingBuffer::Put(samplePtr buffer, sampleFormat format,
CopySamples(src, format, CopySamples(src, format,
mBuffer.ptr() + pos * SAMPLE_SIZE(mFormat), mFormat, mBuffer.ptr() + pos * SAMPLE_SIZE(mFormat), mFormat,
block); block, DitherType::none);
src += block * SAMPLE_SIZE(format); src += block * SAMPLE_SIZE(format);
pos = (pos + block) % mBufferSize; pos = (pos + block) % mBufferSize;
@ -167,7 +167,7 @@ size_t RingBuffer::Get(samplePtr buffer, sampleFormat format,
CopySamples(mBuffer.ptr() + start * SAMPLE_SIZE(mFormat), mFormat, CopySamples(mBuffer.ptr() + start * SAMPLE_SIZE(mFormat), mFormat,
dest, format, dest, format,
block); block, DitherType::none);
dest += block * SAMPLE_SIZE(format); dest += block * SAMPLE_SIZE(format);
start = (start + block) % mBufferSize; start = (start + block) % mBufferSize;

View File

@ -24,6 +24,7 @@ class RingBuffer {
// //
size_t AvailForPut(); size_t AvailForPut();
//! Does not apply dithering
size_t Put(samplePtr buffer, sampleFormat format, size_t samples, size_t Put(samplePtr buffer, sampleFormat format, size_t samples,
// optional number of trailing zeroes // optional number of trailing zeroes
size_t padding = 0); size_t padding = 0);
@ -34,6 +35,7 @@ class RingBuffer {
// //
size_t AvailForGet(); size_t AvailForGet();
//! Does not apply dithering
size_t Get(samplePtr buffer, sampleFormat format, size_t samples); size_t Get(samplePtr buffer, sampleFormat format, size_t samples);
size_t Discard(size_t samples); size_t Discard(size_t samples);

View File

@ -43,11 +43,10 @@
#include <string.h> #include <string.h>
#include "Prefs.h" #include "Prefs.h"
#include "Dither.h"
#include "Internat.h" #include "Internat.h"
static DitherType gLowQualityDither = DitherType::none; DitherType gLowQualityDither = DitherType::none;
static DitherType gHighQualityDither = DitherType::none; DitherType gHighQualityDither = DitherType::shaped;
static Dither gDitherAlgorithm; static Dither gDitherAlgorithm;
void InitDitherers() void InitDitherers()
@ -99,25 +98,22 @@ void ReverseSamples(samplePtr dst, sampleFormat format,
} }
} }
void CopySamples(constSamplePtr src, sampleFormat srcFormat, void SamplesToFloats(constSamplePtr src, sampleFormat srcFormat,
samplePtr dst, sampleFormat dstFormat, float *dst, size_t len, size_t srcStride, size_t dstStride)
unsigned int len,
bool highQuality, /* = true */
unsigned int srcStride /* = 1 */,
unsigned int dstStride /* = 1 */)
{ {
gDitherAlgorithm.Apply( CopySamples( src, srcFormat,
highQuality ? gHighQualityDither : gLowQualityDither, reinterpret_cast<samplePtr>(dst), floatSample, len,
src, srcFormat, dst, dstFormat, len, srcStride, dstStride); DitherType::none,
srcStride, dstStride);
} }
void CopySamplesNoDither(samplePtr src, sampleFormat srcFormat, void CopySamples(constSamplePtr src, sampleFormat srcFormat,
samplePtr dst, sampleFormat dstFormat, samplePtr dst, sampleFormat dstFormat, size_t len,
unsigned int len, DitherType ditherType, /* = gHighQualityDither */
unsigned int srcStride /* = 1 */, unsigned int srcStride /* = 1 */,
unsigned int dstStride /* = 1 */) unsigned int dstStride /* = 1 */)
{ {
gDitherAlgorithm.Apply( gDitherAlgorithm.Apply(
DitherType::none, ditherType,
src, srcFormat, dst, dstFormat, len, srcStride, dstStride); src, srcFormat, dst, dstFormat, len, srcStride, dstStride);
} }

View File

@ -2,7 +2,7 @@
Audacity: A Digital Audio Editor Audacity: A Digital Audio Editor
SampleFormat.h @file SampleFormat.h
Dominic Mazzoni Dominic Mazzoni
@ -17,11 +17,15 @@
#include <wx/defs.h> #include <wx/defs.h>
#include "audacity/Types.h" #include "audacity/Types.h"
#include "Dither.h"
// //
// Definitions / Meta-Information // Definitions / Meta-Information
// //
//! These global variables are assigned at application startup or after change of preferences.
extern AUDACITY_DLL_API DitherType gLowQualityDither, gHighQualityDither;
#if 0 #if 0
// Moved to audacity/types.h // Moved to audacity/types.h
typedef enum { typedef enum {
@ -125,18 +129,29 @@ private:
// //
AUDACITY_DLL_API AUDACITY_DLL_API
void CopySamples(constSamplePtr src, sampleFormat srcFormat, //! Copy samples from any format into the widest format, which is 32 bit float, with no dithering
samplePtr dst, sampleFormat dstFormat, /*!
unsigned int len, bool highQuality=true, @param src address of source samples
unsigned int srcStride=1, @param srcFormat format of source samples, determines sizeof each one
unsigned int dstStride=1); @param dst address of floating-point numbers
@param len count of samples to copy
@param srcStride how many samples to advance src after copying each one
@param dstString how many samples to advance dst after copying each one
*/
void SamplesToFloats(constSamplePtr src, sampleFormat srcFormat,
float *dst, size_t len, size_t srcStride = 1, size_t dstStride = 1);
AUDACITY_DLL_API AUDACITY_DLL_API
void CopySamplesNoDither(samplePtr src, sampleFormat srcFormat, //! Copy samples from any format to any other format; apply dithering only if narrowing the format
samplePtr dst, sampleFormat dstFormat, /*!
unsigned int len, @copydetails SamplesToFloats()
unsigned int srcStride=1, @param dstFormat format of destination samples, determines sizeof each one
unsigned int dstStride=1); @param ditherType choice of dithering algorithm to use if narrowing the format
*/
void CopySamples(constSamplePtr src, sampleFormat srcFormat,
samplePtr dst, sampleFormat dstFormat, size_t len,
DitherType ditherType = gHighQualityDither, //!< default is loaded from a global variable
unsigned int srcStride=1, unsigned int dstStride=1);
AUDACITY_DLL_API AUDACITY_DLL_API
void ClearSamples(samplePtr buffer, sampleFormat format, void ClearSamples(samplePtr buffer, sampleFormat format,

View File

@ -593,6 +593,42 @@ size_t SqliteSampleBlock::GetBlob(void *dest,
srcoffset += 0; srcoffset += 0;
} }
/*
Will dithering happen in CopySamples? Answering this as of 3.0.3 by
examining all uses.
As this function is called from GetSummary, no, because destination format
is float.
There is only one other call to this function, in DoGetSamples. At one
call to that function, in DoGetMinMaxRMS, again format is float always.
There is only one other call to DoGetSamples, in SampleBlock::GetSamples().
In one call to that function, in WaveformView.cpp, again format is float.
That leaves two calls in Sequence.cpp. One of those can be proved to be
used only in copy and paste operations, always supplying the same sample
format as the samples were stored in, therefore no dither.
That leaves uses of Sequence::Read(). There are uses of Read() in internal
operations also easily shown to use only the saved format, and
GetWaveDisplay() always reads as float.
The remaining use of Sequence::Read() is in Sequence::Get(). That is used
by WaveClip::Resample(), always fetching float. It is also used in
WaveClip::GetSamples().
There is only one use of that function not always fetching float, in
WaveTrack::Get().
It can be shown that the only paths to WaveTrack::Get() not specifying
floatSample are in Benchmark, which is only a diagnostic test, and there
the sample format is the same as what the track was constructed with.
Therefore, no dithering even there!
*/
wxASSERT(destformat == floatSample || destformat == srcformat);
CopySamples(src + srcoffset, CopySamples(src + srcoffset,
srcformat, srcformat,
(samplePtr) dest, (samplePtr) dest,
@ -806,11 +842,8 @@ void SqliteSampleBlock::CalcSummary(Sizes sizes)
else else
{ {
samplebuffer.reinit((unsigned) mSampleCount); samplebuffer.reinit((unsigned) mSampleCount);
CopySamples(mSamples.get(), SamplesToFloats(mSamples.get(), mSampleFormat,
mSampleFormat, samplebuffer.get(), mSampleCount);
(samplePtr) samplebuffer.get(),
floatSample,
mSampleCount);
samples = samplebuffer.get(); samples = samplebuffer.get();
} }

View File

@ -149,7 +149,7 @@ sampleCount VoiceKey::OnForward (
//To speed things up, create a local buffer to store things in, to avoid the costly t.Get(); //To speed things up, create a local buffer to store things in, to avoid the costly t.Get();
//Only go through the first SignalWindowSizeInt samples, and choose the first that trips the key. //Only go through the first SignalWindowSizeInt samples, and choose the first that trips the key.
Floats buffer{ remaining }; Floats buffer{ remaining };
t.Get((samplePtr)buffer.get(), floatSample, t.GetFloats(buffer.get(),
lastsubthresholdsample, remaining); lastsubthresholdsample, remaining);
@ -299,7 +299,7 @@ sampleCount VoiceKey::OnBackward (
//To speed things up, create a local buffer to store things in, to avoid the costly t.Get(); //To speed things up, create a local buffer to store things in, to avoid the costly t.Get();
//Only go through the first mSilentWindowSizeInt samples, and choose the first that trips the key. //Only go through the first mSilentWindowSizeInt samples, and choose the first that trips the key.
Floats buffer{ remaining }; Floats buffer{ remaining };
t.Get((samplePtr)buffer.get(), floatSample, t.GetFloats(buffer.get(),
lastsubthresholdsample - remaining, remaining); lastsubthresholdsample - remaining, remaining);
//Initialize these trend markers atrend and ztrend. They keep track of the //Initialize these trend markers atrend and ztrend. They keep track of the
@ -442,7 +442,7 @@ sampleCount VoiceKey::OffForward (
//To speed things up, create a local buffer to store things in, to avoid the costly t.Get(); //To speed things up, create a local buffer to store things in, to avoid the costly t.Get();
//Only go through the first SilentWindowSizeInt samples, and choose the first that trips the key. //Only go through the first SilentWindowSizeInt samples, and choose the first that trips the key.
Floats buffer{ remaining }; Floats buffer{ remaining };
t.Get((samplePtr)buffer.get(), floatSample, t.GetFloats(buffer.get(),
lastsubthresholdsample, remaining); lastsubthresholdsample, remaining);
//Initialize these trend markers atrend and ztrend. They keep track of the //Initialize these trend markers atrend and ztrend. They keep track of the
@ -579,7 +579,7 @@ sampleCount VoiceKey::OffBackward (
//To speed things up, create a local buffer to store things in, to avoid the costly t.Get(); //To speed things up, create a local buffer to store things in, to avoid the costly t.Get();
//Only go through the first SilentWindowSizeInt samples, and choose the first that trips the key. //Only go through the first SilentWindowSizeInt samples, and choose the first that trips the key.
Floats buffer{ remaining }; Floats buffer{ remaining };
t.Get((samplePtr)buffer.get(), floatSample, t.GetFloats(buffer.get(),
lastsubthresholdsample - remaining, remaining); lastsubthresholdsample - remaining, remaining);
//Initialize these trend markers atrend and ztrend. They keep track of the //Initialize these trend markers atrend and ztrend. They keep track of the
@ -870,7 +870,7 @@ double VoiceKey::TestEnergy (
//Figure out how much to grab //Figure out how much to grab
auto block = limitSampleBufferSize ( t.GetBestBlockSize(s), len ); auto block = limitSampleBufferSize ( t.GetBestBlockSize(s), len );
t.Get((samplePtr)buffer.get(), floatSample, s,block); //grab the block; t.GetFloats(buffer.get(), s,block); //grab the block;
//Now, go through the block and calculate energy //Now, go through the block and calculate energy
for(decltype(block) i = 0; i< block; i++) for(decltype(block) i = 0; i< block; i++)
@ -913,7 +913,7 @@ double VoiceKey::TestSignChanges(
//Figure out how much to grab //Figure out how much to grab
auto block = limitSampleBufferSize ( t.GetBestBlockSize(s), len ); auto block = limitSampleBufferSize ( t.GetBestBlockSize(s), len );
t.Get((samplePtr)buffer.get(), floatSample, s, block); //grab the block; t.GetFloats(buffer.get(), s, block); //grab the block;
if (len == originalLen) if (len == originalLen)
{ {
@ -970,7 +970,7 @@ double VoiceKey::TestDirectionChanges(
//Figure out how much to grab //Figure out how much to grab
auto block = limitSampleBufferSize ( t.GetBestBlockSize(s), len ); auto block = limitSampleBufferSize ( t.GetBestBlockSize(s), len );
t.Get((samplePtr)buffer.get(), floatSample, s, block); //grab the block; t.GetFloats(buffer.get(), s, block); //grab the block;
if (len == originalLen) { if (len == originalLen) {
//The first time through, set stuff up special. //The first time through, set stuff up special.

View File

@ -525,9 +525,9 @@ bool WaveClip::GetWaveDisplay(WaveDisplay &display, double t0,
else { else {
b.reinit(len); b.reinit(len);
pb = b.get(); pb = b.get();
CopySamples(mAppendBuffer.ptr() + sLeft * SAMPLE_SIZE(seqFormat), SamplesToFloats(
seqFormat, mAppendBuffer.ptr() + sLeft * SAMPLE_SIZE(seqFormat),
(samplePtr)pb, floatSample, len); seqFormat, pb, len);
} }
float theMax, theMin, sumsq; float theMax, theMin, sumsq;
@ -707,8 +707,8 @@ bool SpecCache::CalculateOneSpectrum
} }
if (myLen > 0) { if (myLen > 0) {
useBuffer = (float*)(waveTrackCache.Get( useBuffer = (float*)(waveTrackCache.GetFloats(
floatSample, sampleCount( sampleCount(
floor(0.5 + from.as_double() + offset * rate) floor(0.5 + from.as_double() + offset * rate)
), ),
myLen, myLen,
@ -1248,7 +1248,7 @@ bool WaveClip::Append(constSamplePtr buffer, sampleFormat format,
mAppendBuffer.ptr() + mAppendBufferLen * SAMPLE_SIZE(seqFormat), mAppendBuffer.ptr() + mAppendBufferLen * SAMPLE_SIZE(seqFormat),
seqFormat, seqFormat,
toCopy, toCopy,
true, // high quality gHighQualityDither,
stride); stride);
mAppendBufferLen += toCopy; mAppendBufferLen += toCopy;

View File

@ -2542,9 +2542,10 @@ void WaveTrackCache::SetTrack(const std::shared_ptr<const WaveTrack> &pTrack)
} }
} }
constSamplePtr WaveTrackCache::Get(sampleFormat format, const float *WaveTrackCache::GetFloats(
sampleCount start, size_t len, bool mayThrow) sampleCount start, size_t len, bool mayThrow)
{ {
constexpr auto format = floatSample;
if (format == floatSample && len > 0) { if (format == floatSample && len > 0) {
const auto end = start + len; const auto end = start + len;
@ -2593,10 +2594,10 @@ constSamplePtr WaveTrackCache::Get(sampleFormat format,
if (start0 >= 0) { if (start0 >= 0) {
const auto len0 = mPTrack->GetBestBlockSize(start0); const auto len0 = mPTrack->GetBestBlockSize(start0);
wxASSERT(len0 <= mBufferSize); wxASSERT(len0 <= mBufferSize);
if (!mPTrack->Get( if (!mPTrack->GetFloats(
samplePtr(mBuffers[0].data.get()), floatSample, start0, len0, mBuffers[0].data.get(), start0, len0,
fillZero, mayThrow)) fillZero, mayThrow))
return 0; return nullptr;
mBuffers[0].start = start0; mBuffers[0].start = start0;
mBuffers[0].len = len0; mBuffers[0].len = len0;
if (!fillSecond && if (!fillSecond &&
@ -2621,8 +2622,8 @@ constSamplePtr WaveTrackCache::Get(sampleFormat format,
if (start1 == end0) { if (start1 == end0) {
const auto len1 = mPTrack->GetBestBlockSize(start1); const auto len1 = mPTrack->GetBestBlockSize(start1);
wxASSERT(len1 <= mBufferSize); wxASSERT(len1 <= mBufferSize);
if (!mPTrack->Get(samplePtr(mBuffers[1].data.get()), floatSample, start1, len1, fillZero, mayThrow)) if (!mPTrack->GetFloats(mBuffers[1].data.get(), start1, len1, fillZero, mayThrow))
return 0; return nullptr;
mBuffers[1].start = start1; mBuffers[1].start = start1;
mBuffers[1].len = len1; mBuffers[1].len = len1;
mNValidBuffers = 2; mNValidBuffers = 2;
@ -2631,7 +2632,7 @@ constSamplePtr WaveTrackCache::Get(sampleFormat format,
} }
wxASSERT(mNValidBuffers < 2 || mBuffers[0].end() == mBuffers[1].start); wxASSERT(mNValidBuffers < 2 || mBuffers[0].end() == mBuffers[1].start);
samplePtr buffer = 0; samplePtr buffer = nullptr; // will point into mOverlapBuffer
auto remaining = len; auto remaining = len;
// Possibly get an initial portion that is uncached // Possibly get an initial portion that is uncached
@ -2646,9 +2647,11 @@ constSamplePtr WaveTrackCache::Get(sampleFormat format,
mOverlapBuffer.Resize(len, format); mOverlapBuffer.Resize(len, format);
// initLen is not more than len: // initLen is not more than len:
auto sinitLen = initLen.as_size_t(); auto sinitLen = initLen.as_size_t();
if (!mPTrack->Get(mOverlapBuffer.ptr(), format, start, sinitLen, if (!mPTrack->GetFloats(
fillZero, mayThrow)) // See comment below about casting
return 0; reinterpret_cast<float *>(mOverlapBuffer.ptr()),
start, sinitLen, fillZero, mayThrow))
return nullptr;
wxASSERT( sinitLen <= remaining ); wxASSERT( sinitLen <= remaining );
remaining -= sinitLen; remaining -= sinitLen;
start += initLen; start += initLen;
@ -2669,12 +2672,12 @@ constSamplePtr WaveTrackCache::Get(sampleFormat format,
// All is contiguous already. We can completely avoid copying // All is contiguous already. We can completely avoid copying
// leni is nonnegative, therefore start falls within mBuffers[ii], // leni is nonnegative, therefore start falls within mBuffers[ii],
// so starti is bounded between 0 and buffer length // so starti is bounded between 0 and buffer length
return samplePtr(mBuffers[ii].data.get() + starti.as_size_t() ); return mBuffers[ii].data.get() + starti.as_size_t() ;
} }
else if (leni > 0) { else if (leni > 0) {
// leni is nonnegative, therefore start falls within mBuffers[ii] // leni is nonnegative, therefore start falls within mBuffers[ii]
// But we can't satisfy all from one buffer, so copy // But we can't satisfy all from one buffer, so copy
if (buffer == 0) { if (!buffer) {
mOverlapBuffer.Resize(len, format); mOverlapBuffer.Resize(len, format);
buffer = mOverlapBuffer.ptr(); buffer = mOverlapBuffer.ptr();
} }
@ -2692,23 +2695,31 @@ constSamplePtr WaveTrackCache::Get(sampleFormat format,
if (remaining > 0) { if (remaining > 0) {
// Very big request! // Very big request!
// Fall back to direct fetch // Fall back to direct fetch
if (buffer == 0) { if (!buffer) {
mOverlapBuffer.Resize(len, format); mOverlapBuffer.Resize(len, format);
buffer = mOverlapBuffer.ptr(); buffer = mOverlapBuffer.ptr();
} }
if (!mPTrack->Get(buffer, format, start, remaining, fillZero, mayThrow)) // See comment below about casting
if (!mPTrack->GetFloats( reinterpret_cast<float*>(buffer),
start, remaining, fillZero, mayThrow))
return 0; return 0;
} }
return mOverlapBuffer.ptr(); // Overlap buffer was meant for the more general support of sample formats
// besides float, which explains the cast
return reinterpret_cast<const float*>(mOverlapBuffer.ptr());
} }
else {
#if 0
// Cache works only for float format. // Cache works only for float format.
mOverlapBuffer.Resize(len, format); mOverlapBuffer.Resize(len, format);
if (mPTrack->Get(mOverlapBuffer.ptr(), format, start, len, fillZero, mayThrow)) if (mPTrack->Get(mOverlapBuffer.ptr(), format, start, len, fillZero, mayThrow))
return mOverlapBuffer.ptr(); return mOverlapBuffer.ptr();
else #else
return 0; // No longer handling other than float format. Therefore len is 0.
#endif
return nullptr;
}
} }
void WaveTrackCache::Free() void WaveTrackCache::Free()

View File

@ -246,6 +246,31 @@ private:
/// same value for "start" in both calls to "Set" and "Get" it is /// same value for "start" in both calls to "Set" and "Get" it is
/// guaranteed that the same samples are affected. /// guaranteed that the same samples are affected.
/// ///
//! Retrieve samples from a track in floating-point format, regardless of the storage format
/*!
@param buffer receives the samples
@param start starting sample, relative to absolute time zero (not to the track's offset value)
@param len how many samples to get. buffer is assumed sufficiently large
@param fill how to assign values for sample positions between clips
@param mayThrow if false, fill buffer with zeros when there is failure to retrieve samples; else throw
@param[out] pNumWithinClips Report how many samples were copied from within clips, rather
than filled according to fillFormat; but these were not necessarily one contiguous range.
*/
bool GetFloats(float *buffer, sampleCount start, size_t len,
fillFormat fill = fillZero, bool mayThrow = true,
sampleCount * pNumWithinClips = nullptr) const
{
//! Cast the pointer to pass it to Get() which handles multiple destination formats
return Get(reinterpret_cast<samplePtr>(buffer),
floatSample, start, len, fill, mayThrow, pNumWithinClips);
}
//! Retrieve samples from a track in a specified format
/*!
@copydetails WaveTrack::GetFloats()
@param format sample format of the destination buffer
*/
bool Get(samplePtr buffer, sampleFormat format, bool Get(samplePtr buffer, sampleFormat format,
sampleCount start, size_t len, sampleCount start, size_t len,
fillFormat fill = fillZero, fillFormat fill = fillZero,
@ -254,6 +279,7 @@ private:
// filled according to fillFormat; but these were not necessarily one // filled according to fillFormat; but these were not necessarily one
// contiguous range. // contiguous range.
sampleCount * pNumWithinClips = nullptr) const; sampleCount * pNumWithinClips = nullptr) const;
void Set(constSamplePtr buffer, sampleFormat format, void Set(constSamplePtr buffer, sampleFormat format,
sampleCount start, size_t len); sampleCount start, size_t len);
@ -592,10 +618,9 @@ private:
std::unique_ptr<WaveformSettings> mpWaveformSettings; std::unique_ptr<WaveformSettings> mpWaveformSettings;
}; };
// This is meant to be a short-lived object, during whose lifetime, //! A short-lived object, during whose lifetime, the contents of the WaveTrack are assumed not to change.
// the contents of the WaveTrack are known not to change. It can replace /*! It can replace repeated calls to WaveTrack::Get() (each of which opens and closes at least one block).
// repeated calls to WaveTrack::Get() (each of which opens and closes at least */
// one block file).
class AUDACITY_DLL_API WaveTrackCache { class AUDACITY_DLL_API WaveTrackCache {
public: public:
WaveTrackCache() WaveTrackCache()
@ -617,12 +642,11 @@ public:
const std::shared_ptr<const WaveTrack>& GetTrack() const { return mPTrack; } const std::shared_ptr<const WaveTrack>& GetTrack() const { return mPTrack; }
void SetTrack(const std::shared_ptr<const WaveTrack> &pTrack); void SetTrack(const std::shared_ptr<const WaveTrack> &pTrack);
// Uses fillZero always //! Retrieve samples as floats from the track or from the memory cache
// Returns null on failure /*! Uses fillZero always
// Returned pointer may be invalidated if Get is called again @return null on failure; this object owns the memory; may be invalidated if GetFloats() is called again
// Do not DELETE[] the pointer */
constSamplePtr Get( const float *GetFloats(sampleCount start, size_t len, bool mayThrow);
sampleFormat format, sampleCount start, size_t len, bool mayThrow);
private: private:
void Free(); void Free();

View File

@ -138,8 +138,8 @@ bool CompareAudioCommand::Apply(const CommandContext & context)
auto block = limitSampleBufferSize( auto block = limitSampleBufferSize(
mTrack0->GetBestBlockSize(position), s1 - position mTrack0->GetBestBlockSize(position), s1 - position
); );
mTrack0->Get((samplePtr)buff0.get(), floatSample, position, block); mTrack0->GetFloats(buff0.get(), position, block);
mTrack1->Get((samplePtr)buff1.get(), floatSample, position, block); mTrack1->GetFloats(buff1.get(), position, block);
for (decltype(block) buffPos = 0; buffPos < block; ++buffPos) for (decltype(block) buffPos = 0; buffPos < block; ++buffPos)
{ {

View File

@ -321,7 +321,7 @@ bool EffectAutoDuck::Process()
{ {
const auto len = limitSampleBufferSize( kBufSize, end - pos ); const auto len = limitSampleBufferSize( kBufSize, end - pos );
mControlTrack->Get((samplePtr)buf.get(), floatSample, pos, len); mControlTrack->GetFloats(buf.get(), pos, len);
for (auto i = pos; i < pos + len; i++) for (auto i = pos; i < pos + len; i++)
{ {
@ -559,7 +559,7 @@ bool EffectAutoDuck::ApplyDuckFade(int trackNum, WaveTrack* t,
{ {
const auto len = limitSampleBufferSize( kBufSize, end - pos ); const auto len = limitSampleBufferSize( kBufSize, end - pos );
t->Get((samplePtr)buf.get(), floatSample, pos, len); t->GetFloats(buf.get(), pos, len);
for (auto i = pos; i < pos + len; i++) for (auto i = pos; i < pos + len; i++)
{ {

View File

@ -477,7 +477,7 @@ void EffectChangePitch::DeduceFrequencies()
Floats freq{ windowSize / 2 }; Floats freq{ windowSize / 2 };
Floats freqa{ windowSize / 2, true }; Floats freqa{ windowSize / 2, true };
track->Get((samplePtr) buffer.get(), floatSample, start, analyzeSize); track->GetFloats(buffer.get(), start, analyzeSize);
for(unsigned i = 0; i < numWindows; i++) { for(unsigned i = 0; i < numWindows; i++) {
ComputeSpectrum(buffer.get() + i * windowSize, windowSize, ComputeSpectrum(buffer.get() + i * windowSize, windowSize,
windowSize, rate, freq.get(), true); windowSize, rate, freq.get(), true);

View File

@ -520,7 +520,7 @@ bool EffectChangeSpeed::ProcessOne(WaveTrack * track,
); );
//Get the samples from the track and put them in the buffer //Get the samples from the track and put them in the buffer
track->Get((samplePtr) inBuffer.get(), floatSample, samplePos, blockSize); track->GetFloats(inBuffer.get(), samplePos, blockSize);
const auto results = resample.Process(mFactor, const auto results = resample.Process(mFactor,
inBuffer.get(), inBuffer.get(),

View File

@ -233,7 +233,7 @@ bool EffectClickRemoval::ProcessOne(int count, WaveTrack * track, sampleCount st
{ {
auto block = limitSampleBufferSize( idealBlockLen, len - s ); auto block = limitSampleBufferSize( idealBlockLen, len - s );
track->Get((samplePtr) buffer.get(), floatSample, start + s, block); track->GetFloats(buffer.get(), start + s, block);
for (decltype(block) i = 0; i + windowSize / 2 < block; i += windowSize / 2) for (decltype(block) i = 0; i + windowSize / 2 < block; i += windowSize / 2)
{ {

View File

@ -1637,10 +1637,10 @@ bool Effect::ProcessTrack(int count,
limitSampleBufferSize( mBufferSize, inputRemaining ); limitSampleBufferSize( mBufferSize, inputRemaining );
// Fill the input buffers // Fill the input buffers
left->Get((samplePtr) inBuffer[0].get(), floatSample, inPos, inputBufferCnt); left->GetFloats(inBuffer[0].get(), inPos, inputBufferCnt);
if (right) if (right)
{ {
right->Get((samplePtr) inBuffer[1].get(), floatSample, inPos, inputBufferCnt); right->GetFloats(inBuffer[1].get(), inPos, inputBufferCnt);
} }
// Reset the input buffer positions // Reset the input buffer positions

View File

@ -1330,7 +1330,7 @@ bool EffectEqualization::ProcessOne(int count, WaveTrack * t,
{ {
auto block = limitSampleBufferSize( idealBlockLen, len ); auto block = limitSampleBufferSize( idealBlockLen, len );
t->Get((samplePtr)buffer.get(), floatSample, s, block); t->GetFloats(buffer.get(), s, block);
for(size_t i = 0; i < block; i += L) //go through block in lumps of length L for(size_t i = 0; i < block; i += L) //go through block in lumps of length L
{ {

View File

@ -198,7 +198,7 @@ bool EffectFindClipping::ProcessOne(LabelTrack * lt,
block = limitSampleBufferSize( blockSize, len - s ); block = limitSampleBufferSize( blockSize, len - s );
wt->Get((samplePtr)buffer.get(), floatSample, start + s, block); wt->GetFloats(buffer.get(), start + s, block);
ptr = buffer.get(); ptr = buffer.get();
} }

View File

@ -515,7 +515,7 @@ void EffectLoudness::LoadBufferBlock(TrackIterRange<WaveTrack> range,
int idx = 0; int idx = 0;
for(auto channel : range) for(auto channel : range)
{ {
channel->Get((samplePtr) mTrackBuffer[idx].get(), floatSample, pos, len ); channel->GetFloats(mTrackBuffer[idx].get(), pos, len );
++idx; ++idx;
} }
mTrackBufferLen = len; mTrackBufferLen = len;

View File

@ -1330,7 +1330,7 @@ bool EffectNoiseReduction::Worker::ProcessOne
); );
//Get the samples from the track and put them in the buffer //Get the samples from the track and put them in the buffer
track->Get((samplePtr)&buffer[0], floatSample, samplePos, blockSize); track->GetFloats(&buffer[0], samplePos, blockSize);
samplePos += blockSize; samplePos += blockSize;
mInSampleCount += blockSize; mInSampleCount += blockSize;

View File

@ -435,7 +435,7 @@ bool EffectNormalize::AnalyseTrackData(const WaveTrack * track, const Translatab
); );
//Get the samples from the track and put them in the buffer //Get the samples from the track and put them in the buffer
track->Get((samplePtr) buffer.get(), floatSample, s, block, fillZero, true, &blockSamples); track->GetFloats(buffer.get(), s, block, fillZero, true, &blockSamples);
totalSamples += blockSamples; totalSamples += blockSamples;
//Process the buffer. //Process the buffer.
@ -495,7 +495,7 @@ bool EffectNormalize::ProcessOne(
); );
//Get the samples from the track and put them in the buffer //Get the samples from the track and put them in the buffer
track->Get((samplePtr) buffer.get(), floatSample, s, block); track->GetFloats(buffer.get(), s, block);
//Process the buffer. //Process the buffer.
ProcessData(buffer.get(), block, offset); ProcessData(buffer.get(), block, offset);

View File

@ -359,7 +359,7 @@ bool EffectPaulstretch::ProcessOne(WaveTrack *track,double t0,double t1,int coun
decltype(len) s=0; decltype(len) s=0;
while (s < len) { while (s < len) {
track->Get((samplePtr)bufferptr0, floatSample, start + s, nget); track->GetFloats(bufferptr0, start + s, nget);
stretch.process(buffer0.get(), nget); stretch.process(buffer0.get(), nget);
if (first_time) { if (first_time) {
@ -369,7 +369,7 @@ bool EffectPaulstretch::ProcessOne(WaveTrack *track,double t0,double t1,int coun
s += nget; s += nget;
if (first_time){//blend the start of the selection if (first_time){//blend the start of the selection
track->Get((samplePtr)fade_track_smps.get(), floatSample, start, fade_len); track->GetFloats(fade_track_smps.get(), start, fade_len);
first_time = false; first_time = false;
for (size_t i = 0; i < fade_len; i++){ for (size_t i = 0; i < fade_len; i++){
float fi = (float)i / (float)fade_len; float fi = (float)i / (float)fade_len;
@ -378,7 +378,7 @@ bool EffectPaulstretch::ProcessOne(WaveTrack *track,double t0,double t1,int coun
} }
} }
if (s >= len){//blend the end of the selection if (s >= len){//blend the end of the selection
track->Get((samplePtr)fade_track_smps.get(), floatSample, end - fade_len, fade_len); track->GetFloats(fade_track_smps.get(), end - fade_len, fade_len);
for (size_t i = 0; i < fade_len; i++){ for (size_t i = 0; i < fade_len; i++){
float fi = (float)i / (float)fade_len; float fi = (float)i / (float)fade_len;
auto i2 = bufsize / 2 - 1 - i; auto i2 = bufsize / 2 - 1 - i;

View File

@ -146,7 +146,7 @@ bool EffectRepair::ProcessOne(int count, WaveTrack * track,
size_t repairStart, size_t repairLen) size_t repairStart, size_t repairLen)
{ {
Floats buffer{ len }; Floats buffer{ len };
track->Get((samplePtr) buffer.get(), floatSample, start, len); track->GetFloats(buffer.get(), start, len);
InterpolateAudio(buffer.get(), len, repairStart, repairLen); InterpolateAudio(buffer.get(), len, repairStart, repairLen);
track->Set((samplePtr)&buffer[repairStart], floatSample, track->Set((samplePtr)&buffer[repairStart], floatSample,
start + repairStart, repairLen); start + repairStart, repairLen);

View File

@ -232,8 +232,8 @@ bool EffectReverse::ProcessOneClip(int count, WaveTrack *track,
limitSampleBufferSize( track->GetBestBlockSize(first), len / 2 ); limitSampleBufferSize( track->GetBestBlockSize(first), len / 2 );
auto second = first + (len - block); auto second = first + (len - block);
track->Get((samplePtr)buffer1.get(), floatSample, first, block); track->GetFloats(buffer1.get(), first, block);
track->Get((samplePtr)buffer2.get(), floatSample, second, block); track->GetFloats(buffer2.get(), second, block);
for (decltype(block) i = 0; i < block; i++) { for (decltype(block) i = 0; i < block; i++) {
tmp = buffer1[i]; tmp = buffer1[i];
buffer1[i] = buffer2[block-i-1]; buffer1[i] = buffer2[block-i-1];

View File

@ -99,10 +99,10 @@ long resampleCB(void *cb_data, SBSMSFrame *data)
// does not seem to let us report error codes, so use this roundabout to // does not seem to let us report error codes, so use this roundabout to
// stop the effect early. // stop the effect early.
try { try {
r->leftTrack->Get( r->leftTrack->GetFloats(
(samplePtr)(r->leftBuffer.get()), floatSample, r->offset, blockSize); (r->leftBuffer.get()), r->offset, blockSize);
r->rightTrack->Get( r->rightTrack->GetFloats(
(samplePtr)(r->rightBuffer.get()), floatSample, r->offset, blockSize); (r->rightBuffer.get()), r->offset, blockSize);
} }
catch ( ... ) { catch ( ... ) {
// Save the exception object for re-throw when out of the library // Save the exception object for re-throw when out of the library

View File

@ -96,7 +96,7 @@ bool EffectSimpleMono::ProcessOne(WaveTrack * track,
limitSampleBufferSize( track->GetBestBlockSize(s), end - s ); limitSampleBufferSize( track->GetBestBlockSize(s), end - s );
//Get the samples from the track and put them in the buffer //Get the samples from the track and put them in the buffer
track->Get((samplePtr) buffer.get(), floatSample, s, block); track->GetFloats(buffer.get(), s, block);
//Process the buffer. If it fails, clean up and exit. //Process the buffer. If it fails, clean up and exit.
if (!ProcessSimpleMono(buffer.get(), block)) if (!ProcessSimpleMono(buffer.get(), block))

View File

@ -217,7 +217,7 @@ bool EffectSoundTouch::ProcessOne(WaveTrack *track,
limitSampleBufferSize( track->GetBestBlockSize(s), end - s )); limitSampleBufferSize( track->GetBestBlockSize(s), end - s ));
//Get the samples from the track and put them in the buffer //Get the samples from the track and put them in the buffer
track->Get((samplePtr)buffer.get(), floatSample, s, block); track->GetFloats(buffer.get(), s, block);
//Add samples to SoundTouch //Add samples to SoundTouch
mSoundTouch->putSamples(buffer.get(), block); mSoundTouch->putSamples(buffer.get(), block);
@ -298,8 +298,8 @@ bool EffectSoundTouch::ProcessStereo(
); );
// Get the samples from the tracks and put them in the buffers. // Get the samples from the tracks and put them in the buffers.
leftTrack->Get((samplePtr)(leftBuffer.get()), floatSample, sourceSampleCount, blockSize); leftTrack->GetFloats((leftBuffer.get()), sourceSampleCount, blockSize);
rightTrack->Get((samplePtr)(rightBuffer.get()), floatSample, sourceSampleCount, blockSize); rightTrack->GetFloats((rightBuffer.get()), sourceSampleCount, blockSize);
// Interleave into soundTouchBuffer. // Interleave into soundTouchBuffer.
for (decltype(blockSize) index = 0; index < blockSize; index++) { for (decltype(blockSize) index = 0; index < blockSize; index++) {

View File

@ -568,8 +568,8 @@ bool EffectTruncSilence::DoRemoval
auto t1 = wt->TimeToLongSamples(cutStart) - blendFrames / 2; auto t1 = wt->TimeToLongSamples(cutStart) - blendFrames / 2;
auto t2 = wt->TimeToLongSamples(cutEnd) - blendFrames / 2; auto t2 = wt->TimeToLongSamples(cutEnd) - blendFrames / 2;
wt->Get((samplePtr)buf1.get(), floatSample, t1, blendFrames); wt->GetFloats(buf1.get(), t1, blendFrames);
wt->Get((samplePtr)buf2.get(), floatSample, t2, blendFrames); wt->GetFloats(buf2.get(), t2, blendFrames);
for (decltype(blendFrames) i = 0; i < blendFrames; ++i) for (decltype(blendFrames) i = 0; i < blendFrames; ++i)
{ {
@ -689,7 +689,7 @@ bool EffectTruncSilence::Analyze(RegionList& silenceList,
auto count = limitSampleBufferSize( blockLen, end - *index ); auto count = limitSampleBufferSize( blockLen, end - *index );
// Fill buffer // Fill buffer
wt->Get((samplePtr)(buffer.get()), floatSample, *index, count); wt->GetFloats((buffer.get()), *index, count);
// Look for silenceList in current block // Look for silenceList in current block
for (decltype(count) i = 0; i < count; ++i) { for (decltype(count) i = 0; i < count; ++i) {

View File

@ -130,7 +130,7 @@ bool EffectTwoPassSimpleMono::ProcessOne(WaveTrack * track, WaveTrack * outTrack
std::min( maxblock, track->GetBestBlockSize(start) ), end - start ); std::min( maxblock, track->GetBestBlockSize(start) ), end - start );
//Get the samples from the track and put them in the buffer //Get the samples from the track and put them in the buffer
track->Get((samplePtr) buffer1.get(), floatSample, start, samples1); track->GetFloats(buffer1.get(), start, samples1);
// Process the first buffer with a NULL previous buffer // Process the first buffer with a NULL previous buffer
if (mPass == 0) if (mPass == 0)
@ -152,7 +152,7 @@ bool EffectTwoPassSimpleMono::ProcessOne(WaveTrack * track, WaveTrack * outTrack
); );
//Get the samples from the track and put them in the buffer //Get the samples from the track and put them in the buffer
track->Get((samplePtr)buffer2.get(), floatSample, s, samples2); track->GetFloats(buffer2.get(), s, samples2);
//Process the buffer. If it fails, clean up and exit. //Process the buffer. If it fails, clean up and exit.
if (mPass == 0) if (mPass == 0)

View File

@ -29,6 +29,7 @@ effects from this one class.
#include <algorithm> #include <algorithm>
#include <cmath> #include <cmath>
#include <cstring>
#include <locale.h> #include <locale.h>
@ -1390,12 +1391,12 @@ bool NyquistEffect::ProcessOne()
// Put the fetch buffers in a clean initial state // Put the fetch buffers in a clean initial state
for (size_t i = 0; i < mCurNumChannels; i++) for (size_t i = 0; i < mCurNumChannels; i++)
mCurBuffer[i].Free(); mCurBuffer[i].reset();
// Guarantee release of memory when done // Guarantee release of memory when done
auto cleanup = finally( [&] { auto cleanup = finally( [&] {
for (size_t i = 0; i < mCurNumChannels; i++) for (size_t i = 0; i < mCurNumChannels; i++)
mCurBuffer[i].Free(); mCurBuffer[i].reset();
} ); } );
// Evaluate the expression, which may invoke the get callback, but often does // Evaluate the expression, which may invoke the get callback, but often does
@ -1563,7 +1564,7 @@ bool NyquistEffect::ProcessOne()
// Clean the initial buffer states again for the get callbacks // Clean the initial buffer states again for the get callbacks
// -- is this really needed? // -- is this really needed?
mCurBuffer[i].Free(); mCurBuffer[i].reset();
} }
// Now fully evaluate the sound // Now fully evaluate the sound
@ -2426,15 +2427,15 @@ int NyquistEffect::StaticGetCallback(float *buffer, int channel,
int NyquistEffect::GetCallback(float *buffer, int ch, int NyquistEffect::GetCallback(float *buffer, int ch,
int64_t start, int64_t len, int64_t WXUNUSED(totlen)) int64_t start, int64_t len, int64_t WXUNUSED(totlen))
{ {
if (mCurBuffer[ch].ptr()) { if (mCurBuffer[ch]) {
if ((mCurStart[ch] + start) < mCurBufferStart[ch] || if ((mCurStart[ch] + start) < mCurBufferStart[ch] ||
(mCurStart[ch] + start)+len > (mCurStart[ch] + start)+len >
mCurBufferStart[ch]+mCurBufferLen[ch]) { mCurBufferStart[ch]+mCurBufferLen[ch]) {
mCurBuffer[ch].Free(); mCurBuffer[ch].reset();
} }
} }
if (!mCurBuffer[ch].ptr()) { if (!mCurBuffer[ch]) {
mCurBufferStart[ch] = (mCurStart[ch] + start); mCurBufferStart[ch] = (mCurStart[ch] + start);
mCurBufferLen[ch] = mCurTrack[ch]->GetBestBlockSize(mCurBufferStart[ch]); mCurBufferLen[ch] = mCurTrack[ch]->GetBestBlockSize(mCurBufferStart[ch]);
@ -2446,10 +2447,11 @@ int NyquistEffect::GetCallback(float *buffer, int ch,
limitSampleBufferSize( mCurBufferLen[ch], limitSampleBufferSize( mCurBufferLen[ch],
mCurStart[ch] + mCurLen - mCurBufferStart[ch] ); mCurStart[ch] + mCurLen - mCurBufferStart[ch] );
mCurBuffer[ch].Allocate(mCurBufferLen[ch], floatSample); // C++20
// mCurBuffer[ch] = std::make_unique_for_overwrite(mCurBufferLen[ch]);
mCurBuffer[ch] = Buffer{ safenew float[ mCurBufferLen[ch] ] };
try { try {
mCurTrack[ch]->Get( mCurTrack[ch]->GetFloats( mCurBuffer[ch].get(),
mCurBuffer[ch].ptr(), floatSample,
mCurBufferStart[ch], mCurBufferLen[ch]); mCurBufferStart[ch], mCurBufferLen[ch]);
} }
catch ( ... ) { catch ( ... ) {
@ -2462,9 +2464,8 @@ int NyquistEffect::GetCallback(float *buffer, int ch,
// We have guaranteed above that this is nonnegative and bounded by // We have guaranteed above that this is nonnegative and bounded by
// mCurBufferLen[ch]: // mCurBufferLen[ch]:
auto offset = ( mCurStart[ch] + start - mCurBufferStart[ch] ).as_size_t(); auto offset = ( mCurStart[ch] + start - mCurBufferStart[ch] ).as_size_t();
CopySamples(mCurBuffer[ch].ptr() + offset*SAMPLE_SIZE(floatSample), floatSample, const void *src = &mCurBuffer[ch][offset];
(samplePtr)buffer, floatSample, std::memcpy(buffer, src, len * sizeof(float));
len);
if (ch == 0) { if (ch == 0) {
double progress = mScale * double progress = mScale *

View File

@ -267,7 +267,8 @@ private:
double mProgressTot; double mProgressTot;
double mScale; double mScale;
SampleBuffer mCurBuffer[2]; using Buffer = std::unique_ptr<float[]>;
Buffer mCurBuffer[2];
sampleCount mCurBufferStart[2]; sampleCount mCurBufferStart[2];
size_t mCurBufferLen[2]; size_t mCurBufferLen[2];

View File

@ -453,12 +453,12 @@ bool VampEffect::Process()
if (left) if (left)
{ {
left->Get((samplePtr)data[0].get(), floatSample, pos, request); left->GetFloats(data[0].get(), pos, request);
} }
if (right) if (right)
{ {
right->Get((samplePtr)data[1].get(), floatSample, pos, request); right->GetFloats(data[1].get(), pos, request);
} }
if (request < block) if (request < block)

View File

@ -643,12 +643,13 @@ ProgressResult ExportPCM::Export(AudacityProject *project,
CopySamples( CopySamples(
mixed + (c * SAMPLE_SIZE(format)), format, mixed + (c * SAMPLE_SIZE(format)), format,
dither.data() + (c * SAMPLE_SIZE(int24Sample)), int24Sample, dither.data() + (c * SAMPLE_SIZE(int24Sample)), int24Sample,
numSamples, true, info.channels, info.channels numSamples, gHighQualityDither, info.channels, info.channels
); );
CopySamplesNoDither( // Copy back without dither
CopySamples(
dither.data() + (c * SAMPLE_SIZE(int24Sample)), int24Sample, dither.data() + (c * SAMPLE_SIZE(int24Sample)), int24Sample,
mixed + (c * SAMPLE_SIZE(format)), format, mixed + (c * SAMPLE_SIZE(format)), format,
numSamples, info.channels, info.channels); numSamples, DitherType::none, info.channels, info.channels);
} }
} }

View File

@ -221,6 +221,10 @@ private:
AUPImportPlugin::AUPImportPlugin() AUPImportPlugin::AUPImportPlugin()
: ImportPlugin(FileExtensions(exts.begin(), exts.end())) : ImportPlugin(FileExtensions(exts.begin(), exts.end()))
{ {
static_assert(
sizeof(long long) >= sizeof(uint64_t) &&
sizeof(long) >= sizeof(uint32_t),
"Assumptions about sizes in XMLValueChecker calls are invalid!");
} }
AUPImportPlugin::~AUPImportPlugin() AUPImportPlugin::~AUPImportPlugin()
@ -643,8 +647,6 @@ bool AUPImportFileHandle::HandleProject(XMLTagHandler *&handler)
{ {
const wxChar *attr = *mAttrs++; const wxChar *attr = *mAttrs++;
const wxChar *value = *mAttrs++; const wxChar *value = *mAttrs++;
long lValue;
long long llValue;
double dValue; double dValue;
if (!value) if (!value)
@ -664,6 +666,7 @@ bool AUPImportFileHandle::HandleProject(XMLTagHandler *&handler)
// ViewInfo // ViewInfo
if (!wxStrcmp(attr, wxT("vpos"))) if (!wxStrcmp(attr, wxT("vpos")))
{ {
long lValue;
if (!XMLValueChecker::IsGoodInt(strValue) || !strValue.ToLong(&lValue) || (lValue < 0)) if (!XMLValueChecker::IsGoodInt(strValue) || !strValue.ToLong(&lValue) || (lValue < 0))
{ {
return SetError(XO("Invalid project 'vpos' attribute.")); return SetError(XO("Invalid project 'vpos' attribute."));
@ -1115,14 +1118,13 @@ bool AUPImportFileHandle::HandleSequence(XMLTagHandler *&handler)
break; break;
} }
long long nValue = 0;
const wxString strValue = value; // promote string, we need this for all const wxString strValue = value; // promote string, we need this for all
if (!wxStrcmp(attr, wxT("maxsamples"))) if (!wxStrcmp(attr, wxT("maxsamples")))
{ {
// This attribute is a sample count, so can be 64bit // This attribute is a sample count, so can be 64bit
if (!XMLValueChecker::IsGoodInt64(strValue) || !strValue.ToLongLong(&nValue) || (nValue < 0)) long long llvalue;
if (!XMLValueChecker::IsGoodInt64(strValue) || !strValue.ToLongLong(&llvalue) || (llvalue < 0))
{ {
return SetError(XO("Invalid sequence 'maxsamples' attribute.")); return SetError(XO("Invalid sequence 'maxsamples' attribute."));
} }
@ -1130,7 +1132,7 @@ bool AUPImportFileHandle::HandleSequence(XMLTagHandler *&handler)
// Dominic, 12/10/2006: // Dominic, 12/10/2006:
// Let's check that maxsamples is >= 1024 and <= 64 * 1024 * 1024 // Let's check that maxsamples is >= 1024 and <= 64 * 1024 * 1024
// - that's a pretty wide range of reasonable values. // - that's a pretty wide range of reasonable values.
if ((nValue < 1024) || (nValue > 64 * 1024 * 1024)) if ((llvalue < 1024) || (llvalue > 64 * 1024 * 1024))
{ {
return SetError(XO("Invalid sequence 'maxsamples' attribute.")); return SetError(XO("Invalid sequence 'maxsamples' attribute."));
} }
@ -1150,7 +1152,8 @@ bool AUPImportFileHandle::HandleSequence(XMLTagHandler *&handler)
else if (!wxStrcmp(attr, wxT("numsamples"))) else if (!wxStrcmp(attr, wxT("numsamples")))
{ {
// This attribute is a sample count, so can be 64bit // This attribute is a sample count, so can be 64bit
if (!XMLValueChecker::IsGoodInt64(strValue) || !strValue.ToLongLong(&nValue) || (nValue < 0)) long long llvalue;
if (!XMLValueChecker::IsGoodInt64(strValue) || !strValue.ToLongLong(&llvalue) || (llvalue < 0))
{ {
return SetError(XO("Invalid sequence 'numsamples' attribute.")); return SetError(XO("Invalid sequence 'numsamples' attribute."));
} }
@ -1169,8 +1172,6 @@ bool AUPImportFileHandle::HandleWaveBlock(XMLTagHandler *&handler)
const wxChar *attr = *mAttrs++; const wxChar *attr = *mAttrs++;
const wxChar *value = *mAttrs++; const wxChar *value = *mAttrs++;
long long nValue = 0;
if (!value) if (!value)
{ {
break; break;
@ -1181,7 +1182,8 @@ bool AUPImportFileHandle::HandleWaveBlock(XMLTagHandler *&handler)
if (!wxStrcmp(attr, wxT("start"))) if (!wxStrcmp(attr, wxT("start")))
{ {
// making sure that values > 2^31 are OK because long clips will need them. // making sure that values > 2^31 are OK because long clips will need them.
if (!XMLValueChecker::IsGoodInt64(strValue) || !strValue.ToLongLong(&nValue) || (nValue < 0)) long long llvalue;
if (!XMLValueChecker::IsGoodInt64(strValue) || !strValue.ToLongLong(&llvalue) || (llvalue < 0))
{ {
return SetError(XO("Unable to parse the waveblock 'start' attribute")); return SetError(XO("Unable to parse the waveblock 'start' attribute"));
} }
@ -1196,13 +1198,12 @@ bool AUPImportFileHandle::HandleWaveBlock(XMLTagHandler *&handler)
bool AUPImportFileHandle::HandleSimpleBlockFile(XMLTagHandler *&handler) bool AUPImportFileHandle::HandleSimpleBlockFile(XMLTagHandler *&handler)
{ {
FilePath filename; FilePath filename;
sampleCount len = 0; size_t len = 0;
while (*mAttrs) while (*mAttrs)
{ {
const wxChar *attr = *mAttrs++; const wxChar *attr = *mAttrs++;
const wxChar *value = *mAttrs++; const wxChar *value = *mAttrs++;
long long nValue;
if (!value) if (!value)
{ {
@ -1229,12 +1230,13 @@ bool AUPImportFileHandle::HandleSimpleBlockFile(XMLTagHandler *&handler)
} }
else if (!wxStrcmp(attr, wxT("len"))) else if (!wxStrcmp(attr, wxT("len")))
{ {
if (!XMLValueChecker::IsGoodInt64(strValue) || !strValue.ToLongLong(&nValue) || (nValue <= 0)) long lValue;
if (!XMLValueChecker::IsGoodInt(strValue) || !strValue.ToLong(&lValue) || (lValue <= 0))
{ {
return SetError(XO("Missing or invalid simpleblockfile 'len' attribute.")); return SetError(XO("Missing or invalid simpleblockfile 'len' attribute."));
} }
len = nValue; len = lValue;
} }
} }
@ -1248,13 +1250,12 @@ bool AUPImportFileHandle::HandleSimpleBlockFile(XMLTagHandler *&handler)
bool AUPImportFileHandle::HandleSilentBlockFile(XMLTagHandler *&handler) bool AUPImportFileHandle::HandleSilentBlockFile(XMLTagHandler *&handler)
{ {
FilePath filename; FilePath filename;
sampleCount len = 0; size_t len = 0;
while (*mAttrs) while (*mAttrs)
{ {
const wxChar *attr = *mAttrs++; const wxChar *attr = *mAttrs++;
const wxChar *value = *mAttrs++; const wxChar *value = *mAttrs++;
long long nValue;
if (!value) if (!value)
{ {
@ -1265,12 +1266,13 @@ bool AUPImportFileHandle::HandleSilentBlockFile(XMLTagHandler *&handler)
if (!wxStrcmp(attr, wxT("len"))) if (!wxStrcmp(attr, wxT("len")))
{ {
if (!XMLValueChecker::IsGoodInt64(value) || !strValue.ToLongLong(&nValue) || !(nValue > 0)) long lValue;
if (!XMLValueChecker::IsGoodInt(value) || !strValue.ToLong(&lValue) || !(lValue > 0))
{ {
return SetError(XO("Missing or invalid silentblockfile 'len' attribute.")); return SetError(XO("Missing or invalid silentblockfile 'len' attribute."));
} }
len = nValue; len = lValue;
} }
} }
@ -1286,7 +1288,7 @@ bool AUPImportFileHandle::HandlePCMAliasBlockFile(XMLTagHandler *&handler)
wxString summaryFilename; wxString summaryFilename;
wxFileName filename; wxFileName filename;
sampleCount start = 0; sampleCount start = 0;
sampleCount len = 0; size_t len = 0;
int channel = 0; int channel = 0;
wxString name; wxString name;
@ -1294,7 +1296,6 @@ bool AUPImportFileHandle::HandlePCMAliasBlockFile(XMLTagHandler *&handler)
{ {
const wxChar *attr = *mAttrs++; const wxChar *attr = *mAttrs++;
const wxChar *value = *mAttrs++; const wxChar *value = *mAttrs++;
long long nValue;
if (!value) if (!value)
{ {
@ -1328,31 +1329,33 @@ bool AUPImportFileHandle::HandlePCMAliasBlockFile(XMLTagHandler *&handler)
} }
else if (!wxStricmp(attr, wxT("aliasstart"))) else if (!wxStricmp(attr, wxT("aliasstart")))
{ {
if (!XMLValueChecker::IsGoodInt64(strValue) || !strValue.ToLongLong(&nValue) || (nValue < 0)) long long llValue;
if (!XMLValueChecker::IsGoodInt64(strValue) || !strValue.ToLongLong(&llValue) || (llValue < 0))
{ {
return SetError(XO("Missing or invalid pcmaliasblockfile 'aliasstart' attribute.")); return SetError(XO("Missing or invalid pcmaliasblockfile 'aliasstart' attribute."));
} }
start = nValue; start = llValue;
} }
else if (!wxStricmp(attr, wxT("aliaslen"))) else if (!wxStricmp(attr, wxT("aliaslen")))
{ {
if (!XMLValueChecker::IsGoodInt64(strValue) || !strValue.ToLongLong(&nValue) || (nValue <= 0)) long lValue;
if (!XMLValueChecker::IsGoodInt(strValue) || !strValue.ToLong(&lValue) || (lValue <= 0))
{ {
return SetError(XO("Missing or invalid pcmaliasblockfile 'aliaslen' attribute.")); return SetError(XO("Missing or invalid pcmaliasblockfile 'aliaslen' attribute."));
} }
len = nValue; len = lValue;
} }
else if (!wxStricmp(attr, wxT("aliaschannel"))) else if (!wxStricmp(attr, wxT("aliaschannel")))
{ {
long nValue; long lValue;
if (!XMLValueChecker::IsGoodInt(strValue) || !strValue.ToLong(&nValue) || (nValue < 0)) if (!XMLValueChecker::IsGoodInt(strValue) || !strValue.ToLong(&lValue) || (lValue < 0))
{ {
return SetError(XO("Missing or invalid pcmaliasblockfile 'aliaslen' attribute.")); return SetError(XO("Missing or invalid pcmaliasblockfile 'aliaslen' attribute."));
} }
channel = nValue; channel = lValue;
} }
} }
@ -1512,6 +1515,12 @@ bool AUPImportFileHandle::AddSamples(const FilePath &blockFilename,
auto cleanup = finally([&] auto cleanup = finally([&]
{ {
// Do this before any throwing might happen
if (sf)
{
SFCall<int>(sf_close, sf);
}
if (!success) if (!success)
{ {
SetWarning(XO("Error while processing %s\n\nInserting silence.").Format(audioFilename)); SetWarning(XO("Error while processing %s\n\nInserting silence.").Format(audioFilename));
@ -1522,11 +1531,6 @@ bool AUPImportFileHandle::AddSamples(const FilePath &blockFilename,
// If this does throw, let that propagate, don't guard the call // If this does throw, let that propagate, don't guard the call
AddSilence(len); AddSilence(len);
} }
if (sf)
{
SFCall<int>(sf_close, sf);
}
}); });
if (!f.Open(audioFilename)) if (!f.Open(audioFilename))
@ -1569,6 +1573,8 @@ bool AUPImportFileHandle::AddSamples(const FilePath &blockFilename,
size_t framesRead = 0; size_t framesRead = 0;
// These cases preserve the logic formerly in BlockFile.cpp,
// which was deleted at commit 98d1468.
if (channels == 1 && format == int16Sample && sf_subtype_is_integer(info.format)) if (channels == 1 && format == int16Sample && sf_subtype_is_integer(info.format))
{ {
// If both the src and dest formats are integer formats, // If both the src and dest formats are integer formats,
@ -1620,6 +1626,18 @@ bool AUPImportFileHandle::AddSamples(const FilePath &blockFilename,
} }
else else
{ {
/*
Therefore none of the three cases above:
!(channels == 1 && format == int16Sample && sf_subtype_is_integer(info.format))
&&
!(channels == 1 && format == int24Sample && sf_subtype_is_integer(info.format))
&&
!(format == int16Sample && !sf_subtype_more_than_16_bits(info.format))
So format is not 16 bits with wider file format (third conjunct),
but still maybe it is 24 bits with float file format (second conjunct).
*/
// Otherwise, let libsndfile handle the conversion and // Otherwise, let libsndfile handle the conversion and
// scaling, and pass us normalized data as floats. We can // scaling, and pass us normalized data as floats. We can
// then convert to whatever format we want. // then convert to whatever format we want.
@ -1635,12 +1653,25 @@ bool AUPImportFileHandle::AddSamples(const FilePath &blockFilename,
return true; return true;
} }
/*
Dithering will happen in CopySamples if format is 24 bits.
Should that be done?
Either the file is an ordinary simple block file -- and presumably the
track was saved specifying a matching format, so format is float and
there is no dithering.
Or else this is the very unusual case of an .auf file, importing PCM data
on demand. The destination format is narrower, requiring dither, only
if the user also specified a narrow format for the track. In such a
case, dithering is right.
*/
CopySamples((samplePtr)(tmpptr + channel), CopySamples((samplePtr)(tmpptr + channel),
floatSample, floatSample,
bufptr, bufptr,
format, format,
framesRead, framesRead,
true /* high quality by default */, gHighQualityDither /* high quality by default */,
channels /* source stride */); channels /* source stride */);
} }

View File

@ -70,7 +70,7 @@ double NearestZeroCrossing
auto s = one->TimeToLongSamples(t0); auto s = one->TimeToLongSamples(t0);
// fillTwo to ensure that missing values are treated as 2, and hence do // fillTwo to ensure that missing values are treated as 2, and hence do
// not get used as zero crossings. // not get used as zero crossings.
one->Get((samplePtr)oneDist.get(), floatSample, one->GetFloats(oneDist.get(),
s - (int)oneWindowSize/2, oneWindowSize, fillTwo); s - (int)oneWindowSize/2, oneWindowSize, fillTwo);

View File

@ -593,7 +593,7 @@ void OnPunchAndRoll(const CommandContext &context)
if (getLen > 0) { if (getLen > 0) {
float *const samples = data.data(); float *const samples = data.data();
const sampleCount pos = wt->TimeToLongSamples(t1); const sampleCount pos = wt->TimeToLongSamples(t1);
wt->Get((samplePtr)samples, floatSample, pos, getLen); wt->GetFloats(samples, pos, getLen);
} }
crossfadeData.push_back(std::move(data)); crossfadeData.push_back(std::move(data));
} }

View File

@ -127,7 +127,7 @@ UIHandlePtr SampleHandle::HitTest
float oneSample; float oneSample;
const double rate = wavetrack->GetRate(); const double rate = wavetrack->GetRate();
const auto s0 = (sampleCount)(tt * rate + 0.5); const auto s0 = (sampleCount)(tt * rate + 0.5);
if (! wavetrack->Get((samplePtr)&oneSample, floatSample, s0, 1, fillZero, if (! wavetrack->GetFloats(&oneSample, s0, 1, fillZero,
// Do not propagate exception but return a failure value // Do not propagate exception but return a failure value
false) ) false) )
return {}; return {};
@ -238,7 +238,7 @@ UIHandle::Result SampleHandle::Click
Floats newSampleRegion{ 1 + 2 * (size_t)SMOOTHING_BRUSH_RADIUS }; Floats newSampleRegion{ 1 + 2 * (size_t)SMOOTHING_BRUSH_RADIUS };
//Get a sample from the track to do some tricks on. //Get a sample from the track to do some tricks on.
mClickedTrack->Get((samplePtr)sampleRegion.get(), floatSample, mClickedTrack->GetFloats(sampleRegion.get(),
mClickedStartSample - SMOOTHING_KERNEL_RADIUS - SMOOTHING_BRUSH_RADIUS, mClickedStartSample - SMOOTHING_KERNEL_RADIUS - SMOOTHING_BRUSH_RADIUS,
sampleRegionSize); sampleRegionSize);

View File

@ -1364,9 +1364,9 @@ void SelectHandle::StartSnappingFreqSelection
end - start)); end - start));
const auto effectiveLength = std::max(minLength, length); const auto effectiveLength = std::max(minLength, length);
frequencySnappingData.resize(effectiveLength, 0.0f); frequencySnappingData.resize(effectiveLength, 0.0f);
pTrack->Get( pTrack->GetFloats(
reinterpret_cast<samplePtr>(&frequencySnappingData[0]), &frequencySnappingData[0],
floatSample, start, length, fillZero, start, length, fillZero,
// Don't try to cope with exceptions, just read zeroes instead. // Don't try to cope with exceptions, just read zeroes instead.
false); false);