text: /* 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_list.h"
  #include "allegro5/internal/aintern_vector.h"
  #include "../allegro_audio.h"
  
  struct ALLEGRO_AUDIO_RECORDER {
    ALLEGRO_EVENT_SOURCE source;
    
    ALLEGRO_THREAD           *thread;
    ALLEGRO_MUTEX            *mutex;
    ALLEGRO_COND             *cond;
                             /* recording is done in its own thread as
                                implemented by the driver */
    
    ALLEGRO_AUDIO_DEPTH      depth;
    ALLEGRO_CHANNEL_CONF     chan_conf;
    unsigned int             frequency;
  
    void                     **fragments;
                             /* the buffers to record into */
  
    unsigned int             fragment_count;
                             /* the number of fragments */
  
    unsigned int             samples;
                             /* the number of samples returned at every FRAGMENT event */
                             
    size_t                   fragment_size;
                             /* size in bytes of each fragument */
  
    unsigned int             sample_size;
                             /* the size in bytes of each sample */
    
    volatile bool            is_recording; 
                             /* true if the driver should actively be updating
                                the buffer */
                                
    void                     *extra;
                             /* custom data for the driver to use as needed */
  };
  
  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_OPENSL     = 0x20007,
     ALLEGRO_AUDIO_DRIVER_SDL        = 0x20008
  } 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);
     
     
     int            (*allocate_recorder)(struct ALLEGRO_AUDIO_RECORDER *);
     void           (*deallocate_recorder)(struct ALLEGRO_AUDIO_RECORDER *);
  
     _AL_LIST*      (*get_output_devices)(void);
  };
  
  extern ALLEGRO_AUDIO_DRIVER *_al_kcm_driver;
  
  const void *_al_voice_update(ALLEGRO_VOICE *voice, ALLEGRO_MUTEX *mutex,
     unsigned int *samples);
  bool _al_kcm_set_voice_playing(ALLEGRO_VOICE *voice, ALLEGRO_MUTEX *mutex,
     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;
  
     _AL_LIST_ITEM        *dtor_item;
  
     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.
                           */
     _AL_LIST_ITEM        *dtor_item;
  };
  
  /* 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.
                           */
     _AL_LIST_ITEM        *dtor_item;
  };
  
  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.
                            */
  
     uint64_t              consumed_fragments;
                           /* Number of complete fragment buffers consumed since
                            * the stream was started.
                            */
  
     ALLEGRO_THREAD        *feed_thread;
     ALLEGRO_MUTEX         *feed_thread_started_mutex;
     ALLEGRO_COND          *feed_thread_started_cond;
     bool                  feed_thread_started;
     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.
                            */
  
     _AL_LIST_ITEM        *dtor_item;
  
     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.
                              */
     _AL_LIST_ITEM           *dtor_item;
  };
  
  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);
  
  struct ALLEGRO_AUDIO_DEVICE {
     char* name;
     void* identifier;
  };
  
  /* Supposedly internal */
  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);
  _AL_LIST_ITEM *_al_kcm_register_destructor(char const *name, void *object,
     void (*func)(void*));
  void _al_kcm_unregister_destructor(_AL_LIST_ITEM *dtor_item);
  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));
  
  ALLEGRO_KCM_AUDIO_FUNC(void, _al_emit_audio_event, (int event_type));
  
  #endif
  
  /* vim: set sts=3 sw=3 et: */
  