forked from JS8Call-improved/JS8Call-improved
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDetector.cpp
More file actions
213 lines (177 loc) · 6.31 KB
/
Detector.cpp
File metadata and controls
213 lines (177 loc) · 6.31 KB
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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
#include "Detector.hpp"
#include <algorithm>
#include <cmath>
#include <QDateTime>
#include <QLoggingCategory>
#include <QMutexLocker>
#include <QtAlgorithms>
#include "commons.h"
#include "DriftingDateTime.h"
/******************************************************************************/
// FIR Filter Coefficients
/******************************************************************************/
namespace
{
// Filter coefficients for an FIR lowpass filter designed using ScopeFIR.
//
// fsample = 48000 Hz
// Ntaps = 49
// fc = 4500 Hz
// fstop = 6000 Hz
// Ripple = 1 dB
// Stop Atten = 40 dB
// fout = 12000 Hz
constexpr std::array LOWPASS
{
0.000861074040f, 0.010051920210f, 0.010161983649f, 0.011363155076f,
0.008706594219f, 0.002613872664f, -0.005202883094f, -0.011720748164f,
-0.013752163325f, -0.009431602741f, 0.000539063909f, 0.012636767098f,
0.021494659597f, 0.021951235065f, 0.011564169382f, -0.007656470131f,
-0.028965787341f, -0.042637874109f, -0.039203309748f, -0.013153301537f,
0.034320769178f, 0.094717832646f, 0.154224604789f, 0.197758325022f,
0.213715139513f, 0.197758325022f, 0.154224604789f, 0.094717832646f,
0.034320769178f, -0.013153301537f, -0.039203309748f, -0.042637874109f,
-0.028965787341f, -0.007656470131f, 0.011564169382f, 0.021951235065f,
0.021494659597f, 0.012636767098f, 0.000539063909f, -0.009431602741f,
-0.013752163325f, -0.011720748164f, -0.005202883094f, 0.002613872664f,
0.008706594219f, 0.011363155076f, 0.010161983649f, 0.010051920210f,
0.000861074040f
};
}
/******************************************************************************/
// Implementation
/******************************************************************************/
#include "moc_Detector.cpp"
Q_DECLARE_LOGGING_CATEGORY(detector_js8)
Detector::Detector(unsigned frameRate,
unsigned periodLengthInSeconds,
QObject * parent)
: AudioDevice (parent)
, m_frameRate (frameRate)
, m_period (periodLengthInSeconds)
, m_filter (LOWPASS)
{
clear();
}
void
Detector::setBlockSize (unsigned n)
{
m_samplesPerFFT = n;
}
bool
Detector::reset()
{
clear ();
// don't call base call reset because it calls seek(0) which causes
// a warning
return isOpen ();
}
void
Detector::clear()
{
#if JS8_RING_BUFFER
resetBufferPosition();
resetBufferContent();
#else
dec_data.params.kin = 0;
m_bufferPos = 0;
#endif
// fill buffer with zeros (G4WJS commented out because it might cause decoder hangs)
// qFill (dec_data.d2, dec_data.d2 + sizeof (dec_data.d2) / sizeof (dec_data.d2[0]), 0);
}
void
Detector::resetBufferPosition()
{
QMutexLocker mutex(&m_lock);
// set index to roughly where we are in time (1ms resolution)
qint64 const now = DriftingDateTime::currentMSecsSinceEpoch ();
unsigned const msInPeriod = (now % 86400000LL) % (m_period * 1000);
int const prevKin = dec_data.params.kin;
dec_data.params.kin = qMin ((msInPeriod * m_frameRate) / 1000, static_cast<unsigned> (sizeof (dec_data.d2) / sizeof (dec_data.d2[0])));
m_bufferPos = 0;
m_ns = secondInPeriod();
int const delta = dec_data.params.kin - prevKin;
qCDebug(detector_js8) << "advancing detector buffer from" << prevKin << "to" << dec_data.params.kin << "delta" << delta;
// rotate buffer moving the contents that were at prevKin to the new kin position
if (delta < 0)
{
std::rotate(std::begin(dec_data.d2),
std::begin(dec_data.d2) - delta,
std::end (dec_data.d2));
}
else
{
std::rotate(std::rbegin(dec_data.d2),
std::rbegin(dec_data.d2) + delta,
std::rend (dec_data.d2));
}
}
void
Detector::resetBufferContent()
{
QMutexLocker mutex(&m_lock);
std::fill(std::begin(dec_data.d2), std::end(dec_data.d2), 0);
qCDebug(detector_js8) << "clearing detector buffer content";
}
qint64
Detector::writeData(char const * const data,
qint64 const maxSize)
{
QMutexLocker mutex(&m_lock);
// When ns has wrapped around to zero, restart the buffers.
int const ns = secondInPeriod();
if(ns < m_ns) {
dec_data.params.kin = 0;
m_bufferPos = 0;
}
m_ns = ns;
// No torn frames.
Q_ASSERT (!(maxSize % static_cast<qint64>(bytesPerFrame())));
// These are in terms of input frames (not down sampled).
size_t const framesAcceptable = (sizeof(dec_data.d2) / sizeof(dec_data.d2[0]) - dec_data.params.kin) * Filter::NDOWN;
size_t const framesAccepted = qMin(static_cast<size_t>(maxSize /bytesPerFrame()), framesAcceptable);
if (framesAccepted < static_cast<size_t>(maxSize / bytesPerFrame()))
{
qCDebug(detector_js8) << "dropped " << maxSize / bytesPerFrame () - framesAccepted
<< " frames of data on the floor!"
<< dec_data.params.kin
<< ns;
}
for (auto remaining = framesAccepted;
remaining;)
{
size_t const numFramesProcessed = qMin(m_samplesPerFFT * Filter::NDOWN - m_bufferPos, remaining);
store (&data[(framesAccepted - remaining) * bytesPerFrame()],
numFramesProcessed,
&m_buffer[m_bufferPos]);
m_bufferPos += numFramesProcessed;
if (m_bufferPos == m_samplesPerFFT * Filter::NDOWN)
{
if (dec_data.params.kin >= 0 &&
dec_data.params.kin < static_cast<int>(JS8_NTMAX * 12000 - m_samplesPerFFT))
{
for (std::size_t i = 0; i < m_samplesPerFFT; ++i)
{
dec_data.d2[dec_data.params.kin++] = m_filter.downSample(&m_buffer[i * Filter::NDOWN]);
}
}
Q_EMIT framesWritten (dec_data.params.kin);
m_bufferPos = 0;
}
remaining -= numFramesProcessed;
}
// We drop any data past the end of the buffer on the floor
// until the next period starts
return maxSize;
}
unsigned
Detector::secondInPeriod() const
{
// we take the time of the data as the following assuming no latency
// delivering it to us (not true but close enough for us)
qint64 now (DriftingDateTime::currentMSecsSinceEpoch ());
unsigned secondInToday ((now % 86400000LL) / 1000);
return secondInToday % m_period;
}
/******************************************************************************/
Q_LOGGING_CATEGORY(detector_js8, "detector.js8", QtWarningMsg)