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>
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 "
100 << getName() << std::endl
;
101 throw Error(Error::UNKNOWN_AUDIO_FORMAT
, getName());
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
) openFile();
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
) openFile();
183 else ov_raw_seek(&mOggStream
, 0);
189 OggVorbis_File mOggStream
;
201 Impl(const std::string
& name
)
213 alGenSources(1, &mSource
);
215 ALfloat zero
[] = {0.0f
, 0.0f
, 0.0f
};
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
);
221 alSourcei(mSource
, AL_LOOPING
, mIsLooping
);
228 alDeleteSources(1, &mSource
);
230 while (!mBuffers
.empty())
232 alDeleteBuffers(1, &mBuffers
.back());
240 if (mQueue
.empty()) return;
242 if (!mIsLoaded
) mQueue
.front()->loadAll(mSource
);
244 alSourcePlay(mSource
);
251 if (mQueue
.empty()) return;
255 alSourcei(mSource
, AL_LOOPING
, false);
259 if (!mStreamTimer
.isValid())
261 mStreamTimer
.init(boost::bind(&Impl::streamUpdate
, this, _1
, _2
),
265 alSourcePlay(mSource
);
272 for (int i
= mBuffers
.size(); i
<= 8; ++i
)
274 alGenBuffers(1, &buffer
);
276 if (mQueue
.front()->stream(buffer
))
278 alSourceQueueBuffers(mSource
, 1, &buffer
);
279 mBuffers
.push_back(buffer
);
283 alDeleteBuffers(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");
321 // queue up any unused buffers
326 // reload the same buffer
327 mQueue
.push_back(buffer
);
329 buffer
->stream(bufferObj
);
330 alSourceQueueBuffers(mSource
, 1, &bufferObj
);
331 logInfo("looping same buffer");
335 // nothing more to play, stopping...
337 std::remove(mBuffers
.begin(), mBuffers
.end(), bufferObj
);
343 alGetSourcei(mSource
, AL_SOURCE_STATE
, &state
);
345 // restart playing if we're stopped but supposed to be playing... this
346 // means we didn't queue enough and the audio skipped :-(
347 if (mIsPlaying
&& state
!= AL_PLAYING
)
349 alSourcePlay(mSource
);
356 alSourceStop(mSource
);
359 mStreamTimer
.invalidate();
364 alSourcePause(mSource
);
367 mStreamTimer
.invalidate();
371 void setSample(const std::string
& name
)
374 alSourcei(mSource
, AL_BUFFER
, AL_NONE
);
381 while (!mBuffers
.empty())
383 alDeleteBuffers(1, &mBuffers
.back());
388 void enqueue(const std::string
& name
)
390 BufferP buffer
= Buffer::getInstance(name
);
391 mQueue
.push_back(buffer
);
395 bool isPlaying() const
397 if (mIsPlaying
) return true;
400 alGetSourcei(mSource
, AL_SOURCE_STATE
, &state
);
402 return state
== AL_PLAYING
;
406 void setLooping(bool looping
)
408 mIsLooping
= looping
;
411 alGetSourcei(mSource
, AL_SOURCE_TYPE
, &type
);
413 if (type
!= AL_STREAMING
)
415 alSourcei(mSource
, AL_LOOPING
, mIsLooping
);
420 void streamUpdate(Timer
& timer
, Scalar t
)
422 // don't let the music die!
424 // TODO - might be nice to also allow using threads for streaming rather
425 // than a timer, probably as a compile-time option
430 std::list
<ALuint
> mBuffers
;
436 std::deque
<BufferP
> mQueue
;
444 mImpl(new Sound::Impl
) {}
446 Sound::Sound(const std::string
& name
) :
448 mImpl(new Sound::Impl(name
)) {}
451 void Sound::setSample(const std::string
& name
)
454 mImpl
->setSample(name
);
479 if (isPlaying()) pause();
483 bool Sound::isPlaying() const
486 return mImpl
->isPlaying();
490 void Sound::setPosition(const Vector3
& position
)
492 float vec
[3] = {position
[0], position
[1], position
[2]};
493 alSourcefv(mImpl
->mSource
, AL_POSITION
, vec
);
496 void Sound::setVelocity(const Vector3
& velocity
)
498 float vec
[3] = {velocity
[0], velocity
[1], velocity
[2]};
499 alSourcefv(mImpl
->mSource
, AL_VELOCITY
, vec
);
502 void Sound::setGain(Scalar gain
)
504 alSourcef(mImpl
->mSource
, AL_GAIN
, float(gain
));
507 void Sound::setPitch(Scalar pitch
)
509 alSourcef(mImpl
->mSource
, AL_PITCH
, float(pitch
));
512 void Sound::setLooping(bool looping
)
515 mImpl
->setLooping(looping
);
519 void Sound::setListenerPosition(const Vector3
& position
)
521 //alListener3f(AL_POSITION, float(position[0]), float(position[1]),
522 //float(position[2]));
523 float vec
[] = {position
[0], position
[1], position
[2]};
524 alListenerfv(AL_POSITION
, vec
);
527 void Sound::setListenerVelocity(const Vector3
& velocity
)
529 //alListener3f(AL_VELOCITY, float(velocity[0]), float(velocity[1]),
530 //float(velocity[2]));
531 float vec
[] = {velocity
[0], velocity
[1], velocity
[2]};
532 alListenerfv(AL_VELOCITY
, vec
);
535 void Sound::setListenerOrientation(const Vector3
& forward
, const Vector3
& up
)
538 vec
[0] = float(forward
[0]);
539 vec
[1] = float(forward
[1]);
540 vec
[2] = float(forward
[2]);
541 vec
[3] = float(up
[0]);
542 vec
[4] = float(up
[1]);
543 vec
[5] = float(up
[2]);
544 alListenerfv(AL_ORIENTATION
, vec
);
548 std::string
Sound::getPath(const std::string
& name
)
550 std::string path
= Resource::getPath("sounds/" + name
+ ".ogg");
555 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
558 void SoundStream::enqueue(const std::string
& name
)
561 mImpl
->enqueue(name
);
565 void SoundStream::play()
574 /** vim: set ts=4 sw=4 tw=80: *************************************************/