musescore / MuseScore

MuseScore is an open source and free music notation software. For support, contribution, bug reports, visit MuseScore.org. Fork and make pull requests!
https://musescore.org
Other
12.06k stars 2.6k forks source link

Support loading .scl and .kbm files #19564

Open DaffodilAura opened 11 months ago

DaffodilAura commented 11 months ago

Your idea

Basically, I'm thinking that we need support for MTS-ESP, as well as .scl files and .kbm files so that MuseScore can support existing mapping tables, and we could also benefit from both having multiple mapping channels, so that lumatone users might be better able to use MuseScore, and so that advanced tuning systems can be supported.

Problem to be solved

Currently, retuning MuseScore's notes for other tuning systems involves manually retuning each note by cents and then copy-pasting- this is a laborious and error-prone process. Furthermore, the current method of tuning in cents is more limited in precision, which is sometimes a problem for advanced equal temperament systems. Having support for these additional file types would make the process of writing in other tuning systems much easier.

Prior art

Most notation software has very limited support for microtonality, while the few DAWs that support microtonality are generally limited by having only 128 notes available in MIDI, thus necessitating multiple piano rolls for more advanced tuning systems such as 159tet and 311tet, the latter of which is renowned for its ability to approximate Just Intonation. MuseScore, with the right innovations, could help microtonal composers get past the aforementioned limitations. So far, Oddsounds has support for other tunings systems, and we could use portions of that library as a basis for handling internal standards.

https://github.com/ODDSound/MTS-ESP

Additional context

I've been writing microtonal music in MuseScore for a few years now, especially in 159tet, so I know some of the problems that I face.

lyrra commented 11 months ago

See this PR: https://github.com/musescore/MuseScore/pull/19572

Which add supports for a tuning map file:

mscore --tuning ji.json

And the tuning file ji.json is a JSON file that maps MIDI_Pitch to cents. Example:

{
  "74": 73,
  "73": -58,
  "72": 69,
  "71": 62,
  "70": 2,
  "69": 75,
  "68": -56,
  "67": 71,
  "66": -60,
  "65": 4,
  "64": 77,
  "63": 0
}

By default if given a --tuning argument, it will ignore your scores note tunings. If you would still like to adjust some notes, add the option --tuningmode:

mscore --tuning ji.json --tuningmode=add

And if you would like to transfer the tunings from the tuningmap to the score (NOT IMPLEMENTED):

mscore --tuning ji.json --tuningmode=write

It doesn't solve the problem of being limited to 12 notes per octave. A possible solution is to look at the note symbol.

An audio example of using above tuningfile: https://audio.com/larryz/audio/tune

DaffodilAura commented 11 months ago

What do you mean by looking at note symbols? Do you mean accidentals?

lyrra commented 11 months ago

I've pushed a change to the PR, that adds tuning support for accidentals. This is how the score looks like: bild

Those accidentals are mapped from the json file (--tuning ):

{
  "accidental1CommaFlat": -10,
  "accidental2CommaFlat": -20,
  "accidental3CommaFlat": -30,
  "accidental4CommaFlat": -40,
  "accidental1CommaSharp": 10,
  "accidental2CommaSharp": 20,
  "accidental3CommaSharp": 30,
  "accidental5CommaSharp": 40,
  "midi-74": 73,
  "midi-73": -58,
  "midi-72": 69,
  "midi-71": 62,
  "midi-70": 2,
  "midi-69": 75,
  "midi-68": -56,
  "midi-67": 71,
  "midi-66": -60,
  "midi-65": 4,
  "midi-64": 77,
  "midi-63": 0
}

In this tuning-file, those key-value pairs where the key begin with midi- will be used for all "regular 12-key" notes, unless an microtonal accidental is used. So in the example score, these will be used in the lowers bars in the picture. And the other key-value pairs in json file, are the accidental names, so in this example, for example "accidental1CommaFlat" is mapped to the accidental shown in the first bar, and therefore that note is tuned accordingly. So these will be used at the upper bars in the score.

Here's an updated audio example of the same score: https://audio.com/larryz/audio/xz

DaffodilAura commented 11 months ago

I see. I wonder if you can customize the microtonal accidentals, or if you can input tunings on the MIDI notes such as, say, 15.09 cents flat...

lyrra commented 11 months ago

I see. I wonder if you can customize the microtonal accidentals, or if you can input tunings on the MIDI notes such as, say, 15.09 cents flat...

This is exactly what the PR does. The json file is used to customize the cents for each accidental. And also normal midi notes can be tuned.

As it is, this solution is rather "bolted-on", instead of working with the ACC_LIST in src/engraving/dom/accidental.cpp, and adjusting that, it builts its own mappings.

You can try it out by downloading the PR;s built artifact. This would save you from having to copy paste tunings manually, which sounds dreadful.

DaffodilAura commented 11 months ago

Manual tuning and copy pasting is difficult for sure, but the only way to cut that out completely for systems like 159tet is to be sure you can modulate to different subsets without having to change the tuning of all the notes every time you change keys.

lyrra commented 11 months ago

systems like 159tet is to be sure you can modulate to different subsets without having to change the tuning of all the notes every time you change keys.

I have a bunch of more tricky questions, hope it is OK I ask you these. Others are welcome to to chime in too!

When you say modulate, do you mean that the "same looking" notes tuning is not only dependent on its accidental, but also on its implicit musical context (whereas a key change would be explicit given the new key symbol). Another more subtler example could be in an a cappella setting where the singer stays in just intonation, which wouldn't be notated in the score.

In general, mechanically taking the tuning from the accidental symbol might be wrong. Those arent "tuning-accidental", but first class notes. And first class notes (like 12 TET), can receive multiple tunings within one piece, as stated above.

Could one possible solution be that you annotate in the score (per staff), whenever a tuning-subset change occurs, that could then read in the octave tunings during playback?

I would like to gather all possible use cases, preferably in a score.

One problem with relying on external files like json or .scl and .kbm, is that it isn't future-proof. We could load those file, and then copy the tunings, to be saved inside the score file.

DaffodilAura commented 11 months ago

I have a bunch of more tricky questions, hope it is OK I ask you these. Others are welcome to to chime in too!

Go ahead.

When you say modulate, do you mean that the "same looking" notes tuning is not only dependent on its accidental, but also on its implicit musical context (whereas a key change would be explicit given the new key symbol). Another more subtler example could be in an a cappella setting where the singer stays in just intonation, which wouldn't be notated in the score.

In general, mechanically taking the tuning from the accidental symbol might be wrong. Those arent "tuning-accidental", but first class notes. And first class notes (like 12 TET), can receive multiple tunings within one piece, as stated above.

Notes being tuned based on musical context is inevitable in any tuning system though generally the ability of a system to represent pitches accurately controls which sorts of JI intervals get used in performance, while the size of the equal-step tuning system's smallest interval controls how the bandwidth of sounds is covered. In 159tet, for instance, you wouldn't have contextual deviations from the center of the pitch by more than 3 cents- that is, unless you count high-bandwidth sounds such as choir and strings, in which case the accidental controls the center pitch of the sound sample. In systems with fine distinctions such as 159tet, some of what you're describing is literally captured by the accidentals themselves, for instance, in a C Major scale, an E that is tuned closer to just would literally be notated in 159tet as E-Synflat, that is, an E that's been lowered by a Syntonic comma (see https://en.wikipedia.org/wiki/Syntonic_comma for details on this comma), while an E-Natural would approximate the E of Pythagorean tuning.

Could one possible solution be that you annotate in the score (per staff), whenever a tuning-subset change occurs, that could then read in the octave tunings during playback?

Given what I said above, I'm not entirely sure this would be a viable solution outside of cases where one literally changes tuning systems mid-piece or uses multiple tuning systems at once- something I've seen some Xen musicians do. An additional reason for this is that sometimes, I personally will use two or more notes each separated roughly by a syntonic comma as part of musical gestures such as acciaccaturas, appoggiaturas, or very quick passing tones, all within a single measure.

I would like to gather all possible use cases, preferably in a score.

This is indeed important to do.

One problem with relying on external files like json or .scl and .kbm, is that it isn't future-proof. We could load those file, and then copy the tunings, to be saved inside the score file.

This is good to know, but I think it would be wise to gather all the use cases before we nail down the solution here.

tank-trax commented 9 months ago

as a microtonal composer I am watching this with great interest

baconpaul commented 9 months ago

If you do decide to support scl/kbm rather than a cents-per-note JSON file, https://github.com/surge-synthesizer/tuning-library is a battle tested SCL/KBM -> frequency table / cents offset / etc.... library.

I know nothing about microtonal staff notation but have grappled with SCL/KBM and MTS-ESP quite a bit and am happy to help if I can.

baconpaul commented 9 months ago

One problem with relying on external files like json or .scl and .kbm, is that it isn't future-proof. We could load those file, and then copy the tunings, to be saved inside the score file.

This is a real issue. The way we dealt with it in surge and our other synths is: if you have a .scl/.kbm file (which are very small text files - almost always less than 1k sometimes way less) we just stream them into our state. I think muse-score uses an XML store right? So just add the scl/kbm as a cdata block directly or what not and then you can get the scale intent back (and take the score and analyze the scale in other tools). Just a thought but agree score + tuning is an atomic bundle of content.