wfview/rthandler.cpp

402 wiersze
11 KiB
C++
Czysty Zwykły widok Historia

#include "rthandler.h"
#include "logcategories.h"
#if defined(Q_OS_WIN)
#include <objbase.h>
#endif
#undef RT_EXCEPTION
rtHandler::rtHandler(QObject* parent)
{
Q_UNUSED(parent)
}
rtHandler::~rtHandler()
{
2022-07-05 09:37:10 +00:00
if (converterThread != Q_NULLPTR) {
converterThread->quit();
converterThread->wait();
}
if (isInitialized) {
2022-07-05 09:37:10 +00:00
#ifdef RT_EXCEPTION
try {
#endif
audio->abortStream();
audio->closeStream();
#ifdef RT_EXCEPTION
}
2022-05-14 14:05:48 +00:00
catch (RtAudioError& e) {
qInfo(logAudio()) << "Error closing stream:" << aParams.deviceId << ":" << QString::fromStdString(e.getMessage());
}
#endif
delete audio;
}
}
bool rtHandler::init(audioSetup setup)
{
if (isInitialized) {
return false;
}
this->setup = setup;
2022-05-14 13:02:31 +00:00
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "RTAudio handler starting:" << setup.name;
if (setup.portInt==-1)
{
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "No audio device was found.";
return false;
}
inFormat = toQAudioFormat(setup.codec, setup.sampleRate);
qDebug(logAudio()) << "Creating" << (setup.isinput ? "Input" : "Output") << "audio device:" << setup.name <<
2022-12-29 15:26:41 +00:00
#if (QT_VERSION < QT_VERSION_CHECK(6,0,0))
", bits" << inFormat.sampleSize() <<
2022-08-22 22:09:09 +00:00
#else
", format" << inFormat.sampleFormat() <<
#endif
", codec" << setup.codec <<
", latency" << setup.latency <<
", localAFGain" << setup.localAFgain <<
", radioChan" << inFormat.channelCount() <<
", resampleQuality" << setup.resampleQuality <<
", samplerate" << inFormat.sampleRate() <<
", uLaw" << setup.ulaw;
#if !defined(Q_OS_MACX)
2022-05-13 09:20:17 +00:00
options.flags = ((!RTAUDIO_HOG_DEVICE) | (RTAUDIO_MINIMIZE_LATENCY));
//options.flags = RTAUDIO_MINIMIZE_LATENCY;
#endif
#if defined(Q_OS_LINUX)
audio = new RtAudio(RtAudio::Api::LINUX_ALSA);
#elif defined(Q_OS_WIN)
audio = new RtAudio(RtAudio::Api::WINDOWS_WASAPI);
#elif defined(Q_OS_MACX)
audio = new RtAudio(RtAudio::Api::MACOSX_CORE);
#endif
2022-08-22 22:09:09 +00:00
codecType codec = LPCM;
if (setup.codec == 0x01 || setup.codec == 0x20)
codec = PCMU;
else if (setup.codec == 0x40 || setup.codec == 0x40)
codec = OPUS;
2022-05-12 11:24:27 +00:00
options.numberOfBuffers = int(setup.latency/setup.blockSize);
if (setup.portInt > 0) {
aParams.deviceId = setup.portInt;
}
else if (setup.isinput) {
aParams.deviceId = audio->getDefaultInputDevice();
}
else {
aParams.deviceId = audio->getDefaultOutputDevice();
}
aParams.firstChannel = 0;
#ifdef RT_EXCEPTION
try {
#endif
info = audio->getDeviceInfo(aParams.deviceId);
#ifdef RT_EXCEPTION
}
2022-05-14 14:05:48 +00:00
catch (RtAudioError e) {
2022-05-14 09:33:31 +00:00
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Device exception:" << aParams.deviceId << ":" << QString::fromStdString(e.getMessage());
2022-05-14 09:41:44 +00:00
goto errorHandler;
}
#endif
#ifdef RT_EXCEPTION
if (info.probed)
{
#endif
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << QString::fromStdString(info.name) << "(" << aParams.deviceId << ") successfully probed";
RtAudioFormat sampleFormat;
2022-08-22 22:09:09 +00:00
2022-12-29 15:26:41 +00:00
#if (QT_VERSION < QT_VERSION_CHECK(6,0,0))
outFormat.setByteOrder(QAudioFormat::LittleEndian);
outFormat.setCodec("audio/pcm");
2022-08-22 22:09:09 +00:00
#endif
if (info.nativeFormats == 0)
{
qCritical(logAudio()) << " No natively supported data formats!";
2022-05-14 09:41:44 +00:00
goto errorHandler;
}
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;
if (setup.isinput) {
outFormat.setChannelCount(info.inputChannels);
}
else {
outFormat.setChannelCount(info.outputChannels);
}
qInfo(logAudio()) << " Channels:" << outFormat.channelCount();
if (outFormat.channelCount() > 2) {
outFormat.setChannelCount(2);
}
else if (outFormat.channelCount() < 1)
{
qCritical(logAudio()) << (setup.isinput ? "Input" : "Output") << "No channels found, aborting setup.";
2022-05-14 09:41:44 +00:00
goto errorHandler;
}
if (outFormat.channelCount() == 1 && inFormat.channelCount() == 2) {
outFormat.setChannelCount(2);
}
aParams.nChannels = outFormat.channelCount();
outFormat.setSampleRate(info.preferredSampleRate);
if (outFormat.sampleRate() < 44100) {
outFormat.setSampleRate(48000);
}
if (info.nativeFormats & RTAUDIO_FLOAT32) {
2022-12-29 15:26:41 +00:00
#if (QT_VERSION < QT_VERSION_CHECK(6,0,0))
outFormat.setSampleType(QAudioFormat::Float);
outFormat.setSampleSize(32);
2022-08-22 22:09:09 +00:00
#else
outFormat.setSampleFormat(QAudioFormat::Float);
#endif
sampleFormat = RTAUDIO_FLOAT32;
}
else if (info.nativeFormats & RTAUDIO_SINT32) {
2022-12-29 15:26:41 +00:00
#if (QT_VERSION < QT_VERSION_CHECK(6,0,0))
outFormat.setSampleType(QAudioFormat::SignedInt);
outFormat.setSampleSize(32);
2022-08-22 22:09:09 +00:00
#else
outFormat.setSampleFormat(QAudioFormat::Int32);
#endif
sampleFormat = RTAUDIO_SINT32;
}
else if (info.nativeFormats & RTAUDIO_SINT16) {
2022-12-29 15:26:41 +00:00
#if (QT_VERSION < QT_VERSION_CHECK(6,0,0))
outFormat.setSampleType(QAudioFormat::SignedInt);
outFormat.setSampleSize(16);
2022-08-22 22:09:09 +00:00
#else
outFormat.setSampleFormat(QAudioFormat::Int16);
#endif
sampleFormat = RTAUDIO_SINT16;
}
else {
qCritical(logAudio()) << "Cannot find supported sample format!";
2022-05-14 09:41:44 +00:00
goto errorHandler;
}
}
2022-12-29 15:26:41 +00:00
#if (QT_VERSION < QT_VERSION_CHECK(6,0,0))
qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Selected format: SampleSize" << outFormat.sampleSize() << "Channel Count" << outFormat.channelCount() <<
"Sample Rate" << outFormat.sampleRate() << "Codec" << outFormat.codec() << "Sample Type" << outFormat.sampleType();
2022-08-22 22:09:09 +00:00
#else
qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Selected format: SampleFormat" << outFormat.sampleFormat() << "Channel Count" << outFormat.channelCount() <<
"Sample Rate" << outFormat.sampleRate() << "Codec" << codec;
#endif
// We "hopefully" now have a valid format that is supported so try connecting
converter = new audioConverter();
converterThread = new QThread(this);
if (setup.isinput) {
converterThread->setObjectName("audioConvIn()");
}
else {
converterThread->setObjectName("audioConvOut()");
}
converter->moveToThread(converterThread);
2022-08-22 22:09:09 +00:00
connect(this, SIGNAL(setupConverter(QAudioFormat, codecType, QAudioFormat, codecType, quint8, quint8)), converter, SLOT(init(QAudioFormat, codecType, QAudioFormat, codecType, quint8, quint8)));
connect(converterThread, SIGNAL(finished()), converter, SLOT(deleteLater()));
connect(this, SIGNAL(sendToConverter(audioPacket)), converter, SLOT(convert(audioPacket)));
converterThread->start(QThread::TimeCriticalPriority);
// Per channel chunk size.
this->chunkSize = (outFormat.bytesForDuration(setup.blockSize * 1000) / outFormat.bytesPerFrame());
#ifdef RT_EXCEPTION
try {
#endif
if (setup.isinput) {
audio->openStream(NULL, &aParams, sampleFormat, outFormat.sampleRate(), &this->chunkSize, &staticWrite, this, &options);
2022-08-22 22:09:09 +00:00
emit setupConverter(outFormat, codec, inFormat, codecType::LPCM, 7, setup.resampleQuality);
connect(converter, SIGNAL(converted(audioPacket)), this, SLOT(convertedInput(audioPacket)));
}
else {
audio->openStream(&aParams, NULL, sampleFormat, outFormat.sampleRate(), &this->chunkSize, &staticRead, this , &options);
2022-08-22 22:09:09 +00:00
emit setupConverter(inFormat, codecType::LPCM, outFormat, codec, 7, setup.resampleQuality);
connect(converter, SIGNAL(converted(audioPacket)), this, SLOT(convertedOutput(audioPacket)));
}
audio->startStream();
isInitialized = true;
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "device successfully opened";
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "detected latency:" << audio->getStreamLatency();
#ifdef RT_EXCEPTION
}
catch (RtAudioError& e) {
qInfo(logAudio()) << "Error opening:" << QString::fromStdString(e.getMessage());
// Try again?
2022-05-14 09:41:44 +00:00
goto errorHandler;
}
#endif
#ifdef RT_EXCEPTION
}
else
{
2022-05-14 09:41:44 +00:00
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << QString::fromStdString(info.name) << "(" << aParams.deviceId << ") could not be probed, check audio configuration!";
goto errorHandler;
}
#endif
2022-05-13 08:55:16 +00:00
this->setVolume(setup.localAFgain);
return isInitialized;
2022-05-14 09:41:44 +00:00
errorHandler:
if (retryConnectCount < 10) {
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "*** Attempting to reconnect to audio device in 500ms";
QTimer::singleShot(500, this, std::bind(&rtHandler::init, this, setup));
retryConnectCount++;
}
else
{
qCritical(logAudio()) << (setup.isinput ? "Input" : "Output") << "*** Retry count exceeded, giving up!";
}
return false;
}
void rtHandler::setVolume(unsigned char volume)
{
this->volume = audiopot[volume];
}
void rtHandler::incomingAudio(audioPacket packet)
{
packet.volume = volume;
emit sendToConverter(packet);
return;
}
int rtHandler::readData(void* outputBuffer, void* inputBuffer,
unsigned int nFrames, double streamTime, RtAudioStreamStatus status)
{
Q_UNUSED(inputBuffer);
Q_UNUSED(streamTime);
int nBytes = nFrames * outFormat.bytesPerFrame();
//lastSentSeq = packet.seq;
if (arrayBuffer.length() >= nBytes) {
2022-05-12 19:30:51 +00:00
if (audioMutex.tryLock(0)) {
std::memcpy(outputBuffer, arrayBuffer.constData(), nBytes);
arrayBuffer.remove(0, nBytes);
audioMutex.unlock();
}
}
if (status == RTAUDIO_INPUT_OVERFLOW) {
isUnderrun = true;
}
else if (status == RTAUDIO_OUTPUT_UNDERFLOW) {
isOverrun = true;
}
else
{
isUnderrun = false;
isOverrun = false;
}
return 0;
}
int rtHandler::writeData(void* outputBuffer, void* inputBuffer,
unsigned int nFrames, double streamTime, RtAudioStreamStatus status)
{
Q_UNUSED(outputBuffer);
Q_UNUSED(streamTime);
Q_UNUSED(status);
audioPacket packet;
packet.time = QTime::currentTime();
packet.sent = 0;
packet.volume = volume;
memcpy(&packet.guid, setup.guid, GUIDLEN);
packet.data.append((char*)inputBuffer, nFrames * outFormat.bytesPerFrame());
emit sendToConverter(packet);
if (status == RTAUDIO_INPUT_OVERFLOW) {
isUnderrun = true;
}
else if (status == RTAUDIO_OUTPUT_UNDERFLOW) {
isOverrun = true;
}
else
{
isUnderrun = false;
isOverrun = false;
}
return 0;
}
void rtHandler::convertedOutput(audioPacket packet)
{
2022-05-12 19:30:51 +00:00
audioMutex.lock();
arrayBuffer.append(packet.data);
2022-05-12 19:30:51 +00:00
audioMutex.unlock();
2022-12-29 14:16:22 +00:00
amplitude = packet.amplitudePeak;
currentLatency = packet.time.msecsTo(QTime::currentTime()) + (outFormat.durationForBytes(audio->getStreamLatency() * outFormat.bytesPerFrame()) / 1000);
2022-12-29 14:16:22 +00:00
emit haveLevels(getAmplitude(), packet.amplitudeRMS, setup.latency, currentLatency, isUnderrun, isOverrun);
}
2022-05-13 09:20:17 +00:00
void rtHandler::convertedInput(audioPacket packet)
{
2022-05-13 09:20:17 +00:00
if (packet.data.size() > 0) {
emit haveAudioData(packet);
2022-12-29 14:16:22 +00:00
amplitude = packet.amplitudePeak;
currentLatency = packet.time.msecsTo(QTime::currentTime()) + (outFormat.durationForBytes(audio->getStreamLatency() * outFormat.bytesPerFrame()) / 1000);
2022-12-29 14:16:22 +00:00
emit haveLevels(getAmplitude(), static_cast<quint16>(packet.amplitudeRMS * 255.0), setup.latency, currentLatency, isUnderrun, isOverrun);
}
}
void rtHandler::changeLatency(const quint16 newSize)
{
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Changing latency to: " << newSize << " from " << setup.latency;
}
int rtHandler::getLatency()
{
return currentLatency;
}
quint16 rtHandler::getAmplitude()
{
2022-05-14 00:11:00 +00:00
return static_cast<quint16>(amplitude * 255.0);
}