Subversion Repositories Games.Chess Giants

Rev

Rev 136 | Go to most recent revision | 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_sound;
  46. static int source_count;
  47.  
  48.  
  49. bool Audio_Init (void)
  50. {
  51.    // this function initializes the audio subsystem (OpenAL)
  52.  
  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.  
  69.    enqueued_sound.type = 0; // no sound is enqueued yet
  70.  
  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.  
  79.    openal_buffer_t *soundbuffer;
  80.    openal_buffer_t *oldbuffer;
  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
  98.    soundbuffer = soundbuffers;
  99.    while (soundbuffer != NULL)
  100.    {
  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
  108.    }
  109.    soundbuffers = NULL; // at this point, all elements in the linked list are free
  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.  
  119. void Audio_Think (void)
  120. {
  121.    // this function enqueues sounds, plays them and disposes of sound buffers and sources that have finished playing
  122.  
  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;
  139.    int source_index;
  140.    uint32_t chunk_id;
  141.    uint32_t blksiz;
  142.    uint32_t temp32;
  143.    uint16_t temp16;
  144.    ALint status;
  145.    float angle;
  146.    float sin_pitch;
  147.    float sin_yaw;
  148.    float cos_pitch;
  149.    float cos_yaw;
  150.    float pitch;
  151.  
  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.  
  180.    // is one sound enqueued for playing ?
  181.    if (enqueued_sound.type != 0)
  182.    {
  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)
  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      (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.       }
  212.  
  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
  217.  
  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 ?)
  225.  
  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;
  232.  
  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.             }
  265.  
  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
  270.  
  271.          // compute the total number of samples (number of channels * number of frames)
  272.          sample_count /= sample_size;
  273.  
  274.          // allocate space for one more sound buffer, and tie it to the linked list
  275.          if (soundbuffers == NULL)
  276.          {
  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
  279.          }
  280.          else
  281.          {
  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
  285.          }
  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++)
  295.          {
  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;
  305.          }
  306.  
  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
  313.       }
  314.  
  315.       // now we have a buffer to play
  316.  
  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)
  324.       {
  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
  327.       }
  328.  
  329.       // now we have a source to play our buffer
  330.  
  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
  333.  
  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
  340.  
  341.       // play the source! Audio_Think() will dispose of it when it's finished
  342.       alSourcePlay (sources[source_index].openal_source);
  343.    }
  344.  
  345.    // cycle through all used sources and see if one is no longer playing
  346.    for (source_index = 0; source_index < source_count; source_index++)
  347.    {
  348.       if (!sources[source_index].is_used)
  349.          continue; // skip unused slots
  350.  
  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
  358.    }
  359.  
  360.    return; // finished, audio has been handled
  361. }
  362.  
  363.  
  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!)
  367.  
  368.    if (!options.want_sounds)
  369.       return; // if we want no sound, don't play anything
  370.  
  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.  
  376.    return; // finished
  377. }
  378.