Difference between revisions of "SLOrk/Instruments/DrumCircle"

From CCRMA Wiki
Jump to: navigation, search
 
(Running)
 
(12 intermediate revisions by the same user not shown)
Line 1: Line 1:
 
== Description ==
 
== Description ==
  
This instrument play intervals. The tilting of the laptop controls the intensity of each note (tilting front or back play the root note and tilting to the sides play the "interval" note). The sound is played trough a LeSLOrklie speaker (it rotates around the Hemi at an adjustable spin frequency).
+
Drum Circle (DC) plays on the idea that the computer can act as a drum (make sound when hit) and takes advantage of the onboard sms sensor.  When running DC, a graphical interface in Open GL displays a pattern of different size balls located around a circle.  There is a clock hand that points to a given ball, and when hit, a note is played. At this point, the clock hand advances to the next ball in counter-clockwise motion. A melody or bass line can be played by continuously hitting the computer to trigger notes in sequence.
  
 
== Usage ==
 
== Usage ==
 +
DC can be used for a variety of different sounds and purposes.  The sound generation is detached from the graphics interface making sound generation modification easy.  The first two performances of DC included a polyphonic sample playback sound as well as a polyphonic blow bottle synthesizer in Chuck.  Currently, the blow bottle patch is set up in svn.
 +
  
 
=== Installation ===
 
=== Installation ===
* Copy the file [[#ChucK Code|intervalis.ck]] to any folder you like
+
* Copy the folder dc to any directory you like (can be found in [slork/users/njb/])
 
* Done!
 
* Done!
  
 
=== Running ===
 
=== Running ===
* Using a terminal go to the folder were "intervalis.ck" is located
 
* On the terminal window type: ''(replace N with the number of output channels of the audio card)'' <pre>chuck -cN intervalis</pre>
 
* Play!
 
  
=== Execution ===
+
To run DC, two chuck patches and a separate OpenGL program must be running simultaneously.
 +
The two separate Chuck files can be run individually (sms.ck, polyBlow.ck) or by a separate chuck file that adds the other two (dc.ck). For simplicity, the instructions below do as such.
  
# Select the root note by using 2nd and 3rd rows of the keyboard (from Tab & CAPS to ] & RETURN). See [[#Root note selection|root note selection]] for details
 
# Select interval using the numbers (1 to 8)
 
# Play notes by tilting the laptop
 
# Optional:
 
#* adjust timbre ([[#Other control keys|detail]])
 
#* adjust spin frequency ([[#Other control keys|detail]])
 
#* change register ([[#Other control keys|detail]])
 
#* detune the instrument ([[#Other control keys|detail]])
 
#* mute one or both notes ([[#Mute keys|detail]])
 
  
==== Key mapping ====
+
* Start miniAudicle and open dc.ck
 +
* Make sure the file path preferences are set to the dc folder
 +
* Start the virtual machine and add the shred
 +
* Open Terminal
 +
* Navigate to the dc folder
 +
* Type: ./dc
  
===== Root note selection =====
+
=== Execution ===
  
{|style="color:green; background-color:#eeeeee;" cellpadding="5" cellspacing="0" border="1"
+
Once Chuck and the Drum Cirlce graphics program is running, simply hit the computer and play through the note sequence. See key mapping for more advanced control.
|F4#||G4||G4#||A4|| A4#|| B4|| C5|| C5#|| D5|| D5#|| E5|| F5|| F5#|| G5|| G5#|| A5|| A5#|| B5|| C6|| C6#|| D6|| D6#|| E6
+
|-
+
|TAB||CAPS||Q||A||W ||S ||D ||R ||F ||T ||G ||H ||U ||J ||I ||K ||O ||L ||; ||[ ||' ||] ||RETURN |
+
|}
+
  
===== Interval selection =====
 
Number keys from 1 to 8
 
  
===== Interval quality selection =====
+
==== Key mapping ====
* M (Major/Perfect)
+
* N (Minor/Tritone)
+
''(no diminished or augmented)''
+
  
===== Other control keys =====
+
All control is managed through the graphics interface with the following controls:
 +
*s -full screen
 +
*e -rotate pattern up
 +
*r -rotate pattern down
 +
*> -adds a circle
 +
*< -subtracts a circle
 +
*[ -rotate graphics back
 +
*] -rotate graphics forward
 +
*+ -move graphics forward
 +
*- -move graphics backward
 +
*q -quits
  
{|style="color:green; background-color:#eeeeee;" cellpadding="5" cellspacing="0" border="1"
+
To play more interesting melodies and use the r or e keys while striking the computer to exactly control the order of the note sequence
!Parameter!!Decrease!!Increase
+
|-
+
|Register||,||.
+
|-
+
|Detune||9||0
+
|-
+
|Spin frequency||Arrow Left||Arrow Right
+
|-
+
|Number of harmonics (timber)||Arrow Down||Arrow Up
+
|}
+
 
+
===== Mute keys =====
+
 
+
{|style="color:green; background-color:#eeeeee;" cellpadding="5" cellspacing="0" border="1"
+
!Key!!Mutes ...
+
|-
+
|SPACE BAR|| Both notes
+
|-
+
|Left SHIFT|| Root note
+
|-
+
|Right SHIFT|| Interval note
+
|}
+
  
 
== Advanced users ==
 
== Advanced users ==
 
+
*The main sound generation can be found at: polyBlow.ck
=== ChucK Code ===
+
*The main graphics file can be found at: dc.cpp
 
+
**The note pattern is a global array at the top
If you just want to look at the code, here it is:
+
 
+
<pre>
+
// filename: intervalis.ck
+
// INTERVALIS - Play intervals
+
//
+
// MUSIC 128 - HW1
+
// By: Jorge Herrera
+
//
+
 
+
/************************************
+
* MAIN CONTROL PARAMETERS
+
*************************************/
+
10 => int _init_harmonics; // Control over the intial timbre
+
1 => float _init_spin_freq; // LeSLOrklie initial speed [Hz]
+
2 => int numChan; // LeSLOrklie is implemented over this many channels
+
440 => float _a440; // frequency associated to the A key
+
0 => int _init_quality;          // Initial quality of the interval (0 -> Major, 1 -> minor)
+
5 => int _init_interval;        // Initial interval
+
0.15 => float _tilt_threshold;  // [0 - 1): threshold for silence (mute) level
+
 
+
 
+
 
+
/************************************
+
* THE CODE
+
*************************************/
+
 
+
// safety checks
+
if(_tilt_threshold >= 1) 0.999 => _tilt_threshold;
+
 
+
 
+
// The synthesis
+
Blit tonic => PitShift choir => JCRev rev => PoleZero mainOut;
+
Blit interval => choir;
+
mainOut.blockZero(0.99);
+
 
+
 
+
// Synth. initialization
+
_a440 => tonic.freq;
+
_init_harmonics => tonic.harmonics => interval.harmonics;
+
0 => tonic.gain => interval.gain;
+
0.12 => rev.mix;
+
1.0 => choir.shift;
+
0.2 => choir.mix;
+
 
+
 
+
// Interval definition array:
+
int intervals[2][8];
+
 
+
// Major&Perfect intervals (defined in semitones)
+
0 => intervals[0][0];
+
2 => intervals[0][1];
+
4 => intervals[0][2];
+
5 => intervals[0][3];
+
7 => intervals[0][4];
+
9 => intervals[0][5];
+
11 => intervals[0][6];
+
12 => intervals[0][7];
+
 
+
// Minor&Tritone intervals (defined in semitones)
+
0 => intervals[1][0];
+
1 => intervals[1][1];
+
3 => intervals[1][2];
+
5 => intervals[1][3];
+
6 => intervals[1][4]; /* m5 == tritone */
+
8 => intervals[1][5];
+
10 => intervals[1][6];
+
12 => intervals[1][7];
+
 
+
// Interval initialization
+
0 => float tonicTarget;
+
_init_quality => int gQuality; /* 0 = major, 1 = minor */
+
_init_interval - 1 => int gInterval => float intervalTarget;
+
updateInterval(gQuality, gInterval);
+
 
+
// Flags to determine if the sounds are muted
+
0 => int tonicMuted => int intervalMuted;
+
 
+
 
+
/**********************
+
*      FUNCTIONS
+
*
+
* HANDLE WITH CARE!!!
+
***********************
+
 
+
/**************************
+
* The "LeSLOrklie"
+
**************************/
+
_init_spin_freq => float spinFreq;
+
Gain outGains[numChan];
+
 
+
fun void spin() {
+
    for(0 => int i; i<numChan; i++)
+
    {
+
        mainOut => outGains[i] => dac.chan(i);
+
    }
+
    0 => float angle;
+
    0.001 => float step;      // must be in seconds
+
    while(step::second => now)
+
    {
+
        2.0*Math.PI*spinFreq*step +=> angle;
+
        for(0 => int i; i<numChan; i++) {
+
            (Math.sin(angle + (2*Math.PI*i)$float/numChan) +
+
            1.0)/2.0 => outGains[i].gain;
+
        }
+
    }   
+
}
+
 
+
 
+
/**************************
+
* GET MOTION INPUT
+
**************************/
+
 
+
// infinite while loop
+
fun void getMotion()
+
{
+
    // instantiate a HidIn object
+
    Hid accel;
+
    HidMsg movementMsg;
+
   
+
    // open tilt sensor
+
    if( !accel.openTiltSensor() ) {
+
        <<< "", "tilt sensor unavailable", "" >>>;
+
        me.exit();
+
    }
+
   
+
    // print
+
    <<< "", "tilt sensor ready", "" >>>;
+
   
+
    while( true )
+
    {
+
        // poll the tilt sensor, expect to get back 3 element array of
+
        // (9 for now means accelerometer, 0 selects 0th accelerometer)
+
        accel.read( 9, 0, movementMsg );
+
        if( !tonicMuted ) {
+
            //<<< movementMsg.x, movementMsg.y, movementMsg.z >>>;
+
            if(Math.fabs(movementMsg.y$float/300.0) < _tilt_threshold) 0 => tonicTarget;
+
        else (Math.fabs(movementMsg.y$float/300.0) - _tilt_threshold)/(1 - _tilt_threshold) => tonicTarget;
+
}
+
 
+
if( !intervalMuted ) {
+
    if(Math.fabs(movementMsg.x$float/300.0) < _tilt_threshold) 0 => intervalTarget;
+
else (Math.fabs(movementMsg.x$float/300.0) - _tilt_threshold)/(1 - _tilt_threshold) => intervalTarget;
+
}
+
// advance time
+
50::ms => now;
+
}
+
}
+
 
+
// Smooth gain changes
+
fun void rampGains()
+
{
+
    .5 => float slew;
+
    while(20::ms => now) {
+
        (tonicTarget - tonic.gain())*slew + tonic.gain() => tonic.gain;
+
        (intervalTarget - interval.gain())*slew + interval.gain() =>
+
        interval.gain;
+
    }
+
}
+
 
+
 
+
/**************************
+
* GET KEYBOARD INPUT
+
**************************/
+
 
+
// infinite event loop
+
fun void getKeys()
+
{   
+
    Hid hiKbd;
+
    HidMsg msgKbd;
+
   
+
    // which keyboard
+
    0 => int device;
+
    // get from command line
+
    if( me.args() ) me.arg(0) => Std.atoi => device;
+
   
+
    // open keyboard (get device number from command line)
+
    if( !hiKbd.openKeyboard( device ) ) me.exit();
+
    <<< "", "keyboard '" + hiKbd.name() + "' ready", "" >>>;
+
   
+
    0 => int gRegister;
+
   
+
    while( true )
+
    {
+
        // wait on event
+
        hiKbd => now;
+
        // get one or more messages
+
        while( hiKbd.recv( msgKbd ) )
+
        {
+
            // check for action type
+
            if( msgKbd.isButtonDown() )
+
            {
+
                //<<< "", "down:", msgKbd.which, "(code)", msgKbd.key, "(usb key)", msgKbd.ascii, "(ascii)" >>>;
+
                //<<< "", "down:", msgKbd.which >>>;
+
               
+
                // Effect keys
+
               
+
                // Spin frequency control
+
                if(msgKbd.which == 79) {
+
                    0.2 +=> spinFreq;
+
                }
+
                if(msgKbd.which == 80 && spinFreq > 0.0000000001) {
+
                    0.2 -=> spinFreq;
+
                }
+
               
+
                // Harmonics control
+
                if(msgKbd.which == 81 && tonic.harmonics() > 0) {
+
                    tonic.harmonics() - 1 => tonic.harmonics;
+
                    interval.harmonics() - 1 => interval.harmonics;
+
                    if(tonic.harmonics() < 5) {
+
                        <<< "***** ", tonic.harmonics(), " harmonics  *****" >>>;
+
                    }
+
                }
+
                if(msgKbd.which == 82) {
+
                    tonic.harmonics() + 1 => tonic.harmonics;
+
                    interval.harmonics() + 1 => interval.harmonics;
+
                    if(tonic.harmonics() < 5) {
+
                        <<< "***** ", tonic.harmonics(), " harmonics  *****" >>>;
+
                    }
+
                }
+
               
+
                // PitShift
+
                if(msgKbd.which == 38 && choir.shift() > 0.8) {
+
                    choir.shift() - 0.01 => choir.shift;
+
                    if(choir.shift() == 1.0)
+
                        <<< "***** IN TUNE  *****" >>>;
+
                }
+
                if(msgKbd.which == 39 && choir.shift() < 1.2) {
+
                    choir.shift() + 0.01 => choir.shift;
+
                    if(choir.shift() == 1.0)
+
                        <<< "***** IN TUNE  *****" >>>;
+
                }
+
               
+
                // Interval selection
+
                if(msgKbd.which >= 30 && msgKbd.which <= 37) {
+
                    msgKbd.which - 30 => gInterval;
+
                }
+
               
+
                // Octave change
+
                // ","
+
                if(msgKbd.which == 54) {
+
                    2 /=> _a440;
+
                    tonic.freq()/2 => tonic.freq;
+
                    gRegister - 1 => gRegister;
+
                    <<< "Register: ", gRegister >>>;
+
                }
+
                // "."
+
                if(msgKbd.which == 55) {
+
                    2 *=> _a440;
+
                    tonic.freq()*2 => tonic.freq;
+
                    gRegister + 1 => gRegister;
+
                    <<< "Register: ", gRegister >>>;
+
                }
+
               
+
               
+
                // Base pitch selection
+
                if(msgKbd.which == 43) { // tab = F#
+
                    _a440*Math.pow(2,-3/12.0) => tonic.freq;
+
                }
+
                if(msgKbd.which == 57) { // CAPS = G
+
                    _a440*Math.pow(2,-2/12.0) => tonic.freq;
+
                }
+
                if(msgKbd.which == 20) { // Q = G#
+
                    _a440*Math.pow(2,-1/12.0) => tonic.freq;
+
                }
+
                if(msgKbd.which == 4) {
+
                    _a440*Math.pow(2,0/12.0) => tonic.freq;
+
                }
+
                if(msgKbd.which == 26) {
+
                    _a440*Math.pow(2,1/12.0) => tonic.freq;
+
                }
+
                if(msgKbd.which == 22) {
+
                    _a440*Math.pow(2,2/12.0) => tonic.freq;
+
                }
+
                if(msgKbd.which == 7) {
+
                    _a440*Math.pow(2,3/12.0) => tonic.freq;
+
                }
+
                if(msgKbd.which == 21) {
+
                    _a440*Math.pow(2,4/12.0) => tonic.freq;
+
                }
+
                if(msgKbd.which == 9) {
+
                    _a440*Math.pow(2,5/12.0) => tonic.freq;
+
                }
+
                if(msgKbd.which == 23) {
+
                    _a440*Math.pow(2,6/12.0) => tonic.freq;
+
                }
+
                if(msgKbd.which == 10) {
+
                    _a440*Math.pow(2,7/12.0) => tonic.freq;
+
                }
+
                if(msgKbd.which == 11) {
+
                    _a440*Math.pow(2,8/12.0) => tonic.freq;
+
                }
+
                if(msgKbd.which == 24) {
+
                    _a440*Math.pow(2,9/12.0) => tonic.freq;
+
                }
+
                if(msgKbd.which == 13) {
+
                    _a440*Math.pow(2,10/12.0) => tonic.freq;
+
                }
+
                if(msgKbd.which == 12) {
+
                    _a440*Math.pow(2,11/12.0) => tonic.freq;
+
                }
+
                if(msgKbd.which == 14) {
+
                    _a440*Math.pow(2,12/12.0) => tonic.freq;
+
                }
+
                if(msgKbd.which == 18) {
+
                    _a440*Math.pow(2,13/12.0) => tonic.freq;
+
                }
+
                if(msgKbd.which == 15) {
+
                    _a440*Math.pow(2,14/12.0) => tonic.freq;
+
                }
+
                if(msgKbd.which == 51) {
+
                    _a440*Math.pow(2,15/12.0) => tonic.freq;
+
                }
+
                if(msgKbd.which == 47) { // "[" = C#
+
                    _a440*Math.pow(2,16/12.0) => tonic.freq;
+
                }
+
                if(msgKbd.which == 52) { // "'" = D
+
                    _a440*Math.pow(2,17/12.0) => tonic.freq;
+
                }
+
                if(msgKbd.which == 48) { // "]" = D#
+
                    _a440*Math.pow(2,18/12.0) => tonic.freq;
+
                }
+
                if(msgKbd.which == 40) { // RETURN = E
+
                    _a440*Math.pow(2,19/12.0) => tonic.freq;
+
                }
+
               
+
                // Major(perfect) || minor(tritone) gQuality selection
+
                if(msgKbd.which == 16) {
+
                    0 => gQuality;
+
                    <<< "", "Major mode" >>>;
+
                }
+
                if(msgKbd.which == 17) {
+
                    1 => gQuality;
+
                    <<< "", "Minor mode" >>>;
+
                }
+
               
+
                updateInterval(gQuality, gInterval);
+
               
+
               
+
                // Mute controls
+
               
+
                // Space bar mute both sounds
+
                if(msgKbd.which == 44) {
+
                    1 => tonicMuted => intervalMuted;
+
                    0 => tonicTarget => intervalTarget;
+
                }
+
               
+
                // Left shift mute root (tonic) note
+
                if(msgKbd.which == 225) {
+
                    1 => tonicMuted;
+
                    0 => tonicTarget;
+
                }
+
               
+
                // Right shift mute the interval note
+
                if(msgKbd.which == 229) {
+
                    1 => intervalMuted;
+
                    0 => intervalTarget;
+
                }
+
               
+
            }
+
           
+
            // check for action type
+
            if( msgKbd.isButtonUp() )
+
            {
+
                // Mute controls
+
               
+
                // Space bar mute both sounds
+
                if(msgKbd.which == 44) {
+
                    0 => tonicMuted => intervalMuted;
+
                }
+
               
+
                // Left shift mute root (tonic) note
+
                if(msgKbd.which == 225) {
+
                    0 => tonicMuted;
+
                }
+
               
+
                // Right shift mute the interval note
+
                if(msgKbd.which == 229) {
+
                    0 => intervalMuted;
+
                }
+
            }
+
        }
+
    }
+
}
+
 
+
fun void updateInterval(int m, int i)
+
{
+
    Math.pow(2,intervals[m][i]/12.0)*tonic.freq() => interval.freq;
+
}
+
 
+
 
+
/**************************
+
* GET OSC MESSAGES
+
**************************/
+
 
+
// infinite event loop
+
fun void receiveOSC()
+
{
+
    // create our OSC receiver
+
    OscRecv recv;
+
    // use port 6449 (or whatever)
+
    6449 => recv.port;
+
    // start listening (launch thread)
+
    recv.listen();
+
   
+
    // create an address in the receiver, store in new variable
+
    recv.event( "/message, s i s s" ) @=> OscEvent @ oe;
+
   
+
    while( true )
+
    {
+
        // wait for event to arrive
+
        oe => now;
+
       
+
        // grab the next message from the queue.
+
        while( oe.nextMsg() )
+
        {
+
            string _base;
+
            int _interval;
+
            string _quality;
+
            string _tilt;
+
           
+
            // getFloat fetches the expected float (as indicated by "i f")
+
            oe.getString() => _base;
+
            oe.getInt() => _interval;
+
            oe.getString() => _quality;
+
            oe.getString() => _tilt;
+
           
+
            // print
+
            <<< "", "" >>>;
+
           
+
            <<< "*********** NEW MESSAGE RECEIVED ************", ""            >>>;
+
            <<< "Root key", _base >>>;
+
            <<< "Interval", _interval >>>;
+
            <<< "Quality", _quality >>>;
+
            <<< "Tilt", _interval >>>;
+
        }
+
    }
+
}
+
 
+
 
+
fun void printInstructions()
+
{
+
    <<< "", "" >>>;
+
    <<< "", "" >>>;
+
    <<< "", "This instrument play intervals, where the gain relationship" >>>;
+
    <<< "", "between the 2 playing pitches is controlled by the performer"  >>>;
+
    <<< "", "using the tilt sensor." >>>;
+
    <<< "", "" >>>;
+
    <<< "", "There is separate control over the pitch, using the keyboard." >>>;
+
    <<< "", "" >>>;
+
    <<< "", "- Use keys [ A - L ] as a piano keyboard starting at A (pitch) = A (key)" >>>;
+
    <<< "", "- Use numbers to set the interval (1: Prime, 2: Second, ..., 8: Octave)" >>>;
+
    <<< "", "- Use keys M (major/perfect) & N (minor/tritone) to control the quality of the interval" >>>;
+
    <<< "", "- Use UP/DOWN arrow keys to change the number of harmonics of the sound"  >>>;
+
    <<< "", "- Use LEFT/RIGHT arrow keys to change speed of rotation of the 'LeSLOrklie'" >>>;
+
    <<< "", "- To change octave use ',' (down) or '.' (up)" >>>;
+
    <<< "", "- '9' & '0' play with de-tuning" >>>;
+
    <<< "", "- Use 'space bar' or 'shift keys' to mute the sounds" >>>;
+
}
+
 
+
 
+
 
+
// Run the threads
+
spork ~ getMotion(); // Get motion input
+
spork ~ getKeys(); // Get keyboard input
+
spork ~ spin(); // Make the sound spee
+
spork ~ rampGains(); // Make the gain ratio change smooth
+
spork ~ receiveOSC();      // Listen to OSC messages
+
 
+
printInstructions();
+
 
+
 
+
// The infinite loop
+
while(true)
+
{
+
    100::ms => now;
+
}
+
 
+
</pre>
+

Latest revision as of 15:59, 13 November 2011

Description

Drum Circle (DC) plays on the idea that the computer can act as a drum (make sound when hit) and takes advantage of the onboard sms sensor. When running DC, a graphical interface in Open GL displays a pattern of different size balls located around a circle. There is a clock hand that points to a given ball, and when hit, a note is played. At this point, the clock hand advances to the next ball in counter-clockwise motion. A melody or bass line can be played by continuously hitting the computer to trigger notes in sequence.

Usage

DC can be used for a variety of different sounds and purposes. The sound generation is detached from the graphics interface making sound generation modification easy. The first two performances of DC included a polyphonic sample playback sound as well as a polyphonic blow bottle synthesizer in Chuck. Currently, the blow bottle patch is set up in svn.


Installation

  • Copy the folder dc to any directory you like (can be found in [slork/users/njb/])
  • Done!

Running

To run DC, two chuck patches and a separate OpenGL program must be running simultaneously. The two separate Chuck files can be run individually (sms.ck, polyBlow.ck) or by a separate chuck file that adds the other two (dc.ck). For simplicity, the instructions below do as such.


  • Start miniAudicle and open dc.ck
  • Make sure the file path preferences are set to the dc folder
  • Start the virtual machine and add the shred
  • Open Terminal
  • Navigate to the dc folder
  • Type: ./dc

Execution

Once Chuck and the Drum Cirlce graphics program is running, simply hit the computer and play through the note sequence. See key mapping for more advanced control.


Key mapping

All control is managed through the graphics interface with the following controls:

  • s -full screen
  • e -rotate pattern up
  • r -rotate pattern down
  • > -adds a circle
  • < -subtracts a circle
  • [ -rotate graphics back
  • ] -rotate graphics forward
  • + -move graphics forward
  • - -move graphics backward
  • q -quits

To play more interesting melodies and use the r or e keys while striking the computer to exactly control the order of the note sequence

Advanced users

  • The main sound generation can be found at: polyBlow.ck
  • The main graphics file can be found at: dc.cpp
    • The note pattern is a global array at the top