Avoid unnecessary templates
[alure.git] / examples / alure-dumb.cpp
blobf2fb1ff600649cb3e42165c2dd4207de0cb88526
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;
52 // Inherit from alure::Decoder to make a custom decoder (DUMB for this example)
53 class DumbDecoder final : public alure::Decoder {
54 alure::UniquePtr<std::istream> mFile;
56 alure::UniquePtr<DUMBFILE_SYSTEM> mDfs;
57 DUMBFILE *mDumbfile{nullptr};
58 DUH *mDuh{nullptr};
59 DUH_SIGRENDERER *mRenderer{nullptr};
61 alure::SampleType mSampleType{alure::SampleType::UInt8};
62 ALuint mFrequency{0};
64 alure::Vector<sample_t> mSampleBuf;
66 public:
67 DumbDecoder(alure::UniquePtr<std::istream> file, alure::UniquePtr<DUMBFILE_SYSTEM> dfs,
68 DUMBFILE *dfile, DUH *duh, DUH_SIGRENDERER *renderer, alure::SampleType stype,
69 ALuint srate) noexcept
70 : mFile(std::move(file)), mDfs(std::move(dfs)), mDumbfile(dfile), mDuh(duh)
71 , mRenderer(renderer), mSampleType(stype), mFrequency(srate)
72 { }
73 ~DumbDecoder() override
75 duh_end_sigrenderer(mRenderer);
76 mRenderer = nullptr;
78 unload_duh(mDuh);
79 mDuh = nullptr;
81 dumbfile_close(mDumbfile);
82 mDumbfile = nullptr;
85 ALuint getFrequency() const noexcept override
86 { return mFrequency; }
87 alure::ChannelConfig getChannelConfig() const noexcept override
89 // We always have DUMB render to stereo
90 return alure::ChannelConfig::Stereo;
92 alure::SampleType getSampleType() const noexcept override
94 // DUMB renders to 8.24 normalized fixed point, which we convert to
95 // 32-bit float or signed 16-bit samples
96 return mSampleType;
99 uint64_t getLength() const noexcept override
101 // Modules have no explicit length, they just keep playing as long as
102 // more samples get generated.
103 return 0;
106 bool seek(uint64_t) noexcept override
108 // Cannot seek
109 return false;
112 std::pair<uint64_t,uint64_t> getLoopPoints() const noexcept override
114 // No loop points
115 return std::make_pair(0, 0);
118 ALuint read(ALvoid *ptr, ALuint count) noexcept override
120 ALuint ret = 0;
122 mSampleBuf.resize(count*2);
123 alure::Array<sample_t*,1> samples{{mSampleBuf.data()}};
125 dumb_silence(samples[0], mSampleBuf.size());
126 ret = duh_sigrenderer_generate_samples(mRenderer, 1.0f, 65536.0f/mFrequency, count,
127 samples.data());
128 if(mSampleType == alure::SampleType::Float32)
130 ALfloat *out = reinterpret_cast<ALfloat*>(ptr);
131 for(ALuint i = 0;i < ret*2;i++)
132 out[i] = (ALfloat)samples[0][i] * (1.0f/8388608.0f);
134 else
136 ALshort *out = reinterpret_cast<ALshort*>(ptr);
137 for(ALuint i = 0;i < ret*2;i++)
139 sample_t smp = samples[0][i]>>8;
140 if(smp < -32768) smp = -32768;
141 else if(smp > 32767) smp = 32767;
142 out[i] = smp;
146 return ret;
150 // Inherit from alure::DecoderFactory to use our custom decoder
151 class DumbFactory final : public alure::DecoderFactory {
152 alure::SharedPtr<alure::Decoder> createDecoder(alure::UniquePtr<std::istream> &file) noexcept override
154 static const alure::Array<DUH*(*)(DUMBFILE*),3> init_funcs{{
155 dumb_read_it, dumb_read_xm, dumb_read_s3m
158 auto dfs = alure::MakeUnique<DUMBFILE_SYSTEM>();
159 std::memset(dfs.get(), 0, sizeof(DUMBFILE_SYSTEM));
160 dfs->open = nullptr;
161 dfs->skip = cb_skip;
162 dfs->getc = cb_read_char;
163 dfs->getnc = cb_read;
164 dfs->close = nullptr;
166 alure::Context ctx = alure::Context::GetCurrent();
167 alure::SampleType stype = alure::SampleType::Float32;
168 if(!ctx.isSupported(alure::ChannelConfig::Stereo, stype))
169 stype = alure::SampleType::Int16;
170 ALuint freq = ctx.getDevice().getFrequency();
172 DUMBFILE *dfile = nullptr;
173 for(auto init : init_funcs)
175 dfile = dumbfile_open_ex(file.get(), dfs.get());
176 if(!dfile) return nullptr;
178 DUH *duh;
179 if((duh=init(dfile)) != nullptr)
181 DUH_SIGRENDERER *renderer;
182 if((renderer=duh_start_sigrenderer(duh, 0, 2, 0)) != nullptr)
183 return alure::MakeShared<DumbDecoder>(
184 std::move(file), std::move(dfs), dfile, duh, renderer, stype, freq
187 unload_duh(duh);
188 duh = nullptr;
191 dumbfile_close(dfile);
192 dfile = nullptr;
194 file->clear();
195 if(!file->seekg(0))
196 break;
199 return nullptr;
203 } // namespace
206 int main(int argc, char *argv[])
208 // Set our custom factory for decoding modules.
209 alure::RegisterDecoder("dumb", alure::MakeUnique<DumbFactory>());
211 alure::DeviceManager devMgr = alure::DeviceManager::getInstance();
213 int fileidx = 1;
214 alure::Device dev;
215 if(argc > 3 && strcmp(argv[1], "-device") == 0)
217 fileidx = 3;
218 dev = devMgr.openPlayback(argv[2], std::nothrow);
219 if(!dev)
220 std::cerr<< "Failed to open \""<<argv[2]<<"\" - trying default" <<std::endl;
222 if(!dev)
223 dev = devMgr.openPlayback();
224 std::cout<< "Opened \""<<dev.getName()<<"\"" <<std::endl;
226 alure::Context ctx = dev.createContext();
227 alure::Context::MakeCurrent(ctx);
229 for(int i = fileidx;i < argc;i++)
231 alure::SharedPtr<alure::Decoder> decoder(ctx.createDecoder(argv[i]));
232 alure::Source source = ctx.createSource();
233 source.play(decoder, 12000, 4);
234 std::cout<< "Playing "<<argv[i]<<" ("<<alure::GetSampleTypeName(decoder->getSampleType())<<", "
235 <<alure::GetChannelConfigName(decoder->getChannelConfig())<<", "
236 <<decoder->getFrequency()<<"hz)" <<std::endl;
238 float invfreq = 1.0f / decoder->getFrequency();
239 while(source.isPlaying())
241 std::cout<< "\r "<<std::fixed<<std::setprecision(2)<<
242 source.getSecOffset().count()<<" / "<<(decoder->getLength()*invfreq);
243 std::cout.flush();
244 std::this_thread::sleep_for(std::chrono::milliseconds(25));
245 ctx.update();
247 std::cout<<std::endl;
249 source.release();
250 decoder.reset();
253 alure::Context::MakeCurrent(nullptr);
254 ctx.destroy();
255 dev.close();
257 return 0;