Audaspace: port changes from upstream.

More accurate seeking for animated time stretching.
This commit is contained in:
Jörg Müller
2025-09-09 08:12:03 +02:00
parent 6066a0f83c
commit 719e207fcc
11 changed files with 106 additions and 40 deletions

View File

@@ -213,9 +213,9 @@ Sequence_setAnimationData(Sequence* self, PyObject* args)
}
py_data_len= PySequence_Size(py_data);
std::vector<float> data;
data.resize(py_data_len);
data.reserve(py_data_len);
PyObject* py_value;
float value;

View File

@@ -105,7 +105,7 @@ SequenceEntry_setAnimationData(SequenceEntry* self, PyObject* args)
py_data_len= PySequence_Size(py_data);
std::vector<float> data;
data.resize(py_data_len);
data.reserve(py_data_len);
PyObject* py_value;
float value;

View File

@@ -38,16 +38,6 @@ AUD_NAMESPACE_BEGIN
class AUD_API TimeStretchPitchScaleReader : public EffectReader
{
private:
/**
* The current position.
*/
int m_position;
/**
* Whether the reader has reached the end of stream.
*/
bool m_finishedReader;
/**
* The input buffer for the reader.
*/
@@ -63,11 +53,6 @@ private:
*/
std::vector<sample_t*> m_channelData;
/**
* Rubberband stretcher.
*/
std::unique_ptr<RubberBandStretcher> m_stretcher;
/**
* Number of samples that need to be dropped at the beginning or after a seek.
*/
@@ -77,11 +62,27 @@ private:
TimeStretchPitchScaleReader(const TimeStretchPitchScaleReader&) = delete;
TimeStretchPitchScaleReader& operator=(const TimeStretchPitchScaleReader&) = delete;
protected:
/**
* Feeds the number of required zeo samples to the stretcher and queries the amount of samples to drop.
* Feeds the number of required zero samples to the stretcher and queries the amount of samples to drop.
*/
void reset();
/**
* Rubberband stretcher.
*/
std::unique_ptr<RubberBandStretcher> m_stretcher;
/**
* The current position.
*/
int m_position;
/**
* Whether the reader has reached the end of stream.
*/
bool m_finishedReader;
public:
/**
* Creates a new stretcher reader.

View File

@@ -130,7 +130,7 @@ public:
void read(float position, float* out);
/**
* Reads the property's value at the specified position, assuming there is exactly one value
* Reads the property's value at the specified position, assuming there is exactly one value.
* \param position The position in the animation in frames.
* \return The value at the position.
*/
@@ -141,6 +141,11 @@ public:
* \return Whether the property is animated.
*/
bool isAnimated() const;
/**
* Returns this object cast as a Buffer.
*/
const Buffer& getBuffer();
};
AUD_NAMESPACE_END

View File

@@ -58,7 +58,12 @@ public:
/**
* Returns the pointer to the buffer in memory.
*/
sample_t* getBuffer() const;
const sample_t* getBuffer() const;
/**
* Returns the pointer to the buffer in memory.
*/
sample_t* getBuffer();
/**
* Returns the size of the buffer in bytes.

View File

@@ -59,7 +59,12 @@ public:
/**
* Returns the pointer to the ring buffer in memory.
*/
sample_t* getBuffer() const;
const sample_t* getBuffer() const;
/**
* Returns the pointer to the ring buffer in memory.
*/
sample_t* getBuffer();
/**
* Returns the size of the ring buffer in bytes.

View File

@@ -45,14 +45,52 @@ void AnimateableTimeStretchPitchScaleReader::read(int& length, bool& eos, sample
void AnimateableTimeStretchPitchScaleReader::seek(int position)
{
double time = double(position) / double(m_reader->getSpecs().rate);
float frame = time * m_fps;
const double sampleRate = double(m_reader->getSpecs().rate);
const double samplesPerFrame = sampleRate / m_fps;
const double frame = double(position) / samplesPerFrame;
float timeRatio = m_timeStretch->readSingle(frame);
setTimeRatio(timeRatio);
float pitchScale = m_pitchScale->readSingle(frame);
setPitchScale(pitchScale);
TimeStretchPitchScaleReader::seek(position);
const int totalFrames = static_cast<int>(frame);
float ratio = 1.0f;
double inputSamplePos = 0.0;
const sample_t* animationSamples = m_timeStretch->getBuffer().getBuffer();
const int bufferFrames = m_timeStretch->getBuffer().getSize() / (sizeof(sample_t) * m_timeStretch->getCount());
for(int frameIndex = 0; frameIndex < std::min(bufferFrames, totalFrames); frameIndex++)
{
ratio = std::max(animationSamples[frameIndex], 1.0f / 256.0f);
inputSamplePos += samplesPerFrame / ratio;
}
if(totalFrames > bufferFrames)
{
// The position is past the end of animation buffer and so use the last read ratio
// This already includes the fractional frame
inputSamplePos += (samplesPerFrame * (frame - bufferFrames)) / ratio;
}
else
{
// The position is before the end of the animation buffer and so read one last time for the remaining fractional frame
double remainderFrame = frame - totalFrames;
float remainderRatio = std::max(m_timeStretch->readSingle(frame), 1.0f / 256.0f);
inputSamplePos += (samplesPerFrame * remainderFrame) / remainderRatio;
}
m_reader->seek(static_cast<int>(inputSamplePos));
m_finishedReader = false;
m_stretcher->reset();
reset();
m_position = position;
}
AUD_NAMESPACE_END

View File

@@ -43,7 +43,7 @@ void TimeStretchPitchScaleReader::reset()
}
TimeStretchPitchScaleReader::TimeStretchPitchScaleReader(std::shared_ptr<IReader> reader, double timeRatio, double pitchScale, StretcherQuality quality, bool preserveFormant) :
EffectReader(reader), m_position(0), m_finishedReader(false), m_channelData(reader->getSpecs().channels), m_deinterleaved(reader->getSpecs().channels)
EffectReader(reader), m_position(0), m_finishedReader(false), m_deinterleaved(reader->getSpecs().channels), m_channelData(reader->getSpecs().channels)
{
if (pitchScale < 1.0 / 256.0 || pitchScale > 256.0)
AUD_THROW(StateException, "The pitch scale must be between 1/256 and 256");
@@ -82,9 +82,6 @@ void TimeStretchPitchScaleReader::read(int& length, bool& eos, sample_t* buffer)
int samplesize = AUD_SAMPLE_SIZE(m_reader->getSpecs());
int channels = m_reader->getSpecs().channels;
sample_t* buf;
int samplesRead = 0;
eos = false;

View File

@@ -26,13 +26,13 @@ AUD_NAMESPACE_BEGIN
AnimateableProperty::AnimateableProperty(int count) :
Buffer(count * sizeof(float)), m_count(count), m_isAnimated(false)
{
std::memset(getBuffer(), 0, count * sizeof(float));
std::memset(Buffer::getBuffer(), 0, count * sizeof(float));
}
AnimateableProperty::AnimateableProperty(int count, float value) :
Buffer(count * sizeof(float)), m_count(count), m_isAnimated(false)
{
sample_t* buf = getBuffer();
sample_t* buf = Buffer::getBuffer();
for(int i = 0; i < count; i++)
buf[i] = value;
@@ -40,7 +40,7 @@ AnimateableProperty::AnimateableProperty(int count, float value) :
void AnimateableProperty::updateUnknownCache(int start, int end)
{
float* buf = getBuffer();
float* buf = Buffer::getBuffer();
// we could do a better interpolation than zero order, but that doesn't work with Blender's animation system
// as frames are only written when changing, so to support jumps, we need zero order interpolation here.
@@ -63,13 +63,13 @@ void AnimateableProperty::write(const float* data)
m_isAnimated = false;
m_unknown.clear();
std::memcpy(getBuffer(), data, m_count * sizeof(float));
std::memcpy(Buffer::getBuffer(), data, m_count * sizeof(float));
}
void AnimateableProperty::writeConstantRange(const float* data, int position_start, int position_end)
{
assureSize(position_end * m_count * sizeof(float), true);
float* buffer = getBuffer();
float* buffer = Buffer::getBuffer();
for(int i = position_start; i < position_end; i++)
{
@@ -92,7 +92,7 @@ void AnimateableProperty::write(const float* data, int position, int count)
assureSize((count + position) * m_count * sizeof(float), true);
float* buf = getBuffer();
float* buf = Buffer::getBuffer();
std::memcpy(buf + position * m_count, data, count * m_count * sizeof(float));
@@ -172,7 +172,7 @@ void AnimateableProperty::read(float position, float* out)
if(!m_isAnimated)
{
std::memcpy(out, getBuffer(), m_count * sizeof(float));
std::memcpy(out, Buffer::getBuffer(), m_count * sizeof(float));
return;
}
@@ -193,7 +193,7 @@ void AnimateableProperty::read(float position, float* out)
if(t == 0)
{
std::memcpy(out, getBuffer() + int(std::floor(position)) * m_count, m_count * sizeof(float));
std::memcpy(out, Buffer::getBuffer() + int(std::floor(position)) * m_count, m_count * sizeof(float));
}
else
{
@@ -202,7 +202,7 @@ void AnimateableProperty::read(float position, float* out)
float t3 = t2 * t;
float m0, m1;
float* p0;
float* p1 = getBuffer() + pos;
float* p1 = Buffer::getBuffer() + pos;
float* p2;
float* p3;
last *= m_count;
@@ -242,4 +242,9 @@ bool AnimateableProperty::isAnimated() const
return m_isAnimated;
}
const Buffer& AnimateableProperty::getBuffer()
{
return *this;
}
AUD_NAMESPACE_END

View File

@@ -36,7 +36,12 @@ Buffer::~Buffer()
std::free(m_buffer);
}
sample_t* Buffer::getBuffer() const
const sample_t* Buffer::getBuffer() const
{
return (sample_t*) ALIGN(m_buffer);
}
sample_t* Buffer::getBuffer()
{
return (sample_t*) ALIGN(m_buffer);
}

View File

@@ -32,7 +32,12 @@ RingBuffer::RingBuffer(int size) :
{
}
sample_t* RingBuffer::getBuffer() const
const sample_t* RingBuffer::getBuffer() const
{
return m_buffer.getBuffer();
}
sample_t* RingBuffer::getBuffer()
{
return m_buffer.getBuffer();
}