def test_sub(self): """ Test subtraction """ f0 = Fraction(5) f1 = Fraction(6) self.assertEqual(f0 - f1, -1) f2 = Fraction(7, 12) - f0 self.assertEqual(f2, Fraction("-4 5/12")) self.assertEqual(f2, Fraction(-53, 12))
def test_simplify(self): """ Basic test of simplification of fractions. """ f0 = Fraction(9, 10) f1 = Fraction(18, 20) self.assertEqual(f0, f1) f2 = Fraction(17, 20) self.assertNotEqual(f0, f2)
def test_add(self): """ Try adding Fractions together. """ f0 = Fraction(5) f1 = Fraction(6) self.assertEqual(f0 + f1, 11) f2 = Fraction(7, 12) + f0 self.assertEqual(f2, Fraction("5 7/12")) self.assertEqual(f2, Fraction(67, 12))
def test_reparse_string(self): """ Create some random fractions, get their string representation and check the this can by used to correctly reinstantiate the fraction. """ from random import randint for i in range(50): # Create a random fraction f0 = Fraction(randint(0, 100), randint(1, 100)) f0_str = str(f0) self.assertEqual(f0, Fraction(f0_str))
def test_mul(self): """ Test multiplication """ f0 = Fraction(1, 2) * Fraction(3) self.assertEqual(f0, Fraction(3, 2)) f1 = Fraction(5, 7) * Fraction(3, 9) self.assertEqual(f1, Fraction(15, 63)) f2 = Fraction(-5, 7) * Fraction(3, -9) self.assertEqual(f2, f1)
def test_int(self): """ Conversion to int """ from random import randint for i in range(50): n = randint(0, 100) d = randint(1, 100) self.assertEqual(int(Fraction(n, d)), n / d)
def test_long(self): """ Conversion to long """ from random import randint for i in range(50): n = randint(0, 100) d = randint(1, 100) self.assertEqual(long(Fraction(n, d)), long(n) / long(d))
def get_signs_for_word(self, index, offset=0): if offset > 0: return [] else: all_signs = self.tags[index] return [(sign, sign.tag, Fraction(1, len(all_signs))) for sign in all_signs]
def test_float(self): """ Conversion to float """ from random import randint for i in range(50): n = randint(0, 100) d = randint(1, 100) self.assertEqual(float(Fraction(n, d)), float(n) / float(d))
def test_zero_denominator(self): """ Setting a Fraction's denominator to 0 should raise an error. """ self.assertRaises(ZeroDivisionError, Fraction, 5, 0) f = Fraction(1) self.assertRaises(ZeroDivisionError, lambda x: f / x, 0)
def _get_duration(tokens): """Get a 'for' duration specification out of a line's tokens. Returns duration in quarter notes.""" duration = 1 if "for" in tokens: forpos = tokens.index("for") try: duration = float(Fraction(tokens.pop(forpos+1))) except: raise HarmonicalInputFileReadError, "'for' "\ "must be followed by an integer number "\ "of beats." tokens.pop(forpos) return duration
def __init__(self, inputs, durations=None, times=None, roman=False, *args, **kwargs): super(ChordInput, self).__init__(*args, **kwargs) self.inputs = inputs self.durations = durations self.times = times self.roman = roman # Compute the durations from times or vice versa if durations is None and times is None: raise ValueError, "cannot create a ChordInput with neither "\ "times nor durations given" elif times is None: self.times = [ sum(durations[:i], Fraction(0)) for i in range(len(durations)) ] elif durations is None: from jazzparser.utils.base import group_pairs self.durations = [ time1 - time0 for (time1, time0) in group_pairs(times) ] + [Fraction(1)] # Convert all strings to internal chord representation # Done now so we check the chords can all be understood before doing # anything else self.chords = [ Chord.from_name(name, roman=roman).to_db_mirror() for name in inputs ] for chord, dur in zip(self.chords, self.durations): chord.duration = dur
def test_create_string(self): """ Test creating a fraction from a string representation. """ f0 = Fraction("1 1/4") self.assertEqual(f0, Fraction(5, 4)) f1 = Fraction("5") self.assertEqual(f1, Fraction(5)) f2 = Fraction("5/4") self.assertEqual(f2, Fraction(5, 4)) for invalid in ["", "1.5", "1/1/1", "5 5", "4\\5", "a", "X"]: self.assertRaises(Fraction.ValueError, Fraction, invalid)
def __init__(self, inputs, durations=None, times=None, id=None, \ chords=None, sequence=None, *args, **kwargs): super(DbInput, self).__init__(*args, **kwargs) self.inputs = inputs self.durations = durations self.times = times self.id = id self.chords = chords self.sequence = sequence if durations is None and times is None: raise ValueError, "cannot create a DbInput with neither "\ "times nor durations given" elif times is None: self.times = [sum(durations[:i]) for i in range(len(durations))] elif durations is None: from jazzparser.utils.base import group_pairs self.durations = [ time1 - time0 for (time1, time0) in group_pairs(times) ] + [Fraction(1)]
def assign_durations(string): """ Computes the durations for each chord token in the string. Chords on their own get a duration of 1. Comma-separated sequences of chords get a duration of 1, split evenly between the members of the sequence. """ from jazzparser.data import Fraction # Make sure the commas are separate tokens string = string.replace(",", " , ") string = string.replace(":", " : ") string = string.replace("|", "") tokens = string.split() # Group tokens into comma-separated sequences in_sequence = False units = [] for token in tokens: if token == "," or token == ":": # The next token is in the same sequence as the last in_sequence = True elif in_sequence: # This token should be added to the previous token's sequence units[-1].append(token) in_sequence = False else: # Start of a new sequence units.append([token]) # Now build the list of durations durations = [] for unit in units: # Each sequence has a total length of 4 (assume 4 beats in bar) # Split this between its members. denominator = len(unit) members = [Fraction(4,denominator)] * denominator durations.extend(members) return durations
def test_div(self): """ Test division """ f0 = Fraction(1, 2) / 3 self.assertEqual(f0, Fraction(1, 6)) f0 = Fraction(1, 2) / Fraction(3) self.assertEqual(f0, Fraction(1, 6)) f1 = Fraction(5, 7) / Fraction(3, 9) self.assertEqual(f1, Fraction(45, 21)) f2 = Fraction(-5, 7) / Fraction(3, -9) self.assertEqual(f2, f1) f3 = Fraction(5, 7) / 0.5 self.assertEqual(f3, 10.0 / 7.0)
def render(self): """ Creates MIDI data from the path and chord types. @rtype: midi.EventStream @return: an event stream containing all the midi events """ mid = EventStream() mid.add_track() # Set the tempo at the beginning tempo = SetTempoEvent() tempo.tempo = self.tempo mid.add_event(tempo) # Set the instrument at the beginning instr = ProgramChangeEvent() instr.value = self.instrument mid.add_event(instr) beat_length = mid.resolution # Work out when each root change occurs time = Fraction(0) root_times = [] for root, length in self.path: root_times.append((root, time)) time += length def _root_at_time(time): current_root = root_times[0][0] for root, rtime in root_times[1:]: # Move through root until we get the first one that # occurs after the previous time if rtime > time: return current_root current_root = root # If we're beyond the time of the last root, use that one return current_root # Add each chord time = Fraction(0) bass_events = [] bass = self.bass_root is not None for chord_type, length in self.chord_types: tick_length = length * beat_length - 10 tick_time = time * beat_length # Find out what root we're on at this time root = _root_at_time(time) # Add all the necessary events for this chord chord_events = events_for_chord( root, chord_type, int(tick_time), int(tick_length), equal_temperament=self.equal_temperament, root_octave=self.root_octave, double_root=(self.double_root or bass)) if bass: # Add the bass note to the bass track bass_events.extend([copy.copy(ev) for ev in chord_events[-1]]) if bass and not self.double_root: # Remove the doubled root that we got for the bass line chord_events = sum(chord_events[:-1], []) # Add the main chord notes to the midi track for ev in chord_events: mid.add_event(ev) time += length if bass: bass_channel = 1 # Add another track to the midi file for the bass notes mid.add_track() # Select a bass instrument - picked bass instr = ProgramChangeEvent() instr.value = 33 instr.channel = bass_channel mid.add_event(instr) # Add all the bass notes for ev in bass_events: ev.channel = bass_channel mid.add_event(ev) return mid
def test_create_fraction(self): """ Simplest instantiation: fraction """ f = Fraction(9, 10)
def test_neg(self): """ Try negating Fractions """ f0 = Fraction(5, 7) self.assertEqual(-f0, Fraction(-5, 7)) f1 = Fraction("5 4/5") self.assertEqual(-f1, Fraction("-5 4/5"))
def test_equal(self): """ Test that equal Fractions evaluate as equal """ from random import randint for i in range(10): n = randint(0, 100) d = randint(1, 100) self.assertEqual(Fraction(n, d), Fraction(n, d)) self.assertEqual(Fraction(50, 4), Fraction("12 1/2")) self.assertEqual(Fraction(50, 4), Fraction(25, 2)) self.assertEqual(Fraction(7, 6), --Fraction(7, 6)) f = Fraction(7, 19) self.assertEqual(f, f / Fraction(6, 17) * Fraction(12, 34))
def path_to_tones(path, tempo=120, chord_types=None, root_octave=0, double_root=False, equal_temperament=False, timings=False): """ Takes a tonal space path, given as a list of coordinates, and generates the tones of the roots. @type path: list of (3d-coordinate,length) tuples @param path: coordinates of the points in the sequence and the length of each, in beats @type tempo: int @param tempo: speed in beats per second (Maelzel's metronome) @type chord_types: list of (string,length) @param chord_types: the type of chord to use for each tone and the time spent on that chord type, in beats. See L{CHORD_TYPES} keys for possible values. @type equal_temperament: bool @param equal_temperament: render all the pitches as they would be played in equal temperament. @rtype: L{ToneMatrix} @return: a tone matrix that can be used to render the sound """ # Use this envelope for all notes envelope = piano_envelope() sample_rate = DEFAULT_SAMPLE_RATE beat_length = 60.0 / tempo if timings: root_times = path else: # Work out when each root change occurs time = Fraction(0) root_times = [] for root,length in path: root_times.append((root,time)) time += length def _root_at_time(time): current_root = root_times[0][0] for root,rtime in root_times[1:]: # Move through root until we get the first one that # occurs after the previous time if rtime > time: return current_root current_root = root # If we're beyond the time of the last root, use that one return current_root if chord_types is None: # Default to just pure tones chord_types = [('prime',length) for __,length in path] if equal_temperament: _pitch_ratio = tonal_space_et_pitch else: _pitch_ratio = tonal_space_pitch_2d # Build the tone matrix by adding the tones one by one matrix = ToneMatrix(sample_rate=sample_rate) time = Fraction(0) for ctype,length in chord_types: coord = _root_at_time(time) pitch_ratio = _pitch_ratio(coord) duration = beat_length * float(length) # We want all enharmonic equivs of I to come out close to I, # not an octave above if not equal_temperament and coordinate_to_et_2d(coord) == 0 \ and pitch_ratio > 1.5: pitch_ratio /= 2.0 # Use a sine tone for each note tone = SineChordEvent(220*pitch_ratio, chord_type=ctype, duration=duration, envelope=envelope, root_octave=root_octave, root_weight=1.2, double_root=double_root) matrix.add_tone(beat_length * float(time), tone) time += length return matrix
def test_create_int(self): """ Simplest instantiation: int """ f = Fraction(9) self.assertEqual(f, 9)