16 // Inherit from std::streambuf to handle custom I/O (PhysFS for this example)
17 class StreamBuf
: public std::streambuf
{
18 static const size_t sBufferSize
= 4096;
21 char mBuffer
[sBufferSize
];
23 virtual int_type
underflow()
25 if(mFile
&& gptr() == egptr())
27 // Read in the next chunk of data, and set the read pointers on
29 PHYSFS_sint64 got
= PHYSFS_read(mFile
, mBuffer
, sizeof(mBuffer
[0]), sBufferSize
);
30 if(got
!= -1) setg(mBuffer
, mBuffer
, mBuffer
+got
);
33 return traits_type::eof();
34 return (*gptr())&0xFF;
37 virtual pos_type
seekoff(off_type offset
, std::ios_base::seekdir whence
, std::ios_base::openmode mode
)
39 if(!mFile
|| (mode
&std::ios_base::out
) || !(mode
&std::ios_base::in
))
40 return traits_type::eof();
42 // PhysFS only seeks using absolute offsets, so we have to convert cur-
43 // and end-relative offsets.
47 case std::ios_base::beg
:
50 case std::ios_base::cur
:
51 // Need to offset for the read pointers with std::ios_base::cur
53 offset
-= off_type(egptr()-gptr());
54 if((fpos
=PHYSFS_tell(mFile
)) == -1)
55 return traits_type::eof();
59 case std::ios_base::end
:
60 if((fpos
=PHYSFS_fileLength(mFile
)) == -1)
61 return traits_type::eof();
66 return traits_type::eof();
68 // Range check - absolute offsets cannot be less than 0, nor be greater
69 // than PhysFS's offset type.
70 if(offset
< 0 || offset
>= std::numeric_limits
<PHYSFS_sint64
>::max())
71 return traits_type::eof();
72 if(PHYSFS_seek(mFile
, PHYSFS_sint64(offset
)) == 0)
73 return traits_type::eof();
74 // Clear read pointers so underflow() gets called on the next read
80 virtual pos_type
seekpos(pos_type pos
, std::ios_base::openmode mode
)
82 // Simplified version of seekoff
83 if(!mFile
|| (mode
&std::ios_base::out
) || !(mode
&std::ios_base::in
))
84 return traits_type::eof();
86 if(pos
< 0 || pos
>= std::numeric_limits
<PHYSFS_sint64
>::max())
87 return traits_type::eof();
88 if(PHYSFS_seek(mFile
, PHYSFS_sint64(pos
)) == 0)
89 return traits_type::eof();
95 bool open(const char *filename
)
97 mFile
= PHYSFS_openRead(filename
);
98 if(!mFile
) return false;
102 StreamBuf() : mFile(nullptr)
111 // Inherit from std::istream to use our custom streambuf
112 class Stream
: public std::istream
{
114 Stream(const char *filename
) : std::istream(new StreamBuf())
116 // Set the failbit if the file failed to open.
117 if(!(static_cast<StreamBuf
*>(rdbuf())->open(filename
)))
124 // Inherit from alure::FileIOFactory to use our custom istream
125 class FileFactory
: public alure::FileIOFactory
{
127 FileFactory(const char *argv0
)
129 // Need to initialize PhysFS before using it
130 if(PHYSFS_init(argv0
) == 0)
132 std::stringstream sstr
;
133 sstr
<< "Failed to initialize PhysFS: "<<PHYSFS_getLastError();
134 throw std::runtime_error(sstr
.str());
137 std::cout
<< "Initialized PhysFS, supported archive formats:" <<std::endl
;
138 for(const PHYSFS_ArchiveInfo
**i
= PHYSFS_supportedArchiveTypes();*i
!= NULL
;i
++)
139 std::cout
<< " "<<(*i
)->extension
<<": "<<(*i
)->description
<<std::endl
;
140 std::cout
<<std::endl
;
142 virtual ~FileFactory()
147 virtual alure::SharedPtr
<std::istream
> openFile(const alure::String
&name
)
149 alure::SharedPtr
<Stream
> stream(new Stream(name
.c_str()));
150 if(stream
->fail()) stream
.reset();
154 // A PhysFS-specific function to mount a new path to the virtual directory
156 static bool Mount(const char *path
, const char *mountPoint
=nullptr, int append
=0)
158 std::cout
<< "Adding new file source "<<path
;
159 if(mountPoint
) std::cout
<< " to "<<mountPoint
;
160 std::cout
<<"..."<<std::endl
;
162 if(PHYSFS_mount(path
, mountPoint
, append
) == 0)
164 std::cerr
<< "Failed to add "<<path
<<": "<<PHYSFS_getLastError() <<std::endl
;
174 int main(int argc
, char *argv
[])
178 std::cerr
<< "Usage: "<<argv
[0]<<" -add <directory | archive> file_entries ..." <<std::endl
;
182 // Set our custom factory for file IO (Alure holds a reference to the factory
183 // instance). From now on, all filenames given to Alure will be used with
184 // our custom factory.
185 alure::FileIOFactory::set(alure::SharedPtr
<alure::FileIOFactory
>(new FileFactory(argv
[0])));
187 alure::DeviceManager
*devMgr
= alure::DeviceManager::get();
189 alure::Device
*dev
= devMgr
->openPlayback();
190 std::cout
<< "Opened \""<<dev
->getName(alure::PlaybackDevType_Basic
)<<"\"" <<std::endl
;
192 alure::Context
*ctx
= dev
->createContext();
193 alure::Context::MakeCurrent(ctx
);
195 for(int i
= 1;i
< argc
;i
++)
197 if(strcasecmp(argv
[i
], "-add") == 0 && argc
-i
> 1)
199 FileFactory::Mount(argv
[++i
]);
203 alure::SharedPtr
<alure::Decoder
> decoder(ctx
->createDecoder(argv
[i
]));
204 alure::Source
*source
= ctx
->getSource();
205 source
->play(decoder
, 32768, 4);
206 std::cout
<< "Playing "<<argv
[i
]<<" ("<<alure::GetSampleTypeName(decoder
->getSampleType())<<", "
207 <<alure::GetChannelConfigName(decoder
->getChannelConfig())<<", "
208 <<decoder
->getFrequency()<<"hz)" <<std::endl
;
210 float invfreq
= 1.0f
/ decoder
->getFrequency();
211 while(source
->isPlaying())
213 std::cout
<< "\r "<<std::setiosflags(std::ios::fixed
)<<std::setprecision(2)<<
214 (source
->getOffset()*invfreq
)<<" / "<<(decoder
->getLength()*invfreq
);
216 std::this_thread::sleep_for(std::chrono::milliseconds(25));
219 std::cout
<<std::endl
;
226 alure::Context::MakeCurrent(nullptr);