- // audio.cpp 
-   
- #include "common.h" 
-   
- // OpenAL includes 
- #include "openal/al.h" 
- #include "openal/alc.h" 
-   
-   
- // attenuation factor (lower means sounds fade LESS with distance, higher means sounds fade MORE with distance) 
- #define ATTENUATION_FACTOR 0.02f 
-   
-   
- // structures used in this module only 
- typedef struct enqueued_sound_s 
- { 
-    int type; 
-    float emitterlocation_x; 
-    float emitterlocation_y; 
-    float emitterlocation_z; 
- } enqueued_sound_t; 
-   
-   
- typedef struct openal_buffer_s 
- { 
-    wchar_t *pathname; // sound pathname (mallocated, if NULL then slot is empty) 
-    ALshort *openal_samples; // OpenAL samples (16-bit mono) 
-    ALuint openal_buffer; // associated OpenAL buffer ID 
-    struct openal_buffer_s *next; // pointer to the next element in list 
- } openal_buffer_t; 
-   
-   
- typedef struct openal_source_s 
- { 
-    bool is_used; // set to TRUE if this source is used 
-    ALuint openal_source; // OpenAL sound source 
- } openal_source_t; 
-   
-   
- // global variables used in this module only 
- static ALCdevice *openal_device; 
- static ALCcontext *openal_context; 
- static openal_buffer_t *soundbuffers; // mallocated linked list 
- static openal_source_t *sources; // mallocated 
- static enqueued_sound_t enqueued_sound; 
- static int source_count; 
-   
-   
- bool Audio_Init (void) 
- { 
-    // this function initializes the audio subsystem (OpenAL) 
-   
-    openal_device = alcOpenDevice (NULL); // open audio device 
-    if (openal_device == NULL) 
-       return (false); 
-   
-    openal_context = alcCreateContext (openal_device, NULL); // create audio context 
-    if (openal_context == NULL) 
-       return (false); 
-   
-    if (!alcMakeContextCurrent (openal_context)) // select this audio context 
-       return (false); 
-   
-    soundbuffers = NULL; // we know no soundbuffer yet 
-   
-    sources = NULL; // we have no playing source yet 
-    source_count = 0; 
-   
-    enqueued_sound.type = 0; // no sound is enqueued yet 
-   
-    return (true); // audio subsystem successfully initialized 
- } 
-   
-   
- void Audio_Shutdown (void) 
- { 
-    // this function shuts down the audio subsystem 
-   
-    openal_buffer_t *soundbuffer; 
-    openal_buffer_t *oldbuffer; 
-    int array_index; 
-    ALint status; 
-   
-    // cycle through all sound sources, stop them if needed and delete them 
-    for (array_index = 0; array_index < source_count; array_index++) 
-    { 
-       if (!sources[array_index].is_used) 
-          continue; // skip unused sources 
-       alGetSourcei (sources[array_index].openal_source, AL_SOURCE_STATE, &status); // get this source's playing state 
-       if (status == AL_PLAYING) 
-          alSourceStop (sources[array_index].openal_source); // stop all playing sources 
-       alSourcei (sources[array_index].openal_source, AL_BUFFER, NULL); // untie the buffer from this source 
-       alDeleteSources (1, &sources[array_index].openal_source); // and tell OpenAL to dispose of it 
-       sources[array_index].is_used = false; // mark this source as unused now 
-    } 
-   
-    // cycle through all known sound buffers and delete them 
-    soundbuffer = soundbuffers; 
-    while (soundbuffer != NULL) 
-    { 
-       alDeleteBuffers (1, &soundbuffer->openal_buffer); // tell OpenAL to dispose of this buffer 
-       SAFE_free ((void **) &soundbuffer->openal_samples); // free the buffer by our side 
-       SAFE_free ((void **) &soundbuffer->pathname); // remember this buffer is now empty 
-   
-       oldbuffer = soundbuffer; // save a pointer to the element we just processed 
-       soundbuffer = soundbuffer->next; // proceed to the next element in the linked list 
-       SAFE_free ((void **) &oldbuffer); // and free the element we just processed 
-    } 
-    soundbuffers = NULL; // at this point, all elements in the linked list are free 
-   
-    alcMakeContextCurrent (NULL); // unselect the audio context 
-    alcDestroyContext (openal_context); // destroy it (only after it's been unselected!) 
-    alcCloseDevice (openal_device); // and close the audio device 
-   
-    return; // finished, audio subsystem has been shutdown 
- } 
-   
-   
- void Audio_Think (void) 
- { 
-    // this function enqueues sounds, plays them and disposes of sound buffers and sources that have finished playing 
-   
-    static wchar_t soundfile_path[MAX_PATH]; 
-   
-    enqueued_sound_t processed_sound; 
-    openal_buffer_t *soundbuffer; 
-    ALfloat camera_position[3]; 
-    ALfloat forward_and_up[6]; 
-    unsigned long current_pos; 
-    buffer_t soundfile; 
-    int sample_value; 
-    int sample_count; 
-    int sample_index; 
-    int sample_size; 
-    int sample_rate; 
-    int channel_count; 
-    int channel_index; 
-    int channel_size; 
-    int source_index; 
-    uint32_t chunk_id; 
-    uint32_t blksiz; 
-    uint32_t temp32; 
-    uint16_t temp16; 
-    ALint status; 
-    float angle; 
-    float sin_pitch; 
-    float sin_yaw; 
-    float cos_pitch; 
-    float cos_yaw; 
-    float pitch; 
-   
-    // compute the sine and cosine of the pitch component 
-    angle = current_pitch * TO_RADIANS; 
-    sin_pitch = sinf (angle); 
-    cos_pitch = cosf (angle); 
-   
-    // compute the sine and cosine of the yaw component 
-    angle = current_yaw * TO_RADIANS; 
-    sin_yaw = sinf (angle); 
-    cos_yaw = cosf (angle); 
-   
-    // build the camera position 
-    camera_position[0] = (ALfloat) -(cos_pitch * cos_yaw) * current_distance * ATTENUATION_FACTOR; 
-    camera_position[1] = (ALfloat) -(cos_pitch * sin_yaw) * current_distance * ATTENUATION_FACTOR; 
-    camera_position[2] = (ALfloat) sin_pitch * current_distance * ATTENUATION_FACTOR; 
-   
-    // build the camera orientation 
-    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 
-    forward_and_up[1] = -camera_position[1]; 
-    forward_and_up[2] = -camera_position[2]; 
-    forward_and_up[3] = 0.0f; 
-    forward_and_up[3] = 0.0f; 
-    forward_and_up[3] = 1.0f; // FIXME: upwards direction is not quite exact. It depends on the lookdown angle. 
-   
-    // update the listener's position and orientation 
-    alListener3f (AL_POSITION, camera_position[0], camera_position[1], camera_position[2]); 
-    alListener3f (AL_VELOCITY, 0, 0, 0); // TODO: compute velocity dynamically with previous position 
-    alListenerfv (AL_ORIENTATION, forward_and_up); 
-   
-    // is one sound enqueued for playing ? 
-    if (enqueued_sound.type != 0) 
-    { 
-       memcpy (&processed_sound, &enqueued_sound, sizeof (enqueued_sound_t)); 
-       enqueued_sound.type = 0; // have a copy of it quickly and reset it (helps preserve thread safety) 
-   
-       // given the type of sound we want, enqueue the right one 
-       pitch = 1.0f; // assume fixed pitch until told otherwise 
-       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); 
-       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); 
-       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); 
-       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); 
-       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); 
-       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); 
-       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); 
-       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); 
-       else if (processed_sound.type == SOUNDTYPE_MOVE) 
-       { 
-          temp32 = rand () % 6; // there are several movement sounds, pick one at random 
-          if      (temp32 == 0) swprintf_s (soundfile_path, WCHAR_SIZEOF (soundfile_path), L"%s/themes/%s/sounds/move1.wav", app_path, theme->name); 
-          else if (temp32 == 1) swprintf_s (soundfile_path, WCHAR_SIZEOF (soundfile_path), L"%s/themes/%s/sounds/move2.wav", app_path, theme->name); 
-          else if (temp32 == 2) swprintf_s (soundfile_path, WCHAR_SIZEOF (soundfile_path), L"%s/themes/%s/sounds/move3.wav", app_path, theme->name); 
-          else if (temp32 == 3) swprintf_s (soundfile_path, WCHAR_SIZEOF (soundfile_path), L"%s/themes/%s/sounds/move4.wav", app_path, theme->name); 
-          else if (temp32 == 4) swprintf_s (soundfile_path, WCHAR_SIZEOF (soundfile_path), L"%s/themes/%s/sounds/move5.wav", app_path, theme->name); 
-          else                  swprintf_s (soundfile_path, WCHAR_SIZEOF (soundfile_path), L"%s/themes/%s/sounds/move6.wav", app_path, theme->name); 
-          pitch = 1.0f + ((((float) rand ()) / RAND_MAX) - 0.5f) / 2.0f; // set a random pitch for these sounds between 0.75 and 1.25 
-       } 
-       else if (processed_sound.type == SOUNDTYPE_SLIDE) 
-       { 
-          swprintf_s (soundfile_path, WCHAR_SIZEOF (soundfile_path), L"%s/themes/%s/sounds/slide.wav", app_path, theme->name); 
-          pitch = 1.0f + ((((float) rand ()) / RAND_MAX) - 0.5f) / 2.0f; // set a random pitch for these sounds between 0.75 and 1.25 
-       } 
-   
-       // now cycle through our known OpenAL buffers and see if we already know this one 
-       for (soundbuffer = soundbuffers; soundbuffer != NULL; soundbuffer = soundbuffer->next) 
-          if ((soundbuffer->pathname != NULL) && (wcscmp (soundbuffer->pathname, soundfile_path) == 0)) 
-             break; // break as soon as we find it 
-   
-       // have we NOT found it ? if so, we must create it 
-       if (soundbuffer == NULL) 
-       { 
-          // load the sound file 
-          Buffer_Initialize (&soundfile); 
-          if (!Buffer_ReadFromFileW (&soundfile, soundfile_path)) 
-             return; // if unable to load this sound file, give up (FIXME: log something ?) 
-   
-          // parse the WAV file 
-          sample_count = channel_count = sample_size = channel_size = 0; 
-          current_pos = 0; 
-          for (;;) 
-          { 
- #define READ_DATA(type) *((type *) &soundfile.data[current_pos]); current_pos += sizeof (type); if (current_pos >= soundfile.size) break; 
-   
-             chunk_id = READ_DATA (uint32_t); 
-             if (chunk_id == *((uint32_t *) "RIFF")) 
-             { 
-                temp32 = READ_DATA (uint32_t); // skip the "chunk size" field 
-                temp32 = READ_DATA (uint32_t); // skip the "riff style" field (typically "WAVE") 
-             } 
-             else if (chunk_id == *((uint32_t *) "fmt ")) 
-             { 
-                blksiz = READ_DATA (uint32_t); 
-                temp16 = READ_DATA (uint16_t); if (temp16 != 1) break; // compressed WAVs are unsupported 
-                temp32 = READ_DATA (uint16_t); channel_count = (int) temp16; 
-                temp32 = READ_DATA (uint32_t); sample_rate = (int) temp32; 
-                temp32 = READ_DATA (uint32_t); 
-                temp16 = READ_DATA (uint16_t); sample_size = (int) temp16; 
-                temp16 = READ_DATA (uint16_t); channel_size = (int) temp16 / 8; 
-                if (blksiz > 16) 
-                   current_pos += blksiz - 16; 
-                if (current_pos >= soundfile.size) 
-                   break; // don't go beyond the end of the file 
-             } 
-             else if (chunk_id == *((uint32_t *) "data")) 
-             { 
-                temp32 = READ_DATA (uint32_t); sample_count = (int) temp32; 
-                break; // current_pos is now at the beginning of data, and data measures sample_count bytes long 
-             } 
-             else 
-             { 
-                blksiz = READ_DATA (uint32_t); // skip the "chunk size" field 
-                current_pos += blksiz; // useless chunk, skip it 
-                if (current_pos >= soundfile.size) 
-                   break; // don't go beyond the end of the file 
-             } 
-   
- #undef READ_DATA 
-          } 
-          if ((sample_count == 0) || (channel_count == 0) || (sample_size == 0) || (channel_size == 0)) 
-             return; // FIXME: not a wav file 
-   
-          // compute the total number of samples (number of channels * number of frames) 
-          sample_count /= sample_size; 
-   
-          // allocate space for one more sound buffer, and tie it to the linked list 
-          if (soundbuffers == NULL) 
-          { 
-             soundbuffers = (openal_buffer_t *) SAFE_malloc (1, sizeof (openal_buffer_t), false); // allocate a new slot to initialize the linked list 
-             soundbuffer = soundbuffers; // and set the pointer to the beginning of the list 
-          } 
-          else 
-          { 
-             for (soundbuffer = soundbuffers; soundbuffer->next != NULL; soundbuffer = soundbuffer->next); // locate the last slot in the linked list 
-             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 
-             soundbuffer = soundbuffer->next; // and update the pointer to it 
-          } 
-          soundbuffer->next = NULL; // this is the last sound buffer in the linked list, do remember it. 
-   
-          temp32 = wcslen (soundfile_path) + 1; // compute pathname buffer length (including null terminator) 
-          soundbuffer->pathname = (wchar_t *) SAFE_malloc (temp32, sizeof (wchar_t), false); // allocate space for it 
-          wcscpy_s (soundbuffer->pathname, temp32, soundfile_path); // and copy this sound's pathname in the newly allocated buffer 
-   
-          // allocate the samples buffer and fill it, mixing all the WAV channels altogether in a 16-bit mono stream 
-          soundbuffer->openal_samples = (ALshort *) SAFE_malloc (sample_count, sizeof (ALshort), false); 
-          for (sample_index = 0; sample_index < sample_count; sample_index++) 
-          { 
-             sample_value = 0; 
-             if (channel_size == 1) 
-                for (channel_index = 0; channel_index < channel_count; channel_index++) 
-                   sample_value += *((int8_t *) soundfile.data[current_pos + sample_index * sample_size + channel_index * channel_size]); 
-             else if (channel_size == 2) 
-                for (channel_index = 0; channel_index < channel_count; channel_index++) 
-                   sample_value += *((int16_t *) &soundfile.data[current_pos + sample_index * sample_size + channel_index * channel_size]); 
-             sample_value /= channel_count; 
-             soundbuffer->openal_samples[sample_index] = (ALshort) sample_value; 
-          } 
-   
-          Buffer_Forget (&soundfile); // we can now forget this sound file 
-   
-          alGenBuffers (1, &soundbuffer->openal_buffer); // create an OpenAL sound buffer and fill it with our samples 
-          alBufferData (soundbuffer->openal_buffer, AL_FORMAT_MONO16, soundbuffer->openal_samples, sample_count * sizeof (ALushort), (ALsizei) sample_rate); 
-          if (alGetError () != AL_NO_ERROR) 
-             return; // FIXME: couldn't fill OpenAL buffer 
-       } 
-   
-       // now we have a buffer to play 
-   
-       // cycle through our known OpenAL sources and find a free one 
-       for (source_index = 0; source_index < source_count; source_index++) 
-          if (!sources[source_index].is_used) 
-             break; // break as soon as we find it 
-   
-       // have we NOT found any ? if so, reallocate so as to have one more 
-       if (source_index == source_count) 
-       { 
-          sources = (openal_source_t *) SAFE_realloc (sources, source_count, source_count + 1, sizeof (openal_source_t), false); 
-          source_count++; // one more source has been created 
-       } 
-   
-       // now we have a source to play our buffer 
-   
-       sources[source_index].is_used = true; // immediately mark it as used 
-       alGenSources (1, &sources[source_index].openal_source); // (re)create an OpenAL source 
-   
-       alSourcef (sources[source_index].openal_source, AL_PITCH, (ALfloat) pitch); // set the source's pitch 
-       alSourcef (sources[source_index].openal_source, AL_GAIN, 1.0f); // set the source's volume (full) 
-       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); 
-       alSource3f (sources[source_index].openal_source, AL_VELOCITY, 0, 0, 0); // set the source's velocity (static) 
-       alSourcei (sources[source_index].openal_source, AL_LOOPING, AL_FALSE); // set it as non-looping 
-       alSourcei (sources[source_index].openal_source, AL_BUFFER, soundbuffer->openal_buffer); // attach our bufferized data to it 
-   
-       // play the source! Audio_Think() will dispose of it when it's finished 
-       alSourcePlay (sources[source_index].openal_source); 
-    } 
-   
-    // cycle through all used sources and see if one is no longer playing 
-    for (source_index = 0; source_index < source_count; source_index++) 
-    { 
-       if (!sources[source_index].is_used) 
-          continue; // skip unused slots 
-   
-       alGetSourcei (sources[source_index].openal_source, AL_SOURCE_STATE, &status); // get this source's playing state 
-       if (status == AL_PLAYING) 
-          continue; // skip sources that are still playing 
-   
-       alSourcei (sources[source_index].openal_source, AL_BUFFER, NULL); // untie the buffer from this source 
-       alDeleteSources (1, &sources[source_index].openal_source); // and tell OpenAL to dispose of it 
-       sources[source_index].is_used = false; // mark this source as unused now 
-    } 
-   
-    return; // finished, audio has been handled 
- } 
-   
-   
- void Audio_PlaySound (int sound_type, float pos_x, float pos_y, float pos_z) 
- { 
-    // helper function to play a sound (WARNING: it is NOT thread-safe!) 
-   
-    if (!options.want_sounds) 
-       return; // if we want no sound, don't play anything 
-   
-    enqueued_sound.type = sound_type; // enqueue this sound for playing. Audio_Think() will take care of it. 
-    enqueued_sound.emitterlocation_x = pos_x; 
-    enqueued_sound.emitterlocation_y = pos_y; 
-    enqueued_sound.emitterlocation_z = pos_z; 
-   
-    return; // finished 
- } 
-