How to Write BBCut FX

First, here’s my file:

CutMask : CutSynth { 
 var bits,sr,bitadd,srmult; 
 var synthid;
 
 //makes SynthDef for filter FX Synth 
 *initClass { 
 
   StartUp.add({
  
  2.do({arg i;

  SynthDef.writeOnce("cutmaskchan"++((i+1).asSymbol),{ arg inbus=0, outbus=0, bits; 
  var input, fx;
  
  input= In.ar(inbus,i+1);
  
  fx = MantissaMask.ar(input, bits);
  
  ReplaceOut.ar(outbus,fx);
  
  }); 
  });
  
  });
  
 } 
 
 *new{arg bits=16,sr,bitadd=1,srmult=1;
  
 ^super.new.bits_(bits).bitadd_(bitadd).sr_(sr ?? {Server.default.sampleRate/2}).srmult_(srmult);
 }
 
 setup { 
 //tail of cutgroup
 synthid= cutgroup.server.nextNodeID;
  
 cutgroup.server.sendMsg(s_new, cutmaskchan++(cutgroup.numChannels.asSymbol), synthid, 1,cutgroup.fxgroup.nodeID,inbus,cutgroup.index,outbus,cutgroup.index, bits, bits);
   
 } 


//can't assume, individual free required for cut fx
//synth should be freed automatically by group free
 free {
  cutgroup.server.sendMsg(n_free,synthid); 
 }

 renderBlock {arg block,clock;
  var samprate,bitstart,bitarray,srarray, s;
  
  s= cutgroup.server;

  bitstart= bits.value(block);
  samprate= sr.value(block);
  
  srarray= Array.geom(block.cuts.size,samprate,srmult.value(block));
  bitarray= Array.series(block.cuts.size,bitstart,bitadd.value(block));

  bitarray= 0.5**((bitarray).max(2)-1);

  block.cuts.do({arg cut,i;
  
  block.msgs[i].add([n_set, synthid,bits,bits]);
  
  });
  
  //don't need to return block, updated by reference
 }
 

}

What I did there: I took CutBit1.sc and did a saveAs CutMask.sc. Then I changed the name of the class to CutMask.

In initClass

I changed the synthdef name to cutmaskchan
I changed the arguments to the SynthDef
I put in my own code for the fx = line. That’s where the magic happens!

In new

I changed cutgroup.server.sendMsg to so it uses my synthdef name and my synthdef arguments

In renderBlock

I changed block.msgs[i].add( to have my synthdef arguments
since mine doesn’t change, I could skip sending anything, afaik

Chiptune, Dub, BBCut

This is based off MCLD’s Dubstep Patch, with a modification to make it output a Pulse wave and then using some BBCut from a previous post.

(
var bus, sf, buf, clock, synthgroup, bbgroup, loop, group, cut1, cut2, cut3, stream, pb,
  cut4, out;


SynthDef(dub, {|out = 0, amp|
 
    var trig, note, son, sweep;

    trig = CoinGate.kr(0.5, Impulse.kr(/*2*/ 1.5.reciprocal));

    note = Demand.kr(trig, 0, Dseq((22,24..44).midicps.scramble, inf));

    sweep = LFSaw.ar(Demand.kr(trig, 0, Drand([1, 2, 2, 3, 4, 5, 6, 8, 16], inf))).exprange(40, 5000);

    son = Pulse.ar(note * [0.99, 1, 1.01]).sum;
    son = LPF.ar(son, sweep);   
    son = Normalizer.ar(son);
    son = son + BPF.ar(son, 2000, 2);

    //////// special flavours:
    // hi manster
    son = Select.ar(TRand.kr(trig: trig) < 0.05, [son, HPF.ar(son, 1000) * 4]);
    // sweep manster
    son = Select.ar(TRand.kr(trig: trig) < 0.05, [son, HPF.ar(son, sweep) * 4]);
    // decimate
    son = Select.ar(TRand.kr(trig: trig) < 0.05, [son, son.round(0.1)]);

    son = (son * 5).tanh;
    //son = son + GVerb.ar(son, 10, 0.1, 0.7, mul: 0.3);
    //son.dup;
    Out.ar(out, son.dup * amp);
}).add;


 // groups
 synthgroup= Group.head(Node.basicNew(s,1)); // one at the head
 bbgroup= Group.after(synthgroup); // this one comes after, so it can do stuff with audio
        // from the synthgroup
 bus= Bus.audio(s,1); // a bus to route audio around

 // a buffer holding a breakbeat. The first argument is the filename, the second is the number of
 // beats in the file.
 sf = BBCutBuffer("sounds/drums/breaks/hiphop/22127__nikolat__oldskoolish_90bpm.wav", 16);
 
 // a buffer used by BBCut to hold anaylsis
 buf = BBCutBuffer.alloc(s,44100,1);
 
 //  The default clock.  90 is the BPM / 60 for the number of seconds in a minute
 TempoClock.default.tempo_(180/60);

 // BBCut uses it's own clock class. We're using the default clock as a base
 clock= ExternalClock(TempoClock.default); 
 clock.play;  
 
 // Where stuff actually happens
 Routine.run({

  s.sync; // wait for buffers to load

 loop = (instrument:dub, out:0, amp: 0.3,
    group:synthgroup.nodeID).play(clock.tempoclock);

  /* That's an Event, which you can create by using parens like this.  We're using
  an event because of the timing built in to that class.  Passing the clock
  argument to play means that the loop will always start on a beat and thus be 
  synced with other BBCut stuff. */
  
  // let it play for 5 seconds
  5.wait;
  
  group = CutGroup(CutBuf3(sf, 0.5));
  group.add(CutBit1.new(4));
    cut2 = BBCut2(group, BBCutProc11(8, 4, 16, 2, 0.2)).play(clock);

  
  // start a process to cut things coming in on the bus
  cut1 = BBCut2(CutGroup(CutStream1(bus.index, buf), bbgroup), 
   BBCutProc11(8, 4, 16, 2, 0.2)).play(clock);

 2.wait;
 
   loop.set(out, bus.index);
   
 "bbcut".postln;
 
 30.wait;
 
   cut4 = BBCut2(CutGroup(CutStream1(bus.index, buf), bbgroup), 
    SQPusher2.new).play(clock);

   
 });
)

I think it would be cool to run the drums through a 4 or 8 bit MantissaMask, but first I have to figure out how to write FX.

Stupid BBCut Tricks

I’ve been messing a out with the BBCut Library and will shortly be generating some documentation for my students. In the mean time, I give you some commented source code and the output which it creates. In order to play at home, you need a particular sample.

(

 var bus, sf, buf, clock, synthgroup, bbgroup, loop, group, cut1, cut2, cut3, stream, pb,
  cut4, out;

 // this first synth is just to play notes
 SynthDef(squared, { |out, freq, amp, pan, dur|
  
  var tri, env, panner;
  
  env = EnvGen.kr(Env.triangle(dur, amp), doneAction: 2);
  tri = MantissaMask.ar(Saw.ar(freq, env), 8);
  panner = Pan2.ar(tri, pan);
  Out.ar(out, panner)
 }).add;
 
 
 // a looping buffer player
 SynthDef(loop, { |out = 0, bufnum = 0, amp = 0.2, loop=1|

  var player;
  
  player = PlayBuf.ar(2, bufnum, 2 * BufRateScale.kr(bufnum), loop: loop, doneAction:2);
  Out.ar(out, player * amp);
 }).add;
 
 // groups
 synthgroup= Group.head(Node.basicNew(s,1)); // one at the head
 bbgroup= Group.after(synthgroup); // this one comes after, so it can do stuff with audio
        // from the synthgroup
 bus= Bus.audio(s,1); // a bus to route audio around

 // a buffer holding a breakbeat. The first argument is the filename, the second is the number of
 // beats in the file.
 sf = BBCutBuffer("sounds/drums/breaks/hiphop/22127__nikolat__oldskoolish_90bpm.wav", 16);
 
 // a buffer used by BBCut to hold anaylsis
 buf = BBCutBuffer.alloc(s,44100,1);
 
 //  The default clock.  180 is the BPM / 60 for the number of seconds in a minute
 TempoClock.default.tempo_(180/60);

 // BBCut uses it's own clock class. We're using the default clock as a base
 clock= ExternalClock(TempoClock.default); 
 clock.play;  
 
 // Where stuff actually happens
 Routine.run({

  s.sync; // wait for buffers to load
  
  // start playing the breakbeat
  loop = (instrument:loop, out:0, bufnum: sf.bufnum, amp: 0.5, loop:1, 
    group:synthgroup.nodeID).play(clock.tempoclock);

  /* That's an Event, which you can create by using parens like this.  We're using
  an event because of the timing built in to that class.  Passing the clock
  argument to play means that the loop will always start on a beat and thus be 
  synced with other BBCut stuff. */
  
  // let it play for 5 seconds
  5.wait;
  
  // start a process to cut things coming in on the bus
  cut1 = BBCut2(CutGroup(CutStream1(bus.index, buf), bbgroup), 
   BBCutProc11(8, 4, 16, 2, 0.2)).play(clock);

  /*  
  We use a cut group to make sure that the BBCut synths get added to the bbgroup.
  This is to make sure that all the audio happens in the right order.
  
  CutStream1 cuts up an audio stream. In this case, from our bus.  It uses a buffer to 
  hold analysis data.
  
  BBCutProc11 is a cut proceedure.  
  The arguments are: sdiv, barlength, phrasebars, numrepeats, stutterchance, 
  stutterspeed, stutterarea
  * sdiv - is subdivision. 8 subdivsions gives quaver (eighthnote) resolution.
  * barlength - is normally set to 4 for 4/4 bars. If you give it 3, you get 3/4
  * phrasebars - the length of the current phrase is barlength * phrasebars
  * numrepeats - Total number of repeats for normal cuts. So 2 corresponds to a 
  particular size cut at one offset plus one exact repetition.
  * stutterchance - the tail of a phrase has this chance of becoming a repeating 
  one unit cell stutter (0.0 to 1.0)

  For more on this, see the helpfile.
  
  And we play it with the clock to line everything up
  */

  // wait a bit, so the BBCut2 stuff has a time to start
  2.wait;

  // change the output of the looping synth from 0 to the bus, so the BBCut buffer
  // can start working on it
  loop.set(out, bus.index);
  
  // let it play for 5 seconds
  5.wait;
  
  // start another BBCut process, this one just using the sound file.
  cut2 = BBCut2(CutBuf3(sf, 0.3), BBCutProc11(8, 4, 16, 2, 0.2)).play(clock);
  // We use CutBuf instead of CutStream, because we're just cutting a buffer
  
  // stop looping the first synth we started
  loop.set(loop, 0);

  cut1.stop;

  10.wait;
  
  // To add in some extra effects, we can use a CutGroup
  group = CutGroup(CutBuf3(sf, 0.5));
  cut3 = BBCut2(group, BBCutProc11(8, 4, 16, 2, 0.2)).play(clock);

  // play is straight for 5 seconds
  5.wait;

  // add a couple of filters to our cutgroup
  group.add(CutMod1.new);
  group.add(CutBRF1({rrand(1000,5000)},{rrand(0.1,0.9)},{rrand(1.01,1.05)}));

  10.wait;
  
  // we can take the filters back off
  group.removeAt(2);
  group.removeAt(2);
  
  // we can use BBCut cut proceedures to control Pbinds
  stream = CutProcStream(BBCutProc11.new);
  
  pb = Pbindf(
   stream,
   instrument, squared,
   scale,  Scale.gong,
   degree,  Pwhite(0,7, inf),
   octave,  Prand([2, 3], inf),
   amp,  0.2,
   sustain,  0.01,
   out,  0,
   group,  synthgroup.nodeID
  ).play(clock.tempoclock);

  // the stream provides durations
  
  10.wait;
  
  // We can also process this is like we did the loop at the start
  
  pb.stop;
  pb = Pbindf(
   stream,
   instrument, squared,
   scale,  Scale.gong,
   degree,  Pwrand([Pwhite(0,7, inf), rest], [0.8, 0.2], inf),
   octave,  Prand([3, 4], inf),
   amp,  0.2,
   sustain,  0.01,
   out,  bus.index,
   group,  synthgroup.nodeID
  ).play(clock.tempoclock);
  
  
  
  cut4 = BBCut2(CutGroup(CutStream1(bus.index, buf), bbgroup), 
    SQPusher2.new).play(clock);
    
  // SQPusher2 is another cut proc
  
  
  30.wait;
  cut3.stop;
  5.wait;
  cut2.stop;
  1.wait;
  cut4.stop;
  pb.stop;
  
 })
)