Subversion Repositories Games.Chess Giants

Rev

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

  1. // audio.cpp
  2.  
  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 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.  
  24. typedef struct openal_buffer_s
  25. {
  26.    wchar_t *pathname; // sound pathname (mallocated, if NULL then slot is empty)
  27.    ALshort *openal_samples; // OpenAL samples (16-bit mono)
  28.    ALuint openal_buffer; // associated OpenAL buffer ID
  29.    struct openal_buffer_s *next; // pointer to the next element in list
  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.  
  40. // global variables used in this module only
  41. static ALCdevice *openal_device;
  42. static ALCcontext *openal_context;
  43. static openal_buffer_t *soundbuffers; // mallocated linked list
  44. static openal_source_t *sources; // mallocated
  45. static enqueued_sound_t enqueued_sounds[8];
  46. static int enqueued_sound_count = 0;
  47. static int source_count;
  48.  
  49.  
  50. bool Audio_Init (void)
  51. {
  52.    // this function initializes the audio subsystem (OpenAL)
  53.  
  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.  
  70.    memset (enqueued_sounds, 0, sizeof (enqueued_sounds));
  71.    enqueued_sound_count = 0; // no sound is enqueued yet
  72.  
  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.  
  81.    openal_buffer_t *soundbuffer;
  82.    openal_buffer_t *oldbuffer;
  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
  100.    soundbuffer = soundbuffers;
  101.    while (soundbuffer != NULL)
  102.    {
  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
  110.    }
  111.    soundbuffers = NULL; // at this point, all elements in the linked list are free
  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.  
  121. void Audio_Think (void)
  122. {
  123.    // this function enqueues sounds, plays them and disposes of sound buffers and sources that have finished playing
  124.  
  125.    static wchar_t soundfile_path[MAX_PATH];
  126.  
  127.    enqueued_sound_t *processed_sound;
  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;
  141.    int source_index;
  142.    int sound_index;
  143.    uint32_t chunk_id;
  144.    uint32_t blksiz;
  145.    uint32_t temp32;
  146.    uint16_t temp16;
  147.    ALint status;
  148.    float angle;
  149.    float sin_pitch;
  150.    float sin_yaw;
  151.    float cos_pitch;
  152.    float cos_yaw;
  153.    float pitch;
  154.  
  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.  
  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++)
  185.    {
  186.       processed_sound = &enqueued_sounds[sound_index]; // quick access to enqueued sound
  187.  
  188.       // given the type of sound we want, enqueue the right one
  189.       pitch = 1.0f; // assume fixed pitch until told otherwise
  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)
  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.       }
  209.       else if (processed_sound->type == SOUNDTYPE_SLIDE)
  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.       }
  214.  
  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
  219.  
  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 ?)
  227.  
  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;
  234.  
  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.             }
  267.  
  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
  272.  
  273.          // compute the total number of samples (number of channels * number of frames)
  274.          sample_count /= sample_size;
  275.  
  276.          // allocate space for one more sound buffer, and tie it to the linked list
  277.          if (soundbuffers == NULL)
  278.          {
  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
  281.          }
  282.          else
  283.          {
  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
  287.          }
  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++)
  297.          {
  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;
  307.          }
  308.  
  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
  315.       }
  316.  
  317.       // now we have a buffer to play
  318.  
  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)
  326.       {
  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
  329.       }
  330.  
  331.       // now we have a source to play our buffer
  332.  
  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
  335.  
  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)
  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);
  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
  342.  
  343.       // play the source! Audio_Think() will dispose of it when it's finished
  344.       alSourcePlay (sources[source_index].openal_source);
  345.    }
  346.  
  347.    enqueued_sound_count = 0; // after that, remember that all enqueued sounds have been sent to OpenAL
  348.  
  349.    // cycle through all used sources and see if one is no longer playing
  350.    for (source_index = 0; source_index < source_count; source_index++)
  351.    {
  352.       if (!sources[source_index].is_used)
  353.          continue; // skip unused slots
  354.  
  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
  362.    }
  363.  
  364.    return; // finished, audio has been handled
  365. }
  366.  
  367.  
  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!)
  371.  
  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
  374.  
  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
  380.  
  381.    return; // finished
  382. }
  383.