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