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 "
101 << getName() << std::endl
;
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 "
138 << getName() << std::endl
;
142 alGenBuffers(1, &mBuffer
);
144 alBufferData(mBuffer
, mFormat
, data
, size
, mFreq
);
145 alSourcei(source
, AL_BUFFER
, mBuffer
);
147 // don't need to keep this loaded
148 ov_clear(&mOggStream
);
149 mOggStream
.datasource
= 0;
152 bool stream(ALuint buffer
)
154 char data
[BUFFER_SIZE
];
157 while (size
< BUFFER_SIZE
)
160 int result
= ov_read(&mOggStream
, data
+ size
,
161 BUFFER_SIZE
- size
, 0, 2, 1, §ion
);
169 if (result
< 0) logWarning("vorbis playback error");
174 if (size
== 0) return false;
176 alBufferData(buffer
, mFormat
, data
, size
, mFreq
);
183 if (!mOggStream
.datasource
) openFile();
184 else ov_raw_seek(&mOggStream
, 0);
190 OggVorbis_File mOggStream
;
202 Impl(const std::string
& name
)
210 // make sure the engine is initialized
211 Engine::getInstance();
217 alGenSources(1, &mSource
);
219 ALfloat zero
[] = {0.0f
, 0.0f
, 0.0f
};
220 alSourcef(mSource
, AL_PITCH
, 1.0f
);
221 alSourcef(mSource
, AL_GAIN
, 1.0f
);
222 alSourcefv(mSource
, AL_POSITION
, zero
);
223 alSourcefv(mSource
, AL_VELOCITY
, zero
);
225 alSourcei(mSource
, AL_LOOPING
, mIsLooping
);
232 alDeleteSources(1, &mSource
);
234 while (!mBuffers
.empty())
236 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
434 std::list
<ALuint
> mBuffers
;
440 std::deque
<BufferP
> mQueue
;
448 mImpl(new Sound::Impl
) {}
450 Sound::Sound(const std::string
& name
) :
452 mImpl(new Sound::Impl(name
)) {}
455 void Sound::setSample(const std::string
& name
)
458 mImpl
->setSample(name
);
483 if (isPlaying()) pause();
487 bool Sound::isPlaying() const
490 return mImpl
->isPlaying();
494 void Sound::setPosition(const Vector3
& position
)
496 float vec
[3] = {position
[0], position
[1], position
[2]};
497 alSourcefv(mImpl
->mSource
, AL_POSITION
, vec
);
500 void Sound::setVelocity(const Vector3
& velocity
)
502 float vec
[3] = {velocity
[0], velocity
[1], velocity
[2]};
503 alSourcefv(mImpl
->mSource
, AL_VELOCITY
, vec
);
506 void Sound::setGain(Scalar gain
)
508 alSourcef(mImpl
->mSource
, AL_GAIN
, float(gain
));
511 void Sound::setPitch(Scalar pitch
)
513 alSourcef(mImpl
->mSource
, AL_PITCH
, float(pitch
));
516 void Sound::setLooping(bool looping
)
519 mImpl
->setLooping(looping
);
523 void Sound::setListenerPosition(const Vector3
& position
)
525 //alListener3f(AL_POSITION, float(position[0]), float(position[1]),
526 //float(position[2]));
527 float vec
[] = {position
[0], position
[1], position
[2]};
528 alListenerfv(AL_POSITION
, vec
);
531 void Sound::setListenerVelocity(const Vector3
& velocity
)
533 //alListener3f(AL_VELOCITY, float(velocity[0]), float(velocity[1]),
534 //float(velocity[2]));
535 float vec
[] = {velocity
[0], velocity
[1], velocity
[2]};
536 alListenerfv(AL_VELOCITY
, vec
);
539 void Sound::setListenerOrientation(const Vector3
& forward
, const Vector3
& up
)
542 vec
[0] = float(forward
[0]);
543 vec
[1] = float(forward
[1]);
544 vec
[2] = float(forward
[2]);
545 vec
[3] = float(up
[0]);
546 vec
[4] = float(up
[1]);
547 vec
[5] = float(up
[2]);
548 alListenerfv(AL_ORIENTATION
, vec
);
552 std::string
Sound::getPath(const std::string
& name
)
554 std::string path
= Resource::getPath("sounds/" + name
+ ".ogg");
559 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
562 void SoundStream::enqueue(const std::string
& name
)
565 mImpl
->enqueue(name
);
569 void SoundStream::play()
578 /** vim: set ts=4 sw=4 tw=80: *************************************************/