Constify some methods
[alure.git] / src / decoders / opusfile.cpp
blob53ed943881cc4a1858c5f02ecff51dd7e4ce9d74
2 #include "opusfile.hpp"
4 #include <stdexcept>
5 #include <iostream>
6 #include <limits>
8 #include "buffer.h"
10 #include "opusfile.h"
12 namespace alure
15 static int read(void *user_data, unsigned char *ptr, int size)
17 std::istream *stream = static_cast<std::istream*>(user_data);
18 stream->clear();
20 if(size < 0 || !stream->read(reinterpret_cast<char*>(ptr), size))
21 return -1;
22 return stream->gcount();
25 static int seek(void *user_data, opus_int64 offset, int whence)
27 std::istream *stream = static_cast<std::istream*>(user_data);
28 stream->clear();
30 if(whence == SEEK_CUR)
31 stream->seekg(offset, std::ios_base::cur);
32 else if(whence == SEEK_SET)
33 stream->seekg(offset, std::ios_base::beg);
34 else if(whence == SEEK_END)
35 stream->seekg(offset, std::ios_base::end);
36 else
37 return -1;
39 return stream->good() ? 0 : -1;
42 static opus_int64 tell(void *user_data)
44 std::istream *stream = static_cast<std::istream*>(user_data);
45 stream->clear();
46 return stream->tellg();
50 class OpusFileDecoder : public Decoder {
51 UniquePtr<std::istream> mFile;
53 OggOpusFile *mOggFile;
54 int mOggBitstream;
56 ChannelConfig mChannelConfig;
58 public:
59 OpusFileDecoder(UniquePtr<std::istream> file, OggOpusFile *oggfile, ChannelConfig sconfig)
60 : mFile(std::move(file)), mOggFile(oggfile), mOggBitstream(0), mChannelConfig(sconfig)
61 { }
62 ~OpusFileDecoder() override final;
64 ALuint getFrequency() const override final;
65 ChannelConfig getChannelConfig() const override final;
66 SampleType getSampleType() const override final;
68 uint64_t getLength() const override final;
69 uint64_t getPosition() const override final;
70 bool seek(uint64_t pos) override final;
72 std::pair<uint64_t,uint64_t> getLoopPoints() const override final;
74 ALuint read(ALvoid *ptr, ALuint count) override final;
77 OpusFileDecoder::~OpusFileDecoder()
79 op_free(mOggFile);
83 ALuint OpusFileDecoder::getFrequency() const
85 // libopusfile always decodes to 48khz.
86 return 48000;
89 ChannelConfig OpusFileDecoder::getChannelConfig() const
91 return mChannelConfig;
94 SampleType OpusFileDecoder::getSampleType() const
96 return SampleType::Int16;
100 uint64_t OpusFileDecoder::getLength() const
102 ogg_int64_t len = op_pcm_total(mOggFile, -1);
103 return std::max<ogg_int64_t>(len, 0);
106 uint64_t OpusFileDecoder::getPosition() const
108 ogg_int64_t pos = op_pcm_tell(mOggFile);
109 return std::max<ogg_int64_t>(pos, 0);
112 bool OpusFileDecoder::seek(uint64_t pos)
114 return op_pcm_seek(mOggFile, pos) == 0;
117 std::pair<uint64_t,uint64_t> OpusFileDecoder::getLoopPoints() const
119 return std::make_pair(0, std::numeric_limits<uint64_t>::max());
122 ALuint OpusFileDecoder::read(ALvoid *ptr, ALuint count)
124 ALuint total = 0;
125 opus_int16 *samples = (opus_int16*)ptr;
126 int num_chans = FramesToBytes(1, mChannelConfig, SampleType::UInt8);
127 while(total < count)
129 if(num_chans != op_head(mOggFile, -1)->channel_count)
130 break;
131 int len = (count-total) * num_chans;
133 long got = op_read(mOggFile, samples, len, &mOggBitstream);
134 if(got <= 0) break;
136 samples += got*num_chans;
137 total += got;
140 // 1, 2, and 4 channel files decode into the same channel order as
141 // OpenAL, however 6 (5.1), 7 (6.1), and 8 (7.1) channel files need to be
142 // re-ordered.
143 if(mChannelConfig == ChannelConfig::X51)
145 samples = (opus_int16*)ptr;
146 for(ALuint i = 0;i < total;++i)
148 // OpenAL : FL, FR, FC, LFE, RL, RR
149 // Opus : FL, FC, FR, RL, RR, LFE
150 std::swap(samples[i*6 + 1], samples[i*6 + 2]);
151 std::swap(samples[i*6 + 3], samples[i*6 + 5]);
152 std::swap(samples[i*6 + 4], samples[i*6 + 5]);
155 else if(mChannelConfig == ChannelConfig::X61)
157 samples = (opus_int16*)ptr;
158 for(ALuint i = 0;i < total;++i)
160 // OpenAL : FL, FR, FC, LFE, RC, SL, SR
161 // Opus : FL, FC, FR, SL, SR, RC, LFE
162 std::swap(samples[i*7 + 1], samples[i*7 + 2]);
163 std::swap(samples[i*7 + 3], samples[i*7 + 6]);
164 std::swap(samples[i*7 + 4], samples[i*7 + 5]);
165 std::swap(samples[i*7 + 5], samples[i*7 + 6]);
168 else if(mChannelConfig == ChannelConfig::X71)
170 samples = (opus_int16*)ptr;
171 for(ALuint i = 0;i < total;++i)
173 // OpenAL : FL, FR, FC, LFE, RL, RR, SL, SR
174 // Opus : FL, FC, FR, SL, SR, RL, RR, LFE
175 std::swap(samples[i*8 + 1], samples[i*8 + 2]);
176 std::swap(samples[i*8 + 3], samples[i*8 + 7]);
177 std::swap(samples[i*8 + 4], samples[i*8 + 5]);
178 std::swap(samples[i*8 + 5], samples[i*8 + 6]);
179 std::swap(samples[i*8 + 6], samples[i*8 + 7]);
183 return total;
187 SharedPtr<Decoder> OpusFileDecoderFactory::createDecoder(UniquePtr<std::istream> &file)
189 static const OpusFileCallbacks streamIO = {
190 read, seek, tell, nullptr
193 OggOpusFile *oggfile = op_open_callbacks(file.get(), &streamIO, nullptr, 0, nullptr);
194 if(!oggfile) return nullptr;
196 int num_chans = op_head(oggfile, -1)->channel_count;
197 ChannelConfig channels = ChannelConfig::Mono;
198 if(num_chans == 1)
199 channels = ChannelConfig::Mono;
200 else if(num_chans == 2)
201 channels = ChannelConfig::Stereo;
202 else if(num_chans == 4)
203 channels = ChannelConfig::Quad;
204 else if(num_chans == 6)
205 channels = ChannelConfig::X51;
206 else if(num_chans == 7)
207 channels = ChannelConfig::X61;
208 else if(num_chans == 8)
209 channels = ChannelConfig::X71;
210 else
212 op_free(oggfile);
213 return nullptr;
216 return MakeShared<OpusFileDecoder>(std::move(file), oggfile, channels);