如何在Ubuntu QML应用中进行语言录音

在QML API中,目前并没有一个相应的API来进行录音。我们必须使用Qt C++ API QAudioRecorder来进行录音的工作。在这篇文章中,我们来介绍如何使用这个API来进行录音。


首先,我们来创建一个“QML App with C++ plugin (qmake)”模版的应用。注意qmake的项目必须是在15.04及以上的target上才可以运行。


为了录音,我创建了一个叫做“AudioRecorder”的类:


audiorecorder.h


#ifndef AUDIORECORDER_H
#define AUDIORECORDER_H

#include <QAudioRecorder>
#include <QUrl>

class AudioRecorder : public QObject
{
    Q_OBJECT
    Q_PROPERTY ( bool recording READ recording NOTIFY recordingChanged )
    Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)

public:
    explicit AudioRecorder(QObject *parent = 0);
    const bool recording() const;
    QString name() const;

    Q_INVOKABLE QStringList supportedAudioCodecs();
    Q_INVOKABLE QStringList supportedContainers();
    Q_INVOKABLE QUrl path() {
        return m_path;
    }

signals:
    void recordingChanged(bool);
    void nameChanged(QUrl);

public slots:
    void setName(QString name);
    void setRecording(bool recording );
    void record();
    void stop();

private:
    QString getFilePath(const QString filename) const;

private:
    QAudioRecorder * m_audioRecorder;
    bool m_recording;
    QString m_name;
    QUrl m_path;
};

#endif // AUDIORECORDER_H


audiorecorder.cpp


#include <QUrl>
#include <QStandardPaths>
#include <QDir>

#include "audiorecorder.h"

AudioRecorder::AudioRecorder(QObject *parent) : QObject(parent)
{
    m_audioRecorder = new QAudioRecorder( this );
    QAudioEncoderSettings audioSettings;
    audioSettings.setCodec("audio/PCM");
    audioSettings.setQuality(QMultimedia::HighQuality);
    m_audioRecorder->setEncodingSettings(audioSettings);
    // https://forum.qt.io/topic/42541/recording-audio-using-qtaudiorecorder/2
    m_audioRecorder->setContainerFormat("wav");
    m_recording = false;
}

const bool AudioRecorder::recording() const
{
    return m_recording;
}

void AudioRecorder::setRecording(bool recording ) {
    if (m_recording == recording)
        return;

    m_recording = recording;
    emit recordingChanged(m_recording);
}


void AudioRecorder::record()
{
    qDebug() << "Entering record!";

    if ( m_audioRecorder->state() == QMediaRecorder::StoppedState ) {
        qDebug() << "recording....! ";

        m_audioRecorder->record ( );

        m_recording = true;
        qDebug() << "m_recording: " << m_recording;
        emit recordingChanged(m_recording);
    }
}

void AudioRecorder::stop()
{
    qDebug() << "Entering stop!";

    if ( m_audioRecorder->state() == QMediaRecorder::RecordingState ) {
        qDebug() << "Stopping....";
        m_audioRecorder->stop();
        m_recording = false;
        emit recordingChanged(m_recording);
    }
}

QString AudioRecorder::name() const
{
    return m_name;
}

void AudioRecorder::setName(QString name)
{
    if (m_name == name)
        return;

    m_name = name;
    emit nameChanged(name);

    // at the same time update the path
    m_path = QUrl(getFilePath(name));

    // set the path
    m_audioRecorder->setOutputLocation(m_path);
}

QStringList AudioRecorder::supportedAudioCodecs() {
    return m_audioRecorder->supportedAudioCodecs();
}

QStringList AudioRecorder::supportedContainers() {
    return m_audioRecorder->supportedContainers();
}


QString AudioRecorder::getFilePath(const QString filename) const
{
    QString writablePath = QStandardPaths::
            writableLocation(QStandardPaths::DataLocation);
    qDebug() << "writablePath: " << writablePath;

    QString absolutePath = QDir(writablePath).absolutePath();
    qDebug() << "absoluePath: " << absolutePath;

    // We need to make sure we have the path for storage
    QDir dir(absolutePath);
    if ( dir.mkdir(absolutePath) ) {
        qDebug() << "Successfully created the path!";
    }

    QString path = absolutePath + "/" + filename;

    qDebug() << "path: " << path;

    return path;
}

在这里,我们使用了QStandardPath来获得在Ubuntu手机中可以访问的文件目录。这个QAudioRecorder的API的使用也是非常直接的。

我们的Main.qml的界面也非常简单:


Main.qml


import QtQuick 2.0
import Ubuntu.Components 1.1
import QtMultimedia 5.0
import AudioRecorder 1.0

/*!
    \brief MainView with a Label and Button elements.
*/

MainView {
    // objectName for functional testing purposes (autopilot-qt5)
    objectName: "mainView"

    // Note! applicationName needs to match the "name" field of the click manifest
    applicationName: "audiorecorder.liu-xiao-guo"

    /*
     This property enables the application to change orientation
     when the device is rotated. The default is false.
    */
    //automaticOrientation: true

    // Removes the old toolbar and enables new features of the new header.
    useDeprecatedToolbar: false

    width: units.gu(60)
    height: units.gu(85)

    Page {
        title: i18n.tr("AudioRecorder")

        AudioRecorder {
            id: audio
            name: "sample.wav"

            onRecordingChanged: {
                console.log("recording: " + recording);
            }
        }

        MediaPlayer {
            id: player
            autoPlay: true
            volume: 1.0
        }

        Column {
            anchors.fill: parent
            spacing: units.gu(1)

            Label {
                text: "Supported Audio codecs:"
            }
            ListView {
                id: audiocodecs
                width: parent.width
                height: audiocodecs.contentHeight
                model:audio.supportedAudioCodecs()
                delegate: Text {
                    text: modelData
                }
            }

            Rectangle {
                width: parent.width
                height: units.gu(0.1)
            }

            Label {
                text: "Supported Containers:"
            }
            ListView {
                id: audiocontainer
                width: parent.width
                height: audiocontainer.contentHeight
                model:audio.supportedContainers()
                delegate: Text {
                    text: modelData
                }
            }
        }

        Row {
            anchors.bottom: parent.bottom
            anchors.bottomMargin: units.gu(2)
            anchors.horizontalCenter: parent.horizontalCenter
            anchors.margins: units.gu(2)

            spacing: units.gu(2)

            Button {
                id: record
                text: "Record Audio"

                enabled: !audio.recording

                onClicked: {
                    audio.record();
                }
            }

            Button {
                id: stop
                text: "Stop"

                onClicked: {
                    audio.stop();
                    player.stop();
                }
            }

            Button {
                id: play
                text: "Play Audio"

                onClicked: {
                    console.log("path: " + audio.path() );
                    player.source = audio.path();
                    player.play();
                }
            }

        }
    }
}


在QML中,我们直接地使用:

        AudioRecorder {
            id: audio
            name: "sample.wav"

            onRecordingChanged: {
                console.log("recording: " + recording);
            }
        }


我们可以通过“Record Audio”按钮来进行录音的工作。应用的界面如下:

  

经过在手机上的实验,录音的效果非常好,非常清晰。我们可以使用“Play Audio”按钮来播放。