Manually track the source decoder sample pos
[alure.git] / src / decoders / vorbisfile.cpp
blobc1512735d59c40814dcb079136743ea1ce743486
2 #include "vorbisfile.hpp"
4 #include <stdexcept>
5 #include <iostream>
7 #include "vorbis/vorbisfile.h"
9 namespace alure
12 static int seek(void *user_data, ogg_int64_t offset, int whence)
14 std::istream *stream = static_cast<std::istream*>(user_data);
15 stream->clear();
17 if(whence == SEEK_CUR)
18 stream->seekg(offset, std::ios_base::cur);
19 else if(whence == SEEK_SET)
20 stream->seekg(offset, std::ios_base::beg);
21 else if(whence == SEEK_END)
22 stream->seekg(offset, std::ios_base::end);
23 else
24 return -1;
26 return stream->tellg();
29 static size_t read(void *ptr, size_t size, size_t nmemb, void *user_data)
31 std::istream *stream = static_cast<std::istream*>(user_data);
32 stream->clear();
34 stream->read(static_cast<char*>(ptr), nmemb*size);
35 size_t ret = stream->gcount();
36 return ret/size;
39 static long tell(void *user_data)
41 std::istream *stream = static_cast<std::istream*>(user_data);
42 stream->clear();
43 return stream->tellg();
46 static int close(void*)
48 return 0;
52 class VorbisFileDecoder : public Decoder {
53 UniquePtr<std::istream> mFile;
55 UniquePtr<OggVorbis_File> mOggFile;
56 vorbis_info *mVorbisInfo;
57 int mOggBitstream;
59 ChannelConfig mChannelConfig;
61 public:
62 VorbisFileDecoder(UniquePtr<std::istream> file, UniquePtr<OggVorbis_File> oggfile, vorbis_info *vorbisinfo, ChannelConfig sconfig)
63 : mFile(std::move(file)), mOggFile(std::move(oggfile)), mVorbisInfo(vorbisinfo)
64 , mOggBitstream(0), mChannelConfig(sconfig)
65 { }
66 ~VorbisFileDecoder() override final;
68 ALuint getFrequency() const override final;
69 ChannelConfig getChannelConfig() const override final;
70 SampleType getSampleType() const override final;
72 uint64_t getLength() const override final;
73 bool seek(uint64_t pos) override final;
75 std::pair<uint64_t,uint64_t> getLoopPoints() const override final;
77 ALuint read(ALvoid *ptr, ALuint count) override final;
80 VorbisFileDecoder::~VorbisFileDecoder()
82 ov_clear(mOggFile.get());
86 ALuint VorbisFileDecoder::getFrequency() const
88 return mVorbisInfo->rate;
91 ChannelConfig VorbisFileDecoder::getChannelConfig() const
93 return mChannelConfig;
96 SampleType VorbisFileDecoder::getSampleType() const
98 return SampleType::Int16;
102 uint64_t VorbisFileDecoder::getLength() const
104 ogg_int64_t len = ov_pcm_total(mOggFile.get(), -1);
105 return std::max<ogg_int64_t>(len, 0);
108 bool VorbisFileDecoder::seek(uint64_t pos)
110 return ov_pcm_seek(mOggFile.get(), pos) == 0;
113 std::pair<uint64_t,uint64_t> VorbisFileDecoder::getLoopPoints() const
115 return std::make_pair(0, 0);
118 ALuint VorbisFileDecoder::read(ALvoid *ptr, ALuint count)
120 ALuint total = 0;
121 ALshort *samples = (ALshort*)ptr;
122 while(total < count)
124 int len = (count-total) * mVorbisInfo->channels * 2;
125 #ifdef __BIG_ENDIAN__
126 long got = ov_read(mOggFile.get(), reinterpret_cast<char*>(samples), len, 1, 2, 1, &mOggBitstream);
127 #else
128 long got = ov_read(mOggFile.get(), reinterpret_cast<char*>(samples), len, 0, 2, 1, &mOggBitstream);
129 #endif
130 if(got <= 0) break;
132 got /= 2;
133 samples += got;
134 got /= mVorbisInfo->channels;
135 total += got;
138 // 1, 2, and 4 channel files decode into the same channel order as
139 // OpenAL, however 6 (5.1), 7 (6.1), and 8 (7.1) channel files need to be
140 // re-ordered.
141 if(mChannelConfig == ChannelConfig::X51)
143 samples = (ALshort*)ptr;
144 for(ALuint i = 0;i < total;++i)
146 // OpenAL : FL, FR, FC, LFE, RL, RR
147 // Vorbis : FL, FC, FR, RL, RR, LFE
148 std::swap(samples[i*6 + 1], samples[i*6 + 2]);
149 std::swap(samples[i*6 + 3], samples[i*6 + 5]);
150 std::swap(samples[i*6 + 4], samples[i*6 + 5]);
153 else if(mChannelConfig == ChannelConfig::X61)
155 samples = (ALshort*)ptr;
156 for(ALuint i = 0;i < total;++i)
158 // OpenAL : FL, FR, FC, LFE, RC, SL, SR
159 // Vorbis : FL, FC, FR, SL, SR, RC, LFE
160 std::swap(samples[i*7 + 1], samples[i*7 + 2]);
161 std::swap(samples[i*7 + 3], samples[i*7 + 6]);
162 std::swap(samples[i*7 + 4], samples[i*7 + 5]);
163 std::swap(samples[i*7 + 5], samples[i*7 + 6]);
166 else if(mChannelConfig == ChannelConfig::X71)
168 samples = (ALshort*)ptr;
169 for(ALuint i = 0;i < total;++i)
171 // OpenAL : FL, FR, FC, LFE, RL, RR, SL, SR
172 // Vorbis : FL, FC, FR, SL, SR, RL, RR, LFE
173 std::swap(samples[i*8 + 1], samples[i*8 + 2]);
174 std::swap(samples[i*8 + 3], samples[i*8 + 7]);
175 std::swap(samples[i*8 + 4], samples[i*8 + 5]);
176 std::swap(samples[i*8 + 5], samples[i*8 + 6]);
177 std::swap(samples[i*8 + 6], samples[i*8 + 7]);
181 return total;
185 SharedPtr<Decoder> VorbisFileDecoderFactory::createDecoder(UniquePtr<std::istream> &file)
187 static const ov_callbacks streamIO = {
188 read, seek, close, tell
191 vorbis_info *vorbisinfo = nullptr;
192 auto oggfile = MakeUnique<OggVorbis_File>();
193 if(ov_open_callbacks(file.get(), oggfile.get(), NULL, 0, streamIO) != 0)
194 return nullptr;
196 vorbisinfo = ov_info(oggfile.get(), -1);
197 if(!vorbisinfo)
199 ov_clear(oggfile.get());
200 return nullptr;
203 ChannelConfig channels = ChannelConfig::Mono;
204 if(vorbisinfo->channels == 1)
205 channels = ChannelConfig::Mono;
206 else if(vorbisinfo->channels == 2)
207 channels = ChannelConfig::Stereo;
208 else if(vorbisinfo->channels == 4)
209 channels = ChannelConfig::Quad;
210 else if(vorbisinfo->channels == 6)
211 channels = ChannelConfig::X51;
212 else if(vorbisinfo->channels == 7)
213 channels = ChannelConfig::X61;
214 else if(vorbisinfo->channels == 8)
215 channels = ChannelConfig::X71;
216 else
218 ov_clear(oggfile.get());
219 return nullptr;
222 return MakeShared<VorbisFileDecoder>(
223 std::move(file), std::move(oggfile), vorbisinfo, channels