来自数组的Qaudioput

QAudioInput from array

本文关键字:Qaudioput 数组      更新时间:2023-10-16

我有一个带有麦克风的设备,该设备通过以太网连接到我的计算机,QT无法将其视为音频设备,因此,我从中获取数据包并将其放在QbyTearray上。我需要从流中播放这些数据包。在互联网中的某个地方,我找到了几乎相同问题的解决方案,但是使用了内部麦克风。

#include <QApplication>
#include <iostream>
#include <cassert>
#include <QCoreApplication>
#include <QAudioInput>
#include <QAudioOutput>
#include <QBuffer>
int main(int argc, char *argv[]) {
    QCoreApplication app(argc, argv);
    QBuffer rdBuff;
    QBuffer wrBuff;
    wrBuff.open(QBuffer::WriteOnly);
    rdBuff.open(QBuffer::ReadOnly);
    QObject::connect(&wrBuff, &QIODevice::bytesWritten, [&wrBuff, &rdBuff](qint64)
    {
        rdBuff.buffer().remove(0, rdBuff.pos());
        // set pointer to the beginning of the unread data
        const auto res = rdBuff.seek(0);
        assert(res);
        // write new data
        rdBuff.buffer().append(wrBuff.buffer());
        // remove all data that was already written
        wrBuff.buffer().clear();
        wrBuff.seek(0);
    });
    const auto decideAudioFormat = [](const QAudioDeviceInfo& devInfo)
    {
        QAudioFormat format;
        format.setSampleRate(8000);
        format.setChannelCount(1);
        format.setSampleSize(16);
        format.setCodec("audio/pcm");
        format.setByteOrder(QAudioFormat::LittleEndian);
        format.setSampleType(QAudioFormat::SignedInt);
        if (devInfo.isFormatSupported(format))
        {
            return format;
        }
        else
        {
            std::cerr << "Raw audio format not supported by backend, cannot play audio.n";
            throw 0;
        }
    };
    QAudioInput audioInput(decideAudioFormat(QAudioDeviceInfo::defaultInputDevice()));
    QAudioOutput audioOutput(decideAudioFormat(QAudioDeviceInfo::defaultOutputDevice()));
    audioInput.start(&wrBuff);
    audioOutput.start(&rdBuff);
    return app.exec();
}

它运行良好,但是我需要将QbyTearray设置为QaudioInput的来源。有可能的解决方案吗?

不确定我是否直接回答您的问题。但是可能的解决方案是在新数据到来时手动馈送输出音频设备(按下模式(。

您也可以使用自定义(Qfile继承(类记录声音,当声音到达时,馈送文件和输出音频设备。

这是一个示例:

AudioOutput.h:

#ifndef AUDIOOUTPUT_H
#define AUDIOOUTPUT_H
#include <QtCore>
#include <QtMultimedia>
#define MAX_BUFFERED_TIME 10*1000
static inline int timeToSize(int ms, const QAudioFormat &format)
{
    return ((format.channelCount() * (format.sampleSize() / 8) * format.sampleRate()) * ms / 1000);
}
class AudioOutput : public QObject
{
    Q_OBJECT
public:
    explicit AudioOutput(QObject *parent = nullptr);
public slots:
    bool start(const QAudioDeviceInfo &devinfo,
               const QAudioFormat &format,
               int time_to_buffer);
    void write(const QByteArray &data);
private slots:
    void verifyBuffer();
    void preplay();
    void play();
private:
    bool m_initialized;
    QAudioOutput *m_audio_output;
    QIODevice *m_device;
    QByteArray m_buffer;
    bool m_buffer_requested;
    bool m_play_called;
    int m_size_to_buffer;
    int m_time_to_buffer;
    int m_max_size_to_buffer;
    QAudioFormat m_format;
};
#endif // AUDIOOUTPUT_H

audiorecorder.h:

#ifndef AUDIORECORDER_H
#define AUDIORECORDER_H
#include <QtCore>
#include <QtMultimedia>
class AudioRecorder : public QFile
{
    Q_OBJECT
public:
    explicit AudioRecorder(const QString &name, const QAudioFormat &format, QObject *parent = nullptr);
    ~AudioRecorder();
    using QFile::open;
public slots:
    bool open();
    qint64 write(const QByteArray &data);
    void close();
private:
    void writeHeader();
    bool hasSupportedFormat();
    QAudioFormat format;
};
#endif // AUDIORECORDER_H

AudioOutput.cpp:

#include "audiooutput.h"
AudioOutput::AudioOutput(QObject *parent) : QObject(parent)
{
    m_initialized = false;
    m_audio_output = nullptr;
    m_device = nullptr;
    m_buffer_requested = true;
    m_play_called = false;
    m_size_to_buffer = 0;
    m_time_to_buffer = 0;
    m_max_size_to_buffer = 0;
}
bool AudioOutput::start(const QAudioDeviceInfo &devinfo,
                        const QAudioFormat &format,
                        int time_to_buffer)
{
    if (!devinfo.isFormatSupported(format))
    {
        qDebug() << "Format not supported by output device";
        return m_initialized;
    }
    m_format = format;
    int internal_buffer_size;
    //Adjust internal buffer size
    if (format.sampleRate() >= 44100)
        internal_buffer_size = (1024 * 10) * format.channelCount();
    else if (format.sampleRate() >= 24000)
        internal_buffer_size = (1024 * 6) * format.channelCount();
    else
        internal_buffer_size = (1024 * 4) * format.channelCount();
    //Initialize the audio output device
    m_audio_output = new QAudioOutput(devinfo, format, this);
    //Increase the buffer size to enable higher sample rates
    m_audio_output->setBufferSize(internal_buffer_size);
    m_time_to_buffer = time_to_buffer;
    //Compute the size in bytes to be buffered based on the current format
    m_size_to_buffer = timeToSize(m_time_to_buffer, m_format);
    //Define a highest size that the buffer are allowed to have in the given time
    //This value is used to discard too old buffered data
    m_max_size_to_buffer = m_size_to_buffer + timeToSize(MAX_BUFFERED_TIME, m_format);
    m_device = m_audio_output->start();
    if (!m_device)
    {
        qDebug() << "Failed to open output audio device";
        return m_initialized;
    }
    //Timer that helps to keep playing data while it's available on the internal buffer
    QTimer *timer_play = new QTimer(this);
    timer_play->setTimerType(Qt::PreciseTimer);
    connect(timer_play, &QTimer::timeout, this, &AudioOutput::preplay);
    timer_play->start(10);
    //Timer that checks for too old data in the buffer
    QTimer *timer_verifier = new QTimer(this);
    connect(timer_verifier, &QTimer::timeout, this, &AudioOutput::verifyBuffer);
    timer_verifier->start(qMax(m_time_to_buffer, 10));
    m_initialized = true;
    return m_initialized;
}
void AudioOutput::verifyBuffer()
{
    if (m_buffer.size() >= m_max_size_to_buffer)
        m_buffer.clear();
}
void AudioOutput::write(const QByteArray &data)
{
    m_buffer.append(data);
    preplay();
}
void AudioOutput::preplay()
{
    if (!m_initialized)
        return;
    //Verify if exists a pending call to play function
    //If not, call the play function async
    if (!m_play_called)
    {
        m_play_called = true;
        QMetaObject::invokeMethod(this, "play", Qt::QueuedConnection);
    }
}
void AudioOutput::play()
{
    //Set that last async call was triggered
    m_play_called = false;
    if (m_buffer.isEmpty())
    {
        //If data is empty set that nothing should be played
        //until the buffer has at least the minimum buffered size already set
        m_buffer_requested = true;
        return;
    }
    else if (m_buffer.size() < m_size_to_buffer)
    {
        //If buffer doesn't contains enough data,
        //check if exists a already flag telling that the buffer comes
        //from a empty state and should not play anything until have the minimum data size
        if (m_buffer_requested)
            return;
    }
    else
    {
        //Buffer is ready and data can be played
        m_buffer_requested = false;
    }
    int readlen = m_audio_output->periodSize();
    int chunks = m_audio_output->bytesFree() / readlen;
    //Play data while it's available in the output device
    while (chunks)
    {
        //Get chunk from the buffer
        QByteArray samples = m_buffer.mid(0, readlen);
        int len = samples.size();
        m_buffer.remove(0, len);
        //Write data to the output device
        if (len)
            m_device->write(samples);
        //If chunk is smaller than the output chunk size, exit loop
        if (len != readlen)
            break;
        //Decrease the available number of chunks
        chunks--;
    }
}

audiorecorder.cpp:

#include "audiorecorder.h"
AudioRecorder::AudioRecorder(const QString &name, const QAudioFormat &format, QObject *parent) : QFile(name, parent), format(format)
{
}
AudioRecorder::~AudioRecorder()
{
    if (!isOpen())
        return;
    close();
}
bool AudioRecorder::hasSupportedFormat()
{
    return (format.sampleSize() == 8
            && format.sampleType() == QAudioFormat::UnSignedInt)
            || (format.sampleSize() > 8
                && format.sampleType() == QAudioFormat::SignedInt
                && format.byteOrder() == QAudioFormat::LittleEndian);
}
bool AudioRecorder::open()
{
    if (!hasSupportedFormat())
    {
        setErrorString("Wav PCM supports only 8-bit unsigned samples "
                       "or 16-bit (or more) signed samples (in little endian)");
        return false;
    }
    else
    {
        if (!QFile::open(ReadWrite | Truncate))
            return false;
        writeHeader();
        return true;
    }
}
qint64 AudioRecorder::write(const QByteArray &data)
{
    return QFile::write(data);
}
void AudioRecorder::writeHeader()
{
    QDataStream out(this);
    out.setByteOrder(QDataStream::LittleEndian);
    // RIFF chunk
    out.writeRawData("RIFF", 4);
    out << quint32(0); // Placeholder for the RIFF chunk size (filled by close())
    out.writeRawData("WAVE", 4);
    // Format description chunk
    out.writeRawData("fmt ", 4);
    out << quint32(16); // "fmt " chunk size (always 16 for PCM)
    out << quint16(1); // data format (1 => PCM)
    out << quint16(format.channelCount());
    out << quint32(format.sampleRate());
    out << quint32(format.sampleRate() * format.channelCount()
                   * format.sampleSize() / 8 ); // bytes per second
    out << quint16(format.channelCount() * format.sampleSize() / 8); // Block align
    out << quint16(format.sampleSize()); // Significant Bits Per Sample
    // Data chunk
    out.writeRawData("data", 4);
    out << quint32(0); // Placeholder for the data chunk size (filled by close())
    Q_ASSERT(pos() == 44); // Must be 44 for WAV PCM
}
void AudioRecorder::close()
{
    // Fill the header size placeholders
    quint32 fileSize = size();
    QDataStream out(this);
    // Set the same ByteOrder like in writeHeader()
    out.setByteOrder(QDataStream::LittleEndian);
    // RIFF chunk size
    seek(4);
    out << quint32(fileSize - 8);
    // data chunk size
    seek(40);
    out << quint32(fileSize - 44);
    QFile::close();
}

main.cpp:

#include <QtCore>
#include "audiooutput.h"
#include "audiorecorder.h"
#include <signal.h>
QByteArray tone_generator()
{
    //Tone generator from http://www.cplusplus.com/forum/general/129827/
    const unsigned int samplerate = 8000;
    const unsigned short channels = 1;
    const double pi = M_PI;
    const qint16 amplitude = std::numeric_limits<qint16>::max() * 0.5;
    const unsigned short n_frequencies = 8;
    const unsigned short n_seconds_each = 1;
    float frequencies[n_frequencies] = {55.0, 110.0, 220.0, 440.0, 880.0, 1760.0, 3520.0, 7040.0};
    const int n_samples = channels * samplerate * n_frequencies * n_seconds_each;
    QVector<qint16> data;
    data.resize(n_samples);
    int index = n_samples / n_frequencies;
    for (unsigned short i = 0; i < n_frequencies; ++i)
    {
        float freq = frequencies[i];
        double d = (samplerate / freq);
        int c = 0;
        for (int j = index * i; j < index * (i + 1); j += 2)
        {
            double deg = 360.0 / d;
            data[j] = data[j + (channels - 1)] = qSin((c++ * deg) * pi / 180.0) * amplitude;
        }
    }
    return QByteArray((char*)data.data(), data.size() * sizeof(qint16));
}
void signalHandler(int signum)
{
    qDebug().nospace() << "Interrupt signal (" << signum << ") received.";
    qApp->exit();
}
int main(int argc, char *argv[])
{
    //Handle console close to ensure destructors being called
#ifdef Q_OS_WIN
    signal(SIGBREAK, signalHandler);
#else
    signal(SIGHUP, signalHandler);
#endif
    signal(SIGINT, signalHandler);
    QCoreApplication a(argc, argv);
    QAudioFormat format;
    format.setSampleRate(8000);
    format.setChannelCount(1);
    format.setSampleSize(16);
    format.setCodec("audio/pcm");
    format.setByteOrder(QAudioFormat::LittleEndian);
    format.setSampleType(QAudioFormat::SignedInt);
    AudioOutput output;
    AudioRecorder file("tone.wav", format);
    if (!output.start(QAudioDeviceInfo::defaultOutputDevice(), format, 10 * 1000)) //10 seconds of buffer
        return a.exec();
    if (!file.open())
    {
        qDebug() << qPrintable(file.errorString());
        return a.exec();
    }
    qDebug() << "Started!";
    QByteArray audio_data = tone_generator();
    QTimer timer;
    QObject::connect(&timer, &QTimer::timeout, [&]{
        qDebug() << "Writting" << audio_data.size() << "bytes";
        output.write(audio_data);
        file.write(audio_data);
    });
    qDebug() << "Writting" << audio_data.size() << "bytes";
    output.write(audio_data);
    file.write(audio_data);
    timer.start(8000); //8 seconds because we generated 8 seconds of sound
    return a.exec();
}

很好。我试图将代码(现在只是播放(应用于我的情况,但是如果没有发射" morepackets(("或根本不播放的问题,它会播放一次数据,如果" Morepackets(("发生。

micserver.h

#ifndef MICSERVER_H
#define MICSERVER_H
//-----------------------------//
#include <QObject>
#include <QDebug>
#include <QTcpServer>
#include <QTcpSocket>
#include <QAudioFormat>
#include <QAudioOutput>
#include <QFile>
#include <QBuffer>
#include <QByteArray>
#include <QDataStream>
#include <iostream>
#include "audiooutput.h"
//-----------------------------//
class MicServer : public QObject {
    Q_OBJECT
public:
    explicit MicServer(QObject *parent = nullptr);
    QTcpSocket* Socket;
private:
    QTcpServer* Server;
    QAudioFormat format;
    AudioOutput output;
    QByteArray GetPackets(QTcpSocket* ClientP, int PacketsNumP);
signals:
    void MorePackets();
public slots:
    void NewConnection();
    void Play();
};
//-----------------------------//
#endif

micserver.cpp

#include "micserver.h"
//-----------------------------//
QByteArray MicServer :: GetPackets(QTcpSocket* ClientP, int PacketsNumP) {
    QBuffer BufferL;
    BufferL.open(QBuffer :: WriteOnly);
    QDataStream InputL(&BufferL);
    InputL.setVersion(QDataStream::Qt_5_10);
    QByteArray TempArray;
    for (int i = 0; i < PacketsNumP; i++) {
        ClientP -> waitForReadyRead(3000);
        InputL << ClientP -> readAll();
    }
    for (int i = 0; i < PacketsNumP; i++) {
        TempArray.push_back(BufferL.buffer().mid(76 + i * 1172, 256));
    }
    BufferL.close();
    return TempArray;
}
//-----------------------------//
MicServer :: MicServer(QObject *parent) : QObject(parent) {
    Server = new QTcpServer;
    if (!Server -> listen(QHostAddress :: Any, 49112)) {
        qDebug() << "Failed to launch server!";
    } else {
        qDebug() << "Server launched!";
    }
    connect(Server, SIGNAL(newConnection()), this, SLOT(NewConnection()));
    connect(Server, SIGNAL(newConnection()), this, SLOT(Play()));
    connect(this, SIGNAL(MorePackets()), this, SLOT(Play()));
    format.setSampleRate(16000);
    format.setChannelCount(1);
    format.setSampleSize(16);
    format.setCodec("audio/pcm");
    format.setByteOrder(QAudioFormat::LittleEndian);
    format.setSampleType(QAudioFormat::SignedInt);
    output.start(QAudioDeviceInfo::defaultOutputDevice(), format);
}
void MicServer :: NewConnection() {
    Socket = Server -> nextPendingConnection();
    qDebug() << "New connection!";
    qDebug() << Socket -> localAddress().toString();
}
void MicServer :: Play() {
    QByteArray audio_data = GetPackets(Socket, 250);
    QTimer timer;
    QObject::connect(&timer, &QTimer::timeout, [&]{
        qDebug() << "Writting" << audio_data.size() << "bytes";
        output.write(audio_data);
    });
    qDebug() << "Writting" << audio_data.size() << "bytes";
    output.write(audio_data);
    timer.start(2000);
    emit(MorePackets());
}