// @title m220a-DataReader.ck // @desc data(.dat) file reader for Music 220A class // @author Chris Chafe (cc@ccrma) // IMPORTANT NOTE: add this class only once or an error will be thrown // to re-shred. In that case, you have to stop and restart Virtual Machine. // HOW TO USE: use this file to declare a public class for reading in a // data file and play the data with a separate "player" file // or type in a data file's full pathname as argument to this file and // it'll play the data file itself in miniAudicle // the argument goes into the text field just above this document or // in command line chuck it goes after this file e.g., // // on CCRMA linux: // chuck m220a-DataReader.ck:[your_data_file] // // again, this file can only be shredded once since it declares // a public class. // @class DataReader // @desc define the DataReader public class. reads time series file and // serves up values expects single column of float or int data public class DataReader { string inFilename; 999999999999.9 => float bigNum; bigNum => float minVal; -bigNum => float maxVal; float range; float scale; 0 => int cnt; 0 => int numPoints; fun float min() { return minVal; } fun float max() { return maxVal; } fun float rng() { return range; } fun float scl() { return scale; } fun int count() { return cnt; } float val; // set data file fun void setDataSource( string f ) { f => inFilename; // full path } // file pointer FileIO @ in; new FileIO @=> in; // flag end of file true => int finished; fun int isFinished() { return finished || (numPoints && (cnt >= numPoints)); } // flag if calibrated false => int calibrated; fun int isCalibrated() { return calibrated; } // open file, close first if already opened fun void openData( ) { if( in.good() ) in.close(); false => finished; in.open( inFilename, FileIO.READ ); <<>>; if( !in.good() ) <<< "can't open file: ", inFilename, " for reading">>>; } // run through all data points to find statistics fun void calibrate( ) { bigNum => minVal; -bigNum => maxVal; openData(); while(next()) { if (val > maxVal) val => maxVal; if (val < minVal) val => minVal; } <<>>; max()-min() => range; 1.0 / rng() => scale; true => calibrated; cnt => numPoints; } // hack that will check if input string is actually a number, maybe not foolproof fun int isNumber (string s) { // handle all cases where string is equal to zero if (s=="0") return true; if (s=="0.0") return true; if (s=="0.00") return true; if (s=="0.000") return true; if (s=="0.0000") return true; if (s=="0.00000") return true; // all other cases return !(Std.atof(s)==0.0000); // atof returns zero if non-number string } // read a line if we can and input a number after checking for validity fun int next() { if (!in.good()) {<<<"file not open">>>; return false;} if (isFinished()) {return false;} -1.0 => val; // default value of -1.0 intentionally out of range string s; in => s; // first read a string up to first space or newline if (in.eof()) { //<<<"encountered EOF">>>; true => finished; return false; } in.readLine() => string rest; // read rest of line cnt++; // number of lines read if (isNumber(s)) Std.atof(s) => val; // should be good to go else <<< "non-number string found in data, line ", cnt, " = ", s+rest>>>; return true; } // calibrate and cue up at start fun void start( ) { if (!calibrated) calibrate(); 0 => cnt; openData(); } // return one datum fun float nextVal( ) { next(); return val; } // scaled version fun float scaledVal( ) { return scl() * (nextVal() - min()); } } // end of class definition // optional very simple player // invoked only if argument is supplied if (me.args()) { DataReader data; data.setDataSource(me.arg(0)); // file name is assumed to be first argument data.start(); data.scaledVal(); SinOsc s => dac; while (!data.isFinished()) { data.scaledVal() => float val; // next data point, scaled in 0.0 - 1.0 range s.gain(val); s.freq(Std.mtof(80.0 + val*20.0)); 100::ms => now; } }