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#include <config.h>
20
21#include <assert.h>
22#include <physfs.h>
23
24OggSoundFile::OggSoundFile(PHYSFS_File* file_, double loop_begin_, double loop_at_) :
25 m_file(file_),
26 m_vorbis_file(),
27 m_loop_begin(),
28 m_loop_at()
29{
30 ov_callbacks callbacks = { cb_read, cb_seek, cb_close, cb_tell };
31 ov_open_callbacks(m_file, &m_vorbis_file, nullptr, 0, callbacks);
32
33 vorbis_info* vi = ov_info(&m_vorbis_file, -1);
34
35 m_channels = vi->channels;
36 m_rate = static_cast<int>(vi->rate);
37 m_bits_per_sample = 16;
38 m_size = static_cast<size_t> (ov_pcm_total(&m_vorbis_file, -1) * 2);
39
40 double samples_begin = loop_begin_ * m_rate;
41 double sample_loop = loop_at_ * m_rate;
42
43 m_loop_begin = static_cast<ogg_int64_t>(samples_begin);
44 if (loop_begin_ < 0) {
45 m_loop_at = static_cast<ogg_int64_t>(-1);
46 } else {
47 m_loop_at = static_cast<ogg_int64_t>(sample_loop);
48 }
49}
50
51OggSoundFile::~OggSoundFile()
52{
53 ov_clear(&m_vorbis_file);
54}
55
56size_t
57OggSoundFile::read(void* _buffer, size_t buffer_size)
58{
59 char* buffer = reinterpret_cast<char*> (_buffer);
60 int section = 0;
61 size_t totalBytesRead = 0;
62
63 while (buffer_size>0) {
64#ifdef WORDS_BIGENDIAN
65 int bigendian = 1;
66#else
67 int bigendian = 0;
68#endif
69
70 size_t bytes_to_read = buffer_size;
71 if (m_loop_at > 0) {
72 size_t bytes_per_sample = 2;
73 ogg_int64_t time = ov_pcm_tell(&m_vorbis_file);
74 ogg_int64_t samples_left_till_loop = m_loop_at - time;
75 ogg_int64_t bytes_left_till_loop = samples_left_till_loop * bytes_per_sample;
76 if (bytes_left_till_loop <= 4)
77 break;
78
79 if (bytes_left_till_loop < static_cast<ogg_int64_t>(bytes_to_read)) {
80 bytes_to_read = static_cast<size_t>(bytes_left_till_loop);
81 }
82 }
83
84 long bytesRead
85 = ov_read(&m_vorbis_file, buffer, static_cast<int>(bytes_to_read), bigendian,
86 2, 1, &section);
87 if (bytesRead == 0) {
88 break;
89 }
90 buffer_size -= bytesRead;
91 buffer += bytesRead;
92 totalBytesRead += bytesRead;
93 }
94
95 return totalBytesRead;
96}
97
98void
99OggSoundFile::reset()
100{
101 ov_pcm_seek(&m_vorbis_file, m_loop_begin);
102}
103
104size_t
105OggSoundFile::cb_read(void* ptr, size_t size, size_t nmemb, void* source)
106{
107 auto file = reinterpret_cast<PHYSFS_file*> (source);
108
109 PHYSFS_sint64 res
110 = PHYSFS_readBytes(file, ptr, static_cast<PHYSFS_uint32> (size) * static_cast<PHYSFS_uint32> (nmemb));
111 if (res <= 0)
112 return 0;
113
114 return static_cast<size_t> (res) / size;
115}
116
117int
118OggSoundFile::cb_seek(void* source, ogg_int64_t offset, int whence)
119{
120 auto 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 assert(false);
137 return -1;
138 }
139 return 0;
140}
141
142int
143OggSoundFile::cb_close(void* source)
144{
145 auto file = reinterpret_cast<PHYSFS_file*> (source);
146 PHYSFS_close(file);
147 return 0;
148}
149
150long
151OggSoundFile::cb_tell(void* source)
152{
153 auto file = reinterpret_cast<PHYSFS_file*> (source);
154 return static_cast<long> (PHYSFS_tell(file));
155}
156
157/* EOF */
158