/* * Copyright 2009 Phil Burk, Mobileer Inc * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.jsyn.unitgen; import com.jsyn.ports.UnitInputPort; import com.jsyn.ports.UnitOutputPort; import com.jsyn.util.PseudoRandom; /** * Random output with 3dB per octave rolloff providing a soft natural noise sound. Generated using * Gardner method. Optimization suggested by James McCartney uses a tree to select which random * value to replace. * *
 *  x x x x x x x x x x x x x x x x 
 *  x   x   x   x   x   x   x   x   
 *  x       x       x       x       
 *  x               x               
 *  x
 * 
* * Tree is generated by counting trailing zeros in an increasing index. When the index is zero, no * random number is selected. Author: Phil Burk (C) 1996 SoftSynth.com. */ public class PinkNoise extends UnitGenerator implements UnitSource { public UnitInputPort amplitude; public UnitOutputPort output; private final int NUM_ROWS = 16; private final int RANDOM_BITS = 24; private final int RANDOM_SHIFT = 32 - RANDOM_BITS; private PseudoRandom randomNum; protected double prevNoise, currNoise; private long[] rows = new long[NUM_ROWS]; // NEXT RANDOM UNSIGNED 32 private double scalar; // used to scale within range of -1.0 to +1.0 private int runningSum; // used to optimize summing of generators private int index; // incremented with each sample private int indexMask; // index wrapped and ANDing with this mask /* Define Unit Ports used by connect() and set(). */ public PinkNoise() { addPort(amplitude = new UnitInputPort("Amplitude", UnitOscillator.DEFAULT_AMPLITUDE)); addPort(output = new UnitOutputPort("Output")); randomNum = new PseudoRandom(); // set up for N rows of generators index = 0; indexMask = (1 << NUM_ROWS) - 1; // Calculate maximum possible signed random value. Extra 1 for white // noise always added. int pmax = (NUM_ROWS + 1) * (1 << (RANDOM_BITS - 1)); scalar = 1.0 / pmax; // initialize rows for (int i = 0; i < NUM_ROWS; i++) { rows[i] = 0; } runningSum = 0; } @Override public void generate(int start, int limit) { double[] amplitudes = amplitude.getValues(); double[] outputs = output.getValues(); for (int i = start; i < limit; i++) { outputs[i] = generatePinkNoise() * amplitudes[i]; } } public double generatePinkNoise() { index = (index + 1) & indexMask; // If index is zero, don't update any random values. if (index != 0) { // Determine how many trailing zeros in PinkIndex. // This algorithm will hang of n==0 so test first int numZeros = 0; int n = index; while ((n & 1) == 0) { n = n >> 1; numZeros++; } // Replace the indexed ROWS random value. // Subtract and add back to RunningSum instead of adding all the // random values together. Only one changes each time. runningSum -= rows[numZeros]; int newRandom = randomNum.nextRandomInteger() >> RANDOM_SHIFT; runningSum += newRandom; rows[numZeros] = newRandom; } // Add extra white noise value. int newRandom = randomNum.nextRandomInteger() >> RANDOM_SHIFT; int sum = runningSum + newRandom; // Scale to range of -1.0 to 0.9999. return scalar * sum; } @Override public UnitOutputPort getOutput() { return output; } }