Non working test

merge-requests/5/head
Phil Taylor 2021-05-23 16:09:41 +01:00
rodzic f22f4224dd
commit ae69ef05e5
9 zmienionych plików z 187 dodań i 840 usunięć

Wyświetl plik

@ -1,247 +1,130 @@
/* /*
This class handles both RX and TX audio, each is created as a seperate instance of the class This class handles both RX and TX audio, each is created as a seperate instance of the class
but as the setup/handling if output (RX) and input (TX) devices is so similar I have combined them. but as the setup/handling if output (RX) and input (TX) devices is so similar I have combined them.
*/ */
#include "audiohandler.h" #include "audiohandler.h"
#ifndef USE_RTAUDIO
#include "logcategories.h" #include "logcategories.h"
#define MULAW_BIAS 33
#define MULAW_MAX 0x1fff
audioHandler::audioHandler(QObject* parent) : audioHandler::audioHandler(QObject* parent) :
QIODevice(parent), isInitialized(false),
isInitialized(false), isUlaw(false),
audioOutput(Q_NULLPTR), audioLatency(0),
audioInput(Q_NULLPTR), isInput(0),
isUlaw(false),
audioLatency(0),
isInput(0),
chunkAvailable(false) chunkAvailable(false)
{ {
} }
audioHandler::~audioHandler() audioHandler::~audioHandler()
{ {
stop(); //stop();
if (audioOutput != Q_NULLPTR) {
audioOutput->stop();
delete audioOutput;
}
if (audioInput != Q_NULLPTR) {
audioInput->stop();
delete audioInput;
}
if (resampler != NULL) { if (resampler != Q_NULLPTR) {
speex_resampler_destroy(resampler); speex_resampler_destroy(resampler);
} }
if (audio.isStreamRunning())
{
audio.stopStream();
}
delete buf;
} }
bool audioHandler::init(const quint8 bits, const quint8 channels, const quint16 samplerate, const quint16 audioLatency, const bool ulaw, const bool isinput, int device, quint8 resampleQuality) bool audioHandler::init(const quint8 bits, const quint8 channels, const quint16 samplerate, const quint16 latency, const bool ulaw, const bool isinput, int port, quint8 resampleQuality)
{ {
if (isInitialized) { if (isInitialized) {
return false; return false;
} }
/* Always use 16 bit 48K samples internally*/
format.setSampleSize(16);
format.setChannelCount(channels);
format.setSampleRate(INTERNAL_SAMPLE_RATE);
format.setCodec("audio/pcm");
format.setByteOrder(QAudioFormat::LittleEndian);
format.setSampleType(QAudioFormat::SignedInt);
this->audioLatency = latency; this->audioLatency = latency;
this->isUlaw = ulaw; this->isUlaw = ulaw;
this->isInput = isinput; this->isInput = isinput;
this->radioSampleBits = bits; this->radioSampleBits = bits;
this->radioSampleRate = samplerate; this->radioSampleRate = samplerate;
this->radioChannels = channels; this->radioChannels = channels;
// chunk size is always relative to Internal Sample Rate. // chunk size is always relative to Internal Sample Rate.
this->chunkSize = (INTERNAL_SAMPLE_RATE / 25) * radioChannels; this->chunkSize = (INTERNAL_SAMPLE_RATE / 25) * radioChannels;
qInfo(logAudio()) << (isInput ? "Input" : "Output") << "chunkSize: " << this->chunkSize; if (port > 0) {
qInfo(logAudio()) << (isInput ? "Input" : "Output") << "bufferLength (latency): " << this->latency; aParams.deviceId = port;
}
else if (isInput) {
aParams.deviceId = audio.getDefaultInputDevice();
}
else {
aParams.deviceId = audio.getDefaultOutputDevice();
}
aParams.nChannels = channels;
aParams.firstChannel = 0;
int resample_error=0; info = audio.getDeviceInfo(aParams.deviceId);
buf = new quint16[960];
if (info.probed)
{
qInfo(logAudio()) << (isInput ? "Input" : "Output") << QString::fromStdString(info.name) << "(" << aParams.deviceId << ") successfully probed";
if (info.nativeFormats == 0)
qInfo(logAudio()) << " No natively supported data formats!";
else {
qDebug(logAudio()) << " Supported formats:" <<
(info.nativeFormats & RTAUDIO_SINT8 ? "8-bit int," : "") <<
(info.nativeFormats & RTAUDIO_SINT16 ? "16-bit int," : "") <<
(info.nativeFormats & RTAUDIO_SINT24 ? "24-bit int," : "") <<
(info.nativeFormats & RTAUDIO_SINT32 ? "32-bit int," : "") <<
(info.nativeFormats & RTAUDIO_FLOAT32 ? "32-bit float," : "") <<
(info.nativeFormats & RTAUDIO_FLOAT64 ? "64-bit float," : "");
qInfo(logAudio()) << " Preferred sample rate:" << info.preferredSampleRate;
}
qInfo(logAudio()) << " chunkSize: " << chunkSize;
}
else
{
qCritical(logAudio()) << (isInput ? "Input" : "Output") << QString::fromStdString(info.name) << "(" << aParams.deviceId << ") could not be probed, check audio configuration!";
}
int resample_error = 0;
if (isInput) { if (isInput) {
resampler = wf_resampler_init(radioChannels, INTERNAL_SAMPLE_RATE, samplerate, resampleQuality, &resample_error); resampler = wf_resampler_init(radioChannels, INTERNAL_SAMPLE_RATE, samplerate, resampleQuality, &resample_error);
try {
isInitialized = setDevice(port); audio.openStream(NULL, &aParams, RTAUDIO_SINT16, INTERNAL_SAMPLE_RATE, &this->chunkSize, &staticWrite);
audio.startStream();
if (!isInitialized) { }
qInfo(logAudio()) << "Input device " << port.deviceName() << " not found, using default"; catch (RtAudioError& e) {
isInitialized = setDevice(QAudioDeviceInfo::defaultInputDevice()); qInfo(logAudio()) << "Error opening:" << QString::fromStdString(e.getMessage());
return false;
} }
} }
else else
{ {
resampler = wf_resampler_init(radioChannels, samplerate, INTERNAL_SAMPLE_RATE, resampleQuality, &resample_error); resampler = wf_resampler_init(radioChannels, samplerate, INTERNAL_SAMPLE_RATE, resampleQuality, &resample_error);
try {
isInitialized = setDevice(port); unsigned int length = chunkSize / 2;
audio.openStream(&aParams, NULL, RTAUDIO_SINT16, INTERNAL_SAMPLE_RATE, &length, &staticRead);
if (!isInitialized) { audio.startStream();
qInfo(logAudio()) << "Output device " << deviceInfo.deviceName() << " not found, using default"; }
isInitialized = setDevice(QAudioDeviceInfo::defaultOutputDevice()); catch (RtAudioError& e) {
qInfo(logAudio()) << "Error opening:" << QString::fromStdString(e.getMessage());
return false;
} }
} }
qInfo(logAudio()) << (isInput ? "Input" : "Output") << "device successfully opened";
wf_resampler_get_ratio(resampler, &ratioNum, &ratioDen); wf_resampler_get_ratio(resampler, &ratioNum, &ratioDen);
qInfo(logAudio()) << (isInput ? "Input" : "Output") << "wf_resampler_init() returned: " << resample_error << " ratioNum" << ratioNum << " ratioDen" << ratioDen; qInfo(logAudio()) << (isInput ? "Input" : "Output") << "wf_resampler_init() returned: " << resample_error << " ratioNum" << ratioNum << " ratioDen" << ratioDen;
qInfo(logAudio()) << (isInput ? "Input" : "Output") << "audio port name: " << deviceInfo.deviceName(); return isInitialized;
return isInitialized;
} }
void audioHandler::setVolume(unsigned char volume) void audioHandler::setVolume(unsigned char volume)
{ {
//qInfo(logAudio()) << (isInput ? "Input" : "Output") << "setVolume: " << volume << "(" << (qreal)(volume/255.0) << ")"; qInfo(logAudio()) << (isInput ? "Input" : "Output") << "setVolume: " << volume << "(" << (qreal)(volume/255.0) << ")";
if (audioOutput != Q_NULLPTR) {
audioOutput->setVolume((qreal)(volume / 255.0));
}
else if (audioInput != Q_NULLPTR) {
audioInput->setVolume((qreal)(volume / 255.0));
}
} }
bool audioHandler::setDevice(QAudioDeviceInfo deviceInfo)
{
bool ret = true;
qInfo(logAudio()) << (isInput ? "Input" : "Output") << "setDevice() running :" << deviceInfo.deviceName();
if (!deviceInfo.isFormatSupported(format)) {
if (deviceInfo.isNull())
{
qInfo(logAudio()) << "No audio device was found. You probably need to install libqt5multimedia-plugins.";
ret = false;
}
else {
/*
qInfo(logAudio()) << "Audio Devices found: ";
const auto deviceInfos = QAudioDeviceInfo::availableDevices(QAudio::AudioOutput);
for (const QAudioDeviceInfo& deviceInfo : deviceInfos)
{
qInfo(logAudio()) << "Device name: " << deviceInfo.deviceName();
qInfo(logAudio()) << "is null (probably not good):" << deviceInfo.isNull();
qInfo(logAudio()) << "channel count:" << deviceInfo.supportedChannelCounts();
qInfo(logAudio()) << "byte order:" << deviceInfo.supportedByteOrders();
qInfo(logAudio()) << "supported codecs:" << deviceInfo.supportedCodecs();
qInfo(logAudio()) << "sample rates:" << deviceInfo.supportedSampleRates();
qInfo(logAudio()) << "sample sizes:" << deviceInfo.supportedSampleSizes();
qInfo(logAudio()) << "sample types:" << deviceInfo.supportedSampleTypes();
}
qInfo(logAudio()) << "----- done with audio info -----";
*/
ret=false;
}
qInfo(logAudio()) << "Format not supported, choosing nearest supported format - which may not work!";
deviceInfo.nearestFormat(format);
}
this->deviceInfo = deviceInfo;
this->reinit();
return ret;
}
void audioHandler::reinit()
{
qInfo(logAudio()) << (isInput ? "Input" : "Output") << "reinit() running";
if (audioOutput != Q_NULLPTR && audioOutput->state() != QAudio::StoppedState) {
this->stop();
}
if (this->isInput)
{
// (Re)initialize audio input
if (audioInput != Q_NULLPTR)
delete audioInput;
audioInput = new QAudioInput(deviceInfo, format, this);
connect(audioInput, SIGNAL(notify()), SLOT(notified()));
connect(audioInput, SIGNAL(stateChanged(QAudio::State)), SLOT(stateChanged(QAudio::State)));
}
else {
// (Re)initialize audio output
if (audioOutput != Q_NULLPTR)
delete audioOutput;
audioOutput = Q_NULLPTR;
audioOutput = new QAudioOutput(deviceInfo, format, this);
// This seems to only be needed on Linux but is critical in aligning buffer sizes.
//#ifdef Q_OS_MAC
audioOutput->setBufferSize(chunkSize*8);
//#else
// audioOutput->setBufferSize(chunkSize*4);
//#endif
connect(audioOutput, SIGNAL(notify()), SLOT(notified()));
connect(audioOutput, SIGNAL(stateChanged(QAudio::State)), SLOT(stateChanged(QAudio::State)));
}
this->start();
this->flush();
}
void audioHandler::start()
{
qInfo(logAudio()) << (isInput ? "Input" : "Output") << "start() running";
if ((audioOutput == Q_NULLPTR || audioOutput->state() != QAudio::StoppedState) &&
(audioInput == Q_NULLPTR || audioInput->state() != QAudio::StoppedState) ) {
return;
}
if (isInput) {
//this->open(QIODevice::WriteOnly | QIODevice::Unbuffered);
this->open(QIODevice::WriteOnly);
audioInput->start(this);
}
else {
//this->open(QIODevice::ReadOnly | QIODevice::Unbuffered);
this->open(QIODevice::ReadOnly);
audioOutput->start(this);
}
}
void audioHandler::flush()
{
qInfo(logAudio()) << (isInput ? "Input" : "Output") << "flush() running";
this->stop();
if (isInput) {
audioInput->reset();
}
else {
audioOutput->reset();
}
QMutexLocker locker(&mutex);
audioBuffer.clear();
this->start();
}
void audioHandler::stop()
{
if (audioOutput != Q_NULLPTR && audioOutput->state() != QAudio::StoppedState) {
// Stop audio output
audioOutput->stop();
this->close();
}
if (audioInput != Q_NULLPTR && audioInput->state() != QAudio::StoppedState) {
// Stop audio output
audioInput->stop();
this->close();
}
}
/// <summary> /// <summary>
/// This function processes the incoming audio FROM the radio and pushes it into the playback buffer *data /// This function processes the incoming audio FROM the radio and pushes it into the playback buffer *data
@ -249,85 +132,34 @@ void audioHandler::stop()
/// <param name="data"></param> /// <param name="data"></param>
/// <param name="maxlen"></param> /// <param name="maxlen"></param>
/// <returns></returns> /// <returns></returns>
qint64 audioHandler::readData(char* data, qint64 maxlen) int audioHandler::readData(void* outputBuffer, void* inputBuffer, unsigned int nFrames, double streamTime, RtAudioStreamStatus status)
{ {
// Calculate output length, always full samples // Calculate output length, always full samples
int sentlen = 0; int sentlen = 0;
qint16* buffer = (qint16*)outputBuffer;
qDebug(logAudio()) << "looking for: " << nFrames;
//qInfo(logAudio()) << "Looking for: " << maxlen << " bytes"; for (int f = 0; f < nFrames; f++)
// We must lock the mutex for the entire time that the buffer may be modified.
// Get next packet from buffer.
if (!inputBuffer.isEmpty())
{ {
qDebug(logAudio()) << "*";
// Output buffer is ALWAYS 16 bit.
QMutexLocker locker(&mutex); *buffer++ = getBuffer(f);
auto packet = inputBuffer.begin(); //*buffer++ = f;
while (packet != inputBuffer.end() && sentlen < maxlen)
{
int timediff = packet->time.msecsTo(QTime::currentTime());
if (timediff > (int)audioLatency * 2) {
qInfo(logAudio()) << (isInput ? "Input" : "Output") << "Packet " << hex << packet->seq <<
" arrived too late (increase output latency!) " <<
dec << packet->time.msecsTo(QTime::currentTime()) << "ms";
while (packet !=audioBuffer.end() && timediff > (int)audioLatency) {
timediff = packet->time.msecsTo(QTime::currentTime());
lastSeq=packet->seq;
packet = inputBuffer.erase(packet); // returns next packet
}
if (packet == inputBuffer.end()) {
break;
}
}
// If we got here then packet time must be within latency threshold
if (packet->seq == lastSeq+1 || packet->seq <= lastSeq)
{
int send = qMin((int)maxlen-sentlen, packet->dataout.length() - packet->sent);
lastSeq = packet->seq;
//qInfo(logAudio()) << "Packet " << hex << packet->seq << " arrived on time " << Qt::dec << packet->time.msecsTo(QTime::currentTime()) << "ms";
memcpy(data + sentlen, packet->dataout.constData() + packet->sent, send);
sentlen = sentlen + send;
if (send == packet->dataout.length() - packet->sent)
{
//qInfo(logAudio()) << "Get next packet";
packet = inputBuffer.erase(packet); // returns next packet
}
else
{
// Store sent amount (could be zero if audioOutput buffer full) then break.
packet->sent = send;
break;
}
} else {
qInfo(logAudio()) << (isInput ? "Input" : "Output") << "Missing audio packet(s) from: " << hex << lastSeq + 1 << " to " << hex << packet->seq - 1;
lastSeq = packet->seq;
}
}
}
else {
// Fool audio system into thinking it has valid data, this seems to be required
// for MacOS Built in audio but shouldn't cause any issues with other platforms.
memset(data, 0x0, maxlen);
return maxlen;
} }
return sentlen; return 0;
} }
qint64 audioHandler::writeData(const char* data, qint64 len) quint16 audioHandler::getBuffer(int i) {
return buf[i];
}
int audioHandler::writeData(void* outputBuffer, void* inputBuffer, unsigned int nFrames, double streamTime, RtAudioStreamStatus status)
{ {
qint64 sentlen = 0; int sentlen = 0;
/*
QMutexLocker locker(&mutex); QMutexLocker locker(&mutex);
audioPacket *current; audioPacket* current;
while (sentlen < len) { while (sentlen < len) {
if (!audioBuffer.isEmpty()) if (!audioBuffer.isEmpty())
@ -345,37 +177,37 @@ qint64 audioHandler::writeData(const char* data, qint64 len)
} }
current = &audioBuffer.last(); current = &audioBuffer.last();
int send = qMin((int)(len - sentlen), (int)chunkSize-current->sent); int send = qMin((int)(len - sentlen), (int)chunkSize - current->sent);
current->datain.append(QByteArray::fromRawData(data + sentlen, send )); current->datain.append(QByteArray::fromRawData(data + sentlen, send));
sentlen = sentlen + send; sentlen = sentlen + send;
current->seq = 0; // Not used in TX current->seq = 0; // Not used in TX
current->time = QTime::currentTime(); current->time = QTime::currentTime();
current->sent = current->datain.length(); current->sent = current->datain.length();
if (current->sent == chunkSize) if (current->sent == chunkSize)
{ {
chunkAvailable = true; chunkAvailable = true;
} }
else if (audioBuffer.length()<=1 && current->sent != chunkSize) { else if (audioBuffer.length() <= 1 && current->sent != chunkSize) {
chunkAvailable = false; chunkAvailable = false;
} }
}
return (sentlen); // Always return the same number as we received }
*/
return (sentlen); // Always return the same number as we received
} }
qint64 audioHandler::bytesAvailable() const qint64 audioHandler::bytesAvailable() const
{ {
return 0; return 0;
} }
bool audioHandler::isSequential() const bool audioHandler::isSequential() const
{ {
return true; return true;
} }
void audioHandler::notified() void audioHandler::notified()
@ -385,46 +217,13 @@ void audioHandler::notified()
void audioHandler::stateChanged(QAudio::State state) void audioHandler::stateChanged(QAudio::State state)
{ {
// Process the state
switch (state)
{
case QAudio::IdleState:
{
qInfo(logAudio()) << (isInput ? "Input" : "Output") << "Audio now in idle state: " << audioBuffer.length() << " packets in buffer";
if (audioOutput != Q_NULLPTR && audioOutput->error() == QAudio::UnderrunError)
{
qInfo(logAudio()) << (isInput ? "Input" : "Output") << "buffer underrun";
audioOutput->suspend();
}
break;
}
case QAudio::ActiveState:
{
qInfo(logAudio()) << (isInput ? "Input" : "Output") << "Audio now in active state: " << audioBuffer.length() << " packets in buffer";
break;
}
case QAudio::SuspendedState:
{
qInfo(logAudio()) << (isInput ? "Input" : "Output") << "Audio now in suspended state: " << audioBuffer.length() << " packets in buffer";
break;
}
case QAudio::StoppedState:
{
qInfo(logAudio()) << (isInput ? "Input" : "Output") << "Audio now in stopped state: " << audioBuffer.length() << " packets in buffer";
break;
}
default: {
qInfo(logAudio()) << (isInput ? "Input" : "Output") << "Unhandled audio state: " << audioBuffer.length() << " packets in buffer";
}
}
} }
void audioHandler::incomingAudio(audioPacket data) void audioHandler::incomingAudio(audioPacket data)
{ {
if (audioOutput != Q_NULLPTR && audioOutput->state() != QAudio::StoppedState) {
QMutexLocker locker(&mutex);
// Incoming data is 8bits? // Incoming data is 8bits?
if (radioSampleBits == 8) if (radioSampleBits == 8)
@ -446,7 +245,7 @@ void audioHandler::incomingAudio(audioPacket data)
data.datain = inPacket; // Replace incoming data with converted. data.datain = inPacket; // Replace incoming data with converted.
} }
//qInfo(logAudio()) << "Adding packet to buffer:" << data.seq << ": " << data.datain.length(); qInfo(logAudio()) << "Adding packet to buffer:" << data.seq << ": " << data.datain.length();
/* We now have an array of 16bit samples in the NATIVE samplerate of the radio /* We now have an array of 16bit samples in the NATIVE samplerate of the radio
If the radio sample rate is below 48000, we need to resample. If the radio sample rate is below 48000, we need to resample.
@ -467,42 +266,37 @@ void audioHandler::incomingAudio(audioPacket data)
err = wf_resampler_process_interleaved_int(resampler, (const qint16*)data.datain.constData(), &inFrames, (qint16*)data.dataout.data(), &outFrames); err = wf_resampler_process_interleaved_int(resampler, (const qint16*)data.datain.constData(), &inFrames, (qint16*)data.dataout.data(), &outFrames);
} }
if (err) { if (err) {
qInfo(logAudio()) <<(isInput ? "Input" : "Output") << "Resampler error " << err << " inFrames:" << inFrames << " outFrames:" << outFrames; qInfo(logAudio()) << (isInput ? "Input" : "Output") << "Resampler error " << err << " inFrames:" << inFrames << " outFrames:" << outFrames;
} }
} }
else { else {
data.dataout = data.datain; data.dataout = data.datain;
} }
inputBuffer.insert(data.seq, data); memcpy(buf, data.dataout.constData(), data.dataout.length());
qDebug(logAudio()) << "Got data: " << data.dataout.length();
// Restart playback //audioBuffer.insert({ data.seq, data });
if (audioOutput->state() == QAudio::SuspendedState)
{
qInfo(logAudio()) << "Output Audio Suspended, Resuming...";
audioOutput->resume();
}
}
} }
void audioHandler::changeLatency(const quint16 newSize) void audioHandler::changeLatency(const quint16 newSize)
{ {
qInfo(logAudio()) << (isInput ? "Input" : "Output") << "Changing latency to: " << newSize << " from " << audioLatency; qInfo(logAudio()) << (isInput ? "Input" : "Output") << "Changing latency to: " << newSize << " from " << audioLatency;
audioLatency = newSize; audioLatency = newSize;
} }
void audioHandler::getLatency() void audioHandler::getLatency()
{ {
emit sendLatency(audioLatency); emit sendLatency(audioLatency);
} }
bool audioHandler::isChunkAvailable() bool audioHandler::isChunkAvailable()
{ {
return (chunkAvailable); return (chunkAvailable);
} }
void audioHandler::getNextAudioChunk(QByteArray& ret) void audioHandler::getNextAudioChunk(QByteArray& ret)
{ {
/*
if (!audioBuffer.isEmpty() && chunkAvailable) if (!audioBuffer.isEmpty() && chunkAvailable)
{ {
@ -512,18 +306,18 @@ void audioHandler::getNextAudioChunk(QByteArray& ret)
auto packet = audioBuffer.begin(); auto packet = audioBuffer.begin();
while (packet != audioBuffer.end()) while (packet != audioBuffer.end())
{ {
if (packet->time.msecsTo(QTime::currentTime()) > latency) { if (packet->time.msecsTo(QTime::currentTime()) > 100) {
//qInfo(logAudio()) << "TX Packet too old " << dec << packet->time.msecsTo(QTime::currentTime()) << "ms"; //qInfo(logAudio()) << "TX Packet too old " << dec << packet->time.msecsTo(QTime::currentTime()) << "ms";
packet = audioBuffer.erase(packet); // returns next packet packet = audioBuffer.erase(packet); // returns next packet
} }
else { else {
if (packet->datain.length() == chunkSize && ret.length() == 0) if (packet->datain.length() == chunkSize && ret.length() == 0)
{ {
/* We now have an array of samples in the computer native format (48000) // We now have an array of samples in the computer native format (48000)
If the radio sample rate is below 48000, we need to resample. // If the radio sample rate is below 48000, we need to resample.
*/
if (ratioNum != 1) if (ratioNum != 1)
{ {
// We need to resample (we are STILL 16 bit!) // We need to resample (we are STILL 16 bit!)
quint32 outFrames = ((packet->datain.length() / 2) / ratioNum) / radioChannels; quint32 outFrames = ((packet->datain.length() / 2) / ratioNum) / radioChannels;
@ -546,11 +340,11 @@ void audioHandler::getNextAudioChunk(QByteArray& ret)
//qInfo(logAudio()) << "Resampler run inLen:" << packet->datain.length() << " outLen:" << packet->dataout.length(); //qInfo(logAudio()) << "Resampler run inLen:" << packet->datain.length() << " outLen:" << packet->dataout.length();
if (radioSampleBits == 8) if (radioSampleBits == 8)
{ {
packet->datain=packet->dataout; // Copy output packet back to input buffer. packet->datain = packet->dataout; // Copy output packet back to input buffer.
packet->dataout.clear(); // Buffer MUST be cleared ready to be re-filled by the upsampling below. packet->dataout.clear(); // Buffer MUST be cleared ready to be re-filled by the upsampling below.
} }
} }
else if (radioSampleBits == 16 ){ else if (radioSampleBits == 16) {
// Only copy buffer if radioSampleBits is 16, as it will be handled below otherwise. // Only copy buffer if radioSampleBits is 16, as it will be handled below otherwise.
packet->dataout = packet->datain; packet->dataout = packet->datain;
} }
@ -575,7 +369,7 @@ void audioHandler::getNextAudioChunk(QByteArray& ret)
packet->dataout[f] = (char)outdata; packet->dataout[f] = (char)outdata;
} }
} }
ret=packet->dataout; ret = packet->dataout;
packet = audioBuffer.erase(packet); // returns next packet packet = audioBuffer.erase(packet); // returns next packet
} }
else { else {
@ -584,8 +378,9 @@ void audioHandler::getNextAudioChunk(QByteArray& ret)
} }
} }
} }
return; */
return;
} }
#endif

Wyświetl plik

@ -1,32 +1,17 @@
#ifndef AUDIOHANDLER_H #ifndef AUDIOHANDLER_H
#define AUDIOHANDLER_H #define AUDIOHANDLER_H
#define USE_RTAUDIO
#undef USE_QTAUDIO
#include <QObject> #include <QObject>
#include <QtMultimedia/QAudioOutput> #include <QtMultimedia/QAudioOutput>
#include <QMutexLocker>
#include <QByteArray> #include <QByteArray>
#include <QtEndian>
#ifdef USE_QTAUDIO
#include <QAudioFormat>
#include <QAudioDeviceInfo>
#include <QAudioOutput>
#include <QAudioInput>
#include <QIODevice>
#endif
#ifdef USE_RTAUDIO
#include "rtaudio/RtAudio.h" #include "rtaudio/RtAudio.h"
typedef signed short MY_TYPE; typedef signed short MY_TYPE;
#define FORMAT RTAUDIO_SINT16 #define FORMAT RTAUDIO_SINT16
#define SCALE 32767.0 #define SCALE 32767.0
#endif
#include <QThread> #include <QThread>
#include <QTimer> #include <QTimer>
@ -774,12 +759,10 @@ struct audioPacket {
QByteArray dataout; QByteArray dataout;
}; };
class audioHandler : public QObject
class audioHandler : public QIODevice
{ {
Q_OBJECT Q_OBJECT
public: public:
audioHandler(QObject* parent = 0); audioHandler(QObject* parent = 0);
~audioHandler(); ~audioHandler();
@ -792,17 +775,27 @@ public:
void flush(); void flush();
void stop(); void stop();
qint64 readData(char* data, qint64 maxlen); int readData(void* outputBuffer, void* inputBuffer, unsigned int nFrames, double streamTime, RtAudioStreamStatus status);
qint64 writeData(const char* data, qint64 len);
static int staticRead(void* outputBuffer, void* inputBuffer, unsigned int nFrames, double streamTime, RtAudioStreamStatus status, void* userData) {
return static_cast<audioHandler*>(userData)->readData(outputBuffer, inputBuffer, nFrames, streamTime, status);
}
int writeData(void* outputBuffer, void* inputBuffer, unsigned int nFrames, double streamTime, RtAudioStreamStatus status);
static int staticWrite(void* outputBuffer, void* inputBuffer, unsigned int nFrames, double streamTime, RtAudioStreamStatus status, void* userData) {
return static_cast<audioHandler*>(userData)->writeData(outputBuffer, inputBuffer, nFrames, streamTime, status);
}
qint64 bytesAvailable() const; qint64 bytesAvailable() const;
bool isSequential() const; bool isSequential() const;
void getNextAudioChunk(QByteArray &data); void getNextAudioChunk(QByteArray &data);
bool isChunkAvailable(); bool isChunkAvailable();
private slots:
bool init(const quint8 bits, const quint8 channels, const quint16 samplerate, const quint16 latency, const bool isulaw, const bool isinput, int port, quint8 resampleQuality); bool init(const quint8 bits, const quint8 channels, const quint16 samplerate, const quint16 latency, const bool isulaw, const bool isinput, int port, quint8 resampleQuality);
void incomingAudio(const audioPacket data); void incomingAudio(const audioPacket data);
void changeLatency(const quint16 newSize); void changeLatency(const quint16 newSize);
private slots:
void notified(); void notified();
void stateChanged(QAudio::State state); void stateChanged(QAudio::State state);
void setVolume(unsigned char volume); void setVolume(unsigned char volume);
@ -815,28 +808,22 @@ signals:
private: private:
void reinit(); void reinit();
quint16 getBuffer(int);
QMutex mutex; std::mutex mutex;
bool isInitialized; bool isInitialized;
#ifdef USE_RTAUDIO
RtAudio audio; RtAudio audio;
int audioDevice = 0; int audioDevice = 0;
RtAudio::StreamParameters aParams; RtAudio::StreamParameters aParams;
RtAudio::DeviceInfo info; RtAudio::DeviceInfo info;
#elif defined(USE_QTAUDIO) SpeexResamplerState* resampler = Q_NULLPTR;
QAudioOutput* audioOutput;
QAudioInput* audioInput;
QAudioFormat format;
QAudioDeviceInfo deviceInfo;
#endif
SpeexResamplerState* resampler;
bool isUlaw; bool isUlaw;
quint16 audioLatency; quint16 audioLatency;
bool isInput; // Used to determine whether input or output audio bool isInput; // Used to determine whether input or output audio
int chunkSize; unsigned int chunkSize;
bool chunkAvailable; bool chunkAvailable;
quint32 lastSeq; quint32 lastSeq;
@ -844,10 +831,10 @@ private:
quint16 radioSampleRate; quint16 radioSampleRate;
quint8 radioSampleBits; quint8 radioSampleBits;
quint8 radioChannels; quint8 radioChannels;
QVector <audioPacket> audioBuffer;
QMap<quint32, audioPacket>inputBuffer;
SpeexResamplerState* resampler=NULL; quint16 *buf;
std::map<quint32, audioPacket>audioBuffer;
unsigned int ratioNum; unsigned int ratioNum;
unsigned int ratioDen; unsigned int ratioDen;

Wyświetl plik

@ -1,421 +0,0 @@
/*
This class handles both RX and TX audio, each is created as a seperate instance of the class
but as the setup/handling if output (RX) and input (TX) devices is so similar I have combined them.
*/
#include "audiohandler.h"
#include "logcategories.h"
audioHandler::audioHandler(QObject* parent) :
QIODevice(parent),
isInitialized(false),
isUlaw(false),
audioLatency(0),
isInput(0),
chunkAvailable(false)
{
}
audioHandler::~audioHandler()
{
//stop();
if (resampler) {
speex_resampler_destroy(resampler);
}
}
bool audioHandler::init(const quint8 bits, const quint8 channels, const quint16 samplerate, const quint16 latency, const bool ulaw, const bool isinput, QString port, int device, quint8 resampleQuality)
{
if (isInitialized) {
return false;
}
this->audioDevice = device;
this->audioLatency = latency;
this->isUlaw = ulaw;
this->isInput = isinput;
this->radioSampleBits = bits;
this->radioSampleRate = samplerate;
this->radioChannels = channels;
// chunk size is always relative to Internal Sample Rate.
this->chunkSize = (INTERNAL_SAMPLE_RATE / 25) * radioChannels;
if (device != 0) {
aParams.deviceId = device;
}
else if (isInput) {
aParams.deviceId = audio.getDefaultInputDevice();
}
else {
aParams.deviceId = audio.getDefaultOutputDevice();
}
info = audio.getDeviceInfo(aParams.deviceId);
if (info.probed)
{
qInfo(logAudio()) << (isInput ? "Input" : "Output") << QString::fromStdString(info.name) << "(" << aParams.deviceId << ") successfully probed";
if (info.nativeFormats == 0)
qInfo(logAudio()) << " No natively supported data formats!";
else {
qDebug(logAudio()) << " Supported formats:" <<
(info.nativeFormats & RTAUDIO_SINT8 ? "8-bit int," : "") <<
(info.nativeFormats & RTAUDIO_SINT16 ? "16-bit int," : "") <<
(info.nativeFormats & RTAUDIO_SINT24 ? "24-bit int," : "") <<
(info.nativeFormats & RTAUDIO_SINT32 ? "32-bit int," : "") <<
(info.nativeFormats & RTAUDIO_FLOAT32 ? "32-bit float," : "") <<
(info.nativeFormats & RTAUDIO_FLOAT64 ? "64-bit float," : "");
qInfo(logAudio()) << " Preferred sample rate:" << info.preferredSampleRate;
}
qInfo(logAudio()) << " chunkSize: " << chunkSize;
}
else
{
qCritical(logAudio()) << (isInput ? "Input" : "Output") << QString::fromStdString(info.name) << "(" << aParams.deviceId << ") could not be probed, check audio configuration!";
}
int resample_error = 0;
if (isInput) {
resampler = wf_resampler_init(radioChannels, INTERNAL_SAMPLE_RATE, samplerate, resampleQuality, &resample_error);
//audio.openStream(&aParams, NULL, RTAUDIO_SINT16, INTERNAL_SAMPLE_RATE, &this->chunkSize, &output, (void*)&data);
}
else
{
resampler = wf_resampler_init(radioChannels, samplerate, INTERNAL_SAMPLE_RATE, resampleQuality, &resample_error);
//audio.openStream(&aParams, NULL, RTAUDIO_SINT16, INTERNAL_SAMPLE_RATE, &this->chunkSize, &output, (void*)&data);
}
wf_resampler_get_ratio(resampler, &ratioNum, &ratioDen);
qInfo(logAudio()) << " wf_resampler_init() returned: " << resample_error << " ratioNum" << ratioNum << " ratioDen" << ratioDen;
return isInitialized;
}
void audioHandler::setVolume(unsigned char volume)
{
qInfo(logAudio()) << (isInput ? "Input" : "Output") << "setVolume: " << volume << "(" << (qreal)(volume/255.0) << ")";
}
/// <summary>
/// This function processes the incoming audio FROM the radio and pushes it into the playback buffer *data
/// </summary>
/// <param name="data"></param>
/// <param name="maxlen"></param>
/// <returns></returns>
qint64 audioHandler::readData(char* data, qint64 maxlen)
{
// Calculate output length, always full samples
int sentlen = 0;
//qInfo(logAudio()) << "Looking for: " << maxlen << " bytes";
// We must lock the mutex for the entire time that the buffer may be modified.
// Get next packet from buffer.
if (!audioBuffer.isEmpty())
{
// Output buffer is ALWAYS 16 bit.
QMutexLocker locker(&mutex);
auto packet = audioBuffer.begin();
while (packet != audioBuffer.end() && sentlen < maxlen)
{
int timediff = packet->time.msecsTo(QTime::currentTime());
if (timediff > (int)audioLatency * 2) {
qInfo(logAudio()) << (isInput ? "Input" : "Output") << "Packet " << hex << packet->seq <<
" arrived too late (increase output latency!) " <<
dec << packet->time.msecsTo(QTime::currentTime()) << "ms";
while (packet != audioBuffer.end() && timediff > (int)audioLatency) {
timediff = packet->time.msecsTo(QTime::currentTime());
lastSeq = packet->seq;
packet = audioBuffer.erase(packet); // returns next packet
}
if (packet == audioBuffer.end()) {
break;
}
}
// If we got here then packet time must be within latency threshold
if (packet->seq == lastSeq + 1 || packet->seq <= lastSeq)
{
int send = qMin((int)maxlen - sentlen, packet->dataout.length() - packet->sent);
lastSeq = packet->seq;
//qInfo(logAudio()) << "Packet " << hex << packet->seq << " arrived on time " << Qt::dec << packet->time.msecsTo(QTime::currentTime()) << "ms";
memcpy(data + sentlen, packet->dataout.constData() + packet->sent, send);
sentlen = sentlen + send;
if (send == packet->dataout.length() - packet->sent)
{
//qInfo(logAudio()) << "Get next packet";
packet = audioBuffer.erase(packet); // returns next packet
}
else
{
// Store sent amount (could be zero if audioOutput buffer full) then break.
packet->sent = send;
break;
}
}
else {
qInfo(logAudio()) << (isInput ? "Input" : "Output") << "Missing audio packet(s) from: " << hex << lastSeq + 1 << " to " << hex << packet->seq - 1;
lastSeq = packet->seq;
}
}
}
else {
// Fool audio system into thinking it has valid data, this seems to be required
// for MacOS Built in audio but shouldn't cause any issues with other platforms.
memset(data, 0x0, maxlen);
return maxlen;
}
return sentlen;
}
qint64 audioHandler::writeData(const char* data, qint64 len)
{
qint64 sentlen = 0;
QMutexLocker locker(&mutex);
audioPacket* current;
while (sentlen < len) {
if (!audioBuffer.isEmpty())
{
if (audioBuffer.last().sent == chunkSize)
{
audioBuffer.append(audioPacket());
audioBuffer.last().sent = 0;
}
}
else
{
audioBuffer.append(audioPacket());
audioBuffer.last().sent = 0;
}
current = &audioBuffer.last();
int send = qMin((int)(len - sentlen), (int)chunkSize - current->sent);
current->datain.append(QByteArray::fromRawData(data + sentlen, send));
sentlen = sentlen + send;
current->seq = 0; // Not used in TX
current->time = QTime::currentTime();
current->sent = current->datain.length();
if (current->sent == chunkSize)
{
chunkAvailable = true;
}
else if (audioBuffer.length() <= 1 && current->sent != chunkSize) {
chunkAvailable = false;
}
}
return (sentlen); // Always return the same number as we received
}
qint64 audioHandler::bytesAvailable() const
{
return 0;
}
bool audioHandler::isSequential() const
{
return true;
}
void audioHandler::notified()
{
}
void audioHandler::stateChanged(QAudio::State state)
{
}
void audioHandler::incomingAudio(audioPacket data)
{
QMutexLocker locker(&mutex);
// Incoming data is 8bits?
if (radioSampleBits == 8)
{
QByteArray inPacket((int)data.datain.length() * 2, (char)0xff);
qint16* in = (qint16*)inPacket.data();
for (int f = 0; f < data.datain.length(); f++)
{
if (isUlaw)
{
in[f] = ulaw_decode[(quint8)data.datain[f]];
}
else
{
// Convert 8-bit sample to 16-bit
in[f] = (qint16)(((quint8)data.datain[f] << 8) - 32640);
}
}
data.datain = inPacket; // Replace incoming data with converted.
}
//qInfo(logAudio()) << "Adding packet to buffer:" << data.seq << ": " << data.datain.length();
/* We now have an array of 16bit samples in the NATIVE samplerate of the radio
If the radio sample rate is below 48000, we need to resample.
*/
if (ratioDen != 1) {
// We need to resample
quint32 outFrames = ((data.datain.length() / 2) * ratioDen) / radioChannels;
quint32 inFrames = (data.datain.length() / 2) / radioChannels;
data.dataout.resize(outFrames * 2 * radioChannels); // Preset the output buffer size.
int err = 0;
if (this->radioChannels == 1) {
err = wf_resampler_process_int(resampler, 0, (const qint16*)data.datain.constData(), &inFrames, (qint16*)data.dataout.data(), &outFrames);
}
else {
err = wf_resampler_process_interleaved_int(resampler, (const qint16*)data.datain.constData(), &inFrames, (qint16*)data.dataout.data(), &outFrames);
}
if (err) {
qInfo(logAudio()) << (isInput ? "Input" : "Output") << "Resampler error " << err << " inFrames:" << inFrames << " outFrames:" << outFrames;
}
}
else {
data.dataout = data.datain;
}
audioBuffer.push_back(data);
// Sort the buffer by seq number. This is important and audio packets may have arrived out-of-order
std::sort(audioBuffer.begin(), audioBuffer.end(),
[](const audioPacket& a, const audioPacket& b) -> bool
{
return a.seq < b.seq;
});
}
void audioHandler::changeLatency(const quint16 newSize)
{
qInfo(logAudio()) << (isInput ? "Input" : "Output") << "Changing latency to: " << newSize << " from " << audioLatency;
audioLatency = newSize;
}
void audioHandler::getLatency()
{
emit sendLatency(audioLatency);
}
bool audioHandler::isChunkAvailable()
{
return (chunkAvailable);
}
void audioHandler::getNextAudioChunk(QByteArray& ret)
{
if (!audioBuffer.isEmpty() && chunkAvailable)
{
QMutexLocker locker(&mutex);
// Skip through audio buffer deleting any old entry.
auto packet = audioBuffer.begin();
while (packet != audioBuffer.end())
{
if (packet->time.msecsTo(QTime::currentTime()) > 100) {
//qInfo(logAudio()) << "TX Packet too old " << dec << packet->time.msecsTo(QTime::currentTime()) << "ms";
packet = audioBuffer.erase(packet); // returns next packet
}
else {
if (packet->datain.length() == chunkSize && ret.length() == 0)
{
/* We now have an array of samples in the computer native format (48000)
If the radio sample rate is below 48000, we need to resample.
*/
if (ratioNum != 1)
{
// We need to resample (we are STILL 16 bit!)
quint32 outFrames = ((packet->datain.length() / 2) / ratioNum) / radioChannels;
quint32 inFrames = (packet->datain.length() / 2) / radioChannels;
packet->dataout.resize(outFrames * 2 * radioChannels); // Preset the output buffer size.
const qint16* in = (qint16*)packet->datain.constData();
qint16* out = (qint16*)packet->dataout.data();
int err = 0;
if (this->radioChannels == 1) {
err = wf_resampler_process_int(resampler, 0, in, &inFrames, out, &outFrames);
}
else {
err = wf_resampler_process_interleaved_int(resampler, in, &inFrames, out, &outFrames);
}
if (err) {
qInfo(logAudio()) << (isInput ? "Input" : "Output") << "Resampler error " << err << " inFrames:" << inFrames << " outFrames:" << outFrames;
}
//qInfo(logAudio()) << "Resampler run " << err << " inFrames:" << inFrames << " outFrames:" << outFrames;
//qInfo(logAudio()) << "Resampler run inLen:" << packet->datain.length() << " outLen:" << packet->dataout.length();
if (radioSampleBits == 8)
{
packet->datain = packet->dataout; // Copy output packet back to input buffer.
packet->dataout.clear(); // Buffer MUST be cleared ready to be re-filled by the upsampling below.
}
}
else if (radioSampleBits == 16) {
// Only copy buffer if radioSampleBits is 16, as it will be handled below otherwise.
packet->dataout = packet->datain;
}
// Do we need to convert 16-bit to 8-bit?
if (radioSampleBits == 8) {
packet->dataout.resize(packet->datain.length() / 2);
qint16* in = (qint16*)packet->datain.data();
for (int f = 0; f < packet->dataout.length(); f++)
{
quint8 outdata = 0;
if (isUlaw) {
qint16 enc = qFromLittleEndian<quint16>(in + f);
if (enc >= 0)
outdata = ulaw_encode[enc];
else
outdata = 0x7f & ulaw_encode[-enc];
}
else {
outdata = (quint8)(((qFromLittleEndian<qint16>(in + f) >> 8) ^ 0x80) & 0xff);
}
packet->dataout[f] = (char)outdata;
}
}
ret = packet->dataout;
packet = audioBuffer.erase(packet); // returns next packet
}
else {
packet++;
}
}
}
}
return;
}

Wyświetl plik

@ -43,6 +43,8 @@ struct udpPreferences {
QString password; QString password;
int audioOutput; int audioOutput;
int audioInput; int audioInput;
QString audioOutputName;
QString audioInputName;
quint16 audioRXLatency; quint16 audioRXLatency;
quint16 audioTXLatency; quint16 audioTXLatency;

Wyświetl plik

@ -395,7 +395,7 @@ void udpServer::controlReceived()
connect(this, SIGNAL(setupTxAudio(quint8,quint8,quint16,quint16,bool,bool,int,quint8)), txaudio, SLOT(init(quint8,quint8,quint16,quint16,bool,bool,int,quint8))); connect(this, SIGNAL(setupTxAudio(quint8,quint8,quint16,quint16,bool,bool,int,quint8)), txaudio, SLOT(init(quint8,quint8,quint16,quint16,bool,bool,int,quint8)));
connect(txAudioThread, SIGNAL(finished()), txaudio, SLOT(deleteLater())); connect(txAudioThread, SIGNAL(finished()), txaudio, SLOT(deleteLater()));
emit setupTxAudio(samples, channels, current->txSampleRate, in->txbuffer, uLaw, false, config.audioOutput,config.audioOutputDevice, config.resampleQuality); emit setupTxAudio(samples, channels, current->txSampleRate, in->txbuffer, uLaw, false, config.audioOutput, config.resampleQuality);
hasTxAudio=datagram.senderAddress(); hasTxAudio=datagram.senderAddress();
connect(this, SIGNAL(haveAudioData(audioPacket)), txaudio, SLOT(incomingAudio(audioPacket))); connect(this, SIGNAL(haveAudioData(audioPacket)), txaudio, SLOT(incomingAudio(audioPacket)));
@ -428,7 +428,7 @@ void udpServer::controlReceived()
connect(this, SIGNAL(setupRxAudio(quint8,quint8,quint16,quint16,bool,bool,QString,int,quint8)), rxaudio, SLOT(init(quint8,quint8,quint16,quint16,bool,bool,int,quint8))); connect(this, SIGNAL(setupRxAudio(quint8,quint8,quint16,quint16,bool,bool,QString,int,quint8)), rxaudio, SLOT(init(quint8,quint8,quint16,quint16,bool,bool,int,quint8)));
connect(rxAudioThread, SIGNAL(finished()), txaudio, SLOT(deleteLater())); connect(rxAudioThread, SIGNAL(finished()), txaudio, SLOT(deleteLater()));
emit setupRxAudio(samples, channels, current->rxSampleRate, 150, uLaw, true, config.audioInput, config.audioInputDevice, config.resampleQuality); emit setupRxAudio(samples, channels, current->rxSampleRate, 150, uLaw, true, config.audioInput, config.resampleQuality);
rxAudioTimer = new QTimer(); rxAudioTimer = new QTimer();
rxAudioTimer->setTimerType(Qt::PreciseTimer); rxAudioTimer->setTimerType(Qt::PreciseTimer);

Wyświetl plik

@ -169,7 +169,6 @@ wfmain::wfmain(const QString serialPortCL, const QString hostCL, const QString s
// Enumerate audio devices, need to do before settings are loaded. // Enumerate audio devices, need to do before settings are loaded.
#ifdef USE_RTAUDIO
std::map<int, std::string> apiMap; std::map<int, std::string> apiMap;
apiMap[RtAudio::MACOSX_CORE] = "OS-X Core Audio"; apiMap[RtAudio::MACOSX_CORE] = "OS-X Core Audio";
apiMap[RtAudio::WINDOWS_ASIO] = "Windows ASIO"; apiMap[RtAudio::WINDOWS_ASIO] = "Windows ASIO";
@ -197,7 +196,7 @@ wfmain::wfmain(const QString serialPortCL, const QString hostCL, const QString s
unsigned int devices = audio.getDeviceCount(); unsigned int devices = audio.getDeviceCount();
qInfo(logAudio()) << "Found " << devices << " audio device(s) ..."; qInfo(logAudio()) << "Found " << devices << " audio device(s) ...";
for (unsigned int i = 0; i < devices; i++) { for (unsigned int i = 1; i < devices; i++) {
info = audio.getDeviceInfo(i); info = audio.getDeviceInfo(i);
if (info.outputChannels > 0) { if (info.outputChannels > 0) {
qInfo(logAudio()) << (info.isDefaultOutput ? "*" : " ") << "(" << i << ") Output Device : " << QString::fromStdString(info.name); qInfo(logAudio()) << (info.isDefaultOutput ? "*" : " ") << "(" << i << ") Output Device : " << QString::fromStdString(info.name);
@ -297,8 +296,8 @@ wfmain::wfmain(const QString serialPortCL, const QString hostCL, const QString s
serverConfig.audioInput = udpPrefs.audioInput; serverConfig.audioInput = udpPrefs.audioInput;
serverConfig.audioOutput = udpPrefs.audioOutput; serverConfig.audioOutput = udpPrefs.audioOutput;
serverConfig.baudRate = prefs.serialPortBaud; serverConfig.baudRate = prefs.serialPortBaud;
serverConfig.inputDevice = udpPrefs.inputDevice; serverConfig.audioInput = udpPrefs.audioInput;
serverConfig.outputDevice = udpPrefs.outputDevice; serverConfig.audioOutput = udpPrefs.audioOutput;
} }
udp = new udpServer(serverConfig); udp = new udpServer(serverConfig);
@ -952,15 +951,8 @@ void wfmain::setDefPrefs()
udpDefPrefs.audioLANPort = 50003; udpDefPrefs.audioLANPort = 50003;
udpDefPrefs.username = QString(""); udpDefPrefs.username = QString("");
udpDefPrefs.password = QString(""); udpDefPrefs.password = QString("");
#if defined(USE_RTAUDIO) udpDefPrefs.audioOutput = 0;
udpDefPrefs.audioOutput = QString::fromStdString(audio.getDeviceInfo(audio.getDefaultOutputDevice()).name); udpDefPrefs.audioInput = 0;
udpDefPrefs.audioInput = QString::fromStdString(audio.getDeviceInfo(audio.getDefaultInputDevice()).name);
#elif defined(USE_QTAUDIO)
udpDefPrefs.audioOutput = QAudioDeviceInfo::defaultOutputDevice().deviceName();
udpDefPrefs.audioInput = QAudioDeviceInfo::defaultInputDevice().deviceName();
#endif
udpDefPrefs.audioInputDevice = 0;
udpDefPrefs.audioOutputDevice = 0;
udpDefPrefs.audioRXLatency = 150; udpDefPrefs.audioRXLatency = 150;
udpDefPrefs.audioTXLatency = 150; udpDefPrefs.audioTXLatency = 150;
udpDefPrefs.audioRXSampleRate = 48000; udpDefPrefs.audioRXSampleRate = 48000;
@ -1123,7 +1115,7 @@ void wfmain::loadSettings()
ui->audioSampleRateCombo->setEnabled(ui->lanEnableBtn->isChecked()); ui->audioSampleRateCombo->setEnabled(ui->lanEnableBtn->isChecked());
int audioSampleRateIndex = ui->audioSampleRateCombo->findText(QString::number(udpDefPrefs.audioRXSampleRate)); int audioSampleRateIndex = ui->audioSampleRateCombo->findText(QString::number(udpDefPrefs.audioRXSampleRate));
if (audioSampleRateIndex != -1) { if (audioSampleRateIndex != -1) {
ui->audioOutputCombo->setCurrentIndex(audioSampleRateIndex); ui->audioSampleRateCombo->setCurrentIndex(audioSampleRateIndex);
} }
// Add codec combobox items here so that we can add userdata! // Add codec combobox items here so that we can add userdata!
@ -1150,22 +1142,20 @@ void wfmain::loadSettings()
if (ui->audioTXCodecCombo->itemData(f).toInt() == udpPrefs.audioTXCodec) if (ui->audioTXCodecCombo->itemData(f).toInt() == udpPrefs.audioTXCodec)
ui->audioTXCodecCombo->setCurrentIndex(f); ui->audioTXCodecCombo->setCurrentIndex(f);
udpPrefs.audioOutput = settings->value("AudioOutput", udpDefPrefs.audioOutput).toString(); udpPrefs.audioOutputName = settings->value("AudioOutput", udpDefPrefs.audioOutputName).toString();
qInfo(logGui()) << "Got Audio Output: " << udpPrefs.audioOutput; qInfo(logGui()) << "Got Audio Output: " << udpPrefs.audioOutputName;
//ui->audioOutputCombo->setEnabled(ui->lanEnableBtn->isChecked()); int audioOutputIndex = ui->audioOutputCombo->findText(udpPrefs.audioOutputName);
int audioOutputIndex = ui->audioOutputCombo->findText(udpPrefs.audioOutput);
if (audioOutputIndex != -1) { if (audioOutputIndex != -1) {
ui->audioOutputCombo->setCurrentIndex(audioOutputIndex); ui->audioOutputCombo->setCurrentIndex(audioOutputIndex);
udpPrefs.audioOutputDevice = ui->audioOutputCombo->itemData(audioOutputIndex).toInt(); udpPrefs.audioOutput = ui->audioOutputCombo->itemData(audioOutputIndex).toInt();
} }
udpPrefs.audioInput = settings->value("AudioInput", udpDefPrefs.audioInput).toString(); udpPrefs.audioInputName = settings->value("AudioInput", udpDefPrefs.audioInputName).toString();
qInfo(logGui()) << "Got Audio Input: " << udpPrefs.audioInput; qInfo(logGui()) << "Got Audio Input: " << udpPrefs.audioInputName;
//ui->audioInputCombo->setEnabled(ui->lanEnableBtn->isChecked()); int audioInputIndex = ui->audioInputCombo->findText(udpPrefs.audioInputName);
int audioInputIndex = ui->audioInputCombo->findText(udpPrefs.audioInput);
if (audioInputIndex != -1) { if (audioInputIndex != -1) {
ui->audioInputCombo->setCurrentIndex(audioInputIndex); ui->audioInputCombo->setCurrentIndex(audioInputIndex);
udpPrefs.audioInputDevice = ui->audioInputCombo->itemData(audioInputIndex).toInt(); udpPrefs.audioInput = ui->audioInputCombo->itemData(audioInputIndex).toInt();
} }
udpPrefs.resampleQuality = settings->value("ResampleQuality", udpDefPrefs.resampleQuality).toInt(); udpPrefs.resampleQuality = settings->value("ResampleQuality", udpDefPrefs.resampleQuality).toInt();
@ -1279,8 +1269,8 @@ void wfmain::saveSettings()
settings->setValue("AudioRXCodec", udpPrefs.audioRXCodec); settings->setValue("AudioRXCodec", udpPrefs.audioRXCodec);
settings->setValue("AudioTXSampleRate", udpPrefs.audioRXSampleRate); settings->setValue("AudioTXSampleRate", udpPrefs.audioRXSampleRate);
settings->setValue("AudioTXCodec", udpPrefs.audioTXCodec); settings->setValue("AudioTXCodec", udpPrefs.audioTXCodec);
settings->setValue("AudioOutput", udpPrefs.audioOutput); settings->setValue("AudioOutput", udpPrefs.audioOutputName);
settings->setValue("AudioInput", udpPrefs.audioInput); settings->setValue("AudioInput", udpPrefs.audioInputName);
settings->setValue("ResampleQuality", udpPrefs.resampleQuality); settings->setValue("ResampleQuality", udpPrefs.resampleQuality);
settings->setValue("ClientName", udpPrefs.clientName); settings->setValue("ClientName", udpPrefs.clientName);
settings->endGroup(); settings->endGroup();
@ -3577,14 +3567,14 @@ void wfmain::on_passwordTxt_textChanged(QString text)
void wfmain::on_audioOutputCombo_currentIndexChanged(int value) void wfmain::on_audioOutputCombo_currentIndexChanged(int value)
{ {
udpPrefs.audioOutput = ui->audioOutputCombo->currentText(); udpPrefs.audioOutput = ui->audioOutputCombo->itemData(value).toInt();
udpPrefs.audioOutputDevice = ui->audioOutputCombo->itemData(value).toInt(); udpPrefs.audioOutputName = ui->audioOutputCombo->itemText(value);
} }
void wfmain::on_audioInputCombo_currentIndexChanged(int value) void wfmain::on_audioInputCombo_currentIndexChanged(int value)
{ {
udpPrefs.audioInput = ui->audioInputCombo->currentText(); udpPrefs.audioInput = ui->audioInputCombo->itemData(value).toInt();
udpPrefs.audioInputDevice = ui->audioOutputCombo->itemData(value).toInt(); udpPrefs.audioInputName = ui->audioInputCombo->itemText(value);
} }
void wfmain::on_audioSampleRateCombo_currentIndexChanged(QString text) void wfmain::on_audioSampleRateCombo_currentIndexChanged(QString text)

Wyświetl plik

@ -26,7 +26,7 @@
#include "qledlabel.h" #include "qledlabel.h"
#include "rigctld.h" #include "rigctld.h"
#include <qcustomplot.h> #include <../qcustomplot/qcustomplot.h>
#include <qserialportinfo.h> #include <qserialportinfo.h>
namespace Ui { namespace Ui {
@ -705,9 +705,7 @@ private:
SERVERCONFIG serverConfig; SERVERCONFIG serverConfig;
#ifdef USE_RTAUDIO
RtAudio audio; RtAudio audio;
#endif
}; };
Q_DECLARE_METATYPE(struct rigCapabilities) Q_DECLARE_METATYPE(struct rigCapabilities)

Wyświetl plik

@ -374,7 +374,8 @@
</QtUic> </QtUic>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="audiohandler_rt.cpp" /> <ClCompile Include="..\qcustomplot\qcustomplot.cpp" />
<ClCompile Include="audiohandler.cpp" />
<ClCompile Include="calibrationwindow.cpp" /> <ClCompile Include="calibrationwindow.cpp" />
<ClCompile Include="commhandler.cpp" /> <ClCompile Include="commhandler.cpp" />
<ClCompile Include="freqmemory.cpp" /> <ClCompile Include="freqmemory.cpp" />
@ -382,14 +383,12 @@
<ClCompile Include="qledlabel.cpp" /> <ClCompile Include="qledlabel.cpp" />
<ClCompile Include="logcategories.cpp" /> <ClCompile Include="logcategories.cpp" />
<ClCompile Include="main.cpp" /> <ClCompile Include="main.cpp" />
<ClCompile Include="c:\qcustomplot\qcustomplot.cpp" />
<ClCompile Include="meter.cpp" /> <ClCompile Include="meter.cpp" />
<ClCompile Include="repeatersetup.cpp" /> <ClCompile Include="repeatersetup.cpp" />
<ClCompile Include="resampler\resample.c" /> <ClCompile Include="resampler\resample.c" />
<ClCompile Include="rigcommander.cpp" /> <ClCompile Include="rigcommander.cpp" />
<ClCompile Include="rigctld.cpp" /> <ClCompile Include="rigctld.cpp" />
<ClCompile Include="rigidentities.cpp" /> <ClCompile Include="rigidentities.cpp" />
<ClCompile Include="audiohandler.cpp" />
<ClCompile Include="rtaudio\RtAudio.cpp" /> <ClCompile Include="rtaudio\RtAudio.cpp" />
<ClCompile Include="satellitesetup.cpp" /> <ClCompile Include="satellitesetup.cpp" />
<ClCompile Include="udphandler.cpp" /> <ClCompile Include="udphandler.cpp" />
@ -398,6 +397,7 @@
<ClCompile Include="wfmain.cpp" /> <ClCompile Include="wfmain.cpp" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<QtMoc Include="..\qcustomplot\qcustomplot.h" />
<ClInclude Include="rtaudio\RtAudio.h" /> <ClInclude Include="rtaudio\RtAudio.h" />
<QtMoc Include="udpserver.h" /> <QtMoc Include="udpserver.h" />
<QtMoc Include="udpserversetup.h" /> <QtMoc Include="udpserversetup.h" />
@ -409,8 +409,6 @@
<ClInclude Include="freqmemory.h" /> <ClInclude Include="freqmemory.h" />
<QtMoc Include="qledlabel.h" /> <QtMoc Include="qledlabel.h" />
<ClInclude Include="logcategories.h" /> <ClInclude Include="logcategories.h" />
<QtMoc Include="c:\qcustomplot\qcustomplot.h">
</QtMoc>
<QtMoc Include="rigcommander.h"> <QtMoc Include="rigcommander.h">
</QtMoc> </QtMoc>
<QtMoc Include="meter.h" /> <QtMoc Include="meter.h" />

Wyświetl plik

@ -69,9 +69,6 @@
<ClCompile Include="main.cpp"> <ClCompile Include="main.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="c:\qcustomplot\qcustomplot.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="rigcommander.cpp"> <ClCompile Include="rigcommander.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
@ -84,9 +81,6 @@
<ClCompile Include="wfmain.cpp"> <ClCompile Include="wfmain.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="audiohandler.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="calibrationwindow.cpp"> <ClCompile Include="calibrationwindow.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
@ -120,7 +114,10 @@
<ClCompile Include="rtaudio\RtAudio.cpp"> <ClCompile Include="rtaudio\RtAudio.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="audiohandler_rt.cpp"> <ClCompile Include="audiohandler.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\qcustomplot\qcustomplot.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
</ItemGroup> </ItemGroup>
@ -134,9 +131,6 @@
<ClInclude Include="logcategories.h"> <ClInclude Include="logcategories.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<QtMoc Include="c:\qcustomplot\qcustomplot.h">
<Filter>Header Files</Filter>
</QtMoc>
<QtMoc Include="rigcommander.h"> <QtMoc Include="rigcommander.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</QtMoc> </QtMoc>
@ -179,6 +173,9 @@
<QtMoc Include="rigctld.h"> <QtMoc Include="rigctld.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</QtMoc> </QtMoc>
<QtMoc Include="..\qcustomplot\qcustomplot.h">
<Filter>Header Files</Filter>
</QtMoc>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<CustomBuild Include="debug\moc_predefs.h.cbt"> <CustomBuild Include="debug\moc_predefs.h.cbt">
@ -359,6 +356,7 @@
<None Include="resources\wfview.png" /> <None Include="resources\wfview.png" />
<None Include="resources\wfview.png" /> <None Include="resources\wfview.png" />
<None Include="resources\wfview.png" /> <None Include="resources\wfview.png" />
<None Include="resources\wfview.png" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="resources\install.sh"> <None Include="resources\install.sh">