1
# This file is part of the Frescobaldi project, http://www.frescobaldi.org/
3
# Copyright (c) 2008, 2009, 2010 by Wilbert Berendsen
5
# This program is free software; you can redistribute it and/or
6
# modify it under the terms of the GNU General Public License
7
# as published by the Free Software Foundation; either version 2
8
# of the License, or (at your option) any later version.
10
# This program is distributed in the hope that it will be useful,
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
# GNU General Public License for more details.
15
# You should have received a copy of the GNU General Public License
16
# along with this program; if not, write to the Free Software
17
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18
# See http://www.gnu.org/licenses/ for more information.
20
from __future__ import unicode_literals
23
from fractions import Fraction
29
A pitch with note, alter, octave and cautionary and octaveCheck
30
(for relative pitches)
33
self.note = 0 # base note (c, d, e, f, g, a, b)
34
self.alter = 0 # # = 2; b = -2; natural = 0
35
self.octave = 0 # '' = 2; ,, = -2
36
self.cautionary = '' # '!' or '?' or ''
37
self.octaveCheck = None
41
""" Return a pitch c' """
48
""" Return a pitch c """
52
""" Return a new instance with our attributes. """
56
p.octave = self.octave
57
p.cautionary = self.cautionary
58
p.octaveCheck = self.octaveCheck
61
def absolute(self, lastPitch):
63
Set our octave height from lastPitch (which is absolute), as if
64
we are a relative pitch. If the octaveCheck attribute is set to an
65
octave number, that is used instead.
67
if self.octaveCheck is not None:
68
self.octave = self.octaveCheck
69
self.octaveCheck = None
71
dist = self.note - lastPitch.note
76
self.octave += lastPitch.octave + (lastPitch.note + dist) // 7
78
def relative(self, lastPitch):
80
Returns a new Pitch instance with the current pitch relative to
81
the absolute pitch in lastPitch.
84
dist = self.note - lastPitch.note
85
p.octave = self.octave - lastPitch.octave + (dist + 3) // 7
88
def output(self, language):
90
Return the pitch as a string in the given pitch name language.
91
Raises ly.QuarterToneAlterationNotAvailable is an alteration is
92
requested that is not available in that language.
94
output = [pitchWriter[language](self.note, self.alter),
95
octaveToString(self.octave),
97
if self.octaveCheck is not None:
98
output.append('=' + octaveToString(self.octaveCheck))
99
return ''.join(output)
102
class Transposer(object):
106
Instantiate with a from- and to-Pitch, and optionally a scale.
107
The scale is a list with the pitch height of the unaltered step (0 .. 6).
108
The default scale is the normal scale: C, D, E, F, G, A, B.
110
scale = (0, 1, 2, Fraction(5, 2), Fraction(7, 2), Fraction(9, 2), Fraction(11, 2))
112
def __init__(self, fromPitch, toPitch, scale = None):
113
if scale is not None:
116
# the number of octaves we need to transpose
117
self.octave = toPitch.octave - fromPitch.octave
119
# the number of base note steps (c->d == 1, e->f == 1, etc.)
120
self.steps = toPitch.note - fromPitch.note
122
# the number (fraction) of real whole steps
123
self.alter = (self.scale[toPitch.note] + toPitch.alter -
124
self.scale[fromPitch.note] - fromPitch.alter)
126
def transpose(self, pitch):
127
doct, note = divmod(pitch.note + self.steps, 7)
128
pitch.alter += self.alter - doct * 6 - self.scale[note] + self.scale[pitch.note]
129
pitch.octave += self.octave + doct
133
class PitchWriter(object):
134
def __init__(self, names, accs, replacements=()):
137
self.replacements = replacements
139
def __call__(self, note, alter = 0):
141
Returns a string representing the pitch in our language.
142
Raises ly.QuarterToneAlterationNotAvailable if the requested pitch
143
has an alteration that is not available in the current language.
145
pitch = self.names[note]
147
acc = self.accs[int(alter * 4 + 4)]
149
raise ly.QuarterToneAlterationNotAvailable
151
for s, r in self.replacements:
152
if pitch.startswith(s):
153
pitch = r + pitch[len(s):]
158
class PitchReader(object):
159
def __init__(self, names, accs, replacements=()):
160
self.names = list(names)
161
self.accs = list(accs)
162
self.replacements = replacements
163
self.rx = re.compile("({0})({1})?$".format("|".join(names),
164
"|".join(acc for acc in accs if acc)))
166
def __call__(self, text):
167
for s, r in self.replacements:
168
if text.startswith(r):
169
text = s + text[len(r):]
171
m = self.rx.match(text)
173
note = self.names.index(m.group(1))
175
alter = Fraction(self.accs.index(m.group(2)) - 4, 4)
179
# HACK: were we using (rarely used) long english syntax?
180
text = text.replace('flat', 'f').replace('sharp', 's')
184
def octaveToString(octave):
186
Convert numeric octave to a string with apostrophes or commas.
187
0 -> "" ; 1 -> "'" ; -1 -> "," ; etc.
189
return octave < 0 and ',' * -octave or "'" * octave
191
def octaveToNum(octave):
193
Convert string octave to an integer:
194
"" -> 0 ; "," -> -1 ; "'''" -> 3 ; etc.
196
return octave.count("'") - octave.count(",")
201
('c','d','e','f','g','a','b'),
202
('eses', 'eseh', 'es', 'eh', '', 'ih','is','isih','isis'),
203
(('ees', 'es'), ('aes', 'as'))
206
('c','d','e','f','g','a','b'),
207
('ff', 'tqf', 'f', 'qf', '', 'qs', 's', 'tqs', 'ss'),
210
('c','d','e','f','g','a','h'),
211
('eses', 'eseh', 'es', 'eh', '', 'ih','is','isih','isis'),
212
(('ees', 'es'), ('aes', 'as'), ('hes', 'b'))
215
('c','d','e','f','g','a','h'),
216
('essess', '', 'ess', '', '', '','iss','','ississ'),
217
(('ees', 'es'), ('aes', 'as'), ('hess', 'b'))
220
('do', 're', 'mi', 'fa', 'sol', 'la', 'si'),
221
('bb', 'bsb', 'b', 'sb', '', 'sd', 'd', 'dsd', 'dd')
224
('do', 're', 'mi', 'fa', 'sol', 'la', 'si'),
225
('bb', '', 'b', '', '', '', 's', '', 'ss')
228
('do', 're', 'mi', 'fa', 'sol', 'la', 'si'),
229
('bb', 'btqt', 'b', 'bqt', '', 'sqt', 's', 'stqt', 'ss')
232
('do', 're', 'mi', 'fa', 'sol', 'la', 'si'),
233
('bb', '', 'b', '', '', '', 'k', '', 'kk')
237
pitchInfo['norsk'] = pitchInfo['deutsch']
238
pitchInfo['suomi'] = pitchInfo['deutsch']
239
pitchInfo['catalan'] = pitchInfo['italiano']
243
(lang, PitchWriter(*data)) for lang, data in pitchInfo.iteritems())
246
(lang, PitchReader(*data)) for lang, data in pitchInfo.iteritems())