Professional Documents
Culture Documents
7.1 Interaction 2
7.1 Interaction 2
(
Server.default=s=Server.local;
s.boot;
)
MIDI
There may be more than one source and destination device, each containing different
input and output ports.
Incoming MIDI messages can be easily handled through some callback functions in
MIDIIn. However, from SuperCollider 3.5, the use of MIDIFunc is much preferred.
MIDI messages typically have a 7-bit (2**7) value range, so take on integers from 0
to 127. The vel/127 above converts from this range to a 0.0 to 1.0 range befitting
an amplitude control.
Examples:
saw= Saw.ar(freq);
filter= Resonz.ar(saw,1000,0.1)*amp;
env= EnvGen.ar(Env([0,1,0],[0.01,0.1]),doneAction:2);
//dup(2) duplicates the mono signal onto two channels, giving instant stereo
middle panned output
Out.ar(0,(filter*env).dup(2))
}).add
)
//create one Synth for every new note, Synths will be of finite duration because of
the envelope
MIDIIn.noteOn = { arg src,chan, midinote, velocity; Synth(\sound,[\
freq,midinote.midicps,\amp,velocity/127.0]) };
Keeping track of active (held-down, sustained) notes can be a chore in MIDI. Here
is an example of doing this using an array with 128 slots, one for each possible
MIDI note.
saw= Saw.ar(freq);
filter= Resonz.ar(saw,1000,0.1)*amp;
env= EnvGen.ar(Env.asr(0.005,1.0,0.1),gate,doneAction:2);
Out.ar(0,(filter*env).dup(2))
}).add
)
(
var activenotes = nil!128; //make Array of 128 slots, initially with nil objects in
to represent nothing
var releasefunction = {|index|
//create one Synth for every new note, with logic to check existing notes (though
not MIDI channel sensitive)
MIDIIn.noteOn = { arg src,chan, midinote, velocity;
"received".postln;
releasefunction.value(midinote);
//put active note in array; function above tidied any existing note on this
key
activenotes[midinote] =
Synth(\sound,[\freq,midinote.midicps,\amp,velocity/127.0]);
};
//must also look for note offs as indicated end of held note
MIDIIn.noteOff = { arg src,chan, midinote, velocity;
releasefunction.value(midinote);
};
//using control change for continuous variation; run one block/line at a time here
//no envelope this time, permanent sound
(
SynthDef(\sound,{arg freq=440, amp=0.1;
var saw, filter, env;
saw= Saw.ar(freq);
filter= Resonz.ar(saw,1000,0.1)*amp;
Out.ar(0,filter.dup(2))
}).add
)
//use the set message to update the control inputs of the running synth
MIDIIn.control = { arg src, chan, num, val; a.set(\amp, val/127) };
There are also some helper classes to allow you to more easily set up multiple
independent callbacks for the same type of message:
[MIDIFunc]
[MIDIdef]
(
b = MIDIFunc.noteOn({ |velocity, midipitch, channel|
[\velocity,velocity, \midinote,midipitch, \channel, channel].postln;
});
)
(
c = MIDIFunc.noteOn({ |velocity, midipitch, channel|
"note on!".postln;
});
)
b.free;
//note that by default, cmd+period will remove any MIDIFuncs that haven't been made
permanent
SoundIn
(
{ //ring modulator
SinOsc.ar(MouseX.kr(0.001,110,'exponential' ))*SoundIn.ar(0,0.5)
}.play; // stereo through patching from input to output
)
SuperCollider comes with an amplitude tracker and pitch tracker for realtime audio
(
// use input amplitude to control Pulse amplitude - use headphones to prevent
feedback.
{
Pulse.ar(90, 0.3, Amplitude.kr(SoundIn.ar(0)))
}.play
)
(
{
var input,inputAmp,threshhold,gate;
var basefreq;
input = SoundIn.ar(0,0.1);
inputAmp = Amplitude.kr(input);
threshhold = 0.02; // noise gating threshold
gate = Lag.kr(inputAmp > threshhold, 0.01);
(input * gate)
}.play;
)
The Pitch follower has many input arguments, though you usually take the defaults
without worrying. It returns two outputs- the tracked frequency and a signal
indicating
whether it has locked onto any periodicity or not
Server.internal.boot; //if on a Mac you'll need to swap back to internal server for
using .scope- you can have both the internal and localhost server on at once, but
you might need to press the -> default button
//showing the outputs - K2A makes sure control rate signals are converted to audio
rate, because the final output of a Synth has to be audio rate
(
{
var freq, hasFreq;
# freq, hasFreq = Pitch.kr(SoundIn.ar(1,0.1));
[K2A.ar(freq*0.001), K2A.ar(hasFreq)]
}.scope
)
in = SoundIn.ar(1);
amp = Amplitude.kr(in, mul: 0.4);
# freq, hasFreq = Pitch.kr(in);
out=if(hasFreq,Pulse.ar(freq,0.5,0.1),SinOsc.ar(freq,0,0.1));
6.do({
out = AllpassN.ar(out, 0.040, [0.040.rand,0.040.rand], 2)
});
out
}.play
)
There are various other machine listening capabilities in SuperCollider. Machine
listening is getting the computer to extract perceptually and musically meaingful
attributes by analyzing an input sound.
[Onsets]
[PV_HainsworthFoote]
[PV_JensenAndersen]
They rely on using the FFT UGen in the front end to go from time domain to
frequency domain. You can trust the code examples for now and we'll investigate FFT
properly later on (or explore the help file yourself).
//now this
(
{
var source, detect;
source= SoundIn.ar(0);
RecordBuf
If you'd like to capture live sound, the RecordBuf UGen is your friend.
You need to set up a buffer to store the recorded sample data.
(
var b;
You might sync captured buffers to tempo for dance music, and add refinements like
a user interface to choose when to rerecord the buffer...
There are also facilities for control from graphics tablets and joysticks:
[SC2DTabletSlider]
[HIDDeviceService]
[GeneralHID]
[SerialPort] //serial port (via USB usually these days) for talking to
certain external devices
Another standard way is to communicate with other applications using Open Sound
Control, a network music protocol; we'll cover this in a later week in this course.