From mboxrd@z Thu Jan 1 00:00:00 1970 From: Takashi Iwai Subject: Re: [Alsa-user] No sound on Extigy Date: Thu, 23 Jan 2003 14:23:44 +0100 Sender: alsa-devel-admin@lists.sourceforge.net Message-ID: References: Mime-Version: 1.0 (generated by SEMI 1.14.4 - "Hosorogi") Content-Type: multipart/mixed; boundary="Multipart_Thu_Jan_23_14:23:44_2003-1" Return-path: In-Reply-To: Errors-To: alsa-devel-admin@lists.sourceforge.net List-Help: List-Post: List-Subscribe: , List-Unsubscribe: , List-Archive: To: Clemens Ladisch Cc: Jean-Marc Valin , alsa-devel@lists.sourceforge.net List-Id: alsa-devel@alsa-project.org --Multipart_Thu_Jan_23_14:23:44_2003-1 Content-Type: text/plain; charset=US-ASCII (moved to alsa-devel up to now...) At Thu, 23 Jan 2003 11:33:50 +0100, I wrote: > > At Thu, 23 Jan 2003 11:26:02 +0100 (MET), > Clemens Ladisch wrote: > > > > The default output device used by aplay is "hw:0", which doesn't > > automatically convert sample rates. Try "aplay -D plughw:0 something.wav". > > i'm afraid that it doesn't work, too, because this configuration is > not handled well. anyway please try once. > > it's tough to solve... if the original usb-audio driver doesn't work with plughw like above, please try the attached patch. it will show many debug messages. you can suppress it by undefining HW_CONST_DEBUG at line 1167. Takashi --Multipart_Thu_Jan_23_14:23:44_2003-1 Content-Type: application/octet-stream Content-Disposition: attachment; filename="usb-const.dif" Content-Transfer-Encoding: 7bit Index: alsa-kernel/usb/usbaudio.c =================================================================== RCS file: /suse/tiwai/cvs/alsa/alsa-kernel/usb/usbaudio.c,v retrieving revision 1.38 diff -u -r1.38 usbaudio.c --- alsa-kernel/usb/usbaudio.c 22 Jan 2003 15:46:51 -0000 1.38 +++ alsa-kernel/usb/usbaudio.c 23 Jan 2003 13:19:44 -0000 @@ -36,7 +36,7 @@ #include #include #include -#include +#include #define SNDRV_GET_ID #include @@ -1161,12 +1161,243 @@ }; /* + * h/w constraints + */ + +#define HW_CONST_DEBUG + +static int hw_check_valid_format(snd_pcm_hw_params_t *params, struct audioformat *fp) +{ + snd_interval_t *it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + snd_interval_t *ct = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + snd_mask_t *fmts = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + + /* check the format */ + if (! snd_mask_test(fmts, fp->format)) + return 0; + /* check the channels */ + if (fp->channels < ct->min || fp->channels > ct->max) + return 0; + /* check the rate is within the range */ + if (fp->rate_min > it->max || (fp->rate_min == it->max && !it->openmax)) + return 0; + if (fp->rate_max < it->min || (fp->rate_max == it->min && !it->openmin)) + return 0; + return 1; +} + +static int hw_rule_rate(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + snd_usb_substream_t *subs = rule->private; + struct list_head *p; + snd_interval_t *it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + int rmin, rmax, changed; + +#ifdef HW_CONST_DEBUG + printk(KERN_DEBUG "hw_rule_rate: (%d,%d)\n", it->min, it->max); +#endif + changed = 0; + rmin = rmax = 0; + list_for_each(p, &subs->fmt_list) { + struct audioformat *fp; + fp = list_entry(p, struct audioformat, list); + if (! hw_check_valid_format(params, fp)) + continue; + if (changed++) { + if (rmin > fp->rate_min) + rmin = fp->rate_min; + if (rmax < fp->rate_max) + rmax = fp->rate_max; + } else { + rmin = fp->rate_min; + rmax = fp->rate_max; + } + } + + if (! changed) { +#ifdef HW_CONST_DEBUG + printk(KERN_DEBUG " --> get empty\n"); +#endif + it->empty = 1; + return -EINVAL; + } + + changed = 0; + if (it->min < rmin) { + it->min = rmin; + it->openmin = 0; + changed = 1; + } + if (it->max > rmax) { + it->max = rmax; + it->openmax = 0; + changed = 1; + } + if (snd_interval_checkempty(it)) { + it->empty = 1; + return -EINVAL; + } +#ifdef HW_CONST_DEBUG + printk(KERN_DEBUG " --> (%d, %d) (changed = %d)\n", it->min, it->max, changed); +#endif + return changed; +} + + +static int hw_rule_channels(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + snd_usb_substream_t *subs = rule->private; + struct list_head *p; + snd_interval_t *it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + int rmin, rmax, changed; + +#ifdef HW_CONST_DEBUG + printk(KERN_DEBUG "hw_rule_channels: (%d,%d)\n", it->min, it->max); +#endif + changed = 0; + rmin = rmax = 0; + list_for_each(p, &subs->fmt_list) { + struct audioformat *fp; + fp = list_entry(p, struct audioformat, list); + if (! hw_check_valid_format(params, fp)) + continue; + if (changed++) { + if (rmin > fp->channels) + rmin = fp->channels; + if (rmax < fp->channels) + rmax = fp->channels; + } else { + rmin = fp->channels; + rmax = fp->channels; + } + } + + if (! changed) { +#ifdef HW_CONST_DEBUG + printk(KERN_DEBUG " --> get empty\n"); +#endif + it->empty = 1; + return -EINVAL; + } + + changed = 0; + if (it->min < rmin) { + it->min = rmin; + it->openmin = 0; + changed = 1; + } + if (it->max > rmax) { + it->max = rmax; + it->openmax = 0; + changed = 1; + } + if (snd_interval_checkempty(it)) { + it->empty = 1; + return -EINVAL; + } +#ifdef HW_CONST_DEBUG + printk(KERN_DEBUG " --> (%d, %d) (changed = %d)\n", it->min, it->max, changed); +#endif + return changed; +} + +static int hw_rule_format(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + snd_usb_substream_t *subs = rule->private; + struct list_head *p; + snd_mask_t *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + u64 fbits; + u32 oldbits[2]; + int changed; + +#ifdef HW_CONST_DEBUG + printk(KERN_DEBUG "hw_rule_format: %x:%x\n", fmt->bits[0], fmt->bits[1]); +#endif + fbits = 0; + list_for_each(p, &subs->fmt_list) { + struct audioformat *fp; + fp = list_entry(p, struct audioformat, list); + if (! hw_check_valid_format(params, fp)) + continue; + fbits |= (1UL << fp->format); + } + + oldbits[0] = fmt->bits[0]; + oldbits[1] = fmt->bits[1]; + fmt->bits[0] &= (u32)fbits; + fmt->bits[1] &= (u32)(fbits >> 32); + if (! fmt->bits[0] && ! fmt->bits[1]) { +#ifdef HW_CONST_DEBUG + printk(KERN_DEBUG " --> get empty\n"); +#endif + return -EINVAL; + } + changed = (oldbits[0] != fmt->bits[0] || oldbits[1] != fmt->bits[1]); +#ifdef HW_CONST_DEBUG + printk(KERN_DEBUG " --> %x:%x (changed = %d)\n", fmt->bits[0], fmt->bits[1], changed); +#endif + return changed; +} + +/* + * check whether the registered audio formats need special hw-constraints + */ +static int check_hw_params_convention(snd_usb_substream_t *subs) +{ + int i; + u32 channels[64]; + u32 rates[64]; + u32 cmaster, rmaster; + struct list_head *p; + + memset(channels, 0, sizeof(channels)); + memset(rates, 0, sizeof(rates)); + + list_for_each(p, &subs->fmt_list) { + struct audioformat *f; + f = list_entry(p, struct audioformat, list); + /* uncoventional rates? */ + if (f->channels > 32 || (f->rates & SNDRV_PCM_RATE_KNOT)) + return 1; + /* combination of continuous rates and fixed rates? */ + if (rates[f->format] & SNDRV_PCM_RATE_CONTINUOUS) { + if (f->rates != rates[f->format]) + return 1; + } + if (f->rates & SNDRV_PCM_RATE_CONTINUOUS) { + if (rates[f->format] && rates[f->format] != f->rates) + return 1; + } + channels[f->format] |= (1 << f->channels); + rates[f->format] |= f->rates; + } + /* check whether channels and rates match for all formats */ + cmaster = rmaster = 0; + for (i = 0; i < 64; i++) { + if (cmaster != channels[i] && cmaster && channels[i]) + return 1; + if (rmaster != rates[i] && rmaster && rates[i]) + return 1; + if (channels[i]) + cmaster = channels[i]; + if (rates[i]) + rmaster = rates[i]; + } + return 0; +} + + +/* * set up the runtime hardware information. */ -static void setup_hw_info(snd_pcm_runtime_t *runtime, snd_usb_substream_t *subs) +static int setup_hw_info(snd_pcm_runtime_t *runtime, snd_usb_substream_t *subs) { struct list_head *p; + int err; runtime->hw.formats = subs->formats; @@ -1195,9 +1426,30 @@ 1000 * MIN_PACKS_URB, /*(NRPACKS * MAX_URBS) * 1000*/ UINT_MAX); - /* FIXME: we need more constraints to restrict the format type, - * channels and rates according to the audioformat list! - */ + if (check_hw_params_convention(subs)) { +#ifdef HW_CONST_DEBUG + printk(KERN_DEBUG "setting extra hw constraints...\n"); +#endif + if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + hw_rule_rate, subs, + SNDRV_PCM_HW_PARAM_FORMAT, + SNDRV_PCM_HW_PARAM_CHANNELS, + -1)) < 0) + return err; + if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + hw_rule_channels, subs, + SNDRV_PCM_HW_PARAM_FORMAT, + SNDRV_PCM_HW_PARAM_RATE, + -1)) < 0) + return err; + if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT, + hw_rule_format, subs, + SNDRV_PCM_HW_PARAM_RATE, + SNDRV_PCM_HW_PARAM_CHANNELS, + -1)) < 0) + return err; + } + return 0; } static int snd_usb_pcm_open(snd_pcm_substream_t *substream, int direction, @@ -1212,8 +1464,7 @@ runtime->hw = *hw; runtime->private_data = subs; subs->pcm_substream = substream; - setup_hw_info(runtime, subs); - return 0; + return setup_hw_info(runtime, subs); } static int snd_usb_pcm_close(snd_pcm_substream_t *substream, int direction) @@ -1821,10 +2072,8 @@ break; } } -#if 0 // FIXME - we need to define constraint - if (c >= 13) - fp->rates |= SNDRV_PCM_KNOT; /* unconventional rate */ -#endif + if (c >= 13) /* unconventional rate */ + fp->rates |= SNDRV_PCM_RATE_KNOT; } } else { --Multipart_Thu_Jan_23_14:23:44_2003-1-- ------------------------------------------------------- This SF.NET email is sponsored by: SourceForge Enterprise Edition + IBM + LinuxWorld = Something 2 See! http://www.vasoftware.com