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()
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()
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()
class Scene(object): def __init__(self, bpm=None, scale=None, pre_fx=None, post_fx=None, patterns=None, bar_count=None): self.scale = scale self.bpm = None self.pre_fx = pre_fx self.post_fx = post_fx self.patterns = patterns self.bar_count = bar_count self._factory = None # set up by scenes.py self._scale_source = ScaleSource(scales=core_scale(self.scale)) self._output = None self._players = dict() def to_data(self): result = dict( scale = self.scale, bpm = self.bpm, pre_fx = self.pre_fx, post_fx = self.post_fx, patterns = self.patterns, bar_count = self.bar_count ) #if self.pre_fx: # for (k,v) in self.pre_fx.items(): # result['pre_fx'][k] = v.to_data() #if self.post_fx: # for (k,v) in self.post_fx.items(): # result['post_fx'][k] = v.to_data() #if self.patterns: # for (k,v) in self.patterns.items(): # result['patterns'][k] = v.to_data() return result def build(self): self._build_output() self._build_fx_chains() self._build_players() self._build_interconnects() def _build_output(self): # FIXME: the system uses stop_seconds to cap a scene at a given number of seconds # but we are really more interested in beats. We probably want to attach a new timer # instance to the output to make this work instead, until then stop_seconds is hard coded # for DEBUG only and should be removed. This should use defaults/scene_max_bars and bars on # the scene object. if self.bpm is None: self.bpm = self._factory.defaults['bpm'] self._output = Performance(bpm=self.bpm, stop_seconds = 10) def _build_fx_chains(self): for (chain_name, bus) in self._factory.fx_buses.items(): previous = None nodes = bus.nodes() for item in nodes: if previous is not None: previous.sends = [] previous.send_to(item) previous = item 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) def _stitch_fx_chain(self, assignments, instrument_name, from_node, to_node): # SHOULD BE REMOVABLE #if assignments is None or instrument_name not in assignments: # return fx_chain_name = assignments[instrument_name] if fx_chain_name not in self._factory.fx_buses: # FIXME: typed exceptions everywhere to make it easier for higher level apps raise Exception("fx bus not found: %s" % fx_chain_name) fx_chain = self._factory.fx_buses[fx_chain_name].nodes() # FIXME: BOOKMARK: I think need to implement COPY methods here as reuse of the same chain may cause # some interestingness... maybe. Nodes should return copies of the objects (?) head = fx_chain[0] tail = fx_chain[-1] print("FROM %s to %s" % (from_node, head)) print("AND FROM %s to %s" % (tail, to_node)) from_node.send_to(head) tail.send_to(to_node) def _build_interconnects(self): for (instrument_name, player) in self._players.items(): # FIXME: all of this stuff should use python standard logging print("---") print("CONFIGURING INSTRUMENT CHAIN: %s" % instrument_name) print(dir(player)) player = self._players[instrument_name] #import pdb; pdb.set_trace() # TODO: consider whether it makes sense for a FxBus to connect to another FxBus # ignoring for now. if self.pre_fx is None or instrument_name not in self.pre_fx: # connect directly to source print("PRE DIRECT CONNECT") self._scale_source.send_to(self._players[instrument_name]) else: # ensure prefx is coupled to source and tail is coupled to output print("PRE CONNECT") self._stitch_fx_chain(self.pre_fx, instrument_name, self._scale_source, player) instrument = self._factory.instruments[instrument_name] channel_assign = Channel(channel=instrument.channel) player.send_to(channel_assign) if self.post_fx is None or instrument_name not in self.post_fx: # connect directly to output print("POST DIRECT CONNECT") channel_assign.send_to(self._output) else: # ensure player is coupled to head of post fx and tail is coupled to output print("POST CONNECT") self._stitch_fx_chain(self.post_fx, instrument_name, channel_assign, self._output) def get_signals(self): return [ self._scale_source ] def get_output(self): return self._output
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()
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()
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()