From alsa-devel-owner@alsa.jcu.cz  Fri Feb 12 03:51:37 1999
Received: from havoc.gtf.org (IDENT:data@panic.ohr.gatech.edu [130.207.47.194])
	by marvin.jcu.cz (8.9.1a/8.9.1) with ESMTP id DAA12222
	for <alsa-devel@alsa.jcu.cz>; Fri, 12 Feb 1999 03:51:04 +0100
Received: (from data@localhost)
	by havoc.gtf.org (8.8.7/8.8.7) id VAA27164
	for alsa-devel@alsa.jcu.cz; Thu, 11 Feb 1999 21:51:02 -0500
From: Data <data@havoc.gtf.org>
Message-Id: <199902120251.VAA27164@havoc.gtf.org>
Subject: More PCM/RT stuff (replies mostly)
To: alsa-devel@alsa.jcu.cz (alsa)
Date: Thu, 11 Feb 1999 21:51:02 -0500 (EST)
Content-Type: text
Reply-To: alsa-devel@alsa.jcu.cz
Sender: alsa-devel-owner@alsa.jcu.cz
Precedence: list


Wow, looks like I maybe touched a nerve here!  I'm really glad to see
I'm not the only one interested in this sort of thing, especially since
this seems like a talented bunch.

Regarding Mr. Kysela's comments:

-- JK: -- 
Small correction. You can handle PCM timer code in fragment
acknowledge function which lowlevel driver calls when one fragment is
played / recorded. An application should set fragment size which will
determine clock step. 
---------

Excellent!  I've located the point at which this seems to happen - it
looks like the function called at that point is in pcm1.  I haven't
looked through pcm1_oss.c, but in pcm1_native.c the function is
snd_pcm1_interrupt_playback, with an analogous function called
snd_pcm1_interrupt_record.  I've not been thinking of this enough,
but of course it will be necessary to generate accurate timecode during
_recording_ as well as playback.  Fortunately the methods being dicussed
here are not restricted to one or the other.  Anyway it should be quite
simple to call the sequencer timer callback from there.
  I'd originally thought to have the application set the fragment size,
but I've got misgivings about this now.  I think it would make more sense 
to not require applications to be concerned with the fragment size, as this
depends on things like framerate etc.  Perhaps it would be better to let
the sequencer do this, at least in situations where accurate clocking
off the PCM driver is called for.  For example, a user might tell the
sequencer to sync off of PCM, and generate MTC at a 25 fps framerate.
This could be done through an ioctl.

-- JK: --
We need add generic MTC/SMPTE support to sequencer code. Please,
keep this in your mind. 
---------

Most definitely; but I've been under the impression, from reading Mr. van
der Pol's proposal, that the intention was to leave that sort of thing
to clients.  However, I do think it would make good sense in this case to
incorporate SMPTE into the sequencer itself.  At present the sequencer
maintains two parallel timebases, which make use of the timer.c service
(I suppose this often winds up using the system timer): one is straight
sec/nsec time, the other bar/beat time with an associated resolution
(ppq) and tempo (bpm).  Now a SMPTE time is expressed as hours / minutes /
seconds / frames / (optional) subframes; so to derive a SMPTE time from
the current sec/nsec counter requires some division <shudder>.  Perhaps
the sequencer could be altered to use a SMPTE-format counter internally,
and generate SMPTE timestamps, so that no conversion would be necessary.
Updating a SMPTE counter would require more time than updating the current
time-counter, but probably far less than converting from that to SMPTE,
an operation that would have to be done rather frequently.
  Furthermore, the audio and video worlds both run on SMPTE, and it works 
very well.  I had concerns about timing when I first got into SMPTE sync - 
I'm a bit obsessive about timing, I'm afraid - but my fears were abated; a 
good sequencer (Emagic's Logic, for example) can compensate for small amounts
of jitter and adds little or none of its own.  Therefore it would make
some sense for the sequencer section to speak SMPTE as well.
  On this topic I welcome any comments from particularly Mr. van der Pol.

-- JK: -- 
There shouldn't be probably any timing messages in sequencer's
queue.  I assume that timing messages will be delivered directly to
sequencer devices when they'll arrive. 
---------

I think that if this turns out to be an issue, it will not be too
difficult to deal with.  I can't remember, but perhaps there is an
'out-of-band' flag for events or the priority queue code automatically
bumps up timing events.

Regarding Mr. van de Pol's comments:

- FvdP: - 
This looks good, and is indeed the most generic way for
syncing the ALSA sequencer to an external source. The SMPTE (or MTC)
QFrame message contain enough info to construct a full timestamp. This
incoming timestamp will be used to adjust the timer's current
time. Once the ALSA sequencer timers (seq_timer.c) allow external
sync, we can accept many sources: audio playback/recording, SMPTE,
MTC, and (by adjusting tempo instead of time) also midi clock. See
<http://www.vande-pol.demon.nl/alsa/node5.html> for the description of
some of my ideas on this issue. 
---------

Again, I propose that the sequencer run _internally_ on SMPTE time.
The thing I would like to know is how best to change the sequencer's
sync source; I suggest via ioctls, but there may be a way that better
fits the sequencer's design.  If need be I will be happy to code these
changes into the sequencer - they should not be especially difficult.

- FvdP: -
In your scenario the PCM devices would act as a timing
source. The problem would be to have the codecs (which are often playing
using DMA) send out the timing events at required rate (as for MTC). A
more practical approach would be to have the PCM device send out 'some'
(complete) timestamp event when it want to (eg. every few interrupts,
at a pace of approx 10 Hz or better if one wants to - it is basicly
up to the PCM driver). This "Current time is xxxxxxx" message is to
be send (schedule time = NOW) to the sequencer timer, and this timer
will slowly/fast adjust current time to match the received timestamp.
---------

I have been considering an alternate approach to my original proposal
which would require no change to the core sequencer at all.  One could
construct a 'MTC generator' client.  This would register itself with the
sequencer and wait for 'start' messages to come through.  Once they did
it would begin polling the PCM driver and waiting for the sample queue
tail to advance.  Based on the amount it advances, it would generate MTC.
The client would be kernel-side (of course) and use the system timer to
trigger polls.
  I have done some calculation on the jitter inherent in this scheme, by 
writing a sort of 'simulator'.  With a 44100 Hz sampling rate, 250 Hz polling 
rate, and 30 fps frame rate (requiring a quarter-frame message to be sent 
every 8 1/3 mS), the jitter would be, on average, 1 mS - approximately 12 %,
using a minimal-error algorithm.  Furthermore the error (which is as
great as 2 mS for these parameters) varies greatly and quickly, and
would be seen as almost pathological timecode by even the best receiver.
  These errors cannot be minimised until the polling rate reaches 
approximately half a kilohertz, and become truly tolerable at one kilohertz.  
As the system timer (HZ variable) on most Intel systems is only a hundred 
hertz, we must conclude that some better algorithm is needed under these
circumstances for generating reasonably accurate timecode.  (However,
on Alphas the system would work very well, as the HZ value for those
systems is usually 1024!)
  One obvious solution would be to forego the services of timer.c and register 
wakeups with usleep or some such; this way the pause could be made variable 
and continuously adjustable to compensate for measured error.  I have
successfully used this sort of system on a sampling drum-machine program
under Linux.  However, the timing requirements for that were not nearly
so critical as they are here.
  The real trouble with a polling system such as the one I have just 
described is not, after all, latency, but jitter - random jitter at that.  
Such delays cannot be compensated, as they cannot be easily predicted, 
certainly not by a real-time algorithm.  If there is, on the other hand, a 
constant and predictable latency, this can be compensated for.
  In Mr. van de Pol's scenario, we would be relying fundamentally on the system 
clock, and using sample position messages arriving from the PCM driver (albeit 
at low frequency) to 'correct' this running clock, forcing it to correspond
to the PCM driver's notion of time.  This, I think, would work well for
many purposes, but not necessarily this one.  Timecode generators are
expected to emit messages at very regular intervals.  MTC messages _must_
go out every 8 1/3 mS, and from an embedded system or LTC-MTC converter
this is an entirely reasonable requirement, and not at all difficult
to fulfil.  If we could guarantee that the MTC routine would have the
processor at exactly the 8 1/3 mS intervals required, Mr.  van de
Pol's system would, I think, be quite satisfactory.  But we cannot
guarantee this.  This is why we need to find a source of interrupts
which we can make 'fire off' at the desired frequency.  The playback /
record interrupts are ideal for this, since they guarantee that we will
receive interrupts at intervals synchronised to the actual playback or
recording rates - which is, of course, our purpose.  (The problem of
generating unsynchronised MTC or beat clocks, unrelated to the playback
clock, I leave for the present to others!)
  My main concern, however, is that we in fact often require interrupts at 
_half_-sample intervals.  For example, with a sampling rate of 44.1 KHz and a 
frame rate of 30 fps, interrupts must occur every 367.5 samples.  Obviously 
if we round off, using for example a 368-byte blocksize, we will quickly find
ourselves out of sync with the playback signal.  The obvious solution to
me would be to employ a variable blocksize, using for example alternating
367 and 368-sample blocks, or at least to have alternating _series_
of these.  I do not know, without further study of the code, whether
this would be practical.  Mr.  Kysela, have you any thoughts on this?

- FvdP -
This 'PCM timing source' scheme can be implemented as a (kernel)
sequencer client which has a port for each of the PCM devices present in
the system.  These ports are only capable of sending out timing events
(time stamps, start, stop). 
--------

This sounds like an excellent and sensible idea to me, and would open
the possibility of synchronising different cards to each other.

Regarding Mr. Barton-Davis' comments:

- PBD --
It turns out that using the PCM device for implicit timing
(i.e. I set the sample rate to X Hz, and then know exactly how
much time has passed once N samples have been played) works fairly
well. However, it doesn't solve the latency problem by itself. Just
because the ALSA (or OSS) PCM driver decides to wake up a task once a
certain threshold/watermark is passed doesn't tell you much about when
that task actually wakes up. 
--------

This is precisely why we should do these sorts of critical timing
tasks under interrupt, at kernel level.  The idea of using the
playback interrupt is in fact the kernel-level equivalent of the method
you're using.  It encourages me greatly to hear that you've tried the
non-interrupt way and it worked fairly well!

- PBD --
Quasimodo, for the most part, wants very small amounts of
buffering of its PCM output by the driver, because it wants to respond
"quickly" to MIDI messages. Right now, I used about 3 2K fragments bytes @
48KHz, or about 0.06 seconds, of buffering. This works pretty well. Right
now, however, I'm also using the Pentium cycle counter to measure things
more precisely, since it gives me (on a 450Mhz CPU) resolution down to
about 20nanoseconds. Since I have a dual CPU machine, I am actually busy
waiting for periods of less than 1 usec sometimes. 
--------

Hmm.  Could the Pentium cycle counter be, perhaps, employed as a system
timer by timer.c, for example?  Having 20 nS resolution sounds almost
luxurious to me :)

- PBD --
Running the DSP thread in Quasimodo as RT_FIFO helps thing a
lot. It makes it possible to do busy waits for up to 2ms c/o the kernel,
for example, and because of the kernel's "need_resched" flag, reduces the
"driver-wakes-task" to "task-is-now-running" latency to the absolute
minimum (since the task gets switched to during the return from the
interrupt handler). This is very nice.

But RT_FIFO is limiting, because it requires root priviledge, and also
dangerous, so I'm unclear how realistic it is to use this mechanism for
programs intended to be run by "regular folk". 
--------

I'm ashamed to admit unfamiliarity with RT_FIFO, but it sounds
fascinating, and potentially useful.  I'll look in to it.

Thanks very much for everybody's insightful comments, and let's do keep
this thing going so we can wind up with CODE!  (which you'll see coming
from me regardless, I'm afraid! :) )

cheers and thanks,
Michael Ashton



