#include "MusimatTutorial.h"
Go to the source code of this file.
Functions | |
MusimatTutorialSection (B0202) | |
Void | realToRational (Real f, Integer Reference num, Integer Reference den) |
Static Void | para1 () |
Real | realRhythm (Rhythm x) |
Void | para2 () |
MusimatTutorialSection | ( | B0202 | ) |
Definition at line 2 of file B0202.cpp.
References para1(), and para2().
{ Print("*** B.2.2 Rhythm ***"); /***************************************************************************** B.2.2 Rhythm Duration in common music notation is expressed as a fraction of a whole note. For example a whole note equals four quarter notes: w = q + q + q + q /***************************************************************************** We could write this mathematically as follows: 1 1 1 1 1 - = -,-,-,- 1 4 4 4 4 which suggests using rational fractions to represent rhythmic durations. A rational fraction is a ratio of integers. Musimat comes with a built-in data type called Rhythm, which emulates com- mon musical notation conventions regarding rhythm. For example, the quarter note is defined as *****************************************************************************/ Rhythm Q = Rhythm( 1, 4 ); /***************************************************************************** The first number is the numerator of the rational fraction, the second is the denominator. Note that we can't write Rhythm(1/4) because the integer quotient of 1/4 is 0 with a remainder of 1; integer division is performed if both the numerator and denominator are integers, which won't work here. Specifying the numerator and denominator separately avoids this problem and has some other numerical advantages as well. Executing Print(Q); prints (1, 4). Internally, Rhythm() keeps the integer numerator and denominator values separately. Rhythmic duration can also be given as a real expression. *****************************************************************************/ Print(Rhythm(0.5)); /***************************************************************************** prints (1, 2). How does Rhythm() convert this real expression into a ratio of integers? It does so by calling the following function internally: *****************************************************************************/ para1(); // Step into this function to continue the tutorial para2(); // Step into this function to continue the tutorial }
Static Void para1 | ( | ) |
Definition at line 68 of file B0202.cpp.
{ /***************************************************************************** Function realToRational() takes a Real value f and attempts to find a rational fraction num/den that is as close as possible to it. It starts by setting num = den = 1 and asking whether num/den is already close enough to f. If so, it returns. Otherwise, it asks whether (num+1) / den is closer than num / (den+1). If so, it increments num; otherwise it increments den and repeats the process. Because num and den are Reference arguments, any changes to these variables within realToRational() are reflected in the value of the actual arguments supplied to it. This method can be used to find rational approximations to most any real value. For example, *****************************************************************************/ Real Pi = 3.14159265; Print(Rhythm(Pi)); /***************************************************************************** prints (1953857, 621932). Note that 1953857/621932 = 3.14159265, which is pretty close to the value of Pi. This method is limited by the precision of the computer hardware. The precision of a rational approximation depends upon the value of the built-in variables iterations and limit. For example, with the values shown in the preceding code, it took realToRational() 2,575,787 trials to come up with its best approximation of Pi, requiring about 1 second on my computer. The iteration and limit parameters can be set to whatever values produce the optimal performance/accuracy cost/benefit ratio. Barring obscure rhythms (nothing, say, beyond triplet eights), iterations = 240 and limit = 1.0/480 should be satisfactory. Although the details go beyond the scope of this book, here is a sketch of how Rhythm() uses realToRational(): Rhythm(Real x) { Integer num, den; // internal parameters for Rhythm realToRational( x, num, den ); // convert x to num / den rational fraction // . . . } When called with a Real argument, Rhythm() calls realToRational() to set its internal integer rational fraction values. Musimat provides built-in definitions for standard binary divisions of a whole note: *****************************************************************************/ Rhythm Whole = Rhythm(1.0/1.0); Rhythm Half = Rhythm(1.0/2.0); Rhythm Quarter = Rhythm(1.0/4.0); Rhythm Eighth = Rhythm(1.0/8.0); Rhythm Sixteenth = Rhythm(1.0/16.0); /***************************************************************************** It is easy to define ternary divisions as well. For example, a triplet eighth is Rhythm(1.0/12.0) because there are triplet eighths per whole note. By the same reasoning, a quintuplet eighth is *****************************************************************************/ Rhythm Qe = Rhythm(1.0/20.0); /***************************************************************************** We can express compound rhythms by addition. For example, a dotted half note is *****************************************************************************/ Rhythm Hd = Rhythm(1.0/2.0 + 1.0/4.0); // dotted half /***************************************************************************** Equivalently, *****************************************************************************/ Rhythm H1 = Rhythm(3.0 / 4.0); // also a dotted half /***************************************************************************** We can also do arithmetic directly with rhythms. For example, *****************************************************************************/ Print(Eighth+Sixteenth); /***************************************************************************** prints (3, 16). Also, *****************************************************************************/ Print(Whole - Sixteenth); /***************************************************************************** prints (15,16), *****************************************************************************/ Print(Quarter * Sixteenth); /***************************************************************************** prints (1,64), and *****************************************************************************/ Print(Quarter / Sixteenth); /***************************************************************************** prints (4,1). The last value corresponds to a duration of four whole notes. We can extract the numerator and denominator from Rhythm(): *****************************************************************************/ Integer num, den; Rhythm z = Rhythm(Eighth + Sixteenth, num, den); // assigns rational fraction for E+S to num and den /***************************************************************************** Used this way, Rhythm() calculates the rational fraction of its first argument and sets num and den by reference to the result. For the preceding example, num is set to 3 and den is set to 16. We can leverage this capability to obtain the duration of a rhythm as a real number: *****************************************************************************/ }
Void para2 | ( | ) |
Definition at line 184 of file B0202.cpp.
{ /***************************************************************************** Then, for example, executing Print(realRhythm(E + S)); prints 0.1875. As with pitches, we can make lists of rhythms. *****************************************************************************/ RhythmList RL = RhythmList(Quarter, Eighth, Eighth, Eighth, Sixteenth, Sixteenth, Quarter); Print(RL); /***************************************************************************** prints {(1,4), (1,8), (1,8), (1,8), (1,16), (1,16), (1,4)}. *****************************************************************************/ }
Real realRhythm | ( | Rhythm | x ) |
Void realToRational | ( | Real | f, |
Integer Reference | num, | ||
Integer Reference | den | ||
) |
Definition at line 49 of file B0202.cpp.
{ Const Integer iterations = 3000000; Const Real limit = 0.000000000001; num = den = 1; // start off with ratio of 1/1 For (Integer i = 0; i < iterations; i = i + 1) { If (Abs( Real(num) / Real(den) - f ) < limit) Return; // we have reached the limit Else { Real x = Abs(Real(num+1) / Real(den) - f); Real y = Abs(Real(num) / Real(den+1) - f); If (x < y) num = num + 1; Else den = den + 1; } } Return; // if we get here, we've not converged on the limit }