Remove an unnecessary variable
[alure.git] / src / decoders / opusfile.cpp
blob5e62d0b5de0f696c2a84a54ed9f924f74271e3f1
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 template<typename T> struct OggTypeInfo { };
51 template<>
52 struct OggTypeInfo<ogg_int16_t>
54 template<typename ...Args>
55 static int read(Args&& ...args)
56 { return op_read(std::forward<Args>(args)...); }
58 template<>
59 struct OggTypeInfo<float>
61 template<typename ...Args>
62 static int read(Args&& ...args)
63 { return op_read_float(std::forward<Args>(args)...); }
66 class OpusFileDecoder final : public Decoder {
67 UniquePtr<std::istream> mFile;
69 OggOpusFile *mOggFile;
70 int mOggBitstream;
72 ChannelConfig mChannelConfig;
73 SampleType mSampleType;
75 template<typename T>
76 ALuint do_read(T *ptr, ALuint count)
78 ALuint total = 0;
79 T *samples = ptr;
80 int num_chans = FramesToBytes(1, mChannelConfig, SampleType::UInt8);
81 while(total < count)
83 if(num_chans != op_head(mOggFile, -1)->channel_count)
84 break;
85 int len = (count-total) * num_chans;
87 long got = OggTypeInfo<T>::read(mOggFile, samples, len, &mOggBitstream);
88 if(got <= 0) break;
90 samples += got*num_chans;
91 total += got;
94 // 1, 2, and 4 channel files decode into the same channel order as
95 // OpenAL, however 6 (5.1), 7 (6.1), and 8 (7.1) channel files need to be
96 // re-ordered.
97 if(mChannelConfig == ChannelConfig::X51)
99 samples = ptr;
100 for(ALuint i = 0;i < total;++i)
102 // OpenAL : FL, FR, FC, LFE, RL, RR
103 // Opus : FL, FC, FR, RL, RR, LFE
104 std::swap(samples[i*6 + 1], samples[i*6 + 2]);
105 std::swap(samples[i*6 + 3], samples[i*6 + 5]);
106 std::swap(samples[i*6 + 4], samples[i*6 + 5]);
109 else if(mChannelConfig == ChannelConfig::X61)
111 samples = ptr;
112 for(ALuint i = 0;i < total;++i)
114 // OpenAL : FL, FR, FC, LFE, RC, SL, SR
115 // Opus : FL, FC, FR, SL, SR, RC, LFE
116 std::swap(samples[i*7 + 1], samples[i*7 + 2]);
117 std::swap(samples[i*7 + 3], samples[i*7 + 6]);
118 std::swap(samples[i*7 + 4], samples[i*7 + 5]);
119 std::swap(samples[i*7 + 5], samples[i*7 + 6]);
122 else if(mChannelConfig == ChannelConfig::X71)
124 samples = ptr;
125 for(ALuint i = 0;i < total;++i)
127 // OpenAL : FL, FR, FC, LFE, RL, RR, SL, SR
128 // Opus : FL, FC, FR, SL, SR, RL, RR, LFE
129 std::swap(samples[i*8 + 1], samples[i*8 + 2]);
130 std::swap(samples[i*8 + 3], samples[i*8 + 7]);
131 std::swap(samples[i*8 + 4], samples[i*8 + 5]);
132 std::swap(samples[i*8 + 5], samples[i*8 + 6]);
133 std::swap(samples[i*8 + 6], samples[i*8 + 7]);
137 return total;
140 public:
141 OpusFileDecoder(UniquePtr<std::istream> file, OggOpusFile *oggfile, ChannelConfig sconfig, SampleType stype)
142 : mFile(std::move(file)), mOggFile(oggfile), mOggBitstream(0), mChannelConfig(sconfig), mSampleType(stype)
144 ~OpusFileDecoder() override;
146 ALuint getFrequency() const override;
147 ChannelConfig getChannelConfig() const override;
148 SampleType getSampleType() const override;
150 uint64_t getLength() const override;
151 bool seek(uint64_t pos) override;
153 std::pair<uint64_t,uint64_t> getLoopPoints() const override;
155 ALuint read(ALvoid *ptr, ALuint count) override;
158 OpusFileDecoder::~OpusFileDecoder()
160 op_free(mOggFile);
164 ALuint OpusFileDecoder::getFrequency() const
166 // libopusfile always decodes to 48khz.
167 return 48000;
170 ChannelConfig OpusFileDecoder::getChannelConfig() const
172 return mChannelConfig;
175 SampleType OpusFileDecoder::getSampleType() const
177 return mSampleType;
181 uint64_t OpusFileDecoder::getLength() const
183 ogg_int64_t len = op_pcm_total(mOggFile, -1);
184 return std::max<ogg_int64_t>(len, 0);
187 bool OpusFileDecoder::seek(uint64_t pos)
189 return op_pcm_seek(mOggFile, pos) == 0;
192 std::pair<uint64_t,uint64_t> OpusFileDecoder::getLoopPoints() const
194 return std::make_pair(0, std::numeric_limits<uint64_t>::max());
197 ALuint OpusFileDecoder::read(ALvoid *ptr, ALuint count)
199 if(mSampleType == SampleType::Float32)
200 return do_read<float>(reinterpret_cast<float*>(ptr), count);
201 return do_read<ogg_int16_t>(reinterpret_cast<ogg_int16_t*>(ptr), count);
205 SharedPtr<Decoder> OpusFileDecoderFactory::createDecoder(UniquePtr<std::istream> &file)
207 static const OpusFileCallbacks streamIO = {
208 read, seek, tell, nullptr
211 OggOpusFile *oggfile = op_open_callbacks(file.get(), &streamIO, nullptr, 0, nullptr);
212 if(!oggfile) return nullptr;
214 int num_chans = op_head(oggfile, -1)->channel_count;
215 ChannelConfig channels = ChannelConfig::Mono;
216 if(num_chans == 1)
217 channels = ChannelConfig::Mono;
218 else if(num_chans == 2)
219 channels = ChannelConfig::Stereo;
220 else if(num_chans == 4)
221 channels = ChannelConfig::Quad;
222 else if(num_chans == 6)
223 channels = ChannelConfig::X51;
224 else if(num_chans == 7)
225 channels = ChannelConfig::X61;
226 else if(num_chans == 8)
227 channels = ChannelConfig::X71;
228 else
230 op_free(oggfile);
231 return nullptr;
234 if(Context::GetCurrent().isSupported(channels, SampleType::Float32))
235 return MakeShared<OpusFileDecoder>(std::move(file), oggfile, channels, SampleType::Float32);
236 return MakeShared<OpusFileDecoder>(std::move(file), oggfile, channels, SampleType::Int16);