Ecasound and timebase master

Julien Claassen <[email protected]> Wed, 17 Jul 2013 20:50:37 +0200 (CEST)
Newsgroups gmane.comp.audio.ecasound.general
Message-ID <[email protected]>
Hello Kai!
   I wondered, if it would be possible for Ecasound to become a timebase 
master. So, taht it can give JACK hints about beats and bars. I took a look at 
klick, the metronome by Florian Schmidt, if I'm not mistaken. I think I found 
the relevant parts in metronome_jack.cc, which you find attached. the 
process_callback looks rather promising.
   My question: would it be possible to easily add this functionality to 
Ecasound, sayas a built-in object? Perhaps as in input object (like null) or 
as a modifier (effect) or perhaps as a general feature. As an effect it ouwld 
be most valuable, since it might be possible to use other controllers on it. 
Though I dare say, that as an effect it can't produce its own click sound.
   The reason I am not really comfortable with using klick itself is this: in 
its interactive mode, it can't be used as a timebase master and if its not in 
interactive mode, it can't be adjusted on the fly. It would have to be killed 
and restarted, which is awkward. Besides Ecasound is the most feasable 
transport master after all, it being the DAW in the system.
   The plan is this: have a software, that can generate JACK transport timebase 
(or whatever it's officially called) and use jack_midi_clock to generate 
start/stop and clock events from it, to sync it to Midish. I know, that 
implementing more MIDI capabilities in Ecasound would take a huge effort and 
is out fo the question for the time being. But I thought, that this might fit 
in with the current structure and klick looked reasonable small, so it would 
be easy to pinch the code. I'd be prepared to do some work on it, on my own, 
but I will need guidance. Before I can do anything though, I certainly need 
your insides on where to best put it, if it is at all possible.
   I'll keep on looking at klick to see, if I can spot more helpful code and 
piece the really important bits together. Probably the good thing about it all 
is, that it is already written in c++.
   Warm regards
         Julien

----------------------------------------
http://juliencoder.de/nama/music.html

------------------------------------------------------------------------------
See everything from the browser to the database with AppDynamics
Get end-to-end visibility with application monitoring from AppDynamics
Isolate bottlenecks and diagnose root cause in seconds.
Start your free trial of AppDynamics Pro today!
http://pubads.g.doubleclick.net/gampad/clk?id=48808831&iu=/4140/ostg.clktrk

_______________________________________________
Ecasound-list mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/ecasound-list
metronome_jack.cc (text/x-c++src, 2.7 KB)
/*
 * klick - an advanced metronome for jack
 *
 * Copyright (C) 2007-2009  Dominic Sacré  <[email protected]>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 */

#include "metronome_jack.hh"
#include "audio_interface_jack.hh"
#include "audio_chunk.hh"

#include <jack/jack.h>
#include <jack/transport.h>

#include "util/debug.hh"


MetronomeJack::MetronomeJack(AudioInterfaceJack & audio)
  : Metronome(audio)
  , _audio(audio)
  , _last_click_frame(0)
{
}


MetronomeJack::~MetronomeJack()
{
}


void MetronomeJack::process_callback(sample_t * /*buffer*/, nframes_t nframes)
{
    if (!active() || !_audio.transport_rolling()) {
        return;
    }

    jack_position_t pos = _audio.position();

    if (!(pos.valid & JackPositionBBT)) {
        // not much we can do
        return;
    }

    // make sure the transport master provided us with sane position info
    ASSERT(pos.beats_per_minute > 0.0);
    ASSERT(pos.beats_per_bar > 0.0);
    ASSERT(pos.ticks_per_beat > 0.0);
    ASSERT(pos.beat > 0 && pos.beat <= pos.beats_per_bar);
    ASSERT(pos.tick >= 0 && pos.tick < pos.ticks_per_beat);

    // convert BBT position to a frame number in this period
    double frames_per_beat = _audio.samplerate() * 60.0 / pos.beats_per_minute;
    nframes_t offset = (nframes_t)(frames_per_beat * (1.0 - (pos.tick / pos.ticks_per_beat)));
    bool emphasis;

    // avoid playing the same click twice due to rounding errors
    if (_last_click_frame && (pos.frame >= _last_click_frame) &&
        (pos.frame < _last_click_frame + MIN_FRAMES_DIFF)) {
        return;
    }

    if (offset % (nframes_t)frames_per_beat == 0) {
        // click starts at first frame. pos already refers to this beat
        offset = 0;
        emphasis = (pos.beat == 1);

        // deal with faulty timebase masters
        if ((pos.beat == pos.beats_per_bar + 1) ||
            (pos.beat == pos.beats_per_bar && pos.tick == pos.ticks_per_beat)) {
            emphasis = true;
        }

        _last_click_frame = pos.frame;
    }
    else if (offset < nframes) {
        // click starts somewhere during this period. since pos is the position at the start
        // of the period, the click played is actually at "pos + 1"
        emphasis = (pos.beat == (int)pos.beats_per_bar);

        _last_click_frame = pos.frame + offset;
    }
    else {
        // no click in this period
        return;
    }

    play_click(emphasis, offset);
}