]>
Dogcows Code - chaz/yoink/blob - Moof/Sound.cc
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)
52 static ALenum
getAudioFormat(const vorbis_info
* audioInfo
)
54 if (audioInfo
->channels
== 1) return AL_FORMAT_MONO16
;
55 else return AL_FORMAT_STEREO16
;
59 typedef boost::shared_ptr
<Buffer
> BufferP
;
61 class Buffer
: public Mippleton
<Buffer
>
63 OggVorbis_File oggStream
;
66 std::vector
<ALuint
> objects
;
70 Buffer(const std::string
& name
) :
71 Mippleton
<Buffer
>(name
)
73 oggStream
.datasource
= 0;
79 while (!objects
.empty())
81 alDeleteBuffers(1, &objects
.back());
85 if (oggStream
.datasource
)
94 if (oggStream
.datasource
)
97 oggStream
.datasource
= 0;
100 std::string filePath
= Sound::getPath(getName());
101 int result
= ov_fopen((char*)filePath
.c_str(), &oggStream
);
105 logWarning("error while loading sound %s",
107 throw Exception(Exception::BAD_AUDIO_FORMAT
);
110 vorbis_info
* vorbisInfo
= ov_info(&oggStream
, -1);
111 audioFormat
= getAudioFormat(vorbisInfo
);
112 audioFreq
= vorbisInfo
->rate
;
114 logDebug(" channels: %d", vorbisInfo
->channels
);
115 logDebug(" frequency: %d", vorbisInfo
->rate
);
119 void loadAll(ALuint source
)
121 if (!oggStream
.datasource
) openFile();
122 if (!oggStream
.datasource
) return;
124 char data
[BUFFER_SIZE
];
130 int result
= ov_read(&oggStream
, data
+ size
,
131 BUFFER_SIZE
- size
, 0, 2, 1, §ion
);
139 if (result
< 0) logWarning("vorbis playback error");
145 logWarning("decoded no bytes from %s", getName().c_str());
146 //throw Exception(Exception::FILE_NOT_FOUND);
151 alGenBuffers(1, &obj
);
153 alBufferData(obj
, audioFormat
, data
, size
, audioFreq
);
155 objects
.push_back(obj
);
157 alSourcei(source
, AL_BUFFER
, obj
);
159 // don't need this anymore
160 ov_clear(&oggStream
);
161 oggStream
.datasource
= 0;
165 void beginStream(ALuint source
, int nBuffers
= 8)
167 if (!oggStream
.datasource
) openFile();
168 if (!oggStream
.datasource
) return;
170 ALuint objs
[nBuffers
];
171 alGenBuffers(nBuffers
, objs
);
173 for (int i
= 0; i
< nBuffers
; ++i
)
175 objects
.push_back(objs
[i
]);
179 alSourceQueueBuffers(source
, nBuffers
, objs
);
189 StreamStatus
stream(ALuint buffer
)
191 std::vector
<ALuint
>::iterator it
=
192 std::find(objects
.begin(), objects
.end(), buffer
);
194 // that buffer doesn't belong to us
195 if (it
== objects
.end()) return STREAM_WRONG
;
197 char data
[BUFFER_SIZE
];
200 while (size
< BUFFER_SIZE
)
203 int result
= ov_read(&oggStream
, data
+ size
,
204 BUFFER_SIZE
- size
, 0, 2, 1, §ion
);
212 if (result
< 0) logWarning("vorbis playback error");
217 if (size
== 0) return STREAM_EOF
;
219 alBufferData(buffer
, audioFormat
, data
, size
, audioFreq
);
226 if (!oggStream
.datasource
) openFile();
227 else ov_raw_seek(&oggStream
, 0);
231 // delete unused buffers, return true if all buffers deleted
234 // clear any openal errors
237 while (!objects
.empty())
239 ALuint buffer
= objects
.back();
240 alDeleteBuffers(1, &buffer
);
242 // if an error occured, the buffer was not deleted because it's
243 // still in use by some source
244 if (alGetError() != AL_NO_ERROR
) return false;
254 Impl(const std::string
& name
) :
255 buffer_(Buffer::getInstance(name
)),
259 ALfloat zero
[] = {0.0f
, 0.0f
, 0.0f
};
261 alGenSources(1, &source_
);
263 alSourcef(source_
, AL_PITCH
, 1.0f
);
264 alSourcef(source_
, AL_GAIN
, 1.0f
);
265 alSourcefv(source_
, AL_POSITION
, zero
);
266 alSourcefv(source_
, AL_VELOCITY
, zero
);
271 alDeleteSources(1, &source_
);
278 alGetSourcei(source_
, AL_SOURCE_TYPE
, &type
);
280 if (type
!= AL_STATIC
)
282 buffer_
->loadAll(source_
);
285 alSourcei(source_
, AL_LOOPING
, looping_
);
286 alSourcePlay(source_
);
294 alGetSourcei(source_
, AL_SOURCE_TYPE
, &type
);
296 alSourcei(source_
, AL_BUFFER
, AL_NONE
);
298 buffer_
->beginStream(source_
);
300 alSourcei(source_
, AL_LOOPING
, AL_FALSE
);
301 alSourcePlay(source_
);
304 streamTimer
.init(boost::bind(&Impl::streamUpdate
, this, _1
, _2
), 1.0,
312 alGetSourcei(source_
, AL_BUFFERS_PROCESSED
, &finished
);
314 while (finished
-- > 0)
318 alSourceUnqueueBuffers(source_
, 1, &buffer
);
320 Buffer::StreamStatus status
= buffer_
->stream(buffer
);
322 if (status
== Buffer::STREAM_OK
)
324 alSourceQueueBuffers(source_
, 1, &buffer
);
326 else if (status
== Buffer::STREAM_EOF
)
330 // begin the next buffer in the queue
331 expired_
.push_back(buffer_
);
332 buffer_
= queue_
.front();
334 buffer_
->beginStream(source_
, 1);
338 // restart from the beginning
340 buffer_
->stream(buffer
);
341 alSourceQueueBuffers(source_
, 1, &buffer
);
344 else if (status
== Buffer::STREAM_WRONG
)
347 buffer_
->beginStream(source_
, 1);
352 alGetSourcei(source_
, AL_SOURCE_STATE
, &state
);
354 // restart playing if we're stopped but supposed to be playing... this
355 // means we didn't queue enough and the audio skipped
356 if (playing_
&& state
!= AL_PLAYING
)
358 alSourcePlay(source_
);
364 // try to remove expired buffers
365 std::vector
<BufferP
>::iterator it
;
366 for (it
= expired_
.end() - 1; it
>= expired_
.begin(); --it
)
368 if ((*it
)->clear()) expired_
.erase(it
);
375 alSourceStop(source_
);
381 alSourcePause(source_
);
387 alSourcePlay(source_
);
392 inline void setSample(const std::string
& name
)
394 bool playing
= isPlaying();
396 alGetSourcei(source_
, AL_SOURCE_TYPE
, &type
);
400 //alSourcei(source_, AL_BUFFER, AL_NONE);
401 buffer_
= Buffer::getInstance(name
);
403 if (type
== AL_STREAMING
)
405 if (playing
) stream();
413 inline void enqueue(const std::string
& name
)
415 BufferP buffer
= Buffer::getInstance(name
);
420 inline bool isPlaying() const
422 if (playing_
) return true;
425 alGetSourcei(source_
, AL_SOURCE_STATE
, &state
);
427 return state
== AL_PLAYING
;
431 inline void setLooping(bool looping
)
436 alGetSourcei(source_
, AL_SOURCE_TYPE
, &type
);
438 if (type
!= AL_STREAMING
)
440 alSourcei(source_
, AL_LOOPING
, looping_
);
451 std::queue
<BufferP
> queue_
;
452 std::vector
<BufferP
> expired_
;
456 void streamUpdate(Timer
& timer
, Scalar t
)
458 // don't let the music die!
460 // TODO - might be nice to also allow using threads for streaming rather
461 // than a timer, probably as a compile-time option
465 Sound::Sound(const std::string
& name
) :
467 impl_(new Sound::Impl(name
)) {}
503 if (impl_
->playing_
) pause();
508 void Sound::setSample(const std::string
& name
)
511 impl_
->setSample(name
);
514 void Sound::enqueue(const std::string
& name
)
517 impl_
->enqueue(name
);
521 bool Sound::isPlaying() const
524 return impl_
->isPlaying();
527 void Sound::setPosition(const Vector3
& position
)
529 float p
[3] = {position
[0], position
[1], position
[2]};
530 alSourcefv(impl_
->source_
, AL_POSITION
, p
);
533 void Sound::setVelocity(const Vector3
& velocity
)
535 float v
[3] = {velocity
[0], velocity
[1], velocity
[2]};
536 alSourcefv(impl_
->source_
, AL_VELOCITY
, v
);
539 void Sound::setGain(Scalar gain
)
541 alSourcef(impl_
->source_
, AL_GAIN
, float(gain
));
544 void Sound::setPitch(Scalar pitch
)
546 alSourcef(impl_
->source_
, AL_PITCH
, float(pitch
));
549 void Sound::setLooping(bool looping
)
552 impl_
->setLooping(looping
);
556 void Sound::setListenerPosition(const Vector3
& position
)
558 alListener3f(AL_POSITION
, float(position
[0]), float(position
[1]),
562 void Sound::setListenerVelocity(const Vector3
& velocity
)
564 alListener3f(AL_VELOCITY
, float(velocity
[0]), float(velocity
[1]),
568 void Sound::setListenerOrientation(const Vector3
& forward
, const Vector3
& up
)
571 vec
[0] = float(forward
[0]);
572 vec
[1] = float(forward
[1]);
573 vec
[2] = float(forward
[2]);
574 vec
[3] = float(up
[0]);
575 vec
[4] = float(up
[1]);
576 vec
[5] = float(up
[2]);
577 alListenerfv(AL_ORIENTATION
, vec
);
581 std::string
Sound::getPath(const std::string
& name
)
583 std::string path
= Resource::getPath("sounds/" + name
+ ".ogg");
590 /** vim: set ts=4 sw=4 tw=80: *************************************************/
This page took 0.061624 seconds and 4 git commands to generate.