1
# GNU Solfege - free ear training software
2
# Copyright (C) 2000, 2001, 2002, 2003, 2004, 2007, 2008 Tom Cato Amundsen
4
# This program is free software: you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation, either version 3 of the License, or
7
# (at your option) any later version.
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
# GNU General Public License for more details.
14
# You should have received a copy of the GNU General Public License
15
# along with this program. If not, see <http://www.gnu.org/licenses/>.
19
>>> import locale, gettext
20
>>> gettext.NullTranslations().install()
21
>>> a = MusicalPitch.new_from_notename("g")
22
>>> b = MusicalPitch.new_from_notename("f")
25
>>> print (a - 2).get_octave_notename()
27
>>> print (a + 3).get_octave_notename()
33
>>> print MusicalPitch.new_from_int(55) == a
35
>>> print MusicalPitch.new_from_notename(a.get_notename()) == a
37
>>> print a.clone() == a, id(a) == id(a.clone())
40
>>> print a.m_octave_i == a.m_accidental_i == a.m_octave_i == 0
42
>>> print a.semitone_pitch()
44
>>> print a.get_octave_notename()
46
>>> print (a+2).get_octave_notename()
48
>>> print (2+a).get_octave_notename()
50
>>> print MusicalPitch.new_from_notename("des'").get_notename()
52
>>> print MusicalPitch.new_from_int(50).semitone_pitch()
54
>>> print MusicalPitch.new_from_int(50).get_notename()
56
>>> n=MusicalPitch.new_from_notename("fis,")
57
>>> print n.get_user_octave_notename()
59
>>> n=MusicalPitch.new_from_notename("b,,")
60
>>> print n.get_octave_notename()
62
>>> print n.get_user_octave_notename()
64
>>> gettext.translation('solfege', './share/locale/', languages=['nb_NO']).install()
65
>>> print n.get_octave_notename()
67
>>> print n.get_user_octave_notename()
69
>>> print n.get_user_notename()
73
>>> n = MusicalPitch()
74
>>> n.set_from_notename("d'")
75
>>> print n.get_octave_notename()
83
# The following are here so that the strings are caught by pygettext
120
class InvalidNotenameException(_exceptions.MpdException):
121
def __init__(self, n):
122
_exceptions.MpdException.__init__(self)
125
return _("Invalid notename: %s") % self.m_notename
132
r.m_octave_i = self.m_octave_i
133
r.m_notename_i = self.m_notename_i
134
r.m_accidental_i = self.m_accidental_i
136
def new_from_notename(n):
137
assert isinstance(n, basestring)
139
r.set_from_notename(n)
141
new_from_notename = staticmethod(new_from_notename)
143
assert type(i) == type(0)
147
new_from_int = staticmethod(new_from_int)
150
c,,,, is lowest: m_octave_i == -4, steps() == -28
151
g'''''' is highest: m_octave_i = 6, steps() == 46
153
self.m_octave_i = self.m_accidental_i = self.m_notename_i = 0
154
def transpose_by_musicalpitch(self, P):
155
"""Silly function used by mpd/parser.py and company
156
(d') transposes up one major second.
158
tra = P.semitone_pitch() - 60
159
old_p = self.semitone_pitch()
160
self.m_notename_i = self.m_notename_i + P.m_notename_i
161
self.m_accidental_i = self.m_accidental_i + P.m_accidental_i
162
if self.m_notename_i > 6:
163
self.m_notename_i = self.m_notename_i - 7
164
self.m_octave_i = self.m_octave_i + 1
165
self.m_octave_i = self.m_octave_i + P.m_octave_i - 1
166
if self.semitone_pitch()-old_p < tra:
167
self.m_accidental_i = self.m_accidental_i + 1
168
elif self.semitone_pitch()-old_p > tra:
169
self.m_accidental_i = self.m_accidental_i - 1
170
self.sanitate_accidental()
172
def sanitate_accidental(self):
174
Make use self.m_accidental_i is some of the values -2, -1, 0, 1, 2
175
It can be out of this range if the musicalpitch has been transposed.
176
This function will change notenames like gisisis, where m_accidental_i
177
is 3 to ais where m_accidental_i is 1
179
if not -3 < self.m_accidental_i < 3:
180
p = self.semitone_pitch()
182
def enharmonic_flip(self):#FIXME find proper name.
184
Change the notename, so that gis becomes aes.
185
What about d, should it be cisis or eeses??
201
if self.m_accidental_i == 1 and self.m_notename_i < 6:
202
self.m_accidental_i = -1
203
self.m_notename_i += 1
204
def normalize_double_accidental(self):
206
Change the tone so that we avoid double accidentals.
208
if self.m_accidental_i == 2:
209
if self.m_notename_i in (0, 1, 3, 4, 5): # c d f g a
210
self.m_notename_i += 1
211
self.m_accidental_i = 0
212
elif self.m_notename_i == 2: # e
213
self.m_notename_i = 3
214
self.m_accidental_i = 1
216
assert self.m_notename_i == 6 # b
217
self.m_notename_i = 0
218
self.m_accidental_i = 1
220
elif self.m_accidental_i == -2:
221
if self.m_notename_i in (1, 2, 4, 5, 6): # d e g a b
222
self.m_notename_i -= 1
223
self.m_accidental_i = 0
224
elif self.m_notename_i == 3: # f
225
self.m_notename_i = 2
226
self.m_accidental_i = -1
228
assert self.m_notename_i == 0
229
self.m_notename_i = 6
230
self.m_accidental_i = -1
233
return self.m_notename_i + self.m_octave_i * 7
234
def semitone_pitch(self):
235
return [0, 2, 4, 5, 7, 9, 11][self.m_notename_i] + \
236
self.m_accidental_i + self.m_octave_i * 12 + 48
237
def set_from_int(self, midiint):
238
self.m_octave_i = (midiint-48)/12
239
self.m_notename_i = {0:0, 1:0, 2:1, 3:1, 4:2, 5:3, 6:3, 7:4, 8:4,
240
9:5, 10:5, 11:6}[midiint % 12]
241
self.m_accidental_i = midiint-(self.m_octave_i+4)*12 \
242
-[0, 2, 4, 5, 7, 9, 11][self.m_notename_i]
243
def set_from_notename(self, notename):
245
raise InvalidNotenameException(notename)
247
self.m_accidental_i = self.m_octave_i = 0
248
while notename[-1] in ["'", ","]:
249
if notename[-1] == "'":
250
self.m_octave_i = self.m_octave_i + 1
251
elif notename[-1] == ",":
252
self.m_octave_i = self.m_octave_i - 1
253
notename = notename[:-1]
254
if notename.startswith('es'):
255
notename = 'ees' + notename[2:]
256
if notename.startswith('as'):
257
notename = 'aes' + notename[2:]
258
while notename.endswith('es'):
259
self.m_accidental_i = self.m_accidental_i -1
260
notename = notename[:-2]
261
while notename.endswith('is'):
262
self.m_accidental_i = self.m_accidental_i + 1
263
notename = notename[:-2]
265
self.m_notename_i = ['c', 'd', 'e', 'f', 'g', 'a', 'b'].index(notename)
267
raise InvalidNotenameException(tmp)
268
def randomize(self, lowest, highest):
270
lowest and highest can be an integer, string or a MusicalPitch instance
272
assert type(lowest) == type(highest)
273
if isinstance(lowest, basestring):
274
lowest = MusicalPitch.new_from_notename(lowest).semitone_pitch()
275
if isinstance(highest, basestring):
276
highest = MusicalPitch.new_from_notename(highest).semitone_pitch()
277
self.set_from_int(random.randint(int(lowest), int(highest)))
279
def __radd__(self, a):
281
def __add__(self, i):
283
MusicalPitch + integer = MusicalPitch
284
MusicalPitch + Interval = MusicalPitch
286
if type(i) == type(0):
287
v = self.semitone_pitch()
288
if not 0 <= v + i < 128:
290
return MusicalPitch.new_from_int(v+i)
291
elif i.__class__.__name__ == 'Interval':#isinstance(i, interval.Interval):
292
if not 0 <= self.semitone_pitch() + i.get_intvalue() < 128:
295
_p = r.semitone_pitch()
296
r.m_notename_i = r.m_notename_i + i.m_interval * i.m_dir
297
r.m_octave_i = r.m_octave_i + r.m_notename_i / 7 + i.m_octave * i.m_dir
298
r.m_notename_i = r.m_notename_i % 7
299
_diff = r.semitone_pitch() - _p
300
r.m_accidental_i = r.m_accidental_i + (i.get_intvalue() - _diff)
301
# to avoid notenames like ciscisciscis :
302
if r.m_accidental_i > 2:
304
if r.m_notename_i in (0, 1, 3, 4, 5):
305
r.m_accidental_i -= 2
307
assert r.m_notename_i in (2, 6), r.m_notename_i
308
r.m_accidental_i -= 1
309
r.m_notename_i = r.m_notename_i + 1
310
if r.m_notename_i == 7:
312
r.m_octave_i = r.m_octave_i + 1
313
if r.m_accidental_i < -2:
314
r.m_accidental_i = r.m_accidental_i + 2
315
r.m_notename_i = r.m_notename_i - 1
316
if r.m_notename_i == -1:
318
r.m_octave_i = r.m_octave_i - 1
319
if not 0 <= int(self) <= 127:
323
raise _exceptions.MpdException("Cannot add %s" %type(i))
324
def __sub__(self, i):
326
MusicalPitch - MusicalPitch = integer
327
MusicalPitch - integer = MusicalPitch
329
if isinstance(i, MusicalPitch):
330
return self.semitone_pitch() - i.semitone_pitch()
331
assert isinstance(i, int)
332
v = self.semitone_pitch()
333
assert 0 <= v - i < 128
334
return MusicalPitch.new_from_int(v-i)
336
return self.semitone_pitch()
337
def __cmp__(self, B):
338
if (self is None or self is None):
348
return "(MusicalPitch %s)" % self.get_octave_notename()
349
def get_user_notename(self):
350
# xgettext:no-python-format
351
return self._format_notename(_i("notenameformat|%(notename)s"))
352
def get_user_octave_notename(self):
353
# xgettext:no-python-format
354
return self._format_notename(_i("notenameformat|%(notename)s%(oct)s"))
355
def get_notename(self):
356
return self._format_notename("%(utnotename)s")
357
def get_octave_notename(self):
358
return self._format_notename("%(utnotename)s%(oct)s")
359
def _format_notename(self, format_string):
361
utnotename : untranslated notename, solfege-internal format.
362
notename : as the value translated in the po file
363
notename2 : lowercase, but capitalized if below the tone c (as
364
"c" is defined internally in solfege.
365
suboct : '' (nothing) for c or higher
368
suboct2: '' (nothing) for c, and higher
370
<sub>2</sub> for c,,,
371
supoct: '' (nothing) for tones lower than c'
373
<sup>2</sup> for c'' etc.
375
assert -3 < self.m_accidental_i < 3, self.m_accidental_i
376
utnotename = ['c', 'd', 'e', 'f', 'g', 'a', 'b'][self.m_notename_i]\
377
+ ['eses', 'es', '', 'is', 'isis'][self.m_accidental_i+2]
378
notename = "notename|" \
379
+ ['c', 'd', 'e', 'f', 'g', 'a', 'b'][self.m_notename_i]\
380
+ ['bb', 'b', '', '#', 'x'][self.m_accidental_i+2]
381
notename = _i(notename)
382
if self.m_octave_i < 0:
383
notename2 = notename.capitalize()
386
if self.m_octave_i > 0:
387
oct = "'" * self.m_octave_i
388
elif self.m_octave_i < 0:
389
oct = "," * (-self.m_octave_i)
392
if self.m_octave_i < 0:
393
suboct = "<sub>%s</sub>" % (-self.m_octave_i)
396
if self.m_octave_i < -1:
397
suboct2 = "<sub>%s</sub>" % (-self.m_octave_i-1)
400
if self.m_octave_i > 0:
401
supoct = "<sup>%s</sup>" % (self.m_octave_i)
404
D = {'utnotename': utnotename,
405
'notename': notename,
406
'notename2': notename2,
412
return format_string % D
414
logging.error("musicalpitch: Bad translation of notenameformat string")
415
return "%(notename)s%(oct)s" % D