2 #include "vorbisfile.hpp"
8 #include "vorbis/vorbisfile.h"
13 static int seek(void *user_data
, ogg_int64_t offset
, int whence
)
15 std::istream
*stream
= static_cast<std::istream
*>(user_data
);
18 if(whence
== SEEK_CUR
)
19 stream
->seekg(offset
, std::ios_base::cur
);
20 else if(whence
== SEEK_SET
)
21 stream
->seekg(offset
, std::ios_base::beg
);
22 else if(whence
== SEEK_END
)
23 stream
->seekg(offset
, std::ios_base::end
);
27 return stream
->tellg();
30 static size_t read(void *ptr
, size_t size
, size_t nmemb
, void *user_data
)
32 std::istream
*stream
= static_cast<std::istream
*>(user_data
);
35 stream
->read(static_cast<char*>(ptr
), nmemb
*size
);
36 size_t ret
= stream
->gcount();
40 static long tell(void *user_data
)
42 std::istream
*stream
= static_cast<std::istream
*>(user_data
);
44 return stream
->tellg();
47 static int close(void*)
53 class VorbisFileDecoder final
: public Decoder
{
54 UniquePtr
<std::istream
> mFile
;
56 UniquePtr
<OggVorbis_File
> mOggFile
;
57 vorbis_info
*mVorbisInfo
;
60 ChannelConfig mChannelConfig
;
62 std::pair
<uint64_t,uint64_t> mLoopPoints
;
65 VorbisFileDecoder(UniquePtr
<std::istream
> file
, UniquePtr
<OggVorbis_File
> oggfile
,
66 vorbis_info
*vorbisinfo
, ChannelConfig sconfig
,
67 std::pair
<uint64_t,uint64_t> loop_points
) noexcept
68 : mFile(std::move(file
)), mOggFile(std::move(oggfile
)), mVorbisInfo(vorbisinfo
)
69 , mOggBitstream(0), mChannelConfig(sconfig
), mLoopPoints(loop_points
)
71 ~VorbisFileDecoder() override
;
73 ALuint
getFrequency() const noexcept override
;
74 ChannelConfig
getChannelConfig() const noexcept override
;
75 SampleType
getSampleType() const noexcept override
;
77 uint64_t getLength() const noexcept override
;
78 bool seek(uint64_t pos
) noexcept override
;
80 std::pair
<uint64_t,uint64_t> getLoopPoints() const noexcept override
;
82 ALuint
read(ALvoid
*ptr
, ALuint count
) noexcept override
;
85 VorbisFileDecoder::~VorbisFileDecoder()
87 ov_clear(mOggFile
.get());
91 ALuint
VorbisFileDecoder::getFrequency() const noexcept
93 return mVorbisInfo
->rate
;
96 ChannelConfig
VorbisFileDecoder::getChannelConfig() const noexcept
98 return mChannelConfig
;
101 SampleType
VorbisFileDecoder::getSampleType() const noexcept
103 return SampleType::Int16
;
107 uint64_t VorbisFileDecoder::getLength() const noexcept
109 ogg_int64_t len
= ov_pcm_total(mOggFile
.get(), -1);
110 return std::max
<ogg_int64_t
>(len
, 0);
113 bool VorbisFileDecoder::seek(uint64_t pos
) noexcept
115 return ov_pcm_seek(mOggFile
.get(), pos
) == 0;
118 std::pair
<uint64_t,uint64_t> VorbisFileDecoder::getLoopPoints() const noexcept
123 ALuint
VorbisFileDecoder::read(ALvoid
*ptr
, ALuint count
) noexcept
126 ALshort
*samples
= (ALshort
*)ptr
;
129 int len
= (count
-total
) * mVorbisInfo
->channels
* 2;
130 #ifdef __BIG_ENDIAN__
131 long got
= ov_read(mOggFile
.get(), reinterpret_cast<char*>(samples
), len
, 1, 2, 1, &mOggBitstream
);
133 long got
= ov_read(mOggFile
.get(), reinterpret_cast<char*>(samples
), len
, 0, 2, 1, &mOggBitstream
);
139 got
/= mVorbisInfo
->channels
;
143 // 1, 2, and 4 channel files decode into the same channel order as
144 // OpenAL, however 6 (5.1), 7 (6.1), and 8 (7.1) channel files need to be
146 if(mChannelConfig
== ChannelConfig::X51
)
148 samples
= (ALshort
*)ptr
;
149 for(ALuint i
= 0;i
< total
;++i
)
151 // OpenAL : FL, FR, FC, LFE, RL, RR
152 // Vorbis : FL, FC, FR, RL, RR, LFE
153 std::swap(samples
[i
*6 + 1], samples
[i
*6 + 2]);
154 std::swap(samples
[i
*6 + 3], samples
[i
*6 + 5]);
155 std::swap(samples
[i
*6 + 4], samples
[i
*6 + 5]);
158 else if(mChannelConfig
== ChannelConfig::X61
)
160 samples
= (ALshort
*)ptr
;
161 for(ALuint i
= 0;i
< total
;++i
)
163 // OpenAL : FL, FR, FC, LFE, RC, SL, SR
164 // Vorbis : FL, FC, FR, SL, SR, RC, LFE
165 std::swap(samples
[i
*7 + 1], samples
[i
*7 + 2]);
166 std::swap(samples
[i
*7 + 3], samples
[i
*7 + 6]);
167 std::swap(samples
[i
*7 + 4], samples
[i
*7 + 5]);
168 std::swap(samples
[i
*7 + 5], samples
[i
*7 + 6]);
171 else if(mChannelConfig
== ChannelConfig::X71
)
173 samples
= (ALshort
*)ptr
;
174 for(ALuint i
= 0;i
< total
;++i
)
176 // OpenAL : FL, FR, FC, LFE, RL, RR, SL, SR
177 // Vorbis : FL, FC, FR, SL, SR, RL, RR, LFE
178 std::swap(samples
[i
*8 + 1], samples
[i
*8 + 2]);
179 std::swap(samples
[i
*8 + 3], samples
[i
*8 + 7]);
180 std::swap(samples
[i
*8 + 4], samples
[i
*8 + 5]);
181 std::swap(samples
[i
*8 + 5], samples
[i
*8 + 6]);
182 std::swap(samples
[i
*8 + 6], samples
[i
*8 + 7]);
190 SharedPtr
<Decoder
> VorbisFileDecoderFactory::createDecoder(UniquePtr
<std::istream
> &file
) noexcept
192 static const ov_callbacks streamIO
= {
193 read
, seek
, close
, tell
196 vorbis_info
*vorbisinfo
= nullptr;
197 auto oggfile
= MakeUnique
<OggVorbis_File
>();
198 if(ov_open_callbacks(file
.get(), oggfile
.get(), NULL
, 0, streamIO
) != 0)
201 vorbisinfo
= ov_info(oggfile
.get(), -1);
204 ov_clear(oggfile
.get());
208 std::pair
<uint64_t,uint64_t> loop_points
= { 0, std::numeric_limits
<uint64_t>::max() };
209 if(vorbis_comment
*vc
= ov_comment(oggfile
.get(), -1))
211 for(int i
= 0;i
< vc
->comments
;i
++)
213 auto seppos
= StringView(
214 vc
->user_comments
[i
], vc
->comment_lengths
[i
]
215 ).find_first_of('=');
216 if(seppos
== StringView::npos
) continue;
218 StringView
key(vc
->user_comments
[i
], seppos
);
219 StringView
val(vc
->user_comments
[i
]+seppos
+1, vc
->comment_lengths
[i
]-(seppos
+1));
221 // RPG Maker seems to recognize LOOPSTART and LOOPLENGTH for loop
222 // points in a Vorbis comment. ZDoom recognizes LOOP_START and
223 // LOOP_END. We can recognize both.
224 if(key
== "LOOP_START" || key
== "LOOPSTART")
226 auto pt
= ParseTimeval(val
, vorbisinfo
->rate
);
227 if(pt
.index() == 1) loop_points
.first
= std::get
<1>(pt
);
231 if(key
== "LOOP_END")
233 auto pt
= ParseTimeval(val
, vorbisinfo
->rate
);
234 if(pt
.index() == 1) loop_points
.second
= std::get
<1>(pt
);
238 if(key
== "LOOPLENGTH")
240 auto pt
= ParseTimeval(val
, vorbisinfo
->rate
);
242 loop_points
.second
= loop_points
.first
+ std::get
<1>(pt
);
248 ChannelConfig channels
= ChannelConfig::Mono
;
249 if(vorbisinfo
->channels
== 1)
250 channels
= ChannelConfig::Mono
;
251 else if(vorbisinfo
->channels
== 2)
252 channels
= ChannelConfig::Stereo
;
253 else if(vorbisinfo
->channels
== 4)
254 channels
= ChannelConfig::Quad
;
255 else if(vorbisinfo
->channels
== 6)
256 channels
= ChannelConfig::X51
;
257 else if(vorbisinfo
->channels
== 7)
258 channels
= ChannelConfig::X61
;
259 else if(vorbisinfo
->channels
== 8)
260 channels
= ChannelConfig::X71
;
263 ov_clear(oggfile
.get());
267 return MakeShared
<VorbisFileDecoder
>(
268 std::move(file
), std::move(oggfile
), vorbisinfo
, channels
, loop_points