2 * An example showing how to use an external decoder to play files through the
20 // Some I/O function callback wrappers for DUMB to read from an std::istream
21 static int cb_skip(void *user_data
, long offset
)
23 std::istream
*stream
= static_cast<std::istream
*>(user_data
);
26 if(stream
->seekg(offset
, std::ios_base::cur
))
31 static long cb_read(char *ptr
, long size
, void *user_data
)
33 std::istream
*stream
= static_cast<std::istream
*>(user_data
);
36 stream
->read(ptr
, size
);
37 return stream
->gcount();
40 static int cb_read_char(void *user_data
)
42 std::istream
*stream
= static_cast<std::istream
*>(user_data
);
46 stream
->read(reinterpret_cast<char*>(&ret
), 1);
47 if(stream
->gcount() > 0) return ret
;
51 static int cb_seek(void *user_data
, long n
)
53 std::istream
*stream
= static_cast<std::istream
*>(user_data
);
61 static long cb_get_size(void *user_data
)
63 std::istream
*stream
= static_cast<std::istream
*>(user_data
);
67 std::streampos pos
= stream
->tellg();
68 if(pos
!= -1 && stream
->seekg(0, std::ios::end
))
70 len
= stream
->tellg();
77 // Inherit from alure::Decoder to make a custom decoder (DUMB for this example)
78 class DumbDecoder
: public alure::Decoder
{
79 alure::UniquePtr
<std::istream
> mFile
;
80 alure::UniquePtr
<DUMBFILE_SYSTEM
> mDfs
;
83 DUH_SIGRENDERER
*mRenderer
;
85 alure::Vector
<sample_t
> mSampleBuf
;
89 DumbDecoder(alure::UniquePtr
<std::istream
> file
, alure::UniquePtr
<DUMBFILE_SYSTEM
> dfs
, DUMBFILE
*dfile
, DUH
*duh
, DUH_SIGRENDERER
*renderer
, ALuint freq
)
90 : mFile(std::move(file
)), mDfs(std::move(dfs
)), mDumbfile(dfile
), mDuh(duh
)
91 , mRenderer(renderer
), mFrequency(freq
), mStreamPos(0)
93 ~DumbDecoder() override final
95 duh_end_sigrenderer(mRenderer
);
101 dumbfile_close(mDumbfile
);
105 ALuint
getFrequency() const override final
106 { return mFrequency
; }
107 alure::ChannelConfig
getChannelConfig() const override final
109 // We always have DUMB render to stereo
110 return alure::ChannelConfig::Stereo
;
112 alure::SampleType
getSampleType() const override final
114 // DUMB renders to 8.24 normalized fixed point, which we convert to
115 // signed 16-bit samples
116 return alure::SampleType::Int16
;
119 uint64_t getLength() const override final
121 // Modules have no explicit length, they just keep playing as long as
122 // more samples get generated.
126 uint64_t getPosition() const override final
131 bool seek(uint64_t) override final
137 std::pair
<uint64_t,uint64_t> getLoopPoints() const override final
140 return std::make_pair(0, 0);
143 ALuint
read(ALvoid
*ptr
, ALuint count
) override final
147 mSampleBuf
.resize(count
*2);
148 std::array
<sample_t
*,1> samples
{{mSampleBuf
.data()}};
150 dumb_silence(samples
[0], mSampleBuf
.size());
151 ret
= duh_sigrenderer_generate_samples(mRenderer
, 1.0f
, 65536.0f
/mFrequency
, count
,
153 for(ALuint i
= 0;i
< ret
*2;i
++)
155 sample_t smp
= samples
[0][i
]>>8;
156 if(smp
< -32768) smp
= -32768;
157 else if(smp
> 32767) smp
= 32767;
158 ((ALshort
*)ptr
)[i
] = smp
;
166 // Inherit from alure::DecoderFactory to use our custom decoder
167 class DumbFactory
: public alure::DecoderFactory
{
168 alure::SharedPtr
<alure::Decoder
> createDecoder(alure::UniquePtr
<std::istream
> &file
) override final
170 static const std::array
<DUH
*(*)(DUMBFILE
*),3> init_funcs
{{
171 dumb_read_it
, dumb_read_xm
, dumb_read_s3m
174 auto dfs
= alure::MakeUnique
<DUMBFILE_SYSTEM
>();
177 dfs
->getc
= cb_read_char
;
178 dfs
->getnc
= cb_read
;
179 dfs
->close
= nullptr;
181 dfs
->get_size
= cb_get_size
;
183 DUMBFILE
*dfile
= dumbfile_open_ex(file
.get(), dfs
.get());
184 if(!dfile
) return nullptr;
186 ALuint freq
= alure::Context::GetCurrent()->getDevice()->getFrequency();
187 DUH_SIGRENDERER
*renderer
;
190 for(auto init
: init_funcs
)
192 if((duh
=init(dfile
)) != nullptr)
194 if((renderer
=duh_start_sigrenderer(duh
, 0, 2, 0)) != nullptr)
195 return alure::MakeShared
<DumbDecoder
>(
196 std::move(file
), std::move(dfs
), dfile
, duh
, renderer
, freq
203 dumbfile_seek(dfile
, 0, SEEK_SET
);
206 if((duh
=dumb_read_mod(dfile
, 1)) != nullptr)
208 if((renderer
=duh_start_sigrenderer(duh
, 0, 2, 0)) != nullptr)
209 return alure::MakeShared
<DumbDecoder
>(
210 std::move(file
), std::move(dfs
), dfile
, duh
, renderer
, freq
217 dumbfile_close(dfile
);
225 int main(int argc
, char *argv
[])
227 // Set our custom factory for decoding modules.
228 alure::RegisterDecoder("dumb", alure::MakeUnique
<DumbFactory
>());
230 alure::DeviceManager
&devMgr
= alure::DeviceManager::get();
232 alure::Device
*dev
= devMgr
.openPlayback();
233 std::cout
<< "Opened \""<<dev
->getName()<<"\"" <<std::endl
;
235 alure::Context
*ctx
= dev
->createContext();
236 alure::Context::MakeCurrent(ctx
);
238 for(int i
= 1;i
< argc
;i
++)
240 alure::SharedPtr
<alure::Decoder
> decoder(ctx
->createDecoder(argv
[i
]));
241 alure::Source
*source
= ctx
->createSource();
242 source
->play(decoder
, 32768, 4);
243 std::cout
<< "Playing "<<argv
[i
]<<" ("<<alure::GetSampleTypeName(decoder
->getSampleType())<<", "
244 <<alure::GetChannelConfigName(decoder
->getChannelConfig())<<", "
245 <<decoder
->getFrequency()<<"hz)" <<std::endl
;
247 float invfreq
= 1.0f
/ decoder
->getFrequency();
248 while(source
->isPlaying())
250 std::cout
<< "\r "<<std::setiosflags(std::ios::fixed
)<<std::setprecision(2)<<
251 (source
->getOffset()*invfreq
)<<" / "<<(decoder
->getLength()*invfreq
);
253 std::this_thread::sleep_for(std::chrono::milliseconds(25));
256 std::cout
<<std::endl
;
263 alure::Context::MakeCurrent(nullptr);