Beispiel #1
0
/**
 * @brief Plays a sound in a group.
 */
int sound_al_playGroup( int group, alSound *s, int once )
{
   int i, j;
   alGroup_t *g;
   ALint state;

   for (i=0; i<al_ngroups; i++) {

      /* Find group. */
      if (al_groups[i].id != group)
         continue;

      g = &al_groups[i];
      g->state = VOICE_PLAYING;
      soundLock();
      for (j=0; j<g->nsources; j++) {
         alGetSourcei( g->sources[j], AL_SOURCE_STATE, &state );

         /* No free ones, just smash the last one. */
         if (j == g->nsources-1) {
            if (state != AL_STOPPED) {
               alSourceStop( g->sources[j] );
               alSourcef( g->sources[j], AL_GAIN, svolume );
            }
         }
         /* Ignore playing/paused. */
         else if ((state == AL_PLAYING) || (state == AL_PAUSED))
            continue;

         /* Attach buffer. */
         alSourcei( g->sources[j], AL_BUFFER, s->u.al.buf );

         /* Do not do positional sound. */
         alSourcei( g->sources[j], AL_SOURCE_RELATIVE, AL_TRUE );

         /* See if should loop. */
         alSourcei( g->sources[j], AL_LOOPING, (once) ? AL_FALSE : AL_TRUE );

         /* Start playing. */
         alSourcePlay( g->sources[j] );

         /* Check for errors. */
         al_checkErr();

         soundUnlock();
         return 0;
      }
      soundUnlock();

      WARN("Group '%d' has no free sounds.", group );

      /* Group matched but not found. */
      break;
   }

   if (i>=al_ngroups)
      WARN("Group '%d' not found.", group);

   return -1;
}
Beispiel #2
0
/**
 * @brief Updates the group sounds.
 */
void sound_al_update (void)
{
   int i, j;
   alGroup_t *g;
   ALfloat d, v;
   unsigned int t, f;

   t = SDL_GetTicks();

   for (i=0; i<al_ngroups; i++) {
      g = &al_groups[i];
      /* Handle fadeout. */
      if (g->state != VOICE_FADEOUT)
         continue;

      /* Calculate fadeout. */
      f = t - g->fade_timer;
      if (f < SOUND_FADEOUT) {
         d = 1. - (ALfloat) f / (ALfloat) SOUND_FADEOUT;
         v = d * svolume * g->volume;
         if (g->speed)
            v *= svolume_speed;
         soundLock();
         for (j=0; j<g->nsources; j++)
            alSourcef( g->sources[j], AL_GAIN, v );
         /* Check for errors. */
         al_checkErr();
         soundUnlock();
      }
      /* Fadeout done. */
      else {
         soundLock();
         v = svolume * g->volume;
         if (g->speed)
            v *= svolume_speed;
         for (j=0; j<g->nsources; j++) {
            alSourceStop( g->sources[j] );
            alSourcei( g->sources[j], AL_BUFFER, AL_NONE );
            alSourcef( g->sources[j], AL_GAIN, v );
         }
         /* Check for errors. */
         al_checkErr();
         soundUnlock();

         /* Mark as done. */
         g->state = VOICE_PLAYING;
      }
   }
}
Beispiel #3
0
/**
 * @brief Updates the listener.
 */
int sound_al_updateListener( double dir, double px, double py,
      double vx, double vy )
{
   double c, s;
   ALfloat ori[6], pos[3], vel[3];

   c = cos(dir);
   s = sin(dir);
   
   soundLock();

   ori[0] = c;
   ori[1] = s;
   ori[2] = 0.;
   ori[3] = 0.;
   ori[4] = 0.;
   ori[5] = 1.;
   alListenerfv( AL_ORIENTATION, ori );
   pos[0] = px;
   pos[1] = py;
   pos[2] = 0.;
   alListenerfv( AL_POSITION, pos );
   vel[0] = vx;
   vel[1] = vy;
   vel[2] = 0.;
   alListenerfv( AL_VELOCITY, vel );

   /* Check for errors. */
   al_checkErr();

   soundUnlock();

   return 0;
}
Beispiel #4
0
/**
 * @brief Frees the music.
 */
void music_al_exit (void)
{
   /* Kill the thread. */
   music_kill();
   SDL_WaitThread( music_player, NULL );

   soundLock();

   /* Free the music. */
   alDeleteBuffers( 2, music_buffer );
   alDeleteSources( 1, &music_source );

   /* Check for errors. */
   al_checkErr();

   soundUnlock();

   /* Free the buffer. */
   if (music_buf != NULL)
      free(music_buf);
   music_buf = NULL;

   /* Destroy the mutex. */
   SDL_DestroyMutex( music_vorbis_lock );
   SDL_DestroyMutex( music_state_lock );
   SDL_DestroyCond( music_state_cond );
}
Beispiel #5
0
/**
 * @brief Creates a sound environment.
 */
int sound_al_env( SoundEnv_t env, double param )
{
   int i;
   ALuint s;
   ALfloat f;

   soundLock();
   switch (env) {
      case SOUND_ENV_NORMAL:
         /* Set global parameters. */
         alSpeedOfSound( 3433. );

         if (al_info.efx == AL_TRUE) {
            /* Disconnect the effect. */
            nalAuxiliaryEffectSloti( efx_directSlot,
                  AL_EFFECTSLOT_EFFECT, AL_EFFECT_NULL );

            /* Set per-source parameters. */
            for (i=0; i<source_ntotal; i++) {
               s = source_total[i];
               alSourcef( s, AL_AIR_ABSORPTION_FACTOR, 0. );
            }
         }
         break;

      case SOUND_ENV_NEBULA:
         f = param / 1000.;

         /* Set global parameters. */
         alSpeedOfSound( 3433./(1. + f*2.) );

         if (al_info.efx == AL_TRUE) {

            if (al_info.efx_reverb == AL_TRUE) {
               /* Tweak the reverb. */
               nalEffectf( efx_reverb, AL_REVERB_DECAY_TIME, 10. );
               nalEffectf( efx_reverb, AL_REVERB_DECAY_HFRATIO, 0.5 );

               /* Connect the effect. */
               nalAuxiliaryEffectSloti( efx_directSlot,
                     AL_EFFECTSLOT_EFFECT, efx_reverb );
            }

            /* Set per-source parameters. */
            for (i=0; i<source_ntotal; i++) {
               s = source_total[i];
               alSourcef( s, AL_AIR_ABSORPTION_FACTOR, 3.*f );
            }
         }
         break;
   }

   /* Check for errors. */
   al_checkErr();

   soundUnlock();

   return 0;
}
Beispiel #6
0
/**
 * @brief Resumes all sounds.
 */
void sound_al_resume (void)
{
   soundLock();
   al_resumev( source_ntotal, source_total );
   /* Check for errors. */
   al_checkErr();
   soundUnlock();
}
Beispiel #7
0
/**
 * @brief Frees the source.
 */
void sound_al_free( alSound *snd )
{
   soundLock();

   /* free the stuff */
   alDeleteBuffers( 1, &snd->u.al.buf );

   soundUnlock();
}
Beispiel #8
0
/**
 * @brief Cleans up after the sound subsytem.
 */
void sound_al_exit (void)
{
   int i;

   soundLock();

   /* Free groups. */
   for (i=0; i<al_ngroups; i++) {
      if (al_groups[i].sources != NULL) {
         free(al_groups[i].sources);
      }
      al_groups[i].sources  = NULL;
      al_groups[i].nsources = 0;
   }
   if (al_groups != NULL)
      free(al_groups);
   al_groups  = NULL;
   al_ngroups = 0;

   /* Free stacks. */
   if (source_all != NULL) {
      alSourceStopv(   source_nall, source_all );
      alDeleteSources( source_nall, source_all );
      free(source_all);
   }
   source_all        = NULL;
   source_nall       = 0;
   if (source_total)
      free(source_total);
   source_total      = NULL;
   source_ntotal     = 0;
   if (source_stack != NULL)
      free(source_stack);
   source_stack      = NULL;
   source_nstack     = 0;
   source_mstack     = 0;

   /* Clean up EFX stuff. */
   if (al_info.efx == AL_TRUE) {
      nalDeleteAuxiliaryEffectSlots( 1, &efx_directSlot );
      if (al_info.efx_reverb == AL_TRUE)
         nalDeleteEffects( 1, &efx_reverb );
      if (al_info.efx_echo == AL_TRUE)
         nalDeleteEffects( 1, &efx_echo );
   }

   /* Clean up global stuff. */
   if (al_context) {
      alcMakeContextCurrent(NULL);
      alcDestroyContext( al_context );
   }
   if (al_device)
      alcCloseDevice( al_device );

   soundUnlock();
   SDL_DestroyMutex( sound_lock );
}
Beispiel #9
0
/**
 * @brief Updates the voice.
 *
 *    @param v Voice to update.
 */
void sound_al_updateVoice( alVoice *v )
{
   ALint state;

   /* Invalid source, mark to delete. */
   if (v->u.al.source == 0) {
      v->state = VOICE_DESTROY;
      return;
   }

   soundLock();

   /* Get status. */
   alGetSourcei( v->u.al.source, AL_SOURCE_STATE, &state );
   if (state == AL_STOPPED) {

      /* Remove buffer so it doesn't start up again if resume is called. */
      alSourcei( v->u.al.source, AL_BUFFER, AL_NONE );

      /* Check for errors. */
      al_checkErr();

      soundUnlock();

      /* Put source back on the list. */
      source_stack[source_nstack] = v->u.al.source;
      source_nstack++;
      v->u.al.source = 0;

      /* Mark as stopped - erased next iteration. */
      v->state = VOICE_STOPPED;
      return;
   }

   /* Set up properties. */
   alSourcef(  v->u.al.source, AL_GAIN, svolume );
   alSourcefv( v->u.al.source, AL_POSITION, v->u.al.pos );
   alSourcefv( v->u.al.source, AL_VELOCITY, v->u.al.vel );

   /* Check for errors. */
   al_checkErr();

   soundUnlock();
}
Beispiel #10
0
/**
 * @brief Set the playing speed.
 */
void sound_al_setSpeed( double s )
{
   int i;
   soundLock();
   for (i=0; i<source_nall; i++)
      alSourcef( source_all[i], AL_PITCH, s );
   /* Check for errors. */
   al_checkErr();
   soundUnlock();
}
Beispiel #11
0
/**
 * @brief Resumes a group.
 */
void sound_al_resumeGroup( int group )
{
   alGroup_t *g;
   g = sound_al_getGroup( group );
   if (g == NULL)
      return;

   soundLock();
   al_resumev( g->nsources, g->sources );
   soundUnlock();
}
Beispiel #12
0
/**
 * @brief Loads the sound.
 *
 *    @param snd Sound to load.
 *    @param filename Name of the file to load into sound.
 */
int sound_al_load( alSound *snd, const char *filename )
{
   int ret;
   SDL_RWops *rw;
   OggVorbis_File vf;
   ALint freq, bits, channels, size;

   /* get the file data buffer from packfile */
   rw = ndata_rwops( filename );

   /* Check to see if it's an OGG. */
   if (ov_test_callbacks( rw, &vf, NULL, 0, sound_al_ovcall_noclose )==0) {
      ret = sound_al_loadOgg( snd, &vf );
   }
   /* Otherwise try WAV. */
   else {
      /* Destroy the partially loaded vorbisfile. */
      ov_clear(&vf);

      /* Try to load Wav. */
      ret = sound_al_loadWav( snd, rw );
   }

   /* Close RWops. */
   SDL_RWclose(rw);

   /* Failed to load. */
   if (ret != 0) {
      WARN("Failed to load sound file '%s'.", filename);
      return ret;
   }

   soundLock();

   /* Get the length of the sound. */
   alGetBufferi( snd->u.al.buf, AL_FREQUENCY, &freq );
   alGetBufferi( snd->u.al.buf, AL_BITS, &bits );
   alGetBufferi( snd->u.al.buf, AL_CHANNELS, &channels );
   alGetBufferi( snd->u.al.buf, AL_SIZE, &size );
   if ((freq==0) || (bits==0) || (channels==0)) {
      WARN("Something went wrong when loading sound file '%s'.", filename);
      snd->length = 0;
   }
   else
      snd->length = (double)size / (double)(freq * (bits/8) * channels);

   /* Check for errors. */
   al_checkErr();

   soundUnlock();

   return 0;
}
Beispiel #13
0
/**
 * @brief Stops playing sound.
 */
void sound_al_stop( alVoice* voice )
{
   soundLock();

   if (voice->u.al.source != 0)
      alSourceStop( voice->u.al.source );

   /* Check for errors. */
   al_checkErr();

   soundUnlock();
}
Beispiel #14
0
/**
 * @brief Sets all the sounds volume to vol
 */
int sound_al_volume( double vol )
{
   int i;
   svolume = (ALfloat) vol;

   soundLock();
   for (i=0; i<source_nall; i++)
      alSourcef( source_all[i], AL_GAIN, svolume );
   soundUnlock();

   return 0;
}
Beispiel #15
0
/**
 * @brief Sets all the sounds volume to vol
 */
int sound_al_volume( double vol )
{
   int i;

   svolume_lin = vol;
   if (vol > 0.) /* Floor of -48 dB (0.00390625 amplitude) */
      svolume = (ALfloat) 1 / pow(2, (1 - vol) * 8);
   else
      svolume     = 0.;
   soundLock();
   for (i=0; i<source_nall; i++)
      alSourcef( source_all[i], AL_GAIN, svolume );
   soundUnlock();

   return 0;
}
Beispiel #16
0
/**
 * @brief Plays a voice.
 */
static int al_playVoice( alVoice *v, alSound *s,
      ALfloat px, ALfloat py, ALfloat vx, ALfloat vy, ALint relative )
{
   /* Must be below the limit. */
   if (sound_speed > SOUND_SPEED_PLAY_LIMIT)
      return 0;

   /* Set up the source and buffer. */
   v->u.al.source = sound_al_getSource();
   if (v->u.al.source == 0)
      return -1;
   v->u.al.buffer = s->u.al.buf;

   soundLock();

   /* Attach buffer. */
   alSourcei( v->u.al.source, AL_BUFFER, v->u.al.buffer );

   /* Enable positional sound. */
   alSourcei( v->u.al.source, AL_SOURCE_RELATIVE, relative );

   /* Update position. */
   v->u.al.pos[0] = px;
   v->u.al.pos[1] = py;
   v->u.al.pos[2] = 0.;
   v->u.al.vel[0] = vx;
   v->u.al.vel[1] = vy;
   v->u.al.vel[2] = 0.;

   /* Set up properties. */
   alSourcef(  v->u.al.source, AL_GAIN, svolume*svolume_speed );
   alSourcefv( v->u.al.source, AL_POSITION, v->u.al.pos );
   alSourcefv( v->u.al.source, AL_VELOCITY, v->u.al.vel );

   /* Defaults just in case. */
   alSourcei( v->u.al.source, AL_LOOPING, AL_FALSE );

   /* Start playing. */
   alSourcePlay( v->u.al.source );

   /* Check for errors. */
   al_checkErr();

   soundUnlock();

   return 0;
}
Beispiel #17
0
/**
 * @brief Loads a wav file from the rw if possible.
 *
 * @note Closes the rw.
 *
 *    @param snd Sound to load wav into.
 *    @param rw Data for the wave.
 */
static int sound_al_loadWav( alSound *snd, SDL_RWops *rw )
{
   SDL_AudioSpec wav_spec;
   Uint32 wav_length;
   Uint8 *wav_buffer;
   ALenum format;

   SDL_RWseek( rw, 0, SEEK_SET );

   /* Load WAV. */
   if (SDL_LoadWAV_RW( rw, 0, &wav_spec, &wav_buffer, &wav_length) == NULL) {
      WARN(_("SDL_LoadWav_RW failed: %s"), SDL_GetError());
      return -1;
   }

   /* Handle format. */
   switch (wav_spec.format) {
      case AUDIO_U8:
      case AUDIO_S8:
         format = (wav_spec.channels==1) ? AL_FORMAT_MONO8 : AL_FORMAT_STEREO8;
         break;
      case AUDIO_U16LSB:
      case AUDIO_S16LSB:
         format = (wav_spec.channels==1) ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16;
         break;
      case AUDIO_U16MSB:
      case AUDIO_S16MSB:
         WARN( _("Big endian WAVs unsupported!") );
         return -1;
      default:
         WARN( _("Invalid WAV format!") );
         return -1;
   }

   /* Load into openal. */
   soundLock();
   /* Create new buffer. */
   alGenBuffers( 1, &snd->u.al.buf );
   /* Put into the buffer. */
   alBufferData( snd->u.al.buf, format, wav_buffer, wav_length, wav_spec.freq );
   soundUnlock();

   /* Clean up. */
   free( wav_buffer );
   return 0;
}
Beispiel #18
0
/**
 * @brief Initializes the OpenAL music subsystem.
 */
int music_al_init (void)
{
   ALfloat v[] = { 0., 0., 0. };

   /* Create threading mechanisms. */
   music_state_cond  = SDL_CreateCond();
   music_state_lock  = SDL_CreateMutex();
   music_vorbis_lock = SDL_CreateMutex();
   music_vorbis.rw   = NULL; /* indication it's not loaded */

   /* Create the buffer. */
   music_bufSize     = conf.al_bufsize * 1024;
   music_buf         = malloc( music_bufSize );

   soundLock();

   /* music_source created in sound_al_init. */

   /* Generate buffers and sources. */
   alGenBuffers( 2, music_buffer );

   /* Set up OpenAL properties. */
   alSourcef(  music_source, AL_GAIN, music_vol );
   alSourcei(  music_source, AL_SOURCE_RELATIVE, AL_TRUE );
   alSourcefv( music_source, AL_POSITION, v );
   alSourcefv( music_source, AL_VELOCITY, v );

   /* Check for errors. */
   al_checkErr();

   /* Set state to none. */
   music_state = 0;

   soundUnlock();

   /*
    * Start up thread and have it inform us when it already reaches the main loop.
    */
   musicLock();
   music_state  = MUSIC_STATE_STARTUP;
   music_player = SDL_CreateThread( music_thread, NULL );
   SDL_CondWait( music_state_cond, music_state_lock );
   musicUnlock();

   return 0;
}
Beispiel #19
0
/**
 * @brief Loads an ogg file from a tested format if possible.
 *
 *    @param snd Sound to load ogg into.
 *    @param vf Vorbisfile containing the song.
 */
static int sound_al_loadOgg( alSound *snd, OggVorbis_File *vf )
{
   int ret;
   long i;
   int section;
   vorbis_info *info;
   ALenum format;
   ogg_int64_t len;
   char *buf;

   /* Finish opening the file. */
   ret = ov_test_open(vf);
   if (ret) {
      WARN("Failed to finish loading OGG file: %s", vorbis_getErr(ret) );
      return -1;
   }

   /* Get file information. */
   info   = ov_info( vf, -1 );
   format = (info->channels == 1) ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16;
   len    = ov_pcm_total( vf, -1 ) * info->channels * 2;

   /* Allocate memory. */
   buf = malloc( len );

   /* Fill buffer. */
   i = 0;
   while (i < len) {
      /* Fill buffer with data in the 16 bit signed samples format. */
      i += ov_read( vf, &buf[i], len-i, VORBIS_ENDIAN, 2, 1, &section );
   }

   soundLock();
   /* Create new buffer. */
   alGenBuffers( 1, &snd->u.al.buf );
   /* Put into buffer. */
   alBufferData( snd->u.al.buf, format, buf, len, info->rate );
   soundUnlock();

   /* Clean up. */
   free(buf);
   ov_clear(vf);

   return 0;
}
Beispiel #20
0
/**
 * @brief Resumes a group.
 */
void sound_al_resumeGroup( int group )
{
   int i;
   alGroup_t *g;

   for (i=0; i<al_ngroups; i++) {
      if (al_groups[i].id == group) {
         g = &al_groups[i];
         soundLock();
         al_resumev( g->nsources, g->sources );
         soundUnlock();
         break;
      }
   }

   if (i>=al_ngroups)
      WARN("Group '%d' not found.", group);
}
Beispiel #21
0
/**
 * @brief Sets the volume.
 */
int music_al_volume( double vol )
{
   soundLock();

   music_vol = vol;

   /* only needed if playing */
   if (music_al_isPlaying()) {

      alSourcef( music_source, AL_GAIN, vol );

      /* Check for errors. */
      al_checkErr();
   }
 
   soundUnlock();

   return 0;
}
Beispiel #22
0
/**
 * @brief Internal volume update function.
 */
static void sound_al_volumeUpdate (void)
{
   int i, j;
   alGroup_t *g;
   double v;

   soundLock();
   /* Do generic ones. */
   for (i=0; i<source_ntotal; i++)
      alSourcef( source_total[i], AL_GAIN, svolume*svolume_speed );
   /* Do specific groups. */
   for (i=0; i<al_ngroups; i++) {
      g = &al_groups[i];
      v = svolume * g->volume;
      if (g->speed)
         v *= svolume_speed;
      for (j=0; j<g->nsources; j++)
         alSourcef( g->sources[j], AL_GAIN, v );
   }
   soundUnlock();
}
Beispiel #23
0
/**
 * @brief Plays a voice.
 */
static int al_playVoice( alVoice *v, alSound *s,
      ALfloat px, ALfloat py, ALfloat vx, ALfloat vy, ALint relative )
{
   /* Set up the source and buffer. */
   v->u.al.source = sound_al_getSource();
   if (v->u.al.source == 0)
      return -1;
   v->u.al.buffer = s->u.al.buf;

   soundLock();

   /* Attach buffer. */
   alSourcei( v->u.al.source, AL_BUFFER, v->u.al.buffer );

   /* Enable positional sound. */
   alSourcei( v->u.al.source, AL_SOURCE_RELATIVE, relative );

   /* Update position. */
   v->u.al.pos[0] = px;
   v->u.al.pos[1] = py;
   v->u.al.pos[2] = 0.;
   v->u.al.vel[0] = vx;
   v->u.al.vel[1] = vy;
   v->u.al.vel[2] = 0.;

   /* Set up properties. */
   alSourcef(  v->u.al.source, AL_GAIN, svolume );
   alSourcefv( v->u.al.source, AL_POSITION, v->u.al.pos );
   alSourcefv( v->u.al.source, AL_VELOCITY, v->u.al.vel );

   /* Start playing. */
   alSourcePlay( v->u.al.source );

   /* Check for errors. */
   al_checkErr();

   soundUnlock();

   return 0;
}
Beispiel #24
0
/**
 * @brief Set the playing speed.
 */
void sound_al_setSpeed( double s )
{
   int i, j;
   alGroup_t *g;

   soundLock();
   sound_speed = s; /* Set the speed. */
   /* Do all the groupless. */
   for (i=0; i<source_ntotal; i++)
      alSourcef( source_total[i], AL_PITCH, s );
   /* Do specific groups. */
   for (i=0; i<al_ngroups; i++) {
      g = &al_groups[i];
      if (!g->speed)
         continue;
      for (j=0; j<g->nsources; j++)
         alSourcef( g->sources[j], AL_PITCH, s );
   }
   /* Check for errors. */
   al_checkErr();
   soundUnlock();
}
Beispiel #25
0
/**
 * @brief Sets the volume.
 */
int music_al_volume( double vol )
{
   soundLock();

   music_vol_lin = vol;
   if (vol > 0.) /* Floor of -48 dB (0.00390625 amplitude) */
      music_vol = 1 / pow(2, (1 - vol) * 8 );
   else
      music_vol = 0.;

   /* only needed if playing */
   if (music_al_isPlaying()) {

      alSourcef( music_source, AL_GAIN, music_vol );

      /* Check for errors. */
      al_checkErr();
   }

   soundUnlock();

   return 0;
}
Beispiel #26
0
/**
 * @brief Initializes the sound subsystem.
 *
 *    @return 0 on success.
 */
int sound_al_init (void)
{
   int ret;
   ALuint s;
   ALint freq;
   ALint attribs[4] = { 0, 0, 0, 0 };

   /* Default values. */
   ret = 0;

   /* we'll need a mutex */
   sound_lock = SDL_CreateMutex();
   soundLock();

   /* opening the default device */
   al_device = alcOpenDevice(NULL);
   if (al_device == NULL) {
      WARN(_("Unable to open default sound device"));
      ret = -1;
      goto snderr_dev;
   }

   /* Query EFX extension. */
   if (conf.al_efx) {
      al_info.efx = alcIsExtensionPresent( al_device, "ALC_EXT_EFX" );
      if (al_info.efx == AL_TRUE) {
         attribs[0] = ALC_MAX_AUXILIARY_SENDS;
         attribs[1] = 4;
      }
   }
   else
      al_info.efx = AL_FALSE;

   /* Create the OpenAL context */
   al_context = alcCreateContext( al_device, attribs );
   if (al_context == NULL) {
      WARN(_("Unable to create OpenAL context"));
      ret = -2;
      goto snderr_ctx;
   }

   /* Clear the errors */
   alGetError();

   /* Set active context */
   if (alcMakeContextCurrent( al_context )==AL_FALSE) {
      WARN(_("Failure to set default context"));
      ret = -4;
      goto snderr_act;
   }

   /* Get context information. */
   alcGetIntegerv( al_device, ALC_FREQUENCY, sizeof(freq), &freq );

   /* Try to enable EFX. */
   if (al_info.efx == AL_TRUE)
      al_enableEFX();
   else {
      al_info.efx_reverb = AL_FALSE;
      al_info.efx_echo   = AL_FALSE;
   }

   /* Allocate source for music. */
   alGenSources( 1, &music_source );

   /* Check for errors. */
   al_checkErr();

   /* Start allocating the sources - music has already taken his */
   source_nstack  = 0;
   source_mstack  = 0;
   while (source_nstack < conf.snd_voices) {
      if (source_mstack < source_nstack+1) { /* allocate more memory */
         if (source_mstack == 0)
            source_mstack = conf.snd_voices;
         else
            source_mstack *= 2;
         source_stack = realloc( source_stack, sizeof(ALuint) * source_mstack );
      }
      alGenSources( 1, &s );
      source_stack[source_nstack] = s;

      /* How OpenAL distance model works:
       *
       * Clamped:
       *  gain = distance_function( CLAMP( AL_REFERENCE_DISTANCE, AL_MAX_DISTANCE, distance ) );
       *
       * Distance functions:
       *                                       AL_REFERENCE_DISTANCE
       *  * Inverse = ------------------------------------------------------------------------------
       *              AL_REFERENCE_DISTANCE + AL_ROLLOFF_FACTOR ( distance - AL_REFERENCE_DISTANCE )
       *
       *             1 - AL_ROLLOFF_FACTOR ( distance - AL_REFERENCE_DISTANCE )
       *  * Linear = ----------------------------------------------------------
       *                      AL_MAX_DISTANCE - AL_REFERENCE_DISTANCE
       *
       *                  /       distance        \ -AL_ROLLOFF_FACTOR
       *  * Exponential = | --------------------- |
       *                  \ AL_REFERENCE_DISTANCE /
       *
       *
       * Some values:
       *
       *  model    falloff  reference   100     1000    5000   10000
       *  linear     1        500      1.000   0.947   0.526   0.000
       *  inverse    1        500      1.000   0.500   0.100   0.050
       *  exponent   1        500      1.000   0.500   0.100   0.050
       *  inverse   0.5       500      1.000   0.667   0.182   0.095
       *  exponent  0.5       500      1.000   0.707   0.316   0.223
       *  inverse    2        500      1.000   0.333   0.052   0.026
       *  exponent   2        500      1.000   0.250   0.010   0.003
       */
      alSourcef( s, AL_REFERENCE_DISTANCE, 500. ); /* Close distance to clamp at (doesn't get louder). */
      alSourcef( s, AL_MAX_DISTANCE,       25000. ); /* Max distance to clamp at (doesn't get quieter). */
      alSourcef( s, AL_ROLLOFF_FACTOR,     1. ); /* Determines how it drops off. */

      /* Set the filter. */
      if (al_info.efx == AL_TRUE)
         alSource3i( s, AL_AUXILIARY_SEND_FILTER, efx_directSlot, 0, AL_FILTER_NULL );

      /* Check for error. */
      if (alGetError() == AL_NO_ERROR)
         source_nstack++;
      else
         break;
   }
   /* Reduce ram usage. */
   source_mstack = source_nstack;
   source_stack  = realloc( source_stack, sizeof(ALuint) * source_mstack );
   /* Copy allocated sources to total stack. */
   source_ntotal = source_mstack;
   source_total  = malloc( sizeof(ALuint) * source_mstack );
   memcpy( source_total, source_stack, sizeof(ALuint) * source_mstack );
   /* Copy allocated sources to all stack. */
   source_nall   = source_mstack;
   source_all    = malloc( sizeof(ALuint) * source_mstack );
   memcpy( source_all, source_stack, sizeof(ALuint) * source_mstack );

   /* Set up how sound works. */
   alDistanceModel( AL_INVERSE_DISTANCE_CLAMPED ); /* Clamping is fundamental so it doesn't sound like crap. */
   alDopplerFactor( 1. );
   sound_al_env( SOUND_ENV_NORMAL, 0. );

   /* Check for errors. */
   al_checkErr();

   /* we can unlock now */
   soundUnlock();

   /* debug magic */
   DEBUG(_("OpenAL started: %d Hz"), freq);
   DEBUG(_("Renderer: %s"), alGetString(AL_RENDERER));
   if (al_info.efx == AL_FALSE)
      DEBUG(_("Version: %s without EFX"), alGetString(AL_VERSION));
   else
      DEBUG(_("Version: %s with EFX %d.%d"), alGetString(AL_VERSION),
            al_info.efx_major, al_info.efx_minor);
   DEBUG("");

   return ret;

   /*
    * error handling
    */
snderr_act:
   alcDestroyContext( al_context );
snderr_ctx:
   al_context = NULL;
   alcCloseDevice( al_device );
snderr_dev:
   al_device = NULL;
   soundUnlock();
   SDL_DestroyMutex( sound_lock );
   sound_lock = NULL;
   return ret;
}
Beispiel #27
0
/**
 * @brief Loads a buffer.
 *
 *    @param buffer Buffer to load.
 */
static int stream_loadBuffer( ALuint buffer )
{
   int ret, size, section, result;

   musicVorbisLock();

   /* Make sure music is valid. */
   if (music_vorbis.rw == NULL) {
      musicVorbisUnlock();
      return -1;
   }

   ret  = 0;
   size = 0;
   while (size < music_bufSize) { /* fille up the entire data buffer */

      result = ov_read_filter(
            &music_vorbis.stream,   /* stream */
            &music_buf[size],       /* data */
            music_bufSize - size,   /* amount to read */
            VORBIS_ENDIAN,          /* big endian? */
            2,                      /* 16 bit */
            1,                      /* signed */
            &section,               /* current bitstream */
            rg_filter,              /* filter function */
            &music_vorbis );        /* filter parameter */

      /* End of file. */
      if (result == 0) {
         if (size == 0) {
            musicVorbisUnlock();
            return -2;
         }
         ret = 1;
         break;
      }
      /* Hole error. */
      else if (result == OV_HOLE) {
         musicVorbisUnlock();
         WARN("OGG: Vorbis hole detected in music!");
         return 0;
      }
      /* Bad link error. */
      else if (result == OV_EBADLINK) {
         musicVorbisUnlock();
         WARN("OGG: Invalid stream section or corrupt link in music!");
         return -1;
      }

      size += result;
   }

   musicVorbisUnlock();

   /* load the buffer up */
   soundLock();
   alBufferData( buffer, music_vorbis.format,
         music_buf, size, music_vorbis.info->rate );
   soundUnlock();

   return ret;
}
Beispiel #28
0
/**
 * @brief The music thread.
 *
 *    @param unused Unused.
 */
static int music_thread( void* unused )
{
   (void)unused;

   int ret;
   int active; /* active buffer */
   ALint state;
   ALuint removed[2];
   ALenum value;
   music_state_t cur_state;
   ALfloat gain;
   int fadein_start = 0;
   uint32_t fade, fade_timer = 0;

   while (1) {

      /* Handle states. */
      musicLock();

      /* Handle new command. */
      switch (music_command) {
         case MUSIC_CMD_KILL:
            if (music_state != MUSIC_STATE_IDLE)
               music_state = MUSIC_STATE_STOPPING;
            else {
               music_state = MUSIC_STATE_DEAD;
            }
            /* Does not clear command. */
            break;

         case MUSIC_CMD_STOP:
            /* Notify of stopped. */
            if (music_state == MUSIC_STATE_IDLE)
               SDL_CondBroadcast( music_state_cond );
            else
               music_state = MUSIC_STATE_STOPPING;
            break;

         case MUSIC_CMD_PLAY:
            /* Set appropriate state. */
            if (music_state == MUSIC_STATE_PAUSING)
               music_state = MUSIC_STATE_RESUMING;
            else if (music_state == MUSIC_STATE_FADEIN)
               fade_timer = SDL_GetTicks() - MUSIC_FADEIN_DELAY;
            else
               music_state = MUSIC_STATE_LOADING;
            /* Disable fadein. */
            fadein_start = 0;
            /* Clear command. */
            music_command = MUSIC_CMD_NONE;
            SDL_CondBroadcast( music_state_cond );
            break;

         case MUSIC_CMD_FADEOUT:
            /* Notify of stopped. */
            if (music_state != MUSIC_STATE_IDLE) {
               music_state = MUSIC_STATE_FADEOUT;
               /* Set timer. */
               fade_timer = SDL_GetTicks();
            }
            /* Clear command. */
            music_command = MUSIC_CMD_NONE;
            SDL_CondBroadcast( music_state_cond );
            break;

         case MUSIC_CMD_FADEIN:
            if ((music_state == MUSIC_STATE_FADEIN) ||
                  (music_state == MUSIC_STATE_PLAYING))
               SDL_CondBroadcast( music_state_cond );
            else {
               music_state = MUSIC_STATE_LOADING;
               /* Set timer. */
               fade_timer = SDL_GetTicks();
               fadein_start = 1;
            }
            /* Clear command. */
            music_command = MUSIC_CMD_NONE;
            break;

         case MUSIC_CMD_PAUSE:
            if (music_state == MUSIC_STATE_PAUSED)
               SDL_CondBroadcast( music_state_cond );
            else if ((music_state == MUSIC_STATE_PLAYING) ||
                  (music_state == MUSIC_STATE_FADEIN))
               music_state = MUSIC_STATE_PAUSING;
            music_command = MUSIC_CMD_NONE;
            break;

         case MUSIC_CMD_NONE:
            break;
      }
      cur_state = music_state;
      musicUnlock();

      /*
       * Main processing loop.
       */
      switch (cur_state) {
         /*
          * Basically send a message that thread is up and running.
          */
         case MUSIC_STATE_STARTUP:
            musicLock();
            music_state = MUSIC_STATE_IDLE;
            SDL_CondBroadcast( music_state_cond );
            musicUnlock();
            break;

         /*
          * We died.
          */
         case MUSIC_STATE_DEAD:
            return 0;
            break;

         /*
          * Delays at the end.
          */
         case MUSIC_STATE_PAUSED:
         case MUSIC_STATE_IDLE:
            break;

         /*
          * Resumes the paused song.
          */
         case MUSIC_STATE_RESUMING:
            soundLock();
            alSourcePlay( music_source );
            alSourcef( music_source, AL_GAIN, music_vol );
            /* Check for errors. */
            al_checkErr();
            soundUnlock();

            musicLock();
            music_state = MUSIC_STATE_PLAYING;
            SDL_CondBroadcast( music_state_cond );
            musicUnlock();
            break;

         /*
          * Pause the song.
          */
         case MUSIC_STATE_PAUSING:
            soundLock();
            alSourcePause( music_source );
            /* Check for errors. */
            al_checkErr();
            soundUnlock();

            musicLock();
            music_state = MUSIC_STATE_PAUSED;
            SDL_CondBroadcast( music_state_cond );
            musicUnlock();
            break;

         /*
          * Stop song setting to IDLE.
          */
         case MUSIC_STATE_STOPPING:
            soundLock();

            /* Stop and remove buffers. */
            alSourceStop( music_source );
            alGetSourcei( music_source, AL_BUFFERS_PROCESSED, &value );
            if (value > 0)
               alSourceUnqueueBuffers( music_source, value, removed );
            /* Clear timer. */
            fade_timer = 0;

            /* Reset volume. */
            alSourcef( music_source, AL_GAIN, music_vol );

            soundUnlock();

            musicLock();
            music_state = MUSIC_STATE_IDLE;
            SDL_CondBroadcast( music_state_cond );
            if (!music_forced)
               music_rechoose();
            musicUnlock();
            break;

         /*
          * Load the song.
          */
         case MUSIC_STATE_LOADING:

            /* Load buffer and start playing. */
            active = 0; /* load first buffer */
            ret = stream_loadBuffer( music_buffer[active] );
            soundLock();
            alSourceQueueBuffers( music_source, 1, &music_buffer[active] );
            /* Special case NULL file or error. */
            if (ret < 0) {
               soundUnlock();
               /* Force state to stopped. */
               musicLock();
               music_state = MUSIC_STATE_IDLE;
               SDL_CondBroadcast( music_state_cond );
               if (!music_forced)
                  music_rechoose();
               musicUnlock();
               break;
            }
            /* Force volume level. */
            alSourcef( music_source, AL_GAIN, (fadein_start) ? 0. : music_vol );

            /* Start playing. */
            alSourcePlay( music_source );

            /* Check for errors. */
            al_checkErr();

            soundUnlock();
            /* Special case of a very short song. */
            if (ret > 1) {
               active = -1;

               musicLock();
               if (fadein_start)
                  music_state = MUSIC_STATE_FADEIN;
               else
                  music_state = MUSIC_STATE_PLAYING;
               SDL_CondBroadcast( music_state_cond );
               musicUnlock();
               break;
            }

            /* Load second buffer. */
            active = 1;
            ret = stream_loadBuffer( music_buffer[active] );
            if (ret < 0) {
               active = -1;
            }
            else {
               soundLock();
               alSourceQueueBuffers( music_source, 1, &music_buffer[active] );
               /* Check for errors. */
               al_checkErr();
               soundUnlock();
               active = 1 - active;
            }

            musicLock();
            if (fadein_start)
               music_state = MUSIC_STATE_FADEIN;
            else
               music_state = MUSIC_STATE_PLAYING;
            SDL_CondBroadcast( music_state_cond );
            musicUnlock();
            break;

         /*
          * Fades in the music.
          */
         case MUSIC_STATE_FADEOUT:
         case MUSIC_STATE_FADEIN:
            /* See if must still fade. */
            fade = SDL_GetTicks() - fade_timer;
            if (cur_state == MUSIC_STATE_FADEIN) {

               if (fade < MUSIC_FADEIN_DELAY) {
                  gain = (ALfloat)fade / (ALfloat)MUSIC_FADEIN_DELAY;
                  soundLock();
                  alSourcef( music_source, AL_GAIN, gain*music_vol );
                  /* Check for errors. */
                  al_checkErr();
                  soundUnlock();
               }
               /* No need to fade anymore. */
               else {
                  /* Set volume to normal level. */
                  soundLock();
                  alSourcef( music_source, AL_GAIN, music_vol );
                  /* Check for errors. */
                  al_checkErr();
                  soundUnlock();

                  /* Change state to playing. */
                  musicLock();
                  music_state = MUSIC_STATE_PLAYING;
                  musicUnlock();
               }
            }
            else if (cur_state == MUSIC_STATE_FADEOUT) {

               if (fade < MUSIC_FADEOUT_DELAY) {
                  gain = 1. - (ALfloat)fade / (ALfloat)MUSIC_FADEOUT_DELAY;
                  soundLock();
                  alSourcef( music_source, AL_GAIN, gain*music_vol );
                  /* Check for errors. */
                  al_checkErr();
                  soundUnlock();
               }
               else {
                  /* Music should stop. */
                  musicLock();
                  music_state = MUSIC_STATE_STOPPING;
                  musicUnlock();
                  break;
               }
            }

            /* Purpose fallthrough. */

         /*
          * Play the song if needed.
          */
         case MUSIC_STATE_PLAYING:

            /* Special case where file has ended. */
            if (active < 0) {
               soundLock();
               alGetSourcei( music_source, AL_SOURCE_STATE, &state );

               if (state == AL_STOPPED) {
                  alGetSourcei( music_source, AL_BUFFERS_PROCESSED, &value );
                  if (value > 0)
                     alSourceUnqueueBuffers( music_source, value, removed );
                  soundUnlock();

                  musicLock();
                  music_state = MUSIC_STATE_IDLE;
                  if (!music_forced)
                     music_rechoose();
                  musicUnlock();
                  break;
               }

               soundUnlock();

               break;
            }

            soundLock();

            /* See if needs another buffer set. */
            alGetSourcei( music_source, AL_BUFFERS_PROCESSED, &state );
            if (state > 0) {

               /* refill active buffer */
               alSourceUnqueueBuffers( music_source, 1, removed );
               ret = stream_loadBuffer( music_buffer[active] );
               if (ret < 0) {
                  active = -1;
               }
               else {
                  alSourceQueueBuffers( music_source, 1, &music_buffer[active] );
                  active = 1 - active;
               }
            }

            /* Check for errors. */
            al_checkErr();

            soundUnlock();
      }

      /*
       * Global thread delay.
       */
      SDL_Delay(0);

   }

   return 0;
}
Beispiel #29
0
/**
 * @brief Initializes the sound subsystem.
 *
 *    @return 0 on success.
 */
int sound_al_init (void)
{
   int ret;
   const ALchar* dev;
   ALuint s;
   ALint freq;
   ALint attribs[4] = { 0, 0, 0, 0 };

   /* Default values. */
   ret = 0;

   /* we'll need a mutex */
   sound_lock = SDL_CreateMutex();
   soundLock();

   /* Get the sound device. */
   dev = alcGetString( NULL, ALC_DEFAULT_DEVICE_SPECIFIER );

   /* opening the default device */
   al_device = alcOpenDevice(NULL);
   if (al_device == NULL) {
      WARN("Unable to open default sound device");
      ret = -1;
      goto snderr_dev;
   }

   /* Query EFX extension. */
   if (conf.al_efx) {
      al_info.efx = alcIsExtensionPresent( al_device, "ALC_EXT_EFX" );
      if (al_info.efx == AL_TRUE) {
         attribs[0] = ALC_MAX_AUXILIARY_SENDS;
         attribs[1] = 4;
      }
   }
   else
      al_info.efx = AL_FALSE;

   /* Create the OpenAL context */
   al_context = alcCreateContext( al_device, attribs );
   if (sound_lock == NULL) {
      WARN("Unable to create OpenAL context");
      ret = -2;
      goto snderr_ctx;
   }

   /* Clear the errors */
   alGetError();

   /* Set active context */
   if (alcMakeContextCurrent( al_context )==AL_FALSE) {
      WARN("Failure to set default context");
      ret = -4;
      goto snderr_act;
   }

   /* Get context information. */
   alcGetIntegerv( al_device, ALC_FREQUENCY, sizeof(freq), &freq );

   /* Try to enable EFX. */
   if (al_info.efx == AL_TRUE)
      al_enableEFX();
   else {
      al_info.efx_reverb = AL_FALSE;
      al_info.efx_echo   = AL_FALSE;
   }

   /* Allocate source for music. */
   alGenSources( 1, &music_source );

   /* Check for errors. */
   al_checkErr();

   /* Start allocating the sources - music has already taken his */
   source_nstack  = 0;
   source_mstack  = 0;
   while (source_nstack < SOUND_MAX_SOURCES) {
      if (source_mstack < source_nstack+1) { /* allocate more memory */
         source_mstack += 32;
         source_stack = realloc( source_stack, sizeof(ALuint) * source_mstack );
      }
      alGenSources( 1, &s );
      source_stack[source_nstack] = s;

      /* Distance model defaults. */
      alSourcef( s, AL_MAX_DISTANCE,       5000. );
      alSourcef( s, AL_ROLLOFF_FACTOR,     1. );
      alSourcef( s, AL_REFERENCE_DISTANCE, 500. );

      /* Set the filter. */
      if (al_info.efx == AL_TRUE)
         alSource3i( s, AL_AUXILIARY_SEND_FILTER, efx_directSlot, 0, AL_FILTER_NULL );

      /* Check for error. */
      if (alGetError() == AL_NO_ERROR)
         source_nstack++;
      else
         break;
   }
   /* Reduce ram usage. */
   source_mstack = source_nstack;
   source_stack  = realloc( source_stack, sizeof(ALuint) * source_mstack );
   /* Copy allocated sources to total stack. */
   source_ntotal = source_mstack;
   source_total  = malloc( sizeof(ALuint) * source_mstack );
   memcpy( source_total, source_stack, sizeof(ALuint) * source_mstack );
   /* Copy allocated sources to all stack. */
   source_nall   = source_mstack;
   source_all    = malloc( sizeof(ALuint) * source_mstack );
   memcpy( source_all, source_stack, sizeof(ALuint) * source_mstack );

   /* Set up how sound works. */
   alDistanceModel( AL_INVERSE_DISTANCE_CLAMPED );
   alDopplerFactor( 1. );
   sound_al_env( SOUND_ENV_NORMAL, 0. );

   /* Check for errors. */
   al_checkErr();

   /* we can unlock now */
   soundUnlock();

   /* debug magic */
   DEBUG("OpenAL started: %d Hz", freq);
   DEBUG("Renderer: %s", alGetString(AL_RENDERER));
   if (al_info.efx == AL_FALSE)
      DEBUG("Version: %s without EFX", alGetString(AL_VERSION));
   else
      DEBUG("Version: %s with EFX %d.%d", alGetString(AL_VERSION),
            al_info.efx_major, al_info.efx_minor);
   DEBUG();

   return 0;

   /*
    * error handling
    */
snderr_act:
   alcDestroyContext( al_context );
snderr_ctx:
   al_context = NULL;
   alcCloseDevice( al_device );
snderr_dev:
   al_device = NULL;
   soundUnlock();
   SDL_DestroyMutex( sound_lock );
   sound_lock = NULL;
   return ret;
}
Beispiel #30
0
/**
 * @brief Loads a wav file from the rw if possible.
 *
 * @note Closes the rw.
 *
 *    @param snd Sound to load wav into.
 *    @param rw Data for the wave.
 */
static int sound_al_loadWav( alSound *snd, SDL_RWops *rw )
{
   int len;
   uint32_t i;
   char magic[4], *buf;
   ALenum format;
   uint32_t filelen, chunklen, rate, unused32;
   uint16_t compressed, channels, align, unused16;

   /* Some initialization. */
   compressed = 0;
   channels   = 0;

   /* Seek to start. */
   SDL_RWseek( rw, 0, SEEK_SET );

   /* Check RIFF header. */
   if (sound_al_wavReadCmp( rw, "RIFF", 4 )) {
      WARN("RIFF header not found.");
      goto wav_err;
   }
   /* Get file length. */
   if (sound_al_wavGetLen32( rw, &filelen )) {
      WARN("Unable to get WAVE length.");
      goto wav_err;
   }
   /* Check WAVE header. */
   if (sound_al_wavReadCmp( rw, "WAVE", 4 )) {
      WARN("WAVE header not found.");
      goto wav_err;
   }

   /*
    * Chunk information header.
    */
   /* Check chunk header. */
   if (sound_al_wavReadCmp( rw, "fmt ", 4 )) {
      WARN("Chunk header 'fmt ' header not found.");
      goto wav_err;
   }
   /* Get chunk length. */
   if (sound_al_wavGetLen32( rw, &chunklen )) {
      WARN("Unable to get WAVE chunk length.");
      goto wav_err;
   }
   i = 0;

   /* Get compression. */
   if (sound_al_wavGetLen16( rw, &compressed )) {
      WARN("Unable to get WAVE chunk compression type.");
      goto wav_err;
   }
   if (compressed != 0x0001) {
      WARN("Unsupported WAVE chunk compression '0x%04x'.", compressed);
      goto wav_err;
   }
   i += 2;

   /* Get channels. */
   if (sound_al_wavGetLen16( rw, &channels )) {
      WARN("Unable to get WAVE chunk channels.");
      goto wav_err;
   }
   i += 2;

   /* Get sample rate. */
   if (sound_al_wavGetLen32( rw, &rate )) {
      WARN("Unable to get WAVE chunk sample rate.");
      goto wav_err;
   }
   i += 4;

   /* Get average bytes. */
   if (sound_al_wavGetLen32( rw, &unused32 )) {
      WARN("Unable to get WAVE chunk average byte rate.");
      goto wav_err;
   }
   i += 4;

   /* Get block align. */
   if (sound_al_wavGetLen16( rw, &unused16 )) {
      WARN("Unable to get WAVE chunk block align.");
      goto wav_err;
   }
   i += 2;

   /* Get significant bits. */
   if (sound_al_wavGetLen16( rw, &align )) {
      WARN("Unable to get WAVE chunk significant bits.");
      goto wav_err;
   }
   align /= channels;
   i += 2;

   /* Seek to end. */
   SDL_RWseek( rw, chunklen-i, SEEK_CUR );


   /* Read new header. */
   len = SDL_RWread( rw, magic, 4, 1 );
   if (len != 1) {
      WARN("Unable to read chunk header.");
      goto wav_err;
   }

   /* Skip fact. */
   if (memcmp( magic, "fact", 4)==0) {
      /* Get chunk length. */
      if (sound_al_wavGetLen32( rw, &chunklen )) {
         WARN("Unable to get WAVE chunk data length.");
         goto wav_err;
      }

      /* Seek to end of chunk. */
      SDL_RWseek( rw, chunklen, SEEK_CUR );

      /* Read new header. */
      len = SDL_RWread( rw, magic, 4, 1 );
      if (len != 1) {
         WARN("Unable to read chunk header.");
         goto wav_err;
      }
   }

   /* Should be chunk header now. */
   if (memcmp( magic, "data", 4)) {
      WARN("Unable to find WAVE 'data' chunk header.");
      goto wav_err;
   }

   /*
    * Chunk data header.
    */
   /* Get chunk length. */
   if (sound_al_wavGetLen32( rw, &chunklen )) {
      WARN("Unable to get WAVE chunk data length.");
      goto wav_err;
   }

   /* Load the chunk data. */
   buf = malloc( chunklen );
   i = 0;
   while (i < chunklen) {
      i += SDL_RWread( rw, &buf[i], 1, chunklen-i );
   }

   /* Calculate format. */
   if (channels == 2) {
      if (align == 16)
         format = AL_FORMAT_STEREO16;
      else if (align == 8)
         format = AL_FORMAT_STEREO8;
      else {
         WARN("Unsupported byte alignment (%d) in WAVE file.", align);
         goto chunk_err;
      }
   }
   else if (channels == 1) {
      if (align == 16)
         format = AL_FORMAT_MONO16;
      else if (align == 8)
         format = AL_FORMAT_MONO8;
      else {
         WARN("Unsupported byte alignment (%d) in WAVE file.", align);
         goto chunk_err;
      }
   }
   else {
      WARN("Unsupported number of channels (%d) in WAVE file.", channels);
      goto chunk_err;
   }

   soundLock();
   /* Create new buffer. */
   alGenBuffers( 1, &snd->u.al.buf );
   /* Put into the buffer. */
   alBufferData( snd->u.al.buf, format, buf, chunklen, rate );
   soundUnlock();

   free(buf);
   return 0;

chunk_err:
   free(buf);
wav_err:
   return -1;
}