Update some comment docs about thread-local contexts
[alure.git] / examples / alure-dumb.cpp
blobc039fc44f5a3226d68870fc86161dbf663a16d35
1 #ifdef _WIN32
2 #define WIN32_LEAN_AND_MEAN
3 #include <windows.h>
4 #else
5 #include <errno.h>
6 #include <time.h>
7 #include <stdint.h>
8 inline void Sleep(uint32_t ms)
10 struct timespec ts, rem;
11 ts.tv_sec = ms / 1000;
12 ts.tv_nsec = (ms % 1000) * 1000000;
13 while(nanosleep(&ts, &rem) == -1 && errno == EINTR)
14 ts = rem;
16 #endif
18 #include <iostream>
19 #include <iomanip>
20 #include <cstring>
21 #include <limits>
23 #include "dumb.h"
25 #include "alure2.h"
27 namespace
30 // Some I/O function callback wrappers for DUMB to read from an std::istream
31 static int cb_skip(void *user_data, long offset)
33 std::istream *stream = static_cast<std::istream*>(user_data);
34 stream->clear();
36 if(stream->seekg(offset, std::ios_base::cur))
37 return 0;
38 return 1;
41 static long cb_read(char *ptr, long size, void *user_data)
43 std::istream *stream = static_cast<std::istream*>(user_data);
44 stream->clear();
46 stream->read(ptr, size);
47 return stream->gcount();
50 static int cb_read_char(void *user_data)
52 std::istream *stream = static_cast<std::istream*>(user_data);
53 stream->clear();
55 unsigned char ret;
56 stream->read(reinterpret_cast<char*>(&ret), 1);
57 if(stream->gcount() > 0) return ret;
58 return -1;
61 static int cb_seek(void *user_data, long n)
63 std::istream *stream = static_cast<std::istream*>(user_data);
64 stream->clear();
66 if(!stream->seekg(n))
67 return 1;
68 return 0;
71 static long cb_get_size(void *user_data)
73 std::istream *stream = static_cast<std::istream*>(user_data);
74 stream->clear();
76 long len = -1;
77 std::streampos pos = stream->tellg();
78 if(pos != -1 && stream->seekg(0, std::ios::end))
80 len = stream->tellg();
81 stream->seekg(pos);
83 return len;
87 // Inherit from alure::Decoder to make a custom decoder (DUMB for this example)
88 class DumbDecoder : public alure::Decoder {
89 alure::SharedPtr<std::istream> mFile;
90 std::unique_ptr<DUMBFILE_SYSTEM> mDfs;
91 DUMBFILE *mDumbfile;
92 DUH *mDuh;
93 DUH_SIGRENDERER *mRenderer;
94 ALuint mFrequency;
95 std::vector<sample_t> mSampleBuf;
96 uint64_t mStreamPos;
98 public:
99 DumbDecoder(alure::SharedPtr<std::istream> file, std::unique_ptr<DUMBFILE_SYSTEM> &&dfs, DUMBFILE *dfile, DUH *duh, DUH_SIGRENDERER *renderer, ALuint freq)
100 : mFile(file), mDfs(std::move(dfs)), mDumbfile(dfile), mDuh(duh), mRenderer(renderer), mFrequency(freq),
101 mStreamPos(0)
103 virtual ~DumbDecoder()
105 duh_end_sigrenderer(mRenderer);
106 mRenderer = nullptr;
108 unload_duh(mDuh);
109 mDuh = nullptr;
111 dumbfile_close(mDumbfile);
112 mDumbfile = nullptr;
115 virtual ALuint getFrequency() const final
116 { return mFrequency; }
117 virtual alure::ChannelConfig getChannelConfig() const final
119 // We always have DUMB render to stereo
120 return alure::ChannelConfig_Stereo;
122 virtual alure::SampleType getSampleType() const final
124 // DUMB renders to 8.24 normalized fixed point, which we convert to
125 // signed 16-bit samples
126 return alure::SampleType_Int16;
129 virtual uint64_t getLength() final
131 // Modules have no explicit length, they just keep playing as long as
132 // more samples get generated.
133 return 0;
136 virtual uint64_t getPosition() final
138 return mStreamPos;
141 virtual bool seek(uint64_t) final
143 // Cannot seek
144 return false;
147 virtual std::pair<uint64_t,uint64_t> getLoopPoints() const final
149 // No loop points
150 return std::make_pair(0, 0);
153 virtual ALuint read(ALvoid *ptr, ALuint count) final
155 ALuint ret = 0;
157 mSampleBuf.resize(count*2);
158 sample_t *samples[] = {
159 mSampleBuf.data()
162 dumb_silence(samples[0], mSampleBuf.size());
163 ret = duh_sigrenderer_generate_samples(mRenderer, 1.0f, 65536.0f/mFrequency, count, samples);
164 for(ALuint i = 0;i < ret*2;i++)
166 sample_t smp = samples[0][i]>>8;
167 if(smp < -32768) smp = -32768;
168 else if(smp > 32767) smp = 32767;
169 ((ALshort*)ptr)[i] = smp;
171 mStreamPos += ret;
173 return ret;
177 // Inherit from alure::DecoderFactory to use our custom decoder
178 class DumbFactory : public alure::DecoderFactory {
179 virtual alure::SharedPtr<alure::Decoder> createDecoder(alure::SharedPtr<std::istream> file)
181 static DUH* (*funcs[])(DUMBFILE*) = {
182 dumb_read_it,
183 dumb_read_xm,
184 dumb_read_s3m,
185 nullptr
188 std::unique_ptr<DUMBFILE_SYSTEM> dfs(new DUMBFILE_SYSTEM);
189 dfs->open = nullptr;
190 dfs->skip = cb_skip;
191 dfs->getc = cb_read_char;
192 dfs->getnc = cb_read;
193 dfs->close = nullptr;
194 dfs->seek = cb_seek;
195 dfs->get_size = cb_get_size;
197 DUMBFILE *dfile = dumbfile_open_ex(file.get(), dfs.get());
198 if(!dfile) return nullptr;
200 ALuint freq = alure::Context::GetCurrent()->getDevice()->getFrequency();
201 DUH_SIGRENDERER *renderer;
202 DUH *duh;
204 for(size_t i = 0;funcs[i];i++)
206 if((duh=funcs[i](dfile)) != nullptr)
208 if((renderer=duh_start_sigrenderer(duh, 0, 2, 0)) != nullptr)
209 return alure::SharedPtr<alure::Decoder>(new DumbDecoder(
210 file, std::move(dfs), dfile, duh, renderer, freq
213 unload_duh(duh);
214 duh = nullptr;
217 dumbfile_seek(dfile, 0, SEEK_SET);
220 if((duh=dumb_read_mod(dfile, 1)) != nullptr)
222 if((renderer=duh_start_sigrenderer(duh, 0, 2, 0)) != nullptr)
223 return alure::SharedPtr<alure::Decoder>(new DumbDecoder(
224 file, std::move(dfs), dfile, duh, renderer, freq
227 unload_duh(duh);
228 duh = nullptr;
231 dumbfile_close(dfile);
232 return nullptr;
236 } // namespace
239 int main(int argc, char *argv[])
241 // Set our custom factory for decoding modules (Alure keeps a reference to the factory
242 // instance).
243 alure::RegisterDecoder("dumb", alure::SharedPtr<alure::DecoderFactory>(new DumbFactory));
245 alure::DeviceManager *devMgr = alure::DeviceManager::get();
247 alure::Device *dev = devMgr->openPlayback();
248 std::cout<< "Opened \""<<dev->getName(alure::PlaybackDevType_Basic)<<"\"" <<std::endl;
250 alure::Context *ctx = dev->createContext();
251 alure::Context::MakeCurrent(ctx);
253 for(int i = 1;i < argc;i++)
255 alure::SharedPtr<alure::Decoder> decoder(ctx->createDecoder(argv[i]));
256 alure::Source *source = ctx->getSource();
257 source->play(decoder, 32768, 4);
258 std::cout<< "Playing "<<argv[i]<<" ("<<alure::GetSampleTypeName(decoder->getSampleType())<<", "
259 <<alure::GetChannelConfigName(decoder->getChannelConfig())<<", "
260 <<decoder->getFrequency()<<"hz)" <<std::endl;
262 float invfreq = 1.0f / decoder->getFrequency();
263 while(source->isPlaying())
265 std::cout<< "\r "<<std::setiosflags(std::ios::fixed)<<std::setprecision(2)<<
266 (source->getOffset()*invfreq)<<" / "<<(decoder->getLength()*invfreq);
267 std::cout.flush();
268 Sleep(25);
269 ctx->update();
271 std::cout<<std::endl;
273 source->release();
274 source = nullptr;
275 decoder.reset();
278 alure::Context::MakeCurrent(nullptr);
279 ctx->destroy();
280 ctx = 0;
281 dev->close();
282 dev = 0;
284 return 0;