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(name
);
79 if (!Sound::getPath(path
))
81 Error(Error::RESOURCE_NOT_FOUND
, path
).raise();
84 if (ov_fopen((char*)path
.c_str(), &mOggStream
) < 0)
86 Error(Error::UNKNOWN_AUDIO_FORMAT
, path
).raise();
89 vorbis_info
* vorbisInfo
= ov_info(&mOggStream
, -1);
90 mFormat
= getAudioFormat(vorbisInfo
);
91 mFreq
= vorbisInfo
->rate
;
95 void loadAll(ALuint source
)
97 if (!mOggStream
.datasource
) init(getName());
98 if (!mOggStream
.datasource
) return;
100 char data
[BUFFER_SIZE
];
106 int result
= ov_read(&mOggStream
, data
+ size
,
107 BUFFER_SIZE
- size
, 0, 2, 1, §ion
);
115 if (result
< 0) logWarning("vorbis playback error");
121 logWarning
<< "decoded no bytes from "
122 << getName() << std::endl
;
126 alGenBuffers(1, &mBuffer
);
128 alBufferData(mBuffer
, mFormat
, data
, size
, mFreq
);
129 alSourcei(source
, AL_BUFFER
, mBuffer
);
131 // don't need to keep this loaded
132 ov_clear(&mOggStream
);
133 mOggStream
.datasource
= 0;
136 bool stream(ALuint buffer
)
138 char data
[BUFFER_SIZE
];
141 while (size
< BUFFER_SIZE
)
144 int result
= ov_read(&mOggStream
, data
+ size
,
145 BUFFER_SIZE
- size
, 0, 2, 1, §ion
);
153 if (result
< 0) logWarning("vorbis playback error");
158 if (size
== 0) return false;
160 alBufferData(buffer
, mFormat
, data
, size
, mFreq
);
167 if (!mOggStream
.datasource
) init(getName());
168 else ov_raw_seek(&mOggStream
, 0);
174 OggVorbis_File mOggStream
;
186 Impl(const std::string
& name
)
200 alGenSources(1, &mSource
);
202 ALfloat zero
[] = {0.0f
, 0.0f
, 0.0f
};
203 alSourcef(mSource
, AL_PITCH
, 1.0f
);
204 alSourcef(mSource
, AL_GAIN
, 1.0f
);
205 alSourcefv(mSource
, AL_POSITION
, zero
);
206 alSourcefv(mSource
, AL_VELOCITY
, zero
);
208 alSourcei(mSource
, AL_LOOPING
, mIsLooping
);
215 alDeleteSources(1, &mSource
);
217 while (!mBuffers
.empty())
219 alDeleteBuffers(1, &mBuffers
.back());
229 if (mQueue
.empty()) return;
231 if (!mIsLoaded
) mQueue
.front()->loadAll(mSource
);
233 alSourcePlay(mSource
);
240 if (mQueue
.empty()) return;
244 alSourcei(mSource
, AL_LOOPING
, false);
248 if (!mStreamTimer
.isValid())
250 mStreamTimer
.init(boost::bind(&Impl::streamUpdate
, this, _1
, _2
),
254 alSourcePlay(mSource
);
261 for (int i
= mBuffers
.size(); i
<= 8; ++i
)
263 alGenBuffers(1, &buffer
);
265 if (mQueue
.front()->stream(buffer
))
267 alSourceQueueBuffers(mSource
, 1, &buffer
);
268 mBuffers
.push_back(buffer
);
272 alDeleteBuffers(1, &buffer
);
283 alGetSourcei(mSource
, AL_BUFFERS_PROCESSED
, &finished
);
285 while (finished
-- > 0)
288 alSourceUnqueueBuffers(mSource
, 1, &bufferObj
);
290 BufferP buffer
= mQueue
.front();
291 bool streamed
= buffer
->stream(bufferObj
);
295 alSourceQueueBuffers(mSource
, 1, &bufferObj
);
299 // the buffer couldn't be streamed, so get rid of it
304 // begin the next buffer in the queue
305 mQueue
.front()->rewind();
306 mQueue
.front()->stream(bufferObj
);
307 alSourceQueueBuffers(mSource
, 1, &bufferObj
);
308 logInfo("loading new buffer");
310 // queue up any unused buffers
315 // reload the same buffer
316 mQueue
.push_back(buffer
);
318 buffer
->stream(bufferObj
);
319 alSourceQueueBuffers(mSource
, 1, &bufferObj
);
320 logInfo("looping same buffer");
324 // nothing more to play, stopping...
326 std::remove(mBuffers
.begin(), mBuffers
.end(),
333 alGetSourcei(mSource
, AL_SOURCE_STATE
, &state
);
335 // restart playing if we're stopped but supposed to be playing...
336 // this means we didn't queue enough and the audio skipped :-(
337 if (mIsPlaying
&& state
!= AL_PLAYING
)
339 alSourcePlay(mSource
);
346 alSourceStop(mSource
);
349 mStreamTimer
.invalidate();
354 alSourcePause(mSource
);
357 mStreamTimer
.invalidate();
361 void setSample(const std::string
& name
)
364 alSourcei(mSource
, AL_BUFFER
, AL_NONE
);
371 while (!mBuffers
.empty())
373 alDeleteBuffers(1, &mBuffers
.back());
378 void enqueue(const std::string
& name
)
380 BufferP buffer
= Buffer::getInstance(name
);
381 mQueue
.push_back(buffer
);
385 bool isPlaying() const
387 if (mIsPlaying
) return true;
390 alGetSourcei(mSource
, AL_SOURCE_STATE
, &state
);
392 return state
== AL_PLAYING
;
396 void setLooping(bool looping
)
398 mIsLooping
= looping
;
401 alGetSourcei(mSource
, AL_SOURCE_TYPE
, &type
);
403 if (type
!= AL_STREAMING
)
405 alSourcei(mSource
, AL_LOOPING
, mIsLooping
);
410 void streamUpdate(Timer
& timer
, Scalar t
)
412 // don't let the music die!
414 // TODO - might be nice to also allow using threads for streaming
415 // rather than a timer, probably as a compile-time option
418 static void retainBackend()
420 if (gRetainCount
++ == 0)
422 gAlDevice
= alcOpenDevice(0);
423 gAlContext
= alcCreateContext(gAlDevice
, 0);
424 if (!gAlDevice
|| !gAlContext
)
426 const char* error
= alcGetString(gAlDevice
,
427 alcGetError(gAlDevice
));
428 logError
<< "audio subsystem initialization failure: "
429 << error
<< std::endl
;
433 alcMakeContextCurrent(gAlContext
);
434 logInfo
<< "opened sound device `"
435 << alcGetString(gAlDevice
,
436 ALC_DEFAULT_DEVICE_SPECIFIER
)
442 static void releaseBackend()
444 if (--gRetainCount
== 0)
446 alcMakeContextCurrent(0);
447 alcDestroyContext(gAlContext
);
448 alcCloseDevice(gAlDevice
);
454 std::list
<ALuint
> mBuffers
;
460 std::deque
<BufferP
> mQueue
;
464 static unsigned gRetainCount
;
465 static ALCdevice
* gAlDevice
;
466 static ALCcontext
* gAlContext
;
469 unsigned Sound::Impl::gRetainCount
= 0;
470 ALCdevice
* Sound::Impl::gAlDevice
= 0;
471 ALCcontext
* Sound::Impl::gAlContext
= 0;
476 mImpl(new Sound::Impl
) {}
478 Sound::Sound(const std::string
& name
) :
480 mImpl(new Sound::Impl(name
)) {}
483 void Sound::setSample(const std::string
& name
)
486 mImpl
->setSample(name
);
511 if (isPlaying()) pause();
515 bool Sound::isPlaying() const
518 return mImpl
->isPlaying();
522 void Sound::setPosition(const Vector3
& position
)
524 float vec
[3] = {position
[0], position
[1], position
[2]};
525 alSourcefv(mImpl
->mSource
, AL_POSITION
, vec
);
528 void Sound::setVelocity(const Vector3
& velocity
)
530 float vec
[3] = {velocity
[0], velocity
[1], velocity
[2]};
531 alSourcefv(mImpl
->mSource
, AL_VELOCITY
, vec
);
534 void Sound::setGain(Scalar gain
)
536 alSourcef(mImpl
->mSource
, AL_GAIN
, float(gain
));
539 void Sound::setPitch(Scalar pitch
)
541 alSourcef(mImpl
->mSource
, AL_PITCH
, float(pitch
));
544 void Sound::setLooping(bool looping
)
547 mImpl
->setLooping(looping
);
551 void Sound::setListenerPosition(const Vector3
& position
)
553 float vec
[] = {position
[0], position
[1], position
[2]};
554 alListenerfv(AL_POSITION
, vec
);
557 void Sound::setListenerVelocity(const Vector3
& velocity
)
559 float vec
[] = {velocity
[0], velocity
[1], velocity
[2]};
560 alListenerfv(AL_VELOCITY
, vec
);
563 void Sound::setListenerOrientation(const Vector3
& forward
,
567 vec
[0] = float(forward
[0]);
568 vec
[1] = float(forward
[1]);
569 vec
[2] = float(forward
[2]);
570 vec
[3] = float(up
[0]);
571 vec
[4] = float(up
[1]);
572 vec
[5] = float(up
[2]);
573 alListenerfv(AL_ORIENTATION
, vec
);
577 bool Sound::getPath(std::string
& name
)
579 return Resource::getPath(name
, "sounds/", "ogg");
583 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
586 void SoundStream::enqueue(const std::string
& name
)
589 mImpl
->enqueue(name
);
593 void SoundStream::play()