src/audio/wav_sound_file.cpp

Go to the documentation of this file.
00001 //  SuperTux
00002 //  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
00003 //
00004 //  This program is free software: you can redistribute it and/or modify
00005 //  it under the terms of the GNU General Public License as published by
00006 //  the Free Software Foundation, either version 3 of the License, or
00007 //  (at your option) any later version.
00008 //
00009 //  This program is distributed in the hope that it will be useful,
00010 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012 //  GNU General Public License for more details.
00013 //
00014 //  You should have received a copy of the GNU General Public License
00015 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
00016 
00017 #include "audio/wav_sound_file.hpp"
00018 
00019 #include <string.h>
00020 #include <stdint.h>
00021 #include <assert.h>
00022 
00023 #include "audio/sound_error.hpp"
00024 #include "util/log.hpp"
00025 
00026 static inline uint32_t read32LE(PHYSFS_file* file)
00027 {
00028   uint32_t result;
00029   if(PHYSFS_readULE32(file, &result) == 0)
00030     throw SoundError("file too short");
00031 
00032   return result;
00033 }
00034 
00035 static inline uint16_t read16LE(PHYSFS_file* file)
00036 {
00037   uint16_t result;
00038   if(PHYSFS_readULE16(file, &result) == 0)
00039     throw SoundError("file too short");
00040 
00041   return result;
00042 }
00043 
00044 WavSoundFile::WavSoundFile(PHYSFS_file* file_) :
00045   file(file_),
00046   datastart()
00047 {
00048   assert(file);
00049   char magic[4];
00050   if(PHYSFS_read(file, magic, sizeof(magic), 1) != 1)
00051     throw SoundError("Couldn't read file magic (not a wave file)");
00052   if(strncmp(magic, "RIFF", 4) != 0) {
00053     log_debug << "MAGIC: " << magic << std::endl;
00054     throw SoundError("file is not a RIFF wav file");
00055   }
00056 
00057   uint32_t wavelen = read32LE(file);
00058   (void) wavelen;
00059 
00060   if(PHYSFS_read(file, magic, sizeof(magic), 1) != 1)
00061     throw SoundError("Couldn't read chunk header (not a wav file?)");
00062   if(strncmp(magic, "WAVE", 4) != 0)
00063     throw SoundError("file is not a valid RIFF/WAVE file");
00064 
00065   char chunkmagic[4];
00066   uint32_t chunklen;
00067 
00068   // search audio data format chunk
00069   do {
00070     if(PHYSFS_read(file, chunkmagic, sizeof(chunkmagic), 1) != 1)
00071       throw SoundError("EOF while searching format chunk");
00072     chunklen = read32LE(file);
00073 
00074     if(strncmp(chunkmagic, "fmt ", 4) == 0)
00075       break;
00076 
00077     if(strncmp(chunkmagic, "fact", 4) == 0
00078        || strncmp(chunkmagic, "LIST", 4) == 0) {
00079       // skip chunk
00080       if(PHYSFS_seek(file, PHYSFS_tell(file) + chunklen) == 0)
00081         throw SoundError("EOF while searching fmt chunk");
00082     } else {
00083       throw SoundError("complex WAVE files not supported");
00084     }
00085   } while(true);
00086 
00087   if(chunklen < 16)
00088     throw SoundError("Format chunk too short");
00089 
00090   // parse format
00091   uint16_t encoding = read16LE(file);
00092   if(encoding != 1)
00093     throw SoundError("only PCM encoding supported");
00094   channels = read16LE(file);
00095   rate = read32LE(file);
00096   uint32_t byterate = read32LE(file);
00097   (void) byterate;
00098   uint16_t blockalign = read16LE(file);
00099   (void) blockalign;
00100   bits_per_sample = read16LE(file);
00101 
00102   if(chunklen > 16) {
00103     if(PHYSFS_seek(file, PHYSFS_tell(file) + (chunklen-16)) == 0)
00104       throw SoundError("EOF while reading rest of format chunk");
00105   }
00106 
00107   // set file offset to DATA chunk data
00108   do {
00109     if(PHYSFS_read(file, chunkmagic, sizeof(chunkmagic), 1) != 1)
00110       throw SoundError("EOF while searching data chunk");
00111     chunklen = read32LE(file);
00112 
00113     if(strncmp(chunkmagic, "data", 4) == 0)
00114       break;
00115 
00116     // skip chunk
00117     if(PHYSFS_seek(file, PHYSFS_tell(file) + chunklen) == 0)
00118       throw SoundError("EOF while searching fmt chunk");
00119   } while(true);
00120 
00121   datastart = PHYSFS_tell(file);
00122   size = static_cast<size_t> (chunklen);
00123 }
00124 
00125 WavSoundFile::~WavSoundFile()
00126 {
00127   PHYSFS_close(file);
00128 }
00129 
00130 void
00131 WavSoundFile::reset()
00132 {
00133   if(PHYSFS_seek(file, datastart) == 0)
00134     throw SoundError("Couldn't seek to data start");
00135 }
00136 
00137 size_t
00138 WavSoundFile::read(void* buffer, size_t buffer_size)
00139 {
00140   PHYSFS_sint64 end = datastart + size;
00141   PHYSFS_sint64 cur = PHYSFS_tell(file);
00142   if(cur >= end)
00143     return 0;
00144 
00145   size_t readsize = std::min(static_cast<size_t> (end - cur), buffer_size);
00146   if(PHYSFS_read(file, buffer, readsize, 1) != 1)
00147     throw SoundError("read error while reading samples");
00148 
00149 #ifdef WORDS_BIGENDIAN
00150   if (bits_per_sample != 16)
00151     return readsize;
00152   char *tmp = (char*)buffer;
00153 
00154   size_t i;
00155   char c;
00156   for (i = 0; i < readsize / 2; i++)
00157   {
00158     c          = tmp[2*i];
00159     tmp[2*i]   = tmp[2*i+1];
00160     tmp[2*i+1] = c;
00161   }
00162 
00163   buffer = tmp;
00164 #endif
00165 
00166   return readsize;
00167 }
00168 
00169 /* EOF */

Generated on Mon Jun 9 03:38:16 2014 for SuperTux by  doxygen 1.5.1