
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