Showing posts with label Routine. Show all posts
Showing posts with label Routine. Show all posts

Wednesday, October 13, 2010

More Buffers

Waiting

Last time, we learned that the language does not wait for the server to finish loading buffers before it carries on with the programme, which could lead to a situation where we instruct the server to start playing a buffer that hasn't yet loaded. This will fail to play correctly. Fortunately, there are a few ways to make sure this doesn't happen.

If we use a Task (or a Routine), we can tell it to pause until the server has caught up.

s.boot;
 
(
 var buf;
 
 SynthDef(\playBufMono, {| out = 0, bufnum = 0, rate = 1 |
  var scaledRate, player;
  scaledRate = rate * BufRateScale.kr(bufnum);
  player = PlayBuf.ar(1, bufnum, scaledRate, doneAction:2);
  Out.ar(out, player)
 }).add;

 buf = Buffer.read(s, "sounds/a11wlk01.wav");

 Task.new({
  
  s.sync; // wait for the server
  Synth(\playBufMono, [\out, 0, \bufnum, buf.bufnum, \rate, 1])
  
 }).play
)

The s.sync makes the Task wait for the server to get caught up with the language. The SynthDef, like the buffer, is also asynchronous, so the sync gives everything a chance to get caught up.

We can also give the buffer an action. This is a function that gets evaluated when the buffer has finished loading. This might be a good idea when we are going to load several buffers, but don't need to use all of them right away. We could use the action, for example, to set a flag so our programme knows it's ok to start using the buffer:

s.boot;
 
(
 var buf, bufloaded;
 
 SynthDef(\playBuf, {| out = 0, bufnum = 0, rate = 1, 
       dur = 0.2, amp = 0.2, startPos = 0 |
  var scaledRate, player, env;
  
  env = EnvGen.kr(Env.sine(dur, amp), doneAction: 2);
  scaledRate = rate * BufRateScale.kr(bufnum);
  player = PlayBuf.ar(1, bufnum, scaledRate, 
       startPos: startPos, loop:1);
  Out.ar(out, player * env)
 }).add;

 bufloaded = false;
 
 buf = Buffer.read(s, "sounds/a11wlk01.wav", 
  action: { bufloaded = nil});


 Pseq([
  Pbind( // play this Pbind first
   \scale,  Scale.gong,
   \dur,   Pwhite(1, 3, inf) * 0.001,
   \degree,  Prand([0, 2, 4, \rest], inf),
   \amp,  0.2,
   \test,  Pfunc({ bufloaded }) // when this is nil, this Pbind ends
  ),
  Pbind( // next play this one
   \instrument, \playBuf,
   \bufnum,  Pfunc({buf.bufnum}), // use the buffer, now that we know it's ready
   \dur,  Pwhite(0.01, 0.3, 20),
   \startFrame, Pfunc({buf.numFrames.rand}),
   \amp,  1
  )
 ], 1). play

)


Recall that when a Pbind gets a nil, it stops playing and the pattern goes on to the next section, so setting the flag to nil, in this case, advances the piece. Because the second buffer has not yet loaded when the interpreter first evaluates the second Pbind, buf.numFrames will still be nil. Therefore, we put that into a Pfunc so that it gets evaluated when the Pbind plays, instead of when the interpretter first looks at the code.

In that example, we don't always start playing back at the start of the Buffer, but instead offset a random number of samples (called "Frames" here). Coupled with the short duration, this can be an interesting effect.

When you are writing your own actions, you can also do a more typical true / false flag or do anything else, as it's a function. For example, it's possible to combine the two approaches:

s.boot;
 
(
 var buf1, syn, pink;
 
 SynthDef(\playBuf1, {| out = 0, bufnum = 0, rate = 1, loop = 1 |
  var scaledRate, player;
  scaledRate = rate * BufRateScale.kr(bufnum);
  player = PlayBuf.ar(1, bufnum, scaledRate, loop: loop,
       doneAction:2);
  Out.ar(out, player)
 }).add;

 SynthDef(\playBufST, {| out = 0, bufnum = 0, rate = 1, loop = 1,
       dur = 1, amp = 0.2 |
  var scaledRate, player, env;
  
  env = EnvGen.kr(Env.sine(dur, amp), doneAction: 2);
  scaledRate = rate * BufRateScale.kr(bufnum);
  player = PlayBuf.ar(2, bufnum, scaledRate, loop:loop);
  Out.ar(out, player * env)
 }).add;

 buf1 = Buffer.read(s, "sounds/a11wlk01.wav");

 Task.new({
  
  s.sync; // wait for buf1
  syn = Synth(\playBuf1, [\out, 0, \bufnum, buf1.bufnum, 
       \rate, 1, \loop, 1]);  // play buf1

  pink =  Buffer.read(s, "sounds/SinedPink.aiff",
   action: {  // run this when pink loads
    syn.set("loop", 0);
    syn = Synth(\playBufST, [\out, 0, \bufnum, pink.bufnum,
        \rate, 1, \loop, 1, \dur, 10]); // play pink
   });
    
  
 }).play
)

In the above example, we first wait for the allwlk01 buffer to load and then start playing it. While it's playing, we tell the SinedPink buffer to load also and give it in action. When it has loaded, the action will be evaluated. The action tells the first synth to stop looping and then starts playing a new Synth with the new buffer.

If we wanted to, we could use the action to set a flag or do any number of other things.

Recording

We don't need to just play Buffers, we can also record to them. Let's start by allocating a new Buffer:

b = Buffer.alloc(s, s.sampleRate * 3, 1)

The first argument is the server. The second is the number of frames. I've allocated a 3 second long Buffer. The third argument is the number of channels.

Now, let's make a synth to record into it:

 SynthDef(\recBuf, {| in = 1, bufnum, loop = 1|
 
  var input;
  input = AudioIn.ar(in);
  RecordBuf.ar(input, bufnum, recLevel: 0.7, preLevel: 0.4, loop:loop, doneAction: 2);
 }).add;

AudioIn.ar reads from our microphone or line in. Channels start at 1, for this UGen.

RecordBuf takes an input array, a bufnum and several other arguments. In this example, we're scaling the input by 0.7 and keeping previous data in the buffer, scaled by 0.4. We'll keep looping until instructed to stop.

We can mix this with a stuttering playback from earlier:

s.boot;
 
(
 var buf, syn, pb;
 
 SynthDef(\playBuf2, {| out = 0, bufnum = 0, rate = 1, 
       dur = 5, amp = 1, startPos = 0 |
  var scaledRate, player, env;
  
  env = EnvGen.kr(Env.sine(dur, amp), doneAction: 2);
  scaledRate = rate * BufRateScale.kr(bufnum);
  player = PlayBuf.ar(1, bufnum, scaledRate, 
       startPos: startPos, loop:1);
  Out.ar(out, player * env)
 }).add;
 
 SynthDef(\recBuf, {| in = 1, bufnum, loop = 1|
 
  var input;
  input = AudioIn.ar(in);
  RecordBuf.ar(input, bufnum, recLevel: 0.7, preLevel: 0.4, loop:loop, doneAction: 2);
 }).add;
 
 buf = Buffer.alloc(s, s.sampleRate * 3, 1);
 
 Task({
  
  s.sync; // wait for the buffer
  
  syn = Synth(\recBuf, [\in, 1, \bufnum, buf.bufnum, \loop, 1]); // start recording
  
  2.wait; // let some stuff get recorded;
  
  
  pb = Pbind( // play from the same buffer
   \instrument, \playBuf2,
   \bufnum,  buf.bufnum,
   \dur,  Pwhite(0.01, 0.5, 2000),
   \startFrame, Pwhite(0, buf.numFrames.rand, inf),
   \amp,  1
  ).play;
  
  5.wait;
  
  syn.set("loop", 0);  // stop looping the recorder
  
  3.wait;
  pb.stop; // stop playing back
 }).play
)

Summary

  • Buffers load asynchronously and we can't count on them to be ready unless we wait for them.
  • One way to wait is to call s.ync inside a Task or a Routine.
  • We can start playing back a Buffer from any sample with the startFrame argument.
  • Buffer.read has an action argument, to which we can pass a function that will be evaluated after the Buffer has been read on the server.
  • We can allocate empty Buffers on the server with Buffer.alloc.
  • AudioIn.ar starts with channel 1, even though most other input busses start at 0.
  • RecordBuf.ar records to a Buffer.

Problems

  1. Write a programme that opens and plays several Buffers. Make sure that every Buffer is ready before you play it. Spend the least possible amount of time waiting.
  2. Write a programme that reads a Buffer, starts playing it back in some way and the starts recording to the same Buffer. You can use AudioIn.ar or check out InFeedback.ar. Note that, as the name implies, you may get feedback, especially if you are playing back in a linear manner at a rate of 1. Try other rates or ways of playing back to avoid this.
  3. Find a short audio file that contains some beat-driven audio. If you know the number of beats in the file, could you make an array of startFrames? Write a Pbind that does something interesting with your file and your array.

Tuesday, June 08, 2010

Math with Patterns & Prout

Let's say that you want to pick random numbers? You could use Pwhite. But what if you wanted random multiples of 2? You would still use Pwhite:

(
 Pbind(
  \dur,  0.2,
  \note,  Pwhite(0, 6) * 2
 ).play
)

What if you want to square your random numbers?

(
 Pbind(
  \dur,  0.2,
  \note,  Pwhite(0, 5).squared
 ).play
)

You can do any math operation with a pattern as if it was a number.

(
 Pbind(
  \dur,  0.2, 
  \note,  Pseq([1, 2, 3], 15) * Pseq([1, 2, 3, 5, 4], inf)
 ).play
)

If you want to do math with a pattern and a variable that might change over time, you still need to use a Pfunc:

(
 var num;
 num = 5;
 
 Pbind(
  \dur,  0.2,
  \note, Pwhite(0, 5) * Pfunc({num})
 ).play;
 
 Task({
  5.wait;
  num = 2;
 }).play;
)

Let's say you want step through every item in a shuffled array. You could use Pseq:

Pseq([1, 3, 5, 7].scramble, 4)

Or Pshuf does the same thing:

Pshuf([1, 3, 5, 7], 4)

But what if you want the array to reshuffle after every tme through the loop? (As far as I know) there's not a pre-existing pattern for that. But you can write one with Prout.

(
 var arr, rout;
 
 arr = [1, 3, 5, 7];
 
 rout = Prout({ 
  5.do({
   arr.scramble.do({|item|
    item.yield;
  })})
 });

 
 Pbind(
  \dur, 0.2,
  \note, rout
 ).play
)

A Routine is a function that can pause while it's running. It can also return values more than once. Prout is a wrapper for a Routine. Everytime it gets to item.yield, it returns the item and then waits to be asked for it's next return value. Then it starts running again until it finds another yield or runs out of things to run.

Like Pfunc, Prout evaluates variables as it runs. In the above example, if arr had changed, the Prout would have used the new arr:

(
 var arr, rout;
 
 arr = [1, 3, 5, 7];
 
 rout = Prout({ 
  10.do({
   arr.scramble.do({|item|
    item.yield;
  })})
 });

 
 Pbind(
  \dur, 0.2,
  \note, rout
 ).play;
 
 Task({
  2.wait;
  arr = [12, 15, 19, 21];
  2.wait;
  arr = [-12, -13, -11];
 }).play
)

You could do something similar also, this way:

(
 var arr1, arr2, arr3, rout;
 
 arr1 = [1, 3, 5, 7];
 arr2 = [12, 15, 19, 21];
 arr3 = [-12, -13, -11];
 
 rout = Prout({ 
  3.do({
   arr1.scramble.do({|item|
    item.yield;
  })});
  3.do({
   arr2.scramble.do({|item|
    item.yield;
  })});
  3.do({
   arr3.scramble.do({|item|
    item.yield;
  })})
 });

 
 Pbind(
  \dur, 0.2,
  \note, rout
 ).play;
 
)

You can have as many yields as you want in a Routine - or a Prout.

If you want to pass in arguments, you can use a wrapper function:

(
 var rout;
 
 rout = {|arr, repeats = 5|
  Prout({ 
   repeats.do({
    arr.scramble.do({|item|
     item.yield;
   })})
  });
 };

 
 Pbind(
  \dur, 0.2,
  \note, rout.value([0, 2, 4, 6], 6)
 ).play
)

And, as with Pfunc, you can do math:

(
 var rout; 
 
 rout = {|arr, repeats = 5|
  Prout({ 
   repeats.do({
    arr.scramble.do({|item|
     item.yield;
   })})
  });
 };

 
 Pbind(
  \dur, 0.2,
  \note, rout.value([0, 2, 4, 5], 6) * Pseq([1, 2, 3], inf)
 ).play
)

Yielding only works inside Routines. If you try this in a Pfunc, it does not work:

(
 var func; 
 
 func = Pfunc({ 
  5.do({
   [1, 2, 5].scramble.do({|item|
    item.yield; // you can't do this in a Pfunc!!
  })})
 });
 

 
 Pbind(
  \dur, 0.2,
  \note, func
 ).play
)

Note that it does not create an error message, it just behaves in a bizarre way. Silent errors are the hardest to track down, so beware.

Summary

  • Patterns can behave like numbers for math operations.
  • A Routine is a function that can pause
  • A Prout is like a Pfunc, except that it can stop in the midst of things and resume.
  • Routines can return more than one item, at any point in their execution, whenever you yield a value.
  • You can only yield in a Routine or a Prout