Jump to content

Mensuration Canon Generation Tool


TheMooseman

Recommended Posts

The idea would be to have a set of note length values and a set of pitches. These arrays could be of different lengths, let's say there are 3 note lengths and 5 pitches. The pitches would be placed on the note lengths but the lengths would go back to the beginning of the array before the pitches would. Does anyone have ideas about how to structure this?
Link to comment
Share on other sites

Here's a simple example that does what you are wanting. Should give you an idea how to go about it and you can expand it as needed. Let me know if you have any questions.

 

//
// Simple example to show loops through a given array of pitches
// and for each one sustains it with the length given by second array
// assumings to be monophonic

var pitches = [
  MIDI.noteNumber("C4"),
  MIDI.noteNumber("D4"),
  MIDI.noteNumber("E4")
]

// array of durations as fractions of a beat
var durations = [ 1, 0.5 ];



var pi = 0;
var di = 0;
var nextOn = 1;

var note = new NoteOn;
note.channel = 1;

var NeedsTimingInfo = true;

function ProcessMIDI() {

    var trans = GetTimingInfo();
    if(!trans.playing) {
        return;
    }
         
    if(nextOn >= trans.blockStartBeat 
            && nextOn <= trans.blockEndBeat) {
        sendNextNote();
    }     
}


function sendNextNote() {
   note.pitch = nextPitch();
   note.velocity = 100;
   note.beatPos = nextOn;
   note.send();
   
   var thisDuration = nextDuration();
   note.velocity = 0;    
   note.sendAfterBeats(thisDuration);
   
   nextOn = nextOn+thisDuration;
}

function nextPitch() {
   var out = pitches[pi];
   if(pi >= pitches.length-1) {
       pi=0;
   }
   else {
       pi++;
   }
   return out;
}

function nextDuration() {
   var out = durations[di];
   
   if(di >= durations.length-1) {
       di=0;
   }
   else {
       di++;
   }
   return out;
}

function Reset() {
   MIDI.allNotesOff();
   nextOn = 1;  // only handles starting from bar 1
   pi = 0;
   di = 0;
}
Link to comment
Share on other sites

If your brain didn't explode from the last one...here is another example that is based on a more OOP approach. OOP requires a bit more code to realize and if you don't know OOP you might not understand it...but the power of this is that its pretty easy to setup two completely independent canon's that play at the same time, for example.

 

//
// OOP example of Mensuration Canon
//

//================================
// Array Iterator class
//================================

var ArrayIterator = function(array) {
   this.idx = 0;
   this.range = array;
};

ArrayIterator.prototype.next = function() {
   var out = this.range[this.idx];
   if(this.idx >= this.range.length-1) {
       this.idx=0;
   }
   else {
       this.idx++;
   }
   return out;
};

ArrayIterator.prototype.reset = function() {
   this.idx = 0;
};

//================================
// Player class
//================================

var MPlayer = function(p,d) {
   this.nextOn = 1;
   this.started = false;
   this.note = new NoteOn;
   this.note.channel = 1;
   this.pitches = new ArrayIterator(p);    
   this.durations = new ArrayIterator(d);
};

MPlayer.prototype.reset = function() {
   this.nextOn = 1;
   this.started = false;
   this.pitches.reset();
   this.durations.reset();
}

MPlayer.prototype.playNext = function() {
    
    var trans = GetTimingInfo();
    if(!trans.playing) {
        this.started = false;
        return;
    }
    
    if(!this.started) {
        this.nextOn = trans.blockStartBeat;
        this.started = true;
    }
    
    if(this.nextOn >= trans.blockStartBeat 
            && this.nextOn <= trans.blockEndBeat) {

       this.note.pitch = this.pitches.next();
       this.note.velocity = 100;
       this.note.beatPos = this.nextOn;
       this.note.send();
   
       var currDuration = this.durations.next();
       this.note.velocity = 0;    
       this.note.sendAfterBeats(currDuration);
   
       this.nextOn = this.nextOn+currDuration;
   }
};

//============================================
// Create a new MPlayer, pass in array of 
// pitches and array of durations
//============================================

var player = new MPlayer([ MIDI.noteNumber("C4"),
                          MIDI.noteNumber("D4"),
                          MIDI.noteNumber("E4")],
                        [ 1, 0.5]);

var player2 = new MPlayer([ MIDI.noteNumber("F4"),
                          MIDI.noteNumber("G#4"),
                          MIDI.noteNumber("A4")],
                        [ 1, 0.5, .25, .75]);

//============================
// callback functions
//=============================

var NeedsTimingInfo = true;

function ProcessMIDI() {
    player.playNext();   
    player2.playNext();
}

function Reset() {
   player.reset();
}

 

Let me know if you have any questions about either two. The OOP example is a bit cleaner, it can be started from anywhere in the sequence, hit PLAY to hear it. The first simple example has to start at Bar 1, beat 1 when you hit play for it to work

Edited by Dewdman42
Link to comment
Share on other sites

The first one was just avoiding OOP, using some global variables instead. In general I have found with Scripter that often its not worth the extra code it takes to do things the OOP way. Sometimes it is. But its usually also confusing to most musicians who are barely hanging on to understand simple scripter tasks.

 

The important point, regardless of whether you want to write a simple script without OOP principles or with... is that in Scripter you have to maintain some global state of things, then the callback functions are called as Logic runs along and inside the call back function you look at those global variables to do what you need to do. For example, rather then having a simple FOR loop to go through the pitches array, each time the ProcessMIDI() callback function is called, you see if you have work to do and if so, then do it, then get out and wait for another callback, etc. Its an event driven engine, so you have to keep track of running state in some kind of global variables and then provide code in the callback functions which will get executed by Logic as it runs along.

 

 

Using OOP objects to keep track of that global state, requires more code in the single case, but as shown, it does make it a lot simpler and even less code if you end up reusing the same structure more than once...

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...