/* Function: al_create_sample */ ALLEGRO_SAMPLE *al_create_sample(void *buf, unsigned int samples, unsigned int freq, ALLEGRO_AUDIO_DEPTH depth, ALLEGRO_CHANNEL_CONF chan_conf, bool free_buf) { ALLEGRO_SAMPLE *spl; ASSERT(buf); if (!freq) { _al_set_error(ALLEGRO_INVALID_PARAM, "Invalid sample frequency"); return NULL; } spl = al_calloc(1, sizeof(*spl)); if (!spl) { _al_set_error(ALLEGRO_GENERIC_ERROR, "Out of memory allocating sample data object"); return NULL; } spl->depth = depth; spl->chan_conf = chan_conf; spl->frequency = freq; spl->len = samples; spl->buffer.ptr = buf; spl->free_buf = free_buf; _al_kcm_register_destructor(spl, (void (*)(void *)) al_destroy_sample); return spl; }
/* 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_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_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_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_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_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_create_voice */ ALLEGRO_VOICE *al_create_voice(unsigned int freq, ALLEGRO_AUDIO_DEPTH depth, ALLEGRO_CHANNEL_CONF chan_conf) { ALLEGRO_VOICE *voice = NULL; if (!freq) { _al_set_error(ALLEGRO_INVALID_PARAM, "Invalid Voice Frequency"); return NULL; } voice = calloc(1, sizeof(*voice)); if (!voice) { return NULL; } voice->depth = depth; voice->chan_conf = chan_conf; voice->frequency = freq; voice->mutex = al_create_mutex(); /* XXX why is this needed? there should only be one active driver */ voice->driver = _al_kcm_driver; ASSERT(_al_kcm_driver); if (_al_kcm_driver->allocate_voice(voice) != 0) { al_destroy_mutex(voice->mutex); free(voice); return NULL; } _al_kcm_register_destructor(voice, (void (*)(void *)) al_destroy_voice); return voice; }
/* 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_length */ bool al_set_sample_instance_length(ALLEGRO_SAMPLE_INSTANCE *spl, unsigned int val) { ASSERT(spl); if (spl->is_playing) { _al_set_error(ALLEGRO_INVALID_OBJECT, "Attempted to change the length of a playing sample"); return false; } spl->spl_data.len = val; return true; }
/* 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; }
/* Function: al_create_sample_instance */ ALLEGRO_SAMPLE_INSTANCE *al_create_sample_instance(ALLEGRO_SAMPLE *sample_data) { ALLEGRO_SAMPLE_INSTANCE *spl; spl = al_calloc(1, sizeof(*spl)); if (!spl) { _al_set_error(ALLEGRO_GENERIC_ERROR, "Out of memory allocating sample object"); return NULL; } if (sample_data) { spl->spl_data = *sample_data; } spl->spl_data.free_buf = false; spl->loop = ALLEGRO_PLAYMODE_ONCE; spl->speed = 1.0f; spl->gain = 1.0f; spl->pan = 0.0f; spl->pos = 0; spl->loop_start = 0; spl->loop_end = sample_data ? sample_data->len : 0; spl->step = 0; spl->matrix = NULL; spl->is_mixer = false; spl->spl_read = NULL; spl->mutex = NULL; spl->parent.u.ptr = NULL; _al_kcm_register_destructor("sample_instance", spl, (void (*)(void *))al_destroy_sample_instance); return spl; }
/* 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 bool do_install_audio(ALLEGRO_AUDIO_DRIVER_ENUM mode) { bool retVal; /* check to see if a driver is already installed and running */ if (_al_kcm_driver) { _al_set_error(ALLEGRO_GENERIC_ERROR, "A driver already running"); return false; } if (mode == ALLEGRO_AUDIO_DRIVER_AUTODETECT) { mode = get_config_audio_driver(); } switch (mode) { case ALLEGRO_AUDIO_DRIVER_AUTODETECT: #if defined(ALLEGRO_CFG_KCM_AQUEUE) retVal = do_install_audio(ALLEGRO_AUDIO_DRIVER_AQUEUE); if (retVal) return retVal; #endif /* If a PA server is running, we should use it by default as it will * hijack ALSA and OSS and using those then just means extra lag. * * FIXME: Detect if no PA server is running and in that case prefer * ALSA and OSS first. */ #if defined(ALLEGRO_CFG_KCM_PULSEAUDIO) retVal = do_install_audio(ALLEGRO_AUDIO_DRIVER_PULSEAUDIO); if (retVal) return retVal; #endif #if defined(ALLEGRO_CFG_KCM_ALSA) retVal = do_install_audio(ALLEGRO_AUDIO_DRIVER_ALSA); if (retVal) return retVal; #endif #if defined(ALLEGRO_CFG_KCM_DSOUND) retVal = do_install_audio(ALLEGRO_AUDIO_DRIVER_DSOUND); if (retVal) return retVal; #endif #if defined(ALLEGRO_CFG_KCM_OSS) retVal = do_install_audio(ALLEGRO_AUDIO_DRIVER_OSS); if (retVal) return retVal; #endif #if defined(ALLEGRO_CFG_KCM_OPENAL) retVal = do_install_audio(ALLEGRO_AUDIO_DRIVER_OPENAL); if (retVal) return retVal; #endif #if defined(ALLEGRO_CFG_KCM_OPENSL) retVal = do_install_audio(ALLEGRO_AUDIO_DRIVER_OPENSL); if (retVal) return retVal; #endif _al_set_error(ALLEGRO_INVALID_PARAM, "No audio driver can be used."); _al_kcm_driver = NULL; return false; case ALLEGRO_AUDIO_DRIVER_AQUEUE: #if defined(ALLEGRO_CFG_KCM_AQUEUE) if (_al_kcm_aqueue_driver.open() == 0) { ALLEGRO_INFO("Using Apple Audio Queue driver\n"); _al_kcm_driver = &_al_kcm_aqueue_driver; return true; } return false; #else _al_set_error(ALLEGRO_INVALID_PARAM, "Audio Queue driver not available on this platform"); return false; #endif case ALLEGRO_AUDIO_DRIVER_OPENAL: #if defined(ALLEGRO_CFG_KCM_OPENAL) if (_al_kcm_openal_driver.open() == 0) { ALLEGRO_INFO("Using OpenAL driver\n"); _al_kcm_driver = &_al_kcm_openal_driver; return true; } return false; #else _al_set_error(ALLEGRO_INVALID_PARAM, "OpenAL not available on this platform"); return false; #endif case ALLEGRO_AUDIO_DRIVER_OPENSL: #if defined(ALLEGRO_CFG_KCM_OPENSL) if (_al_kcm_opensl_driver.open() == 0) { ALLEGRO_INFO("Using OpenSL driver\n"); _al_kcm_driver = &_al_kcm_opensl_driver; return true; } return false; #else _al_set_error(ALLEGRO_INVALID_PARAM, "OpenSL not available on this platform"); return false; #endif case ALLEGRO_AUDIO_DRIVER_ALSA: #if defined(ALLEGRO_CFG_KCM_ALSA) if (_al_kcm_alsa_driver.open() == 0) { ALLEGRO_INFO("Using ALSA driver\n"); _al_kcm_driver = &_al_kcm_alsa_driver; return true; } return false; #else _al_set_error(ALLEGRO_INVALID_PARAM, "ALSA not available on this platform"); return false; #endif case ALLEGRO_AUDIO_DRIVER_OSS: #if defined(ALLEGRO_CFG_KCM_OSS) if (_al_kcm_oss_driver.open() == 0) { ALLEGRO_INFO("Using OSS driver\n"); _al_kcm_driver = &_al_kcm_oss_driver; return true; } return false; #else _al_set_error(ALLEGRO_INVALID_PARAM, "OSS not available on this platform"); return false; #endif case ALLEGRO_AUDIO_DRIVER_PULSEAUDIO: #if defined(ALLEGRO_CFG_KCM_PULSEAUDIO) if (_al_kcm_pulseaudio_driver.open() == 0) { ALLEGRO_INFO("Using PulseAudio driver\n"); _al_kcm_driver = &_al_kcm_pulseaudio_driver; return true; } return false; #else _al_set_error(ALLEGRO_INVALID_PARAM, "PulseAudio not available on this platform"); return false; #endif case ALLEGRO_AUDIO_DRIVER_DSOUND: #if defined(ALLEGRO_CFG_KCM_DSOUND) if (_al_kcm_dsound_driver.open() == 0) { ALLEGRO_INFO("Using DirectSound driver\n"); _al_kcm_driver = &_al_kcm_dsound_driver; return true; } return false; #else _al_set_error(ALLEGRO_INVALID_PARAM, "DirectSound not available on this platform"); return false; #endif default: _al_set_error(ALLEGRO_INVALID_PARAM, "Invalid audio driver"); return false; } }
/* 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; }