Source code for mingus.containers.note_container

#!/usr/bin/python
# -*- coding: utf-8 -*-

#    mingus - Music theory Python package, note_container module.
#    Copyright (C) 2008-2009, Bart Spaans
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.

from note import Note
from mingus.core import intervals, chords, progressions
from mt_exceptions import UnexpectedObjectError

[docs]class NoteContainer(object): """A container for notes. The NoteContainer provides a container for the mingus.containers.Note objects. It can be used to store single and multiple notes and is required for working with Bars. """ notes = []
[docs] def __init__(self, notes=[]): self.empty() self.add_notes(notes)
[docs] def empty(self): """Empty the container.""" self.notes = []
[docs] def add_note(self, note, octave=None, dynamics={}): """Add a note to the container and sorts the notes from low to high. The note can either be a string, in which case you could also use the octave and dynamics arguments, or a Note object. """ if type(note) == str: if octave is not None: note = Note(note, octave, dynamics) elif len(self.notes) == 0: note = Note(note, 4, dynamics) else: if Note(note, self.notes[-1].octave) < self.notes[-1]: note = Note(note, self.notes[-1].octave + 1, dynamics) else: note = Note(note, self.notes[-1].octave, dynamics) if not hasattr(note, 'name'): raise UnexpectedObjectError("Object '%s' was not expected. " "Expecting a mingus.containers.Note object." % note) if note not in self.notes: self.notes.append(note) self.notes.sort() return self.notes
[docs] def add_notes(self, notes): """Feed notes to self.add_note. The notes can either be an other NoteContainer, a list of Note objects or strings or a list of lists formatted like this: >>> notes = [['C', 5], ['E', 5], ['G', 6]] or even: >>> notes = [['C', 5, {'volume': 20}], ['E', 6, {'volume': 20}]] """ if hasattr(notes, 'notes'): for x in notes.notes: self.add_note(x) return self.notes elif hasattr(notes, 'name'): self.add_note(notes) return self.notes elif type(notes) == str: self.add_note(notes) return self.notes for x in notes: if type(x) == list and len(x) != 1: if len(x) == 2: self.add_note(x[0], x[1]) else: self.add_note(x[0], x[1], x[2]) else: self.add_note(x) return self.notes
[docs] def from_chord(self, shorthand): """Shortcut to from_chord_shorthand.""" return self.from_chord_shorthand(shorthand)
[docs] def from_chord_shorthand(self, shorthand): """Empty the container and add the notes in the shorthand. See mingus.core.chords.from_shorthand for an up to date list of recognized format. Example: >>> NoteContainer().from_chord_shorthand('Am') ['A-4', 'C-5', 'E-5'] """ self.empty() self.add_notes(chords.from_shorthand(shorthand)) return self
[docs] def from_interval(self, startnote, shorthand, up=True): """Shortcut to from_interval_shorthand.""" return self.from_interval_shorthand(startnote, shorthand, up)
[docs] def from_interval_shorthand(self, startnote, shorthand, up=True): """Empty the container and add the note described in the startnote and shorthand. See core.intervals for the recognized format. Examples: >>> nc = NoteContainer() >>> nc.from_interval_shorthand('C', '5') ['C-4', 'G-4'] >>> nc.from_interval_shorthand('C', '5', False) ['F-3', 'C-4'] """ self.empty() if type(startnote) == str: startnote = Note(startnote) n = Note(startnote.name, startnote.octave, startnote.dynamics) n.transpose(shorthand, up) self.add_notes([startnote, n]) return self
[docs] def from_progression(self, shorthand, key='C'): """Shortcut to from_progression_shorthand.""" return self.from_progression_shorthand(shorthand, key)
[docs] def from_progression_shorthand(self, shorthand, key='C'): """Empty the container and add the notes described in the progressions shorthand (eg. 'IIm6', 'V7', etc). See mingus.core.progressions for all the recognized format. Example: >>> NoteContainer().from_progression_shorthand('VI') ['A-4', 'C-5', 'E-5'] """ self.empty() chords = progressions.to_chords(shorthand, key) # warning Throw error, not a valid shorthand if chords == []: return False notes = chords[0] self.add_notes(notes) return self
[docs] def _consonance_test(self, testfunc, param=None): """Private function used for testing consonance/dissonance.""" n = list(self.notes) while len(n) > 1: first = n[0] for second in n[1:]: if param is None: if not testfunc(first.name, second.name): return False else: if not testfunc(first.name, second.name, param): return False n = n[1:] return True
[docs] def is_consonant(self, include_fourths=True): """Test whether the notes are consonants. See the core.intervals module for a longer description on consonance. """ return self._consonance_test(intervals.is_consonant, include_fourths)
[docs] def is_perfect_consonant(self, include_fourths=True): """Test whether the notes are perfect consonants. See the core.intervals module for a longer description on consonance. """ return self._consonance_test(intervals.is_perfect_consonant, include_fourths)
[docs] def is_imperfect_consonant(self): """Test whether the notes are imperfect consonants. See the core.intervals module for a longer description on consonance. """ return self._consonance_test(intervals.is_imperfect_consonant)
[docs] def is_dissonant(self, include_fourths=False): """Test whether the notes are dissonants. See the core.intervals module for a longer description. """ return not self.is_consonant(not include_fourths)
[docs] def remove_note(self, note, octave=-1): """Remove note from container. The note can either be a Note object or a string representing the note's name. If no specific octave is given, the note gets removed in every octave. """ res = [] for x in self.notes: if type(note) == str: if x.name != note: res.append(x) else: if x.octave != octave and octave != -1: res.append(x) else: if x != note: res.append(x) self.notes = res return res
[docs] def remove_notes(self, notes): """Remove notes from the containers. This function accepts a list of Note objects or notes as strings and also single strings or Note objects. """ if type(notes) == str: return self.remove_note(notes) elif hasattr(notes, 'name'): return self.remove_note(notes) else: map(lambda x: self.remove_note(x), notes) return self.notes
[docs] def remove_duplicate_notes(self): """Remove duplicate and enharmonic notes from the container.""" res = [] for x in self.notes: if x not in res: res.append(x) self.notes = res return res
[docs] def sort(self): """Sort the notes in the container from low to high.""" self.notes.sort()
[docs] def augment(self): """Augment all the notes in the NoteContainer.""" for n in self.notes: n.augment()
[docs] def diminish(self): """Diminish all the notes in the NoteContainer.""" for n in self.notes: n.diminish()
[docs] def determine(self, shorthand=False): """Determine the type of chord or interval currently in the container.""" return chords.determine(self.get_note_names(), shorthand)
[docs] def transpose(self, interval, up=True): """Transpose all the notes in the container up or down the given interval.""" for n in self.notes: n.transpose(interval, up) return self
[docs] def get_note_names(self): """Return a list with all the note names in the current container. Every name will only be mentioned once. """ res = [] for n in self.notes: if n.name not in res: res.append(n.name) return res
[docs] def __repr__(self): """Return a nice and clean string representing the note container.""" return str(self.notes)
[docs] def __getitem__(self, item): """Enable the use of the container as a simple array. Example: >>> n = NoteContainer(['C', 'E', 'G']) >>> n[0] 'C-4' """ return self.notes[item]
[docs] def __setitem__(self, item, value): """Enable the use of the [] notation on NoteContainers. This function accepts Notes and notes as string. Example: >>> n = NoteContainer(['C', 'E', 'G']) >>> n[0] = 'B' >>> n ['B-4', 'E-4', 'G-4'] """ if type(value) == str: n = Note(value) self.notes[item] = n else: self.notes[item] = value return self.notes
[docs] def __add__(self, notes): """Enable the use of the '+' operator on NoteContainers. Example: >>> n = NoteContainer(['C', 'E', 'G']) >>> n + 'B' ['C-4', 'E-4', 'G-4', 'B-4'] """ self.add_notes(notes) return self
[docs] def __sub__(self, notes): """Enable the use of the '-' operator on NoteContainers. Example: >>> n = NoteContainer(['C', 'E', 'G']) >>> n - 'E' ['C-4', 'G-4'] """ self.remove_notes(notes) return self
[docs] def __len__(self): """Return the number of notes in the container.""" return len(self.notes)
[docs] def __eq__(self, other): """Enable the '==' operator for NoteContainer instances.""" for x in self: if x not in other: return False return True