]> Dogcows Code - chaz/yoink/blob - src/Moof/Sound.cc
refactoring needed for win32 crash
[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 <deque>
31 #include <list>
32 #include <string>
33
34 #include <AL/al.h>
35 #include <vorbis/codec.h>
36 #include <vorbis/vorbisfile.h>
37
38 #include "Core.hh"
39 #include "Error.hh"
40 #include "Library.hh"
41 #include "Log.hh"
42 #include "Sound.hh"
43 #include "Timer.hh"
44
45 #define BUFFER_SIZE (64 * 1024)
46 //#define BUFFER_SIZE (5*2048)
47
48 namespace Mf {
49
50
51 class Sound::Impl
52 {
53 public:
54
55 static ALenum getAudioFormat(const vorbis_info* audioInfo)
56 {
57 if (audioInfo->channels == 1) return AL_FORMAT_MONO16;
58 else return AL_FORMAT_STEREO16;
59 }
60
61
62 class Buffer;
63 typedef boost::shared_ptr<Buffer> BufferP;
64
65 class Buffer : public Library<Buffer>
66 {
67 public:
68
69 explicit Buffer(const std::string& name) :
70 Library<Buffer>(name),
71 mBuffer(-1)
72 {
73 mOggStream.datasource = 0;
74 openFile();
75 }
76
77 ~Buffer()
78 {
79 if (mOggStream.datasource)
80 {
81 ov_clear(&mOggStream);
82 }
83 if (int(mBuffer) != -1) alDeleteBuffers(1, &mBuffer);
84 }
85
86
87 void openFile()
88 {
89 if (mOggStream.datasource)
90 {
91 ov_clear(&mOggStream);
92 mOggStream.datasource = 0;
93 }
94
95 std::string filePath = Sound::getPath(getName());
96 int result = ov_fopen((char*)filePath.c_str(), &mOggStream);
97
98 if (result < 0)
99 {
100 logWarning << "error while loading sound "
101 << getName() << std::endl;
102 throw Error(Error::UNKNOWN_AUDIO_FORMAT, getName());
103 }
104
105 vorbis_info* vorbisInfo = ov_info(&mOggStream, -1);
106 mFormat = getAudioFormat(vorbisInfo);
107 mFreq = vorbisInfo->rate;
108 }
109
110
111 void loadAll(ALuint source)
112 {
113 if (!mOggStream.datasource) openFile();
114 if (!mOggStream.datasource) return;
115
116 char data[BUFFER_SIZE];
117 int size = 0;
118
119 for (;;)
120 {
121 int section;
122 int result = ov_read(&mOggStream, data + size,
123 BUFFER_SIZE - size, 0, 2, 1, &section);
124
125 if (result > 0)
126 {
127 size += result;
128 }
129 else
130 {
131 if (result < 0) logWarning("vorbis playback error");
132 break;
133 }
134 }
135 if (size == 0)
136 {
137 logWarning << "decoded no bytes from "
138 << getName() << std::endl;
139 return;
140 }
141
142 alGenBuffers(1, &mBuffer);
143
144 alBufferData(mBuffer, mFormat, data, size, mFreq);
145 alSourcei(source, AL_BUFFER, mBuffer);
146
147 // don't need to keep this loaded
148 ov_clear(&mOggStream);
149 mOggStream.datasource = 0;
150 }
151
152 bool stream(ALuint buffer)
153 {
154 char data[BUFFER_SIZE];
155 int size = 0;
156
157 while (size < BUFFER_SIZE)
158 {
159 int section;
160 int result = ov_read(&mOggStream, data + size,
161 BUFFER_SIZE - size, 0, 2, 1, &section);
162
163 if (result > 0)
164 {
165 size += result;
166 }
167 else
168 {
169 if (result < 0) logWarning("vorbis playback error");
170 break;
171 }
172 }
173
174 if (size == 0) return false;
175
176 alBufferData(buffer, mFormat, data, size, mFreq);
177
178 return true;
179 }
180
181 void rewind()
182 {
183 if (!mOggStream.datasource) openFile();
184 else ov_raw_seek(&mOggStream, 0);
185 }
186
187
188 private:
189
190 OggVorbis_File mOggStream;
191 ALenum mFormat;
192 ALsizei mFreq;
193 ALuint mBuffer;
194 };
195
196
197 Impl()
198 {
199 init();
200 }
201
202 Impl(const std::string& name)
203 {
204 init();
205 enqueue(name);
206 }
207
208 void init()
209 {
210 mIsLoaded = false;
211 mIsPlaying = false;
212 mIsLooping = false;
213
214 alGenSources(1, &mSource);
215
216 ALfloat zero[] = {0.0f, 0.0f, 0.0f};
217 alSourcef(mSource, AL_PITCH, 1.0f);
218 alSourcef(mSource, AL_GAIN, 1.0f);
219 alSourcefv(mSource, AL_POSITION, zero);
220 alSourcefv(mSource, AL_VELOCITY, zero);
221
222 alSourcei(mSource, AL_LOOPING, mIsLooping);
223 }
224
225 ~Impl()
226 {
227 stop();
228
229 alDeleteSources(1, &mSource);
230
231 while (!mBuffers.empty())
232 {
233 alDeleteBuffers(1, &mBuffers.back());
234 mBuffers.pop_back();
235 }
236 }
237
238
239 void play()
240 {
241 if (mQueue.empty()) return;
242
243 if (!mIsLoaded) mQueue.front()->loadAll(mSource);
244
245 alSourcePlay(mSource);
246 mIsLoaded = true;
247 }
248
249
250 void playStream()
251 {
252 if (mQueue.empty()) return;
253
254 if (!mIsPlaying)
255 {
256 alSourcei(mSource, AL_LOOPING, false);
257 bufferStream();
258 }
259
260 if (!mStreamTimer.isValid())
261 {
262 mStreamTimer.init(boost::bind(&Impl::streamUpdate, this, _1, _2),
263 1.0, Timer::REPEAT);
264 }
265
266 alSourcePlay(mSource);
267 mIsPlaying = true;
268 }
269
270 void bufferStream()
271 {
272 ALuint buffer;
273 for (int i = mBuffers.size(); i <= 8; ++i)
274 {
275 alGenBuffers(1, &buffer);
276
277 if (mQueue.front()->stream(buffer))
278 {
279 alSourceQueueBuffers(mSource, 1, &buffer);
280 mBuffers.push_back(buffer);
281 }
282 else
283 {
284 alDeleteBuffers(1, &buffer);
285 break;
286 }
287 }
288 }
289
290
291 void update()
292 {
293 ALint finished = 0;
294
295 alGetSourcei(mSource, AL_BUFFERS_PROCESSED, &finished);
296
297 while (finished-- > 0)
298 {
299 ALuint bufferObj;
300 alSourceUnqueueBuffers(mSource, 1, &bufferObj);
301
302 BufferP buffer = mQueue.front();
303 bool streamed = buffer->stream(bufferObj);
304
305 if (streamed)
306 {
307 alSourceQueueBuffers(mSource, 1, &bufferObj);
308 }
309 else
310 {
311 // the buffer couldn't be streamed, so get rid of it
312 mQueue.pop_front();
313
314 if (!mQueue.empty())
315 {
316 // begin the next buffer in the queue
317 mQueue.front()->rewind();
318 mQueue.front()->stream(bufferObj);
319 alSourceQueueBuffers(mSource, 1, &bufferObj);
320 logInfo("loading new buffer");
321
322 // queue up any unused buffers
323 bufferStream();
324 }
325 else if (mIsLooping)
326 {
327 // reload the same buffer
328 mQueue.push_back(buffer);
329 buffer->rewind();
330 buffer->stream(bufferObj);
331 alSourceQueueBuffers(mSource, 1, &bufferObj);
332 logInfo("looping same buffer");
333 }
334 else
335 {
336 // nothing more to play, stopping...
337 mIsPlaying = false;
338 std::remove(mBuffers.begin(), mBuffers.end(), bufferObj);
339 }
340 }
341 }
342
343 ALenum state;
344 alGetSourcei(mSource, AL_SOURCE_STATE, &state);
345
346 // restart playing if we're stopped but supposed to be playing... this
347 // means we didn't queue enough and the audio skipped :-(
348 if (mIsPlaying && state != AL_PLAYING)
349 {
350 alSourcePlay(mSource);
351 }
352 }
353
354
355 void stop()
356 {
357 alSourceStop(mSource);
358 mIsPlaying = false;
359
360 mStreamTimer.invalidate();
361 }
362
363 void pause()
364 {
365 alSourcePause(mSource);
366 mIsPlaying = false;
367
368 mStreamTimer.invalidate();
369 }
370
371
372 void setSample(const std::string& name)
373 {
374 stop();
375 alSourcei(mSource, AL_BUFFER, AL_NONE);
376
377 mQueue.clear();
378 mIsLoaded = false;
379
380 enqueue(name);
381
382 while (!mBuffers.empty())
383 {
384 alDeleteBuffers(1, &mBuffers.back());
385 mBuffers.pop_back();
386 }
387 }
388
389 void enqueue(const std::string& name)
390 {
391 BufferP buffer = Buffer::getInstance(name);
392 mQueue.push_back(buffer);
393 }
394
395
396 bool isPlaying() const
397 {
398 if (mIsPlaying) return true;
399
400 ALenum state;
401 alGetSourcei(mSource, AL_SOURCE_STATE, &state);
402
403 return state == AL_PLAYING;
404 }
405
406
407 void setLooping(bool looping)
408 {
409 mIsLooping = looping;
410
411 ALenum type;
412 alGetSourcei(mSource, AL_SOURCE_TYPE, &type);
413
414 if (type != AL_STREAMING)
415 {
416 alSourcei(mSource, AL_LOOPING, mIsLooping);
417 }
418 }
419
420
421 void streamUpdate(Timer& timer, Scalar t)
422 {
423 // don't let the music die!
424 update();
425 // TODO - might be nice to also allow using threads for streaming rather
426 // than a timer, probably as a compile-time option
427 }
428
429
430 ALuint mSource;
431 std::list<ALuint> mBuffers;
432
433 bool mIsLoaded;
434 bool mIsPlaying;
435 bool mIsLooping;
436
437 std::deque<BufferP> mQueue;
438
439 Timer mStreamTimer;
440
441 Backend mBackend;
442 };
443
444
445 Sound::Sound() :
446 // pass through
447 mImpl(new Sound::Impl) {}
448
449 Sound::Sound(const std::string& name) :
450 // pass through
451 mImpl(new Sound::Impl(name)) {}
452
453
454 void Sound::setSample(const std::string& name)
455 {
456 // pass through
457 mImpl->setSample(name);
458 }
459
460
461 void Sound::play()
462 {
463 // pass through
464 mImpl->play();
465 }
466
467 void Sound::stop()
468 {
469 // pass through
470 mImpl->stop();
471 }
472
473 void Sound::pause()
474 {
475 // pass through
476 mImpl->pause();
477 }
478
479
480 void Sound::toggle()
481 {
482 if (isPlaying()) pause();
483 else play();
484 }
485
486 bool Sound::isPlaying() const
487 {
488 // pass through
489 return mImpl->isPlaying();
490 }
491
492
493 void Sound::setPosition(const Vector3& position)
494 {
495 float vec[3] = {position[0], position[1], position[2]};
496 alSourcefv(mImpl->mSource, AL_POSITION, vec);
497 }
498
499 void Sound::setVelocity(const Vector3& velocity)
500 {
501 float vec[3] = {velocity[0], velocity[1], velocity[2]};
502 alSourcefv(mImpl->mSource, AL_VELOCITY, vec);
503 }
504
505 void Sound::setGain(Scalar gain)
506 {
507 alSourcef(mImpl->mSource, AL_GAIN, float(gain));
508 }
509
510 void Sound::setPitch(Scalar pitch)
511 {
512 alSourcef(mImpl->mSource, AL_PITCH, float(pitch));
513 }
514
515 void Sound::setLooping(bool looping)
516 {
517 // pass through
518 mImpl->setLooping(looping);
519 }
520
521
522 void Sound::setListenerPosition(const Vector3& position)
523 {
524 //alListener3f(AL_POSITION, float(position[0]), float(position[1]),
525 //float(position[2]));
526 float vec[] = {position[0], position[1], position[2]};
527 alListenerfv(AL_POSITION, vec);
528 }
529
530 void Sound::setListenerVelocity(const Vector3& velocity)
531 {
532 //alListener3f(AL_VELOCITY, float(velocity[0]), float(velocity[1]),
533 //float(velocity[2]));
534 float vec[] = {velocity[0], velocity[1], velocity[2]};
535 alListenerfv(AL_VELOCITY, vec);
536 }
537
538 void Sound::setListenerOrientation(const Vector3& forward, const Vector3& up)
539 {
540 float vec[6];
541 vec[0] = float(forward[0]);
542 vec[1] = float(forward[1]);
543 vec[2] = float(forward[2]);
544 vec[3] = float(up[0]);
545 vec[4] = float(up[1]);
546 vec[5] = float(up[2]);
547 alListenerfv(AL_ORIENTATION, vec);
548 }
549
550
551 std::string Sound::getPath(const std::string& name)
552 {
553 std::string path = Resource::getPath("sounds/" + name + ".ogg");
554 return path;
555 }
556
557
558 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
559
560
561 void SoundStream::enqueue(const std::string& name)
562 {
563 // pass through
564 mImpl->enqueue(name);
565 }
566
567
568 void SoundStream::play()
569 {
570 // pass through
571 mImpl->playStream();
572 }
573
574
575 } // namespace Mf
576
577 /** vim: set ts=4 sw=4 tw=80: *************************************************/
578
This page took 0.057334 seconds and 4 git commands to generate.