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