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>
38 #include "Exception.hh"
44 #define BUFFER_SIZE (64 * 1024)
45 //#define BUFFER_SIZE (5*2048)
54 static ALenum
getAudioFormat(const vorbis_info
* audioInfo
)
56 if (audioInfo
->channels
== 1) return AL_FORMAT_MONO16
;
57 else return AL_FORMAT_STEREO16
;
62 typedef boost::shared_ptr
<Buffer
> BufferP
;
64 class Buffer
: public Library
<Buffer
>
68 Buffer(const std::string
& name
) :
69 Library
<Buffer
>(name
),
72 mOggStream
.datasource
= 0;
78 if (mOggStream
.datasource
)
80 ov_clear(&mOggStream
);
82 if (int(mBuffer
) != -1) alDeleteBuffers(1, &mBuffer
);
88 if (mOggStream
.datasource
)
90 ov_clear(&mOggStream
);
91 mOggStream
.datasource
= 0;
94 std::string filePath
= Sound::getPath(getName());
95 int result
= ov_fopen((char*)filePath
.c_str(), &mOggStream
);
99 logWarning("error while loading sound %s",
101 throw Exception(ErrorCode::UNKNOWN_AUDIO_FORMAT
);
104 vorbis_info
* vorbisInfo
= ov_info(&mOggStream
, -1);
105 mFormat
= getAudioFormat(vorbisInfo
);
106 mFreq
= vorbisInfo
->rate
;
108 logDebug(" channels: %d", vorbisInfo
->channels
);
109 logDebug(" frequency: %d", vorbisInfo
->rate
);
113 void loadAll(ALuint source
)
115 if (!mOggStream
.datasource
) openFile();
116 if (!mOggStream
.datasource
) return;
118 char data
[BUFFER_SIZE
];
124 int result
= ov_read(&mOggStream
, data
+ size
,
125 BUFFER_SIZE
- size
, 0, 2, 1, §ion
);
133 if (result
< 0) logWarning("vorbis playback error");
139 logWarning("decoded no bytes from %s", getName().c_str());
140 //throw Exception("file_not_found");
144 alGenBuffers(1, &mBuffer
);
146 alBufferData(mBuffer
, mFormat
, data
, size
, mFreq
);
147 alSourcei(source
, AL_BUFFER
, mBuffer
);
149 // don't need to keep this loaded
150 ov_clear(&mOggStream
);
151 mOggStream
.datasource
= 0;
154 bool stream(ALuint buffer
)
156 char data
[BUFFER_SIZE
];
159 while (size
< BUFFER_SIZE
)
162 int result
= ov_read(&mOggStream
, data
+ size
,
163 BUFFER_SIZE
- size
, 0, 2, 1, §ion
);
171 if (result
< 0) logWarning("vorbis playback error");
176 if (size
== 0) return false;
178 alBufferData(buffer
, mFormat
, data
, size
, mFreq
);
185 if (!mOggStream
.datasource
) openFile();
186 else ov_raw_seek(&mOggStream
, 0);
192 OggVorbis_File mOggStream
;
204 Impl(const std::string
& name
)
212 ALfloat zero
[] = {0.0f
, 0.0f
, 0.0f
};
214 alGenSources(1, &mSource
);
216 alSourcef(mSource
, AL_PITCH
, 1.0f
);
217 alSourcef(mSource
, AL_GAIN
, 1.0f
);
218 alSourcefv(mSource
, AL_POSITION
, zero
);
219 alSourcefv(mSource
, AL_VELOCITY
, zero
);
229 alDeleteSources(1, &mSource
);
231 while (!mBufferObjects
.empty())
233 alDeleteBuffers(1, &mBufferObjects
.back());
234 mBufferObjects
.pop_back();
241 if (mQueue
.empty()) return;
244 alGetSourcei(mSource
, AL_SOURCE_TYPE
, &type
);
246 if (type
!= AL_STATIC
)
248 mQueue
.front()->loadAll(mSource
);
251 alSourcei(mSource
, AL_LOOPING
, mIsLooping
);
252 alSourcePlay(mSource
);
261 alSourcei(mSource
, AL_BUFFER
, AL_NONE
);
262 mQueue
.front()->rewind();
265 alSourcei(mSource
, AL_LOOPING
, AL_FALSE
);
266 alSourcePlay(mSource
);
269 mStreamTimer
.init(boost::bind(&Impl::streamUpdate
, this, _1
, _2
), 1.0,
276 for (int i
= mBufferObjects
.size(); i
< 4; ++i
)
278 alGenBuffers(1, &buffer
);
279 mBufferObjects
.push_back(buffer
);
281 for (int i
= 0; i
< 4; ++i
)
283 buffer
= mBufferObjects
[i
];
284 mQueue
.front()->stream(buffer
);
285 alSourceQueueBuffers(mSource
, 1, &buffer
);
294 alGetSourcei(mSource
, AL_BUFFERS_PROCESSED
, &finished
);
296 while (finished
-- > 0)
299 alSourceUnqueueBuffers(mSource
, 1, &bufferObj
);
301 BufferP buffer
= mQueue
.front();
302 bool streamed
= buffer
->stream(bufferObj
);
306 alSourceQueueBuffers(mSource
, 1, &bufferObj
);
310 // the buffer couldn't be streamed, so get rid of it
315 // begin the next buffer in the queue
316 mQueue
.front()->rewind();
317 mQueue
.front()->stream(bufferObj
);
318 alSourceQueueBuffers(mSource
, 1, &bufferObj
);
319 logInfo("loading new buffer");
323 // reload the same buffer
324 mQueue
.push_back(buffer
);
326 buffer
->stream(bufferObj
);
327 alSourceQueueBuffers(mSource
, 1, &bufferObj
);
328 logInfo("looping same buffer");
334 alGetSourcei(mSource
, AL_SOURCE_STATE
, &state
);
336 // restart playing if we're stopped but supposed to be playing... this
337 // means we didn't queue enough and the audio skipped :-(
338 if (mIsPlaying
&& state
!= AL_PLAYING
)
340 alSourcePlay(mSource
);
347 alSourceStop(mSource
);
350 mStreamTimer
.invalidate();
355 alSourcePause(mSource
);
358 mStreamTimer
.invalidate();
363 alSourcePlay(mSource
);
367 alGetSourcei(mSource
, AL_SOURCE_TYPE
, &type
);
369 if (type
== AL_STREAMING
)
371 mStreamTimer
.init(boost::bind(&Impl::streamUpdate
, this, _1
, _2
),
377 void setSample(const std::string
& name
)
379 bool playing
= isPlaying();
381 alGetSourcei(mSource
, AL_SOURCE_TYPE
, &type
);
386 //alSourcei(mSource, AL_BUFFER, AL_NONE);
391 if (type
== AL_STREAMING
) stream();
396 void enqueue(const std::string
& name
)
398 BufferP buffer
= Buffer::getInstance(name
);
399 mQueue
.push_back(buffer
);
403 bool isPlaying() const
405 if (mIsPlaying
) return true;
408 alGetSourcei(mSource
, AL_SOURCE_STATE
, &state
);
410 return state
== AL_PLAYING
;
414 void setLooping(bool looping
)
416 mIsLooping
= looping
;
419 alGetSourcei(mSource
, AL_SOURCE_TYPE
, &type
);
421 if (type
!= AL_STREAMING
)
423 alSourcei(mSource
, AL_LOOPING
, mIsLooping
);
428 void streamUpdate(Timer
& timer
, Scalar t
)
430 // don't let the music die!
432 // TODO - might be nice to also allow using threads for streaming rather
433 // than a timer, probably as a compile-time option
438 std::vector
<ALuint
> mBufferObjects
;
443 std::deque
<BufferP
> mQueue
;
451 mImpl(new Sound::Impl
) {}
453 Sound::Sound(const std::string
& name
) :
455 mImpl(new Sound::Impl(name
)) {}
491 if (mImpl
->mIsPlaying
) pause();
496 void Sound::setSample(const std::string
& name
)
499 mImpl
->setSample(name
);
502 void Sound::enqueue(const std::string
& name
)
505 mImpl
->enqueue(name
);
509 bool Sound::isPlaying() const
512 return mImpl
->isPlaying();
515 void Sound::setPosition(const Vector3
& position
)
517 float p
[3] = {position
[0], position
[1], position
[2]};
518 alSourcefv(mImpl
->mSource
, AL_POSITION
, p
);
521 void Sound::setVelocity(const Vector3
& velocity
)
523 float v
[3] = {velocity
[0], velocity
[1], velocity
[2]};
524 alSourcefv(mImpl
->mSource
, AL_VELOCITY
, v
);
527 void Sound::setGain(Scalar gain
)
529 alSourcef(mImpl
->mSource
, AL_GAIN
, float(gain
));
532 void Sound::setPitch(Scalar pitch
)
534 alSourcef(mImpl
->mSource
, AL_PITCH
, float(pitch
));
537 void Sound::setLooping(bool looping
)
540 mImpl
->setLooping(looping
);
544 void Sound::setListenerPosition(const Vector3
& position
)
546 alListener3f(AL_POSITION
, float(position
[0]), float(position
[1]),
550 void Sound::setListenerVelocity(const Vector3
& velocity
)
552 alListener3f(AL_VELOCITY
, float(velocity
[0]), float(velocity
[1]),
556 void Sound::setListenerOrientation(const Vector3
& forward
, const Vector3
& up
)
559 vec
[0] = float(forward
[0]);
560 vec
[1] = float(forward
[1]);
561 vec
[2] = float(forward
[2]);
562 vec
[3] = float(up
[0]);
563 vec
[4] = float(up
[1]);
564 vec
[5] = float(up
[2]);
565 alListenerfv(AL_ORIENTATION
, vec
);
569 std::string
Sound::getPath(const std::string
& name
)
571 std::string path
= Resource::getPath("sounds/" + name
+ ".ogg");
578 /** vim: set ts=4 sw=4 tw=80: *************************************************/