~ubuntu-branches/ubuntu/vivid/frescobaldi/vivid

« back to all changes in this revision

Viewing changes to python/ly/pitch.py

  • Committer: Package Import Robot
  • Author(s): Ryan Kavanagh
  • Date: 2012-01-03 16:20:11 UTC
  • mfrom: (1.4.1)
  • Revision ID: package-import@ubuntu.com-20120103162011-tsjkwl4sntwmprea
Tags: 2.0.0-1
* New upstream release 
* Drop the following uneeded patches:
  + 01_checkmodules_no_python-kde4_build-dep.diff
  + 02_no_pyc.diff
  + 04_no_binary_lilypond_upgrades.diff
* Needs new dependency python-poppler-qt4
* Update debian/watch for new download path
* Update copyright file with new holders and years

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# This file is part of the Frescobaldi project, http://www.frescobaldi.org/
2
 
#
3
 
# Copyright (c) 2008, 2009, 2010 by Wilbert Berendsen
4
 
#
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.
9
 
#
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.
14
 
#
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.
19
 
 
20
 
from __future__ import unicode_literals
21
 
 
22
 
import re
23
 
from fractions import Fraction
24
 
 
25
 
import ly
26
 
 
27
 
class Pitch(object):
28
 
    """
29
 
    A pitch with note, alter, octave and cautionary and octaveCheck
30
 
    (for relative pitches)
31
 
    """
32
 
    def __init__(self):
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
38
 
    
39
 
    @classmethod
40
 
    def c1(cls):
41
 
        """ Return a pitch c' """
42
 
        p = cls()
43
 
        p.octave = 1
44
 
        return p
45
 
 
46
 
    @classmethod
47
 
    def c0(cls):
48
 
        """ Return a pitch c """
49
 
        return cls()
50
 
 
51
 
    def copy(self):
52
 
        """ Return a new instance with our attributes. """
53
 
        p = self.__class__()
54
 
        p.note = self.note
55
 
        p.alter = self.alter
56
 
        p.octave = self.octave
57
 
        p.cautionary = self.cautionary
58
 
        p.octaveCheck = self.octaveCheck
59
 
        return p
60
 
        
61
 
    def absolute(self, lastPitch):
62
 
        """
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.
66
 
        """
67
 
        if self.octaveCheck is not None:
68
 
            self.octave = self.octaveCheck
69
 
            self.octaveCheck = None
70
 
        else:
71
 
            dist = self.note - lastPitch.note
72
 
            if dist > 3:
73
 
                dist -= 7
74
 
            elif dist < -3:
75
 
                dist += 7
76
 
            self.octave += lastPitch.octave  + (lastPitch.note + dist) // 7
77
 
        
78
 
    def relative(self, lastPitch):
79
 
        """
80
 
        Returns a new Pitch instance with the current pitch relative to
81
 
        the absolute pitch in lastPitch.
82
 
        """
83
 
        p = self.copy()
84
 
        dist = self.note - lastPitch.note
85
 
        p.octave = self.octave - lastPitch.octave + (dist + 3) // 7
86
 
        return p
87
 
        
88
 
    def output(self, language):
89
 
        """
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.
93
 
        """
94
 
        output = [pitchWriter[language](self.note, self.alter),
95
 
                  octaveToString(self.octave),
96
 
                  self.cautionary]
97
 
        if self.octaveCheck is not None:
98
 
            output.append('=' + octaveToString(self.octaveCheck))
99
 
        return ''.join(output)
100
 
 
101
 
 
102
 
class Transposer(object):
103
 
    """
104
 
    Transpose pitches.
105
 
    
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.
109
 
    """
110
 
    scale = (0, 1, 2, Fraction(5, 2), Fraction(7, 2), Fraction(9, 2), Fraction(11, 2))
111
 
        
112
 
    def __init__(self, fromPitch, toPitch, scale = None):
113
 
        if scale is not None:
114
 
            self.scale = scale
115
 
        
116
 
        # the number of octaves we need to transpose
117
 
        self.octave = toPitch.octave - fromPitch.octave
118
 
        
119
 
        # the number of base note steps (c->d == 1, e->f == 1, etc.)
120
 
        self.steps = toPitch.note - fromPitch.note
121
 
        
122
 
        # the number (fraction) of real whole steps
123
 
        self.alter = (self.scale[toPitch.note] + toPitch.alter -
124
 
                      self.scale[fromPitch.note] - fromPitch.alter)
125
 
                  
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
130
 
        pitch.note = note
131
 
 
132
 
 
133
 
class PitchWriter(object):
134
 
    def __init__(self, names, accs, replacements=()):
135
 
        self.names = names
136
 
        self.accs = accs
137
 
        self.replacements = replacements
138
 
 
139
 
    def __call__(self, note, alter = 0):
140
 
        """
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.
144
 
        """
145
 
        pitch = self.names[note]
146
 
        if alter:
147
 
            acc = self.accs[int(alter * 4 + 4)]
148
 
            if not acc:
149
 
                raise ly.QuarterToneAlterationNotAvailable
150
 
            pitch += acc
151
 
        for s, r in self.replacements:
152
 
            if pitch.startswith(s):
153
 
                pitch = r + pitch[len(s):]
154
 
                break
155
 
        return pitch
156
 
 
157
 
 
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)))
165
 
 
166
 
    def __call__(self, text):
167
 
        for s, r in self.replacements:
168
 
            if text.startswith(r):
169
 
                text = s + text[len(r):]
170
 
        for dummy in 1, 2:
171
 
            m = self.rx.match(text)
172
 
            if m:
173
 
                note = self.names.index(m.group(1))
174
 
                if m.group(2):
175
 
                    alter = Fraction(self.accs.index(m.group(2)) - 4, 4)
176
 
                else:
177
 
                    alter = 0
178
 
                return note, alter
179
 
            # HACK: were we using (rarely used) long english syntax?
180
 
            text = text.replace('flat', 'f').replace('sharp', 's')
181
 
        return False
182
 
            
183
 
            
184
 
def octaveToString(octave):
185
 
    """
186
 
    Convert numeric octave to a string with apostrophes or commas.
187
 
    0 -> "" ; 1 -> "'" ; -1 -> "," ; etc.
188
 
    """
189
 
    return octave < 0 and ',' * -octave or "'" * octave
190
 
 
191
 
def octaveToNum(octave):
192
 
    """
193
 
    Convert string octave to an integer:
194
 
    "" -> 0 ; "," -> -1 ; "'''" -> 3 ; etc.
195
 
    """
196
 
    return octave.count("'") - octave.count(",")
197
 
 
198
 
 
199
 
pitchInfo = {
200
 
    'nederlands': (
201
 
        ('c','d','e','f','g','a','b'),
202
 
        ('eses', 'eseh', 'es', 'eh', '', 'ih','is','isih','isis'),
203
 
        (('ees', 'es'), ('aes', 'as'))
204
 
    ),
205
 
    'english': (
206
 
        ('c','d','e','f','g','a','b'),
207
 
        ('ff', 'tqf', 'f', 'qf', '', 'qs', 's', 'tqs', 'ss'),
208
 
    ),
209
 
    'deutsch': (
210
 
        ('c','d','e','f','g','a','h'),
211
 
        ('eses', 'eseh', 'es', 'eh', '', 'ih','is','isih','isis'),
212
 
        (('ees', 'es'), ('aes', 'as'), ('hes', 'b'))
213
 
    ),
214
 
    'svenska': (
215
 
        ('c','d','e','f','g','a','h'),
216
 
        ('essess', '', 'ess', '', '', '','iss','','ississ'),
217
 
        (('ees', 'es'), ('aes', 'as'), ('hess', 'b'))
218
 
    ),
219
 
    'italiano': (
220
 
        ('do', 're', 'mi', 'fa', 'sol', 'la', 'si'),
221
 
        ('bb', 'bsb', 'b', 'sb', '', 'sd', 'd', 'dsd', 'dd')
222
 
    ),
223
 
    'espanol': (
224
 
        ('do', 're', 'mi', 'fa', 'sol', 'la', 'si'),
225
 
        ('bb', '', 'b', '', '', '', 's', '', 'ss')
226
 
    ),
227
 
    'portugues': (
228
 
        ('do', 're', 'mi', 'fa', 'sol', 'la', 'si'),
229
 
        ('bb', 'btqt', 'b', 'bqt', '', 'sqt', 's', 'stqt', 'ss')
230
 
    ),
231
 
    'vlaams': (
232
 
        ('do', 're', 'mi', 'fa', 'sol', 'la', 'si'),
233
 
        ('bb', '', 'b', '', '', '', 'k', '', 'kk')
234
 
    ),
235
 
}
236
 
 
237
 
pitchInfo['norsk'] = pitchInfo['deutsch']
238
 
pitchInfo['suomi'] = pitchInfo['deutsch']
239
 
pitchInfo['catalan'] = pitchInfo['italiano']
240
 
 
241
 
 
242
 
pitchWriter = dict(
243
 
    (lang, PitchWriter(*data)) for lang, data in pitchInfo.iteritems())
244
 
 
245
 
pitchReader = dict(
246
 
    (lang, PitchReader(*data)) for lang, data in pitchInfo.iteritems())
247
 
 
248