]> Dogcows Code - chaz/yoink/blob - src/Moof/Sound.cc
improved new vorbisfile compatibility
[chaz/yoink] / src / Moof / Sound.cc
1
2 /*******************************************************************************
3
4 Copyright (c) 2009, Charles McGarvey
5 All rights reserved.
6
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions are met:
9
10 * Redistributions of source code must retain the above copyright notice,
11 this list of conditions and the following disclaimer.
12 * Redistributions in binary form must reproduce the above copyright notice,
13 this list of conditions and the following disclaimer in the documentation
14 and/or other materials provided with the distribution.
15
16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
20 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
27 *******************************************************************************/
28
29 #include <cstdio>
30 #include <string>
31 #include <queue>
32 #include <vector>
33
34 #include <AL/al.h>
35 #include <vorbis/codec.h>
36 #include <vorbis/vorbisfile.h>
37
38 #include "Log.hh"
39 #include "Mippleton.hh"
40 #include "Sound.hh"
41
42 #define BUFFER_SIZE (64 * 1024)
43 //#define BUFFER_SIZE (5*2048)
44
45 namespace Mf {
46
47
48 struct Sound::Impl
49 {
50
51 static ALenum getAudioFormat(const vorbis_info* audioInfo)
52 {
53 if (audioInfo->channels == 1) return AL_FORMAT_MONO16;
54 else return AL_FORMAT_STEREO16;
55 }
56
57 class Buffer;
58 typedef boost::shared_ptr<Buffer> BufferP;
59
60 class Buffer : public Mippleton<Buffer>
61 {
62 OggVorbis_File oggStream;
63 ALenum audioFormat;
64 ALsizei audioFreq;
65 std::vector<ALuint> objects;
66
67 public:
68
69 Buffer(const std::string& name) :
70 Mippleton<Buffer>(name)
71 {
72 oggStream.datasource = 0;
73 openFile();
74 }
75
76 ~Buffer()
77 {
78 while (!objects.empty())
79 {
80 alDeleteBuffers(1, &objects.back());
81 objects.pop_back();
82 }
83
84 if (oggStream.datasource)
85 {
86 ov_clear(&oggStream);
87 }
88 }
89
90
91 void openFile()
92 {
93 if (oggStream.datasource)
94 {
95 ov_clear(&oggStream);
96 oggStream.datasource = 0;
97 }
98
99 std::string filePath = Sound::getPath(getName());
100 int result = ov_fopen((char*)filePath.c_str(), &oggStream);
101
102 if (result < 0)
103 {
104 logWarning("error while loading sound %s",
105 getName().c_str());
106 throw Exception(Exception::BAD_AUDIO_FORMAT);
107 }
108
109 vorbis_info* vorbisInfo = ov_info(&oggStream, -1);
110 audioFormat = getAudioFormat(vorbisInfo);
111 audioFreq = vorbisInfo->rate;
112
113 logDebug(" channels: %d", vorbisInfo->channels);
114 logDebug(" frequency: %d", vorbisInfo->rate);
115 }
116
117
118 void loadAll(ALuint source)
119 {
120 if (!oggStream.datasource) openFile();
121 if (!oggStream.datasource) return;
122
123 char data[BUFFER_SIZE];
124 int size = 0;
125
126 for (;;)
127 {
128 int section;
129 int result = ov_read(&oggStream, data + size,
130 BUFFER_SIZE - size, 0, 2, 1, &section);
131
132 if (result > 0)
133 {
134 size += result;
135 }
136 else
137 {
138 if (result < 0) logWarning("vorbis playback error");
139 break;
140 }
141 }
142 if (size == 0)
143 {
144 logWarning("decoded no bytes from %s", getName().c_str());
145 //throw Exception(Exception::FILE_NOT_FOUND);
146 return;
147 }
148
149 ALuint obj;
150 alGenBuffers(1, &obj);
151
152 alBufferData(obj, audioFormat, data, size, audioFreq);
153
154 objects.push_back(obj);
155
156 alSourcei(source, AL_BUFFER, obj);
157
158 // don't need this anymore
159 ov_clear(&oggStream);
160 oggStream.datasource = 0;
161 }
162
163
164 void beginStream(ALuint source, int nBuffers = 4)
165 {
166 if (!oggStream.datasource) openFile();
167 if (!oggStream.datasource) return;
168
169 ALuint objs[nBuffers];
170 alGenBuffers(nBuffers, objs);
171
172 for (int i = 0; i < nBuffers; ++i)
173 {
174 objects.push_back(objs[i]);
175 stream(objs[i]);
176 }
177
178 alSourceQueueBuffers(source, nBuffers, objs);
179 }
180
181 enum StreamStatus
182 {
183 STREAM_OK = 0,
184 STREAM_EOF = 1,
185 STREAM_WRONG = 2
186 };
187
188 StreamStatus stream(ALuint buffer)
189 {
190 std::vector<ALuint>::iterator it =
191 std::find(objects.begin(), objects.end(), buffer);
192
193 // that buffer doesn't belong to us
194 if (it == objects.end()) return STREAM_WRONG;
195
196 char data[BUFFER_SIZE];
197 int size = 0;
198
199 while (size < BUFFER_SIZE)
200 {
201 int section;
202 int result = ov_read(&oggStream, data + size,
203 BUFFER_SIZE - size, 0, 2, 1, &section);
204
205 if (result > 0)
206 {
207 size += result;
208 }
209 else
210 {
211 if (result < 0) logWarning("vorbis playback error");
212 break;
213 }
214 }
215
216 if (size == 0) return STREAM_EOF;
217
218 alBufferData(buffer, audioFormat, data, size, audioFreq);
219
220 return STREAM_OK;
221 }
222
223 inline void rewind()
224 {
225 if (!oggStream.datasource) openFile();
226 else ov_raw_seek(&oggStream, 0);
227 }
228
229
230 // delete unused buffers, return true if all buffers deleted
231 inline bool clear()
232 {
233 // clear any openal errors
234 alGetError();
235
236 while (!objects.empty())
237 {
238 ALuint buffer = objects.back();
239 alDeleteBuffers(1, &buffer);
240
241 // if an error occured, the buffer was not deleted because it's
242 // still in use by some source
243 if (alGetError() != AL_NO_ERROR) return false;
244
245 objects.pop_back();
246 }
247
248 return true;
249 }
250 };
251
252
253 Impl(const std::string& name) :
254 buffer_(Buffer::getInstance(name)),
255 playing_(false),
256 looping_(false)
257 {
258 ALfloat zero[] = {0.0f, 0.0f, 0.0f};
259
260 alGenSources(1, &source_);
261
262 alSourcef(source_, AL_PITCH, 1.0f);
263 alSourcef(source_, AL_GAIN, 1.0f);
264 alSourcefv(source_, AL_POSITION, zero);
265 alSourcefv(source_, AL_VELOCITY, zero);
266 }
267
268 ~Impl()
269 {
270 alDeleteSources(1, &source_);
271 }
272
273
274 void play()
275 {
276 ALenum type;
277 alGetSourcei(source_, AL_SOURCE_TYPE, &type);
278
279 if (type != AL_STATIC)
280 {
281 buffer_->loadAll(source_);
282 }
283
284 alSourcei(source_, AL_LOOPING, looping_);
285 alSourcePlay(source_);
286 playing_ = true;
287 }
288
289
290 void stream()
291 {
292 ALenum type;
293 alGetSourcei(source_, AL_SOURCE_TYPE, &type);
294
295 alSourcei(source_, AL_BUFFER, AL_NONE);
296 buffer_->rewind();
297 buffer_->beginStream(source_);
298
299 alSourcei(source_, AL_LOOPING, AL_FALSE);
300 alSourcePlay(source_);
301 playing_ = true;
302 }
303
304 inline void update()
305 {
306 ALint finished = 0;
307
308 alGetSourcei(source_, AL_BUFFERS_PROCESSED, &finished);
309
310 while (finished-- > 0)
311 {
312 ALuint buffer;
313
314 alSourceUnqueueBuffers(source_, 1, &buffer);
315
316 Buffer::StreamStatus status = buffer_->stream(buffer);
317
318 if (status == Buffer::STREAM_OK)
319 {
320 alSourceQueueBuffers(source_, 1, &buffer);
321 }
322 else if (status == Buffer::STREAM_EOF)
323 {
324 if (!queue_.empty())
325 {
326 // begin the next buffer in the queue
327 expired_.push_back(buffer_);
328 buffer_ = queue_.front();
329 queue_.pop();
330 buffer_->beginStream(source_, 1);
331 }
332 else if (looping_)
333 {
334 // restart from the beginning
335 buffer_->rewind();
336 buffer_->stream(buffer);
337 alSourceQueueBuffers(source_, 1, &buffer);
338 }
339 }
340 else if (status == Buffer::STREAM_WRONG)
341 {
342 clear();
343 buffer_->beginStream(source_, 1);
344 }
345 }
346
347 ALenum state;
348 alGetSourcei(source_, AL_SOURCE_STATE, &state);
349
350 // restart playing if we're stopped but supposed to be playing... this
351 // means we didn't queue enough and the audio skipped
352 if (playing_ && state != AL_PLAYING)
353 {
354 alSourcePlay(source_);
355 }
356 }
357
358 inline void clear()
359 {
360 // try to remove expired buffers
361 std::vector<BufferP>::iterator it;
362 for (it = expired_.end() - 1; it >= expired_.begin(); --it)
363 {
364 if ((*it)->clear()) expired_.erase(it);
365 }
366 }
367
368
369 void stop()
370 {
371 alSourceStop(source_);
372 playing_ = false;
373 }
374
375 inline void pause()
376 {
377 alSourcePause(source_);
378 playing_ = false;
379 }
380
381 inline void resume()
382 {
383 alSourcePlay(source_);
384 playing_ = true;
385 }
386
387
388 inline void setSample(const std::string& name)
389 {
390 bool playing = isPlaying();
391 ALenum type;
392 alGetSourcei(source_, AL_SOURCE_TYPE, &type);
393
394 stop();
395
396 //alSourcei(source_, AL_BUFFER, AL_NONE);
397 buffer_ = Buffer::getInstance(name);
398
399 if (type == AL_STREAMING)
400 {
401 if (playing) stream();
402 }
403 else
404 {
405 if (playing) play();
406 }
407 }
408
409 inline void enqueue(const std::string& name)
410 {
411 BufferP buffer = Buffer::getInstance(name);
412 queue_.push(buffer);
413 }
414
415
416 inline bool isPlaying() const
417 {
418 if (playing_) return true;
419
420 ALenum state;
421 alGetSourcei(source_, AL_SOURCE_STATE, &state);
422
423 return state == AL_PLAYING;
424 }
425
426
427 inline void setLooping(bool looping)
428 {
429 looping_ = looping;
430
431 ALenum type;
432 alGetSourcei(source_, AL_SOURCE_TYPE, &type);
433
434 if (type != AL_STREAMING)
435 {
436 alSourcei(source_, AL_LOOPING, looping_);
437 }
438 }
439
440
441 ALuint source_;
442 BufferP buffer_;
443
444 bool playing_;
445 bool looping_;
446
447 std::queue<BufferP> queue_;
448 std::vector<BufferP> expired_;
449 };
450
451 Sound::Sound(const std::string& name) :
452 // pass through
453 impl_(new Sound::Impl(name)) {}
454
455
456 void Sound::play()
457 {
458 // pass through
459 impl_->play();
460 }
461
462
463 void Sound::stream()
464 {
465 // pass through
466 impl_->stream();
467 }
468
469 void Sound::update(Scalar t, Scalar dt)
470 {
471 // pass through
472 impl_->update();
473 }
474
475
476 void Sound::stop()
477 {
478 // pass through
479 impl_->stop();
480 }
481
482 void Sound::pause()
483 {
484 // pass through
485 impl_->pause();
486 }
487
488 void Sound::resume()
489 {
490 // pass through
491 impl_->resume();
492 }
493
494 void Sound::toggle()
495 {
496 if (impl_->playing_) pause();
497 else resume();
498 }
499
500
501 void Sound::setSample(const std::string& name)
502 {
503 // pass through
504 impl_->setSample(name);
505 }
506
507 void Sound::enqueue(const std::string& name)
508 {
509 // pass through
510 impl_->enqueue(name);
511 }
512
513
514 bool Sound::isPlaying() const
515 {
516 // pass through
517 return impl_->isPlaying();
518 }
519
520 void Sound::setPosition(Vector3 position)
521 {
522 float p[3] = {position[0], position[1], position[2]};
523 alSourcefv(impl_->source_, AL_POSITION, p);
524 }
525
526 void Sound::setVelocity(Vector3 velocity)
527 {
528 float v[3] = {velocity[0], velocity[1], velocity[2]};
529 alSourcefv(impl_->source_, AL_VELOCITY, v);
530 }
531
532 void Sound::setGain(Scalar gain)
533 {
534 alSourcef(impl_->source_, AL_GAIN, float(gain));
535 }
536
537 void Sound::setPitch(Scalar pitch)
538 {
539 alSourcef(impl_->source_, AL_PITCH, float(pitch));
540 }
541
542 void Sound::setLooping(bool looping)
543 {
544 // pass through
545 impl_->setLooping(looping);
546 }
547
548
549 std::string Sound::getPath(const std::string& name)
550 {
551 std::string path = Resource::getPath("sounds/" + name + ".ogg");
552 return path;
553 }
554
555
556 } // namespace Mf
557
558 /** vim: set ts=4 sw=4 tw=80: *************************************************/
559
This page took 0.061933 seconds and 5 git commands to generate.