Beispiel #1
0
/* 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;
}
Beispiel #3
0
/* 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;
}
Beispiel #4
0
/* 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;
}
Beispiel #5
0
/* 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;
}
Beispiel #8
0
/* 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;
}
Beispiel #9
0
/* 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;
}
Beispiel #12
0
/* 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;
}
Beispiel #13
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;
}
Beispiel #14
0
/* 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;
}
Beispiel #15
0
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;
    }
}
Beispiel #16
0
/* 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;
}