int
sa_stream_get_min_write(sa_stream_t *s, size_t *size) {
  int r;
  snd_pcm_uframes_t threshold;
  snd_pcm_sw_params_t* swparams;
  if (s == NULL || s->output_unit == NULL) {
    return SA_ERROR_NO_INIT;
  }
  snd_pcm_sw_params_alloca(&swparams);
  snd_pcm_sw_params_current(s->output_unit, swparams);
  r = snd_pcm_sw_params_get_start_threshold(swparams, &threshold);
  if (r < 0) {
    return SA_ERROR_NO_INIT;
  }
  *size = snd_pcm_frames_to_bytes(s->output_unit, threshold);

  return SA_SUCCESS;
}
Beispiel #2
0
static int set_swparams(snd_pcm_t *handle, snd_pcm_sw_params_t *swparams, int playback)
{
#ifndef ALSAAPI9
  int err;
  snd_pcm_uframes_t ps,ops;
  snd_pcm_uframes_t bs,obs;

  /* get the current swparams */
  err = snd_pcm_sw_params_current(handle, swparams);
  if (err < 0) {
    check_error(err,"Unable to determine current swparams for playback");
    return err;
  }

  /* AUTOSTART: start the transfer on each write/commit ??? */
  snd_pcm_sw_params_get_start_threshold(swparams, &obs);

  err = snd_pcm_sw_params_set_start_threshold(handle, swparams, 0U);
  if (err < 0) {
    check_error(err,"Unable to set start threshold mode");
    return err;
  }

  snd_pcm_sw_params_get_start_threshold(swparams, &bs);

#ifdef ALSAMM_DEBUG
  if(sys_verbose)
    post("sw_params: got start_thresh_hold= %d (was %d)",(int) bs,(int)obs);
#endif

  /* AUTOSTOP:  never stop the machine */

  snd_pcm_sw_params_get_stop_threshold(swparams, &obs);

  err = snd_pcm_sw_params_set_stop_threshold(handle, swparams, (snd_pcm_uframes_t)-1);
  if (err < 0) {
    check_error(err,"Unable to set stop threshold mode");
    return err;
  }

  snd_pcm_sw_params_get_stop_threshold(swparams, &bs);
#ifdef ALSAMM_DEBUG
  if(sys_verbose)
    post("sw_params: set stop_thresh_hold= %d (was %d)", (int) bs,(int)obs);
#endif


  /* AUTOSILENCE: silence if overrun.... */

  snd_pcm_sw_params_get_silence_threshold (swparams, &ops);
  if ((err = snd_pcm_sw_params_set_silence_threshold (handle, swparams, alsamm_period_size)) < 0) {
    check_error (err,"cannot set silence threshold for");
    return -1;
  }
  snd_pcm_sw_params_get_silence_threshold (swparams, &ps);
#ifdef ALSAMM_DEBUG
  if(sys_verbose)
    post("sw_params: set silence_threshold = %d (was %d)", (int) ps,(int)ops);
#endif

  snd_pcm_sw_params_get_silence_size (swparams, &ops);
  if ((err = snd_pcm_sw_params_set_silence_size(handle, swparams, alsamm_period_size)) < 0) {
    check_error (err,"cannot set silence size for");
    return -1;
  }
  snd_pcm_sw_params_get_silence_size (swparams, &ps);
#ifdef ALSAMM_DEBUG
  if(sys_verbose)
    post("sw_params: set silence_size = %d (was %d)", (int) ps,(int)ops);
#endif

  /* AVAIL: allow the transfer when at least period_size samples can be processed */

  snd_pcm_sw_params_get_avail_min(swparams, &ops);

  err = snd_pcm_sw_params_set_avail_min(handle, swparams, alsamm_transfersize/2);
  if (err < 0) {
    check_error(err,"Unable to set avail min for");
    return err;
    }

  snd_pcm_sw_params_get_avail_min(swparams, &ps);
#ifdef ALSAMM_DEBUG
  if(sys_verbose)
    post("sw_params: set avail_min= %d (was  %d)", (int) ps, (int) ops);
#endif

  /* write the parameters to the playback device */

  err = snd_pcm_sw_params(handle, swparams);
  if (err < 0) {
    check_error(err,"Unable to set sw params");
    return err;
  }

#ifdef ALSAMM_DEBUG
  if(sys_verbose)
    post("set sw finished");
#endif
#else
  post("alsa: need version 1.0 or above for mmap operation");
#endif /* ALSAAPI9 */
  return 0;
}
/*----------------------------------------------------------------------
|    AlsaOutput_Configure
+---------------------------------------------------------------------*/
static BLT_Result
AlsaOutput_Configure(AlsaOutput*             self, 
                     const BLT_PcmMediaType* format)
{
    snd_pcm_hw_params_t* hw_params;
    snd_pcm_sw_params_t* sw_params;
    unsigned int         rate = format->sample_rate;
    unsigned int         buffer_time = BLT_ALSA_DEFAULT_BUFFER_TIME;
    snd_pcm_uframes_t    buffer_size = 0;
    snd_pcm_uframes_t    period_size = BLT_ALSA_DEFAULT_PERIOD_SIZE;
    snd_pcm_format_t     pcm_format_id = SND_PCM_FORMAT_UNKNOWN;
    int                  ior;
    BLT_Result           result;

    switch (self->state) {
      case BLT_ALSA_OUTPUT_STATE_CLOSED:
        /* first, we need to open the device */
        result = AlsaOutput_Open(self);
        if (BLT_FAILED(result)) return result;

        /* FALLTHROUGH */

      case BLT_ALSA_OUTPUT_STATE_CONFIGURED:
      case BLT_ALSA_OUTPUT_STATE_PREPARED:
        /* check to see if the format has changed */
        if (format->sample_rate     != self->media_type.sample_rate   ||
            format->channel_count   != self->media_type.channel_count ||
            format->bits_per_sample != self->media_type.bits_per_sample) {
            /* new format */

            /* check the format */
            if (format->sample_rate     == 0 ||
                format->channel_count   == 0 ||
                format->bits_per_sample == 0) {
                return BLT_ERROR_INVALID_MEDIA_FORMAT;
            }
        
            /* unprepare (forget current settings) */
            result = AlsaOutput_Unprepare(self);
            if (BLT_FAILED(result)) return result;
        } else {
            /* same format, do nothing */
            return BLT_SUCCESS;
        }
        
        /* FALLTHROUGH */

      case BLT_ALSA_OUTPUT_STATE_OPEN:
        /* configure the device with the new format */
        ATX_LOG_FINER("configuring ALSA device");

        /* copy the format */
        self->media_type = *format;

        ATX_LOG_FINE_3("new format: sr=%d, ch=%d, bps=%d",
                       format->sample_rate,
                       format->channel_count,
                       format->bits_per_sample);

        /* allocate a new blank configuration */
        snd_pcm_hw_params_alloca_no_assert(&hw_params);
        snd_pcm_hw_params_any(self->device_handle, hw_params);

        /* use interleaved access */
        ior = snd_pcm_hw_params_set_access(self->device_handle, hw_params, 
                                           SND_PCM_ACCESS_RW_INTERLEAVED);
        if (ior != 0) {
            ATX_LOG_WARNING_2("snd_pcm_hw_params_set_access failed (%d:%s)", ior, snd_strerror(ior));
            return BLT_FAILURE;
        }

        /* set the sample rate */
        ior = snd_pcm_hw_params_set_rate_near(self->device_handle, 
                                              hw_params, 
                                              &rate, NULL);
        if (ior != 0) {
            ATX_LOG_WARNING_3("snd_pcm_hw_params_set_rate_near(%d) failed (%d:%s)", rate, ior, snd_strerror(ior));
            return BLT_FAILURE;
        }

        /* set the number of channels */
        ior = snd_pcm_hw_params_set_channels(self->device_handle, 
                                             hw_params,
                                             format->channel_count);
        if (ior != 0) {
            ATX_LOG_WARNING_3("snd_pcm_hw_params_set_channels(%d) failed (%d:%s)", format->channel_count, ior, snd_strerror(ior));
            return BLT_FAILURE;
        }

        /* set the sample format */
        switch (format->sample_format) {
        	case BLT_PCM_SAMPLE_FORMAT_SIGNED_INT_LE:
                ATX_LOG_FINE("sample format is BLT_PCM_SAMPLE_FORMAT_SIGNED_INT_LE");
        		switch (format->bits_per_sample) {
        			case  8: pcm_format_id = SND_PCM_FORMAT_S8;      break;
        			case 16: pcm_format_id = SND_PCM_FORMAT_S16_LE;  break;
        			case 24: pcm_format_id = SND_PCM_FORMAT_S24_3LE; break;
        			case 32: pcm_format_id = SND_PCM_FORMAT_S32_LE;  break;
        		}
        		break;
        		
        	case BLT_PCM_SAMPLE_FORMAT_UNSIGNED_INT_LE:
                ATX_LOG_FINE("sample format is BLT_PCM_SAMPLE_FORMAT_UNSIGNED_INT_LE");
        		switch (format->bits_per_sample) {
        			case  8: pcm_format_id = SND_PCM_FORMAT_U8;      break;
        			case 16: pcm_format_id = SND_PCM_FORMAT_U16_LE;  break;
        			case 24: pcm_format_id = SND_PCM_FORMAT_U24_3LE; break;
        			case 32: pcm_format_id = SND_PCM_FORMAT_U32_LE;  break;
        		}
        		break;

        	case BLT_PCM_SAMPLE_FORMAT_FLOAT_LE:
                ATX_LOG_FINE("sample format is BLT_PCM_SAMPLE_FORMAT_FLOAT_LE");
        		switch (format->bits_per_sample) {
        			case 32: pcm_format_id = SND_PCM_FORMAT_FLOAT_LE; break;
        		}
        		break;

        	case BLT_PCM_SAMPLE_FORMAT_SIGNED_INT_BE:
                ATX_LOG_FINE("sample format is BLT_PCM_SAMPLE_FORMAT_SIGNED_INT_BE");
        		switch (format->bits_per_sample) {
        			case  8: pcm_format_id = SND_PCM_FORMAT_S8;      break;
        			case 16: pcm_format_id = SND_PCM_FORMAT_S16_BE;  break;
        			case 24: pcm_format_id = SND_PCM_FORMAT_S24_3BE; break;
        			case 32: pcm_format_id = SND_PCM_FORMAT_S32_BE;  break;
        		}
        		break;
        		
        	case BLT_PCM_SAMPLE_FORMAT_UNSIGNED_INT_BE:
                ATX_LOG_FINE("sample format is BLT_PCM_SAMPLE_FORMAT_UNSIGNED_INT_BE");
        		switch (format->bits_per_sample) {
        			case  8: pcm_format_id = SND_PCM_FORMAT_U8;      break;
        			case 16: pcm_format_id = SND_PCM_FORMAT_U16_BE;  break;
        			case 24: pcm_format_id = SND_PCM_FORMAT_U24_3BE; break;
        			case 32: pcm_format_id = SND_PCM_FORMAT_U32_BE;  break;
        		}
        		break;

        	case BLT_PCM_SAMPLE_FORMAT_FLOAT_BE:
                ATX_LOG_FINE("sample format is BLT_PCM_SAMPLE_FORMAT_FLOAT_LE");
        		switch (format->bits_per_sample) {
        			case 32: pcm_format_id = SND_PCM_FORMAT_FLOAT_BE; break;
        		}
        		break;
        }

        if (pcm_format_id == SND_PCM_FORMAT_UNKNOWN) {
            return BLT_ERROR_INVALID_MEDIA_TYPE;
        }
        ior = snd_pcm_hw_params_set_format(self->device_handle, 
                                           hw_params,
                                           pcm_format_id);
        if (ior != 0) {
            ATX_LOG_WARNING_2("snd_pcm_hw_params_set_format() failed (%d:%s)", ior, snd_strerror(ior));
            return BLT_FAILURE;
        }

        /* set the period size */
        ior = snd_pcm_hw_params_set_period_size_near(self->device_handle, 
                                                     hw_params,
                                                     &period_size,
                                                     NULL);
        if (ior != 0) {
            ATX_LOG_WARNING_2("snd_pcm_hw_params_set_period_size_near() failed (%d:%s)", ior, snd_strerror(ior));
            return BLT_FAILURE;
        }
        
                                                
        /* set the buffer time (duration) */
        ior = snd_pcm_hw_params_set_buffer_time_near(self->device_handle,
                                                     hw_params, 
                                                     &buffer_time,
                                                     NULL);
        if (ior != 0) {
            ATX_LOG_WARNING_2("snd_pcm_hw_params_set_buffer_time_near() failed (%d:%s)", ior, snd_strerror(ior));
            return BLT_FAILURE;
        }

        /* get the actual buffer size */
        snd_pcm_hw_params_get_buffer_size(hw_params, &buffer_size);

        /* activate this configuration */
        ior = snd_pcm_hw_params(self->device_handle, hw_params);
        if (ior != 0) {
            ATX_LOG_WARNING_2("snd_pcm_hw_params() failed (%d:%s)", ior, snd_strerror(ior));
            return BLT_FAILURE;
        }

        /* configure the software parameters */
        snd_pcm_sw_params_alloca_no_assert(&sw_params);
        snd_pcm_sw_params_current(self->device_handle, sw_params);

        /* set the start threshold to 1/2 the buffer size */
        snd_pcm_sw_params_set_start_threshold(self->device_handle, 
                                              sw_params, 
                                              buffer_size/2);

        /* set the buffer alignment */
        /* NOTE: this call is now obsolete */
        /* snd_pcm_sw_params_set_xfer_align(self->device_handle, sw_params, 1); */

        /* activate the sofware parameters */
        ior = snd_pcm_sw_params(self->device_handle, sw_params);
        if (ior != 0) {
            ATX_LOG_SEVERE_2("snd_pcm_sw_params() failed (%d:%s)", ior, snd_strerror(ior));
            return BLT_FAILURE;
        }

        /* print status info */
        {
            snd_pcm_uframes_t val;
            ATX_LOG_FINER_1("sample type = %x", pcm_format_id);
            if (rate != format->sample_rate) {
                ATX_LOG_FINER_1("actual sample = %d", rate);
            }
            ATX_LOG_FINER_1("actual buffer time = %d", (int)buffer_time);
            ATX_LOG_FINER_1("buffer size = %d", (int)buffer_size); 
            snd_pcm_sw_params_get_start_threshold(sw_params, &val);
            ATX_LOG_FINER_1("start threshold = %d", (int)val); 
            snd_pcm_sw_params_get_stop_threshold(sw_params, &val);
            ATX_LOG_FINER_1("stop threshold = %d", (int)val); 
            snd_pcm_hw_params_get_period_size(hw_params, &val, NULL);
            ATX_LOG_FINER_1("period size = %d", (int)val);
        }

        break;
    }

    /* update the state */
    AlsaOutput_SetState(self, BLT_ALSA_OUTPUT_STATE_CONFIGURED);

    return BLT_SUCCESS;
}