humdrum: remove unneeded Regexes, fix partial-beam direction#1944
Conversation
…ringToNote Bug fix: a lowercase `k` is a partial beam to the *left* and an uppercase `K` to the *right*, but both were appended as `'right'`. `k` now produces a left partial beam. Bumps the beta version since this changes kern parsing output (and so invalidates pickled-stream caches). Performance/cleanup, with identical output: - accidentals: count '#'/'-' directly (the count is the alteration) and build a pitch.Accidental, instead of two `re.search` calls; - rational durations: only run the `\d+%\d+` search when '%' is present; - pitch: only search when the event is not a rest (walrus in the elif). This cuts re.search calls for a mazurka6 parse from 5974 to 3435 (about -40%). Also, from ideas in PR #1706 (Alexander Morgan): comment out the phrase-mark, slur, and arpeggiation blocks that only ran `pass`, and rename the unused beaming loop variable from `i` to `_`. AI-assisted (Claude).
| # than allocating a copy; one that has its own duration replaces it below. | ||
| thisObject: note.GeneralNote | ||
| if 'r' in contents: | ||
| thisObject = note.Rest(duration=defaultDurationOrNone) |
There was a problem hiding this comment.
Even if you don't want to factor out duration evaluation to its own function, I would still recommend doing it before anything else in this hdStringToNote function. The reason is that, e.g. for the case of rests here on 2366, you're still creating an initial duration that will just get replaced with the actual duration when you calculate it at the end of the function. So that's still 2 duration objects per rest which might account for the speedup being a little less than what I measured (10-12%). But that will increase the diff size quite a bit.
| @@ -82,6 +82,15 @@ def testSingleNote(self): | |||
| self.assertEqual(b.duration.dots, 0) | |||
| self.assertEqual(b.duration.tuplets[0].durationNormal.dots, 2) | |||
|
|
|||
There was a problem hiding this comment.
I had added these 4 tests and I would recommend keeping them to cover the new functionality. I had to add KernSpine.parse() because the new version of the KernSpine class got rid of the __init__ constructor, so some of the class properties were only available if I called .parse() first.
If you don't like calling .parse() there's probably a different way to run these tests, but I would definitely keep the tests in some form.
def testGraceNote(self):
ks = KernSpine()
ks.parse()
# noinspection SpellCheckingInspection
a = ks.processNoteEvent('4Cq')
self.assertEqual(a.duration.type, 'quarter')
self.assertEqual(a.duration.slash, True)
def testGraceNote2(self):
ks = KernSpine()
ks.parse()
# noinspection SpellCheckingInspection
a = ks.processNoteEvent('16Cqq')
self.assertEqual(a.duration.type, '16th')
self.assertEqual(a.duration.slash, False)
def testChord(self):
ks = KernSpine()
ks.parse()
# noinspection SpellCheckingInspection
c = ks.processChordEvent('8C 8E')
self.assertEqual(len(c.notes), 2)
self.assertEqual(c.notes[0].duration, c.duration)
def testChord2(self):
# noinspection SpellCheckingInspection
ks = KernSpine()
ks.parse()
c = ks.processChordEvent('8C E')
self.assertEqual(c.notes[0].duration, c.notes[1].duration)
Third and last PR broken out from #1706
Reduce the number of regex searches needed to parse a humdrum scope -- Chopin moves from 6000 to 3400. Simplify the code and make it more elegant.
But barely moves on performance. hdStringToNote speeds up by 5% 8.59 µs/call to 8.14. Full parse speeds up by about 1%
Performance/cleanup, with identical output:
re.searchcalls;\d+%\d+search when '%' is present;Also, from ideas in PR #1706 (Alexander Morgan): comment out the phrase-mark, slur, and arpeggiation blocks that only ran
pass, and rename the unused beaming loop variable fromito_.AI-assisted (Claude). Read closely by Myke -- co-author Alexander Morgan