package main; public class NoiseGenerator extends Object { float minFrequency = 1, maxFrequency = 100; int numFrequencies = 20; float frequencyStep = (maxFrequency - minFrequency) / (numFrequencies - 1); /** The maximum amplitude of noise at each frequency. The actual amplitude will be selected randomly from a flat distribution between - max and max. Currently, the max's are all set to the same value. In the future, we might want to make it so the noise at different frequencies can be different amplitudes, and that's what this machinery is for. */ float [] maxAmplitudes = new float[numFrequencies]; float [] amplitudes = new float[numFrequencies]; float [] values; float firstTime = 0; int numPts, firstPt; float timepointLength; // = 1 / minFrequency in simplest case public NoiseGenerator() { // Initialize max amplitude to 1 for(int i = 0; i < maxAmplitudes.length; i++) maxAmplitudes[i] = 1; reset(); } public float getNoise(float time) { int pos; if(time < firstTime) { System.out.println("ERROR: Not enough noise timepoints stored, noise inaccurate"); return 0f; // Maybe throw an exception instead? } // Check if need to make more noise timepoints while(time > firstTime + (numPts - 2) * timepointLength) { if(numPts == values.length) { pos = firstPt; firstPt++; firstPt = firstPt % values.length; firstTime += timepointLength; } else { pos = numPts; numPts++; } float nexttime = pos * timepointLength + firstTime; values[pos] = calculateNoiseForTime(nexttime); } // Find the noise through linear interpolation pos = (int)((time - firstTime) / timepointLength) + firstPt; pos = pos % values.length; int pos2 = (pos + 1) % values.length; float pos_in_interval = ((time - firstTime) - pos * timepointLength) / timepointLength; return values[pos] * (1 - pos_in_interval) + values[pos+1] * pos_in_interval; } private float calculateNoiseForTime(float time) { float value = 0; // NOTE - in here, get big speed-up by using lookup table for(int freqstep = 0; freqstep < amplitudes.length; freqstep++) { float freq = minFrequency + frequencyStep * freqstep; value += amplitudes[freqstep] * Math.sin(time * (2 * 3.14159 / freq)); } return value; } public void reset() { firstTime = 0; firstPt = 0; numPts = 0; // Pick new amplitudes for(int i = 0; i < amplitudes.length; i++) amplitudes[i] = MoreMath.random() * maxAmplitudes[i]; } public void setNumFrequencies(int num) throws Exception { if(num == numFrequencies) return; if(num < 1) throw new Exception("Number of noise frequencies must be >= 1"); numFrequencies = num; amplitudes = new float[numFrequencies]; // Fill maxAmplitudes with 1 - this will become more complex if ever allow waveform shape to noise float max_amp = maxAmplitudes[0]; maxAmplitudes = new float[numFrequencies]; for(int i = 0; i < maxAmplitudes.length; i++) maxAmplitudes[i] = max_amp; reset(); } public int getNumFrequencies() { return numFrequencies; } public void setMinFrequency(float min) throws Exception { if(min == minFrequency) return; if(min < 0.000001) throw new Exception("Minimum frequency for noise was set too low"); if(min < 0.1) System.out.println("WARNING: A small minimum noise frequency may cause the integrator to slow down"); minFrequency = min; reset(); } public float getMinFrequency() { return minFrequency; } public void setMaxFrequency(float max) throws Exception { if(max == maxFrequency) return; if(max < minFrequency) throw new Exception("Maximum frequency for noise was set below minimum"); maxFrequency = max; reset(); } public float getMaxFrequency() { return maxFrequency; } public void setMaxAmplitude(float max) { if(max == maxAmplitudes[0]) return; for(int i = 0; i < maxAmplitudes.length; i++) maxAmplitudes[i] = max; reset(); } public float getMaxAmplitude() { return maxAmplitudes[0]; } }