On Mon, 15 Dec 2003, Chris Cannam wrote:
> On Monday 15 Dec 2003 9:16 am, Chris Cannam wrote:
> > This seems to suggest that the default system-based timer is doing
> > something a bit naive, like assuming it was called back on time
> > without actually checking.
>
> Hmm yes, even this crappy little patch to alsa-kernel/core/timer.c
> (gross adjustment to each system timer tick according to how late the
> previous one was) improves things for me. With this the timer still
> slips, but never quite enough to be audible in a MIDI-only stream.
> The PCM clock is still much much better though.
>
> @@ -940,9 +940,16 @@
> return 1000000000L / HZ;
> }
>
> +static long lateness;
> +
> static void snd_timer_s_function(unsigned long data)
> {
> + struct timer_list *tlist;
> snd_timer_t *timer = (snd_timer_t *)data;
> +
> + tlist = (struct timer_list *) timer->private_data;
> + lateness = (long)jiffies - (long)tlist->expires;
> +
> snd_timer_interrupt(timer, timer->sticks);
> }
>
> @@ -951,7 +958,7 @@
> struct timer_list *tlist;
>
> tlist = (struct timer_list *) timer->private_data;
> - tlist->expires = jiffies + timer->sticks;
> + tlist->expires = jiffies + timer->sticks - lateness;
> add_timer(tlist);
> return 0;
> }
Thanks for insigh to the problem. Can you try the attached patch?
(It's also in CVS.)
Jaroslav
-----
Jaroslav Kysela <[EMAIL PROTECTED]>
Linux Kernel Sound Maintainer
ALSA Project, SuSE Labs
Index: timer.c
===================================================================
RCS file: /cvsroot/alsa/alsa-kernel/core/timer.c,v
retrieving revision 1.52
retrieving revision 1.55
diff -u -r1.52 -r1.55
--- timer.c 23 Oct 2003 14:34:52 -0000 1.52
+++ timer.c 17 Dec 2003 15:14:33 -0000 1.55
@@ -935,6 +935,14 @@
* System timer
*/
+struct snd_timer_system_private {
+ struct timer_list tlist;
+ struct timer * timer;
+ unsigned long last_expires;
+ unsigned long last_jiffies;
+ unsigned long correction;
+};
+
unsigned int snd_timer_system_resolution(void)
{
return 1000000000L / HZ;
@@ -943,26 +951,44 @@
static void snd_timer_s_function(unsigned long data)
{
snd_timer_t *timer = (snd_timer_t *)data;
- snd_timer_interrupt(timer, timer->sticks);
+ struct snd_timer_system_private *priv = timer->private_data;
+ unsigned long jiff = jiffies;
+ if (time_after(jiff, priv->last_expires))
+ priv->correction = (long)jiff - (long)priv->last_expires;
+ snd_timer_interrupt(timer, (long)jiff - (long)priv->last_jiffies);
}
static int snd_timer_s_start(snd_timer_t * timer)
{
- struct timer_list *tlist;
+ struct snd_timer_system_private *priv;
+ unsigned long njiff;
- tlist = (struct timer_list *) timer->private_data;
- tlist->expires = jiffies + timer->sticks;
- add_timer(tlist);
+ priv = (struct snd_timer_system_private *) timer->private_data;
+ njiff = (priv->last_jiffies = jiffies);
+ if (priv->correction > timer->sticks - 1) {
+ priv->correction -= timer->sticks - 1;
+ njiff++;
+ } else {
+ njiff += timer->sticks - priv->correction;
+ priv->correction -= timer->sticks;
+ }
+ priv->last_expires = priv->tlist.expires = njiff;
+ add_timer(&priv->tlist);
return 0;
}
static int snd_timer_s_stop(snd_timer_t * timer)
{
- struct timer_list *tlist;
+ struct snd_timer_system_private *priv;
+ unsigned long jiff;
- tlist = (struct timer_list *) timer->private_data;
- del_timer(tlist);
- timer->sticks = tlist->expires - jiffies;
+ priv = (struct snd_timer_system_private *) timer->private_data;
+ del_timer(&priv->tlist);
+ jiff = jiffies;
+ if (time_before(jiff, priv->last_expires))
+ timer->sticks = priv->last_expires - jiff;
+ else
+ timer->sticks = 1;
return 0;
}
@@ -984,22 +1010,22 @@
static int snd_timer_register_system(void)
{
snd_timer_t *timer;
- struct timer_list *tlist;
+ struct snd_timer_system_private *priv;
int err;
if ((err = snd_timer_global_new("system", SNDRV_TIMER_GLOBAL_SYSTEM, &timer))
< 0)
return err;
strcpy(timer->name, "system timer");
timer->hw = snd_timer_system;
- tlist = (struct timer_list *) snd_kcalloc(sizeof(struct timer_list),
GFP_KERNEL);
- if (tlist == NULL) {
+ priv = (struct snd_timer_system_private *) snd_kcalloc(sizeof(struct
snd_timer_system_private), GFP_KERNEL);
+ if (priv == NULL) {
snd_timer_free(timer);
return -ENOMEM;
}
- init_timer(tlist);
- tlist->function = snd_timer_s_function;
- tlist->data = (unsigned long) timer;
- timer->private_data = tlist;
+ init_timer(&priv->tlist);
+ priv->tlist.function = snd_timer_s_function;
+ priv->tlist.data = (unsigned long) timer;
+ timer->private_data = priv;
timer->private_free = snd_timer_free_system;
return snd_timer_global_register(timer);
}