37 int channelIndex)
noexcept
40 mFirstClipSampleID != clip.GetSequence(0)->GetNumSamples() ||
41 mSampleType != outBlock.DataType)
43 mFirstClipSampleID = clip.GetSequence(0)->GetNumSamples();
44 mLastProcessedSample = 0;
45 mSampleType = outBlock.DataType;
55 const size_t appendedSamples = clip.GetAppendBufferLen(channelIndex);
57 if (appendedSamples == 0)
60 const auto appendBuffer = GetAppendBufferPointer(clip, channelIndex);
66 float* outBuffer = outBlock.GetWritePointer(appendedSamples);
68 std::copy(appendBuffer, appendBuffer + appendedSamples, outBuffer);
72 FillBlocksFromAppendBuffer<256>(
73 appendBuffer, appendedSamples, outBlock);
76 FillBlocksFromAppendBuffer<64 * 1024>(
77 appendBuffer, appendedSamples, outBlock);
85 if (mFirstClipSampleID != clip.GetSequence(0)->GetNumSamples() ||
86 appendedSamples > clip.GetAppendBufferLen(channelIndex)) {
97 const auto appendBuffer =
101 return static_cast<const float*
>(appendBuffer);
104 mConvertedAppendBufferData.resize(samplesCount);
108 const auto int16Buffer =
static_cast<const int16_t*
>(appendBuffer);
110 for (
size_t i = 0; i < samplesCount; ++i)
111 mConvertedAppendBufferData[i] =
float(int16Buffer[i]) / (1 << 15);
115 const auto int24Buffer =
static_cast<const int32_t*
>(appendBuffer);
117 for (
size_t i = 0; i < samplesCount; ++i)
118 mConvertedAppendBufferData[i] =
float(int24Buffer[i]) / (1 << 23);
121 return mConvertedAppendBufferData.data();
124 template<
size_t blockSize>
126 const float* bufferSamples,
size_t samplesCount,
129 const size_t startingBlock = mLastProcessedSample / blockSize;
130 const size_t blocksCount =
RoundUpUnsafe(samplesCount, blockSize);
132 for (
size_t blockIndex = startingBlock; blockIndex < blocksCount;
135 const size_t samplesInBlock =
136 std::min(blockSize, samplesCount - blockIndex * blockSize);
138 float min = std::numeric_limits<float>::infinity();
139 float max = -std::numeric_limits<float>::infinity();
143 auto samples = bufferSamples + blockIndex * blockSize;
145 for (
size_t sampleIndex = 0; sampleIndex < samplesInBlock;
148 const float sample = *samples++;
151 max = std::max(max, sample);
153 const double dbl = sample;
158 const auto rms =
static_cast<float>(
std::sqrt(sqSum / samplesInBlock));
160 mCachedData[blockIndex] = {
min, max, rms };
163 mLastProcessedSample = samplesCount;
165 sizeof(
CacheItem) * blocksCount /
sizeof(float));
167 std::memmove(ptr, mCachedData.data(),
sizeof(
CacheItem) * blocksCount);
184 size_t mLastProcessedSample { 0 };
190 return [sequence = clip.
GetSequence(channelIndex), clip = &clip,
195 if (requiredSample < 0)
198 auto sequenceSampleCount = sequence->GetNumSamples();
199 if (requiredSample >= sequenceSampleCount)
201 auto appendBufferOffset = requiredSample - sequence->GetNumSamples().as_long_long();
206 outBlock.DataType = dataType;
207 outBlock.FirstSample = sequenceSampleCount.as_long_long();
210 bool success = appendBufferHelper.FillBuffer(*clip, outBlock, channelIndex);
214 if (sequenceSampleCount == sequence->GetNumSamples()) {
218 if (requiredSample >= sequence->GetNumSamples()) {
225 const auto blockIndex = sequence->FindBlock(requiredSample);
226 const auto& inputBlock = sequence->GetBlockArray()[blockIndex];
228 outBlock.FirstSample = inputBlock.start.as_long_long();
229 outBlock.NumSamples = inputBlock.sb->GetSampleCount();
236 static_cast<void*
>(outBlock.GetWritePointer(outBlock.NumSamples)));
238 inputBlock.sb->GetSamples(
244 size_t framesCount =
RoundUpUnsafe(outBlock.NumSamples, 256);
247 static_cast<float*
>(outBlock.GetWritePointer(framesCount * 3));
249 inputBlock.sb->GetSummary256(ptr, 0, framesCount);
254 size_t framesCount =
RoundUpUnsafe(outBlock.NumSamples, 64 * 1024);
257 static_cast<float*
>(outBlock.GetWritePointer(framesCount * 3));
259 inputBlock.sb->GetSummary64k(ptr, 0, framesCount);
266 outBlock.DataType = dataType;
276 waveClip.
GetRate() / waveClip.GetStretchRatio(),
277 [] {
return std::make_unique<WaveCacheElement>(); })
279 , mWaveClip { waveClip }
280 , mStretchChangedSubscription {
285 mWaveClip.GetRate() / mWaveClip.GetStretchRatio());
299 int64_t firstSample =
key.FirstSample;
301 const auto samplesPerColumn =
304 const size_t elementSamplesCount =
306 size_t processedSamples = 0;
309 samplesPerColumn >= 64 * 1024 ?
317 size_t columnIndex = 0;
324 static_cast<size_t>(
std::round(samplesPerColumn * (columnIndex + 1)) -
std::round(samplesPerColumn * columnIndex));
326 while (samplesLeft != 0)
343 auto& column = element.
Data[columnIndex];
345 column.min = summary.
Min;
346 column.max = summary.
Max;
353 const auto prevColumn = element.
Data[columnIndex - 1];
354 auto& column = element.
Data[columnIndex];
356 bool updated =
false;
358 if (prevColumn.min > column.max)
360 column.max = prevColumn.min;
364 if (prevColumn.max < column.min)
366 column.min = prevColumn.max;
371 column.rms = std::clamp(column.rms, column.min, column.max);
374 if (samplesLeft != 0)
382 element.
IsComplete = processedSamples == elementSamplesCount;
384 return processedSamples != 0;
389 return sampleIndex >= FirstSample &&
390 (sampleIndex < (FirstSample + NumSamples));
395 mData.resize(floatsCount);
407template<
size_t blockSize>
409 const float* input, int64_t from,
size_t count,
412 input = input + 3 * (from / blockSize);
416 float max = summary.
Max;
419 for (
size_t idx = 0; idx < count; ++idx)
422 max = std::max(max, *input++);
424 const double rms = *input++;
426 squareSum += rms * rms * blockSize;
442 from = from - FirstSample;
444 std::min<size_t>(samplesCount, std::max<int64_t>(0, NumSamples - from));
446 const auto to = from + samplesCount;
449 static_cast<const float*
>(
static_cast<const void*
>(mData.data()));
460 for (
auto sampleIndex = from; sampleIndex < to; ++sampleIndex)
462 const float sample = data[sampleIndex];
465 summary.
Max = std::max(summary.
Max, sample);
467 summary.
SquaresSum += double(sample) * double(sample);
470 assert(summary.
Min <= summary.
Max);
474 processBlock<256>(data, from, samplesCount, summary);
477 processBlock<64 * 1024>(data, from, samplesCount, summary);
493 if (prev->AvailableColumns == 0)
496 const auto prevLastColumn = prev->
Data[prev->AvailableColumns - 1];
497 auto& firstColumn =
Data[0];
499 bool updated =
false;
501 if (prevLastColumn.min > firstColumn.max)
503 firstColumn.max = prevLastColumn.min;
507 if (prevLastColumn.max < firstColumn.min)
509 firstColumn.min = prevLastColumn.max;
515 std::clamp(firstColumn.rms, firstColumn.min, firstColumn.max);
auto RoundUpUnsafe(LType numerator, RType denominator) noexcept
Returns a rounded up integer result for numerator / denominator.
@ WaveDataCache
Time required to access the data cache.
static Stopwatch CreateStopwatch(SectionID section) noexcept
Create a Stopwatch for the section specified.
static constexpr uint32_t CacheElementWidth
double GetScaledSampleRate() const noexcept
Returns the sample rate associated with cache.
An object that sends messages to an open-ended list of subscribed callbacks.
This allows multiple clips to be a part of one WaveTrack.
constSamplePtr GetAppendBuffer(size_t ii) const
Get one channel of the append buffer.
SampleFormats GetSampleFormats() const
size_t GetAppendBufferLen(size_t ii) const
Sequence * GetSequence(size_t ii)
std::function< bool(int64_t requiredSample, WaveCacheSampleBlock::Type dataType, WaveCacheSampleBlock &block)> DataProvider
WaveCacheSampleBlock mCachedBlock
WaveDataCache(const WaveClip &waveClip, int channelIndex)
bool InitializeElement(const GraphicsDataCacheKey &key, WaveCacheElement &element) override
std::vector< float > mConvertedAppendBufferData
const float * GetAppendBufferPointer(const WaveClip &clip, int channelIndex)
void FillBlocksFromAppendBuffer(const float *bufferSamples, size_t samplesCount, WaveCacheSampleBlock &outBlock)
bool FillBuffer(const WaveClip &clip, WaveCacheSampleBlock &outBlock, int channelIndex) noexcept
std::vector< CacheItem > mCachedData
Positions or offsets within audio files need a wide type.
RegisteredInitializer initializer
double GetRate(const Track &track)
WaveDataCache::DataProvider MakeDefaultDataProvider(const WaveClip &clip, int channelIndex)
void processBlock(const float *input, int64_t from, size_t count, WaveCacheSampleBlock::Summary &summary)
fastfloat_really_inline void round(adjusted_mantissa &am, callback cb) noexcept
__finl float_x4 __vecc sqrt(const float_x4 &a)
void copy(const T *src, T *dst, int32_t n)
A base class for the for cache elements.
bool AwaitsEviction
This flag is used to simplify the eviction algorithm.
bool IsComplete
Cache implementation is responsible to set this flag when all the data of the item is filled.
A key into the graphics data cache.
An element of a cache that contains the waveform data.
void Smooth(GraphicsDataCacheElementBase *prevElement) override
This method is called during the lookup when new items are inserted. prevElement can be nullptr....
Summary calculated over the requested range.
Helper structure used to transfer the data between the data and graphics layers.
Summary GetSummary(int64_t from, size_t samplesCount, const Summary &initializer) const noexcept
std::vector< float > mData
float * GetWritePointer(size_t floatsCount)
Gets a pointer to a data buffer enough to store floatsCount floats.
Type
Type of the data of the request.
@ Samples
Each element of the resulting array is a sample.
bool ContainsSample(int64_t sampleIndex) const noexcept
Checks if sample is in the range represented by this block.