/** * @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; }
/** * @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; } } }
/** * @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; }
/** * @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 ); }
/** * @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; }
/** * @brief Resumes all sounds. */ void sound_al_resume (void) { soundLock(); al_resumev( source_ntotal, source_total ); /* Check for errors. */ al_checkErr(); soundUnlock(); }
/** * @brief Frees the source. */ void sound_al_free( alSound *snd ) { soundLock(); /* free the stuff */ alDeleteBuffers( 1, &snd->u.al.buf ); soundUnlock(); }
/** * @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 ); }
/** * @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(); }
/** * @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(); }
/** * @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(); }
/** * @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; }
/** * @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(); }
/** * @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; }
/** * @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; }
/** * @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; }
/** * @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; }
/** * @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; }
/** * @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, §ion ); } 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; }
/** * @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); }
/** * @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; }
/** * @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(); }
/** * @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; }
/** * @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(); }
/** * @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; }
/** * @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; }
/** * @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 */ §ion, /* 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; }
/** * @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; }
/** * @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; }
/** * @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; }