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 "Mippleton.hh"
43 #define BUFFER_SIZE (64 * 1024)
44 //#define BUFFER_SIZE (5*2048)
53 static ALenum
getAudioFormat(const vorbis_info
* audioInfo
)
55 if (audioInfo
->channels
== 1) return AL_FORMAT_MONO16
;
56 else return AL_FORMAT_STEREO16
;
61 typedef boost::shared_ptr
<Buffer
> BufferP
;
63 class Buffer
: public Mippleton
<Buffer
>
65 OggVorbis_File mOggStream
;
68 std::vector
<ALuint
> mObjects
;
72 Buffer(const std::string
& name
) :
73 Mippleton
<Buffer
>(name
)
75 mOggStream
.datasource
= 0;
81 while (!mObjects
.empty())
83 alDeleteBuffers(1, &mObjects
.back());
87 if (mOggStream
.datasource
)
89 ov_clear(&mOggStream
);
96 if (mOggStream
.datasource
)
98 ov_clear(&mOggStream
);
99 mOggStream
.datasource
= 0;
102 std::string filePath
= Sound::getPath(getName());
103 int result
= ov_fopen((char*)filePath
.c_str(), &mOggStream
);
107 logWarning("error while loading sound %s",
109 throw Exception(Exception::BAD_AUDIO_FORMAT
);
112 vorbis_info
* vorbisInfo
= ov_info(&mOggStream
, -1);
113 mFormat
= getAudioFormat(vorbisInfo
);
114 mFreq
= vorbisInfo
->rate
;
116 logDebug(" channels: %d", vorbisInfo
->channels
);
117 logDebug(" frequency: %d", vorbisInfo
->rate
);
121 void loadAll(ALuint source
)
123 if (!mOggStream
.datasource
) openFile();
124 if (!mOggStream
.datasource
) return;
126 char data
[BUFFER_SIZE
];
132 int result
= ov_read(&mOggStream
, data
+ size
,
133 BUFFER_SIZE
- size
, 0, 2, 1, §ion
);
141 if (result
< 0) logWarning("vorbis playback error");
147 logWarning("decoded no bytes from %s", getName().c_str());
148 //throw Exception(Exception::FILE_NOT_FOUND);
153 alGenBuffers(1, &obj
);
155 alBufferData(obj
, mFormat
, data
, size
, mFreq
);
157 mObjects
.push_back(obj
);
159 alSourcei(source
, AL_BUFFER
, obj
);
161 // don't need this anymore
162 ov_clear(&mOggStream
);
163 mOggStream
.datasource
= 0;
167 void beginStream(ALuint source
, int nBuffers
= 8)
169 if (!mOggStream
.datasource
) openFile();
170 if (!mOggStream
.datasource
) return;
172 ALuint objs
[nBuffers
];
173 alGenBuffers(nBuffers
, objs
);
175 for (int i
= 0; i
< nBuffers
; ++i
)
177 mObjects
.push_back(objs
[i
]);
181 alSourceQueueBuffers(source
, nBuffers
, objs
);
191 StreamStatus
stream(ALuint buffer
)
193 std::vector
<ALuint
>::iterator it
=
194 std::find(mObjects
.begin(), mObjects
.end(), buffer
);
196 // that buffer doesn't belong to us
197 if (it
== mObjects
.end()) return STREAM_WRONG
;
199 char data
[BUFFER_SIZE
];
202 while (size
< BUFFER_SIZE
)
205 int result
= ov_read(&mOggStream
, data
+ size
,
206 BUFFER_SIZE
- size
, 0, 2, 1, §ion
);
214 if (result
< 0) logWarning("vorbis playback error");
219 if (size
== 0) return STREAM_EOF
;
221 alBufferData(buffer
, mFormat
, data
, size
, mFreq
);
228 if (!mOggStream
.datasource
) openFile();
229 else ov_raw_seek(&mOggStream
, 0);
233 // delete unused buffers, return true if all buffers deleted
236 // clear any openal errors
239 while (!mObjects
.empty())
241 ALuint buffer
= mObjects
.back();
242 alDeleteBuffers(1, &buffer
);
244 // if an error occured, the buffer was not deleted because it's
245 // still in use by some source
246 if (alGetError() != AL_NO_ERROR
) return false;
256 Impl(const std::string
& name
) :
257 mBuffer(Buffer::getInstance(name
)),
261 ALfloat zero
[] = {0.0f
, 0.0f
, 0.0f
};
263 alGenSources(1, &mSource
);
265 alSourcef(mSource
, AL_PITCH
, 1.0f
);
266 alSourcef(mSource
, AL_GAIN
, 1.0f
);
267 alSourcefv(mSource
, AL_POSITION
, zero
);
268 alSourcefv(mSource
, AL_VELOCITY
, zero
);
273 alDeleteSources(1, &mSource
);
280 alGetSourcei(mSource
, AL_SOURCE_TYPE
, &type
);
282 if (type
!= AL_STATIC
)
284 mBuffer
->loadAll(mSource
);
287 alSourcei(mSource
, AL_LOOPING
, mIsLooping
);
288 alSourcePlay(mSource
);
296 alGetSourcei(mSource
, AL_SOURCE_TYPE
, &type
);
298 alSourcei(mSource
, AL_BUFFER
, AL_NONE
);
300 mBuffer
->beginStream(mSource
);
302 alSourcei(mSource
, AL_LOOPING
, AL_FALSE
);
303 alSourcePlay(mSource
);
306 mStreamTimer
.init(boost::bind(&Impl::streamUpdate
, this, _1
, _2
), 1.0,
314 alGetSourcei(mSource
, AL_BUFFERS_PROCESSED
, &finished
);
316 while (finished
-- > 0)
320 alSourceUnqueueBuffers(mSource
, 1, &buffer
);
322 Buffer::StreamStatus status
= mBuffer
->stream(buffer
);
324 if (status
== Buffer::STREAM_OK
)
326 alSourceQueueBuffers(mSource
, 1, &buffer
);
328 else if (status
== Buffer::STREAM_EOF
)
332 // begin the next buffer in the queue
333 mExpired
.push_back(mBuffer
);
334 mBuffer
= mQueue
.front();
336 mBuffer
->beginStream(mSource
, 1);
340 // restart from the beginning
342 mBuffer
->stream(buffer
);
343 alSourceQueueBuffers(mSource
, 1, &buffer
);
346 else if (status
== Buffer::STREAM_WRONG
)
349 mBuffer
->beginStream(mSource
, 1);
354 alGetSourcei(mSource
, AL_SOURCE_STATE
, &state
);
356 // restart playing if we're stopped but supposed to be playing... this
357 // means we didn't queue enough and the audio skipped
358 if (mIsPlaying
&& state
!= AL_PLAYING
)
360 alSourcePlay(mSource
);
366 // try to remove expired buffers
367 std::vector
<BufferP
>::iterator it
;
368 for (it
= mExpired
.end() - 1; it
>= mExpired
.begin(); --it
)
370 if ((*it
)->clear()) mExpired
.erase(it
);
377 alSourceStop(mSource
);
383 alSourcePause(mSource
);
389 alSourcePlay(mSource
);
394 inline void setSample(const std::string
& name
)
396 bool playing
= isPlaying();
398 alGetSourcei(mSource
, AL_SOURCE_TYPE
, &type
);
402 //alSourcei(mSource, AL_BUFFER, AL_NONE);
403 mBuffer
= Buffer::getInstance(name
);
405 if (type
== AL_STREAMING
)
407 if (playing
) stream();
415 inline void enqueue(const std::string
& name
)
417 BufferP buffer
= Buffer::getInstance(name
);
422 inline bool isPlaying() const
424 if (mIsPlaying
) return true;
427 alGetSourcei(mSource
, AL_SOURCE_STATE
, &state
);
429 return state
== AL_PLAYING
;
433 inline void setLooping(bool looping
)
435 mIsLooping
= looping
;
438 alGetSourcei(mSource
, AL_SOURCE_TYPE
, &type
);
440 if (type
!= AL_STREAMING
)
442 alSourcei(mSource
, AL_LOOPING
, mIsLooping
);
453 std::queue
<BufferP
> mQueue
;
454 std::vector
<BufferP
> mExpired
;
458 void streamUpdate(Timer
& timer
, Scalar t
)
460 // don't let the music die!
462 // TODO - might be nice to also allow using threads for streaming rather
463 // than a timer, probably as a compile-time option
467 Sound::Sound(const std::string
& name
) :
469 mImpl(new Sound::Impl(name
)) {}
505 if (mImpl
->mIsPlaying
) pause();
510 void Sound::setSample(const std::string
& name
)
513 mImpl
->setSample(name
);
516 void Sound::enqueue(const std::string
& name
)
519 mImpl
->enqueue(name
);
523 bool Sound::isPlaying() const
526 return mImpl
->isPlaying();
529 void Sound::setPosition(const Vector3
& position
)
531 float p
[3] = {position
[0], position
[1], position
[2]};
532 alSourcefv(mImpl
->mSource
, AL_POSITION
, p
);
535 void Sound::setVelocity(const Vector3
& velocity
)
537 float v
[3] = {velocity
[0], velocity
[1], velocity
[2]};
538 alSourcefv(mImpl
->mSource
, AL_VELOCITY
, v
);
541 void Sound::setGain(Scalar gain
)
543 alSourcef(mImpl
->mSource
, AL_GAIN
, float(gain
));
546 void Sound::setPitch(Scalar pitch
)
548 alSourcef(mImpl
->mSource
, AL_PITCH
, float(pitch
));
551 void Sound::setLooping(bool looping
)
554 mImpl
->setLooping(looping
);
558 void Sound::setListenerPosition(const Vector3
& position
)
560 alListener3f(AL_POSITION
, float(position
[0]), float(position
[1]),
564 void Sound::setListenerVelocity(const Vector3
& velocity
)
566 alListener3f(AL_VELOCITY
, float(velocity
[0]), float(velocity
[1]),
570 void Sound::setListenerOrientation(const Vector3
& forward
, const Vector3
& up
)
573 vec
[0] = float(forward
[0]);
574 vec
[1] = float(forward
[1]);
575 vec
[2] = float(forward
[2]);
576 vec
[3] = float(up
[0]);
577 vec
[4] = float(up
[1]);
578 vec
[5] = float(up
[2]);
579 alListenerfv(AL_ORIENTATION
, vec
);
583 std::string
Sound::getPath(const std::string
& name
)
585 std::string path
= Resource::getPath("sounds/" + name
+ ".ogg");
592 /** vim: set ts=4 sw=4 tw=80: *************************************************/