无法在 Qt 中将音频从麦克风录制到 QFile

Posted

技术标签:

【中文标题】无法在 Qt 中将音频从麦克风录制到 QFile【英文标题】:Unable to record audio from microphone to QFile in Qt 【发布时间】:2014-01-29 11:32:57 【问题描述】:

我正在尝试从桌面上的默认麦克风录制音频并将其存储在 QFile 中。 最后,我想使用 windows 媒体播放器播放录制的文件。问题是 QAudioInput 有一个方法 bytesReady() 在我的情况下它总是返回 0 。即使音频状态是 QAudio::ActiveState,bytesReady() 也会显示 0 字节,这意味着没有音频字节可以从麦克风中读取。系统麦克风工作正常。我的代码得到了完美的编译,我还在指定位置获得了一个大小为 80KB 的文件,但即使保存为 .wav,该文件也无法使用 Windows Media Player 播放,它会返回错误。 在我的情况下,输出窗口每次都将 bytesReady 显示为 0。我怀疑 QAudioInput 无法从麦克风读取音频数据。

您能帮我找出我的代码中的错误吗?

 `class mikeDemoClass : public QWidget
    
        Q_OBJECT

    public:
        mikeDemoClass(QWidget *parent = 0, Qt::WFlags flags = 0);
        ~mikeDemoClass();

    public slots:

        void startRecording();
        void browseFiles();
        void stopRecording();
        void handleAudioInputState(QAudio::State);
        void notified();

    private:
        Ui::mikeDemoWidget ui;
        QAudioInput *audioInput;
        QFile *recordFile;
        QTimer *testTimer;
        int audio_state;

    ;

// cpp file starts here

    #include <QIODevice>
    #include "mic_demo.h"

    mikeDemoClass::mikeDemoClass(QWidget *parent, Qt::WFlags flags)
        : QWidget(parent, flags)
    
        ui.setupUi(this);
        audioInput = NULL;
        recordFile = NULL;
        audio_state = -1;

        connect(ui.browseButton,SIGNAL(clicked()),this,SLOT(browseFiles()));
        connect(ui.recordingButton,SIGNAL(clicked()), this,SLOT(startRecording()));
    

    mikeDemoClass::~mikeDemoClass()
    
        if(recordFile)
        
            delete recordFile;
            recordFile = NULL;
        
    

    void mikeDemoClass::browseFiles()
    
        QString FileName = QFileDialog::getSaveFileName(this, tr("Browse Files"), "D:/", tr("Media Files (*.raw)"));
        if(!FileName.isEmpty())
        
            recordFile = new QFile(FileName);
            QTextDocument *textDoc = new QTextDocument(FileName);
            ui.textEdit->setDocument(textDoc);
        

    

    void mikeDemoClass::startRecording()
    
        bool status = recordFile->open(QIODevice::WriteOnly);
        if(!status)
        
            qDebug() <<"Error opening the file";
        


        QString default_deviceName = "";
        QAudioFormat preferred_format;

        QList<QAudioDeviceInfo> device_list = QAudioDeviceInfo::availableDevices(QAudio::AudioInput);
        int count = device_list.count();

        if(device_list.empty())
        
            qDebug() <<"The Audio Input Devices is empty";
        
        else
        

        foreach(QAudioDeviceInfo device_info, device_list)
        
            QString device_name = device_info.deviceName();
            qDebug() << "device_name:" << device_name.toLatin1();
        

        QAudioDeviceInfo info = QAudioDeviceInfo::defaultInputDevice();
        default_deviceName = info.deviceName();

        if(!default_deviceName.isEmpty())
        
            preferred_format = info.preferredFormat();
            QString codec = preferred_format.codec();
            int sampleRate = preferred_format.sampleRate();
            int sampleSize = preferred_format.sampleSize();
            int channelCount = preferred_format.channelCount();
            int sampleType = preferred_format.sampleType();
            int byteOrder =  preferred_format.byteOrder();
            qDebug() << "codec:" << codec.toLatin1() << "sampleRate :" << sampleRate << "sampleSize:" << sampleSize << "channel Count:" << channelCount << "sample type:" <<sampleType
                << "byteOrder:" << byteOrder;
        

    

    QAudioFormat format;
    format.setSampleRate(8000);
    format.setChannels(1);
    format.setSampleSize(8);
    format.setCodec("audio/PCM");
    format.setByteOrder(QAudioFormat::LittleEndian);
    format.setSampleType(QAudioFormat::UnSignedInt);

    QAudioDeviceInfo info = QAudioDeviceInfo::defaultInputDevice();
    if(!info.isFormatSupported(format))
    
        qDebug() <<"Default format not supported, try to use nearest format";
        format = info.nearestFormat(format);
    

    audioInput = new QAudioInput(info, format, this);

     connect(audioInput, SIGNAL(notify()), this, SLOT(notified()));
    connect(audioInput,SIGNAL(stateChanged(QAudio::State)),this, SLOT(handleAudioInputState(QAudio::State)));
    QTimer::singleShot(10000, this, SLOT(stopRecording()));

    //audioInput->setBufferSize(4096);
    qDebug() << "platform buffer size:" << audioInput->bufferSize();
    audioInput->start(recordFile);


void mikeDemoClass::stopRecording()

    testTimer->stop();
    audioInput->stop();
    recordFile->close();
    delete audioInput;


void  mikeDemoClass::handleAudioInputState(QAudio::State state)

    qDebug() << "Audio State:" << state;

    audio_state = state;

    if(state == QAudio::StoppedState)
    
        qDebug() << "Error State:" << audioInput->error();

        if(audioInput->error() != QAudio::NoError)
        
            qDebug() << "QAudioInput error:" << audioInput->error();
        
    


void  mikeDemoClass::notified()

    if(audio_state ==  QAudio::ActiveState)
    
        qDebug() << "Error State:" << audioInput->error();

        qDebug() << "platform buffer size after calling QAudioInput start():" << audioInput->bufferSize();

        qDebug() << "bytesReady = " << audioInput->bytesReady()
        << ", " << "elapsedUSecs = " <<audioInput->elapsedUSecs()
        << ", " << "processedUSecs = "<<audioInput->processedUSecs();
    

`

输出显示如下:

   device_name: "Microphone (High Definition Aud" 
device_name: "default" 
codec: "audio/pcm" sampleRate : 11025 sampleSize: 8 channel Count: 1 sample type: 1 byteOrder: 1 
Default format not supported, try to use nearest format 
platform buffer size: 0 
Audio State: 0 
Error State: 0 
platform buffer size after called QAudioInput start(): 1600 
bytesReady =  0 ,  elapsedUSecs =  1003000 ,  processedUSecs =  1000000 
Error State: 0 
platform buffer size after called QAudioInput start(): 1600 
bytesReady =  0 ,  elapsedUSecs =  1998000 ,  processedUSecs =  2000000 
Error State: 0 
platform buffer size after called QAudioInput start(): 1600 
bytesReady =  0 ,  elapsedUSecs =  3003000 ,  processedUSecs =  3000000 
Error State: 0 
platform buffer size after called QAudioInput start(): 1600 
bytesReady =  0 ,  elapsedUSecs =  4000000 ,  processedUSecs =  4000000 
Error State: 0 
platform buffer size after called QAudioInput start(): 1600 
bytesReady =  0 ,  elapsedUSecs =  5006000 ,  processedUSecs =  5000000 
Error State: 0 
platform buffer size after called QAudioInput start(): 1600 
bytesReady =  0 ,  elapsedUSecs =  6001000 ,  processedUSecs =  6000000 
Error State: 0 
platform buffer size after called QAudioInput start(): 1600 
bytesReady =  0 ,  elapsedUSecs =  7005000 ,  processedUSecs =  7000000 
Error State: 0 
platform buffer size after called QAudioInput start(): 1600 
bytesReady =  0 ,  elapsedUSecs =  8002000 ,  processedUSecs =  8000000 
Error State: 0 
platform buffer size after called QAudioInput start(): 1600 
bytesReady =  0 ,  elapsedUSecs =  8997000 ,  processedUSecs =  9000000 
Audio State: 2 
Error State: 0 

【问题讨论】:

上述代码的输出如图所示: 试试看例子qt-project.org/doc/qt-5.0/qtmultimedia/… 我想你忘记了音频标题。 @Samoth:我完全没有编译错误,我已经包含了所有内容。我没有显示头文件。 @jeevan_reddy 我并没有考虑 c++ 标头,而是这个:wave header 【参考方案1】:

将您的代码与Qt documentation 中的示例进行比较后,您正在使用通知信号来处理 QAudio::ActiveState,而示例代码将插槽连接到 stateChanged 信号:-

void AudioInputExample::handleStateChanged(QAudio::State newState)

    switch (newState) 
        case QAudio::StoppedState:
            if (audio->error() != QAudio::NoError) 
                // Error handling
             else 
                // Finished recording
            
            break;

        case QAudio::ActiveState:
            // Started recording - read from IO device
            break;

        default:
            // ... other cases as appropriate
            break;
    

如 notify() 所述:-

在 setNotifyInterval 设置的时间间隔处理 x ms 的音频数据时发出此信号

间隔可以为零吗?

但是,我建议按照documentation 中所示的示例代码并使用 stateChanged 信号,而不是通知。

【讨论】:

setNotifyInterval 默认为 1 秒。如果未设置通知间隔,则没有问题。我的代码完美编译,我也可以执行它。 @jeevan_reddy,我从未暗示您的代码无法编译,或者您无法执行它。听起来您只阅读了答案的倒数第二行。 我阅读了您的整个 cmets,我的重点是 QAudioInput 的 bytesReady() 方法,据我所知,该方法应该返回在 QMedia::ActiveState 中从麦克风读取的音频数据。除了打印 sme 调试语句外,在 QActiveState 中我不会做任何有意义的事情。真正的问题是为什么 bytesReady() 在状态为 Active 时返回 0。我认为这不应该发生。 您是否尝试过等待 stateChanged 信号,而不是通知? 我试过了,我在 stateChaged 信号发出时调用的插槽中打印 bytesRead() 。我检查了 QAudio::ActiveState 然后打印 bytesRead() 给出 0.【参考方案2】:

将 PHDEBUG 或 PHDBG 视为 qDebug() 并查看以下内容(我认为您的缺失点是作为缓冲区的 QIODevice。):

Reader.cpp:

#include "PhLtcReader.h"

PhLtcReader::PhLtcReader(QObject *parent) :
    QObject(parent),
    _input(NULL),
    _position(0),
    _buffer(NULL)

    _decoder = ltc_decoder_create(1920, 3840);
    PHDEBUG << "LTC Reader created";


bool PhLtcReader::init(QString input)

    QList<QAudioDeviceInfo> list = QAudioDeviceInfo::availableDevices(QAudio::AudioInput);

    if(list.isEmpty())
    
        PHDEBUG << "No audio input device";
        return false;
    

    QAudioDeviceInfo info = QAudioDeviceInfo::defaultInputDevice();

    foreach(QAudioDeviceInfo device, list)
    
        if(device.deviceName() == input)
            info = device;
    

    PHDEBUG << "LTC input device :" << info.deviceName();

    QAudioFormat format;
    format.setCodec("audio/pcm");
    format.setByteOrder(QAudioFormat::LittleEndian);
    format.setSampleRate(48000);
    format.setChannelCount(1);
    format.setSampleSize(8);
    format.setSampleType(QAudioFormat::SignedInt);

    if(!info.isFormatSupported(format))
    
        PHDEBUG << "Unsupported audio format";
        return false;
    

    _position = 0;
    _input = new QAudioInput(info, format);

    connect(_input, SIGNAL(notify()), this, SLOT(onNotify()));
    _buffer = _input->start();
    _input->setNotifyInterval(10);

    _pauseDetector.start();

    return true;


void PhLtcReader::close()

    if(_input)
    
        _input->stop();
        delete _buffer;
        _buffer = NULL;
        delete _input;
        _input = NULL;
    


QList<QString> PhLtcReader::inputList()

    QList<QString> names;
    QList<QAudioDeviceInfo> list = QAudioDeviceInfo::availableDevices(QAudio::AudioInput);

    foreach(QAudioDeviceInfo device, list)
        names.append(device.deviceName());

    return names;


void PhLtcReader::onNotify()


    QByteArray array = _buffer->readAll();
    char max = 0;
    for(int i = 0; i < array.count(); i++)
    
        if(array.at(i) > max)
            max = array.at(i);
    

    ltc_decoder_write(_decoder, (ltcsnd_sample_t*)array.data(), array.count(), _position);

    LTCFrameExt frame;
    unsigned int *hhmmssff = new unsigned int[4];
    while(ltc_decoder_read(_decoder, &frame))
    
        PhFrame oldFrame = _clock.frame();
        hhmmssff[0] = frame.ltc.hours_tens * 10 + frame.ltc.hours_units;
        hhmmssff[1] = frame.ltc.mins_tens * 10 + frame.ltc.mins_units;
        hhmmssff[2] = frame.ltc.secs_tens * 10 + frame.ltc.secs_units;
        hhmmssff[3] = frame.ltc.frame_tens * 10 + frame.ltc.frame_units;
    
    delete hhmmssff;

    _position += array.count();

阅读器.h

class PhLtcReader : public QObject

    Q_OBJECT

public:
    explicit PhLtcReader(QObject *parent = 0);

    bool init(QString _input="");
    void close();

    static QList<QString> inputList();


private slots:
    void onNotify();

private:
    QAudioInput *_input;
    qint64 _position;
    QIODevice * _buffer;
;

#endif // PHLTCREADER_H

【讨论】:

麦克风音频首先没有存储在原始文件中。 smebdy 请让我知道上面显示的代码中的错误是什么。任何帮助表示赞赏。 我无法从我的桌面录制麦克风音频并首先保存到原始文件中。我真的需要帮助,为什么 QAudioInput 没有首先执行它的工作。任何帮助表示赞赏。在我的情况下,即使音频状态为 Active,QAudioInput bytesReady() 也会显示 0。 QAudioInput 无法从桌面的麦克风读取数据。 @jeevan_reddy 编辑了我的代码,尝试修改它然后回来;)【参考方案3】:

您的问题的答案可以在输出中找到。

有一句话说

Default format not supported, try to use nearest format

这是问题所在。您的平台不支持您使用的格式。尝试其他格式或安装编解码器/驱动程序以支持该格式。

【讨论】:

以上是关于无法在 Qt 中将音频从麦克风录制到 QFile的主要内容,如果未能解决你的问题,请参考以下文章

在 Linux 上使用 bluealsa 从蓝牙耳机录制音频

播放流后音频队列无法录制音频

如何录制网络/浏览器音频输出(不是麦克风音频)

如何使用openAL将实时音频输入从麦克风录制到文件中? (里面有C++代码)

使用麦克风录制时,SDL 音频捕获未检测到任何音频

在 iOS 上录制音频的权限