kernpy 0.0.1__py3-none-any.whl → 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- kernpy/__init__.py +215 -0
- kernpy/__main__.py +217 -0
- kernpy/core/__init__.py +119 -0
- kernpy/core/_io.py +48 -0
- kernpy/core/base_antlr_importer.py +61 -0
- kernpy/core/base_antlr_spine_parser_listener.py +196 -0
- kernpy/core/basic_spine_importer.py +43 -0
- kernpy/core/document.py +965 -0
- kernpy/core/dyn_importer.py +30 -0
- kernpy/core/dynam_spine_importer.py +42 -0
- kernpy/core/error_listener.py +51 -0
- kernpy/core/exporter.py +535 -0
- kernpy/core/fing_spine_importer.py +42 -0
- kernpy/core/generated/kernSpineLexer.interp +444 -0
- kernpy/core/generated/kernSpineLexer.py +535 -0
- kernpy/core/generated/kernSpineLexer.tokens +236 -0
- kernpy/core/generated/kernSpineParser.interp +425 -0
- kernpy/core/generated/kernSpineParser.py +9954 -0
- kernpy/core/generated/kernSpineParser.tokens +236 -0
- kernpy/core/generated/kernSpineParserListener.py +1200 -0
- kernpy/core/generated/kernSpineParserVisitor.py +673 -0
- kernpy/core/generic.py +426 -0
- kernpy/core/gkern.py +526 -0
- kernpy/core/graphviz_exporter.py +89 -0
- kernpy/core/harm_spine_importer.py +41 -0
- kernpy/core/import_humdrum_old.py +853 -0
- kernpy/core/importer.py +285 -0
- kernpy/core/importer_factory.py +43 -0
- kernpy/core/kern_spine_importer.py +73 -0
- kernpy/core/mens_spine_importer.py +23 -0
- kernpy/core/mhxm_spine_importer.py +44 -0
- kernpy/core/pitch_models.py +338 -0
- kernpy/core/root_spine_importer.py +58 -0
- kernpy/core/spine_importer.py +45 -0
- kernpy/core/text_spine_importer.py +43 -0
- kernpy/core/tokenizers.py +239 -0
- kernpy/core/tokens.py +2011 -0
- kernpy/core/transposer.py +300 -0
- kernpy/io/__init__.py +14 -0
- kernpy/io/public.py +355 -0
- kernpy/polish_scores/__init__.py +13 -0
- kernpy/polish_scores/download_polish_dataset.py +357 -0
- kernpy/polish_scores/iiif.py +47 -0
- kernpy/test_grammar.sh +22 -0
- kernpy/util/__init__.py +14 -0
- kernpy/util/helpers.py +55 -0
- kernpy/util/store_cache.py +35 -0
- kernpy/visualize_analysis.sh +23 -0
- kernpy-1.0.0.dist-info/METADATA +501 -0
- kernpy-1.0.0.dist-info/RECORD +51 -0
- {kernpy-0.0.1.dist-info → kernpy-1.0.0.dist-info}/WHEEL +1 -2
- kernpy/example.py +0 -0
- kernpy-0.0.1.dist-info/LICENSE +0 -19
- kernpy-0.0.1.dist-info/METADATA +0 -19
- kernpy-0.0.1.dist-info/RECORD +0 -7
- kernpy-0.0.1.dist-info/top_level.txt +0 -1
@@ -0,0 +1,338 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from abc import ABC, abstractmethod
|
4
|
+
from enum import Enum
|
5
|
+
|
6
|
+
|
7
|
+
pitches = {
|
8
|
+
'A',
|
9
|
+
'B',
|
10
|
+
'C',
|
11
|
+
'D',
|
12
|
+
'E',
|
13
|
+
'F',
|
14
|
+
'G'
|
15
|
+
}
|
16
|
+
|
17
|
+
|
18
|
+
Chromas = {
|
19
|
+
'C--': 0,
|
20
|
+
'C-': 1,
|
21
|
+
'C': 2,
|
22
|
+
'C+': 3,
|
23
|
+
'C++': 4,
|
24
|
+
'D---': 5,
|
25
|
+
'D--': 6,
|
26
|
+
'D-': 7,
|
27
|
+
'D': 8,
|
28
|
+
'D+': 9,
|
29
|
+
'D++': 10,
|
30
|
+
'E---': 11,
|
31
|
+
'E--': 12,
|
32
|
+
'E-': 13,
|
33
|
+
'E': 14,
|
34
|
+
'E+': 15,
|
35
|
+
'E++': 16,
|
36
|
+
'F--': 17,
|
37
|
+
'F-': 18,
|
38
|
+
'F': 19,
|
39
|
+
'F+': 20,
|
40
|
+
'F++': 21,
|
41
|
+
# 22 is unused
|
42
|
+
'G--': 23,
|
43
|
+
'G-': 24,
|
44
|
+
'G': 25,
|
45
|
+
'G+': 26,
|
46
|
+
'G++': 27,
|
47
|
+
'A---': 28,
|
48
|
+
'A--': 29,
|
49
|
+
'A-': 30,
|
50
|
+
'A': 31,
|
51
|
+
'A+': 32,
|
52
|
+
'A++': 33,
|
53
|
+
'B---': 34,
|
54
|
+
'B--': 35,
|
55
|
+
'B-': 36,
|
56
|
+
'B': 37,
|
57
|
+
'B+': 38,
|
58
|
+
'B++': 39
|
59
|
+
}
|
60
|
+
|
61
|
+
ChromasByValue = {v: k for k, v in Chromas.items()} # reverse the key-value pairs
|
62
|
+
|
63
|
+
class NotationEncoding(Enum):
|
64
|
+
AMERICAN = 'american'
|
65
|
+
HUMDRUM = 'kern'
|
66
|
+
|
67
|
+
class Direction(Enum):
|
68
|
+
UP = 'up'
|
69
|
+
DOWN = 'down'
|
70
|
+
|
71
|
+
|
72
|
+
class AgnosticPitch:
|
73
|
+
"""
|
74
|
+
Represents a pitch in a generic way, independent of the notation system used.
|
75
|
+
"""
|
76
|
+
|
77
|
+
ASCENDANT_ACCIDENTAL_ALTERATION = '+'
|
78
|
+
DESCENDENT_ACCIDENTAL_ALTERATION = '-'
|
79
|
+
ACCIDENTAL_ALTERATIONS = {
|
80
|
+
ASCENDANT_ACCIDENTAL_ALTERATION,
|
81
|
+
DESCENDENT_ACCIDENTAL_ALTERATION
|
82
|
+
}
|
83
|
+
|
84
|
+
|
85
|
+
def __init__(self, name: str, octave: int):
|
86
|
+
"""
|
87
|
+
Initialize the AgnosticPitch object.
|
88
|
+
|
89
|
+
Args:
|
90
|
+
name (str): The name of the pitch (e.g., 'C', 'D#', 'Bb').
|
91
|
+
octave (int): The octave of the pitch (e.g., 4 for middle C).
|
92
|
+
"""
|
93
|
+
self.name = name
|
94
|
+
self.octave = octave
|
95
|
+
|
96
|
+
@property
|
97
|
+
def name(self):
|
98
|
+
return self.__name
|
99
|
+
|
100
|
+
@name.setter
|
101
|
+
def name(self, name):
|
102
|
+
accidentals = ''.join([c for c in name if c in ['-', '+']])
|
103
|
+
name = name.upper()
|
104
|
+
name = name.replace('#', '+').replace('b', '-')
|
105
|
+
|
106
|
+
check_name = name.replace('+', '').replace('-', '')
|
107
|
+
if check_name not in pitches:
|
108
|
+
raise ValueError(f"Invalid pitch: {name}")
|
109
|
+
if len(accidentals) > 3:
|
110
|
+
raise ValueError(f"Invalid pitch: {name}. Maximum of 3 accidentals allowed. ")
|
111
|
+
self.__name = name
|
112
|
+
|
113
|
+
@property
|
114
|
+
def octave(self):
|
115
|
+
return self.__octave
|
116
|
+
|
117
|
+
@octave.setter
|
118
|
+
def octave(self, octave):
|
119
|
+
if not isinstance(octave, int):
|
120
|
+
raise ValueError(f"Invalid octave: {octave}")
|
121
|
+
self.__octave = octave
|
122
|
+
|
123
|
+
def get_chroma(self):
|
124
|
+
return 40 * self.octave + Chromas[self.name]
|
125
|
+
|
126
|
+
@classmethod
|
127
|
+
def to_transposed(cls, agnostic_pitch: 'AgnosticPitch', raw_interval, direction: str = Direction.UP.value) -> 'AgnosticPitch':
|
128
|
+
delta = raw_interval if direction == Direction.UP.value else - raw_interval
|
129
|
+
chroma = agnostic_pitch.get_chroma() + delta
|
130
|
+
name = ChromasByValue[chroma % 40]
|
131
|
+
octave = chroma // 40
|
132
|
+
return AgnosticPitch(name, octave)
|
133
|
+
|
134
|
+
@classmethod
|
135
|
+
def get_chroma_from_interval(cls, pitch_a: 'AgnosticPitch', pitch_b: 'AgnosticPitch'):
|
136
|
+
return pitch_b.get_chroma() - pitch_a.get_chroma()
|
137
|
+
|
138
|
+
def __str__(self):
|
139
|
+
return f"<{self.name}, {self.octave}>"
|
140
|
+
|
141
|
+
def __repr__(self):
|
142
|
+
return f"{self.__name}(name={self.name}, octave={self.octave})"
|
143
|
+
|
144
|
+
def __eq__(self, other):
|
145
|
+
if not isinstance(other, AgnosticPitch):
|
146
|
+
return False
|
147
|
+
return self.name == other.name and self.octave == other.octave
|
148
|
+
|
149
|
+
def __ne__(self, other):
|
150
|
+
if not isinstance(other, AgnosticPitch):
|
151
|
+
return True
|
152
|
+
return self.name != other.name or self.octave != other.octave
|
153
|
+
|
154
|
+
def __hash__(self):
|
155
|
+
return hash((self.name, self.octave))
|
156
|
+
|
157
|
+
def __lt__(self, other):
|
158
|
+
if not isinstance(other, AgnosticPitch):
|
159
|
+
return NotImplemented
|
160
|
+
if self.octave == other.octave:
|
161
|
+
return Chromas[self.name] < Chromas[other.name]
|
162
|
+
return self.octave < other.octave
|
163
|
+
|
164
|
+
def __gt__(self, other):
|
165
|
+
if not isinstance(other, AgnosticPitch):
|
166
|
+
return NotImplemented
|
167
|
+
if self.octave == other.octave:
|
168
|
+
return Chromas[self.name] > Chromas[other.name]
|
169
|
+
return self.octave > other.octave
|
170
|
+
|
171
|
+
|
172
|
+
|
173
|
+
class PitchImporter(ABC):
|
174
|
+
def __init__(self):
|
175
|
+
self.octave = None
|
176
|
+
self.name = None
|
177
|
+
|
178
|
+
@abstractmethod
|
179
|
+
def import_pitch(self, encoding: str) -> AgnosticPitch:
|
180
|
+
pass
|
181
|
+
|
182
|
+
@abstractmethod
|
183
|
+
def _parse_pitch(self, pitch: str):
|
184
|
+
pass
|
185
|
+
|
186
|
+
class HumdrumPitchImporter(PitchImporter):
|
187
|
+
"""
|
188
|
+
Represents the pitch in the Humdrum Kern format.
|
189
|
+
|
190
|
+
The name is represented using the International Standard Organization (ISO) name notation.
|
191
|
+
The first line below the staff is the C4 in G clef. The above C is C5, the below C is C3, etc.
|
192
|
+
|
193
|
+
The Humdrum Kern format uses the following name representation:
|
194
|
+
'c' = C4
|
195
|
+
'cc' = C5
|
196
|
+
'ccc' = C6
|
197
|
+
'cccc' = C7
|
198
|
+
|
199
|
+
'C' = C3
|
200
|
+
'CC' = C2
|
201
|
+
'CCC' = C1
|
202
|
+
|
203
|
+
This class do not limit the name ranges.
|
204
|
+
|
205
|
+
In the following example, the name is represented by the letter 'c'. The name of 'c' is C4, 'cc' is C5, 'ccc' is C6.
|
206
|
+
```
|
207
|
+
**kern
|
208
|
+
*clefG2
|
209
|
+
2c // C4
|
210
|
+
2cc // C5
|
211
|
+
2ccc // C6
|
212
|
+
2C // C3
|
213
|
+
2CC // C2
|
214
|
+
2CCC // C1
|
215
|
+
*-
|
216
|
+
```
|
217
|
+
"""
|
218
|
+
C4_PITCH_LOWERCASE = 'c'
|
219
|
+
C4_OCATAVE = 4
|
220
|
+
C3_PITCH_UPPERCASE = 'C'
|
221
|
+
C3_OCATAVE = 3
|
222
|
+
VALID_PITCHES = 'abcdefg' + 'ABCDEFG'
|
223
|
+
|
224
|
+
def __init__(self):
|
225
|
+
super().__init__()
|
226
|
+
|
227
|
+
def import_pitch(self, encoding: str) -> AgnosticPitch:
|
228
|
+
self.name, self.octave = self._parse_pitch(encoding)
|
229
|
+
return AgnosticPitch(self.name, self.octave)
|
230
|
+
|
231
|
+
def _parse_pitch(self, encoding: str) -> tuple:
|
232
|
+
accidentals = ''.join([c for c in encoding if c in ['#', '-']])
|
233
|
+
accidentals = accidentals.replace('#', '+')
|
234
|
+
encoding = encoding.replace('#', '').replace('-', '')
|
235
|
+
pitch = encoding[0].lower()
|
236
|
+
octave = None
|
237
|
+
if encoding[0].islower():
|
238
|
+
min_octave = HumdrumPitchImporter.C4_OCATAVE
|
239
|
+
octave = min_octave + (len(encoding) - 1)
|
240
|
+
elif encoding[0].isupper():
|
241
|
+
max_octave = HumdrumPitchImporter.C3_OCATAVE
|
242
|
+
octave = max_octave - (len(encoding) - 1)
|
243
|
+
name = f"{pitch}{accidentals}"
|
244
|
+
return name, octave
|
245
|
+
|
246
|
+
|
247
|
+
class AmericanPitchImporter(PitchImporter):
|
248
|
+
def __init__(self):
|
249
|
+
super().__init__()
|
250
|
+
|
251
|
+
def import_pitch(self, encoding: str) -> AgnosticPitch:
|
252
|
+
self.name, self.octave = self._parse_pitch(encoding)
|
253
|
+
return AgnosticPitch(self.name, self.octave)
|
254
|
+
|
255
|
+
def _parse_pitch(self, encoding: str):
|
256
|
+
octave = int(''.join([n for n in encoding if n.isnumeric()]))
|
257
|
+
chroma = ''.join([c.lower() for c in encoding if c.isalpha() or c in ['-', '+', '#', 'b']])
|
258
|
+
|
259
|
+
return chroma, octave
|
260
|
+
|
261
|
+
|
262
|
+
class PitchImporterFactory:
|
263
|
+
@classmethod
|
264
|
+
def create(cls, encoding: str) -> PitchImporter:
|
265
|
+
if encoding == NotationEncoding.AMERICAN.value:
|
266
|
+
return AmericanPitchImporter()
|
267
|
+
elif encoding == NotationEncoding.HUMDRUM.value:
|
268
|
+
return HumdrumPitchImporter()
|
269
|
+
else:
|
270
|
+
raise ValueError(f"Invalid encoding: {encoding}. \nUse one of {NotationEncoding.__members__.values()}")
|
271
|
+
|
272
|
+
|
273
|
+
class PitchExporter(ABC):
|
274
|
+
def __init__(self):
|
275
|
+
self.pitch = None
|
276
|
+
|
277
|
+
@abstractmethod
|
278
|
+
def export_pitch(self, pitch: AgnosticPitch) -> str:
|
279
|
+
pass
|
280
|
+
|
281
|
+
def _is_valid_pitch(self):
|
282
|
+
clean_pitch = ''.join([c for c in self.pitch.name if c.isalpha()])
|
283
|
+
clean_pitch = clean_pitch.upper()
|
284
|
+
if len(clean_pitch) > 1:
|
285
|
+
clean_pitch = clean_pitch[0]
|
286
|
+
return clean_pitch in pitches
|
287
|
+
|
288
|
+
|
289
|
+
class HumdrumPitchExporter(PitchExporter):
|
290
|
+
C4_PITCH_LOWERCASE = 'c'
|
291
|
+
C4_OCATAVE = 4
|
292
|
+
C3_PITCH_UPPERCASE = 'C'
|
293
|
+
C3_OCATAVE = 3
|
294
|
+
|
295
|
+
def __init__(self):
|
296
|
+
super().__init__()
|
297
|
+
|
298
|
+
def export_pitch(self, pitch: AgnosticPitch) -> str:
|
299
|
+
accidentals = ''.join([c for c in pitch.name if c in ['-', '+']])
|
300
|
+
accidentals = accidentals.replace('+', '#')
|
301
|
+
accidentals_output = len(accidentals) * accidentals[0] if len(accidentals) > 0 else ''
|
302
|
+
pitch.name = pitch.name.replace('+', '').replace('-', '')
|
303
|
+
|
304
|
+
if pitch.octave >= HumdrumPitchExporter.C4_OCATAVE:
|
305
|
+
return f"{pitch.name.lower() * (pitch.octave - HumdrumPitchExporter.C4_OCATAVE + 1)}{accidentals_output}"
|
306
|
+
else:
|
307
|
+
return f"{pitch.name.upper() * (HumdrumPitchExporter.C3_OCATAVE - pitch.octave + 1)}{accidentals_output}"
|
308
|
+
|
309
|
+
|
310
|
+
class AmericanPitchExporter(PitchExporter):
|
311
|
+
def __init__(self):
|
312
|
+
super().__init__()
|
313
|
+
|
314
|
+
def export_pitch(self, pitch: AgnosticPitch) -> str:
|
315
|
+
self.pitch = pitch
|
316
|
+
|
317
|
+
if not self._is_valid_pitch():
|
318
|
+
raise ValueError(f"Invalid pitch: {self.pitch.name}")
|
319
|
+
|
320
|
+
clean_name = ''.join([c for c in self.pitch.name if c.isalpha()])
|
321
|
+
clean_name = clean_name.upper()
|
322
|
+
accidentals = ''.join([c for c in self.pitch.name if c in ['-', '+']])
|
323
|
+
total_accidentals = len(accidentals)
|
324
|
+
accidentals_output = ''
|
325
|
+
if total_accidentals > 0:
|
326
|
+
accidentals_output = total_accidentals * '#' if accidentals == '+' else total_accidentals * 'b'
|
327
|
+
return f"{clean_name}{accidentals_output}{self.pitch.octave}"
|
328
|
+
|
329
|
+
|
330
|
+
class PitchExporterFactory:
|
331
|
+
@classmethod
|
332
|
+
def create(cls, encoding: str) -> PitchExporter:
|
333
|
+
if encoding == NotationEncoding.AMERICAN.value:
|
334
|
+
return AmericanPitchExporter()
|
335
|
+
elif encoding == NotationEncoding.HUMDRUM.value:
|
336
|
+
return HumdrumPitchExporter()
|
337
|
+
else:
|
338
|
+
raise ValueError(f"Invalid encoding: {encoding}. \nUse one of {NotationEncoding.__members__.values()}")
|
@@ -0,0 +1,58 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
from typing import Optional
|
3
|
+
|
4
|
+
from antlr4 import InputStream, CommonTokenStream, ParseTreeWalker, BailErrorStrategy, \
|
5
|
+
PredictionMode
|
6
|
+
|
7
|
+
from .base_antlr_importer import BaseANTLRListenerImporter
|
8
|
+
from .base_antlr_spine_parser_listener import BaseANTLRSpineParserListener
|
9
|
+
from .error_listener import ErrorListener
|
10
|
+
from .kern_spine_importer import KernSpineListener, KernSpineImporter
|
11
|
+
from .spine_importer import SpineImporter
|
12
|
+
from .generated.kernSpineLexer import kernSpineLexer
|
13
|
+
from .generated.kernSpineParser import kernSpineParser
|
14
|
+
from .tokens import TokenCategory, Token, SimpleToken
|
15
|
+
|
16
|
+
|
17
|
+
class RootSpineListener(BaseANTLRSpineParserListener):
|
18
|
+
def __init__(self):
|
19
|
+
super().__init__()
|
20
|
+
|
21
|
+
|
22
|
+
class RootListenerImporter(BaseANTLRListenerImporter):
|
23
|
+
|
24
|
+
def createListener(self):
|
25
|
+
return KernSpineListener()
|
26
|
+
|
27
|
+
def createLexer(self, tokenStream):
|
28
|
+
return kernSpineLexer(tokenStream)
|
29
|
+
|
30
|
+
def createParser(self, tokenStream):
|
31
|
+
return kernSpineParser(tokenStream)
|
32
|
+
|
33
|
+
def startRule(self):
|
34
|
+
return self.parser.start()
|
35
|
+
|
36
|
+
|
37
|
+
class RootSpineImporter(SpineImporter):
|
38
|
+
def __init__(self, verbose: Optional[bool] = False):
|
39
|
+
"""
|
40
|
+
KernSpineImporter constructor.
|
41
|
+
|
42
|
+
Args:
|
43
|
+
verbose (Optional[bool]): Level of verbosity for error messages.
|
44
|
+
"""
|
45
|
+
super().__init__(verbose=verbose)
|
46
|
+
|
47
|
+
def import_listener(self) -> BaseANTLRSpineParserListener:
|
48
|
+
#return RootSpineListener() # TODO: Create a custom functional listener for RootSpineImporter
|
49
|
+
return KernSpineListener()
|
50
|
+
|
51
|
+
def import_token(self, encoding: str) -> Token:
|
52
|
+
self._raise_error_if_wrong_input(encoding)
|
53
|
+
|
54
|
+
kern_spine_importer = KernSpineImporter()
|
55
|
+
token = kern_spine_importer.import_token(encoding)
|
56
|
+
|
57
|
+
return token # The **root spine tokens are always a subset of the **kern spine tokens
|
58
|
+
|
@@ -0,0 +1,45 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from abc import ABC, abstractmethod
|
4
|
+
from typing import Optional
|
5
|
+
|
6
|
+
from antlr4 import InputStream, CommonTokenStream, ParseTreeWalker, BailErrorStrategy, \
|
7
|
+
PredictionMode
|
8
|
+
|
9
|
+
from .generated.kernSpineLexer import kernSpineLexer
|
10
|
+
from .generated.kernSpineParser import kernSpineParser
|
11
|
+
from .base_antlr_spine_parser_listener import BaseANTLRSpineParserListener
|
12
|
+
from .error_listener import ErrorListener
|
13
|
+
from .tokens import Token
|
14
|
+
|
15
|
+
|
16
|
+
class SpineImporter(ABC):
|
17
|
+
def __init__(self, verbose: Optional[bool] = False):
|
18
|
+
"""
|
19
|
+
SpineImporter constructor.
|
20
|
+
This class is an abstract base class for importing all kinds of spines.
|
21
|
+
|
22
|
+
Args:
|
23
|
+
verbose (Optional[bool]): Level of verbosity for error messages.
|
24
|
+
"""
|
25
|
+
self.import_listener = self.import_listener()
|
26
|
+
self.error_listener = ErrorListener(verbose=verbose)
|
27
|
+
|
28
|
+
@abstractmethod
|
29
|
+
def import_listener(self) -> BaseANTLRSpineParserListener:
|
30
|
+
pass
|
31
|
+
|
32
|
+
@abstractmethod
|
33
|
+
def import_token(self, encoding: str) -> Token:
|
34
|
+
pass
|
35
|
+
|
36
|
+
@classmethod
|
37
|
+
def _raise_error_if_wrong_input(cls, encoding: str):
|
38
|
+
if encoding is None:
|
39
|
+
raise ValueError("Encoding cannot be None")
|
40
|
+
if not isinstance(encoding, str):
|
41
|
+
raise TypeError("Encoding must be a string")
|
42
|
+
if encoding == '':
|
43
|
+
raise ValueError("Encoding cannot be an empty string")
|
44
|
+
|
45
|
+
|
@@ -0,0 +1,43 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
from typing import Optional
|
3
|
+
|
4
|
+
from .base_antlr_spine_parser_listener import BaseANTLRSpineParserListener
|
5
|
+
from .spine_importer import SpineImporter
|
6
|
+
from .spine_importer import SpineImporter
|
7
|
+
from .kern_spine_importer import KernSpineImporter, KernSpineListener
|
8
|
+
from .tokens import SimpleToken, TokenCategory, Token
|
9
|
+
|
10
|
+
|
11
|
+
class TextSpineImporter(SpineImporter):
|
12
|
+
def __init__(self, verbose: Optional[bool] = False):
|
13
|
+
"""
|
14
|
+
KernSpineImporter constructor.
|
15
|
+
|
16
|
+
Args:
|
17
|
+
verbose (Optional[bool]): Level of verbosity for error messages.
|
18
|
+
"""
|
19
|
+
super().__init__(verbose=verbose)
|
20
|
+
|
21
|
+
def import_listener(self) -> BaseANTLRSpineParserListener:
|
22
|
+
return KernSpineListener() # TODO: Create a custom functional listener for TextSpineImporter
|
23
|
+
|
24
|
+
def import_token(self, encoding: str) -> Token:
|
25
|
+
self._raise_error_if_wrong_input(encoding)
|
26
|
+
|
27
|
+
kern_spine_importer = KernSpineImporter()
|
28
|
+
token = kern_spine_importer.import_token(encoding)
|
29
|
+
|
30
|
+
ACCEPTED_CATEGORIES = {
|
31
|
+
TokenCategory.STRUCTURAL,
|
32
|
+
TokenCategory.SIGNATURES,
|
33
|
+
TokenCategory.EMPTY,
|
34
|
+
TokenCategory.BARLINES,
|
35
|
+
TokenCategory.IMAGE_ANNOTATIONS,
|
36
|
+
TokenCategory.BARLINES,
|
37
|
+
TokenCategory.COMMENTS,
|
38
|
+
}
|
39
|
+
|
40
|
+
if not any(TokenCategory.is_child(child=token.category, parent=cat) for cat in ACCEPTED_CATEGORIES):
|
41
|
+
return SimpleToken(encoding, TokenCategory.LYRICS)
|
42
|
+
|
43
|
+
return token
|