static void *pulseaudio_update(ALLEGRO_THREAD *self, void *data) { ALLEGRO_VOICE *voice = data; PULSEAUDIO_VOICE *pv = voice->extra; while (!al_get_thread_should_stop(self)) { if (pv->status == PV_PLAYING) { unsigned int frames = 4096; if (voice->is_streaming) { // streaming audio const void *data = _al_voice_update(voice, &frames); if (data) { pa_simple_write(pv->s, data, frames * pv->frame_size, NULL); } } else { // direct buffer audio al_lock_mutex(pv->buffer_mutex); const char *data = pv->buffer; unsigned int len = frames * pv->frame_size; pv->buffer += frames * pv->frame_size; if (pv->buffer > pv->buffer_end) { len = pv->buffer_end - data; pv->buffer = voice->attached_stream->spl_data.buffer.ptr; voice->attached_stream->pos = 0; if (voice->attached_stream->loop == ALLEGRO_PLAYMODE_ONCE) { pv->status = PV_STOPPING; } } else { voice->attached_stream->pos += frames; } al_unlock_mutex(pv->buffer_mutex); pa_simple_write(pv->s, data, len, NULL); } } else if (pv->status == PV_STOPPING) { pa_simple_flush(pv->s, NULL); pv->status = PV_STOPPED; } else if (pv->status == PV_STOPPED) { al_rest(0.001); } } return NULL; }
/* Custom routine which runs in another thread to periodically check if DirectSound wants more data for a stream */ static void* _dsound_update(ALLEGRO_THREAD *self, void *arg) { ALLEGRO_VOICE *voice = (ALLEGRO_VOICE *)arg; ALLEGRO_DS_DATA *ex_data = (ALLEGRO_DS_DATA*)voice->extra; const int bytes_per_sample = ex_data->bits_per_sample / 8; DWORD play_cursor = 0; DWORD write_cursor; DWORD saved_play_cursor = 0; unsigned int samples; LPVOID ptr1, ptr2; DWORD block1_bytes, block2_bytes; unsigned char *data; HRESULT hr; (void)self; unsigned char *silence = (unsigned char *)al_malloc(buffer_size); int silence_value = _al_kcm_get_silence(voice->depth); memset(silence, silence_value, buffer_size); /* Fill buffer */ hr = ex_data->ds8_buffer->Lock(0, buffer_size, &ptr1, &block1_bytes, &ptr2, &block2_bytes, DSBLOCK_ENTIREBUFFER); if (!FAILED(hr)) { samples = buffer_size / bytes_per_sample / ex_data->channels; data = (unsigned char *) _al_voice_update(voice, &samples); memcpy(ptr1, data, block1_bytes); memcpy(ptr2, data + block1_bytes, block2_bytes); ex_data->ds8_buffer->Unlock(ptr1, block1_bytes, ptr2, block2_bytes); } ex_data->ds8_buffer->Play(0, 0, DSBPLAY_LOOPING); do { if (!_dsound_voice_is_playing(voice)) { ex_data->ds8_buffer->Play(0, 0, DSBPLAY_LOOPING); } ex_data->ds8_buffer->GetCurrentPosition(&play_cursor, &write_cursor); /* We try to fill the gap between the saved_play_cursor and the * play_cursor. */ int d = play_cursor - saved_play_cursor; if (d < 0) d += buffer_size; /* Don't fill small gaps. Let it accumulate to amortise the cost of * mixing the samples and locking/unlocking the buffer. */ if (d < MIN_FILL) { al_rest(0.005); continue; } /* Don't generate too many samples at once. The buffer may underrun * while we wait for _al_voice_update to complete. */ samples = d / bytes_per_sample / ex_data->channels; if (samples > MAX_FILL) { samples = MAX_FILL; } /* Generate the samples. */ data = (unsigned char *) _al_voice_update(voice, &samples); if (data == NULL) { data = silence; } hr = ex_data->ds8_buffer->Lock(saved_play_cursor, samples * bytes_per_sample * ex_data->channels, &ptr1, &block1_bytes, &ptr2, &block2_bytes, 0); if (!FAILED(hr)) { memcpy(ptr1, data, block1_bytes); memcpy(ptr2, data + block1_bytes, block2_bytes); hr = ex_data->ds8_buffer->Unlock(ptr1, block1_bytes, ptr2, block2_bytes); if (FAILED(hr)) { ALLEGRO_ERROR("Unlock failed: %s\n", ds_get_error(hr)); } } saved_play_cursor += block1_bytes + block2_bytes; saved_play_cursor %= buffer_size; } while (!ex_data->stop_voice); ex_data->ds8_buffer->Stop(); al_free(silence); ex_data->stop_voice = 0; al_broadcast_cond(voice->cond); return NULL; }
static void* oss_update(ALLEGRO_THREAD *self, void *arg) { ALLEGRO_VOICE *voice = arg; OSS_VOICE *oss_voice = voice->extra; (void)self; while (!oss_voice->quit_poll_thread) { /* For possible eventual non-blocking mode: audio_buf_info bi; if (ioctl(oss_voice->fd, SNDCTL_DSP_GETOSPACE, &bi) == -1) { TRACE(PREFIX_E "Error SNDCTL_DSP_GETOSPACE.\n"); TRACE(PREFIX_E "errno: %i -- %s\n", errno, strerror(errno)); return NULL; } len = bi.bytes; */ /* How many bytes are we supposed to try to write at once? */ unsigned int frames = 1024; if (oss_voice->stop && !oss_voice->stopped) { oss_voice->stopped = true; } if (!oss_voice->stop && oss_voice->stopped) { oss_voice->stopped = false; } if (!voice->is_streaming && !oss_voice->stopped) { void *buf; int bytes = frames * oss_voice->frame_size; oss_update_nonstream_voice(voice, &buf, &bytes); frames = bytes / oss_voice->frame_size; if (write(oss_voice->fd, buf, bytes) == -1) { ALLEGRO_ERROR("errno: %i -- %s\n", errno, strerror(errno)); if (errno != EINTR) return NULL; } } else if (voice->is_streaming && !oss_voice->stopped) { const void *data = _al_voice_update(voice, &frames); if (data == NULL) goto silence; if (write(oss_voice->fd, data, frames * oss_voice->frame_size) == -1) { ALLEGRO_ERROR("errno: %i -- %s\n", errno, strerror(errno)); if (errno != EINTR) return NULL; } } else { silence: /* If stopped just fill with silence. */ memset(sil_buf, _al_kcm_get_silence(voice->depth), SIL_BUF_SIZE); if(write(oss_voice->fd, sil_buf, SIL_BUF_SIZE) == -1) { ALLEGRO_ERROR("errno: %i -- %s\n", errno, strerror(errno)); } } } return NULL; }
/* TODO: review */ static void *_openal_update(ALLEGRO_THREAD *self, void *arg) { ALLEGRO_VOICE *voice = (ALLEGRO_VOICE*) arg; ALLEGRO_AL_DATA *ex_data = (ALLEGRO_AL_DATA*)voice->extra; unsigned int i, samples_per_update; unsigned int bytes_per_sample; const void *data; void *silence; /* Streams should not be set to looping */ alSourcei(ex_data->source, AL_LOOPING, AL_FALSE); silence = al_calloc(1, ex_data->buffer_size); if (ex_data->format == AL_FORMAT_STEREO8 || ex_data->format == AL_FORMAT_MONO8) { memset(silence, 0x80, ex_data->buffer_size); } for (i = 0; i < ex_data->num_buffers; i++) { alBufferData(ex_data->buffers[i], ex_data->format, silence, ex_data->buffer_size, voice->frequency); } alSourceQueueBuffers(ex_data->source, ex_data->num_buffers, ex_data->buffers); alSourcePlay(ex_data->source); switch (ex_data->format) { case AL_FORMAT_STEREO16: bytes_per_sample = 4; break; case AL_FORMAT_STEREO8: case AL_FORMAT_MONO16: bytes_per_sample = 2; break; default: bytes_per_sample = 1; break; } samples_per_update = ex_data->buffer_size / bytes_per_sample; data = silence; while (!al_get_thread_should_stop(self)) { ALint status = 0; alGetSourcei(ex_data->source, AL_BUFFERS_PROCESSED, &status); if (status <= 0) { /* FIXME what is this for ? */ al_rest(0.001); continue; } while (--status >= 0) { ALuint buffer; data = _al_voice_update(voice, voice->mutex, &samples_per_update); if (data == NULL) data = silence; alSourceUnqueueBuffers(ex_data->source, 1, &buffer); alBufferData(buffer, ex_data->format, data, samples_per_update * bytes_per_sample, voice->frequency); alSourceQueueBuffers(ex_data->source, 1, &buffer); } alGetSourcei(ex_data->source, AL_SOURCE_STATE, &status); if (status == AL_STOPPED) { alSourcePlay(ex_data->source); } } alSourceStop(ex_data->source); al_free(silence); ex_data->stopped = true; al_broadcast_cond(voice->cond); return NULL; }
static void *pulseaudio_update(ALLEGRO_THREAD *self, void *data) { ALLEGRO_VOICE *voice = data; PULSEAUDIO_VOICE *pv = voice->extra; (void)self; for (;;) { enum PULSEAUDIO_VOICE_STATUS status; al_lock_mutex(voice->mutex); while ((status = pv->status) == PV_IDLE) { al_wait_cond(pv->status_cond, voice->mutex); } al_unlock_mutex(voice->mutex); if (status == PV_JOIN) { break; } if (status == PV_PLAYING) { unsigned int frames = pv->buffer_size_in_frames; if (voice->is_streaming) { // streaming audio const void *data = _al_voice_update(voice, voice->mutex, &frames); if (data) { pa_simple_write(pv->s, data, frames * pv->frame_size_in_bytes, NULL); } } else { // direct buffer audio al_lock_mutex(pv->buffer_mutex); const char *data = pv->buffer; unsigned int len = frames * pv->frame_size_in_bytes; pv->buffer += frames * pv->frame_size_in_bytes; if (pv->buffer > pv->buffer_end) { len = pv->buffer_end - data; pv->buffer = voice->attached_stream->spl_data.buffer.ptr; voice->attached_stream->pos = 0; if (voice->attached_stream->loop == ALLEGRO_PLAYMODE_ONCE) { al_lock_mutex(voice->mutex); pv->status = PV_STOPPING; al_broadcast_cond(pv->status_cond); al_unlock_mutex(voice->mutex); } } else { voice->attached_stream->pos += frames; } al_unlock_mutex(pv->buffer_mutex); pa_simple_write(pv->s, data, len, NULL); } } else if (status == PV_STOPPING) { pa_simple_drain(pv->s, NULL); al_lock_mutex(voice->mutex); pv->status = PV_IDLE; al_broadcast_cond(pv->status_cond); al_unlock_mutex(voice->mutex); } } return NULL; }