Bjorklund algorithm
Hi all,
I implemented Bjorklunds algorithm to Javascript for use in Max and thought I would share.
I know there exists some other implementations for various languages around the web but I found that most were broken for more than 16 steps so I fairly closely ported the original c implementation.
Hopefully someone will find it useful
Thanks - I can't look at it right now, because the network police have banned me from using Max at work (really!), but thought you might be interested in this post, if you hadn't already seen it:
There was also a js Max implementation - I have the patch here, but can't open it of course, and I can't find it on the forum, though I think it may have been by Axiom-Crux ?
cheers
Roger
haha, I also made an implementation of this bjorklund algorithm (in java) and I plan to share that later too, but you beat me to it :-)
@roger.carruthers Thanks for the linkage. That ruby code looks very similar to a lot of the other implementations that I found to not work with more than 16 steps (although for 16 or less steps they work fine and are more efficient).
A few of the options are listed here http://createdigitalmusic.com/2011/03/circles-and-euclidian-rhythms-off-the-grid-a-few-music-makers-that-go-round-and-round/
most look to be based on the same logical implementation (across different languages), but whatever was used as the base implementation that has subsequently been ported does not follow Bjorklunds original c version. That is, as far as I can tell, I am by no means an algorithm expert or particularly at maths! But as I said I have noticed funny results when trying to create patterns greater that 16 steps with all but my version.
@Timo Rozendal
If you have some java code please make it available, I was possibly going to make an mxj to see if it performs much better, although I am hoping that the new Javascript engine in Max 6 might solve that issue for me!
Downloaded this as the other implementations have errors in them...How do you use this in max? I saved the file in the folder I'm working in and used [js bjorklund.js] ... According to the source it's one function (bjorklund) that takes 2 arguments (steps, pulses) and returns an array...I've tried sending a message "bjorklund 8 3" to the object but nothing comes out the outlet. Any ideas?
Nevermind... I had to:
1) initiate the object such as [js bjorklund 8 3]
2) edit the .js and at the end instead of returning the value send it to outlet 0
3) call the function as: "bjorklund 8 3"
I'm having trouble getting this to work in max and Marcos instructions leave me confused. new to the js object and javascript coding in general.
I updated the bjorklund example and made it a little more user friendly...
I'm getting 'js:no function generatePattern' in the Max window,
Cheers
Roger
Are you loading the bjorklund.js I up loaded? generatePattern is definitely there...
Woops, yes the old one was still in my search path!
Cheers
Roger
thanks for the remake!
weird, the fix either breaks the code or there is something wrong with the original
the code's output is offset for certain pulse values. for "generatePattern 16 5" i get
[ . x . . x . . x . . x . . x . . ] when the article says the expected result is
[ x . . x . . x . . x . . x . . . ] "generatePattern 8 5" causes the same offset.
weirdly, the starting example of "generatePattern 8 3" works but "generatePattern 16 3" does not
i went back to withakay's original code
replaced only "return pattern.reverse();" on line 63 with "outlet(0, pattern.reverse());"
the error remained the same. going to fiddle around with it and see what's happening.
i've used the java code from http://doc.gold.ac.uk/~ma801dp/blog/?p=40 in the past
might go back to that for now if a few test values come back correct.
edit: jurgen schmitz's m4l device works while using this code because the js ends with
----------------------------------
build(level);
pattern = pattern.reverse();
// make sure pattern starts with a '1'
for(s = 0; pattern[s] != 1; s++);
pattern = rotate(pattern, -s);
return pattern;
----------------------------------
but was designed to work specifically with his device, as just copying out the original portion gives "js: bjorklundFIX2.js: Javascript ReferenceError: rotate is not defined, line 75"
the fiddling continues....
jurgen's idea about rotating the list until it starts with a 1 is great, simple and efficient.
isn't it strange the rotate function doesn't work in js ? there should be a way...
Actually, I have been looking a little more closely at the output. It seems to be doing
exactly what I would expect. Given the following sequence of input, you get the following
output...
8 beats 3 pulses
output: 1 0 0 1 0 0 1 0
16 beats 3 pulses
output: 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0
8 beats 4 pulses
output: 1 0 1 0 1 0 1 0
So what this is really doing, is taking a pattern of beats and evenly distributing
another pattern within it.
I have updated the helper patch to allow for easier control of pattern lengths
and note division. By adding two separate clicks you can really hear the difference.
when using more than one generator at the same time, it's nice to have a synchronisation point, which is the start of each loop.
if every loop start with 1, it feels more musical, and rotating the list doesn't change the evenly distribution.
it's a kind of normalisation.
bertrandfraysse, not so fast. it seems jurgen's code is flawed as well.
for example E(5, 8)
[ . x x . x x . x ] = bjorklund code output
[ x x . x x . x . ] = jurgen's rotate fix (from ADesso ERG v2.3)
[ x . x x . x x . ] = expected result (from Toussaint's paper)
but for an example like E(5, 16) the pattern from the m4l device is correct.
so for some pulse values the array needs to be rotated positively, others negatively.
maybe it feels more natural to have 1 0 as a beginning than 1 1.
so we need to make our algorithm to try and find the right rotation to find 0 after 1 if it can...
just an idea...
i feel like i'm overcomplicating this, especially as i'm only planning to use the algorithm for generating quick drum loops to be refined later. going to stick to the current code and put a zl.rot at the end to adjust the pattern as i like. thanks again for the help, anthony.
Try this code,that I wrote it for my euklid-sequencer m4l-device http://www.maxforlive.com/library/device.php?id=993 . The outcoming list always starts with a 1. I'm not an experienced js-coder, so there are probably smarter ways to compute this list, but it works ;-).
inlets = 1;
outlets = 1;
function bjorklund(pulses, steps)
{
if (steps >= pulses && steps > 1)
{
var pauses = steps - pulses;
var rythm = new Array();
var colSizes = new Array();
//build arrays
for (var i = 0; i < pulses; i++)
{
rythm.push(1);
colSizes.push(1);
}
for (var i = 0; i < pauses; i++)
{
rythm.push(0);
colSizes.push(1);
}
//actual algorithm
while ( !((pauses==1 && pulses==1) || (pauses==0 || pulses==0)) )
{
var counter = 0;
if (pauses>pulses)
{
counter = pulses;
pauses -= pulses;
}
else
{
counter = pauses;
pulses -= pauses;
}
var writePosition = 0;
var level = colSizes[colSizes.length - 1];
for (var i = 0; i < counter; i++)
{
writePosition += colSizes[i];
var readPosition = rythm.length - level;
var elementsToMove = rythm.splice(readPosition, level);
for (var j = 0; j < elementsToMove.length; j++)
{
rythm.splice(writePosition, 0, elementsToMove[j]);
writePosition ++;
}
colSizes[i] += level;
colSizes.pop();
}
}
outlet(0, rythm);
}
}
sorry david but your results are coming back out of order.
example (3, 8)
[ x . . x . x . . ] = your code
[ x . . x . . x . ] = Bjorklund code & Toussaint's paper
example (5, 8)
[ x . x x . x . x ] = your code
[ . x x . x x . x ] = bjorklund code
[ x . x x . x x . ] = Toussaint's paper
same for most other values i tried. wish i could learn js code quicker so i could offer something useful other than criticism. sorry.
edit: SEE BELOW
well i've almost cracked it. this new code accounts for the list rotation (either positively or negatively).
so far the only example error i've found is E(5,13)
[ x . x . . x . x . . x . . ] = my code
[ x . . x . x . . x . x . . ] = toussaint's paper
if anyone finds another let me know. not sure how to go about correcting that but for my use it won't be an issue.
save the new bjorklund.js file i've attached and use the below patch for testing
Yes, I think you are overcomplicating this a bit ;-). The output of all three algorithms are valid as they evenly distribute the events over the number of steps. They are all just rotations of each other, and I would always include some kind of rotation-parameter in a device anyway.
For beats I just find it much easier, if the list always starts with a 1 and not a 0, which is the reason why I wrote my code.
and here is my quick and dirty (string manipulating) way in java
I like how with verbose on you see the process in the max window :-)
I should have posted this a year ago, but I forgot, these recent messages in this forum reminded me of this
thank you very much timo !
I use the original Bjorklund method, and I find the placement of beats nice and serendipitous, which is why I use it in the first place :)
Thanks a lot for sharing this! Here's a little m4l device in which I used the algorithm (cudnylon's version):
This topic seems pretty well addressed at this point but I thought I would add a method to accomplish this with standard max objects. I found this implementation in acreil's algo-acid PD patch (found here: ) Simply feed it with a counter and it will bang on the beats that are in the E-rhythm. Here is a max version.
----------begin_max5_patcher----------
{
"boxes" : [ {
"box" : {
"maxclass" : "comment",
"text" : "i found this implementation in acriel's algorithmic acid pure data patch. http://acreil.wordpress.com/2013/04/20/algorithmic-composition-of-acid-house-with-lo-fi-timbres/ ",
"linecount" : 4,
"numinlets" : 1,
"fontsize" : 12.0,
"numoutlets" : 0,
"patching_rect" : [ 425.0, 261.0, 150.0, 60.0 ],
"frgb" : 0.0,
"id" : "obj-3",
"fontname" : "Arial"
}
}
, {
"box" : {
"maxclass" : "comment",
"text" : "generates E-rhythms, send a counter in... or any number... good luck",
"linecount" : 3,
"numinlets" : 1,
"fontsize" : 12.0,
"numoutlets" : 0,
"patching_rect" : [ 425.0, 24.0, 174.0, 47.0 ],
"frgb" : 0.0,
"id" : "obj-2",
"fontname" : "Arial"
}
}
, {
"box" : {
"maxclass" : "comment",
"text" : "int",
"numinlets" : 1,
"fontsize" : 12.0,
"numoutlets" : 0,
"patching_rect" : [ 50.0, 123.0, 32.0, 20.0 ],
"frgb" : 0.0,
"id" : "obj-27",
"fontname" : "Arial"
}
}
, {
"box" : {
"maxclass" : "comment",
"text" : "#hits/bar",
"numinlets" : 1,
"fontsize" : 12.0,
"numoutlets" : 0,
"patching_rect" : [ 116.75, 49.0, 61.0, 20.0 ],
"frgb" : 0.0,
"id" : "obj-26",
"fontname" : "Arial"
}
}
, {
"box" : {
"maxclass" : "comment",
"text" : "rota",
"numinlets" : 1,
"fontsize" : 12.0,
"numoutlets" : 0,
"patching_rect" : [ 283.25, 14.0, 54.0, 20.0 ],
"frgb" : 0.0,
"id" : "obj-25",
"fontname" : "Arial"
}
}
, {
"box" : {
"maxclass" : "comment",
"text" : "length",
"numinlets" : 1,
"fontsize" : 12.0,
"numoutlets" : 0,
"patching_rect" : [ 332.0, 67.5, 54.0, 20.0 ],
"frgb" : 0.0,
"id" : "obj-24",
"fontname" : "Arial"
}
}
, {
"box" : {
"maxclass" : "newobj",
"text" : "select 1",
"numinlets" : 2,
"fontsize" : 12.0,
"numoutlets" : 2,
"outlettype" : [ "bang", "" ],
"patching_rect" : [ 72.0, 281.0, 52.0, 20.0 ],
"id" : "obj-21",
"fontname" : "Arial"
}
}
, {
"box" : {
"maxclass" : "newobj",
"text" : "< #1",
"numinlets" : 2,
"fontsize" : 12.0,
"numoutlets" : 1,
"outlettype" : [ "int" ],
"patching_rect" : [ 72.0, 250.0, 34.0, 20.0 ],
"id" : "obj-20",
"fontname" : "Arial"
}
}
, {
"box" : {
"maxclass" : "newobj",
"text" : "% #3",
"numinlets" : 2,
"fontsize" : 12.0,
"numoutlets" : 1,
"outlettype" : [ "int" ],
"patching_rect" : [ 72.0, 217.0, 38.0, 20.0 ],
"id" : "obj-19",
"fontname" : "Arial"
}
}
, {
"box" : {
"maxclass" : "newobj",
"text" : "* #1",
"numinlets" : 2,
"fontsize" : 12.0,
"numoutlets" : 1,
"outlettype" : [ "int" ],
"patching_rect" : [ 72.0, 185.0, 32.5, 20.0 ],
"id" : "obj-18",
"fontname" : "Arial"
}
}
, {
"box" : {
"maxclass" : "newobj",
"text" : "+ #2",
"numinlets" : 2,
"fontsize" : 12.0,
"numoutlets" : 1,
"outlettype" : [ "int" ],
"patching_rect" : [ 72.0, 146.0, 34.0, 20.0 ],
"id" : "obj-14",
"fontname" : "Arial"
}
}
, {
"box" : {
"maxclass" : "inlet",
"numinlets" : 0,
"numoutlets" : 1,
"outlettype" : [ "int" ],
"patching_rect" : [ 76.5, 14.0, 25.0, 25.0 ],
"id" : "obj-28",
"comment" : "step (int)"
}
}
, {
"box" : {
"maxclass" : "inlet",
"numinlets" : 0,
"numoutlets" : 1,
"outlettype" : [ "int" ],
"patching_rect" : [ 143.75, 24.0, 25.0, 25.0 ],
"id" : "obj-29",
"comment" : "#hits(int)"
}
}
, {
"box" : {
"maxclass" : "inlet",
"numinlets" : 0,
"numoutlets" : 1,
"outlettype" : [ "int" ],
"patching_rect" : [ 252.75, 14.0, 25.0, 25.0 ],
"id" : "obj-30",
"comment" : "rotation (int)"
}
}
, {
"box" : {
"maxclass" : "inlet",
"numinlets" : 0,
"numoutlets" : 1,
"outlettype" : [ "int" ],
"patching_rect" : [ 307.0, 65.0, 25.0, 25.0 ],
"id" : "obj-32",
"comment" : "length (int)"
}
}
, {
"box" : {
"maxclass" : "outlet",
"numinlets" : 1,
"numoutlets" : 0,
"patching_rect" : [ 72.0, 361.0, 25.0, 25.0 ],
"id" : "obj-33",
"comment" : ""
}
}
],
"lines" : [ {
"patchline" : {
"source" : [ "obj-32", 0 ],
"destination" : [ "obj-19", 1 ],
"hidden" : 0,
"midpoints" : [ 316.5, 201.0, 195.0, 201.0, 195.0, 213.0, 100.5, 213.0 ],
"disabled" : 0
}
}
, {
"patchline" : {
"source" : [ "obj-30", 0 ],
"destination" : [ "obj-14", 1 ],
"hidden" : 0,
"midpoints" : [ 262.25, 132.0, 96.5, 132.0 ],
"disabled" : 0
}
}
, {
"patchline" : {
"source" : [ "obj-29", 0 ],
"destination" : [ "obj-20", 1 ],
"hidden" : 0,
"midpoints" : [ 153.25, 87.0, 114.0, 87.0, 114.0, 204.0, 120.0, 204.0, 120.0, 246.0, 96.5, 246.0 ],
"disabled" : 0
}
}
, {
"patchline" : {
"source" : [ "obj-29", 0 ],
"destination" : [ "obj-18", 1 ],
"hidden" : 0,
"midpoints" : [ 153.25, 87.0, 108.0, 87.0, 108.0, 180.0, 95.0, 180.0 ],
"disabled" : 0
}
}
, {
"patchline" : {
"source" : [ "obj-28", 0 ],
"destination" : [ "obj-14", 0 ],
"hidden" : 0,
"midpoints" : [ 86.0, 141.0, 81.5, 141.0 ],
"disabled" : 0
}
}
, {
"patchline" : {
"source" : [ "obj-21", 0 ],
"destination" : [ "obj-33", 0 ],
"hidden" : 0,
"midpoints" : [ 81.5, 303.0, 81.5, 303.0 ],
"disabled" : 0
}
}
, {
"patchline" : {
"source" : [ "obj-20", 0 ],
"destination" : [ "obj-21", 0 ],
"hidden" : 0,
"midpoints" : [ 81.5, 270.0, 81.5, 270.0 ],
"disabled" : 0
}
}
, {
"patchline" : {
"source" : [ "obj-19", 0 ],
"destination" : [ "obj-20", 0 ],
"hidden" : 0,
"midpoints" : [ 81.5, 237.0, 81.5, 237.0 ],
"disabled" : 0
}
}
, {
"patchline" : {
"source" : [ "obj-18", 0 ],
"destination" : [ "obj-19", 0 ],
"hidden" : 0,
"midpoints" : [ 81.5, 207.0, 81.5, 207.0 ],
"disabled" : 0
}
}
, {
"patchline" : {
"source" : [ "obj-14", 0 ],
"destination" : [ "obj-18", 0 ],
"hidden" : 0,
"midpoints" : [ 81.5, 168.0, 81.5, 168.0 ],
"disabled" : 0
}
}
],
"appversion" : {
"major" : 6,
"minor" : 0,
"revision" : 7
}
}
-----------end_max5_patcher-----------
Cheers and happy Turkey day
-X