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 *******************************************************************************/
35 #include <vorbis/codec.h>
36 #include <vorbis/vorbisfile.h>
39 #include "Exception.hh"
45 #define BUFFER_SIZE (64 * 1024)
46 //#define BUFFER_SIZE (5*2048)
55 static ALenum
getAudioFormat(const vorbis_info
* audioInfo
)
57 if (audioInfo
->channels
== 1) return AL_FORMAT_MONO16
;
58 else return AL_FORMAT_STEREO16
;
63 typedef boost::shared_ptr
<Buffer
> BufferP
;
65 class Buffer
: public Library
<Buffer
>
69 Buffer(const std::string
& name
) :
70 Library
<Buffer
>(name
),
73 mOggStream
.datasource
= 0;
79 if (mOggStream
.datasource
)
81 ov_clear(&mOggStream
);
83 if (int(mBuffer
) != -1) alDeleteBuffers(1, &mBuffer
);
89 if (mOggStream
.datasource
)
91 ov_clear(&mOggStream
);
92 mOggStream
.datasource
= 0;
95 std::string filePath
= Sound::getPath(getName());
96 int result
= ov_fopen((char*)filePath
.c_str(), &mOggStream
);
100 logWarning("error while loading sound %s",
102 throw Exception(ErrorCode::UNKNOWN_AUDIO_FORMAT
, getName());
105 vorbis_info
* vorbisInfo
= ov_info(&mOggStream
, -1);
106 mFormat
= getAudioFormat(vorbisInfo
);
107 mFreq
= vorbisInfo
->rate
;
111 void loadAll(ALuint source
)
113 if (!mOggStream
.datasource
) openFile();
114 if (!mOggStream
.datasource
) return;
116 char data
[BUFFER_SIZE
];
122 int result
= ov_read(&mOggStream
, data
+ size
,
123 BUFFER_SIZE
- size
, 0, 2, 1, §ion
);
131 if (result
< 0) logWarning("vorbis playback error");
137 logWarning("decoded no bytes from %s", getName().c_str());
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
) openFile();
183 else ov_raw_seek(&mOggStream
, 0);
189 OggVorbis_File mOggStream
;
201 Impl(const std::string
& name
)
209 // make sure the engine is initialized
210 Engine::getInstance();
216 alGenSources(1, &mSource
);
218 ALfloat zero
[] = {0.0f
, 0.0f
, 0.0f
};
219 alSourcef(mSource
, AL_PITCH
, 1.0f
);
220 alSourcef(mSource
, AL_GAIN
, 1.0f
);
221 alSourcefv(mSource
, AL_POSITION
, zero
);
222 alSourcefv(mSource
, AL_VELOCITY
, zero
);
224 alSourcei(mSource
, AL_LOOPING
, mIsLooping
);
231 alDeleteSources(1, &mSource
);
233 while (!mBuffers
.empty())
235 alDeleteBuffers(1, &mBuffers
.back());
243 if (mQueue
.empty()) return;
245 if (!mIsLoaded
) mQueue
.front()->loadAll(mSource
);
247 alSourcePlay(mSource
);
254 if (mQueue
.empty()) return;
258 alSourcei(mSource
, AL_LOOPING
, false);
262 if (!mStreamTimer
.isValid())
264 mStreamTimer
.init(boost::bind(&Impl::streamUpdate
, this, _1
, _2
),
268 alSourcePlay(mSource
);
275 for (int i
= mBuffers
.size(); i
<= 8; ++i
)
277 alGenBuffers(1, &buffer
);
279 if (mQueue
.front()->stream(buffer
))
281 alSourceQueueBuffers(mSource
, 1, &buffer
);
282 mBuffers
.push_back(buffer
);
286 alDeleteBuffers(1, &buffer
);
297 alGetSourcei(mSource
, AL_BUFFERS_PROCESSED
, &finished
);
299 while (finished
-- > 0)
302 alSourceUnqueueBuffers(mSource
, 1, &bufferObj
);
304 BufferP buffer
= mQueue
.front();
305 bool streamed
= buffer
->stream(bufferObj
);
309 alSourceQueueBuffers(mSource
, 1, &bufferObj
);
313 // the buffer couldn't be streamed, so get rid of it
318 // begin the next buffer in the queue
319 mQueue
.front()->rewind();
320 mQueue
.front()->stream(bufferObj
);
321 alSourceQueueBuffers(mSource
, 1, &bufferObj
);
322 logInfo("loading new buffer");
324 // queue up any unused buffers
329 // reload the same buffer
330 mQueue
.push_back(buffer
);
332 buffer
->stream(bufferObj
);
333 alSourceQueueBuffers(mSource
, 1, &bufferObj
);
334 logInfo("looping same buffer");
338 // nothing more to play, stopping...
340 std::remove(mBuffers
.begin(), mBuffers
.end(), bufferObj
);
346 alGetSourcei(mSource
, AL_SOURCE_STATE
, &state
);
348 // restart playing if we're stopped but supposed to be playing... this
349 // means we didn't queue enough and the audio skipped :-(
350 if (mIsPlaying
&& state
!= AL_PLAYING
)
352 alSourcePlay(mSource
);
359 alSourceStop(mSource
);
362 mStreamTimer
.invalidate();
367 alSourcePause(mSource
);
370 mStreamTimer
.invalidate();
374 void setSample(const std::string
& name
)
377 alSourcei(mSource
, AL_BUFFER
, AL_NONE
);
384 while (!mBuffers
.empty())
386 alDeleteBuffers(1, &mBuffers
.back());
391 void enqueue(const std::string
& name
)
393 BufferP buffer
= Buffer::getInstance(name
);
394 mQueue
.push_back(buffer
);
398 bool isPlaying() const
400 if (mIsPlaying
) return true;
403 alGetSourcei(mSource
, AL_SOURCE_STATE
, &state
);
405 return state
== AL_PLAYING
;
409 void setLooping(bool looping
)
411 mIsLooping
= looping
;
414 alGetSourcei(mSource
, AL_SOURCE_TYPE
, &type
);
416 if (type
!= AL_STREAMING
)
418 alSourcei(mSource
, AL_LOOPING
, mIsLooping
);
423 void streamUpdate(Timer
& timer
, Scalar t
)
425 // don't let the music die!
427 // TODO - might be nice to also allow using threads for streaming rather
428 // than a timer, probably as a compile-time option
433 std::list
<ALuint
> mBuffers
;
439 std::deque
<BufferP
> mQueue
;
447 mImpl(new Sound::Impl
) {}
449 Sound::Sound(const std::string
& name
) :
451 mImpl(new Sound::Impl(name
)) {}
454 void Sound::setSample(const std::string
& name
)
457 mImpl
->setSample(name
);
482 if (isPlaying()) pause();
486 bool Sound::isPlaying() const
489 return mImpl
->isPlaying();
493 void Sound::setPosition(const Vector3
& position
)
495 float vec
[3] = {position
[0], position
[1], position
[2]};
496 alSourcefv(mImpl
->mSource
, AL_POSITION
, vec
);
499 void Sound::setVelocity(const Vector3
& velocity
)
501 float vec
[3] = {velocity
[0], velocity
[1], velocity
[2]};
502 alSourcefv(mImpl
->mSource
, AL_VELOCITY
, vec
);
505 void Sound::setGain(Scalar gain
)
507 alSourcef(mImpl
->mSource
, AL_GAIN
, float(gain
));
510 void Sound::setPitch(Scalar pitch
)
512 alSourcef(mImpl
->mSource
, AL_PITCH
, float(pitch
));
515 void Sound::setLooping(bool looping
)
518 mImpl
->setLooping(looping
);
522 void Sound::setListenerPosition(const Vector3
& position
)
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
);
530 void Sound::setListenerVelocity(const Vector3
& velocity
)
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
);
538 void Sound::setListenerOrientation(const Vector3
& forward
, const Vector3
& up
)
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
);
551 std::string
Sound::getPath(const std::string
& name
)
553 std::string path
= Resource::getPath("sounds/" + name
+ ".ogg");
558 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
561 void SoundStream::enqueue(const std::string
& name
)
564 mImpl
->enqueue(name
);
568 void SoundStream::play()
577 /** vim: set ts=4 sw=4 tw=80: *************************************************/