Host SYNC (LFO/ADSR)
-
- KVRer
- 26 posts since 14 May, 2006
I'm trying this for some time now. I got a free run LFO to work, but i have no idea how to get it to sync to the HOST tempo and exact sample position. I use JUCE, but that's not a problem or a solution. At any given moment i have access to all data that the host is passing (samplerate, sample position in bar, ppq and so on). But how do i calculate the frequency of the lfo, and how often to i call it's tick() method (the method that generates the next value of the lfo). Perhaps someone has a ready example class ? This is ofcourse VST subsystem.
-
- KVRAF
- 1718 posts since 3 Sep, 2003
There are two steps to it.
A. Calculate samples per step. 120 BPM => 500 ms => 22050.0 samples for quarter notes.
B. Create a counter that you MOD by the bar position for every buffer, and wrap when it exceeds the sample count from A.
Then map the counter to a wave index or something.
A. Calculate samples per step. 120 BPM => 500 ms => 22050.0 samples for quarter notes.
B. Create a counter that you MOD by the bar position for every buffer, and wrap when it exceeds the sample count from A.
Then map the counter to a wave index or something.
-
- KVRAF
- 1718 posts since 3 Sep, 2003
Here's a metronome class from a non-vst thing I did. It's a similar mechanism.
Edited for clarity.
Code: Select all
#pragma once
#include "stereoBuffer.hpp"
class metronome
{
private:
float samplesPerClick;
int sampleCounter;
int clicksPerBar;
int clickCount;
int sampleRate;
float lfoS[2];
float l_pitch[4];
public:
metronome()
{
sampleCounter = 0;
samplesPerClick = 0.0f;
clicksPerBar = 4;
clickCount = 0;
lfoS[0] = 1.0f;
lfoS[1] = 0.0f;
}
~metronome()
{
}
void setClicksPerBar(int cpb)
{
clicksPerBar = cpb;
}
void setSamplesPerClick( ... )
{
samplesPerClick =
}
void setSampleRate(int s)
{
sampleRate = s;
l_pitch[0] = 2.0f * (float)sin(M_PI * 1760.0f / sampleRate);
l_pitch[1] = 2.0f * (float)sin(M_PI * 880.0f / sampleRate);
l_pitch[2] = 2.0f * (float)sin(M_PI * 880.0f / sampleRate);
l_pitch[3] = 2.0f * (float)sin(M_PI * 880.0f / sampleRate);
}
void doProcess(stereoBuffer* buffer, int numSamples, int samplesFromBarStart = -1)
{
float* ob1 = buffer->getDataPointer(0);
float* ob2 = buffer->getDataPointer(1);
for(int i = 0; i < numSamples; i++)
{
lfoS[0] = lfoS[0] - l_pitch[clickCount] * lfoS[1];
lfoS[1] = lfoS[1] + l_pitch[clickCount] * lfoS[0];
lfoS[0] *= 0.98f;
ob1[i] += lfoS[0] * 0.7f;
ob2[i] += lfoS[0] * 0.7f;
sampleCounter++;
if(sampleCounter > samplesPerClick) //Reset
{
clickCount++;
clickCount %= clicksPerBar;
sampleCounter = 0;
lfoS[0] = 1.0f;
lfoS[1] = 0.0f;
}
}
}
};
-
Leslie Sanford Leslie Sanford https://www.kvraudio.com/forum/memberlist.php?mode=viewprofile&u=131095
- KVRAF
- 1640 posts since 4 Dec, 2006
First, get the tempo and convert it to frequency:atomix1040 wrote:I'm trying this for some time now. I got a free run LFO to work, but i have no idea how to get it to sync to the HOST tempo and exact sample position. I use JUCE, but that's not a problem or a solution. At any given moment i have access to all data that the host is passing (samplerate, sample position in bar, ppq and so on). But how do i calculate the frequency of the lfo, and how often to i call it's tick() method (the method that generates the next value of the lfo). Perhaps someone has a ready example class ? This is ofcourse VST subsystem.
Code: Select all
frequency = tempo > 0.0 ? 60.0 / tempo : 0.0;
I use the following tables:
Code: Select all
enum NoteType
{
Regular,
Triplet,
Dotted
};
enum NoteDivision
{
Whole,
Half,
Quarter,
Eighth,
Sixteenth,
Thirtysecond,
Sixtyfourth,
};
const double NoteTypeScalers[] = { 1.0, 2.0 / 3.0, 1.5 };
const double NoteDivisionScalers[] = { 1.0 / 1.0 * 4.0,
1.0 / 2.0 * 4.0,
1.0 / 4.0 * 4.0,
1.0 / 8.0 * 4.0,
1.0 / 16.0 * 4.0,
1.0 / 32.0 * 4.0,
1.0 / 64.0 * 4.0 };
Code: Select all
scaler = NoteTypeScalers[noteType] *
NoteDivisionScalers[noteDivision];
syncFrequency = frequency * scaler;
Code: Select all
phaseIncrement = syncFrequency / sampleRate;
Code: Select all
// Sine output, could be some other waveform, e.g triangle.
output = sin(2 * pi * phaseAccumulator);
phaseAccumulator += phaseIncrement;
if(phaseAccumulator >= 1.0f)
{
phaseAccumulator -= 1.0f;
}
Code: Select all
// Do this for every call to processReplacing.
p = ppqnPosition * scaler;
phaseAccumulator = p - Floor(p);
-
- KVRer
- Topic Starter
- 26 posts since 14 May, 2006
@Leslie Sanford
well i got something running, but i'm stuck on types. witch variable is what type? is it all float ?
i have a running and working free-run LFO but there is a mix of unsigned int and float values, and the type casts are a key here.
Ofcourse all the values like tempo samplerate and so on are set and accurate at thhe moment when retrigger() is called. Also the inc parameter is the internal phase increment (it is uint32)
As for the ppqnPosition, i disabled this for testing (also type problems). I'm getting some results but i don't think it's all accurate.
When the LFO is free running the frequency is a large value, when it is in sync it is a small value (i guess your math assumes floating point math).
Could you clarify this for me please.
well i got something running, but i'm stuck on types. witch variable is what type? is it all float ?
i have a running and working free-run LFO but there is a mix of unsigned int and float values, and the type casts are a key here.
Ofcourse all the values like tempo samplerate and so on are set and accurate at thhe moment when retrigger() is called. Also the inc parameter is the internal phase increment (it is uint32)
As for the ppqnPosition, i disabled this for testing (also type problems). I'm getting some results but i don't think it's all accurate.
Code: Select all
void LFO::retrigger()
{
if (isSyncedToTempo)
{
frequency = (float)(tempo > 0.0 ? 60.0 / tempo : 0.0);
scaler = noteTypeScalers[noteType] * noteDivisionScalers[noteDivision];
syncFrequency = (float)(frequency * scaler);
inc = (uint32)((256.0f * syncFrequency / samplerate) * (float)(1<<24));
}
else
{
inc = (uint32)((256.0f * rateInHz / samplerate) * (float)(1<<24));
}
}
float LFO::tick()
{
if (isSyncedToTempo)
{
const double p = ppqnPosition * scaler;
inc = (uint32)(p - floor(p));
}
// the 8 MSB are the index in the table in the range 0-255
int i = phase >> 24;
// and the 24 LSB are the fractionnal part
float frac = (phase & 0x00FFFFFF) * k1Div24lowerBits;
// increment the phase for the next tick
phase += inc; // the phase overflow itself
return table[i]*(1.0f-frac) + table[i+1]*frac; // linear interpolation
}
When the LFO is free running the frequency is a large value, when it is in sync it is a small value (i guess your math assumes floating point math).
Could you clarify this for me please.
