static int pulseaudio_allocate_voice(ALLEGRO_VOICE *voice) { PULSEAUDIO_VOICE *pv = al_malloc(sizeof(PULSEAUDIO_VOICE)); pa_sample_spec ss; pa_buffer_attr ba; ss.channels = al_get_channel_count(voice->chan_conf); ss.rate = voice->frequency; if (voice->depth == ALLEGRO_AUDIO_DEPTH_UINT8) ss.format = PA_SAMPLE_U8; else if (voice->depth == ALLEGRO_AUDIO_DEPTH_INT16) ss.format = PA_SAMPLE_S16NE; #if PA_API_VERSION > 11 else if (voice->depth == ALLEGRO_AUDIO_DEPTH_INT24) ss.format = PA_SAMPLE_S24NE; #endif else if (voice->depth == ALLEGRO_AUDIO_DEPTH_FLOAT32) ss.format = PA_SAMPLE_FLOAT32NE; else { ALLEGRO_ERROR("Unsupported PulseAudio sound format.\n"); al_free(pv); return 1; } ba.maxlength = 0x10000; // maximum length of buffer ba.tlength = 0x2000; // target length of buffer ba.prebuf = 0; // minimum data size required before playback starts ba.minreq = 0; // minimum size of request ba.fragsize = -1; // fragment size (recording) pv->s = pa_simple_new(NULL, // Use the default server. al_get_app_name(), PA_STREAM_PLAYBACK, NULL, // Use the default device. "Allegro Voice", &ss, NULL, // Use default channel map &ba, NULL // Ignore error code. ); if (!pv->s) { al_free(pv); return 1; } voice->extra = pv; pv->frame_size = ss.channels * al_get_audio_depth_size(voice->depth); pv->status = PV_STOPPED; pv->buffer_mutex = al_create_mutex(); pv->poll_thread = al_create_thread(pulseaudio_update, (void*)voice); al_start_thread(pv->poll_thread); return 0; }
static int pulseaudio_allocate_recorder(ALLEGRO_AUDIO_RECORDER *r) { PULSEAUDIO_RECORDER *pa; pa = al_calloc(1, sizeof(*pa)); if (!pa) { ALLEGRO_ERROR("Unable to allocate memory for PULSEAUDIO_RECORDER.\n"); return 1; } pa->ss.channels = al_get_channel_count(r->chan_conf); pa->ss.rate = r->frequency; if (r->depth == ALLEGRO_AUDIO_DEPTH_UINT8) pa->ss.format = PA_SAMPLE_U8; else if (r->depth == ALLEGRO_AUDIO_DEPTH_INT16) pa->ss.format = PA_SAMPLE_S16NE; #if PA_API_VERSION > 11 else if (r->depth == ALLEGRO_AUDIO_DEPTH_INT24) pa->ss.format = PA_SAMPLE_S24NE; #endif else if (r->depth == ALLEGRO_AUDIO_DEPTH_FLOAT32) pa->ss.format = PA_SAMPLE_FLOAT32NE; else { ALLEGRO_ERROR("Unsupported PulseAudio sound format (depth).\n"); al_free(pa); return 1; } /* maximum length of the PulseAudio buffer. -1 => let the server decide. */ pa->ba.maxlength = -1; /* fragment size (bytes) controls how much data is returned back per read. The documentation recommends -1 for default behavior, but that sets a latency of around 2 seconds. Lower value decreases latency but increases overhead. The following attempts to set it (the base latency) to 1/8 of a second. */ pa->ba.fragsize = (r->sample_size * r->frequency) / 8; pa->s = pa_simple_new(NULL, al_get_app_name(), PA_STREAM_RECORD, NULL, "Allegro Audio Recorder", &pa->ss, NULL, &pa->ba, NULL); if (!pa->s) { ALLEGRO_ERROR("pa_simple_new() failed.\n"); al_free(pa); return 1; } r->thread = al_create_thread(pulse_audio_update_recorder, r); r->extra = pa; return 0; };
/* _al_kcm_refill_stream: * Called by the mixer when the current buffer has been used up. It should * point to the next pending buffer and reset the sample position. * Returns true if the next buffer is available and set up. * Otherwise returns false. */ bool _al_kcm_refill_stream(ALLEGRO_AUDIO_STREAM *stream) { ALLEGRO_SAMPLE_INSTANCE *spl = &stream->spl; void *old_buf = spl->spl_data.buffer.ptr; void *new_buf; size_t i; if (old_buf) { /* Slide the buffers down one position and put the * completed buffer into the used array to be refilled. */ for (i = 0; i < stream->buf_count-1 && stream->pending_bufs[i]; i++) { stream->pending_bufs[i] = stream->pending_bufs[i+1]; } stream->pending_bufs[i] = NULL; for (i = 0; stream->used_bufs[i]; i++) ; stream->used_bufs[i] = old_buf; } new_buf = stream->pending_bufs[0]; stream->spl.spl_data.buffer.ptr = new_buf; if (!new_buf) { ALLEGRO_WARN("Out of buffers\n"); return false; } /* Copy the last MAX_LAG sample values to the front of the new buffer * for interpolation. */ if (old_buf) { const int bytes_per_sample = al_get_channel_count(spl->spl_data.chan_conf) * al_get_audio_depth_size(spl->spl_data.depth); memcpy( (char *) new_buf - bytes_per_sample * MAX_LAG, (char *) old_buf + bytes_per_sample * (spl->pos-MAX_LAG), bytes_per_sample * MAX_LAG); stream->consumed_fragments++; } stream->spl.pos = 0; return true; }
/* stream_read: * This passes the next waiting stream buffer to the voice via vbuf. */ static void stream_read(void *source, void **vbuf, unsigned int *samples, ALLEGRO_AUDIO_DEPTH buffer_depth, size_t dest_maxc) { ALLEGRO_AUDIO_STREAM *stream = (ALLEGRO_AUDIO_STREAM*)source; unsigned int len = stream->spl.spl_data.len; unsigned int pos = stream->spl.pos; if (!stream->spl.is_playing) { *vbuf = NULL; *samples = 0; return; } if (*samples > len) *samples = len; if (pos >= len) { _al_kcm_refill_stream(stream); if (!stream->pending_bufs[0]) { if (stream->is_draining) { stream->spl.is_playing = false; } *vbuf = NULL; *samples = 0; return; } *vbuf = stream->pending_bufs[0]; pos = *samples; _al_kcm_emit_stream_events(stream); } else { int bytes = pos * al_get_channel_count(stream->spl.spl_data.chan_conf) * al_get_audio_depth_size(stream->spl.spl_data.depth); *vbuf = ((char *)stream->pending_bufs[0]) + bytes; if (pos + *samples > len) *samples = len - pos; pos += *samples; } stream->spl.pos = pos; (void)dest_maxc; (void)buffer_depth; }
static void reset_stopped_stream(ALLEGRO_AUDIO_STREAM *stream) { const int bytes_per_sample = al_get_channel_count(stream->spl.spl_data.chan_conf) * al_get_audio_depth_size(stream->spl.spl_data.depth); const int fragment_buffer_size = bytes_per_sample * (stream->spl.spl_data.len + MAX_LAG); size_t i, n; /* Write silence to the "invisible" part in between fragment buffers to * avoid interpolation artifacts. It's tempting to zero the complete * memory block in one go but some of the buffers might be getting * refilled. So they are currently "owned" by the library user and * should not be overwritten. But zeroing the parts not visible to the * user should be OK. */ for (i = 0; i < stream->buf_count; ++i) { al_fill_silence((char *)stream->main_buffer + i * fragment_buffer_size, MAX_LAG, stream->spl.spl_data.depth, stream->spl.spl_data.chan_conf); } /* Get the current number of entries in the used_buf list. */ for (n = 0; n < stream->buf_count && stream->used_bufs[n]; n++) ; /* Move everything from pending_bufs to used_bufs. */ i = 0; while (i < stream->buf_count && n < stream->buf_count && stream->pending_bufs[i]) { stream->used_bufs[n] = stream->pending_bufs[i]; stream->pending_bufs[i] = NULL; n++; i++; } /* No fragment buffer is currently playing. */ stream->spl.spl_data.buffer.ptr = NULL; stream->spl.pos = stream->spl.spl_data.len; stream->spl.pos_bresenham_error = 0; stream->consumed_fragments = 0; }
/* Function: al_attach_audio_stream_to_voice */ bool al_attach_audio_stream_to_voice(ALLEGRO_AUDIO_STREAM *stream, ALLEGRO_VOICE *voice) { bool ret; ASSERT(voice); ASSERT(stream); if (voice->attached_stream) { _al_set_error(ALLEGRO_INVALID_OBJECT, "Attempted to attach to a voice that already has an attachment"); return false; } if (stream->spl.parent.u.ptr) { _al_set_error(ALLEGRO_INVALID_OBJECT, "Attempted to attach a stream that is already attached"); return false; } if (voice->chan_conf != stream->spl.spl_data.chan_conf || voice->frequency != stream->spl.spl_data.frequency || voice->depth != stream->spl.spl_data.depth) { _al_set_error(ALLEGRO_INVALID_OBJECT, "Stream settings do not match voice settings"); return false; } al_lock_mutex(voice->mutex); voice->attached_stream = &stream->spl; _al_kcm_stream_set_mutex(&stream->spl, voice->mutex); stream->spl.parent.u.voice = voice; stream->spl.parent.is_voice = true; voice->is_streaming = true; voice->num_buffers = stream->buf_count; voice->buffer_size = (stream->spl.spl_data.len) * al_get_channel_count(stream->spl.spl_data.chan_conf) * al_get_audio_depth_size(stream->spl.spl_data.depth); ASSERT(stream->spl.spl_read == NULL); stream->spl.spl_read = stream_read; if (voice->driver->start_voice(voice) != 0) { voice->attached_stream = NULL; _al_kcm_stream_set_mutex(&stream->spl, NULL); stream->spl.parent.u.voice = NULL; stream->spl.spl_read = NULL; _al_set_error(ALLEGRO_GENERIC_ERROR, "Unable to start stream"); ret = false; } else { ret = true; } al_unlock_mutex(voice->mutex); return ret; }
/* Function: al_attach_sample_instance_to_voice */ bool al_attach_sample_instance_to_voice(ALLEGRO_SAMPLE_INSTANCE *spl, ALLEGRO_VOICE *voice) { bool ret; ASSERT(voice); ASSERT(spl); if (voice->attached_stream) { ALLEGRO_WARN( "Attempted to attach to a voice that already has an attachment\n"); _al_set_error(ALLEGRO_INVALID_OBJECT, "Attempted to attach to a voice that already has an attachment"); return false; } if (spl->parent.u.ptr) { ALLEGRO_WARN("Attempted to attach a sample that is already attached\n"); _al_set_error(ALLEGRO_INVALID_OBJECT, "Attempted to attach a sample that is already attached"); return false; } if (voice->chan_conf != spl->spl_data.chan_conf || voice->frequency != spl->spl_data.frequency || voice->depth != spl->spl_data.depth) { ALLEGRO_WARN("Sample settings do not match voice settings\n"); _al_set_error(ALLEGRO_INVALID_OBJECT, "Sample settings do not match voice settings"); return false; } al_lock_mutex(voice->mutex); voice->attached_stream = spl; voice->is_streaming = false; voice->num_buffers = 1; voice->buffer_size = (spl->spl_data.len) * al_get_channel_count(voice->chan_conf) * al_get_audio_depth_size(voice->depth); spl->spl_read = NULL; _al_kcm_stream_set_mutex(spl, voice->mutex); spl->parent.u.voice = voice; spl->parent.is_voice = true; if (voice->driver->load_voice(voice, spl->spl_data.buffer.ptr) != 0 || (spl->is_playing && voice->driver->start_voice(voice) != 0)) { voice->attached_stream = NULL; spl->spl_read = NULL; _al_kcm_stream_set_mutex(spl, NULL); spl->parent.u.voice = NULL; ALLEGRO_ERROR("Unable to load sample into voice\n"); ret = false; } else { ret = true; } al_unlock_mutex(voice->mutex); return ret; }
static int oss_allocate_voice(ALLEGRO_VOICE *voice) { int format; int chan_count; OSS_VOICE *ex_data = calloc(1, sizeof(OSS_VOICE)); if (!ex_data) return 1; ex_data->fd = open(oss_audio_device, O_WRONLY/*, O_NONBLOCK*/); if (ex_data->fd == -1) { ALLEGRO_ERROR("Failed to open audio device '%s'.\n", oss_audio_device); ALLEGRO_ERROR("errno: %i -- %s\n", errno, strerror(errno)); free(ex_data); return 1; } chan_count = al_get_channel_count(voice->chan_conf); ex_data->frame_size = chan_count * al_get_depth_size(voice->depth); if (!ex_data->frame_size) goto Error; ex_data->stop = true; ex_data->stopped = true; if (voice->depth == ALLEGRO_AUDIO_DEPTH_INT8) format = AFMT_S8; else if (voice->depth == ALLEGRO_AUDIO_DEPTH_UINT8) format = AFMT_U8; else if (voice->depth == ALLEGRO_AUDIO_DEPTH_INT16) format = AFMT_S16_NE; else if (voice->depth == ALLEGRO_AUDIO_DEPTH_UINT16) format = AFMT_U16_NE; #ifdef OSS_VER_4 else if (voice->depth == ALLEGRO_AUDIO_DEPTH_INT24) format = AFMT_S24_NE; else if (voice->depth == ALLEGRO_AUDIO_DEPTH_FLOAT32) format = AFMT_FLOAT; #endif else { ALLEGRO_ERROR("Unsupported OSS sound format.\n"); goto Error; } int tmp_format = format; int tmp_chan_count = chan_count; unsigned int tmp_freq = voice->frequency; int tmp_oss_fragsize = oss_fragsize; if (using_ver_4) { #ifdef OSS_VER_4 int tmp_oss_timing_policy = oss_timing_policy; if (ioctl(ex_data->fd, SNDCTL_DSP_POLICY, &tmp_oss_timing_policy) == -1) { ALLEGRO_ERROR("Failed to set_timig policity to '%i'.\n", tmp_oss_timing_policy); ALLEGRO_ERROR("errno: %i -- %s\n", errno, strerror(errno)); goto Error; } ALLEGRO_INFO("Accepted timing policy value: %i\n", tmp_oss_timing_policy); #endif } else { if (ioctl(ex_data->fd, SNDCTL_DSP_SETFRAGMENT, &tmp_oss_fragsize) == -1) { ALLEGRO_ERROR("Failed to set fragment size.\n"); ALLEGRO_ERROR("errno: %i -- %s\n", errno, strerror(errno)); goto Error; } } if (ioctl(ex_data->fd, SNDCTL_DSP_SETFMT, &tmp_format) == -1) { ALLEGRO_ERROR("Failed to set sample format.\n"); ALLEGRO_ERROR("errno: %i -- %s\n", errno, strerror(errno)); goto Error; } if (tmp_format != format) { ALLEGRO_ERROR("Sample format not supported by the driver.\n"); goto Error; } if (ioctl(ex_data->fd, SNDCTL_DSP_CHANNELS, &tmp_chan_count)) { ALLEGRO_ERROR("Failed to set channel count.\n"); ALLEGRO_ERROR("errno: %i -- %s\n", errno, strerror(errno)); goto Error; } if (tmp_chan_count != chan_count) { ALLEGRO_ERROR("Requested sample channe count %i, got %i.\n", tmp_chan_count, chan_count); } if (ioctl(ex_data->fd, SNDCTL_DSP_SPEED, &tmp_freq) == -1) { ALLEGRO_ERROR("Failed to set sample rate.\n"); ALLEGRO_ERROR("errno: %i -- %s\n", errno, strerror(errno)); goto Error; } if (voice->frequency != tmp_freq) { ALLEGRO_ERROR("Requested sample rate %u, got %iu.\n", voice->frequency, tmp_freq); } voice->extra = ex_data; ex_data->quit_poll_thread = false; ex_data->poll_thread = al_create_thread(oss_update, (void*)voice); al_start_thread(ex_data->poll_thread); return 0; Error: close(ex_data->fd); free(ex_data); return 1; }
int main(int argc, char **argv) { ALLEGRO_VOICE *voice; ALLEGRO_MIXER *mixer; ALLEGRO_SAMPLE_INSTANCE *sample; int i; char const **filenames; int n; if (argc < 2) { n = 1; filenames = malloc(sizeof *filenames); filenames[0] = "data/welcome.wav"; } else { n = argc - 1; filenames = malloc(sizeof *filenames * n); for (i = 1; i < argc; ++i) { filenames[i - 1] = argv[i]; } } if (!al_init()) { abort_example("Could not init Allegro.\n"); } open_log(); al_init_acodec_addon(); if (!al_install_audio()) { abort_example("Could not init sound!\n"); } voice = al_create_voice(44100, ALLEGRO_AUDIO_DEPTH_INT16, ALLEGRO_CHANNEL_CONF_2); if (!voice) { abort_example("Could not create ALLEGRO_VOICE.\n"); } mixer = al_create_mixer(44100, ALLEGRO_AUDIO_DEPTH_FLOAT32, ALLEGRO_CHANNEL_CONF_2); if (!mixer) { abort_example("al_create_mixer failed.\n"); } if (!al_attach_mixer_to_voice(mixer, voice)) { abort_example("al_attach_mixer_to_voice failed.\n"); } sample = al_create_sample_instance(NULL); if (!sample) { abort_example("al_create_sample failed.\n"); } for (i = 0; i < n; ++i) { ALLEGRO_SAMPLE *sample_data = NULL; const char *filename = filenames[i]; float sample_time = 0; /* A matrix that puts everything in the left channel. */ float mono_to_stereo[] = {1.0, 0.0}; /* Load the entire sound file from disk. */ sample_data = al_load_sample(filename); if (!sample_data) { abort_example("Could not load sample from '%s'!\n", filename); continue; } if (!al_set_sample(sample, sample_data)) { abort_example("al_set_sample_instance_ptr failed.\n"); continue; } if (!al_attach_sample_instance_to_mixer(sample, mixer)) { abort_example("al_attach_sample_instance_to_mixer failed.\n"); goto done; } /* Play sample in looping mode. */ al_set_sample_instance_playmode(sample, ALLEGRO_PLAYMODE_LOOP); al_play_sample_instance(sample); sample_time = al_get_sample_instance_time(sample); log_printf("Playing '%s' (%.3f seconds) normally.\n", filename, sample_time); al_rest(sample_time); if (al_get_channel_count(al_get_sample_instance_channels(sample)) == 1) { if (!al_set_sample_instance_channel_matrix(sample, mono_to_stereo)) { abort_example("Failed to set channel matrix.\n"); } log_printf("Playing left channel only.\n"); al_rest(sample_time); } if (!al_set_sample_instance_gain(sample, 0.5)) { abort_example("Failed to set gain.\n"); } log_printf("Playing with gain 0.5.\n"); al_rest(sample_time); if (!al_set_sample_instance_gain(sample, 0.25)) { abort_example("Failed to set gain.\n"); } log_printf("Playing with gain 0.25.\n"); al_rest(sample_time); al_stop_sample_instance(sample); log_printf("Done playing '%s'\n", filename); /* Free the memory allocated. */ al_set_sample(sample, NULL); al_destroy_sample(sample_data); } al_destroy_sample_instance(sample); al_destroy_mixer(mixer); al_destroy_voice(voice); al_uninstall_audio(); done: close_log(true); return 0; }
static int _dsound_open_recorder(ALLEGRO_AUDIO_RECORDER *r) { HRESULT hr; if (capture_device != NULL) { /* FIXME: It's wrong to assume only a single recording device, but since there is no enumeration of devices, it doesn't matter for now. */ ALLEGRO_ERROR("Already recording.\n"); return 1; } ALLEGRO_INFO("Creating default capture device.\n"); /* FIXME: Use default device until we have device enumeration */ hr = DirectSoundCaptureCreate8(NULL, &capture_device, NULL); if (FAILED(hr)) { ALLEGRO_ERROR("DirectSoundCaptureCreate8 failed: %s\n", ds_get_error(hr)); return 1; } hr = device->SetCooperativeLevel(get_window(), DSSCL_PRIORITY); if (FAILED(hr)) { ALLEGRO_ERROR("SetCooperativeLevel failed: %s\n", ds_get_error(hr)); return 1; } DSOUND_RECORD_DATA *extra = (DSOUND_RECORD_DATA *) al_calloc(1, sizeof(*extra)); DSCCAPS dsccaps; dsccaps.dwSize = sizeof(DSCCAPS); hr = capture_device->GetCaps(&dsccaps); if (FAILED(hr)) { ALLEGRO_ERROR("DirectSoundCaptureCreate8::GetCaps failed: %s\n", ds_get_error(hr)); } else { ALLEGRO_INFO("caps: %lu %lu\n", dsccaps.dwFormats, dsccaps.dwFormats & WAVE_FORMAT_2M16); } memset(&extra->wave_fmt, 0, sizeof(extra->wave_fmt)); extra->wave_fmt.wFormatTag = WAVE_FORMAT_PCM; extra->wave_fmt.nChannels = (WORD)al_get_channel_count(r->chan_conf); extra->wave_fmt.nSamplesPerSec = r->frequency; extra->wave_fmt.wBitsPerSample = (WORD)al_get_audio_depth_size(r->depth) * 8; extra->wave_fmt.nBlockAlign = extra->wave_fmt.nChannels * extra->wave_fmt.wBitsPerSample / 8; extra->wave_fmt.nAvgBytesPerSec = extra->wave_fmt.nSamplesPerSec * extra->wave_fmt.nBlockAlign; memset(&extra->desc, 0, sizeof(extra->desc)); extra->desc.dwSize = sizeof(extra->desc); extra->desc.lpwfxFormat = &extra->wave_fmt; extra->desc.dwBufferBytes = extra->wave_fmt.nAvgBytesPerSec * 5; hr = capture_device->CreateCaptureBuffer(&extra->desc, &extra->buffer, NULL); if (FAILED(hr)) { al_free(extra); ALLEGRO_ERROR("Unable to create Capture Buffer\n"); return 1; } extra->buffer->QueryInterface(_al_IID_IDirectSoundCaptureBuffer8, (void **) &extra->buffer8); r->extra = extra; r->thread = al_create_thread(_dsound_update_recorder, r); return 0; }
/* _al_kcm_feed_stream: * A routine running in another thread that feeds the stream buffers as * neccesary, usually getting data from some file reader backend. */ void *_al_kcm_feed_stream(ALLEGRO_THREAD *self, void *vstream) { ALLEGRO_AUDIO_STREAM *stream = vstream; ALLEGRO_EVENT_QUEUE *queue; ALLEGRO_EVENT event; (void)self; ALLEGRO_DEBUG("Stream feeder thread started.\n"); queue = al_create_event_queue(); al_register_event_source(queue, &stream->spl.es); al_lock_mutex(stream->feed_thread_started_mutex); stream->feed_thread_started = true; al_broadcast_cond(stream->feed_thread_started_cond); al_unlock_mutex(stream->feed_thread_started_mutex); stream->quit_feed_thread = false; while (!stream->quit_feed_thread) { char *fragment; ALLEGRO_EVENT event; al_wait_for_event(queue, &event); if (event.type == ALLEGRO_EVENT_AUDIO_STREAM_FRAGMENT && !stream->is_draining) { unsigned long bytes; unsigned long bytes_written; ALLEGRO_MUTEX *stream_mutex; fragment = al_get_audio_stream_fragment(stream); if (!fragment) { /* This is not an error. */ continue; } bytes = (stream->spl.spl_data.len) * al_get_channel_count(stream->spl.spl_data.chan_conf) * al_get_audio_depth_size(stream->spl.spl_data.depth); stream_mutex = maybe_lock_mutex(stream->spl.mutex); bytes_written = stream->feeder(stream, fragment, bytes); maybe_unlock_mutex(stream_mutex); if (stream->spl.loop == _ALLEGRO_PLAYMODE_STREAM_ONEDIR) { /* Keep rewinding until the fragment is filled. */ while (bytes_written < bytes && stream->spl.loop == _ALLEGRO_PLAYMODE_STREAM_ONEDIR) { size_t bw; al_rewind_audio_stream(stream); stream_mutex = maybe_lock_mutex(stream->spl.mutex); bw = stream->feeder(stream, fragment + bytes_written, bytes - bytes_written); bytes_written += bw; maybe_unlock_mutex(stream_mutex); } } else if (bytes_written < bytes) { /* Fill the rest of the fragment with silence. */ int silence_samples = (bytes - bytes_written) / (al_get_channel_count(stream->spl.spl_data.chan_conf) * al_get_audio_depth_size(stream->spl.spl_data.depth)); al_fill_silence(fragment + bytes_written, silence_samples, stream->spl.spl_data.depth, stream->spl.spl_data.chan_conf); } if (!al_set_audio_stream_fragment(stream, fragment)) { ALLEGRO_ERROR("Error setting stream buffer.\n"); continue; } /* The streaming source doesn't feed any more, drain buffers and quit. */ if (bytes_written != bytes && stream->spl.loop == _ALLEGRO_PLAYMODE_STREAM_ONCE) { al_drain_audio_stream(stream); stream->quit_feed_thread = true; } } else if (event.type == _KCM_STREAM_FEEDER_QUIT_EVENT_TYPE) { stream->quit_feed_thread = true; } } event.user.type = ALLEGRO_EVENT_AUDIO_STREAM_FINISHED; event.user.timestamp = al_get_time(); al_emit_user_event(&stream->spl.es, &event, NULL); al_destroy_event_queue(queue); ALLEGRO_DEBUG("Stream feeder thread finished.\n"); return NULL; }
/* Function: al_create_audio_stream */ ALLEGRO_AUDIO_STREAM *al_create_audio_stream(size_t fragment_count, unsigned int frag_samples, unsigned int freq, ALLEGRO_AUDIO_DEPTH depth, ALLEGRO_CHANNEL_CONF chan_conf) { ALLEGRO_AUDIO_STREAM *stream; unsigned long bytes_per_sample; unsigned long bytes_per_frag_buf; size_t i; if (!fragment_count) { _al_set_error(ALLEGRO_INVALID_PARAM, "Attempted to create stream with no buffers"); return NULL; } if (!frag_samples) { _al_set_error(ALLEGRO_INVALID_PARAM, "Attempted to create stream with no buffer size"); return NULL; } if (!freq) { _al_set_error(ALLEGRO_INVALID_PARAM, "Attempted to create stream with no frequency"); return NULL; } bytes_per_sample = al_get_channel_count(chan_conf) * al_get_audio_depth_size(depth); bytes_per_frag_buf = frag_samples * bytes_per_sample; stream = al_calloc(1, sizeof(*stream)); if (!stream) { _al_set_error(ALLEGRO_GENERIC_ERROR, "Out of memory allocating stream object"); return NULL; } stream->spl.is_playing = true; stream->is_draining = false; stream->spl.loop = _ALLEGRO_PLAYMODE_STREAM_ONCE; stream->spl.spl_data.depth = depth; stream->spl.spl_data.chan_conf = chan_conf; stream->spl.spl_data.frequency = freq; stream->spl.speed = 1.0f; stream->spl.gain = 1.0f; stream->spl.pan = 0.0f; stream->spl.step = 0; stream->spl.pos = frag_samples; stream->spl.spl_data.len = stream->spl.pos; stream->buf_count = fragment_count; stream->used_bufs = al_calloc(1, fragment_count * sizeof(void *) * 2); if (!stream->used_bufs) { al_free(stream->used_bufs); al_free(stream); _al_set_error(ALLEGRO_GENERIC_ERROR, "Out of memory allocating stream buffer pointers"); return NULL; } stream->pending_bufs = stream->used_bufs + fragment_count; /* The main_buffer holds all the buffer fragments in contiguous memory. * To support interpolation across buffer fragments, we allocate extra * MAX_LAG samples at the start of each buffer fragment, to hold the * last few sample values which came before that fragment. */ stream->main_buffer = al_calloc(1, (MAX_LAG * bytes_per_sample + bytes_per_frag_buf) * fragment_count); if (!stream->main_buffer) { al_free(stream->used_bufs); al_free(stream); _al_set_error(ALLEGRO_GENERIC_ERROR, "Out of memory allocating stream buffer"); return NULL; } for (i = 0; i < fragment_count; i++) { char *buffer = (char *)stream->main_buffer + i * (MAX_LAG * bytes_per_sample + bytes_per_frag_buf); al_fill_silence(buffer, MAX_LAG, depth, chan_conf); stream->used_bufs[i] = buffer + MAX_LAG * bytes_per_sample; } al_init_user_event_source(&stream->spl.es); /* This can lead to deadlocks on shutdown, hence we don't do it. */ /* _al_kcm_register_destructor(stream, (void (*)(void *)) al_destroy_audio_stream); */ return stream; }