/
noteGrouping.py
289 lines (242 loc) · 9.87 KB
/
noteGrouping.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
# -*- coding: utf-8 -*-
# ------------------------------------------------------------------------------
# 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')
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
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
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)
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
def translateRest(self, currentRest):
return basic.restToBraille(currentRest)
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
def translateDynamic(self, currentDynamic):
brailleDynamic = basic.dynamicToBraille(currentDynamic)
self.previousNote = None
self.showLeadingOctave = True
return brailleDynamic
def translateTextExpression(self, currentExpression):
brailleExpression = basic.textExpressionToBraille(currentExpression)
self.previousNote = None
self.showLeadingOctave = True
return brailleExpression
def translateBarline(self, currentBarline):
return basic.barlineToBraille(currentBarline)
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),
])
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
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
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')