Closed Scowluga closed 3 years ago
...although... :smile: After having begun to look at #364, I wonder if maybe this post-processing step might be a better place to apply the transformation where we turn notes expressed as MIDI note numbers into octave changes and notes expressed as letters + accidentals + durations. It seems like your idea with the post-processing step in the importer is to take the more "direct" output of the MusicXML importer and massage it into a series of ScoreUpdates that will be translated into more idiomatic Alda.
I don't have a strong preference either way, but I think I would tend to lean towards doing this as a post-processing step of the MusicXML importer. It might be advantageous if we kept the code gen part more general (i.e. less specific to importing from MusicXML), so that we could potentially reuse it as-is sometime down the road if we decide to implement other kinds of importers.
Thoughts?
I'm not opposed to doing the mapping in the postprocessor. The only thing stopping us would be if there is eventually going to be some Alda representation that allows you to define notes using their midi pitches (directly, not using Lisp). I don't think this will be the case, so I'll go ahead and add a mapping in the postprocessor (optimizer).
We want to add support for unpitched percussion instruments in MusicXML import.
These unpitched instruments work differently from normal instruments. See https://en.wikipedia.org/wiki/General_MIDI#Percussion. Basically, all unpitched instruments use the same
midi-percussion
instrument, but each correspond to a specific pitch. So "pitch" for themidi-percussion
instrument does not actually correspond to a pitch, but instead a different unpitched instrument.Handling Percussion Parts
One possible way to handle unpitched percussion parts is to import all of them into a single instance of
midi-percussion
, but with many different voices. This works since Alda supports an unbounded number of voices (instead of the usual 4). This works, but runs into some problems. The biggest one is that is it not easy to separate voices between different parts. Voices 1-4 could be for one part, then 5-7 could be for a separate part, etc. There is no part-wise differentiation in this singlemidi-percussion
part, so the grouping of voices can be confusing. All the voices seem the same, but really they should be grouped by parts.I've gone with what I think is a better solution. I spawn separate
midi-percussion
instruments for each MusicXML part. This maintains thescore-partwise
integrity where eachscore-part
corresponds to one Alda part. To differentiate the now multiplemidi-percussion
parts, I use the Alda aliasing mechanism. This is a lot more clear now, as different parts can still have voices, and each voice corresponds to a part.Now obviously any
midi-percussion
part is different than normalmusicXMLPart
s. So I've added two parameters that provide additional information for unpitched percussion parts:Now when we reach an unpitched note, we will be able to access this
unpitched
mapping in the current part to determine the correct note number (pitch).Handling Note Pitches
Ok now we have a number, how do we convert this to an Alda note pitch? One option is to use the normal
model.PitchIdentifier
and create some sort of translation. The examplepercussion.alda
file showcases this.However, Alda supports the
model.MidiNoteNumber
pitch structure. This is precisely what we want. I believe it makes more sense to import into this direct note number pitch instead of doing some mapping. Perhaps the mapping can be accomplished elsewhere. That would not be the job of the importer.I think it's actually going to be interesting to see how this plays out in code generation. Since
model.MidiNoteNumber
can only arise from MusicXML import or from Lisp lists, we can even hardcode this code generation. This is a discussion for next time.Testing - Dealing with Barlines with
standardizeBarlines
When testing we run into a familiar enemy: the barline. Alda parsers
model.Barline
into the[]model.DurationComponent
of the most recent update with a duration. This is so we can deal with ties across barlines. We've run into this problem multiple times.I've decided to solve this problem once and for all by introducing the
standardizeBarlines
method which processes[]model.ScoreUpdate
s and places barlines in a standard location. I've extracted bothstandardizeBarlines
andevaluateLisp
into the utility file. Postprocessing will need to standardize barlines within the importer.