//
// Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: 22 Nov 1998
// Last Modified: 22 Nov 1998
// Last Modified: Sat Mar 27 18:14:41 PST 1999
// Filename: ...sig/doc/examples/improv/synthImprov/2dpos/2dpos.cpp
// Syntax: C++; synthImprov 2.0; NIDAQ (Windows 95 only)
//
// Description: plays notes according to 2D input data coming from NIDAQ card.
// the keys (pitches) of the output are based on one channel of
// input and their velocities are based on another channel of input
//
#include "synthImprov.h" /* basic MIDI I/O interface with MIDI synthesizer */
#include "Nidaq.h" /* interface with NIDAQ Card(Windows 95 only) */
/*----------------- beginning of improvization algorithms ---------------*/
// global variables:
Nidaq sensor; // NIDAQ card interface for sensor
int keychan = 0; // the NIDAQ channel to listen to for keys
int velchan = 0; // the NIDAQ channel to listen to for velocities
int keyselect = 0; // boolean to listen to keyboard for key chann
int velselect = 0; // boolean to listen to keyboard for vel chann
int keydir = 1; // for changing sensor direction of key numbers
int veldir = 1; // for changing sensor direction of velocities
Voice voice; // for MIDI output (auto handles note off messages)
int inst = GM_VIBRAPHONE; // initial instrument to play notes on
int sustain = 0; // for sustain pedal
double keymin = 10.0; // miniumum value from NIDAQ for keys, adapts
double keymax = -10.0; // maximum value from NIDAQ for keys, adapts
double velmin = 10.0; // miniumum value from NIDAQ for vel, adapts
double velmax = -10.0; // maximum value from NIDAQ for vel, adapts
int keyno; // MIDI keynumber to play
int velocity; // MIDI attack velocity
int minDur = 40; // minimum duration of output notes
SigTimer displayTimer; // for displaying positions on the screen
int display = 0; // boolean for display sensor data on screen
// non-interface function declarations:
void makeNote(void);
/*--------------------- maintenance algorithms --------------------------*/
//////////////////////////////
//
// description -- this function is called by the improv interface
// whenever a capital "D" is pressed on the computer keyboard.
// Put a description of the program and how to use it here.
//
void description(void) {
cout << '\n';
printboxtop();
pcl("2dpos: by Craig Stuart Sapp <craig@ccrma.stanford.edu> 22 Nov 1998");
printintermediateline();
psl(" NIDAQ device initially on channel 0. Initial sensor range will ");
psl(" adapt as sensor moves. So go through whole range of sensor");
psl(" to fix the software range.");
psl(" Commands:");
psl(" \"0\"-\"9\" = select NIDAQ analog input channel");
psl(" type \"k\" before number to select key channel");
psl(" type \"v\" before number to select velocity channel");
psl(" \"-\", \"=\" = change instruments, \" \" = sustain pedal");
psl(" \"z\" = reset key sensor range, \"x\" = reset vel sensor range");
psl(" \"a\" = switch key sensor range, \"s\" = switch vel sensor range");
psl(" \"d\" = toggle sensor display.");
printboxbottom();
}
//////////////////////////////
//
// initialization -- this function is called by the improv
// intervace once at the start of the program. Put items
// here which need to be initialized at the beginning of
// the program.
//
void initialization(void) {
sensor.initialize(options.argv()); // start CVIRTE stuff for NIDAQ card
sensor.setPollPeriod(1); // check for new data every 1 millisecond
sensor.setFrameSize(1); // data transfer size from NIDAQ card
sensor.setModeLatest(); // just look at most recent data in buffer
sensor.setSrate(500); // set NIDAQ sampling rate to X Hz
sensor.activateAllChannels(); // turn on all channels for sampling
cout << "starting data aquisition ... " << flush;
sensor.start(); // start aquiring data from NIDAQ card
cout << "ready." << endl;
voice.setPort(synth.getOutputPort()); // specify output port of voice
voice.setChannel(0); // specify output chan of voice
voice.pc(inst);
displayTimer.setPeriod(200); // display position every X milliseconds
}
//////////////////////////////
//
// finishup -- this function is called by the improv interface
// whenever the function is exited. Put items here which
// need to be taken care of when the program is finished.
//
void finishup(void) { }
/*-------------------- main loop algorithms -----------------------------*/
//////////////////////////////
//
// mainloopalgorithms -- this function is called by the improv interface
// continuously while the program is running. The variable t_time
// which stores the current time is set just before this function is
// called and remains constant while in this functions.
//
void mainloopalgorithms(void) {
sensor.checkPoll(); // see if it is time to check for new data frame
makeNote(); // check if it is time to play a MIDI note
if (display && displayTimer.expired()) {
displayTimer.reset();
cout << "\r\t\t\t\t\t\t\t\t\t";
cout << "\rkey0= " << (int)(100 * keymin)/100.0
<< " keyx= " << (int)(100 * keymax)/100.0
<< " keyc= " << (int)(100 * sensor[keychan][0])/100.0
<< " \tvel0= " << (int)(100 * velmin)/100.0
<< " velx= " << (int)(100 * velmax)/100.0
<< " velc= " << (int)(100 * sensor[velchan][0])/100.0
<< " " << flush;
}
}
//////////////////////////////
//
// makeNote -- plays a new note whenever sensor detects
// a new note value, but limit the minimum time between
// notes.
void makeNote(void) {
// if the duration of the previous note is long enough and
// the sensor is not at its min or max value, then play a note
// if the interval distance has been traversed since the last
// note which was played.
if (t_time - voice.getOnTime() > minDur) {
// adjust the range of the sensor values if necessary:
if (sensor[keychan][0] > keymax) keymax = sensor[keychan][0];
if (sensor[keychan][0] < keymin) keymin = sensor[keychan][0];
if (sensor[velchan][0] > velmax) velmax = sensor[velchan][0];
if (sensor[velchan][0] < velmin) velmin = sensor[velchan][0];
// keyno is the note to be played. It is in the range from 0 to 127
keyno = (int)((sensor[keychan][0] - keymin) /
(keymax - keymin) * 127);
if (keydir == -1) {
keyno = 127 - keyno;
}
velocity = (int)((sensor[velchan][0] - velmin) /
(velmax - velmin) * 127);
if (veldir == -1) {
velocity = 127 - velocity;
}
// limit the range of keyno to valid range, in case max/min not correct
if (keyno < 0) keyno = 0;
if (keyno > 127) keyno = 127;
if (velocity < 0) velocity = 0;
if (velocity > 127) velocity = 127;
if (keyno < 2 || keyno > 125) { // turn off notes out of sensor range
voice.off();
} else {
voice.play(keyno, velocity);
}
}
}
/*-------------------- triggered algorithms -----------------------------*/
///////////////////////////////
//
// keyboardchar -- this function is called by the improv interface
// whenever a key is pressed on the computer keyboard.
// Put commands here which will be executed when a key is
// pressed on the computer keyboard.
//
void keyboardchar(int key) {
// select a NIDAQ channel if the key is a number from 0 to 9:
if (isdigit(key)) {
if (keyselect) {
keychan = key - '0';
keyselect = 0;
cout << "\nKey Channel " << key - '0' << " selected" << endl;
}
if (velselect) {
velchan = key - '0';
velselect = 0;
cout << "\nVelocity Channel " << key - '0' << " selected" << endl;
}
return;
}
switch (key) {
case 'd': // toggle display of sensor data on screen
display = !display;
break;
case 'k': // toggle key number channel select option
keyselect = !keyselect;
break;
case 'v': // toggle velocity channel select option
velselect = !velselect;
break;
case 'z': // reset the key sensor range
keymax = -10.0;
keymin = 10.0;
break;
case 'x': // reset the velocity sensor range
velmax = -10.0;
velmin = 10.0;
break;
case 'a': // reverse the key axis of the sensor data
keydir = -keydir;
break;
case 's': // reverse the veolcity sensor direction
veldir = -veldir;
break;
case '-': // decrement instrument
inst--;
if (inst < 0) inst = 127;
voice.pc(inst);
cout << "\ninst = " << inst << endl;
break;
case '=': // increment instrument
inst++;
if (inst > 127) inst = 0;
voice.pc(inst);
cout << "\ninst = " << inst << endl;
break;
case ' ': // sustain pedal on/off
sustain = !sustain;
voice.sustain(sustain);
cout << "\nsustain = " << sustain << endl;
break;
}
}
/*------------------ end improvization algorithms -----------------------*/
// md5sum: 7039d0f8b8c44c88411361e93b892257 2dpos.cpp [20050403]