def test_hseq_repeats(self): from sebastian.core import HSeq from sebastian.core import MIDI_PITCH s1 = HSeq([ { MIDI_PITCH: 50 }, { MIDI_PITCH: 51 }, ]) expected = HSeq([ { MIDI_PITCH: 50 }, { MIDI_PITCH: 51 }, { MIDI_PITCH: 50 }, { MIDI_PITCH: 51 }, ]) self.assertEqual(expected, s1 * 2)
def build_movement(): # Define our alberti bass signature. alberti = arpeggio([0, 2, 1, 2]) # Create the basic interval pattern. intervals = HSeq({DEGREE: x} for x in [1, 5, 1, 4, 1, 5, 1]) # Create the rhythm rhythm = HSeq({DURATION_64: x} for x in [128, 64, 64, 64, 64, 64, 64]) # Set specific chords to be used in certain measures. intervals[1]["chord"] = (-3, -1, 0, 2) # second inversion 7th intervals[3]["chord"] = (-3, 0, 2) # second inversion intervals[5]["chord"] = (-5, -3, 0) # first inversion # Combine the sequences, make them chords, produce alberti on the chords, # fill with each being 8, expand it to a flat sequence. melody = intervals & rhythm | chord() | alberti | fill(8) | expand # Define our key C_major = Key("C", major_scale) #key(major_scale(-2)) # Set the degree, add the midi pitch, make it an OSequence, add debugging information. return melody | degree_in_key_with_octave(C_major, 5) | midi_pitch() | OSequence | debug
def test_hseq_subseq(self): """ Ensure that two sequences can be zipped together, unifying its points. """ from sebastian.core import HSeq from sebastian.core import MIDI_PITCH, DURATION_64 s1 = HSeq([{ MIDI_PITCH: 50, DURATION_64: 10 }, { MIDI_PITCH: 51, DURATION_64: 10 }, { MIDI_PITCH: 52, DURATION_64: 10 }, { MIDI_PITCH: 53, DURATION_64: 10 }]) s2 = s1.subseq(20, 40) self.assertEqual( s2, HSeq([ { MIDI_PITCH: 51, DURATION_64: 10 }, { MIDI_PITCH: 52, DURATION_64: 10 }, ]))
def test_hseq_doesnt_track_duration_on_append(self): """ Ensure hseq doesnt do offset modification """ from sebastian.core import HSeq, Point s1 = HSeq() s1.append(Point(duration=10)) s1.append(Point(duration=10)) for point in s1: self.assertTrue('offset' not in point)
def next_by_counts(self, count=None): self.note.reset() self.duration.reset() self.velocity.reset() seq = HSeq() if counts != None: counts = random.randint(1,30) for i in range(counts): next = self.note.next() next.update({DURATION_64: self.duration.next(), "velocity": self.velocity.next()}) seq.append(next) self.last = OSequence(seq) return self.last
def gen_rand_seq(self, counts=None): snippet=" " seq = HSeq() note = Wandering(self.keyboard) duration = Wandering(range(4,8),3) velocity = Wandering(range(40,127),10) if counts == None: counts = random.randint(1,30) for i in range(counts): next = note.next() next.update({DURATION_64: duration.next(), "velocity": velocity.next()}) seq.append(next) self.last_sequence = seq return seq
def test_write(self): import tempfile pitches = HSeq({"pitch": n} for n in [-2, 0, 2, -3]) seq = pitches | add({"octave": 5, DURATION_64: 16}) | lilypond() f = tempfile.NamedTemporaryFile(suffix=".ly", delete=False) write(f.name, seq) with open(f.name) as g: self.assertEqual(g.read(), "{ c'4 d'4 e'4 f'4 }")
def test_sequence_map(self): """ Ensure map_points applys the function """ from sebastian.core import HSeq, Point s1 = (HSeq(Point(a=3, c=5)) + HSeq([Point(d=x) for x in range(10)]) + HSeq(Point(a=5))) def double_a(point): if 'a' in point: point['a'] *= 2 return point s2 = s1.map_points(double_a) self.assertEqual(s2[0]['a'], 6) self.assertEqual(s2[-1]['a'], 10)
def test_display_skipped_on_empty(self): """ If all lilypond output is empty, ensure we don't call lilypond """ empty = HSeq({"fake": n} for n in range(2)) seq = empty | lilypond() displayed = seq.display() self.assertTrue(isinstance(displayed, HSeq))
def test_hseq_subseq(self): """ Ensure that two sequences can be zipped together, unifying its points. """ from sebastian.core import HSeq from sebastian.core import MIDI_PITCH, DURATION_64 s1 = HSeq([ {MIDI_PITCH: 50, DURATION_64: 10}, {MIDI_PITCH: 51, DURATION_64: 10}, {MIDI_PITCH: 52, DURATION_64: 10}, {MIDI_PITCH: 53, DURATION_64: 10} ]) s2 = s1.subseq(20, 40) self.assertEqual(s2, HSeq([ {MIDI_PITCH: 51, DURATION_64: 10}, {MIDI_PITCH: 52, DURATION_64: 10}, ]))
def test_basic_sequence_zip(self): """ Ensure that two sequences can be zipped together, unifying its points. """ from sebastian.core import HSeq from sebastian.core import MIDI_PITCH, DURATION_64 s1 = HSeq([{ MIDI_PITCH: 50 }, { MIDI_PITCH: 51 }, { MIDI_PITCH: 53 }, { MIDI_PITCH: 54 }]) s2 = HSeq([{ DURATION_64: 17 }, { DURATION_64: 18 }, { DURATION_64: 20 }, { DURATION_64: 21 }]) s_zipped = s1 & s2 self.assertEqual( s_zipped, HSeq([{ MIDI_PITCH: 50, DURATION_64: 17 }, { MIDI_PITCH: 51, DURATION_64: 18 }, { MIDI_PITCH: 53, DURATION_64: 20 }, { MIDI_PITCH: 54, DURATION_64: 21 }]))
def test_lilypond_transform_rhythms(self): """ Ensure points without pitches can render to lilypond """ from sebastian.core.transforms import lilypond from sebastian.core import DURATION_64 from sebastian.core import HSeq, Point h1 = HSeq([Point({DURATION_64: 64}), Point({DURATION_64: 0}), Point()]) h2 = h1 | lilypond() self.assertEqual(h2._elements[0]['lilypond'], r"\xNote c'1") self.assertEqual(h2._elements[1]['lilypond'], '') self.assertEqual(h2._elements[2]['lilypond'], '')
def test_hseq_concat(self): """ Ensure hseq can concatenate """ from sebastian.core import HSeq from sebastian.core import MIDI_PITCH s1 = HSeq([ { MIDI_PITCH: 50 }, { MIDI_PITCH: 51 }, ]) s2 = HSeq([ { MIDI_PITCH: 60 }, { MIDI_PITCH: 59 }, ]) expected = HSeq([ { MIDI_PITCH: 50 }, { MIDI_PITCH: 51 }, { MIDI_PITCH: 60 }, { MIDI_PITCH: 59 }, ]) self.assertEqual(expected, s1 + s2)
def alberti(triad): """ takes a VSeq of 3 notes and returns an HSeq of those notes in an alberti figuration. """ return HSeq(triad[i] for i in [0, 2, 1, 2])
def make_horizontal_sequence(self): from sebastian.core import HSeq, Point return HSeq([Point(degree=degree) for degree in self.make_notes()])
def test_lilypond_transform(self): """ Ensure that it plays in G major. """ from sebastian.core.transforms import midi_pitch, add, lilypond from sebastian.core import DURATION_64 from sebastian.core import HSeq, Point h1 = HSeq( [Point(pitch=pitch) for pitch in [0, 1, 2, 3, 4, 11, -4, -11]]) positioned = h1 | add({'octave': 4, DURATION_64: 8}) pitched = positioned | midi_pitch() pitched[3]['octave'] = 5 pitched[4]['octave'] = 3 lilyed = pitched | lilypond() import pprint pprint.pprint(list(lilyed)) self.assertEqual(lilyed._elements, [{ 'duration_64': 8, 'lilypond': 'd8', 'midi_pitch': 50, 'octave': 4, 'pitch': 0 }, { 'duration_64': 8, 'lilypond': 'a8', 'midi_pitch': 57, 'octave': 4, 'pitch': 1 }, { 'duration_64': 8, 'lilypond': 'e8', 'midi_pitch': 52, 'octave': 4, 'pitch': 2 }, { 'duration_64': 8, 'lilypond': "b'8", 'midi_pitch': 59, 'octave': 5, 'pitch': 3 }, { 'duration_64': 8, 'lilypond': 'fis,8', 'midi_pitch': 54, 'octave': 3, 'pitch': 4 }, { 'duration_64': 8, 'lilypond': 'fisis8', 'midi_pitch': 55, 'octave': 4, 'pitch': 11 }, { 'duration_64': 8, 'lilypond': 'bes8', 'midi_pitch': 58, 'octave': 4, 'pitch': -4 }, { 'duration_64': 8, 'lilypond': 'beses8', 'midi_pitch': 57, 'octave': 4, 'pitch': -11 }])
def end(scale): return HSeq(scale[i] for i in [2, 1]) | add(quaver_point)
def h2_end1(scale): return HSeq(scale[i] for i in [0, 4]) | add(quaver_point)
def end(scale): return HSeq(scale[i] for i in [2, 1]) | add(quaver_point) def h2(scale): return HSeq(scale[i] for i in [0, 4, 3, 4]) | add(quaver_point) def h2_end1(scale): return HSeq(scale[i] for i in [0, 4]) | add(quaver_point) # there's two important quarter notes used at the ends of sections e1 = HSeq(scale[3]) | add(quarter_point) e2 = HSeq(scale[0]) | add(quarter_point) partA = h1(scale) + h1_end1(scale) + e1 + h1(scale) + end(scale) + e2 partB = h2(scale) + h2_end1(scale) + e1 + h2(scale) + end(scale) + e2 # here we see the basic structure of the song oseq = OSequence((partA * 2) + (partB * 2)) C_major = Key("C", major_scale) # note values filled-out for C major in octave 5 then MIDI pitches calculated seq = oseq | degree_in_key_with_octave(C_major, 5) | midi_pitch() # write to file: write_midi.write("shortning_bread_1.mid", [seq])
def h1(scale): return HSeq(scale[i] for i in [5, 4, 3, 4]) | add(quaver_point)
def make_hseq(notes): return HSeq(Point(degree=n, duration_64=d) for n, d in notes)
def test_output(self): pitches = HSeq({"pitch": n} for n in [-2, 0, 2, -3]) seq = pitches | add({"octave": 5, DURATION_64: 16}) | lilypond() self.assertEqual(output(seq), "{ c'4 d'4 e'4 f'4 }")
def arpeggio(pattern, point): """ turns each subsequence into an arpeggio matching the given ``pattern``. """ point['sequence'] = HSeq(point['sequence'][i] for i in pattern) return point
""" The main title theme to HBO's Game of Thrones by Ramin Djawadi """ from sebastian.core import Point, HSeq, OSequence from sebastian.core.transforms import midi_pitch, degree_in_key, add from sebastian.core.notes import Key, major_scale, minor_scale from sebastian.midi import write_midi C_Major = Key("C", major_scale) C_minor = Key("C", minor_scale) motive_degrees = HSeq(Point(degree=n) for n in [5, 1, 3, 4]) motive_rhythm_1 = HSeq(Point(duration_64=n) for n in [16, 16, 8, 8]) motive_rhythm_2 = HSeq( Point(duration_64=n) for n in [48, 48, 8, 8, 32, 32, 8, 8]) motive_1 = motive_degrees & motive_rhythm_1 motive_2 = (motive_degrees * 2) & motive_rhythm_2 # add key and octave seq1 = (motive_1 * 4) | add({"octave": 5}) | degree_in_key(C_minor) seq2 = (motive_1 * 4) | add({"octave": 5}) | degree_in_key(C_Major) seq3 = motive_2 | add({"octave": 4}) | degree_in_key(C_minor) seq = (seq1 + seq2 + seq3) | midi_pitch() | OSequence write_midi.write("game_of_thrones.mid", [seq], instruments=[49], tempo=350000)
seq3 = seq1 + seq2 # transpose and reverse seq4 = seq3 | transpose(12) | reverse() # merge seq5 = seq3 // seq4 # play MIDI player.play([seq5]) # write to MIDI write_midi.write("seq5.mid", [seq5]) # contruct a horizontal sequence of scale degrees seq6 = HSeq(Point(degree=degree) for degree in [1, 2, 3, 2, 1]) # put that sequence into C major, octave 4 quavers C_MAJOR = Key("C", major_scale) seq7 = seq6 | add({"octave": 4, DURATION_64: 8}) | degree_in_key(C_MAJOR) # convert to MIDI pitch and play player.play([OSequence(seq7 | midi_pitch())]) # sequence of first four degree of a scale seq8 = HSeq(Point(degree=n) for n in [1, 2, 3, 4]) # add duration and octave seq8 = seq8 | add({DURATION_64: 16, "octave": 5}) # put into C major