Constify some methods
[alure.git] / examples / alure-dumb.cpp
blob1ad4b2003bf5f39371b22209834e4179740a23bf
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 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);
252 std::cout.flush();
253 std::this_thread::sleep_for(std::chrono::milliseconds(25));
254 ctx->update();
256 std::cout<<std::endl;
258 source->release();
259 source = nullptr;
260 decoder.reset();
263 alure::Context::MakeCurrent(nullptr);
264 ctx->destroy();
265 ctx = 0;
266 dev->close();
267 dev = 0;
269 return 0;