1 /** Used SDL_mixer and glest source as reference */
4 #include "sound_file.hpp"
13 #include <vorbis/codec.h>
14 #include <vorbis/vorbisfile.h>
17 class WavSoundFile : public SoundFile
20 WavSoundFile(PHYSFS_file* file);
23 size_t read(void* buffer, size_t buffer_size);
29 PHYSFS_sint64 datastart;
32 static inline uint32_t read32LE(PHYSFS_file* file)
35 if(PHYSFS_readULE32(file, &result) == 0)
36 throw std::runtime_error("file too short");
41 static inline uint16_t read16LE(PHYSFS_file* file)
44 if(PHYSFS_readULE16(file, &result) == 0)
45 throw std::runtime_error("file too short");
50 WavSoundFile::WavSoundFile(PHYSFS_file* file)
55 if(PHYSFS_read(file, magic, sizeof(magic), 1) != 1)
56 throw std::runtime_error("Couldn't read file magic (not a wave file)");
57 if(strncmp(magic, "RIFF", 4) != 0) {
58 log_debug << "MAGIC: " << magic << std::endl;
59 throw std::runtime_error("file is not a RIFF wav file");
62 uint32_t wavelen = read32LE(file);
65 if(PHYSFS_read(file, magic, sizeof(magic), 1) != 1)
66 throw std::runtime_error("Couldn't read chunk header (not a wav file?)");
67 if(strncmp(magic, "WAVE", 4) != 0)
68 throw std::runtime_error("file is not a valid RIFF/WAVE file");
73 // search audio data format chunk
75 if(PHYSFS_read(file, chunkmagic, sizeof(chunkmagic), 1) != 1)
76 throw std::runtime_error("EOF while searching format chunk");
77 chunklen = read32LE(file);
79 if(strncmp(chunkmagic, "fmt ", 4) == 0)
82 if(strncmp(chunkmagic, "fact", 4) == 0
83 || strncmp(chunkmagic, "LIST", 4) == 0) {
85 if(PHYSFS_seek(file, PHYSFS_tell(file) + chunklen) == 0)
86 throw std::runtime_error("EOF while searching fmt chunk");
88 throw std::runtime_error("complex WAVE files not supported");
93 throw std::runtime_error("Format chunk too short");
96 uint16_t encoding = read16LE(file);
98 throw std::runtime_error("only PCM encoding supported");
99 channels = read16LE(file);
100 rate = read32LE(file);
101 uint32_t byterate = read32LE(file);
103 uint16_t blockalign = read16LE(file);
105 bits_per_sample = read16LE(file);
108 if(PHYSFS_seek(file, PHYSFS_tell(file) + (chunklen-16)) == 0)
109 throw std::runtime_error("EOF while reading reast of format chunk");
112 // set file offset to DATA chunk data
114 if(PHYSFS_read(file, chunkmagic, sizeof(chunkmagic), 1) != 1)
115 throw std::runtime_error("EOF while searching data chunk");
116 chunklen = read32LE(file);
118 if(strncmp(chunkmagic, "data", 4) == 0)
122 if(PHYSFS_seek(file, PHYSFS_tell(file) + chunklen) == 0)
123 throw std::runtime_error("EOF while searching fmt chunk");
126 datastart = PHYSFS_tell(file);
127 size = static_cast<size_t> (chunklen);
130 WavSoundFile::~WavSoundFile()
136 WavSoundFile::reset()
138 if(PHYSFS_seek(file, datastart) == 0)
139 throw std::runtime_error("Couldn't seek to data start");
143 WavSoundFile::read(void* buffer, size_t buffer_size)
145 PHYSFS_sint64 end = datastart + size;
146 PHYSFS_sint64 cur = PHYSFS_tell(file);
150 size_t readsize = std::min(static_cast<size_t> (end - cur), buffer_size);
151 if(PHYSFS_read(file, buffer, readsize, 1) != 1)
152 throw std::runtime_error("read error while reading samples");
154 #ifdef WORDS_BIGENDIAN
155 if (bits_per_sample != 16)
157 char *tmp = (char*)buffer;
161 for (i = 0; i < readsize / 2; i++)
164 tmp[2*i] = tmp[2*i+1];
174 //---------------------------------------------------------------------------
176 class OggSoundFile : public SoundFile
179 OggSoundFile(PHYSFS_file* file);
182 size_t read(void* buffer, size_t buffer_size);
186 static size_t cb_read(void* ptr, size_t size, size_t nmemb, void* source);
187 static int cb_seek(void* source, ogg_int64_t offset, int whence);
188 static int cb_close(void* source);
189 static long cb_tell(void* source);
192 OggVorbis_File vorbis_file;
195 OggSoundFile::OggSoundFile(PHYSFS_file* file)
199 ov_callbacks callbacks = { cb_read, cb_seek, cb_close, cb_tell };
200 ov_open_callbacks(file, &vorbis_file, 0, 0, callbacks);
202 vorbis_info* vi = ov_info(&vorbis_file, -1);
203 channels = vi->channels;
205 bits_per_sample = 16;
206 size = static_cast<size_t> (ov_pcm_total(&vorbis_file, -1) * 2);
209 OggSoundFile::~OggSoundFile()
211 ov_clear(&vorbis_file);
215 OggSoundFile::read(void* _buffer, size_t buffer_size)
217 char* buffer = reinterpret_cast<char*> (_buffer);
219 size_t totalBytesRead= 0;
221 while(buffer_size>0){
223 = ov_read(&vorbis_file, buffer, static_cast<int> (buffer_size),
224 #ifdef WORDS_BIGENDIAN
233 buffer_size -= bytesRead;
235 totalBytesRead += bytesRead;
238 return totalBytesRead;
242 OggSoundFile::reset()
244 ov_raw_seek(&vorbis_file, 0);
248 OggSoundFile::cb_read(void* ptr, size_t size, size_t nmemb, void* source)
250 PHYSFS_file* file = reinterpret_cast<PHYSFS_file*> (source);
253 = PHYSFS_read(file, ptr, static_cast<PHYSFS_uint32> (size),
254 static_cast<PHYSFS_uint32> (nmemb));
258 return static_cast<size_t> (res);
262 OggSoundFile::cb_seek(void* source, ogg_int64_t offset, int whence)
264 PHYSFS_file* file = reinterpret_cast<PHYSFS_file*> (source);
268 if(PHYSFS_seek(file, static_cast<PHYSFS_uint64> (offset)) == 0)
272 if(PHYSFS_seek(file, PHYSFS_tell(file) + offset) == 0)
276 if(PHYSFS_seek(file, PHYSFS_fileLength(file) + offset) == 0)
290 OggSoundFile::cb_close(void* source)
292 PHYSFS_file* file = reinterpret_cast<PHYSFS_file*> (source);
298 OggSoundFile::cb_tell(void* source)
300 PHYSFS_file* file = reinterpret_cast<PHYSFS_file*> (source);
301 return static_cast<long> (PHYSFS_tell(file));
304 //---------------------------------------------------------------------------
307 SoundFile* load_sound_file(const std::string& filename)
309 PHYSFS_file* file = PHYSFS_openRead(filename.c_str());
311 std::stringstream msg;
312 msg << "Couldn't open '" << filename << "': " << PHYSFS_getLastError();
313 throw std::runtime_error(msg.str());
318 if(PHYSFS_read(file, magic, sizeof(magic), 1) != 1)
319 throw std::runtime_error("Couldn't read magic, file too short");
320 PHYSFS_seek(file, 0);
321 if(strncmp(magic, "RIFF", 4) == 0)
322 return new WavSoundFile(file);
323 else if(strncmp(magic, "OggS", 4) == 0)
324 return new OggSoundFile(file);
326 throw std::runtime_error("Unknown file format");
327 } catch(std::exception& e) {
328 std::stringstream msg;
329 msg << "Couldn't read '" << filename << "': " << e.what();
330 throw std::runtime_error(msg.str());