Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 1 addition & 13 deletions build/install.qbs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ Product {
if (!Qt.core.frameworkBuild) {
var libPrefix = (qbs.targetOS.contains("linux") ? "lib" : "") + "Qt" + Qt.core.versionMajor
var libPostfix = ((qbs.targetOS.contains("windows") && qbs.debugInformation) ? "d": "") + cpp.dynamicLibrarySuffix
var libs = ["Core", "Gui", "Network", "Multimedia", "Svg", "Widgets"]
var libs = ["Core", "Gui", "Svg", "Widgets"]

if(qbs.targetOS.contains("linux")) {
for(var it in libs) {
Expand Down Expand Up @@ -97,8 +97,6 @@ Product {
list.push("**/QtCore.framework/**")
list.push("**/QtGui.framework/**")
list.push("**/QtWidgets.framework/**")
list.push("**/QtNetwork.framework/**")
list.push("**/QtMultimedia.framework/**")
list.push("**/QtSvg.framework/**")
list.push("**/QtDBus.framework/**")

Expand Down Expand Up @@ -138,16 +136,6 @@ Product {
qbs.installPrefix: install.PREFIX
}

Group {
name: "Qt Media Service Plugins"
prefix: FileInfo.joinPaths(Qt.core.pluginPath, "/mediaservice/")
files: pluginFiles
excludeFiles: pluginExcludeFiles
qbs.install: true
qbs.installDir: install.QTPLUGINS_PATH + "/mediaservice"
qbs.installPrefix: install.PREFIX
}

Group {
name: "Qt Platform Plugins"
prefix: FileInfo.joinPaths(Qt.core.pluginPath, "/platforms/")
Expand Down
53 changes: 5 additions & 48 deletions modules/media/includes/converters/audioconverter.h
Original file line number Diff line number Diff line change
@@ -1,27 +1,17 @@
#ifndef AUDIOCONVERTER_H
#define AUDIOCONVERTER_H

#include "resources/audioclip.h"

#include <assetconverter.h>

#include <vorbis/vorbisenc.h>

#include <QObject>

class QAudioDecoder;
class QAudioFormat;
class QEventLoop;

class AudioProxy;

class AudioImportSettings : public AssetConverterSettings {
A_OBJECT(AudioImportSettings, AssetConverterSettings, Editor)

A_PROPERTIES(
A_PROPERTY(bool, Streamed, AudioImportSettings::stream, AudioImportSettings::setStream),
A_PROPERTY(bool, Force_Mono, AudioImportSettings::mono, AudioImportSettings::setMono),
A_PROPERTY(float, Quality, AudioImportSettings::quality, AudioImportSettings::setQuality)
A_PROPERTY(bool, streamed, AudioImportSettings::stream, AudioImportSettings::setStream),
A_PROPERTY(bool, forceMono, AudioImportSettings::mono, AudioImportSettings::setMono),
A_PROPERTY(float, quality, AudioImportSettings::quality, AudioImportSettings::setQuality)
)

public:
Expand Down Expand Up @@ -52,49 +42,16 @@ class AudioConverter : public AssetConverter {
public:
AudioConverter();

void onBufferReady();
void onFinished();

protected:
void init() override;

StringList suffixes() const override { return {"mp3", "wav", "ogg"}; }
ReturnCode convertFile(AssetConverterSettings *) override;
AssetConverterSettings *createSettings() override;

VariantMap convertResource(AudioImportSettings *, int32_t srcChanels);

bool readOgg(AssetConverterSettings *settings, int32_t &channels);

private:
QByteArray m_buffer;

AudioProxy *m_proxy;
VariantMap convertResource(AudioImportSettings *, int32_t srcChanels, int32_t sampleRate, const ByteArray &buffer);

QAudioDecoder *m_decoder;

QEventLoop *m_loop;

};

class AudioProxy : public QObject {
Q_OBJECT
public:
void setConverter(AudioConverter *converter) {
m_converter = converter;
}

public slots:
void onBufferReady() {
m_converter->onBufferReady();
}

void onFinished() {
m_converter->onFinished();
}

private:
AudioConverter *m_converter;
bool readOgg(AssetConverterSettings *settings, int32_t &channels, ByteArray &buffer);

};

Expand Down
5 changes: 3 additions & 2 deletions modules/media/media.qbs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ Project {
"../../thirdparty/next/inc/core",
"../../thirdparty/openal/include",
"../../thirdparty/libogg/src",
"../../thirdparty/libvorbis/src"
"../../thirdparty/libvorbis/src",
"../../thirdparty/miniaudio"
]

DynamicLibrary {
Expand All @@ -39,7 +40,7 @@ Project {
Depends { name: "ogg-editor" }
Depends { name: "vorbis-editor" }
Depends { name: "vorbisfile-editor" }
Depends { name: "Qt"; submodules: ["core", "gui", "multimedia"]; }
Depends { name: "Qt"; submodules: ["core", "gui"]; }
bundle.isBundle: false

cpp.defines: ["SHARED_DEFINE", "MEDIA_LIBRARY"]
Expand Down
146 changes: 91 additions & 55 deletions modules/media/src/converters/audioconverter.cpp
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
#include "converters/audioconverter.h"

#include <QAudioDecoder>
#include <QEventLoop>
#include <QBuffer>
#include <QUrl>

#include <ctime>
#define MINIAUDIO_IMPLEMENTATION
#include <miniaudio.h>

#include <log.h>
#include <url.h>

#include <vorbis/vorbisfile.h>

#include "resources/audioclip.h"

#define HEADER "Header"
#define BLOCK_SIZE 1024
#define FORMAT_VERSION 1
Expand Down Expand Up @@ -62,20 +60,8 @@ void AudioImportSettings::setQuality(float quality) {
}
}

AudioConverter::AudioConverter() :
m_proxy(new AudioProxy),
m_decoder(new QAudioDecoder(m_proxy)),
m_loop(new QEventLoop(m_proxy)) {

m_proxy->setConverter(this);

QObject::connect(m_decoder, &QAudioDecoder::bufferReady, m_proxy, &AudioProxy::onBufferReady);
QObject::connect(m_decoder, &QAudioDecoder::finished, m_proxy, &AudioProxy::onFinished);

AudioProxy::connect(m_decoder, QOverload<QAudioDecoder::Error>::of(&QAudioDecoder::error), [=](QAudioDecoder::Error error) {
AudioConverter::AudioConverter() {

m_loop->exit(error);
});
}

void AudioConverter::init() {
Expand All @@ -86,30 +72,91 @@ void AudioConverter::init() {
}
}

ma_result customReadProc(ma_decoder *decoder, void *buffer, size_t bytesToRead, size_t *bytesRead) {
FILE *fp = reinterpret_cast<FILE *>(decoder->pUserData);
*bytesRead = fread(buffer, 1, bytesToRead, fp);

return MA_SUCCESS;
}

ma_result customSeekProc(ma_decoder *decoder, ma_int64 offset, ma_seek_origin origin) {
FILE *fp = reinterpret_cast<FILE *>(decoder->pUserData);

int fseek_origin = (origin == ma_seek_origin_start) ? SEEK_SET : SEEK_CUR;
if(fseek(fp, offset, fseek_origin) == 0) {
return MA_SUCCESS;
}

return MA_BAD_SEEK;
}

size_t read(const ByteArray &data, char *buffer, size_t maxSize, size_t &pos) {
if(pos >= data.size()) {
return 0;
}

size_t bytesToRead = MIN(maxSize, data.size() - pos);
std::copy(data.begin() + pos, data.begin() + pos + bytesToRead, buffer);
pos += bytesToRead;

return bytesToRead;
}

AssetConverter::ReturnCode AudioConverter::convertFile(AssetConverterSettings *settings) {
m_buffer.clear();
ByteArray buffer;

int32_t channels = 1;
int32_t sampleRate = 0;

Url info(settings->source());

if(info.suffix() == "ogg") {
readOgg(settings, channels);
readOgg(settings, channels, buffer);
} else {
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
m_decoder->setSourceFilename(settings->source().data());
#else
m_decoder->setSource(QUrl(settings->source().data()));
#endif
m_decoder->start();
TString filename(settings->source());

FILE *fp = fopen(filename.data(), "rb");
if(!fp) {
aError() << "Unable to open file:" << filename.data();
return InternalError;
}

ma_decoder_config decoderConfig = ma_decoder_config_init(ma_format_s16, 0, 0);
ma_decoder decoder;

ma_result decodeResult = ma_decoder_init(customReadProc, customSeekProc, fp, &decoderConfig, &decoder);
if(decodeResult != MA_SUCCESS) {
aError() << "Unable to initilize decoder:" << decodeResult;
fclose(fp);
return InternalError;
}

channels = decoder.outputChannels;
sampleRate = decoder.outputSampleRate;

ma_uint64 totalFrames;
decodeResult = ma_decoder_get_length_in_pcm_frames(&decoder, &totalFrames);
if(decodeResult != MA_SUCCESS) {
aError() << "Unable to read file length";
ma_decoder_uninit(&decoder);
fclose(fp);
return InternalError;
}

int32_t code = m_loop->exec();
if(code != QAudioDecoder::NoError) {
aError() << "Unable to convert:" << info.baseName() << "error code:" << code;
ma_uint32 sampleSize = ma_get_bytes_per_sample(decoder.outputFormat);
ma_uint64 dataSizeInBytes = totalFrames * channels * sampleSize;

buffer.resize(dataSizeInBytes);

ma_uint64 framesRead;
decodeResult = ma_decoder_read_pcm_frames(&decoder, buffer.data(), totalFrames, &framesRead);
if(decodeResult != MA_SUCCESS || framesRead != totalFrames) {
aError() << "Unable to decode file. Frames converted:" << framesRead << "from" << totalFrames;
return InternalError;
}

channels = m_decoder->audioFormat().channelCount();
ma_decoder_uninit(&decoder);
fclose(fp);
}

AudioClip *clip = Engine::loadResource<AudioClip>(settings->destination());
Expand All @@ -127,12 +174,12 @@ AssetConverter::ReturnCode AudioConverter::convertFile(AssetConverterSettings *s
Engine::replaceUUID(clip, uuid);
}

clip->loadUserData(convertResource(static_cast<AudioImportSettings *>(settings), channels));
clip->loadUserData(convertResource(static_cast<AudioImportSettings *>(settings), channels, sampleRate, buffer));

return settings->saveBinary(Engine::toVariant(clip), settings->absoluteDestination());
}

VariantMap AudioConverter::convertResource(AudioImportSettings *settings, int32_t srcChanels) {
VariantMap AudioConverter::convertResource(AudioImportSettings *settings, int32_t srcChanels, int32_t sampleRate, const ByteArray &buffer) {
VariantMap result;

ogg_stream_state stream;
Expand All @@ -144,13 +191,14 @@ VariantMap AudioConverter::convertResource(AudioImportSettings *settings, int32_
vorbis_comment comment;

vorbis_info_init(&info);
vorbis_comment_init(&comment);

int32_t channels = srcChanels;
if(settings->mono()) {
channels = 1;
}
vorbis_encode_init_vbr(&info, channels, 44100, CLAMP(settings->quality(), 0.0, 1.0));
vorbis_encode_init_vbr(&info, channels, sampleRate, CLAMP(settings->quality(), 0.0, 1.0));

vorbis_comment_init(&comment);
vorbis_comment_add_tag(&comment, "encoder", "thunder");

vorbis_analysis_init(&state, &info);
Expand Down Expand Up @@ -188,25 +236,23 @@ VariantMap AudioConverter::convertResource(AudioImportSettings *settings, int32_
file.write(reinterpret_cast<const char *>(page.body), page.body_len);
}

QBuffer buffer(&m_buffer);
buffer.open(QIODevice::ReadOnly);

int64_t offset = 2 * srcChanels;
size_t pos = 0;

char *ptr = new char[BLOCK_SIZE * offset];

bool eof = false;
while(!eof) {
int64_t bytes = buffer.read(ptr, BLOCK_SIZE * offset);
int64_t bytes = read(buffer, ptr, BLOCK_SIZE * offset, pos);
if(bytes == 0) {
vorbis_analysis_wrote(&state, 0);
} else {
float **data = vorbis_analysis_buffer(&state, bytes / offset);
uint64_t i;
for(i = 0; i < bytes / offset; i++) {
data[0][i] = ((ptr[i*offset+1] << 8) | (0x00ff & (int)ptr[i * offset])) / 32768.f;
data[0][i] = ((ptr[i*offset+1] << 8) | (0x00ff & (int)ptr[i * offset])) / 32768.f;
if(channels > 1) {
data[1][i] = ((ptr[i*offset+3] << 8) | (0x00ff & (int)ptr[i*offset+2])) / 32768.f;
data[1][i] = ((ptr[i*offset+3] << 8) | (0x00ff & (int)ptr[i*offset+2])) / 32768.f;
}
}
vorbis_analysis_wrote(&state, i);
Expand Down Expand Up @@ -252,7 +298,7 @@ VariantMap AudioConverter::convertResource(AudioImportSettings *settings, int32_
VariantList header;
header.push_back(resInfo.uuid);
header.push_back(settings->stream());
result[HEADER] = header;
result[HEADER] = header;

return result;
}
Expand All @@ -261,17 +307,7 @@ AssetConverterSettings *AudioConverter::createSettings() {
return new AudioImportSettings();
}

void AudioConverter::onBufferReady() {
QAudioBuffer buffer = m_decoder->read();

m_buffer.append(buffer.constData<char>(), buffer.byteCount());
}

void AudioConverter::onFinished() {
m_loop->exit();
}

bool AudioConverter::readOgg(AssetConverterSettings *settings, int32_t &channels) {
bool AudioConverter::readOgg(AssetConverterSettings *settings, int32_t &channels, ByteArray &buffer) {
OggVorbis_File vorbisFile;
if(ov_fopen(settings->source().data(), &vorbisFile) < 0) {
return false;
Expand All @@ -285,12 +321,12 @@ bool AudioConverter::readOgg(AssetConverterSettings *settings, int32_t &channels
int32_t section = 0;
int32_t result = 0;
while(result < BLOCK_SIZE) {
int32_t length = ov_read(&vorbisFile, out + result, BLOCK_SIZE - result, 0, 2, 1, &section);
int32_t length = ov_read(&vorbisFile, out + result, BLOCK_SIZE - result, 0, 2, 1, &section);
if(length <= 0) {
return true;
}
result += length;
}
m_buffer.append(out, result);
buffer.insert(buffer.end(), out, out + result);
}
}
Loading
Loading