PageRenderTime 40ms CodeModel.GetById 17ms app.highlight 20ms RepoModel.GetById 1ms app.codeStats 0ms

/xbmc/screensavers/rsxs-0.9/src/sound.cc

http://github.com/xbmc/xbmc
C++ | 272 lines | 219 code | 28 blank | 25 comment | 18 complexity | 292750e1b2c4dfeefb59c18252708803 MD5 | raw file
  1/*
  2 * Really Slick XScreenSavers
  3 * Copyright (C) 2002-2006  Michael Chapman
  4 *
  5 * This program is free software; you can redistribute it and/or modify
  6 * it under the terms of the GNU General Public License version 2 as
  7 * published by the Free Software Foundation.
  8 *
  9 * This program is distributed in the hope that it will be useful,
 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 12 * GNU General Public License for more details.
 13 *
 14 * You should have received a copy of the GNU General Public License
 15 * along with this program; if not, write to the Free Software
 16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 17 *
 18 *****************************************************************************
 19 *
 20 * This is a Linux port of the Really Slick Screensavers,
 21 * Copyright (C) 2002 Terence M. Welsh, available from www.reallyslick.com
 22 */
 23#include <common.hh>
 24
 25#if HAVE_SOUND
 26
 27#if HAVE_UNISTD_H
 28	#include <unistd.h>
 29#endif
 30#include <sound.hh>
 31
 32#include <dlopen.hh>
 33
 34ALCdevice*         Sound::_device;
 35ALCcontext*        Sound::_context;
 36std::list<Source*> Sound::_inactive, Sound::_active;
 37
 38float           Sound::_speedOfSound;
 39const Vector*   Sound::_cameraPos;
 40const Vector*   Sound::_cameraVel;
 41const UnitQuat* Sound::_cameraDir;
 42
 43#if USE_DLOPEN
 44	#define AL_FUNC(T, F) T F;
 45#else
 46	#define AL_FUNC(T, F) using ::F;
 47#endif
 48namespace AL {
 49	AL_FUNC(LPALCCLOSEDEVICE,        alcCloseDevice);
 50	AL_FUNC(LPALCCREATECONTEXT,      alcCreateContext);
 51	AL_FUNC(LPALCDESTROYCONTEXT,     alcDestroyContext);
 52	AL_FUNC(LPALCGETCURRENTCONTEXT,  alcGetCurrentContext);
 53	AL_FUNC(LPALCMAKECONTEXTCURRENT, alcMakeContextCurrent);
 54	AL_FUNC(LPALCOPENDEVICE,         alcOpenDevice);
 55	AL_FUNC(LPALCPROCESSCONTEXT,     alcProcessContext);
 56
 57	AL_FUNC(LPALBUFFERDATA,          alBufferData);
 58	AL_FUNC(LPALDELETEBUFFERS,       alDeleteBuffers);
 59	AL_FUNC(LPALDELETESOURCES,       alDeleteSources);
 60	AL_FUNC(LPALDISTANCEMODEL,       alDistanceModel);
 61	AL_FUNC(LPALGENBUFFERS,          alGenBuffers);
 62	AL_FUNC(LPALGENSOURCES,          alGenSources);
 63	AL_FUNC(LPALGETSOURCEI,          alGetSourcei);
 64	AL_FUNC(LPALLISTENERF,           alListenerf);
 65	AL_FUNC(LPALLISTENERFV,          alListenerfv);
 66	AL_FUNC(LPALSOURCEF,             alSourcef);
 67	AL_FUNC(LPALSOURCEFV,            alSourcefv);
 68	AL_FUNC(LPALSOURCEI,             alSourcei);
 69	AL_FUNC(LPALSOURCEPAUSE,         alSourcePause);
 70	AL_FUNC(LPALSOURCEPLAY,          alSourcePlay);
 71	AL_FUNC(LPALSOURCEREWIND,        alSourceRewind);
 72};
 73
 74Buffer::Buffer(
 75	ALenum format, const ALvoid* data, ALsizei size, ALuint frequency
 76) {
 77	AL::alGenBuffers(1, &_buffer);
 78	AL::alBufferData(_buffer, format, data, size, frequency);
 79}
 80
 81Buffer::~Buffer() { AL::alDeleteBuffers(1, &_buffer); }
 82
 83class Device : public ResourceManager::Resource<ALCdevice*> {
 84private:
 85	ALCdevice* _device;
 86public:
 87	Device(const std::string& spec) {
 88		_device = AL::alcOpenDevice(spec.c_str());
 89		if (!_device) {
 90			// Sleep for a moment before trying again
 91			sleep(1);
 92			_device = AL::alcOpenDevice(spec.c_str());
 93			if (!_device)
 94				throw ResourceManager::Exception(
 95					stdx::oss() << "Could not open AL device: " <<
 96						(spec.length() ? spec : "(default)")
 97				);
 98		}
 99	}
100	~Device() { AL::alcCloseDevice(_device); }
101	ALCdevice* operator()() const { return _device; }
102};
103
104class Context : public ResourceManager::Resource<ALCcontext*> {
105private:
106	ALCcontext* _prev;
107	ALCcontext* _context;
108public:
109	Context(ALCdevice* device) : _prev(AL::alcGetCurrentContext()) {
110		_context = AL::alcCreateContext(device, NULL);
111		if (!_context)
112			throw ResourceManager::Exception("Could not create AL context");
113		AL::alcMakeContextCurrent(_context);
114	}
115	~Context() {
116		AL::alcMakeContextCurrent(_prev);
117		AL::alcDestroyContext(_context);
118	}
119	ALCcontext* operator()() const { return _context; }
120};
121
122class Source : public ResourceManager::Resource<ALuint> {
123private:
124	ALuint _source;
125	Vector _pos;
126	float _time;
127public:
128	Source() { AL::alGenSources(1, &_source); }
129	~Source() { AL::alDeleteSources(1, &_source); }
130	ALuint operator()() const { return _source; }
131
132	void start(ALuint buffer, const Vector& pos, float ref, float delay) {
133		AL::alSourceRewind(_source);
134		AL::alSourcei(_source, AL_BUFFER, buffer);
135		AL::alSourcef(_source, AL_GAIN, 1.0f);
136		AL::alSourcef(_source, AL_REFERENCE_DISTANCE, ref);
137		AL::alSourcef(_source, AL_ROLLOFF_FACTOR, 1.0f);
138		AL::alSourcei(_source, AL_LOOPING, AL_FALSE);
139		AL::alSourcefv(_source, AL_POSITION, (float*)_pos.get());
140		_pos = pos;
141		_time = -delay;
142	}
143
144	bool update() {
145		_time += Common::elapsedTime;
146		if (_time < 0.0f) return false;
147
148		// Do doppler effects manually due to OpenAL bugs
149		float soundTime = _time;
150		if (Sound::_speedOfSound) {
151			Vector toCamera(*Sound::_cameraPos - _pos);
152			float vls = Vector::dot(toCamera, *Sound::_cameraVel / Common::elapsedSecs) / toCamera.length();
153			if (vls > Sound::_speedOfSound) vls = Sound::_speedOfSound;
154			float factor = (Sound::_speedOfSound - vls) / Sound::_speedOfSound;
155			AL::alSourcef(_source, AL_PITCH, Common::speed * factor);
156			soundTime -= toCamera.length() / Sound::_speedOfSound;
157		}
158
159		ALenum state = 0x1234;
160		AL::alGetSourcei(_source, AL_SOURCE_STATE, &state);
161		switch (state) {
162		case AL_INITIAL:
163		case AL_PAUSED:
164			if (Common::speed > 0.0f && soundTime >= 0.0f)
165				AL::alSourcePlay(_source);
166			break;
167		case AL_PLAYING:
168			if (Common::speed == 0.0f || soundTime < 0.0f)
169				AL::alSourcePause(_source);
170			break;
171		case AL_STOPPED:
172			return false;
173		}
174
175		return true;
176	}
177};
178
179void Sound::init(
180	const std::string& spec, float volume, float speedOfSound,
181	const Vector* cameraPos, const Vector* cameraVel, const UnitQuat* cameraDir
182) {
183#if USE_DLOPEN
184	try {
185		const Library* libopenal = Common::resources->manage(new Library("libopenal"));
186		AL::alcCloseDevice        = (LPALCCLOSEDEVICE)(*libopenal)("alcCloseDevice");
187		AL::alcCreateContext      = (LPALCCREATECONTEXT)(*libopenal)("alcCreateContext");
188		AL::alcDestroyContext     = (LPALCDESTROYCONTEXT)(*libopenal)("alcDestroyContext");
189		AL::alcGetCurrentContext  = (LPALCGETCURRENTCONTEXT)(*libopenal)("alcGetCurrentContext");
190		AL::alcMakeContextCurrent = (LPALCMAKECONTEXTCURRENT)(*libopenal)("alcMakeContextCurrent");
191		AL::alcOpenDevice         = (LPALCOPENDEVICE)(*libopenal)("alcOpenDevice");
192		AL::alcProcessContext     = (LPALCPROCESSCONTEXT)(*libopenal)("alcProcessContext");
193		AL::alBufferData          = (LPALBUFFERDATA)(*libopenal)("alBufferData");
194		AL::alDeleteBuffers       = (LPALDELETEBUFFERS)(*libopenal)("alDeleteBuffers");
195		AL::alDeleteSources       = (LPALDELETESOURCES)(*libopenal)("alDeleteSources");
196		AL::alDistanceModel       = (LPALDISTANCEMODEL)(*libopenal)("alDistanceModel");
197		AL::alGenBuffers          = (LPALGENBUFFERS)(*libopenal)("alGenBuffers");
198		AL::alGenSources          = (LPALGENSOURCES)(*libopenal)("alGenSources");
199		AL::alGetSourcei          = (LPALGETSOURCEI)(*libopenal)("alGetSourcei");
200		AL::alListenerf           = (LPALLISTENERF)(*libopenal)("alListenerf");
201		AL::alListenerfv          = (LPALLISTENERFV)(*libopenal)("alListenerfv");
202		AL::alSourcef             = (LPALSOURCEF)(*libopenal)("alSourcef");
203		AL::alSourcefv            = (LPALSOURCEFV)(*libopenal)("alSourcefv");
204		AL::alSourcei             = (LPALSOURCEI)(*libopenal)("alSourcei");
205		AL::alSourcePause         = (LPALSOURCEPAUSE)(*libopenal)("alSourcePause");
206		AL::alSourcePlay          = (LPALSOURCEPLAY)(*libopenal)("alSourcePlay");
207		AL::alSourceRewind        = (LPALSOURCEREWIND)(*libopenal)("alSourceRewind");
208	} catch (Common::Exception e) {
209		throw Unavailable();
210	}
211#endif // USE_DLOPEN
212
213	_device = Common::resources->manage(new Device(spec));
214	_context = Common::resources->manage(new Context(_device));
215
216	for (unsigned int i = 0; i < SOURCES; ++i) {
217		Source* source = new Source();
218		Common::resources->manage(source);
219		_inactive.push_back(source);
220	}
221
222	AL::alDistanceModel(AL_INVERSE_DISTANCE);
223	AL::alListenerf(AL_GAIN, volume * 0.01f);
224	_speedOfSound = speedOfSound;
225	_cameraPos = cameraPos;
226	_cameraVel = cameraVel;
227	_cameraDir = cameraDir;
228}
229
230void Sound::update() {
231	Vector forward(_cameraDir->forward());
232	Vector up(_cameraDir->up());
233	float orientation[] = {
234		forward.x(), forward.y(), forward.z(),
235		up.x(), up.y(), up.z()
236	};
237			
238	AL::alListenerfv(AL_POSITION, (float*)_cameraPos->get());
239	AL::alListenerfv(AL_ORIENTATION, orientation);
240
241	for (
242		std::list<Source*>::iterator it = _active.begin();
243		it != _active.end();
244	) {
245		if (!(*it)->update()) {
246			_inactive.push_back(*it);
247			it = _active.erase(it);
248			continue;
249		}
250		++it;
251	}
252
253	AL::alcProcessContext(_context);
254}
255
256void Sound::play(const Vector& pos) {
257	Source* source;
258	if (_inactive.empty()) {
259		/* Kill the oldest source */
260		source = _active.front();
261		_active.pop_front();
262	} else {
263		source = _inactive.front();
264		_inactive.pop_front();
265	}
266	_active.push_back(source);
267	source->start(_buffer, pos, _ref, _delay);
268}
269
270Sound::Sound(float ref, float delay) : _ref(ref), _delay(delay) {}
271
272#endif // HAVE_SOUND