# ------------------------------------------------------------------------------
# Name: noteGrouping.py
# Purpose: Transcribes note groupings into Braille
# Authors: Jose Cabal-Ugaz
# Michael Scott Asato Cuthbert
#
# Copyright: Copyright © 2012, 2016 Michael Scott Asato Cuthbert
# License: BSD, see license.txt
# ------------------------------------------------------------------------------
from __future__ import annotations
from collections import OrderedDict
import unittest
from music21 import clef
from music21 import dynamics
from music21 import environment
from music21 import expressions
from music21.braille import basic
from music21.braille.lookup import symbols
environRules = environment.Environment('braille/noteGrouping.py')
[docs]
class NoteGroupingTranscriber:
'''
Object that can transcribe note groupings.
'''
def __init__(self, brailleElementGrouping=None):
self.showLeadingOctave = True
self.brailleElementGrouping = brailleElementGrouping
self._upperFirstInFingering = None
self._showClefSigns = None
# duplicate of reset
self.previousNote = None
self.previousElement = None
self.trans = []
self.transText = None
[docs]
def reset(self):
self.previousNote = None
self.previousElement = None
self.trans = []
self.transText = None
@property
def showClefSigns(self):
'''
Generally, in Braille, clef signs are not used. However, they
can be shown for pedagogical purposes or to make a facsimile
transcription of the sighted text.
If not set but self.brailleElementGrouping.showClefSigns
is set, uses that instead.
>>> ngt = braille.noteGrouping.NoteGroupingTranscriber()
>>> ngt.showClefSigns
False
>>> beg = braille.segment.BrailleElementGrouping()
>>> ngt.brailleElementGrouping = beg
>>> ngt.showClefSigns
False
>>> beg.showClefSigns = True
>>> ngt.showClefSigns
True
>>> ngt.showClefSigns = False
>>> ngt.showClefSigns
False
'''
if self._showClefSigns is not None:
return self._showClefSigns
elif self.brailleElementGrouping is not None:
return self.brailleElementGrouping.showClefSigns
else:
return False
@showClefSigns.setter
def showClefSigns(self, new):
self._showClefSigns = new
@property
def upperFirstInFingering(self):
'''
When there are multiple fingering patterns listed at the same time,
should the highest be listed first (default True) or last?
If not set but self.brailleElementGrouping.upperFirstInNoteFingering
is set, uses that instead. (note the slight difference in names: NoteFingering)
>>> ngt = braille.noteGrouping.NoteGroupingTranscriber()
>>> ngt.upperFirstInFingering
True
>>> beg = braille.segment.BrailleElementGrouping()
>>> ngt.brailleElementGrouping = beg
>>> ngt.upperFirstInFingering
True
>>> beg.upperFirstInNoteFingering = False
>>> ngt.upperFirstInFingering
False
>>> ngt.upperFirstInFingering = True
>>> ngt.upperFirstInFingering
True
'''
if self._upperFirstInFingering is not None:
return self._upperFirstInFingering
elif self.brailleElementGrouping is not None:
return self.brailleElementGrouping.upperFirstInNoteFingering
else:
return True
@upperFirstInFingering.setter
def upperFirstInFingering(self, new):
self._upperFirstInFingering = new
[docs]
def transcribeGroup(self, brailleElementGrouping=None):
'''
transcribe a group of notes, possibly excluding certain attributes.
Returns a (unicode) string of brailleElementGrouping transcribed.
'''
self.reset()
if brailleElementGrouping is not None:
self.brailleElementGrouping = brailleElementGrouping
for brailleElement in self.brailleElementGrouping:
self.transcribeOneElement(brailleElement)
if brailleElementGrouping.withHyphen:
self.trans.append(symbols['music_hyphen'])
return ''.join(self.trans)
[docs]
def translateNote(self, currentNote):
if self.previousNote is None:
doShowOctave = self.showLeadingOctave
else:
doShowOctave = basic.showOctaveWithNote(self.previousNote, currentNote)
brailleNote = basic.noteToBraille(currentNote,
showOctave=doShowOctave,
upperFirstInFingering=self.upperFirstInFingering)
self.previousNote = currentNote
return brailleNote
[docs]
def translateRest(self, currentRest):
return basic.restToBraille(currentRest)
[docs]
def translateChord(self, currentChord):
allNotes = sorted(currentChord.notes, key=lambda n: n.pitch)
if self.brailleElementGrouping.descendingChords:
currentNote = allNotes[-1]
else:
currentNote = allNotes[0]
if self.previousNote is None:
doShowOctave = self.showLeadingOctave
else:
doShowOctave = basic.showOctaveWithNote(self.previousNote, currentNote)
descendingChords = self.brailleElementGrouping.descendingChords
brailleChord = basic.chordToBraille(currentChord,
descending=descendingChords,
showOctave=doShowOctave)
self.previousNote = currentNote
return brailleChord
[docs]
def translateDynamic(self, currentDynamic):
brailleDynamic = basic.dynamicToBraille(currentDynamic)
self.previousNote = None
self.showLeadingOctave = True
return brailleDynamic
[docs]
def translateTextExpression(self, currentExpression):
brailleExpression = basic.textExpressionToBraille(currentExpression)
self.previousNote = None
self.showLeadingOctave = True
return brailleExpression
[docs]
def translateBarline(self, currentBarline):
return basic.barlineToBraille(currentBarline)
[docs]
def translateClef(self, currentClef):
'''
translate Clefs to braille IF self.showClefSigns is True
'''
if self.showClefSigns:
brailleClef = basic.clefToBraille(currentClef)
self.previousNote = None
self.showLeadingOctave = True
return brailleClef
translateDict = OrderedDict([
('Note', translateNote),
('Rest', translateRest),
('Chord', translateChord),
('Dynamic', translateDynamic),
('TextExpression', translateTextExpression),
('Barline', translateBarline),
('Clef', translateClef),
])
[docs]
def transcribeOneElement(self, el):
'''
Transcribe a single element and add it to self.trans, setting self.previousElement
along the way.
>>> ngt = braille.noteGrouping.NoteGroupingTranscriber()
>>> n = note.Note('C4')
>>> ngt.transcribeOneElement(n)
>>> ''.join(ngt.trans)
'⠐⠹'
>>> ngt.previousElement
<music21.note.Note C>
'''
elClasses = el.classes
for className, classMethod in self.translateDict.items():
if className in elClasses:
addBraille = classMethod(self, el)
if addBraille is not None:
self.trans.append(addBraille)
break
else:
environRules.warn(f'{el} not transcribed to braille.')
self.optionallyAddDotToPrevious(el)
self.previousElement = el
[docs]
def optionallyAddDotToPrevious(self, el=None):
'''
if el is None or not a Dynamic or TextExpression, add a dot-3 Dot
before the current transcription
under certain circumstances:
1. self.previousElement exists
2. the last character in the current transcription (self.trans) fits the criteria for
basic.yieldDots()
3. one of these three. PreviousElement was:
a. a Dynamic.
b. a Clef and clef signs are being transcribed
c. a TextExpression not ending in "."
Returns True if a dot as added, or False otherwise.
'''
prev = self.previousElement
if prev is None:
return False
if not self.trans:
return False # need to consult previous element in translation
if el is not None and isinstance(el, dynamics.Dynamic):
return False
if el is not None and isinstance(el, expressions.TextExpression):
return False
if (isinstance(prev, dynamics.Dynamic)
or (isinstance(prev, clef.Clef)
and self.showClefSigns)
or (isinstance(prev, expressions.TextExpression)
# TE is an abbreviation, no extra dot 3 necessary
and prev.content[-1] != '.')):
for dot in basic.yieldDots(self.trans[-1][0]):
self.trans.insert(-1, dot) # insert one before the end, not append
prev.editorial.brailleEnglish.append(f'Dot 3 {dot}')
return True # only append max one dot.
return False
[docs]
def transcribeNoteGrouping(brailleElementGrouping, showLeadingOctave=True):
'''
transcribe a group of notes, possibly excluding certain attributes.
To be DEPRECATED -- called only by BrailleGrandSegment now.
'''
ngt = NoteGroupingTranscriber()
ngt.showLeadingOctave = showLeadingOctave
return ngt.transcribeGroup(brailleElementGrouping)
# ------------------------------------------------------------------------------
class Test(unittest.TestCase):
pass
if __name__ == '__main__':
import music21
music21.mainTest(Test) # , runTest='testGetRawSegments')