]> git.tdb.fi Git - libs/al.git/blob - source/sound.cpp
Update Build file with new builder features
[libs/al.git] / source / sound.cpp
1 #include <cstring>
2 #include <stdexcept>
3 #include "sound.h"
4
5 using namespace std;
6
7 namespace {
8
9 struct MemorySource
10 {
11         const void *data;
12         unsigned length;
13         unsigned pos;
14
15         MemorySource(const void *d, unsigned l): data(d), length(l), pos(0) { }
16 };
17
18 size_t memory_read(void *ptr, size_t size, size_t nmemb, void *src)
19 {
20         MemorySource &memsrc = *reinterpret_cast<MemorySource *>(src);
21         unsigned len = min<unsigned>(size*nmemb, memsrc.length-memsrc.pos);
22         memcpy(ptr, reinterpret_cast<const char *>(memsrc.data)+memsrc.pos, len);
23         memsrc.pos += len;
24
25         return len/size;
26 }
27
28 int memory_seek(void *src, ogg_int64_t offset, int whence)
29 {
30         MemorySource &memsrc = *reinterpret_cast<MemorySource *>(src);
31         if(whence==SEEK_SET)
32                 memsrc.pos = offset;
33         else if(whence==SEEK_CUR)
34                 memsrc.pos += offset;
35         else if(whence==SEEK_END)
36                 memsrc.pos = memsrc.length-offset;
37         memsrc.pos = min(memsrc.pos, memsrc.length);
38
39         return memsrc.pos;
40 }
41
42 int memory_close(void *src)
43 {
44         delete reinterpret_cast<MemorySource *>(src);
45         return 0;
46 }
47
48 long memory_tell(void *src)
49 {
50         MemorySource &memsrc = *reinterpret_cast<MemorySource *>(src);
51         return memsrc.pos;
52 }
53
54 ov_callbacks memory_callbacks=
55 {
56         &memory_read,
57         &memory_seek,
58         &memory_close,
59         &memory_tell
60 };
61
62 } // namespace
63
64 namespace Msp {
65 namespace AL {
66
67 Sound::Sound():
68         data(0),
69         eof_flag(false)
70 {
71         ovfile.datasource = 0;
72 }
73
74 Sound::~Sound()
75 {
76         delete[] data;
77         if(ovfile.datasource)
78                 ov_clear(&ovfile);
79 }
80
81 void Sound::open_file(const string &fn)
82 {
83         if(ovfile.datasource)
84                 throw logic_error("Sound has already been opened");
85         if(ov_fopen(const_cast<char *>(fn.c_str()), &ovfile)<0)
86                 throw runtime_error("Could not open ogg vorbis file "+fn);
87
88         open_common();
89 }
90
91 void Sound::open_memory(const void *d, unsigned len)
92 {
93         if(ovfile.datasource)
94                 throw logic_error("Sound has already been opened");
95
96         MemorySource *src = new MemorySource(d, len);
97         if(ov_open_callbacks(src, &ovfile, 0, 0, memory_callbacks)<0)
98         {
99                 delete src;
100                 throw runtime_error("Could not open ogg vorbis memory block");
101         }
102
103         open_common();
104 }
105
106 void Sound::open_common()
107 {
108         delete data;
109         data = 0;
110
111         vorbis_info *info = ov_info(&ovfile, -1);
112         freq = info->rate;
113         switch(info->channels)
114         {
115         case 1: format = MONO16; break;
116         case 2: format = STEREO16; break;
117         default: throw runtime_error("Unsupported number of channels");
118         }
119 }
120
121 void Sound::load_data()
122 {
123         if(data)
124                 throw logic_error("Data has already been loaded");
125
126         size = ov_pcm_total(&ovfile, 0)*4;
127         char *dptr = new char[size];
128         unsigned pos = 0;
129         while(unsigned len = read(dptr+pos, size-pos))
130                 pos += len;
131         data = dptr;
132         size = pos;
133         read_pos = 0;
134 }
135
136 void Sound::load_file(const std::string &fn)
137 {
138         open_file(fn);
139         load_data();
140         close();
141 }
142
143 void Sound::load_memory(const void *d, unsigned len)
144 {
145         open_memory(d, len);
146         load_data();
147         close();
148 }
149
150 void Sound::close()
151 {
152         if(ovfile.datasource)
153                 ov_clear(&ovfile);
154 }
155
156 void Sound::rewind()
157 {
158         if(data)
159                 read_pos = 0;
160         else
161                 ov_pcm_seek(&ovfile, 0);
162 }
163
164 unsigned Sound::read(char *buf, unsigned len)
165 {
166         if(data)
167         {
168                 len = min(len, size-read_pos);
169                 memcpy(buf, data+read_pos, len);
170                 read_pos += len;
171                 return len;
172         }
173         else if(ovfile.datasource)
174         {
175                 int section = 0;
176                 int res = ov_read(&ovfile, buf, len, 0, 2, 1, &section);
177                 if(res<0)
178                         throw runtime_error("Error reading ogg vorbis file");
179                 else if(res==0)
180                         eof_flag = true;
181                 return res;
182         }
183         else
184                 throw logic_error("No data available");
185 }
186
187 const char *Sound::get_data() const
188 {
189         if(!data)
190                 throw logic_error("Data has not been loaded");
191         return data;
192 }
193
194 } // namespace AL
195 } // namespace Msp