Euclidean rhythms update

E(5, 13) pattern

E(5, 13) pattern

There’s an error in the Euclidean rhythms generator I posted in January. A few days ago I got a comment by Thomas pointing out the app generates wrong patterns. And he’s absolutely right.

Here’s an updated version in which I rewrote the algorithm code. I checked it with several patterns and they all came out correct, so hopefully I have it right this time.

The new Flash app

Get Adobe Flash player

 

Download the source files here.

I didn’t update the download ZIP file in the original post. So if you tire of the regular Euclidean patterns you can still download the old version and use those quirky ‘not quite right’ patterns. :-)

The difference

The difference between the old and new version can be seen in the outcome of the algorithm for a pattern of thirteen steps with five notes. The old version generates as outcome:

E(5, 13) = [x..x..x..x.x.]

while it should be:

E(5, 13) = [x..x.x..x.x..]

and the correct calculation broken down in iterations:

[x][x][x][x][x][.][.][.][.][.][.][.][.]
[x.][x.][x.][x.][x.][.][.][.]
[x..][x..][x..][x.][x.]
[x..x.][x..x.][x..]

It appears the old code stops one iteration too early: The wrong pattern is the same as the one before last iteration. Here’s the ActionScript code of the new version. It might be more compact and optimized, but I find it clearer to follow this way.

package com.hisschemoller.utils
{
   /**
    * @author Wouter Hisschemoller
    * (c) May 18, 2011
    */
   public class EuclidGenerator
   {
       private var _pattern : Vector. = new Vector.( );

       public function EuclidGenerator ( total : int, hits : int )
       {
           if ( hits >= total || hits == 1 || hits == 0 )
           {
               _pattern = new Vector.( );
               if ( hits >= total )
               {
                   for ( var i : int = 0; i < total; i ++ )
                   {
                       _pattern.push( true );
                   }
               }
               else if ( total == 1 )
               {
                   _pattern.push( hits == 1 );
               }
               else
               {
                   for ( i = 0; i < total; i ++ )
                   {
                       _pattern.push( false );
                   }
               }
               
               return;
           }
           
           var bigArray : Vector.<Vector.> = new Vector.<Vector.>( );
           for ( i = 0; i < hits; i ++ )
           {
               var boolArray : Vector. = new Vector.( );
               boolArray.push( true );
               bigArray.push( boolArray );
           }
   
           var smallArray : Vector.<Vector.> = new Vector.<Vector.>( );
           for ( i = 0; i < ( total - hits ); i ++ )
           {
               boolArray = new Vector.( );
               boolArray.push( false );
               smallArray.push( boolArray );
           }
           
           _pattern = bjorklund( bigArray, smallArray );
       }

       public function get pattern () : Vector.
       {
           return _pattern;
       }

       private function bjorklund ( bigArray : Vector.<Vector.>, smallArray : Vector.<Vector.> ) : Vector.
       {
           /** Done, flatten arrays. */
           if ( smallArray.length <= 1 )
           {
               var resultList : Vector. = new Vector.( );
               for ( var i : int = 0; i < bigArray.length; i ++ )
               {
                   for ( var j : int = 0; j < bigArray[ i ].length; j ++ )
                   {
                       resultList.push( bigArray[ i ][ j ] );
                   }
               }
               for ( i = 0; i < smallArray.length; i ++ )
               {
                   for ( j = 0; j < smallArray[ i ].length; j ++ )
                   {
                       resultList.push( smallArray[i][j] );
                   }
               }
               return resultList;
           }

           var fullRounds : int = Math.floor( smallArray.length / bigArray.length );
           for ( i = 0; i < fullRounds; i ++ )
           {
               for ( j = 0; j < bigArray.length; j ++ )
               {
                   var sourceBoolArray : Vector. = smallArray[0];
                   for ( var k : int = 0; k < sourceBoolArray.length; k ++ )
                   {
                       bigArray[j].push( sourceBoolArray[k] );
                   }
                   smallArray.splice( 0, 1 );
               }
           }

           var remainder : int = smallArray.length % bigArray.length;
           for ( i = 0; i < remainder; i ++ )
           {
               sourceBoolArray = smallArray[0];
               for ( k = 0; k < sourceBoolArray.length; k ++ )
               {
                   bigArray[i].push( sourceBoolArray[k] );
               }
               smallArray.splice( 0, 1 );
           }

           /** Split result in two new arrays. */
           var newBigArray : Vector.<Vector.> = new Vector.<Vector.>( );
           var newSmallArray : Vector.<Vector.> = new Vector.<Vector.>( );
           for ( i = 0; i < bigArray.length; i ++ )
           {
               if ( remainder == 0 )
               {
                   newBigArray.push( bigArray[i] );
               }
               else
               {
                   if ( i < remainder )
                   {
                       newBigArray.push( bigArray[i] );
                   }
                   else
                   {
                       newSmallArray.push( bigArray[i] );
                   }
               }
           }
           
           return bjorklund( newBigArray, newSmallArray );
       }
   }
}

3 Comments

Wouter,

@Tom Whitwell: A hardware version is a great idea. I’m very curious to see what it will look like. Exciting. I hope there will be a way to use such a thing myself.

Just had a look at the paper and saw the examples. In the Euclidean strings chapter. I did indeed skip this last iteration in my code because elsewhere in the paper (chapter 2) he says: “Note that one could proceed one step further in this process by inserting [100] into [10010] [10010] . However, Bjorklund argues that since the sequence is cyclic it does not matter (hence his stopping rule).”.

Tom Whitwell,

Hi,
This is very inspiring – I’m putting together a hardware version of this for my modular synth.
Have you noticed that in Toussaint’s paper, he describes beats with only one rest like this:
E(3,4) = [x.xx]
E(7,8) = [x.xxxxxx]
Whereas your code (and mine, so far) delivers:
E(3,4) = [1110]
E(7,8) = [11111110]
Not sure why he does that one iteration, which seems spurious (but perhaps matches up with the Colombian Cumba and the Tuareg drumming!)

Leave a Reply

Your email address will not be published. Required fields are marked *

2 + two =