Re: [linux-audio-dev] Sequencer Sync Woes

New Message Reply About this list Date view Thread view Subject view Author view Other groups

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


New Message Reply About this list Date view Thread view Subject view Author view Other groups

This archive was generated by hypermail 2b28 : Wed Feb 07 2001 - 20:14:00 EET