Esempio n. 1
0
def play():

    # in example 14 we showed how to nest patterns in ways that are easier
    # to conceptualize.  For instance, these patterns play in order, and we
    # call that a movement.  However, it's often tedious to specify things
    # that replay again and again.  To do make this easy, we have a new
    # selector to introduce - Repeatedly!

    output = Performance(bpm=240, stop_seconds=100)

    scale_choices = Endlessly([scale("D minor")])
    source = ScaleSource(scales=scale_choices)

    pattern1 = Roman(symbols=Repeatedly("1 2 3 4".split(), cycles=2))
    pattern2 = Roman(symbols="4 3 2 1".split())
    movement1 = Ordered(sources=[pattern1, pattern2])

    pattern3 = Roman(symbols=Repeatedly("1 4 1 4 1 5 1 5".split(), cycles=2))
    pattern4 = Roman(symbols="I IV V I".split())
    movement2 = Ordered(sources=[pattern3, pattern4])

    suite = Ordered(sources=[movement1, movement2], channel=1)

    source.send_to(suite)

    # for this example, we won't do anything crazy with transpositions or anything.

    suite.send_to(output)

    conductor = Conductor(signal=[source], performance=output)
    conductor.start()
Esempio n. 2
0
def play():

    # examples/06_subdivide showed how to chop up the incoming quarter note signal
    # to create faster signals.  However, sometimes in a given bar we want a mix of note durations.
    # how do we do that?

    # For this example, lets play whole note chords underneath a quarter note melody.

    # implementation caveat:
    # Because the "beat" signal from the conductor comes in every quarter note, we have to take
    # care and rest in the right places of the input signal to avoid funness.  This may get easier
    # later.

    # this is pretty close to what we did in 09_harmony.py

    output = Performance(bpm=120, stop_seconds=15)

    # this performance will actually complete so stop_seconds is mostly ignorable.

    scale_choices = Endlessly([scale("D major")])
    source = ScaleSource(scales=scale_choices)

    # the underlying chords - note the rests
    chords = Roman(symbols=Endlessly(
        "I - - - I - - - IV - - - V - - - ".split()),
                   channel=1)
    # every beat is a quarter note, but on the first beat in a cycle of 4
    # play a WHOLE note
    chords_len = Duration(lengths=Endlessly([1, 0, 0, 0]))
    source.chain([chords, chords_len, output])

    # the melody
    # TODO: Roman should figure out if input is a string here and auto-split.
    pattern2 = Roman("1 2 3 1 2 2 1 3 3 1 3 2".split(), channel=2)
    pattern3 = Roman("1 3 3 1 4 4 1 3 3 1 5 -".split(), channel=2)
    pattern4 = Roman("1 5 5 1 4 4 1 3 3 1 2 2".split(), channel=2)
    pattern5 = Roman("1 2 4 6 4 2 1 6 4 2 1 -".split(), channel=2)
    melody_sequence = [pattern2, pattern3, pattern4, pattern5]
    melody = Ordered(sources=Endlessly(melody_sequence))
    # we could just do: source.chain(source, ordered) here
    # but it would mean later we would have less flexibility
    # to make the patterns feel different.
    source.chain([melody])
    for pattern in melody_sequence:
        source.chain([pattern, output])

    conductor = Conductor(signal=[source], performance=output)
    conductor.start()
Esempio n. 3
0
def play():

    # Let's show how to play a sequence of one length but omit every Nth note

    output = Performance(bpm=120, stop_seconds=15)

    source = ScaleSource(scales=scale("D minor"))

    melody = Roman(symbols=Endlessly("1 2 3 4 5 6 7".split()), channel=1)
    silence = Permit(when=Endlessly([1, 1, 0]))

    # so we're playing a pattern of 1 2 3 4 5 6 7 of scale degree notes in a giant loop
    # the first time through what really happens is:
    #    1 2 <REST> 4 5 <REST> 7
    # and the second time:
    #    1 <REST> 3 4 <REST> 6 7
    # and the third time:
    #    <REST> 2 3 <REST> 5 6 <REST>
    # all of this is because the pattern and the 'silence' pattern are of unequal lengths

    # this is not ALWAYS relevant in a composition, but if you want to do something
    # polyrhytmic and unorthodox (not just with 'Silence') patterns of  unequal length
    # can be interesting.

    # HOMEWORK: experiment with changing the melody patterns and the silence patterns

    # HOMEWORK: change endlessly to randomly, and see what happens

    source.chain([melody, silence, output])
    conductor = Conductor(signal=[source], performance=output)
    conductor.start()
Esempio n. 4
0
    def _build_players(self):

        for (instrument_name, pattern_list) in self.patterns.items():

            instrument = self._factory.instruments[instrument_name]
            channel = instrument.channel
            notation = instrument.notation
            sources = []

            print("PATTERN LIST= %s" % pattern_list)
            if isinstance(pattern_list, str):
                pattern_list = [ pattern_list ]

            for pattern_name in pattern_list:

                pattern = self._factory.patterns.get(pattern_name, None)
                if pattern is None:
                    raise Exception("pattern not found: %s" % pattern_name)

                real_pattern = None

                if notation == 'roman':
                    real_pattern = Roman(symbols=pattern)
                    print("RP=%s" % pattern)
                elif notation == 'literal':
                    real_pattern = Literal(symbols=pattern)
                else:
                    raise Exception("unknown notation type for instrument: %s" % instrument_name)

                sources.append(real_pattern)

            self._players[instrument_name] = Ordered(sources=sources)
Esempio n. 5
0
def play():

    # now modifying our previous example, instead of playing the same two
    # scales in a loop, let's play a given set of notes in each scale.
    # We'll use roman notation to play the 1st, 4th, and 5th note in the scale
    # followed by the 1st, 4th, and 5th major chord
    # finally, we'll play the 2nd, 3rd, and 6th minor chord.


    output = Performance(bpm=120, stop_seconds=10)

    # this is just as before, playing one scale then the other in a loop of 7
    # notes each
    scale1 = scale("c6 major")
    scale2 = scale("c6 minor")
    scale_choices = [ dict(scale=scale1, beats=7), dict(scale=scale2, beats=7) ]
    source = ScaleSource(scales=Endlessly(scale_choices))

    # the scale follower will play the first 7 notes in each scale, whatever the current
    # scale is.  Note that a scale change that doesn't quite line up with the length
    # of the roman pattern rolling over might sound a bit weird.  That's ok.
    roman = Roman(symbols=Endlessly("1 4 5 I IV V ii iii vi".split()), channel=1)
    source.chain([roman, output])

    conductor = Conductor(signal=[source], performance=output)
    conductor.start()
Esempio n. 6
0
def play():

    # in example 13 we showed the use of Ordered to trigger
    # one pattern after another.  As programs grow, it might be nice
    # to group them conceptually in a larger composition.

    # This is a demo that shows how Ordered can nest to not work only
    # with basic patterns, but also larger movements.

    # The idea is this: Generators stack!

    # Like before, we're setting stop seconds to 100 seconds but this composition
    # will complete due to the sources being exhausted first, which is the point.

    output = Performance(bpm=240, stop_seconds=100)

    for scale_name in ["C major", "D minor"]:

        scale_choices = Endlessly([scale(scale_name)])
        source = ScaleSource(scales=scale_choices)

        pattern1 = Roman(symbols="1 2 3 4 5 6 7".split())
        pattern2 = Roman(symbols="i ii iii iv v v vii".split())
        movement1 = Ordered(sources=[pattern1, pattern2])

        pattern3 = Roman(symbols="7 6 5 4 3 2 1".split())
        pattern4 = Roman(symbols="vii vi v iv iii ii i".split())
        movement2 = Ordered(sources=[pattern3, pattern4])
        movement2_transposer = Transpose(octaves=Endlessly([-2]))
        movement2.send_to(movement2_transposer)

        suite = Ordered(sources=[movement1, movement2], channel=1)

        source.send_to(suite)
        output.listens_to([movement1, movement2_transposer])

        # BONUS TIP: we technically don't have to have just one conductor invocation, if it
        # keeps it simple.

        conductor = Conductor(signal=[source], performance=output)
        conductor.start()
Esempio n. 7
0
def play():

    # here's an example that might *START* to approximate a song.
    # here we have two instruments playing on two different tracks.

    output = Performance(bpm=60, stop_seconds=10)

    # both instruments will use the same scale at the same time, but there is a scale
    # change as we cycle between scales every 24 beats

    scale1 = scale("c5 major_pentatonic")
    scale2 = scale("e5 mixolydian")
    scale_choices = [
        dict(scale=scale1, beats=24),
        dict(scale=scale2, beats=24)
    ]
    source = ScaleSource(scales=Endlessly(scale_choices))

    # the first instrument plays a series of chords, transposed down an octave
    # from the original scale carrier signal.

    roman1 = Roman(symbols=Endlessly("I IV V IV III:dim ii".split()),
                   channel=1)
    transpose1 = Transpose(octaves=-1)
    source.chain([roman1, transpose1, output])

    # the second instrument plays a series of notes, but is responding to sixteenth
    # notes, not quarter notes, because of the subdivide.

    subdivide2 = Subdivide(splits=4)
    roman2 = Roman(symbols=Endlessly("1 4 3 4 4 3 2 1".split()), channel=2)
    source.chain([subdivide2, roman2, output])

    # homework assignment:
    # change subdivide2 so it's chained BEFORE roman2
    # what happens and why?

    conductor = Conductor(signal=[source], performance=output)
    conductor.start()
Esempio n. 8
0
def play():

    # we've focussed a lot on randomness now.  So far, we've shown how to pick random values
    # for notes and how to conditionally do something or not with "when=".

    # What's interesting though is that, due to the way some effects plugins work, we can choose
    # to intermix effects.  Here, we show that there are really two patterns going on, but only one
    # is going to win and actually get to play a note.

    # we're relying one 'musician' object stomping on another, but only sometimes.

    output = Performance(bpm=120, stop_seconds=15)

    source = ScaleSource(scales=scale("Ab blues"))

    pattern1 = Endlessly("1 2 3 4 5 6 7".split())
    pattern2 = Randomly(
        "VII:power VI:power V:power IV:power III:power II:power I:power".split(
        ))
    cut_over = Randomly([0, 0, 0, 1, 1, 1], mode='probability')

    melody1 = Roman(symbols=pattern1, channel=1)
    melody2 = Roman(symbols=pattern2, channel=2, when=cut_over)
    transpose = Transpose(octaves=Endlessly([-2, -1, 0, 1, 2]))

    # there is no sane reason to want to do this, but what happens is that for the first three notes we are guaranteed
    # to play pattern1.  Then three notes of pattern 2.  The last note is acutally a toss up between pattern1 and pattern2.
    # since they live in the same chain, pattern1 is going to overwrite pattern2 even though they are expressed in a chain.
    # we can also even choose to overwrite the MIDI channel, which we do, all while sharing a transposition cycle.

    # again, this isn't realistic for most compositions, we'd likely just set up a bunch of patterns with RESTS, but
    # I wanted to show all the possibilities before leaving the theme of randomness for a while.  Hopefully the
    # idea of "when=" is now drummed in.

    source.chain([melody1, melody2, transpose, output])
    conductor = Conductor(signal=[source], performance=output)
    conductor.start()
Esempio n. 9
0
def play():

    # here we aren't introducing any new concepts, but we're arranging some
    # concepts a little differently.  This is somewhat of a review, as we're going
    # to start exploring some more (hopefully) musical demos in our examples now.

    # This one sounds just a bit Perrey and Kingsley with the right VST.

    output = Performance(bpm=60, stop_seconds=10)

    # let's play in the A major scale

    scale_choices = Endlessly([dict(scale=scale("a4 major"))])

    source = ScaleSource(scales=scale_choices)

    # we're going to alternate between the 1st, 4th, and 5th notes
    # of the scale, but we didn't just jump to chords because of what
    # we are about to do with the arp.

    roman = Roman(symbols=Endlessly("1 4 5".split()), channel=1)

    # and now for a clever use of the arp, to machine gun 4 repititions of
    # each chord, with rests in between.  Subdivide alone couldn't do this.
    # Also notice this is running with NO tranpositions.

    arp = Arp(
        # no transpositions
        semitones=Endlessly([0, 0, 0, 0, 0, 0, 0, 0]),
        # every beat gets sliced up 8 times, no exceptions
        splits=Endlessly([8]),
        # try uncommenting this next line:
        #octaves=Endlessly([0,0,1,0,2,0,3,0]),
        # play every other note on the arp
        # TODO: IDEA: seems like we should also allow arp velocity!
        rests=Endlessly([0, 1, 0, 1, 0, 1, 0, 1]))

    # now the output here is just machine gunned notes.  Turn it into major chords.

    chordify = Chordify(types=Endlessly(['major']))

    source.chain([roman, arp, chordify, output])

    conductor = Conductor(signal=[source], performance=output)
    conductor.start()
Esempio n. 10
0
def play():

    # Previously, in 11_randomness we introduced the default 'choice' random mode, which just endlessly
    # picks something from a list.

    # in 18_randomness2.py we introduced 'probability', which returns True or False depending on the chances
    # given.  It's used with the "when" statement, shown in both 18_randomness2.py and 19_skipover.py for doing
    # some rather fun things.

    # Some more types of randomness are "exhaust" and "human_random".

    # just to prove this performance will terminate by reaching the END of the performance, we're setting the stop
    # seconds to a crazy long amount of time.  This one won't loop endlessly, because of the way 'exhaust' mode works.
    output = Performance(bpm=120, stop_seconds=9999)

    # exhaust takes a list and feeds it in random order.  It would be the same as shuffle sorting the list, basically, and then
    # popping off the first element repeatedly.  The result is all items will be used, in a random order, and then the sequence
    # will stop.
    #
    # We normally write this in a one liner, but we'll break it up in this example to make things a bit more clear

    scale_choices = [
        scale("Ab blues"),
        scale("C major"),
        scale("Eb mixolydian")
    ]
    scale_chooser = Randomly(scale_choices, mode='exhaust')
    source = ScaleSource(scales=Randomly(scale_choices, mode='exhaust'))

    # human_random is inspired by the Apple iTunes problem.  In a party shuffle mode, people may get suprised to learn
    # that the system played three Van Halen songs in a row.  Apple changed the system to switch between artists more frequently.
    # in CAMP, human random will play each item in a list once, before going back and starting over with a new random selection.

    note_choices = "1 2 3 4 5 6 7".split()
    note_chooser = Randomly(note_choices, mode='human_random')
    melody = Roman(symbols=note_chooser, channel=1)

    # combining these two concepts together, we are going to play 3 random scales in sequence, and then FOR EACH SCALE, play
    # the scale notes in random order.

    source.chain([melody, chordify, transpose, output])
    conductor = Conductor(signal=[source], performance=output)
    conductor.start()
Esempio n. 11
0
def play():

    # in example 11 we showed off some random functionality
    # in example 17 we showed how to silence notes based on that
    # knowledge of 'random'.

    # here is a probabilistic way to use random, that combines the concepts.

    output = Performance(bpm=120, stop_seconds=15)

    source = ScaleSource(scales=scale("Ab blues"))

    melody = Roman(symbols=Endlessly("1 2 3 4 5 6 7".split()), channel=1)
    silence = Permit(when=Randomly([1, 0.5], mode='probability'))

    # so we're playing a scale pattern, but every other note has a 50% chance
    # of not being played.

    source.chain([melody, silence, output])
    conductor = Conductor(signal=[source], performance=output)
    conductor.start()
Esempio n. 12
0
    def test_simple_band(self):

        output = Performance(bpm=120, stop_seconds=10)

        scale1 = scale("c6 major")
        scale2 = scale("c6 minor")

        source = ScaleSource(
            scales=[dict(scale=scale1, beats=7),
                    dict(scale=scale2, beats=7)])

        subdivide = Subdivide(splits=[4])
        roman = Roman(symbols="1 2 3 4 I IV V iii".split(), channel=1)

        follower = ScaleFollower(lengths=[7])
        chordify = Chordify(types=['power'])
        shift = Transpose(octaves=[-3], channel=2)

        source.chain([subdivide, roman, output])
        source.chain([follower, chordify, shift, output])

        conductor = Conductor(signal=[source], performance=output)
        conductor.start()
Esempio n. 13
0
def play():

    # this is sort of a variation of example 08.  Except now, rather than
    # playing two different patterns that might clash, we're selecting chord
    # patterns for instrument 2 to harmonize with what instrument 1 is playing.

    output = Performance(bpm=120, stop_seconds=10)

    # both instruments will use the same scale at the same time, but there is a scale
    # change as we cycle between scales every 24 beats

    scale1 = scale("c5 major_pentatonic")
    scale2 = scale("e5 mixolydian")
    scale_choices = [ dict(scale=scale1, beats=24), dict(scale=scale2, beats=24) ]
    source = ScaleSource(scales=Endlessly(scale_choices))

    # the first instrument plays a series of notes

    roman1 = Roman(symbols=Endlessly("1 2 4 1 2 3 1 2 4 3 3".split()), channel=1)
    source.chain([roman1, output])

    # the second instrument transposes that note down two octaves and plays a power
    # chord.  We could pass in an array of chords to vary the chord type, but this is
    # probably going to sound less prone to clashing.

    chordify = Chordify(types=Endlessly(["power"]), channel=2)
    transpose = Transpose(octaves=Endlessly([-2]))
    source.chain([roman1, chordify, transpose, output])

    # note that roman 1 is part of each chain, but the channel number is overridden
    # in the second set.  This can be done because the event objects are copied as they
    # are passed between each layer.  Technically the channel can be overriden at any
    # time.  Ideas for future chaos, perhaps?

    conductor = Conductor(signal=[source], performance=output)
    conductor.start()
Esempio n. 14
0
def play():

    # Permit, introduced in the example 18_randomness2.py allows us to
    # silence an entire chain when certain conditions are met.

    # However, sometimes, we may want to decide to apply an affect only
    # when something is true.  Because this could be useful ANYWHERE
    # the same type of "when" logic can actually be attached to ANYTHING.

    # here is a probabilistic way to use random, that combines the concepts.

    output = Performance(bpm=120, stop_seconds=15)

    source = ScaleSource(scales=scale("Ab blues"))

    melody = Roman(symbols=Endlessly("1 2 3 4 5 6 7".split()), channel=1)
    chordify = Chordify(types=Endlessly(['major', 'minor']),
                        when=Randomly([0, 0.45], mode='probability'))
    transpose = Transpose(octaves=Endlessly([2, -2]),
                          when=Randomly([0, 0, 0.75], mode='probability'))

    # the result is every other note has a 40% chance of becoming a chord,  which is always alternating
    # major and minor when it happens

    # every THIRD note has a 75 percent chance of being transposed, which will be alternating +2/-2 octaves
    # when it happens

    # so now, we have easily inserted CONDITIONAL effects.  The use of when=Randomly wasn't required.
    # we could also have used Endlessly or Repeatedly.  Though keep in mind if using Repeatedly, when
    # the event chain is exhausted, that particular part of the performance will stop.  And when everything stops,
    # the performance is done.  Because this is likely being applied to an effect chain, Repeatedly probably
    # doesn't make the most sense with "when".  But Endlessly?  Sure!

    source.chain([melody, chordify, transpose, output])
    conductor = Conductor(signal=[source], performance=output)
    conductor.start()
Esempio n. 15
0
def play():

    # so far what we've done has been mostly theoretical.
    # to construct larger songs, obviously, one thing has to occur
    # after another.  To do this, we could just pass in GIGANTIC
    # arrays to everything but that would suck!

    # now, everything in CAMP is built off the idea of generators.
    # this means the system can pretty well know if one thing is done
    # playing and more on to the next.

    # while we could just pass in huge note arrays, that kind of sucks.
    # What if we want a flute to play
    # it's part and then have the trombone come in?  To do that,
    # we need a facility for ordering - to say "this comes next"

    # NOTE: we're setting stop seconds to 100 seconds but this composition
    # will complete due to the sources being exhausted first, which is the point.
    # we're not writing an endlessly generative piece here, but moving towards
    # more explicit songwriting tools.  The point of this example is to show
    # we are writing a finite song in PIECES.

    output = Performance(bpm=240, stop_seconds=100)
    scale_choices = Endlessly([scale("a3 major")])

    source = ScaleSource(scales=scale_choices)

    pattern1 = Roman(symbols="1 2 3 4".split())
    pattern2 = Roman(symbols="I I I I".split())
    pattern2_transpose = Transpose(octaves=Endlessly([2]))
    pattern2.send_to(pattern2_transpose)

    # in this example, we're going to play pattern1 until it is exhausted
    # then pattern2, and when that's done, we're done.

    ordered = Ordered(sources=[pattern1, pattern2], channel=1)

    # HOMEWORK ASSIGNMENT:
    # uncomment this line to randomly pick a pattern and play it until
    # it is exhausted, but keep doing that endlessly.  Patterns might
    # repeat, see notes about advanced usages of Randomly in examples/11_randomness.py
    #
    # ordered = Ordered(sources=Randomly([pattern1,pattern2]), channel=1)

    # HOMEWORK ASSIGNMENT:
    #
    # uncomment this next line to play pattern1, pattern2 in an endless sequence
    # ordered = Ordered(Endlessly([pattern1,pattern2]))

    # TODO: IDEA: it might be nice to have a "beats" flag on ordered where it can stop
    # producing after a certain number of beats
    # ordered = Ordered(Randomly([pattern1,pattern2,pattern3]), beats=24)
    # this would allow for patterns of patterns, and better nesting.

    # TODO: IDEA: anything that can take a "beats" should really take a bars=, it's just easier
    # to think about

    # HOMEWORK: using a pattern in Ordered that uses Endlessly will cause the pattern to NOT switch.
    # Try this out and understand why pattern 2 will never play.
    # pattern1 = Roman(symbols=Endlessly("3 6 3 6 5 1 2".split()))
    # ordered = Ordered([pattern1,pattern2,pattern3])

    # TODO: IDEA, make the above homework example actually work, with something like:
    # pattern1 = Roman(symbols=Endlessly("3 6 3 6 5 1 2".split()))
    # ordered = Ordered([ dict(pattern=pattern1, beats=8), dict(pattern=pattern2, beats=8) ])

    # NOTE: in simple cases we could connect ordered to the output, but we'd miss the transposition,
    # and I wanted to show off a more complete example.  This also underscores the idea that source.chain
    # is just syntactic sugar and you don't have to use it.

    source.send_to(ordered)
    pattern1.send_to(output)
    pattern2_transpose.send_to(output)

    conductor = Conductor(signal=[source], performance=output)
    conductor.start()
Esempio n. 16
0
def play():

    # in example 15 we show how to drift between different patterns.

    # so far, we've mostly been showing patterns inside a scale, because
    # that sounds good for melodic instruments.  Not everything lives
    # inside a scale though - accidentals etc.  Transpose and the arp
    # can move by semitones to overcome that.

    # however, when dealing with drum kits, the scale is pretty much never
    # relevant

    # Usually these things trigger with something like C3 for a kick drum, D4
    # for a Tom, and so on -- but it varies by drumkit.

    # Having a transpose or scale change in there would
    # mess it all up.

    # Here's an example of switching through some drum patterns, and it also
    # fires multiple types of drum hits on a single channel.

    # when setting this example up, put any synth you want on MIDI channel 2,
    # but on MIDI channel 1 put some kind of drumkit.  You may need to change
    # the MIDI note names to make it sound right

    K = "F4"
    H = "A4"
    C = "Bb4"

    output = Performance(bpm=120, stop_seconds=15)

    melody_trigger = ScaleSource(scales=scale("D minor"))
    melody = Roman(symbols=Endlessly("1 - - - - 2 - - - -".split()), channel=1)

    drum_trigger = Subdivide(splits=4,
                             channel=2)  # gimme 16th notes for the drum tracks

    # kick drum on the quarter notes
    kicks = Literal(symbols=Endlessly([
        K, None, None, None, K, None, None, None, K, None, None, None, K, None,
        None, None
    ]))

    # hihat on the 8th notes every other bar
    # but wait one bar before starting that up.
    hihat = Ordered(sources=Endlessly([
        Literal(symbols="- - - - - - - - - - - - - - - -".split()),
        Literal(symbols=Endlessly([
            H, None, H, None, H, None, H, None, H, None, H, None, H, None, H,
            None
        ]))
    ]))

    # cymbals on the half notes
    cymbals = Literal(symbols=Endlessly([
        C, None, None, None, None, None, None, C, None, None, None, None, None,
        None
    ]))

    melody_trigger.send_to(melody)
    drum_trigger.send_to([kicks, hihat, cymbals])
    output.listens_to([melody, kicks, hihat, cymbals])

    conductor = Conductor(signal=[melody_trigger, drum_trigger],
                          performance=output)
    conductor.start()
Esempio n. 17
0
def play():

    # in example 10 we showed you how powerful the arpeggiator can get.
    # well, that's great for some degrees of chaos, but what if we want
    # to let the computer make MORE decisions?

    output = Performance(bpm=120, stop_seconds=10)

    # randomness is one way to do that.

    # in example 03 we showed how to use Endlessly to loop over a set of input.
    # that used SELECTORS, but Endlessly is just a LINEAR selector that loops
    # when it gets to the end of the input.

    # randomness can also be accessed, using the Randomly selector, which
    # chooses an item from a list.
    # each choice is then repeated for a given number of times.
    # here lets pick some random scales, but whatever we pick, hold that choice
    # for 7 beats, which we covered in example 01.

    random_scale_choices = Randomly([
        # each of these items are scale specifications.  You've seen these in the very first examples.
        dict(scale=scale("c6 major"), beats=7),
        dict(scale=scale("Db4 minor_pentatonic"), beats=7),
        dict(scale=scale("Eb3 mixolydian"), beats=7)
    ])

    # note that the random selector could repeat a selection as currently
    # implemented and/or specified.  See ideas below.

    # note if we want a random choice to be made EVERY beat we can simplify
    # our usage of randomly like so:

    random_note_choices = Randomly("1 2 3 4 5 6 7".split())

    # what does that do?  It randomly plays one of 7 notes in the current scale.
    # that's syntactically clean, but super basic.  Let's override that with something
    # showing more options available to Randomly.

    # we're playing CHORDS now, and if we choose the "I" chord we are going to play
    # it twice in the current scale (which might change after we play the first, so this is
    # a minor lie). All within the current scale, of course.

    random_note_choices = Randomly([
        dict(value="I", hold=2),
        dict(value="IV", hold=1),
        dict(value="V", hold=1),
        dict(value="i", hold=1),
        dict(value="ii", hold=1),
        dict(value="iv:dim7", hold=1),
    ])

    # HOMEWORK: add some more scales to the random_scale_choices above and take out some
    # notes from random_note_choices.  Mix scales with notes!

    # FUTURE IDEA: also support a notion of frequency controlling what will be drawn.
    # frequencies should add up to 1.  This would still be compatible with "hold" above.

    #random_note_choices = Randomly([
    #    dict(value="I", frequency=0.4),
    #    dict(value="IV", frequency=0.2),
    #    dict(value="V", frequency=0.2),
    #    dict(value="i", frequency=0.1),
    #    dict(value="ii", frequency=0.05),
    #    dict(value="iv:dim7", frequency=0.05),
    #])

    # for more examples, see the demos labelled randomness2.py, randomness3.py, etc.
    # we want to cover some other things first, so I'm not going to go completelyinto
    # randomness now.

    source = ScaleSource(scales=random_scale_choices)
    roman = Roman(symbols=random_note_choices, channel=1)
    source.chain([roman, output])

    conductor = Conductor(signal=[source], performance=output)
    conductor.start()