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