PPQ from DAW: understand the value and trigger somethings

DSP, Plugin and Host development discussion.
RELATED
PRODUCTS

Post

Hi all,

I've a problem syncing my plugin with DAW :)

I've check the PPQ value the DAW send to my plugin at every buffer (which are variable in size, not fixed).
I'm analysing these values, because I need to trigger somethings at every 1/96 beat (which is the PPQ of my DAW, FL Studio; 96 pulses per beat, thus 384 per bar).

These are the first 7 values I got at 300BPM (Sample rate 44100):

buffer: 0 => PPQ 0
buffer: 92 => PPQ 0.0104308
buffer: 184 => PPQ 0.0208617
buffer: 256 => PPQ 0.0290249
buffer: 276 => PPQ 0.0312925

as you can see, 0, 92, 184 and 276 are fixed buffer that represent 1/96 (more or less), while 256 is a variable buffer between 184 and 276. (that I need to ignore).

Since I need to sync only with 0,92,184 and 276, I need to understand why they got those PPQ values.
In fact, double divisions in C++ returns:

1/96. = 0.0104167
2/96. = 0.0208333
3/96. = 0.03125

which are different of the ones from DAW.
Which kind of approx is this?
How would you check PPQ values from DAW and understanding (so trigger) that I'm at 1/96? I need this approch (instead of just summing samples and check the sum/position) because I change tempo during the playing.

I hope the question and my target is clear, and that you can help me as you always did in the past! Thank you very much!

[EDIT]
I could try to use sampleToNextClock, but FL Studio doesn't send this value to the plug (so, even enabling it within IPlug, I always get 0). I find this on internet, to calculate it at every buffer:

Code: Select all

double clockperquaternote = 96.0;
double clocksperminute = (param_Value_Tempo * clockperquaternote);
double clocksperseconds = clocksperminute / 60.0;
int blocksize = nFrames;
double currentsample = timeInfo.mSamplePos;
double secondsinsong = currentsample / GetSampleRate();
double nextmidiclock = secondsinsong * clocksperseconds;
double sampleToNextClock = ceil(floor(nextmidiclock) / clocksperseconds * GetSampleRate()) - (currentsample - blocksize);
but I think its wrong or it doesn't works, since it deliver to me weird results.

Post

Nowhk wrote:Hi all,

I've a problem syncing my plugin with DAW :)

I've check the PPQ value the DAW send to my plugin at every buffer (which are variable in size, not fixed).
I'm analysing these values, because I need to trigger somethings at every 1/96 beat (which is the PPQ of my DAW, FL Studio; 96 pulses per beat, thus 384 per bar).

These are the first 7 values I got at 300BPM (Sample rate 44100):

buffer: 0 => PPQ 0
buffer: 92 => PPQ 0.0104308
buffer: 184 => PPQ 0.0208617
buffer: 256 => PPQ 0.0290249
buffer: 276 => PPQ 0.0312925

as you can see, 0, 92, 184 and 276 are fixed buffer that represent 1/96 (more or less), while 256 is a variable buffer between 184 and 276. (that I need to ignore).

Since I need to sync only with 0,92,184 and 276, I need to understand why they got those PPQ values.
In fact, double divisions in C++ returns:

1/96. = 0.0104167
2/96. = 0.0208333
3/96. = 0.03125

which are different of the ones from DAW.
Which kind of approx is this?
How would you check PPQ values from DAW and understanding (so trigger) that I'm at 1/96? I need this approch (instead of just summing samples and check the sum/position) because I change tempo during the playing.

I hope the question and my target is clear, and that you can help me as you always did in the past! Thank you very much!
If memory serves me well, PPQ is the absolute position in quarter notes.

Let's say the time signature is 4/4, so that the beat matches a quarter note:

BPM = 300
BPM_in_seconds = 60 / BPM = 0.2
BPM_in_samples = BPM_in_seconds * srate = 0.2 * 44100 = 8820

So you have 8820 samples per quarter note.

After 92 samples PPQ is 92 / 8820 (0,0104308...)
After 184 samples PPQ is 184 / 8820 (0,0208616...)
After 256 samples PPQ is 256 / 8820 (0,0290249...)

Post

bitwise wrote: If memory serves me well, PPQ is the absolute position in quarter notes.

Let's say the time signature is 4/4, so that the beat matches a quarter note:

BPM = 300
BPM_in_seconds = 60 / BPM = 0.2
BPM_in_samples = BPM_in_seconds * srate = 0.2 * 44100 = 8820

So you have 8820 samples per quarter note.

After 92 samples PPQ is 92 / 8820 (0,0104308...)
After 184 samples PPQ is 184 / 8820 (0,0208616...)
After 256 samples PPQ is 256 / 8820 (0,0290249...)
Hi man! Thanks for the reply and the help :wink:

Yes, but I can only calculate "8820" on each buffer. 92, 184 and 256 are just the samples passed till now (constant tempo). If I edit the tempo during the time, this amount (of passed samples) will vary every time. So I can't rely on it to check if the current ppq is exactly the clock (1/96) I need or a ppq (a buffer) in the middle.

Do you see what I mean?

Post

Nowhk wrote:Hi man! Thanks for the reply and the help :wink:

Yes, but I can only calculate "8820" on each buffer. 92, 184 and 256 are just the samples passed till now (constant tempo). If I edit the tempo during the time, this amount (of passed samples) will vary every time. So I can't rely on it to check if the current ppq is exactly the clock (1/96) I need or a ppq (a buffer) in the middle.

Do you see what I mean?
You have ppqPos and tempo:

http://ygrabit.steinberg.de/~ygrabit/pu ... eInfo.html

isn't that enough ?

Post

bitwise wrote: You have ppqPos and tempo:

http://ygrabit.steinberg.de/~ygrabit/pu ... eInfo.html

isn't that enough ?
I don't know :) As I said, I need to check if the readed PPQ is one where I need to trigger (i.e. a 1/96 quarter note).

Let say DAW sends to me these values (in fact it sends to me them):

buffer: 3583 => PPQ 0.406236
buffer: 3584 => PPQ 0.406349

I need to evalutate e select only the PPQ (buffer) 3853, which is where a PPQ tick of a 1/96 (the 39° 1/96 of quarter note) occurs: 91,875*39=3583,125
(Note: FL Studio round it using Round half to even, so 3583).

And I need to ignore the other (3584), which is a buffer of 1 sample that occurs after 3583 (so in between a "valid" clock/buffer and another, i.e. the next one 91,875*40=3675).

My question is: how would you select it?
I've tried this:

Code: Select all

double samplesPerBeat = GetSampleRate() / (GetTempo() / 60.);
int currentSample = lrint(samplesPerBeat*timeInfo.mPPQPos);
double samplesPerPulse = samplesPerBeat / 96;
int times = lrint(currentSample / samplesPerPulse);
double diff = abs(samplesPerPulse * times - currentSample);

// trigger (snap 1/96)
if (diff < 1) {
    ...
}
but it triggers also at 3584 (since the "diff" is 0.875<1). :ud: :idea:

Post

Nowhk wrote: I don't know :) As I said, I need to check if the readed PPQ is one where I need to trigger (i.e. a 1/96 quarter note).

Let say DAW sends to me these values (in fact it sends to me them):

buffer: 3583 => PPQ 0.406236
buffer: 3584 => PPQ 0.406349

I need to evalutate e select only the PPQ (buffer) 3853, which is where a PPQ tick of a 1/96 (the 39° 1/96 of quarter note) occurs: 91,875*39=3583,125
(Note: FL Studio round it using Round half to even, so 3583).

And I need to ignore the other (3584), which is a buffer of 1 sample that occurs after 3583 (so in between a "valid" clock/buffer and another, i.e. the next one 91,875*40=3675).

My question is: how would you select it?
The buffer and its size define an interval:

[t1ppq, t2ppq[ = [0.406236, 0.406236 + 1/8820[ in ppq's

or [t1q, t2q[ = [38.998656, 39.00954] in qticks, with q = 1 / 96

You are looking for qtick = round(t1ppq / q) = 39

and you get it when: t1q <= qtick < t2q

If the buffer is large, it might contain more qticks and you have to loop.

Post

Your reasoning looks brilliant. I'm not at home right now to check it immediatly. I will when I got home.
Till that moment, thanks very much for the precious help. I'll bump the topic as soon as possible. Thanks again dude. Have a nice weekend.

Post

bitwise wrote: The buffer and its size define an interval:

[t1ppq, t2ppq[ = [0.406236, 0.406236 + 1/8820[ in ppq's

or [t1q, t2q[ = [38.998656, 39.00954] in qticks, with q = 1 / 96

You are looking for qtick = round(t1ppq / q) = 39

and you get it when: t1q <= qtick < t2q
Hi dude. I've finally check your code, but it doesn't works with all "values" :(

With buffer 459 it works:

Code: Select all

ppq from daw = 0.0520408 
t1q: 4.99592
t2q: 5.0068 
qtick: 5

t1q <= qtick < t2q become 4.99592 <= 5 < 5.0068
which is satisfy! The same with your previous values and buffer 3583.
But for buffer 92 for example it doesn't works:

Code: Select all

ppq from daw = 0.0104308 
t1q: 1.00136
t2q: 1.01224
qtick: 1

t1q <= qtick < t2q become 1.00136 <= 1 < 1.01224
fail, because 1.00136 > 1!
But it should be choosed as ppq "clock".

This is how I calculate t1q, t2q and qtick:

Code: Select all

double t1q = timeInfo.mPPQPos * hostPPQ;
double t2q = (timeInfo.mPPQPos + 1/8820.) * hostPPQ;
double qtick = round(t1q);
Here's a fast link where you can try/emulate my "dirty" code and see that 92 return incorrect: http://cpp.sh/4v3pa
Where am I wrong? :dog:

Post

Nowhk wrote: Hi dude. I've finally check your code, but it doesn't works with all "values" :(

With buffer 459 it works:

Code: Select all

ppq from daw = 0.0520408 
t1q: 4.99592
t2q: 5.0068 
qtick: 5

t1q <= qtick < t2q become 4.99592 <= 5 < 5.0068
which is satisfy! The same with your previous values and buffer 3583.
But for buffer 92 for example it doesn't works:

Code: Select all

ppq from daw = 0.0104308 
t1q: 1.00136
t2q: 1.01224
qtick: 1

t1q <= qtick < t2q become 1.00136 <= 1 < 1.01224
fail, because 1.00136 > 1!
But it should be choosed as ppq "clock".

This is how I calculate t1q, t2q and qtick:

Code: Select all

double t1q = timeInfo.mPPQPos * hostPPQ;
double t2q = (timeInfo.mPPQPos + 1/8820.) * hostPPQ;
double qtick = round(t1q);
Here's a fast link where you can try/emulate my "dirty" code and see that 92 return incorrect: http://cpp.sh/4v3pa
Where am I wrong? :dog:
qtick 1 belongs to a previous buffer. The current buffer begins at
1.00136. On the other hand, the next qtick is 1 + 1/96 = 1.0104167 which falls inside [1.00136, 1.01224[

Post

bitwise wrote:qtick 1 belongs to a previous buffer. The current buffer begins at
1.00136.
DAW doesn't send the exact buffer/ppq value, because in fact it's that buffer/ppq (91,875) rounded (92).
Since I'm at 300BPM, each PPQ is 91,875 samples (44100/(5*96)), which need to be rounded to 92 (it can't deal with "double" sample position). Thus, even the PPQ value is rounded.

I will never get from the DAW the exact PPQ/buffer since it needs some approximation of that value.
How can I know if the value I get is an approximation of a clock (thus, the clock itself) or not?
bitwise wrote:On the other hand, the next qtick is 1 + 1/96 = 1.0104167 which falls inside [1.00136, 1.01224[
I don't understand what you mean. With 1 + 1/96 I'll already "after" the tick 1. i.e. a value that theoretically I need to discard. Or am I misunderstanding? :ud:

Post

Nowhk wrote: DAW doesn't send the exact buffer/ppq value, because in fact it's that buffer/ppq (91,875) rounded (92).
Since I'm at 300BPM, each PPQ is 91,875 samples (44100/(5*96)), which need to be rounded to 92 (it can't deal with "double" sample position). Thus, even the PPQ value is rounded.
Is 92 a value you expect or is it the actual buffer length you get from the DAW?

Post

bitwise wrote:
Nowhk wrote: DAW doesn't send the exact buffer/ppq value, because in fact it's that buffer/ppq (91,875) rounded (92).
Since I'm at 300BPM, each PPQ is 91,875 samples (44100/(5*96)), which need to be rounded to 92 (it can't deal with "double" sample position). Thus, even the PPQ value is rounded.
Is 92 a value you expect or is it the actual buffer length you get from the DAW?
Its the actual buffer lenght I get from DAW at the "first" 1/96 clock, instead of the expected value which would be 91,875. At the same time, I get 0.0104308 (1/8820*92) as PPQ instead of 0,0104167 (1/8820*91,875).

I can't force DAW to returns "not-rounded" buffer (since its integer). Thus, even the PPQ I get it's (correctly) rounded.
So, I still don't understand due to this "round" how to understand how to evaluate this (rounded) buffer :cry:

Is it more clear now what's my problem?

Post

Nowhk wrote: Its the actual buffer lenght I get from DAW at the "first" 1/96 clock, instead of the expected value which would be 91,875. At the same time, I get 0.0104308 (1/8820*92) as PPQ instead of 0,0104167 (1/8820*91,875).

I can't force DAW to returns "not-rounded" buffer (since its integer). Thus, even the PPQ I get it's (correctly) rounded.
So, I still don't understand due to this "round" how to understand how to evaluate this (rounded) buffer :cry:

Is it more clear now what's my problem?
In this case what you could do is store the last time you got a qtickA and, let qtickB be the new buffer's time, if qtickB - qtickA is about 1/96 you detect it as real qtick.

Post

bitwise wrote:
Nowhk wrote: Its the actual buffer lenght I get from DAW at the "first" 1/96 clock, instead of the expected value which would be 91,875. At the same time, I get 0.0104308 (1/8820*92) as PPQ instead of 0,0104167 (1/8820*91,875).

I can't force DAW to returns "not-rounded" buffer (since its integer). Thus, even the PPQ I get it's (correctly) rounded.
So, I still don't understand due to this "round" how to understand how to evaluate this (rounded) buffer :cry:

Is it more clear now what's my problem?
In this case what you could do is store the last time you got a qtickA and, let qtickB be the new buffer's time, if qtickB - qtickA is about 1/96 you detect it as real qtick.
But I will know qtickA is a real qtick at qtickB time (so, at the next buffer).
I need to check the "clock" tick in the "current" buffer, not in the next one :(

Post

Nowhk wrote: But I will know qtickA is a real qtick at qtickB time (so, at the next buffer).
I need to check the "clock" tick in the "current" buffer, not in the next one :(

The first buffer is 0 so this is a valid qtick, call it qtickA ( = 0).

After 96 frames you get next buffer and you get the rounding error qtickB = 1.00136

but qtickB - qtickA = 1.00136 which is greater than 1 so... "qtick time".

Post Reply

Return to “DSP and Plugin Development”