Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RomanText writer doesn't write "slow 3/8" #1417

Open
malcolmsailor opened this issue Sep 9, 2022 · 19 comments · May be fixed by #1418
Open

RomanText writer doesn't write "slow 3/8" #1417

malcolmsailor opened this issue Sep 9, 2022 · 19 comments · May be fixed by #1418

Comments

@malcolmsailor
Copy link
Contributor

music21 version

8.0.0a12

Problem summary

If music21 reads a RomanText file in "slow 3/8", then writes it out again, it writes it in "3/8" (which defaults to "fast 3/8"), omitting the "slow". The output file will likely be unreadable since it may contain beats that are then illegal, e.g., "b2".

Steps to reproduce

>>> from io import StringIO
>>> rntxt = """Time Signature: slow 3/8
... m1 C: I b2 V"""
>>> s = music21.converter.parse(rntxt, format="romanText")
>>> text_stream = StringIO()
>>> s.write("romanText", text_stream)
>>> newrntxt = text_stream.getvalue()
>>> print(newrntxt) # NB not "Slow 3/8"
Composer: Composer unknown
Title: Title unknown
Analyst: 
Proofreader: 

Time Signature: 3/8
m1 C: I b2 V
>>> s2 = music21.converter.parse(newrntxt, format="romanText")
[rest of TraceBack clipped]
    raise RomanTextTranslateException(
music21.romanText.translate.RomanTextTranslateException: too many notes in this measure: m1 C: I b2 V

I will see if I can't fix this myself.

@malcolmsailor malcolmsailor linked a pull request Sep 9, 2022 that will close this issue
@mscuthbert
Copy link
Member

"slow X" vs "fast X" is a music21 convention -- is it in the romanText standard?

I think that romanText should choose one to be the standard (I think it's "slow X") because it's not about encoding the piece, it's about what we expect beat numbers to correspond to.

@malcolmsailor
Copy link
Contributor Author

I guess it's not in the romanText standard but there are roman text files that use both conventions and that are many files labeled "slow" or "fast" respectively in the When In Rome corpus:

malcolm When-in-Rome$ rg --count-matches 'slow 3/8' | wc -l
      29
malcolm When-in-Rome$ rg --count-matches 'fast 3/8' | wc -l
      45
malcolm When-in-Rome$ rg --count-matches ': 3/8' | wc -l
      69

Moreover, understanding of "slow" and "fast" is already manifested by the romanText reader, so

Time signature: slow 3/8
m1 I b2 V

parses fine but

Time signature: 3/8
m1 I b2 V

will raise the exception mentioned in my previous issue.

the standard (I think it's "slow X")

Presently "fast", not "slow" seems to be the default, which is why the above example raises an exception when parsed.

I think that romanText should choose one to be the standard (I think it's "slow X") because it's not about encoding the piece, it's about what we expect beat numbers to correspond to.

Presently, if the reader sees

  • "Time Signature: 3/8" or "Time Signature: fast 3/8" it will set the TimeSignature instance's beatCount to 1 and beatDivisionCountName to "Compound", etc.
  • "Time Signature: 3/8" or "Time Signature: slow 3/8" it will set the TimeSignature instance's beatCount to 3 and beatDivisionCountName to "Simple", etc.

In writing out the file, the writer observes the slow and/or fast distinction (e.g., it will write "b2" in slow 3/8 but "b1.66" in fast 3/8) except for not specifying "slow" or "fast" 3/8. I think the simplest solution, which doesn't contradict the romanText standard (since it's silent on this question) and conforms to the existing corpus of romanText files, is simply to write out "slow" for slow 3/8 as per my PR. This doesn't make an arbitrary choice for the user---the only way that "slow 3/8" will be output is if they either

  • read in a file that was already in slow 3/8 and then write it out again (as per my example), or
  • specify slow 3/8 within music21 then write it out
    Otherwise, the writer will simply write 3/8 and treat it as the default fast 3/8 and the user never has to think about any of this.

@malcolmsailor
Copy link
Contributor Author

Incidentally the reason I am posting this issue is because I am myself getting exceptions in work where I do the following:

  1. read romanText files from the When In Rome corpus
  2. do some processing, then write them out again
  3. read them again later (where I get exceptions)

So it's not just hypothetical that this could cause users issues ;)

@mscuthbert
Copy link
Member

Feel free to loop in Dmitri Tymoczko on email to see what his thoughts are. He's the keeper of the RomanText standard and see what he wants to do for this. My thought though is that RomanText files should always be in fast X, based on the files used to generate original spec. If music21 chooses to parse a non-standard "slow X" it should still write out files in fast X.

I remember now making an argument that slow everything should be the standard because it's easier to parse than 2.66.5 but that's what's in the standard. I'm highly weary of creating conflicting standards for a format so young.

@tymoczko
Copy link

Hi everyone!

I get the problem, 3/8 is ambiguous, as the limit of two different sequences 5/8, 4/8, 3/8 (slow) and 9/8, 6/8, 3/8 (fast).

Personally, I have always assumed 3/8 defaulted to the "slow" version, with three beats (b1, b2, b3), but it seems a bit churlish to force that on everyone. What if we had 3/8 default to "slow" but allowed people to specify "slow" or "fast" if they wanted to be explicit?

Oof, it occurs to me that in principle you have the same issue with 3/4, with 6/4 sometimes meaning two dotted-half beats ... and there are definitely pieces in which each measure functions as one big beat (e.g. the Scherzo of Beethoven's ninth). So probably one wants to implement "slow" vs. "fast" for every 3/x meter.

DT

@malcolmsailor
Copy link
Contributor Author

Personally, I have always assumed 3/8 defaulted to the "slow" version

Me too, see #1185.

What if we had 3/8 default to "slow" but allowed people to specify "slow" or "fast" if they wanted to be explicit?

This is the sort of solution I have in mind, but Myke may not like this because the current implementation defaults to "fast" so there would potentially be people whose files this would break. (We'd have to update the When-in-Rome corpus, which [has been adjusted to the current default[(https://github.com/MarkGotham/When-in-Rome/issues/35), but that's not a big deal.) What about just leaving the default of "fast"? (To be clear, as it currently stands, the music21 roman text parser understands both slow and fast 3/8, with a default of fast; this issue only concerns what its behavior when writing out files should be.)

Oof, it occurs to me that in principle you have the same issue with 3/4

In fact, it appears these are already implemented in music21. So having the romanText writer write them out should "come for free" with having it write out slow/fast 3/8.

In [46]: music21.meter.TimeSignature("slow 3/4").beatCount
Out[46]: 3

In [47]: music21.meter.TimeSignature("fast 3/4").beatCount
Out[47]: 1

In [48]: music21.meter.TimeSignature("slow 3/2").beatCount
Out[48]: 3

In [49]: music21.meter.TimeSignature("fast 3/2").beatCount
Out[49]: 1

The only difference with 3/8 versus 3/4, 3/2, etc., seems to be that 3/8 (as well as 3/16, etc.) defaults to fast:

In [50]: music21.meter.TimeSignature("3/4").beatCount # slow
Out[50]: 3

In [51]: music21.meter.TimeSignature("3/2").beatCount # slow
Out[51]: 3

In [52]: music21.meter.TimeSignature("3/8").beatCount # fast
Out[52]: 1

In [53]: music21.meter.TimeSignature("3/16").beatCount # fast
Out[53]: 1

@MarkGotham
Copy link
Contributor

Thanks all.

In the ISMIR paper, we say:

RomanText-TS

This does not comment specifically on fast vs slow but it's probably most fair to interpret this to mean that the string will accept anything that music21 accepts as a valid for creating a Time Signature object. That viewpoint also has the benefit of being consistent with how rntxt and music21 currently interact. So I'd vote to continue accepting fast/slow and rest assured that the system works as advertised.

I'm happy to write the docs to clarify this point.

On the specific question of writing: I'd vote to keep and write information that's already in there. So (leaving defaults aside), once a user has specified one of the other, that should stay.

In short:

  • accept fast / slow (as currently)
  • lacking this informant, default in the ways the music21 does otherwise (as currently)
  • once a user has specified fast / slow, keep and write that info (TODO).

@malcolmsailor
Copy link
Contributor Author

  • accept fast / slow (as currently)
  • lacking this informant, default in the ways the music21 does otherwise (as currently)
  • once a user has specified fast / slow, keep and write that info (TODO).

This is what my open PR does, except that it doesn't write "fast" in the case of fast 3/8. I don't believe that whether or not a romantext file specified fast 3/8 (as opposed to just relying on it being the default) is saved after reading the romantext file in, and since fast is the default, it isn't strictly necessary. But as I said in the PR, if we wanted, we could have the writer always write out "fast" or "slow" to be fully explicit.

@MarkGotham
Copy link
Contributor

Thanks @malcolmsailor – sounds great!

I can see specifying "fast" argued ether way. No strong feeling from me on best practice there.

@mscuthbert
Copy link
Member

I don't think that "anything that music21 supports" goes is a good idea for a specification. What music21 can parse/read is one thing, what music21's exporter produces puts a burden on other parsers (there is one other parser, and it's written in Max/MSP) to have to be able to read it in.

Since the spec assumes, fast X/Y, but files might have slow X/Y in them, I'm okay with taking a PR that converts slow X/Y TimeSignatures to fast X/Y before writing out the file.

I'm also working on getting the spec in a standard standards' place like W3C so that there will be a process for amending it in the future.

@malcolmsailor
Copy link
Contributor Author

I don't think that "anything that music21 supports" goes is a good idea for a specification.

Fair enough.

Since the spec assumes, fast X/Y

I believe the spec is silent on fast/slow. It's music21 that assumes fast 3/8. From Dmitri's comments above it appears he would have expected slow 3/8 (like me).

@mscuthbert
Copy link
Member

mscuthbert commented Sep 25, 2022

Ah -- okay. -- the music21 3/8, 3/16 interpretation was only changed a version or two ago. I can't remember what the reason was, but it was a pretty convincing one to me.

I didn't see that @tymoczko had weighed in here. My mistake. So we have three active authors of the original paper and another very committed user of the spec here, so maybe we declare a specification revision here. Here are the things I think we should put:


Version 2022.09

2.1 (Metadata) is revised to add an additional metadata line:
"Version: The version of the RomanText standard that the piece(s) have been encoded in, given as a four-digit year with an optional period and two digit month. If omitted, Version 2019 (the original RomanText standard) is assumed."

things that can go anywhere in the file:

Time Signatures:
"Time Signature: Specify time signatures on a separate line in the form of a simple string, of numerator / denominator such as:

Time Signature: 6/8

Time signatures whose numerators are multiples of three may optionally be preceded by either slow or fast indicating whether (slow) the denominator indicates the beat, e.g. 6 beats of eighth notes, or (fast) the next higher hierarchical level gets the beat, e.g. 2 beats of dotted-quarter notes. The expressions slow or fast are not to be used for non-multiple-of-three numerators.

If unspecified, the time signature is assumed to be slow if the numerator is 3, or fast for all other multiples of 3.

Mid-measure time signature changes and multiple simultaneous time signatures are not currently supported in RomanText. Analysts should encode the entire bar as if measured in one time signature or another and add a Note tag.

If no Time Signature is specified, 4/4 is assumed.


thoughts?

@malcolmsailor
Copy link
Contributor Author

As a user of the standard, that looks good to me.

For revisions that consist of specifying previously unspecified behavior (like fast vs slow 3/8), will we simply implement them for Version 2019 as well?

@tymoczko
Copy link

I like this too! Thanks!

@mscuthbert
Copy link
Member

For revisions that consist of specifying previously unspecified behavior (like fast vs slow 3/8), will we simply implement them for Version 2019 as well?

yeah, basically. I think the general rule with specs the make something that was ambiguous later clear is: the behavior was underdefined before so implementations are free to choose what they want to do for older files, but it'd probably be better if you follow the later specification since obviously that's what the community decided was more reasonable/logical/easier-to-implement or what-not.

Basically, you don't need to look at the version number so long as some version doesn't contradict a spec from a previous version. Like if we stupidly decided in 2022.10 that "a roman numeral w/o a beat sign is assumed to be beat 2" then we'd need to look at the version number to see if it's above or below the threshold of contradiction.

So in m21 we'll override the current RomanText interpretation for 3/8 to make sure that it's slow 3/8, but then we should write that out as "slow 3/8" with a "Version: 2022.09" metadata tag as well.

Here's a small excerpt from Mozart K280 movement 3 that we can use for 3/8 testing, since we don't have one in the practice set. (It also lets us add a "Pedal" indication from a real excerpt, which is probably something to standardize in the next revision, both in the format of the "Pedal: mX bN mY bM" and also -- just noticing, does that get copied with the "m21-24=m17-20" since it precedes m17, AND once the format is parsable, will the pedal appear?).

Movement: 3
Tempo: Presto
Time signature: 3/8
Version: 2022.09

Form: exposition
m1 F: I
m2 V4/3
m3 I
m4 V2
m5 I6
m6 ii6
m7 ii b2 ii6 b3 viio/V
m8 V
m9-14 = m1-6
m15 I6/4 b3 V
m16 I
Pedal: F m17 b1 m19 b3
m17 IV b2 I b3 viio
m17var1 vii/o7
m18 I
m19 I b2 ii b3 viio
m20 I
m21-24 = m17-20

@malcolmsailor
Copy link
Contributor Author

I noticed that the current roman text implementation uses numeric version numbers, e.g.,

ROMANTEXT_VERSION = 1.0

elif lineToken.isVersion():

(note in particular the cast to float)

I like the date-based versioning since it's more semantically meaningful. Do we need to worry about the change, or do anything other than support "1.0" (as far as I know there was never a higher version?) as a version as well as date-based versions?

@tymoczko
Copy link

I am happy to switch to year. I don't use the versioning at all.

@mscuthbert
Copy link
Member

I had no idea I put it in there in the code -- it didn't make the spec. We'll use the RTVersion: tag but switch to a year going forward. Thankfully 2022.09 > 1.0 :-)

@malcolmsailor
Copy link
Contributor Author

So I assume the right place to put the RTVersion is in some kind of metadata. @mscuthbert, do you have any suggestions about the best way of doing that? (I've never worked with metadata in music21 before). Or do you want to look after that part yourself?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants