diff options
author | Luke Ross <luke@lukeross.name> | 2006-08-29 04:46:32 -0400 |
---|---|---|
committer | Jaroslav Kysela <perex@suse.cz> | 2006-09-23 04:44:58 -0400 |
commit | a79eee8d3d8a80c37d235e1181d67c3705c7bbfe (patch) | |
tree | 1a0618e6aba24c6ca10d5822bc08c03219d794ee /sound/usb | |
parent | 9f458e7fb5b92385d348fb6039ba7211a6d6ba6e (diff) |
[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: Luke Ross <luke@lukeross.name>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Jaroslav Kysela <perex@suse.cz>
Diffstat (limited to 'sound/usb')
-rw-r--r-- | sound/usb/usbaudio.c | 46 |
1 files changed, 46 insertions, 0 deletions
diff --git a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c index 087f9b64d8a0..664dd4c21e66 100644 --- a/sound/usb/usbaudio.c +++ b/sound/usb/usbaudio.c | |||
@@ -123,6 +123,7 @@ struct audioformat { | |||
123 | unsigned int rate_min, rate_max; /* min/max rates */ | 123 | unsigned int rate_min, rate_max; /* min/max rates */ |
124 | unsigned int nr_rates; /* number of rate table entries */ | 124 | unsigned int nr_rates; /* number of rate table entries */ |
125 | unsigned int *rate_table; /* rate table */ | 125 | unsigned int *rate_table; /* rate table */ |
126 | unsigned int needs_knot; /* any unusual rates? */ | ||
126 | }; | 127 | }; |
127 | 128 | ||
128 | struct snd_usb_substream; | 129 | struct snd_usb_substream; |
@@ -1759,6 +1760,9 @@ static int check_hw_params_convention(struct snd_usb_substream *subs) | |||
1759 | } | 1760 | } |
1760 | channels[f->format] |= (1 << f->channels); | 1761 | channels[f->format] |= (1 << f->channels); |
1761 | rates[f->format] |= f->rates; | 1762 | rates[f->format] |= f->rates; |
1763 | /* needs knot? */ | ||
1764 | if (f->needs_knot) | ||
1765 | goto __out; | ||
1762 | } | 1766 | } |
1763 | /* check whether channels and rates match for all formats */ | 1767 | /* check whether channels and rates match for all formats */ |
1764 | cmaster = rmaster = 0; | 1768 | cmaster = rmaster = 0; |
@@ -1799,6 +1803,38 @@ static int check_hw_params_convention(struct snd_usb_substream *subs) | |||
1799 | return err; | 1803 | return err; |
1800 | } | 1804 | } |
1801 | 1805 | ||
1806 | /* | ||
1807 | * If the device supports unusual bit rates, does the request meet these? | ||
1808 | */ | ||
1809 | static int snd_usb_pcm_check_knot(struct snd_pcm_runtime *runtime, | ||
1810 | struct snd_usb_substream *subs) | ||
1811 | { | ||
1812 | struct list_head *p; | ||
1813 | struct snd_pcm_hw_constraint_list constraints_rates; | ||
1814 | int err; | ||
1815 | |||
1816 | list_for_each(p, &subs->fmt_list) { | ||
1817 | struct audioformat *fp; | ||
1818 | fp = list_entry(p, struct audioformat, list); | ||
1819 | |||
1820 | if (!fp->needs_knot) | ||
1821 | continue; | ||
1822 | |||
1823 | constraints_rates.count = fp->nr_rates; | ||
1824 | constraints_rates.list = fp->rate_table; | ||
1825 | constraints_rates.mask = 0; | ||
1826 | |||
1827 | err = snd_pcm_hw_constraint_list(runtime, 0, | ||
1828 | SNDRV_PCM_HW_PARAM_RATE, | ||
1829 | &constraints_rates); | ||
1830 | |||
1831 | if (err < 0) | ||
1832 | return err; | ||
1833 | } | ||
1834 | |||
1835 | return 0; | ||
1836 | } | ||
1837 | |||
1802 | 1838 | ||
1803 | /* | 1839 | /* |
1804 | * set up the runtime hardware information. | 1840 | * 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 | SNDRV_PCM_HW_PARAM_CHANNELS, | 1897 | SNDRV_PCM_HW_PARAM_CHANNELS, |
1862 | -1)) < 0) | 1898 | -1)) < 0) |
1863 | return err; | 1899 | return err; |
1900 | if ((err = snd_usb_pcm_check_knot(runtime, subs)) < 0) | ||
1901 | return err; | ||
1864 | } | 1902 | } |
1865 | return 0; | 1903 | return 0; |
1866 | } | 1904 | } |
@@ -2406,6 +2444,7 @@ static int parse_audio_format_rates(struct snd_usb_audio *chip, struct audioform | |||
2406 | unsigned char *fmt, int offset) | 2444 | unsigned char *fmt, int offset) |
2407 | { | 2445 | { |
2408 | int nr_rates = fmt[offset]; | 2446 | int nr_rates = fmt[offset]; |
2447 | int found; | ||
2409 | if (fmt[0] < offset + 1 + 3 * (nr_rates ? nr_rates : 2)) { | 2448 | if (fmt[0] < offset + 1 + 3 * (nr_rates ? nr_rates : 2)) { |
2410 | snd_printk(KERN_ERR "%d:%u:%d : invalid FORMAT_TYPE desc\n", | 2449 | snd_printk(KERN_ERR "%d:%u:%d : invalid FORMAT_TYPE desc\n", |
2411 | chip->dev->devnum, fp->iface, fp->altsetting); | 2450 | 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 | return -1; | 2467 | return -1; |
2429 | } | 2468 | } |
2430 | 2469 | ||
2470 | fp->needs_knot = 0; | ||
2431 | fp->nr_rates = nr_rates; | 2471 | fp->nr_rates = nr_rates; |
2432 | fp->rate_min = fp->rate_max = combine_triple(&fmt[8]); | 2472 | fp->rate_min = fp->rate_max = combine_triple(&fmt[8]); |
2433 | for (r = 0, idx = offset + 1; r < nr_rates; r++, idx += 3) { | 2473 | 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 | fp->rate_min = rate; | 2476 | fp->rate_min = rate; |
2437 | else if (rate > fp->rate_max) | 2477 | else if (rate > fp->rate_max) |
2438 | fp->rate_max = rate; | 2478 | fp->rate_max = rate; |
2479 | found = 0; | ||
2439 | for (c = 0; c < (int)ARRAY_SIZE(conv_rates); c++) { | 2480 | for (c = 0; c < (int)ARRAY_SIZE(conv_rates); c++) { |
2440 | if (rate == conv_rates[c]) { | 2481 | if (rate == conv_rates[c]) { |
2482 | found = 1; | ||
2441 | fp->rates |= (1 << c); | 2483 | fp->rates |= (1 << c); |
2442 | break; | 2484 | break; |
2443 | } | 2485 | } |
2444 | } | 2486 | } |
2487 | if (!found) | ||
2488 | fp->needs_knot = 1; | ||
2445 | } | 2489 | } |
2490 | if (fp->needs_knot) | ||
2491 | fp->rates |= SNDRV_PCM_RATE_KNOT; | ||
2446 | } else { | 2492 | } else { |
2447 | /* continuous rates */ | 2493 | /* continuous rates */ |
2448 | fp->rates = SNDRV_PCM_RATE_CONTINUOUS; | 2494 | fp->rates = SNDRV_PCM_RATE_CONTINUOUS; |