Subversion Repositories Games.Chess Giants

Rev

Rev 18 | Rev 130 | 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 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;
  28.  
  29.  
  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
  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. }
  97.  
  98.  
  99. void Audio_Think (void)
  100. {
  101.    // this function disposes of sound buffers and sources that have finished playing
  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
  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;
  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);
  140.  
  141.    // cycle through all used sources and see if one is no longer playing
  142.    for (source_index = 0; source_index < source_count; source_index++)
  143.    {
  144.       if (!sources[source_index].is_used)
  145.          continue; // skip unused slots
  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
  152.       alDeleteSources (1, &sources[source_index].openal_source); // and tell OpenAL to dispose of it
  153.       sources[source_index].is_used = false; // mark this source as unused now
  154.    }
  155.  
  156.    return; // finished, audio has been handled
  157. }
  158.  
  159.  
  160. void Audio_PlaySound (int sound_type, float pos_x, float pos_y, float pos_z)
  161. {
  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;
  182.  
  183.    if (!options.want_sounds)
  184.       return; // if we want no sound, don't play anything
  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.  
  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.  
  325.    return; // finished
  326. }
  327.