I'm trying to develop an application that reads data from guitar pro file and then for each note in there, it gives me timestamps (with some other useful infos for sure).
However, it isn't trivial to get timestamps for each note, because calculating note duration doesn't look easy as I've tried a lot of ways already. None of them matched completely to what Guitar Pro app actually shows.
The easiest formula (that actually ignores time signature) I've tried is:
Let's say T
is tempo, P
is note duration (1 for whole, 0.5 for half, 0.25 for quarter and so on), then I tried two formulas (60 / T) × P
and (60 / T) × P × 4
. This doesn't work obviously, but I still tried because I've seen it somewhere on web.
Other formula I came up with is calculating measure (bar) durations in seconds, so I went like this:
If tempo is T
and time signature numerator N
, then formula looked like (60 / T) × N
. And the idea behind this is that (60 / T)
is duration of one beat, and because we have N
beats in measure, I multiply those two values. Although I want note durations and not measure durations, if this was correct for measures, I would somehow split it with notes, but measure duration is already incorrect. One important factor regarding this formula though is that it actually works for common time (4/4) as long as it's something else (like 6/8) it messes up.
Also, I doubt in time signature the numerator doesn't always tell me the number of beats, although that's what its definition looks like. Because if I assume in guitar pro file with 6/8 time signature, 3 beats per measure, instead of 6, it looks correct. So I would like to clear doubts on this part as well.
Thanks a lot.
EDIT: Sharing the code, using alphatab library.
//..omitted imports
const tempos = new Map();
function getPairValue(x, y) {
return tempos.get([x, y].toString()) || null;
}
function bpmToQnpm(bpm, numerator, denominator) {
let conversionFactor = 1; // Default for 4/4 time
switch (denominator) {
case 2: // Half note gets the beat
conversionFactor = 2;
break;
case 4: // Quarter note gets the beat
conversionFactor = 1; // This applies to 2/4, 3/4, and 4/4
break;
case 8: // Eighth note gets the beat
if (numerator === 6 || numerator === 12) {
// For 6/8 and 12/8, the dotted quarter note gets the beat
conversionFactor = 1.5;
} else {
conversionFactor = 0.5;
}
break;
}
return bpm * conversionFactor;
}
function parseGpFile(filePath) {
//..omitted
let tempo = score.tempo;
let currentTime = 0;
const data = [];
for (const measure of measures) {
const beats = measure.voices.flatMap(voice => voice.beats);
//In guitar pro as I see beat is just a group of notes played as the same time
for (let i = 0; i < beats.length; i++) {
const beat = beats[i];
if (beat.isRest)
continue;
{
let newTempo = getPairValue(measure.index, beat.index);
if (newTempo)
tempo = newTempo;
}
let part = 1 / beat.duration;
if (beat.dots === 1) {
part += part / 2;
} else if (beats.dots === 2) {
part += part / 4;
}
const qnpm = bpmToQnpm(
tempo,
measure.masterBar.timeSignatureNumerator,
measure.masterBar.timeSignatureDenominator);
part *= 4;
const beatDuration = (60 / qnpm) * part;
let chord = [];
for (const note of beat.notes) {
chord.push({
'string': 7 - note.string,
'fret': note.fret,
'timestamp': currentTime
});
}
data.push(chord);
currentTime += beatDuration;
}
}
return data;
}