2 * An example showing how to use an external decoder to play files through the
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
);
26 if(stream
->seekg(offset
, std::ios_base::cur
))
31 static long cb_read(char *ptr
, long size
, void *user_data
)
33 std::istream
*stream
= static_cast<std::istream
*>(user_data
);
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
);
46 stream
->read(reinterpret_cast<char*>(&ret
), 1);
47 if(stream
->gcount() > 0) return ret
;
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};
59 DUH_SIGRENDERER
*mRenderer
{nullptr};
61 alure::SampleType mSampleType
{alure::SampleType::UInt8
};
64 alure::Vector
<sample_t
> mSampleBuf
;
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
)
73 ~DumbDecoder() override
75 duh_end_sigrenderer(mRenderer
);
81 dumbfile_close(mDumbfile
);
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
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.
106 bool seek(uint64_t) noexcept override
112 std::pair
<uint64_t,uint64_t> getLoopPoints() const noexcept override
115 return std::make_pair(0, 0);
118 ALuint
read(ALvoid
*ptr
, ALuint count
) noexcept override
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
,
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
);
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;
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
));
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;
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
191 dumbfile_close(dfile
);
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();
215 if(argc
> 3 && strcmp(argv
[1], "-device") == 0)
218 dev
= devMgr
.openPlayback(argv
[2], std::nothrow
);
220 std::cerr
<< "Failed to open \""<<argv
[2]<<"\" - trying default" <<std::endl
;
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
);
244 std::this_thread::sleep_for(std::chrono::milliseconds(25));
247 std::cout
<<std::endl
;
253 alure::Context::MakeCurrent(nullptr);