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