8 #include <QStringBuilder>
16 using namespace swift::misc::audio;
17 using namespace swift::sound;
18 using namespace swift::sound::sample_provider;
20 namespace swift::core::afv::audio
22 CAudioOutputBuffer::CAudioOutputBuffer(
ISampleProvider *sampleProvider, QObject *parent)
23 : QIODevice(parent), m_sampleProvider(sampleProvider)
25 Q_ASSERT_X(sampleProvider, Q_FUNC_INFO,
"need sample provide");
26 const QString on = QStringLiteral(
"%1 for %2").arg(
classNameShort(
this), sampleProvider->objectName());
27 this->setObjectName(on);
31 qint64 CAudioOutputBuffer::bytesAvailable()
const
39 return 3840 + QIODevice::bytesAvailable();
45 const int sampleBytes = m_outputFormat.bytesPerSample();
46 const int channelCount = m_outputFormat.channelCount();
47 const qint64 count = maxlen / (sampleBytes * channelCount);
48 QVector<float> buffer;
51 for (
float sample : std::as_const(buffer))
53 const float absSample = qAbs(sample);
54 if (absSample > m_maxSampleOutput) { m_maxSampleOutput = absSample; }
57 m_sampleCount += buffer.size();
58 if (m_sampleCount >= SampleCountPerEvent)
61 outputVolumeStreamArgs.
PeakRaw = m_maxSampleOutput / 1.0;
62 outputVolumeStreamArgs.
PeakDb =
static_cast<float>(20 * std::log10(outputVolumeStreamArgs.
PeakRaw));
63 const double db = qBound(m_minDb, outputVolumeStreamArgs.
PeakDb, m_maxDb);
64 double ratio = (db - m_minDb) / (m_maxDb - m_minDb);
65 if (ratio < 0.30) { ratio = 0.0; }
66 if (ratio > 1.0) { ratio = 1.0; }
67 outputVolumeStreamArgs.
PeakVU = ratio;
70 m_maxSampleOutput = 0;
73 if (channelCount == 2) { buffer = convertFromMonoToStereo(buffer); }
75 memcpy(data, buffer.constData(),
static_cast<size_t>(maxlen));
90 if (m_started) {
return; }
94 if (m_audioOutputBuffer) { m_audioOutputBuffer->deleteLater(); }
98 m_device = outputDevice;
100 QAudioFormat outputFormat;
101 outputFormat.setSampleRate(48000);
102 outputFormat.setChannelCount(1);
103 outputFormat.setSampleFormat(QAudioFormat::Float);
104 static_assert(Q_BYTE_ORDER == Q_LITTLE_ENDIAN);
106 const QString format = toQString(outputFormat);
107 const QAudioDevice selectedDevice = getLowestLatencyDevice(outputDevice, outputFormat);
108 CLogMessage(
this).
info(u
"Starting: '%1' with: %2") << selectedDevice.description() << format;
110 m_audioOutput.reset(
new QAudioSink(selectedDevice, outputFormat));
111 m_audioOutputBuffer->open(QIODevice::ReadWrite | QIODevice::Unbuffered);
113 m_audioOutput->start(m_audioOutputBuffer);
120 if (!m_started) {
return; }
122 m_audioOutput->stop();
123 m_audioOutput.reset();
124 if (m_audioOutputBuffer)
126 m_audioOutputBuffer->deleteLater();
127 m_audioOutputBuffer =
nullptr;
qint64 writeData(const char *data, qint64 len)
void outputVolumeStream(const OutputVolumeStreamArgs &args)
Volume stream.
void setAudioFormat(const QAudioFormat &format)
Set the format.
qint64 readData(char *data, qint64 maxlen)
void outputVolumeStream(const OutputVolumeStreamArgs &args)
Streaming data.
COutput(QObject *parent=nullptr)
Ctor.
void start(const swift::misc::audio::CAudioDeviceInfo &outputDevice, swift::sound::sample_provider::ISampleProvider *sampleProvider)
Start output.
Class for emitting a log message.
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 isOutputDevice() const
Output device.
bool isValid() const
Valid audio device object?
Sample provider interface.
virtual int readSamples(QVector< float > &samples, qint64 count)=0
Read samples.
Free functions in swift::misc.
QString classNameShort(const QObject *object)
Class name as from QMetaObject::className without namespace.
#define SWIFT_VERIFY_X(COND, WHERE, WHAT)
A weaker kind of assert.