Subversion Repositories Games.Chess Giants

Rev

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