Merged changes from branches/supertux-milestone2-grumbel/ to trunk/supertux/
[supertux.git] / src / audio / ogg_sound_file.cpp
1 //  SuperTux
2 //  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
3 //
4 //  This program is free software: you can redistribute it and/or modify
5 //  it under the terms of the GNU General Public License as published by
6 //  the Free Software Foundation, either version 3 of the License, or
7 //  (at your option) any later version.
8 //
9 //  This program is distributed in the hope that it will be useful,
10 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
11 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 //  GNU General Public License for more details.
13 //
14 //  You should have received a copy of the GNU General Public License
15 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
17 #include "audio/ogg_sound_file.hpp"
18
19 OggSoundFile::OggSoundFile(PHYSFS_file* file, double loop_begin, double loop_at) :
20   file(),
21   vorbis_file(),
22   loop_begin(),
23   loop_at(),
24   normal_buffer_loop() 
25 {
26   this->file = file;
27
28   ov_callbacks callbacks = { cb_read, cb_seek, cb_close, cb_tell };
29   ov_open_callbacks(file, &vorbis_file, 0, 0, callbacks);
30
31   vorbis_info* vi = ov_info(&vorbis_file, -1);
32
33   channels        = vi->channels;
34   rate            = vi->rate;
35   bits_per_sample = 16;
36   size            = static_cast<size_t> (ov_pcm_total(&vorbis_file, -1) * 2);
37
38   double samples_begin = loop_begin * rate;
39   double sample_loop   = loop_at * rate;
40
41   this->loop_begin     = (ogg_int64_t) samples_begin;
42   if(loop_begin < 0) {
43     this->loop_at = (ogg_int64_t) -1;
44   } else {
45     this->loop_at = (ogg_int64_t) sample_loop;
46   }
47 }
48
49 OggSoundFile::~OggSoundFile()
50 {
51   ov_clear(&vorbis_file);
52 }
53
54 size_t
55 OggSoundFile::read(void* _buffer, size_t buffer_size)
56 {
57   char*  buffer         = reinterpret_cast<char*> (_buffer);
58   int    section        = 0;
59   size_t totalBytesRead = 0;
60
61   while(buffer_size>0) {
62 #ifdef WORDS_BIGENDIAN
63     int bigendian = 1;
64 #else
65     int bigendian = 0;
66 #endif
67
68     size_t bytes_to_read    = buffer_size;
69     if(loop_at > 0) {
70       size_t      bytes_per_sample       = 2;
71       ogg_int64_t time                   = ov_pcm_tell(&vorbis_file);
72       ogg_int64_t samples_left_till_loop = loop_at - time;
73       ogg_int64_t bytes_left_till_loop
74         = samples_left_till_loop * bytes_per_sample;
75       if(bytes_left_till_loop <= 4)
76         break;
77
78       if(bytes_left_till_loop < (ogg_int64_t) bytes_to_read) {
79         bytes_to_read    = (size_t) bytes_left_till_loop;
80       }
81     }
82
83     long bytesRead
84       = ov_read(&vorbis_file, buffer, bytes_to_read, bigendian,
85                 2, 1, &section);
86     if(bytesRead == 0) {
87       break;
88     }
89     buffer_size    -= bytesRead;
90     buffer         += bytesRead;
91     totalBytesRead += bytesRead;
92   }
93
94   return totalBytesRead;
95 }
96
97 void
98 OggSoundFile::reset()
99 {
100   ov_pcm_seek(&vorbis_file, loop_begin);
101 }
102
103 size_t
104 OggSoundFile::cb_read(void* ptr, size_t size, size_t nmemb, void* source)
105 {
106   PHYSFS_file* file = reinterpret_cast<PHYSFS_file*> (source);
107
108   PHYSFS_sint64 res
109     = PHYSFS_read(file, ptr, static_cast<PHYSFS_uint32> (size),
110                   static_cast<PHYSFS_uint32> (nmemb));
111   if(res <= 0)
112     return 0;
113
114   return static_cast<size_t> (res);
115 }
116
117 int
118 OggSoundFile::cb_seek(void* source, ogg_int64_t offset, int whence)
119 {
120   PHYSFS_file* file = reinterpret_cast<PHYSFS_file*> (source);
121
122   switch(whence) {
123     case SEEK_SET:
124       if(PHYSFS_seek(file, static_cast<PHYSFS_uint64> (offset)) == 0)
125         return -1;
126       break;
127     case SEEK_CUR:
128       if(PHYSFS_seek(file, PHYSFS_tell(file) + offset) == 0)
129         return -1;
130       break;
131     case SEEK_END:
132       if(PHYSFS_seek(file, PHYSFS_fileLength(file) + offset) == 0)
133         return -1;
134       break;
135     default:
136 #ifdef DEBUG
137       assert(false);
138 #else
139       return -1;
140 #endif
141   }
142   return 0;
143 }
144
145 int
146 OggSoundFile::cb_close(void* source)
147 {
148   PHYSFS_file* file = reinterpret_cast<PHYSFS_file*> (source);
149   PHYSFS_close(file);
150   return 0;
151 }
152
153 long
154 OggSoundFile::cb_tell(void* source)
155 {
156   PHYSFS_file* file = reinterpret_cast<PHYSFS_file*> (source);
157   return static_cast<long> (PHYSFS_tell(file));
158 }
159
160 /* EOF */