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:
Klaus Basan
2019-09-19 00:56:54 +02:00
committed by Mat Sutcliffe
parent d064da13b5
commit 384aa3ce19
46 changed files with 2985 additions and 2916 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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