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

[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
...@@ -123,6 +123,7 @@ struct audioformat { ...@@ -123,6 +123,7 @@ struct audioformat {
unsigned int rate_min, rate_max; /* min/max rates */ unsigned int rate_min, rate_max; /* min/max rates */
unsigned int nr_rates; /* number of rate table entries */ unsigned int nr_rates; /* number of rate table entries */
unsigned int *rate_table; /* rate table */ unsigned int *rate_table; /* rate table */
unsigned int needs_knot; /* any unusual rates? */
}; };
struct snd_usb_substream; struct snd_usb_substream;
...@@ -1759,6 +1760,9 @@ static int check_hw_params_convention(struct snd_usb_substream *subs) ...@@ -1759,6 +1760,9 @@ static int check_hw_params_convention(struct snd_usb_substream *subs)
} }
channels[f->format] |= (1 << f->channels); channels[f->format] |= (1 << f->channels);
rates[f->format] |= f->rates; rates[f->format] |= f->rates;
/* needs knot? */
if (f->needs_knot)
goto __out;
} }
/* check whether channels and rates match for all formats */ /* check whether channels and rates match for all formats */
cmaster = rmaster = 0; cmaster = rmaster = 0;
...@@ -1799,6 +1803,38 @@ static int check_hw_params_convention(struct snd_usb_substream *subs) ...@@ -1799,6 +1803,38 @@ static int check_hw_params_convention(struct snd_usb_substream *subs)
return err; 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. * set up the runtime hardware information.
...@@ -1861,6 +1897,8 @@ static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substre ...@@ -1861,6 +1897,8 @@ static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substre
SNDRV_PCM_HW_PARAM_CHANNELS, SNDRV_PCM_HW_PARAM_CHANNELS,
-1)) < 0) -1)) < 0)
return err; return err;
if ((err = snd_usb_pcm_check_knot(runtime, subs)) < 0)
return err;
} }
return 0; return 0;
} }
...@@ -2406,6 +2444,7 @@ static int parse_audio_format_rates(struct snd_usb_audio *chip, struct audioform ...@@ -2406,6 +2444,7 @@ static int parse_audio_format_rates(struct snd_usb_audio *chip, struct audioform
unsigned char *fmt, int offset) unsigned char *fmt, int offset)
{ {
int nr_rates = fmt[offset]; int nr_rates = fmt[offset];
int found;
if (fmt[0] < offset + 1 + 3 * (nr_rates ? nr_rates : 2)) { if (fmt[0] < offset + 1 + 3 * (nr_rates ? nr_rates : 2)) {
snd_printk(KERN_ERR "%d:%u:%d : invalid FORMAT_TYPE desc\n", snd_printk(KERN_ERR "%d:%u:%d : invalid FORMAT_TYPE desc\n",
chip->dev->devnum, fp->iface, fp->altsetting); chip->dev->devnum, fp->iface, fp->altsetting);
...@@ -2428,6 +2467,7 @@ static int parse_audio_format_rates(struct snd_usb_audio *chip, struct audioform ...@@ -2428,6 +2467,7 @@ static int parse_audio_format_rates(struct snd_usb_audio *chip, struct audioform
return -1; return -1;
} }
fp->needs_knot = 0;
fp->nr_rates = nr_rates; fp->nr_rates = nr_rates;
fp->rate_min = fp->rate_max = combine_triple(&fmt[8]); fp->rate_min = fp->rate_max = combine_triple(&fmt[8]);
for (r = 0, idx = offset + 1; r < nr_rates; r++, idx += 3) { 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 ...@@ -2436,13 +2476,19 @@ static int parse_audio_format_rates(struct snd_usb_audio *chip, struct audioform
fp->rate_min = rate; fp->rate_min = rate;
else if (rate > fp->rate_max) else if (rate > fp->rate_max)
fp->rate_max = rate; fp->rate_max = rate;
found = 0;
for (c = 0; c < (int)ARRAY_SIZE(conv_rates); c++) { for (c = 0; c < (int)ARRAY_SIZE(conv_rates); c++) {
if (rate == conv_rates[c]) { if (rate == conv_rates[c]) {
found = 1;
fp->rates |= (1 << c); fp->rates |= (1 << c);
break; break;
} }
} }
if (!found)
fp->needs_knot = 1;
} }
if (fp->needs_knot)
fp->rates |= SNDRV_PCM_RATE_KNOT;
} else { } else {
/* continuous rates */ /* continuous rates */
fp->rates = SNDRV_PCM_RATE_CONTINUOUS; fp->rates = SNDRV_PCM_RATE_CONTINUOUS;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment