PageRenderTime 24ms CodeModel.GetById 0ms RepoModel.GetById 0ms app.codeStats 0ms

/src/lipsofsuna/extension/sound/ext-module.c

https://github.com/deldiablo/GodheadLips
C | 455 lines | 342 code | 54 blank | 59 comment | 89 complexity | 623acc72f5eefbbcda6c6abc2a96c14a MD5 | raw file
  1. /* Lips of Suna
  2. * Copyright© 2007-2011 Lips of Suna development team.
  3. *
  4. * Lips of Suna is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU Lesser General Public License as
  6. * published by the Free Software Foundation, either version 3 of the
  7. * License, or (at your option) any later version.
  8. *
  9. * Lips of Suna 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 Lesser General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU Lesser General Public License
  15. * along with Lips of Suna. If not, see <http://www.gnu.org/licenses/>.
  16. */
  17. /**
  18. * \addtogroup LIExt Extension
  19. * @{
  20. * \addtogroup LIExtSound Sound
  21. * @{
  22. */
  23. #include <lipsofsuna/network.h>
  24. #include "ext-module.h"
  25. #ifndef LI_DISABLE_SOUND
  26. static int private_tick (
  27. LIExtModule* self,
  28. float secs);
  29. #endif
  30. /*****************************************************************************/
  31. LIMaiExtensionInfo liext_sound_info =
  32. {
  33. LIMAI_EXTENSION_VERSION, "Sound",
  34. liext_sound_new,
  35. liext_sound_free
  36. };
  37. LIExtModule* liext_sound_new (
  38. LIMaiProgram* program)
  39. {
  40. LIExtModule* self;
  41. /* Allocate self. */
  42. self = lisys_calloc (1, sizeof (LIExtModule));
  43. if (self == NULL)
  44. return NULL;
  45. self->program = program;
  46. self->music_looping = 0;
  47. self->music_volume = 1.0f;
  48. self->music_fading = 1.0f;
  49. self->listener_rotation = limat_quaternion_identity ();
  50. #ifndef LI_DISABLE_SOUND
  51. /* Allocate objects. */
  52. self->objects = lialg_u32dic_new ();
  53. if (self->objects == NULL)
  54. {
  55. liext_sound_free (self);
  56. return NULL;
  57. }
  58. /* Initialize sound. */
  59. self->system = lisnd_system_new ();
  60. if (self->system != NULL)
  61. self->sound = lisnd_manager_new (self->system);
  62. else
  63. printf ("WARNING: cannot initialize sound\n");
  64. /* Register callbacks. */
  65. if (!lical_callbacks_insert (program->callbacks, "tick", 1, private_tick, self, self->calls + 0))
  66. {
  67. liext_sound_free (self);
  68. return NULL;
  69. }
  70. #endif
  71. /* Register classes. */
  72. liscr_script_set_userdata (program->script, LIEXT_SCRIPT_SOUND, self);
  73. liext_script_sound (program->script);
  74. return self;
  75. }
  76. void liext_sound_free (
  77. LIExtModule* self)
  78. {
  79. #ifndef LI_DISABLE_SOUND
  80. LIAlgU32dicIter iter;
  81. /* Remove callbacks. */
  82. lical_handle_releasev (self->calls, sizeof (self->calls) / sizeof (LICalHandle));
  83. /* Free objects. */
  84. if (self->objects != NULL)
  85. {
  86. LIALG_U32DIC_FOREACH (iter, self->objects)
  87. liext_object_free (iter.value);
  88. lialg_u32dic_free (self->objects);
  89. }
  90. /* Free music. */
  91. if (self->music != NULL)
  92. lisnd_source_free (self->music);
  93. if (self->music_fade != NULL)
  94. lisnd_source_free (self->music_fade);
  95. /* Disable sound. */
  96. if (self->sound != NULL)
  97. lisnd_manager_free (self->sound);
  98. if (self->sound != NULL)
  99. lisnd_system_free (self->system);
  100. #endif
  101. lisys_free (self);
  102. }
  103. #ifndef LI_DISABLE_SOUND
  104. /**
  105. * \brief Finds a sound sample by name.
  106. *
  107. * \param self Module.
  108. * \param name Name of the sample.
  109. * \return Sample owned by the module or NULL.
  110. */
  111. LISndSample* liext_sound_find_sample (
  112. LIExtModule* self,
  113. const char* name)
  114. {
  115. int ret;
  116. char* path;
  117. LISndSample* sample;
  118. /* Check for existing. */
  119. sample = lisnd_manager_get_sample (self->sound, name);
  120. if (sample != NULL)
  121. return sample;
  122. /* Try to load FLAC. */
  123. path = lisys_path_format (self->program->paths->module_data,
  124. LISYS_PATH_SEPARATOR, "sounds",
  125. LISYS_PATH_SEPARATOR, name, ".flac", NULL);
  126. if (path == NULL)
  127. return NULL;
  128. if (lisys_filesystem_access (path, LISYS_ACCESS_READ))
  129. {
  130. ret = lisnd_manager_set_sample (self->sound, name, path);
  131. lisys_free (path);
  132. if (ret)
  133. return lisnd_manager_get_sample (self->sound, name);
  134. }
  135. else
  136. lisys_free (path);
  137. /* Try to load OGG. */
  138. path = lisys_path_format (self->program->paths->module_data,
  139. LISYS_PATH_SEPARATOR, "sounds",
  140. LISYS_PATH_SEPARATOR, name, ".ogg", NULL);
  141. if (path == NULL)
  142. return NULL;
  143. if (lisys_filesystem_access (path, LISYS_ACCESS_READ))
  144. {
  145. ret = lisnd_manager_set_sample (self->sound, name, path);
  146. lisys_free (path);
  147. if (ret)
  148. return lisnd_manager_get_sample (self->sound, name);
  149. }
  150. else
  151. lisys_free (path);
  152. return 0;
  153. }
  154. LISndSource* liext_sound_set_effect (
  155. LIExtModule* self,
  156. uint32_t object,
  157. const char* effect,
  158. int flags)
  159. {
  160. int create;
  161. LIEngObject* engobj;
  162. LIExtObject* extobj;
  163. LIMatTransform transform;
  164. LISndSample* sample;
  165. LISndSource* source;
  166. /* Find sample. */
  167. if (self->sound == NULL)
  168. {
  169. lisys_error_set (ENOTSUP, "no sound support");
  170. return NULL;
  171. }
  172. sample = liext_sound_find_sample (self, effect);
  173. if (sample == NULL)
  174. return NULL;
  175. /* Find engine object. */
  176. create = 0;
  177. engobj = lieng_engine_find_object (self->program->engine, object);
  178. if (engobj == NULL)
  179. return NULL;
  180. /* Find or create sound object. */
  181. extobj = lialg_u32dic_find (self->objects, object);
  182. if (extobj == NULL)
  183. {
  184. create = 1;
  185. extobj = liext_object_new ();
  186. if (extobj == NULL)
  187. return NULL;
  188. if (!lialg_u32dic_insert (self->objects, object, extobj))
  189. {
  190. liext_object_free (extobj);
  191. return NULL;
  192. }
  193. }
  194. /* Allocate new source. */
  195. source = lisnd_source_new_with_sample (self->system, sample, flags & LIEXT_SOUND_FLAG_NONPOSITIONAL);
  196. if (source == NULL)
  197. {
  198. if (create)
  199. {
  200. lialg_u32dic_remove (self->objects, object);
  201. liext_object_free (extobj);
  202. }
  203. return NULL;
  204. }
  205. if (!lialg_list_prepend (&extobj->sounds, source))
  206. {
  207. if (create)
  208. {
  209. lialg_u32dic_remove (self->objects, object);
  210. liext_object_free (extobj);
  211. }
  212. lisnd_source_free (source);
  213. return NULL;
  214. }
  215. /* Set properties. */
  216. lieng_object_get_transform (engobj, &transform);
  217. lisnd_source_set_position (source, &transform.position);
  218. if (flags & LIEXT_SOUND_FLAG_REPEAT)
  219. lisnd_source_set_looping (source, 1);
  220. lisnd_source_set_playing (source, 1);
  221. return source;
  222. }
  223. int liext_sound_set_music (
  224. LIExtModule* self,
  225. const char* value)
  226. {
  227. LISndSource* music;
  228. LISndSample* sample;
  229. /* Find sample. */
  230. if (self->sound == NULL)
  231. return 1;
  232. sample = liext_sound_find_sample (self, value);
  233. if (sample == NULL)
  234. return 0;
  235. /* Fade in a new music track. */
  236. music = lisnd_source_new (self->system, 1);
  237. if (music == NULL)
  238. return 0;
  239. lisnd_source_queue_sample (music, sample);
  240. lisnd_source_set_fading (music, 0.0f, 1.0f / self->music_fading);
  241. lisnd_source_set_volume (music, self->music_volume);
  242. lisnd_source_set_looping (music, self->music_looping);
  243. lisnd_source_set_playing (music, 1);
  244. /* Fade out the old music track. */
  245. if (self->music_fade != NULL)
  246. lisnd_source_free (self->music_fade);
  247. if (self->music != NULL)
  248. lisnd_source_set_fading (self->music, 1.0f, -1.0f / self->music_fading);
  249. self->music_fade = self->music;
  250. self->music = music;
  251. return 1;
  252. }
  253. void liext_sound_set_music_fading (
  254. LIExtModule* self,
  255. float value)
  256. {
  257. self->music_fading = value;
  258. }
  259. void liext_sound_set_music_looping (
  260. LIExtModule* self,
  261. int value)
  262. {
  263. if (self->music_looping != value)
  264. {
  265. self->music_looping = value;
  266. if (self->music != NULL)
  267. lisnd_source_set_looping (self->music, self->music_looping);
  268. }
  269. }
  270. void liext_sound_set_music_volume (
  271. LIExtModule* self,
  272. float value)
  273. {
  274. if (self->sound == NULL)
  275. return;
  276. if (value < 0.0f)
  277. value = 0.0f;
  278. if (value > 1.0f)
  279. value = 1.0f;
  280. self->music_volume = value;
  281. if (self->music != NULL)
  282. lisnd_source_set_volume (self->music, value);
  283. if (self->music_fade != NULL)
  284. lisnd_source_set_volume (self->music_fade, value);
  285. }
  286. #endif
  287. /*****************************************************************************/
  288. #ifndef LI_DISABLE_SOUND
  289. LIExtObject* liext_object_new ()
  290. {
  291. return lisys_calloc (1, sizeof (LIExtObject));
  292. }
  293. void liext_object_free (
  294. LIExtObject* self)
  295. {
  296. LIAlgList* ptr;
  297. for (ptr = self->sounds ; ptr != NULL ; ptr = ptr->next)
  298. lisnd_source_free (ptr->data);
  299. lialg_list_free (self->sounds);
  300. lisys_free (self);
  301. }
  302. int liext_object_update (
  303. LIExtObject* self,
  304. LIEngObject* object,
  305. LIExtModule* module,
  306. float secs)
  307. {
  308. LIAlgList* ptr;
  309. LIAlgList* next;
  310. LIMatTransform transform;
  311. LIMatVector vector;
  312. LIPhyObject* phyobj;
  313. LIPhyPhysics* physics;
  314. LISndSource* source;
  315. /* Find the optional physics manager. */
  316. physics = limai_program_find_component (module->program, "physics");
  317. /* Update each sound source. */
  318. for (ptr = self->sounds ; ptr != NULL ; ptr = next)
  319. {
  320. next = ptr->next;
  321. source = ptr->data;
  322. if (source->stereo)
  323. {
  324. lisnd_source_set_position (source, &module->listener_position);
  325. }
  326. else if (object != NULL)
  327. {
  328. lieng_object_get_transform (object, &transform);
  329. lisnd_source_set_position (source, &transform.position);
  330. }
  331. if (physics != NULL && object != NULL)
  332. {
  333. phyobj = liphy_physics_find_object (physics, object->id);
  334. if (phyobj != NULL)
  335. {
  336. liphy_object_get_velocity (phyobj, &vector);
  337. lisnd_source_set_velocity (source, &vector);
  338. }
  339. }
  340. if (!lisnd_source_update (source, secs))
  341. {
  342. lisnd_source_free (source);
  343. lialg_list_remove (&self->sounds, ptr);
  344. }
  345. }
  346. return self->sounds != NULL;
  347. }
  348. #endif
  349. /*****************************************************************************/
  350. #ifndef LI_DISABLE_SOUND
  351. static int private_tick (
  352. LIExtModule* self,
  353. float secs)
  354. {
  355. LIAlgU32dicIter iter;
  356. LIEngObject* engobj;
  357. LIExtObject* extobj;
  358. LIMatVector direction;
  359. LIMatVector velocity;
  360. LIMatVector up;
  361. /* Update listener position. */
  362. velocity = self->listener_velocity;
  363. direction = limat_quaternion_get_basis (self->listener_rotation, 2);
  364. up = limat_quaternion_get_basis (self->listener_rotation, 1);
  365. lisnd_system_set_listener (self->system, &self->listener_position, &velocity, &direction, &up);
  366. /* Update music. */
  367. if (self->music_fade != NULL)
  368. {
  369. if (!lisnd_source_update (self->music_fade, secs))
  370. {
  371. limai_program_event (self->program, "music-fade-ended", NULL);
  372. lisnd_source_free (self->music_fade);
  373. self->music_fade = NULL;
  374. }
  375. }
  376. if (self->music != NULL)
  377. {
  378. lisnd_source_set_position (self->music, &self->listener_position);
  379. if (!lisnd_source_update (self->music, secs))
  380. {
  381. limai_program_event (self->program, "music-ended", NULL);
  382. lisnd_source_free (self->music);
  383. self->music = NULL;
  384. }
  385. }
  386. /* Update sound effects. */
  387. LIALG_U32DIC_FOREACH (iter, self->objects)
  388. {
  389. extobj = iter.value;
  390. engobj = lieng_engine_find_object (self->program->engine, iter.key);
  391. if (!liext_object_update (extobj, engobj, self, secs))
  392. {
  393. lialg_u32dic_remove (self->objects, iter.key);
  394. liext_object_free (extobj);
  395. }
  396. }
  397. return 1;
  398. }
  399. #endif
  400. /** @} */
  401. /** @} */