Usually I would implement tap tempo in sclang but the other day I was in a situation where it was convenient to implement tap tempo on the server and as this subject comes up from time to time, I wanted to share my code. It works like this:
- 3 taps in a new tempo sets the new tempo
- continuous tapping in the same tempo refines the tempo by supplying more data points
- if the rate of tapping (ie. the time interval between this and the previous tap) changes by more than 200 ms, the tap counting is reset.
Try it:
(
s.waitForBoot{
SynthDef(\tapTempo, {
var trig = K2A.ar(\t_trig.kr(0));
var trig1 = Delay1.ar(trig);
var trig2 = Delay2.ar(trig);
var phasor = Phasor.ar(trig2, 1, 0, inf, 0);
var thistime = Latch.ar(phasor / SampleRate.ir, trig1);
var lasttime = Latch.ar(thistime, trig);
var reset = (thistime - lasttime).abs > 0.2;
var count = PulseCount.ar(trig, reset);
var phasor2 = Phasor.ar(reset, 1, 0, inf, 0);
var tapDur = Latch.ar(phasor2 / count / SampleRate.ir, trig1 * (count > 0) * (1 - reset));
var tempo = Select.kr(tapDur > 0, [1, tapDur]);
Out.ar(0, Impulse.ar(1/tempo))
}).add;
s.sync;
~tapSynth = Synth(\tapTempo);
~button = Button()
.fixedHeight_(80)
.fixedWidth_(80)
.canFocus_(false)
.states_([ [\Tap, \, Color.grey.alpha_(0.2)], [\Tap, \, Color.green.alpha_(0.6)] ])
.action_{|n|
if (n.() == 1)
{
~tapSynth.set(\t_trig, 1);
{ 0.1.wait; ~button.value_(0) }.fork(AppClock)
}
};
~view = View().layout_(HLayout(~button)).front;
}
)
(
~tapSynth.free;
~view.close
)