From alsa-devel-owner@alsa.jcu.cz  Fri Mar  5 10:48:52 1999
Received: from faui45.informatik.uni-erlangen.de (root@faui45.informatik.uni-erlangen.de [131.188.2.45])
	by marvin.jcu.cz (8.9.1a/8.9.1) with ESMTP id KAA20042
	for <alsa-devel@alsa.jcu.cz>; Fri, 5 Mar 1999 10:44:24 +0100
Received: from servww1.ww.uni-erlangen.de (servww1.ww.uni-erlangen.de [131.188.142.2])
	by faui45.informatik.uni-erlangen.de (8.9.1/8.1.49-FAU) with SMTP id KAA17972
	for <alsa-devel%alsa.jcu.cz@faui45.informatik.uni-erlangen.de>; Fri, 5 Mar 1999 10:44:21 +0100 (MET)
Received: from hong.ww.uni-erlangen.de by ww.uni-erlangen.de with SMTP;
	id AA12650 (1.37.109.4/7.3m-FAU); Fri, 5 Mar 99 10:44:30 +0100
Message-Id: <9903050944.AA12650@servww1.ww.uni-erlangen.de>
Received: by hong (sSMTP sendmail emulation); Fri, 05 Mar 1999 10:45:47 +0100
From: "Takashi Iwai" <iwai@ww.uni-erlangen.de>
To: alsa-devel@alsa.jcu.cz
Subject: Re: OSS Seq / Timer
In-Reply-To: <Pine.LNX.4.04.9903050041260.1307-100000@syrjala.pp.sci.fi>
References: <Pine.LNX.4.04.9903050041260.1307-100000@syrjala.pp.sci.fi>
Date: Fri, 05 Mar 1999 10:45:47 +0100
Reply-To: alsa-devel@alsa.jcu.cz
Sender: alsa-devel-owner@alsa.jcu.cz
Precedence: list

>>>>> On Fri, 5 Mar 1999 00:50:13 +0200 (EET), Ville Syrjala
<villes@syrjala.pp.sci.fi> said: 

>  Do the soundcard timers on GUS PnP work with OSS Seq emulation?. I
> thought it was meant to use the system timer. I've been trying to play
> some midi files with playmidi but the it doesn't work. It plays everythin=
> g
> too fast (so fast that I can't tell what it is that comes out of my
> speakers) /proc/asound/timers tells me that ossseq uses the first timer
> on the list, which is AMD Interwave.

Sorry, this is an obvious bug of OSS seq.
The timer resolution was not flexible.
I didn't take care because I'm an AWE64 user, which has only system
timer.
The following is a patch to kernel/seq/ossseq/ossseq_timer.c.
I didn't test it by myself, so hopefully it works...

-- 
Takashi Iwai / iwai@ww.uni-erlangen.de
Department of Materials Science
Friedrich-Alexander-University Erlangen-Nuernberg
================================================================
--- ossseq_timer.c	1999/03/01 11:29:02	1.1.2.1.4.1
+++ ossseq_timer.c	1999/03/05 09:34:49
@@ -8,7 +8,7 @@
 #include "ossseq_legacy.h"
 
 
-#define DEFAULT_RESOLUTION	SND_OSSSEQ_CTRLRATE
+// #define USE_SYSTEM_TIMER
 #define MAX_TICK	(unsigned int)-1
 
 /*
@@ -26,7 +26,7 @@
 
 /*
  */
-static int ntimers;
+static int ntimers;	/* number of clients accessing to timer */
 static snd_timer_t *timerp = NULL;
 static ossseq_timer_t *tlist[SND_OSSSEQ_MAX_CLIENTS];
 static volatile abstime_t cur_sys_tick, next_sys_tick;
@@ -41,7 +41,7 @@
 static int timer_open(void);
 static void timer_close(void);
 static void timer_start(void);
-static void timer_callback(snd_timer_t *tmr, void *private);
+static void timer_check_queues(void);
 static void timer_reset(ossseq_timer_t *rec);
 
 
@@ -97,26 +97,58 @@
 	snd_mutex_up_static(register);
 }
 
+
+#ifndef USE_SYSTEM_TIMER
+
+typedef unsigned int timer_tick_t;
+
+static unsigned int timer_resolution;	/* resolution of the current timer */
+static timer_tick_t tick_inc, tick_inc_err;
+static volatile timer_tick_t tick_cur_count, tick_next_count, tick_cur_err;
 
-#if 1
+static void timer_callback(snd_timer_t *tmr, void *private);
+
 /*
- * open system timer
+ * open ALSA timer
  */
 static int
 timer_open(void)
 {
-	timerp = snd_timer_open_always("ossseq", DEFAULT_RESOLUTION);
+	unsigned int system_resolution = 1000000000UL / SND_OSSSEQ_CTRLRATE;
+
+	timerp = snd_timer_open_always("ossseq", system_resolution);
 	if (! timerp) {
 		snd_printk("sequencer: can't create timer\n");
 		return -ENODEV;
 	}
 
+	if ((timer_resolution = snd_timer_resolution(timerp)) <= 0) {
+		snd_printk("sequencer: illegal timer resolution %d\n", timer_resolution);
+		snd_timer_close_always(timerp);
+		timerp = NULL;
+		return -ENODEV;
+	}
+
+	if (timer_resolution > system_resolution) {
+		snd_printk("sequencer: too large timer resolution: use default\n");
+		timer_resolution = system_resolution;
+	}
+
+	/* increment at each timer interrupt */
+	tick_inc = (timer_resolution + (system_resolution-1)) / system_resolution;
+	/* accumulated error at each time checking queues */
+	tick_inc_err = system_resolution - (system_resolution / tick_inc) * tick_inc;
+
 	timerp->callback = timer_callback;
 	timerp->callback_data = NULL;
 	timerp->flags |= SND_TIMER_FLG_AUTO;
 	cur_sys_tick = 0;
 	next_sys_tick = MAX_TICK;
 
+	tick_cur_count = 0;  /* interrupted count */
+	tick_next_count = tick_inc;  /* next time for checking queues */
+	tick_cur_err = 0; /* currently accumulated errors */
+
 	return 0;
 }
 
@@ -142,13 +174,34 @@
 	snd_timer_start(timerp, 1);
 }
 
+/*
+ * timer interrupt callback
+ */
+static void
+timer_callback(snd_timer_t *tmr, void *private)
+{
+	tick_cur_count++;
+	if (tick_cur_count >= tick_next_count) {
+		tick_cur_count = 0;
+		tick_cur_err += tick_inc_err;
+		tick_next_count = tick_inc + (tick_cur_err / tick_inc);
+		tick_cur_err %= tick_inc;
+
+		timer_check_queues();
+	}
+}
+
+
 #else
 
+/* use system timer -- more simple, but works only on i386 (HZ=100) system */
+
 /* callback from system timer */
 static void systimer_interrupt(unsigned long dummy);
 /* for system timer */
 static struct timer_list seq_timer = {NULL, NULL, 0, 0, systimer_interrupt};
 
+/* callback from timer - reprogram timer interrupt */
 static void
 systimer_interrupt(unsigned long dummy)
 {
@@ -156,10 +209,10 @@
 	/* arm next timer */
 	seq_timer.expires = curt + 1;
 	add_timer(&seq_timer);
-	timer_callback(NULL, NULL);
+	timer_check_queues();
 }
 
-/* start system timer */
+/* open system timer - only initialize variables */
 static int
 timer_open(void)
 {
@@ -168,6 +221,7 @@
 	return 0;
 }
 	
+/* start system timer - add timer interrupt */
 static void
 timer_start(void)
 {
@@ -175,7 +229,7 @@
 	add_timer(&seq_timer);
 }
 
-/* stop system timer */
+/* stop system timer - delete timer interrupt */
 static void
 timer_close(void)
 {
@@ -189,7 +243,7 @@
  * system timer callback function
  */
 static void
-timer_callback(snd_timer_t *tmr, void *private)
+timer_check_queues(void)
 {
 	int i;
 
@@ -197,6 +251,7 @@
 	if (cur_sys_tick < next_sys_tick)
 		return;
 
+	/* activate queues */
 	next_sys_tick = MAX_TICK;
 	for (i = 0; i < SND_OSSSEQ_MAX_CLIENTS; i++) {
 		ossseq_timer_t *t;

