mirror of
https://github.com/swift-project/pilotclient.git
synced 2026-04-09 05:28:09 +08:00
Ref T730, namespace for BlackCore::Afv
* added namespace * removed some *.pri files and added files to blackcore.pro * added copyright etc.
This commit is contained in:
committed by
Mat Sutcliffe
parent
d064da13b5
commit
384aa3ce19
@@ -1,67 +1,82 @@
|
||||
/* Copyright (C) 2019
|
||||
* swift project Community / Contributors
|
||||
*
|
||||
* This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level
|
||||
* directory of this distribution. No part of swift project, including this file, may be copied, modified, propagated,
|
||||
* or distributed except according to the terms contained in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "callsigndelaycache.h"
|
||||
|
||||
void CallsignDelayCache::initialise(const QString &callsign)
|
||||
namespace BlackCore
|
||||
{
|
||||
if (!m_delayCache.contains(callsign))
|
||||
m_delayCache[callsign] = delayDefault;
|
||||
|
||||
if (!successfulTransmissionsCache.contains(callsign))
|
||||
successfulTransmissionsCache[callsign] = 0;
|
||||
}
|
||||
|
||||
int CallsignDelayCache::get(const QString &callsign)
|
||||
{
|
||||
return m_delayCache[callsign];
|
||||
}
|
||||
|
||||
void CallsignDelayCache::underflow(const QString &callsign)
|
||||
{
|
||||
if (!successfulTransmissionsCache.contains(callsign))
|
||||
return;
|
||||
|
||||
successfulTransmissionsCache[callsign] = 0;
|
||||
increaseDelayMs(callsign);
|
||||
}
|
||||
|
||||
void CallsignDelayCache::success(const QString &callsign)
|
||||
{
|
||||
if (!successfulTransmissionsCache.contains(callsign))
|
||||
return;
|
||||
|
||||
successfulTransmissionsCache[callsign]++;
|
||||
if (successfulTransmissionsCache[callsign] > 5)
|
||||
namespace Afv
|
||||
{
|
||||
decreaseDelayMs(callsign);
|
||||
successfulTransmissionsCache[callsign] = 0;
|
||||
}
|
||||
}
|
||||
namespace Audio
|
||||
{
|
||||
void CallsignDelayCache::initialise(const QString &callsign)
|
||||
{
|
||||
if (!m_delayCache.contains(callsign)) { m_delayCache[callsign] = delayDefault; }
|
||||
if (!successfulTransmissionsCache.contains(callsign)) { successfulTransmissionsCache[callsign] = 0; }
|
||||
}
|
||||
|
||||
void CallsignDelayCache::increaseDelayMs(const QString &callsign)
|
||||
{
|
||||
if (!m_delayCache.contains(callsign))
|
||||
return;
|
||||
int CallsignDelayCache::get(const QString &callsign)
|
||||
{
|
||||
return m_delayCache[callsign];
|
||||
}
|
||||
|
||||
m_delayCache[callsign] += delayIncrement;
|
||||
if (m_delayCache[callsign] > delayMax)
|
||||
{
|
||||
m_delayCache[callsign] = delayMax;
|
||||
}
|
||||
}
|
||||
void CallsignDelayCache::underflow(const QString &callsign)
|
||||
{
|
||||
if (!successfulTransmissionsCache.contains(callsign))
|
||||
return;
|
||||
|
||||
void CallsignDelayCache::decreaseDelayMs(const QString &callsign)
|
||||
{
|
||||
if (!m_delayCache.contains(callsign))
|
||||
return;
|
||||
successfulTransmissionsCache[callsign] = 0;
|
||||
increaseDelayMs(callsign);
|
||||
}
|
||||
|
||||
m_delayCache[callsign] -= delayIncrement;
|
||||
if (m_delayCache[callsign] < delayMin)
|
||||
{
|
||||
m_delayCache[callsign] = delayMin;
|
||||
}
|
||||
}
|
||||
void CallsignDelayCache::success(const QString &callsign)
|
||||
{
|
||||
if (!successfulTransmissionsCache.contains(callsign))
|
||||
return;
|
||||
|
||||
CallsignDelayCache &CallsignDelayCache::instance()
|
||||
{
|
||||
static CallsignDelayCache cache;
|
||||
return cache;
|
||||
}
|
||||
successfulTransmissionsCache[callsign]++;
|
||||
if (successfulTransmissionsCache[callsign] > 5)
|
||||
{
|
||||
decreaseDelayMs(callsign);
|
||||
successfulTransmissionsCache[callsign] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void CallsignDelayCache::increaseDelayMs(const QString &callsign)
|
||||
{
|
||||
if (!m_delayCache.contains(callsign))
|
||||
return;
|
||||
|
||||
m_delayCache[callsign] += delayIncrement;
|
||||
if (m_delayCache[callsign] > delayMax)
|
||||
{
|
||||
m_delayCache[callsign] = delayMax;
|
||||
}
|
||||
}
|
||||
|
||||
void CallsignDelayCache::decreaseDelayMs(const QString &callsign)
|
||||
{
|
||||
if (!m_delayCache.contains(callsign))
|
||||
return;
|
||||
|
||||
m_delayCache[callsign] -= delayIncrement;
|
||||
if (m_delayCache[callsign] < delayMin)
|
||||
{
|
||||
m_delayCache[callsign] = delayMin;
|
||||
}
|
||||
}
|
||||
|
||||
CallsignDelayCache &CallsignDelayCache::instance()
|
||||
{
|
||||
static CallsignDelayCache cache;
|
||||
return cache;
|
||||
}
|
||||
|
||||
} // ns
|
||||
} // ns
|
||||
} // ns
|
||||
|
||||
@@ -1,32 +1,53 @@
|
||||
/* Copyright (C) 2019
|
||||
* swift project Community / Contributors
|
||||
*
|
||||
* This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level
|
||||
* directory of this distribution. No part of swift project, including this file, may be copied, modified, propagated,
|
||||
* or distributed except according to the terms contained in the LICENSE file.
|
||||
*/
|
||||
|
||||
//! \file
|
||||
|
||||
#ifndef CALLSIGNDELAYCACHE_H
|
||||
#define CALLSIGNDELAYCACHE_H
|
||||
|
||||
#include <QHash>
|
||||
#include <QString>
|
||||
|
||||
|
||||
class CallsignDelayCache
|
||||
namespace BlackCore
|
||||
{
|
||||
public:
|
||||
void initialise(const QString &callsign);
|
||||
int get(const QString &callsign);
|
||||
void underflow(const QString &callsign);
|
||||
void success(const QString &callsign);
|
||||
void increaseDelayMs(const QString &callsign);
|
||||
void decreaseDelayMs(const QString &callsign);
|
||||
namespace Afv
|
||||
{
|
||||
namespace Audio
|
||||
{
|
||||
//! Callsign delay cache
|
||||
class CallsignDelayCache
|
||||
{
|
||||
public:
|
||||
void initialise(const QString &callsign);
|
||||
int get(const QString &callsign);
|
||||
void underflow(const QString &callsign);
|
||||
void success(const QString &callsign);
|
||||
void increaseDelayMs(const QString &callsign);
|
||||
void decreaseDelayMs(const QString &callsign);
|
||||
|
||||
static CallsignDelayCache &instance();
|
||||
static CallsignDelayCache &instance();
|
||||
|
||||
private:
|
||||
CallsignDelayCache() = default;
|
||||
private:
|
||||
//! Ctor
|
||||
CallsignDelayCache() = default;
|
||||
|
||||
static constexpr int delayDefault = 60;
|
||||
static constexpr int delayMin = 40;
|
||||
static constexpr int delayIncrement = 20;
|
||||
static constexpr int delayMax = 300;
|
||||
static constexpr int delayDefault = 60;
|
||||
static constexpr int delayMin = 40;
|
||||
static constexpr int delayIncrement = 20;
|
||||
static constexpr int delayMax = 300;
|
||||
|
||||
QHash<QString, int> m_delayCache;
|
||||
QHash<QString, int> successfulTransmissionsCache;
|
||||
};
|
||||
QHash<QString, int> m_delayCache;
|
||||
QHash<QString, int> successfulTransmissionsCache;
|
||||
};
|
||||
|
||||
#endif // CALLSIGNDELAYCACHE_H
|
||||
} // ns
|
||||
} // ns
|
||||
} // ns
|
||||
|
||||
#endif // guard
|
||||
|
||||
@@ -1,191 +1,205 @@
|
||||
/* Copyright (C) 2019
|
||||
* swift project Community / Contributors
|
||||
*
|
||||
* This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level
|
||||
* directory of this distribution. No part of swift project, including this file, may be copied, modified, propagated,
|
||||
* or distributed except according to the terms contained in the LICENSE file.
|
||||
*/
|
||||
|
||||
//! \file
|
||||
|
||||
#include "callsignsampleprovider.h"
|
||||
#include "callsigndelaycache.h"
|
||||
#include "blacksound/sampleprovider/samples.h"
|
||||
#include <QtMath>
|
||||
#include <QDebug>
|
||||
|
||||
CallsignSampleProvider::CallsignSampleProvider(const QAudioFormat &audioFormat, QObject *parent) :
|
||||
ISampleProvider(parent),
|
||||
m_audioFormat(audioFormat),
|
||||
m_decoder(audioFormat.sampleRate(), 1)
|
||||
namespace BlackCore
|
||||
{
|
||||
Q_ASSERT(audioFormat.channelCount() == 1);
|
||||
|
||||
mixer = new MixingSampleProvider(this);
|
||||
crackleSoundProvider = new ResourceSoundSampleProvider(Samples::instance().crackle(), mixer);
|
||||
crackleSoundProvider->setLooping(true);
|
||||
crackleSoundProvider->setGain(0.0);
|
||||
whiteNoise = new ResourceSoundSampleProvider(Samples::instance().whiteNoise(), mixer);
|
||||
whiteNoise->setLooping(true);
|
||||
whiteNoise->setGain(0.0);
|
||||
acBusNoise = new SawToothGenerator(400, mixer);
|
||||
audioInput = new BufferedWaveProvider(audioFormat, mixer);
|
||||
|
||||
// Create the compressor
|
||||
simpleCompressorEffect = new SimpleCompressorEffect(audioInput, mixer);
|
||||
simpleCompressorEffect->setMakeUpGain(-5.5);
|
||||
|
||||
// Create the voice EQ
|
||||
voiceEq = new EqualizerSampleProvider(simpleCompressorEffect, EqualizerPresets::VHFEmulation, mixer);
|
||||
|
||||
mixer->addMixerInput(whiteNoise);
|
||||
mixer->addMixerInput(acBusNoise);
|
||||
mixer->addMixerInput(voiceEq);
|
||||
|
||||
m_timer.setInterval(100);
|
||||
connect(&m_timer, &QTimer::timeout, this, &CallsignSampleProvider::timerElapsed);
|
||||
}
|
||||
|
||||
int CallsignSampleProvider::readSamples(QVector<qint16> &samples, qint64 count)
|
||||
{
|
||||
int noOfSamples = mixer->readSamples(samples, count);
|
||||
|
||||
if (m_inUse && m_lastPacketLatch && audioInput->getBufferedBytes() == 0)
|
||||
namespace Afv
|
||||
{
|
||||
idle();
|
||||
m_lastPacketLatch = false;
|
||||
}
|
||||
namespace Audio
|
||||
{
|
||||
CallsignSampleProvider::CallsignSampleProvider(const QAudioFormat &audioFormat, QObject *parent) :
|
||||
ISampleProvider(parent),
|
||||
m_audioFormat(audioFormat),
|
||||
m_decoder(audioFormat.sampleRate(), 1)
|
||||
{
|
||||
Q_ASSERT(audioFormat.channelCount() == 1);
|
||||
|
||||
if (m_inUse && !m_underflow && audioInput->getBufferedBytes() == 0)
|
||||
{
|
||||
qDebug() << "[" << m_callsign << "] [Delay++]";
|
||||
CallsignDelayCache::instance().underflow(m_callsign);
|
||||
m_underflow = true;
|
||||
}
|
||||
mixer = new MixingSampleProvider(this);
|
||||
crackleSoundProvider = new ResourceSoundSampleProvider(Samples::instance().crackle(), mixer);
|
||||
crackleSoundProvider->setLooping(true);
|
||||
crackleSoundProvider->setGain(0.0);
|
||||
whiteNoise = new ResourceSoundSampleProvider(Samples::instance().whiteNoise(), mixer);
|
||||
whiteNoise->setLooping(true);
|
||||
whiteNoise->setGain(0.0);
|
||||
acBusNoise = new SawToothGenerator(400, mixer);
|
||||
audioInput = new BufferedWaveProvider(audioFormat, mixer);
|
||||
|
||||
return noOfSamples;
|
||||
}
|
||||
// Create the compressor
|
||||
simpleCompressorEffect = new SimpleCompressorEffect(audioInput, mixer);
|
||||
simpleCompressorEffect->setMakeUpGain(-5.5);
|
||||
|
||||
void CallsignSampleProvider::timerElapsed()
|
||||
{
|
||||
if (m_inUse && audioInput->getBufferedBytes() == 0 && m_lastSamplesAddedUtc.msecsTo(QDateTime::currentDateTimeUtc()) > idleTimeoutMs)
|
||||
{
|
||||
idle();
|
||||
}
|
||||
}
|
||||
// Create the voice EQ
|
||||
voiceEq = new EqualizerSampleProvider(simpleCompressorEffect, EqualizerPresets::VHFEmulation, mixer);
|
||||
|
||||
QString CallsignSampleProvider::type() const
|
||||
{
|
||||
return m_type;
|
||||
}
|
||||
mixer->addMixerInput(whiteNoise);
|
||||
mixer->addMixerInput(acBusNoise);
|
||||
mixer->addMixerInput(voiceEq);
|
||||
|
||||
void CallsignSampleProvider::active(const QString &callsign, const QString &aircraftType)
|
||||
{
|
||||
m_callsign = callsign;
|
||||
CallsignDelayCache::instance().initialise(callsign);
|
||||
m_type = aircraftType;
|
||||
m_decoder.resetState();
|
||||
m_inUse = true;
|
||||
setEffects();
|
||||
m_underflow = false;
|
||||
m_timer.setInterval(100);
|
||||
connect(&m_timer, &QTimer::timeout, this, &CallsignSampleProvider::timerElapsed);
|
||||
}
|
||||
|
||||
int delayMs = CallsignDelayCache::instance().get(callsign);
|
||||
qDebug() << "[" << m_callsign << "] [Delay " << delayMs << "ms]";
|
||||
if (delayMs > 0)
|
||||
{
|
||||
int phaseDelayLength = (m_audioFormat.sampleRate() / 1000) * delayMs;
|
||||
QVector<qint16> phaseDelay(phaseDelayLength * 2, 0);
|
||||
audioInput->addSamples(phaseDelay);
|
||||
}
|
||||
}
|
||||
int CallsignSampleProvider::readSamples(QVector<qint16> &samples, qint64 count)
|
||||
{
|
||||
int noOfSamples = mixer->readSamples(samples, count);
|
||||
|
||||
void CallsignSampleProvider::activeSilent(const QString &callsign, const QString &aircraftType)
|
||||
{
|
||||
m_callsign = callsign;
|
||||
CallsignDelayCache::instance().initialise(callsign);
|
||||
m_type = aircraftType;
|
||||
m_decoder.resetState();
|
||||
m_inUse = true;
|
||||
setEffects(true);
|
||||
m_underflow = true;
|
||||
}
|
||||
if (m_inUse && m_lastPacketLatch && audioInput->getBufferedBytes() == 0)
|
||||
{
|
||||
idle();
|
||||
m_lastPacketLatch = false;
|
||||
}
|
||||
|
||||
void CallsignSampleProvider::clear()
|
||||
{
|
||||
idle();
|
||||
audioInput->clearBuffer();
|
||||
}
|
||||
if (m_inUse && !m_underflow && audioInput->getBufferedBytes() == 0)
|
||||
{
|
||||
qDebug() << "[" << m_callsign << "] [Delay++]";
|
||||
CallsignDelayCache::instance().underflow(m_callsign);
|
||||
m_underflow = true;
|
||||
}
|
||||
|
||||
void CallsignSampleProvider::addOpusSamples(const IAudioDto &audioDto, float distanceRatio)
|
||||
{
|
||||
m_distanceRatio = distanceRatio;
|
||||
return noOfSamples;
|
||||
}
|
||||
|
||||
QVector<qint16> audio = decodeOpus(audioDto.audio);
|
||||
audioInput->addSamples(audio);
|
||||
m_lastPacketLatch = audioDto.lastPacket;
|
||||
if (audioDto.lastPacket && !m_underflow)
|
||||
CallsignDelayCache::instance().success(m_callsign);
|
||||
void CallsignSampleProvider::timerElapsed()
|
||||
{
|
||||
if (m_inUse && audioInput->getBufferedBytes() == 0 && m_lastSamplesAddedUtc.msecsTo(QDateTime::currentDateTimeUtc()) > idleTimeoutMs)
|
||||
{
|
||||
idle();
|
||||
}
|
||||
}
|
||||
|
||||
m_lastSamplesAddedUtc = QDateTime::currentDateTimeUtc();
|
||||
if (!m_timer.isActive()) { m_timer.start(); }
|
||||
}
|
||||
QString CallsignSampleProvider::type() const
|
||||
{
|
||||
return m_type;
|
||||
}
|
||||
|
||||
void CallsignSampleProvider::addSilentSamples(const IAudioDto &audioDto)
|
||||
{
|
||||
// Disable all audio effects
|
||||
setEffects(true);
|
||||
void CallsignSampleProvider::active(const QString &callsign, const QString &aircraftType)
|
||||
{
|
||||
m_callsign = callsign;
|
||||
CallsignDelayCache::instance().initialise(callsign);
|
||||
m_type = aircraftType;
|
||||
m_decoder.resetState();
|
||||
m_inUse = true;
|
||||
setEffects();
|
||||
m_underflow = false;
|
||||
|
||||
// TODO audioInput->addSamples(decoderByteBuffer, 0, frameCount * 2);
|
||||
m_lastPacketLatch = audioDto.lastPacket;
|
||||
int delayMs = CallsignDelayCache::instance().get(callsign);
|
||||
qDebug() << "[" << m_callsign << "] [Delay " << delayMs << "ms]";
|
||||
if (delayMs > 0)
|
||||
{
|
||||
int phaseDelayLength = (m_audioFormat.sampleRate() / 1000) * delayMs;
|
||||
QVector<qint16> phaseDelay(phaseDelayLength * 2, 0);
|
||||
audioInput->addSamples(phaseDelay);
|
||||
}
|
||||
}
|
||||
|
||||
m_lastSamplesAddedUtc = QDateTime::currentDateTimeUtc();
|
||||
if (!m_timer.isActive()) { m_timer.start(); }
|
||||
}
|
||||
void CallsignSampleProvider::activeSilent(const QString &callsign, const QString &aircraftType)
|
||||
{
|
||||
m_callsign = callsign;
|
||||
CallsignDelayCache::instance().initialise(callsign);
|
||||
m_type = aircraftType;
|
||||
m_decoder.resetState();
|
||||
m_inUse = true;
|
||||
setEffects(true);
|
||||
m_underflow = true;
|
||||
}
|
||||
|
||||
QString CallsignSampleProvider::callsign() const
|
||||
{
|
||||
return m_callsign;
|
||||
}
|
||||
void CallsignSampleProvider::clear()
|
||||
{
|
||||
idle();
|
||||
audioInput->clearBuffer();
|
||||
}
|
||||
|
||||
void CallsignSampleProvider::idle()
|
||||
{
|
||||
m_timer.stop();
|
||||
m_inUse = false;
|
||||
setEffects();
|
||||
m_callsign = QString();
|
||||
m_type = QString();
|
||||
}
|
||||
void CallsignSampleProvider::addOpusSamples(const IAudioDto &audioDto, float distanceRatio)
|
||||
{
|
||||
m_distanceRatio = distanceRatio;
|
||||
|
||||
QVector<qint16> CallsignSampleProvider::decodeOpus(const QByteArray &opusData)
|
||||
{
|
||||
int decodedLength = 0;
|
||||
QVector<qint16> decoded = m_decoder.decode(opusData, opusData.size(), &decodedLength);
|
||||
return decoded;
|
||||
}
|
||||
QVector<qint16> audio = decodeOpus(audioDto.audio);
|
||||
audioInput->addSamples(audio);
|
||||
m_lastPacketLatch = audioDto.lastPacket;
|
||||
if (audioDto.lastPacket && !m_underflow)
|
||||
CallsignDelayCache::instance().success(m_callsign);
|
||||
|
||||
void CallsignSampleProvider::setEffects(bool noEffects)
|
||||
{
|
||||
if (noEffects || m_bypassEffects || !m_inUse)
|
||||
{
|
||||
crackleSoundProvider->setGain(0.0);
|
||||
whiteNoise->setGain(0.0);
|
||||
acBusNoise->setGain(0.0);
|
||||
simpleCompressorEffect->setEnabled(false);
|
||||
voiceEq->setBypassEffects(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
float crackleFactor = (float)(((qExp(m_distanceRatio) * qPow(m_distanceRatio, -4.0)) / 350) - 0.00776652);
|
||||
m_lastSamplesAddedUtc = QDateTime::currentDateTimeUtc();
|
||||
if (!m_timer.isActive()) { m_timer.start(); }
|
||||
}
|
||||
|
||||
if (crackleFactor < 0.0f) { crackleFactor = 0.0f; }
|
||||
if (crackleFactor > 0.20f) { crackleFactor = 0.20f; }
|
||||
void CallsignSampleProvider::addSilentSamples(const IAudioDto &audioDto)
|
||||
{
|
||||
// Disable all audio effects
|
||||
setEffects(true);
|
||||
|
||||
crackleSoundProvider->setGain(crackleFactor * 2);
|
||||
whiteNoise->setGain(whiteNoiseGainMin);
|
||||
acBusNoise->setGain(acBusGainMin);
|
||||
simpleCompressorEffect->setEnabled(true);
|
||||
voiceEq->setBypassEffects(false);
|
||||
voiceEq->setOutputGain(1.0 - crackleFactor * 3.7);
|
||||
}
|
||||
}
|
||||
// TODO audioInput->addSamples(decoderByteBuffer, 0, frameCount * 2);
|
||||
m_lastPacketLatch = audioDto.lastPacket;
|
||||
|
||||
void CallsignSampleProvider::setBypassEffects(bool bypassEffects)
|
||||
{
|
||||
m_bypassEffects = bypassEffects;
|
||||
setEffects();
|
||||
}
|
||||
m_lastSamplesAddedUtc = QDateTime::currentDateTimeUtc();
|
||||
if (!m_timer.isActive()) { m_timer.start(); }
|
||||
}
|
||||
|
||||
bool CallsignSampleProvider::inUse() const
|
||||
{
|
||||
return m_inUse;
|
||||
}
|
||||
void CallsignSampleProvider::idle()
|
||||
{
|
||||
m_timer.stop();
|
||||
m_inUse = false;
|
||||
setEffects();
|
||||
m_callsign = QString();
|
||||
m_type = QString();
|
||||
}
|
||||
|
||||
QVector<qint16> CallsignSampleProvider::decodeOpus(const QByteArray &opusData)
|
||||
{
|
||||
int decodedLength = 0;
|
||||
QVector<qint16> decoded = m_decoder.decode(opusData, opusData.size(), &decodedLength);
|
||||
return decoded;
|
||||
}
|
||||
|
||||
void CallsignSampleProvider::setEffects(bool noEffects)
|
||||
{
|
||||
if (noEffects || m_bypassEffects || !m_inUse)
|
||||
{
|
||||
crackleSoundProvider->setGain(0.0);
|
||||
whiteNoise->setGain(0.0);
|
||||
acBusNoise->setGain(0.0);
|
||||
simpleCompressorEffect->setEnabled(false);
|
||||
voiceEq->setBypassEffects(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
float crackleFactor = (float)(((qExp(m_distanceRatio) * qPow(m_distanceRatio, -4.0)) / 350) - 0.00776652);
|
||||
|
||||
if (crackleFactor < 0.0f) { crackleFactor = 0.0f; }
|
||||
if (crackleFactor > 0.20f) { crackleFactor = 0.20f; }
|
||||
|
||||
crackleSoundProvider->setGain(crackleFactor * 2);
|
||||
whiteNoise->setGain(whiteNoiseGainMin);
|
||||
acBusNoise->setGain(acBusGainMin);
|
||||
simpleCompressorEffect->setEnabled(true);
|
||||
voiceEq->setBypassEffects(false);
|
||||
voiceEq->setOutputGain(1.0 - crackleFactor * 3.7);
|
||||
}
|
||||
}
|
||||
|
||||
void CallsignSampleProvider::setBypassEffects(bool bypassEffects)
|
||||
{
|
||||
m_bypassEffects = bypassEffects;
|
||||
setEffects();
|
||||
}
|
||||
|
||||
bool CallsignSampleProvider::inUse() const
|
||||
{
|
||||
return m_inUse;
|
||||
}
|
||||
} // ns
|
||||
} // ns
|
||||
} // ns
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
/* Copyright (C) 2019
|
||||
* swift project Community / Contributors
|
||||
*
|
||||
* This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level
|
||||
* directory of this distribution. No part of swift project, including this file, may be copied, modified, propagated,
|
||||
* or distributed except according to the terms contained in the LICENSE file.
|
||||
*/
|
||||
|
||||
//! \file
|
||||
|
||||
#ifndef CALLSIGNSAMPLEPROVIDER_H
|
||||
#define CALLSIGNSAMPLEPROVIDER_H
|
||||
|
||||
@@ -17,64 +27,77 @@
|
||||
#include <QTimer>
|
||||
#include <QDateTime>
|
||||
|
||||
class CallsignSampleProvider : public ISampleProvider
|
||||
|
||||
namespace BlackCore
|
||||
{
|
||||
Q_OBJECT
|
||||
namespace Afv
|
||||
{
|
||||
namespace Audio
|
||||
{
|
||||
//! Callsign provide
|
||||
class CallsignSampleProvider : public ISampleProvider
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
CallsignSampleProvider(const QAudioFormat &audioFormat, QObject *parent = nullptr);
|
||||
public:
|
||||
CallsignSampleProvider(const QAudioFormat &audioFormat, QObject *parent = nullptr);
|
||||
|
||||
int readSamples(QVector<qint16> &samples, qint64 count) override;
|
||||
int readSamples(QVector<qint16> &samples, qint64 count) override;
|
||||
|
||||
QString callsign() const;
|
||||
QString type() const;
|
||||
//! The callsign
|
||||
const QString callsign() const { return m_callsign; }
|
||||
|
||||
void active(const QString &callsign, const QString &aircraftType);
|
||||
void activeSilent(const QString &callsign, const QString &aircraftType);
|
||||
QString type() const;
|
||||
|
||||
void clear();
|
||||
void active(const QString &callsign, const QString &aircraftType);
|
||||
void activeSilent(const QString &callsign, const QString &aircraftType);
|
||||
|
||||
void addOpusSamples(const IAudioDto &audioDto, float distanceRatio);
|
||||
void addSilentSamples(const IAudioDto &audioDto);
|
||||
void clear();
|
||||
|
||||
bool inUse() const;
|
||||
void addOpusSamples(const IAudioDto &audioDto, float distanceRatio);
|
||||
void addSilentSamples(const IAudioDto &audioDto);
|
||||
|
||||
void setBypassEffects(bool bypassEffects);
|
||||
bool inUse() const;
|
||||
|
||||
private:
|
||||
void timerElapsed();
|
||||
void idle();
|
||||
QVector<qint16> decodeOpus(const QByteArray &opusData);
|
||||
void setEffects(bool noEffects = false);
|
||||
void setBypassEffects(bool bypassEffects);
|
||||
|
||||
QAudioFormat m_audioFormat;
|
||||
private:
|
||||
void timerElapsed();
|
||||
void idle();
|
||||
QVector<qint16> decodeOpus(const QByteArray &opusData);
|
||||
void setEffects(bool noEffects = false);
|
||||
|
||||
const double whiteNoiseGainMin = 0.15; //0.01;
|
||||
const double acBusGainMin = 0.003; //0.002;
|
||||
const int frameCount = 960;
|
||||
const int idleTimeoutMs = 500;
|
||||
QAudioFormat m_audioFormat;
|
||||
|
||||
QString m_callsign;
|
||||
QString m_type;
|
||||
bool m_inUse = false;
|
||||
const double whiteNoiseGainMin = 0.15; //0.01;
|
||||
const double acBusGainMin = 0.003; //0.002;
|
||||
const int frameCount = 960;
|
||||
const int idleTimeoutMs = 500;
|
||||
|
||||
bool m_bypassEffects = false;
|
||||
QString m_callsign;
|
||||
QString m_type;
|
||||
bool m_inUse = false;
|
||||
|
||||
float m_distanceRatio = 1.0;
|
||||
bool m_bypassEffects = false;
|
||||
|
||||
MixingSampleProvider *mixer;
|
||||
ResourceSoundSampleProvider *crackleSoundProvider;
|
||||
ResourceSoundSampleProvider *whiteNoise;
|
||||
SawToothGenerator *acBusNoise;
|
||||
SimpleCompressorEffect *simpleCompressorEffect;
|
||||
EqualizerSampleProvider *voiceEq;
|
||||
BufferedWaveProvider *audioInput;
|
||||
QTimer m_timer;
|
||||
float m_distanceRatio = 1.0;
|
||||
|
||||
COpusDecoder m_decoder;
|
||||
bool m_lastPacketLatch = false;
|
||||
QDateTime m_lastSamplesAddedUtc;
|
||||
bool m_underflow = false;
|
||||
};
|
||||
MixingSampleProvider *mixer;
|
||||
ResourceSoundSampleProvider *crackleSoundProvider;
|
||||
ResourceSoundSampleProvider *whiteNoise;
|
||||
SawToothGenerator *acBusNoise;
|
||||
SimpleCompressorEffect *simpleCompressorEffect;
|
||||
EqualizerSampleProvider *voiceEq;
|
||||
BufferedWaveProvider *audioInput;
|
||||
QTimer m_timer;
|
||||
|
||||
#endif // CALLSIGNSAMPLEPROVIDER_H
|
||||
COpusDecoder m_decoder;
|
||||
bool m_lastPacketLatch = false;
|
||||
QDateTime m_lastSamplesAddedUtc;
|
||||
bool m_underflow = false;
|
||||
};
|
||||
} // ns
|
||||
} // ns
|
||||
} // ns
|
||||
|
||||
#endif // guard
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
/* Copyright (C) 2019
|
||||
* swift project Community / Contributors
|
||||
*
|
||||
* This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level
|
||||
* directory of this distribution. No part of swift project, including this file, may be copied, modified, propagated,
|
||||
* or distributed except according to the terms contained in the LICENSE file.
|
||||
*/
|
||||
|
||||
//! \file
|
||||
|
||||
#include "input.h"
|
||||
#include "blacksound/audioutilities.h"
|
||||
|
||||
@@ -5,148 +15,158 @@
|
||||
#include <QDebug>
|
||||
#include <cmath>
|
||||
|
||||
void AudioInputBuffer::start()
|
||||
namespace BlackCore
|
||||
{
|
||||
open(QIODevice::WriteOnly);
|
||||
}
|
||||
|
||||
void AudioInputBuffer::stop()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
qint64 AudioInputBuffer::readData(char *data, qint64 maxlen)
|
||||
{
|
||||
Q_UNUSED(data)
|
||||
Q_UNUSED(maxlen)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
qint64 AudioInputBuffer::writeData(const char *data, qint64 len)
|
||||
{
|
||||
QByteArray buffer(data, static_cast<int>(len));
|
||||
m_buffer.append(buffer);
|
||||
// 20 ms = 960 samples * 2 bytes = 1920 Bytes
|
||||
if (m_buffer.size() >= 1920)
|
||||
namespace Afv
|
||||
{
|
||||
emit frameAvailable(m_buffer.left(1920));
|
||||
m_buffer.remove(0, 1920);
|
||||
}
|
||||
namespace Audio
|
||||
{
|
||||
void AudioInputBuffer::start()
|
||||
{
|
||||
open(QIODevice::WriteOnly);
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
void AudioInputBuffer::stop()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
Input::Input(int sampleRate, QObject *parent) :
|
||||
QObject(parent),
|
||||
m_sampleRate(sampleRate),
|
||||
m_encoder(sampleRate, 1, OPUS_APPLICATION_VOIP)
|
||||
{
|
||||
m_encoder.setBitRate(16 * 1024);
|
||||
}
|
||||
qint64 AudioInputBuffer::readData(char *data, qint64 maxlen)
|
||||
{
|
||||
Q_UNUSED(data)
|
||||
Q_UNUSED(maxlen)
|
||||
|
||||
bool Input::started() const
|
||||
{
|
||||
return m_started;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Input::opusBytesEncoded() const
|
||||
{
|
||||
return m_opusBytesEncoded;
|
||||
}
|
||||
qint64 AudioInputBuffer::writeData(const char *data, qint64 len)
|
||||
{
|
||||
QByteArray buffer(data, static_cast<int>(len));
|
||||
m_buffer.append(buffer);
|
||||
// 20 ms = 960 samples * 2 bytes = 1920 Bytes
|
||||
if (m_buffer.size() >= 1920)
|
||||
{
|
||||
emit frameAvailable(m_buffer.left(1920));
|
||||
m_buffer.remove(0, 1920);
|
||||
}
|
||||
|
||||
void Input::setOpusBytesEncoded(int opusBytesEncoded)
|
||||
{
|
||||
m_opusBytesEncoded = opusBytesEncoded;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
float Input::volume() const
|
||||
{
|
||||
return m_volume;
|
||||
}
|
||||
Input::Input(int sampleRate, QObject *parent) :
|
||||
QObject(parent),
|
||||
m_sampleRate(sampleRate),
|
||||
m_encoder(sampleRate, 1, OPUS_APPLICATION_VOIP)
|
||||
{
|
||||
m_encoder.setBitRate(16 * 1024);
|
||||
}
|
||||
|
||||
void Input::setVolume(float volume)
|
||||
{
|
||||
m_volume = volume;
|
||||
}
|
||||
bool Input::started() const
|
||||
{
|
||||
return m_started;
|
||||
}
|
||||
|
||||
void Input::start(const QAudioDeviceInfo &inputDevice)
|
||||
{
|
||||
if (m_started) { return; }
|
||||
int Input::opusBytesEncoded() const
|
||||
{
|
||||
return m_opusBytesEncoded;
|
||||
}
|
||||
|
||||
QAudioFormat waveFormat;
|
||||
void Input::setOpusBytesEncoded(int opusBytesEncoded)
|
||||
{
|
||||
m_opusBytesEncoded = opusBytesEncoded;
|
||||
}
|
||||
|
||||
waveFormat.setSampleRate(m_sampleRate);
|
||||
waveFormat.setChannelCount(1);
|
||||
waveFormat.setSampleSize(16);
|
||||
waveFormat.setSampleType(QAudioFormat::SignedInt);
|
||||
waveFormat.setByteOrder(QAudioFormat::LittleEndian);
|
||||
waveFormat.setCodec("audio/pcm");
|
||||
float Input::volume() const
|
||||
{
|
||||
return m_volume;
|
||||
}
|
||||
|
||||
QAudioFormat inputFormat = waveFormat;
|
||||
if (!inputDevice.isFormatSupported(inputFormat))
|
||||
{
|
||||
qWarning() << "Default format not supported - trying to use nearest";
|
||||
inputFormat = inputDevice.nearestFormat(inputFormat);
|
||||
}
|
||||
void Input::setVolume(float volume)
|
||||
{
|
||||
m_volume = volume;
|
||||
}
|
||||
|
||||
m_audioInput.reset(new QAudioInput(inputDevice, inputFormat));
|
||||
// We want 20 ms of buffer size
|
||||
// 20 ms * nSamplesPerSec × nChannels × wBitsPerSample / 8 x 1000
|
||||
int bufferSize = 20 * inputFormat.sampleRate() * inputFormat.channelCount() * inputFormat.sampleSize() / ( 8 * 1000 );
|
||||
m_audioInput->setBufferSize(bufferSize);
|
||||
m_audioInputBuffer.start();
|
||||
m_audioInput->start(&m_audioInputBuffer);
|
||||
connect(&m_audioInputBuffer, &AudioInputBuffer::frameAvailable, this, &Input::audioInDataAvailable);
|
||||
void Input::start(const QAudioDeviceInfo &inputDevice)
|
||||
{
|
||||
if (m_started) { return; }
|
||||
|
||||
m_started = true;
|
||||
}
|
||||
QAudioFormat waveFormat;
|
||||
|
||||
void Input::stop()
|
||||
{
|
||||
if (! m_started) { return; }
|
||||
waveFormat.setSampleRate(m_sampleRate);
|
||||
waveFormat.setChannelCount(1);
|
||||
waveFormat.setSampleSize(16);
|
||||
waveFormat.setSampleType(QAudioFormat::SignedInt);
|
||||
waveFormat.setByteOrder(QAudioFormat::LittleEndian);
|
||||
waveFormat.setCodec("audio/pcm");
|
||||
|
||||
m_started = false;
|
||||
QAudioFormat inputFormat = waveFormat;
|
||||
if (!inputDevice.isFormatSupported(inputFormat))
|
||||
{
|
||||
qWarning() << "Default format not supported - trying to use nearest";
|
||||
inputFormat = inputDevice.nearestFormat(inputFormat);
|
||||
}
|
||||
|
||||
m_audioInput->stop();
|
||||
m_audioInput.reset();
|
||||
}
|
||||
m_audioInput.reset(new QAudioInput(inputDevice, inputFormat));
|
||||
// We want 20 ms of buffer size
|
||||
// 20 ms * nSamplesPerSec × nChannels × wBitsPerSample / 8 x 1000
|
||||
int bufferSize = 20 * inputFormat.sampleRate() * inputFormat.channelCount() * inputFormat.sampleSize() / (8 * 1000);
|
||||
m_audioInput->setBufferSize(bufferSize);
|
||||
m_audioInputBuffer.start();
|
||||
m_audioInput->start(&m_audioInputBuffer);
|
||||
connect(&m_audioInputBuffer, &AudioInputBuffer::frameAvailable, this, &Input::audioInDataAvailable);
|
||||
|
||||
void Input::audioInDataAvailable(const QByteArray &frame)
|
||||
{
|
||||
const QVector<qint16> samples = convertBytesTo16BitPCM(frame);
|
||||
m_started = true;
|
||||
}
|
||||
|
||||
int length;
|
||||
QByteArray encodedBuffer = m_encoder.encode(samples, samples.size(), &length);
|
||||
m_opusBytesEncoded += length;
|
||||
void Input::stop()
|
||||
{
|
||||
if (! m_started) { return; }
|
||||
|
||||
for (const qint16 sample : samples)
|
||||
{
|
||||
qint16 sampleInput = sample;
|
||||
sampleInput = qAbs(sampleInput);
|
||||
if (sampleInput > m_maxSampleInput)
|
||||
m_maxSampleInput = sampleInput;
|
||||
}
|
||||
m_started = false;
|
||||
|
||||
m_sampleCount += samples.size();
|
||||
if (m_sampleCount >= c_sampleCountPerEvent)
|
||||
{
|
||||
InputVolumeStreamArgs inputVolumeStreamArgs;
|
||||
qint16 maxInt = std::numeric_limits<qint16>::max();
|
||||
inputVolumeStreamArgs.PeakRaw = m_maxSampleInput / maxInt;
|
||||
inputVolumeStreamArgs.PeakDB = (float)(20 * std::log10(inputVolumeStreamArgs.PeakRaw));
|
||||
float db = qBound(minDb, inputVolumeStreamArgs.PeakDB, maxDb);
|
||||
float ratio = (db - minDb) / (maxDb - minDb);
|
||||
if (ratio < 0.30)
|
||||
ratio = 0;
|
||||
if (ratio > 1.0)
|
||||
ratio = 1;
|
||||
inputVolumeStreamArgs.PeakVU = ratio;
|
||||
emit inputVolumeStream(inputVolumeStreamArgs);
|
||||
m_sampleCount = 0;
|
||||
m_maxSampleInput = 0;
|
||||
}
|
||||
m_audioInput->stop();
|
||||
m_audioInput.reset();
|
||||
}
|
||||
|
||||
OpusDataAvailableArgs opusDataAvailableArgs = { m_audioSequenceCounter++, encodedBuffer };
|
||||
emit opusDataAvailable(opusDataAvailableArgs);
|
||||
}
|
||||
void Input::audioInDataAvailable(const QByteArray &frame)
|
||||
{
|
||||
const QVector<qint16> samples = convertBytesTo16BitPCM(frame);
|
||||
|
||||
int length;
|
||||
QByteArray encodedBuffer = m_encoder.encode(samples, samples.size(), &length);
|
||||
m_opusBytesEncoded += length;
|
||||
|
||||
for (const qint16 sample : samples)
|
||||
{
|
||||
qint16 sampleInput = sample;
|
||||
sampleInput = qAbs(sampleInput);
|
||||
if (sampleInput > m_maxSampleInput)
|
||||
m_maxSampleInput = sampleInput;
|
||||
}
|
||||
|
||||
m_sampleCount += samples.size();
|
||||
if (m_sampleCount >= c_sampleCountPerEvent)
|
||||
{
|
||||
InputVolumeStreamArgs inputVolumeStreamArgs;
|
||||
qint16 maxInt = std::numeric_limits<qint16>::max();
|
||||
inputVolumeStreamArgs.PeakRaw = m_maxSampleInput / maxInt;
|
||||
inputVolumeStreamArgs.PeakDB = (float)(20 * std::log10(inputVolumeStreamArgs.PeakRaw));
|
||||
float db = qBound(minDb, inputVolumeStreamArgs.PeakDB, maxDb);
|
||||
float ratio = (db - minDb) / (maxDb - minDb);
|
||||
if (ratio < 0.30)
|
||||
ratio = 0;
|
||||
if (ratio > 1.0)
|
||||
ratio = 1;
|
||||
inputVolumeStreamArgs.PeakVU = ratio;
|
||||
emit inputVolumeStream(inputVolumeStreamArgs);
|
||||
m_sampleCount = 0;
|
||||
m_maxSampleInput = 0;
|
||||
}
|
||||
|
||||
OpusDataAvailableArgs opusDataAvailableArgs = { m_audioSequenceCounter++, encodedBuffer };
|
||||
emit opusDataAvailable(opusDataAvailableArgs);
|
||||
}
|
||||
|
||||
} // ns
|
||||
} // ns
|
||||
} // ns
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
#ifndef AUDIO_INPUT_H
|
||||
#define AUDIO_INPUT_H
|
||||
/* Copyright (C) 2019
|
||||
* swift project Community / Contributors
|
||||
*
|
||||
* This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level
|
||||
* directory of this distribution. No part of swift project, including this file, may be copied, modified, propagated,
|
||||
* or distributed except according to the terms contained in the LICENSE file.
|
||||
*/
|
||||
|
||||
//! \file
|
||||
|
||||
#ifndef BLACKCORE_AFV_AUDIO_AUDIO_INPUT_H
|
||||
#define BLACKCORE_AFV_AUDIO_AUDIO_INPUT_H
|
||||
|
||||
#include "blacksound/sampleprovider/bufferedwaveprovider.h"
|
||||
#include "blacksound/codecs/opusencoder.h"
|
||||
@@ -10,85 +20,95 @@
|
||||
#include <QDateTime>
|
||||
#include <QSharedPointer>
|
||||
|
||||
class AudioInputBuffer : public QIODevice
|
||||
namespace BlackCore
|
||||
{
|
||||
Q_OBJECT
|
||||
namespace Afv
|
||||
{
|
||||
namespace Audio
|
||||
{
|
||||
//! Input buffer
|
||||
class AudioInputBuffer : public QIODevice
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
AudioInputBuffer() {}
|
||||
public:
|
||||
AudioInputBuffer() {}
|
||||
|
||||
void start();
|
||||
void stop();
|
||||
void start();
|
||||
void stop();
|
||||
|
||||
qint64 readData(char *data, qint64 maxlen) override;
|
||||
qint64 writeData(const char *data, qint64 len) override;
|
||||
qint64 readData(char *data, qint64 maxlen) override;
|
||||
qint64 writeData(const char *data, qint64 len) override;
|
||||
|
||||
signals:
|
||||
void frameAvailable(const QByteArray &frame);
|
||||
signals:
|
||||
void frameAvailable(const QByteArray &frame);
|
||||
|
||||
private:
|
||||
static constexpr qint64 frameSize = 960;
|
||||
QByteArray m_buffer;
|
||||
};
|
||||
private:
|
||||
static constexpr qint64 frameSize = 960;
|
||||
QByteArray m_buffer;
|
||||
};
|
||||
|
||||
struct OpusDataAvailableArgs
|
||||
{
|
||||
uint sequenceCounter = 0;
|
||||
QByteArray audio;
|
||||
};
|
||||
struct OpusDataAvailableArgs
|
||||
{
|
||||
uint sequenceCounter = 0;
|
||||
QByteArray audio;
|
||||
};
|
||||
|
||||
struct InputVolumeStreamArgs
|
||||
{
|
||||
QAudioDeviceInfo DeviceNumber;
|
||||
float PeakRaw = 0.0;
|
||||
float PeakDB = -1 * std::numeric_limits<float>::infinity();
|
||||
float PeakVU = 0.0;
|
||||
};
|
||||
struct InputVolumeStreamArgs
|
||||
{
|
||||
QAudioDeviceInfo DeviceNumber;
|
||||
float PeakRaw = 0.0;
|
||||
float PeakDB = -1 * std::numeric_limits<float>::infinity();
|
||||
float PeakVU = 0.0;
|
||||
};
|
||||
|
||||
class Input : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
class Input : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Input(int sampleRate, QObject *parent = nullptr);
|
||||
public:
|
||||
Input(int sampleRate, QObject *parent = nullptr);
|
||||
|
||||
bool started() const;
|
||||
bool started() const;
|
||||
|
||||
int opusBytesEncoded() const;
|
||||
void setOpusBytesEncoded(int opusBytesEncoded);
|
||||
int opusBytesEncoded() const;
|
||||
void setOpusBytesEncoded(int opusBytesEncoded);
|
||||
|
||||
float volume() const;
|
||||
void setVolume(float volume);
|
||||
float volume() const;
|
||||
void setVolume(float volume);
|
||||
|
||||
void start(const QAudioDeviceInfo &inputDevice);
|
||||
void stop();
|
||||
void start(const QAudioDeviceInfo &inputDevice);
|
||||
void stop();
|
||||
|
||||
signals:
|
||||
void inputVolumeStream(const InputVolumeStreamArgs &args);
|
||||
void opusDataAvailable(const OpusDataAvailableArgs &args);
|
||||
signals:
|
||||
void inputVolumeStream(const InputVolumeStreamArgs &args);
|
||||
void opusDataAvailable(const OpusDataAvailableArgs &args);
|
||||
|
||||
private:
|
||||
void audioInDataAvailable(const QByteArray &frame);
|
||||
private:
|
||||
void audioInDataAvailable(const QByteArray &frame);
|
||||
|
||||
static constexpr qint64 c_frameSize = 960;
|
||||
int m_sampleRate = 0;
|
||||
static constexpr qint64 c_frameSize = 960;
|
||||
int m_sampleRate = 0;
|
||||
|
||||
COpusEncoder m_encoder;
|
||||
QScopedPointer<QAudioInput> m_audioInput;
|
||||
COpusEncoder m_encoder;
|
||||
QScopedPointer<QAudioInput> m_audioInput;
|
||||
|
||||
bool m_started = false;
|
||||
int m_opusBytesEncoded = 0;
|
||||
float m_volume = 1.0f;
|
||||
int m_sampleCount = 0;
|
||||
float m_maxSampleInput = 0;
|
||||
bool m_started = false;
|
||||
int m_opusBytesEncoded = 0;
|
||||
float m_volume = 1.0f;
|
||||
int m_sampleCount = 0;
|
||||
float m_maxSampleInput = 0;
|
||||
|
||||
const int c_sampleCountPerEvent = 4800;
|
||||
const float maxDb = 0;
|
||||
const float minDb = -40;
|
||||
const int c_sampleCountPerEvent = 4800;
|
||||
const float maxDb = 0;
|
||||
const float minDb = -40;
|
||||
|
||||
uint m_audioSequenceCounter = 0;
|
||||
uint m_audioSequenceCounter = 0;
|
||||
|
||||
AudioInputBuffer m_audioInputBuffer;
|
||||
};
|
||||
AudioInputBuffer m_audioInputBuffer;
|
||||
};
|
||||
} // ns
|
||||
} // ns
|
||||
} // ns
|
||||
|
||||
#endif // AIRCRAFTVHFINPUT_H
|
||||
#endif // guard
|
||||
|
||||
@@ -1,92 +1,111 @@
|
||||
/* Copyright (C) 2019
|
||||
* swift project Community / Contributors
|
||||
*
|
||||
* This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level
|
||||
* directory of this distribution. No part of swift project, including this file, may be copied, modified, propagated,
|
||||
* or distributed except according to the terms contained in the LICENSE file.
|
||||
*/
|
||||
|
||||
//! \file
|
||||
|
||||
#include "output.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <cmath>
|
||||
|
||||
AudioOutputBuffer::AudioOutputBuffer(ISampleProvider *sampleProvider, QObject *parent) :
|
||||
QIODevice(parent),
|
||||
m_sampleProvider(sampleProvider)
|
||||
{ }
|
||||
|
||||
qint64 AudioOutputBuffer::readData(char *data, qint64 maxlen)
|
||||
namespace BlackCore
|
||||
{
|
||||
int sampleBytes = m_outputFormat.sampleSize() / 8;
|
||||
int channelCount = m_outputFormat.channelCount();
|
||||
int count = maxlen / ( sampleBytes * channelCount);
|
||||
QVector<qint16> buffer;
|
||||
m_sampleProvider->readSamples(buffer, count);
|
||||
|
||||
for (const qint16 sample : buffer)
|
||||
namespace Afv
|
||||
{
|
||||
qint16 sampleInput = sample;
|
||||
sampleInput = qAbs(sampleInput);
|
||||
if (sampleInput > m_maxSampleOutput)
|
||||
m_maxSampleOutput = sampleInput;
|
||||
}
|
||||
namespace Audio
|
||||
{
|
||||
AudioOutputBuffer::AudioOutputBuffer(ISampleProvider *sampleProvider, QObject *parent) :
|
||||
QIODevice(parent),
|
||||
m_sampleProvider(sampleProvider)
|
||||
{ }
|
||||
|
||||
m_sampleCount += buffer.size();
|
||||
if (m_sampleCount >= c_sampleCountPerEvent)
|
||||
{
|
||||
OutputVolumeStreamArgs outputVolumeStreamArgs;
|
||||
qint16 maxInt = std::numeric_limits<qint16>::max();
|
||||
outputVolumeStreamArgs.PeakRaw = m_maxSampleOutput / maxInt;
|
||||
outputVolumeStreamArgs.PeakDB = (float)(20 * std::log10(outputVolumeStreamArgs.PeakRaw));
|
||||
float db = qBound(minDb, outputVolumeStreamArgs.PeakDB, maxDb);
|
||||
float ratio = (db - minDb) / (maxDb - minDb);
|
||||
if (ratio < 0.30)
|
||||
ratio = 0;
|
||||
if (ratio > 1.0)
|
||||
ratio = 1;
|
||||
outputVolumeStreamArgs.PeakVU = ratio;
|
||||
emit outputVolumeStream(outputVolumeStreamArgs);
|
||||
m_sampleCount = 0;
|
||||
m_maxSampleOutput = 0;
|
||||
}
|
||||
qint64 AudioOutputBuffer::readData(char *data, qint64 maxlen)
|
||||
{
|
||||
int sampleBytes = m_outputFormat.sampleSize() / 8;
|
||||
int channelCount = m_outputFormat.channelCount();
|
||||
int count = maxlen / (sampleBytes * channelCount);
|
||||
QVector<qint16> buffer;
|
||||
m_sampleProvider->readSamples(buffer, count);
|
||||
|
||||
memcpy(data, buffer.constData(), maxlen);
|
||||
return maxlen;
|
||||
}
|
||||
for (const qint16 sample : buffer)
|
||||
{
|
||||
qint16 sampleInput = sample;
|
||||
sampleInput = qAbs(sampleInput);
|
||||
if (sampleInput > m_maxSampleOutput)
|
||||
m_maxSampleOutput = sampleInput;
|
||||
}
|
||||
|
||||
qint64 AudioOutputBuffer::writeData(const char *data, qint64 len)
|
||||
{
|
||||
Q_UNUSED(data);
|
||||
Q_UNUSED(len);
|
||||
return -1;
|
||||
}
|
||||
m_sampleCount += buffer.size();
|
||||
if (m_sampleCount >= c_sampleCountPerEvent)
|
||||
{
|
||||
OutputVolumeStreamArgs outputVolumeStreamArgs;
|
||||
qint16 maxInt = std::numeric_limits<qint16>::max();
|
||||
outputVolumeStreamArgs.PeakRaw = m_maxSampleOutput / maxInt;
|
||||
outputVolumeStreamArgs.PeakDB = (float)(20 * std::log10(outputVolumeStreamArgs.PeakRaw));
|
||||
float db = qBound(minDb, outputVolumeStreamArgs.PeakDB, maxDb);
|
||||
float ratio = (db - minDb) / (maxDb - minDb);
|
||||
if (ratio < 0.30)
|
||||
ratio = 0;
|
||||
if (ratio > 1.0)
|
||||
ratio = 1;
|
||||
outputVolumeStreamArgs.PeakVU = ratio;
|
||||
emit outputVolumeStream(outputVolumeStreamArgs);
|
||||
m_sampleCount = 0;
|
||||
m_maxSampleOutput = 0;
|
||||
}
|
||||
|
||||
Output::Output(QObject *parent) : QObject(parent)
|
||||
{ }
|
||||
memcpy(data, buffer.constData(), maxlen);
|
||||
return maxlen;
|
||||
}
|
||||
|
||||
void Output::start(const QAudioDeviceInfo &device, ISampleProvider *sampleProvider)
|
||||
{
|
||||
m_audioOutputBuffer = new AudioOutputBuffer(sampleProvider, this);
|
||||
connect(m_audioOutputBuffer, &AudioOutputBuffer::outputVolumeStream, this, &Output::outputVolumeStream);
|
||||
qint64 AudioOutputBuffer::writeData(const char *data, qint64 len)
|
||||
{
|
||||
Q_UNUSED(data);
|
||||
Q_UNUSED(len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
QAudioFormat outputFormat;
|
||||
outputFormat.setSampleRate(48000);
|
||||
outputFormat.setChannelCount(1);
|
||||
outputFormat.setSampleSize(16);
|
||||
outputFormat.setSampleType(QAudioFormat::SignedInt);
|
||||
outputFormat.setByteOrder(QAudioFormat::LittleEndian);
|
||||
outputFormat.setCodec("audio/pcm");
|
||||
Output::Output(QObject *parent) : QObject(parent)
|
||||
{ }
|
||||
|
||||
if (!device.isFormatSupported(outputFormat))
|
||||
{
|
||||
qWarning() << "Default format not supported - trying to use nearest";
|
||||
outputFormat = device.nearestFormat(outputFormat);
|
||||
}
|
||||
void Output::start(const QAudioDeviceInfo &device, ISampleProvider *sampleProvider)
|
||||
{
|
||||
m_audioOutputBuffer = new AudioOutputBuffer(sampleProvider, this);
|
||||
connect(m_audioOutputBuffer, &AudioOutputBuffer::outputVolumeStream, this, &Output::outputVolumeStream);
|
||||
|
||||
m_audioOutputCom1.reset(new QAudioOutput(device, outputFormat));
|
||||
// m_audioOutput->setBufferSize(bufferSize);
|
||||
m_audioOutputBuffer->open(QIODevice::ReadWrite | QIODevice::Unbuffered);
|
||||
m_audioOutputBuffer->setAudioFormat(outputFormat);
|
||||
m_audioOutputCom1->start(m_audioOutputBuffer);
|
||||
QAudioFormat outputFormat;
|
||||
outputFormat.setSampleRate(48000);
|
||||
outputFormat.setChannelCount(1);
|
||||
outputFormat.setSampleSize(16);
|
||||
outputFormat.setSampleType(QAudioFormat::SignedInt);
|
||||
outputFormat.setByteOrder(QAudioFormat::LittleEndian);
|
||||
outputFormat.setCodec("audio/pcm");
|
||||
|
||||
m_started = true;
|
||||
}
|
||||
if (!device.isFormatSupported(outputFormat))
|
||||
{
|
||||
qWarning() << "Default format not supported - trying to use nearest";
|
||||
outputFormat = device.nearestFormat(outputFormat);
|
||||
}
|
||||
|
||||
void Output::stop()
|
||||
{
|
||||
if (!m_started) { return; }
|
||||
m_started = false;
|
||||
}
|
||||
m_audioOutputCom1.reset(new QAudioOutput(device, outputFormat));
|
||||
// m_audioOutput->setBufferSize(bufferSize);
|
||||
m_audioOutputBuffer->open(QIODevice::ReadWrite | QIODevice::Unbuffered);
|
||||
m_audioOutputBuffer->setAudioFormat(outputFormat);
|
||||
m_audioOutputCom1->start(m_audioOutputBuffer);
|
||||
|
||||
m_started = true;
|
||||
}
|
||||
|
||||
void Output::stop()
|
||||
{
|
||||
if (!m_started) { return; }
|
||||
m_started = false;
|
||||
}
|
||||
} // ns
|
||||
} // ns
|
||||
} // ns
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
#ifndef OUTPUT_H
|
||||
#define OUTPUT_H
|
||||
/* Copyright (C) 2019
|
||||
* swift project Community / Contributors
|
||||
*
|
||||
* This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level
|
||||
* directory of this distribution. No part of swift project, including this file, may be copied, modified, propagated,
|
||||
* or distributed except according to the terms contained in the LICENSE file.
|
||||
*/
|
||||
|
||||
//! \file
|
||||
|
||||
#ifndef BLACKCORE_AFV_AUDIO_OUTPUT_H
|
||||
#define BLACKCORE_AFV_AUDIO_OUTPUT_H
|
||||
|
||||
#include "blacksound/sampleprovider/sampleprovider.h"
|
||||
|
||||
@@ -7,59 +17,69 @@
|
||||
#include <QAudioDeviceInfo>
|
||||
#include <QAudioOutput>
|
||||
|
||||
struct OutputVolumeStreamArgs
|
||||
namespace BlackCore
|
||||
{
|
||||
QAudioDeviceInfo DeviceNumber;
|
||||
float PeakRaw = 0.0;
|
||||
float PeakDB = -1 * std::numeric_limits<float>::infinity();
|
||||
float PeakVU = 0.0;
|
||||
};
|
||||
namespace Afv
|
||||
{
|
||||
namespace Audio
|
||||
{
|
||||
//! Stream args
|
||||
struct OutputVolumeStreamArgs
|
||||
{
|
||||
QAudioDeviceInfo DeviceNumber;
|
||||
float PeakRaw = 0.0;
|
||||
float PeakDB = -1 * std::numeric_limits<float>::infinity();
|
||||
float PeakVU = 0.0;
|
||||
};
|
||||
|
||||
class AudioOutputBuffer : public QIODevice
|
||||
{
|
||||
Q_OBJECT
|
||||
class AudioOutputBuffer : public QIODevice
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
AudioOutputBuffer(ISampleProvider *sampleProvider, QObject *parent = nullptr);
|
||||
public:
|
||||
AudioOutputBuffer(ISampleProvider *sampleProvider, QObject *parent = nullptr);
|
||||
|
||||
ISampleProvider *m_sampleProvider = nullptr;
|
||||
ISampleProvider *m_sampleProvider = nullptr;
|
||||
|
||||
void setAudioFormat(const QAudioFormat &format) { m_outputFormat = format; }
|
||||
void setAudioFormat(const QAudioFormat &format) { m_outputFormat = format; }
|
||||
|
||||
signals:
|
||||
void outputVolumeStream(const OutputVolumeStreamArgs &args);
|
||||
signals:
|
||||
void outputVolumeStream(const OutputVolumeStreamArgs &args);
|
||||
|
||||
protected:
|
||||
virtual qint64 readData(char *data, qint64 maxlen) override;
|
||||
virtual qint64 writeData(const char *data, qint64 len) override;
|
||||
protected:
|
||||
virtual qint64 readData(char *data, qint64 maxlen) override;
|
||||
virtual qint64 writeData(const char *data, qint64 len) override;
|
||||
|
||||
private:
|
||||
QAudioFormat m_outputFormat;
|
||||
private:
|
||||
QAudioFormat m_outputFormat;
|
||||
|
||||
float m_maxSampleOutput = 0;
|
||||
int m_sampleCount = 0;
|
||||
const int c_sampleCountPerEvent = 4800;
|
||||
const float maxDb = 0;
|
||||
const float minDb = -40;
|
||||
};
|
||||
float m_maxSampleOutput = 0;
|
||||
int m_sampleCount = 0;
|
||||
const int c_sampleCountPerEvent = 4800;
|
||||
const float maxDb = 0;
|
||||
const float minDb = -40;
|
||||
};
|
||||
|
||||
class Output : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
Output(QObject *parent = nullptr);
|
||||
class Output : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
Output(QObject *parent = nullptr);
|
||||
|
||||
void start(const QAudioDeviceInfo &device, ISampleProvider *sampleProvider);
|
||||
void stop();
|
||||
void start(const QAudioDeviceInfo &device, ISampleProvider *sampleProvider);
|
||||
void stop();
|
||||
|
||||
signals:
|
||||
void outputVolumeStream(const OutputVolumeStreamArgs &args);
|
||||
signals:
|
||||
void outputVolumeStream(const OutputVolumeStreamArgs &args);
|
||||
|
||||
private:
|
||||
bool m_started = false;
|
||||
private:
|
||||
bool m_started = false;
|
||||
|
||||
QScopedPointer<QAudioOutput> m_audioOutputCom1;
|
||||
AudioOutputBuffer *m_audioOutputBuffer;
|
||||
};
|
||||
QScopedPointer<QAudioOutput> m_audioOutputCom1;
|
||||
AudioOutputBuffer *m_audioOutputBuffer;
|
||||
};
|
||||
} // ns
|
||||
} // ns
|
||||
} // ns
|
||||
|
||||
#endif // OUTPUT_H
|
||||
#endif // guard
|
||||
|
||||
@@ -1,189 +1,205 @@
|
||||
/* Copyright (C) 2019
|
||||
* swift project Community / Contributors
|
||||
*
|
||||
* This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level
|
||||
* directory of this distribution. No part of swift project, including this file, may be copied, modified, propagated,
|
||||
* or distributed except according to the terms contained in the LICENSE file.
|
||||
*/
|
||||
|
||||
//! \file
|
||||
|
||||
#include "receiversampleprovider.h"
|
||||
#include "blacksound/sampleprovider/resourcesoundsampleprovider.h"
|
||||
#include "blacksound/sampleprovider/samples.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
ReceiverSampleProvider::ReceiverSampleProvider(const QAudioFormat &audioFormat, quint16 id, int voiceInputNumber, QObject *parent) :
|
||||
ISampleProvider(parent),
|
||||
m_id(id)
|
||||
namespace BlackCore
|
||||
{
|
||||
m_mixer = new MixingSampleProvider(this);
|
||||
|
||||
for (int i = 0; i < voiceInputNumber; i++)
|
||||
{
|
||||
auto voiceInput = new CallsignSampleProvider(audioFormat, m_mixer);
|
||||
m_voiceInputs.push_back(voiceInput);
|
||||
m_mixer->addMixerInput(voiceInput);
|
||||
};
|
||||
|
||||
// TODO blockTone = new SignalGenerator(WaveFormat.SampleRate, 1) { Gain = 0, Type = SignalGeneratorType.Sin, Frequency = 180 };
|
||||
// TODO mixer.AddMixerInput(blockTone.ToMono());
|
||||
// TODO volume = new VolumeSampleProvider(mixer);
|
||||
}
|
||||
|
||||
void ReceiverSampleProvider::setBypassEffects(bool value)
|
||||
{
|
||||
for (CallsignSampleProvider *voiceInput : m_voiceInputs)
|
||||
namespace Afv
|
||||
{
|
||||
voiceInput->setBypassEffects(value);
|
||||
}
|
||||
}
|
||||
|
||||
void ReceiverSampleProvider::setFrequency(const uint &frequency)
|
||||
{
|
||||
if (frequency != m_frequency)
|
||||
{
|
||||
for (CallsignSampleProvider *voiceInput : m_voiceInputs)
|
||||
namespace Audio
|
||||
{
|
||||
voiceInput->clear();
|
||||
}
|
||||
}
|
||||
m_frequency = frequency;
|
||||
}
|
||||
ReceiverSampleProvider::ReceiverSampleProvider(const QAudioFormat &audioFormat, quint16 id, int voiceInputNumber, QObject *parent) :
|
||||
ISampleProvider(parent),
|
||||
m_id(id)
|
||||
{
|
||||
m_mixer = new MixingSampleProvider(this);
|
||||
|
||||
int ReceiverSampleProvider::activeCallsigns() const
|
||||
{
|
||||
int numberOfCallsigns = std::count_if(m_voiceInputs.begin(), m_voiceInputs.end(), [] (const CallsignSampleProvider *p)
|
||||
{
|
||||
return p->inUse() == true;
|
||||
});
|
||||
return numberOfCallsigns;
|
||||
}
|
||||
for (int i = 0; i < voiceInputNumber; i++)
|
||||
{
|
||||
auto voiceInput = new CallsignSampleProvider(audioFormat, m_mixer);
|
||||
m_voiceInputs.push_back(voiceInput);
|
||||
m_mixer->addMixerInput(voiceInput);
|
||||
};
|
||||
|
||||
float ReceiverSampleProvider::volume() const
|
||||
{
|
||||
return 1.0;
|
||||
}
|
||||
// TODO blockTone = new SignalGenerator(WaveFormat.SampleRate, 1) { Gain = 0, Type = SignalGeneratorType.Sin, Frequency = 180 };
|
||||
// TODO mixer.AddMixerInput(blockTone.ToMono());
|
||||
// TODO volume = new VolumeSampleProvider(mixer);
|
||||
}
|
||||
|
||||
bool ReceiverSampleProvider::getMute() const
|
||||
{
|
||||
return m_mute;
|
||||
}
|
||||
void ReceiverSampleProvider::setBypassEffects(bool value)
|
||||
{
|
||||
for (CallsignSampleProvider *voiceInput : m_voiceInputs)
|
||||
{
|
||||
voiceInput->setBypassEffects(value);
|
||||
}
|
||||
}
|
||||
|
||||
void ReceiverSampleProvider::setMute(bool value)
|
||||
{
|
||||
m_mute = value;
|
||||
if (value)
|
||||
{
|
||||
for (CallsignSampleProvider *voiceInput : m_voiceInputs)
|
||||
{
|
||||
voiceInput->clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
void ReceiverSampleProvider::setFrequency(const uint &frequency)
|
||||
{
|
||||
if (frequency != m_frequency)
|
||||
{
|
||||
for (CallsignSampleProvider *voiceInput : m_voiceInputs)
|
||||
{
|
||||
voiceInput->clear();
|
||||
}
|
||||
}
|
||||
m_frequency = frequency;
|
||||
}
|
||||
|
||||
int ReceiverSampleProvider::readSamples(QVector<qint16> &samples, qint64 count)
|
||||
{
|
||||
int numberOfInUseInputs = activeCallsigns();
|
||||
int ReceiverSampleProvider::activeCallsigns() const
|
||||
{
|
||||
int numberOfCallsigns = std::count_if(m_voiceInputs.begin(), m_voiceInputs.end(), [](const CallsignSampleProvider * p)
|
||||
{
|
||||
return p->inUse() == true;
|
||||
});
|
||||
return numberOfCallsigns;
|
||||
}
|
||||
|
||||
if (numberOfInUseInputs > 1)
|
||||
{
|
||||
float ReceiverSampleProvider::volume() const
|
||||
{
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
bool ReceiverSampleProvider::getMute() const
|
||||
{
|
||||
return m_mute;
|
||||
}
|
||||
|
||||
void ReceiverSampleProvider::setMute(bool value)
|
||||
{
|
||||
m_mute = value;
|
||||
if (value)
|
||||
{
|
||||
for (CallsignSampleProvider *voiceInput : m_voiceInputs)
|
||||
{
|
||||
voiceInput->clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int ReceiverSampleProvider::readSamples(QVector<qint16> &samples, qint64 count)
|
||||
{
|
||||
int numberOfInUseInputs = activeCallsigns();
|
||||
|
||||
if (numberOfInUseInputs > 1)
|
||||
{
|
||||
// blockTone.Frequency = 180;
|
||||
// blockTone.Gain = blockToneGain;
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
// blockTone.Gain = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_doClickWhenAppropriate && numberOfInUseInputs == 0)
|
||||
{
|
||||
ResourceSoundSampleProvider *resourceSound = new ResourceSoundSampleProvider(Samples::instance().click(), m_mixer);
|
||||
m_mixer->addMixerInput(resourceSound);
|
||||
qDebug() << "Click...";
|
||||
m_doClickWhenAppropriate = false;
|
||||
}
|
||||
if (m_doClickWhenAppropriate && numberOfInUseInputs == 0)
|
||||
{
|
||||
ResourceSoundSampleProvider *resourceSound = new ResourceSoundSampleProvider(Samples::instance().click(), m_mixer);
|
||||
m_mixer->addMixerInput(resourceSound);
|
||||
qDebug() << "Click...";
|
||||
m_doClickWhenAppropriate = false;
|
||||
}
|
||||
|
||||
if (numberOfInUseInputs != lastNumberOfInUseInputs)
|
||||
{
|
||||
QStringList receivingCallsigns;
|
||||
for (const CallsignSampleProvider *voiceInput : m_voiceInputs)
|
||||
{
|
||||
QString callsign = voiceInput->callsign();
|
||||
if (! callsign.isEmpty())
|
||||
{
|
||||
receivingCallsigns.push_back(callsign);
|
||||
}
|
||||
}
|
||||
if (numberOfInUseInputs != lastNumberOfInUseInputs)
|
||||
{
|
||||
QStringList receivingCallsigns;
|
||||
for (const CallsignSampleProvider *voiceInput : m_voiceInputs)
|
||||
{
|
||||
QString callsign = voiceInput->callsign();
|
||||
if (! callsign.isEmpty())
|
||||
{
|
||||
receivingCallsigns.push_back(callsign);
|
||||
}
|
||||
}
|
||||
|
||||
TransceiverReceivingCallsignsChangedArgs args = { m_id, receivingCallsigns };
|
||||
emit receivingCallsignsChanged(args);
|
||||
}
|
||||
lastNumberOfInUseInputs = numberOfInUseInputs;
|
||||
TransceiverReceivingCallsignsChangedArgs args = { m_id, receivingCallsigns };
|
||||
emit receivingCallsignsChanged(args);
|
||||
}
|
||||
lastNumberOfInUseInputs = numberOfInUseInputs;
|
||||
|
||||
// return volume.Read(buffer, offset, count);
|
||||
return m_mixer->readSamples(samples, count);
|
||||
}
|
||||
return m_mixer->readSamples(samples, count);
|
||||
}
|
||||
|
||||
void ReceiverSampleProvider::addOpusSamples(const IAudioDto &audioDto, uint frequency, float distanceRatio)
|
||||
{
|
||||
if (m_frequency != frequency) //Lag in the backend means we get the tail end of a transmission
|
||||
return;
|
||||
void ReceiverSampleProvider::addOpusSamples(const IAudioDto &audioDto, uint frequency, float distanceRatio)
|
||||
{
|
||||
if (m_frequency != frequency) //Lag in the backend means we get the tail end of a transmission
|
||||
return;
|
||||
|
||||
CallsignSampleProvider *voiceInput = nullptr;
|
||||
CallsignSampleProvider *voiceInput = nullptr;
|
||||
|
||||
auto it = std::find_if(m_voiceInputs.begin(), m_voiceInputs.end(), [audioDto] (const CallsignSampleProvider *p)
|
||||
{
|
||||
return p->callsign() == audioDto.callsign;
|
||||
});
|
||||
if (it != m_voiceInputs.end())
|
||||
{
|
||||
voiceInput = *it;
|
||||
}
|
||||
auto it = std::find_if(m_voiceInputs.begin(), m_voiceInputs.end(), [audioDto](const CallsignSampleProvider * p)
|
||||
{
|
||||
return p->callsign() == audioDto.callsign;
|
||||
});
|
||||
if (it != m_voiceInputs.end())
|
||||
{
|
||||
voiceInput = *it;
|
||||
}
|
||||
|
||||
if (! voiceInput)
|
||||
{
|
||||
it = std::find_if(m_voiceInputs.begin(), m_voiceInputs.end(), [] (const CallsignSampleProvider *p) { return p->inUse() == false; });
|
||||
if (it != m_voiceInputs.end())
|
||||
{
|
||||
voiceInput = *it;
|
||||
voiceInput->active(audioDto.callsign, "");
|
||||
}
|
||||
}
|
||||
if (! voiceInput)
|
||||
{
|
||||
it = std::find_if(m_voiceInputs.begin(), m_voiceInputs.end(), [](const CallsignSampleProvider * p) { return p->inUse() == false; });
|
||||
if (it != m_voiceInputs.end())
|
||||
{
|
||||
voiceInput = *it;
|
||||
voiceInput->active(audioDto.callsign, "");
|
||||
}
|
||||
}
|
||||
|
||||
if (voiceInput)
|
||||
{
|
||||
voiceInput->addOpusSamples(audioDto, distanceRatio);
|
||||
}
|
||||
if (voiceInput)
|
||||
{
|
||||
voiceInput->addOpusSamples(audioDto, distanceRatio);
|
||||
}
|
||||
|
||||
m_doClickWhenAppropriate = true;
|
||||
}
|
||||
m_doClickWhenAppropriate = true;
|
||||
}
|
||||
|
||||
void ReceiverSampleProvider::addSilentSamples(const IAudioDto &audioDto, uint frequency, float distanceRatio)
|
||||
{
|
||||
Q_UNUSED(distanceRatio);
|
||||
if (m_frequency != frequency) //Lag in the backend means we get the tail end of a transmission
|
||||
return;
|
||||
void ReceiverSampleProvider::addSilentSamples(const IAudioDto &audioDto, uint frequency, float distanceRatio)
|
||||
{
|
||||
Q_UNUSED(distanceRatio);
|
||||
if (m_frequency != frequency) //Lag in the backend means we get the tail end of a transmission
|
||||
return;
|
||||
|
||||
CallsignSampleProvider *voiceInput = nullptr;
|
||||
CallsignSampleProvider *voiceInput = nullptr;
|
||||
|
||||
auto it = std::find_if(m_voiceInputs.begin(), m_voiceInputs.end(), [audioDto] (const CallsignSampleProvider *p)
|
||||
{
|
||||
return p->callsign() == audioDto.callsign;
|
||||
});
|
||||
if (it != m_voiceInputs.end())
|
||||
{
|
||||
voiceInput = *it;
|
||||
}
|
||||
auto it = std::find_if(m_voiceInputs.begin(), m_voiceInputs.end(), [audioDto](const CallsignSampleProvider * p)
|
||||
{
|
||||
return p->callsign() == audioDto.callsign;
|
||||
});
|
||||
if (it != m_voiceInputs.end())
|
||||
{
|
||||
voiceInput = *it;
|
||||
}
|
||||
|
||||
if (! voiceInput)
|
||||
{
|
||||
it = std::find_if(m_voiceInputs.begin(), m_voiceInputs.end(), [] (const CallsignSampleProvider *p) { return p->inUse() == false; });
|
||||
if (it != m_voiceInputs.end())
|
||||
{
|
||||
voiceInput = *it;
|
||||
voiceInput->active(audioDto.callsign, "");
|
||||
}
|
||||
}
|
||||
if (! voiceInput)
|
||||
{
|
||||
it = std::find_if(m_voiceInputs.begin(), m_voiceInputs.end(), [](const CallsignSampleProvider * p) { return p->inUse() == false; });
|
||||
if (it != m_voiceInputs.end())
|
||||
{
|
||||
voiceInput = *it;
|
||||
voiceInput->active(audioDto.callsign, "");
|
||||
}
|
||||
}
|
||||
|
||||
if (voiceInput)
|
||||
{
|
||||
voiceInput->addSilentSamples(audioDto);
|
||||
}
|
||||
}
|
||||
if (voiceInput)
|
||||
{
|
||||
voiceInput->addSilentSamples(audioDto);
|
||||
}
|
||||
}
|
||||
|
||||
quint16 ReceiverSampleProvider::getId() const
|
||||
{
|
||||
return m_id;
|
||||
}
|
||||
|
||||
} // ns
|
||||
} // ns
|
||||
} // ns
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
#ifndef RECEIVERSAMPLEPROVIDER_H
|
||||
#define RECEIVERSAMPLEPROVIDER_H
|
||||
/* Copyright (C) 2019
|
||||
* swift project Community / Contributors
|
||||
*
|
||||
* This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level
|
||||
* directory of this distribution. No part of swift project, including this file, may be copied, modified, propagated,
|
||||
* or distributed except according to the terms contained in the LICENSE file.
|
||||
*/
|
||||
|
||||
//! \file
|
||||
|
||||
#ifndef BLACKCORE_AFV_AUDIO_RECEIVERSAMPLEPROVIDER_H
|
||||
#define BLACKCORE_AFV_AUDIO_RECEIVERSAMPLEPROVIDER_H
|
||||
|
||||
#include "callsignsampleprovider.h"
|
||||
#include "blacksound/sampleprovider/sampleprovider.h"
|
||||
@@ -7,53 +17,64 @@
|
||||
|
||||
#include <QtGlobal>
|
||||
|
||||
struct TransceiverReceivingCallsignsChangedArgs
|
||||
namespace BlackCore
|
||||
{
|
||||
quint16 transceiverID;
|
||||
QStringList receivingCallsigns;
|
||||
};
|
||||
namespace Afv
|
||||
{
|
||||
namespace Audio
|
||||
{
|
||||
//! Arguments
|
||||
struct TransceiverReceivingCallsignsChangedArgs
|
||||
{
|
||||
quint16 transceiverID;
|
||||
QStringList receivingCallsigns;
|
||||
};
|
||||
|
||||
class ReceiverSampleProvider : public ISampleProvider
|
||||
{
|
||||
Q_OBJECT
|
||||
//! A sample provider
|
||||
class ReceiverSampleProvider : public ISampleProvider
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ReceiverSampleProvider(const QAudioFormat &audioFormat, quint16 id, int voiceInputNumber, QObject *parent = nullptr);
|
||||
public:
|
||||
ReceiverSampleProvider(const QAudioFormat &audioFormat, quint16 id, int voiceInputNumber, QObject *parent = nullptr);
|
||||
|
||||
void setBypassEffects(bool value);
|
||||
void setFrequency(const uint &frequency);
|
||||
int activeCallsigns() const;
|
||||
float volume() const;
|
||||
void setBypassEffects(bool value);
|
||||
void setFrequency(const uint &frequency);
|
||||
int activeCallsigns() const;
|
||||
float volume() const;
|
||||
|
||||
bool getMute() const;
|
||||
void setMute(bool value);
|
||||
bool getMute() const;
|
||||
void setMute(bool value);
|
||||
|
||||
virtual int readSamples(QVector<qint16> &samples, qint64 count) override;
|
||||
virtual int readSamples(QVector<qint16> &samples, qint64 count) override;
|
||||
|
||||
void addOpusSamples(const IAudioDto &audioDto, uint frequency, float distanceRatio);
|
||||
void addSilentSamples(const IAudioDto &audioDto, uint frequency, float distanceRatio);
|
||||
void addOpusSamples(const IAudioDto &audioDto, uint frequency, float distanceRatio);
|
||||
void addSilentSamples(const IAudioDto &audioDto, uint frequency, float distanceRatio);
|
||||
|
||||
quint16 getId() const;
|
||||
quint16 getId() const { return m_id; }
|
||||
|
||||
signals:
|
||||
void receivingCallsignsChanged(const TransceiverReceivingCallsignsChangedArgs &args);
|
||||
signals:
|
||||
void receivingCallsignsChanged(const TransceiverReceivingCallsignsChangedArgs &args);
|
||||
|
||||
private:
|
||||
uint m_frequency = 122800;
|
||||
bool m_mute = false;
|
||||
private:
|
||||
uint m_frequency = 122800;
|
||||
bool m_mute = false;
|
||||
|
||||
const float m_clickGain = 1.0f;
|
||||
const double m_blockToneGain = 0.10f;
|
||||
const float m_clickGain = 1.0f;
|
||||
const double m_blockToneGain = 0.10f;
|
||||
|
||||
quint16 m_id;
|
||||
quint16 m_id;
|
||||
|
||||
// TODO VolumeSampleProvider volume;
|
||||
MixingSampleProvider *m_mixer;
|
||||
// TODO SignalGenerator blockTone;
|
||||
QVector<CallsignSampleProvider *> m_voiceInputs;
|
||||
// TODO VolumeSampleProvider volume;
|
||||
MixingSampleProvider *m_mixer;
|
||||
// TODO SignalGenerator blockTone;
|
||||
QVector<CallsignSampleProvider *> m_voiceInputs;
|
||||
|
||||
bool m_doClickWhenAppropriate = false;
|
||||
int lastNumberOfInUseInputs = 0;
|
||||
};
|
||||
bool m_doClickWhenAppropriate = false;
|
||||
int lastNumberOfInUseInputs = 0;
|
||||
};
|
||||
} // ns
|
||||
} // ns
|
||||
} // ns
|
||||
|
||||
#endif // RECEIVERSAMPLEPROVIDER_H
|
||||
#endif // guard
|
||||
|
||||
@@ -1,158 +1,178 @@
|
||||
/* Copyright (C) 2019
|
||||
* swift project Community / Contributors
|
||||
*
|
||||
* This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level
|
||||
* directory of this distribution. No part of swift project, including this file, may be copied, modified, propagated,
|
||||
* or distributed except according to the terms contained in the LICENSE file.
|
||||
*/
|
||||
|
||||
//! \file
|
||||
|
||||
#include "soundcardsampleprovider.h"
|
||||
|
||||
SoundcardSampleProvider::SoundcardSampleProvider(int sampleRate, const QVector<quint16> &transceiverIDs, QObject *parent) :
|
||||
ISampleProvider(parent),
|
||||
m_mixer(new MixingSampleProvider())
|
||||
namespace BlackCore
|
||||
{
|
||||
m_waveFormat.setSampleRate(sampleRate);
|
||||
m_waveFormat.setChannelCount(1);
|
||||
m_waveFormat.setSampleSize(16);
|
||||
m_waveFormat.setSampleType(QAudioFormat::SignedInt);
|
||||
m_waveFormat.setByteOrder(QAudioFormat::LittleEndian);
|
||||
m_waveFormat.setCodec("audio/pcm");
|
||||
|
||||
m_mixer = new MixingSampleProvider(this);
|
||||
|
||||
m_receiverIDs = transceiverIDs;
|
||||
|
||||
for (quint16 transceiverID : transceiverIDs)
|
||||
namespace Afv
|
||||
{
|
||||
ReceiverSampleProvider *transceiverInput = new ReceiverSampleProvider(m_waveFormat, transceiverID, 4, m_mixer);
|
||||
connect(transceiverInput, &ReceiverSampleProvider::receivingCallsignsChanged, this, &SoundcardSampleProvider::receivingCallsignsChanged);
|
||||
m_receiverInputs.push_back(transceiverInput);
|
||||
m_receiverIDs.push_back(transceiverID);
|
||||
m_mixer->addMixerInput(transceiverInput);
|
||||
}
|
||||
}
|
||||
|
||||
QAudioFormat SoundcardSampleProvider::waveFormat() const
|
||||
{
|
||||
return m_waveFormat;
|
||||
}
|
||||
|
||||
void SoundcardSampleProvider::setBypassEffects(bool value)
|
||||
{
|
||||
for (ReceiverSampleProvider *receiverInput : m_receiverInputs)
|
||||
{
|
||||
receiverInput->setBypassEffects(value);
|
||||
}
|
||||
}
|
||||
|
||||
void SoundcardSampleProvider::pttUpdate(bool active, const QVector<TxTransceiverDto> &txTransceivers)
|
||||
{
|
||||
if (active)
|
||||
{
|
||||
if (txTransceivers.size() > 0)
|
||||
namespace Audio
|
||||
{
|
||||
QVector<TxTransceiverDto> txTransceiversFiltered = txTransceivers;
|
||||
|
||||
txTransceiversFiltered.erase(std::remove_if(txTransceiversFiltered.begin(), txTransceiversFiltered.end(), [this] (const TxTransceiverDto &d)
|
||||
SoundcardSampleProvider::SoundcardSampleProvider(int sampleRate, const QVector<quint16> &transceiverIDs, QObject *parent) :
|
||||
ISampleProvider(parent),
|
||||
m_mixer(new MixingSampleProvider())
|
||||
{
|
||||
return ! m_receiverIDs.contains(d.id);
|
||||
}),
|
||||
txTransceiversFiltered.end());
|
||||
m_waveFormat.setSampleRate(sampleRate);
|
||||
m_waveFormat.setChannelCount(1);
|
||||
m_waveFormat.setSampleSize(16);
|
||||
m_waveFormat.setSampleType(QAudioFormat::SignedInt);
|
||||
m_waveFormat.setByteOrder(QAudioFormat::LittleEndian);
|
||||
m_waveFormat.setCodec("audio/pcm");
|
||||
|
||||
m_mixer = new MixingSampleProvider(this);
|
||||
|
||||
for (const TxTransceiverDto &txTransceiver : txTransceiversFiltered)
|
||||
{
|
||||
auto it = std::find_if(m_receiverInputs.begin(), m_receiverInputs.end(), [txTransceiver] (const ReceiverSampleProvider *p)
|
||||
m_receiverIDs = transceiverIDs;
|
||||
|
||||
for (quint16 transceiverID : transceiverIDs)
|
||||
{
|
||||
return p->getId() == txTransceiver.id;
|
||||
});
|
||||
|
||||
if (it != m_receiverInputs.end()) { (*it)->setMute(true); }
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (ReceiverSampleProvider *receiverInput : m_receiverInputs)
|
||||
{
|
||||
receiverInput->setMute(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int SoundcardSampleProvider::readSamples(QVector<qint16> &samples, qint64 count)
|
||||
{
|
||||
return m_mixer->readSamples(samples, count);
|
||||
}
|
||||
|
||||
void SoundcardSampleProvider::addOpusSamples(const IAudioDto &audioDto, const QVector<RxTransceiverDto> &rxTransceivers)
|
||||
{
|
||||
QVector<RxTransceiverDto> rxTransceiversFilteredAndSorted = rxTransceivers;
|
||||
|
||||
rxTransceiversFilteredAndSorted.erase(std::remove_if(rxTransceiversFilteredAndSorted.begin(), rxTransceiversFilteredAndSorted.end(), [this] (const RxTransceiverDto &r)
|
||||
{
|
||||
return !m_receiverIDs.contains(r.id);
|
||||
}),
|
||||
rxTransceiversFilteredAndSorted.end());
|
||||
|
||||
std::sort(rxTransceiversFilteredAndSorted.begin(), rxTransceiversFilteredAndSorted.end(), [](const RxTransceiverDto & a, const RxTransceiverDto & b) -> bool
|
||||
{
|
||||
return a.distanceRatio > b.distanceRatio;
|
||||
});
|
||||
|
||||
if (rxTransceiversFilteredAndSorted.size() > 0)
|
||||
{
|
||||
bool audioPlayed = false;
|
||||
QVector<quint16> handledTransceiverIDs;
|
||||
for (int i = 0; i < rxTransceiversFilteredAndSorted.size(); i++)
|
||||
{
|
||||
RxTransceiverDto rxTransceiver = rxTransceiversFilteredAndSorted[i];
|
||||
if (!handledTransceiverIDs.contains(rxTransceiver.id))
|
||||
{
|
||||
handledTransceiverIDs.push_back(rxTransceiver.id);
|
||||
|
||||
ReceiverSampleProvider *receiverInput = nullptr;
|
||||
auto it = std::find_if(m_receiverInputs.begin(), m_receiverInputs.end(), [rxTransceiver] (const ReceiverSampleProvider *p)
|
||||
{
|
||||
return p->getId() == rxTransceiver.id;
|
||||
});
|
||||
if (it != m_receiverInputs.end())
|
||||
{
|
||||
receiverInput = *it;
|
||||
ReceiverSampleProvider *transceiverInput = new ReceiverSampleProvider(m_waveFormat, transceiverID, 4, m_mixer);
|
||||
connect(transceiverInput, &ReceiverSampleProvider::receivingCallsignsChanged, this, &SoundcardSampleProvider::receivingCallsignsChanged);
|
||||
m_receiverInputs.push_back(transceiverInput);
|
||||
m_receiverIDs.push_back(transceiverID);
|
||||
m_mixer->addMixerInput(transceiverInput);
|
||||
}
|
||||
}
|
||||
|
||||
if (! receiverInput) { continue; }
|
||||
if (receiverInput->getMute()) { continue; }
|
||||
QAudioFormat SoundcardSampleProvider::waveFormat() const
|
||||
{
|
||||
return m_waveFormat;
|
||||
}
|
||||
|
||||
if (!audioPlayed)
|
||||
void SoundcardSampleProvider::setBypassEffects(bool value)
|
||||
{
|
||||
for (ReceiverSampleProvider *receiverInput : m_receiverInputs)
|
||||
{
|
||||
receiverInput->addOpusSamples(audioDto, rxTransceiver.frequency, rxTransceiver.distanceRatio);
|
||||
audioPlayed = true;
|
||||
receiverInput->setBypassEffects(value);
|
||||
}
|
||||
}
|
||||
|
||||
void SoundcardSampleProvider::pttUpdate(bool active, const QVector<TxTransceiverDto> &txTransceivers)
|
||||
{
|
||||
if (active)
|
||||
{
|
||||
if (txTransceivers.size() > 0)
|
||||
{
|
||||
QVector<TxTransceiverDto> txTransceiversFiltered = txTransceivers;
|
||||
|
||||
txTransceiversFiltered.erase(std::remove_if(txTransceiversFiltered.begin(), txTransceiversFiltered.end(), [this](const TxTransceiverDto & d)
|
||||
{
|
||||
return ! m_receiverIDs.contains(d.id);
|
||||
}),
|
||||
txTransceiversFiltered.end());
|
||||
|
||||
|
||||
for (const TxTransceiverDto &txTransceiver : txTransceiversFiltered)
|
||||
{
|
||||
auto it = std::find_if(m_receiverInputs.begin(), m_receiverInputs.end(), [txTransceiver](const ReceiverSampleProvider * p)
|
||||
{
|
||||
return p->getId() == txTransceiver.id;
|
||||
});
|
||||
|
||||
if (it != m_receiverInputs.end()) { (*it)->setMute(true); }
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
receiverInput->addSilentSamples(audioDto, rxTransceiver.frequency, rxTransceiver.distanceRatio);
|
||||
for (ReceiverSampleProvider *receiverInput : m_receiverInputs)
|
||||
{
|
||||
receiverInput->setMute(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SoundcardSampleProvider::updateRadioTransceivers(const QVector<TransceiverDto> &radioTransceivers)
|
||||
{
|
||||
for (const TransceiverDto &radioTransceiver : radioTransceivers)
|
||||
{
|
||||
auto it = std::find_if(m_receiverInputs.begin(), m_receiverInputs.end(), [radioTransceiver] (const ReceiverSampleProvider *p)
|
||||
{
|
||||
return p->getId() == radioTransceiver.id;
|
||||
});
|
||||
int SoundcardSampleProvider::readSamples(QVector<qint16> &samples, qint64 count)
|
||||
{
|
||||
return m_mixer->readSamples(samples, count);
|
||||
}
|
||||
|
||||
if (it)
|
||||
{
|
||||
(*it)->setFrequency(radioTransceiver.frequency);
|
||||
}
|
||||
}
|
||||
void SoundcardSampleProvider::addOpusSamples(const IAudioDto &audioDto, const QVector<RxTransceiverDto> &rxTransceivers)
|
||||
{
|
||||
QVector<RxTransceiverDto> rxTransceiversFilteredAndSorted = rxTransceivers;
|
||||
|
||||
for (ReceiverSampleProvider *receiverInput : m_receiverInputs)
|
||||
{
|
||||
quint16 transceiverID = receiverInput->getId();
|
||||
bool contains = std::any_of(radioTransceivers.begin(), radioTransceivers.end(), [&] (const auto &tx) { return transceiverID == tx.id; });
|
||||
if (! contains)
|
||||
{
|
||||
receiverInput->setFrequency(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
rxTransceiversFilteredAndSorted.erase(std::remove_if(rxTransceiversFilteredAndSorted.begin(), rxTransceiversFilteredAndSorted.end(), [this](const RxTransceiverDto & r)
|
||||
{
|
||||
return !m_receiverIDs.contains(r.id);
|
||||
}),
|
||||
rxTransceiversFilteredAndSorted.end());
|
||||
|
||||
std::sort(rxTransceiversFilteredAndSorted.begin(), rxTransceiversFilteredAndSorted.end(), [](const RxTransceiverDto & a, const RxTransceiverDto & b) -> bool
|
||||
{
|
||||
return a.distanceRatio > b.distanceRatio;
|
||||
});
|
||||
|
||||
if (rxTransceiversFilteredAndSorted.size() > 0)
|
||||
{
|
||||
bool audioPlayed = false;
|
||||
QVector<quint16> handledTransceiverIDs;
|
||||
for (int i = 0; i < rxTransceiversFilteredAndSorted.size(); i++)
|
||||
{
|
||||
RxTransceiverDto rxTransceiver = rxTransceiversFilteredAndSorted[i];
|
||||
if (!handledTransceiverIDs.contains(rxTransceiver.id))
|
||||
{
|
||||
handledTransceiverIDs.push_back(rxTransceiver.id);
|
||||
|
||||
ReceiverSampleProvider *receiverInput = nullptr;
|
||||
auto it = std::find_if(m_receiverInputs.begin(), m_receiverInputs.end(), [rxTransceiver](const ReceiverSampleProvider * p)
|
||||
{
|
||||
return p->getId() == rxTransceiver.id;
|
||||
});
|
||||
if (it != m_receiverInputs.end())
|
||||
{
|
||||
receiverInput = *it;
|
||||
}
|
||||
|
||||
if (! receiverInput) { continue; }
|
||||
if (receiverInput->getMute()) { continue; }
|
||||
|
||||
if (!audioPlayed)
|
||||
{
|
||||
receiverInput->addOpusSamples(audioDto, rxTransceiver.frequency, rxTransceiver.distanceRatio);
|
||||
audioPlayed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
receiverInput->addSilentSamples(audioDto, rxTransceiver.frequency, rxTransceiver.distanceRatio);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SoundcardSampleProvider::updateRadioTransceivers(const QVector<TransceiverDto> &radioTransceivers)
|
||||
{
|
||||
for (const TransceiverDto &radioTransceiver : radioTransceivers)
|
||||
{
|
||||
auto it = std::find_if(m_receiverInputs.begin(), m_receiverInputs.end(), [radioTransceiver](const ReceiverSampleProvider * p)
|
||||
{
|
||||
return p->getId() == radioTransceiver.id;
|
||||
});
|
||||
|
||||
if (it)
|
||||
{
|
||||
(*it)->setFrequency(radioTransceiver.frequency);
|
||||
}
|
||||
}
|
||||
|
||||
for (ReceiverSampleProvider *receiverInput : m_receiverInputs)
|
||||
{
|
||||
quint16 transceiverID = receiverInput->getId();
|
||||
bool contains = std::any_of(radioTransceivers.begin(), radioTransceivers.end(), [&](const auto & tx) { return transceiverID == tx.id; });
|
||||
if (! contains)
|
||||
{
|
||||
receiverInput->setFrequency(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // ns
|
||||
} // ns
|
||||
} // ns
|
||||
|
||||
@@ -1,35 +1,58 @@
|
||||
#ifndef SOUNDCARDSAMPLEPROVIDER_H
|
||||
#define SOUNDCARDSAMPLEPROVIDER_H
|
||||
/* Copyright (C) 2019
|
||||
* swift project Community / Contributors
|
||||
*
|
||||
* This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level
|
||||
* directory of this distribution. No part of swift project, including this file, may be copied, modified, propagated,
|
||||
* or distributed except according to the terms contained in the LICENSE file.
|
||||
*/
|
||||
|
||||
//! \file
|
||||
|
||||
#ifndef BLACKCORE_AFV_AUDIO_SOUNDCARDSAMPLEPROVIDER_H
|
||||
#define BLACKCORE_AFV_AUDIO_SOUNDCARDSAMPLEPROVIDER_H
|
||||
|
||||
#include "blacksound/sampleprovider/sampleprovider.h"
|
||||
#include "blacksound/sampleprovider/mixingsampleprovider.h"
|
||||
#include "receiversampleprovider.h"
|
||||
|
||||
#include <QAudioFormat>
|
||||
#include <QObject>
|
||||
|
||||
class SoundcardSampleProvider : public ISampleProvider
|
||||
namespace BlackCore
|
||||
{
|
||||
Q_OBJECT
|
||||
namespace Afv
|
||||
{
|
||||
namespace Audio
|
||||
{
|
||||
//! Soundcard sample
|
||||
class SoundcardSampleProvider : public ISampleProvider
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
SoundcardSampleProvider(int sampleRate, const QVector<quint16> &transceiverIDs, QObject *parent = nullptr);
|
||||
public:
|
||||
//! Ctor
|
||||
SoundcardSampleProvider(int sampleRate, const QVector<quint16> &transceiverIDs, QObject *parent = nullptr);
|
||||
|
||||
QAudioFormat waveFormat() const;
|
||||
QAudioFormat waveFormat() const;
|
||||
|
||||
void setBypassEffects(bool value);
|
||||
void pttUpdate(bool active, const QVector<TxTransceiverDto> &txTransceivers);
|
||||
virtual int readSamples(QVector<qint16> &samples, qint64 count) override;
|
||||
void addOpusSamples(const IAudioDto &audioDto, const QVector<RxTransceiverDto> &rxTransceivers);
|
||||
void updateRadioTransceivers(const QVector<TransceiverDto> &radioTransceivers);
|
||||
void setBypassEffects(bool value);
|
||||
void pttUpdate(bool active, const QVector<TxTransceiverDto> &txTransceivers);
|
||||
virtual int readSamples(QVector<qint16> &samples, qint64 count) override;
|
||||
void addOpusSamples(const IAudioDto &audioDto, const QVector<RxTransceiverDto> &rxTransceivers);
|
||||
void updateRadioTransceivers(const QVector<TransceiverDto> &radioTransceivers);
|
||||
|
||||
signals:
|
||||
void receivingCallsignsChanged(const TransceiverReceivingCallsignsChangedArgs &args);
|
||||
signals:
|
||||
void receivingCallsignsChanged(const TransceiverReceivingCallsignsChangedArgs &args);
|
||||
|
||||
private:
|
||||
QAudioFormat m_waveFormat;
|
||||
MixingSampleProvider *m_mixer;
|
||||
QVector<ReceiverSampleProvider *> m_receiverInputs;
|
||||
QVector<quint16> m_receiverIDs;
|
||||
};
|
||||
private:
|
||||
QAudioFormat m_waveFormat;
|
||||
MixingSampleProvider *m_mixer;
|
||||
QVector<ReceiverSampleProvider *> m_receiverInputs;
|
||||
QVector<quint16> m_receiverIDs;
|
||||
};
|
||||
|
||||
#endif // SOUNDCARDSAMPLEPROVIDER_H
|
||||
} // ns
|
||||
} // ns
|
||||
} // ns
|
||||
|
||||
#endif // guard
|
||||
|
||||
Reference in New Issue
Block a user