X-Git-Url: https://git.verplant.org/?a=blobdiff_plain;f=src%2Faudio%2Fsound_file.cpp;h=98a615b148aa3941c9c69f703765d2cac696fde1;hb=6b0c80bde84af0bf9323320d99f2fccd7c9eeedd;hp=a05fc1a7d4f1ce3e5c38d36fff2c8f78a732c814;hpb=e6a940db5904743e8220491ce10b5107e119a44c;p=supertux.git diff --git a/src/audio/sound_file.cpp b/src/audio/sound_file.cpp index a05fc1a7d..98a615b14 100644 --- a/src/audio/sound_file.cpp +++ b/src/audio/sound_file.cpp @@ -1,3 +1,22 @@ +// $Id$ +// +// SuperTux +// Copyright (C) 2006 Matthias Braun +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + /** Used SDL_mixer and glest source as reference */ #include @@ -13,6 +32,9 @@ #include #include #include "log.hpp" +#include "lisp/parser.hpp" +#include "lisp/lisp.hpp" +#include "file_system.hpp" class WavSoundFile : public SoundFile { @@ -25,7 +47,7 @@ public: private: PHYSFS_file* file; - + PHYSFS_sint64 datastart; }; @@ -61,7 +83,7 @@ WavSoundFile::WavSoundFile(PHYSFS_file* file) uint32_t wavelen = read32LE(file); (void) wavelen; - + if(PHYSFS_read(file, magic, sizeof(magic), 1) != 1) throw std::runtime_error("Couldn't read chunk header (not a wav file?)"); if(strncmp(magic, "WAVE", 4) != 0) @@ -73,9 +95,9 @@ WavSoundFile::WavSoundFile(PHYSFS_file* file) // search audio data format chunk do { if(PHYSFS_read(file, chunkmagic, sizeof(chunkmagic), 1) != 1) - throw std::runtime_error("EOF while searching format chunk"); + throw std::runtime_error("EOF while searching format chunk"); chunklen = read32LE(file); - + if(strncmp(chunkmagic, "fmt ", 4) == 0) break; @@ -87,11 +109,11 @@ WavSoundFile::WavSoundFile(PHYSFS_file* file) } else { throw std::runtime_error("complex WAVE files not supported"); } - } while(true); + } while(true); if(chunklen < 16) throw std::runtime_error("Format chunk too short"); - + // parse format uint16_t encoding = read16LE(file); if(encoding != 1) @@ -112,7 +134,7 @@ WavSoundFile::WavSoundFile(PHYSFS_file* file) // set file offset to DATA chunk data do { if(PHYSFS_read(file, chunkmagic, sizeof(chunkmagic), 1) != 1) - throw std::runtime_error("EOF while searching data chunk"); + throw std::runtime_error("EOF while searching data chunk"); chunklen = read32LE(file); if(strncmp(chunkmagic, "data", 4) == 0) @@ -146,7 +168,7 @@ WavSoundFile::read(void* buffer, size_t buffer_size) PHYSFS_sint64 cur = PHYSFS_tell(file); if(cur >= end) return 0; - + size_t readsize = std::min(static_cast (end - cur), buffer_size); if(PHYSFS_read(file, buffer, readsize, 1) != 1) throw std::runtime_error("read error while reading samples"); @@ -176,7 +198,7 @@ WavSoundFile::read(void* buffer, size_t buffer_size) class OggSoundFile : public SoundFile { public: - OggSoundFile(PHYSFS_file* file); + OggSoundFile(PHYSFS_file* file, double loop_begin, double loop_at); ~OggSoundFile(); size_t read(void* buffer, size_t buffer_size); @@ -187,12 +209,15 @@ private: static int cb_seek(void* source, ogg_int64_t offset, int whence); static int cb_close(void* source); static long cb_tell(void* source); - - PHYSFS_file* file; + + PHYSFS_file* file; OggVorbis_File vorbis_file; + ogg_int64_t loop_begin; + ogg_int64_t loop_at; + size_t normal_buffer_loop; }; -OggSoundFile::OggSoundFile(PHYSFS_file* file) +OggSoundFile::OggSoundFile(PHYSFS_file* file, double loop_begin, double loop_at) { this->file = file; @@ -200,10 +225,22 @@ OggSoundFile::OggSoundFile(PHYSFS_file* file) ov_open_callbacks(file, &vorbis_file, 0, 0, callbacks); vorbis_info* vi = ov_info(&vorbis_file, -1); - channels = vi->channels; - rate = vi->rate; + + channels = vi->channels; + rate = vi->rate; bits_per_sample = 16; - size = static_cast (ov_pcm_total(&vorbis_file, -1) * 2); + size = static_cast (ov_pcm_total(&vorbis_file, -1) * 2); + + double sample_len = 1.0f / rate; + double samples_begin = loop_begin / sample_len; + double sample_loop = loop_at / sample_len; + + this->loop_begin = (ogg_int64_t) samples_begin; + if(loop_begin < 0) { + this->loop_at = (ogg_int64_t) -1; + } else { + this->loop_at = (ogg_int64_t) sample_loop; + } } OggSoundFile::~OggSoundFile() @@ -214,42 +251,58 @@ OggSoundFile::~OggSoundFile() size_t OggSoundFile::read(void* _buffer, size_t buffer_size) { - char* buffer = reinterpret_cast (_buffer); - int section = 0; - size_t totalBytesRead= 0; + char* buffer = reinterpret_cast (_buffer); + int section = 0; + size_t totalBytesRead = 0; - while(buffer_size>0){ - long bytesRead - = ov_read(&vorbis_file, buffer, static_cast (buffer_size), + while(buffer_size>0) { #ifdef WORDS_BIGENDIAN -1, + int bigendian = 1; #else -0, + int bigendian = 0; #endif + + size_t bytes_to_read = buffer_size; + if(loop_at > 0) { + size_t bytes_per_sample = 2; + ogg_int64_t time = ov_pcm_tell(&vorbis_file); + ogg_int64_t samples_left_till_loop = loop_at - time; + ogg_int64_t bytes_left_till_loop + = samples_left_till_loop * bytes_per_sample; + if(bytes_left_till_loop <= 4) + break; + + if(bytes_left_till_loop < (ogg_int64_t) bytes_to_read) { + bytes_to_read = (size_t) bytes_left_till_loop; + } + } + + long bytesRead + = ov_read(&vorbis_file, buffer, bytes_to_read, bigendian, 2, 1, §ion); - if(bytesRead==0){ + if(bytesRead == 0) { break; } - buffer_size -= bytesRead; - buffer += bytesRead; + buffer_size -= bytesRead; + buffer += bytesRead; totalBytesRead += bytesRead; } - + return totalBytesRead; } void OggSoundFile::reset() { - ov_raw_seek(&vorbis_file, 0); + ov_pcm_seek(&vorbis_file, loop_begin); } size_t OggSoundFile::cb_read(void* ptr, size_t size, size_t nmemb, void* source) { PHYSFS_file* file = reinterpret_cast (source); - - PHYSFS_sint64 res + + PHYSFS_sint64 res = PHYSFS_read(file, ptr, static_cast (size), static_cast (nmemb)); if(res <= 0) @@ -285,7 +338,7 @@ OggSoundFile::cb_seek(void* source, ogg_int64_t offset, int whence) } return 0; } - + int OggSoundFile::cb_close(void* source) { @@ -303,16 +356,57 @@ OggSoundFile::cb_tell(void* source) //--------------------------------------------------------------------------- -#include +SoundFile* load_music_file(const std::string& filename) +{ + lisp::Parser parser(false); + const lisp::Lisp* root = parser.parse(filename); + const lisp::Lisp* music = root->get_lisp("supertux-music"); + if(music == NULL) + throw std::runtime_error("file is not a supertux-music file."); + + std::string raw_music_file; + float loop_begin = 0; + float loop_at = -1; + + music->get("file", raw_music_file); + music->get("loop-begin", loop_begin); + music->get("loop-at", loop_at); + + if(loop_begin < 0) { + throw std::runtime_error("can't loop from negative value"); + } + + std::string basedir = FileSystem::dirname(filename); + raw_music_file = FileSystem::normalize(basedir + raw_music_file); + + PHYSFS_file* file = PHYSFS_openRead(raw_music_file.c_str()); + if(!file) { + std::stringstream msg; + msg << "Couldn't open '" << raw_music_file << "': " << PHYSFS_getLastError(); + throw std::runtime_error(msg.str()); + } + + return new OggSoundFile(file, loop_begin, loop_at); +} + SoundFile* load_sound_file(const std::string& filename) { + if(filename.length() > 6 + && filename.compare(filename.length()-6, 6, ".music") == 0) { + return load_music_file(filename); + } + PHYSFS_file* file = PHYSFS_openRead(filename.c_str()); if(!file) { + log_warning << "Couldn't open '" << filename << "': " << PHYSFS_getLastError() << ", using dummy sound file." << std::endl; + file = PHYSFS_openRead("sounds/empty.wav"); + if (!file) { std::stringstream msg; - msg << "Couldn't open '" << filename << "': " << PHYSFS_getLastError(); - throw std::runtime_error(msg.str()); + msg << "Couldn't open dummy sound file '" << filename << "': " << PHYSFS_getLastError(); + throw std::runtime_error(msg.str()); + } } - + try { char magic[4]; if(PHYSFS_read(file, magic, sizeof(magic), 1) != 1) @@ -321,7 +415,7 @@ SoundFile* load_sound_file(const std::string& filename) if(strncmp(magic, "RIFF", 4) == 0) return new WavSoundFile(file); else if(strncmp(magic, "OggS", 4) == 0) - return new OggSoundFile(file); + return new OggSoundFile(file, 0, -1); else throw std::runtime_error("Unknown file format"); } catch(std::exception& e) { @@ -330,4 +424,3 @@ SoundFile* load_sound_file(const std::string& filename) throw std::runtime_error(msg.str()); } } -