Digital Music Programming II: Henon Map




This lab demonstrates a Max external object which outputs a fractal sequence of MIDI note numbers. There are two integer inputs into the object, plus a bang input, making the interface to the object similar to the diff example object presented in the last class. Here are the functions of each input into the henon object:

  1. bang messages from another object, such as metro.
  2. parameter 1 (alpha) setting for the note generator. This is a number from 0 to 127.
  3. parameter 2 (beta) setting for the note generator. This is a number from 0 to 127.

Here is a picture of what the final object will look like in Max:

The output from the object will be a sequence of numbers from 0-127 which can be used as MIDI note numbers, or for any other purpose. The following equation is the difference equation for the Henon map:

   y[n] = a * y[n-1]^2 + b * y[n-2] + 1
Or in English: The current output is equal to alpha times the previous output squared PLUS beta times the previous-previous output from the sequence PLUS one. The significance of this equation was discovered in the 1960's and 1970's by French astronomer Michel Henon in work he had done on calculating the orbits of stars in globular clusters.

Here is sample output from the henon external object which maps output from the Henon map equation into the range from 0 to 127:


  0  27  94 104  86 114  64 126  29  98  99  96 101  92 107  81 119  51 122  41
 64 126  29  98  99  96 101  92 106  81 119  52 123  40 112  68 126  31 100  94
 87 113  65 126  30  98  98  97 100  94 103  88 112  69 126  32 102  92 107  80
 48 121  46 119  51 122  42 115  62 126  29  98  99  96 100  94 104  86 114  63
 29  98  99  96 101  92 106  81 119  52 123  39 112  69 126  32 102  92 107  80
 49 121  45 118  53 124  38 110  74 124  37 109  76 123  40 113  67 126  30  99
101  92 107  80 119  50 122  44 117  57 125  33 103  89 110  73 124  36 108  79
120  47 120  49 121  45 118  55 125  35 106  82 118  54 124  36 108  78 121  44
117  56 125  34 104  86 114  64 126  29  98  99  96 101  92 107  81 119  51 122
115  63 126  29  98  99  96 101  93 105  83 117  56 125  33 104  87 113  66 126
 98  97  98  97 100  94 103  88 112  68 126  31 100  94 104  87 113  66 126  30
 98  98  97  99  96 100  94 104  86 114  65 126  29  98  99  96 100  94 104  86

The henon object is designed to receive a steady pulse of bang messages. To generate an interesting rhythm, the henon object has control over the minimum and maximum note which will be output. If a number in the above sequence falls outside of the range from 21 to 107, the output will be suppressed. This can generate interesting rhythmic patterns, since some of the notes in the sequence will be removed which lengthens the duration of other notes. Here is the output from the henon object showing the suppressed values in green:


  0  27  94 104  86 114  64 126  29  98  99  96 101  92 107  81 119  51 122  41
 64 126  29  98  99  96 101  92 106  81 119  52 123  40 112  68 126  31 100  94
 87 113  65 126  30  98  98  97 100  94 103  88 112  69 126  32 102  92 107  80
 48 121  46 119  51 122  42 115  62 126  29  98  99  96 100  94 104  86 114  63
 29  98  99  96 101  92 106  81 119  52 123  39 112  69 126  32 102  92 107  80
 49 121  45 118  53 124  38 110  74 124  37 109  76 123  40 113  67 126  30  99
101  92 107  80 119  50 122  44 117  57 125  33 103  89 110  73 124  36 108  79
120  47 120  49 121  45 118  55 125  35 106  82 118  54 124  36 108  78 121  44
117  56 125  34 104  86 114  64 126  29  98  99  96 101  92 107  81 119  51 122
115  63 126  29  98  99  96 101  93 105  83 117  56 125  33 104  87 113  66 126
 98  97  98  97 100  94 103  88 112  68 126  31 100  94 104  87 113  66 126  30
 98  98  97  99  96 100  94 104  86 114  65 126  29  98  99  96 100  94 104  86

Source Code for the Henon External Object

There are four general sections to the source code for the henon object which are given different colors:

  1. black -- The basic setup code declaring variables and functions used in the object.
  2. red -- The initialization functions which instruct Max/MSP how to create a new object.
  3. blue -- The interface behavior functions which instruct Max/MSP what to do when certain messages are given to the object.
  4. green -- Helper functions only used by the object and not necessary for communication to Max/MSP.
henon.c source code:

#include "ext.h"

typedef struct {
   t_object max_data;      
   double   y;           
   double   yy;           
   double   starty;       
   double   startyy;      
   double   alpha;        
   double   beta;         
   double   alphamin;     
   double   alphamax;     
   double   betamin;      
   double   betamax;      
   long     minnote;      
   long     maxnote;      
   void*    output;       
} MyObject;

void* object_data = NULL;

void*  create_object    (long alphainit, long betainit);
void   InputAlpha       (MyObject* mo, long value);
void   InputBeta        (MyObject* mo, long value);
void   InputBang        (MyObject* mo);
double henonmap         (double y, double yy, double alpha, double beta);
double scaleDataToRange (long value, double min, double max);
long   midilimit        (long value);

void main(void) {
   setup((t_messlist**)&object_data, (method)create_object, NULL, 
      sizeof(MyObject), NULL, A_DEFLONG, A_DEFLONG, A_NOTHING);
   addbang((method)InputBang);
   addinx ((method)InputAlpha, 2);
   addinx ((method)InputBeta,  1);
}

void* create_object(long alphainit, long betainit) {
   MyObject *mo;
   mo = (MyObject*)newobject(object_data);
   mo->starty     = 0.0;
   mo->startyy    = 0.0;
   mo->y          = mo->starty;
   mo->yy         = mo->startyy;
   mo->alphamin   =  -2.0;
   mo->alphamax   =  -1.0;
   mo->betamin    =  -0.25;
   mo->betamax    =   0.25;
   mo->alpha      =   scaleDataToRange(alphainit, mo->alphamin, mo->alphamax);
   mo->beta       =   scaleDataToRange(betainit, mo->betamin, mo->betamax);
   mo->minnote    =  21;
   mo->maxnote    = 109;
   mo->output     = intout(mo);
   intin(mo, 1);
   intin(mo, 2);
   return mo;
}

void InputAlpha(MyObject* mo, long value) {
   mo->alpha = scaleDataToRange(value, mo->alphamin, mo->alphamax);
   mo->y     = mo->starty;
   mo->yy    = mo->startyy;
}

void InputBeta(MyObject* mo, long value) {
   mo->beta = scaleDataToRange(value, mo->betamin, mo->betamax);
   mo->y    = mo->starty;
   mo->yy   = mo->startyy;
}

void InputBang(MyObject* mo) {
   double newy;
   long midinote;
   newy   = henonmap(mo->y, mo->yy, mo->alpha, mo->beta);
   mo->yy = mo->y;
   mo->y  = newy;
   midinote = midilimit((int)((newy+1.0)/2.0*127.0 + 0.5));
   if ((midinote > mo->minnote) && (midinote < mo->maxnote)) {
      outlet_int(mo->output, midinote);
   }
}

double henonmap(double y, double yy, double alpha, double beta) {
   return 1.0 + alpha * y * y + beta * yy;
}

long midilimit(long value) {
   if (value < 0)     return   0;
   if (value > 127)   return 127;
   return value;
}

double scaleDataToRange(long value, double min, double max) {
   return value/127.0 * (max - min) + min;
}

Parameter Space

The following picture visualizes the periodic behavior of the generated sequence. Each color represents a different number of notes into which the sequence settles down into (after 10,000 notes have been generated). Black regions either decay to one note, or they go off to infinity. White regions continue to generate non-repeating sequences, even after 10,000 notes. Other colors generate repetitive patterns of notes depending on legend on the right of the picture. For example, the color red indicates the sequence decays to a cycle of two notes.
    black = fixed point ending or goes out of bounds
    red = 2 point cycle
    orange = 3 point cycle
    yellow = 4 point cycle
    green = 5 point cycle
    blue = 6 point cycle
    indigo = 7-11 point cycle
    violet = 11-20 point cycle
    white = no repetition (through 10,000 iterations)


Further Reading


Exercises

Everyone must do exercise 1. Then, pick one of the other exercises to do as well. Do more than one other exercise for extra credit.
  1. Compile the henon.c program and run the object in Max. What is the terminal period (if any) for these values of input for alpha and beta:
    alphabeta
    10088
    5622
    26115
    116100
    1064