2 #include "opusfile.hpp"
15 static int read(void *user_data
, unsigned char *ptr
, int size
)
17 std::istream
*stream
= static_cast<std::istream
*>(user_data
);
20 if(size
< 0 || !stream
->read(reinterpret_cast<char*>(ptr
), size
))
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
);
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
);
39 return stream
->good() ? 0 : -1;
42 static opus_int64
tell(void *user_data
)
44 std::istream
*stream
= static_cast<std::istream
*>(user_data
);
46 return stream
->tellg();
50 template<typename T
> struct OggTypeInfo
{ };
52 struct OggTypeInfo
<ogg_int16_t
>
54 template<typename
...Args
>
55 static int read(Args
&& ...args
)
56 { return op_read(std::forward
<Args
>(args
)...); }
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
;
72 ChannelConfig mChannelConfig
;
73 SampleType mSampleType
;
76 ALuint
do_read(T
*ptr
, ALuint count
) noexcept
80 int num_chans
= FramesToBytes(1, mChannelConfig
, SampleType::UInt8
);
83 if(num_chans
!= op_head(mOggFile
, -1)->channel_count
)
85 int len
= (count
-total
) * num_chans
;
87 long got
= OggTypeInfo
<T
>::read(mOggFile
, samples
, len
, &mOggBitstream
);
90 samples
+= got
*num_chans
;
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
97 if(mChannelConfig
== ChannelConfig::X51
)
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
)
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
)
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]);
141 OpusFileDecoder(UniquePtr
<std::istream
> file
, OggOpusFile
*oggfile
, ChannelConfig sconfig
, SampleType stype
) noexcept
142 : mFile(std::move(file
)), mOggFile(oggfile
), mOggBitstream(0), mChannelConfig(sconfig
), mSampleType(stype
)
144 ~OpusFileDecoder() override
;
146 ALuint
getFrequency() const noexcept override
;
147 ChannelConfig
getChannelConfig() const noexcept override
;
148 SampleType
getSampleType() const noexcept override
;
150 uint64_t getLength() const noexcept override
;
151 bool seek(uint64_t pos
) noexcept override
;
153 std::pair
<uint64_t,uint64_t> getLoopPoints() const noexcept override
;
155 ALuint
read(ALvoid
*ptr
, ALuint count
) noexcept override
;
158 OpusFileDecoder::~OpusFileDecoder()
164 ALuint
OpusFileDecoder::getFrequency() const noexcept
166 // libopusfile always decodes to 48khz.
170 ChannelConfig
OpusFileDecoder::getChannelConfig() const noexcept
172 return mChannelConfig
;
175 SampleType
OpusFileDecoder::getSampleType() const noexcept
181 uint64_t OpusFileDecoder::getLength() const noexcept
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
) noexcept
189 return op_pcm_seek(mOggFile
, pos
) == 0;
192 std::pair
<uint64_t,uint64_t> OpusFileDecoder::getLoopPoints() const noexcept
194 return std::make_pair(0, std::numeric_limits
<uint64_t>::max());
197 ALuint
OpusFileDecoder::read(ALvoid
*ptr
, ALuint count
) noexcept
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
) noexcept
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 std::pair
<uint64_t,uint64_t> loop_points
= { 0, std::numeric_limits
<uint64_t>::max() };
215 if(const OpusTags
*tags
= op_tags(oggfile
, -1))
217 for(int i
= 0;i
< tags
->comments
;i
++)
219 auto seppos
= StringView(
220 tags
->user_comments
[i
], tags
->comment_lengths
[i
]
221 ).find_first_of('=');
222 if(seppos
== StringView::npos
) continue;
224 StringView
key(tags
->user_comments
[i
], seppos
);
225 StringView
val(tags
->user_comments
[i
]+seppos
+1, tags
->comment_lengths
[i
]-(seppos
+1));
227 // RPG Maker seems to recognize LOOPSTART and LOOPLENGTH for loop
228 // points in a Vorbis comment. ZDoom recognizes LOOP_START and
229 // LOOP_END. We can recognize both.
230 if(key
== "LOOP_START" || key
== "LOOPSTART")
232 auto pt
= parse_timeval(val
, 48000.0);
233 if(pt
.index() == 1) loop_points
.first
= std::get
<1>(pt
);
237 if(key
== "LOOP_END")
239 auto pt
= parse_timeval(val
, 48000.0);
240 if(pt
.index() == 1) loop_points
.second
= std::get
<1>(pt
);
244 if(key
== "LOOPLENGTH")
246 auto pt
= parse_timeval(val
, 48000.0);
248 loop_points
.second
= loop_points
.first
+ std::get
<1>(pt
);
254 int num_chans
= op_head(oggfile
, -1)->channel_count
;
255 ChannelConfig channels
= ChannelConfig::Mono
;
257 channels
= ChannelConfig::Mono
;
258 else if(num_chans
== 2)
259 channels
= ChannelConfig::Stereo
;
260 else if(num_chans
== 4)
261 channels
= ChannelConfig::Quad
;
262 else if(num_chans
== 6)
263 channels
= ChannelConfig::X51
;
264 else if(num_chans
== 7)
265 channels
= ChannelConfig::X61
;
266 else if(num_chans
== 8)
267 channels
= ChannelConfig::X71
;
274 if(Context::GetCurrent().isSupported(channels
, SampleType::Float32
))
275 return MakeShared
<OpusFileDecoder
>(std::move(file
), oggfile
, channels
, SampleType::Float32
);
276 return MakeShared
<OpusFileDecoder
>(std::move(file
), oggfile
, channels
, SampleType::Int16
);