-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathreferenceReader.cpp
More file actions
354 lines (302 loc) · 12.1 KB
/
Copy pathreferenceReader.cpp
File metadata and controls
354 lines (302 loc) · 12.1 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
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
/******************************************************************************
* *
* Copyright (C) 2024 Acoustic Echo Cancellation Component *
* All Rights Reserved. *
* *
******************************************************************************/
#include "referenceReader.hpp"
#include <yarp/os/LogStream.h>
ReferenceReader::ReferenceReader()
: ReferenceReader("/aecComponent/reference:i", false)
{
}
ReferenceReader::ReferenceReader(const std::string &portName, bool logAecStats)
: m_portName(portName),
m_running(false),
m_shouldExit(false),
m_logAecStats(logAecStats)
{
m_slidingWindowIndex = 0;
m_audioPlayerDelayMs = 0.0f;
yInfo() << "[ReferenceReader::ReferenceReader] Constructed ReferenceReader for port:" << m_portName;
yInfo() << "[ReferenceReader::ReferenceReader] Logging AEC stats:" << (m_logAecStats ? "ENABLED" : "DISABLED");
}
ReferenceReader::~ReferenceReader()
{
// Make shutdown safe even if the caller forgets to call close().
close();
}
bool ReferenceReader::open()
{
// Reuse the configured default port name.
return open(m_portName);
}
bool ReferenceReader::open(const std::string &portName)
{
// If the reader was already running, stop it before reopening.
close();
m_portName = portName;
m_shouldExit = false;
m_running = false;
if (!m_referencePort.open(m_portName))
{
yError() << "[ReferenceReader::open] Unable to open reference port:" << m_portName;
return false;
}
if (!m_statusPort.open("/aecComponent/audioPlayerStatus:i"))
{
yError() << "[ReferenceReader::open] Unable to open status port: /aecComponent/audioPlayerStatus:i";
return false;
}
// Start a dedicated blocking reader thread so incoming data is queued.
m_readerThread = std::thread(&ReferenceReader::readerThreadFunction, this);
m_running = true;
yInfo() << "[ReferenceReader::open] Reference port opened:" << m_portName;
return true;
}
void ReferenceReader::close()
{
// Tell the reader loop to exit, then close the port so blocking reads unblock.
m_shouldExit = true;
m_referencePort.close();
if (m_readerThread.joinable())
{
m_readerThread.join();
}
m_running = false;
}
bool ReferenceReader::isRunning() const
{
// Expose the thread state without taking the mutex.
return m_running.load();
}
std::size_t ReferenceReader::queuedBlocks() const
{
// Read the queue size under lock so the count is consistent.
std::lock_guard<std::mutex> lock(m_mutex);
return m_queue.size();
}
std::string ReferenceReader::portName() const
{
// Return the port name currently associated with this reader.
return m_portName;
}
void ReferenceReader::setLogAecStats(bool logAecStats)
{
m_logAecStats = logAecStats;
}
void ReferenceReader::readerThreadFunction()
{
while (!m_shouldExit)
{
// Block on the YARP port and enqueue every sound block that arrives.
yarp::sig::Sound *sound = m_referencePort.read(true);
if (m_logAecStats)
{
yInfo() << "[ReferenceReader::readerThreadFunction] Received reference block with"
<< (sound ? sound->getSamples() : 0)
<< "samples at"
<< (sound ? sound->getFrequency() : 0)
<< "Hz";
}
if (!sound)
{
if (m_shouldExit)
{
break;
}
continue;
}
auto now = std::chrono::system_clock::now();
double soundSeconds = static_cast<double>(sound->getSamples()) / sound->getFrequency();
auto now_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
now.time_since_epoch())
.count();
if (m_logAecStats)
{
yInfo() << "[ReferenceReader::readerThreadFunction] Current time:"
<< now_ms << "ms since epoch";
yInfo() << "[ReferenceReader::readerThreadFunction] Sound duration:"
<< soundSeconds << "seconds";
yInfo() << "[ReferenceReader::readerThreadFunction] Enqueued reference block with" << sound->getSamples() << "samples at" << sound->getFrequency() << "Hz";
}
{
std::lock_guard<std::mutex> lock(m_mutex);
m_queue.push_back(*sound);
}
if (m_logAecStats)
{
yInfo() << "[ReferenceReader::readerThreadFunction] Added reference block to queue. Queue size is now" << m_queue.size();
}
}
}
yarp::sig::Sound ReferenceReader::getRecordedReferenceBlocks()
{
if (m_queue.empty())
{
if (m_logAecStats)
{
yInfo() << "[ReferenceReader::getRecordedReferenceBlocks] Reference queue is empty, returning empty Sound";
}
return yarp::sig::Sound();
}
if (!isPlayerActive())
{
if (m_logAecStats)
{
yInfo() << "[ReferenceReader::getRecordedReferenceBlocks] Audio player is not active, returning empty Sound";
}
return yarp::sig::Sound();
}
if (m_queue.front().getSamples() <= m_slidingWindowIndex)
{
m_slidingWindowIndex = 0;
std::lock_guard<std::mutex> lock(m_mutex);
// remove the first element of the m_queue
m_queue.pop_front();
if (m_logAecStats)
{
yInfo() << "[ReferenceReader::getRecordedReferenceBlocks] Removed first reference block from queue due to sliding window index exceeding its size. Queue size is now" << m_queue.size();
}
}
std::deque<yarp::sig::Sound> blocks;
{
std::lock_guard<std::mutex> lock(m_mutex);
blocks = std::deque<yarp::sig::Sound>(m_queue.begin(), m_queue.end());
}
yarp::sig::Sound blockNeighborhood;
yarp::sig::Sound firstBlock;
if (!blocks.empty())
{
firstBlock = blocks.front();
int soundFrequency = firstBlock.getFrequency();
// use consumedSamples to get a neighborhood of size 4000 samples at 16000 Hz from the first block, starting from the sample that has not been consumed yet. If the block has sound frequency different than 16000 Hz, scale the neighborhood size accordingly. If the block has less than 8000 samples remaining, return all remaining samples.
int neighborhoodSize = static_cast<int>(4000 * soundFrequency / 16000);
int startSample = m_slidingWindowIndex;
if (m_logAecStats)
{
yInfo() << "[ReferenceReader::getRecordedReferenceBlocks] First block has" << firstBlock.getSamples() << "samples at" << soundFrequency << "Hz, neighborhood size:" << neighborhoodSize << ", start sample:" << startSample;
}
int endSample = std::min(static_cast<int>(firstBlock.getSamples()), startSample + neighborhoodSize);
m_slidingWindowIndex += endSample - startSample;
blockNeighborhood.resize(endSample - startSample, firstBlock.getChannels());
blockNeighborhood.setFrequency(soundFrequency);
for (int i = startSample; i < endSample; ++i)
{
for (int j = 0; j < firstBlock.getChannels(); ++j)
{
blockNeighborhood.set(firstBlock.get(i, j), i - startSample, j);
}
}
if (static_cast<int>(blockNeighborhood.getSamples()) < neighborhoodSize)
{
int samplesToAppend = neighborhoodSize - static_cast<int>(blockNeighborhood.getSamples());
if (m_logAecStats)
{
yInfo() << "[ReferenceReader::getRecordedReferenceBlocks] Block neighborhood has less than" << neighborhoodSize << "samples, attempting to append next block in queue if it exists";
}
if (blocks.size() > 1)
{
{
std::lock_guard<std::mutex> lock(m_mutex);
// populate nextBlock with the first samplesToAppend samples of the second block in the queue, starting from sample 0
yarp::sig::Sound nextBlock = blocks.at(1).subSound(0, samplesToAppend);
m_queue.at(1) = blocks.at(1).subSound(samplesToAppend, blocks.at(1).getSamples());
}
blockNeighborhood.resize(neighborhoodSize, firstBlock.getChannels());
for (int i = 0; i < samplesToAppend; ++i)
{
for (int j = 0; j < firstBlock.getChannels(); ++j)
{
blockNeighborhood.set(blocks.at(1).get(i, j), static_cast<int>(blockNeighborhood.getSamples()) - samplesToAppend + i, j);
}
}
}
else // append null samples to blockNeighborhood until it has neighborhoodSize samples
{
if (m_logAecStats)
{
yInfo() << "[ReferenceReader::getRecordedReferenceBlocks] No next block in queue, appending null samples to block neighborhood";
}
int currentSamples = static_cast<int>(blockNeighborhood.getSamples());
blockNeighborhood.resize(neighborhoodSize, firstBlock.getChannels());
for (int i = currentSamples; i < neighborhoodSize; ++i)
{
for (int j = 0; j < firstBlock.getChannels(); ++j)
{
blockNeighborhood.set(0, i, j);
}
}
}
}
}
return blocks.empty() ? yarp::sig::Sound() : blockNeighborhood;
}
bool ReferenceReader::isPlayerActive()
{
yarp::sig::AudioPlayerStatus *status = m_statusPort.read(false);
if (status && status->current_buffer_size > 0)
{
// std::string statusStr = status->get(0).asString();
int firstBlockSize = 0;
if (m_logAecStats)
{
yInfo() << "[ReferenceReader::isPlayerActive] Received audio player status with" << status->current_buffer_size << "elements, assuming active";
}
{
std::lock_guard<std::mutex> lock(m_mutex);
if (!m_queue.empty())
{
firstBlockSize = m_queue.front().getSamples();
if (m_logAecStats)
{
yInfo() << "[ReferenceReader::isPlayerActive] First reference block in queue has" << firstBlockSize << "samples";
}
}
else
{
firstBlockSize = 0;
if (m_logAecStats)
{
yInfo() << "[ReferenceReader::isPlayerActive] Reference queue is empty";
}
}
}
int audioPlayerDelaySamples = firstBlockSize - static_cast<int>(status->current_buffer_size) - static_cast<int>(m_slidingWindowIndex);
if (audioPlayerDelaySamples > 0)
{
m_audioPlayerDelayMs = static_cast<float>(audioPlayerDelaySamples) * 1000.0f / 24000.0f; // Assuming 24kHz sample rate for delay calculation
}
return true;
}
else
{
if (m_logAecStats)
{
yInfo() << "[ReferenceReader::isPlayerActive] No audio player status received, assuming inactive";
}
if (m_slidingWindowIndex > 0)
{
if (m_logAecStats)
{
yInfo() << "[ReferenceReader::isPlayerActive] Resetting sliding window index to 0";
yInfo() << "[ReferenceReader::isPlayerActive] Removing samples from queue:" << m_slidingWindowIndex;
}
std::lock_guard<std::mutex> lock(m_mutex);
m_slidingWindowIndex = 0;
if (!m_queue.empty())
{
m_queue.pop_front();
if (m_logAecStats)
{
yInfo() << "[ReferenceReader::isPlayerActive] Removed first reference block from queue. Queue size is now" << m_queue.size();
}
}
}
return false;
}
}
float ReferenceReader::getLastEstimatedAudioPlayerDelay()
{
return m_audioPlayerDelayMs;
}