Replies: 7 comments 12 replies
-
This requires changing the alignment positions into fractions internally. This is a big change, but I can look into it. |
Beta Was this translation helpful? Give feedback.
-
It is a good idea in general, although there would be two main concerns: (1) it increases the verbosity(size) of the timemap, (2) it increases the computational complexity of dealing with rational numbers. The main complication with rational numbers is the need to do prime factorization to create reduced fractions, such as "3/6" to "1/2". The proposed names could be improved: "cfraction" is long and does not show its relationship to qstamp. That being said, I use rational numbers for rhythm processing in humlib, and I don't find any noticeable problems with computing efficiency. Here is a verovio issue that was probably related to using floats instead of rational numbers: There have not been any significant problems other than this I think, so not completely necessary, but using rational numbers for rhythm alignment would be more elegant since there will be no round-off error problem to compensate for. Rational numbers can be converted to floating-point numbers as needed after rhythm alignment, such as when dealing with
https://github.com/craigsapp/humlib/blob/master/include/HumNum.h Other than implementing a rational number class (which I have already done, but see Rational class below), converting the alignment system to rational numbers should take about a day, with most of that involving testing rather than implementation. The I had ChatGPT create a rational number class which seems to cover the essentials of what is needed (and which tells me about std::gcd existing in the numeric package, which I did not know about): #include <iostream>
#include <numeric> // For std::gcd in C++17 and later
class Rational {
private:
int numerator;
int denominator;
// Helper function to reduce the fraction
void reduce() {
int gcd = std::gcd(numerator, denominator);
numerator /= gcd;
denominator /= gcd;
if (denominator < 0) { // Make denominator positive
numerator = -numerator;
denominator = -denominator;
}
}
public:
Rational(int num = 0, int denom = 1) : numerator(num), denominator(denom) {
if (denom == 0) {
throw std::invalid_argument("Denominator cannot be zero");
}
reduce();
}
// Addition
Rational operator+(const Rational& rhs) const {
return Rational(numerator * rhs.denominator + rhs.numerator * denominator,
denominator * rhs.denominator);
}
// Subtraction
Rational operator-(const Rational& rhs) const {
return Rational(numerator * rhs.denominator - rhs.numerator * denominator,
denominator * rhs.denominator);
}
// Multiplication
Rational operator*(const Rational& rhs) const {
return Rational(numerator * rhs.numerator, denominator * rhs.denominator);
}
// Division
Rational operator/(const Rational& rhs) const {
if (rhs.numerator == 0) {
throw std::invalid_argument("Division by zero");
}
return Rational(numerator * rhs.denominator, denominator * rhs.numerator);
}
// Display function
void display() const {
std::cout << numerator << "/" << denominator << std::endl;
}
};
int main() {
Rational r1(3, 4); // 3/4
Rational r2(2, 5); // 2/5
Rational r3 = r1 + r2;
r3.display(); // 23/20
Rational r4 = r1 - r2;
r4.display(); // 7/20
Rational r5 = r1 * r2;
r5.display(); // 6/20 -> 3/10
Rational r6 = r1 / r2;
r6.display(); // 15/8
return 0;
} |
Beta Was this translation helpful? Give feedback.
-
I think the computational effort to apply GCD is worth it to reduce the fractions. Fractions are useful for getting precise alignments for complicated tuplet rhythms in the calculation of music notation as well as for using in the timemap. @lpugin is working on converting the double system for rhythm alignment, which will then make fractions available for timemap at no additional time cost.
This is an interesting idea. Currently there are three options to control the contents of the timemap (1) exclude cue notes, which is set with I propose adding an additional option such as It might be nice to have separate verovio options for measures and rests inclusions in the timemap (or make it easier to figure out how to set those parameters by mentioning them in the |
Beta Was this translation helpful? Give feedback.
-
How to interact with the
On the command-line this would not matter since only MIDI or only timemap will be created before the toolkit is exited, but for a persistent toolkit, the options might be more complicated to handle when creating both MIDI and timemaps. |
Beta Was this translation helpful? Give feedback.
-
That is OK. In the documentation for command-line use, it would be helpful to explain that the outer quotes should be single and the inner quotes double (or vice versa). Otherwise back slashes would be needed if they are both double quotes (and maybe also works with single quotes):
|
Beta Was this translation helpful? Give feedback.
-
A question is how to implement the Fraction option for timemap. Do you have a preference @yucongj (or others)? Currently there is one timemap parameters called
(1) Add a new parameter called (2) Alternatively, the data type of
In this case there is only In general, users will probably only want doubles or Fractions, so the second one seems a good idea unless both are needed at the same time. But this would complicate automated cases since you would not necessarily know the data type of For maximum flexibility it would be best to have an option that adds
This would add the There is also the request for the fractional duration from the start of the last barline. This could be called |
Beta Was this translation helpful? Give feedback.
-
For For me, I do not have any particular preference for expressing If there is a usage for having both such as wanting Fractions primarily, but then use it for realtime highlighting, the one usage would be to create the Fraction form, and then later calculate the double version on the fly, either dynamically from the Fraction value, or to preprocess the Fraction and create a separate entry with double. In that case being able to use Outside the context of verovio, I have done another variant for accessing both doubles and fractions, which would be something like this: "qstamp": [4.5, 1, 2] The first number is the double value, and the second/third numbers are the Fraction part of the non-integer part of the double:
This allows easier understanding of the Fraction value since |
Beta Was this translation helpful? Give feedback.
-
Currently in Timemap, the score time is somewhat represented in a field called
qstamp
, which is a number representing "the time in quarter notes from the start of the music to the start of the current event entry". For example (the last example in Timemap), the notation below would result inqstamp
s:0
,1.333333
,2.666667
, and4
(offset).However, representing score times as float numbers loses information when e.g., rounding$\frac{4}{3}$ to be $\frac{171}{128}$ .
1.333333
. This is problematic in applications where precise score times (as fractions) are needed, as converting from floats back to fractions could be ambiguious, and may result in incorrect fractions likeSince every score time is essentially a fraction (however complicated) that can be unambiguiously generated from the MEI file, why not preserve this information in Timemap? :-) I propose adding two fields to Timemap: (i)$\frac{1}{4}$ ; and (ii)
cfraction
: a fraction representing the cumulative music time of a JSON object, with a quarter note worthmfraction
: a fraction representing this object's local position within the measure. The field names and other details can be determined in the future, but here's an example for demonstrating the idea:The table below shows the values of
qstamp
,cfraction
, andmfraction
at the beginning of Beethoven_Op31_No3, and then the two triplets in Measure 8. For readability, the first column specifies the measure number (though this also could be included as a new field).Such fractions can be directly calculated from the MEI file, maining using each note's
dur
attribute (e.g.,dur=8
represents an eighth note), and when present, using thetuplet
tag as well (which in this case would interpretdur=8
as the value1/12
instead of1/8
).I believe including these fractions in the Timemap output is a generic feature that can benefit a variety of applications, especially the ones that involve time stamps in performance recordings. This would certainly benefit my project (which involves audio-to-score alignment!) with improved robustness and easier debugging.
What do you all think?
Beta Was this translation helpful? Give feedback.
All reactions