package jass.generators;
import jass.engine.*;
import java.io.*;
/** Map HSB color to [pitch reson-width loudness]
Represent a color (h,s,b) by a noise source of loudness b,
filtered through a reson bank with Shepard frequencies
(i.e. octaves apart covering the audible range) and some damping d = 1*freq/freq_lowest.
The hue h [0 1] will be mapped to an octave range in freq. (Note the dampings
are also scaled when freq. is scaled to preserve scale invariance of octaves.) The saturation
s [0 1] will be mapped to the "material" (i.e., the width of the resonances will be
multiplied by a factor depending on the saturation.
@author Kees van den Doel (kvdoel@cs.ubc.ca)
*/
public class ColorSonificator extends ModalObjectWithOneContactInterpolated {
//public class ColorSonificator extends ModalObjectWithOneContact {
private float lowestFrequency = 10; // lower limit of hearing
private float minimumDamping = 3; // for completely saturated color
private float maximumDamping = 25; // for completely unsaturated color
// envelope unused:
private float dBMax = 60; // level difference between black and white
private double fudgePower = .35; // in theory energy in mode (f,d,a) is a^2/d, so if we scale a by
// d^.5 enery damping independent. But does not sound so because of critical band effect so fudge power
/** Create and initialize.
@param srate sampling rate in Hertz.
@param bufferSize Buffer size used for real-time rendering.
*/
public ColorSonificator(float srate,int bufferSize) {
super(bufferSize);
this.srate = srate;
int np = 1; // 1 location
// f=lowestFrequency*2^n, n = 0,...nf-1
int nf = (int)(Math.floor(Math.log((srate/2)/lowestFrequency)/Math.log(2)));
this.modalModel = new ModalModel(nf,np);
allocate(nf,np);
this.tmpBuf = new float[bufferSize];
createModalModel();
// only if deriving from ModalObjectWithOneContactInterpolated
super.allocate_new(nf,np);
}
private final static double minDB = 1.e-10;
/** Convert from level to decibel
@param a level
@return decibels = 20Log_10(a)
*/
public static double decibel(double a) {
double y;
double abs_a = Math.abs(a);
if(abs_a < minDB) {
y = minDB;
} else {
y = abs_a;
}
return 20*Math.log(abs_a)/Math.log(10);
}
/**
Set power in scaling law for gains a = a*d^fudgePower. .5 for constant energy but lower
in practice because of critical band effect, to have d-independent loudness
@param p fudgePower
*/
public void setFudgePower(float p) {
fudgePower = (double)p;
}
/**
Get power in scaling law for gains a = a*d^fudgePower. .5 for constant energy but lower
in practice because of critical band effect, to have d-independent loudness
@return fudgePower
*/
public float getFudgePower() {
return (float)fudgePower;
}
protected void createModalModel() {
for(int i=0;i<modalModel.nf;i++) {
modalModel.d[i] = 1;
modalModel.f[i] = lowestFrequency * (float)Math.pow(2.,(double)i);
modalModel.d[i] = modalModel.f[i]/lowestFrequency;
modalModel.a[0][i] = 1;
//System.out.println("f,a="+modalModel.f[i]+","+modalModel.a[0][i]);
}
}
/** Set damping range corresponding to saturation
@param dmin damping for saturated color
@param dmax damping for unsaturated color
*/
public void setSaturationLimits(float dmin,float dmax) {
this.minimumDamping = dmin;
this.maximumDamping = dmax;
}
/** Set level difference between white and black
@param dbmax maximum level difference between white and black
*/
public void setMaximumLevelDifference(float dbmax) {
this.dBMax = dbmax;
}
/** Get level difference between white and black
@return maximum level difference between white and black
*/
public float getMaximumLevelDifference() {
return this.dBMax;
}
private float[] satLimits = new float[2];
/** Get damping range corresponding to saturation
@return [minimumDamping maximumDamping]
*/
public float[] getSaturationLimits() {
satLimits[0] = minimumDamping;
satLimits[1] = maximumDamping;
return satLimits;
}
/** Set hue, saturation, brightness and slide velocity (1 = max)
@param h hue in range 0-1
@param s saturation in range 0-1
@param b brightness in range 0-1
@param v velocity in range 0-1
*/
public void setHSB_V(float h,float s,float b, float v) {
float dscale = (float)(maximumDamping*Math.exp(-s*Math.log(maximumDamping/minimumDamping)));
float fscale = (float)(v * Math.pow(2.,(double)h));
// to keep level damping independent:
double bcorrected = b*Math.pow((double)(dscale/maximumDamping),fudgePower);
//float ascale = (float)(Math.exp(Math.log(10)*dBMax*(bcorrected-1)/20));
float ascale = (float)Math.pow((double)(dscale*fscale),fudgePower);
setDamping(fscale*dscale/v); // damping not affected by speed
setFrequencyScale(fscale);
//System.out.println(fscale);
//ascale = 1;
setGain(ascale);
}
/** Set hue, saturation and brightness
@param h hue in range 0-1
@param s saturation in range 0-1
@param b brightness in range 0-1
*/
public void setHSB(float h,float s,float b) {
float velocity = 1;
setHSB_V(h,s,b,velocity);
}
}