added support for nut settings in BD engine to level editor
[rocksndiamonds.git] / src / game_bd / bd_sound.c
1 /*
2  * Copyright (c) 2007, 2008, 2009, Czirkos Zoltan <cirix@fw.hu>
3  *
4  * Permission to use, copy, modify, and distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16
17 #include "main_bd.h"
18
19
20 /*
21   The C64 sound chip (the SID) had 3 channels. Boulder Dash used all 3 of them.
22
23   Different channels were used for different sounds.
24
25   Channel 1: other small sounds, ie. diamonds falling, boulders rolling.
26
27   Channel 2: Walking, diamond collecting and explosion; also time running out
28              sound.
29
30   Channel 3: amoeba sound, magic wall sound, cave cover & uncover sound, and
31              the crack sound (gate open)
32
33   Sounds have precedence over each other. Ie. the crack sound is given
34   precedence over other sounds (amoeba, for example.)
35   Different channels also behave differently. Channel 2 sounds are not stopped,
36   ie. walking can not be heard, until the explosion sound is finished playing
37   completely.
38
39   Explosions are always restarted, though. This is controlled by the array
40   defined in cave.c.
41
42   Channel 1 sounds are always stopped, if a new sound is requested.
43
44   Here we implement this a bit differently. We use samples, instead of
45   synthesizing the sounds. By stopping samples, sometimes small clicks
46   generate. Therefore we do not stop them, rather fade them out quickly.
47   (The SID had filters, which stopped these small clicks.)
48
49   Also, channel 1 and 2 should be stopped very often. So I decided to use two
50   SDL_Mixer channels to emulate one C64 channel; and they are used alternating.
51   SDL channel 4 is the "backup" channel 1, channel 5 is the backup channel 2.
52   Other channels have the same indexes.
53 */
54
55
56 #define MAX_CHANNELS            5
57
58 typedef enum _sound_flag
59 {
60   GD_SP_LOOPED  = 1 << 0,
61   GD_SP_FORCE   = 1 << 1,  // force restart, regardless of precedence level
62 } GdSoundFlag;
63
64 typedef struct _sound_property
65 {
66   GdSound sound;
67   int channel;        // channel this sound is played on.
68   int precedence;     // greater numbers will have precedence.
69   int flags;
70 } SoundProperty;
71
72 static SoundProperty sound_flags[] =
73 {
74   { 0, GD_S_NONE,                       0, 0                                    },
75
76   // channel 1 sounds.
77   // CHANNEL 1 SOUNDS ARE ALWAYS RESTARTED, so no need for GD_SP_FORCE flag.
78   { GD_S_STONE_PUSHING,                 1, 10                                   },
79   { GD_S_STONE_FALLING,                 1, 10                                   },
80   { GD_S_STONE_IMPACT,                  1, 10                                   },
81   { GD_S_MEGA_STONE_PUSHING,            1, 10                                   },
82   { GD_S_MEGA_STONE_FALLING,            1, 10                                   },
83   { GD_S_MEGA_STONE_IMPACT,             1, 10                                   },
84   { GD_S_FLYING_STONE_PUSHING,          1, 10                                   },
85   { GD_S_FLYING_STONE_FALLING,          1, 10                                   },
86   { GD_S_FLYING_STONE_IMPACT,           1, 10                                   },
87   { GD_S_WAITING_STONE_PUSHING,         1, 10                                   },
88   { GD_S_CHASING_STONE_PUSHING,         1, 10                                   },
89   // nut falling is relatively silent, so low precedence.
90   { GD_S_NUT_PUSHING,                   1, 8                                    },
91   { GD_S_NUT_FALLING,                   1, 8                                    },
92   { GD_S_NUT_IMPACT,                    1, 8                                    },
93   // higher precedence than a stone bouncing.
94   { GD_S_NUT_CRACKING,                  1, 12                                   },
95   // sligthly lower precedence, as stones and diamonds should be "louder"
96   { GD_S_DIRT_BALL_FALLING,             1, 8                                    },
97   { GD_S_DIRT_BALL_IMPACT,              1, 8                                    },
98   { GD_S_DIRT_LOOSE_FALLING,            1, 8                                    },
99   { GD_S_DIRT_LOOSE_IMPACT,             1, 8                                    },
100   { GD_S_NITRO_PACK_PUSHING,            1, 10                                   },
101   { GD_S_NITRO_PACK_FALLING,            1, 10                                   },
102   { GD_S_NITRO_PACK_IMPACT,             1, 10                                   },
103   { GD_S_FALLING_WALL_FALLING,          1, 10                                   },
104   { GD_S_FALLING_WALL_IMPACT,           1, 10                                   },
105   { GD_S_EXPANDING_WALL,                1, 10                                   },
106   { GD_S_WALL_REAPPEARING,              1, 9                                    },
107   { GD_S_DIAMOND_FALLING_RANDOM,        1, 10                                   },
108   { GD_S_DIAMOND_FALLING_1,             1, 10                                   },
109   { GD_S_DIAMOND_FALLING_2,             1, 10                                   },
110   { GD_S_DIAMOND_FALLING_3,             1, 10                                   },
111   { GD_S_DIAMOND_FALLING_4,             1, 10                                   },
112   { GD_S_DIAMOND_FALLING_5,             1, 10                                   },
113   { GD_S_DIAMOND_FALLING_6,             1, 10                                   },
114   { GD_S_DIAMOND_FALLING_7,             1, 10                                   },
115   { GD_S_DIAMOND_FALLING_8,             1, 10                                   },
116   { GD_S_DIAMOND_IMPACT_RANDOM,         1, 10                                   },
117   { GD_S_DIAMOND_IMPACT_1,              1, 10                                   },
118   { GD_S_DIAMOND_IMPACT_2,              1, 10                                   },
119   { GD_S_DIAMOND_IMPACT_3,              1, 10                                   },
120   { GD_S_DIAMOND_IMPACT_4,              1, 10                                   },
121   { GD_S_DIAMOND_IMPACT_5,              1, 10                                   },
122   { GD_S_DIAMOND_IMPACT_6,              1, 10                                   },
123   { GD_S_DIAMOND_IMPACT_7,              1, 10                                   },
124   { GD_S_DIAMOND_IMPACT_8,              1, 10                                   },
125   { GD_S_FLYING_DIAMOND_FALLING_RANDOM, 1, 10                                   },
126   { GD_S_FLYING_DIAMOND_FALLING_1,      1, 10                                   },
127   { GD_S_FLYING_DIAMOND_FALLING_2,      1, 10                                   },
128   { GD_S_FLYING_DIAMOND_FALLING_3,      1, 10                                   },
129   { GD_S_FLYING_DIAMOND_FALLING_4,      1, 10                                   },
130   { GD_S_FLYING_DIAMOND_FALLING_5,      1, 10                                   },
131   { GD_S_FLYING_DIAMOND_FALLING_6,      1, 10                                   },
132   { GD_S_FLYING_DIAMOND_FALLING_7,      1, 10                                   },
133   { GD_S_FLYING_DIAMOND_FALLING_8,      1, 10                                   },
134   { GD_S_FLYING_DIAMOND_IMPACT_RANDOM,  1, 10                                   },
135   { GD_S_FLYING_DIAMOND_IMPACT_1,       1, 10                                   },
136   { GD_S_FLYING_DIAMOND_IMPACT_2,       1, 10                                   },
137   { GD_S_FLYING_DIAMOND_IMPACT_3,       1, 10                                   },
138   { GD_S_FLYING_DIAMOND_IMPACT_4,       1, 10                                   },
139   { GD_S_FLYING_DIAMOND_IMPACT_5,       1, 10                                   },
140   { GD_S_FLYING_DIAMOND_IMPACT_6,       1, 10                                   },
141   { GD_S_FLYING_DIAMOND_IMPACT_7,       1, 10                                   },
142   { GD_S_FLYING_DIAMOND_IMPACT_8,       1, 10                                   },
143   // diamond collect sound has precedence over everything.
144   { GD_S_DIAMOND_COLLECTING,            1, 100                                  },
145   { GD_S_FLYING_DIAMOND_COLLECTING,     1, 100                                  },
146
147   // collect sounds have higher precedence than falling sounds and the like.
148   { GD_S_SKELETON_COLLECTING,           1, 100                                  },
149   { GD_S_PNEUMATIC_COLLECTING,          1, 50                                   },
150   { GD_S_BOMB_COLLECTING,               1, 50                                   },
151   { GD_S_CLOCK_COLLECTING,              1, 50                                   },
152   { GD_S_SWEET_COLLECTING,              1, 50                                   },
153   { GD_S_KEY_COLLECTING,                1, 50                                   },
154   { GD_S_DIAMOND_KEY_COLLECTING,        1, 50                                   },
155   // slime has lower precedence than diamond and stone falling sounds.
156   { GD_S_SLIME,                         1, 5                                    },
157   // lava has low precedence, too.
158   { GD_S_LAVA,                          1, 5                                    },
159   { GD_S_REPLICATOR,                    1, 5                                    },
160   // same for acid, even lower.
161   { GD_S_ACID_SPREADING,                1, 3                                    },
162   // same for bladder.
163   { GD_S_BLADDER_MOVING,                1, 5                                    },
164   { GD_S_BLADDER_PUSHING,               1, 5                                    },
165   { GD_S_BLADDER_CONVERTING,            1, 8                                    },
166   { GD_S_BLADDER_SPENDER,               1, 8                                    },
167   // very low precedence. biters tend to produce too much sound.
168   { GD_S_BITER_EATING,                  1, 3                                    },
169
170   // channel2 sounds.
171   { GD_S_DOOR_OPENING,                  2, 10                                   },
172   { GD_S_DIRT_WALKING,                  2, 10                                   },
173   { GD_S_EMPTY_WALKING,                 2, 10                                   },
174   { GD_S_STIRRING,                      2, 10                                   },
175   { GD_S_BOX_PUSHING,                   2, 10                                   },
176   { GD_S_TELEPORTER,                    2, 10                                   },
177   // timeout sounds have increasing precedence so they are always started
178   { GD_S_TIMEOUT_10,                    2, 20                                   },
179   // timeout sounds are examples which do not need "force restart" flag.
180   { GD_S_TIMEOUT_9,                     2, 21                                   },
181   { GD_S_TIMEOUT_8,                     2, 22                                   },
182   { GD_S_TIMEOUT_7,                     2, 23                                   },
183   { GD_S_TIMEOUT_6,                     2, 24                                   },
184   { GD_S_TIMEOUT_5,                     2, 25                                   },
185   { GD_S_TIMEOUT_4,                     2, 26                                   },
186   { GD_S_TIMEOUT_3,                     2, 27                                   },
187   { GD_S_TIMEOUT_2,                     2, 28                                   },
188   { GD_S_TIMEOUT_1,                     2, 29                                   },
189   { GD_S_TIMEOUT_0,                     2, 150, GD_SP_FORCE                     },
190   { GD_S_EXPLODING,                     2, 100, GD_SP_FORCE                     },
191   { GD_S_BOMB_EXPLODING,                2, 100, GD_SP_FORCE                     },
192   { GD_S_GHOST_EXPLODING,               2, 100, GD_SP_FORCE                     },
193   { GD_S_VOODOO_EXPLODING,              2, 100, GD_SP_FORCE                     },
194   { GD_S_NITRO_PACK_EXPLODING,          2, 100, GD_SP_FORCE                     },
195   { GD_S_BOMB_PLACING,                  2, 10                                   },
196   // precedence larger than normal, but smaller than timeout sounds
197   { GD_S_FINISHED,                      2, 15,  GD_SP_FORCE | GD_SP_LOOPED      },
198   { GD_S_SWITCH_BITER,                  2, 10                                   },
199   { GD_S_SWITCH_CREATURES,              2, 10                                   },
200   { GD_S_SWITCH_GRAVITY,                2, 10                                   },
201   { GD_S_SWITCH_EXPANDING,              2, 10                                   },
202   { GD_S_SWITCH_CONVEYOR,               2, 10                                   },
203   { GD_S_SWITCH_REPLICATOR,             2, 10                                   },
204
205   // channel 3 sounds.
206   { GD_S_AMOEBA,                        3, 30,  GD_SP_LOOPED                    },
207   { GD_S_AMOEBA_MAGIC,                  3, 40,  GD_SP_LOOPED                    },
208   { GD_S_MAGIC_WALL,                    3, 35,  GD_SP_LOOPED                    },
209   { GD_S_COVERING,                      3, 100, GD_SP_LOOPED                    },
210   { GD_S_PNEUMATIC_HAMMER,              3, 50,  GD_SP_LOOPED                    },
211   { GD_S_WATER,                         3, 20,  GD_SP_LOOPED                    },
212   { GD_S_CRACKING,                      3, 150                                  },
213   { GD_S_GRAVITY_CHANGING,              3, 60                                   },
214
215   // other sounds
216   // the bonus life sound has nothing to do with the cave.
217   // playing on channel 4.
218   { GD_S_BONUS_LIFE,                    4, 0                                    },
219 };
220
221 struct GdSoundInfo
222 {
223   int x, y;
224   int element;
225   int sound;
226 };
227
228 static GdSound snd_playing[MAX_CHANNELS];
229 struct GdSoundInfo sound_info_to_play[MAX_CHANNELS];
230 struct GdSoundInfo sound_info_playing[MAX_CHANNELS];
231
232 static boolean gd_sound_is_looped(GdSound sound)
233 {
234   return (sound_flags[sound].flags & GD_SP_LOOPED) != 0;
235 }
236
237 static boolean gd_sound_force_start(GdSound sound)
238 {
239   return (sound_flags[sound].flags & GD_SP_FORCE) != 0;
240 }
241
242 static int gd_sound_get_channel(GdSound sound)
243 {
244   return sound_flags[sound].channel;
245 }
246
247 static int gd_sound_get_precedence(GdSound sound)
248 {
249   return sound_flags[sound].precedence;
250 }
251
252 static GdSound sound_playing(int channel)
253 {
254   struct GdSoundInfo *si = &sound_info_playing[channel];
255
256   // check if sound has finished playing
257   if (snd_playing[channel] != GD_S_NONE)
258     if (!isSoundPlaying_BD(si->element, si->sound))
259       snd_playing[channel] = GD_S_NONE;
260
261   return snd_playing[channel];
262 }
263
264 static void halt_channel(int channel)
265 {
266   struct GdSoundInfo *si = &sound_info_playing[channel];
267
268 #if 0
269   if (isSoundPlaying_BD(si->element, si->sound))
270     printf("::: stop sound %d\n", si->sound);
271 #endif
272
273   if (isSoundPlaying_BD(si->element, si->sound))
274     StopSound_BD(si->element, si->sound);
275
276   snd_playing[channel] = GD_S_NONE;
277 }
278
279 static void play_channel_at_position(int channel)
280 {
281   struct GdSoundInfo *si = &sound_info_to_play[channel];
282
283   PlayLevelSound_BD(si->x, si->y, si->element, si->sound);
284
285   sound_info_playing[channel] = *si;
286 }
287
288 static void play_sound(int channel, GdSound sound)
289 {
290   // channel 1 and channel 4 are used alternating
291   // channel 2 and channel 5 are used alternating
292   static const GdSound diamond_falling_sounds[] =
293   {
294     GD_S_DIAMOND_FALLING_1,
295     GD_S_DIAMOND_FALLING_2,
296     GD_S_DIAMOND_FALLING_3,
297     GD_S_DIAMOND_FALLING_4,
298     GD_S_DIAMOND_FALLING_5,
299     GD_S_DIAMOND_FALLING_6,
300     GD_S_DIAMOND_FALLING_7,
301     GD_S_DIAMOND_FALLING_8,
302   };
303   static const GdSound diamond_impact_sounds[] =
304   {
305     GD_S_DIAMOND_IMPACT_1,
306     GD_S_DIAMOND_IMPACT_2,
307     GD_S_DIAMOND_IMPACT_3,
308     GD_S_DIAMOND_IMPACT_4,
309     GD_S_DIAMOND_IMPACT_5,
310     GD_S_DIAMOND_IMPACT_6,
311     GD_S_DIAMOND_IMPACT_7,
312     GD_S_DIAMOND_IMPACT_8,
313   };
314   static const GdSound flying_diamond_falling_sounds[] =
315   {
316     GD_S_FLYING_DIAMOND_FALLING_1,
317     GD_S_FLYING_DIAMOND_FALLING_2,
318     GD_S_FLYING_DIAMOND_FALLING_3,
319     GD_S_FLYING_DIAMOND_FALLING_4,
320     GD_S_FLYING_DIAMOND_FALLING_5,
321     GD_S_FLYING_DIAMOND_FALLING_6,
322     GD_S_FLYING_DIAMOND_FALLING_7,
323     GD_S_FLYING_DIAMOND_FALLING_8,
324   };
325   static const GdSound flying_diamond_impact_sounds[] =
326   {
327     GD_S_FLYING_DIAMOND_IMPACT_1,
328     GD_S_FLYING_DIAMOND_IMPACT_2,
329     GD_S_FLYING_DIAMOND_IMPACT_3,
330     GD_S_FLYING_DIAMOND_IMPACT_4,
331     GD_S_FLYING_DIAMOND_IMPACT_5,
332     GD_S_FLYING_DIAMOND_IMPACT_6,
333     GD_S_FLYING_DIAMOND_IMPACT_7,
334     GD_S_FLYING_DIAMOND_IMPACT_8,
335   };
336
337   if (sound == GD_S_NONE)
338     return;
339
340   // change diamond falling random to a selected diamond falling sound.
341   if (sound == GD_S_DIAMOND_FALLING_RANDOM)
342     sound = diamond_falling_sounds[gd_random_int_range(0, 8)];
343   else if (sound == GD_S_DIAMOND_IMPACT_RANDOM)
344     sound = diamond_impact_sounds[gd_random_int_range(0, 8)];
345   else if (sound == GD_S_FLYING_DIAMOND_FALLING_RANDOM)
346     sound = flying_diamond_falling_sounds[gd_random_int_range(0, 8)];
347   else if (sound == GD_S_FLYING_DIAMOND_IMPACT_RANDOM)
348     sound = flying_diamond_impact_sounds[gd_random_int_range(0, 8)];
349
350   // channel 1 may have been changed to channel 4 above.
351
352   if (!gd_sound_is_looped(sound))
353     halt_channel(channel);
354
355   play_channel_at_position(channel);
356
357   snd_playing[channel] = sound;
358 }
359
360 void gd_sound_init(void)
361 {
362   int i;
363
364   for (i = 0; i < GD_S_MAX; i++)
365     if (sound_flags[i].sound != i)
366       Fail("sound db index mismatch: %d", i);
367
368   for (i = 0; i < MAX_CHANNELS; i++)
369     snd_playing[i] = GD_S_NONE;
370 }
371
372 void gd_sound_off(void)
373 {
374   int i;
375
376   // stop all sounds.
377   for (i = 0; i < ARRAY_SIZE(snd_playing); i++)
378     halt_channel(i);
379 }
380
381 void gd_sound_play_bonus_life(void)
382 {
383   // required to set extended sound information for native sound engine
384   gd_sound_play(NULL, GD_S_BONUS_LIFE, O_NONE, -1, -1);
385
386   // now play the sound directly (on non-standard sound channel)
387   play_sound(gd_sound_get_channel(GD_S_BONUS_LIFE), GD_S_BONUS_LIFE);
388 }
389
390 static void play_sounds(GdSound sound1, GdSound sound2, GdSound sound3)
391 {
392   // CHANNEL 1 is for small sounds
393   if (sound1 != GD_S_NONE)
394   {
395     // start new sound if higher or same precedence than the one currently playing
396     if (gd_sound_get_precedence(sound1) >= gd_sound_get_precedence(sound_playing(1)))
397       play_sound(1, sound1);
398   }
399   else
400   {
401     // only interrupt looped sounds. non-looped sounds will go away automatically.
402     if (gd_sound_is_looped(sound_playing(1)))
403       halt_channel(1);
404   }
405
406   // CHANNEL 2 is for walking, explosions
407   // if no sound requested, do nothing.
408   if (sound2 != GD_S_NONE)
409   {
410     boolean play = FALSE;
411
412     // always start if not currently playing a sound.
413     if (sound_playing(2) == GD_S_NONE ||
414         gd_sound_force_start(sound2) ||
415         gd_sound_get_precedence(sound2) > gd_sound_get_precedence(sound_playing(2)))
416       play = TRUE;
417
418     // if figured out to play: do it.
419     // (if the requested sound is looped, this is required to continue playing it)
420     if (play)
421       play_sound(2, sound2);
422   }
423   else
424   {
425     // only interrupt looped sounds. non-looped sounds will go away automatically.
426     if (gd_sound_is_looped(sound_playing(2)))
427       halt_channel(2);
428   }
429
430   // CHANNEL 3 is for crack sound, amoeba and magic wall.
431   if (sound3 != GD_S_NONE)
432   {
433     boolean play = TRUE;
434
435     // if requests a non-looped sound, play that immediately.
436     // that can be a crack sound, gravity change, new life, ...
437
438     // do not interrupt the previous sound, if it is non-looped.
439     // later calls of this function will probably contain the same sound3,
440     // and then it will be set.
441     if (!gd_sound_is_looped(sound_playing(3)) &&
442         gd_sound_is_looped(sound3) &&
443         sound_playing(3) != GD_S_NONE)
444       play = FALSE;
445
446     // if figured out to play: do it.
447     if (play)
448       play_sound(3, sound3);
449   }
450   else
451   {
452     // sound3 = none, so interrupt sound requested.
453     // only interrupt looped sounds. non-looped sounds will go away automatically.
454     if (gd_sound_is_looped(sound_playing(3)))
455       halt_channel(3);
456   }
457 }
458
459 void gd_sound_play_cave(GdCave *cave)
460 {
461   play_sounds(cave->sound1, cave->sound2, cave->sound3);
462 }
463
464 static void gd_sound_info_to_play(int channel, int x, int y, int element, int sound)
465 {
466   struct GdSoundInfo *si = &sound_info_to_play[channel];
467
468   si->x = x;
469   si->y = y;
470   si->element = element;
471   si->sound = sound;
472 }
473
474 // plays sound in a cave
475 void gd_sound_play(GdCave *cave, GdSound sound, GdElement element, int x, int y)
476 {
477   if (sound == GD_S_NONE)
478     return;
479
480   // do not play sounds when fast-forwarding until player hatched
481   if (setup.bd_skip_hatching && !game_bd.game->cave->hatched &&
482       game_bd.game->state_counter == GAME_INT_CAVE_RUNNING)
483     return;
484
485   // when using native sound engine or if no position specified, use middle screen position
486   if (game.use_native_bd_sound_engine || (x == -1 && y == -1))
487   {
488     x = get_play_area_w() / 2 + get_scroll_x();
489     y = get_play_area_h() / 2 + get_scroll_y();
490   }
491
492   if (!game.use_native_bd_sound_engine)
493   {
494     // when not using native sound engine, just play the sound
495     PlayLevelSound_BD(x, y, element, sound);
496
497     return;
498   }
499
500   GdSound *s = &sound;          // use reliable default value
501   int channel = gd_sound_get_channel(sound);
502
503   switch (channel)
504   {
505     case 1: s = &cave->sound1; break;
506     case 2: s = &cave->sound2; break;
507     case 3: s = &cave->sound3; break;
508     default: break;
509   }
510
511   if (gd_sound_get_precedence(sound) >= gd_sound_get_precedence(*s))
512   {
513     // set sound
514     *s = sound;
515
516     // set extended sound information for native sound engine
517     gd_sound_info_to_play(channel, x, y, element, sound);
518   }
519 }