Created
April 23, 2010 00:40
-
-
Save rndmcnlly/376028 to your computer and use it in GitHub Desktop.
DSP Engine from Ethereal Dialpad
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package as.adamsmith.etherealdialpad.dsp; | |
import android.media.AudioFormat; | |
import android.media.AudioManager; | |
import android.media.AudioTrack; | |
public class Dac extends UGen { | |
private final float[] localBuffer; | |
private boolean isClean; | |
private final AudioTrack track; | |
private final short [] target = new short[UGen.CHUNK_SIZE]; | |
private final short [] silentTarget = new short[UGen.CHUNK_SIZE]; | |
public Dac() { | |
localBuffer = new float[CHUNK_SIZE]; | |
int minSize = AudioTrack.getMinBufferSize( | |
UGen.SAMPLE_RATE, | |
AudioFormat.CHANNEL_CONFIGURATION_MONO, | |
AudioFormat.ENCODING_PCM_16BIT); | |
track = new AudioTrack( | |
AudioManager.STREAM_MUSIC, | |
UGen.SAMPLE_RATE, | |
AudioFormat.CHANNEL_CONFIGURATION_MONO, | |
AudioFormat.ENCODING_PCM_16BIT, | |
Math.max(UGen.CHUNK_SIZE*4, minSize), | |
AudioTrack.MODE_STREAM); | |
} | |
public boolean render(final float[] _buffer) { | |
if(!isClean) { | |
zeroBuffer(localBuffer); | |
isClean = true; | |
} | |
// localBuffer is always clean right here, does it stay that way? | |
isClean = !renderKids(localBuffer); | |
return !isClean; // we did some work if the buffer isn't clean | |
} | |
public void open() { | |
track.play(); | |
} | |
public void tick() { | |
render(localBuffer); | |
if(isClean) { | |
// sleeping is messy, so lets just queue this silent buffer | |
track.write(silentTarget, 0, silentTarget.length); | |
} else { | |
for(int i = 0; i < CHUNK_SIZE; i++) { | |
target[i] = (short)(32768.0f*localBuffer[i]); | |
} | |
track.write(target, 0, target.length); | |
} | |
} | |
public void close() { | |
track.stop(); | |
track.release(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package as.adamsmith.etherealdialpad.dsp; | |
public class Delay extends UGen { | |
final float[] line; | |
int pointer; | |
public Delay(int length) { | |
super(); | |
line = new float[length]; | |
} | |
public boolean render(final float[] buffer) { | |
renderKids(buffer); | |
final float[] localLine = line; | |
final int lineLength = line.length; | |
for(int i = 0; i < CHUNK_SIZE; i++) { | |
buffer[i] = buffer[i] - 0.5f*localLine[pointer]; | |
localLine[pointer] = buffer[i]; | |
pointer = (pointer+1)%lineLength; | |
} | |
// ugh, looks like we can never be sure it's silent | |
// without checking every sample because of the feedback | |
return true; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package as.adamsmith.etherealdialpad.dsp; | |
public class ExpEnv extends UGen { | |
public static float hardFactor = 0.005f; | |
public static float softFactor = 0.00005f; | |
boolean state; | |
float attenuation; | |
float factor = softFactor; | |
final float idealMarker = 0.25f; | |
float marker = idealMarker; | |
public synchronized void setActive(boolean nextState) { | |
state = nextState; | |
} | |
public synchronized void setFactor(float nextFactor) { | |
factor = nextFactor; | |
} | |
public synchronized void setGain(float gain) { | |
marker = gain * idealMarker; | |
} | |
public synchronized boolean render(final float[] buffer) { | |
if(!state && attenuation < 0.0001f) return false; | |
if(!renderKids(buffer)) return false; | |
for(int i = 0; i < CHUNK_SIZE; i++) { | |
buffer[i] *= attenuation; | |
if(!state) { | |
attenuation += (0-attenuation)*factor; | |
} else { | |
attenuation += (marker-attenuation)*factor; | |
} | |
} | |
return true; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package as.adamsmith.etherealdialpad.dsp; | |
import java.util.ArrayList; | |
public abstract class UGen { | |
public static final int CHUNK_SIZE = 256; | |
public static final int SAMPLE_RATE = 22050; | |
ArrayList<UGen> kids = new ArrayList<UGen>(0); | |
// fill CHUNK_SIZE samples | |
// and return true if you actually did any work | |
abstract public boolean render(final float[] buffer); | |
final public synchronized UGen chuck(UGen that) { | |
if(!that.kids.contains(this)) that.kids.add(this); | |
return that; // returns RHS | |
} | |
final public synchronized UGen unchuck(UGen that) { | |
if(that.kids.contains(this)) that.kids.remove(this); | |
return that; // returns RHS | |
} | |
protected void zeroBuffer(final float[] buffer) { | |
for(int i = 0; i < CHUNK_SIZE; i++) { | |
buffer[i] = 0; | |
} | |
} | |
protected boolean renderKids(final float[] buffer) { | |
boolean didSomeRealWork = false; | |
for(int k = 0; k < kids.size(); k++) { | |
didSomeRealWork |= kids.get(k).render(buffer); | |
} | |
return didSomeRealWork; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
init { | |
WtOsc ugOscA1 = new WtOsc(); | |
WtOsc ugOscA2 = new WtOsc(); | |
ExpEnv ugEnvA = new ExpEnv(); | |
ugOscA1.fillWithHardSin(7.0f); | |
ugOscA2.fillWithHardSin(2.0f); | |
Dac ugDac = new Dac(); | |
Delay ugDelay = new Delay(UGen.SAMPLE_RATE/2); | |
ugEnvA.chuck(ugDelay); | |
ugDelay.chuck(ugDac); | |
ugOscA1.chuck(ugEnvA); | |
ugOscA2.chuck(ugEnvA); | |
ugEnvA.setFactor(ExpEnv.hardFactor); | |
} | |
touch { | |
ugOscA1.setFreq(buildFrequency(scale, octaves, x)); | |
ugOscA2.setFreq(buildFrequency(scale, octaves, y)); | |
} | |
run { | |
ugDac.open(); | |
while(!isInterrupted()) { | |
ugDac.tick(); | |
} | |
ugDac.close(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package as.adamsmith.etherealdialpad.dsp; | |
import android.util.FloatMath; | |
public class WtOsc extends UGen { | |
public static final int BITS = 8; | |
public static final int ENTRIES = 1<<(BITS-1); | |
public static final int MASK = ENTRIES-1; | |
private float phase; | |
private float cyclesPerSample; | |
final float[] table; | |
public WtOsc () { | |
table = new float[ENTRIES]; | |
} | |
public synchronized void setFreq(float freq) { | |
cyclesPerSample = freq/SAMPLE_RATE; | |
} | |
public synchronized boolean render(final float[] buffer) { // assume t is in 0.0 to 1.0 | |
for(int i = 0; i < CHUNK_SIZE; i++) { | |
float scaled = phase*ENTRIES; | |
final float fraction = scaled-(int)scaled; | |
final int index = (int)scaled; | |
buffer[i] += (1.0f-fraction)*table[index&MASK]+fraction*table[(index+1)&MASK]; | |
phase = (phase+cyclesPerSample) - (int)phase; | |
} | |
return true; | |
} | |
public WtOsc fillWithSin() { | |
final float dt = (float)(2.0*Math.PI/ENTRIES); | |
for(int i = 0; i < ENTRIES; i++) { | |
table[i] = FloatMath.sin(i*dt); | |
} | |
return this; | |
} | |
public WtOsc fillWithHardSin(final float exp) { | |
final float dt = (float)(2.0*Math.PI/ENTRIES); | |
for(int i = 0; i < ENTRIES; i++) { | |
table[i] = (float) Math.pow(FloatMath.sin(i*dt),exp); | |
} | |
return this; | |
} | |
public WtOsc fillWithZero() { | |
for(int i = 0; i < ENTRIES; i++) { | |
table[i] = 0; | |
} | |
return this; | |
} | |
public WtOsc fillWithSqr() { | |
for(int i = 0; i < ENTRIES; i++) { | |
table[i] = i<ENTRIES/2?1f:-1f; | |
} | |
return this; | |
} | |
public WtOsc fillWithSqrDuty(float fraction) { | |
for(int i = 0; i < ENTRIES; i++) { | |
table[i] = (float)i/ENTRIES<fraction?1f:-1f; | |
} | |
return this; | |
} | |
public WtOsc fillWithSaw() { | |
float dt = (float)(2.0/ENTRIES); | |
for(int i = 0; i < ENTRIES; i++) { | |
table[i] = 1.0f-i*dt; | |
} | |
return this; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment