2 /*******************************************************************************
4 Copyright (c) 2009, Charles McGarvey
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions are met:
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.
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.
27 *******************************************************************************/
34 #include <boost/algorithm/string.hpp>
38 #include <vorbis/codec.h>
39 #include <vorbis/vorbisfile.h>
47 #define BUFFER_SIZE (64 * 1024)
48 //#define BUFFER_SIZE (5*2048)
57 static ALenum
getAudioFormat(const vorbis_info
* audioInfo
)
59 if (audioInfo
->channels
== 1) return AL_FORMAT_MONO16
;
60 else return AL_FORMAT_STEREO16
;
65 typedef boost::shared_ptr
<Buffer
> BufferP
;
67 class Buffer
: public Manager
<Buffer
>
74 mOggStream
.datasource
= 0;
79 if (mOggStream
.datasource
)
81 ov_clear(&mOggStream
);
83 if (int(mBuffer
) != -1) alDeleteBuffers(1, &mBuffer
);
87 void init(const std::string
& name
)
89 if (mOggStream
.datasource
)
91 ov_clear(&mOggStream
);
92 mOggStream
.datasource
= 0;
95 std::string path
= Sound::getPath(name
);
96 int result
= ov_fopen((char*)path
.c_str(), &mOggStream
);
100 logWarning
<< "couldn't load sound: " << path
<< std::endl
;
101 throw Error(Error::UNKNOWN_AUDIO_FORMAT
, path
);
104 vorbis_info
* vorbisInfo
= ov_info(&mOggStream
, -1);
105 mFormat
= getAudioFormat(vorbisInfo
);
106 mFreq
= vorbisInfo
->rate
;
110 void loadAll(ALuint source
)
112 if (!mOggStream
.datasource
) init(getName());
113 if (!mOggStream
.datasource
) return;
115 char data
[BUFFER_SIZE
];
121 int result
= ov_read(&mOggStream
, data
+ size
,
122 BUFFER_SIZE
- size
, 0, 2, 1, §ion
);
130 if (result
< 0) logWarning("vorbis playback error");
136 logWarning
<< "decoded no bytes from "
137 << getName() << std::endl
;
141 alGenBuffers(1, &mBuffer
);
143 alBufferData(mBuffer
, mFormat
, data
, size
, mFreq
);
144 alSourcei(source
, AL_BUFFER
, mBuffer
);
146 // don't need to keep this loaded
147 ov_clear(&mOggStream
);
148 mOggStream
.datasource
= 0;
151 bool stream(ALuint buffer
)
153 char data
[BUFFER_SIZE
];
156 while (size
< BUFFER_SIZE
)
159 int result
= ov_read(&mOggStream
, data
+ size
,
160 BUFFER_SIZE
- size
, 0, 2, 1, §ion
);
168 if (result
< 0) logWarning("vorbis playback error");
173 if (size
== 0) return false;
175 alBufferData(buffer
, mFormat
, data
, size
, mFreq
);
182 if (!mOggStream
.datasource
) init(getName());
183 else ov_raw_seek(&mOggStream
, 0);
189 OggVorbis_File mOggStream
;
201 Impl(const std::string
& name
)
215 alGenSources(1, &mSource
);
217 ALfloat zero
[] = {0.0f
, 0.0f
, 0.0f
};
218 alSourcef(mSource
, AL_PITCH
, 1.0f
);
219 alSourcef(mSource
, AL_GAIN
, 1.0f
);
220 alSourcefv(mSource
, AL_POSITION
, zero
);
221 alSourcefv(mSource
, AL_VELOCITY
, zero
);
223 alSourcei(mSource
, AL_LOOPING
, mIsLooping
);
230 alDeleteSources(1, &mSource
);
232 while (!mBuffers
.empty())
234 alDeleteBuffers(1, &mBuffers
.back());
244 if (mQueue
.empty()) return;
246 if (!mIsLoaded
) mQueue
.front()->loadAll(mSource
);
248 alSourcePlay(mSource
);
255 if (mQueue
.empty()) return;
259 alSourcei(mSource
, AL_LOOPING
, false);
263 if (!mStreamTimer
.isValid())
265 mStreamTimer
.init(boost::bind(&Impl::streamUpdate
, this, _1
, _2
),
269 alSourcePlay(mSource
);
276 for (int i
= mBuffers
.size(); i
<= 8; ++i
)
278 alGenBuffers(1, &buffer
);
280 if (mQueue
.front()->stream(buffer
))
282 alSourceQueueBuffers(mSource
, 1, &buffer
);
283 mBuffers
.push_back(buffer
);
287 alDeleteBuffers(1, &buffer
);
298 alGetSourcei(mSource
, AL_BUFFERS_PROCESSED
, &finished
);
300 while (finished
-- > 0)
303 alSourceUnqueueBuffers(mSource
, 1, &bufferObj
);
305 BufferP buffer
= mQueue
.front();
306 bool streamed
= buffer
->stream(bufferObj
);
310 alSourceQueueBuffers(mSource
, 1, &bufferObj
);
314 // the buffer couldn't be streamed, so get rid of it
319 // begin the next buffer in the queue
320 mQueue
.front()->rewind();
321 mQueue
.front()->stream(bufferObj
);
322 alSourceQueueBuffers(mSource
, 1, &bufferObj
);
323 logInfo("loading new buffer");
325 // queue up any unused buffers
330 // reload the same buffer
331 mQueue
.push_back(buffer
);
333 buffer
->stream(bufferObj
);
334 alSourceQueueBuffers(mSource
, 1, &bufferObj
);
335 logInfo("looping same buffer");
339 // nothing more to play, stopping...
341 std::remove(mBuffers
.begin(), mBuffers
.end(), bufferObj
);
347 alGetSourcei(mSource
, AL_SOURCE_STATE
, &state
);
349 // restart playing if we're stopped but supposed to be playing... this
350 // means we didn't queue enough and the audio skipped :-(
351 if (mIsPlaying
&& state
!= AL_PLAYING
)
353 alSourcePlay(mSource
);
360 alSourceStop(mSource
);
363 mStreamTimer
.invalidate();
368 alSourcePause(mSource
);
371 mStreamTimer
.invalidate();
375 void setSample(const std::string
& name
)
378 alSourcei(mSource
, AL_BUFFER
, AL_NONE
);
385 while (!mBuffers
.empty())
387 alDeleteBuffers(1, &mBuffers
.back());
392 void enqueue(const std::string
& name
)
394 BufferP buffer
= Buffer::getInstance(name
);
395 mQueue
.push_back(buffer
);
399 bool isPlaying() const
401 if (mIsPlaying
) return true;
404 alGetSourcei(mSource
, AL_SOURCE_STATE
, &state
);
406 return state
== AL_PLAYING
;
410 void setLooping(bool looping
)
412 mIsLooping
= looping
;
415 alGetSourcei(mSource
, AL_SOURCE_TYPE
, &type
);
417 if (type
!= AL_STREAMING
)
419 alSourcei(mSource
, AL_LOOPING
, mIsLooping
);
424 void streamUpdate(Timer
& timer
, Scalar t
)
426 // don't let the music die!
428 // TODO - might be nice to also allow using threads for streaming rather
429 // than a timer, probably as a compile-time option
432 static void retainBackend()
434 if (gRetainCount
++ == 0)
436 gAlDevice
= alcOpenDevice(0);
437 gAlContext
= alcCreateContext(gAlDevice
, 0);
438 if (!gAlDevice
|| !gAlContext
)
440 const char* error
= alcGetString(gAlDevice
,
441 alcGetError(gAlDevice
));
442 logError
<< "audio subsystem initialization failure: "
443 << error
<< std::endl
;
447 alcMakeContextCurrent(gAlContext
);
448 logInfo
<< "opened sound device `"
449 << alcGetString(gAlDevice
, ALC_DEFAULT_DEVICE_SPECIFIER
)
455 static void releaseBackend()
457 if (--gRetainCount
== 0)
459 alcMakeContextCurrent(0);
460 alcDestroyContext(gAlContext
);
461 alcCloseDevice(gAlDevice
);
467 std::list
<ALuint
> mBuffers
;
473 std::deque
<BufferP
> mQueue
;
477 static unsigned gRetainCount
;
478 static ALCdevice
* gAlDevice
;
479 static ALCcontext
* gAlContext
;
482 unsigned Sound::Impl::gRetainCount
= 0;
483 ALCdevice
* Sound::Impl::gAlDevice
= 0;
484 ALCcontext
* Sound::Impl::gAlContext
= 0;
489 mImpl(new Sound::Impl
) {}
491 Sound::Sound(const std::string
& name
) :
493 mImpl(new Sound::Impl(name
)) {}
496 void Sound::setSample(const std::string
& name
)
499 mImpl
->setSample(name
);
524 if (isPlaying()) pause();
528 bool Sound::isPlaying() const
531 return mImpl
->isPlaying();
535 void Sound::setPosition(const Vector3
& position
)
537 float vec
[3] = {position
[0], position
[1], position
[2]};
538 alSourcefv(mImpl
->mSource
, AL_POSITION
, vec
);
541 void Sound::setVelocity(const Vector3
& velocity
)
543 float vec
[3] = {velocity
[0], velocity
[1], velocity
[2]};
544 alSourcefv(mImpl
->mSource
, AL_VELOCITY
, vec
);
547 void Sound::setGain(Scalar gain
)
549 alSourcef(mImpl
->mSource
, AL_GAIN
, float(gain
));
552 void Sound::setPitch(Scalar pitch
)
554 alSourcef(mImpl
->mSource
, AL_PITCH
, float(pitch
));
557 void Sound::setLooping(bool looping
)
560 mImpl
->setLooping(looping
);
564 void Sound::setListenerPosition(const Vector3
& position
)
566 //alListener3f(AL_POSITION, float(position[0]), float(position[1]),
567 //float(position[2]));
568 float vec
[] = {position
[0], position
[1], position
[2]};
569 alListenerfv(AL_POSITION
, vec
);
572 void Sound::setListenerVelocity(const Vector3
& velocity
)
574 //alListener3f(AL_VELOCITY, float(velocity[0]), float(velocity[1]),
575 //float(velocity[2]));
576 float vec
[] = {velocity
[0], velocity
[1], velocity
[2]};
577 alListenerfv(AL_VELOCITY
, vec
);
580 void Sound::setListenerOrientation(const Vector3
& forward
, const Vector3
& up
)
583 vec
[0] = float(forward
[0]);
584 vec
[1] = float(forward
[1]);
585 vec
[2] = float(forward
[2]);
586 vec
[3] = float(up
[0]);
587 vec
[4] = float(up
[1]);
588 vec
[5] = float(up
[2]);
589 alListenerfv(AL_ORIENTATION
, vec
);
593 std::string
Sound::getPath(const std::string
& name
)
595 if (boost::find_last(name
, ".ogg"))
597 return Resource::getPath(name
);
601 std::string
path("sounds/");
604 return Resource::getPath(path
);
609 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
612 void SoundStream::enqueue(const std::string
& name
)
615 mImpl
->enqueue(name
);
619 void SoundStream::play()
628 /** vim: set ts=4 sw=4 tw=80: *************************************************/