One for All

From Xenharmonic Wiki
Jump to navigation Jump to search

Score in sagittal: File:SS01 One for All.pdf

Premiere recording at Xenharmonic Praxis Summer Camp 2011, Hillsboro, WV: http://micro.soonlabel.com/0-praxis/audio/July/july_group_5a_one_for_all2.mp3

Second recording at XPSC 2011: https://youtu.be/S62yVU1pspQ?t=1257

Performance recording at Xenharmonic Praxis Summer Camp 2013, Salem, NH: https://jacobbarton.bandcamp.com/track/one-for-all

Multitrack recording by Jacob Barton, http://oneforall.ytmnd.com

Code tutorial (with assignment for xenharmonic campers) in the ChucK environment (copy and save the below text as a .ck file to use in ChucK):

/* Sagittal Songbook 01
    One for All
    by Ryan Stickney
   
   A four-part round in just intonation, in 11/4 time
   Coded by xenjacob Jan. 2020
   
   The implementation is basic, and shared in this way both to introduce
   some basics of ChucK and to illustrate the
   degree of 'grunt work' which may (not must) accompany any work of art.
   
   ASSIGNMENT FOR XENCAMPERS:
   1. Get this code to work on your setup! Save this text as a .ck file.
   If you are running miniAudicle, all you have to do then is
   'Start Virtual Machine' and then 'Add Shred'.
   2. Try modifying 1/1 pitch and/or tempo (read thru the comments below for how)
   3. Learn the song!
   4. Make a rough recording of yourself singing along to patch. (Change anything, have phun with it)
   5. (Optional) Think up a single alteration that would make this patch
   'more musical', and send it to xenjacob. No need to know how to code it yourself.
   
   The melody line is first encoded as a 2D array corresponding
   to pitch and rhythm values
   [[ pitch, rhythm ], ...]
   
   This kind of score entry is labor-intensive, but a rather good match to the
   Sagittal Songbook project due to the brevity of most of the songs.
   
   Pitches are in JI intervals in relation to the defined 1/1 (notated C)
   A decimal point in the fraction forces ChucK to do non-integer
   arithmetic to create a floating-point value (otherwise it will e.g. compute
   4/3 as 1 remainder 1 and discard the remainder)
   
   Rhythms are declared in relation to the value of a quarter note,
   assigned here to the value 1 (half note = 2, dotted half = 3, etc.)
   */
   
   [
   [ 1/1., 2 ], // All
   [ 3/2., 2 ], // things
   [ 2/1., 1 ], // wing-
   [ 7/4., 1], // -ed
   [ 3/2., 1 ], // are
   [ 4/3., 2 ], // made
   [ 5/4., 2 ], // for
   [ 3/2., 2 ], // jump-
   [ 2/1., 2 ], // -ing
   [ 5/2., 1 ], // in-
   [ 9/4., 1 ], // -to
   [ 2/1., 1 ], // the
   [ 7/4., 1 ], // sky__
   [ 7/4., 2 ], // (bend)
   [ 2/1., 5/3. ], // ____.
   // implementing a rhythmic breath or 'hiccup' at the end of each phrase
   // equal to two-thirds of a beat.
   
   [ 5/4., 2 ], // Fall-
   [ 5/4., 2 ], // -ing
   [ 4/3., 1 ], // brings
   [ 9/8., 1 ], // no
   [ 5/4., 1 ], // di-
   [ 2/1., 2 ], // -sas-
   [ 7/4., 2 ], // -ter
   [ 15/8., 2 ], // when
   [ 5/4., 2 ], // you
   [ 3/2., 3 ], // can
   [ 4/3., 1 ], // fly_
   [ 4/3., 2 ], // (bend)
   [ 5/4., 5/3. ], // __.
   
   [ 3/2., 2 ], // Calls
   [ 2/1., 2 ], // sing
   [ 3/2., 3 ], // birds
   [ 9/8., 2 ], // to
   [ 1/1., 2 ], // each
   [ 9/8., 1 ], // o-
   [ 5/4., 1 ], // -ther
   [ 11/8., 1 ], // but
   [ 3/2., 1], // who
   [ 2/1., 3 ], // knows
   [ 9/4., 1 ], // why?__
   [ 9/4., 2 ], // __(bend)
   [ 2/1., 5/3.], // ___
   
   [ 2/1., 4], // Fly-
   [ 7/4., 3 ], // -ing
   [ 4/3., 2 ], // looks
   [ 3/2., 2 ], // fun;
   [ 3/2., 4 ], // I'm
   [ 1/1., 1 ], // will-
   [ 9/8., 1 ], // -ing
   [ 5/4., 1 ], // to
   [ 1/1., 4.+2/3. ] // try.
   
   ] @=> float score[][]; // "@=>" is the assignment operator for arrays.
   
   // Since there are only a few glissandi in the score,
   // let's collect the index #'s of the notes which gliss
   // into an array "bends":
   
   [ 13, 26, 39 ] @=> int bends[];
   
   // one_one will define the 1/1 frequency in Hz
   // **CHANGE THIS** to try in different keys!
   221 => float one_one;
   // 261.6 is approximately concert C pitch
   // 240 tunes to electrical hum (in the Americas and Asia anyway)
   // B flat (233) has also been traditionally used in this round.
   
   // 'pulse' wil define the time duration of a quarter note
   // **CHANGE THIS** to try different tempi!
   556::ms => dur pulse;
   // the :: must separate the numerical value and the duration unit
   // (which can be samp, ms, second, minute, hour, day, or week)
   
   // PlayScore is a function which will read the score (any score specified
   // in the same manner, actually), note by note, into
   // a dedicated triangle wave oscillator.
   
   // But Wait!
   // Musicality Mod 1 (suggested by Thomas): the ability to play in other registers.
   // PlayScore will take an additional argument, specifying the number of 
   // octaves offset (-1 for octave down, 0 for at pitch, 1 for octave up, etc.
   fun void PlayScore( float play[][], int octave_shift )
   {
       TriOsc t => dac; // direct link to the digital audio converter (soundcard)
       0.25 => t.gain; // range [0,1] - let's leave room for 4 voices here
       // (digital distortion otherwise...which actually makes some really neat effects, try it!
       
       // loop iterates thru the play array, one note entry at a time
       for( 0 => int i; i < play.cap(); i++)
       {
           // read the array, change the frequency
           play[i][0] * one_one * Math.pow(2, octave_shift) => t.freq;
           
           // glissando code
           // hard-coded to 3 total glisses, because ChucK doesn't
           // seem to have any built-in array searching
           if( bends[0] == i || bends[1] == i || bends[2] == i )
           {
               // divides the length of the rhythm of that note
               // into 366 parts, because, hey, leap year!
               366 => float rhdiv;
               for( 0 => int j; j <= rhdiv; j++)
               {
                   // one_one * play[i][0] *
                   //  Math.pow( (play[i+1][0]/play[i][0]) , j/rhdiv ) => t.freq;
                   // Above code divides the glissando down by equal (logarithmic) steps,
                   // Doesn't sound 'natural' to me,
                   // Let's try equal-frequency steps:
                   ((play[i+1][0] - play[i][0])*one_one/rhdiv*j + play[i][0]*one_one) * Math.pow(2, octave_shift) => t.freq;
                   pulse*play[i][1]/rhdiv => now;
                   // I still wasn't happy with it,
                   // but hey, "Publish or die." -- Rob Scott
                }
           }
           // this code is executed in non-glissando case
           else {
               // chucking a duration to now advances time
               // by that amount within the shred
               pulse*play[i][1] => now;
           }
       }
   }
   
   PlayScore( score, 0); // Calling the function once will play thru the melody once.
   // See if you can add multiple shreds of this at just the right time to play in round!
   // Try alternating lower and higher octaves!
   /*
   // remove lines 169 and 205, AND comment/remove line 165, to unlock...
   // CHAPTER TWO - Programmed Polyphony
   // a function to hang out between successive entries
   fun void waitLine() {
       (22.+2/3.)*pulse => now;
   }
   // to execute functions 'in series', simply call them, as 
   // to execute 'in parallel', use 'spork~' to create breakoff shreds.
   // 'spork a shred' > 'fork a thread', good one Ge Wang...
   
   // the below code initiates five separate shreds
   // to simulate five round entries
   
   spork~ PlayScore ( score, 0 ); // first entry
   waitLine();
   spork~ PlayScore ( score, 1 );
   waitLine();
   spork~ PlayScore ( score, 0 );
   waitLine();
   spork~ PlayScore ( score, 1 );
   waitLine();
   // now check this out
   // for the last entry, we gonna do a 'Meno Mosso' (slower tempo)
   // we change ONE VARIABLE, and all four lines follow!
   // if we had passed the pulse as an argument to each function instead of
   // using this sort of global variable, this wouldn't work.
   1.1 *=> pulse; // multiply value of pulse by 1.1 and re-assign
   spork~ PlayScore ( score, 0 ); // final entry
   // now, let the round play out;
   // if the main shred of this script terminated early,
   // it would automatically terminate any child shreds still running
   waitLine();
   waitLine();
   waitLine();
   waitLine();
   /*