Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 89 additions & 0 deletions euouae_check.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# Accepts as arguments file or directory paths,
# checks all gabc files containing antiphons
# for standard EUOUAE lyrics and tones.

import os
import os.path
import sys

import arpeggio
from music21 import converter, volpiano
from music21.text import assembleLyrics
import chant21
from chant21.gabc.converter import AlterationWarning

euouae_tones = {}


class Skip(RuntimeError):
pass


def _check(filename, verbose):
if not filename.endswith('.gabc'):
raise Skip('not a gabc')
if '_' in filename:
raise Skip('submitter (not selected) version')

if verbose:
print('checking {}'.format(filename))

try:
chant = converter.parse(filename)
except (arpeggio.NoMatch, AlterationWarning) as e:
print('ERROR {}: chant21 cannot parse ({})'.format(filename, e))
return

metadata = chant.editorial.metadata
office_part = metadata.get('office-part')
if office_part != 'an':
raise Skip('not an antiphon ({})'.format(office_part))

errors = []

last_section = chant.sections[-1]
lyrics = assembleLyrics(last_section)
has_euouae = 'E U O U A E.' in lyrics
# TODO: is there a rule which ones have EUOUAE and which Et sic finiatur?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EUOUAE is replaced with Et sic finiatur if and only if the GP is omitted at the end of the psalm, which occurs only in Tenebrae (and other hours in the Triduum) and in O.Def.
There are antiphons that I had to duplicate (they are soft-linked through the database website of nocturnale.marteo.fr) because they are used in a context with GP and in another without (e.g. Tenebrae antiphons also used in Our Lady of Sorrows).

if not has_euouae and 'Et sic finiátur.' not in lyrics:
errors.append('unexpected euouae lyrics "{}"'.format(lyrics))

mode = metadata.get('mode')
if mode is None:
errors.append('mode missing')
elif mode not in euouae_tones:
if verbose:
print('differentia {}: setting euouae from {} as standard'
.format(mode, filename))
if not has_euouae:
print('warning: the tone accepted as standard has'
' less-standard EUOUAE lyrics')
euouae_tones[mode] = volpiano.fromStream(last_section)
elif volpiano.fromStream(last_section) != euouae_tones[mode]:
# TODO: further normalization is required to correctly compare
# EUOUAEs with Et sic finiatur's
errors.append('mode {}, expected {}, got {}'
.format(mode, euouae_tones[mode],
volpiano.fromStream(last_section)))

if len(errors) > 0:
print('ERROR {}: {}'.format(filename, '; '.join(errors)))
elif verbose:
print('OK {}'.format(filename))


def check(filename, verbose=False):
try:
return _check(filename, verbose)
except Skip as e:
if verbose:
print('skip {}: {}'.format(filename, e))


for path in sys.argv[1:]:
if os.path.isdir(path):
for root, dirs, files in os.walk(path):
for f in files:
check(os.path.join(root, f))
else:
check(path)