wfview/rthandler.cpp

402 wiersze
12 KiB
C++

#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()
{
if (converterThread != Q_NULLPTR) {
converterThread->quit();
converterThread->wait();
}
if (isInitialized) {
#ifdef RT_EXCEPTION
try {
#endif
audio->abortStream();
audio->closeStream();
#ifdef RT_EXCEPTION
}
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;
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;
}
radioFormat = toQAudioFormat(setup.codec, setup.sampleRate);
qDebug(logAudio()) << "Creating" << (setup.isinput ? "Input" : "Output") << "audio device:" << setup.name <<
#if (QT_VERSION < QT_VERSION_CHECK(6,0,0))
", bits" << radioFormat.sampleSize() <<
#else
", format" << radioFormat.sampleFormat() <<
#endif
", codec" << setup.codec <<
", latency" << setup.latency <<
", localAFGain" << setup.localAFgain <<
", radioChan" << radioFormat.channelCount() <<
", resampleQuality" << setup.resampleQuality <<
", samplerate" << radioFormat.sampleRate() <<
", uLaw" << setup.ulaw;
#if !defined(Q_OS_MACX)
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
codecType codec = LPCM;
if (setup.codec == 0x01 || setup.codec == 0x20)
codec = PCMU;
else if (setup.codec == 0x40 || setup.codec == 0x40)
codec = OPUS;
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
}
catch (RtAudioError e) {
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Device exception:" << aParams.deviceId << ":" << QString::fromStdString(e.getMessage());
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;
#if (QT_VERSION < QT_VERSION_CHECK(6,0,0))
nativeFormat.setByteOrder(QAudioFormat::LittleEndian);
nativeFormat.setCodec("audio/pcm");
#endif
if (info.nativeFormats == 0)
{
qCritical(logAudio()) << " No natively supported data formats!";
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) {
nativeFormat.setChannelCount(info.inputChannels);
}
else {
nativeFormat.setChannelCount(info.outputChannels);
}
qInfo(logAudio()) << " Channels:" << nativeFormat.channelCount();
if (nativeFormat.channelCount() > 2) {
nativeFormat.setChannelCount(2);
}
else if (nativeFormat.channelCount() < 1)
{
qCritical(logAudio()) << (setup.isinput ? "Input" : "Output") << "No channels found, aborting setup.";
goto errorHandler;
}
if (nativeFormat.channelCount() == 1 && radioFormat.channelCount() == 2) {
nativeFormat.setChannelCount(2);
}
aParams.nChannels = nativeFormat.channelCount();
nativeFormat.setSampleRate(info.preferredSampleRate);
if (nativeFormat.sampleRate() < 44100) {
nativeFormat.setSampleRate(48000);
}
if (info.nativeFormats & RTAUDIO_FLOAT32) {
#if (QT_VERSION < QT_VERSION_CHECK(6,0,0))
nativeFormat.setSampleType(QAudioFormat::Float);
nativeFormat.setSampleSize(32);
#else
nativeFormat.setSampleFormat(QAudioFormat::Float);
#endif
sampleFormat = RTAUDIO_FLOAT32;
}
else if (info.nativeFormats & RTAUDIO_SINT32) {
#if (QT_VERSION < QT_VERSION_CHECK(6,0,0))
nativeFormat.setSampleType(QAudioFormat::SignedInt);
nativeFormat.setSampleSize(32);
#else
nativeFormat.setSampleFormat(QAudioFormat::Int32);
#endif
sampleFormat = RTAUDIO_SINT32;
}
else if (info.nativeFormats & RTAUDIO_SINT16) {
#if (QT_VERSION < QT_VERSION_CHECK(6,0,0))
nativeFormat.setSampleType(QAudioFormat::SignedInt);
nativeFormat.setSampleSize(16);
#else
nativeFormat.setSampleFormat(QAudioFormat::Int16);
#endif
sampleFormat = RTAUDIO_SINT16;
}
else {
qCritical(logAudio()) << "Cannot find supported sample format!";
goto errorHandler;
}
}
#if (QT_VERSION < QT_VERSION_CHECK(6,0,0))
qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Selected format: SampleSize" << nativeFormat.sampleSize() << "Channel Count" << nativeFormat.channelCount() <<
"Sample Rate" << nativeFormat.sampleRate() << "Codec" << nativeFormat.codec() << "Sample Type" << nativeFormat.sampleType();
#else
qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Selected format: SampleFormat" << nativeFormat.sampleFormat() << "Channel Count" << nativeFormat.channelCount() <<
"Sample Rate" << nativeFormat.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);
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 = nativeFormat.framesForDuration(setup.blockSize * 1000);
#ifdef RT_EXCEPTION
try {
#endif
if (setup.isinput) {
audio->openStream(NULL, &aParams, sampleFormat, nativeFormat.sampleRate(), &this->chunkSize, &staticWrite, this, &options);
emit setupConverter(nativeFormat, codecType::LPCM, radioFormat, codec, 7, setup.resampleQuality);
connect(converter, SIGNAL(converted(audioPacket)), this, SLOT(convertedInput(audioPacket)));
}
else {
audio->openStream(&aParams, NULL, sampleFormat, nativeFormat.sampleRate(), &this->chunkSize, &staticRead, this , &options);
emit setupConverter(radioFormat, codec, nativeFormat, codecType::LPCM, 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?
goto errorHandler;
}
#endif
#ifdef RT_EXCEPTION
}
else
{
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << QString::fromStdString(info.name) << "(" << aParams.deviceId << ") could not be probed, check audio configuration!";
goto errorHandler;
}
#endif
this->setVolume(setup.localAFgain);
return isInitialized;
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 * nativeFormat.bytesPerFrame();
//lastSentSeq = packet.seq;
if (arrayBuffer.length() >= nBytes) {
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 * nativeFormat.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)
{
audioMutex.lock();
arrayBuffer.append(packet.data);
audioMutex.unlock();
amplitude = packet.amplitudePeak;
currentLatency = packet.time.msecsTo(QTime::currentTime()) + (nativeFormat.durationForBytes(audio->getStreamLatency() * nativeFormat.bytesPerFrame()) / 1000);
emit haveLevels(getAmplitude(), packet.amplitudeRMS, setup.latency, currentLatency, isUnderrun, isOverrun);
}
void rtHandler::convertedInput(audioPacket packet)
{
if (packet.data.size() > 0) {
emit haveAudioData(packet);
amplitude = packet.amplitudePeak;
currentLatency = packet.time.msecsTo(QTime::currentTime()) + (nativeFormat.durationForBytes(audio->getStreamLatency() * nativeFormat.bytesPerFrame()) / 1000);
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()
{
return static_cast<quint16>(amplitude * 255.0);
}