11 constexpr alure::Array
<int,1> CHANNELS_MONO
{{SF_CHANNEL_MAP_MONO
}};
12 constexpr alure::Array
<int,2> CHANNELS_STEREO
{{SF_CHANNEL_MAP_LEFT
, SF_CHANNEL_MAP_RIGHT
}};
13 constexpr alure::Array
<int,2> CHANNELS_REAR
{{SF_CHANNEL_MAP_REAR_LEFT
, SF_CHANNEL_MAP_REAR_RIGHT
}};
14 constexpr alure::Array
<int,4> CHANNELS_QUAD
{{SF_CHANNEL_MAP_LEFT
, SF_CHANNEL_MAP_RIGHT
, SF_CHANNEL_MAP_REAR_LEFT
, SF_CHANNEL_MAP_REAR_RIGHT
}};
15 constexpr alure::Array
<int,6> CHANNELS_5DOT1
{{SF_CHANNEL_MAP_LEFT
, SF_CHANNEL_MAP_RIGHT
, SF_CHANNEL_MAP_CENTER
, SF_CHANNEL_MAP_LFE
, SF_CHANNEL_MAP_SIDE_LEFT
, SF_CHANNEL_MAP_SIDE_RIGHT
}};
16 constexpr alure::Array
<int,6> CHANNELS_5DOT1_REAR
{{SF_CHANNEL_MAP_LEFT
, SF_CHANNEL_MAP_RIGHT
, SF_CHANNEL_MAP_CENTER
, SF_CHANNEL_MAP_LFE
, SF_CHANNEL_MAP_REAR_LEFT
, SF_CHANNEL_MAP_REAR_RIGHT
}};
17 constexpr alure::Array
<int,7> CHANNELS_6DOT1
{{SF_CHANNEL_MAP_LEFT
, SF_CHANNEL_MAP_RIGHT
, SF_CHANNEL_MAP_CENTER
, SF_CHANNEL_MAP_LFE
, SF_CHANNEL_MAP_REAR_CENTER
, SF_CHANNEL_MAP_SIDE_LEFT
, SF_CHANNEL_MAP_SIDE_RIGHT
}};
18 constexpr alure::Array
<int,8> CHANNELS_7DOT1
{{SF_CHANNEL_MAP_LEFT
, SF_CHANNEL_MAP_RIGHT
, SF_CHANNEL_MAP_CENTER
, SF_CHANNEL_MAP_LFE
, SF_CHANNEL_MAP_REAR_LEFT
, SF_CHANNEL_MAP_REAR_RIGHT
, SF_CHANNEL_MAP_SIDE_LEFT
, SF_CHANNEL_MAP_SIDE_RIGHT
}};
19 constexpr alure::Array
<int,3> CHANNELS_BFORMAT2D
{{SF_CHANNEL_MAP_AMBISONIC_B_W
, SF_CHANNEL_MAP_AMBISONIC_B_X
, SF_CHANNEL_MAP_AMBISONIC_B_Y
}};
20 constexpr alure::Array
<int,4> CHANNELS_BFORMAT3D
{{SF_CHANNEL_MAP_AMBISONIC_B_W
, SF_CHANNEL_MAP_AMBISONIC_B_X
, SF_CHANNEL_MAP_AMBISONIC_B_Y
, SF_CHANNEL_MAP_AMBISONIC_B_Z
}};
23 sf_count_t
istream_get_filelen(void *user_data
)
25 std::istream
*file
= reinterpret_cast<std::istream
*>(user_data
);
29 std::streampos pos
= file
->tellg();
30 if(pos
!= static_cast<std::streampos
>(-1) && file
->seekg(0, std::ios::end
))
38 sf_count_t
istream_seek(sf_count_t offset
, int whence
, void *user_data
)
40 std::istream
*file
= reinterpret_cast<std::istream
*>(user_data
);
43 if(!file
->seekg(offset
, std::ios::seekdir(whence
)))
48 sf_count_t
istream_read(void *ptr
, sf_count_t count
, void *user_data
)
50 std::istream
*file
= reinterpret_cast<std::istream
*>(user_data
);
53 file
->read(reinterpret_cast<char*>(ptr
), count
);
54 return file
->gcount();
57 sf_count_t
istream_write(const void*, sf_count_t
, void*)
62 sf_count_t
istream_tell(void *user_data
)
64 std::istream
*file
= reinterpret_cast<std::istream
*>(user_data
);
71 struct SndfileDeleter
{
72 void operator()(SNDFILE
*ptr
) const { sf_close(ptr
); }
74 using SndfilePtr
= alure::UniquePtr
<SNDFILE
,SndfileDeleter
>;
80 class SndFileDecoder final
: public Decoder
{
81 UniquePtr
<std::istream
> mFile
;
86 ChannelConfig mChannelConfig
{ChannelConfig::Mono
};
87 SampleType mSampleType
{SampleType::UInt8
};
88 std::pair
<uint64_t, uint64_t> mLoopPts
{0, 0};
91 SndFileDecoder(UniquePtr
<std::istream
> file
, SndfilePtr sndfile
, const SF_INFO
&sndinfo
,
92 ChannelConfig sconfig
, SampleType stype
, uint64_t loopstart
, uint64_t loopend
) noexcept
93 : mFile(std::move(file
)), mSndFile(std::move(sndfile
)), mSndInfo(sndinfo
)
94 , mChannelConfig(sconfig
), mSampleType(stype
), mLoopPts
{loopstart
, loopend
}
96 ~SndFileDecoder() override
{ }
98 ALuint
getFrequency() const noexcept override
;
99 ChannelConfig
getChannelConfig() const noexcept override
;
100 SampleType
getSampleType() const noexcept override
;
102 uint64_t getLength() const noexcept override
;
103 bool seek(uint64_t pos
) noexcept override
;
105 std::pair
<uint64_t,uint64_t> getLoopPoints() const noexcept override
;
107 ALuint
read(ALvoid
*ptr
, ALuint count
) noexcept override
;
110 ALuint
SndFileDecoder::getFrequency() const noexcept
{ return mSndInfo
.samplerate
; }
111 ChannelConfig
SndFileDecoder::getChannelConfig() const noexcept
{ return mChannelConfig
; }
112 SampleType
SndFileDecoder::getSampleType() const noexcept
{ return mSampleType
; }
114 uint64_t SndFileDecoder::getLength() const noexcept
116 return std::max
<sf_count_t
>(mSndInfo
.frames
, 0);
119 bool SndFileDecoder::seek(uint64_t pos
) noexcept
121 sf_count_t newpos
= sf_seek(mSndFile
.get(), pos
, SEEK_SET
);
122 if(newpos
< 0) return false;
126 std::pair
<uint64_t, uint64_t> SndFileDecoder::getLoopPoints() const noexcept
{ return mLoopPts
; }
128 ALuint
SndFileDecoder::read(ALvoid
*ptr
, ALuint count
) noexcept
133 case SampleType::Mulaw
:
134 case SampleType::UInt8
:
135 got
= sf_read_raw(mSndFile
.get(), static_cast<ALubyte
*>(ptr
),
136 FramesToBytes(count
, mChannelConfig
, mSampleType
));
137 got
= BytesToFrames(got
, mChannelConfig
, mSampleType
);
139 case SampleType::Int16
:
140 got
= sf_readf_short(mSndFile
.get(), static_cast<short *>(ptr
), count
);
142 case SampleType::Float32
:
143 got
= sf_readf_float(mSndFile
.get(), static_cast<float *>(ptr
), count
);
146 return (ALuint
)std::max
<sf_count_t
>(got
, 0);
150 SharedPtr
<Decoder
> SndFileDecoderFactory::createDecoder(UniquePtr
<std::istream
> &file
) noexcept
152 SF_VIRTUAL_IO vio
= {
153 istream_get_filelen
, istream_seek
,
154 istream_read
, istream_write
, istream_tell
157 SndfilePtr
sndfile(sf_open_virtual(&vio
, SFM_READ
, &sndinfo
, file
.get()));
158 if(!sndfile
) return nullptr;
160 std::pair
<uint64_t, uint64_t> cue_points
{0, std::numeric_limits
<uint64_t>::max()};
162 // Needed for compatibility with older sndfile libraries.
163 struct SNDFILE_CUE_POINT
{
169 uint32_t sample_offset
;
175 SNDFILE_CUE_POINT cue_points
[100];
178 enum { SNDFILE_GET_CUE
= 0x10CE };
180 if(sf_command(sndfile
.get(), SNDFILE_GET_CUE
, &cues
, sizeof(cues
)))
182 cue_points
.first
= cues
.cue_points
[0].sample_offset
;
183 if(cues
.cue_count
> 1)
185 cue_points
.second
= cues
.cue_points
[1].sample_offset
;
190 ChannelConfig sconfig
;
191 Vector
<int> chanmap(sndinfo
.channels
);
192 if(sf_command(sndfile
.get(), SFC_GET_CHANNEL_MAP_INFO
, chanmap
.data(), chanmap
.size()*sizeof(int)) == SF_TRUE
)
194 auto matches
= [](const Vector
<int> &first
, ArrayView
<int> second
) -> bool
196 return (first
.size() == second
.size()) &&
197 std::equal(first
.begin(), first
.end(), second
.begin());
200 if(matches(chanmap
, CHANNELS_MONO
))
201 sconfig
= ChannelConfig::Mono
;
202 else if(matches(chanmap
, CHANNELS_STEREO
))
203 sconfig
= ChannelConfig::Stereo
;
204 else if(matches(chanmap
, CHANNELS_REAR
))
205 sconfig
= ChannelConfig::Rear
;
206 else if(matches(chanmap
, CHANNELS_QUAD
))
207 sconfig
= ChannelConfig::Quad
;
208 else if(matches(chanmap
, CHANNELS_5DOT1
) || matches(chanmap
, CHANNELS_5DOT1_REAR
))
209 sconfig
= ChannelConfig::X51
;
210 else if(matches(chanmap
, CHANNELS_6DOT1
))
211 sconfig
= ChannelConfig::X61
;
212 else if(matches(chanmap
, CHANNELS_7DOT1
))
213 sconfig
= ChannelConfig::X71
;
214 else if(matches(chanmap
, CHANNELS_BFORMAT2D
))
215 sconfig
= ChannelConfig::BFormat2D
;
216 else if(matches(chanmap
, CHANNELS_BFORMAT3D
))
217 sconfig
= ChannelConfig::BFormat3D
;
221 else if(sf_command(sndfile
.get(), SFC_WAVEX_GET_AMBISONIC
, nullptr, 0) == SF_AMBISONIC_B_FORMAT
)
223 if(sndinfo
.channels
== 3)
224 sconfig
= ChannelConfig::BFormat2D
;
225 else if(sndinfo
.channels
== 4)
226 sconfig
= ChannelConfig::BFormat3D
;
230 else if(sndinfo
.channels
== 1)
231 sconfig
= ChannelConfig::Mono
;
232 else if(sndinfo
.channels
== 2)
233 sconfig
= ChannelConfig::Stereo
;
237 SampleType stype
= SampleType::Int16
;
238 switch(sndinfo
.format
&SF_FORMAT_SUBMASK
)
240 case SF_FORMAT_PCM_U8
:
241 stype
= SampleType::UInt8
;
244 if(Context::GetCurrent().isSupported(sconfig
, SampleType::Mulaw
))
245 stype
= SampleType::Mulaw
;
247 case SF_FORMAT_FLOAT
:
248 case SF_FORMAT_DOUBLE
:
249 case SF_FORMAT_VORBIS
:
250 if(Context::GetCurrent().isSupported(sconfig
, SampleType::Float32
))
251 stype
= SampleType::Float32
;
254 // For everything else, decode to signed 16-bit
255 stype
= SampleType::Int16
;
259 return MakeShared
<SndFileDecoder
>(std::move(file
), std::move(sndfile
), sndinfo
, sconfig
, stype
,
260 cue_points
.first
, cue_points
.second
);