Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a79eee8d authored by Luke Ross's avatar Luke Ross Committed by Jaroslav Kysela
Browse files

[ALSA] Support for non-standard rates in USB audio driver



There's at least one USB audio chipset out there which supports only one
non-standard rate (ID 0e6a:0310 supports 46875Hz). There's a few other
patches for this card which are unsatisfactory because they attempt to
map this rate to 44.1k leading to sound distortion.
The patch below uses SNDRV_PCM_RATE_KNOT to properly support the
non-standard rates where they are available.

Signed-off-by: default avatarLuke Ross <luke@lukeross.name>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
Signed-off-by: default avatarJaroslav Kysela <perex@suse.cz>
parent 9f458e7f
Loading
Loading
Loading
Loading
+46 −0
Original line number Diff line number Diff line
@@ -123,6 +123,7 @@ struct audioformat {
	unsigned int rate_min, rate_max;	/* min/max rates */
	unsigned int nr_rates;		/* number of rate table entries */
	unsigned int *rate_table;	/* rate table */
	unsigned int needs_knot;	/* any unusual rates? */
};

struct snd_usb_substream;
@@ -1759,6 +1760,9 @@ static int check_hw_params_convention(struct snd_usb_substream *subs)
		}
		channels[f->format] |= (1 << f->channels);
		rates[f->format] |= f->rates;
		/* needs knot? */
		if (f->needs_knot)
			goto __out;
	}
	/* check whether channels and rates match for all formats */
	cmaster = rmaster = 0;
@@ -1799,6 +1803,38 @@ static int check_hw_params_convention(struct snd_usb_substream *subs)
	return err;
}

/*
 *  If the device supports unusual bit rates, does the request meet these?
 */
static int snd_usb_pcm_check_knot(struct snd_pcm_runtime *runtime,
				  struct snd_usb_substream *subs)
{
	struct list_head *p;
	struct snd_pcm_hw_constraint_list constraints_rates;
	int err;

	list_for_each(p, &subs->fmt_list) {
		struct audioformat *fp;
		fp = list_entry(p, struct audioformat, list);

		if (!fp->needs_knot)
			continue;

		constraints_rates.count = fp->nr_rates;
		constraints_rates.list = fp->rate_table;
		constraints_rates.mask = 0;

		err = snd_pcm_hw_constraint_list(runtime, 0,
			SNDRV_PCM_HW_PARAM_RATE,
			&constraints_rates);

		if (err < 0)
			return err;
	}

	return 0;
}


/*
 * set up the runtime hardware information.
@@ -1861,6 +1897,8 @@ static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substre
					       SNDRV_PCM_HW_PARAM_CHANNELS,
					       -1)) < 0)
			return err;
		if ((err = snd_usb_pcm_check_knot(runtime, subs)) < 0)
			return err;
	}
	return 0;
}
@@ -2406,6 +2444,7 @@ static int parse_audio_format_rates(struct snd_usb_audio *chip, struct audioform
				    unsigned char *fmt, int offset)
{
	int nr_rates = fmt[offset];
	int found;
	if (fmt[0] < offset + 1 + 3 * (nr_rates ? nr_rates : 2)) {
		snd_printk(KERN_ERR "%d:%u:%d : invalid FORMAT_TYPE desc\n",
				   chip->dev->devnum, fp->iface, fp->altsetting);
@@ -2428,6 +2467,7 @@ static int parse_audio_format_rates(struct snd_usb_audio *chip, struct audioform
			return -1;
		}

		fp->needs_knot = 0;
		fp->nr_rates = nr_rates;
		fp->rate_min = fp->rate_max = combine_triple(&fmt[8]);
		for (r = 0, idx = offset + 1; r < nr_rates; r++, idx += 3) {
@@ -2436,13 +2476,19 @@ static int parse_audio_format_rates(struct snd_usb_audio *chip, struct audioform
				fp->rate_min = rate;
			else if (rate > fp->rate_max)
				fp->rate_max = rate;
			found = 0;
			for (c = 0; c < (int)ARRAY_SIZE(conv_rates); c++) {
				if (rate == conv_rates[c]) {
					found = 1;
					fp->rates |= (1 << c);
					break;
				}
			}
			if (!found)
				fp->needs_knot = 1;
		}
		if (fp->needs_knot)
			fp->rates |= SNDRV_PCM_RATE_KNOT;
	} else {
		/* continuous rates */
		fp->rates = SNDRV_PCM_RATE_CONTINUOUS;