2 #include "vorbisfile.hpp"
8 #include "vorbis/vorbisfile.h"
12 int istream_seek(void *user_data
, ogg_int64_t offset
, int whence
)
14 std::istream
*stream
= static_cast<std::istream
*>(user_data
);
17 if(whence
== SEEK_CUR
)
18 stream
->seekg(offset
, std::ios_base::cur
);
19 else if(whence
== SEEK_SET
)
20 stream
->seekg(offset
, std::ios_base::beg
);
21 else if(whence
== SEEK_END
)
22 stream
->seekg(offset
, std::ios_base::end
);
26 return stream
->tellg();
29 size_t istream_read(void *ptr
, size_t size
, size_t nmemb
, void *user_data
)
31 std::istream
*stream
= static_cast<std::istream
*>(user_data
);
34 stream
->read(static_cast<char*>(ptr
), nmemb
*size
);
35 size_t ret
= stream
->gcount();
39 long istream_tell(void *user_data
)
41 std::istream
*stream
= static_cast<std::istream
*>(user_data
);
43 return stream
->tellg();
46 int istream_close(void*) { return 0; }
49 struct OggVorbisfileHolder
: public OggVorbis_File
{
50 OggVorbisfileHolder() { this->datasource
= nullptr; }
51 ~OggVorbisfileHolder() { if(this->datasource
) ov_clear(this); }
53 using OggVorbisfilePtr
= alure::UniquePtr
<OggVorbisfileHolder
>;
59 class VorbisFileDecoder final
: public Decoder
{
60 UniquePtr
<std::istream
> mFile
;
62 OggVorbisfilePtr mOggFile
;
63 vorbis_info
*mVorbisInfo
{nullptr};
66 ChannelConfig mChannelConfig
{ChannelConfig::Mono
};
68 std::pair
<uint64_t,uint64_t> mLoopPoints
{0, 0};
71 VorbisFileDecoder(UniquePtr
<std::istream
> file
, OggVorbisfilePtr oggfile
,
72 vorbis_info
*vorbisinfo
, ChannelConfig sconfig
,
73 std::pair
<uint64_t,uint64_t> loop_points
) noexcept
74 : mFile(std::move(file
)), mOggFile(std::move(oggfile
)), mVorbisInfo(vorbisinfo
)
75 , mChannelConfig(sconfig
), mLoopPoints(loop_points
)
77 ~VorbisFileDecoder() override
{ }
79 ALuint
getFrequency() const noexcept override
;
80 ChannelConfig
getChannelConfig() const noexcept override
;
81 SampleType
getSampleType() const noexcept override
;
83 uint64_t getLength() const noexcept override
;
84 bool seek(uint64_t pos
) noexcept override
;
86 std::pair
<uint64_t,uint64_t> getLoopPoints() const noexcept override
;
88 ALuint
read(ALvoid
*ptr
, ALuint count
) noexcept override
;
91 ALuint
VorbisFileDecoder::getFrequency() const noexcept
{ return mVorbisInfo
->rate
; }
92 ChannelConfig
VorbisFileDecoder::getChannelConfig() const noexcept
{ return mChannelConfig
; }
93 SampleType
VorbisFileDecoder::getSampleType() const noexcept
{ return SampleType::Int16
; }
95 uint64_t VorbisFileDecoder::getLength() const noexcept
97 ogg_int64_t len
= ov_pcm_total(mOggFile
.get(), -1);
98 return std::max
<ogg_int64_t
>(len
, 0);
101 bool VorbisFileDecoder::seek(uint64_t pos
) noexcept
103 return ov_pcm_seek(mOggFile
.get(), pos
) == 0;
106 std::pair
<uint64_t,uint64_t> VorbisFileDecoder::getLoopPoints() const noexcept
111 ALuint
VorbisFileDecoder::read(ALvoid
*ptr
, ALuint count
) noexcept
114 ALshort
*samples
= (ALshort
*)ptr
;
117 int len
= (count
-total
) * mVorbisInfo
->channels
* 2;
118 #ifdef __BIG_ENDIAN__
119 long got
= ov_read(mOggFile
.get(), reinterpret_cast<char*>(samples
), len
, 1, 2, 1, &mOggBitstream
);
121 long got
= ov_read(mOggFile
.get(), reinterpret_cast<char*>(samples
), len
, 0, 2, 1, &mOggBitstream
);
127 got
/= mVorbisInfo
->channels
;
131 // 1, 2, and 4 channel files decode into the same channel order as
132 // OpenAL, however 6 (5.1), 7 (6.1), and 8 (7.1) channel files need to be
134 if(mChannelConfig
== ChannelConfig::X51
)
136 samples
= (ALshort
*)ptr
;
137 for(ALuint i
= 0;i
< total
;++i
)
139 // OpenAL : FL, FR, FC, LFE, RL, RR
140 // Vorbis : FL, FC, FR, RL, RR, LFE
141 std::swap(samples
[i
*6 + 1], samples
[i
*6 + 2]);
142 std::swap(samples
[i
*6 + 3], samples
[i
*6 + 5]);
143 std::swap(samples
[i
*6 + 4], samples
[i
*6 + 5]);
146 else if(mChannelConfig
== ChannelConfig::X61
)
148 samples
= (ALshort
*)ptr
;
149 for(ALuint i
= 0;i
< total
;++i
)
151 // OpenAL : FL, FR, FC, LFE, RC, SL, SR
152 // Vorbis : FL, FC, FR, SL, SR, RC, LFE
153 std::swap(samples
[i
*7 + 1], samples
[i
*7 + 2]);
154 std::swap(samples
[i
*7 + 3], samples
[i
*7 + 6]);
155 std::swap(samples
[i
*7 + 4], samples
[i
*7 + 5]);
156 std::swap(samples
[i
*7 + 5], samples
[i
*7 + 6]);
159 else if(mChannelConfig
== ChannelConfig::X71
)
161 samples
= (ALshort
*)ptr
;
162 for(ALuint i
= 0;i
< total
;++i
)
164 // OpenAL : FL, FR, FC, LFE, RL, RR, SL, SR
165 // Vorbis : FL, FC, FR, SL, SR, RL, RR, LFE
166 std::swap(samples
[i
*8 + 1], samples
[i
*8 + 2]);
167 std::swap(samples
[i
*8 + 3], samples
[i
*8 + 7]);
168 std::swap(samples
[i
*8 + 4], samples
[i
*8 + 5]);
169 std::swap(samples
[i
*8 + 5], samples
[i
*8 + 6]);
170 std::swap(samples
[i
*8 + 6], samples
[i
*8 + 7]);
178 SharedPtr
<Decoder
> VorbisFileDecoderFactory::createDecoder(UniquePtr
<std::istream
> &file
) noexcept
180 static const ov_callbacks streamIO
= {
181 istream_read
, istream_seek
, istream_close
, istream_tell
184 auto oggfile
= MakeUnique
<OggVorbisfilePtr::element_type
>();
185 if(ov_open_callbacks(file
.get(), oggfile
.get(), NULL
, 0, streamIO
) != 0)
188 vorbis_info
*vorbisinfo
= ov_info(oggfile
.get(), -1);
189 if(!vorbisinfo
) return nullptr;
191 std::pair
<uint64_t,uint64_t> loop_points
= { 0, std::numeric_limits
<uint64_t>::max() };
192 if(vorbis_comment
*vc
= ov_comment(oggfile
.get(), -1))
194 for(int i
= 0;i
< vc
->comments
;i
++)
196 StringView
val(vc
->user_comments
[i
], vc
->comment_lengths
[i
]);
197 auto seppos
= val
.find_first_of('=');
198 if(seppos
== StringView::npos
) continue;
200 StringView key
= val
.substr(0, seppos
);
201 val
= val
.substr(seppos
+1);
203 // RPG Maker seems to recognize LOOPSTART and LOOPLENGTH for loop
204 // points in a Vorbis comment. ZDoom recognizes LOOP_START and
205 // LOOP_END. We can recognize both.
206 if(key
== "LOOP_START" || key
== "LOOPSTART")
208 auto pt
= ParseTimeval(val
, vorbisinfo
->rate
);
209 if(pt
.index() == 1) loop_points
.first
= std::get
<1>(pt
);
213 if(key
== "LOOP_END")
215 auto pt
= ParseTimeval(val
, vorbisinfo
->rate
);
216 if(pt
.index() == 1) loop_points
.second
= std::get
<1>(pt
);
220 if(key
== "LOOPLENGTH")
222 auto pt
= ParseTimeval(val
, vorbisinfo
->rate
);
224 loop_points
.second
= loop_points
.first
+ std::get
<1>(pt
);
230 ChannelConfig channels
= ChannelConfig::Mono
;
231 if(vorbisinfo
->channels
== 1)
232 channels
= ChannelConfig::Mono
;
233 else if(vorbisinfo
->channels
== 2)
234 channels
= ChannelConfig::Stereo
;
235 else if(vorbisinfo
->channels
== 4)
236 channels
= ChannelConfig::Quad
;
237 else if(vorbisinfo
->channels
== 6)
238 channels
= ChannelConfig::X51
;
239 else if(vorbisinfo
->channels
== 7)
240 channels
= ChannelConfig::X61
;
241 else if(vorbisinfo
->channels
== 8)
242 channels
= ChannelConfig::X71
;
246 return MakeShared
<VorbisFileDecoder
>(
247 std::move(file
), std::move(oggfile
), vorbisinfo
, channels
, loop_points