Re: [Haskell-cafe] music-related problem

erik flister wrote:
I am dealing with ties because I am converting a MusicXML document into a more natural form for my purposes. The initial form of the document will have tied notes (as it comes that way from MusicXML), and I want to convert that into a form that makes it possible to ignore ties and see notes as having a single duration.
but can't you just say that the first note of a tie has the indicated start time and a duration which is the sum of the tied notes? i don't know musicXML, but i'm not seeing why that would be hard...
That's what I want to do. I'm asking about a good way to write the algorithm that traverses the notes and reconstructs the document with the correct duration in each note. Actually, I don't want to lose information about the original form of the document, so I have separate fields for the duration of the graphical single note, and the duration of the tied chain. Actually, it is better to speak of the end time than the duration, because what units do you put duration in? Beats? The time signature could be changing measure to measure. MusicXML "position"? The meaning of "one position" changes measure to measure and can be different in different parts. It can get confusing. So I use this concept of "location": data Loc = Loc Int Rational -- measure number and beat within the measure I.e. measure 1, beat 2 measure 7, beat 3 1/2 I use Rational so there is no worry about precision of Floats. Thanks, Mike

That's what I want to do. I'm asking about a good way to write the algorithm that traverses the notes and reconstructs the document with the correct duration in each note.
why isn't this as simple as foldr'ing something like this over the xml notes (assuming monophony and that they are listed in order of start time)? readNote xmlNote nl | null nl = mkNote : [] | (n:ns) <- nl = if not $ followedByTie xmlNote then mkNote : nl else n {dur = dur n `durPlus` xdur} : ns where xdur = getDur xmlNote mkNote = Note {dur = xdur} durPlus would be fun to implement for the NoteDur class... is polyphony the crux of the problem? if so, then the simple list data structure shown here won't work, you need a graph, as mentioned by serguey. but the idea is the same.
Actually, I don't want to lose information about the original form of the document, so I have separate fields for the duration of the graphical single note, and the duration of the tied chain.
i think that's asking for trouble -- i would recompute the ties myself -- it's a matter of engraving style what the rules about ties are anyway. if you insist on being faithful to the input's ties, i would add some sort of reference to the note tiedTo (and maybe the note tiedFrom) to the Note record, but that means giving each note some sort of name or key or index.
Actually, it is better to speak of the end time than the duration, because what units do you put duration in? Beats?
see my EDSL. duration is in units like (Dotted $ Triplet Half), which is the natural musician's (and engraver's) unit. the time signature specifies the number of beats per quarter. if you store end location instead, you'll need to have some (start -> end -> duration) which has to know how to cross measure boundaries, which is unnecessarily complex, especially with changing time signatures.
The time signature could be changing measure to measure.
yeah i don't deal with changing time signatures or tempos. but it's easy to see how to handle time signature changes, since that can only change at a measure boundary: data Measure = Measure { notes :: [Note] , timeSig :: TimeSig } tempo changes are trickier (esp if allowed mid-measure?), but only matters if you need to resolve onsets/durations/offsets to real-world time. still looks straightforward...
I use Rational so there is no worry about precision of Floats.
forall x. (Real x, Fractional x) => x if you're picky. Rationals are not locally compact and are not a complete metric -- almost all Reals are irrational. :) -e

erik flister wrote:
That's what I want to do. I'm asking about a good way to write the algorithm that traverses the notes and reconstructs the document with the correct duration in each note.
why isn't this as simple as
Erik, I'm learning from your code examples, but I don't understand how this turned into an argument about simplicity. I never said it was complex. I do admit it's probably simpler than my initial approach. Maybe I should take a step back and describe my goals. I am experimenting with algorithms for putting a "human touch" on midi playback. I will use Finale to enter the music, then export as MusicXML to my software. Getting from MusicXML---which is a complex format that encompasses everything from notational subtleties to performance subtleties---into a relatively simple internal representation is truly the easy part. The real meat of my project is the code to perform the music.
foldr'ing something like this over the xml notes (assuming monophony and that they are listed in order of start time)?
In a MusicXML document, you can't assume - that the notes are in order of start time - monophony - that two tied notes are immediately adjacent in the data None of that is true. I conceive of this problem as multiple passes. On the first pass, I translate XML notes into a convenient internal representation, on the second pass sort them, then make a pass to figure out ties. Whether this is the best approach, I do not know. I am here to learn. Regarding my use of Rational, it's because I'm representing *notated* durations or positions in time, which are always fractions of integers. Suppose I give the command to my program to play via midi everything from bar 1 beat 1 to bar 2 beat 2 1/2. I want to use Rational so I know 2 1/2 means 2 1/2 and not 2.49999999999. (*) However, *performed* times are a matter of tempo, accel, rit, trills, tremolo, arpeggio etc., and are probably best conceived as Real. I don't quite get this:
forall x. (Real x, Fractional x) => x if you're picky.
Thanks, Mike (*) I may want to use the location of a note (bar and beat) as the key in a Map, which I believe should be done with Rational and not Float.

I don't understand how this turned into an argument about simplicity. I never said it was complex.
i wasn't arguing, just confused about what you were asking cuz i didn't see what wasn't straightforward. so i addressed the straightforward interpretation in order to ask what that was missing. :) looks like polyphony is the issue, so i think you need a directed acyclic graph (DAG). http://hackage.haskell.org/packages/archive/containers/0.3.0.0/doc/html/Data... since every note has at most one pre-Tie and one post-Tie, i guess you don't need a general DAG -- but a graph library would have the algorithms for walking the Ties (ie, dfs, components, reachable, etc. in the mentioned library). I conceive of this problem as multiple passes. On the first pass, I
translate XML notes into a convenient internal representation, on the second pass sort them, then make a pass to figure out ties. Whether this is the best approach, I do not know.
sure that makes sense, i was just addressing the last pass, the matter of wiring up the tie references.
Regarding my use of Rational, it's because I'm representing *notated* durations or positions in time, which are always fractions of integers. Suppose I give the command to my program to play via midi everything from bar 1 beat 1 to bar 2 beat 2 1/2. I want to use Rational so I know 2 1/2 means 2 1/2 and not 2.49999999999.
i wasn't suggesting anything Numeric for durations -- those are NoteDurs like (Dotted $ Triplet Half). you don't need numerics until resolving temporal locations, like milliseconds or subdivisions of a beat. those may be irrational numbers (consider if the tempo is irrational, or tiny random jitter in timing) -- though it's a totally pedantic point on my part and realistically won't matter. ;)
I don't quite get this:
forall x. (Real x, Fractional x) => x if you're picky.
just being agnostic about the true representation as long as you have (/), compare, realToFrac, toRational. the client can choose some concrete representation like Rationals, Floats, Doubles, some Fixed resolution, some C type, something they make up, etc. space or speed or compatibility may trump precision for some applications. (*) I may want to use the location of a note (bar and beat) as the key in a
Map, which I believe should be done with Rational and not Float.
location needs subdiv, which is (very pedantically) not Rational. :) -e

erik flister wrote:
Michael Mossey wrote:
Regarding my use of Rational, it's because I'm representing *notated* durations or positions in time, which are always fractions of integers. Suppose I give the command to my program to play via midi everything from bar 1 beat 1 to bar 2 beat 2 1/2. I want to use Rational so I know 2 1/2 means 2 1/2 and not 2.49999999999.
i wasn't suggesting anything Numeric for durations -- those are NoteDurs like (Dotted $ Triplet Half). you don't need numerics until resolving temporal locations, like milliseconds or subdivisions of a beat. those may be irrational numbers (consider if the tempo is irrational, or tiny random jitter in timing) -- though it's a totally pedantic point on my part and realistically won't matter. ;)
We must be addressing different problems. My software doesn't have much interest in the concept of eighth notes or dotted notes. What I want to do is process a musical document to answer questions like this: - what notes have onset times between measure 1 beat 1 and measure 1 beat 3? - organize the document into verticals: notes that occur at the same time in any part - what notes finish sounding before measure 4? In music, the passage of time has two meanings. One meaning is provided by the notation: on what beats notes occur and how they last (in terms of beats). This is independent of tempo, rit, accel. Call this "score time". The other meaning is "real-world" performance in which tempo, rit, accel, trills and tremolos are realized. Call this "real time". For the first meaning, my program will find it simple and useful to represent time as measure (Int) and beat (Rational --- or perhaps anything in class Fractional (I have to study this more)). If I tried to represent "score time" as eighth notes or whatever it would drive me crazy. I have no need to do that. (Note that this level of time representation is not intended for a human interface.) Score time can involve fractions composed from numbers greater than four---like 7-tuplets. But to my knowledge, there is no way to notate something that cannot be represented by a fraction. "Real time" is better represented as floating point. It is derived from tempos and tempo maps. Thanks, Mike
participants (2)
-
erik flister
-
Michael Mossey