Beispiel #1
0
    def next(self, length):
        self.note.reset()
        self.duration.reset()
        self.velocity.reset()
        try:
            self.rest.reset()
        except Exception as e:
            print e
            print 'no rest to reset'
        seq = OSequence()
        total_length = 0
        while total_length < length:
            next = self.note.next() 
            next_duration = self.duration.next()
            next_rest = self.rest.next()
            if total_length+next_duration+next_rest > length:
                #if there isn't time for this note, fill the rest of the time with silence 
                #print "fill"+str(total_length)
                duration_left = length - total_length
                next.update({DURATION_64: duration_left, OFFSET_64: total_length, "velocity": 0})
            else:
                #print "add"+str(total_length)
                next.update({DURATION_64: next_duration, OFFSET_64: total_length, "velocity": self.velocity.next()})
            total_length += next_duration+next_rest
            seq.append(next)
            #print 'made it'

        self.last = seq
        return self.last
Beispiel #2
0
 def test_sequence_last_point_empty(self):
     """
     Ensure OSequence.last_point doesn't barf when the sequence is empty
     """
     from sebastian.core import OSequence
     from sebastian.core import OFFSET_64, DURATION_64
     sequence = OSequence([])
     self.assertEqual(sequence.last_point(), {DURATION_64: 0, OFFSET_64: 0})
Beispiel #3
0
 def test_sequence_last_point_empty(self):
     """
     Ensure OSequence.last_point doesn't barf when the sequence is empty
     """
     from sebastian.core import OSequence
     from sebastian.core import OFFSET_64, DURATION_64
     sequence = OSequence([])
     self.assertEqual(sequence.last_point(), {
         DURATION_64: 0, OFFSET_64: 0
     })
Beispiel #4
0
 def test_sequence_last_point(self):
     """
     Ensure that OSequence.last_point returns the highest offset note
     """
     points = [self.make_point(offset=x) for x in range(100, -1, -10)]
     from sebastian.core import OSequence
     from sebastian.core import OFFSET_64, MIDI_PITCH, DURATION_64
     sequence = OSequence(points)
     self.assertEqual(sequence.last_point(), {
         DURATION_64: 17 + 100, OFFSET_64: 16 + 100, MIDI_PITCH: 50 + 100
     })
Beispiel #5
0
 def test_sequence_last_point(self):
     """
     Ensure that OSequence.last_point returns the highest offset note
     """
     points = [self.make_point(offset=x) for x in range(100, -1, -10)]
     from sebastian.core import OSequence
     from sebastian.core import OFFSET_64, MIDI_PITCH, DURATION_64
     sequence = OSequence(points)
     self.assertEqual(sequence.last_point(), {
         DURATION_64: 17 + 100,
         OFFSET_64: 16 + 100,
         MIDI_PITCH: 50 + 100
     })
Beispiel #6
0
 def _(sequence):
     new_elements = []
     last_offset = sequence.next_offset()
     if sequence and sequence[0][OFFSET_64] != 0:
         old_sequence = OSequence([Point({OFFSET_64: 0})]) + sequence
     else:
         old_sequence = sequence
     for point in old_sequence:
         new_point = Point(point)
         new_point[OFFSET_64] = last_offset - new_point[
             OFFSET_64] - new_point.get(DURATION_64, 0)
         if new_point != {OFFSET_64: 0}:
             new_elements.append(new_point)
     return OSequence(sorted(new_elements, key=lambda x: x[OFFSET_64]))
Beispiel #7
0
class SebastianHandler(BaseHandler):

    def header(self, format, num_tracks, division):
        self.division = division
        self.tracks = [None] * num_tracks

    def track_start(self, track_num):
        self.current_sequence = OSequence()
        self.tracks[track_num] = self.current_sequence

    def note(self, offset, channel, midi_pitch, duration):
        offset_64 = 16 * offset // self.division
        duration_64 = 16 * duration // self.division
        point = Point({OFFSET_64: offset_64, MIDI_PITCH: midi_pitch, DURATION_64: duration_64})
        self.current_sequence.append(point)
    def test_velocity_from_note_with_invalid_velocities(self):
        from sebastian.core import OSequence, Point
        from sebastian.core import OFFSET_64, MIDI_PITCH, DURATION_64

        test = OSequence([
            Point({
                OFFSET_64: o,
                MIDI_PITCH: m,
                DURATION_64: d
            }) for (o, m, d) in [(0, 60, 16), (16, 72, 16), (32, 64, 16)]
        ])

        test[0]['velocity'] = -1
        test[1]['velocity'] = 300

        from sebastian.midi.write_midi import SMF
        from io import BytesIO
        out_fd = BytesIO(bytearray())

        expected_bytes = b'MThd\x00\x00\x00\x06\x00\x01\x00\x02\x00\x10MTrk\x00\x00\x00&\x00\xffX\x04\x04\x02\x18\x08\x00\xffY\x02\x00\x00\x00\xffQ\x03\x07\xa1 \x00\xff\x03\ttest song\x00\xff/\x00MTrk\x00\x00\x00\x1f\x00\xc0\x00\x00\x90<\x00\x10\x80<\x00\x00\x90H\xff\x10\x80H\x00\x00\x90@@\x10\x80@\x00\x00\xff/\x00'

        s = SMF([test])
        s.write(out_fd, title="test song")
        actual_bytes = out_fd.getvalue()

        self.assertEqual(expected_bytes, actual_bytes)
Beispiel #9
0
def one_file():
    seq = OSequence([])
    for num, pattern in enumerate(patterns):
        seq = seq + parse(pattern) * 10
    f = open("in_c_all.mid", "w")
    s = SMF([seq])
    s.write(f)
    f.close()
Beispiel #10
0
class SebastianHandler(BaseHandler):
    def header(self, format, num_tracks, division):
        self.division = division
        self.tracks = [None] * num_tracks

    def track_start(self, track_num):
        self.current_sequence = OSequence()
        self.tracks[track_num] = self.current_sequence

    def note(self, offset, channel, midi_pitch, duration):
        offset_64 = 16 * offset // self.division
        duration_64 = 16 * duration // self.division
        point = Point({
            OFFSET_64: offset_64,
            MIDI_PITCH: midi_pitch,
            DURATION_64: duration_64
        })
        self.current_sequence.append(point)
    def test_write_midi_multi_tacks(self):
        """
        Writing out test.mid to ensure midi processing works.

        This isn't really a test.
        """
        from sebastian.core import OSequence, Point
        from sebastian.core import OFFSET_64, MIDI_PITCH, DURATION_64
        test1 = OSequence([
            Point({
                OFFSET_64: o,
                MIDI_PITCH: m,
                DURATION_64: d
            }) for (o, m, d) in [
                (0, 60, 16),
                (16, 72, 16),
                (32, 64, 16),
                (48, 55, 16),
            ]
        ])
        test2 = OSequence([
            Point({
                OFFSET_64: o,
                MIDI_PITCH: m,
                DURATION_64: d
            }) for (o, m, d) in [
                (0, 55, 16),
                (16, 55, 16),
                (32, 64, 16),
                (48 + 16, 55, 16 * 10),
            ]
        ])

        from sebastian.midi.write_midi import SMF
        from io import BytesIO
        out_fd = BytesIO(bytearray())

        expected_bytes = b"""MThd\x00\x00\x00\x06\x00\x01\x00\x03\x00\x10MTrk\x00\x00\x00&\x00\xffX\x04\x04\x02\x18\x08\x00\xffY\x02\x00\x00\x00\xffQ\x03\x07\xa1 \x00\xff\x03\ttest song\x00\xff/\x00MTrk\x00\x00\x00'\x00\xc0\x00\x00\x90<@\x10\x80<\x00\x00\x90H@\x10\x80H\x00\x00\x90@@\x10\x80@\x00\x00\x907@\x10\x807\x00\x00\xff/\x00MTrk\x00\x00\x00(\x00\xc1\x10\x00\x917@\x10\x817\x00\x00\x917@\x10\x817\x00\x00\x91@@\x10\x81@\x00\x10\x917@\x81 \x817\x00\x00\xff/\x00"""
        s = SMF([test1, test2], instruments=[0, 16])
        s.write(out_fd, title="test song")
        actual_bytes = out_fd.getvalue()

        self.assertEqual(expected_bytes, actual_bytes)
Beispiel #12
0
def performance():
    tracks = []
    for track_num in range(8):  # 8 tracks
        seq = OSequence([])
        for pattern in patterns:
            seq += parse(pattern) * random.randint(2, 5)  # repeat 2-5 times
        tracks.append(seq | transpose(random.choice([-12, 0, 12])))  # transpose -1, 0 or 1 octaves
    f = open("in_c_performance.mid", "w")
    s = SMF(tracks)
    s.write(f)
    f.close()
    def test_write_midi(self):
        """
        Writing out test.mid to ensure midi processing works.

        This isn't really a test.
        """

        from sebastian.core import OSequence, Point
        from sebastian.core import OFFSET_64, MIDI_PITCH, DURATION_64
        test = OSequence([
            Point({
                OFFSET_64: o,
                MIDI_PITCH: m,
                DURATION_64: d
            })
            for (o, m,
                 d) in [(0, 60,
                         16), (16, 72,
                               16), (32, 64,
                                     16), (48, 55,
                                           16), (64, 74,
                                                 16), (80, 62,
                                                       16), (96, 50,
                                                             16), (112, 48,
                                                                   16),
                        (128, 36,
                         16), (144, 24,
                               16), (160, 40,
                                     16), (176, 55,
                                           16), (192, 26,
                                                 16), (208, 38,
                                                       16), (224, 50,
                                                             16), (240, 48,
                                                                   16)]
        ])

        from sebastian.midi.write_midi import SMF
        from io import BytesIO
        out_fd = BytesIO(bytearray())

        expected_bytes = b'MThd\x00\x00\x00\x06\x00\x01\x00\x02\x00\x10MTrk\x00\x00\x00%\x00\xffX\x04\x04\x02\x18\x08\x00\xffY\x02\x00\x00\x00\xffQ\x03\x07\xa1 \x00\xff\x03\x08untitled\x00\xff/\x00MTrk\x00\x00\x00\x87\x00\xc0\x00\x00\x90<@\x10\x80<\x00\x00\x90H@\x10\x80H\x00\x00\x90@@\x10\x80@\x00\x00\x907@\x10\x807\x00\x00\x90J@\x10\x80J\x00\x00\x90>@\x10\x80>\x00\x00\x902@\x10\x802\x00\x00\x900@\x10\x800\x00\x00\x90$@\x10\x80$\x00\x00\x90\x18@\x10\x80\x18\x00\x00\x90(@\x10\x80(\x00\x00\x907@\x10\x807\x00\x00\x90\x1a@\x10\x80\x1a\x00\x00\x90&@\x10\x80&\x00\x00\x902@\x10\x802\x00\x00\x900@\x10\x800\x00\x00\xff/\x00'

        s = SMF([test], instruments=None)
        s.write(out_fd)
        actual_bytes = out_fd.getvalue()

        self.assertEqual(expected_bytes, actual_bytes)
Beispiel #14
0
 def test_empty_sequence_merge(self):
     """
     Ensure that an empty sequence merge is an identity operation
     """
     s1 = self.make_sequence()
     from sebastian.core import OSequence
     s2 = OSequence([])
     merged = s1 // s2
     from sebastian.core import OFFSET_64, DURATION_64, MIDI_PITCH
     self.assertEqual(merged._elements, [{
         MIDI_PITCH: 50,
         OFFSET_64: 16,
         DURATION_64: 17
     }, {
         MIDI_PITCH: 53,
         OFFSET_64: 19,
         DURATION_64: 20
     }])
Beispiel #15
0
 def test_make_sequence(self):
     """
     Ensure sequences are composed of notes in the correct order
     """
     p1 = self.make_point()
     p2 = self.make_point(offset=50)  # we need to dedupe somehow
     from sebastian.core import OSequence
     from sebastian.core import OFFSET_64, MIDI_PITCH, DURATION_64
     sequence = OSequence([p1, p2])
     self.assertEqual(sequence._elements, [{
         DURATION_64: 17,
         OFFSET_64: 16,
         MIDI_PITCH: 50
     }, {
         DURATION_64: 17 + 50,
         OFFSET_64: 16 + 50,
         MIDI_PITCH: 50 + 50
     }])
Beispiel #16
0
def parse(s, offset=0):
    return OSequence(list(parse_block(tokenize(s), offset=offset)))
Beispiel #17
0
    MIDI_PITCH: 50,
    DURATION_64: 16,
})

assert p1.tuple(OFFSET_64, DURATION_64) == (16, 16)


## sequences

p2 = Point({
    OFFSET_64: 32,
    MIDI_PITCH: 52,
    DURATION_64: 16,
})

s1 = OSequence([p1, p2])

assert s1._elements == [
    {DURATION_64: 16, OFFSET_64: 16, MIDI_PITCH: 50},
    {DURATION_64: 16, OFFSET_64: 32, MIDI_PITCH: 52}
]

assert s1.last_point() == {
    MIDI_PITCH: 52, OFFSET_64: 32, DURATION_64: 16
}

assert OSequence([]).last_point() == {
    OFFSET_64: 0, DURATION_64: 0
}

Beispiel #18
0
 def track_start(self, track_num):
     self.current_sequence = OSequence()
     self.tracks[track_num] = self.current_sequence
Beispiel #19
0
def make_hseq(notes):
    return HSeq(Point(degree=n, duration_64=d) for n, d in notes)


# produces eighth and quarter notes
hlf = 16
qtr = 8

# tuples specify pitch and duration
p1 = [(8, qtr), (6, qtr), (5, qtr), (6, qtr)]
p2 = [(8, qtr), (6, qtr), (5, hlf)]
p3 = [(3, qtr), (2, qtr), (1, hlf)]
p4 = [(1, qtr), (6, qtr), (5, qtr), (6, qtr)]
p5 = [(1, qtr), (6, qtr), (5, hlf)]

partA = p1 + p2 + p1 + p3
partB = p4 + p5 + p4 + p3
parts = partA + partA + partB + partB

hseq = make_hseq(parts)

oseq = OSequence(hseq)

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_midi.write("shortning_bread_2.mid", [seq])
Beispiel #20
0
from sebastian.core.notes import Key, major_scale
from sebastian.core.transforms import degree_in_key_with_octave, midi_pitch, transpose

# Hanon 1

up_degrees = [1, 3, 4, 5, 6, 5, 4, 3]
down_degrees = [6, 4, 3, 2, 1, 2, 3, 4]
final_degree = [1]

sections = [
    (up_degrees, 4, range(14)),
    (down_degrees, 4, range(13, -2, -1)),
    (final_degree, 32, range(1)),
]

hanon_1 = OSequence()

for section in sections:
    pattern, duration_64, offset = section
    for o in offset:
        for note in pattern:
            hanon_1.append({"degree": note + o, DURATION_64: duration_64})

hanon_1 = hanon_1 | degree_in_key_with_octave(Key("C", major_scale),
                                              4) | midi_pitch()

hanon_rh_1 = hanon_1
hanon_lh_1 = hanon_1 | transpose(-12)

seq = hanon_lh_1 // hanon_rh_1
Beispiel #21
0
from sebastian.core import VSeq, HSeq, Point, OSequence
from sebastian.core.transforms import midi_pitch, degree_in_key_with_octave, add
from sebastian.core.notes import Key, major_scale

from sebastian.midi import write_midi


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])


# an abstract VSeq of 3 notes in root position (degree 1, 3 and 5)
root_triad = VSeq(Point(degree=n) for n in [1, 3, 5])

quaver_point = Point({DURATION_64: 8})

# an OSequence with alberti figuration repeated 16 times using quavers
alberti_osequence = OSequence(alberti(root_triad) * 16 | add(quaver_point))

C_major = Key("C", major_scale)

# note values filled-out for C major in octave 5 then MIDI pitches calculated
seq = alberti_osequence | degree_in_key_with_octave(C_major, 5) | midi_pitch()

# write to file:
write_midi.write("alberti.mid", [seq])
Beispiel #22
0
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])
Beispiel #23
0
 def track_start(self, track_num):
     self.current_sequence = OSequence()
     self.tracks[track_num] = self.current_sequence
Beispiel #24
0
from sebastian.core.transforms import degree_in_key_with_octave, midi_pitch, transpose


# Hanon 1

up_degrees = [1, 3, 4, 5, 6, 5, 4, 3]
down_degrees = [6, 4, 3, 2, 1, 2, 3, 4]
final_degree = [1]

sections = [
    (up_degrees, 4, range(14)),
    (down_degrees, 4, range(13, -2, -1)),
    (final_degree, 32, range(1)),
]

hanon_1 = OSequence()

for section in sections:
    pattern, duration_64, offset = section
    for o in offset:
        for note in pattern:
            hanon_1.append({"degree": note + o, DURATION_64: duration_64})

hanon_1 = hanon_1 | degree_in_key_with_octave(Key("C", major_scale), 4) | midi_pitch()

hanon_rh_1 = hanon_1
hanon_lh_1 = hanon_1 | transpose(-12)

seq = hanon_lh_1 // hanon_rh_1

Beispiel #25
0
 def make_sequence(self, offset=0):
     points = [self.make_point(offset), self.make_point(offset + 3)]
     from sebastian.core import OSequence
     return OSequence(points)
Beispiel #26
0
# 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
seq8 = seq8 | degree_in_key(C_MAJOR)

# annotate with lilypond
seq8 = seq8 | lilypond()

# write out lilypond file
write("example.ly", seq8)