/usr/share/pyshared/pyknon/music.py is in python-pyknon 1.0-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
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 | import collections
import copy
from pyknon import notation
class MusiclibError(Exception):
pass
class Rest(object):
def __init__(self, dur=0.25):
self.dur = dur
def __repr__(self):
return "<R: {0}>".format(self.dur)
def __eq__(self, other):
return self.dur == other.dur
@property
def verbose(self):
return "<Rest: {0}>".format(self.dur)
@property
def midi_dur(self):
# The MIDI library uses 1 for quarter note but we use 0.25
return self.dur * 4
def stretch_dur(self, factor):
return Rest(self.dur * factor)
class Note(object):
def __init__(self, value=0, octave=5, dur=0.25, volume=100):
if isinstance(value, str):
self.value, self.octave, self.dur, self.volume = notation.parse_note(value)
else:
offset, val = divmod(value, 12)
self.value = val
self.octave = octave + offset
self.dur = dur
self.volume = volume
def __eq__(self, other):
return self.value == other.value and self.dur == other.dur and self.octave == other.octave
def __sub__(self, other):
return self.midi_number - other.midi_number
def __repr__(self):
return "<{0}>".format(self.name)
@property
def verbose(self):
return "<Note: {0}, {1}, {2}>".format(self.value, self.octave, self.dur)
@property
def name(self):
note_names = "C C# D D# E F F# G G# A A# B".split()
return note_names[self.value % 12]
@property
def midi_number(self):
return self.value + (self.octave * 12)
@property
def midi_dur(self):
# The MIDI library uses 1 for quarter note but we use 0.25
return self.dur * 4
def __note_octave(self, octave):
"""Return a note value in terms of a given octave octave
n = Note(11, 4)
__note_octave(n, 5) = -1
"""
return self.value + ((self.octave - octave) * 12)
def transposition(self, index):
return Note(self.value + index, self.octave, self.dur, self.volume)
## FIXME: transpose down
def tonal_transposition(self, index, scale):
pos = index + scale.index(self) - 1
octave, rest = divmod(pos, 7)
note = copy.copy(scale[pos % len(scale)])
note.octave += octave
return note
def harmonize(self, scale, interval=3, size=3):
i = (interval - 1)
indices = range(1, size*i, i)
return [self.tonal_transposition(x, scale) for x in indices]
def inversion(self, index=0, initial_octave=None):
value = self.__note_octave(initial_octave) if initial_octave else self.value
octv = initial_octave if initial_octave else self.octave
note_value = (2 * index) - value
return Note(note_value, octv, self.dur, self.volume)
def stretch_dur(self, factor):
return Note(self.value, self.octave, self.dur * factor, self.volume)
class NoteSeq(collections.MutableSequence):
@staticmethod
def _is_note_or_rest(args):
return all([True if isinstance(x, Note) or isinstance(x, Rest) else False for x in args])
@staticmethod
def _make_note_or_rest(note_list):
if note_list[0] is not None:
return Note(*note_list)
else:
return Rest(note_list[2])
@staticmethod
def _parse_score(filename):
with open(filename) as score:
notes = []
for line in score:
notes.extend([note for note in line.split()])
return notes
def __init__(self, args=None):
if isinstance(args, str):
if args.startswith("file://"):
filename = args.replace("file://", "")
note_lists = notation.parse_notes(self._parse_score(filename))
else:
note_lists = notation.parse_notes(args.split())
self.items = [self._make_note_or_rest(x) for x in note_lists]
elif isinstance(args, collections.Iterable):
if self._is_note_or_rest(args):
self.items = args
else:
raise MusiclibError("Every argument have to be a Note or a Rest.")
elif args is None:
self.items = []
else:
raise MusiclibError("NoteSeq doesn't accept this type of data.")
def __iter__(self):
for x in self.items:
yield x
def __delitem__(self, i):
del self.items[i]
def __getitem__(self, i):
if isinstance(i, int):
return self.items[i]
else:
return NoteSeq(self.items[i])
def __len__(self):
return len(self.items)
def __setitem__(self, i, value):
self.items[i] = value
def __repr__(self):
return "<Seq: {0}>".format(self.items)
def __eq__(self, other):
if len(self) == len(other):
return all(x == y for x, y in zip(self.items, other.items))
def __add__(self, other):
return NoteSeq(self.items + other.items)
def __mul__(self, n):
return NoteSeq(self.items * n)
@property
def verbose(self):
string = ", ".join([note.verbose for note in self.items])
return "<NoteSeq: [{0}]>".format(string)
def retrograde(self):
return NoteSeq(list(reversed(self.items)))
def insert(self, i, value):
self.items.insert(i, value)
def transposition(self, index):
return NoteSeq([x.transposition(index) if isinstance(x, Note) else x
for x in self.items])
def _make_note(self, item):
return Note(item) if (isinstance(item, int) or isinstance(item, str)) else item
def transposition_startswith(self, note_start):
note = self._make_note(note_start)
return self.transposition(note - self.items[0])
def inversion(self, index=0):
initial_octave = self.items[0].octave
return NoteSeq([x.inversion(index, initial_octave) if isinstance(x, Note)
else x for x in self.items])
def inversion_startswith(self, note_start):
note = self._make_note(note_start)
inv = self.transposition_startswith(Note(0, note.octave)).inversion()
return inv.transposition_startswith(note)
def harmonize(self, interval=3, size=3):
return [NoteSeq(note.harmonize(self, interval, size)) for note in self]
def rotate(self, n=1):
modn = n % len(self)
result = self.items[modn:] + self.items[0:modn]
return NoteSeq(result)
def stretch_dur(self, factor):
return NoteSeq([x.stretch_dur(factor) for x in self.items])
## TODO: gives an error with rests
def intervals(self):
v1 = [x.value for x in self]
v2 = [x.value for x in self.rotate()]
return [y - x for x, y in zip(v1, v2[:-1])]
def stretch_inverval(self, factor):
intervals = [x + factor for x in self.intervals()]
note = copy.copy(self[0])
result = NoteSeq([note])
for i in intervals:
note = note.transposition(i)
result.append(note)
return result
# Aliases
transp = transposition_startswith
inv = inversion_startswith
|