bool CALSAAudioCapabilities::ProbeDevice()
{
  if (allSampleRateTableSize > NUM_ELEMENTS_IN_ARRAY(m_samplingRates)) {
    error_message("Number of sample rates exceeds audio cap array");
    return false;
  }

  // Open PCM
  snd_pcm_t *pcm_handle;
  snd_pcm_stream_t stream = SND_PCM_STREAM_CAPTURE;
  snd_pcm_hw_params_t *hwparams;
  snd_pcm_hw_params_alloca(&hwparams);
	int err;
  if ((err= snd_pcm_open(&pcm_handle, m_deviceName, stream, SND_PCM_NONBLOCK)) < 0) {
    error_message("Failed to open %s: %s", m_deviceName, snd_strerror(err));
    return false;
  }
  m_canOpen = true;

  if ((err = snd_pcm_hw_params_any(pcm_handle, hwparams)) < 0) {
    error_message("Broken configuration for playback: no configurations available: %s\n", snd_strerror(err));
    return false;
  }

  // union of valid sampling rates for MP3 and AAC
  // for all possible sampling rates
  u_int8_t i;
  for (i = 0; i < allSampleRateTableSize; i++) {
    u_int32_t targetRate = allSampleRateTable[i];
    if (snd_pcm_hw_params_test_rate(pcm_handle, hwparams, targetRate, 0) != 0) {
      debug_message("audio device %s doesn't support sampling rate %u",
                    m_deviceName, targetRate);
      continue;
    }
    debug_message("sampling rate %u supported", targetRate);

    // valid sampling rate
    m_samplingRates[m_numSamplingRates++] = targetRate;
  }

  // zero out remaining sampling rate entries
  for (i = m_numSamplingRates; i < allSampleRateTableSize; i++) {
    m_samplingRates[i] = 0;
  }

  snd_pcm_close(pcm_handle);
  return true;
}
    /**
     *  Checks if sound card supports the chosen parameters.
     *
     *  @returns  true if hardware supports it
     *  @throws AudioOutputException - if device cannot be accessed
     */
    bool AudioOutputDeviceAlsa::HardwareParametersSupported(String card, uint channels, int samplerate, uint numfragments, uint fragmentsize) throw (AudioOutputException) {
        pcm_name = "hw:" + card;
        int err;
        if ((err = snd_pcm_open(&pcm_handle, pcm_name.c_str(), stream, SND_PCM_NONBLOCK)) < 0) {
            throw AudioOutputException(String("Error opening PCM device ") + pcm_name + ": " + snd_strerror(err));
        }
        snd_pcm_hw_params_alloca(&hwparams);
        if (snd_pcm_hw_params_any(pcm_handle, hwparams) < 0) {
            snd_pcm_close(pcm_handle);
            return false;
        }
        if (snd_pcm_hw_params_test_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) {
            snd_pcm_close(pcm_handle);
            return false;
        }
        #if WORDS_BIGENDIAN
        if (snd_pcm_hw_params_test_format(pcm_handle, hwparams, SND_PCM_FORMAT_S16_BE) < 0)
        #else // little endian
        if (snd_pcm_hw_params_test_format(pcm_handle, hwparams, SND_PCM_FORMAT_S16_LE) < 0)
        #endif
        {
            snd_pcm_close(pcm_handle);
            return false;
        }
        int dir = 0;
        if (snd_pcm_hw_params_test_rate(pcm_handle, hwparams, samplerate, dir) < 0) {
            snd_pcm_close(pcm_handle);
            return false;
        }
        if (snd_pcm_hw_params_test_channels(pcm_handle, hwparams, channels) < 0) {
            snd_pcm_close(pcm_handle);
            return false;
        }
        if (snd_pcm_hw_params_test_periods(pcm_handle, hwparams, numfragments, dir) < 0) {
            snd_pcm_close(pcm_handle);
            return false;
        }
        if (snd_pcm_hw_params_test_buffer_size(pcm_handle, hwparams, (fragmentsize * numfragments)) < 0) {
            snd_pcm_close(pcm_handle);
            return false;
        }

        snd_pcm_close(pcm_handle);
        return true;
    }
Beispiel #3
0
static void
as_setup_rate_combo (GawSndData *snd)
{
   unsigned int rate = snd->sparams->rate;
   GtkWidget *combo = snd->w_rate;
   snd_pcm_t *pcm;
   snd_pcm_hw_params_t *hwparams;
   char **p;
   unsigned int val;
   int i ;
   int err;

   for ( i = snd->n_rate ; i > 0; i-- ){
      gtk_combo_box_text_remove (GTK_COMBO_BOX_TEXT (combo), i - 1 );
   }
   snd->n_rate = 0;

   snd_pcm_hw_params_alloca(&hwparams);
   err = snd_pcm_open(&pcm, snd->sparams->device_name, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK);
   if (  err < 0 ) {
      return;
   }
   err = snd_pcm_hw_params_any(pcm, hwparams);
   if ( err < 0 ) {
      snd_pcm_close(pcm);
      return;
   }
      
   for ( p = as_rate_list, i = 0 ; *p ; p++, i++ ){
      val = (unsigned int) g_ascii_strtoull (  *p, NULL, 10 );
      if ( snd_pcm_hw_params_test_rate ( pcm, hwparams, val, 0 ) < 0 ){
	 continue;
      }
      gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), *p);
//      msg_dbg("as_rate %d '%s'", snd->n_rate, *p );
      snd->n_rate++;
      if ( val == rate ) {
	 gtk_combo_box_set_active (GTK_COMBO_BOX (combo), i);
      }
   }
   snd_pcm_close(pcm);
}
Beispiel #4
0
static void get_rate_range(Instance *pi, Range *range)
{
  int i;
  int rc;
  ALSAio_private *priv = (ALSAio_private *)pi;

  if (!priv->c.handle) {
    fprintf(stderr, "*** device is not open!\n");
    return;
  }

  Range_clear(range);
  range->type = RANGE_INT_ENUM;

  for (i=0; i < table_size(rates); i++) {
    rc = snd_pcm_hw_params_test_rate(priv->c.handle, priv->c.hwparams, rates[i], 0);
    if (rc == 0) {
      fprintf(stderr, "  rate %d is available\n", rates[i]);
      Array_append(range->int_enums, rates[i]);
    }
  }
}
Beispiel #5
0
bool test_open(const char *device, unsigned rates[]) {
	int err;
	snd_pcm_t *pcm;
	snd_pcm_hw_params_t *hw_params;
	hw_params = (snd_pcm_hw_params_t *) alloca(snd_pcm_hw_params_sizeof());
	memset(hw_params, 0, snd_pcm_hw_params_sizeof());

	// open device
	if ((err = snd_pcm_open(&pcm, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
		LOG_ERROR("playback open error: %s", snd_strerror(err));
		return false;
	}

	// get max params
	if ((err = snd_pcm_hw_params_any(pcm, hw_params)) < 0) {
		LOG_ERROR("hwparam init error: %s", snd_strerror(err));
		return false;
	}

	// find supported sample rates to enable client side resampling of non supported rates
	unsigned i, ind;
	unsigned ref[] TEST_RATES;

	for (i = 0, ind = 0; ref[i]; ++i) {
		if (snd_pcm_hw_params_test_rate(pcm, hw_params, ref[i], 0) == 0) {
			rates[ind++] = ref[i];
		}
	}

	if ((err = snd_pcm_close(pcm)) < 0) {
		LOG_ERROR("snd_pcm_close error: %s", snd_strerror(err));
		return false;
	}

	return true;
}
size_t stack_alsa_audio_device_list_outputs(StackAudioDeviceDesc **outputs)
{
	static const int common_sample_rates[] = {8000, 11025, 16000, 22050, 32000, 44100, 48000, 88200, 96000, 176400, 192000};
	static const size_t num_common_sample_rates = 11;

	// Initialise pulse audio
	if (!stack_init_alsa_audio())
	{
		// Failed to initialise, return NULL
		*outputs = NULL;
		return 0;	
	}

	// Get some hints	
	void **hints = NULL;
	int result = snd_device_name_hint(-1, "pcm", &hints);
	if (result != 0)
	{
		*outputs = NULL;
		return 0;
	}

	size_t alsa_device_count = 0, stack_device_count = 0;

	// Count how many devices we find
	for (size_t i = 0; hints[i] != NULL; i++)
	{
		alsa_device_count = i + 1;
	}

	// If there are no devices, return immediately
	if (alsa_device_count == 0)
	{
		snd_device_name_free_hint(hints);
		*outputs = NULL;
		return 0;
	}

	StackAudioDeviceDesc* devices = new StackAudioDeviceDesc[alsa_device_count];

	// Iterate over the devices and build information
	for (size_t alsa_device_idx = 0, stack_device_idx = 0; alsa_device_idx < alsa_device_count; alsa_device_idx++)
	{
		char *name = snd_device_name_get_hint(hints[alsa_device_idx], "NAME");
		char *desc = snd_device_name_get_hint(hints[alsa_device_idx], "DESC");

		// Open the device
		snd_pcm_t* pcm = NULL;
		if (snd_pcm_open(&pcm, name, SND_PCM_STREAM_PLAYBACK, 0) != 0)
		{
			free(name);
			free(desc);
			continue;
		}

		// Get parameters
		snd_pcm_hw_params_t* hw_params = NULL;
		snd_pcm_hw_params_malloc(&hw_params);
		snd_pcm_hw_params_any(pcm, hw_params);

		// Get minimum and maximum number of channels
		unsigned int min, max;
		snd_pcm_hw_params_get_channels_min(hw_params, &min);
		snd_pcm_hw_params_get_channels_max(hw_params, &max);

		// Limit the maximum to 32 channels, as some devices return "-1" channels
		if (max > 32)
		{
			max = 32;
		}

		// Store channel counts
		devices[stack_device_idx].min_channels = min;
		devices[stack_device_idx].max_channels = max;

		// Get minimum and maximum sample rates
		int dir;
		snd_pcm_hw_params_get_rate_min(hw_params, &min, &dir);
		snd_pcm_hw_params_get_rate_max(hw_params, &max, &dir);

		// Iterate over our common sample rates and see which ones are valid
		size_t sample_rate_count = 0;
		for (size_t common_rate_idx = 0; common_rate_idx < num_common_sample_rates; common_rate_idx++)
		{
			if (common_sample_rates[common_rate_idx] >= min && common_sample_rates[common_rate_idx] <= max)
			{
				if (snd_pcm_hw_params_test_rate(pcm, hw_params, common_sample_rates[common_rate_idx], 0) == 0)
				{
					sample_rate_count++;
				}
			}
		}

		// Store the sample rates
		devices[stack_device_idx].num_rates = sample_rate_count;
		if (sample_rate_count > 0)
		{
			devices[stack_device_idx].rates = new uint32_t[sample_rate_count];
			for (size_t rate_idx = 0, common_rate_idx = 0; common_rate_idx < num_common_sample_rates; common_rate_idx++)
			{
				if (common_sample_rates[common_rate_idx] >= min && common_sample_rates[common_rate_idx] <= max)
				{
					devices[stack_device_idx].rates[rate_idx] = common_sample_rates[common_rate_idx];
					rate_idx++;
				}
			}

		}
		else
		{
			devices[stack_device_idx].rates = NULL;
		}

		// Store the name and description
		devices[stack_device_idx].name = strdup(name);
		devices[stack_device_idx].desc = strdup(desc);

		// Tidy up
		free(name);
		free(desc);
		snd_pcm_hw_params_free(hw_params);
		snd_pcm_close(pcm);
		stack_device_count++;
		stack_device_idx++;
	}

	// Tidy up
	snd_device_name_free_hint(hints);

	// We can technically return an array that contains more elements than
	// we say (if a device fails to open, for example), but this is not a 
	// problem, as we only allocate and indeed free the internals of the
	// number we say, but we tidy up the whole array
	*outputs = devices;
	return stack_device_count;
}
Beispiel #7
0
void CAESinkALSA::EnumerateDevice(AEDeviceInfoList &list, const std::string &device, const std::string &description, snd_config_t *config)
{
  snd_pcm_t *pcmhandle = NULL;
  if (!OpenPCMDevice(device, "", ALSA_MAX_CHANNELS, &pcmhandle, config))
    return;

  snd_pcm_info_t *pcminfo;
  snd_pcm_info_alloca(&pcminfo);
  memset(pcminfo, 0, snd_pcm_info_sizeof());

  int err = snd_pcm_info(pcmhandle, pcminfo);
  if (err < 0)
  {
    CLog::Log(LOGINFO, "CAESinkALSA - Unable to get pcm_info for \"%s\"", device.c_str());
    snd_pcm_close(pcmhandle);
  }

  int cardNr = snd_pcm_info_get_card(pcminfo);

  CAEDeviceInfo info;
  info.m_deviceName = device;
  info.m_deviceType = AEDeviceTypeFromName(device);

  if (cardNr >= 0)
  {
    /* "HDA NVidia", "HDA Intel", "HDA ATI HDMI", "SB Live! 24-bit External", ... */
    char *cardName;
    if (snd_card_get_name(cardNr, &cardName) == 0)
      info.m_displayName = cardName;

    if (info.m_deviceType == AE_DEVTYPE_HDMI && info.m_displayName.size() > 5 &&
        info.m_displayName.substr(info.m_displayName.size()-5) == " HDMI")
    {
      /* We already know this is HDMI, strip it */
      info.m_displayName.erase(info.m_displayName.size()-5);
    }

    /* "CONEXANT Analog", "USB Audio", "HDMI 0", "ALC889 Digital" ... */
    std::string pcminfoName = snd_pcm_info_get_name(pcminfo);

    /*
     * Filter "USB Audio", in those cases snd_card_get_name() is more
     * meaningful already
     */
    if (pcminfoName != "USB Audio")
      info.m_displayNameExtra = pcminfoName;

    if (info.m_deviceType == AE_DEVTYPE_HDMI)
    {
      /* replace, this was likely "HDMI 0" */
      info.m_displayNameExtra = "HDMI";

      int dev = snd_pcm_info_get_device(pcminfo);

      if (dev >= 0)
      {
        /* lets see if we can get ELD info */

        snd_ctl_t *ctlhandle;
        std::stringstream sstr;
        sstr << "hw:" << cardNr;
        std::string strHwName = sstr.str();

        if (snd_ctl_open_lconf(&ctlhandle, strHwName.c_str(), 0, config) == 0)
        {
          snd_hctl_t *hctl;
          if (snd_hctl_open_ctl(&hctl, ctlhandle) == 0)
          {
            snd_hctl_load(hctl);
            bool badHDMI = false;
            if (!GetELD(hctl, dev, info, badHDMI))
              CLog::Log(LOGDEBUG, "CAESinkALSA - Unable to obtain ELD information for device \"%s\" (not supported by device, or kernel older than 3.2)",
                        device.c_str());

            /* snd_hctl_close also closes ctlhandle */
            snd_hctl_close(hctl);

            if (badHDMI)
            {
              /* 
               * Warn about disconnected devices, but keep them enabled 
               * Detection can go wrong on Intel, Nvidia and on all 
               * AMD (fglrx) hardware, so it is not safe to close those
               * handles
               */
              CLog::Log(LOGDEBUG, "CAESinkALSA - HDMI device \"%s\" may be unconnected (no ELD data)", device.c_str());
            }
          }
          else
          {
            snd_ctl_close(ctlhandle);
          }
        }
      }
    }
    else if (info.m_deviceType == AE_DEVTYPE_IEC958)
    {
      /* append instead of replace, pcminfoName is useful for S/PDIF */
      if (!info.m_displayNameExtra.empty())
        info.m_displayNameExtra += ' ';
      info.m_displayNameExtra += "S/PDIF";
    }
    else if (info.m_displayNameExtra.empty())
    {
      /* for USB audio, it gets a bit confusing as there is
       * - "SB Live! 24-bit External"
       * - "SB Live! 24-bit External, S/PDIF"
       * so add "Analog" qualifier to the first one */
      info.m_displayNameExtra = "Analog";
    }

    /* "default" is a device that will be used for all inputs, while
     * "@" will be mangled to front/default/surroundXX as necessary */
    if (device == "@" || device == "default")
    {
      /* Make it "Default (whatever)" */
      info.m_displayName = "Default (" + info.m_displayName + (info.m_displayNameExtra.empty() ? "" : " " + info.m_displayNameExtra + ")");
      info.m_displayNameExtra = "";
    }

  }
  else
  {
    /* virtual devices: "default", "pulse", ... */
    /* description can be e.g. "PulseAudio Sound Server" - for hw devices it is
     * normally uninteresting, like "HDMI Audio Output" or "Default Audio Device",
     * so we only use it for virtual devices that have no better display name */
    info.m_displayName = description;
  }

  snd_pcm_hw_params_t *hwparams;
  snd_pcm_hw_params_alloca(&hwparams);
  memset(hwparams, 0, snd_pcm_hw_params_sizeof());

  /* ensure we can get a playback configuration for the device */
  if (snd_pcm_hw_params_any(pcmhandle, hwparams) < 0)
  {
    CLog::Log(LOGINFO, "CAESinkALSA - No playback configurations available for device \"%s\"", device.c_str());
    snd_pcm_close(pcmhandle);
    return;
  }

  /* detect the available sample rates */
  for (unsigned int *rate = ALSASampleRateList; *rate != 0; ++rate)
    if (snd_pcm_hw_params_test_rate(pcmhandle, hwparams, *rate, 0) >= 0)
      info.m_sampleRates.push_back(*rate);

  /* detect the channels available */
  int channels = 0;
  for (int i = ALSA_MAX_CHANNELS; i >= 1; --i)
  {
    /* Reopen the device if needed on the special "surroundXX" cases */
    if (info.m_deviceType == AE_DEVTYPE_PCM && (i == 8 || i == 6 || i == 4))
      OpenPCMDevice(device, "", i, &pcmhandle, config);

    if (snd_pcm_hw_params_test_channels(pcmhandle, hwparams, i) >= 0)
    {
      channels = i;
      break;
    }
  }

  if (device == "default" && channels == 2)
  {
    /* This looks like the ALSA standard default stereo dmix device, we
     * probably want to use "@" instead to get surroundXX. */
    snd_pcm_close(pcmhandle);
    EnumerateDevice(list, "@", description, config);
    return;
  }

  CAEChannelInfo alsaChannels;
  for (int i = 0; i < channels; ++i)
  {
    if (!info.m_channels.HasChannel(ALSAChannelMap[i]))
      info.m_channels += ALSAChannelMap[i];
    alsaChannels += ALSAChannelMap[i];
  }

  /* remove the channels from m_channels that we cant use */
  info.m_channels.ResolveChannels(alsaChannels);

  /* detect the PCM sample formats that are available */
  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;
    snd_pcm_format_t fmt = AEFormatToALSAFormat(i);
    if (fmt == SND_PCM_FORMAT_UNKNOWN)
      continue;

    if (snd_pcm_hw_params_test_format(pcmhandle, hwparams, fmt) >= 0)
      info.m_dataFormats.push_back(i);
  }

  snd_pcm_close(pcmhandle);
  list.push_back(info);
}
Beispiel #8
0
bool QAudioDeviceInfoInternal::testSettings(const QAudioFormat& format) const
{
    // Set nearest to closest settings that do work.
    // See if what is in settings will work (return value).
    int err = 0;
    snd_pcm_t* handle;
    snd_pcm_hw_params_t *params;
    QString dev = device;

    QList<QByteArray> devices = QAudioDeviceInfoInternal::availableDevices(QAudio::AudioOutput);

    if(dev.compare(QLatin1String("default")) == 0) {
#if(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14)
        dev = QLatin1String(devices.first().constData());
#else
        dev = QLatin1String("hw:0,0");
#endif
    } else {
#if(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14)
        dev = device;
#else
        int idx = 0;
        char *name;

        QString shortName = device.mid(device.indexOf(QLatin1String("="),0)+1);

        while(snd_card_get_name(idx,&name) == 0) {
            if(shortName.compare(QLatin1String(name)) == 0)
                break;
            idx++;
        }
        dev = QString(QLatin1String("hw:%1,0")).arg(idx);
#endif
    }
    if(mode == QAudio::AudioOutput) {
        err=snd_pcm_open( &handle,dev.toLocal8Bit().constData(),SND_PCM_STREAM_PLAYBACK,0);
    } else {
        err=snd_pcm_open( &handle,dev.toLocal8Bit().constData(),SND_PCM_STREAM_CAPTURE,0);
    }
    if(err < 0) {
        handle = 0;
        return false;
    }

    bool testChannel = false;
    bool testCodec = false;
    bool testFreq = false;
    bool testType = false;
    bool testSize = false;

    int  dir = 0;

    snd_pcm_nonblock( handle, 0 );
    snd_pcm_hw_params_alloca( &params );
    snd_pcm_hw_params_any( handle, params );

    // set the values!
    snd_pcm_hw_params_set_channels(handle,params,format.channels());
    snd_pcm_hw_params_set_rate(handle,params,format.frequency(),dir);

    err = -1;

    switch(format.sampleSize()) {
        case 8:
            if(format.sampleType() == QAudioFormat::SignedInt)
                err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S8);
            else if(format.sampleType() == QAudioFormat::UnSignedInt)
                err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U8);
            break;
        case 16:
            if(format.sampleType() == QAudioFormat::SignedInt) {
                if(format.byteOrder() == QAudioFormat::LittleEndian)
                    err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S16_LE);
                else if(format.byteOrder() == QAudioFormat::BigEndian)
                    err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S16_BE);
            } else if(format.sampleType() == QAudioFormat::UnSignedInt) {
                if(format.byteOrder() == QAudioFormat::LittleEndian)
                    err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U16_LE);
                else if(format.byteOrder() == QAudioFormat::BigEndian)
                    err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U16_BE);
            }
            break;
        case 32:
            if(format.sampleType() == QAudioFormat::SignedInt) {
                if(format.byteOrder() == QAudioFormat::LittleEndian)
                    err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S32_LE);
                else if(format.byteOrder() == QAudioFormat::BigEndian)
                    err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S32_BE);
            } else if(format.sampleType() == QAudioFormat::UnSignedInt) {
                if(format.byteOrder() == QAudioFormat::LittleEndian)
                    err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U32_LE);
                else if(format.byteOrder() == QAudioFormat::BigEndian)
                    err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U32_BE);
            }
    }

    // For now, just accept only audio/pcm codec
    if(!format.codec().startsWith(QLatin1String("audio/pcm"))) {
        err=-1;
    } else
        testCodec = true;

    if(err>=0 && format.channels() != -1) {
        err = snd_pcm_hw_params_test_channels(handle,params,format.channels());
        if(err>=0)
            err = snd_pcm_hw_params_set_channels(handle,params,format.channels());
        if(err>=0)
            testChannel = true;
    }

    if(err>=0 && format.frequency() != -1) {
        err = snd_pcm_hw_params_test_rate(handle,params,format.frequency(),0);
        if(err>=0)
            err = snd_pcm_hw_params_set_rate(handle,params,format.frequency(),dir);
        if(err>=0)
            testFreq = true;
    }

    if((err>=0 && format.sampleSize() != -1) &&
            (format.sampleType() != QAudioFormat::Unknown)) {
        switch(format.sampleSize()) {
            case 8:
                if(format.sampleType() == QAudioFormat::SignedInt)
                    err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S8);
                else if(format.sampleType() == QAudioFormat::UnSignedInt)
                    err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U8);
                break;
            case 16:
                if(format.sampleType() == QAudioFormat::SignedInt) {
                    if(format.byteOrder() == QAudioFormat::LittleEndian)
                        err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S16_LE);
                    else if(format.byteOrder() == QAudioFormat::BigEndian)
                        err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S16_BE);
                } else if(format.sampleType() == QAudioFormat::UnSignedInt) {
                    if(format.byteOrder() == QAudioFormat::LittleEndian)
                        err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U16_LE);
                    else if(format.byteOrder() == QAudioFormat::BigEndian)
                        err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U16_BE);
                }
                break;
            case 32:
                if(format.sampleType() == QAudioFormat::SignedInt) {
                    if(format.byteOrder() == QAudioFormat::LittleEndian)
                        err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S32_LE);
                    else if(format.byteOrder() == QAudioFormat::BigEndian)
                        err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S32_BE);
                } else if(format.sampleType() == QAudioFormat::UnSignedInt) {
                    if(format.byteOrder() == QAudioFormat::LittleEndian)
                        err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U32_LE);
                    else if(format.byteOrder() == QAudioFormat::BigEndian)
                        err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U32_BE);
                }
        }
        if(err>=0) {
            testSize = true;
            testType = true;
        }
    }
    if(err>=0)
        err = snd_pcm_hw_params(handle, params);

    if(err == 0) {
        // settings work
        // close()
        if(handle)
            snd_pcm_close(handle);
        return true;
    }
    if(handle)
        snd_pcm_close(handle);

    return false;
}
bool QAudioDeviceInfoInternal::testSettings(const QAudioFormat& format) const
{
    // Set nearest to closest settings that do work.
    // See if what is in settings will work (return value).
    int err = 0;
    snd_pcm_t* handle;
    snd_pcm_hw_params_t *params;
    QString dev = device;

    // open()
    if(!dev.contains(QLatin1String("default"))) {
        int idx = snd_card_get_index(dev.toLocal8Bit().constData());
        dev = QString(QLatin1String("hw:%1,0")).arg(idx);
    }
    if(mode == QAudio::AudioOutput) {
        err=snd_pcm_open( &handle,dev.toLocal8Bit().constData(),SND_PCM_STREAM_PLAYBACK,0);
    } else {
        err=snd_pcm_open( &handle,dev.toLocal8Bit().constData(),SND_PCM_STREAM_CAPTURE,0);
    }
    if(err < 0) {
        handle = 0;
        return false;
    }

    bool testChannel = false;
    bool testCodec = false;
    bool testFreq = false;
    bool testType = false;
    bool testSize = false;

    int  dir = 0;

    snd_pcm_nonblock( handle, 0 );
    snd_pcm_hw_params_alloca( &params );
    snd_pcm_hw_params_any( handle, params );

    // set the values!
    snd_pcm_hw_params_set_channels(handle,params,format.channels());
    snd_pcm_hw_params_set_rate(handle,params,format.frequency(),dir);
    switch(format.sampleSize()) {
        case 8:
            if(format.sampleType() == QAudioFormat::SignedInt)
                snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S8);
            else if(format.sampleType() == QAudioFormat::UnSignedInt)
                snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U8);
            break;
        case 16:
            if(format.sampleType() == QAudioFormat::SignedInt) {
                if(format.byteOrder() == QAudioFormat::LittleEndian)
                    snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S16_LE);
                else if(format.byteOrder() == QAudioFormat::BigEndian)
                    snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S16_BE);
            } else if(format.sampleType() == QAudioFormat::UnSignedInt) {
                if(format.byteOrder() == QAudioFormat::LittleEndian)
                    snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U16_LE);
                else if(format.byteOrder() == QAudioFormat::BigEndian)
                    snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U16_BE);
            }
            break;
        case 32:
            if(format.sampleType() == QAudioFormat::SignedInt) {
                if(format.byteOrder() == QAudioFormat::LittleEndian)
                    snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S32_LE);
                else if(format.byteOrder() == QAudioFormat::BigEndian)
                    snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S32_BE);
            } else if(format.sampleType() == QAudioFormat::UnSignedInt) {
                if(format.byteOrder() == QAudioFormat::LittleEndian)
                    snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U32_LE);
                else if(format.byteOrder() == QAudioFormat::BigEndian)
                    snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U32_BE);
            }
    }

    // For now, just accept only audio/pcm codec
    if(!format.codec().startsWith(QLatin1String("audio/pcm"))) {
        err=-1;
    } else
        testCodec = true;

    if(err>=0 && format.channels() != -1) {
        err = snd_pcm_hw_params_test_channels(handle,params,format.channels());
        if(err>=0)
            err = snd_pcm_hw_params_set_channels(handle,params,format.channels());
        if(err>=0)
            testChannel = true;
    }

    if(err>=0 && format.frequency() != -1) {
        err = snd_pcm_hw_params_test_rate(handle,params,format.frequency(),0);
        if(err>=0)
            err = snd_pcm_hw_params_set_rate(handle,params,format.frequency(),dir);
        if(err>=0)
            testFreq = true;
    }

    if((err>=0 && format.sampleSize() != -1) &&
            (format.sampleType() != QAudioFormat::Unknown)) {
        switch(format.sampleSize()) {
            case 8:
                if(format.sampleType() == QAudioFormat::SignedInt)
                    err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S8);
                else if(format.sampleType() == QAudioFormat::UnSignedInt)
                    err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U8);
                break;
            case 16:
                if(format.sampleType() == QAudioFormat::SignedInt) {
                    if(format.byteOrder() == QAudioFormat::LittleEndian)
                        err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S16_LE);
                    else if(format.byteOrder() == QAudioFormat::BigEndian)
                        err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S16_BE);
                } else if(format.sampleType() == QAudioFormat::UnSignedInt) {
                    if(format.byteOrder() == QAudioFormat::LittleEndian)
                        err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U16_LE);
                    else if(format.byteOrder() == QAudioFormat::BigEndian)
                        err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U16_BE);
                }
                break;
            case 32:
                if(format.sampleType() == QAudioFormat::SignedInt) {
                    if(format.byteOrder() == QAudioFormat::LittleEndian)
                        err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S32_LE);
                    else if(format.byteOrder() == QAudioFormat::BigEndian)
                        err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S32_BE);
                } else if(format.sampleType() == QAudioFormat::UnSignedInt) {
                    if(format.byteOrder() == QAudioFormat::LittleEndian)
                        err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U32_LE);
                    else if(format.byteOrder() == QAudioFormat::BigEndian)
                        err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U32_BE);
                }
        }
        if(err>=0) {
            testSize = true;
            testType = true;
        }
    }
    if(err>=0)
        err = snd_pcm_hw_params(handle, params);

    if(err == 0) {
        // settings work
        // close()
        if(handle)
            snd_pcm_close(handle);
        return true;
    }
    if(handle)
        snd_pcm_close(handle);

    return false;
}
bool QAlsaAudioDeviceInfo::testSettings(const QAudioFormat& format) const
{
    // Set nearest to closest settings that do work.
    // See if what is in settings will work (return value).
    int err = -1;
    snd_pcm_t* pcmHandle;
    snd_pcm_hw_params_t *params;
    QString dev;

#if(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14)
    dev = device;
    if (dev.compare(QLatin1String("default")) == 0) {
        QList<QByteArray> devices = availableDevices(QAudio::AudioOutput);
        if (!devices.isEmpty())
            dev = QLatin1String(devices.first().constData());
    }
#else
    if (dev.compare(QLatin1String("default")) == 0) {
        dev = QLatin1String("hw:0,0");
    } else {
        int idx = 0;
        char *name;

        QString shortName = device.mid(device.indexOf(QLatin1String("="),0)+1);

        while(snd_card_get_name(idx,&name) == 0) {
            if(shortName.compare(QLatin1String(name)) == 0)
                break;
            idx++;
        }
        dev = QString(QLatin1String("hw:%1,0")).arg(idx);
    }
#endif

    snd_pcm_stream_t stream = mode == QAudio::AudioOutput
                            ? SND_PCM_STREAM_PLAYBACK : SND_PCM_STREAM_CAPTURE;

    if (snd_pcm_open(&pcmHandle, dev.toLocal8Bit().constData(), stream, 0) < 0)
        return false;

    snd_pcm_nonblock(pcmHandle, 0);
    snd_pcm_hw_params_alloca(&params);
    snd_pcm_hw_params_any(pcmHandle, params);

    // set the values!
    snd_pcm_hw_params_set_channels(pcmHandle, params, format.channelCount());
    snd_pcm_hw_params_set_rate(pcmHandle, params, format.sampleRate(), 0);

    snd_pcm_format_t pcmFormat = SND_PCM_FORMAT_UNKNOWN;
    switch (format.sampleSize()) {
    case 8:
        if (format.sampleType() == QAudioFormat::SignedInt)
            pcmFormat = SND_PCM_FORMAT_S8;
        else if (format.sampleType() == QAudioFormat::UnSignedInt)
            pcmFormat = SND_PCM_FORMAT_U8;
        break;
    case 16:
        if (format.sampleType() == QAudioFormat::SignedInt) {
            pcmFormat = format.byteOrder() == QAudioFormat::LittleEndian
                      ? SND_PCM_FORMAT_S16_LE : SND_PCM_FORMAT_S16_BE;
        } else if (format.sampleType() == QAudioFormat::UnSignedInt) {
            pcmFormat = format.byteOrder() == QAudioFormat::LittleEndian
                      ? SND_PCM_FORMAT_U16_LE : SND_PCM_FORMAT_U16_BE;
        }
        break;
    case 32:
        if (format.sampleType() == QAudioFormat::SignedInt) {
            pcmFormat = format.byteOrder() == QAudioFormat::LittleEndian
                      ? SND_PCM_FORMAT_S32_LE : SND_PCM_FORMAT_S32_BE;
        } else if (format.sampleType() == QAudioFormat::UnSignedInt) {
            pcmFormat = format.byteOrder() == QAudioFormat::LittleEndian
                      ? SND_PCM_FORMAT_U32_LE : SND_PCM_FORMAT_U32_BE;
        } else if (format.sampleType() == QAudioFormat::Float) {
            pcmFormat = format.byteOrder() == QAudioFormat::LittleEndian
                      ? SND_PCM_FORMAT_FLOAT_LE : SND_PCM_FORMAT_FLOAT_BE;
        }
    }

    if (pcmFormat != SND_PCM_FORMAT_UNKNOWN)
        err = snd_pcm_hw_params_set_format(pcmHandle, params, pcmFormat);

    // For now, just accept only audio/pcm codec
    if (!format.codec().startsWith(QLatin1String("audio/pcm")))
        err = -1;

    if (err >= 0 && format.channelCount() != -1) {
        err = snd_pcm_hw_params_test_channels(pcmHandle, params, format.channelCount());
        if (err >= 0)
            err = snd_pcm_hw_params_set_channels(pcmHandle, params, format.channelCount());
    }

    if (err >= 0 && format.sampleRate() != -1) {
        err = snd_pcm_hw_params_test_rate(pcmHandle, params, format.sampleRate(), 0);
        if (err >= 0)
            err = snd_pcm_hw_params_set_rate(pcmHandle, params, format.sampleRate(), 0);
    }

    if (err >= 0 && pcmFormat != SND_PCM_FORMAT_UNKNOWN)
        err = snd_pcm_hw_params_set_format(pcmHandle, params, pcmFormat);

    if (err >= 0)
        err = snd_pcm_hw_params(pcmHandle, params);

    snd_pcm_close(pcmHandle);

    return (err == 0);
}
Beispiel #11
0
int main(int argc, char *argv[])
{
  const char *device_name = "hw";
  snd_pcm_t *pcm;
  snd_pcm_hw_params_t *hw_params;
  unsigned int i;
  unsigned int min, max;
  int any_rate;
  int err;

  if (argc > 1)
    device_name = argv[1];

  err = snd_pcm_open(&pcm, device_name, SND_PCM_STREAM_CAPTURE, 
		     SND_PCM_NONBLOCK);
  if (err < 0) {
    fprintf(stderr, "cannot open device '%s': %s\n", device_name, 
	    snd_strerror(err));
    return 1;
  }

  snd_pcm_hw_params_alloca(&hw_params);
  err = snd_pcm_hw_params_any(pcm, hw_params);
  if (err < 0) {
    fprintf(stderr, "cannot get hardware parameters: %s\n", 
	    snd_strerror(err));
    snd_pcm_close(pcm);
    return 1;
  }

  printf("Device: %s (type: %s)\n", device_name, 
	 snd_pcm_type_name(snd_pcm_type(pcm)));

  printf("Access types:");
  for (i = 0; i < ARRAY_SIZE(accesses); ++i) {
    if (!snd_pcm_hw_params_test_access(pcm, hw_params, accesses[i]))
      printf(" %s", snd_pcm_access_name(accesses[i]));
  }
  putchar('\n');

  printf("Formats:");
  for (i = 0; i < ARRAY_SIZE(formats); ++i) {
    if (!snd_pcm_hw_params_test_format(pcm, hw_params, formats[i]))
      printf(" %s", snd_pcm_format_name(formats[i]));
  }
  putchar('\n');

  err = snd_pcm_hw_params_get_channels_min(hw_params, &min);
  if (err < 0) {
    fprintf(stderr, "cannot get minimum channels count: %s\n", 
	    snd_strerror(err));
    snd_pcm_close(pcm);
    return 1;
  }
  err = snd_pcm_hw_params_get_channels_max(hw_params, &max);
  if (err < 0) {
    fprintf(stderr, "cannot get maximum channels count: %s\n", 
	    snd_strerror(err));
    snd_pcm_close(pcm);
    return 1;
  }
  printf("Channels:");
  for (i = min; i <= max; ++i) {
    if (!snd_pcm_hw_params_test_channels(pcm, hw_params, i))
      printf(" %u", i);
  }
  putchar('\n');

  err = snd_pcm_hw_params_get_rate_min(hw_params, &min, NULL);
  if (err < 0) {
    fprintf(stderr, "cannot get minimum rate: %s\n", 
	    snd_strerror(err));
    snd_pcm_close(pcm);
    return 1;
  }
  err = snd_pcm_hw_params_get_rate_max(hw_params, &max, NULL);
  if (err < 0) {
    fprintf(stderr, "cannot get maximum rate: %s\n", 
	    snd_strerror(err));
    snd_pcm_close(pcm);
    return 1;
  }
  printf("Sample rates:");
  if (min == max)
    printf(" %u", min);
  else if (!snd_pcm_hw_params_test_rate(pcm, hw_params, min + 1, 0))
    printf(" %u-%u", min, max);
  else {
    any_rate = 0;
    for (i = 0; i < ARRAY_SIZE(rates); ++i) {
      if (!snd_pcm_hw_params_test_rate(pcm, hw_params, 
				       rates[i], 0)) {
	any_rate = 1;
	printf(" %u", rates[i]);
      }
    }
    if (!any_rate)
      printf(" %u-%u", min, max);
  }
  putchar('\n');

  err = snd_pcm_hw_params_get_period_time_min(hw_params, &min, NULL);
  if (err < 0) {
    fprintf(stderr, "cannot get minimum period time: %s\n", 
	    snd_strerror(err));
    snd_pcm_close(pcm);
    return 1;
  }
  err = snd_pcm_hw_params_get_period_time_max(hw_params, &max, NULL);
  if (err < 0) {
    fprintf(stderr, "cannot get maximum period time: %s\n", 
	    snd_strerror(err));
    snd_pcm_close(pcm);
    return 1;
  }
  printf("Interrupt interval: %u-%u us\n", min, max);

  err = snd_pcm_hw_params_get_buffer_time_min(hw_params, &min, NULL);
  if (err < 0) {
    fprintf(stderr, "cannot get minimum buffer time: %s\n", 
	    snd_strerror(err));
    snd_pcm_close(pcm);
    return 1;
  }
  err = snd_pcm_hw_params_get_buffer_time_max(hw_params, &max, NULL);
  if (err < 0) {
    fprintf(stderr, "cannot get maximum buffer time: %s\n", 
	    snd_strerror(err));
    snd_pcm_close(pcm);
    return 1;
  }
  printf("Buffer size: %u-%u us\n", min, max);

  snd_pcm_close(pcm);
  return 0;
}
Beispiel #12
0
int AlsaIO::Initialize(uint Channels, uint Samplerate, uint Fragments, uint FragmentSize, String Card) {
    this->uiChannels           = Channels;
    this->uiSamplerate         = Samplerate;
    this->uiMaxSamplesPerCycle = FragmentSize;
    this->bInterleaved         = true;

    if (HardwareParametersSupported(Channels, Samplerate, Fragments, FragmentSize)) {
        pcm_name = "hw:" + Card;
    }
    else {
        printf("Warning: your soundcard doesn't support chosen hardware parameters; ");
        printf("trying to compensate support lack with plughw...");
        fflush(stdout);
        pcm_name = "plughw:" + Card;
    }

    int err;

    snd_pcm_hw_params_alloca(&hwparams);  // Allocate the snd_pcm_hw_params_t structure on the stack.

    /* Open PCM. The last parameter of this function is the mode. */
    /* If this is set to 0, the standard mode is used. Possible   */
    /* other values are SND_PCM_NONBLOCK and SND_PCM_ASYNC.       */
    /* If SND_PCM_NONBLOCK is used, read / write access to the    */
    /* PCM device will return immediately. If SND_PCM_ASYNC is    */
    /* specified, SIGIO will be emitted whenever a period has     */
    /* been completely processed by the soundcard.                */
    if ((err = snd_pcm_open(&pcm_handle, pcm_name.c_str(), stream, 0)) < 0) {
        fprintf(stderr, "Error opening PCM device %s: %s\n", pcm_name.c_str(), snd_strerror(err));
        return -1;
    }

    if ((err = snd_pcm_hw_params_any(pcm_handle, hwparams)) < 0) {
        fprintf(stderr, "Error, cannot initialize hardware parameter structure: %s.\n", snd_strerror(err));
        return -1;
    }

    /* Set access type. This can be either    */
    /* SND_PCM_ACCESS_RW_INTERLEAVED or       */
    /* SND_PCM_ACCESS_RW_NONINTERLEAVED.      */
    if ((err = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
        fprintf(stderr, "Error snd_pcm_hw_params_set_access: %s.\n", snd_strerror(err));
        return -1;
    }

    /* Set sample format */
    #if WORDS_BIGENDIAN
    if ((err = snd_pcm_hw_params_set_format(pcm_handle, hwparams, SND_PCM_FORMAT_S16_BE)) < 0) {
    #else // little endian
    if ((err = snd_pcm_hw_params_set_format(pcm_handle, hwparams, SND_PCM_FORMAT_S16_LE)) < 0) {
    #endif
        fprintf(stderr, "Error setting sample format. : %s\n", snd_strerror(err));
        return -1;
    }

    int dir = 0;

    /* Set sample rate. If the exact rate is not supported */
    /* by the hardware, use nearest possible rate.         */
    #if ALSA_MAJOR > 0
    if((err = snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &Samplerate, &dir)) < 0) {
    #else
    if((err = snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, Samplerate, &dir)) < 0) {
    #endif
        fprintf(stderr, "Error setting sample rate. : %s\n", snd_strerror(err));
        return -1;
    }

    if ((err = snd_pcm_hw_params_set_channels(pcm_handle, hwparams, Channels)) < 0) {
        fprintf(stderr, "Error setting number of channels. : %s\n", snd_strerror(err));
        return -1;
    }

    /* Set number of periods. Periods used to be called fragments. */
    if ((err = snd_pcm_hw_params_set_periods(pcm_handle, hwparams, Fragments, dir)) < 0) {
        fprintf(stderr, "Error setting number of periods. : %s\n", snd_strerror(err));
        return -1;
    }

    /* Set buffer size (in frames). The resulting latency is given by */
    /* latency = periodsize * periods / (rate * bytes_per_frame)     */
    if ((err = snd_pcm_hw_params_set_buffer_size(pcm_handle, hwparams, (FragmentSize * Fragments))) < 0) {
        fprintf(stderr, "Error setting buffersize. : %s\n", snd_strerror(err));
        return -1;
    }

    /* Apply HW parameter settings to */
    /* PCM device and prepare device  */
    if ((err = snd_pcm_hw_params(pcm_handle, hwparams)) < 0) {
        fprintf(stderr, "Error setting HW params. : %s\n", snd_strerror(err));
        return -1;
    }

    if (snd_pcm_sw_params_malloc(&swparams) != 0) {
        fprintf(stderr, "Error in snd_pcm_sw_params_malloc. : %s\n", snd_strerror(err));
        return -1;
    }

    if (snd_pcm_sw_params_current(pcm_handle, swparams) != 0) {
        fprintf(stderr, "Error in snd_pcm_sw_params_current. : %s\n", snd_strerror(err));
        return -1;
    }

    if (snd_pcm_sw_params_set_stop_threshold(pcm_handle, swparams, 0xffffffff) != 0) {
        fprintf(stderr, "Error in snd_pcm_sw_params_set_stop_threshold. : %s\n", snd_strerror(err));
        return -1;
    }

    if (snd_pcm_sw_params(pcm_handle, swparams) != 0) {
        fprintf(stderr, "Error in snd_pcm_sw_params. : %s\n", snd_strerror(err));
        return -1;
    }

    if ((err = snd_pcm_prepare(pcm_handle)) < 0) {
        fprintf(stderr, "Error snd_pcm_prepare : %s\n", snd_strerror(err));
        return -1;
    }

    // allocate the audio output buffer
    pOutputBuffer = new int16_t[Channels * FragmentSize];
    
    this->bInitialized = true;

    return 0;
}

/**
 *  Checks if sound card supports the chosen parameters.
 *
 *  @returns  true if hardware supports it
 */
bool AlsaIO::HardwareParametersSupported(uint channels, int samplerate, uint numfragments, uint fragmentsize) {
    pcm_name = "hw:0,0";
    if (snd_pcm_open(&pcm_handle, pcm_name.c_str(), stream, 0) < 0) return false;
    snd_pcm_hw_params_alloca(&hwparams);
    if (snd_pcm_hw_params_any(pcm_handle, hwparams) < 0) {
        snd_pcm_close(pcm_handle);
        return false;
    }
    if (snd_pcm_hw_params_test_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) {
        snd_pcm_close(pcm_handle);
        return false;
    }
    #if WORDS_BIGENDIAN
    if (snd_pcm_hw_params_test_format(pcm_handle, hwparams, SND_PCM_FORMAT_S16_BE) < 0) {
    #else // little endian
    if (snd_pcm_hw_params_test_format(pcm_handle, hwparams, SND_PCM_FORMAT_S16_LE) < 0) {
    #endif
        snd_pcm_close(pcm_handle);
        return false;
    }
    int dir = 0;
    if (snd_pcm_hw_params_test_rate(pcm_handle, hwparams, samplerate, dir) < 0) {
        snd_pcm_close(pcm_handle);
        return false;
    }
    if (snd_pcm_hw_params_test_channels(pcm_handle, hwparams, channels) < 0) {
        snd_pcm_close(pcm_handle);
        return false;
    }
    if (snd_pcm_hw_params_test_periods(pcm_handle, hwparams, numfragments, dir) < 0) {
        snd_pcm_close(pcm_handle);
        return false;
    }
    if (snd_pcm_hw_params_test_buffer_size(pcm_handle, hwparams, (fragmentsize * numfragments)) < 0) {
        snd_pcm_close(pcm_handle);
        return false;
    }

    snd_pcm_close(pcm_handle);
    return true;
}

void AlsaIO::Activate() {
    this->StartThread();
}

int AlsaIO::Main() {
    if (!pEngine) {
        fprintf(stderr, "AlsaIO: No Sampler Engine assigned, exiting.\n");
        exit(EXIT_FAILURE);
    }
    if (!bInitialized) {
        fprintf(stderr, "AlsaIO: Not yet intitialized, exiting.\n");
        exit(EXIT_FAILURE);
    }

    while (true) {

        // let the engine render audio for the current audio fragment
        pEngine->RenderAudio(uiMaxSamplesPerCycle);


        // check clipping in the audio sum, convert to sample_type
        // (from 32bit to 16bit sample) and copy to output buffer
        float sample_point; uint o = 0;
        for (uint s = 0; s < uiMaxSamplesPerCycle; s++) {
            for (uint c = 0; c < uiChannels; c++) {
                sample_point = pEngine->GetAudioSumBuffer(c)[s] * pEngine->Volume;
                if (sample_point < -32768.0) sample_point = -32768.0;
                if (sample_point >  32767.0) sample_point =  32767.0;
                this->pOutputBuffer[o++] = (int32_t) sample_point;
            }
        }


        // output sound
        int res = Output();
        if (res < 0) {
            fprintf(stderr, "AlsaIO: Audio output error, exiting.\n");
            exit(EXIT_FAILURE);
        }
    }
}

/**
 *  Will be called after every audio fragment cycle, to output the audio data
 *  of the current fragment to the soundcard.
 *
 *  @returns  0 on success
 */
int AlsaIO::Output() {
    int err = snd_pcm_writei(pcm_handle, pOutputBuffer, uiMaxSamplesPerCycle);
    if (err < 0) {
        fprintf(stderr, "Error snd_pcm_writei failed. : %s\n", snd_strerror(err));
        return -1;
    }
    return 0;
}

void AlsaIO::Close() {
    if (bInitialized) {
        //dmsg(0,("Stopping Alsa Thread..."));
        //StopThread();  //FIXME: commented out due to a bug in thread.cpp (StopThread() doesn't return at all)
        //dmsg(0,("OK\n"));
        if (pcm_handle) {
            //FIXME: currently commented out due to segfault
            //snd_pcm_close(pcm_handle);
            pcm_handle = NULL;
        }
        if (pOutputBuffer) {
            //FIXME: currently commented out due to segfault
            //delete[] pOutputBuffer;
            pOutputBuffer = NULL;
        }
        bInitialized = false;
    }
}

void* AlsaIO::GetInterleavedOutputBuffer() {
    return pOutputBuffer;
}

void* AlsaIO::GetChannelOutputBufer(uint Channel) {
    fprintf(stderr, "AlsaIO::GetChannelOutputBufer(): Only interleaved access allowed so far, exiting.\n");
    exit(EXIT_FAILURE);
    // just to avoid compiler warnings
    return NULL;
}
Beispiel #13
0
void CAESinkALSA::EnumerateDevicesEx(AEDeviceInfoList &list)
{
  /* ensure that ALSA has been initialized */
  if(!snd_config)
    snd_config_update();

  snd_ctl_t *ctlhandle;
  snd_pcm_t *pcmhandle;

  snd_ctl_card_info_t *ctlinfo;
  snd_ctl_card_info_alloca(&ctlinfo);
  memset(ctlinfo, 0, snd_ctl_card_info_sizeof());

  snd_pcm_hw_params_t *hwparams;
  snd_pcm_hw_params_alloca(&hwparams);
  memset(hwparams, 0, snd_pcm_hw_params_sizeof());

  snd_pcm_info_t *pcminfo;
  snd_pcm_info_alloca(&pcminfo);
  memset(pcminfo, 0, snd_pcm_info_sizeof());

  /* get the sound config */
  snd_config_t *config;
  snd_config_copy(&config, snd_config);

  std::string strHwName;
  int n_cards = -1;
  while (snd_card_next(&n_cards) == 0 && n_cards != -1)
  {
    std::stringstream sstr;
    sstr << "hw:" << n_cards;
    std::string strHwName = sstr.str();

    if (snd_ctl_open_lconf(&ctlhandle, strHwName.c_str(), 0, config) != 0)
    {
      CLog::Log(LOGDEBUG, "CAESinkALSA::EnumerateDevicesEx - Unable to open control for device %s", strHwName.c_str());
      continue;
    }

    if (snd_ctl_card_info(ctlhandle, ctlinfo) != 0)
    {
      CLog::Log(LOGDEBUG, "CAESinkALSA::EnumerateDevicesEx - Unable to get card control info for device %s", strHwName.c_str());
      snd_ctl_close(ctlhandle);
      continue;
    }

    snd_hctl_t *hctl;
    if (snd_hctl_open_ctl(&hctl, ctlhandle) != 0)
      hctl = NULL;
    snd_hctl_load(hctl);

    int pcm_index    = 0;
    int iec958_index = 0;
    int hdmi_index   = 0;

    int dev = -1;
    while (snd_ctl_pcm_next_device(ctlhandle, &dev) == 0 && dev != -1)
    {
      snd_pcm_info_set_device   (pcminfo, dev);
      snd_pcm_info_set_subdevice(pcminfo, 0  );
      snd_pcm_info_set_stream   (pcminfo, SND_PCM_STREAM_PLAYBACK);

      if (snd_ctl_pcm_info(ctlhandle, pcminfo) < 0)
      {
        CLog::Log(LOGDEBUG, "CAESinkALSA::EnumerateDevicesEx - Skipping device %s,%d as it does not have PCM playback ability", strHwName.c_str(), dev);
        continue;
      }

      int dev_index;
      sstr.str(std::string());
      CAEDeviceInfo info;
      std::string devname = snd_pcm_info_get_name(pcminfo);

      bool maybeHDMI = false;

      /* detect HDMI */
      if (devname.find("HDMI") != std::string::npos)
      { 
        info.m_deviceType = AE_DEVTYPE_HDMI;
        dev_index = hdmi_index++;
        sstr << "hdmi";
      }
      else
      {
        /* detect IEC958 */

        /* some HDMI devices (intel) report Digital for HDMI also */
        if (devname.find("Digital") != std::string::npos)
          maybeHDMI = true;

        if (maybeHDMI || devname.find("IEC958" ) != std::string::npos)
        {
          info.m_deviceType = AE_DEVTYPE_IEC958;
          dev_index = iec958_index; /* dont increment, it might be HDMI */
          sstr << "iec958";
        }
        else
        {
          info.m_deviceType = AE_DEVTYPE_PCM;
          dev_index = pcm_index++;
          sstr << "hw";
        }
      }

      /* build the driver string to pass to ALSA */
      sstr << ":CARD=" << snd_ctl_card_info_get_id(ctlinfo) << ",DEV=" << dev_index;
      info.m_deviceName = sstr.str();

      /* get the friendly display name*/
      info.m_displayName      = snd_ctl_card_info_get_name(ctlinfo);
      info.m_displayNameExtra = devname;

      /* open the device for testing */
      int err = snd_pcm_open_lconf(&pcmhandle, info.m_deviceName.c_str(), SND_PCM_STREAM_PLAYBACK, 0, config);

      /* if open of possible IEC958 failed and it could be HDMI, try as HDMI */
      if (err < 0 && maybeHDMI)
      {
        /* check for HDMI if it failed */
        sstr.str(std::string());
        dev_index = hdmi_index;

        sstr << "hdmi";
        sstr << ":CARD=" << snd_ctl_card_info_get_id(ctlinfo) << ",DEV=" << dev_index;
        info.m_deviceName = sstr.str();
        err = snd_pcm_open_lconf(&pcmhandle, info.m_deviceName.c_str(), SND_PCM_STREAM_PLAYBACK, 0, config);

        /* if it was valid, increment the index and set the type */
        if (err >= 0)
        {
          ++hdmi_index;
          info.m_deviceType = AE_DEVTYPE_HDMI;
        }
      }

      /* if it's still IEC958, increment the index */
      if (info.m_deviceType == AE_DEVTYPE_IEC958)
        ++iec958_index;

      /* final error check */
      if (err < 0)
      {
        CLog::Log(LOGINFO, "CAESinkALSA::EnumerateDevicesEx - Unable to open %s for capability detection", strHwName.c_str());
        continue;
      }

      /* see if we can get ELD for this device */
      if (info.m_deviceType == AE_DEVTYPE_HDMI)
      {
        bool badHDMI = false;
        if (hctl && !GetELD(hctl, dev, info, badHDMI))
          CLog::Log(LOGDEBUG, "CAESinkALSA::EnumerateDevicesEx - Unable to obtain ELD information for device %s, make sure you have ALSA >= 1.0.25", info.m_deviceName.c_str());

        if (badHDMI)
        {
          CLog::Log(LOGDEBUG, "CAESinkALSA::EnumerateDevicesEx - Skipping HDMI device %s as it has no ELD data", info.m_deviceName.c_str());
          continue;
        }
      }

      /* ensure we can get a playback configuration for the device */
      if (snd_pcm_hw_params_any(pcmhandle, hwparams) < 0)
      {
        CLog::Log(LOGINFO, "CAESinkALSA::EnumerateDevicesEx - No playback configurations available for device %s", info.m_deviceName.c_str());
        snd_pcm_close(pcmhandle);
        continue;
      }

      /* detect the available sample rates */
      for (unsigned int *rate = ALSASampleRateList; *rate != 0; ++rate)
        if (snd_pcm_hw_params_test_rate(pcmhandle, hwparams, *rate, 0) >= 0)
          info.m_sampleRates.push_back(*rate);

      /* detect the channels available */
      int channels = 0;
      for (int i = 1; i <= ALSA_MAX_CHANNELS; ++i)
        if (snd_pcm_hw_params_test_channels(pcmhandle, hwparams, i) >= 0)
          channels = i;

      CAEChannelInfo alsaChannels;
      for (int i = 0; i < channels; ++i)
      {
        if (!info.m_channels.HasChannel(ALSAChannelMap[i]))
          info.m_channels += ALSAChannelMap[i];
        alsaChannels += ALSAChannelMap[i];
      }

      /* remove the channels from m_channels that we cant use */
      info.m_channels.ResolveChannels(alsaChannels);

      /* detect the PCM sample formats that are available */
      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;
        snd_pcm_format_t fmt = AEFormatToALSAFormat(i);
        if (fmt == SND_PCM_FORMAT_UNKNOWN)
          continue;

        if (snd_pcm_hw_params_test_format(pcmhandle, hwparams, fmt) >= 0)
          info.m_dataFormats.push_back(i);
      }

      snd_pcm_close(pcmhandle);
      list.push_back(info);
    }

    /* snd_hctl_close also closes ctlhandle */
    if (hctl)
      snd_hctl_close(hctl);
    else
      snd_ctl_close(ctlhandle);
  }
}
bool AlsaBackend::ProbeParameters(DeviceInfo* device, StreamSpec& spec, snd_pcm_t* phandle, snd_pcm_stream_t stream, snd_pcm_info_t *pcminfo, char *name, snd_pcm_hw_params_t *params)
{
    // At this point, we just need to figure out the supported data
    // formats and sample rates.  We'll proceed by opening the device in
    // the direction with the maximum number of channels, or playback if
    // they are equal.  This might limit our sample rate options, but so
    // be it.

    int result;
    
    // FIXME: specify the direction in options beforehand?
    if (device->outMaxChannels >= device->inMaxChannels)
        stream = SND_PCM_STREAM_PLAYBACK;
    else
        stream = SND_PCM_STREAM_CAPTURE;
    snd_pcm_info_set_stream(pcminfo, stream);

    result = snd_pcm_open(&phandle, device->guid, stream, SND_PCM_ASYNC | SND_PCM_NONBLOCK);
    if (result < 0) {
        Log("snd_pcm_open error for device %s: %s", name, snd_strerror(result));
        Log("ProbeParameters failed!");
        return false;
    }

    // The device is open ... fill the parameter structure.
    result = snd_pcm_hw_params_any(phandle, params);
    if (result < 0) {
        snd_pcm_close(phandle);
        Log("snd_pcm_hw_params error for device %s: %s", name, snd_strerror(result));
        Log("ProbeParameters failed!");
        return false;
    }

    // Test if exact required rate is supported.
    if (snd_pcm_hw_params_test_rate(phandle, params, spec.rate, 0) == 0)
    {
        Log("Matching rate %d found", spec.rate);
    }
    else
    {
        // try to set rate near the requested one
        //.....
        Log("Matching rate NOT found.");
        unsigned int exactRate = spec.rate;
        result = snd_pcm_hw_params_set_rate_near(phandle, params, &exactRate, 0);
        if (result < 0) {
            snd_pcm_close(phandle);
            Log("no supported sample rates found for device %s: %s", name, snd_strerror(result));
            Log("ProbeParameters failed!");
            return false;
        }

        if (spec.rate != exactRate) {
            Log("The rate %d Hz is not supported by your hardware. Using %d Hz instead.", spec.rate, exactRate);
            spec.rate = exactRate;
        }
    }

    // Probe the supported data formats ... we don't care about endian-ness just yet
    // Check the requested format first and be happy if it works.
    snd_pcm_format_t format;
    device->supportedFormats = 0;
    format = FormatToAlsa(spec.format);
    if (snd_pcm_hw_params_test_format(phandle, params, format) == 0)
    {
        device->supportedFormats |= spec.format;
        snd_pcm_close(phandle);
        device->probed = true;
        return true;
    }

    // Check that we have at least one supported format
    if (device->supportedFormats == 0) {
        Log("pcm device %s data format is not supported, cannot open", name);
        Log("ProbeParameters failed!");
        return false;
    }

    // That's all ... close the device and return
    snd_pcm_close(phandle);
    device->probed = true;
    return true;
}