Files
neochat/src/libneochat/voicerecorder.cpp
2026-02-14 11:04:22 -05:00

125 lines
3.4 KiB
C++

// SPDX-FileCopyrightText: 2026 Tobias Fella <tobias.fella@kde.org>
// SPDX-License-Identifier: GPL-2.0-or-later
#include "voicerecorder.h"
#include <QFile>
#include <QTemporaryFile>
#include <KFormat>
#include <Quotient/events/filesourceinfo.h>
using namespace Qt::Literals::StringLiterals;
VoiceRecorder::VoiceRecorder(QObject *parent)
: QObject(parent)
, m_buffer(new QBuffer)
, m_format(QMediaFormat::FileFormat::Ogg)
{
m_session.setAudioInput(&m_input);
m_recorder.setAudioBitRate(24000);
m_recorder.setAudioSampleRate(48000);
m_format.setAudioCodec(QMediaFormat::AudioCodec::Opus);
m_recorder.setAudioChannelCount(1);
m_recorder.setMediaFormat(m_format);
m_buffer->open(QIODevice::ReadWrite);
m_recorder.setOutputDevice(m_buffer);
m_session.setRecorder(&m_recorder);
}
VoiceRecorder::~VoiceRecorder()
{
delete m_buffer;
}
void VoiceRecorder::startRecording()
{
m_buffer->setData({});
m_recorder.record();
}
void VoiceRecorder::stopRecording()
{
m_recorder.stop();
}
QMediaRecorder *VoiceRecorder::recorder()
{
return &m_recorder;
}
void VoiceRecorder::send()
{
Quotient::FileSourceInfo fileMetadata;
QByteArray data;
m_buffer->seek(0);
if (m_room->usesEncryption()) {
std::tie(fileMetadata, data) = Quotient::encryptFile(m_buffer->data());
m_buffer->close();
m_buffer->setData(data);
m_buffer->open(QIODevice::ReadOnly);
}
auto room = m_room;
auto buffer = m_buffer;
auto duration = m_recorder.duration();
m_buffer = nullptr;
m_room->connection()->uploadContent(buffer, {}, u"audio/ogg"_s).then([fileMetadata, room, buffer, duration](const auto &job) mutable {
QJsonObject mscFile{
{u"mimetype"_s, u"audio/ogg"_s},
{u"name"_s, u"Voice Message"_s},
{u"size"_s, buffer->size()},
};
if (room->usesEncryption()) {
mscFile[u"file"_s] = toJson(fileMetadata);
} else {
mscFile[u"url"_s] = job->contentUri().toString();
}
Quotient::setUrlInSourceInfo(fileMetadata, job->contentUri());
QJsonObject content{
{u"body"_s, u"Voice message"_s},
{u"msgtype"_s, u"m.audio"_s},
{u"org.matrix.msc1767.text"_s,
QJsonObject{{u"body"_s, u"Voice Message (%1, %2)"_s.arg(KFormat().formatDuration(duration), KFormat().formatByteSize(buffer->size()))}}},
{u"org.matrix.msc1767.file"_s, mscFile},
{u"info"_s,
QJsonObject{
{u"mimetype"_s, u"audio/ogg"_s},
{u"size"_s, buffer->size()},
{u"duration"_s, duration},
}},
{u"org.matrix.msc1767.audio"_s,
QJsonObject{
{u"duration"_s, duration},
{u"waveform"_s, QJsonArray{}}, // TODO
}},
{u"org.matrix.msc3245.voice"_s, QJsonObject{}}};
if (room->usesEncryption()) {
content[u"file"_s] = toJson(fileMetadata);
} else {
content[u"url"_s] = job->contentUri().toString();
}
room->postJson(u"m.room.message"_s, content);
});
}
void VoiceRecorder::setRoom(NeoChatRoom *room)
{
m_room = room;
Q_EMIT roomChanged();
}
NeoChatRoom *VoiceRecorder::room() const
{
return m_room.get();
}
bool VoiceRecorder::isSupported() const
{
return m_format.isSupported(QMediaFormat::Encode);
}