Fix compilation with newer versions of DUMB
[alure.git] / src / decoders / sndfile.cpp
blob961bf1788dffa4a55f822ccc9ac81f210cd68df1
2 #include "sndfile.hpp"
4 #include <stdexcept>
5 #include <iostream>
7 #include "sndfile.h"
9 namespace {
11 constexpr alure::Array<int,1> CHANNELS_MONO {{SF_CHANNEL_MAP_MONO}};
12 constexpr alure::Array<int,2> CHANNELS_STEREO {{SF_CHANNEL_MAP_LEFT, SF_CHANNEL_MAP_RIGHT}};
13 constexpr alure::Array<int,2> CHANNELS_REAR {{SF_CHANNEL_MAP_REAR_LEFT, SF_CHANNEL_MAP_REAR_RIGHT}};
14 constexpr alure::Array<int,4> CHANNELS_QUAD {{SF_CHANNEL_MAP_LEFT, SF_CHANNEL_MAP_RIGHT, SF_CHANNEL_MAP_REAR_LEFT, SF_CHANNEL_MAP_REAR_RIGHT}};
15 constexpr alure::Array<int,6> CHANNELS_5DOT1 {{SF_CHANNEL_MAP_LEFT, SF_CHANNEL_MAP_RIGHT, SF_CHANNEL_MAP_CENTER, SF_CHANNEL_MAP_LFE, SF_CHANNEL_MAP_SIDE_LEFT, SF_CHANNEL_MAP_SIDE_RIGHT}};
16 constexpr alure::Array<int,6> CHANNELS_5DOT1_REAR{{SF_CHANNEL_MAP_LEFT, SF_CHANNEL_MAP_RIGHT, SF_CHANNEL_MAP_CENTER, SF_CHANNEL_MAP_LFE, SF_CHANNEL_MAP_REAR_LEFT, SF_CHANNEL_MAP_REAR_RIGHT}};
17 constexpr alure::Array<int,7> CHANNELS_6DOT1 {{SF_CHANNEL_MAP_LEFT, SF_CHANNEL_MAP_RIGHT, SF_CHANNEL_MAP_CENTER, SF_CHANNEL_MAP_LFE, SF_CHANNEL_MAP_REAR_CENTER, SF_CHANNEL_MAP_SIDE_LEFT, SF_CHANNEL_MAP_SIDE_RIGHT}};
18 constexpr alure::Array<int,8> CHANNELS_7DOT1 {{SF_CHANNEL_MAP_LEFT, SF_CHANNEL_MAP_RIGHT, SF_CHANNEL_MAP_CENTER, SF_CHANNEL_MAP_LFE, SF_CHANNEL_MAP_REAR_LEFT, SF_CHANNEL_MAP_REAR_RIGHT, SF_CHANNEL_MAP_SIDE_LEFT, SF_CHANNEL_MAP_SIDE_RIGHT}};
19 constexpr alure::Array<int,3> CHANNELS_BFORMAT2D {{SF_CHANNEL_MAP_AMBISONIC_B_W, SF_CHANNEL_MAP_AMBISONIC_B_X, SF_CHANNEL_MAP_AMBISONIC_B_Y}};
20 constexpr alure::Array<int,4> CHANNELS_BFORMAT3D {{SF_CHANNEL_MAP_AMBISONIC_B_W, SF_CHANNEL_MAP_AMBISONIC_B_X, SF_CHANNEL_MAP_AMBISONIC_B_Y, SF_CHANNEL_MAP_AMBISONIC_B_Z}};
23 sf_count_t istream_get_filelen(void *user_data)
25 std::istream *file = reinterpret_cast<std::istream*>(user_data);
26 file->clear();
28 sf_count_t len = -1;
29 std::streampos pos = file->tellg();
30 if(pos != static_cast<std::streampos>(-1) && file->seekg(0, std::ios::end))
32 len = file->tellg();
33 file->seekg(pos);
35 return len;
38 sf_count_t istream_seek(sf_count_t offset, int whence, void *user_data)
40 std::istream *file = reinterpret_cast<std::istream*>(user_data);
41 file->clear();
43 if(!file->seekg(offset, std::ios::seekdir(whence)))
44 return -1;
45 return file->tellg();
48 sf_count_t istream_read(void *ptr, sf_count_t count, void *user_data)
50 std::istream *file = reinterpret_cast<std::istream*>(user_data);
51 file->clear();
53 file->read(reinterpret_cast<char*>(ptr), count);
54 return file->gcount();
57 sf_count_t istream_write(const void*, sf_count_t, void*)
59 return -1;
62 sf_count_t istream_tell(void *user_data)
64 std::istream *file = reinterpret_cast<std::istream*>(user_data);
65 file->clear();
67 return file->tellg();
71 struct SndfileDeleter {
72 void operator()(SNDFILE *ptr) const { sf_close(ptr); }
74 using SndfilePtr = alure::UniquePtr<SNDFILE,SndfileDeleter>;
76 } // namespace
78 namespace alure {
80 class SndFileDecoder final : public Decoder {
81 UniquePtr<std::istream> mFile;
83 SndfilePtr mSndFile;
84 SF_INFO mSndInfo;
86 ChannelConfig mChannelConfig{ChannelConfig::Mono};
87 SampleType mSampleType{SampleType::UInt8};
88 std::pair<uint64_t, uint64_t> mLoopPts{0, 0};
90 public:
91 SndFileDecoder(UniquePtr<std::istream> file, SndfilePtr sndfile, const SF_INFO &sndinfo,
92 ChannelConfig sconfig, SampleType stype, uint64_t loopstart, uint64_t loopend) noexcept
93 : mFile(std::move(file)), mSndFile(std::move(sndfile)), mSndInfo(sndinfo)
94 , mChannelConfig(sconfig), mSampleType(stype), mLoopPts{loopstart, loopend}
95 { }
96 ~SndFileDecoder() override { }
98 ALuint getFrequency() const noexcept override;
99 ChannelConfig getChannelConfig() const noexcept override;
100 SampleType getSampleType() const noexcept override;
102 uint64_t getLength() const noexcept override;
103 bool seek(uint64_t pos) noexcept override;
105 std::pair<uint64_t,uint64_t> getLoopPoints() const noexcept override;
107 ALuint read(ALvoid *ptr, ALuint count) noexcept override;
110 ALuint SndFileDecoder::getFrequency() const noexcept { return mSndInfo.samplerate; }
111 ChannelConfig SndFileDecoder::getChannelConfig() const noexcept { return mChannelConfig; }
112 SampleType SndFileDecoder::getSampleType() const noexcept { return mSampleType; }
114 uint64_t SndFileDecoder::getLength() const noexcept
116 return std::max<sf_count_t>(mSndInfo.frames, 0);
119 bool SndFileDecoder::seek(uint64_t pos) noexcept
121 sf_count_t newpos = sf_seek(mSndFile.get(), pos, SEEK_SET);
122 if(newpos < 0) return false;
123 return true;
126 std::pair<uint64_t, uint64_t> SndFileDecoder::getLoopPoints() const noexcept { return mLoopPts; }
128 ALuint SndFileDecoder::read(ALvoid *ptr, ALuint count) noexcept
130 sf_count_t got = 0;
131 switch (mSampleType)
133 case SampleType::Mulaw:
134 case SampleType::UInt8:
135 got = sf_read_raw(mSndFile.get(), static_cast<ALubyte *>(ptr),
136 FramesToBytes(count, mChannelConfig, mSampleType));
137 got = BytesToFrames(got, mChannelConfig, mSampleType);
138 break;
139 case SampleType::Int16:
140 got = sf_readf_short(mSndFile.get(), static_cast<short *>(ptr), count);
141 break;
142 case SampleType::Float32:
143 got = sf_readf_float(mSndFile.get(), static_cast<float *>(ptr), count);
144 break;
146 return (ALuint)std::max<sf_count_t>(got, 0);
150 SharedPtr<Decoder> SndFileDecoderFactory::createDecoder(UniquePtr<std::istream> &file) noexcept
152 SF_VIRTUAL_IO vio = {
153 istream_get_filelen, istream_seek,
154 istream_read, istream_write, istream_tell
156 SF_INFO sndinfo;
157 SndfilePtr sndfile(sf_open_virtual(&vio, SFM_READ, &sndinfo, file.get()));
158 if(!sndfile) return nullptr;
160 std::pair<uint64_t, uint64_t> cue_points{0, std::numeric_limits<uint64_t>::max()};
162 // Needed for compatibility with older sndfile libraries.
163 struct SNDFILE_CUE_POINT {
164 int32_t indx;
165 uint32_t position;
166 int32_t fcc_chunk;
167 int32_t chunk_start;
168 int32_t block_start;
169 uint32_t sample_offset;
170 char name[256];
173 struct {
174 uint32_t cue_count;
175 SNDFILE_CUE_POINT cue_points[100];
176 } cues;
178 enum { SNDFILE_GET_CUE = 0x10CE };
180 if(sf_command(sndfile.get(), SNDFILE_GET_CUE, &cues, sizeof(cues)))
182 cue_points.first = cues.cue_points[0].sample_offset;
183 if(cues.cue_count > 1)
185 cue_points.second = cues.cue_points[1].sample_offset;
190 ChannelConfig sconfig;
191 Vector<int> chanmap(sndinfo.channels);
192 if(sf_command(sndfile.get(), SFC_GET_CHANNEL_MAP_INFO, chanmap.data(), chanmap.size()*sizeof(int)) == SF_TRUE)
194 auto matches = [](const Vector<int> &first, ArrayView<int> second) -> bool
196 return (first.size() == second.size()) &&
197 std::equal(first.begin(), first.end(), second.begin());
200 if(matches(chanmap, CHANNELS_MONO))
201 sconfig = ChannelConfig::Mono;
202 else if(matches(chanmap, CHANNELS_STEREO))
203 sconfig = ChannelConfig::Stereo;
204 else if(matches(chanmap, CHANNELS_REAR))
205 sconfig = ChannelConfig::Rear;
206 else if(matches(chanmap, CHANNELS_QUAD))
207 sconfig = ChannelConfig::Quad;
208 else if(matches(chanmap, CHANNELS_5DOT1) || matches(chanmap, CHANNELS_5DOT1_REAR))
209 sconfig = ChannelConfig::X51;
210 else if(matches(chanmap, CHANNELS_6DOT1))
211 sconfig = ChannelConfig::X61;
212 else if(matches(chanmap, CHANNELS_7DOT1))
213 sconfig = ChannelConfig::X71;
214 else if(matches(chanmap, CHANNELS_BFORMAT2D))
215 sconfig = ChannelConfig::BFormat2D;
216 else if(matches(chanmap, CHANNELS_BFORMAT3D))
217 sconfig = ChannelConfig::BFormat3D;
218 else
219 return nullptr;
221 else if(sf_command(sndfile.get(), SFC_WAVEX_GET_AMBISONIC, nullptr, 0) == SF_AMBISONIC_B_FORMAT)
223 if(sndinfo.channels == 3)
224 sconfig = ChannelConfig::BFormat2D;
225 else if(sndinfo.channels == 4)
226 sconfig = ChannelConfig::BFormat3D;
227 else
228 return nullptr;
230 else if(sndinfo.channels == 1)
231 sconfig = ChannelConfig::Mono;
232 else if(sndinfo.channels == 2)
233 sconfig = ChannelConfig::Stereo;
234 else
235 return nullptr;
237 SampleType stype = SampleType::Int16;
238 switch(sndinfo.format&SF_FORMAT_SUBMASK)
240 case SF_FORMAT_PCM_U8:
241 stype = SampleType::UInt8;
242 break;
243 case SF_FORMAT_ULAW:
244 if(Context::GetCurrent().isSupported(sconfig, SampleType::Mulaw))
245 stype = SampleType::Mulaw;
246 break;
247 case SF_FORMAT_FLOAT:
248 case SF_FORMAT_DOUBLE:
249 case SF_FORMAT_VORBIS:
250 if(Context::GetCurrent().isSupported(sconfig, SampleType::Float32))
251 stype = SampleType::Float32;
252 break;
253 default:
254 // For everything else, decode to signed 16-bit
255 stype = SampleType::Int16;
256 break;
259 return MakeShared<SndFileDecoder>(std::move(file), std::move(sndfile), sndinfo, sconfig, stype,
260 cue_points.first, cue_points.second);
263 } // namespace alure