1 /*
2 This file is part of Magnum.
3
4 Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019
5 Vladimír Vondruš <mosra@centrum.cz>
6 Copyright © 2016 Alice Margatroid <loveoverwhelming@gmail.com>
7
8 Permission is hereby granted, free of charge, to any person obtaining a
9 copy of this software and associated documentation files (the "Software"),
10 to deal in the Software without restriction, including without limitation
11 the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 and/or sell copies of the Software, and to permit persons to whom the
13 Software is furnished to do so, subject to the following conditions:
14
15 The above copyright notice and this permission notice shall be included
16 in all copies or substantial portions of the Software.
17
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 DEALINGS IN THE SOFTWARE.
25 */
26
27 #include "DrFlacImporter.h"
28
29 #include <Corrade/Containers/ScopeGuard.h>
30 #include <Corrade/Utility/Assert.h>
31 #include <Corrade/Utility/Debug.h>
32 #include <Corrade/Utility/Endianness.h>
33
34 #include <Magnum/Math/Packing.h>
35
36 #define DR_FLAC_IMPLEMENTATION
37 #include "dr_flac.h"
38
39 namespace Magnum { namespace Audio {
40
41 namespace {
42
43 #define _v(value) BufferFormat::value
44 /* number of channels = 1-8, number of bytes = 1-4 */
45 const BufferFormat flacFormatTable[8][4] = {
46 {_v(Mono8), _v(Mono16), _v(MonoFloat), _v(MonoDouble)}, /* Mono */
47 {_v(Stereo8), _v(Stereo16), _v(StereoFloat), _v(StereoDouble)}, /* Stereo */
48 {BufferFormat{}, BufferFormat{}, BufferFormat{}, BufferFormat{}}, /* Not a thing */
49 {_v(Quad8), _v(Quad16), _v(Quad32), _v(Quad32)}, /* Quad */
50 {BufferFormat{}, BufferFormat{}, BufferFormat{}, BufferFormat{}}, /* Also not a thing */
51 {_v(Surround51Channel8), _v(Surround51Channel16), _v(Surround51Channel32), _v(Surround51Channel32)}, /* 5.1 */
52 {_v(Surround61Channel8), _v(Surround61Channel16), _v(Surround61Channel32), _v(Surround61Channel32)}, /* 6.1 */
53 {_v(Surround71Channel8), _v(Surround71Channel16), _v(Surround71Channel32), _v(Surround71Channel32)} /* 7.1 */
54 };
55 #undef _v
56
57 /* Converts 32-bit PCM into lower bit levels by skipping bytes */
convert32PCM(const Containers::Array<char> & container,const UnsignedInt samples,const UnsignedInt size)58 Containers::Array<char> convert32PCM(const Containers::Array<char>& container, const UnsignedInt samples, const UnsignedInt size) {
59 Containers::Array<char> convertData(samples*size);
60
61 UnsignedInt skip = -1, index = 0;
62 for(char item: container) {
63 ++skip;
64
65 if(skip > 3) skip = 0;
66 if(skip < 4 - size) continue;
67
68 convertData[index] = item;
69 ++index;
70 }
71
72 return convertData;
73 }
74
75 }
76
77 DrFlacImporter::DrFlacImporter() = default;
78
DrFlacImporter(PluginManager::AbstractManager & manager,const std::string & plugin)79 DrFlacImporter::DrFlacImporter(PluginManager::AbstractManager& manager, const std::string& plugin): AbstractImporter{manager, plugin} {}
80
doFeatures() const81 auto DrFlacImporter::doFeatures() const -> Features { return Feature::OpenData; }
82
doIsOpened() const83 bool DrFlacImporter::doIsOpened() const { return !!_data; }
84
doOpenData(Containers::ArrayView<const char> data)85 void DrFlacImporter::doOpenData(Containers::ArrayView<const char> data) {
86 drflac* const handle = drflac_open_memory(data.data(), data.size());
87 if(!handle) {
88 Error() << "Audio::DrFlacImporter::openData(): failed to open and decode FLAC data";
89 return;
90 }
91 Containers::ScopeGuard drflacClose{handle, drflac_close};
92
93 const std::uint64_t samples = handle->totalSampleCount;
94 const std::uint8_t numChannels = handle->channels;
95 const std::uint8_t bitsPerSample = handle->bitsPerSample;
96
97 /* FLAC supports any bitspersample from 4 to 64, but DrFlac always gives us
98 32-bit samples. So we normalize bit amounts to multiples of 8, rounding
99 up. */
100 const UnsignedInt normalizedBytesPerSample = (bitsPerSample + 7)/8;
101
102 if(numChannels == 0 || numChannels == 3 || numChannels == 5 || numChannels > 8 ||
103 normalizedBytesPerSample == 0 || normalizedBytesPerSample > 8) {
104 Error() << "Audio::DrFlacImporter::openData(): unsupported channel count"
105 << numChannels << "with" << bitsPerSample
106 << "bits per sample";
107 return;
108 }
109
110 _frequency = handle->sampleRate;
111 _format = flacFormatTable[numChannels-1][normalizedBytesPerSample-1];
112 CORRADE_INTERNAL_ASSERT(_format != BufferFormat{});
113
114 /* 32-bit integers need to be normalized to Double (with a 32 bit mantissa) */
115 if(normalizedBytesPerSample == 4) {
116 Containers::Array<Int> tempData(samples);
117 drflac_read_s32(handle, samples, reinterpret_cast<Int*>(tempData.begin()));
118
119 /* If the channel is mono/stereo, we can use double samples */
120 if(numChannels < 3) {
121 Containers::Array<Double> doubleData(samples);
122
123 for(std::size_t i = 0; i < samples; ++i) {
124 doubleData[i] = Math::unpack<Double>(tempData[i]);
125 }
126
127 const char* doubleBegin = reinterpret_cast<const char*>(doubleData.begin());
128 const char* doubleEnd = reinterpret_cast<const char*>(doubleData.end());
129
130 _data = Containers::Array<char>(samples*sizeof(Double));
131 std::copy(doubleBegin, doubleEnd, _data->begin());
132
133 /* Otherwise, convert to float */
134 } else {
135 Containers::Array<Float> floatData(samples);
136
137 for(std::size_t i = 0; i < samples; ++i) {
138 floatData[i] = Math::unpack<Float>(tempData[i]);
139 }
140
141 const char* floatBegin = reinterpret_cast<const char*>(floatData.begin());
142 const char* floatEnd = reinterpret_cast<const char*>(floatData.end());
143
144 _data = Containers::Array<char>(samples*sizeof(Float));
145 std::copy(floatBegin, floatEnd, _data->begin());
146 }
147
148 return;
149 }
150
151 Containers::Array<char> tempData(samples*sizeof(Int));
152 drflac_read_s32(handle, samples, reinterpret_cast<Int*>(tempData.begin()));
153
154 _data = convert32PCM(tempData, samples, normalizedBytesPerSample);
155
156 /* 8-bit needs to become unsigned */
157 if(normalizedBytesPerSample == 1) {
158 for(char& item: *_data) item = item - 128;
159
160 /* 24-bit needs to become float */
161 } else if(normalizedBytesPerSample == 3) {
162 Containers::Array<Float> floatData(samples);
163
164 for(std::size_t i = 0; i != samples; ++i) {
165 const UnsignedInt s0 = (*_data)[i*3 + 0];
166 const UnsignedInt s1 = (*_data)[i*3 + 1];
167 const UnsignedInt s2 = (*_data)[i*3 + 2];
168
169 const Int intData = Int((s0 << 8) | (s1 << 16) | (s2 << 24));
170 floatData[i] = Math::unpack<Float>(intData);
171 }
172
173 const char* const floatBegin = reinterpret_cast<const char*>(floatData.begin());
174 const char* const floatEnd = reinterpret_cast<const char*>(floatData.end());
175
176 _data = Containers::Array<char>(samples*sizeof(Float));
177 std::copy(floatBegin, floatEnd, _data->begin());
178 }
179 }
180
doClose()181 void DrFlacImporter::doClose() { _data = Containers::NullOpt; }
182
doFormat() const183 BufferFormat DrFlacImporter::doFormat() const { return _format; }
184
doFrequency() const185 UnsignedInt DrFlacImporter::doFrequency() const { return _frequency; }
186
doData()187 Containers::Array<char> DrFlacImporter::doData() {
188 Containers::Array<char> copy(_data->size());
189 std::copy(_data->begin(), _data->end(), copy.begin());
190 return copy;
191 }
192
193 }}
194
195 CORRADE_PLUGIN_REGISTER(DrFlacAudioImporter, Magnum::Audio::DrFlacImporter,
196 "cz.mosra.magnum.Audio.AbstractImporter/0.1")
197