10 #include <QAudioDevice>
18 using namespace swift::misc::audio;
19 using namespace swift::sound;
21 namespace swift::core::afv::audio
46 m_buffer.
append(data,
static_cast<int>(len));
47 const int byteCount = 1920 * m_format.channelCount();
48 while (m_buffer.
size() > byteCount)
51 m_buffer.
remove(0, byteCount);
57 :
QObject(parent), m_sampleRate(sampleRate), m_encoder(sampleRate, 1, OPUS_APPLICATION_VOIP)
65 if (qFuzzyCompare(m_gainRatio, gainRatio)) {
return false; }
66 m_gainRatio = gainRatio;
88 if (m_started) {
return; }
91 m_device = inputDevice;
93 QAudioFormat inputFormat;
94 inputFormat.setSampleRate(m_sampleRate);
95 inputFormat.setChannelCount(1);
96 inputFormat.setSampleFormat(QAudioFormat::Int16);
98 QAudioDevice selectedDevice = getLowestLatencyDevice(inputDevice, inputFormat);
100 if (selectedDevice.isNull())
102 CLogMessage(
this).
error(u
"No suitable audio input device found for '%1'. Required format: %2")
103 << inputDevice.
getName() << toQString(inputFormat);
107 if (!selectedDevice.isFormatSupported(inputFormat))
109 CLogMessage(
this).
error(u
"Selected device '%1' does not support required format: %2")
110 << selectedDevice.description() << toQString(inputFormat);
114 m_inputFormat = inputFormat;
115 m_audioInput.
reset(
new QAudioSource(selectedDevice, m_inputFormat));
118 m_audioInputBuffer->
start(m_inputFormat);
119 const QString format = toQString(m_inputFormat);
120 CLogMessage(
this).
info(u
"Starting: '%1' with: %2") << selectedDevice.description() << format;
123 this->initMicrophoneMacOS();
125 this->initMicrophone();
130 void CInput::initMicrophoneMacOS()
133 if (status == CMacOSMicrophoneAccess::Authorized) { this->initMicrophone(); }
134 else if (status == CMacOSMicrophoneAccess::NotDetermined)
137 m_micAccess.requestAccess();
140 else {
CLogMessage(
this).
error(u
"Microphone access not granted. Voice input will not work."); }
146 if (!m_started) {
return; }
148 if (m_audioInput) { m_audioInput->stop(); }
149 m_audioInput.
reset();
150 if (m_audioInputBuffer)
152 m_audioInputBuffer->
stop();
154 m_audioInputBuffer =
nullptr;
158 void CInput::audioInDataAvailable(
const QByteArray &frame)
160 static_assert(Q_BYTE_ORDER == Q_LITTLE_ENDIAN);
163 if (m_inputFormat.channelCount() == 2) { samples = convertFromStereoToMono(samples); }
165 const double volume = m_gainRatio;
166 for (qint16 &sample : samples)
168 int value = qRound(sample * volume);
169 if (value > std::numeric_limits<qint16>::max()) value = std::numeric_limits<qint16>::max();
170 if (value < std::numeric_limits<qint16>::min()) value = std::numeric_limits<qint16>::min();
171 sample =
static_cast<qint16
>(value);
175 const qint16 sampleInput =
176 sample != std::numeric_limits<qint16>::min() ? qAbs(sample) : std::numeric_limits<qint16>::max();
177 m_maxSampleInput = qMax(sampleInput, m_maxSampleInput);
181 const QByteArray encodedBuffer = m_encoder.
encode(samples, samples.size(), &length);
182 m_opusBytesEncoded += length;
184 m_sampleCount += samples.size();
185 if (m_sampleCount >= SampleCountPerEvent)
187 InputVolumeStreamArgs inputVolumeStreamArgs;
188 qint16 maxInt = std::numeric_limits<qint16>::max();
189 inputVolumeStreamArgs.PeakRaw =
static_cast<float>(m_maxSampleInput) / maxInt;
190 inputVolumeStreamArgs.PeakDB =
static_cast<float>(20 * std::log10(inputVolumeStreamArgs.PeakRaw));
191 double db = qBound(minDb, inputVolumeStreamArgs.PeakDB, maxDb);
192 double ratio = (db - minDb) / (maxDb - minDb);
193 if (ratio < 0.30) { ratio = 0.0; }
194 ratio = std::min(ratio, 1.0);
195 inputVolumeStreamArgs.PeakVU = ratio;
198 m_maxSampleInput = 0;
201 OpusDataAvailableArgs opusDataAvailableArgs = { m_audioSequenceCounter++, encodedBuffer };
205 void CInput::initMicrophone()
207 m_audioInput->start(m_audioInputBuffer);
Class for emitting a log message.
void permissionRequestAnswered(bool granted)
User has answered the permission request popup.
AuthorizationStatus
Authorization status.
Derived & error(const char16_t(&format)[N])
Set the severity to error, providing a format string.
Derived & info(const char16_t(&format)[N])
Set the severity to info, providing a format string.
Value object encapsulating information of a audio device.
bool isInputDevice() const
Input device.
bool isValid() const
Valid audio device object?
const QString & getName() const
Get the device name.
QByteArray encode(const QVector< qint16 > &pcmSamples, int samplesLength, int *encodedLength)
Encode.
void setBitRate(int bitRate)
Bit rate.
Free functions in swift::misc.
QByteArray & append(QByteArrayView data)
QByteArray left(qsizetype len) &&
QByteArray & remove(qsizetype pos, qsizetype len)
qsizetype size() const const
bool isOpen() const const
virtual bool open(QIODeviceBase::OpenMode mode)
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool disconnect(const QMetaObject::Connection &connection)
void setObjectName(QAnyStringView name)
#define SWIFT_VERIFY_X(COND, WHERE, WHAT)
A weaker kind of assert.