+// $Id$
+//
+// SuperTux
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+//
+// 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 <config.h>
#include <physfs.h>
#include <vorbis/codec.h>
#include <vorbis/vorbisfile.h>
-#include "msg.hpp"
+#include "log.hpp"
+#include "lisp/parser.hpp"
+#include "lisp/lisp.hpp"
+#include "file_system.hpp"
class WavSoundFile : public SoundFile
{
private:
PHYSFS_file* file;
-
+
PHYSFS_sint64 datastart;
};
if(PHYSFS_read(file, magic, sizeof(magic), 1) != 1)
throw std::runtime_error("Couldn't read file magic (not a wave file)");
if(strncmp(magic, "RIFF", 4) != 0) {
- msg_debug << "MAGIC: " << magic << std::endl;
+ log_debug << "MAGIC: " << magic << std::endl;
throw std::runtime_error("file is not a RIFF wav 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)
// 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;
} 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)
// 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)
PHYSFS_sint64 cur = PHYSFS_tell(file);
if(cur >= end)
return 0;
-
+
size_t readsize = std::min(static_cast<size_t> (end - cur), buffer_size);
if(PHYSFS_read(file, buffer, readsize, 1) != 1)
throw std::runtime_error("read error while reading samples");
+#ifdef WORDS_BIGENDIAN
+ if (bits_per_sample != 16)
+ return readsize;
+ char *tmp = (char*)buffer;
+
+ size_t i;
+ char c;
+ for (i = 0; i < readsize / 2; i++)
+ {
+ c = tmp[2*i];
+ tmp[2*i] = tmp[2*i+1];
+ tmp[2*i+1] = c;
+ }
+
+ buffer = tmp;
+#endif
+
return readsize;
}
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);
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;
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<size_t> (ov_pcm_total(&vorbis_file, -1) * 2);
+ size = static_cast<size_t> (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()
size_t
OggSoundFile::read(void* _buffer, size_t buffer_size)
{
- char* buffer = reinterpret_cast<char*> (_buffer);
- int section = 0;
- size_t totalBytesRead= 0;
-
- while(buffer_size>0){
- long bytesRead
- = ov_read(&vorbis_file, buffer, static_cast<int> (buffer_size), 0, 2, 1,
- §ion);
- if(bytesRead==0){
+ char* buffer = reinterpret_cast<char*> (_buffer);
+ int section = 0;
+ size_t totalBytesRead = 0;
+
+ while(buffer_size>0) {
+#ifdef WORDS_BIGENDIAN
+ int bigendian = 1;
+#else
+ 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) {
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<PHYSFS_file*> (source);
-
- PHYSFS_sint64 res
+
+ PHYSFS_sint64 res
= PHYSFS_read(file, ptr, static_cast<PHYSFS_uint32> (size),
static_cast<PHYSFS_uint32> (nmemb));
if(res <= 0)
}
return 0;
}
-
+
int
OggSoundFile::cb_close(void* source)
{
//---------------------------------------------------------------------------
-#include <fstream>
+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) {
std::stringstream msg;
msg << "Couldn't open '" << filename << "': " << PHYSFS_getLastError();
throw std::runtime_error(msg.str());
}
-
+
try {
char magic[4];
if(PHYSFS_read(file, magic, sizeof(magic), 1) != 1)
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) {
throw std::runtime_error(msg.str());
}
}
-