:orphan: Evolving Form ============= *Example directory* (scamp_extensions): `examples/Composition & form/Larger-scale form/evolving_form `__ *Download:* :download:`evolving_form.zip ` **Requires the** ``scamp_extensions`` **package** (``pip install scamp_extensions``). A piece for cello, pianoteq (MIDI), and SuperCollider (OSC), shaped by envelope-driven formal parameters. See definitions.py and formal_parameters.py. *Topics:* :doc:`Time & clocks › Setting & changing tempo `, :doc:`Envelopes › As compositional parameters `, :doc:`Composition & form › Larger-scale form ` .. raw:: html
score page 1
1 / 8
.. raw:: html
.. code-block:: python :caption: evolving_form.py: The main script for the piece, drawing upon definitions.py and formal_parameters.py. from scamp import * from scamp_extensions.pitch import Scale from definitions import * from formal_parameters import * from random import random, choice, uniform, seed seed(0) s = Session() cello = s.new_part("cello") pianoteq = s.new_midi_part("pianoteq", midi_output_device="IAC") supercollider_instrument = s.new_osc_part("superInst", 57120, "127.0.0.1") s.set_tempo_targets([150, 60], [Moment.after_time(TOTAL_LENGTH * 0.618), Moment.after_time(TOTAL_LENGTH)]) s.start_transcribing() interval = 1 cello_pitch = 48 def pianoteq_upbeat(): start_pitch = 72 + cello_pitch % 12 end_pitch = 72 + (cello_pitch + interval) % 12 if end_pitch < start_pitch: end_pitch += 12 for i, p in enumerate([start_pitch + uniform(-k, k) for k in range(7)]): pianoteq.play_note(p, 0.3 + 0.1 * i, 0.5/7) set_rate_target(pianoteq_gesture_rit_factor.value_at(s.time), Moment.after_beats(15)) p = end_pitch volume = 1.0 pianoteq.play_note(p, volume, 0.375) for _ in range(int(pianoteq_gesture_length.value_at(s.time))): scale = Scale.from_pitches([cello_pitch + x for x in (0, 2, 3, 5, 6, 8, 9, 11, 12)]) degree = round(scale.pitch_to_degree(p)) pianoteq.play_note(scale.degree_to_pitch(degree - 4), volume, 0.125) pianoteq.play_note(scale.degree_to_pitch(degree - 3), volume, 0.375) volume *= 0.8 p = scale.degree_to_pitch(degree - 3) def pianoteq_filler(): p = 60 while random() < pianoteq_filler_probability.value_at(s.time): scale = Scale.from_pitches([cello_pitch + x for x in (0, 2, 3, 5, 6, 8, 9, 11, 12)]) degree = round(scale.pitch_to_degree(p)) if p == 60: wait(1/5) for i in range(4): pianoteq.play_chord([scale.degree_to_pitch(degree + 2*i), scale.degree_to_pitch(degree + 2*i + 3)], 0.6 + 0.1*i, 1/5, "staccatissimo") if p != 60 and random() < pianoteq_filler_probability.value_at(s.time): pianoteq.play_chord([scale.degree_to_pitch(degree + 4), scale.degree_to_pitch(degree + 9), scale.degree_to_pitch(degree + 11)], 1.0, 1/5, "staccatissimo") else: wait(1/5) p = wrap_in_range(p + 6, 62, 92) def sc_gesture(): start_pan = uniform(-1, 1) end_pan = uniform(0, 1) if start_pan < 0 else uniform(-1, 0) register_ref = sc_register.value_at(s.time) supercollider_instrument.play_note( register_ref + cello_pitch % 12, [0, 0.5], sc_gesture_length.value_at(s.time), {"param_crackliness": [0, crackliness.value_at(s.time)], "param_pan": start_pan} ) supercollider_instrument.play_note( [register_ref + cello_pitch % 12, register_ref + cello_pitch % 12 + 12], [[0.5, 0], [1], [-4]], sc_gesture_length.value_at(s.time) * 3/2, {"param_crackliness": [crackliness.value_at(s.time), 0], "param_spread": [[0, 0, 0.5], [0.5, 0.5]], "param_pan": end_pan} ) piano_tech_clock = None sc_gesture_clock = None while True: if random() < cello_continue_probability.value_at(s.time): if len(bar_lines) > 0 and s.beat - bar_lines[-1] > 3 and \ (piano_tech_clock is None or not piano_tech_clock.alive): piano_tech_clock = s.fork(pianoteq_filler) do_bar_line(s.beat) cello.play_note(cello_pitch, forte_piano, choice([1.0, 1.5])) else: # end condition; break and go to final note if cello_pitch % 12 == 0 and s.time >= TOTAL_LENGTH: break if s.time > 20 and (sc_gesture_clock is None or not sc_gesture_clock.alive): sc_gesture_clock = s.fork(sc_gesture) cello.play_note(cello_pitch, diminuendo, choice([2.0, 2.5, 3.0])) if piano_tech_clock is not None: piano_tech_clock.kill() wait(choice([0.5, 1.0])) if s.time > 40: piano_tech_clock = s.fork(pianoteq_upbeat) if random() < cello_staccato_probability.value_at(s.time): cello_pitch = wrap_in_range(cello_pitch + interval, 36, 60) interval += 1 wait(0.25) cello.play_note(cello_pitch, 0.9, 0.25, "staccato") else: wait(0.5) do_bar_line(s.beat) cello_pitch = wrap_in_range(cello_pitch + interval, 36, 60) interval += 1 cello.play_note(36, diminuendo, [4.0]) do_bar_line(s.beat) s.stop_transcribing().to_score(bar_line_locations=bar_lines, max_divisor=14).show() .. code-block:: python :caption: definitions.py: Shared dynamics envelopes and bar-line helpers for evolving_form.py. from scamp import * # playback_settings.adjustments.set("staccato", "length * 0.3") forte_piano = Envelope( [0.8, 0.4, 1.0], [0.2, 0.8], curve_shapes=[0, 3] ) diminuendo = Envelope.from_levels([0.8, 0.3]) def wrap_in_range(value, low, high): return (value - low) % (high - low) + low bar_lines = [] def do_bar_line(beat): beats_since_last_bar_line = beat - bar_lines[-1] if len(bar_lines) > 0 else beat if beats_since_last_bar_line % 1 == 0 and beats_since_last_bar_line < 5: bar_lengths = [beats_since_last_bar_line] else: num_bars_to_add = 2 while beats_since_last_bar_line / num_bars_to_add > 4: num_bars_to_add += 1 last_bar_length = round(beats_since_last_bar_line / num_bars_to_add) remaining_bars_length = beats_since_last_bar_line - last_bar_length if remaining_bars_length <= 5: bar_lengths = [remaining_bars_length, last_bar_length] else: first_bar_length = remaining_bars_length % 3 + 3 if remaining_bars_length % 3 < 2 else remaining_bars_length % 3 bar_lengths = [first_bar_length] + [3] * int((remaining_bars_length - first_bar_length) / 3) + [last_bar_length] for bar_length in bar_lengths: bar_lines.append(bar_lines[-1] + bar_length if len(bar_lines) > 0 else bar_length) .. raw:: html
envelope plot
envelope plot
envelope plot
envelope plot
envelope plot
envelope plot
envelope plot
envelope plot
.. code-block:: python :caption: formal_parameters.py: Formal-parameter envelopes for evolving_form.py; run directly to plot them. from scamp import * TOTAL_LENGTH = 180 # golden ratio durations gr, one_minus_gr = TOTAL_LENGTH * 0.618, TOTAL_LENGTH * (1 - 0.618) cello_continue_probability = Envelope([0.4, 1.0, 0], [gr, one_minus_gr]) cello_staccato_probability = Envelope.from_levels([0, 1], length=TOTAL_LENGTH) pianoteq_gesture_length = Envelope.from_levels([0, 0, 10, 0], length=TOTAL_LENGTH) pianoteq_gesture_rit_factor = Envelope([0.25, 0.8, 0.1], [gr, one_minus_gr]) pianoteq_filler_probability = Envelope([0.3, 0.97, 0.3], [gr, one_minus_gr]) crackliness = Envelope([0.6, 1.0, 0], [gr, one_minus_gr]) sc_gesture_length = Envelope([5, 2, 5], [gr, one_minus_gr]) sc_register = Envelope([72, 72, 60, 60, 48, 48, 36, 36 ], [TOTAL_LENGTH * 0.7, 0, TOTAL_LENGTH * 0.1, 0, TOTAL_LENGTH * 0.1, 0, TOTAL_LENGTH * 0.1]) if __name__ == "__main__": cello_continue_probability.show_plot("Cello continue probability") cello_staccato_probability.show_plot("Cello staccato probability") pianoteq_gesture_length.show_plot("Pianoteq gesture length") pianoteq_gesture_rit_factor.show_plot("Pianoteq rit factor") pianoteq_filler_probability.show_plot("Pianoteq filler probability") crackliness.show_plot("SC instrument crackliness") sc_gesture_length.show_plot("SC instrument gesture length") sc_register.show_plot("SC instrument register") .. code-block:: supercollider :caption: Crackler.scd MarcUtilities.setOutputLimiter(0.5, globalMul: 0.3) ( ScampUtils.instrumentFromSynthDef( SynthDef(\superInst, { |freq=440, volume=0, gate=1, crackliness=0, spread=0, pan=0| var synth = Mix.fill(5, { |k| Pan2.ar( SinOsc.ar( freq * spread.linlin(0, 1, 1.0, 0.8).pow(k-2) * LFNoise1.ar(crackliness.linlin(0, 1, 30, 100), crackliness.linlin(0, 1, 0, 0.2), 1), crackliness.linexp(0, 1, 1, 300) ) * EnvGen.kr(Env.perc( attackTime: crackliness.linexp(0, 1, 0.02, 0.01), releaseTime:crackliness.linlin(0, 1, 0.2, 0.01)), gate: Impulse.kr(40)), k.linlin(0, 5, -1, 1) ) }); var panned_out = volume * synth * EnvGen.ar(Env.adsr, gate: gate, doneAction: 2) * Pan2.ar(DC.ar(1), pan); Out.ar(0, panned_out); }); ) )