MMDVM_CM/USRP2DMR/DelayBuffer.cpp
2021-04-10 16:13:33 -04:00

160 lines
3.7 KiB
C++

/*
* Copyright (C) 2018 by Jonathan Naylor G4KLX
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "DelayBuffer.h"
#include "DMRDefines.h"
#include "Log.h"
#include <cstdio>
#include <cassert>
#include <cstring>
CDelayBuffer::CDelayBuffer(const std::string& name, unsigned int blockSize, unsigned int blockTime, unsigned int jitterTime, bool debug) :
m_name(name),
m_blockSize(blockSize),
m_blockTime(blockTime),
m_debug(debug),
m_timer(1000U, 0U, jitterTime),
m_stopWatch(),
m_running(false),
m_buffer(5000U, name.c_str()),
m_outputCount(0U),
m_lastData(NULL),
m_lastDataLength(0U),
m_lastDataValid(false)
{
assert(blockSize > 0U);
assert(blockTime > 0U);
assert(jitterTime > 0U);
m_lastData = new unsigned char[m_blockSize];
reset();
}
CDelayBuffer::~CDelayBuffer()
{
delete[] m_lastData;
}
bool CDelayBuffer::addData(const unsigned char* data, unsigned int length)
{
assert(data != NULL);
assert(length > 0U);
assert(length == m_blockSize);
if (m_debug)
LogDebug("%s, DelayBuffer: appending data", m_name.c_str());
m_buffer.addData(data, length);
if (!m_timer.isRunning()) {
if (m_debug)
LogDebug("%s, DelayBuffer: starting the timer from append", m_name.c_str());
m_timer.start();
}
return true;
}
B_STATUS CDelayBuffer::getData(unsigned char* data, unsigned int& length)
{
assert(data != NULL);
if (!m_running)
return BS_NO_DATA;
unsigned int needed = m_stopWatch.elapsed() / m_blockTime + 2U;
if (needed <= m_outputCount)
return BS_NO_DATA;
if (!m_buffer.isEmpty()) {
if (m_debug)
LogDebug("%s, DelayBuffer: returning data, elapsed=%ums", m_name.c_str(), m_stopWatch.elapsed());
if (m_buffer.getData(data, m_blockSize)) {
length = m_blockSize;
// Save this data in case no more data is available next time
::memcpy(m_lastData, data, length);
m_lastDataLength = length;
m_lastDataValid = true;
m_outputCount++;
return BS_DATA;
}
}
if (m_debug)
LogDebug("%s, DelayBuffer: no data available, elapsed=%ums", m_name.c_str(), m_stopWatch.elapsed());
// Return the last data frame if we have it
if (m_lastDataLength > 0U) {
if(m_lastDataValid) {
if (m_debug)
LogDebug("%s, DelayBuffer: returning the last received frame", m_name.c_str());
// Copy last valid data
::memcpy(data, m_lastData, m_lastDataLength);
} else {
if (m_debug)
LogDebug("%s, DelayBuffer: returning a silence frame", m_name.c_str());
// Copy last network header data
::memcpy(data, m_lastData, 20U);
// We only need to copy silence AMBE data, don't care about LC data for next YSF conversion stage
::memcpy(data + 20U, DMR_SILENCE_DATA, 33U);
data[53U] = 0U;
data[54U] = 0U;
}
m_lastDataValid = false;
length = m_lastDataLength;
m_outputCount++;
return BS_MISSING;
}
return BS_NO_DATA;
}
void CDelayBuffer::reset()
{
m_buffer.clear();
m_lastDataLength = 0U;
m_outputCount = 0U;
m_timer.stop();
m_running = false;
}
void CDelayBuffer::clock(unsigned int ms)
{
m_timer.clock(ms);
if (m_timer.isRunning() && m_timer.hasExpired()) {
if (!m_running) {
m_stopWatch.start();
m_running = true;
}
}
}