2 /*] Copyright (c) 2009-2010, Charles McGarvey [**************************
3 **] All rights reserved.
7 * Distributable under the terms and conditions of the 2-clause BSD license;
8 * see the file COPYING for a complete text of the license.
10 **************************************************************************/
17 #include <boost/algorithm/string.hpp>
21 #include <vorbis/codec.h>
22 #include <vorbis/vorbisfile.h>
30 #define BUFFER_SIZE (64 * 1024)
31 //#define BUFFER_SIZE (5*2048)
40 static ALenum
getAudioFormat(const vorbis_info
* audioInfo
)
42 if (audioInfo
->channels
== 1) return AL_FORMAT_MONO16
;
43 else return AL_FORMAT_STEREO16
;
48 typedef boost::shared_ptr
<Buffer
> BufferP
;
50 class Buffer
: public Manager
<Buffer
>
57 mOggStream
.datasource
= 0;
62 if (mOggStream
.datasource
)
64 ov_clear(&mOggStream
);
66 if (int(mBuffer
) != -1) alDeleteBuffers(1, &mBuffer
);
70 void init(const std::string
& name
)
72 if (mOggStream
.datasource
)
74 ov_clear(&mOggStream
);
75 mOggStream
.datasource
= 0;
78 std::string path
= Sound::getPath(name
);
79 int result
= ov_fopen((char*)path
.c_str(), &mOggStream
);
83 logWarning
<< "couldn't load sound: " << path
<< std::endl
;
84 Error(Error::UNKNOWN_AUDIO_FORMAT
, path
).raise();
87 vorbis_info
* vorbisInfo
= ov_info(&mOggStream
, -1);
88 mFormat
= getAudioFormat(vorbisInfo
);
89 mFreq
= vorbisInfo
->rate
;
93 void loadAll(ALuint source
)
95 if (!mOggStream
.datasource
) init(getName());
96 if (!mOggStream
.datasource
) return;
98 char data
[BUFFER_SIZE
];
104 int result
= ov_read(&mOggStream
, data
+ size
,
105 BUFFER_SIZE
- size
, 0, 2, 1, §ion
);
113 if (result
< 0) logWarning("vorbis playback error");
119 logWarning
<< "decoded no bytes from "
120 << getName() << std::endl
;
124 alGenBuffers(1, &mBuffer
);
126 alBufferData(mBuffer
, mFormat
, data
, size
, mFreq
);
127 alSourcei(source
, AL_BUFFER
, mBuffer
);
129 // don't need to keep this loaded
130 ov_clear(&mOggStream
);
131 mOggStream
.datasource
= 0;
134 bool stream(ALuint buffer
)
136 char data
[BUFFER_SIZE
];
139 while (size
< BUFFER_SIZE
)
142 int result
= ov_read(&mOggStream
, data
+ size
,
143 BUFFER_SIZE
- size
, 0, 2, 1, §ion
);
151 if (result
< 0) logWarning("vorbis playback error");
156 if (size
== 0) return false;
158 alBufferData(buffer
, mFormat
, data
, size
, mFreq
);
165 if (!mOggStream
.datasource
) init(getName());
166 else ov_raw_seek(&mOggStream
, 0);
172 OggVorbis_File mOggStream
;
184 Impl(const std::string
& name
)
198 alGenSources(1, &mSource
);
200 ALfloat zero
[] = {0.0f
, 0.0f
, 0.0f
};
201 alSourcef(mSource
, AL_PITCH
, 1.0f
);
202 alSourcef(mSource
, AL_GAIN
, 1.0f
);
203 alSourcefv(mSource
, AL_POSITION
, zero
);
204 alSourcefv(mSource
, AL_VELOCITY
, zero
);
206 alSourcei(mSource
, AL_LOOPING
, mIsLooping
);
213 alDeleteSources(1, &mSource
);
215 while (!mBuffers
.empty())
217 alDeleteBuffers(1, &mBuffers
.back());
227 if (mQueue
.empty()) return;
229 if (!mIsLoaded
) mQueue
.front()->loadAll(mSource
);
231 alSourcePlay(mSource
);
238 if (mQueue
.empty()) return;
242 alSourcei(mSource
, AL_LOOPING
, false);
246 if (!mStreamTimer
.isValid())
248 mStreamTimer
.init(boost::bind(&Impl::streamUpdate
, this, _1
, _2
),
252 alSourcePlay(mSource
);
259 for (int i
= mBuffers
.size(); i
<= 8; ++i
)
261 alGenBuffers(1, &buffer
);
263 if (mQueue
.front()->stream(buffer
))
265 alSourceQueueBuffers(mSource
, 1, &buffer
);
266 mBuffers
.push_back(buffer
);
270 alDeleteBuffers(1, &buffer
);
281 alGetSourcei(mSource
, AL_BUFFERS_PROCESSED
, &finished
);
283 while (finished
-- > 0)
286 alSourceUnqueueBuffers(mSource
, 1, &bufferObj
);
288 BufferP buffer
= mQueue
.front();
289 bool streamed
= buffer
->stream(bufferObj
);
293 alSourceQueueBuffers(mSource
, 1, &bufferObj
);
297 // the buffer couldn't be streamed, so get rid of it
302 // begin the next buffer in the queue
303 mQueue
.front()->rewind();
304 mQueue
.front()->stream(bufferObj
);
305 alSourceQueueBuffers(mSource
, 1, &bufferObj
);
306 logInfo("loading new buffer");
308 // queue up any unused buffers
313 // reload the same buffer
314 mQueue
.push_back(buffer
);
316 buffer
->stream(bufferObj
);
317 alSourceQueueBuffers(mSource
, 1, &bufferObj
);
318 logInfo("looping same buffer");
322 // nothing more to play, stopping...
324 std::remove(mBuffers
.begin(), mBuffers
.end(),
331 alGetSourcei(mSource
, AL_SOURCE_STATE
, &state
);
333 // restart playing if we're stopped but supposed to be playing...
334 // this means we didn't queue enough and the audio skipped :-(
335 if (mIsPlaying
&& state
!= AL_PLAYING
)
337 alSourcePlay(mSource
);
344 alSourceStop(mSource
);
347 mStreamTimer
.invalidate();
352 alSourcePause(mSource
);
355 mStreamTimer
.invalidate();
359 void setSample(const std::string
& name
)
362 alSourcei(mSource
, AL_BUFFER
, AL_NONE
);
369 while (!mBuffers
.empty())
371 alDeleteBuffers(1, &mBuffers
.back());
376 void enqueue(const std::string
& name
)
378 BufferP buffer
= Buffer::getInstance(name
);
379 mQueue
.push_back(buffer
);
383 bool isPlaying() const
385 if (mIsPlaying
) return true;
388 alGetSourcei(mSource
, AL_SOURCE_STATE
, &state
);
390 return state
== AL_PLAYING
;
394 void setLooping(bool looping
)
396 mIsLooping
= looping
;
399 alGetSourcei(mSource
, AL_SOURCE_TYPE
, &type
);
401 if (type
!= AL_STREAMING
)
403 alSourcei(mSource
, AL_LOOPING
, mIsLooping
);
408 void streamUpdate(Timer
& timer
, Scalar t
)
410 // don't let the music die!
412 // TODO - might be nice to also allow using threads for streaming
413 // rather than a timer, probably as a compile-time option
416 static void retainBackend()
418 if (gRetainCount
++ == 0)
420 gAlDevice
= alcOpenDevice(0);
421 gAlContext
= alcCreateContext(gAlDevice
, 0);
422 if (!gAlDevice
|| !gAlContext
)
424 const char* error
= alcGetString(gAlDevice
,
425 alcGetError(gAlDevice
));
426 logError
<< "audio subsystem initialization failure: "
427 << error
<< std::endl
;
431 alcMakeContextCurrent(gAlContext
);
432 logInfo
<< "opened sound device `"
433 << alcGetString(gAlDevice
,
434 ALC_DEFAULT_DEVICE_SPECIFIER
)
440 static void releaseBackend()
442 if (--gRetainCount
== 0)
444 alcMakeContextCurrent(0);
445 alcDestroyContext(gAlContext
);
446 alcCloseDevice(gAlDevice
);
452 std::list
<ALuint
> mBuffers
;
458 std::deque
<BufferP
> mQueue
;
462 static unsigned gRetainCount
;
463 static ALCdevice
* gAlDevice
;
464 static ALCcontext
* gAlContext
;
467 unsigned Sound::Impl::gRetainCount
= 0;
468 ALCdevice
* Sound::Impl::gAlDevice
= 0;
469 ALCcontext
* Sound::Impl::gAlContext
= 0;
474 mImpl(new Sound::Impl
) {}
476 Sound::Sound(const std::string
& name
) :
478 mImpl(new Sound::Impl(name
)) {}
481 void Sound::setSample(const std::string
& name
)
484 mImpl
->setSample(name
);
509 if (isPlaying()) pause();
513 bool Sound::isPlaying() const
516 return mImpl
->isPlaying();
520 void Sound::setPosition(const Vector3
& position
)
522 float vec
[3] = {position
[0], position
[1], position
[2]};
523 alSourcefv(mImpl
->mSource
, AL_POSITION
, vec
);
526 void Sound::setVelocity(const Vector3
& velocity
)
528 float vec
[3] = {velocity
[0], velocity
[1], velocity
[2]};
529 alSourcefv(mImpl
->mSource
, AL_VELOCITY
, vec
);
532 void Sound::setGain(Scalar gain
)
534 alSourcef(mImpl
->mSource
, AL_GAIN
, float(gain
));
537 void Sound::setPitch(Scalar pitch
)
539 alSourcef(mImpl
->mSource
, AL_PITCH
, float(pitch
));
542 void Sound::setLooping(bool looping
)
545 mImpl
->setLooping(looping
);
549 void Sound::setListenerPosition(const Vector3
& position
)
551 float vec
[] = {position
[0], position
[1], position
[2]};
552 alListenerfv(AL_POSITION
, vec
);
555 void Sound::setListenerVelocity(const Vector3
& velocity
)
557 float vec
[] = {velocity
[0], velocity
[1], velocity
[2]};
558 alListenerfv(AL_VELOCITY
, vec
);
561 void Sound::setListenerOrientation(const Vector3
& forward
,
565 vec
[0] = float(forward
[0]);
566 vec
[1] = float(forward
[1]);
567 vec
[2] = float(forward
[2]);
568 vec
[3] = float(up
[0]);
569 vec
[4] = float(up
[1]);
570 vec
[5] = float(up
[2]);
571 alListenerfv(AL_ORIENTATION
, vec
);
575 std::string
Sound::getPath(const std::string
& name
)
577 if (boost::find_last(name
, ".ogg"))
579 return Resource::getPath(name
);
583 std::string
path("sounds/");
586 return Resource::getPath(path
);
591 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
594 void SoundStream::enqueue(const std::string
& name
)
597 mImpl
->enqueue(name
);
601 void SoundStream::play()