jm.audio.synth.Granulator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jmusic Show documentation
Show all versions of jmusic Show documentation
JMusic - Java Music Library
The newest version!
/*
Copyright (C) 2000 Andrew Sorensen & Andrew Brown
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or any
later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package jm.audio.synth;
import jm.audio.AOException;
import jm.audio.AudioObject;
/**
* An AudioObject for granulating input.
*
* @author Andrew Sorensen
* @version 1.0, Sun Feb 25 18:42:48 2001
* Revised and modified extensively by Timothy Opie
* Last changed v1.4 27/04/2003
*/
public final class Granulator extends AudioObject {
//----------------------------------------------
// Attributes
//----------------------------------------------
private int grainDuration = 1323;
private int envelopeType = 1;
private int nog; //number of grains
private float cfm; //current frequency modifier
private int cgd; //counter for grain duration
private float[] grain; //grain buffer
private float[] newbuf; //output buffer
private int grainCnt = 0;
private int grainsPerSecond = 10;
private float[] tailBuf; //so we don't get cut off between buffers
private float freqMod = 1.0f;
private float[] inBuffer = null;
private boolean inBufActive = false;
private boolean ri = false; //random indexing
private boolean rgd = false; //random grain duration
private int rdist = 0; //random distribution within cloud
private int rdisttemp = 0;
private int gdb = 1000; //the smallest random grainduration
private int gdt = 1000; //the highest random grain duration + gdb
private boolean rf = false; //random frequency
private float rfb = 0.99f; //the lowest random frequency
private float rft = 1.01f; //the highest random frequency + rfb
private float[] durationArray; //premapped grain duration
private float[] gpsArray; //premapped grains per second
private float[] freqArray; //premapped frequency
private boolean premapped = false; //premapped flag so the program
//knows when it is premapped or not.
//----------------------------------------------
// Constructors
//----------------------------------------------
public Granulator(AudioObject ao, int sampleRate, int channels, int duration, int gps) {
super(ao, "[Granulator]");
this.grainDuration = duration;
this.grainsPerSecond = gps;
this.cgd = 0;
this.grain = new float[this.grainDuration];
tailBuf = new float[0];
this.sampleRate = sampleRate;
this.channels = channels;
}
public Granulator(AudioObject ao, int sampleRate, int channels, float[] durationArr, float[] gpsArr,
float[] freqArr, boolean rif, boolean rgdf, boolean rff, int rd) {
super(ao, "[Granulator]");
this.durationArray = durationArr;
this.gpsArray = gpsArr;
this.freqArray = freqArr;
this.grain = new float[(int) this.durationArray[0]];
this.ri = rif;
this.rgd = rgdf;
this.rf = rff;
tailBuf = new float[0];
this.rdist = rd;
premapped = true;
this.sampleRate = sampleRate;
this.channels = channels;
}
/**
* @param buffer The sample buffer.
* @return The number of samples processed.
*/
public int work(float[] buffer) throws AOException {
if (inBuffer == null) {
newbuf = new float[buffer.length];
this.previous[0].nextWork(newbuf);
} else {
newbuf = new float[buffer.length];
for (int i = 0; (i < inBuffer.length) && (i < newbuf.length); i++) {
newbuf[i] = inBuffer[i];
}
inBuffer = null;
}
//number of grains to fit in buffer
if (grainsPerSecond <= 0) grainsPerSecond = 1;
//if (premapped) {
// nog = (int)((float)newbuf.length/
// ((float)(sampleRate*channels)/gpsArray[grainCnt]));
//} else {
nog = (int) ((float) newbuf.length /
((float) (sampleRate * channels) / (float) grainsPerSecond));
//}
if (nog <= 0) nog = 1;
//time between grains
int tbg = (newbuf.length / nog);
//add any grain tails
for (int i = 0; (i < buffer.length) && (i < tailBuf.length); i++) {
buffer[i] += tailBuf[i];
}
tailBuf = new float[newbuf.length];
inBufActive = true;
//add all new grains
for (int i = 0; i < nog; i++) {
if (rdist > 0) rdisttemp = (int) (Math.random() * rdist);
else rdisttemp = 0;
int index = ((i * tbg) + rdisttemp);
setGrain(index - rdisttemp);
for (int j = 0; j < grain.length; j++) {
if (index >= buffer.length) {
tailBuf[index - buffer.length] += grain[j];
} else {
buffer[index] += grain[j];
}
index++;
grainCnt++;
}
}
inBufActive = false;
return buffer.length;
}
/**
* Deviation from the input frequency
*/
public void setFreqMod(float fmod) {
this.freqMod = fmod;
}
/**
* Set the grains duration
*/
public void setGrainDuration(int gdur) {
this.grainDuration = gdur;
}
/**
* Set the number of grains per second
*/
public void setGrainsPerSecond(int gps) {
this.grainsPerSecond = gps;
}
/**
* Set the envelope type
*/
public void setEnvelopeType(int et) {
this.envelopeType = et;
}
/**
* Set a random grain duration
*/
public void setRandomGrainDuration(boolean bool) {
this.rgd = bool;
}
/**
* Set the random grain durations bottom value
*/
public void setRandomGrainBottom(int b) {
this.gdb = b;
}
/**
* Set the random grain durations top value
*/
public void setRandomGrainTop(int t) {
this.gdt = t;
}
/**
* Set random index position
*/
public void setRandomIndex(boolean bool) {
this.ri = bool;
}
/**
* set random frequency
*/
public void setRandomFreq(boolean bool) {
this.rf = bool;
}
/**
* set random distribution within cloud
*/
public void setRandomDist(int rd) {
this.rdist = rd;
}
/**
* Set the random frequency bottom value
*/
public void setRandomFreqBottom(float fb) {
this.rfb = fb;
}
/**
* Set the random frequency bottom value
*/
public void setRandomFreqTop(float ft) {
this.rft = ft;
}
//---------------------------------------
// Private Methods
//----------------------------------------
/**
* Set the grain
*/
private void setGrain(int index) throws AOException {
if (ri) index = (int) (Math.random() * (double) newbuf.length);
float[] buf = newbuf; //reference to the active buffer
if (rgd) this.cgd = gdb + (int) (Math.random() * gdt);
else /* { if (premapped) {
this.cgd = (int)durationArray[grainCnt];
} else { */
this.cgd = this.grainDuration;
// }
//}
//if (premapped) {
// this.cfm = this.freqArray[grainCnt];
//} else {
this.cfm = this.freqMod;
// //System.out.println("cfm" + cfm);
//}
if (rf) cfm = (float) (rfb + (Math.random() * (rft - rfb)));
if (inBufActive) {
inBuffer = new float[newbuf.length];
int ret = this.previous[0].nextWork(inBuffer);
inBufActive = false;
}
this.grain = new float[cgd];
int count = 0;
float tmp = 0.0f;
//positive values of skip are the iterations to skip
//negative values of skip are the iterations to add between
double skip = -1.0 / ((1 - cfm) / cfm);
double remains = 0.0;
int upSample = 0;
if (skip < 0) {
skip = -1.0 / skip;
upSample = 1;
}
if (skip == 0) upSample = 2;
int ind = 0;
//System.out.println("skip" + skip + "cfm" + cfm);
for (int i = index; true; i++) {
if (i == buf.length) {
i = 0;
buf = inBuffer;
}
if (upSample == 0) {//remove samples (up sample)
//if(i%((int)(skip+remains))==0)continue;
if (++ind >= (int) (skip + remains)) {
remains = (skip + remains) % 1.0;
ind = 0;
continue;
}
if (count >= cgd) break;
grain[count++] = buf[i];
} else if (upSample == 1) {//add samples (downsample)
if ((skip + remains) >= 1.0) {
float p = (tmp - buf[i]) / ((int) skip + 1);
for (int k = 0; k < (int) (skip + remains); k++) {
grain[count++] = p * k + buf[i];
if (count == cgd) break;
}
}
if (count == cgd) break;
grain[count++] = buf[i];
tmp = buf[i];
remains = (skip + remains) % 1.0;
} else { //no resample ;)
grain[count++] = buf[i];
}
if (count == cgd) break;
}
//Envelope our new grain
if (envelopeType <= 1) {
for (int i = 0; i < cgd; i++) {
this.grain[i] = this.grain[i] * (float) (0.5 - 0.5 *
Math.cos(2 * Math.PI * i / cgd));
}
}
//else if (envelopeType == 2) //{
// for(int i=0;i