/* Function: al_set_sample_instance_playing */ bool al_set_sample_instance_playing(ALLEGRO_SAMPLE_INSTANCE *spl, bool val) { ASSERT(spl); if (!spl->parent.u.ptr) { _al_set_error(ALLEGRO_INVALID_OBJECT, "Sample has no parent"); return false; } if (!spl->spl_data.buffer.ptr) { _al_set_error(ALLEGRO_INVALID_OBJECT, "Sample has no data"); return false; } if (spl->parent.is_voice) { /* parent is voice */ ALLEGRO_VOICE *voice = spl->parent.u.voice; return al_set_voice_playing(voice, val); } /* parent is mixer */ maybe_lock_mutex(spl->mutex); spl->is_playing = val; if (!val) spl->pos = 0; maybe_unlock_mutex(spl->mutex); return true; }
/* Function: al_set_audio_stream_fragment */ bool al_set_audio_stream_fragment(ALLEGRO_AUDIO_STREAM *stream, void *val) { size_t i; bool ret; ALLEGRO_MUTEX *stream_mutex; ASSERT(stream); stream_mutex = maybe_lock_mutex(stream->spl.mutex); for (i = 0; i < stream->buf_count && stream->pending_bufs[i] ; i++) ; if (i < stream->buf_count) { stream->pending_bufs[i] = val; ret = true; } else { _al_set_error(ALLEGRO_INVALID_OBJECT, "Attempted to set a stream buffer with a full pending list"); ret = false; } maybe_unlock_mutex(stream_mutex); return ret; }
/* Function: al_set_sample_instance_gain */ bool al_set_sample_instance_gain(ALLEGRO_SAMPLE_INSTANCE *spl, float val) { ASSERT(spl); if (spl->parent.u.ptr && spl->parent.is_voice) { _al_set_error(ALLEGRO_GENERIC_ERROR, "Could not set gain of sample attached to voice"); return false; } if (spl->gain != val) { spl->gain = val; /* If attached to a mixer already, need to recompute the sample * matrix to take into account the gain. */ if (spl->parent.u.mixer) { ALLEGRO_MIXER *mixer = spl->parent.u.mixer; maybe_lock_mutex(spl->mutex); _al_kcm_mixer_rejig_sample_matrix(mixer, spl); maybe_unlock_mutex(spl->mutex); } } return true; }
/* Function: al_set_audio_stream_pan */ bool al_set_audio_stream_pan(ALLEGRO_AUDIO_STREAM *stream, float val) { ASSERT(stream); if (stream->spl.parent.u.ptr && stream->spl.parent.is_voice) { _al_set_error(ALLEGRO_GENERIC_ERROR, "Could not set gain of stream attached to voice"); return false; } if (val != ALLEGRO_AUDIO_PAN_NONE && (val < -1.0 || val > 1.0)) { _al_set_error(ALLEGRO_GENERIC_ERROR, "Invalid pan value"); return false; } if (stream->spl.pan != val) { stream->spl.pan = val; /* If attached to a mixer already, need to recompute the sample * matrix to take into account the panning. */ if (stream->spl.parent.u.mixer) { ALLEGRO_MIXER *mixer = stream->spl.parent.u.mixer; ALLEGRO_MUTEX *stream_mutex = maybe_lock_mutex(stream->spl.mutex); _al_kcm_mixer_rejig_sample_matrix(mixer, &stream->spl); maybe_unlock_mutex(stream_mutex); } } return true; }
/* Function: al_set_audio_stream_playing */ bool al_set_audio_stream_playing(ALLEGRO_AUDIO_STREAM *stream, bool val) { bool rc = true; ALLEGRO_MUTEX *stream_mutex; ASSERT(stream); if (stream->spl.parent.u.ptr && stream->spl.parent.is_voice) { ALLEGRO_VOICE *voice = stream->spl.parent.u.voice; if (val != stream->spl.is_playing) { rc = _al_kcm_set_voice_playing(voice, voice->mutex, val); } } stream_mutex = maybe_lock_mutex(stream->spl.mutex); stream->spl.is_playing = rc && val; if (stream->spl.is_playing) { /* We usually emit fragment events when a fragment is used up, but if the * stream has zero pending fragments to begin with then no events would * ever be emitted. */ _al_kcm_emit_stream_events(stream); } else if (!val) { reset_stopped_stream(stream); } maybe_unlock_mutex(stream_mutex); return rc; }
/* Function: al_set_audio_stream_speed */ bool al_set_audio_stream_speed(ALLEGRO_AUDIO_STREAM *stream, float val) { ASSERT(stream); if (val <= 0.0f) { _al_set_error(ALLEGRO_INVALID_PARAM, "Attempted to set stream speed to a zero or negative value"); return false; } if (stream->spl.parent.u.ptr && stream->spl.parent.is_voice) { _al_set_error(ALLEGRO_GENERIC_ERROR, "Could not set voice playback speed"); return false; } stream->spl.speed = val; if (stream->spl.parent.u.mixer) { ALLEGRO_MIXER *mixer = stream->spl.parent.u.mixer; ALLEGRO_MUTEX *stream_mutex = maybe_lock_mutex(stream->spl.mutex); stream->spl.step = (stream->spl.spl_data.frequency) * stream->spl.speed; stream->spl.step_denom = mixer->ss.spl_data.frequency; /* Don't wanna be trapped with a step value of 0 */ if (stream->spl.step == 0) { stream->spl.step = 1; } maybe_unlock_mutex(stream_mutex); } return true; }
/* Function: al_get_audio_stream_fragment */ void *al_get_audio_stream_fragment(const ALLEGRO_AUDIO_STREAM *stream) { size_t i; void *fragment; ALLEGRO_MUTEX *stream_mutex; ASSERT(stream); stream_mutex = maybe_lock_mutex(stream->spl.mutex); if (!stream->used_bufs[0]) { /* No free fragments are available. */ fragment = NULL; } else { fragment = stream->used_bufs[0]; for (i = 0; i < stream->buf_count-1 && stream->used_bufs[i]; i++) { stream->used_bufs[i] = stream->used_bufs[i+1]; } stream->used_bufs[i] = NULL; } maybe_unlock_mutex(stream_mutex); return fragment; }
/* Function: al_set_sample_instance_pan */ bool al_set_sample_instance_pan(ALLEGRO_SAMPLE_INSTANCE *spl, float val) { ASSERT(spl); if (spl->parent.u.ptr && spl->parent.is_voice) { _al_set_error(ALLEGRO_GENERIC_ERROR, "Could not set panning of sample attached to voice"); return false; } if (val != ALLEGRO_AUDIO_PAN_NONE && (val < -1.0 || val > 1.0)) { _al_set_error(ALLEGRO_GENERIC_ERROR, "Invalid pan value"); return false; } if (spl->pan != val) { spl->pan = val; /* If attached to a mixer already, need to recompute the sample * matrix to take into account the panning. */ if (spl->parent.u.mixer) { ALLEGRO_MIXER *mixer = spl->parent.u.mixer; maybe_lock_mutex(spl->mutex); _al_kcm_mixer_rejig_sample_matrix(mixer, spl); maybe_unlock_mutex(spl->mutex); } } return true; }
/* Function: al_set_sample_instance_playmode */ bool al_set_sample_instance_playmode(ALLEGRO_SAMPLE_INSTANCE *spl, ALLEGRO_PLAYMODE val) { ASSERT(spl); if (val < ALLEGRO_PLAYMODE_ONCE || val > ALLEGRO_PLAYMODE_BIDIR) { _al_set_error(ALLEGRO_INVALID_PARAM, "Invalid loop mode"); return false; } maybe_lock_mutex(spl->mutex); spl->loop = val; if (spl->loop != ALLEGRO_PLAYMODE_ONCE) { if (spl->pos < spl->loop_start) spl->pos = spl->loop_start; else if (spl->pos > spl->loop_end-1) spl->pos = spl->loop_end-1; } maybe_unlock_mutex(spl->mutex); return true; }
/* Function: al_seek_audio_stream_secs */ bool al_seek_audio_stream_secs(ALLEGRO_AUDIO_STREAM *stream, double time) { bool ret; if (stream->seek_feeder) { ALLEGRO_MUTEX *stream_mutex = maybe_lock_mutex(stream->spl.mutex); ret = stream->seek_feeder(stream, time); maybe_unlock_mutex(stream_mutex); return ret; } return false; }
/* Function: al_get_audio_stream_length_secs */ double al_get_audio_stream_length_secs(ALLEGRO_AUDIO_STREAM *stream) { double ret; if (stream->get_feeder_length) { ALLEGRO_MUTEX *stream_mutex = maybe_lock_mutex(stream->spl.mutex); ret = stream->get_feeder_length(stream); maybe_unlock_mutex(stream_mutex); return ret; } return 0.0; }
/* Function: al_rewind_audio_stream */ bool al_rewind_audio_stream(ALLEGRO_AUDIO_STREAM *stream) { bool ret; if (stream->rewind_feeder) { ALLEGRO_MUTEX *stream_mutex = maybe_lock_mutex(stream->spl.mutex); ret = stream->rewind_feeder(stream); maybe_unlock_mutex(stream_mutex); return ret; } return false; }
/* Function: al_set_audio_stream_loop_secs */ bool al_set_audio_stream_loop_secs(ALLEGRO_AUDIO_STREAM *stream, double start, double end) { bool ret; if (start >= end) return false; if (stream->set_feeder_loop) { ALLEGRO_MUTEX *stream_mutex = maybe_lock_mutex(stream->spl.mutex); ret = stream->set_feeder_loop(stream, start, end); maybe_unlock_mutex(stream_mutex); return ret; } return false; }
/* Function: al_get_audio_stream_played_samples */ uint64_t al_get_audio_stream_played_samples(const ALLEGRO_AUDIO_STREAM *stream) { uint64_t result; ALLEGRO_MUTEX *stream_mutex; ASSERT(stream); stream_mutex = maybe_lock_mutex(stream->spl.mutex); if (stream->spl.spl_data.buffer.ptr) { result = stream->consumed_fragments * stream->spl.spl_data.len + stream->spl.pos; } else { result = 0; } maybe_unlock_mutex(stream_mutex); return result; }
/* Function: al_set_sample_instance_position */ bool al_set_sample_instance_position(ALLEGRO_SAMPLE_INSTANCE *spl, unsigned int val) { ASSERT(spl); if (spl->parent.u.ptr && spl->parent.is_voice) { ALLEGRO_VOICE *voice = spl->parent.u.voice; if (!al_set_voice_position(voice, val)) return false; } else { maybe_lock_mutex(spl->mutex); spl->pos = val; maybe_unlock_mutex(spl->mutex); } return true; }
/* _al_kcm_detach_from_parent: * This detaches the sample, stream, or mixer from anything it may be attached * to. */ void _al_kcm_detach_from_parent(ALLEGRO_SAMPLE_INSTANCE *spl) { ALLEGRO_MIXER *mixer; int i; if (!spl || !spl->parent.u.ptr) return; if (spl->parent.is_voice) { al_detach_voice(spl->parent.u.voice); return; } mixer = spl->parent.u.mixer; /* Search through the streams and check for this one */ for (i = _al_vector_size(&mixer->streams) - 1; i >= 0; i--) { ALLEGRO_SAMPLE_INSTANCE **slot = _al_vector_ref(&mixer->streams, i); if (*slot == spl) { maybe_lock_mutex(mixer->ss.mutex); _al_vector_delete_at(&mixer->streams, i); spl->parent.u.mixer = NULL; _al_kcm_stream_set_mutex(spl, NULL); if (spl->spl_read != _al_kcm_mixer_read) spl->spl_read = NULL; maybe_unlock_mutex(mixer->ss.mutex); break; } } free(spl->matrix); spl->matrix = NULL; }
/* Function: al_set_sample_instance_speed */ bool al_set_sample_instance_speed(ALLEGRO_SAMPLE_INSTANCE *spl, float val) { ASSERT(spl); if (fabsf(val) < (1.0f/64.0f)) { _al_set_error(ALLEGRO_INVALID_PARAM, "Attempted to set zero speed"); return false; } if (spl->parent.u.ptr && spl->parent.is_voice) { _al_set_error(ALLEGRO_GENERIC_ERROR, "Could not set voice playback speed"); return false; } spl->speed = val; if (spl->parent.u.mixer) { ALLEGRO_MIXER *mixer = spl->parent.u.mixer; maybe_lock_mutex(spl->mutex); spl->step = (spl->spl_data.frequency) * spl->speed; spl->step_denom = mixer->ss.spl_data.frequency; /* Don't wanna be trapped with a step value of 0 */ if (spl->step == 0) { if (spl->speed > 0.0f) spl->step = 1; else spl->step = -1; } maybe_unlock_mutex(spl->mutex); } return true; }
/* _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; }