List available files in the physfs example
[alure.git] / examples / alure-dumb.cpp
blob48f975ca8540979efa2065be2eadc73b6357b7d3
1 /*
2 * An example showing how to use an external decoder to play files through the
3 * DUMB library.
4 */
6 #include <iostream>
7 #include <iomanip>
8 #include <cstring>
9 #include <limits>
10 #include <thread>
11 #include <chrono>
13 #include "dumb.h"
15 #include "alure2.h"
17 namespace
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);
24 stream->clear();
26 if(stream->seekg(offset, std::ios_base::cur))
27 return 0;
28 return 1;
31 static long cb_read(char *ptr, long size, void *user_data)
33 std::istream *stream = static_cast<std::istream*>(user_data);
34 stream->clear();
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);
43 stream->clear();
45 unsigned char ret;
46 stream->read(reinterpret_cast<char*>(&ret), 1);
47 if(stream->gcount() > 0) return ret;
48 return -1;
51 static int cb_seek(void *user_data, long n)
53 std::istream *stream = static_cast<std::istream*>(user_data);
54 stream->clear();
56 if(!stream->seekg(n))
57 return 1;
58 return 0;
61 static long cb_get_size(void *user_data)
63 std::istream *stream = static_cast<std::istream*>(user_data);
64 stream->clear();
66 long len = -1;
67 std::streampos pos = stream->tellg();
68 if(pos != -1 && stream->seekg(0, std::ios::end))
70 len = stream->tellg();
71 stream->seekg(pos);
73 return len;
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;
81 alure::UniquePtr<DUMBFILE_SYSTEM> mDfs;
82 DUMBFILE *mDumbfile;
83 DUH *mDuh;
84 DUH_SIGRENDERER *mRenderer;
86 alure::SampleType mSampleType;
87 ALuint mFrequency;
89 alure::Vector<sample_t> mSampleBuf;
90 uint64_t mStreamPos;
92 public:
93 DumbDecoder(alure::UniquePtr<std::istream> file, alure::UniquePtr<DUMBFILE_SYSTEM> dfs,
94 DUMBFILE *dfile, DUH *duh, DUH_SIGRENDERER *renderer, alure::SampleType stype,
95 ALuint srate)
96 : mFile(std::move(file)), mDfs(std::move(dfs)), mDumbfile(dfile), mDuh(duh)
97 , mRenderer(renderer), mSampleType(stype), mFrequency(srate), mStreamPos(0)
98 { }
99 ~DumbDecoder() override final
101 duh_end_sigrenderer(mRenderer);
102 mRenderer = nullptr;
104 unload_duh(mDuh);
105 mDuh = nullptr;
107 dumbfile_close(mDumbfile);
108 mDumbfile = nullptr;
111 ALuint getFrequency() const override final
112 { return mFrequency; }
113 alure::ChannelConfig getChannelConfig() const override final
115 // We always have DUMB render to stereo
116 return alure::ChannelConfig::Stereo;
118 alure::SampleType getSampleType() const override final
120 // DUMB renders to 8.24 normalized fixed point, which we convert to
121 // 32-bit float or signed 16-bit samples
122 return mSampleType;
125 uint64_t getLength() const override final
127 // Modules have no explicit length, they just keep playing as long as
128 // more samples get generated.
129 return 0;
132 uint64_t getPosition() const override final
134 return mStreamPos;
137 bool seek(uint64_t) override final
139 // Cannot seek
140 return false;
143 std::pair<uint64_t,uint64_t> getLoopPoints() const override final
145 // No loop points
146 return std::make_pair(0, 0);
149 ALuint read(ALvoid *ptr, ALuint count) override final
151 ALuint ret = 0;
153 mSampleBuf.resize(count*2);
154 std::array<sample_t*,1> samples{{mSampleBuf.data()}};
156 dumb_silence(samples[0], mSampleBuf.size());
157 ret = duh_sigrenderer_generate_samples(mRenderer, 1.0f, 65536.0f/mFrequency, count,
158 samples.data());
159 if(mSampleType == alure::SampleType::Float32)
161 ALfloat *out = reinterpret_cast<ALfloat*>(ptr);
162 for(ALuint i = 0;i < ret*2;i++)
163 out[i] = (ALfloat)samples[0][i] * (1.0f/8388608.0f);
165 else
167 ALshort *out = reinterpret_cast<ALshort*>(ptr);
168 for(ALuint i = 0;i < ret*2;i++)
170 sample_t smp = samples[0][i]>>8;
171 if(smp < -32768) smp = -32768;
172 else if(smp > 32767) smp = 32767;
173 out[i] = smp;
176 mStreamPos += ret;
178 return ret;
182 // Inherit from alure::DecoderFactory to use our custom decoder
183 class DumbFactory : public alure::DecoderFactory {
184 alure::SharedPtr<alure::Decoder> createDecoder(alure::UniquePtr<std::istream> &file) override final
186 static const std::array<DUH*(*)(DUMBFILE*),3> init_funcs{{
187 dumb_read_it, dumb_read_xm, dumb_read_s3m
190 auto dfs = alure::MakeUnique<DUMBFILE_SYSTEM>();
191 dfs->open = nullptr;
192 dfs->skip = cb_skip;
193 dfs->getc = cb_read_char;
194 dfs->getnc = cb_read;
195 dfs->close = nullptr;
196 dfs->seek = cb_seek;
197 dfs->get_size = cb_get_size;
199 DUMBFILE *dfile = dumbfile_open_ex(file.get(), dfs.get());
200 if(!dfile) return nullptr;
202 alure::Context ctx = alure::Context::GetCurrent();
203 alure::SampleType stype = alure::SampleType::Float32;
204 if(!ctx.isSupported(alure::ChannelConfig::Stereo, stype))
205 stype = alure::SampleType::Int16;
206 ALuint freq = ctx.getDevice().getFrequency();
208 for(auto init : init_funcs)
210 DUH *duh;
211 if((duh=init(dfile)) != nullptr)
213 DUH_SIGRENDERER *renderer;
214 if((renderer=duh_start_sigrenderer(duh, 0, 2, 0)) != nullptr)
215 return alure::MakeShared<DumbDecoder>(
216 std::move(file), std::move(dfs), dfile, duh, renderer, stype, freq
219 unload_duh(duh);
220 duh = nullptr;
223 dumbfile_seek(dfile, 0, SEEK_SET);
226 DUH *duh;
227 if((duh=dumb_read_mod(dfile, 1)) != nullptr)
229 DUH_SIGRENDERER *renderer;
230 if((renderer=duh_start_sigrenderer(duh, 0, 2, 0)) != nullptr)
231 return alure::MakeShared<DumbDecoder>(
232 std::move(file), std::move(dfs), dfile, duh, renderer, stype, freq
235 unload_duh(duh);
236 duh = nullptr;
239 dumbfile_close(dfile);
240 return nullptr;
244 } // namespace
247 int main(int argc, char *argv[])
249 // Set our custom factory for decoding modules.
250 alure::RegisterDecoder("dumb", alure::MakeUnique<DumbFactory>());
252 alure::DeviceManager &devMgr = alure::DeviceManager::get();
254 int fileidx = 1;
255 alure::Device dev;
256 if(argc > 3 && strcmp(argv[1], "-device") == 0)
258 fileidx = 3;
259 dev = devMgr.openPlayback(argv[2], std::nothrow);
260 if(!dev)
261 std::cerr<< "Failed to open \""<<argv[2]<<"\" - trying default" <<std::endl;
263 if(!dev)
264 dev = devMgr.openPlayback();
265 std::cout<< "Opened \""<<dev.getName()<<"\"" <<std::endl;
267 alure::Context ctx = dev.createContext();
268 alure::Context::MakeCurrent(ctx);
270 for(int i = fileidx;i < argc;i++)
272 alure::SharedPtr<alure::Decoder> decoder(ctx.createDecoder(argv[i]));
273 alure::Source source = ctx.createSource();
274 source.play(decoder, 12000, 4);
275 std::cout<< "Playing "<<argv[i]<<" ("<<alure::GetSampleTypeName(decoder->getSampleType())<<", "
276 <<alure::GetChannelConfigName(decoder->getChannelConfig())<<", "
277 <<decoder->getFrequency()<<"hz)" <<std::endl;
279 float invfreq = 1.0f / decoder->getFrequency();
280 while(source.isPlaying())
282 std::cout<< "\r "<<std::setiosflags(std::ios::fixed)<<std::setprecision(2)<<
283 source.getSecOffset().count()<<" / "<<(decoder->getLength()*invfreq);
284 std::cout.flush();
285 std::this_thread::sleep_for(std::chrono::milliseconds(25));
286 ctx.update();
288 std::cout<<std::endl;
290 source.release();
291 decoder.reset();
294 alure::Context::MakeCurrent(nullptr);
295 ctx.destroy();
296 dev.close();
298 return 0;