1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
|
#include "pch.h"
#include "DspTempo.h"
namespace SaneAudioRenderer
{
void DspTempo::Initialize(double tempo, uint32_t rate, uint32_t channels)
{
m_stouch.clear();
m_active = false;
m_rate = rate;
m_channels = channels;
m_tempo = tempo;
m_ftempo1 = (float)tempo;
m_ftempo2 = std::nexttoward(m_ftempo1, tempo);
m_ftempo = m_ftempo1;
m_outSamples1 = 0;
m_outSamples2 = 0;
if (tempo != 1.0)
{
m_stouch.setSampleRate(rate);
m_stouch.setChannels(channels);
m_stouch.setTempo(m_ftempo);
//m_stouch.setSetting(SETTING_SEQUENCE_MS, 40);
//m_stouch.setSetting(SETTING_SEEKWINDOW_MS, 15);
//m_stouch.setSetting(SETTING_OVERLAP_MS, 8);
m_active = true;
}
}
bool DspTempo::Active()
{
return m_active;
}
void DspTempo::Process(DspChunk& chunk)
{
if (!m_active || chunk.IsEmpty())
return;
assert(chunk.GetRate() == m_rate);
assert(chunk.GetChannelCount() == m_channels);
// DirectShow speed is in double precision, SoundTouch operates in single.
// We have to adjust it dynamically.
AdjustTempo();
DspChunk::ToFloat(chunk);
m_stouch.putSamples((const float*)chunk.GetData(), (uint32_t)chunk.GetFrameCount());
DspChunk output(DspFormat::Float, m_channels, m_stouch.numSamples(), m_rate);
uint32_t done = m_stouch.receiveSamples((float*)output.GetData(), (uint32_t)output.GetFrameCount());
assert(done == output.GetFrameCount());
output.ShrinkTail(done);
auto& outSamples = (m_ftempo == m_ftempo1) ? m_outSamples1 : m_outSamples2;
outSamples += done;
chunk = std::move(output);
}
void DspTempo::Finish(DspChunk& chunk)
{
if (!m_active)
return;
Process(chunk);
m_stouch.flush();
uint32_t undone = m_stouch.numSamples();
if (undone > 0)
{
DspChunk output(DspFormat::Float, m_channels, chunk.GetFrameCount() + undone, m_rate);
if (!chunk.IsEmpty())
memcpy(output.GetData(), chunk.GetData(), chunk.GetSize());
m_stouch.flush();
uint32_t done = m_stouch.receiveSamples((float*)output.GetData() + chunk.GetSampleCount(), undone);
assert(done == undone);
output.ShrinkTail(chunk.GetFrameCount() + done);
chunk = std::move(output);
}
}
void DspTempo::AdjustTempo()
{
if (m_tempo != m_ftempo)
{
assert(m_tempo != m_ftempo1);
assert(m_tempo != m_ftempo2);
double ratio21 = std::abs((m_tempo - m_ftempo1) / (m_tempo - m_ftempo2));
if (m_ftempo != m_ftempo2 &&
m_outSamples1 * ratio21 - m_outSamples2 > 60 * m_rate)
{
DebugOut(ClassName(this), "adjusting for float/double imprecision (2), ratio", ratio21);
m_ftempo = m_ftempo2;
m_stouch.setTempo(m_ftempo);
}
else if (m_ftempo != m_ftempo1 &&
m_outSamples2 - m_outSamples1 * ratio21 > 60 * m_rate)
{
DebugOut(ClassName(this), "adjusting for float/double imprecision (1), ratio", ratio21);
m_ftempo = m_ftempo1;
m_stouch.setTempo(m_ftempo);
}
}
}
}
|