[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: Some fixes to musicxml2ly
From: |
Tuukka Verho |
Subject: |
Re: Some fixes to musicxml2ly |
Date: |
Sun, 17 Sep 2006 13:31:35 +0300 |
On Sat, 2006-09-16 at 13:41 +0200, Karl Hammar wrote:
> > In this case inexactness doesn't matter. The term 2**tolerance_exp makes
> > sure that notes that are slightly shorter than the next whole duration
> > are rounded to the whole duration instead of somethin like "c8.....". In
> > some cases floating point arithmetic can cause some randomness regarding
> > whether the note becomes c8... or c4 (if the duration is exactly 1/32
> > from the next whole duration), but I don't think that matters -- such
> > note length are weird enough to start with.
> >
> > When determining the number of dots the use of floating point arithmetic
> > doesn't obviously harm, because the result is rounded anyway.
> >
> > I can't think of any simple way to achieve the same result without using
> > math.log().
> >
> > The attached diff for musicxml.py now has an explanatory comment.
> >
>
> I don't understand this:
>
> tolerance_exp = -4
> a = - math.log(self._duration) / math.log(2)
> b = 2**tolerance_exp
> c = a - b
>
> 1:
> if tolerance_exp is a constant, why don't you simply say b = 1/16
> (another constant), which is clearly a duration(?) value.
>
> 2:
> why are you taking the difference between the log of a duration and a
> duration?
>
> In physics there is something which is called a dimension test: you
> simply cannot do 1 meter - 1 second. In math, you don't easily agree to
> do log(entity) - entity. And in this case I read your code as
> -2log( seconds ) - seconds which is clearly wrong.
>
> Now b seems to be an implementation detail caused by using floating
> point values, the same with math.ceil, is it so?
>
> If you simply want to map a duration to its -2log, why don't you use
> something like arr = [ 1, 2, 4, ...]; f(x) = arr[x]; which is a clear
> statement about what you are doing (although incomplete, I sense that
> self._duration is a rational number).
>
> 3:
> I don't understand what you are trying to do and your comment does not
> match the code
Maybe I should explain what get_duration_log does. It returns the type
of the base note of the note object. So if, for instance, the note is
c8..., get_duration_log returns 3. If you think the function is badly
named, I agree, but I didn't create it, I only added those two lines to
calculate the type if there is no <type> flag in the xml file.
But you are right in some respect, because I now realise my formula is
incorrect. My intention was to add the difference between a whole
duration (e.g. c4) and a note with a shorter base duration and four dots
(e.g. c8....) to (a) (as you call it), so that if the duration is very
close to the next whole duration, the function returns the next duration
(e.g. c4 instead of c8) (This is to avoid rounding errors and prevent
the note from having an insane number of dots). The difference _is_
constant in logarithmic scale, but i'm using a wrong constant. I was
thinking that for example c4->2, c4.->2.5, c4..->2.75 so the length of
the fourth dot in the logarithmic scale is 1/16. This is of course wrong
but I didn't realise it because my formula worked as desired (!). The
following should be correct:
I have a note with n dots, with duration
d = 2h - (2^-n) h
where h is the length of the base note. I want a factor x which
lengthens it to the next whole duration 2h, so
d*x = 2h
=> x = 1 / (1 - 2^-(n+1) )
Thus log2 (d*x) = log2 (d) + log2 (x). In this case, n = 4. the correct
formula is:
lengthening_factor = 1 / (1 - 2^-5 )
a = - math.log (self._duration * lengthening_factor) / math.log(2)
result = math.ceil(a)
result is the length of the base note. It will be as long or shorter
than than the real duration, except if duration is just slightly shorter
than the next whole duration. Rounding errors may occur if _duration*x
is exactly 2h, but I don't see that as a problem. Such durations are so
unusual that it doesn't matter much if they get 4 dots or are rounded
up.
It would be possible to implement this with rational numbers like this:
i=0
while not (Rational(1, 2**i) < _duration*x
and _duration*x < Rational(1, 2**(i+1) ))
i++
return i
but I'm using logarithms in get_num_dots anyway, and actually i should
start from -2, so this wouldn't be as simple after all.
>
> 4:
> using floating point values for exact things usually open up enough
> nasty bugs and is very hard to verify due to rounding errors. Unless
> you have VERY good reason, just don't do it, it is not worth the
> trouble.
>
> ===========
>
> Later in your patch:
>
> + factor = self._duration / 2**(-self.get_duration_log())
>
> + return int (round (-math.log(2 - factor) / math.log(2)))
>
> If:
>
> a = 1/2**(-self.get_duration_log())
>
> then:
>
> a = 2**self.get_duration_log()
>
> assuming that get_duration_log() returns a 2log and
> that get_duration() exists, then
This is where this goes wrong. get_duration_log doesn't return 2log of
_duration.
>
> a = self.get_duration()
>
> get_duration() look suspiciously like _duration(), so my guess is that
>
> factor = self._duration ** 2
>
> by quickly reading the code as a math guy.
>
> Now, if duration usually is less than 1 (we don't see many breves and
> longas today), then
>
> -math.log(2 - factor) / math.log(2)
>
> more or less reduces to a near constant -1.
>
> Is that your intention, or am I reading your code false?
> I assume that duration is the lenght of a note, like whole note is 1,
> a quater note is 1/4, ...
>
> Regards,
> /Karl
2**-(get_duration_log) is the length of the base note. So factor is the
real duration relative to the length of the base note. So if you have
c16.., factor is 1.75. You subtract it from 2 and you have 0.25, log2 of
which is -2. And 2 is the number of dots.
This could also be implementer with rational numbers, but only if
durations from input correspond with a proper whole duration or a dotted
length. The advantage of using logarithm is that any duration will be
rounded to the closest representation. So if you have an xml file
generated from music played with a midi keyboard for example, the
scripts does its best to find as best representation for it as possible.
I attached a diff, now with the correction and additional comments.
I hope this cleared things.
Tuukka
musicxml.py.diff
Description: Text Data