Example #1
0
static void getparams_periods(snd_pcm_t *handle,
                              snd_pcm_hw_params_t *params,
                              unsigned int *usecs,
                              unsigned int *count,
                              const char *id)
{
    unsigned min = 0, max = 0;

    snd_pcm_hw_params_get_periods_min(params, &min, 0);
    snd_pcm_hw_params_get_periods_max(params, &max, 0);
    if (min && max) {
        if (verbose)
            fprintf(error_fp, "alsa: %s periods range between %u and %u. Want: %u\n",
                    id, min, max, *count);
        if (*count < min)
            *count = min;
        if (*count > max)
            *count = max;
    }

    min = max = 0;
    snd_pcm_hw_params_get_period_time_min(params, &min, 0);
    snd_pcm_hw_params_get_period_time_max(params, &max, 0);
    if (min && max) {
        if (verbose)
            fprintf(error_fp, "alsa: %s period time range between %u and %u. Want: %u\n",
                    id, min, max, *usecs);
        if (*usecs < min)
            *usecs = min;
        if (*usecs > max)
            *usecs = max;
    }
}
    optional<int> AudioOutputDeviceAlsa::ParameterFragments::RangeMaxAsInt(std::map<String,String> Parameters) {
        if (!Parameters.count("CARD")) return optional<int>::nothing;

        // obtain information from given sound card
        String pcm_name       = "hw:" + Parameters["CARD"];
        snd_pcm_t* pcm_handle = NULL;
        if (snd_pcm_open(&pcm_handle, pcm_name.c_str(), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK) < 0) return optional<int>::nothing;
        snd_pcm_hw_params_t* hwparams;
        snd_pcm_hw_params_alloca(&hwparams);
        if (snd_pcm_hw_params_any(pcm_handle, hwparams) < 0) {
            snd_pcm_close(pcm_handle);
            return optional<int>::nothing;
        }
        int dir = 0;
        uint periods_max;
        if (snd_pcm_hw_params_get_periods_max(hwparams, &periods_max, &dir) < 0) {
            snd_pcm_close(pcm_handle);
            return optional<int>::nothing;
        }
        snd_pcm_close(pcm_handle);
        return (int) periods_max;
    }
Example #3
0
static int
set_hwparams (GstAlsaSink * alsa)
{
    guint rrate;
    gint err;
    snd_pcm_hw_params_t *params;
    guint period_time, buffer_time;

    snd_pcm_hw_params_malloc (&params);

    GST_DEBUG_OBJECT (alsa, "Negotiating to %d channels @ %d Hz (format = %s) "
                      "SPDIF (%d)", alsa->channels, alsa->rate,
                      snd_pcm_format_name (alsa->format), alsa->iec958);

    /* start with requested values, if we cannot configure alsa for those values,
     * we set these values to -1, which will leave the default alsa values */
    buffer_time = alsa->buffer_time;
    period_time = alsa->period_time;

retry:
    /* choose all parameters */
    CHECK (snd_pcm_hw_params_any (alsa->handle, params), no_config);
    /* set the interleaved read/write format */
    CHECK (snd_pcm_hw_params_set_access (alsa->handle, params, alsa->access),
           wrong_access);
    /* set the sample format */
    if (alsa->iec958) {
        /* Try to use big endian first else fallback to le and swap bytes */
        if (snd_pcm_hw_params_set_format (alsa->handle, params, alsa->format) < 0) {
            alsa->format = SND_PCM_FORMAT_S16_LE;
            alsa->need_swap = TRUE;
            GST_DEBUG_OBJECT (alsa, "falling back to little endian with swapping");
        } else {
            alsa->need_swap = FALSE;
        }
    }
    CHECK (snd_pcm_hw_params_set_format (alsa->handle, params, alsa->format),
           no_sample_format);
    /* set the count of channels */
    CHECK (snd_pcm_hw_params_set_channels (alsa->handle, params, alsa->channels),
           no_channels);
    /* set the stream rate */
    rrate = alsa->rate;
    CHECK (snd_pcm_hw_params_set_rate_near (alsa->handle, params, &rrate, NULL),
           no_rate);

#ifndef GST_DISABLE_GST_DEBUG
    /* get and dump some limits */
    {
        guint min, max;

        snd_pcm_hw_params_get_buffer_time_min (params, &min, NULL);
        snd_pcm_hw_params_get_buffer_time_max (params, &max, NULL);

        GST_DEBUG_OBJECT (alsa, "buffer time %u, min %u, max %u",
                          alsa->buffer_time, min, max);

        snd_pcm_hw_params_get_period_time_min (params, &min, NULL);
        snd_pcm_hw_params_get_period_time_max (params, &max, NULL);

        GST_DEBUG_OBJECT (alsa, "period time %u, min %u, max %u",
                          alsa->period_time, min, max);

        snd_pcm_hw_params_get_periods_min (params, &min, NULL);
        snd_pcm_hw_params_get_periods_max (params, &max, NULL);

        GST_DEBUG_OBJECT (alsa, "periods min %u, max %u", min, max);
    }
#endif

    /* now try to configure the buffer time and period time, if one
     * of those fail, we fall back to the defaults and emit a warning. */
    if (buffer_time != -1 && !alsa->iec958) {
        /* set the buffer time */
        if ((err = snd_pcm_hw_params_set_buffer_time_near (alsa->handle, params,
                   &buffer_time, NULL)) < 0) {
            GST_ELEMENT_WARNING (alsa, RESOURCE, SETTINGS, (NULL),
                                 ("Unable to set buffer time %i for playback: %s",
                                  buffer_time, snd_strerror (err)));
            /* disable buffer_time the next round */
            buffer_time = -1;
            goto retry;
        }
        GST_DEBUG_OBJECT (alsa, "buffer time %u", buffer_time);
    }
    if (period_time != -1 && !alsa->iec958) {
        /* set the period time */
        if ((err = snd_pcm_hw_params_set_period_time_near (alsa->handle, params,
                   &period_time, NULL)) < 0) {
            GST_ELEMENT_WARNING (alsa, RESOURCE, SETTINGS, (NULL),
                                 ("Unable to set period time %i for playback: %s",
                                  period_time, snd_strerror (err)));
            /* disable period_time the next round */
            period_time = -1;
            goto retry;
        }
        GST_DEBUG_OBJECT (alsa, "period time %u", period_time);
    }

    /* Set buffer size and period size manually for SPDIF */
    if (G_UNLIKELY (alsa->iec958)) {
        snd_pcm_uframes_t buffer_size = SPDIF_BUFFER_SIZE;
        snd_pcm_uframes_t period_size = SPDIF_PERIOD_SIZE;

        CHECK (snd_pcm_hw_params_set_buffer_size_near (alsa->handle, params,
                &buffer_size), buffer_size);
        CHECK (snd_pcm_hw_params_set_period_size_near (alsa->handle, params,
                &period_size, NULL), period_size);
    }

    /* write the parameters to device */
    CHECK (snd_pcm_hw_params (alsa->handle, params), set_hw_params);

    /* now get the configured values */
    CHECK (snd_pcm_hw_params_get_buffer_size (params, &alsa->buffer_size),
           buffer_size);
    CHECK (snd_pcm_hw_params_get_period_size (params, &alsa->period_size, NULL),
           period_size);

    GST_DEBUG_OBJECT (alsa, "buffer size %lu, period size %lu", alsa->buffer_size,
                      alsa->period_size);

    snd_pcm_hw_params_free (params);
    return 0;

    /* ERRORS */
no_config:
    {
        GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL),
                           ("Broken configuration for playback: no configurations available: %s",
                            snd_strerror (err)));
        snd_pcm_hw_params_free (params);
        return err;
    }
wrong_access:
    {
        GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL),
                           ("Access type not available for playback: %s", snd_strerror (err)));
        snd_pcm_hw_params_free (params);
        return err;
    }
no_sample_format:
    {
        GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL),
                           ("Sample format not available for playback: %s", snd_strerror (err)));
        snd_pcm_hw_params_free (params);
        return err;
    }
no_channels:
    {
        gchar *msg = NULL;

        if ((alsa->channels) == 1)
            msg = g_strdup (_("Could not open device for playback in mono mode."));
        if ((alsa->channels) == 2)
            msg = g_strdup (_("Could not open device for playback in stereo mode."));
        if ((alsa->channels) > 2)
            msg =
                g_strdup_printf (_
                                 ("Could not open device for playback in %d-channel mode."),
                                 alsa->channels);
        GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, ("%s", msg),
                           ("%s", snd_strerror (err)));
        g_free (msg);
        snd_pcm_hw_params_free (params);
        return err;
    }
no_rate:
    {
        GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL),
                           ("Rate %iHz not available for playback: %s",
                            alsa->rate, snd_strerror (err)));
        return err;
    }
buffer_size:
    {
        GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL),
                           ("Unable to get buffer size for playback: %s", snd_strerror (err)));
        snd_pcm_hw_params_free (params);
        return err;
    }
period_size:
    {
        GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL),
                           ("Unable to get period size for playback: %s", snd_strerror (err)));
        snd_pcm_hw_params_free (params);
        return err;
    }
set_hw_params:
    {
        GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL),
                           ("Unable to set hw params for playback: %s", snd_strerror (err)));
        snd_pcm_hw_params_free (params);
        return err;
    }
}
Example #4
0
static int
set_hwparams (GstAlsaSrc * alsa)
{
  guint rrate;
  gint err;
  snd_pcm_hw_params_t *params;

  snd_pcm_hw_params_malloc (&params);

  /* choose all parameters */
  CHECK (snd_pcm_hw_params_any (alsa->handle, params), no_config);
  /* set the interleaved read/write format */
  CHECK (snd_pcm_hw_params_set_access (alsa->handle, params, alsa->access),
      wrong_access);
  /* set the sample format */
  CHECK (snd_pcm_hw_params_set_format (alsa->handle, params, alsa->format),
      no_sample_format);
  /* set the count of channels */
  CHECK (snd_pcm_hw_params_set_channels (alsa->handle, params, alsa->channels),
      no_channels);
  /* set the stream rate */
  rrate = alsa->rate;
  CHECK (snd_pcm_hw_params_set_rate_near (alsa->handle, params, &rrate, NULL),
      no_rate);
  if (rrate != alsa->rate)
    goto rate_match;

#ifndef GST_DISABLE_GST_DEBUG
  /* get and dump some limits */
  {
    guint min, max;

    snd_pcm_hw_params_get_buffer_time_min (params, &min, NULL);
    snd_pcm_hw_params_get_buffer_time_max (params, &max, NULL);

    GST_DEBUG_OBJECT (alsa, "buffer time %u, min %u, max %u",
        alsa->buffer_time, min, max);

    snd_pcm_hw_params_get_period_time_min (params, &min, NULL);
    snd_pcm_hw_params_get_period_time_max (params, &max, NULL);

    GST_DEBUG_OBJECT (alsa, "period time %u, min %u, max %u",
        alsa->period_time, min, max);

    snd_pcm_hw_params_get_periods_min (params, &min, NULL);
    snd_pcm_hw_params_get_periods_max (params, &max, NULL);

    GST_DEBUG_OBJECT (alsa, "periods min %u, max %u", min, max);
  }
#endif

  if (alsa->buffer_time != -1) {
    /* set the buffer time */
    CHECK (snd_pcm_hw_params_set_buffer_time_near (alsa->handle, params,
            &alsa->buffer_time, NULL), buffer_time);
    GST_DEBUG_OBJECT (alsa, "buffer time %u", alsa->buffer_time);
  }
  if (alsa->period_time != -1) {
    /* set the period time */
    CHECK (snd_pcm_hw_params_set_period_time_near (alsa->handle, params,
            &alsa->period_time, NULL), period_time);
    GST_DEBUG_OBJECT (alsa, "period time %u", alsa->period_time);
  }

  /* write the parameters to device */
  CHECK (snd_pcm_hw_params (alsa->handle, params), set_hw_params);

  CHECK (snd_pcm_hw_params_get_buffer_size (params, &alsa->buffer_size),
      buffer_size);

  CHECK (snd_pcm_hw_params_get_period_size (params, &alsa->period_size, NULL),
      period_size);

  snd_pcm_hw_params_free (params);
  return 0;

  /* ERRORS */
no_config:
  {
    GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL),
        ("Broken configuration for recording: no configurations available: %s",
            snd_strerror (err)));
    snd_pcm_hw_params_free (params);
    return err;
  }
wrong_access:
  {
    GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL),
        ("Access type not available for recording: %s", snd_strerror (err)));
    snd_pcm_hw_params_free (params);
    return err;
  }
no_sample_format:
  {
    GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL),
        ("Sample format not available for recording: %s", snd_strerror (err)));
    snd_pcm_hw_params_free (params);
    return err;
  }
no_channels:
  {
    gchar *msg = NULL;

    if ((alsa->channels) == 1)
      msg = g_strdup (_("Could not open device for recording in mono mode."));
    if ((alsa->channels) == 2)
      msg = g_strdup (_("Could not open device for recording in stereo mode."));
    if ((alsa->channels) > 2)
      msg =
          g_strdup_printf (_
          ("Could not open device for recording in %d-channel mode"),
          alsa->channels);
    GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, ("%s", msg),
        ("%s", snd_strerror (err)));
    g_free (msg);
    snd_pcm_hw_params_free (params);
    return err;
  }
no_rate:
  {
    GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL),
        ("Rate %iHz not available for recording: %s",
            alsa->rate, snd_strerror (err)));
    snd_pcm_hw_params_free (params);
    return err;
  }
rate_match:
  {
    GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL),
        ("Rate doesn't match (requested %iHz, get %iHz)", alsa->rate, err));
    snd_pcm_hw_params_free (params);
    return -EINVAL;
  }
buffer_time:
  {
    GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL),
        ("Unable to set buffer time %i for recording: %s",
            alsa->buffer_time, snd_strerror (err)));
    snd_pcm_hw_params_free (params);
    return err;
  }
buffer_size:
  {
    GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL),
        ("Unable to get buffer size for recording: %s", snd_strerror (err)));
    snd_pcm_hw_params_free (params);
    return err;
  }
period_time:
  {
    GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL),
        ("Unable to set period time %i for recording: %s", alsa->period_time,
            snd_strerror (err)));
    snd_pcm_hw_params_free (params);
    return err;
  }
period_size:
  {
    GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL),
        ("Unable to get period size for recording: %s", snd_strerror (err)));
    snd_pcm_hw_params_free (params);
    return err;
  }
set_hw_params:
  {
    GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL),
        ("Unable to set hw params for recording: %s", snd_strerror (err)));
    snd_pcm_hw_params_free (params);
    return err;
  }
}
bool CAESinkALSA::InitializeHW(const ALSAConfig &inconfig, ALSAConfig &outconfig)
{
  snd_pcm_hw_params_t *hw_params;

  snd_pcm_hw_params_alloca(&hw_params);
  memset(hw_params, 0, snd_pcm_hw_params_sizeof());

  snd_pcm_hw_params_any(m_pcm, hw_params);
  snd_pcm_hw_params_set_access(m_pcm, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);

  unsigned int sampleRate   = inconfig.sampleRate;
  unsigned int channelCount = inconfig.channels;
#if defined(HAS_AMLPLAYER) || defined(HAS_LIBAMCODEC)
  // alsa/kernel lies, so map everything to 44100 or 48000
  switch(sampleRate)
  {
    case 11025:
    case 22050:
    case 88200:
    case 176400:
      sampleRate = 44100;
      break;
    case 8000:
    case 16000:
    case 24000:
    case 32000:
    case 96000:
    case 192000:
    case 384000:
      sampleRate = 48000;
      break;
  }
#endif

  snd_pcm_hw_params_set_rate_near    (m_pcm, hw_params, &sampleRate, NULL);
  snd_pcm_hw_params_set_channels_near(m_pcm, hw_params, &channelCount);

  /* ensure we opened X channels or more */
  if (inconfig.channels > channelCount)
  {
    CLog::Log(LOGINFO, "CAESinkALSA::InitializeHW - Unable to open the required number of channels");
  }

  /* update outconfig */
  outconfig.channels = channelCount;

  snd_pcm_format_t fmt = AEFormatToALSAFormat(inconfig.format);
  outconfig.format = inconfig.format;

  if (fmt == SND_PCM_FORMAT_UNKNOWN)
  {
    /* if we dont support the requested format, fallback to float */
    fmt = SND_PCM_FORMAT_FLOAT;
    outconfig.format = AE_FMT_FLOAT;
  }

  /* try the data format */
  if (snd_pcm_hw_params_set_format(m_pcm, hw_params, fmt) < 0)
  {
    /* if the chosen format is not supported, try each one in decending order */
    CLog::Log(LOGINFO, "CAESinkALSA::InitializeHW - Your hardware does not support %s, trying other formats", CAEUtil::DataFormatToStr(outconfig.format));
    for (enum AEDataFormat i = AE_FMT_MAX; i > AE_FMT_INVALID; i = (enum AEDataFormat)((int)i - 1))
    {
      if (AE_IS_RAW(i) || i == AE_FMT_MAX)
        continue;

      if (m_passthrough && i != AE_FMT_S16BE && i != AE_FMT_S16LE)
	continue;

      fmt = AEFormatToALSAFormat(i);

      if (fmt == SND_PCM_FORMAT_UNKNOWN || snd_pcm_hw_params_set_format(m_pcm, hw_params, fmt) < 0)
      {
        fmt = SND_PCM_FORMAT_UNKNOWN;
        continue;
      }

      int fmtBits = CAEUtil::DataFormatToBits(i);
      int bits    = snd_pcm_hw_params_get_sbits(hw_params);
      if (bits != fmtBits)
      {
        /* if we opened in 32bit and only have 24bits, pack into 24 */
        if (fmtBits == 32 && bits == 24)
          i = AE_FMT_S24NE4;
        else
          continue;
      }

      /* record that the format fell back to X */
      outconfig.format = i;
      CLog::Log(LOGINFO, "CAESinkALSA::InitializeHW - Using data format %s", CAEUtil::DataFormatToStr(outconfig.format));
      break;
    }

    /* if we failed to find a valid output format */
    if (fmt == SND_PCM_FORMAT_UNKNOWN)
    {
      CLog::Log(LOGERROR, "CAESinkALSA::InitializeHW - Unable to find a suitable output format");
      return false;
    }
  }

  unsigned int periods;
  snd_pcm_uframes_t periodSize, bufferSize;

  snd_pcm_hw_params_get_periods_min(hw_params, &periods, NULL);
  snd_pcm_hw_params_get_period_size_min(hw_params, &periodSize, NULL);
  snd_pcm_hw_params_get_buffer_size_min(hw_params, &bufferSize);
  CLog::Log(LOGDEBUG, "CAESinkALSA::InitializeHW - Min: periodSize %lu, periods %u, bufferSize %lu", periodSize, periods, bufferSize);

  snd_pcm_hw_params_get_periods_max(hw_params, &periods, NULL);
  snd_pcm_hw_params_get_period_size_max(hw_params, &periodSize, NULL);
  snd_pcm_hw_params_get_buffer_size_max(hw_params, &bufferSize);
  CLog::Log(LOGDEBUG, "CAESinkALSA::InitializeHW - Max: periodSize %lu, periods %u, bufferSize %lu", periodSize, periods, bufferSize);

  snd_pcm_hw_params_get_buffer_size_max(hw_params, &bufferSize);
  snd_pcm_hw_params_get_period_size_max(hw_params, &periodSize, NULL);

  /* 
   We want to make sure, that we have max 200 ms Buffer with 
   a periodSize of approx 50 ms. Choosing a higher bufferSize
   will cause problems with menu sounds. Buffer will be increased
   after those are fixed.
  */
  periodSize  = std::min(periodSize, (snd_pcm_uframes_t) sampleRate / 20);
  bufferSize  = std::min(bufferSize, (snd_pcm_uframes_t) sampleRate / 5);
#if defined(HAS_AMLPLAYER) || defined(HAS_LIBAMCODEC)
  // must be pot for pivos.
  bufferSize  = CheckNP2(bufferSize);
#endif

  /*
   According to upstream we should set buffer size first - so make sure it is always at least
   4x period size to not get underruns (some systems seem to have issues with only 2 periods)
  */
  periodSize = std::min(periodSize, bufferSize / 4);
#if defined(HAS_AMLPLAYER) || defined(HAS_LIBAMCODEC)
  // must be pot for pivos.
  periodSize = CheckNP2(periodSize);
#endif

  bufferSize  = std::min(bufferSize, (snd_pcm_uframes_t)8192);
  periodSize  = bufferSize / ALSA_PERIODS;
  periods     = ALSA_PERIODS;

  CLog::Log(LOGDEBUG, "CAESinkALSA::InitializeHW - Req: periodSize %lu, periods %u, bufferSize %lu", periodSize, periods, bufferSize);

  snd_pcm_hw_params_t *hw_params_copy;
  snd_pcm_hw_params_alloca(&hw_params_copy);
  snd_pcm_hw_params_copy(hw_params_copy, hw_params); // copy what we have and is already working

  // Make sure to not initialize too large to not cause underruns
  snd_pcm_uframes_t periodSizeMax = bufferSize / 3;
  if(snd_pcm_hw_params_set_period_size_max(m_pcm, hw_params_copy, &periodSizeMax, NULL) != 0)
  {
    snd_pcm_hw_params_copy(hw_params_copy, hw_params); // restore working copy
    CLog::Log(LOGDEBUG, "CAESinkALSA::InitializeHW - Request: Failed to limit periodSize to %lu", periodSizeMax);
  }
  
  // first trying bufferSize, PeriodSize
  // for more info see here:
  // http://mailman.alsa-project.org/pipermail/alsa-devel/2009-September/021069.html
  // the last three tries are done as within pulseaudio

  // backup periodSize and bufferSize first. Restore them after every failed try
  snd_pcm_uframes_t periodSizeTemp, bufferSizeTemp;
  periodSizeTemp = periodSize;
  bufferSizeTemp = bufferSize;
  if (snd_pcm_hw_params_set_buffer_size_near(m_pcm, hw_params_copy, &bufferSize) != 0
    || snd_pcm_hw_params_set_period_size_near(m_pcm, hw_params_copy, &periodSize, NULL) != 0
    || snd_pcm_hw_params(m_pcm, hw_params_copy) != 0)
  {
    bufferSize = bufferSizeTemp;
    periodSize = periodSizeTemp;
    // retry with PeriodSize, bufferSize
    snd_pcm_hw_params_copy(hw_params_copy, hw_params); // restore working copy
    if (snd_pcm_hw_params_set_period_size_near(m_pcm, hw_params_copy, &periodSize, NULL) != 0
      || snd_pcm_hw_params_set_buffer_size_near(m_pcm, hw_params_copy, &bufferSize) != 0
      || snd_pcm_hw_params(m_pcm, hw_params_copy) != 0)
    {
      // try only periodSize
      periodSize = periodSizeTemp;
      snd_pcm_hw_params_copy(hw_params_copy, hw_params); // restore working copy
      if(snd_pcm_hw_params_set_period_size_near(m_pcm, hw_params_copy, &periodSize, NULL) != 0 
        || snd_pcm_hw_params(m_pcm, hw_params_copy) != 0)
      {
        // try only BufferSize
        bufferSize = bufferSizeTemp;
        snd_pcm_hw_params_copy(hw_params_copy, hw_params); // restory working copy
        if (snd_pcm_hw_params_set_buffer_size_near(m_pcm, hw_params_copy, &bufferSize) != 0
          || snd_pcm_hw_params(m_pcm, hw_params_copy) != 0)
        {
          // set default that Alsa would choose
          CLog::Log(LOGWARNING, "CAESinkAlsa::IntializeHW - Using default alsa values - set failed");
          if (snd_pcm_hw_params(m_pcm, hw_params) != 0)
          {
            CLog::Log(LOGDEBUG, "CAESinkALSA::InitializeHW - Could not init a valid sink");
            return false;
          }
        }
      }
      // reread values when alsa default was kept
      snd_pcm_get_params(m_pcm, &bufferSize, &periodSize);
    }
  }
  
  CLog::Log(LOGDEBUG, "CAESinkALSA::InitializeHW - Got: periodSize %lu, bufferSize %lu", periodSize, bufferSize);

  /* set the format parameters */
  outconfig.sampleRate   = sampleRate;
  outconfig.periodSize   = periodSize;
  outconfig.frameSize    = snd_pcm_frames_to_bytes(m_pcm, 1);

  m_bufferSize = (unsigned int)bufferSize;
  m_timeout    = std::ceil((double)(bufferSize * 1000) / (double)sampleRate);

  CLog::Log(LOGDEBUG, "CAESinkALSA::InitializeHW - Setting timeout to %d ms", m_timeout);

  return true;
}
void info(char *dev_name, snd_pcm_stream_t stream) {
  snd_pcm_hw_params_t *hw_params;
  int err;
  snd_pcm_t *handle;
  unsigned int max;
  unsigned int min;
  unsigned int val;
  unsigned  int dir;
  snd_pcm_uframes_t frames;

  if ((err = snd_pcm_open (&handle, dev_name, stream, 0)) < 0) {
    fprintf (stderr, "cannot open audio device %s (%s)\n", 
	     dev_name,
	     snd_strerror (err));
    return;
  }
		   
  if ((err = snd_pcm_hw_params_malloc (&hw_params)) < 0) {
    fprintf (stderr, "cannot allocate hardware parameter structure (%s)\n",
	     snd_strerror (err));
    exit (1);
  }
				 
  if ((err = snd_pcm_hw_params_any (handle, hw_params)) < 0) {
    fprintf (stderr, "cannot initialize hardware parameter structure (%s)\n",
	     snd_strerror (err));
    exit (1);
  }
  
  if ((err = snd_pcm_hw_params_get_channels_max(hw_params, &max)) < 0) {
    fprintf (stderr, "cannot  (%s)\n",
	     snd_strerror (err));
    exit (1);
  }
  printf("max channels %d\n", max);

  if ((err = snd_pcm_hw_params_get_channels_min(hw_params, &min)) < 0) {
    fprintf (stderr, "cannot get channel info  (%s)\n",
	     snd_strerror (err));
    exit (1);
  }
  printf("min channels %d\n", min);

  /*
  if ((err = snd_pcm_hw_params_get_sbits(hw_params)) < 0) {
      fprintf (stderr, "cannot get bits info  (%s)\n",
	       snd_strerror (err));
      exit (1);
  }
  printf("bits %d\n", err);
  */

  if ((err = snd_pcm_hw_params_get_rate_min(hw_params, &val, &dir)) < 0) {
    fprintf (stderr, "cannot get min rate (%s)\n",
	     snd_strerror (err));
    exit (1);
  }
  printf("min rate %d hz\n", val);

  if ((err = snd_pcm_hw_params_get_rate_max(hw_params, &val, &dir)) < 0) {
    fprintf (stderr, "cannot get max rate (%s)\n",
	     snd_strerror (err));
    exit (1);
  }
  printf("max rate %d hz\n", val);

  
  if ((err = snd_pcm_hw_params_get_period_time_min(hw_params, &val, &dir)) < 0) {
    fprintf (stderr, "cannot get min period time  (%s)\n",
	     snd_strerror (err));
    exit (1);
  }
  printf("min period time %d usecs\n", val);

  if ((err = snd_pcm_hw_params_get_period_time_max(hw_params, &val, &dir)) < 0) {
    fprintf (stderr, "cannot  get max period time  (%s)\n",
	     snd_strerror (err));
    exit (1);
  }
  printf("max period time %d usecs\n", val);

  if ((err = snd_pcm_hw_params_get_period_size_min(hw_params, &frames, &dir)) < 0) {
    fprintf (stderr, "cannot  get min period size  (%s)\n",
	     snd_strerror (err));
    exit (1);
  }
  printf("min period size in frames %d\n", frames);

  if ((err = snd_pcm_hw_params_get_period_size_max(hw_params, &frames, &dir)) < 0) {
    fprintf (stderr, "cannot  get max period size (%s)\n",
	     snd_strerror (err));
    exit (1);
  }
  printf("max period size in frames %d\n", frames);

  if ((err = snd_pcm_hw_params_get_periods_min(hw_params, &val, &dir)) < 0) {
    fprintf (stderr, "cannot  get min periods  (%s)\n",
	     snd_strerror (err));
    exit (1);
  }
  printf("min periods per buffer %d\n", val);

  if ((err = snd_pcm_hw_params_get_periods_max(hw_params, &val, &dir)) < 0) {
    fprintf (stderr, "cannot  get min periods (%s)\n",
	     snd_strerror (err));
    exit (1);
  }
  printf("max periods per buffer %d\n", val);

  if ((err = snd_pcm_hw_params_get_buffer_time_min(hw_params, &val, &dir)) < 0) {
    fprintf (stderr, "cannot get min buffer time (%s)\n",
	     snd_strerror (err));
    exit (1);
  }
  printf("min buffer time %d usecs\n", val);

  if ((err = snd_pcm_hw_params_get_buffer_time_max(hw_params, &val, &dir)) < 0) {
    fprintf (stderr, "cannot get max buffer time  (%s)\n",
	     snd_strerror (err));
    exit (1);
  }
  printf("max buffer time %d usecs\n", val);

  if ((err = snd_pcm_hw_params_get_buffer_size_min(hw_params, &frames)) < 0) {
    fprintf (stderr, "cannot get min buffer size (%s)\n",
	     snd_strerror (err));
    exit (1);
  }
  printf("min buffer size in frames %d\n", frames);

  if ((err = snd_pcm_hw_params_get_buffer_size_max(hw_params, &frames)) < 0) {
    fprintf (stderr, "cannot get max buffer size  (%s)\n",
	     snd_strerror (err));
    exit (1);
  }
  printf("max buffer size in frames %d\n", frames);
}
Example #7
0
bool AlsaSound::AlsaInit()
{
	unsigned int sample_rate = m_mixer->GetSampleRate();
	int err;
	int dir;
	snd_pcm_sw_params_t *swparams;
	snd_pcm_hw_params_t *hwparams;
	snd_pcm_uframes_t buffer_size,buffer_size_max;
	unsigned int periods;

	err = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0);
	if (err < 0)
	{
		ERROR_LOG(AUDIO, "Audio open error: %s\n", snd_strerror(err));
		return false;
	}

	snd_pcm_hw_params_alloca(&hwparams);

	err = snd_pcm_hw_params_any(handle, hwparams);
	if (err < 0)
	{
		ERROR_LOG(AUDIO, "Broken configuration for this PCM: %s\n", snd_strerror(err));
		return false;
	}

	err = snd_pcm_hw_params_set_access(handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED);
	if (err < 0)
	{
		ERROR_LOG(AUDIO, "Access type not available: %s\n", snd_strerror(err));
		return false;
	}

	err = snd_pcm_hw_params_set_format(handle, hwparams, SND_PCM_FORMAT_S16_LE);
	if (err < 0)
	{
		ERROR_LOG(AUDIO, "Sample format not available: %s\n", snd_strerror(err));
		return false;
	}

	dir = 0;
	err = snd_pcm_hw_params_set_rate_near(handle, hwparams, &sample_rate, &dir);
	if (err < 0)
	{
		ERROR_LOG(AUDIO, "Rate not available: %s\n", snd_strerror(err));
		return false;
	}

	err = snd_pcm_hw_params_set_channels(handle, hwparams, 2);
	if (err < 0)
	{
		ERROR_LOG(AUDIO, "Channels count not available: %s\n", snd_strerror(err));
		return false;
	}

	periods = BUFFER_SIZE_MAX / FRAME_COUNT_MIN;
	err = snd_pcm_hw_params_set_periods_max(handle, hwparams, &periods, &dir);
	if (err < 0)
	{
		ERROR_LOG(AUDIO, "Cannot set Minimum periods: %s\n", snd_strerror(err));
		return false;
	}

	buffer_size_max = BUFFER_SIZE_MAX;
	err = snd_pcm_hw_params_set_buffer_size_max(handle, hwparams, &buffer_size_max);
	if (err < 0)
	{
		ERROR_LOG(AUDIO, "Cannot set minimum buffer size: %s\n", snd_strerror(err));
		return false;
	}

	err = snd_pcm_hw_params(handle, hwparams);
	if (err < 0)
	{
		ERROR_LOG(AUDIO, "Unable to install hw params: %s\n", snd_strerror(err));
		return false;
	}

	err = snd_pcm_hw_params_get_buffer_size(hwparams, &buffer_size);
	if (err < 0)
	{
		ERROR_LOG(AUDIO, "Cannot get buffer size: %s\n", snd_strerror(err));
		return false;
	}

	err = snd_pcm_hw_params_get_periods_max(hwparams, &periods, &dir);
	if (err < 0)
	{
		ERROR_LOG(AUDIO, "Cannot get periods: %s\n", snd_strerror(err));
		return false;
	}

	//periods is the number of fragments alsa can wait for during one
	//buffer_size
	frames_to_deliver = buffer_size / periods;
	//limit the minimum size. pulseaudio advertises a minimum of 32 samples.
	if (frames_to_deliver < FRAME_COUNT_MIN)
		frames_to_deliver = FRAME_COUNT_MIN;
	//it is probably a bad idea to try to send more than one buffer of data
	if ((unsigned int)frames_to_deliver > buffer_size)
		frames_to_deliver = buffer_size;
	NOTICE_LOG(AUDIO, "ALSA gave us a %ld sample \"hardware\" buffer with %d periods. Will send %d samples per fragments.\n", buffer_size, periods, frames_to_deliver);

	snd_pcm_sw_params_alloca(&swparams);

	err = snd_pcm_sw_params_current(handle, swparams);
	if (err < 0)
	{
		ERROR_LOG(AUDIO, "cannot init sw params: %s\n", snd_strerror(err));
		return false;
	}

	err = snd_pcm_sw_params_set_start_threshold(handle, swparams, 0U);
	if (err < 0)
	{
		ERROR_LOG(AUDIO, "cannot set start thresh: %s\n", snd_strerror(err));
		return false;
	}

	err = snd_pcm_sw_params(handle, swparams);
	if (err < 0)
	{
		ERROR_LOG(AUDIO, "cannot set sw params: %s\n", snd_strerror(err));
		return false;
	}

	err = snd_pcm_prepare(handle);
	if (err < 0)
	{
		ERROR_LOG(AUDIO, "Unable to prepare: %s\n", snd_strerror(err));
		return false;
	}
	NOTICE_LOG(AUDIO, "ALSA successfully initialized.\n");
	return true;
}
bool S9xAlsaSoundDriver::open_device()
{
    int err;
    unsigned int periods = 8;
    unsigned int buffer_size = gui_config->sound_buffer_size * 1000;
    snd_pcm_sw_params_t *sw_params;
    snd_pcm_hw_params_t *hw_params;
    snd_pcm_uframes_t alsa_buffer_size, alsa_period_size;
    unsigned int min = 0;
    unsigned int max = 0;

    printf("ALSA sound driver initializing...\n");
    printf("    --> (Device: default)...\n");

    err = snd_pcm_open(&pcm,
                       "default",
                       SND_PCM_STREAM_PLAYBACK,
                       SND_PCM_NONBLOCK);

    if (err < 0)
    {
        goto fail;
    }

    printf("    --> (16-bit Stereo, %dhz, %d ms)...\n",
           Settings.SoundPlaybackRate,
           gui_config->sound_buffer_size);

    snd_pcm_hw_params_alloca(&hw_params);
    snd_pcm_hw_params_any(pcm, hw_params);
    snd_pcm_hw_params_set_format(pcm, hw_params, SND_PCM_FORMAT_S16);
    snd_pcm_hw_params_set_access(pcm, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
    snd_pcm_hw_params_set_rate_resample(pcm, hw_params, 0);
    snd_pcm_hw_params_set_channels(pcm, hw_params, 2);

    snd_pcm_hw_params_get_rate_min(hw_params, &min, NULL);
    snd_pcm_hw_params_get_rate_max(hw_params, &max, NULL);
    printf("    --> Available rates: %d to %d\n", min, max);
    if (Settings.SoundPlaybackRate > max && Settings.SoundPlaybackRate < min)
    {
        printf("        Rate %d not available. Using %d instead.\n", Settings.SoundPlaybackRate, max);
        Settings.SoundPlaybackRate = max;
    }
    snd_pcm_hw_params_set_rate_near(pcm, hw_params, &Settings.SoundPlaybackRate, NULL);

    snd_pcm_hw_params_get_buffer_time_min(hw_params, &min, NULL);
    snd_pcm_hw_params_get_buffer_time_max(hw_params, &max, NULL);
    printf("    --> Available buffer sizes: %dms to %dms\n", min / 1000, max / 1000);
    if (buffer_size < min && buffer_size > max)
    {
        printf("        Buffer size %dms not available. Using %d instead.\n", buffer_size / 1000, (min + max) / 2000);
        buffer_size = (min + max) / 2;
    }
    snd_pcm_hw_params_set_buffer_time_near(pcm, hw_params, &buffer_size, NULL);

    snd_pcm_hw_params_get_periods_min(hw_params, &min, NULL);
    snd_pcm_hw_params_get_periods_max(hw_params, &max, NULL);
    printf("    --> Period ranges: %d to %d blocks\n", min, max);
    if (periods > max)
    {
        periods = max;
    }
    snd_pcm_hw_params_set_periods_near(pcm, hw_params, &periods, NULL);

    if ((err = snd_pcm_hw_params(pcm, hw_params)) < 0)
    {
        printf("        Hardware parameter set failed.\n");
        goto close_fail;
    }

    snd_pcm_sw_params_alloca(&sw_params);
    snd_pcm_sw_params_current(pcm, sw_params);
    snd_pcm_get_params(pcm, &alsa_buffer_size, &alsa_period_size);
    /* Don't start until we're [nearly] full */
    snd_pcm_sw_params_set_start_threshold(pcm, sw_params, (alsa_buffer_size / 2));
    err = snd_pcm_sw_params(pcm, sw_params);

    output_buffer_size = snd_pcm_frames_to_bytes(pcm, alsa_buffer_size);

    if (err < 0)
    {
        printf("        Software parameter set failed.\n");
        goto close_fail;
    }

    printf("OK\n");

    S9xSetSamplesAvailableCallback(alsa_samples_available, this);

    return true;

close_fail:
    snd_pcm_drain(pcm);
    snd_pcm_close(pcm);
    pcm = NULL;

fail:
    printf("Failed: %s\n", snd_strerror(err));

    return false;
}