1 /** Used SDL_mixer and glest source as reference */
4 #include "sound_file.h"
12 #include <vorbis/codec.h>
13 #include <vorbis/vorbisfile.h>
15 class WavSoundFile : public SoundFile
18 WavSoundFile(PHYSFS_file* file);
21 size_t read(void* buffer, size_t buffer_size);
27 PHYSFS_sint64 datastart;
30 static inline uint32_t read32LE(PHYSFS_file* file)
33 if(PHYSFS_readULE32(file, &result) == 0)
34 throw std::runtime_error("file too short");
39 static inline uint16_t read16LE(PHYSFS_file* file)
42 if(PHYSFS_readULE16(file, &result) == 0)
43 throw std::runtime_error("file too short");
48 WavSoundFile::WavSoundFile(PHYSFS_file* file)
53 if(PHYSFS_read(file, magic, sizeof(magic), 1) != 1)
54 throw std::runtime_error("Couldn't read file magic (not a wave file)");
55 if(strncmp(magic, "RIFF", 4) != 0) {
56 printf("MAGIC: %4s.\n", magic);
57 throw std::runtime_error("file is not a RIFF wav file");
60 uint32_t wavelen = read32LE(file);
63 if(PHYSFS_read(file, magic, sizeof(magic), 1) != 1)
64 throw std::runtime_error("Couldn't read chunk header (not a wav file?)");
65 if(strncmp(magic, "WAVE", 4) != 0)
66 throw std::runtime_error("file is not a valid RIFF/WAVE file");
71 // search audio data format chunk
73 if(PHYSFS_read(file, chunkmagic, sizeof(chunkmagic), 1) != 1)
74 throw std::runtime_error("EOF while searching format chunk");
75 chunklen = read32LE(file);
77 if(strncmp(chunkmagic, "fmt ", 4) == 0)
80 if(strncmp(chunkmagic, "fact", 4) == 0
81 || strncmp(chunkmagic, "LIST", 4) == 0) {
83 if(PHYSFS_seek(file, PHYSFS_tell(file) + chunklen) == 0)
84 throw std::runtime_error("EOF while searching fmt chunk");
86 throw std::runtime_error("complex WAVE files not supported");
91 throw std::runtime_error("Format chunk too short");
94 uint16_t encoding = read16LE(file);
96 throw std::runtime_error("only PCM encoding supported");
97 channels = read16LE(file);
98 rate = read32LE(file);
99 uint32_t byterate = read32LE(file);
101 uint16_t blockalign = read16LE(file);
103 bits_per_sample = read16LE(file);
106 if(PHYSFS_seek(file, PHYSFS_tell(file) + (chunklen-16)) == 0)
107 throw std::runtime_error("EOF while reading reast of format chunk");
110 // set file offset to DATA chunk data
112 if(PHYSFS_read(file, chunkmagic, sizeof(chunkmagic), 1) != 1)
113 throw std::runtime_error("EOF while searching data chunk");
114 chunklen = read32LE(file);
116 if(strncmp(chunkmagic, "data", 4) == 0)
120 if(PHYSFS_seek(file, PHYSFS_tell(file) + chunklen) == 0)
121 throw std::runtime_error("EOF while searching fmt chunk");
124 datastart = PHYSFS_tell(file);
125 size = static_cast<size_t> (chunklen);
128 WavSoundFile::~WavSoundFile()
134 WavSoundFile::reset()
136 if(PHYSFS_seek(file, datastart) == 0)
137 throw std::runtime_error("Couldn't seek to data start");
141 WavSoundFile::read(void* buffer, size_t buffer_size)
143 PHYSFS_sint64 end = datastart + size;
144 PHYSFS_sint64 cur = PHYSFS_tell(file);
148 size_t readsize = std::min(static_cast<size_t> (end - cur), buffer_size);
149 if(PHYSFS_read(file, buffer, readsize, 1) != 1)
150 throw std::runtime_error("read error while reading samples");
155 //---------------------------------------------------------------------------
157 class OggSoundFile : public SoundFile
160 OggSoundFile(PHYSFS_file* file);
163 size_t read(void* buffer, size_t buffer_size);
167 static size_t cb_read(void* ptr, size_t size, size_t nmemb, void* source);
168 static int cb_seek(void* source, ogg_int64_t offset, int whence);
169 static int cb_close(void* source);
170 static long cb_tell(void* source);
173 OggVorbis_File vorbis_file;
176 OggSoundFile::OggSoundFile(PHYSFS_file* file)
180 ov_callbacks callbacks = { cb_read, cb_seek, cb_close, cb_tell };
181 ov_open_callbacks(file, &vorbis_file, 0, 0, callbacks);
183 vorbis_info* vi = ov_info(&vorbis_file, -1);
184 channels = vi->channels;
186 bits_per_sample = 16;
187 size = static_cast<size_t> (ov_pcm_total(&vorbis_file, -1) * 2);
190 OggSoundFile::~OggSoundFile()
192 ov_clear(&vorbis_file);
196 OggSoundFile::read(void* _buffer, size_t buffer_size)
198 char* buffer = reinterpret_cast<char*> (_buffer);
200 size_t totalBytesRead= 0;
202 while(buffer_size>0){
204 = ov_read(&vorbis_file, buffer, static_cast<int> (buffer_size), 0, 2, 1,
209 buffer_size -= bytesRead;
211 totalBytesRead += bytesRead;
214 return totalBytesRead;
218 OggSoundFile::reset()
220 ov_raw_seek(&vorbis_file, 0);
224 OggSoundFile::cb_read(void* ptr, size_t size, size_t nmemb, void* source)
226 PHYSFS_file* file = reinterpret_cast<PHYSFS_file*> (source);
229 = PHYSFS_read(file, ptr, static_cast<PHYSFS_uint32> (size),
230 static_cast<PHYSFS_uint32> (nmemb));
234 return static_cast<size_t> (res);
238 OggSoundFile::cb_seek(void* source, ogg_int64_t offset, int whence)
240 PHYSFS_file* file = reinterpret_cast<PHYSFS_file*> (source);
244 if(PHYSFS_seek(file, static_cast<PHYSFS_uint64> (offset)) == 0)
248 if(PHYSFS_seek(file, PHYSFS_tell(file) + offset) == 0)
252 if(PHYSFS_seek(file, PHYSFS_fileLength(file) + offset) == 0)
266 OggSoundFile::cb_close(void* source)
268 PHYSFS_file* file = reinterpret_cast<PHYSFS_file*> (source);
274 OggSoundFile::cb_tell(void* source)
276 PHYSFS_file* file = reinterpret_cast<PHYSFS_file*> (source);
277 return static_cast<long> (PHYSFS_tell(file));
280 //---------------------------------------------------------------------------
283 SoundFile* load_sound_file(const std::string& filename)
285 PHYSFS_file* file = PHYSFS_openRead(filename.c_str());
287 std::stringstream msg;
288 msg << "Couldn't open '" << filename << "': " << PHYSFS_getLastError();
289 throw std::runtime_error(msg.str());
294 if(PHYSFS_read(file, magic, sizeof(magic), 1) != 1)
295 throw std::runtime_error("Couldn't read magic, file too short");
296 PHYSFS_seek(file, 0);
297 if(strncmp(magic, "RIFF", 4) == 0)
298 return new WavSoundFile(file);
299 else if(strncmp(magic, "OggS", 4) == 0)
300 return new OggSoundFile(file);
302 throw std::runtime_error("Unknown file format");
303 } catch(std::exception& e) {
304 std::stringstream msg;
305 msg << "Couldn't read '" << filename << "': " << e.what();
306 throw std::runtime_error(msg.str());