Recording Vorbis files works & connected to UI

This commit is contained in:
Sebastian Wolf 2021-01-02 00:15:25 +01:00
parent fd0d188030
commit d38f56b9fe
No known key found for this signature in database
GPG key ID: CEA9522B5F38A90A
17 changed files with 555 additions and 12 deletions

View file

@ -16,7 +16,7 @@ CONFIG += sailfishapp sailfishapp_i18n
PKGCONFIG += nemonotifications-qt5 zlib
QT += core dbus sql
QT += core dbus sql multimedia
DEFINES += QT_STATICPLUGIN

View file

@ -1,5 +1,5 @@
/*
Copyright (C) 2020 Sebastian J. Wolf and other contributors
Copyright (C) 2020-21 Sebastian J. Wolf and other contributors
This file is part of Fernschreiber.
@ -18,13 +18,74 @@
*/
import QtQuick 2.6
import Sailfish.Silica 1.0
import WerkWolf.Fernschreiber 1.0
import "../components"
import "../js/twemoji.js" as Emoji
import "../js/debug.js" as Debug
Item {
id: voiceNoteOverlayItem
anchors.fill: parent
property int recordingState: fernschreiberUtils.getVoiceNoteRecordingState();
property int recordingDuration: 0;
function handleRecordingState() {
switch (recordingState) {
case FernschreiberUtilities.Unavailable:
recordingStateLabel.text = qsTr("Unavailable");
break;
case FernschreiberUtilities.Stopped:
recordingStateLabel.text = qsTr("Stopped");
break;
case FernschreiberUtilities.Starting:
recordingStateLabel.text = qsTr("Starting");
break;
case FernschreiberUtilities.Recording:
recordingStateLabel.text = qsTr("Recording");
break;
case FernschreiberUtilities.Stopping:
recordingStateLabel.text = qsTr("Stopping");
break;
}
}
function getTwoDigitString(numberToBeConverted) {
var numberString = "00";
if (numberToBeConverted > 0 && numberToBeConverted < 10) {
numberString = "0" + String(numberToBeConverted);
}
if (numberToBeConverted >= 10) {
numberString = String(numberToBeConverted);
}
return numberString;
}
function handleRecordingDuration() {
var minutes = Math.floor(recordingDuration / 60);
var seconds = recordingDuration % 60;
recordingDurationLabel.text = getTwoDigitString(minutes) + ":" + getTwoDigitString(seconds);
}
Component.onCompleted: {
handleRecordingState();
handleRecordingDuration();
}
Connections {
target: fernschreiberUtils
onVoiceNoteDurationChanged: {
Debug.log("New duration received: " + duration);
recordingDuration = Math.round(duration / 1000);
handleRecordingDuration();
}
onVoiceNoteRecordingStateChanged: {
Debug.log("New state received: " + state);
recordingState = state;
handleRecordingState();
}
}
Rectangle {
id: stickerPickerOverlayBackground
anchors.fill: parent
@ -52,17 +113,85 @@ Item {
text: qsTr("Record a Voice Note")
}
Image {
id: recorderImage
source: "image://theme/icon-l-recorder"
Label {
wrapMode: Text.Wrap
width: parent.width - ( 2 * Theme.horizontalPageMargin )
horizontalAlignment: Text.AlignHCenter
text: qsTr("Press the button to start recording")
font.pixelSize: Theme.fontSizeMedium
anchors {
horizontalCenter: parent.horizontalCenter
}
}
fillMode: Image.PreserveAspectFit
asynchronous: true
width: Theme.itemSizeLarge
height: Theme.itemSizeLarge
Item {
width: Theme.iconSizeExtraLarge
height: Theme.iconSizeExtraLarge
anchors {
horizontalCenter: parent.horizontalCenter
}
Rectangle {
color: Theme.primaryColor
opacity: Theme.opacityOverlay
width: Theme.iconSizeExtraLarge
height: Theme.iconSizeExtraLarge
anchors.centerIn: parent
radius: width / 2
}
Rectangle {
id: recordButton
color: "red"
width: Theme.iconSizeExtraLarge * 0.6
height: Theme.iconSizeExtraLarge * 0.6
anchors.centerIn: parent
radius: width / 2
MouseArea {
anchors.fill: parent
onClicked: {
recordButton.visible = false;
fernschreiberUtils.startRecordingVoiceNote();
}
}
}
Rectangle {
id: stopButton
visible: !recordButton.visible
color: Theme.overlayBackgroundColor
width: Theme.iconSizeExtraLarge * 0.4
height: Theme.iconSizeExtraLarge * 0.4
anchors.centerIn: parent
MouseArea {
anchors.fill: parent
onClicked: {
recordButton.visible = true;
fernschreiberUtils.stopRecordingVoiceNote();
}
}
}
}
Label {
id: recordingStateLabel
wrapMode: Text.Wrap
width: parent.width - ( 2 * Theme.horizontalPageMargin )
horizontalAlignment: Text.AlignHCenter
font.pixelSize: Theme.fontSizeMedium
anchors {
horizontalCenter: parent.horizontalCenter
}
}
Label {
id: recordingDurationLabel
wrapMode: Text.Wrap
width: parent.width - ( 2 * Theme.horizontalPageMargin )
horizontalAlignment: Text.AlignHCenter
font.pixelSize: Theme.fontSizeMedium
anchors {
horizontalCenter: parent.horizontalCenter
}
}
}

View file

@ -1145,6 +1145,11 @@ Page {
width: parent.width
height: active ? parent.height : 0
source: "../components/VoiceNoteOverlay.qml"
onActiveChanged: {
if (!active) {
fernschreiberUtils.stopRecordingVoiceNote();
}
}
}
}

View file

@ -1,10 +1,57 @@
#include "fernschreiberutils.h"
/*
Copyright (C) 2020-21 Sebastian J. Wolf and other contributors
This file is part of Fernschreiber.
Fernschreiber is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Fernschreiber is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fernschreiber. If not, see <http://www.gnu.org/licenses/>.
*/
#include "fernschreiberutils.h"
#include <QMap>
#include <QVariant>
#include <QAudioEncoderSettings>
#include <QStandardPaths>
#include <QDir>
#include <QFile>
#include <QUrl>
#define DEBUG_MODULE FernschreiberUtils
#include "debuglog.h"
FernschreiberUtils::FernschreiberUtils(QObject *parent) : QObject(parent)
{
LOG("Initializing audio recorder...");
QString temporaryDirectoryPath = QStandardPaths::writableLocation(QStandardPaths::TempLocation) + + "/harbour-fernschreiber";
QDir temporaryDirectory(temporaryDirectoryPath);
if (!temporaryDirectory.exists()) {
temporaryDirectory.mkpath(temporaryDirectoryPath);
}
QAudioEncoderSettings encoderSettings;
encoderSettings.setCodec("audio/vorbis");
encoderSettings.setChannelCount(1);
encoderSettings.setQuality(QMultimedia::LowQuality);
this->audioRecorder.setEncodingSettings(encoderSettings);
this->audioRecorder.setContainerFormat("ogg");
this->audioRecorder.setOutputLocation(QUrl::fromLocalFile(temporaryDirectoryPath + "/voicenote.ogg"));
QMediaRecorder::Status audioRecorderStatus = this->audioRecorder.status();
this->handleAudioRecorderStatusChanged(audioRecorderStatus);
connect(&audioRecorder, SIGNAL(durationChanged(qlonglong)), this, SIGNAL(voiceNoteDurationChanged(qlonglong)));
connect(&audioRecorder, SIGNAL(statusChanged(QMediaRecorder::Status)), this, SLOT(handleAudioRecorderStatusChanged(QMediaRecorder::Status)));
}
@ -128,3 +175,58 @@ QString FernschreiberUtils::getUserName(const QVariantMap &userInformation)
const QString lastName = userInformation.value("last_name").toString();
return QString(firstName + " " + lastName).trimmed();
}
void FernschreiberUtils::startRecordingVoiceNote()
{
LOG("Start recording voice note...");
QString voiceNotePath = this->voiceNotePath();
LOG("Using temporary file at " << voiceNotePath);
if (QFile::exists(voiceNotePath)) {
LOG("Removing old temporary file...");
QFile::remove(voiceNotePath);
}
this->audioRecorder.setVolume(1);
this->audioRecorder.record();
}
void FernschreiberUtils::stopRecordingVoiceNote()
{
LOG("Stop recording voice note...");
this->audioRecorder.stop();
}
QString FernschreiberUtils::voiceNotePath()
{
return this->audioRecorder.outputLocation().toLocalFile();
}
FernschreiberUtils::VoiceNoteRecordingState FernschreiberUtils::getVoiceNoteRecordingState()
{
return this->voiceNoteRecordingState;
}
void FernschreiberUtils::handleAudioRecorderStatusChanged(QMediaRecorder::Status status)
{
LOG("Audio recorder status changed:" << status);
switch (status) {
case QMediaRecorder::UnavailableStatus:
case QMediaRecorder::UnloadedStatus:
case QMediaRecorder::LoadingStatus:
this->voiceNoteRecordingState = VoiceNoteRecordingState::Unavailable;
break;
case QMediaRecorder::LoadedStatus:
case QMediaRecorder::PausedStatus:
this->voiceNoteRecordingState = VoiceNoteRecordingState::Stopped;
break;
case QMediaRecorder::StartingStatus:
this->voiceNoteRecordingState = VoiceNoteRecordingState::Starting;
break;
case QMediaRecorder::FinalizingStatus:
this->voiceNoteRecordingState = VoiceNoteRecordingState::Stopping;
break;
case QMediaRecorder::RecordingStatus:
this->voiceNoteRecordingState = VoiceNoteRecordingState::Recording;
break;
}
emit voiceNoteRecordingStateChanged(this->voiceNoteRecordingState);
}

View file

@ -1,7 +1,27 @@
/*
Copyright (C) 2020-21 Sebastian J. Wolf and other contributors
This file is part of Fernschreiber.
Fernschreiber is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Fernschreiber is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fernschreiber. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef FERNSCHREIBERUTILS_H
#define FERNSCHREIBERUTILS_H
#include <QObject>
#include <QAudioRecorder>
#include "tdlibwrapper.h"
class FernschreiberUtils : public QObject
@ -10,12 +30,34 @@ class FernschreiberUtils : public QObject
public:
explicit FernschreiberUtils(QObject *parent = nullptr);
enum VoiceNoteRecordingState {
Unavailable,
Stopped,
Starting,
Recording,
Stopping
};
Q_ENUM(VoiceNoteRecordingState)
static QString getMessageShortText(TDLibWrapper *tdLibWrapper, const QVariantMap &messageContent, const bool isChannel, const qlonglong currentUserId, const QVariantMap &messageSender);
static QString getUserName(const QVariantMap &userInformation);
signals:
Q_INVOKABLE void startRecordingVoiceNote();
Q_INVOKABLE void stopRecordingVoiceNote();
Q_INVOKABLE QString voiceNotePath();
Q_INVOKABLE VoiceNoteRecordingState getVoiceNoteRecordingState();
signals:
void voiceNoteDurationChanged(qlonglong duration);
void voiceNoteRecordingStateChanged(VoiceNoteRecordingState state);
private slots:
void handleAudioRecorderStatusChanged(QMediaRecorder::Status status);
private:
QAudioRecorder audioRecorder;
VoiceNoteRecordingState voiceNoteRecordingState;
public slots:
};
#endif // FERNSCHREIBERUTILS_H

View file

@ -82,6 +82,7 @@ int main(int argc, char *argv[])
FernschreiberUtils *fernschreiberUtils = new FernschreiberUtils(view.data());
context->setContextProperty("fernschreiberUtils", fernschreiberUtils);
qmlRegisterUncreatableType<FernschreiberUtils>(uri, 1, 0, "FernschreiberUtilities", QString());
DBusAdaptor *dBusAdaptor = tdLibWrapper->getDBusAdaptor();
context->setContextProperty("dBusAdaptor", dBusAdaptor);

View file

@ -1438,6 +1438,30 @@
<source>Record a Voice Note</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Press the button to start recording</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Unavailable</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Stopped</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Starting</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Recording</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Stopping</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>WebPagePreview</name>

View file

@ -1438,6 +1438,30 @@
<source>Record a Voice Note</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Press the button to start recording</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Unavailable</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Stopped</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Starting</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Recording</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Stopping</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>WebPagePreview</name>

View file

@ -1417,6 +1417,30 @@
<source>Record a Voice Note</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Press the button to start recording</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Unavailable</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Stopped</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Starting</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Recording</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Stopping</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>WebPagePreview</name>

View file

@ -1439,6 +1439,30 @@
<source>Record a Voice Note</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Press the button to start recording</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Unavailable</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Stopped</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Starting</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Recording</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Stopping</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>WebPagePreview</name>

View file

@ -1417,6 +1417,30 @@
<source>Record a Voice Note</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Press the button to start recording</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Unavailable</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Stopped</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Starting</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Recording</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Stopping</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>WebPagePreview</name>

View file

@ -1438,6 +1438,30 @@
<source>Record a Voice Note</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Press the button to start recording</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Unavailable</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Stopped</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Starting</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Recording</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Stopping</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>WebPagePreview</name>

View file

@ -1459,6 +1459,30 @@
<source>Record a Voice Note</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Press the button to start recording</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Unavailable</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Stopped</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Starting</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Recording</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Stopping</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>WebPagePreview</name>

View file

@ -1459,6 +1459,30 @@
<source>Record a Voice Note</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Press the button to start recording</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Unavailable</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Stopped</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Starting</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Recording</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Stopping</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>WebPagePreview</name>

View file

@ -1438,6 +1438,30 @@
<source>Record a Voice Note</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Press the button to start recording</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Unavailable</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Stopped</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Starting</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Recording</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Stopping</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>WebPagePreview</name>

View file

@ -1417,6 +1417,30 @@
<source>Record a Voice Note</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Press the button to start recording</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Unavailable</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Stopped</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Starting</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Recording</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Stopping</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>WebPagePreview</name>

View file

@ -1438,6 +1438,30 @@
<source>Record a Voice Note</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Press the button to start recording</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Unavailable</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Stopped</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Starting</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Recording</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Stopping</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>WebPagePreview</name>