Subject: Re: [linux-audio-dev] Sequencer Sync Woes
From: Tim Goetze (tim_AT_quitte.de)
Date: Wed Feb 07 2001 - 19:41:37 EET
Hello, i'm a little late, i admit ....
On Feb 6 Billy Biggs wrote:
>Paul Davis (pbd_AT_Op.Net):
>
>> i'll rephrase :) find me someone who is generating MIDI sync (i would
>> call them "clock") pulses on Linux ...
>
> I guess . . . :) Seems like a pretty important 'feature' to me. :)
to me too, so midithing does send q/24 sync, and it runs on linux, and it
uses the rtc.
>> you're also not carrying usecs over from each computation. i found
>> that the "error" could accumulate fairly quickly.
>
> I think I do carry:
>
> while( diff > usecs_per_tick ) {
> diff -= tt;
> ++midiclockcounter;
> }
>
> After this while, diff is set to how many usecs have past since the
>beat we last played was supposed to be played. I then subtract that
>from the current time, an call that last_sec, last_usec.
i had the chance to improve midithing from the inspiring discussion you
gave me the pleasure to read; now i think i may try and contribute myself.
i must be missing something or do both of you assume the song tempo is
constant?
here's how midithing does it, in a simplified version -
the rtc loop :
have_clock = fds.revents & POLLIN;
if (have_clock)
{
int irqs;
read (rtc.fd, &irqs, sizeof (unsigned long));
time.clock++;
/* now() = gettimeofday() as double */
double t = time.start + now() - time.real_start;
song.play (t, &io);
}
and song.play():
void
m_song::play (double until, m_connector * out)
{
/* only time and tempo first */
int i = 0;
while (i < queue.events.count)
{
m_event * e = queue.event(i);
double t = tick2time (e->generic.tick);
if (t > until)
break;
/* parse() parses only tempo events */
if (parse (e))
queue.remove (e);
else
i++;
}
tick.current = time2tick (time.current = until);
beat.advance (tick.current, this);
while (queue.events.count)
{
m_event * e = queue.event(0);
if (e->generic.tick > tick.current)
break;
if (out) out->event_in (e);
queue.remove (e);
}
if (tick.prerolled <= tick.current + division / 4)
preroll (division);
}
double
m_song::tick2time (m_tick _tick)
{
return time.of_last_tempo + (double)
(_tick - tick.of_last_tempo) * time.per_tick;
}
parse() simplified, only tempo events shown (actually it does time
and key signature too):
bool
m_song::parse (m_tempo_event * e)
{
if (!e->is_tempo())
return false;
time.usec_per_quarter = e->usec_per_quarter();
time.per_quarter =
(double) time.usec_per_quarter /
TEN_TO_THE_SIXTH;
time.per_tick = time.per_quarter / (double) division;
tick.of_last_tempo = tick.current;
time.of_last_tempo = time.current;
return true;
}
i use double as the type of real time mostly out of laziness; do you think
there is too much computational overhead? one could switch to uint.uint
then. i don't even have an idea how many cycles a double / takes ...
i couldn't think of a simpler solution to deal with tempo changes, and
this code has been worked on a lot.
.tim
This archive was generated by hypermail 2b28 : Wed Feb 07 2001 - 20:14:00 EET