/* internal-only header * Updated for 4.9 api inclusion by Ryan Dickie * Originally done by KC/Milan */ #ifndef AINTERN_AUDIO_H #define AINTERN_AUDIO_H #include "allegro5/allegro.h" #include "allegro5/internal/aintern_vector.h" #include "../allegro_audio.h" typedef enum ALLEGRO_AUDIO_DRIVER_ENUM { /* Various driver modes. */ ALLEGRO_AUDIO_DRIVER_AUTODETECT = 0x20000, ALLEGRO_AUDIO_DRIVER_OPENAL = 0x20001, ALLEGRO_AUDIO_DRIVER_ALSA = 0x20002, ALLEGRO_AUDIO_DRIVER_DSOUND = 0x20003, ALLEGRO_AUDIO_DRIVER_OSS = 0x20004, ALLEGRO_AUDIO_DRIVER_AQUEUE = 0x20005, ALLEGRO_AUDIO_DRIVER_PULSEAUDIO = 0x20006 } ALLEGRO_AUDIO_DRIVER_ENUM; typedef struct ALLEGRO_AUDIO_DRIVER ALLEGRO_AUDIO_DRIVER; struct ALLEGRO_AUDIO_DRIVER { const char *specifier; int (*open)(void); void (*close)(void); int (*allocate_voice)(ALLEGRO_VOICE*); void (*deallocate_voice)(ALLEGRO_VOICE*); int (*load_voice)(ALLEGRO_VOICE*, const void*); void (*unload_voice)(ALLEGRO_VOICE*); int (*start_voice)(ALLEGRO_VOICE*); int (*stop_voice)(ALLEGRO_VOICE*); bool (*voice_is_playing)(const ALLEGRO_VOICE*); unsigned int (*get_voice_position)(const ALLEGRO_VOICE*); int (*set_voice_position)(ALLEGRO_VOICE*, unsigned int); }; extern ALLEGRO_AUDIO_DRIVER *_al_kcm_driver; const void *_al_voice_update(ALLEGRO_VOICE *voice, unsigned int *samples); bool _al_kcm_set_voice_playing(ALLEGRO_VOICE *voice, bool val); /* A voice structure that you'd attach a mixer or sample to. Ideally there * would be one ALLEGRO_VOICE per system/hardware voice. */ struct ALLEGRO_VOICE { ALLEGRO_AUDIO_DEPTH depth; ALLEGRO_CHANNEL_CONF chan_conf; unsigned int frequency; size_t buffer_size; size_t num_buffers; /* If non-0, they must be honored by the driver. */ ALLEGRO_SAMPLE_INSTANCE *attached_stream; /* The stream that is attached to the voice, or NULL. * May be an ALLEGRO_SAMPLE_INSTANCE or ALLEGRO_MIXER object. */ bool is_streaming; /* True for voices with an attached mixer. */ ALLEGRO_MUTEX *mutex; ALLEGRO_COND *cond; ALLEGRO_AUDIO_DRIVER *driver; /* XXX shouldn't there only be one audio driver active * at a time? */ void *extra; /* Extra data for use by the driver. */ }; typedef union { float *f32; uint32_t *u24; int32_t *s24; uint16_t *u16; int16_t *s16; uint8_t *u8; int8_t *s8; void *ptr; } any_buffer_t; struct ALLEGRO_SAMPLE { ALLEGRO_AUDIO_DEPTH depth; ALLEGRO_CHANNEL_CONF chan_conf; unsigned int frequency; int len; any_buffer_t buffer; bool free_buf; /* Whether `buffer' needs to be freed when the sample * is destroyed, or when `buffer' changes. */ }; /* Read some samples into a mixer buffer. * * source: * The object to read samples from. This may be one of several types. * * *vbuf: (in-out parameter) * Pointer to pointer to destination buffer. * (should confirm what it means to change the pointer on return) * * *samples: (in-out parameter) * On input indicates the maximum number of samples that can fit into *vbuf. * On output indicates the actual number of samples that were read. * * buffer_depth: * The audio depth of the destination buffer. * * dest_maxc: * The number of channels in the destination. */ typedef void (*stream_reader_t)(void *source, void **vbuf, unsigned int *samples, ALLEGRO_AUDIO_DEPTH buffer_depth, size_t dest_maxc); typedef struct { union { ALLEGRO_MIXER *mixer; ALLEGRO_VOICE *voice; void *ptr; } u; bool is_voice; } sample_parent_t; /* The sample struct also serves the base of ALLEGRO_AUDIO_STREAM, ALLEGRO_MIXER. */ struct ALLEGRO_SAMPLE_INSTANCE { /* ALLEGRO_SAMPLE_INSTANCE does not generate any events yet but ALLEGRO_AUDIO_STREAM * does, which can inherit only ALLEGRO_SAMPLE_INSTANCE. */ ALLEGRO_EVENT_SOURCE es; ALLEGRO_SAMPLE spl_data; volatile bool is_playing; /* Is this sample is playing? */ ALLEGRO_PLAYMODE loop; float speed; float gain; float pan; /* When resampling an audio stream there will be fractional sample * positions due to the difference in frequencies. */ int pos; int pos_bresenham_error; int loop_start; int loop_end; int step; int step_denom; /* The numerator and denominator of the step are * stored separately. The actual step is obtained by * dividing step by step_denom */ float *matrix; /* Used to convert from this format to the attached * mixers, if any. Otherwise is NULL. * The gain is premultiplied in. */ bool is_mixer; stream_reader_t spl_read; /* Reads sample data into the provided buffer, using * the specified format, converting as necessary. */ ALLEGRO_MUTEX *mutex; /* Points to the parent object's mutex. It is NULL if * the sample is not directly or indirectly attached * to a voice. */ sample_parent_t parent; /* The object that this sample is attached to, if any. */ }; void _al_kcm_destroy_sample(ALLEGRO_SAMPLE_INSTANCE *sample, bool unregister); void _al_kcm_stream_set_mutex(ALLEGRO_SAMPLE_INSTANCE *stream, ALLEGRO_MUTEX *mutex); void _al_kcm_detach_from_parent(ALLEGRO_SAMPLE_INSTANCE *spl); typedef size_t (*stream_callback_t)(ALLEGRO_AUDIO_STREAM *, void *, size_t); typedef void (*unload_feeder_t)(ALLEGRO_AUDIO_STREAM *); typedef bool (*rewind_feeder_t)(ALLEGRO_AUDIO_STREAM *); typedef bool (*seek_feeder_t)(ALLEGRO_AUDIO_STREAM *, double); typedef double (*get_feeder_position_t)(ALLEGRO_AUDIO_STREAM *); typedef double (*get_feeder_length_t)(ALLEGRO_AUDIO_STREAM *); typedef bool (*set_feeder_loop_t)(ALLEGRO_AUDIO_STREAM *, double, double); struct ALLEGRO_AUDIO_STREAM { ALLEGRO_SAMPLE_INSTANCE spl; /* ALLEGRO_AUDIO_STREAM is derived from * ALLEGRO_SAMPLE_INSTANCE. */ unsigned int buf_count; /* The stream buffer is divided into a number of * fragments; this is the number of fragments. */ void *main_buffer; /* Pointer to a single buffer big enough to hold all * the fragments. Each fragment has additional samples * at the start for linear/cubic interpolation. */ void **pending_bufs; void **used_bufs; /* Arrays of offsets into the main_buffer. * The arrays are each 'buf_count' long. * * 'pending_bufs' holds pointers to fragments supplied * by the user which are yet to be handed off to the * audio driver. * * 'used_bufs' holds pointers to fragments which * have been sent to the audio driver and so are * ready to receive new data. */ volatile bool is_draining; /* Set to true if sample data is not going to be passed * to the stream any more. The stream must change its * playing state to false after all buffers have been * played. */ ALLEGRO_THREAD *feed_thread; volatile bool quit_feed_thread; unload_feeder_t unload_feeder; rewind_feeder_t rewind_feeder; seek_feeder_t seek_feeder; get_feeder_position_t get_feeder_position; get_feeder_length_t get_feeder_length; set_feeder_loop_t set_feeder_loop; stream_callback_t feeder; /* If ALLEGRO_AUDIO_STREAM has been created by * al_load_audio_stream(), the stream will be fed * by a thread using the 'feeder' callback. Such * streams don't need to be fed by the user. */ void *extra; /* Extra data for use by the flac/vorbis addons. */ }; bool _al_kcm_refill_stream(ALLEGRO_AUDIO_STREAM *stream); typedef void (*postprocess_callback_t)(void *buf, unsigned int samples, void *userdata); /* ALLEGRO_MIXER is derived from ALLEGRO_SAMPLE_INSTANCE. Certain internal functions and * pointers may take either object type, and such things are explicitly noted. * This is never exposed to the user, though. The sample object's read method * will be set to a different function that will call the read method of all * attached streams (which may be a sample, or another mixer). */ struct ALLEGRO_MIXER { ALLEGRO_SAMPLE_INSTANCE ss; /* ALLEGRO_MIXER is derived from ALLEGRO_SAMPLE_INSTANCE. */ ALLEGRO_MIXER_QUALITY quality; postprocess_callback_t postprocess_callback; void *pp_callback_userdata; _AL_VECTOR streams; /* Vector of ALLEGRO_SAMPLE_INSTANCE*. Holds the list of * streams being mixed together. */ }; extern void _al_kcm_mixer_rejig_sample_matrix(ALLEGRO_MIXER *mixer, ALLEGRO_SAMPLE_INSTANCE *spl); extern void _al_kcm_mixer_read(void *source, void **buf, unsigned int *samples, ALLEGRO_AUDIO_DEPTH buffer_depth, size_t dest_maxc); typedef enum { ALLEGRO_NO_ERROR = 0, ALLEGRO_INVALID_PARAM = 1, ALLEGRO_INVALID_OBJECT = 2, ALLEGRO_GENERIC_ERROR = 255 } AL_ERROR_ENUM; extern void _al_set_error(int error, char* string); /* Supposedly internal */ ALLEGRO_KCM_AUDIO_FUNC(int, _al_kcm_get_silence, (ALLEGRO_AUDIO_DEPTH depth)); ALLEGRO_KCM_AUDIO_FUNC(void*, _al_kcm_feed_stream, (ALLEGRO_THREAD *self, void *vstream)); /* Helper to emit an event that the stream has got a buffer ready to be refilled. */ void _al_kcm_emit_stream_events(ALLEGRO_AUDIO_STREAM *stream); void _al_kcm_init_destructors(void); void _al_kcm_shutdown_destructors(void); void _al_kcm_register_destructor(void *object, void (*func)(void*)); void _al_kcm_unregister_destructor(void *object); void _al_kcm_foreach_destructor( void (*callback)(void *object, void (*func)(void *), void *udata), void *userdata); ALLEGRO_KCM_AUDIO_FUNC(void, _al_kcm_shutdown_default_mixer, (void)); ALLEGRO_KCM_AUDIO_FUNC(ALLEGRO_CHANNEL_CONF, _al_count_to_channel_conf, (int num_channels)); ALLEGRO_KCM_AUDIO_FUNC(ALLEGRO_AUDIO_DEPTH, _al_word_size_to_depth_conf, (int word_size)); #endif /* vim: set sts=3 sw=3 et: */