Files
pilotclient/src/blackcore/afv/audio/input.cpp
2019-10-13 16:47:41 +02:00

198 lines
7.4 KiB
C++

/* 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 "blackmisc/logmessage.h"
#include "blacksound/audioutilities.h"
#include <QtGlobal>
#include <QStringBuilder>
#include <QDebug>
#include <QAudioDeviceInfo>
#include <cmath>
using namespace BlackMisc;
using namespace BlackMisc::Audio;
using namespace BlackSound;
namespace BlackCore
{
namespace Afv
{
namespace Audio
{
CAudioInputBuffer::CAudioInputBuffer(QObject *parent) :
QIODevice(parent)
{
this->setObjectName("CAudioInputBuffer");
}
void CAudioInputBuffer::start(int channelCount)
{
m_channelCount = channelCount;
m_buffer.clear();
if (!this->isOpen())
{
open(QIODevice::WriteOnly | QIODevice::Unbuffered);
}
}
void CAudioInputBuffer::stop()
{
this->close();
}
qint64 CAudioInputBuffer::readData(char *data, qint64 maxlen)
{
Q_UNUSED(data)
Q_UNUSED(maxlen)
return 0;
}
qint64 CAudioInputBuffer::writeData(const char *data, qint64 len)
{
m_buffer.append(data, static_cast<int>(len));
const int byteCount = 1920 * m_channelCount;
while (m_buffer.size() > byteCount)
{
// qDebug() << QDateTime::currentMSecsSinceEpoch() << "CAudioInputBuffer::writeData " << m_buffer.size();
emit frameAvailable(m_buffer.left(byteCount));
m_buffer.remove(0, byteCount);
}
return len;
}
CInput::CInput(int sampleRate, QObject *parent) :
QObject(parent),
m_sampleRate(sampleRate),
m_encoder(sampleRate, 1, OPUS_APPLICATION_VOIP)
{
this->setObjectName("CInput");
m_encoder.setBitRate(16 * 1024);
}
bool CInput::setVolume(double volume)
{
if (qFuzzyCompare(m_volume, volume)) { return false; }
m_volume = volume;
return true;
}
void CInput::start(const CAudioDeviceInfo &inputDevice)
{
if (m_started) { return; }
m_device = inputDevice;
m_inputFormat.setSampleRate(m_sampleRate);
m_inputFormat.setChannelCount(1);
m_inputFormat.setSampleSize(16);
m_inputFormat.setSampleType(QAudioFormat::SignedInt);
m_inputFormat.setByteOrder(QAudioFormat::LittleEndian);
m_inputFormat.setCodec("audio/pcm");
QAudioDeviceInfo selectedDevice = getLowestLatencyDevice(inputDevice, m_inputFormat);
m_audioInput.reset(new QAudioInput(selectedDevice, m_inputFormat));
m_audioInputBuffer.start(m_inputFormat.channelCount());
#ifdef Q_OS_MAC
CMacOSMicrophoneAccess::AuthorizationStatus status = m_micAccess.getAuthorizationStatus();
if (status == CMacOSMicrophoneAccess::Authorized)
{
m_audioInput->start(&m_audioInputBuffer);
connect(&m_audioInputBuffer, &CAudioInputBuffer::frameAvailable, this, &CInput::audioInDataAvailable);
m_started = true;
return;
}
else if (status == CMacOSMicrophoneAccess::NotDetermined)
{
connect(&m_micAccess, &CMacOSMicrophoneAccess::permissionRequestAnswered, this, &CInput::delayedInitMicrophone);
m_micAccess.requestAccess();
CLogMessage(this).info(u"MacOS requested input device");
}
else
{
CLogMessage(this).error(u"Microphone access not granted. Voice input will not work.");
return;
}
#else
m_audioInput->start(&m_audioInputBuffer);
connect(&m_audioInputBuffer, &CAudioInputBuffer::frameAvailable, this, &CInput::audioInDataAvailable);
m_started = true;
#endif
}
void CInput::stop()
{
if (!m_started) { return; }
m_started = false;
if (m_audioInput) { m_audioInput->stop(); }
m_audioInput.reset();
m_audioInputBuffer.stop();
}
void CInput::audioInDataAvailable(const QByteArray &frame)
{
QVector<qint16> samples = convertBytesTo16BitPCM(frame);
if (m_inputFormat.channelCount() == 2)
{
samples = convertFromStereoToMono(samples);
}
for (qint16 &sample : samples)
{
int value = qRound(sample * m_volume);
if (value > std::numeric_limits<qint16>::max()) value = std::numeric_limits<qint16>::max();
if (value < std::numeric_limits<qint16>::min()) value = std::numeric_limits<qint16>::min();
sample = static_cast<qint16>(value);
qint16 sampleInput = qAbs(sample);
m_maxSampleInput = qMax(qAbs(sampleInput), m_maxSampleInput);
}
int length;
const QByteArray encodedBuffer = m_encoder.encode(samples, samples.size(), &length);
m_opusBytesEncoded += length;
m_sampleCount += samples.size();
if (m_sampleCount >= SampleCountPerEvent)
{
InputVolumeStreamArgs inputVolumeStreamArgs;
qint16 maxInt = std::numeric_limits<qint16>::max();
inputVolumeStreamArgs.PeakRaw = static_cast<float>(m_maxSampleInput) / maxInt;
inputVolumeStreamArgs.PeakDB = static_cast<float>(20 * std::log10(inputVolumeStreamArgs.PeakRaw));
double db = qBound(minDb, inputVolumeStreamArgs.PeakDB, maxDb);
double ratio = (db - minDb) / (maxDb - minDb);
if (ratio < 0.30) { ratio = 0.0; }
if (ratio > 1.0) { ratio = 1.0; }
inputVolumeStreamArgs.PeakVU = ratio;
emit inputVolumeStream(inputVolumeStreamArgs);
m_sampleCount = 0;
m_maxSampleInput = 0;
}
OpusDataAvailableArgs opusDataAvailableArgs = { m_audioSequenceCounter++, encodedBuffer };
emit opusDataAvailable(opusDataAvailableArgs);
}
#ifdef Q_OS_MAC
void CInput::delayedInitMicrophone()
{
m_audioInput->start(&m_audioInputBuffer);
connect(&m_audioInputBuffer, &CAudioInputBuffer::frameAvailable, this, &CInput::audioInDataAvailable);
m_started = true;
}
#endif
} // ns
} // ns
} // ns