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
|
#include "waveform/visualplayposition.h"
#include <QtDebug>
#include "control/controlproxy.h"
#include "control/controlobject.h"
#include "util/math.h"
#include "waveform/vsyncthread.h"
namespace {
// The offset is limited to two callback intervals.
// This should be sufficiant to compensate jitter,
// but does not continue in case of underflows.
constexpr int kMaxOffsetBufferCnt = 2;
constexpr int kMicrosPerMillis = 1000; // 1 ms contains 1000 µs
} // anonymous namespace
//static
QMap<QString, QWeakPointer<VisualPlayPosition> > VisualPlayPosition::m_listVisualPlayPosition;
PerformanceTimer VisualPlayPosition::m_timeInfoTime;
double VisualPlayPosition::m_dCallbackEntryToDacSecs = 0;
VisualPlayPosition::VisualPlayPosition(const QString& key)
: m_valid(false),
m_key(key) {
m_audioBufferSize = new ControlProxy(
"[Master]", "audio_buffer_size", this);
m_audioBufferSize->connectValueChanged(
SLOT(slotAudioBufferSizeChanged(double)));
m_audioBufferMicros = static_cast<int>(m_audioBufferSize->get() * kMicrosPerMillis);
}
VisualPlayPosition::~VisualPlayPosition() {
m_listVisualPlayPosition.remove(m_key);
}
void VisualPlayPosition::set(double playPos, double rate,
double positionStep, double pSlipPosition) {
VisualPlayPositionData data;
data.m_referenceTime = m_timeInfoTime;
data.m_callbackEntrytoDac = m_dCallbackEntryToDacSecs * 1000000; // s to µs
data.m_enginePlayPos = playPos;
data.m_rate = rate;
data.m_positionStep = positionStep;
data.m_pSlipPosition = pSlipPosition;
// Atomic write
m_data.setValue(data);
m_valid = true;
}
double VisualPlayPosition::getAtNextVSync(VSyncThread* vsyncThread) {
//static double testPos = 0;
//testPos += 0.000017759; //0.000016608; // 1.46257e-05;
//return testPos;
if (m_valid) {
VisualPlayPositionData data = m_data.getValue();
int refToVSync = vsyncThread->fromTimerToNextSyncMicros(data.m_referenceTime);
int offset = refToVSync - data.m_callbackEntrytoDac;
offset = math_min(offset, m_audioBufferMicros * kMaxOffsetBufferCnt);
double playPos = data.m_enginePlayPos; // load playPos for the first sample in Buffer
// add the offset for the position of the sample that will be transferred to the DAC
// When the next display frame is displayed
playPos += data.m_positionStep * offset * data.m_rate / m_audioBufferMicros;
//qDebug() << "playPos" << playPos << offset;
return playPos;
}
return -1;
}
void VisualPlayPosition::getPlaySlipAt(int fromNowMicros, double* playPosition, double* slipPosition) {
//static double testPos = 0;
//testPos += 0.000017759; //0.000016608; // 1.46257e-05;
//return testPos;
if (m_valid) {
VisualPlayPositionData data = m_data.getValue();
int elapsed = data.m_referenceTime.elapsed().toIntegerMicros();
int dacFromNow = elapsed - data.m_callbackEntrytoDac;
int offset = dacFromNow - fromNowMicros;
offset = math_min(offset, m_audioBufferMicros * kMaxOffsetBufferCnt);
double playPos = data.m_enginePlayPos; // load playPos for the first sample in Buffer
playPos += data.m_positionStep * offset * data.m_rate / m_audioBufferMicros;
*playPosition = playPos;
*slipPosition = data.m_pSlipPosition;
}
}
double VisualPlayPosition::getEnginePlayPos() {
if (m_valid) {
VisualPlayPositionData data = m_data.getValue();
return data.m_enginePlayPos;
} else {
return -1;
}
}
void VisualPlayPosition::slotAudioBufferSizeChanged(double sizeMillis) {
m_audioBufferMicros = static_cast<int>(sizeMillis * kMicrosPerMillis);
}
//static
QSharedPointer<VisualPlayPosition> VisualPlayPosition::getVisualPlayPosition(QString group) {
QSharedPointer<VisualPlayPosition> vpp = m_listVisualPlayPosition.value(group);
if (vpp.isNull()) {
vpp = QSharedPointer<VisualPlayPosition>(new VisualPlayPosition(group));
m_listVisualPlayPosition.insert(group, vpp);
}
return vpp;
}
//static
void VisualPlayPosition::setCallbackEntryToDacSecs(double secs, const PerformanceTimer& time) {
// the time is valid only just NOW, so measure the time from NOW for
// later correction
m_timeInfoTime = time;
m_dCallbackEntryToDacSecs = secs;
}
|