[alsa-devel] Need expert's advice - Fast Track Ultra (8R) dropping samples
Hi,
in that past months I've been trying get the Fast Track Ultra devices working properly in Alsa. We've had lots of progress, most of the code has moved to Alsa git and today I've even posted a patch for getting mixer support for these devices.
Now, I need to get some expert's advice: The devices seem to drop samples or frames. Here's a report I've got today on the M-Audio forum:
"I've got a subtle problem to report: I think audio playback is dropping sample frames. To hear the problem, open Audacity at 48 kHz and play a 10-kHz. sine wave. When I do that I hear a regular clicking sound, about four clicks a second. I've tried recording the output and if I'm seeing correctly, exactly one sample frame in every 13312 (13x1024) is being dropped on output. I don't see anything similar on input. When either jack or Pd has both the input and the output open, the delay from input to output gradually decreases until it forces occasional sync errors. (I haven't tried this with audacity though.)"
(see http://forums.m-audio.com/showthread.php?714-Not-a-problem.-FastTrack-on-lin...)
I could reproduce it on my machines, even at 44.1 kHz. The clicking sound is very subtle, it goes unnoticed when not listening to pure sines without attention to clicks.
How can this be sorted out. Any ideas?
Kind regards,
Felix
I guess the right place to fix the issues mentioned in my original post is in snd_usb_init_substream() in urb.c:
case USB_ID(0x0763, 0x2081): /* M-Audio Fast Track Ultra 8R */ subs->ops.prepare_sync = prepare_playback_sync_urb; subs->ops.retire_sync = retire_playback_sync_urb;
I've tried combinations using the _hs and _emu versions of the retire.../prepare... functions. It either didn't change anything or made it worse. I guess we need _ftu versions.
Could somebody at least tell me how to find out what's exactly going wrong with the sync. Can I find something useful monitoring the urbs in Windows?
Please, help me. We're so very close to full support of these devices, but I don't know how to proceed.
Thanks in advance,
Felix
Am 25.09.2010 11:57, schrieb Felix Homann:
Hi,
in that past months I've been trying get the Fast Track Ultra devices working properly in Alsa. We've had lots of progress, most of the code has moved to Alsa git and today I've even posted a patch for getting mixer support for these devices.
Now, I need to get some expert's advice: The devices seem to drop samples or frames. Here's a report I've got today on the M-Audio forum:
"I've got a subtle problem to report: I think audio playback is dropping sample frames. To hear the problem, open Audacity at 48 kHz and play a 10-kHz. sine wave. When I do that I hear a regular clicking sound, about four clicks a second. I've tried recording the output and if I'm seeing correctly, exactly one sample frame in every 13312 (13x1024) is being dropped on output. I don't see anything similar on input. When either jack or Pd has both the input and the output open, the delay from input to output gradually decreases until it forces occasional sync errors. (I haven't tried this with audacity though.)"
(see http://forums.m-audio.com/showthread.php?714-Not-a-problem.-FastTrack-on-lin...)
I could reproduce it on my machines, even at 44.1 kHz. The clicking sound is very subtle, it goes unnoticed when not listening to pure sines without attention to clicks.
How can this be sorted out. Any ideas?
Kind regards,
Felix _______________________________________________ Alsa-devel mailing list Alsa-devel@alsa-project.org http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
Felix Homann wrote:
I guess the right place to fix the issues mentioned in my original post is in snd_usb_init_substream() in urb.c:
case USB_ID(0x0763, 0x2081): /* M-Audio Fast Track Ultra 8R */ subs->ops.prepare_sync = prepare_playback_sync_urb; subs->ops.retire_sync = retire_playback_sync_urb;
I've tried combinations using the _hs and _emu versions of the retire.../prepare... functions. It either didn't change anything or made it worse. I guess we need _ftu versions.
Could somebody at least tell me how to find out what's exactly going wrong with the sync.
Add an else to the inner if in retire_playback_sync_urb, and log the value of f.
The value received in this function should be the desired sample frequency, relative to the USB frame rate. There are different formats (therefore we have three functions); maybe the FTU uses a fourth one.
Regards, Clemens
Dear Clemens, dear Daniel,
thanks for your feedback!
Am 01.10.2010 18:29, schrieb Clemens Ladisch:
Add an else to the inner if in retire_playback_sync_urb, and log the value of f.
The value received in this function should be the desired sample frequency, relative to the USB frame rate. There are different formats (therefore we have three functions); maybe the FTU uses a fourth one.
Trying to log the value of f reveals that retire_playback_sync_urb() doesn't seem to be called at all. What does that mean?
Am 03.10.2010 12:02, schrieb Daniel Mack:
How is this device clocked? It could be the PLL can't get a valid link to its clock source and hence "jumps" once in awhile to compensate the drift. Is there any possibility to sync the device to some sort o digital input?
The device should be clocked internally. But it has an option to be clocked externally at least in Windows. I don't know how this is handled in Alsa's USB audio driver. (Could you please tell me?) Some time ago I logged (via usbmon) the USB packets switching the device on in Windows. Could these logs be useful? Either way, I'll try to connect it to another SPDIF device and look for differences.
If the clicks you hear are very regular, it could also be that some kind of buffer boundary is not handled properly. Do you have access to an USB hardware analyzer to check whether the dropout is in fact part of the USB data stream?
The clicks are absolutely regular. Unfortunately, I don't have access to an USB hardware analyzer. Is there anything else I can do?
Kind regards,
Felix
On Sun, Oct 03, 2010 at 01:50:50PM +0200, Felix Homann wrote:
Am 03.10.2010 12:02, schrieb Daniel Mack:
How is this device clocked? It could be the PLL can't get a valid link to its clock source and hence "jumps" once in awhile to compensate the drift. Is there any possibility to sync the device to some sort o digital input?
The device should be clocked internally. But it has an option to be clocked externally at least in Windows. I don't know how this is handled in Alsa's USB audio driver. (Could you please tell me?)
I assume the device implements UAC in version 1? In this case, clock switching is not part of the standard and will most probably need special handling in the driver (vendor specific requests).
For UAC2 though, there is a way to descrbibe avaliable clock sources, switches etc.
Some time ago I logged (via usbmon) the USB packets switching the device on in Windows. Could these logs be useful?
Switching the clock source you mean? Sure - code to generate these packets should be placed in handlers for a device-specific mixer control.
Either way, I'll try to connect it to another SPDIF device and look for differences.
The idea of wrng clocking is just an assumption though. Maybe it is totally unrelated. It's just that we had similar issues with PLLs going nuts on missing sync input.
If the clicks you hear are very regular, it could also be that some kind of buffer boundary is not handled properly. Do you have access to an USB hardware analyzer to check whether the dropout is in fact part of the USB data stream?
The clicks are absolutely regular. Unfortunately, I don't have access to an USB hardware analyzer. Is there anything else I can do?
Unless you touched critical parts of the PCM data generating code, it is unlikely that the dropouts are part of the USB data stream. A proper USB dump is still very helpful, though.
Daniel
Felix Homann wrote:
Am 01.10.2010 18:29, schrieb Clemens Ladisch:
Add an else to the inner if in retire_playback_sync_urb, and log the value of f.
The value received in this function should be the desired sample frequency, relative to the USB frame rate. There are different formats (therefore we have three functions); maybe the FTU uses a fourth one.
Trying to log the value of f reveals that retire_playback_sync_urb() doesn't seem to be called at all.
The entire function or that part of the code?
Anyway, please try the patch below, which makes the driver autodetect the feedback format. (The actual format can be seen in /proc/asound/cardX/stream0 while running.)
Alex, please test this with your widget.
Regards, Clemens
--- a/sound/usb/card.h +++ b/sound/usb/card.h @@ -62,12 +62,14 @@ struct snd_usb_substream { unsigned int syncinterval; /* P for adaptive mode, 0 otherwise */ unsigned int freqn; /* nominal sampling rate in fs/fps in Q16.16 format */ unsigned int freqm; /* momentary sampling rate in fs/fps in Q16.16 format */ + int freqshift; /* how much to shift the feedback value to get Q16.16 */ unsigned int freqmax; /* maximum sampling rate, used for buffer management */ unsigned int phase; /* phase accumulator */ unsigned int maxpacksize; /* max packet size in bytes */ unsigned int maxframesize; /* max packet size in frames */ unsigned int curpacksize; /* current packet size in bytes (for capture) */ unsigned int curframesize; /* current packet size in frames (for capture) */ + unsigned int syncmaxsize; /* sync endpoint packet size */ unsigned int fill_max: 1; /* fill max packet size always */ unsigned int txfr_quirk:1; /* allow sub-frame alignment */ unsigned int fmt_type; /* USB audio format type (1-3) */ --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -237,6 +237,7 @@ static int set_format(struct snd_usb_sub subs->datainterval = fmt->datainterval; subs->syncpipe = subs->syncinterval = 0; subs->maxpacksize = fmt->maxpacksize; + subs->syncmaxsize = 0; subs->fill_max = 0;
/* we need a sync pipe in async OUT or adaptive IN mode */ @@ -283,6 +284,7 @@ static int set_format(struct snd_usb_sub subs->syncinterval = get_endpoint(alts, 1)->bInterval - 1; else subs->syncinterval = 3; + subs->syncmaxsize = le16_to_cpu(get_endpoint(alts, 1)->wMaxPacketSize); }
/* always fill max packet size */ --- a/sound/usb/proc.c +++ b/sound/usb/proc.c @@ -132,6 +132,11 @@ static void proc_dump_substream_status(s ? get_full_speed_hz(subs->freqm) : get_high_speed_hz(subs->freqm), subs->freqm >> 16, subs->freqm & 0xffff); + if (subs->freqshift != INT_MIN) + snd_iprintf(buffer, " Feedback Format = %d.%d\n", + (subs->syncmaxsize > 3 ? 32 : 24) + - (16 - subs->freqshift), + 16 - subs->freqshift); } else { snd_iprintf(buffer, " Status: Stop\n"); } --- a/sound/usb/urb.c +++ b/sound/usb/urb.c @@ -225,6 +225,7 @@ int snd_usb_init_substream_urbs(struct s else subs->freqn = get_usb_high_speed_rate(rate); subs->freqm = subs->freqn; + subs->freqshift = INT_MIN; /* calculate max. frequency */ if (subs->maxpacksize) { /* whatever fits into a max. size packet */ @@ -513,11 +514,10 @@ static int retire_paused_capture_urb(str
/* - * prepare urb for full speed playback sync pipe + * prepare urb for playback sync pipe * * set up the offset and length to receive the current frequency. */ - static int prepare_playback_sync_urb(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *urb) @@ -525,103 +525,78 @@ static int prepare_playback_sync_urb(str struct snd_urb_ctx *ctx = urb->context;
urb->dev = ctx->subs->dev; /* we need to set this at each time */ - urb->iso_frame_desc[0].length = 3; + urb->iso_frame_desc[0].length = min(4u, ctx->subs->syncmaxsize); urb->iso_frame_desc[0].offset = 0; return 0; }
/* - * prepare urb for high speed playback sync pipe + * process after playback sync complete * - * set up the offset and length to receive the current frequency. - */ - -static int prepare_playback_sync_urb_hs(struct snd_usb_substream *subs, - struct snd_pcm_runtime *runtime, - struct urb *urb) -{ - struct snd_urb_ctx *ctx = urb->context; - - urb->dev = ctx->subs->dev; /* we need to set this at each time */ - urb->iso_frame_desc[0].length = 4; - urb->iso_frame_desc[0].offset = 0; - return 0; -} - -/* - * process after full speed playback sync complete - * - * retrieve the current 10.14 frequency from pipe, and set it. - * the value is referred in prepare_playback_urb(). + * Full speed devices report feedback values in 10.14 format as samples per + * frame, high speed devices in 16.16 format as samples per microframe. + * Because the Audio Class 1 spec was written before USB 2.0, many high speed + * devices use a wrong interpretation, some others use an entirely different + * format. Therefore, we cannot predict what format any particular device uses + * and must detect it automatically. */ static int retire_playback_sync_urb(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *urb) { unsigned int f; + int shift; unsigned long flags;
- if (urb->iso_frame_desc[0].status == 0 && - urb->iso_frame_desc[0].actual_length == 3) { - f = combine_triple((u8*)urb->transfer_buffer) << 2; - if (f >= subs->freqn - subs->freqn / 8 && f <= subs->freqmax) { - spin_lock_irqsave(&subs->lock, flags); - subs->freqm = f; - spin_unlock_irqrestore(&subs->lock, flags); - } - } - - return 0; -} - -/* - * process after high speed playback sync complete - * - * retrieve the current 12.13 frequency from pipe, and set it. - * the value is referred in prepare_playback_urb(). - */ -static int retire_playback_sync_urb_hs(struct snd_usb_substream *subs, - struct snd_pcm_runtime *runtime, - struct urb *urb) -{ - unsigned int f; - unsigned long flags; - - if (urb->iso_frame_desc[0].status == 0 && - urb->iso_frame_desc[0].actual_length == 4) { - f = combine_quad((u8*)urb->transfer_buffer) & 0x0fffffff; - if (f >= subs->freqn - subs->freqn / 8 && f <= subs->freqmax) { - spin_lock_irqsave(&subs->lock, flags); - subs->freqm = f; - spin_unlock_irqrestore(&subs->lock, flags); + if (urb->iso_frame_desc[0].status != 0 || + urb->iso_frame_desc[0].actual_length < 3) + return 0; + + f = le32_to_cpup((__force __le32 *)urb->transfer_buffer); + if (urb->iso_frame_desc[0].actual_length == 3) + f &= 0x00ffffff; + else + f &= 0x0fffffff; + if (f == 0) + return 0; + + if (unlikely(subs->freqshift == INT_MIN)) { + /* + * The first time we see a feedback value, determine its format + * by shifting it left or right until it matches the nominal + * frequency value. This assumes that the feedback does not + * differ from the nominal value more than +50 % or -25 %. + */ + shift = 0; + while (f < subs->freqn - subs->freqn / 4) { + f <<= 1; + shift++; + } + while (f > subs->freqn + subs->freqn / 2) { + f >>= 1; + shift--; } + subs->freqshift = shift; } + else if (subs->freqshift >= 0) + f <<= subs->freqshift; + else + f >>= -subs->freqshift;
- return 0; -} - -/* - * process after E-Mu 0202/0404/Tracker Pre high speed playback sync complete - * - * These devices return the number of samples per packet instead of the number - * of samples per microframe. - */ -static int retire_playback_sync_urb_hs_emu(struct snd_usb_substream *subs, - struct snd_pcm_runtime *runtime, - struct urb *urb) -{ - unsigned int f; - unsigned long flags; - - if (urb->iso_frame_desc[0].status == 0 && - urb->iso_frame_desc[0].actual_length == 4) { - f = combine_quad((u8*)urb->transfer_buffer) & 0x0fffffff; - f >>= subs->datainterval; - if (f >= subs->freqn - subs->freqn / 8 && f <= subs->freqmax) { - spin_lock_irqsave(&subs->lock, flags); - subs->freqm = f; - spin_unlock_irqrestore(&subs->lock, flags); - } + if (likely(f >= subs->freqn - subs->freqn / 8 && f <= subs->freqmax)) { + /* + * If the frequency looks valid, set it. + * This value is referred to in prepare_playback_urb(). + */ + spin_lock_irqsave(&subs->lock, flags); + subs->freqm = f; + spin_unlock_irqrestore(&subs->lock, flags); + } else { + /* + * Out of range; maybe the shift value is wrong. + * Reset it so that we autodetect again the next time. + */ + subs->freqshift = INT_MIN; }
return 0; @@ -878,21 +853,6 @@ static struct snd_urb_ops audio_urb_ops[ }, };
-static struct snd_urb_ops audio_urb_ops_high_speed[2] = { - { - .prepare = prepare_nodata_playback_urb, - .retire = retire_playback_urb, - .prepare_sync = prepare_playback_sync_urb_hs, - .retire_sync = retire_playback_sync_urb_hs, - }, - { - .prepare = prepare_capture_urb, - .retire = retire_capture_urb, - .prepare_sync = prepare_capture_sync_urb_hs, - .retire_sync = retire_capture_sync_urb, - }, -}; - /* * initialize the substream instance. */ @@ -909,23 +869,9 @@ void snd_usb_init_substream(struct snd_u subs->direction = stream; subs->dev = as->chip->dev; subs->txfr_quirk = as->chip->txfr_quirk; - if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL) { - subs->ops = audio_urb_ops[stream]; - } else { - subs->ops = audio_urb_ops_high_speed[stream]; - switch (as->chip->usb_id) { - case USB_ID(0x041e, 0x3f02): /* E-Mu 0202 USB */ - case USB_ID(0x041e, 0x3f04): /* E-Mu 0404 USB */ - case USB_ID(0x041e, 0x3f0a): /* E-Mu Tracker Pre */ - subs->ops.retire_sync = retire_playback_sync_urb_hs_emu; - break; - case USB_ID(0x0763, 0x2080): /* M-Audio Fast Track Ultra 8 */ - case USB_ID(0x0763, 0x2081): /* M-Audio Fast Track Ultra 8R */ - subs->ops.prepare_sync = prepare_playback_sync_urb; - subs->ops.retire_sync = retire_playback_sync_urb; - break; - } - } + subs->ops = audio_urb_ops[stream]; + if (snd_usb_get_speed(subs->dev) >= USB_SPEED_HIGH) + subs->ops.prepare_sync = prepare_capture_sync_urb_hs;
snd_usb_set_pcm_ops(as->pcm, stream);
Am 05.10.2010 09:21, schrieb Clemens Ladisch:
Felix Homann wrote:
Trying to log the value of f reveals that retire_playback_sync_urb() doesn't seem to be called at all.
The entire function or that part of the code?
The entire function. I had placed a printk at the very beginning of retire_playback_sync_urb() whose output doesn't show up.
Anyway, please try the patch below, which makes the driver autodetect the feedback format. (The actual format can be seen in /proc/asound/cardX/stream0 while running.)
Alex, please test this with your widget.
Here's what I get:
:~$ cat /proc/asound/card2/stream0 M-Audio Fast Track Ultra 8R at usb-0000:00:1a.7-4, high speed : USB Audio
Playback: Status: Running Interface = 1 Altset = 1 URBs = 3 [ 57 57 57 ] Packet Size = 312 Momentary freq = 48000 Hz (0x6.0000) Interface 1 Altset 1 Format: S24_3LE Channels: 8 Endpoint: 1 OUT (ADAPTIVE) Rates: 44100, 48000, 88200, 96000 Data packet interval: 125 us
Capture: Status: Running Interface = 2 Altset = 1 URBs = 8 [ 8 8 8 8 8 8 8 8 ] Packet Size = 312 Momentary freq = 48000 Hz (0x6.0000) Interface 2 Altset 1 Format: S24_3LE Channels: 8 Endpoint: 1 IN (ASYNC) Rates: 44100, 48000, 88200, 96000 Data packet interval: 125 us
Does it tell us anything?
Kind regards,
Felix
Felix Homann wrote:
Am 05.10.2010 09:21, schrieb Clemens Ladisch:
Felix Homann wrote:
Trying to log the value of f reveals that retire_playback_sync_urb() doesn't seem to be called at all.
The entire function or that part of the code?
The entire function. I had placed a printk at the very beginning of retire_playback_sync_urb() whose output doesn't show up.
Anyway, please try the patch below, which makes the driver autodetect the feedback format. (The actual format can be seen in /proc/asound/cardX/stream0 while running.)
Playback: Status: Running Interface = 1 Altset = 1 URBs = 3 [ 57 57 57 ] Packet Size = 312 Momentary freq = 48000 Hz (0x6.0000) Interface 1 Altset 1 Format: S24_3LE Channels: 8 Endpoint: 1 OUT (ADAPTIVE) Rates: 44100, 48000, 88200, 96000 Data packet interval: 125 us
Does it tell us anything?
That this device does not use frequency feedback.
What happens when you are recording something and then try to play something else with a different sample rate? (use arecord/aplay with "-D hw:X")
Regards, Clemens
Thanks Clemens for your quick response!
Am 06.10.2010 16:38, schrieb Clemens Ladisch:
Playback: Status: Running Interface = 1 Altset = 1 URBs = 3 [ 57 57 57 ] Packet Size = 312 Momentary freq = 48000 Hz (0x6.0000) Interface 1 Altset 1 Format: S24_3LE Channels: 8 Endpoint: 1 OUT (ADAPTIVE) Rates: 44100, 48000, 88200, 96000 Data packet interval: 125 us
Does it tell us anything?
That this device does not use frequency feedback.
What happens when you are recording something and then try to play something else with a different sample rate? (use arecord/aplay with "-D hw:X")
When I start recording at 44.1 kHz while playing a wav at 48 kHz the playback gets very distorted, sounds like lots of missing samples. I get this from stream0:
M-Audio Fast Track Ultra 8R at usb-0000:00:1a.7-4, high speed : USB Audio
Playback: Status: Running Interface = 1 Altset = 1 URBs = 2 [ 5 6 ] Packet Size = 312 Momentary freq = 48000 Hz (0x6.0000) Interface 1 Altset 1 Format: S24_3LE Channels: 8 Endpoint: 1 OUT (ADAPTIVE) Rates: 44100, 48000, 88200, 96000 Data packet interval: 125 us
Capture: Status: Running Interface = 2 Altset = 1 URBs = 8 [ 8 8 8 8 8 8 8 8 ] Packet Size = 312 Momentary freq = 44100 Hz (0x5.8333) Interface 2 Altset 1 Format: S24_3LE Channels: 8 Endpoint: 1 IN (ASYNC) Rates: 44100, 48000, 88200, 96000 Data packet interval: 125 us
The sound stays corrupted until I record at 48 kHz again. Then everything sounds as before, i.e. with thos noticable clicks we're trying to sort out.
Do you need more infos?
Regards,
Felix
Felix Homann wrote:
Am 06.10.2010 16:38, schrieb Clemens Ladisch:
this device does not use frequency feedback.
What happens when you are recording something and then try to play something else with a different sample rate?
When I start recording at 44.1 kHz while playing a wav at 48 kHz the playback gets very distorted, sounds like lots of missing samples.
So this device expects the computer to take the sample clock from the capture data. This is just like the UA-101; it should be possible to write a similar driver (with opportunities for code sharing, and AFAIK UAC 2 requires such a feedback mode, too).
Regards, Clemens
On Thu, Oct 07, 2010 at 08:37:22AM +0200, Clemens Ladisch wrote:
Felix Homann wrote:
Am 06.10.2010 16:38, schrieb Clemens Ladisch:
this device does not use frequency feedback.
What happens when you are recording something and then try to play something else with a different sample rate?
When I start recording at 44.1 kHz while playing a wav at 48 kHz the playback gets very distorted, sounds like lots of missing samples.
So this device expects the computer to take the sample clock from the capture data. This is just like the UA-101; it should be possible to write a similar driver (with opportunities for code sharing, and AFAIK UAC 2 requires such a feedback mode, too).
Ah, that makes sense, yes. UAC2 calls this mode "implicit feedback". Felix, can you provide a dump of "lsusb -v" for this device?
Daniel
Am 07.10.2010 10:10, schrieb Daniel Mack:
On Thu, Oct 07, 2010 at 08:37:22AM +0200, Clemens Ladisch wrote:
So this device expects the computer to take the sample clock from the capture data. This is just like the UA-101; it should be possible to write a similar driver (with opportunities for code sharing, and AFAIK UAC 2 requires such a feedback mode, too).
Ah, that makes sense, yes. UAC2 calls this mode "implicit feedback". Felix, can you provide a dump of "lsusb -v" for this device?
Daniel
I've attached a dump.
Thank you all for your increased interest!
Regards,
Felix
Am 07.10.2010 10:10, schrieb Daniel Mack:
Ah, that makes sense, yes. UAC2 calls this mode "implicit feedback". Felix, can you provide a dump of "lsusb -v" for this device?
Daniel
A couple of additional notes:
1. If I force the playback endpoint to async mode (.ep_attr = 0x05) the regular clicks disappear at 48 kHz. But then we are back to completely distorted playback at 44.1 kHz.
I guess those of you understanding "implicit feedback" probably expected this result.
2. In Windows switching the clock source from internal to external gives these Co packets in usbmon:
ffff88007426a980 49576636 S Co:1:009:0 s 01 0b 0000 0001 0000 0 ffff88007426a980 49577887 C Co:1:009:0 0 0 ffff88007426ab00 49650954 S Co:1:009:0 s 01 0b 0000 0002 0000 0 ffff88007426ab00 49652256 C Co:1:009:0 0 0 ffff88007426a8c0 49789965 S Co:1:009:0 s 01 0b 0001 0001 0000 0 ffff88007426a8c0 49790233 C Co:1:009:0 0 0 ffff88007426a8c0 49808331 S Co:1:009:0 s 01 0b 0002 0002 0000 0 <---- here's a difference ffff88007426a8c0 49808727 C Co:1:009:0 0 0 ffff88007426aa40 49947450 S Co:1:009:0 s 22 01 0100 0001 0003 3 = 80bb00 ffff88007426aa40 49947823 C Co:1:009:0 0 3 > ffff88007426aa40 49962321 S Co:1:009:0 s 22 01 0100 0081 0003 3 = 80bb00 ffff88007426aa40 49962691 C Co:1:009:0 0 3 >
The device seems to accept an external clock at 48 kHz only.
3. In Windows switching the clock source from external to internal gives these Co packets in usbmon:
ffff88007426aec0 97962867 S Co:1:009:0 s 01 0b 0000 0001 0000 0 ffff88007426aec0 97964056 C Co:1:009:0 0 0 ffff88007426ab00 98031831 S Co:1:009:0 s 01 0b 0000 0002 0000 0 ffff88007426ab00 98033165 C Co:1:009:0 0 0 ffff88007426a980 98093945 S Co:1:009:0 s 01 0b 0001 0001 0000 0 ffff88007426a980 98094160 C Co:1:009:0 0 0 ffff88007426a980 98099994 S Co:1:009:0 s 01 0b 0001 0002 0000 0 <---- here's a difference ffff88007426a980 98101277 C Co:1:009:0 0 0
@Daniel: You asked for the output of lsusb. Are you thinking of modifying the UA-101 driver yourself? Or something more generic? In other words, if you already have an idea of a solution I wouldn't have to work myself through ua101.c (which I don't understand right now).
@all: Did anyone notice the patch adding mixer support for the FTU devices I send on Sept, 25?
Kind regards,
Felix
Felix Homann wrote:
@Daniel: You asked for the output of lsusb. Are you thinking of modifying the UA-101 driver yourself? Or something more generic?
I guess he wanted to look for UAC2 descriptors (which this device does not have).
When (if) the driver has UAC2 implicit feedback support, it's easy to add a simple quirk that makes the FTU work. (In that case, the UA-101 driver can be merged back, too.)
Did anyone notice the patch adding mixer support for the FTU devices I send on Sept, 25?
Yes, I noticed it.
Regards, Clemens
On Fri, Oct 08, 2010 at 08:26:52AM +0200, Clemens Ladisch wrote:
Felix Homann wrote:
@Daniel: You asked for the output of lsusb. Are you thinking of modifying the UA-101 driver yourself? Or something more generic?
I guess he wanted to look for UAC2 descriptors (which this device does not have).
Actually, I wanted to check whether the device marks the inbound (capture) endpoint as isochronous with implicit data feedback usage. Which it doesn't either.
When (if) the driver has UAC2 implicit feedback support, it's easy to add a simple quirk that makes the FTU work. (In that case, the UA-101 driver can be merged back, too.)
I wonder if we can always fall back to implicit feedback in case a playback substream does not have a syncpipe and does not have its fill_max bit set.
Felix, can you add a
printk(KERN_WARNING "%s() is_playback %d fill_max %d syncpipe %d\n", __func__, is_playback, subs->fill_max, subs->syncpipe);
at the end of set_format() in pcm.c and send us the output?
I have an untested patch ready which should add support for implicit feedback, but I'm uncertain about the condition when to activate this mode.
Daniel
Hi Daniel,
Am 12.10.2010 09:18, schrieb Daniel Mack:
Felix, can you add a
printk(KERN_WARNING "%s() is_playback %d fill_max %d syncpipe %d\n", __func__, is_playback, subs->fill_max, subs->syncpipe);
at the end of set_format() in pcm.c and send us the output?
Here you go:
[195791.510900] set_format() is_playback 0 fill_max 0 syncpipe 0 [195791.513495] set_format() is_playback 1 fill_max 0 syncpipe 0
I have an untested patch ready which should add support for implicit feedback, but I'm uncertain about the condition when to activate this mode.
That sounds great. I'm looking forward to give it a try!
Thanks,
Felix
On Tue, Oct 12, 2010 at 10:18:24AM +0200, Felix Homann wrote:
Am 12.10.2010 09:18, schrieb Daniel Mack:
Felix, can you add a
printk(KERN_WARNING "%s() is_playback %d fill_max %d syncpipe %d\n", __func__, is_playback, subs->fill_max, subs->syncpipe);
at the end of set_format() in pcm.c and send us the output?
Here you go:
Thanks for testing!
[195791.510900] set_format() is_playback 0 fill_max 0 syncpipe 0 [195791.513495] set_format() is_playback 1 fill_max 0 syncpipe 0
Ok, This is what I expected - however, these details aren't enough to tell whether the device is using implicit feedback, it could also mean that the OUT endpoint can take any amount of data and applies some kind of audio data resampling, for example.
So we have to rely on the "usage" fields of the isochronous IN endpoints and add quirks for all other devices.
I have an untested patch ready which should add support for implicit feedback, but I'm uncertain about the condition when to activate this mode.
That sounds great. I'm looking forward to give it a try!
I attached a patch below, only compile-tested. Please also check the USB ID again, as I'm not sure whether you sent the right lsusb dump (the device name looks suspicious).
If that doesn't work, try debugging the value returned by urb.c:snd_usb_audio_next_packet_size(). For your device, you should end up in the "subs->stream->implicit_feedback" branch.
Thanks, Daniel
diff --git a/include/linux/usb/ch9.h b/include/linux/usb/ch9.h index da2ed77..7825676 100644 --- a/include/linux/usb/ch9.h +++ b/include/linux/usb/ch9.h @@ -352,6 +352,11 @@ struct usb_endpoint_descriptor { #define USB_ENDPOINT_NUMBER_MASK 0x0f /* in bEndpointAddress */ #define USB_ENDPOINT_DIR_MASK 0x80
+#define USB_ENDPOINT_USAGE_MASK 0x30 +#define USB_ENDPOINT_USAGE_DATA 0x00 +#define USB_ENDPOINT_USAGE_FEEDBACK 0x10 +#define USB_ENDPOINT_USAGE_IMPLICIT_FB 0x20 /* Implicit feedback Data endpoint */ + #define USB_ENDPOINT_SYNCTYPE 0x0c #define USB_ENDPOINT_SYNC_NONE (0 << 2) #define USB_ENDPOINT_SYNC_ASYNC (1 << 2) diff --git a/sound/usb/card.h b/sound/usb/card.h index 1febf2f..f91974e 100644 --- a/sound/usb/card.h +++ b/sound/usb/card.h @@ -71,6 +71,8 @@ struct snd_usb_substream { unsigned int fill_max: 1; /* fill max packet size always */ unsigned int txfr_quirk:1; /* allow sub-frame alignment */ unsigned int fmt_type; /* USB audio format type (1-3) */ + unsigned int frame_count; /* for capture streams acting as implicit + * feedback source */
unsigned int running: 1; /* running status */
@@ -101,6 +103,8 @@ struct snd_usb_stream { unsigned int fmt_type; /* USB audio format type (1-3) */ struct snd_usb_substream substream[2]; struct list_head list; + unsigned int implicit_feedback: 1; /* stream uses its capture data + substream to clock its playback substream */ };
#endif /* __USBAUDIO_CARD_H */ diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index f49756c..7710021 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -289,6 +289,11 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) if (fmt->attributes & UAC_EP_CS_ATTR_FILL_MAX) subs->fill_max = 1;
+ if (!is_playback && + (fmt->ep_attr & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_ISOC && + (fmt->ep_attr & USB_ENDPOINT_USAGE_MASK) == USB_ENDPOINT_USAGE_IMPLICIT_FB) + subs->stream->implicit_feedback = 1; + if ((err = snd_usb_init_pitch(subs->stream->chip, subs->interface, alts, fmt)) < 0) return err;
@@ -382,6 +387,7 @@ static int snd_usb_hw_free(struct snd_pcm_substream *substream)
subs->cur_audiofmt = NULL; subs->cur_rate = 0; + subs->frame_count = 0; subs->period_bytes = 0; if (!subs->stream->chip->shutdown) snd_usb_release_substream_urbs(subs, 0); diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 9a9da09..7fbc315 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -591,6 +591,9 @@ void snd_usb_set_format_quirk(struct snd_usb_substream *subs, case USB_ID(0x041e, 0x3f0a): /* E-Mu Tracker Pre */ set_format_emu_quirk(subs, fmt); break; + case USB_ID(0x0763, 0x2081): /* Midiman M-Audio RunTime DFU */ + subs->stream->implicit_feedback = 1; + break; } }
diff --git a/sound/usb/urb.c b/sound/usb/urb.c index 8deeaad..84362a4 100644 --- a/sound/usb/urb.c +++ b/sound/usb/urb.c @@ -462,6 +462,8 @@ static int retire_capture_urb(struct snd_usb_substream *subs, // continue; } bytes = urb->iso_frame_desc[i].actual_length; + if (subs->stream->implicit_feedback) + subs->frame_count += bytes_to_frames(runtime, bytes); frames = bytes / stride; if (!subs->txfr_quirk) bytes = frames * stride; @@ -502,12 +504,26 @@ static int retire_capture_urb(struct snd_usb_substream *subs, }
/* - * Process after capture complete when paused. Nothing to do. + * Process after capture complete when paused. We need to count the number of + * bytes received and increase subs->frame_count in case this capture stream + * is used as implicit feedback source. */ static int retire_paused_capture_urb(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *urb) { + if (subs->stream->implicit_feedback) { + unsigned long flags; + unsigned int i, bytes = 0; + + for (i = 0; i < urb->number_of_packets; i++) + bytes += urb->iso_frame_desc[i].actual_length; + + spin_lock_irqsave(&subs->lock, flags); + subs->frame_count += bytes_to_frames(runtime, bytes); + spin_unlock_irqrestore(&subs->lock, flags); + } + return 0; }
@@ -630,13 +646,28 @@ static int retire_playback_sync_urb_hs_emu(struct snd_usb_substream *subs, /* determine the number of frames in the next packet */ static int snd_usb_audio_next_packet_size(struct snd_usb_substream *subs) { + if (subs->stream->implicit_feedback) { + unsigned int frames; + unsigned long flags; + struct snd_usb_substream *capture = + &subs->stream->substream[SNDRV_PCM_STREAM_CAPTURE]; + + if (WARN_ONCE(!capture, "implicit feedback with no capture stream is impossible")) + return 0; + + spin_lock_irqsave(&capture->lock, flags); + frames = min(capture->frame_count, subs->maxframesize); + capture->frame_count -= frames; + spin_unlock_irqrestore(&capture->lock, flags); + + return frames; + } + if (subs->fill_max) return subs->maxframesize; - else { - subs->phase = (subs->phase & 0xffff) - + (subs->freqm << subs->datainterval); - return min(subs->phase >> 16, subs->maxframesize); - } + + subs->phase = (subs->phase & 0xffff) + (subs->freqm << subs->datainterval); + return min(subs->phase >> 16, subs->maxframesize); }
/* @@ -959,6 +990,24 @@ int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream, int c { struct snd_usb_substream *subs = substream->runtime->private_data;
+ if (subs->stream->implicit_feedback) { + /* for capture streams used as implicitc feedback source for + * other streams, we must not stop the stream. instead, we + * just put it to pause mode. + */ + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + subs->ops.retire = retire_capture_urb; + return 0; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + subs->ops.retire = retire_paused_capture_urb; + return 0; + } + } + switch (cmd) { case SNDRV_PCM_TRIGGER_START: subs->ops.retire = retire_capture_urb; @@ -986,7 +1035,23 @@ int snd_usb_substream_prepare(struct snd_usb_substream *subs, /* for playback, submit the URBs now; otherwise, the first hwptr_done * updates for all URBs would happen at the same time when starting */ if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK) { + struct snd_usb_substream *capture = + &subs->stream->substream[SNDRV_PCM_STREAM_CAPTURE]; + subs->ops.prepare = prepare_nodata_playback_urb; + + /* if this stream is in implicit feedback mode, start the + * capture stream now as the playback stream relies on the + * amount of data we see on the capture IN endpoint. + */ + if (subs->stream->implicit_feedback && !capture->running) { + int ret; + capture->ops.retire = retire_paused_capture_urb; + ret = start_urbs(capture, runtime); + if (ret) + return ret; + } + return start_urbs(subs, runtime); }
Am 12.10.2010 12:26, schrieb Daniel Mack:
Thanks for testing!
Thanks for *your* help on *my* problem!!!
I attached a patch below, only compile-tested. Please also check the USB ID again, as I'm not sure whether you sent the right lsusb dump (the device name looks suspicious).
Yes, it's been the right lsusb dump. The reported device name is a bit strange indeed. In /proc/asound it is listed as F8R which is is a bit more reasonable.
If that doesn't work, try debugging the value returned by urb.c:snd_usb_audio_next_packet_size(). For your device, you should end up in the "subs->stream->implicit_feedback" branch.
I've tested your patch. Unfortunately I don't get any sound. snd_usb_audio_next_packet_size() returns from the "subs->stream->implicit_feedback" branch. But the return value is always 0. ('frames' is returned, not the 0 from the if (WARN_ONCE...) statement).
Any ideas how to proceed?
Thank you very much again,
Felix
On Wed, Oct 13, 2010 at 09:47:13AM +0200, Felix Homann wrote:
Am 12.10.2010 12:26, schrieb Daniel Mack:
If that doesn't work, try debugging the value returned by urb.c:snd_usb_audio_next_packet_size(). For your device, you should end up in the "subs->stream->implicit_feedback" branch.
I've tested your patch. Unfortunately I don't get any sound. snd_usb_audio_next_packet_size() returns from the "subs->stream->implicit_feedback" branch. But the return value is always 0. ('frames' is returned, not the 0 from the if (WARN_ONCE...) statement).
Hmm, ok. That means that the capture stream doesn't see any data.
Any ideas how to proceed?
Sure :) Can you check whether the capture URBs are started when the playback stream is kicked off? It should be, and the 'retire' callbacks should increase the substream's frame counter. Due to the special case I added, the frames should now always be parsed for this very purpose, even if the record stream is not in use. You should trace why this doesn't happen. And once the capture stream sees data, snd_usb_audio_next_packet_size() will return more reasonable values, and the playback should also start.
Let me know what you find :)
Daniel
Daniel Mack wrote:
I have an untested patch ready which should add support for implicit feedback, but I'm uncertain about the condition when to activate this mode.
For UAC2 devices, when the descriptors say so. For UAC1 devices, never (because UAC1 does not have this mode). It looks as if all UAC1-like devices that need this are actually marked as vendor-specific, so we have quirks for them anyway.
I attached a patch below, only compile-tested. Please also check the USB ID again, as I'm not sure whether you sent the right lsusb dump (the device name looks suspicious).
The actual device name is correct; the "DFU" was taken by lsusb from the usb.ids file and is wrong. (I've submitted the correct name.)
static int snd_usb_audio_next_packet_size(struct snd_usb_substream *subs) {
frames = min(capture->frame_count, subs->maxframesize);
capture->frame_count -= frames;
This assumes that both streams are running continuously, and you have to make sure to start them at the same time to prevent the frame_count from overflowing or underflowing. In my UA-101 driver, I ensure that the buffer fill level is the same for both streams by submitting each playback packet with the same frame count as the corresponding capture packet; but I'm not sure which algorithm is more robust in practice.
/* if this stream is in implicit feedback mode, start the
* capture stream now as the playback stream relies on the
* amount of data we see on the capture IN endpoint.
*/
if (subs->stream->implicit_feedback && !capture->running) {
int ret;
capture->ops.retire = retire_paused_capture_urb;
set_interface?
ret = start_urbs(capture, runtime);
if (ret)
return ret;
}
- return start_urbs(subs, runtime);
I think you have to wait for the first capture packet to be received before you can start submitting playback URBs.
Regards, Clemens
On Fri, Oct 15, 2010 at 09:23:15AM +0200, Clemens Ladisch wrote:
Daniel Mack wrote:
I have an untested patch ready which should add support for implicit feedback, but I'm uncertain about the condition when to activate this mode.
For UAC2 devices, when the descriptors say so. For UAC1 devices, never (because UAC1 does not have this mode).
Yes, but UAC1 also doesn't support other things like high speed transfers in the specs, still the Linux driver supports it, right?
I think we can interpret the ISO endpoint usage bits for this, and if a UAC1 device sets them, it should be ok for the Linux driver. Or is there any other detail in the spec we could use for judging?
static int snd_usb_audio_next_packet_size(struct snd_usb_substream *subs) {
frames = min(capture->frame_count, subs->maxframesize);
capture->frame_count -= frames;
This assumes that both streams are running continuously, and you have to make sure to start them at the same time to prevent the frame_count from overflowing or underflowing.
The caputure stream must be running continuously, yes. Otherwise, the driver can't know how many data it should send. But the counter can't actually underflow; if there is no more data to be sent, the counter will drop to zero, which makes the output stream send zero-length packets.
In my UA-101 driver, I ensure that the buffer fill level is the same for both streams by submitting each playback packet with the same frame count as the corresponding capture packet; but I'm not sure which algorithm is more robust in practice.
Yes, I do exactly the same in my proprietary driver for the Native Instruments devices, but it seems harder to implement in the generic driver. The problem I see with my current approach is that the jitter is higher - we now always send the maximum frame size or zero length.
/* if this stream is in implicit feedback mode, start the
* capture stream now as the playback stream relies on the
* amount of data we see on the capture IN endpoint.
*/
if (subs->stream->implicit_feedback && !capture->running) {
int ret;
capture->ops.retire = retire_paused_capture_urb;
set_interface?
You mean usb_set_interface()? Why should that be neccessary?
ret = start_urbs(capture, runtime);
if (ret)
return ret;
}
- return start_urbs(subs, runtime);
I think you have to wait for the first capture packet to be received before you can start submitting playback URBs.
Hmm, Felix - can you try this? I thought as long as the capture stream doesn't see any data, it just sends out zero-length packets, which should be ok. Or do I miss anything?
Thanks, Daniel
Am 15.10.2010 10:59, schrieb Daniel Mack:
I think you have to wait for the first capture packet to be received before you can start submitting playback URBs.
Hmm, Felix - can you try this?
It doesn't make a difference. (Tested using ardour)
I just returned home. So I need a bit more time for the rest of your suggestions from your previous mail.
Thanks again for your spending time on these devices.
Regards,
Felix
Daniel Mack wrote:
On Fri, Oct 15, 2010 at 09:23:15AM +0200, Clemens Ladisch wrote:
Daniel Mack wrote:
I have an untested patch ready which should add support for implicit feedback, but I'm uncertain about the condition when to activate this mode.
For UAC2 devices, when the descriptors say so. For UAC1 devices, never (because UAC1 does not have this mode).
Yes, but UAC1 also doesn't support other things like high speed transfers in the specs, still the Linux driver supports it, right?
Mostly with quirks. Devices marked as UAC1 would not work in Windows if they required anything not implemented in all more or less current Windows versions; I don't know if any such device actually exists.
The Linux driver support is a default that is hoped to work with most quirky devices, to reduce the amount of quirk code. In the particular case of UAC1 high speed support, I just added what the Audigy 2 NX happened to do, because that was the device I had, and the extensions to UAC1 looked plausible.
I think we can interpret the ISO endpoint usage bits for this, and if a UAC1 device sets them, it should be ok for the Linux driver. Or is there any other detail in the spec we could use for judging?
For practical purposes, we cannot start the capture stream without it being requested by a program when there is more than one possible sample format/rate for it.
static int snd_usb_audio_next_packet_size(struct snd_usb_substream *subs) {
frames = min(capture->frame_count, subs->maxframesize);
capture->frame_count -= frames;
This assumes that both streams are running continuously, and you have to make sure to start them at the same time to prevent the frame_count from overflowing or underflowing.
The caputure stream must be running continuously, yes. Otherwise, the driver can't know how many data it should send.
If the capture stream was started some time before the playback stream, the counter will be quite huge, and the driver will send lots of maximum-sized packets, which will eventually overflow the device's buffer.
If some capture packet gets lost due to an error, the playback stream will try to compensate for that perceived lower rate. Since this protocol does not have sample counters, we cannot work around this; the best idea probably is to stop the stream (xrun). It's also possible for a playback packet to get lost; we cannot do anything about that.
But the counter can't actually underflow; if there is no more data to be sent, the counter will drop to zero, which makes the output stream send zero-length packets.
Using a default rate for the initial packets might make more sense.
(The audio output of the TUSB3200 chip will lock up if it gets several zero-length packets, but that thing is full-speed and adaptive.)
In my UA-101 driver, I ensure that the buffer fill level is the same for both streams by submitting each playback packet with the same frame count as the corresponding capture packet; but I'm not sure which algorithm is more robust in practice.
Yes, I do exactly the same in my proprietary driver for the Native Instruments devices, but it seems harder to implement in the generic driver. The problem I see with my current approach is that the jitter is higher - we now always send the maximum frame size or zero length.
This is called "blocking mode" in FireWire audio, and most FW devices use it. There the jitter doesn't matter as much because the packet frequency is always 8 kHz.
/* if this stream is in implicit feedback mode, start the
* capture stream now as the playback stream relies on the
* amount of data we see on the capture IN endpoint.
*/
if (subs->stream->implicit_feedback && !capture->running) {
int ret;
capture->ops.retire = retire_paused_capture_urb;
set_interface?
You mean usb_set_interface()? Why should that be neccessary?
To tell the device that the capture interface is supposed to send data. Furthermore, any specification-compliant device has a zero-sized endpoint, or no endpoint at all, in the default alternate setting.
Regards, Clemens
On Sat, Sep 25, 2010 at 11:57:53AM +0200, Felix Homann wrote:
in that past months I've been trying get the Fast Track Ultra devices working properly in Alsa. We've had lots of progress, most of the code has moved to Alsa git and today I've even posted a patch for getting mixer support for these devices.
Now, I need to get some expert's advice: The devices seem to drop samples or frames. Here's a report I've got today on the M-Audio forum:
"I've got a subtle problem to report: I think audio playback is dropping sample frames. To hear the problem, open Audacity at 48 kHz and play a 10-kHz. sine wave. When I do that I hear a regular clicking sound, about four clicks a second. I've tried recording the output and if I'm seeing correctly, exactly one sample frame in every 13312 (13x1024) is being dropped on output.
How is this device clocked? It could be the PLL can't get a valid link to its clock source and hence "jumps" once in awhile to compensate the drift. Is there any possibility to sync the device to some sort o digital input?
If the clicks you hear are very regular, it could also be that some kind of buffer boundary is not handled properly. Do you have access to an USB hardware analyzer to check whether the dropout is in fact part of the USB data stream?
Daniel
participants (4)
-
Clemens Ladisch
-
Daniel Mack
-
Daniel Mack
-
Felix Homann