Check for and read float samples from opusfile and sndfile
[alure.git] / src / decoders / opusfile.cpp
blob4afede255a2da49eef1b1e08e33557db8ee6ed3e
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 : 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 final;
146 ALuint getFrequency() const override final;
147 ChannelConfig getChannelConfig() const override final;
148 SampleType getSampleType() const override final;
150 uint64_t getLength() const override final;
151 uint64_t getPosition() const override final;
152 bool seek(uint64_t pos) override final;
154 std::pair<uint64_t,uint64_t> getLoopPoints() const override final;
156 ALuint read(ALvoid *ptr, ALuint count) override final;
159 OpusFileDecoder::~OpusFileDecoder()
161 op_free(mOggFile);
165 ALuint OpusFileDecoder::getFrequency() const
167 // libopusfile always decodes to 48khz.
168 return 48000;
171 ChannelConfig OpusFileDecoder::getChannelConfig() const
173 return mChannelConfig;
176 SampleType OpusFileDecoder::getSampleType() const
178 return mSampleType;
182 uint64_t OpusFileDecoder::getLength() const
184 ogg_int64_t len = op_pcm_total(mOggFile, -1);
185 return std::max<ogg_int64_t>(len, 0);
188 uint64_t OpusFileDecoder::getPosition() const
190 ogg_int64_t pos = op_pcm_tell(mOggFile);
191 return std::max<ogg_int64_t>(pos, 0);
194 bool OpusFileDecoder::seek(uint64_t pos)
196 return op_pcm_seek(mOggFile, pos) == 0;
199 std::pair<uint64_t,uint64_t> OpusFileDecoder::getLoopPoints() const
201 return std::make_pair(0, std::numeric_limits<uint64_t>::max());
204 ALuint OpusFileDecoder::read(ALvoid *ptr, ALuint count)
206 if(mSampleType == SampleType::Float32)
207 return do_read<float>(reinterpret_cast<float*>(ptr), count);
208 return do_read<ogg_int16_t>(reinterpret_cast<ogg_int16_t*>(ptr), count);
212 SharedPtr<Decoder> OpusFileDecoderFactory::createDecoder(UniquePtr<std::istream> &file)
214 static const OpusFileCallbacks streamIO = {
215 read, seek, tell, nullptr
218 OggOpusFile *oggfile = op_open_callbacks(file.get(), &streamIO, nullptr, 0, nullptr);
219 if(!oggfile) return nullptr;
221 int num_chans = op_head(oggfile, -1)->channel_count;
222 ChannelConfig channels = ChannelConfig::Mono;
223 if(num_chans == 1)
224 channels = ChannelConfig::Mono;
225 else if(num_chans == 2)
226 channels = ChannelConfig::Stereo;
227 else if(num_chans == 4)
228 channels = ChannelConfig::Quad;
229 else if(num_chans == 6)
230 channels = ChannelConfig::X51;
231 else if(num_chans == 7)
232 channels = ChannelConfig::X61;
233 else if(num_chans == 8)
234 channels = ChannelConfig::X71;
235 else
237 op_free(oggfile);
238 return nullptr;
241 if(Context::GetCurrent().isSupported(channels, SampleType::Float32))
242 return MakeShared<OpusFileDecoder>(std::move(file), oggfile, channels, SampleType::Float32);
243 return MakeShared<OpusFileDecoder>(std::move(file), oggfile, channels, SampleType::Int16);