Skip to content

Commit

Permalink
Merge 5f7f024 into ced0cb5
Browse files Browse the repository at this point in the history
  • Loading branch information
Alice committed Oct 7, 2016
2 parents ced0cb5 + 5f7f024 commit d402849
Show file tree
Hide file tree
Showing 15 changed files with 257 additions and 24 deletions.
143 changes: 125 additions & 18 deletions src/MagnumPlugins/DrFlacAudioImporter/DrFlacImporter.cpp
Expand Up @@ -30,11 +30,52 @@
#include <Corrade/Utility/Debug.h>
#include <Corrade/Utility/Endianness.h>

#include <Magnum/Math/Functions.h>

#define DR_FLAC_IMPLEMENTATION
#include "dr_flac.h"

namespace Magnum { namespace Audio {

#define bfv(value) Buffer::Format::value
namespace {

/* number of channels = 8, number of bits = 5 */
const Buffer::Format flacFormatTable[8][5] = {
{ bfv(Mono8), bfv(Mono16), bfv(MonoFloat), bfv(MonoDouble) }, // Mono
{ bfv(Stereo8), bfv(Stereo16), bfv(StereoFloat), bfv(StereoDouble) }, // Stereo
{ Buffer::Format{}, Buffer::Format{}, Buffer::Format{}, Buffer::Format{} }, // Not a thing
{ bfv(Quad8), bfv(Quad16), bfv(Quad32), bfv(Quad32) }, // Quad
{ Buffer::Format{}, Buffer::Format{}, Buffer::Format{}, Buffer::Format{} }, // Also not a thing
{ bfv(Surround51Channel8), bfv(Surround51Channel16), bfv(Surround51Channel32), bfv(Surround51Channel32) }, // 5.1
{ bfv(Surround61Channel8), bfv(Surround61Channel16), bfv(Surround61Channel32), bfv(Surround61Channel32) }, // 6.1
{ bfv(Surround71Channel8), bfv(Surround71Channel16), bfv(Surround71Channel32), bfv(Surround71Channel32) } // 7.1
};

/* Converts 32-bit PCM into lower bit levels by skipping bytes */
Containers::Array<char> convert32PCM(const Containers::Array<char>& container, UnsignedInt samples, UnsignedInt size) {
Containers::Array<char> convertData(samples*size);

UnsignedInt skip = -1, index = 0;
for(char item : container) {
++skip;

if(skip > 3) skip = 0;
if(skip < 4 - size) continue;

convertData[index] = item;
++index;
}

return convertData;
}

struct DrFlacDeleter {
void operator()(drflac* handle) { drflac_close(handle); }
};
}
#undef bfv

DrFlacImporter::DrFlacImporter() = default;

DrFlacImporter::DrFlacImporter(PluginManager::AbstractManager& manager, std::string plugin): AbstractImporter(manager, std::move(plugin)) {}
Expand All @@ -44,42 +85,108 @@ auto DrFlacImporter::doFeatures() const -> Features { return Feature::OpenData;
bool DrFlacImporter::doIsOpened() const { return _data; }

void DrFlacImporter::doOpenData(Containers::ArrayView<const char> data) {
drflac* handle = drflac_open_memory(data.data(), data.size());

std::unique_ptr<drflac, DrFlacDeleter> handle(drflac_open_memory(data.data(), data.size()));
if(!handle) {
Error() << "Audio::DrFlacImporter::openData(): failed to open and decode FLAC data";
return;
}

uint64_t samples = handle->totalSampleCount;
uint32_t frequency = handle->sampleRate;
uint8_t numChannels = handle->channels;
uint8_t bitsPerSample = handle->bitsPerSample;

_frequency = frequency;

if(numChannels == 1 && bitsPerSample == 8)
_format = Buffer::Format::Mono8;
else if(numChannels == 1 && bitsPerSample == 16)
_format = Buffer::Format::Mono16;
else if(numChannels == 2 && bitsPerSample == 8)
_format = Buffer::Format::Stereo8;
else if(numChannels == 2 && bitsPerSample == 16)
_format = Buffer::Format::Stereo16;
else {
/* FLAC supports any bitspersample from 4 to 64, but DrFlac always gives us 32-bit samples
So we normalize bit amounts to multiples of 8, rounding up */
UnsignedInt normalizedBytesPerSample = (bitsPerSample + 7)/8;

if(numChannels == 0 || numChannels == 3 || numChannels == 5 || numChannels > 8 ||
normalizedBytesPerSample == 0 || normalizedBytesPerSample > 8) {
Error() << "Audio::DrFlacImporter::openData(): unsupported channel count"
<< numChannels << "with" << bitsPerSample
<< "bits per sample";
return;
}

drflac_close(handle);
/* Can't load something with no samples */
if(samples == 0) {
Error() << "Audio::DrFlacImporter::openData(): no samples";
return;
}

Containers::Array<char> tempData(samples*sizeof(int32_t));
_frequency = handle->sampleRate;
_format = flacFormatTable[numChannels-1][normalizedBytesPerSample-1];
CORRADE_INTERNAL_ASSERT(_format != Buffer::Format{});

/* 32-bit integers need to be normalized to Double (with a 32 bit mantissa) */
if(normalizedBytesPerSample == 4) {
Containers::Array<Int> tempData(samples);
drflac_read_s32(handle.get(), samples, reinterpret_cast<Int*>(tempData.begin()));

/* If the channel is mono/stereo, we can use double samples */
if(numChannels < 3) {
Containers::Array<Double> doubleData(samples);

for (UnsignedInt i = 0; i < samples; ++i) {
doubleData[i] = Math::normalize<Double>(tempData[i]);
}

const char* doubleBegin = reinterpret_cast<const char*>(doubleData.begin());
const char* doubleEnd = reinterpret_cast<const char*>(doubleData.end());

_data = Containers::Array<char>(samples*sizeof(Double));
std::copy(doubleBegin, doubleEnd, _data.begin());

/* Otherwise, convert to float */
} else {
Containers::Array<Float> floatData(samples);

drflac_read_s32(handle, samples, reinterpret_cast<int32_t*>(tempData.begin()));
drflac_close(handle);
for (UnsignedInt i = 0; i < samples; ++i) {
floatData[i] = Math::normalize<Float>(tempData[i]);
}

const char* floatBegin = reinterpret_cast<const char*>(floatData.begin());
const char* floatEnd = reinterpret_cast<const char*>(floatData.end());

_data = Containers::Array<char>(samples*sizeof(Float));
std::copy(floatBegin, floatEnd, _data.begin());
}

return;
}

Containers::Array<char> tempData(samples*sizeof(Int));
drflac_read_s32(handle.get(), samples, reinterpret_cast<Int*>(tempData.begin()));

_data = convert32PCM(tempData, samples, normalizedBytesPerSample);

/* 8-bit needs to become unsigned */
if(normalizedBytesPerSample == 1) {
// Convert to unsigned
for(char& item : _data) {
item = item - 128;
}
/* 24-bit needs to become float */
} else if(normalizedBytesPerSample == 3) {
Containers::Array<Float> floatData(samples);

for (UnsignedInt i = 0; i < samples; ++i) {

UnsignedInt s0 = _data[i*3 + 0];
UnsignedInt s1 = _data[i*3 + 1];
UnsignedInt s2 = _data[i*3 + 2];

Int intData = Int((s0 << 8) | (s1 << 16) | (s2 << 24));
floatData[i] = Math::normalize<Float>(intData);
}

const char* floatBegin = reinterpret_cast<const char*>(floatData.begin());
const char* floatEnd = reinterpret_cast<const char*>(floatData.end());

_data = Containers::Array<char>(samples*sizeof(Float));
std::copy(floatBegin, floatEnd, _data.begin());
}

_data = std::move(tempData);
return;
}

Expand Down
4 changes: 1 addition & 3 deletions src/MagnumPlugins/DrFlacAudioImporter/DrFlacImporter.h
Expand Up @@ -54,9 +54,7 @@ namespace Magnum { namespace Audio {
/**
@brief FLAC audio importer plugin using dr_flac
Supports mono and stereo files with 8/16 bits per channel. The files are
imported with @ref Buffer::Format::Mono8 / @ref Buffer::Format::Mono16 or
@ref Buffer::Format::Stereo8 / @ref Buffer::Format::Stereo16, respectively.
Supports mono, stereo, and surround sound files with 8/16/24/32 bits per channel.
This plugin is built if `WITH_DRFLACAUDIOIMPORTER` is enabled when building
Magnum. To use dynamic plugin, you need to load `DrFlacAudioImporter` plugin
Expand Down
12 changes: 12 additions & 0 deletions src/MagnumPlugins/DrFlacAudioImporter/Test/CMakeLists.txt
Expand Up @@ -46,5 +46,17 @@ endif()
if(CORRADE_TARGET_EMSCRIPTEN)
emscripten_embed_file(DrFlacAudioImporterTest mono8.flac "/mono8.flac")
emscripten_embed_file(DrFlacAudioImporterTest mono16.flac "/mono16.flac")
emscripten_embed_file(DrFlacAudioImporterTest mono24.flac "/mono24.flac")

emscripten_embed_file(DrFlacAudioImporterTest stereo8.flac "/stereo8.flac")
emscripten_embed_file(DrFlacAudioImporterTest stereo16.flac "/stereo16.flac")
emscripten_embed_file(DrFlacAudioImporterTest stereo24.flac "/stereo24.flac")

emscripten_embed_file(DrFlacAudioImporterTest quad16.flac "/quad16.flac")
emscripten_embed_file(DrFlacAudioImporterTest quad24.flac "/quad24.flac")

emscripten_embed_file(DrFlacAudioImporterTest surround51Channel16.flac "/surround51Channel16.flac")
emscripten_embed_file(DrFlacAudioImporterTest surround51Channel24.flac "/surround51Channel24.flac")

emscripten_embed_file(DrFlacAudioImporterTest surround71Channel24.flac "/surround71Channel24.flac")
endif()
122 changes: 119 additions & 3 deletions src/MagnumPlugins/DrFlacAudioImporter/Test/DrFlacImporterTest.cpp
Expand Up @@ -42,30 +42,87 @@ class DrFlacImporterTest: public TestSuite::Tester {

void mono8();
void mono16();
void mono24();

void stereo8();
void stereo16();
void stereo24();

void quad16();
void quad24();

void surround51Channel16();
void surround51Channel24();

void surround71Channel24();
};

DrFlacImporterTest::DrFlacImporterTest() {
addTests({
&DrFlacImporterTest::mono8,
&DrFlacImporterTest::mono16,
&DrFlacImporterTest::stereo16});
&DrFlacImporterTest::mono24,

&DrFlacImporterTest::stereo8,
&DrFlacImporterTest::stereo16,
&DrFlacImporterTest::stereo24,

&DrFlacImporterTest::quad16,
&DrFlacImporterTest::quad24,

&DrFlacImporterTest::surround51Channel16,
&DrFlacImporterTest::surround51Channel24,

&DrFlacImporterTest::surround71Channel24
});
}

void DrFlacImporterTest::mono8() {
DrFlacImporter importer;
CORRADE_VERIFY(importer.openFile(Utility::Directory::join(DRFLACAUDIOIMPORTER_TEST_DIR, "mono8.flac")));

CORRADE_COMPARE(importer.format(), Buffer::Format::Mono8);
CORRADE_COMPARE(importer.frequency(), 22254);
CORRADE_COMPARE(importer.frequency(), 22050);

CORRADE_COMPARE_AS(importer.data().slice(0,4),
Containers::Array<char>::from(127, 127, 127, 127).slice(0,4),
TestSuite::Compare::Container);
}

void DrFlacImporterTest::mono16() {
DrFlacImporter importer;
CORRADE_VERIFY(importer.openFile(Utility::Directory::join(DRFLACAUDIOIMPORTER_TEST_DIR, "mono16.flac")));

CORRADE_COMPARE(importer.format(), Buffer::Format::Mono16);
CORRADE_COMPARE(importer.frequency(), 44100);
CORRADE_COMPARE(importer.frequency(), 44000);

CORRADE_COMPARE_AS(importer.data(),
Containers::Array<char>::from('\x1d', '\x10', '\x71', '\xc5'),
TestSuite::Compare::Container);
}

void DrFlacImporterTest::mono24() {
DrFlacImporter importer;
CORRADE_VERIFY(importer.openFile(Utility::Directory::join(DRFLACAUDIOIMPORTER_TEST_DIR, "mono24.flac")));

CORRADE_COMPARE(importer.format(), Buffer::Format::MonoFloat);
CORRADE_COMPARE(importer.frequency(), 48000);

CORRADE_COMPARE_AS(importer.data().prefix(4),
Containers::Array<char>::from(0, -56, 15, -70).prefix(4),
TestSuite::Compare::Container);
}

void DrFlacImporterTest::stereo8() {
DrFlacImporter importer;
CORRADE_VERIFY(importer.openFile(Utility::Directory::join(DRFLACAUDIOIMPORTER_TEST_DIR, "stereo8.flac")));

CORRADE_COMPARE(importer.format(), Buffer::Format::Stereo8);
CORRADE_COMPARE(importer.frequency(), 96000);

CORRADE_COMPARE_AS(importer.data(),
Containers::Array<char>::from('\xde', '\xfe', '\xca', '\x7e'),
TestSuite::Compare::Container);
}

void DrFlacImporterTest::stereo16() {
Expand All @@ -74,6 +131,65 @@ void DrFlacImporterTest::stereo16() {

CORRADE_COMPARE(importer.format(), Buffer::Format::Stereo16);
CORRADE_COMPARE(importer.frequency(), 44100);

CORRADE_COMPARE_AS(importer.data(),
Containers::Array<char>::from(39, 79, 39, 79),
TestSuite::Compare::Container);
}

void DrFlacImporterTest::stereo24() {
DrFlacImporter importer;
CORRADE_VERIFY(importer.openFile(Utility::Directory::join(DRFLACAUDIOIMPORTER_TEST_DIR, "stereo24.flac")));

CORRADE_COMPARE(importer.format(), Buffer::Format::StereoFloat);
CORRADE_COMPARE(importer.frequency(), 8000);

CORRADE_COMPARE_AS(importer.data().prefix(32),
Containers::Array<char>::from(0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 56, 0, 0, -128, 56, 0,
0, -64, -72, 0, 0, 0, 0).prefix(32),
TestSuite::Compare::Container);
}

void DrFlacImporterTest::quad16() {
DrFlacImporter importer;
CORRADE_VERIFY(importer.openFile(Utility::Directory::join(DRFLACAUDIOIMPORTER_TEST_DIR, "quad16.flac")));

CORRADE_COMPARE(importer.format(), Buffer::Format::Quad16);
CORRADE_COMPARE(importer.frequency(), 44100);
}

void DrFlacImporterTest::quad24() {
DrFlacImporter importer;
CORRADE_VERIFY(importer.openFile(Utility::Directory::join(DRFLACAUDIOIMPORTER_TEST_DIR, "quad24.flac")));

CORRADE_COMPARE(importer.format(), Buffer::Format::Quad32);
CORRADE_COMPARE(importer.frequency(), 44100);
}

void DrFlacImporterTest::surround51Channel16() {
DrFlacImporter importer;
CORRADE_VERIFY(importer.openFile(Utility::Directory::join(DRFLACAUDIOIMPORTER_TEST_DIR, "surround51Channel16.flac")));

CORRADE_COMPARE(importer.format(), Buffer::Format::Surround51Channel16);
CORRADE_COMPARE(importer.frequency(), 48000);
}

void DrFlacImporterTest::surround51Channel24() {
DrFlacImporter importer;
CORRADE_VERIFY(importer.openFile(Utility::Directory::join(DRFLACAUDIOIMPORTER_TEST_DIR, "surround51Channel24.flac")));

CORRADE_COMPARE(importer.format(), Buffer::Format::Surround51Channel32);
CORRADE_COMPARE(importer.frequency(), 48000);
}

void DrFlacImporterTest::surround71Channel24() {
DrFlacImporter importer;
CORRADE_VERIFY(importer.openFile(Utility::Directory::join(DRFLACAUDIOIMPORTER_TEST_DIR, "surround71Channel24.flac")));

CORRADE_COMPARE(importer.format(), Buffer::Format::Surround71Channel32);
CORRADE_COMPARE(importer.frequency(), 48000);
}

}}}
Expand Down
Binary file modified src/MagnumPlugins/DrFlacAudioImporter/Test/mono16.flac
Binary file not shown.
Binary file not shown.
Binary file modified src/MagnumPlugins/DrFlacAudioImporter/Test/mono8.flac
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file modified src/MagnumPlugins/DrFlacAudioImporter/Test/stereo16.flac
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

0 comments on commit d402849

Please sign in to comment.