Revision control
Copy as Markdown
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
/* Audio related snippets */
#include <QFile>
#include <QTimer>
#include <QDebug>
#include <qobject.h>
#include <qfile.h>
#include "qaudiodevice.h"
#include "qaudiosource.h"
#include "qaudiooutput.h"
#include "qaudiodecoder.h"
#include "qmediaplayer.h"
#include "qmediadevices.h"
class AudioInputExample : public QObject {
Q_OBJECT
public:
void setup();
public Q_SLOTS:
void stopRecording();
void handleStateChanged(QtAudio::State newState);
private:
//! [Audio input class members]
QFile destinationFile; // Class member
QAudioSource* audio; // Class member
//! [Audio input class members]
};
void AudioInputExample::setup()
//! [Audio input setup]
{
destinationFile.setFileName("/tmp/test.raw");
destinationFile.open( QIODevice::WriteOnly | QIODevice::Truncate );
QAudioFormat format;
// Set up the desired format, for example:
format.setSampleRate(44100);
format.setChannelCount(1);
format.setSampleFormat(QAudioFormat::Int16);
QAudioDevice info = QMediaDevices::defaultAudioInput();
if (!info.isFormatSupported(format)) {
qWarning() << "Default format not supported, trying to use the nearest.";
}
audio = new QAudioSource(format, this);
connect(audio, &QAudioSource::stateChanged, this, &AudioInputExample::handleStateChanged);
QTimer::singleShot(3000, this, &AudioInputExample::stopRecording);
audio->start(&destinationFile);
// Records audio for 3000ms
}
//! [Audio input setup]
//! [Audio input stop recording]
void AudioInputExample::stopRecording()
{
audio->stop();
destinationFile.close();
delete audio;
}
//! [Audio input stop recording]
//! [Audio input state changed]
void AudioInputExample::handleStateChanged(QtAudio::State newState)
{
switch (newState) {
case QtAudio::StoppedState:
if (audio->error() != QtAudio::NoError) {
// Error handling
} else {
// Finished recording
}
break;
case QtAudio::ActiveState:
// Started recording - read from IO device
break;
default:
// ... other cases as appropriate
break;
}
}
//! [Audio input state changed]
class AudioOutputExample : public QObject {
Q_OBJECT
public:
void setup();
public Q_SLOTS:
void handleStateChanged(QtAudio::State newState);
void stopAudioOutput();
private:
//! [Audio output class members]
QFile sourceFile; // class member.
QAudioSink* audio; // class member.
//! [Audio output class members]
};
void AudioOutputExample::setup()
//! [Audio output setup]
{
sourceFile.setFileName("/tmp/test.raw");
sourceFile.open(QIODevice::ReadOnly);
QAudioFormat format;
// Set up the format, eg.
format.setSampleRate(44100);
format.setChannelCount(1);
format.setSampleFormat(QAudioFormat::Int16);
QAudioDevice info(QMediaDevices::defaultAudioOutput());
if (!info.isFormatSupported(format)) {
qWarning() << "Raw audio format not supported by backend, cannot play audio.";
return;
}
audio = new QAudioSink(format, this);
connect(audio, QAudioSink::stateChanged, this, &AudioInputExample::handleStateChanged);
audio->start(&sourceFile);
}
//! [Audio output setup]
//! [Audio output stop]
void AudioOutputExample::stopAudioOutput()
{
audio->stop();
sourceFile.close();
delete audio;
}
//! [Audio output stop]
//! [Audio output state changed]
void AudioOutputExample::handleStateChanged(QtAudio::State newState)
{
switch (newState) {
case QtAudio::IdleState:
// Finished playing (no more data)
AudioOutputExample::stopAudioOutput();
break;
case QtAudio::StoppedState:
// Stopped for other reasons
if (audio->error() != QtAudio::NoError) {
// Error handling
}
break;
default:
// ... other cases as appropriate
break;
}
}
//! [Audio output state changed]
class AudioOutputWithCallbackExample : public QObject
{
Q_OBJECT
public:
void setupPlaySine();
private:
//! [Audio callback output class members]
QAudioSink* audio; // class member.
float phase; // class member.
//! [Audio callback output class members]
};
void AudioOutputWithCallbackExample::setupPlaySine()
//! [Audio callback output setup sine]
{
QAudioFormat format;
// Set up the format, eg.
format.setSampleRate(44100);
format.setChannelCount(2);
format.setSampleFormat(QAudioFormat::Float);
QAudioDevice info(QMediaDevices::defaultAudioOutput());
if (!info.isFormatSupported(format)) {
qWarning() << "Raw audio format not supported by backend, cannot play audio.";
return;
}
audio = new QAudioSink(format, this);
float phaseIncrement = 2 * M_PI * 220.0 / format.sampleRate(); // 220 Hz sine wave
audio->start([&phase, phaseIncrement] (QSpan<float> interleavedAudioBuffer) {
// The audio callback should not call any functions that may potentially be blocking
// Fill the audio buffer with a sine wave
const int sampleCount = interleavedAudioBuffer.size() / 2; // Stereo, so divide by 2
for (int i = 0; i < sampleCount; ++i) {
float sample = std::sin(phase);
interleavedAudioBuffer[i * 2] = sample; // Left channel
interleavedAudioBuffer[i * 2 + 1] = sample; // Right channel
phase += phaseIncrement; // Increment phase for next sample
}
});
if (!audio->error() == QtAudio::Error::NoError) {
// in addition to the other start() signatures, starting the audio callback will fail if
// * the backend does not implement callback-based IO (the API is available on all major
// platforms)
// * the signature of the audio callback does not match format.sampleFormat()
qWarning() << "Error starting audio output:" << audio->errorString();
}
}
//! [Audio callback output setup sine]
class AudioInputWithCallbackExample : public QObject
{
Q_OBJECT
public:
void setupPeakMeter();
private:
//! [Audio callback capture class members]
QAudioSource* audio; // class member.
std::atomic<float> peakLevel; // class member.
//! [Audio callback capture class members]
};
void AudioInputWithCallbackExample::setupPeakMeter()
//! [Audio callback capture setup peak meter]
{
QAudioFormat format;
// Set up the format, eg.
format.setSampleRate(44100);
format.setChannelCount(2);
format.setSampleFormat(QAudioFormat::Float);
QAudioDevice info(QMediaDevices::defaultAudioOutput());
if (!info.isFormatSupported(format)) {
qWarning() << "Raw audio format not supported by backend, cannot capture audio.";
return;
}
audio = new QAudioSource(format, this);
audio->start([&peakLevel] (QSpan<float> interleavedAudioBuffer) {
float level = peakLevel.load();
for (float sample : interleavedAudioBuffer) {
// Calculate the peak level from the audio samples
level = std::max(level, std::abs(sample));
}
peakLevel.store(level);
// Note: care needs to be taken if the application thread needs to be notified, as the
// audio callback should not use any potentially blocking system calls.
// Good options are autoreset events (windows), eventfd (linux) or kqueue/EVFILT_USER on macos.
});
if (!audio->error() == QtAudio::Error::NoError) {
// in addition to the other start() signatures, starting the audio callback will fail if
// * the backend does not implement callback-based IO (the API is available on all major
// platforms)
// * the signature of the audio callback does not match format.sampleFormat()
qWarning() << "Error starting audio output:" << audio->errorString();
}
}
//! [Audio callback capture setup peak meter]
void AudioDeviceInfo()
{
//! [Setting audio format]
QAudioFormat format;
format.setSampleRate(44100);
// ... other format parameters
format.setSampleFormat(QAudioFormat::Int16);
//! [Setting audio format]
//! [Dumping audio formats]
const auto devices = QMediaDevices::audioOutputs();
for (const QAudioDevice &device : devices)
qDebug() << "Device: " << device.description();
//! [Dumping audio formats]
}
class AudioDecodingExample : public QObject {
Q_OBJECT
public:
void decode();
public Q_SLOTS:
void handleStateChanged(QtAudio::State newState);
void readBuffer();
};
void AudioDecodingExample::decode()
{
//! [Local audio decoding]
QAudioFormat desiredFormat;
desiredFormat.setChannelCount(2);
desiredFormat.setSampleFormat(QAudioFormat::Int16);
desiredFormat.setSampleRate(48000);
QAudioDecoder *decoder = new QAudioDecoder(this);
decoder->setAudioFormat(desiredFormat);
decoder->setSource("level1.mp3");
connect(decoder, &QAudioDecoder::bufferReady, this, &AudioDecodingExample::readBuffer);
decoder->start();
// Now wait for bufferReady() signal and call decoder->read()
//! [Local audio decoding]
}
QMediaPlayer player;
//! [Volume conversion]
void applyVolume(int volumeSliderValue)
{
// volumeSliderValue is in the range [0..100]
qreal linearVolume = QtAudio::convertVolume(volumeSliderValue / qreal(100.0),
QtAudio::LogarithmicVolumeScale,
QtAudio::LinearVolumeScale);
player.setVolume(qRound(linearVolume * 100));
}
//! [Volume conversion]