I am trying to use it for sampled sound, but do not understand the code.
// The Digital Blackboard
// By Fred Reiss
//
// Created: April 29, 2000
// Last modified: May 3, 2000
/**** DESCRIPTION:
With the advent of the computer age and PowerPoint presentations, schools and universities
have seen a precipitous decline in the use of blackboards as instructional tools. The
Digital Blackboard is an attempt to preserve one of the more important artistic functions
of blackboards -- namely the ability to run one's fingernails across them -- as classrooms
move into the twenty-first century.
The Digital Blackboard creates a Virtual Sonic Blackboard Slate underneath the mouse cursor.
Moving the cursor downward causes a set of Virtual Fingernails to run across this surface. As
the Virtual Nails move, the surface of the Blackboard causes rubs against them, causing them to
resonate like violin strings. Of course, the Nails do not quite resonate in quite as narrow a
band of frequencies as violin strings, nor do they stay at the same frequency, as the pressure
of the fingers is continuously varying. When the Virtual Fingernails are moving at the wrong
speed or direction, one will only hear the sound of the edges of the nails "swishing" against
the virtual chalk dust on the Chalkboard surface.
*****/
/***** USES:
The Digital Blackboard has many practical uses, including:
* Inciting anger
* Corporal punishment
* Sadism
*****/
/***** INSTALLATION INSTRUCTIONS:
The Virtual Blackboard requires the Maustrak class to measure the speed of the
mouse. To install this class, copy the section of this file marked "MAUSTRAK CLASS"
into a new file, named "Maustrak.sc", inside your SuperCollider library directory.
Then recompile the SuperCollider library, and select and run the code at the
bottom of this file.
Sorry for the inconvenience.
*****/
/***** VARIABLES
All variables are documented when they are declared.
*****/
/***** MAUSTRAK CLASS
Copy the following code to a file called "Maustrak.sc" in the library directory, and
recompile the library:
// CLASS Maustrak
//
// By Fred Reiss
//
// Created: April 30, 2000
// Last modified: May 3, 2000
//
// A class to track mouse movement. Performs quasilinear interpolation and measures the
// speed of mouse movement.
Maustrak {
// INSTANCE VARIABLES
var
// Objects for grabbing the mouse position.
mausX, mausY,
// Arrays of the last few mouse positios, used for smoothing.
mausPosArrX, mausPosArrY,
// Our current position in the arrays.
mausPosPos = 0,
// How many times we've waited around not sampling.
numWait = 0,
// The current average value of the array.
< mausAvgX = 0.0, < mausAvgY = 0.0,
// The previous average value of the array.
mausPrevAvgX = 0.0, mausPrevAvgY = 0.0,
// The distance the mouse just moved; X, Y, and total.
< lastMausDeltaX = 0, < lastMausDeltaY = 0,
< lastMausDelta = 0,
// The size of our arrays
mausPosSize = 30,
// The number of times we wait before taking a sample.
mausWait = 10,
// KLUDGE ALERT!!! We really should figure out how to initialize our
// variables in a new() method. Until then, we check this flag and call
// init() as necessary.
amInitialized = 0
;
// METHODS
// An ar method to make this class look like a normal UGen
ar {
^lastMausDelta
}
// Initializer.
// TODO: Figure out how to overload the *new method and still access our local
// variables...
init {
mausPosPos = 1;
mausPosArrX = FloatArray.newClear( mausPosSize );
mausPosArrY = FloatArray.newClear( mausPosSize );
mausX = MouseX.new( 0, 1, 'linear' );
mausY = MouseY.new( 0, 1, 'linear' );
amInitialized = 1;
// Initialize the array of mouse positions and the average
// position.
mausPosSize.do( { this.reallySamplePos; } );
mausPrevAvgX = mausPosArrX.sum / mausPosSize;
mausPrevAvgY = mausPosArrY.sum / mausPosSize;
^this;
}
// Make sure we're initialized.
checkInit {
if ( amInitialized == 0, { this.init }, { } );
}
// Methods to get the raw position of the mouse.
xpos {
var ret; // Will be returned.
this.checkInit;
ret = mausX.value;
^ret
}
ypos {
var ret; // Will be returned.
this.checkInit;
ret = mausY.value;
^ret
}
// Takes a sample of the current mouse position and enters this
// position into our arrays. Calculates the smoothed position and
// smoothed rate of change of position.
samplePos {
if ( numWait <= 0,
{ numWait = mausWait; this.reallySamplePos },
{ numWait = numWait - 1 } );
}
// The workhorse behind the samplePos method.
reallySamplePos {
// Make sure this instance is initialized.
this.checkInit;
// Remember the original average positions.
mausPrevAvgX = mausAvgX;
mausPrevAvgY = mausAvgY;
mausPosArrX = mausPosArrX.basicWrapPut( mausPosPos, this.xpos );
mausPosArrY = mausPosArrY.basicWrapPut( mausPosPos, this.ypos );
// Increment our position in the arrays.
mausPosPos = (mausPosPos + 1) % mausPosSize;
// Calculate the smoothed position of the mouse.
this.avgPos;
// Calculate the rate of movement of the mouse.
this.mausDelta;
// No return value.
}
// Calculates the smoothed position of the mouse. Should be called
// by samplePos.
avgPos {
// Calculate average X and Y coords.
mausAvgX = mausPosArrX.sum / mausPosSize;
mausAvgY = mausPosArrY.sum / mausPosSize;
// No return value.
}
// Calculates how much the mouse has moved. Should be called by samplePos.
mausDelta {
// Calculate how much this position has changed.
lastMausDeltaX = mausAvgX - mausPrevAvgX;
lastMausDeltaY = mausAvgY - mausPrevAvgY;
// Apply the Pythagorean Theorem.
lastMausDelta = sqrt( squared(lastMausDeltaX) + squared(lastMausDeltaY) );
// No return value.
}
}
END MAUSTRAK CLASS *****/
/**/
var
// CLASSES
mt, // Instance of a class to track mouse movement.
// FUNCTIONS
takeSamp, // Takes a sample of mouse position and returns 0.
gaussian, // Returns a point along a Gaussian distribution
// COMPONENTS OF THE SCRATCHING SOUND
swish , // A swishing sound.
nail, // The (general) sound of a fingernail resonating as it rubs the blackboard.
index, // The sound of the index finger
middle, // The sound of the middle finger
ring, // The sound of the ring finger
pinkie, // You get the point.
mixed // The four fingers, mixed together.
// VARIABLES
// We don't need no steenkin' variables!
;
// Instantiate the class that will track the mouse for us.
mt = Maustrak.new;
// FUNCTION takeSamp
// Causes our Maustrak object to record the current mouse position and calculate
// the speed of the mouse.
takeSamp = { 0 * Plug.kr({ mt.samplePos } ) };
// FUNCTION gaussian
// Returns a point along a gaussian distribution.
gaussian = {
arg sigma, center, x;
// Normal distribution formula lifted from Barr and Zehna's
// _Probability: Modeling Uncertainty_.
reciprocal( sqrt( 2 * pi * squared( sigma ) ) ) *
exp( (-0.5 * squared( sigma )) * squared( x - center ) )
};
// FUNCTION swish
// Generates the swishing sound of fingernails rubbing against the blackboard
// but not resonating. This sound is generated by multiplying a sine wave by
// some pink noise.
swish = {
var sinFreq, sinAmp, pinkAmp;
// Frequency and amplitude of sine wave component.
sinFreq = Plug.kr( { mt.lastMausDelta * 5e4 } );
sinAmp = Plug.kr( { mt.lastMausDelta * 50 } );
// Amplitude of Pink noise component.
pinkAmp = Plug.kr( { mt.lastMausDelta * 50 } );
// SinOsc.ar( sinFreq, 0, sinAmp ) * PinkNoise.ar( pinkAmp )
SinOsc.ar( sinFreq, 0, 0.8 ) * PinkNoise.ar( pinkAmp )
};
// FUNCTION nail
// Simulates the movement of a single nail across the blackboard.
// We assume that the frequency of vibration is a monotonic increasing function of the
// speed of movement, and that the nail has a single resonant frequency.
// The frequency response of the nail is a Gaussian distribution around its
// resonance frequency.
nail = {
arg
freqMult = 200, // Multiplier for the frequency distribution
freqAdd = 8, // What is added to the log in the frequency distribution
resFreq = 500, // The resonant frequency
ampMult = 0.2, // The amount by which the amplitude (from the Gaussian) is
// multiplied.
ampSigma = 0.01 // The standard deviation of the amplitude distribution.
;
var freq, amp, kludge, threshold = 0.001;
freq = Plug.kr( {
// Only downward movement produces scratching!
// N.B.: NATURAL log!
if ( (freqMult * (log( mt.lastMausDeltaY ) + freqAdd)) > 0,
{
(freqMult * (log( mt.lastMausDeltaY ) + freqAdd))
},
{
0
} )
} );
// Make no sound if the mouse is not moving moving.
kludge = Plug.kr( { if ( mt.lastMausDelta <= threshold, { 0.0 }, { 1.0 } ) } );
amp = kludge * ampMult * gaussian.value( ampSigma, resFreq, freq );
// Modulate the frequency to simulate the changing pressure of the fingertips.
Saw.ar( freq + (freq * LFNoise1.kr( 50, 0.2 )), amp );
};
// The index finger.
index = {
nail.value(
350, // Multiplier for the frequency distribution
8, // What is added to the log in the frequency distribution
900, // The resonant frequency
0.04, // The amount by which the amplitude is multiplied
0.01 // The standard deviation of the amplitude distribution.
)
};
// The middle finger. Larger than the index finger.
middle = {
nail.value(
250, // Multiplier for the frequency distribution
7.8, // What is added to the log in the frequency distribution
600, // The resonant frequency
0.03, // The amount by which the amplitude is multiplied
0.005 // The standard deviation of the amplitude distribution.
)
};
// The ring finger. About average-sized.
ring = {
nail.value(
300, // Multiplier for the frequency distribution
7, // What is added to the log in the frequency distribution
800, // The resonant frequency
0.03, // The amount by which the amplitude is multiplied
0.005 // The standard deviation of the amplitude distribution.
)
};
// The pinkie. Small and high-pitched.
pinkie = {
nail.value(
500, // Multiplier for the frequency distribution
7, // What is added to the log in the frequency distribution
1000, // The resonant frequency
0.03, // The amount by which the amplitude is multiplied
0.005 // The standard deviation of the amplitude distribution.
)
};
// In this case, clipping is GOOD 
mixed = takeSamp + index + middle + ring + pinkie + swish;
mixed.scope;