
/* A basic pd-pure pattern sequencing example. This Pure script generates the
   sequences played by the seqdemo.pd patch. */

using math;

/* Simple (monophonic) musical patterns with aleatoric elements represented as
   streams. */

rand m::int	= int (uint random/0x100000000*(m+1));
rand (n::int,m::int)
		= rand (m-n) + n;

/* Choose an element at random. */

choose xs	= (xs!rand (#xs-1));

/* Concatenate a recursive pattern to a flat sequence. */

mkseq []	= [];
mkseq (x:xs)	= catmap mkseq (x:xs);
mkseq x		= [x] otherwise;

/* Sample patterns. pat1 is shamelessly pilfered from the SuperCollider
   documentation, pat2 is a finite section of pat1, and pat3 is a simple
   random C minor arpeggio. */

pat1		= [[24, 31, 36, 43, 48, 55,
		    [[60, choose [63, 65], 67, choose [70, 72, 74]] |
		     i = 1..rand (2,5)],
		    [choose [74, 75, 77, 79, 81] |
		     i = 1..rand (3,8)]] |
		   i = 1..inf];

pat2		= [[24, 31, 36, 43, 48, 55,
		    [[60, choose [63, 65], 67, choose [70, 72, 74]] |
		     i = 1..rand (2,5)],
		    [choose [74, 75, 77, 79, 81] |
		     i = 1..rand (3,8)]] |
		   i = 1..rand (2,5)];

pat3		= [[60,60,choose [63,67]] | i = 1..inf];

/* The sequencer object. */

nonfix bang reset; // we respond to these
nonfix stop note pan wet; // we generate these

sequencer = process

with

  /* Process events. Respond to the messages 'bang' (produce the next stream
     member), 'reset' (rewind the stream to the beginning), numbers (change
     the note duration a.k.a. delta time value) and a pattern (to switch
     patterns on the fly). Last but not least we also send back a 'stop'
     message when the stream ends (of course this will only happen if the
     sequence is finite). */

  process x::double
		= () when put dur x end;
  process pat@(_:_)
		= ()
		    when pat = mkseq pat;
		      put time 0; put ctr 0;
		      put seq pat; put seq0 pat;
		    end;
  process reset	= () when put seq (get seq0); put time 0; end;
  process bang	= case get seq of
		    n:seq1 = event n when
		        put seq seq1; put time (get dur);
		      end;
		    _ = stop;
		  end;
  process _	= stop;

  /* Turn a stream member (a note number in this case) into a sequence of
     events (a.k.a. Pd messages). The sample event function below illustrates
     how to carry around some state (a running counter in this case) in order
     to implement some dynamic effects, and how to do random pitch, velocity
     and onset variations on the fly. Also note that here we return an entire
     "bundle" (i.e., list) of Pd messages for each pattern element. */

  event n	= {// play a note on voice #2:
		   t 2 note n 1 d,
		   // vary the pan and wet controls of voice #2 in a periodic
		   // fashion to simulate a sound source walking around in
		   // circles:
		   t 2 pan (0.4+0.3*u),
		   t 2 wet (0.35+0.1*w),
		   // play the same note, delayed by a small amount of time,
		   // transposed down an octave and slightly detuned,
		   // alternating between voice #1 and voice #3:
		   (t+dt) (2*k+1) note (n-12+dn) v d}
		    when i = getctr ctr; k = i mod 2;
		      x = i mod 100/100*2*pi; u = cos x; w = sin x;
		      t = get time; d = get dur; dt = rand (0,10);
		      dn = rand (-20,20)/1000;
		      v = rand (90,120)/100;
		    end;
  getctr c	= k when k = get c; put c (k+1) end;

end

when
  // initialize
  dur = ref 500; time = ref 0; ctr = ref 0;
  pat = mkseq pat1;
  seq = ref pat; seq0 = ref pat;
end;
