size_t sound_unqueue_buffers (ALuint source, SoundNameCallback callback, void *user_data) { ALint count; ALuint buf; size_t removed = 0; alGetSourcei (source, AL_BUFFERS_PROCESSED, &count); if (!check_al ("alGetSourcei (AL_BUFFERS_PROCESSED)")) return 0; while (count-- > 0) { alSourceUnqueueBuffers (source, 1, &buf); if (!check_al ("alSourceUnqueueBuffers")) return removed; if (callback != NULL) callback (buf, user_data); removed++; } return removed; }
void sound_delete_source_and_buffers (ALuint source) { alSourceStop (source); check_al ("alSourceStop"); sound_unqueue_buffers (source, (SoundNameCallback) delete_queued_buffer, NULL); alDeleteSources (1, &source); check_al ("alDeleteSources"); }
// Set wav volume // // <int> vol = wav volume, 0-100 // void OpenALAudio::set_wav_volume(int vol) { StreamMap::const_iterator itr; float gain; float gain_mult; int diff; if (!this->wav_init_flag) return; MSG("set_wav_volume(%i)\n", vol); vol = MAX(vol, 0); vol = MIN(vol, 100); vol = vol * 100 - 10000; diff = vol - this->wav_volume; gain_mult = millibels_to_ratio(diff); for (itr = this->streams.begin(); itr != this->streams.end(); ++itr) { alGetSourcef(itr->second->source, AL_GAIN, &gain); alSourcef(itr->second->source, AL_GAIN, gain * gain_mult); check_al(); } this->wav_volume = vol; }
static void unqueue_source_buffers (SourceDescriptor *src) { SoundSample *s; if (src->buffers_used == 0) return; s = src->sample; src->buffers_used -= sound_unqueue_buffers (src->name, (SoundNameCallback) push_free_buffer, NULL); if (src->buffers_used > 0 || s->pos[s->current_buffer] < s->len[s->current_buffer] || s->loop_count == 0) return; /* this check prevents the sound in the intro from abruptly stopping */ if (!s->done[0] && !s->done[1]) return; alSourceStop (src->name); check_al ("alSourceStop"); s->status = 2; }
static bool create_sources (void) { size_t n, m; ALuint source; ALuint buffers[SOUND_BUFFERS_PER_SRC]; for (n = 0; n < SOUND_MUSIC_BUFFERS; n++) { alGenBuffers (1, &buffers[0]); if (!check_al ("alGenBuffers")) goto err; push_free_buffer (buffers[0]); } for (n = 0; n < SOUND_MAX_SOURCES; n++) { alGenSources (1, &source); if (alGetError () != AL_NO_ERROR) { n--; break; } alGenBuffers (SOUND_BUFFERS_PER_SRC, buffers); if (alGetError () != AL_NO_ERROR) { sound_delete_source_and_buffers (source); n--; break; } initialise_descriptor (sound_source_count++, source); for (m = 0; m < SOUND_BUFFERS_PER_SRC; m++) push_free_buffer (buffers[m]); } if (sound_source_count == 0) { fprintf (stderr, "Error: OpenAL: " "Failed to create sound sources.\n"); goto err; } #ifdef ENABLE_DEBUG printf ("OpenAL: Created %zu sound sources.\n", sound_source_count); #endif return true; err: /* TODO: clean up */ return false; }
bool OpenALAudio::StreamContext::init(AudioStream *as) { if (this->source != 0) return false; alGenSources(1, &this->source); if (!check_al()) goto err; this->stream = as; return true; err: return false; }
static void destroy_sources (void) { size_t n; for (n = 0; n < SOUND_MAX_SOURCES; n++) { if (sound_sources[n].name == 0) continue; sound_delete_source_and_buffers (sound_sources[n].name); } alDeleteBuffers (sound_free_buffer_count, sound_free_buffers); check_al ("alDeleteBuffers"); }
/* Changes the volume/panning of a wave. Does not attenuate by wav_volume. */ void OpenALAudio::volume_long_wav(int id, const DsVolume &vol) { StreamContext *sc; StreamMap::const_iterator itr; if (!this->wav_init_flag) return; MSG("volume_long_wav(%i, (%li, %li))\n", id, vol.ds_vol, vol.ds_pan); itr = this->streams.find(id); if (itr == this->streams.end()) return; sc = itr->second; set_source_volume(sc->source, vol.ds_vol); set_source_panning(sc->source, vol.ds_pan); check_al(); }
/* * in - wav file stream to play. Claims ownership and will delete it. * vol - volume/panning * * return: 1 on success, 0 on failure */ int OpenALAudio::play_long_wav(InputStream *in, const DsVolume &vol) { const int BUFFER_COUNT = 4; StreamContext *sc = NULL; WavStream *ws = NULL; int id; assert(this->wav_init_flag); ws = new WavStream; if (!ws->open(in)) { delete in; goto err; } sc = new StreamContext; if (!sc->init(ws)) goto err; set_source_panning(sc->source, vol.ds_pan); set_source_volume(sc->source, vol.ds_vol + this->wav_volume); if (!check_al()) goto err; sc->stream_data(BUFFER_COUNT); id = unused_key(&this->streams); this->streams[id] = sc; return id; err: delete sc; delete ws; return 0; }
bool OpenALAudio::StreamContext::stream_data(int new_buffer_count) { const size_t BUFFER_SIZE = 0x4000; /* * This constant determines how many milliseconds of audio data go into * one buffer. The larger it is, the less probability of skipping, but * the longer the delay from calling stop() to the point when it * actually stops. */ const size_t MAX_BUFFER_TIME_MS = 50; uint8_t data[BUFFER_SIZE]; size_t frames_read; size_t max_frames; ALenum format; ALuint buf; ALint state; if (!this->streaming) return false; format = openal_format(this->stream); max_frames = this->stream->frame_rate() * MAX_BUFFER_TIME_MS / 1000; for (;;) { buf = 0; if (new_buffer_count > 0) { alGenBuffers(1, &buf); if (!check_al()) goto err; new_buffer_count--; } else { ALint processed; alGetSourcei(this->source, AL_BUFFERS_PROCESSED, &processed); if (processed == 0) break; alSourceUnqueueBuffers(this->source, 1, &buf); if (!check_al()) goto err; } size_t space_frames = sizeof(data) / this->stream->frame_size(); space_frames = MIN(space_frames, max_frames); frames_read = this->stream->read(data, space_frames); if (frames_read == 0) { if (this->looping) { this->stream->seek(this->loop_start_frame); frames_read = this->stream->read(data, space_frames); if (frames_read == 0) { this->streaming = false; break; } } else { this->streaming = false; break; } } if (this->fade_frames != 0) this->apply_fading(data, frames_read); alBufferData(buf, format, data, frames_read * this->stream->frame_size(), this->stream->frame_rate()); if (!check_al()) goto err; alSourceQueueBuffers(this->source, 1, &buf); if (!check_al()) goto err; } if (!this->streaming) { alDeleteBuffers(1, &buf); check_al(); } alGetSourcei(this->source, AL_SOURCE_STATE, &state); if (state != AL_PLAYING) { alSourcePlay(this->source); check_al(); } return this->streaming; err: if (buf != 0) alDeleteBuffers(1, &buf); this->streaming = false; return false; }
static void delete_queued_buffer (ALuint buf) { alDeleteBuffers (1, &buf); check_al ("alDeleteBuffers"); }
static void queue_source_buffers (SoundPCMDriver *pcmdrv, SourceDescriptor *src) { size_t len, total_len; void *data; float x_pos; ALint state; SoundSample *s; ALuint buf = 0; if (src->buffers_used >= SOUND_BUFFERS_PER_SRC) return; s = src->sample; total_len = s->len[s->current_buffer] - s->pos[s->current_buffer]; if (total_len == 0) { if (s->done[s->current_buffer ^ 1] == 0) s->current_buffer ^= 1; else return; total_len = s->len[s->current_buffer] - s->pos[s->current_buffer]; } while (total_len > 0 && src->buffers_used < SOUND_BUFFERS_PER_SRC) { data = s->start[s->current_buffer] + s->pos[s->current_buffer]; len = MIN (total_len, SOUND_MAX_BUFSIZE); assert ((s->flags & 1) != 0); buf = pop_free_buffer (); alBufferData (buf, get_pcm_format (s), data, len, s->playback_rate); if (!check_al ("alBufferData")) goto err; alSourceQueueBuffers (src->name, 1, &buf); if (!check_al ("alSourceQueueBuffers")) goto err; src->buffers_used++; alGetSourcei (src->name, AL_SOURCE_STATE, &state); if (!check_al ("alGetSourcei (AL_SOURCE_STATE)")) goto err; alSourcef (src->name, AL_GAIN, (s->volume * (1.f / 127.f) * (pcmdrv->master_volume * (1.f / 127.f)))); if (!check_al ("alSourcef (AL_GAIN)")) goto err; /* XXX: check if panning/position is OK */ if (s->pan == 0) x_pos = 0.f; else x_pos = (127 - s->pan - 64) * (1.f / 64.f); alSource3f (src->name, AL_POSITION, x_pos, 0.f, -.25f); if (!check_al ("alSource3f (AL_POSITION)")) goto err; if (state != AL_PLAYING) { alSourcePlay (src->name); if (!check_al ("alSourcePlay")) goto err; } s->pos[s->current_buffer] += len; total_len -= len; if (total_len == 0 && s->loop_count == 0) { s->pos[s->current_buffer] = 0; total_len = s->len[s->current_buffer] - s->pos[s->current_buffer]; } } return; err: if (buf != 0) push_free_buffer (buf); }