Subversion Repositories Games.Chess Giants

Rev

Rev 171 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 pmbaty 1
// audio.cpp
2
 
3
#include "common.h"
4
 
116 pmbaty 5
// OpenAL includes
6
#include "openal/al.h"
7
#include "openal/alc.h"
1 pmbaty 8
 
116 pmbaty 9
 
10
// attenuation factor (lower means sounds fade LESS with distance, higher means sounds fade MORE with distance)
11
#define ATTENUATION_FACTOR 0.02f
12
 
13
 
14
// structures used in this module only
171 pmbaty 15
typedef struct enqueued_sound_s
16
{
17
   int type;
18
   float emitterlocation_x;
19
   float emitterlocation_y;
20
   float emitterlocation_z;
21
} enqueued_sound_t;
22
 
23
 
116 pmbaty 24
typedef struct openal_buffer_s
25
{
136 pmbaty 26
   wchar_t *pathname; // sound pathname (mallocated, if NULL then slot is empty)
116 pmbaty 27
   ALshort *openal_samples; // OpenAL samples (16-bit mono)
28
   ALuint openal_buffer; // associated OpenAL buffer ID
136 pmbaty 29
   struct openal_buffer_s *next; // pointer to the next element in list
116 pmbaty 30
} openal_buffer_t;
31
 
32
 
33
typedef struct openal_source_s
34
{
35
   bool is_used; // set to TRUE if this source is used
36
   ALuint openal_source; // OpenAL sound source
37
} openal_source_t;
38
 
39
 
1 pmbaty 40
// global variables used in this module only
116 pmbaty 41
static ALCdevice *openal_device;
42
static ALCcontext *openal_context;
136 pmbaty 43
static openal_buffer_t *soundbuffers; // mallocated linked list
116 pmbaty 44
static openal_source_t *sources; // mallocated
181 pmbaty 45
static enqueued_sound_t enqueued_sounds[8];
46
static int enqueued_sound_count = 0;
116 pmbaty 47
static int source_count;
1 pmbaty 48
 
171 pmbaty 49
 
116 pmbaty 50
bool Audio_Init (void)
51
{
52
   // this function initializes the audio subsystem (OpenAL)
1 pmbaty 53
 
116 pmbaty 54
   openal_device = alcOpenDevice (NULL); // open audio device
55
   if (openal_device == NULL)
56
      return (false);
57
 
58
   openal_context = alcCreateContext (openal_device, NULL); // create audio context
59
   if (openal_context == NULL)
60
      return (false);
61
 
62
   if (!alcMakeContextCurrent (openal_context)) // select this audio context
63
      return (false);
64
 
65
   soundbuffers = NULL; // we know no soundbuffer yet
66
 
67
   sources = NULL; // we have no playing source yet
68
   source_count = 0;
69
 
181 pmbaty 70
   memset (enqueued_sounds, 0, sizeof (enqueued_sounds));
71
   enqueued_sound_count = 0; // no sound is enqueued yet
171 pmbaty 72
 
116 pmbaty 73
   return (true); // audio subsystem successfully initialized
74
}
75
 
76
 
77
void Audio_Shutdown (void)
78
{
79
   // this function shuts down the audio subsystem
80
 
136 pmbaty 81
   openal_buffer_t *soundbuffer;
82
   openal_buffer_t *oldbuffer;
116 pmbaty 83
   int array_index;
84
   ALint status;
85
 
86
   // cycle through all sound sources, stop them if needed and delete them
87
   for (array_index = 0; array_index < source_count; array_index++)
88
   {
89
      if (!sources[array_index].is_used)
90
         continue; // skip unused sources
91
      alGetSourcei (sources[array_index].openal_source, AL_SOURCE_STATE, &status); // get this source's playing state
92
      if (status == AL_PLAYING)
93
         alSourceStop (sources[array_index].openal_source); // stop all playing sources
94
      alSourcei (sources[array_index].openal_source, AL_BUFFER, NULL); // untie the buffer from this source
95
      alDeleteSources (1, &sources[array_index].openal_source); // and tell OpenAL to dispose of it
96
      sources[array_index].is_used = false; // mark this source as unused now
97
   }
98
 
99
   // cycle through all known sound buffers and delete them
136 pmbaty 100
   soundbuffer = soundbuffers;
101
   while (soundbuffer != NULL)
116 pmbaty 102
   {
136 pmbaty 103
      alDeleteBuffers (1, &soundbuffer->openal_buffer); // tell OpenAL to dispose of this buffer
104
      SAFE_free ((void **) &soundbuffer->openal_samples); // free the buffer by our side
105
      SAFE_free ((void **) &soundbuffer->pathname); // remember this buffer is now empty
106
 
107
      oldbuffer = soundbuffer; // save a pointer to the element we just processed
108
      soundbuffer = soundbuffer->next; // proceed to the next element in the linked list
109
      SAFE_free ((void **) &oldbuffer); // and free the element we just processed
116 pmbaty 110
   }
136 pmbaty 111
   soundbuffers = NULL; // at this point, all elements in the linked list are free
116 pmbaty 112
 
113
   alcMakeContextCurrent (NULL); // unselect the audio context
114
   alcDestroyContext (openal_context); // destroy it (only after it's been unselected!)
115
   alcCloseDevice (openal_device); // and close the audio device
116
 
117
   return; // finished, audio subsystem has been shutdown
118
}
119
 
120
 
1 pmbaty 121
void Audio_Think (void)
122
{
171 pmbaty 123
   // this function enqueues sounds, plays them and disposes of sound buffers and sources that have finished playing
1 pmbaty 124
 
171 pmbaty 125
   static wchar_t soundfile_path[MAX_PATH];
126
 
181 pmbaty 127
   enqueued_sound_t *processed_sound;
171 pmbaty 128
   openal_buffer_t *soundbuffer;
129
   ALfloat camera_position[3];
130
   ALfloat forward_and_up[6];
131
   unsigned long current_pos;
132
   buffer_t soundfile;
133
   int sample_value;
134
   int sample_count;
135
   int sample_index;
136
   int sample_size;
137
   int sample_rate;
138
   int channel_count;
139
   int channel_index;
140
   int channel_size;
116 pmbaty 141
   int source_index;
181 pmbaty 142
   int sound_index;
171 pmbaty 143
   uint32_t chunk_id;
144
   uint32_t blksiz;
145
   uint32_t temp32;
146
   uint16_t temp16;
116 pmbaty 147
   ALint status;
148
   float angle;
149
   float sin_pitch;
150
   float sin_yaw;
151
   float cos_pitch;
152
   float cos_yaw;
171 pmbaty 153
   float pitch;
1 pmbaty 154
 
116 pmbaty 155
   // compute the sine and cosine of the pitch component
156
   angle = current_pitch * TO_RADIANS;
157
   sin_pitch = sinf (angle);
158
   cos_pitch = cosf (angle);
159
 
160
   // compute the sine and cosine of the yaw component
161
   angle = current_yaw * TO_RADIANS;
162
   sin_yaw = sinf (angle);
163
   cos_yaw = cosf (angle);
164
 
165
   // build the camera position
166
   camera_position[0] = (ALfloat) -(cos_pitch * cos_yaw) * current_distance * ATTENUATION_FACTOR;
167
   camera_position[1] = (ALfloat) -(cos_pitch * sin_yaw) * current_distance * ATTENUATION_FACTOR;
168
   camera_position[2] = (ALfloat) sin_pitch * current_distance * ATTENUATION_FACTOR;
169
 
170
   // build the camera orientation
171
   forward_and_up[0] = -camera_position[0]; // forward direction is the opposite of camera position, since the camera is looking at the center of the scene
172
   forward_and_up[1] = -camera_position[1];
173
   forward_and_up[2] = -camera_position[2];
174
   forward_and_up[3] = 0.0f;
175
   forward_and_up[3] = 0.0f;
176
   forward_and_up[3] = 1.0f; // FIXME: upwards direction is not quite exact. It depends on the lookdown angle.
177
 
178
   // update the listener's position and orientation
179
   alListener3f (AL_POSITION, camera_position[0], camera_position[1], camera_position[2]);
180
   alListener3f (AL_VELOCITY, 0, 0, 0); // TODO: compute velocity dynamically with previous position
181
   alListenerfv (AL_ORIENTATION, forward_and_up);
182
 
181 pmbaty 183
   // are there sounds enqueued for playing ? if so, process them one after the other
184
   for (sound_index = 0; sound_index < enqueued_sound_count; sound_index++)
1 pmbaty 185
   {
181 pmbaty 186
      processed_sound = &enqueued_sounds[sound_index]; // quick access to enqueued sound
1 pmbaty 187
 
171 pmbaty 188
      // given the type of sound we want, enqueue the right one
189
      pitch = 1.0f; // assume fixed pitch until told otherwise
181 pmbaty 190
      if      (processed_sound->type == SOUNDTYPE_CLICK)       swprintf_s (soundfile_path, WCHAR_SIZEOF (soundfile_path), L"%s/themes/%s/sounds/click.wav", app_path, theme->name);
191
      else if (processed_sound->type == SOUNDTYPE_ILLEGALMOVE) swprintf_s (soundfile_path, WCHAR_SIZEOF (soundfile_path), L"%s/themes/%s/sounds/illegal.wav", app_path, theme->name);
192
      else if (processed_sound->type == SOUNDTYPE_VICTORY)     swprintf_s (soundfile_path, WCHAR_SIZEOF (soundfile_path), L"%s/themes/%s/sounds/win.wav", app_path, theme->name);
193
      else if (processed_sound->type == SOUNDTYPE_DEFEAT)      swprintf_s (soundfile_path, WCHAR_SIZEOF (soundfile_path), L"%s/themes/%s/sounds/lose.wav", app_path, theme->name);
194
      else if (processed_sound->type == SOUNDTYPE_CHECK)       swprintf_s (soundfile_path, WCHAR_SIZEOF (soundfile_path), L"%s/themes/%s/sounds/check.wav", app_path, theme->name);
195
      else if (processed_sound->type == SOUNDTYPE_PIECETAKEN)  swprintf_s (soundfile_path, WCHAR_SIZEOF (soundfile_path), L"%s/themes/%s/sounds/take.wav", app_path, theme->name);
196
      else if (processed_sound->type == SOUNDTYPE_HINTWINDOW)  swprintf_s (soundfile_path, WCHAR_SIZEOF (soundfile_path), L"%s/themes/%s/sounds/hintwindow.wav", app_path, theme->name);
197
      else if (processed_sound->type == SOUNDTYPE_IMPORTANT)   swprintf_s (soundfile_path, WCHAR_SIZEOF (soundfile_path), L"%s/themes/%s/sounds/important.wav", app_path, theme->name);
198
      else if (processed_sound->type == SOUNDTYPE_MOVE)
171 pmbaty 199
      {
200
         temp32 = rand () % 6; // there are several movement sounds, pick one at random
201
         if      (temp32 == 0) swprintf_s (soundfile_path, WCHAR_SIZEOF (soundfile_path), L"%s/themes/%s/sounds/move1.wav", app_path, theme->name);
202
         else if (temp32 == 1) swprintf_s (soundfile_path, WCHAR_SIZEOF (soundfile_path), L"%s/themes/%s/sounds/move2.wav", app_path, theme->name);
203
         else if (temp32 == 2) swprintf_s (soundfile_path, WCHAR_SIZEOF (soundfile_path), L"%s/themes/%s/sounds/move3.wav", app_path, theme->name);
204
         else if (temp32 == 3) swprintf_s (soundfile_path, WCHAR_SIZEOF (soundfile_path), L"%s/themes/%s/sounds/move4.wav", app_path, theme->name);
205
         else if (temp32 == 4) swprintf_s (soundfile_path, WCHAR_SIZEOF (soundfile_path), L"%s/themes/%s/sounds/move5.wav", app_path, theme->name);
206
         else                  swprintf_s (soundfile_path, WCHAR_SIZEOF (soundfile_path), L"%s/themes/%s/sounds/move6.wav", app_path, theme->name);
207
         pitch = 1.0f + ((((float) rand ()) / RAND_MAX) - 0.5f) / 2.0f; // set a random pitch for these sounds between 0.75 and 1.25
208
      }
181 pmbaty 209
      else if (processed_sound->type == SOUNDTYPE_SLIDE)
171 pmbaty 210
      {
211
         swprintf_s (soundfile_path, WCHAR_SIZEOF (soundfile_path), L"%s/themes/%s/sounds/slide.wav", app_path, theme->name);
212
         pitch = 1.0f + ((((float) rand ()) / RAND_MAX) - 0.5f) / 2.0f; // set a random pitch for these sounds between 0.75 and 1.25
213
      }
116 pmbaty 214
 
171 pmbaty 215
      // now cycle through our known OpenAL buffers and see if we already know this one
216
      for (soundbuffer = soundbuffers; soundbuffer != NULL; soundbuffer = soundbuffer->next)
217
         if ((soundbuffer->pathname != NULL) && (wcscmp (soundbuffer->pathname, soundfile_path) == 0))
218
            break; // break as soon as we find it
1 pmbaty 219
 
171 pmbaty 220
      // have we NOT found it ? if so, we must create it
221
      if (soundbuffer == NULL)
222
      {
223
         // load the sound file
224
         Buffer_Initialize (&soundfile);
225
         if (!Buffer_ReadFromFileW (&soundfile, soundfile_path))
226
            return; // if unable to load this sound file, give up (FIXME: log something ?)
1 pmbaty 227
 
171 pmbaty 228
         // parse the WAV file
229
         sample_count = channel_count = sample_size = channel_size = 0;
230
         current_pos = 0;
231
         for (;;)
232
         {
233
#define READ_DATA(type) *((type *) &soundfile.data[current_pos]); current_pos += sizeof (type); if (current_pos >= soundfile.size) break;
1 pmbaty 234
 
171 pmbaty 235
            chunk_id = READ_DATA (uint32_t);
236
            if (chunk_id == *((uint32_t *) "RIFF"))
237
            {
238
               temp32 = READ_DATA (uint32_t); // skip the "chunk size" field
239
               temp32 = READ_DATA (uint32_t); // skip the "riff style" field (typically "WAVE")
240
            }
241
            else if (chunk_id == *((uint32_t *) "fmt "))
242
            {
243
               blksiz = READ_DATA (uint32_t);
244
               temp16 = READ_DATA (uint16_t); if (temp16 != 1) break; // compressed WAVs are unsupported
245
               temp32 = READ_DATA (uint16_t); channel_count = (int) temp16;
246
               temp32 = READ_DATA (uint32_t); sample_rate = (int) temp32;
247
               temp32 = READ_DATA (uint32_t);
248
               temp16 = READ_DATA (uint16_t); sample_size = (int) temp16;
249
               temp16 = READ_DATA (uint16_t); channel_size = (int) temp16 / 8;
250
               if (blksiz > 16)
251
                  current_pos += blksiz - 16;
252
               if (current_pos >= soundfile.size)
253
                  break; // don't go beyond the end of the file
254
            }
255
            else if (chunk_id == *((uint32_t *) "data"))
256
            {
257
               temp32 = READ_DATA (uint32_t); sample_count = (int) temp32;
258
               break; // current_pos is now at the beginning of data, and data measures sample_count bytes long
259
            }
260
            else
261
            {
262
               blksiz = READ_DATA (uint32_t); // skip the "chunk size" field
263
               current_pos += blksiz; // useless chunk, skip it
264
               if (current_pos >= soundfile.size)
265
                  break; // don't go beyond the end of the file
266
            }
1 pmbaty 267
 
171 pmbaty 268
#undef READ_DATA
269
         }
270
         if ((sample_count == 0) || (channel_count == 0) || (sample_size == 0) || (channel_size == 0))
271
            return; // FIXME: not a wav file
116 pmbaty 272
 
171 pmbaty 273
         // compute the total number of samples (number of channels * number of frames)
274
         sample_count /= sample_size;
1 pmbaty 275
 
171 pmbaty 276
         // allocate space for one more sound buffer, and tie it to the linked list
277
         if (soundbuffers == NULL)
116 pmbaty 278
         {
171 pmbaty 279
            soundbuffers = (openal_buffer_t *) SAFE_malloc (1, sizeof (openal_buffer_t), false); // allocate a new slot to initialize the linked list
280
            soundbuffer = soundbuffers; // and set the pointer to the beginning of the list
116 pmbaty 281
         }
171 pmbaty 282
         else
116 pmbaty 283
         {
171 pmbaty 284
            for (soundbuffer = soundbuffers; soundbuffer->next != NULL; soundbuffer = soundbuffer->next); // locate the last slot in the linked list
285
            soundbuffer->next = (openal_buffer_t *) SAFE_malloc (1, sizeof (openal_buffer_t), false); // allocate a new slot and tie it there at the same time
286
            soundbuffer = soundbuffer->next; // and update the pointer to it
116 pmbaty 287
         }
171 pmbaty 288
         soundbuffer->next = NULL; // this is the last sound buffer in the linked list, do remember it.
289
 
290
         temp32 = wcslen (soundfile_path) + 1; // compute pathname buffer length (including null terminator)
291
         soundbuffer->pathname = (wchar_t *) SAFE_malloc (temp32, sizeof (wchar_t), false); // allocate space for it
292
         wcscpy_s (soundbuffer->pathname, temp32, soundfile_path); // and copy this sound's pathname in the newly allocated buffer
293
 
294
         // allocate the samples buffer and fill it, mixing all the WAV channels altogether in a 16-bit mono stream
295
         soundbuffer->openal_samples = (ALshort *) SAFE_malloc (sample_count, sizeof (ALshort), false);
296
         for (sample_index = 0; sample_index < sample_count; sample_index++)
116 pmbaty 297
         {
171 pmbaty 298
            sample_value = 0;
299
            if (channel_size == 1)
300
               for (channel_index = 0; channel_index < channel_count; channel_index++)
301
                  sample_value += *((int8_t *) soundfile.data[current_pos + sample_index * sample_size + channel_index * channel_size]);
302
            else if (channel_size == 2)
303
               for (channel_index = 0; channel_index < channel_count; channel_index++)
304
                  sample_value += *((int16_t *) &soundfile.data[current_pos + sample_index * sample_size + channel_index * channel_size]);
305
            sample_value /= channel_count;
306
            soundbuffer->openal_samples[sample_index] = (ALshort) sample_value;
116 pmbaty 307
         }
308
 
171 pmbaty 309
         Buffer_Forget (&soundfile); // we can now forget this sound file
310
 
311
         alGenBuffers (1, &soundbuffer->openal_buffer); // create an OpenAL sound buffer and fill it with our samples
312
         alBufferData (soundbuffer->openal_buffer, AL_FORMAT_MONO16, soundbuffer->openal_samples, sample_count * sizeof (ALushort), (ALsizei) sample_rate);
313
         if (alGetError () != AL_NO_ERROR)
314
            return; // FIXME: couldn't fill OpenAL buffer
116 pmbaty 315
      }
316
 
171 pmbaty 317
      // now we have a buffer to play
116 pmbaty 318
 
171 pmbaty 319
      // cycle through our known OpenAL sources and find a free one
320
      for (source_index = 0; source_index < source_count; source_index++)
321
         if (!sources[source_index].is_used)
322
            break; // break as soon as we find it
323
 
324
      // have we NOT found any ? if so, reallocate so as to have one more
325
      if (source_index == source_count)
136 pmbaty 326
      {
171 pmbaty 327
         sources = (openal_source_t *) SAFE_realloc (sources, source_count, source_count + 1, sizeof (openal_source_t), false);
328
         source_count++; // one more source has been created
136 pmbaty 329
      }
116 pmbaty 330
 
171 pmbaty 331
      // now we have a source to play our buffer
136 pmbaty 332
 
171 pmbaty 333
      sources[source_index].is_used = true; // immediately mark it as used
334
      alGenSources (1, &sources[source_index].openal_source); // (re)create an OpenAL source
116 pmbaty 335
 
171 pmbaty 336
      alSourcef (sources[source_index].openal_source, AL_PITCH, (ALfloat) pitch); // set the source's pitch
337
      alSourcef (sources[source_index].openal_source, AL_GAIN, 1.0f); // set the source's volume (full)
181 pmbaty 338
      alSource3f (sources[source_index].openal_source, AL_POSITION, (ALfloat) processed_sound->emitterlocation_x * ATTENUATION_FACTOR, (ALfloat) processed_sound->emitterlocation_y * ATTENUATION_FACTOR, (ALfloat) processed_sound->emitterlocation_z * ATTENUATION_FACTOR);
171 pmbaty 339
      alSource3f (sources[source_index].openal_source, AL_VELOCITY, 0, 0, 0); // set the source's velocity (static)
340
      alSourcei (sources[source_index].openal_source, AL_LOOPING, AL_FALSE); // set it as non-looping
341
      alSourcei (sources[source_index].openal_source, AL_BUFFER, soundbuffer->openal_buffer); // attach our bufferized data to it
116 pmbaty 342
 
171 pmbaty 343
      // play the source! Audio_Think() will dispose of it when it's finished
344
      alSourcePlay (sources[source_index].openal_source);
116 pmbaty 345
   }
346
 
181 pmbaty 347
   enqueued_sound_count = 0; // after that, remember that all enqueued sounds have been sent to OpenAL
348
 
171 pmbaty 349
   // cycle through all used sources and see if one is no longer playing
116 pmbaty 350
   for (source_index = 0; source_index < source_count; source_index++)
171 pmbaty 351
   {
116 pmbaty 352
      if (!sources[source_index].is_used)
171 pmbaty 353
         continue; // skip unused slots
116 pmbaty 354
 
171 pmbaty 355
      alGetSourcei (sources[source_index].openal_source, AL_SOURCE_STATE, &status); // get this source's playing state
356
      if (status == AL_PLAYING)
357
         continue; // skip sources that are still playing
358
 
359
      alSourcei (sources[source_index].openal_source, AL_BUFFER, NULL); // untie the buffer from this source
360
      alDeleteSources (1, &sources[source_index].openal_source); // and tell OpenAL to dispose of it
361
      sources[source_index].is_used = false; // mark this source as unused now
116 pmbaty 362
   }
363
 
171 pmbaty 364
   return; // finished, audio has been handled
365
}
116 pmbaty 366
 
367
 
171 pmbaty 368
void Audio_PlaySound (int sound_type, float pos_x, float pos_y, float pos_z)
369
{
370
   // helper function to play a sound (WARNING: it is NOT thread-safe!)
116 pmbaty 371
 
181 pmbaty 372
   if (!options.want_sounds || (enqueued_sound_count == sizeof (enqueued_sounds) / sizeof (enqueued_sounds[0])))
373
      return; // if we want no sound OR if there's no space to add one, don't play anything
116 pmbaty 374
 
181 pmbaty 375
   enqueued_sounds[enqueued_sound_count].type = sound_type; // enqueue this sound for playing. Audio_Think() will take care of it.
376
   enqueued_sounds[enqueued_sound_count].emitterlocation_x = pos_x;
377
   enqueued_sounds[enqueued_sound_count].emitterlocation_y = pos_y;
378
   enqueued_sounds[enqueued_sound_count].emitterlocation_z = pos_z;
379
   enqueued_sound_count++; // there's now one more sound to play
171 pmbaty 380
 
1 pmbaty 381
   return; // finished
382
}