Use a more sane streaming buffer length
[alure.git] / examples / alure-dumb.cpp
blob917c6acba26928c0dcc06f1ec925a61d58a9d3bf
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;
80 alure::UniquePtr<DUMBFILE_SYSTEM> mDfs;
81 DUMBFILE *mDumbfile;
82 DUH *mDuh;
83 DUH_SIGRENDERER *mRenderer;
84 ALuint mFrequency;
85 alure::Vector<sample_t> mSampleBuf;
86 uint64_t mStreamPos;
88 public:
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)
92 { }
93 ~DumbDecoder() override final
95 duh_end_sigrenderer(mRenderer);
96 mRenderer = nullptr;
98 unload_duh(mDuh);
99 mDuh = nullptr;
101 dumbfile_close(mDumbfile);
102 mDumbfile = nullptr;
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.
123 return 0;
126 uint64_t getPosition() const override final
128 return mStreamPos;
131 bool seek(uint64_t) override final
133 // Cannot seek
134 return false;
137 std::pair<uint64_t,uint64_t> getLoopPoints() const override final
139 // No loop points
140 return std::make_pair(0, 0);
143 ALuint read(ALvoid *ptr, ALuint count) override final
145 ALuint ret = 0;
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,
152 samples.data());
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;
160 mStreamPos += ret;
162 return ret;
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>();
175 dfs->open = nullptr;
176 dfs->skip = cb_skip;
177 dfs->getc = cb_read_char;
178 dfs->getnc = cb_read;
179 dfs->close = nullptr;
180 dfs->seek = cb_seek;
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;
188 DUH *duh;
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
199 unload_duh(duh);
200 duh = nullptr;
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
213 unload_duh(duh);
214 duh = nullptr;
217 dumbfile_close(dfile);
218 return nullptr;
222 } // namespace
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 int fileidx = 1;
233 alure::Device dev = [argc,argv,&devMgr,&fileidx]() -> alure::Device
235 if(argc > 3 && strcmp(argv[1], "-device") == 0)
237 fileidx = 3;
238 try {
239 return devMgr.openPlayback(argv[2]);
241 catch(...) {
242 std::cerr<< "Failed to open \""<<argv[2]<<"\" - trying default" <<std::endl;
245 return devMgr.openPlayback();
246 }();
247 std::cout<< "Opened \""<<dev.getName()<<"\"" <<std::endl;
249 alure::Context ctx = dev.createContext();
250 alure::Context::MakeCurrent(ctx);
252 for(int i = fileidx;i < argc;i++)
254 alure::SharedPtr<alure::Decoder> decoder(ctx.createDecoder(argv[i]));
255 alure::Source source = ctx.createSource();
256 source.play(decoder, 12000, 4);
257 std::cout<< "Playing "<<argv[i]<<" ("<<alure::GetSampleTypeName(decoder->getSampleType())<<", "
258 <<alure::GetChannelConfigName(decoder->getChannelConfig())<<", "
259 <<decoder->getFrequency()<<"hz)" <<std::endl;
261 float invfreq = 1.0f / decoder->getFrequency();
262 while(source.isPlaying())
264 std::cout<< "\r "<<std::setiosflags(std::ios::fixed)<<std::setprecision(2)<<
265 (source.getOffset()*invfreq)<<" / "<<(decoder->getLength()*invfreq);
266 std::cout.flush();
267 std::this_thread::sleep_for(std::chrono::milliseconds(25));
268 ctx.update();
270 std::cout<<std::endl;
272 source.release();
273 decoder.reset();
276 alure::Context::MakeCurrent(nullptr);
277 ctx.destroy();
278 dev.close();
280 return 0;