My sonification project is done! You can hear the results at https://soundcloud.com/fcahoon/shumann-resonance-at-tomsk . I've been manipulating the data a lot, so it was simple to switch to tab-separated instead of comma-separated values, which ChucK reads without issue. The first few lines of my data looks like 7.8975 6.0000 13.6870 4.6500 19.0720 4.7200 25.5000 2.6200 7.9165 6.0000 13.6030 4.6500 19.0300 4.7200 25.5500 2.6200 7.9355 7.2000 13.5400 4.1200 18.9670 4.3800 25.6000 2.6200 7.9640 7.2000 13.4980 4.1200 18.9250 4.3800 25.6250 2.6200 ... These are four frequency/amplitude pairs on each line. I still did my interpolation sample-by-sample, but was not too difficult. Here is my ChucK code (it seems short enough to post): 16.0 => float FREQ_MULT; 0.008 => float GAIN_MULT; 441 => int SAMPLES_PER_DATAPOINT; SinOsc sig[4]; for ( 0 => int i ; i < sig.cap() ; i++) { sig[i] => dac; sig[i].gain(0); } // open file me.sourceDir() + "tomsk_2017-05-10_000.dat" => string fname; FileIO fio; fio.open(fname, FileIO.READ); if( !fio.good() ) { cherr <= "can't open file: " <= fname <= " for reading..." <= IO.newline(); me.exit(); } float old_freq[4]; float old_gain[4]; float new_freq[4]; float new_gain[4]; float freq_incr[4]; float gain_incr[4]; // read first line for (0 => int i; i < 4; i++) { fio => old_freq[i]; fio => old_gain[i]; } while (fio.more()) { // read next line for (0 => int i; i < 4; i++) { fio => new_freq[i]; fio => new_gain[i]; } // calculate linear interpolation increments for (0 => int i; i < 4; i++) { (new_freq[i] - old_freq[i])/SAMPLES_PER_DATAPOINT => freq_incr[i]; (new_gain[i] - old_gain[i])/SAMPLES_PER_DATAPOINT => gain_incr[i]; } // Interpolate all samples between datapoints: for (0 => int step; step < SAMPLES_PER_DATAPOINT; step++) { // calculate interpolated freqs and gains for this sample for (0 => int i; i < 4; i++) { (old_freq[i] + (step*freq_incr[i])) * FREQ_MULT => sig[i].freq; (old_gain[i] + (step*gain_incr[i])) * GAIN_MULT => sig[i].gain; } //render the sample 1::samp => now; } // save last datapoint values read as "old" in preparation for // reading the next set of "new" datapoint values for (0 => int i; i < 4; i++) { new_freq[i] => old_freq[i]; new_gain[i] => old_gain[i]; } } I'm sure my code must have some non-idiomatic or inefficient things in it; it is my first real ChucK program. Any advice would be appreciated.