Some useful Betablocker DS code patterns as used in common live coding situations.
Firstly, when carrying out a sound check before a live performance it's important to keep some kinds of sound engineers happy generating as many different sounds as you can so they can fiddle around with stuff - of course remember to turn everything up to 8 when asked to make the loudest sound you can in order to leave some overhead to blow the PA.
This is a betablocker program which will play all the sounds in a sound bank at all frequencies. Add more concurrent threads to make it more interesting. Values interpreted as pointers are visualised as arrows:
(the instruction set description can be found here)
In betablocker the beat is always locked to the instruction cycle rate, so fast changing sounds are a matter of optimisation. One of the most common ways around this (and a good way to start things off in a gig) is to use one fast loop program for playing sounds while another slower program modifies the first by overwriting bits of it.
Next week I'm presenting Naked on Pluto at Pixelache'sPixelversity on Wednesday evening (9th November) at the Cable Factory. Owen Kelly will also be there to talk about Pixelversity's ‘Social Identity, Augmented Reality & Virtuality’ Study Group' which will take place next year.
On Saturday afternoon (12th November), Till Bovermann and I will be demoing Supercollider, Fluxus, Scheme Bricks and Betablocker DS and talking about what it means to be a livecoder at the Hacklab Helsinki.
Some random pictures from some excellent (and at times very loud) few days in Paris. Thanks to Sony Computer Science Labs and also the Paris contingent of the TPOLM demo crew for coming out to support us.
The gig was extensively recorded, so hopefully more about this soon.
After a week of heavy development on Germination X, and with a fast approaching performance, it seemed the time to dive back into Gameboy DS homebrew and get the bits and pieces of assembler code I'd been playing with together into something I could build more sounds from.
After getting the low level stuff working properly, I got busy making a bunch of audio scenes that I can trigger from Betablocker DS code.
Mostly I'm doing wavetable based things, with lots of frequency and ring modulation. I was also pleased to find this simple resonant filter thanks to the ever fabulous musicdsp archive (and thanks to Paul Kellett for originally posting it):
// set feedback amount given f and q between 0 and 1
fb = q + q/(1.0- f);// for each sample...
buf0 = buf0 + f *(in - buf0 + fb *(buf0 - buf1));
buf1 = buf1 + f *(buf0 - buf1);
out = buf1;
The next step was to read Jasper Vijn's excellent explanation of fixed point maths which I've used a bit on the Android but not really totally understood before. Mobile devices tend to only have integer maths at the hardware level, so if we restrict the calculations to fixed point it's much faster and kinder to batteries than the floating point equivalent. Here is the fixed point version of the filter:
// we are using .10 fixed point (10 bits for the fractional part)#define FX_SHIFT 10// convert an int into to it's fixed representationinline s32 fx(s32 i){return i<<FX_SHIFT;}// multiply two fixed point numbers (returns fixed point)inline s32 fxmul(s32 a, s32 b){return(a*b)>>FX_SHIFT;}// f is cutoff frequecy, q is resonance, s is the filter statevoid lpf(s16* dst, s16* src, u32 length, s16 f, s16 q, s16 *s){
u32 fb = q+fxmul(q,fx(1)-f);for(u32 i=0; i<length;++i){
s[0]+=fxmul(f,src[i]-s[0]+fxmul(fb,s[0]-s[1]));
s[1]+=fxmul(f,s[0]-s[1]);
dst[i]=s[1];}}
Some images of the Machinima exhibition at FILE 2011 in São Paulo, featuring a movie of a livecoded abstractification of a Missile Command game written/performed/destroyed/recorded in Fluxus.
The algorithm uses feedback to create sounds that take some time to play through a wide range of frequencies. It works by writing into the same buffer it's playing, but at a different rate/position - the resulting ugliness suits the style of BBDS very much. As an aid to our understanding Till converted the algorithm to Supercollider, then over the next couple of days I managed to get it running with an inner loop of 9 instructions on the DS (could probably be optimised further, but I'm still a beginner):
@ ----------------------------------------------------
@ qrz_maximus: *dst, length, readpos, writepos [freq, *tab]
@ ----------------------------------------------------
.global qrz_maximus
.type qrz_maximus, %function
qrz_maximus:
push {r4,r5,r6,r7} @ push the registers we need
ldr r4, [r13,#20] @ load freq from stack into r4
ldr r5, [r13,#24] @ load *tab from stack into r5
ldr r6, .tablength @ load the tablen into r6
.maximus_loop:
ldrh r7, [r5,r2] @ load the sample into r7
strh r7, [r0], #2 @ write output: *dst=r7 dst++
strh r7, [r5,r3] @ feedback into tab[writepos]=r7
add r2, r2, r4 @ readpos+=freq
and r2, r2, r6 @ mask readpos into range
add r3, r3, #2 @ writepos++
and r3, r3, r6 @ mask writepos into range
subs r1, r1, #1 @ length--
bne .maximus_loop @ repeat until length zero
mov r0, r2 @ return readpos
pop {r4,r5,r6,r7}
bx lr @ exit
.tablength:
.word 0x00003FF
And here it is running on autopilot with a test program in Betablocker:
Once the low level code for a Naked on Pluto bot is written in Scheme, it's added by the game using secret commands which program the game inside itself, while it's running. This means the game can be changed while people are playing it - which makes it much easier to add new things without worrying about the disruption caused by restarting the server. We can also work on the game collaboratively, at the same time this way.
(Programming a spybot with it's behaviour)
Eventually the plan is to be able to program the bots fully within the game client - this is still a long term goal, but there are of course some fairly complex security problems with this idea.
(dressing a AudienceBot with another object)
Not all players have the ability to use these secret commands right now, in order to access them a player has to be carrying a special object (also known as a "Golden Medallion"). This allows you access to all areas of the game, including an "administration room" and various other secret powers.
A rainy Sunday, with only the dog for company, so in between walks I thought I'd try and learn some assembler. I've been unhappy with triggering samples with Betablocker DS (I prefer synthesis) and I've heard good things about ARM asm - so it seemed like a good opportunity to attempt a small, fast and dirty synth.
I found some really nice tutorials here and here. I've done a tiny bit of this sort of thing before with microcontrollers, but this is a more of a respectable flavour of assembler, on a decent RISC processor (which derives from the Acorn Archimedes and is now used on IPhones, Androids and Gameboys). Here is a white noise generator:
; white_noise(r0=*dst, r1=clock, r2=length, r3=freq)white_noise:push{r4,r5,r6}; need to restore registers we usemov r4, r1 ; r4 is the rand state (start with clock)
ldr r5,.rnd_data ; r5 is the multiplier value
ldr r6,.rnd_data+1; r6 is the addition value.noise_loop:
mla r4, r5, r4, r6 ; the maths bit: r4 = (r6 + (r5 * r4))
strh r4,[r0], #2; *dst++ = clock;
subs r2, r2, #1; length--;
bne .noise_loop ; branch if length not zeropop{r4,r5,r6}bx lr ; return.rnd_data:.word0x000343FD; nicked from ansi c rand().word0x00269EC3; need to keep large numbers (>8bit) as data
This code is based on the ansi C rand() function that basically looks like this:
randnum = randnum * 214013+2531011;
Which we can do in a single instruction - mla (multiply with accumulate). Of course, gcc would presumably optimise much better code than mine from C++, but there is something more satisfying about doing it this way. I certainly prefer the sound - and over half the cpu usage remains unused with 5 voices and the interface running. The rest of the code is here.
Is seems our meetings at the TAI studio are bearing fruit already, Till Bovermann has ported the Betablocker machine as a Supercollider UGen. What's interesting is rather than taking a similar approach to music making, this version is running at audio rate and the processes directly generate sample data. He can also run lots of them at the same time and control them from SCLang. It's great when people use your code, but even better when it gets used in ways you didn't think about.