moving from SDL_sound to libvorbisfile
[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 FILE* soundFile;
63 OggVorbis_File oggStream;
64 ALenum audioFormat;
65 ALsizei audioFreq;
66 std::vector<ALuint> objects;
67
68 public:
69
70 Buffer(const std::string& name) :
71 Mippleton<Buffer>(name),
72 soundFile(0)
73 {
74 openFile();
75 }
76
77 ~Buffer()
78 {
79 while (!objects.empty())
80 {
81 alDeleteBuffers(1, &objects.back());
82 objects.pop_back();
83 }
84
85 if (soundFile)
86 {
87 ov_clear(&oggStream);
88 }
89 }
90
91
92 void openFile()
93 {
94 if (soundFile)
95 {
96 ov_clear(&oggStream);
97 soundFile = 0;
98 }
99
100 //soundFile = Sound_NewSampleFromFile(soundFile::getPath(getName()).c_str(),
101 //0, BUFFER_SIZE);
102 soundFile = fopen(Sound::getPath(getName()).c_str(), "rb");
103
104 if (!soundFile)
105 {
106 logWarning("error while loading sound %s", getName().c_str());
107 throw Exception(Exception::FILE_NOT_FOUND);
108 }
109
110 int result = ov_open(soundFile, &oggStream, NULL, 0);
111
112 if (result < 0)
113 {
114 fclose(soundFile);
115 soundFile = 0;
116
117 logWarning("error while loading oggvorbis stream %s",
118 getName().c_str());
119 throw Exception(Exception::BAD_AUDIO_FORMAT);
120 }
121
122 vorbis_info* vorbisInfo = ov_info(&oggStream, -1);
123 audioFormat = getAudioFormat(vorbisInfo);
124 audioFreq = vorbisInfo->rate;
125
126 logDebug(" version: %d", vorbisInfo->version);
127 logDebug(" channels: %d", vorbisInfo->channels);
128 logDebug(" frequency: %d", vorbisInfo->rate);
129 }
130
131
132 void loadAll(ALuint source)
133 {
134 if (!soundFile) openFile();
135 if (!soundFile) return;
136
137 char data[BUFFER_SIZE];
138 int size = 0;
139
140 //unsigned decoded = Sound_DecodeAll(soundFile);
141 for (;;)
142 {
143 int section;
144 int result = ov_read(&oggStream, data + size,
145 BUFFER_SIZE - size, 0, 2, 1, &section);
146
147 if (result > 0)
148 {
149 size += result;
150 }
151 else
152 {
153 if (result < 0) logWarning("vorbis playback error");
154 break;
155 }
156 }
157 if (size == 0)
158 {
159 logWarning("decoded no bytes from %s", getName().c_str());
160 //throw Exception(Exception::FILE_NOT_FOUND);
161 return;
162 }
163
164 ALuint obj;
165 alGenBuffers(1, &obj);
166
167 alBufferData(obj, audioFormat, data, size, audioFreq);
168
169 objects.push_back(obj);
170
171 alSourcei(source, AL_BUFFER, obj);
172
173 // don't need this anymore
174 ov_clear(&oggStream);
175 soundFile = 0;
176 }
177
178
179 void beginStream(ALuint source, int nBuffers = 4)
180 {
181 if (!soundFile) openFile();
182 if (!soundFile) return;
183
184 ALuint objs[nBuffers];
185 alGenBuffers(nBuffers, objs);
186
187 for (int i = 0; i < nBuffers; ++i)
188 {
189 objects.push_back(objs[i]);
190 stream(objs[i]);
191 }
192
193 alSourceQueueBuffers(source, nBuffers, objs);
194 }
195
196 enum StreamStatus
197 {
198 STREAM_OK = 0,
199 STREAM_EOF = 1,
200 STREAM_WRONG = 2
201 };
202
203 StreamStatus stream(ALuint buffer)
204 {
205 std::vector<ALuint>::iterator it =
206 std::find(objects.begin(), objects.end(), buffer);
207
208 // that buffer doesn't belong to us
209 if (it == objects.end()) return STREAM_WRONG;
210
211 char data[BUFFER_SIZE];
212 int size = 0;
213
214 //unsigned bytes = Sound_Decode(soundFile);
215 while (size < BUFFER_SIZE)
216 {
217 int section;
218 int result = ov_read(&oggStream, data + size,
219 BUFFER_SIZE - size, 0, 2, 1, &section);
220
221 if (result > 0)
222 {
223 size += result;
224 }
225 else
226 {
227 if (result < 0) logWarning("vorbis playback error");
228 break;
229 }
230 }
231
232 if (size == 0) return STREAM_EOF;
233
234 alBufferData(buffer, audioFormat, data, size, audioFreq);
235
236 return STREAM_OK;
237 }
238
239 inline void rewind()
240 {
241 if (!soundFile) openFile();
242 else ov_raw_seek(&oggStream, 0);
243 }
244
245
246 // delete unused buffers, return true if all buffers deleted
247 inline bool clear()
248 {
249 // clear any openal errors
250 alGetError();
251
252 while (!objects.empty())
253 {
254 ALuint buffer = objects.back();
255 alDeleteBuffers(1, &buffer);
256
257 // if an error occured, the buffer was not deleted because it's
258 // still in use by some source
259 if (alGetError() != AL_NO_ERROR) return false;
260
261 objects.pop_back();
262 }
263
264 return true;
265 }
266 };
267
268
269 Impl(const std::string& name) :
270 buffer_(Buffer::getInstance(name)),
271 playing_(false),
272 looping_(false)
273 {
274 ALfloat zero[] = {0.0f, 0.0f, 0.0f};
275
276 alGenSources(1, &source_);
277
278 alSourcef(source_, AL_PITCH, 1.0f);
279 alSourcef(source_, AL_GAIN, 1.0f);
280 alSourcefv(source_, AL_POSITION, zero);
281 alSourcefv(source_, AL_VELOCITY, zero);
282 }
283
284 ~Impl()
285 {
286 alDeleteSources(1, &source_);
287 }
288
289
290 void play()
291 {
292 ALenum type;
293 alGetSourcei(source_, AL_SOURCE_TYPE, &type);
294
295 if (type != AL_STATIC)
296 {
297 buffer_->loadAll(source_);
298 }
299
300 alSourcei(source_, AL_LOOPING, looping_);
301 alSourcePlay(source_);
302 playing_ = true;
303 }
304
305
306 void stream()
307 {
308 ALenum type;
309 alGetSourcei(source_, AL_SOURCE_TYPE, &type);
310
311 alSourcei(source_, AL_BUFFER, AL_NONE);
312 buffer_->rewind();
313 buffer_->beginStream(source_);
314
315 alSourcei(source_, AL_LOOPING, AL_FALSE);
316 alSourcePlay(source_);
317 playing_ = true;
318 }
319
320 inline void update()
321 {
322 ALint finished = 0;
323
324 alGetSourcei(source_, AL_BUFFERS_PROCESSED, &finished);
325
326 while (finished-- > 0)
327 {
328 ALuint buffer;
329
330 alSourceUnqueueBuffers(source_, 1, &buffer);
331
332 Buffer::StreamStatus status = buffer_->stream(buffer);
333
334 if (status == Buffer::STREAM_OK)
335 {
336 alSourceQueueBuffers(source_, 1, &buffer);
337 }
338 else if (status == Buffer::STREAM_EOF)
339 {
340 if (!queue_.empty())
341 {
342 // begin the next buffer in the queue
343 expired_.push_back(buffer_);
344 buffer_ = queue_.front();
345 queue_.pop();
346 buffer_->beginStream(source_, 1);
347 }
348 else if (looping_)
349 {
350 // restart from the beginning
351 buffer_->rewind();
352 buffer_->stream(buffer);
353 alSourceQueueBuffers(source_, 1, &buffer);
354 }
355 }
356 else if (status == Buffer::STREAM_WRONG)
357 {
358 clear();
359 buffer_->beginStream(source_, 1);
360 }
361 }
362
363 ALenum state;
364 alGetSourcei(source_, AL_SOURCE_STATE, &state);
365
366 // restart playing if we're stopped but supposed to be playing... this
367 // means we didn't queue enough and the audio skipped
368 if (playing_ && state != AL_PLAYING)
369 {
370 alSourcePlay(source_);
371 }
372 }
373
374 inline void clear()
375 {
376 // try to remove expired buffers
377 std::vector<BufferP>::iterator it;
378 for (it = expired_.end() - 1; it >= expired_.begin(); --it)
379 {
380 if ((*it)->clear()) expired_.erase(it);
381 }
382 }
383
384
385 void stop()
386 {
387 alSourceStop(source_);
388 playing_ = false;
389 }
390
391 inline void pause()
392 {
393 alSourcePause(source_);
394 playing_ = false;
395 }
396
397 inline void resume()
398 {
399 alSourcePlay(source_);
400 playing_ = true;
401 }
402
403
404 inline void setSample(const std::string& name)
405 {
406 bool playing = isPlaying();
407 ALenum type;
408 alGetSourcei(source_, AL_SOURCE_TYPE, &type);
409
410 stop();
411
412 //alSourcei(source_, AL_BUFFER, AL_NONE);
413 buffer_ = Buffer::getInstance(name);
414
415 if (type == AL_STREAMING)
416 {
417 if (playing) stream();
418 }
419 else
420 {
421 if (playing) play();
422 }
423 }
424
425 inline void enqueue(const std::string& name)
426 {
427 BufferP buffer = Buffer::getInstance(name);
428 queue_.push(buffer);
429 }
430
431
432 inline bool isPlaying() const
433 {
434 if (playing_) return true;
435
436 ALenum state;
437 alGetSourcei(source_, AL_SOURCE_STATE, &state);
438
439 return state == AL_PLAYING;
440 }
441
442
443 inline void setLooping(bool looping)
444 {
445 looping_ = looping;
446
447 ALenum type;
448 alGetSourcei(source_, AL_SOURCE_TYPE, &type);
449
450 if (type != AL_STREAMING)
451 {
452 alSourcei(source_, AL_LOOPING, looping_);
453 }
454 }
455
456
457 ALuint source_;
458 BufferP buffer_;
459
460 bool playing_;
461 bool looping_;
462
463 std::queue<BufferP> queue_;
464 std::vector<BufferP> expired_;
465 };
466
467 Sound::Sound(const std::string& name) :
468 // pass through
469 impl_(new Sound::Impl(name)) {}
470
471
472 void Sound::play()
473 {
474 // pass through
475 impl_->play();
476 }
477
478
479 void Sound::stream()
480 {
481 // pass through
482 impl_->stream();
483 }
484
485 void Sound::update(Scalar t, Scalar dt)
486 {
487 // pass through
488 impl_->update();
489 }
490
491
492 void Sound::stop()
493 {
494 // pass through
495 impl_->stop();
496 }
497
498 void Sound::pause()
499 {
500 // pass through
501 impl_->pause();
502 }
503
504 void Sound::resume()
505 {
506 // pass through
507 impl_->resume();
508 }
509
510 void Sound::toggle()
511 {
512 if (impl_->playing_) pause();
513 else resume();
514 }
515
516
517 void Sound::setSample(const std::string& name)
518 {
519 // pass through
520 impl_->setSample(name);
521 }
522
523 void Sound::enqueue(const std::string& name)
524 {
525 // pass through
526 impl_->enqueue(name);
527 }
528
529
530 bool Sound::isPlaying() const
531 {
532 // pass through
533 return impl_->isPlaying();
534 }
535
536 void Sound::setPosition(Vector3 position)
537 {
538 float p[3] = {position[0], position[1], position[2]};
539 alSourcefv(impl_->source_, AL_POSITION, p);
540 }
541
542 void Sound::setVelocity(Vector3 velocity)
543 {
544 float v[3] = {velocity[0], velocity[1], velocity[2]};
545 alSourcefv(impl_->source_, AL_VELOCITY, v);
546 }
547
548 void Sound::setGain(Scalar gain)
549 {
550 alSourcef(impl_->source_, AL_GAIN, float(gain));
551 }
552
553 void Sound::setPitch(Scalar pitch)
554 {
555 alSourcef(impl_->source_, AL_PITCH, float(pitch));
556 }
557
558 void Sound::setLooping(bool looping)
559 {
560 // pass through
561 impl_->setLooping(looping);
562 }
563
564
565 std::string Sound::getPath(const std::string& name)
566 {
567 std::string path = Resource::getPath("sounds/" + name + ".ogg");
568 return path;
569 }
570
571
572 } // namespace Mf
573
574 /** vim: set ts=4 sw=4 tw=80: *************************************************/
575
This page took 0.061456 seconds and 4 git commands to generate.