Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mpc-hc/sanear.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Marsev <alex.marsev@gmail.com>2015-10-11 18:25:01 +0300
committerAlex Marsev <alex.marsev@gmail.com>2015-10-12 17:14:19 +0300
commitf1e11f7bfce356ac975179999338ad69509135b2 (patch)
tree666dd02fb13933aac81f3ae096868d2b0cc6d5c8
parent1a4c3f17e3b24312b1e41f6b03fec0bb6d60f868 (diff)
Implement variable rate in DspRate
This basically replaces zita-resampler with soxr in tasks where variable rate resampling is necessary.
-rw-r--r--src/AudioRenderer.cpp6
-rw-r--r--src/AudioRenderer.h4
-rw-r--r--src/DspRate.cpp134
-rw-r--r--src/DspRate.h9
4 files changed, 129 insertions, 24 deletions
diff --git a/src/AudioRenderer.cpp b/src/AudioRenderer.cpp
index 7ae04d4..0c4ca8c 100644
--- a/src/AudioRenderer.cpp
+++ b/src/AudioRenderer.cpp
@@ -583,7 +583,7 @@ namespace SaneAudioRenderer
}
// Correct the rest with variable rate.
- m_dspRealtimeRate.Adjust(padTime);
+ m_dspRate.Adjust(padTime);
m_myClock.OffsetSlavedClock(-padTime);
}
else if (remaining > latency)
@@ -614,7 +614,7 @@ namespace SaneAudioRenderer
}
// Correct the rest with variable rate.
- m_dspRealtimeRate.Adjust(-dropTime);
+ m_dspRate.Adjust(-dropTime);
m_myClock.OffsetSlavedClock(dropTime);
}
}
@@ -639,7 +639,7 @@ namespace SaneAudioRenderer
m_dspMatrix.Initialize(inChannels, inMask, outChannels, outMask);
m_dspRate.Initialize(m_live || m_externalClock, inRate, outRate, outChannels);
- m_dspRealtimeRate.Initialize(m_live || m_externalClock, inRate, outRate, outChannels);
+ //m_dspRealtimeRate.Initialize(m_live || m_externalClock, inRate, outRate, outChannels);
m_dspTempo.Initialize(m_rate, outRate, outChannels);
m_dspCrossfeed.Initialize(m_settings, outRate, outChannels, outMask);
m_dspLimiter.Initialize(outRate, outChannels, m_device->IsExclusive());
diff --git a/src/AudioRenderer.h b/src/AudioRenderer.h
index 7dd8138..91522ac 100644
--- a/src/AudioRenderer.h
+++ b/src/AudioRenderer.h
@@ -75,7 +75,7 @@ namespace SaneAudioRenderer
{
f(&m_dspMatrix);
f(&m_dspRate);
- f(&m_dspRealtimeRate);
+ //f(&m_dspRealtimeRate);
f(&m_dspTempo);
f(&m_dspCrossfeed);
f(&m_dspVolume);
@@ -108,7 +108,7 @@ namespace SaneAudioRenderer
DspMatrix m_dspMatrix;
DspRate m_dspRate;
- DspRealtimeRate m_dspRealtimeRate;
+ //DspRealtimeRate m_dspRealtimeRate;
DspTempo m_dspTempo;
DspCrossfeed m_dspCrossfeed;
DspVolume m_dspVolume;
diff --git a/src/DspRate.cpp b/src/DspRate.cpp
index 4923e11..5fe042c 100644
--- a/src/DspRate.cpp
+++ b/src/DspRate.cpp
@@ -64,12 +64,23 @@ namespace SaneAudioRenderer
m_outputRate = outputRate;
m_channels = channels;
- if (!variable && inputRate != outputRate)
+ m_variableInputFrames = 0;
+ m_variableOutputFrames = 0;
+ m_variableDelay = 0;
+
+ m_adjustTime = 0;
+
+ if (variable)
+ {
+ m_state = State::Variable;
+ CreateBackend();
+ assert(m_soxrv);
+ }
+ else if (inputRate != outputRate)
{
- auto ioSpec = soxr_io_spec(SOXR_FLOAT32_I, SOXR_FLOAT32_I);
- auto qualitySpec = soxr_quality_spec(SOXR_HQ, 0);
- m_soxrc = soxr_create(inputRate, outputRate, channels, nullptr, &ioSpec, &qualitySpec, nullptr);
m_state = State::Constant;
+ CreateBackend();
+ assert(m_soxrc);
}
}
@@ -85,8 +96,36 @@ namespace SaneAudioRenderer
if (!soxr || chunk.IsEmpty())
return;
+ if (m_state == State::Variable && m_variableDelay > 0)
+ {
+ uint64_t inputPosition = llMulDiv(m_variableOutputFrames, m_inputRate, m_outputRate, 0);
+ int64_t adjustedFrames = inputPosition + m_variableDelay - m_variableInputFrames;
+
+ REFERENCE_TIME adjustTime = m_adjustTime - llMulDiv(adjustedFrames, OneSecond, m_inputRate, 0);
+
+ double ratio = (double)m_inputRate * 4 / (m_outputRate * (4 + (double)adjustTime / OneSecond));
+
+ // TODO: decrease jitter
+
+ soxr_set_io_ratio(m_soxrv, ratio, m_outputRate / 1000);
+ }
+
DspChunk output = ProcessChunk(soxr, chunk);
+ if (m_state == State::Variable)
+ {
+ m_variableInputFrames += chunk.GetFrameCount();
+ m_variableOutputFrames += output.GetFrameCount();
+
+ // soxr_delay() method is not implemented for variable rate conversion yet,
+ // but the delay stays more or less constant and we can calculate it in a roundabout way.
+ if (m_variableDelay == 0 && m_variableOutputFrames > 0)
+ {
+ uint64_t inputPosition = llMulDiv(m_variableOutputFrames, m_inputRate, m_outputRate, 0);
+ m_variableDelay = m_variableInputFrames - inputPosition;
+ }
+ }
+
FinishStateTransition(output, chunk, false);
chunk = std::move(output);
@@ -106,6 +145,20 @@ namespace SaneAudioRenderer
chunk = std::move(output);
}
+ void DspRate::Adjust(REFERENCE_TIME time)
+ {
+ if (m_state != State::Variable)
+ {
+ m_state = State::Variable;
+ CreateBackend();
+ assert(m_soxrv);
+
+ m_inStateTransition = true;
+ }
+
+ m_adjustTime += time;
+ }
+
DspChunk DspRate::ProcessChunk(soxr_t soxr, DspChunk& chunk)
{
assert(soxr);
@@ -178,8 +231,16 @@ namespace SaneAudioRenderer
if (!m_transitionCorrelation.first)
m_transitionCorrelation = {true, (size_t)std::round(soxr_delay(m_soxrc))};
- DspChunk::MergeChunks(second, eos ? ProcessEosChunk(m_soxrc, unprocessedChunk) :
- ProcessChunk(m_soxrc, unprocessedChunk));
+ if (m_transitionCorrelation.second > 0)
+ {
+ DspChunk::MergeChunks(second, eos ? ProcessEosChunk(m_soxrc, unprocessedChunk) :
+ ProcessChunk(m_soxrc, unprocessedChunk));
+ }
+ else
+ {
+ // Nothing to flush from constant rate conversion buffer.
+ m_inStateTransition = false;
+ }
}
else
{
@@ -188,21 +249,24 @@ namespace SaneAudioRenderer
DspChunk::MergeChunks(second, unprocessedChunk);
}
- const size_t transitionFrames = m_outputRate / 1000; // 1ms
-
// Cross-fade.
- if (first.GetFrameCount() >= transitionFrames &&
- second.GetFrameCount() >= m_transitionCorrelation.second + transitionFrames)
+ if (m_inStateTransition)
{
- second.ShrinkHead(second.GetFrameCount() - m_transitionCorrelation.second);
- Crossfade(first, second, transitionFrames);
- processedChunk = std::move(first);
- m_inStateTransition = false;
- }
- else if (eos)
- {
- processedChunk = std::move(second);
- m_inStateTransition = false;
+ const size_t transitionFrames = m_outputRate / 1000; // 1ms
+
+ if (first.GetFrameCount() >= transitionFrames &&
+ second.GetFrameCount() >= m_transitionCorrelation.second + transitionFrames)
+ {
+ second.ShrinkHead(second.GetFrameCount() - m_transitionCorrelation.second);
+ Crossfade(first, second, transitionFrames);
+ processedChunk = std::move(first);
+ m_inStateTransition = false;
+ }
+ else if (eos)
+ {
+ processedChunk = std::move(second);
+ m_inStateTransition = false;
+ }
}
if (!m_inStateTransition)
@@ -216,6 +280,38 @@ namespace SaneAudioRenderer
unprocessedChunk = {};
}
+ void DspRate::CreateBackend()
+ {
+ assert(m_state != State::Passthrough);
+ assert(m_inputRate > 0);
+ assert(m_outputRate > 0);
+ assert(m_channels > 0);
+
+ if (m_state == State::Variable)
+ {
+ assert(!m_soxrv);
+
+ auto ioSpec = soxr_io_spec(SOXR_FLOAT32_I, SOXR_FLOAT32_I);
+ auto qualitySpec = soxr_quality_spec(SOXR_HQ, SOXR_VR);
+ m_soxrv = soxr_create(m_inputRate * 2, m_outputRate, m_channels, nullptr, &ioSpec, &qualitySpec, nullptr);
+
+ soxr_set_io_ratio(m_soxrv, (double)m_inputRate / m_outputRate, 0);
+
+ m_variableInputFrames = 0;
+ m_variableOutputFrames = 0;
+ m_variableDelay = 0;
+ }
+ else if (m_state == State::Constant)
+ {
+ assert(m_inputRate != m_outputRate);
+ assert(!m_soxrc);
+
+ auto ioSpec = soxr_io_spec(SOXR_FLOAT32_I, SOXR_FLOAT32_I);
+ auto qualitySpec = soxr_quality_spec(SOXR_HQ, 0);
+ m_soxrc = soxr_create(m_inputRate, m_outputRate, m_channels, nullptr, &ioSpec, &qualitySpec, nullptr);
+ }
+ }
+
soxr_t DspRate::GetBackend()
{
return (m_state == State::Constant) ? m_soxrc :
diff --git a/src/DspRate.h b/src/DspRate.h
index 0124a07..48e77fb 100644
--- a/src/DspRate.h
+++ b/src/DspRate.h
@@ -25,6 +25,8 @@ namespace SaneAudioRenderer
void Process(DspChunk& chunk) override;
void Finish(DspChunk& chunk) override;
+ void Adjust(REFERENCE_TIME time);
+
private:
enum class State
@@ -39,6 +41,7 @@ namespace SaneAudioRenderer
void FinishStateTransition(DspChunk& processedChunk, DspChunk& unprocessedChunk, bool eos);
+ void CreateBackend();
soxr_t GetBackend();
void DestroyBackends();
@@ -54,5 +57,11 @@ namespace SaneAudioRenderer
uint32_t m_inputRate = 0;
uint32_t m_outputRate = 0;
uint32_t m_channels = 0;
+
+ uint64_t m_variableInputFrames = 0;
+ uint64_t m_variableOutputFrames = 0;
+ uint64_t m_variableDelay = 0; // In input samples.
+
+ REFERENCE_TIME m_adjustTime = 0; // Negative time - less samples, positive time - more samples.
};
}