/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. #if HAVE_SOUND
  25. #if HAVE_UNISTD_H
  26. #include <unistd.h>
  27. #endif
  28. #include <sound.hh>
  29. #include <dlopen.hh>
  30. ALCdevice* Sound::_device;
  31. ALCcontext* Sound::_context;
  32. std::list<Source*> Sound::_inactive, Sound::_active;
  33. float Sound::_speedOfSound;
  34. const Vector* Sound::_cameraPos;
  35. const Vector* Sound::_cameraVel;
  36. const UnitQuat* Sound::_cameraDir;
  37. #if USE_DLOPEN
  38. #define AL_FUNC(T, F) T F;
  39. #else
  40. #define AL_FUNC(T, F) using ::F;
  41. #endif
  42. namespace AL {
  43. AL_FUNC(LPALCCLOSEDEVICE, alcCloseDevice);
  44. AL_FUNC(LPALCCREATECONTEXT, alcCreateContext);
  45. AL_FUNC(LPALCDESTROYCONTEXT, alcDestroyContext);
  46. AL_FUNC(LPALCGETCURRENTCONTEXT, alcGetCurrentContext);
  47. AL_FUNC(LPALCMAKECONTEXTCURRENT, alcMakeContextCurrent);
  48. AL_FUNC(LPALCOPENDEVICE, alcOpenDevice);
  49. AL_FUNC(LPALCPROCESSCONTEXT, alcProcessContext);
  50. AL_FUNC(LPALBUFFERDATA, alBufferData);
  51. AL_FUNC(LPALDELETEBUFFERS, alDeleteBuffers);
  52. AL_FUNC(LPALDELETESOURCES, alDeleteSources);
  53. AL_FUNC(LPALDISTANCEMODEL, alDistanceModel);
  54. AL_FUNC(LPALGENBUFFERS, alGenBuffers);
  55. AL_FUNC(LPALGENSOURCES, alGenSources);
  56. AL_FUNC(LPALGETSOURCEI, alGetSourcei);
  57. AL_FUNC(LPALLISTENERF, alListenerf);
  58. AL_FUNC(LPALLISTENERFV, alListenerfv);
  59. AL_FUNC(LPALSOURCEF, alSourcef);
  60. AL_FUNC(LPALSOURCEFV, alSourcefv);
  61. AL_FUNC(LPALSOURCEI, alSourcei);
  62. AL_FUNC(LPALSOURCEPAUSE, alSourcePause);
  63. AL_FUNC(LPALSOURCEPLAY, alSourcePlay);
  64. AL_FUNC(LPALSOURCEREWIND, alSourceRewind);
  65. };
  66. Buffer::Buffer(
  67. ALenum format, const ALvoid* data, ALsizei size, ALuint frequency
  68. ) {
  69. AL::alGenBuffers(1, &_buffer);
  70. AL::alBufferData(_buffer, format, data, size, frequency);
  71. }
  72. Buffer::~Buffer() { AL::alDeleteBuffers(1, &_buffer); }
  73. class Device : public ResourceManager::Resource<ALCdevice*> {
  74. private:
  75. ALCdevice* _device;
  76. public:
  77. Device(const std::string& spec) {
  78. _device = AL::alcOpenDevice(spec.c_str());
  79. if (!_device) {
  80. // Sleep for a moment before trying again
  81. sleep(1);
  82. _device = AL::alcOpenDevice(spec.c_str());
  83. if (!_device)
  84. throw ResourceManager::Exception(
  85. stdx::oss() << "Could not open AL device: " <<
  86. (spec.length() ? spec : "(default)")
  87. );
  88. }
  89. }
  90. ~Device() { AL::alcCloseDevice(_device); }
  91. ALCdevice* operator()() const { return _device; }
  92. };
  93. class Context : public ResourceManager::Resource<ALCcontext*> {
  94. private:
  95. ALCcontext* _prev;
  96. ALCcontext* _context;
  97. public:
  98. Context(ALCdevice* device) : _prev(AL::alcGetCurrentContext()) {
  99. _context = AL::alcCreateContext(device, NULL);
  100. if (!_context)
  101. throw ResourceManager::Exception("Could not create AL context");
  102. AL::alcMakeContextCurrent(_context);
  103. }
  104. ~Context() {
  105. AL::alcMakeContextCurrent(_prev);
  106. AL::alcDestroyContext(_context);
  107. }
  108. ALCcontext* operator()() const { return _context; }
  109. };
  110. class Source : public ResourceManager::Resource<ALuint> {
  111. private:
  112. ALuint _source;
  113. Vector _pos;
  114. float _time;
  115. public:
  116. Source() { AL::alGenSources(1, &_source); }
  117. ~Source() { AL::alDeleteSources(1, &_source); }
  118. ALuint operator()() const { return _source; }
  119. void start(ALuint buffer, const Vector& pos, float ref, float delay) {
  120. AL::alSourceRewind(_source);
  121. AL::alSourcei(_source, AL_BUFFER, buffer);
  122. AL::alSourcef(_source, AL_GAIN, 1.0f);
  123. AL::alSourcef(_source, AL_REFERENCE_DISTANCE, ref);
  124. AL::alSourcef(_source, AL_ROLLOFF_FACTOR, 1.0f);
  125. AL::alSourcei(_source, AL_LOOPING, AL_FALSE);
  126. AL::alSourcefv(_source, AL_POSITION, (float*)_pos.get());
  127. _pos = pos;
  128. _time = -delay;
  129. }
  130. bool update() {
  131. _time += Common::elapsedTime;
  132. if (_time < 0.0f) return false;
  133. // Do doppler effects manually due to OpenAL bugs
  134. float soundTime = _time;
  135. if (Sound::_speedOfSound) {
  136. Vector toCamera(*Sound::_cameraPos - _pos);
  137. float vls = Vector::dot(toCamera, *Sound::_cameraVel / Common::elapsedSecs) / toCamera.length();
  138. if (vls > Sound::_speedOfSound) vls = Sound::_speedOfSound;
  139. float factor = (Sound::_speedOfSound - vls) / Sound::_speedOfSound;
  140. AL::alSourcef(_source, AL_PITCH, Common::speed * factor);
  141. soundTime -= toCamera.length() / Sound::_speedOfSound;
  142. }
  143. ALenum state = 0x1234;
  144. AL::alGetSourcei(_source, AL_SOURCE_STATE, &state);
  145. switch (state) {
  146. case AL_INITIAL:
  147. case AL_PAUSED:
  148. if (Common::speed > 0.0f && soundTime >= 0.0f)
  149. AL::alSourcePlay(_source);
  150. break;
  151. case AL_PLAYING:
  152. if (Common::speed == 0.0f || soundTime < 0.0f)
  153. AL::alSourcePause(_source);
  154. break;
  155. case AL_STOPPED:
  156. return false;
  157. }
  158. return true;
  159. }
  160. };
  161. void Sound::init(
  162. const std::string& spec, float volume, float speedOfSound,
  163. const Vector* cameraPos, const Vector* cameraVel, const UnitQuat* cameraDir
  164. ) {
  165. #if USE_DLOPEN
  166. try {
  167. const Library* libopenal = Common::resources->manage(new Library("libopenal"));
  168. AL::alcCloseDevice = (LPALCCLOSEDEVICE)(*libopenal)("alcCloseDevice");
  169. AL::alcCreateContext = (LPALCCREATECONTEXT)(*libopenal)("alcCreateContext");
  170. AL::alcDestroyContext = (LPALCDESTROYCONTEXT)(*libopenal)("alcDestroyContext");
  171. AL::alcGetCurrentContext = (LPALCGETCURRENTCONTEXT)(*libopenal)("alcGetCurrentContext");
  172. AL::alcMakeContextCurrent = (LPALCMAKECONTEXTCURRENT)(*libopenal)("alcMakeContextCurrent");
  173. AL::alcOpenDevice = (LPALCOPENDEVICE)(*libopenal)("alcOpenDevice");
  174. AL::alcProcessContext = (LPALCPROCESSCONTEXT)(*libopenal)("alcProcessContext");
  175. AL::alBufferData = (LPALBUFFERDATA)(*libopenal)("alBufferData");
  176. AL::alDeleteBuffers = (LPALDELETEBUFFERS)(*libopenal)("alDeleteBuffers");
  177. AL::alDeleteSources = (LPALDELETESOURCES)(*libopenal)("alDeleteSources");
  178. AL::alDistanceModel = (LPALDISTANCEMODEL)(*libopenal)("alDistanceModel");
  179. AL::alGenBuffers = (LPALGENBUFFERS)(*libopenal)("alGenBuffers");
  180. AL::alGenSources = (LPALGENSOURCES)(*libopenal)("alGenSources");
  181. AL::alGetSourcei = (LPALGETSOURCEI)(*libopenal)("alGetSourcei");
  182. AL::alListenerf = (LPALLISTENERF)(*libopenal)("alListenerf");
  183. AL::alListenerfv = (LPALLISTENERFV)(*libopenal)("alListenerfv");
  184. AL::alSourcef = (LPALSOURCEF)(*libopenal)("alSourcef");
  185. AL::alSourcefv = (LPALSOURCEFV)(*libopenal)("alSourcefv");
  186. AL::alSourcei = (LPALSOURCEI)(*libopenal)("alSourcei");
  187. AL::alSourcePause = (LPALSOURCEPAUSE)(*libopenal)("alSourcePause");
  188. AL::alSourcePlay = (LPALSOURCEPLAY)(*libopenal)("alSourcePlay");
  189. AL::alSourceRewind = (LPALSOURCEREWIND)(*libopenal)("alSourceRewind");
  190. } catch (Common::Exception e) {
  191. throw Unavailable();
  192. }
  193. #endif // USE_DLOPEN
  194. _device = Common::resources->manage(new Device(spec));
  195. _context = Common::resources->manage(new Context(_device));
  196. for (unsigned int i = 0; i < SOURCES; ++i) {
  197. Source* source = new Source();
  198. Common::resources->manage(source);
  199. _inactive.push_back(source);
  200. }
  201. AL::alDistanceModel(AL_INVERSE_DISTANCE);
  202. AL::alListenerf(AL_GAIN, volume * 0.01f);
  203. _speedOfSound = speedOfSound;
  204. _cameraPos = cameraPos;
  205. _cameraVel = cameraVel;
  206. _cameraDir = cameraDir;
  207. }
  208. void Sound::update() {
  209. Vector forward(_cameraDir->forward());
  210. Vector up(_cameraDir->up());
  211. float orientation[] = {
  212. forward.x(), forward.y(), forward.z(),
  213. up.x(), up.y(), up.z()
  214. };
  215. AL::alListenerfv(AL_POSITION, (float*)_cameraPos->get());
  216. AL::alListenerfv(AL_ORIENTATION, orientation);
  217. for (
  218. std::list<Source*>::iterator it = _active.begin();
  219. it != _active.end();
  220. ) {
  221. if (!(*it)->update()) {
  222. _inactive.push_back(*it);
  223. it = _active.erase(it);
  224. continue;
  225. }
  226. ++it;
  227. }
  228. AL::alcProcessContext(_context);
  229. }
  230. void Sound::play(const Vector& pos) {
  231. Source* source;
  232. if (_inactive.empty()) {
  233. /* Kill the oldest source */
  234. source = _active.front();
  235. _active.pop_front();
  236. } else {
  237. source = _inactive.front();
  238. _inactive.pop_front();
  239. }
  240. _active.push_back(source);
  241. source->start(_buffer, pos, _ref, _delay);
  242. }
  243. Sound::Sound(float ref, float delay) : _ref(ref), _delay(delay) {}
  244. #endif // HAVE_SOUND