Initial commit.

This commit is contained in:
Woody Folsom
2012-03-06 11:42:35 -05:00
commit 8e83234a87
124 changed files with 9621 additions and 0 deletions

BIN
src/dk/itu/mario/engine/sonar/.DS_Store vendored Normal file

Binary file not shown.

View File

@@ -0,0 +1,38 @@
package dk.itu.mario.engine.sonar;
import dk.itu.mario.engine.sonar.sample.SonarSample;
public class FakeSoundEngine extends SonarSoundEngine
{
public void setListener(SoundListener soundListener)
{
}
public void shutDown()
{
}
public SonarSample loadSample(String resourceName)
{
return null;
}
public void play(SonarSample sample, SoundSource soundSource, float volume, float priority, float rate)
{
}
public void clientTick(float alpha)
{
}
public void tick()
{
}
public void run()
{
}
}

View File

@@ -0,0 +1,33 @@
package dk.itu.mario.engine.sonar;
/**
* @author Administrator
*/
public class FixedSoundSource implements SoundSource
{
private float x;
private float y;
public FixedSoundSource(float x, float y)
{
this.x = x;
this.y = y;
}
public FixedSoundSource(SoundSource soundSource)
{
this.x = soundSource.getX(1);
this.y = soundSource.getY(1);
}
public float getX(float alpha)
{
return x;
}
public float getY(float alpha)
{
return y;
}
}

View File

@@ -0,0 +1,145 @@
package dk.itu.mario.engine.sonar;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import javax.sound.sampled.*;
import dk.itu.mario.engine.sonar.mixer.ListenerMixer;
import dk.itu.mario.engine.sonar.sample.SamplePlayer;
import dk.itu.mario.engine.sonar.sample.SonarSample;
import dk.itu.mario.engine.sonar.sample.SampleLoader;
public class SonarSoundEngine implements Runnable
{
private SonarSample silentSample;
private SourceDataLine sdl;
private int rate = 44100;
private ListenerMixer listenerMixer;
private int bufferSize = rate / 100; // 10 ms
private ByteBuffer soundBuffer = ByteBuffer.allocate(bufferSize * 4);
private float[] leftBuf, rightBuf;
private float amplitude = 1;
private float targetAmplitude = 1;
private boolean alive = true;
protected SonarSoundEngine()
{
}
public SonarSoundEngine(int maxChannels) throws LineUnavailableException
{
silentSample = new SonarSample(new float[] {0}, 44100);
Mixer mixer = AudioSystem.getMixer(null);
sdl = (SourceDataLine) mixer.getLine(new Line.Info(SourceDataLine.class));
sdl.open(new AudioFormat(rate, 16, 2, true, false), bufferSize * 2 * 2 * 2 * 2 * 2);
soundBuffer.order(ByteOrder.LITTLE_ENDIAN);
sdl.start();
try
{
/* FloatControl volumeControl = (FloatControl) sdl.getControl(FloatControl.Type.MASTER_GAIN);
volumeControl.setValue(volumeControl.getMaximum());*/
}
catch (IllegalArgumentException e)
{
System.out.println("Failed to set the sound volume");
}
listenerMixer = new ListenerMixer(maxChannels);
leftBuf = new float[bufferSize];
rightBuf = new float[bufferSize];
Thread thread = new Thread(this);
thread.setDaemon(true);
thread.setPriority(10);
thread.start();
}
public void setListener(SoundListener soundListener)
{
listenerMixer.setSoundListener(soundListener);
}
public void shutDown()
{
alive = false;
}
public SonarSample loadSample(String resourceName)
{
try
{
return SampleLoader.loadSample(resourceName);
}
catch (Exception e)
{
System.out.println("Failed to load sample " + resourceName + ". Using silent sample");
e.printStackTrace();
return silentSample;
}
}
public void play(SonarSample sample, SoundSource soundSource, float volume, float priority, float rate)
{
synchronized (listenerMixer)
{
if(!dk.itu.mario.engine.Art.mute)
listenerMixer.addSoundProducer(new SamplePlayer((SonarSample) sample, rate), soundSource, volume, priority);
}
}
public void clientTick(float alpha)
{
synchronized (listenerMixer)
{
listenerMixer.update(alpha);
}
}
public void tick()
{
soundBuffer.clear();
// targetAmplitude = (targetAmplitude - 1) * 0.9f + 1;
// targetAmplitude = (targetAmplitude - 1) * 0.9f + 1;
synchronized (listenerMixer)
{
float maxAmplitude = listenerMixer.read(leftBuf, rightBuf, rate);
// if (maxAmplitude > targetAmplitude) targetAmplitude = maxAmplitude;
}
soundBuffer.clear();
float gain = 32000;
for (int i = 0; i < bufferSize; i++)
{
// amplitude += (targetAmplitude - amplitude) / rate;
// amplitude = 1;
// float gain = 30000;
int l = (int) (leftBuf[i] * gain);
int r = (int) (rightBuf[i] * gain);
if (l > 32767) l = 32767;
if (r > 32767) r = 32767;
if (l < -32767) l = -32767;
if (r < -32767) r = -32767;
soundBuffer.putShort((short)l);
soundBuffer.putShort((short)r);
}
sdl.write(soundBuffer.array(), 0, bufferSize * 2 * 2);
}
public void run()
{
while (alive)
{
tick();
}
}
}

View File

@@ -0,0 +1,6 @@
package dk.itu.mario.engine.sonar;
public interface SoundListener extends SoundSource
{
}

View File

@@ -0,0 +1,8 @@
package dk.itu.mario.engine.sonar;
public interface SoundProducer
{
public float read(float[] buf, int readRate);
public void skip(int samplesToSkip, int readRate);
public boolean isLive();
}

View File

@@ -0,0 +1,7 @@
package dk.itu.mario.engine.sonar;
public interface SoundSource
{
public float getX(float alpha);
public float getY(float alpha);
}

View File

@@ -0,0 +1,7 @@
package dk.itu.mario.engine.sonar;
public interface StereoSoundProducer
{
public float read(float[] leftBuf, float[] rightBuf, int readRate);
public void skip(int samplesToSkip, int readRate);
}

Binary file not shown.

View File

@@ -0,0 +1,93 @@
package dk.itu.mario.engine.sonar.mixer;
import java.util.*;
import dk.itu.mario.engine.sonar.SoundListener;
import dk.itu.mario.engine.sonar.SoundProducer;
import dk.itu.mario.engine.sonar.SoundSource;
import dk.itu.mario.engine.sonar.StereoSoundProducer;
public class ListenerMixer implements StereoSoundProducer
{
private List<Sound> sounds = new ArrayList<Sound>();
private float[] buf = new float[0];
private int maxChannels;
private SoundListener soundListener;
public ListenerMixer(int maxChannels)
{
this.maxChannels = maxChannels;
}
public void setSoundListener(SoundListener soundListener)
{
this.soundListener = soundListener;
}
public void addSoundProducer(SoundProducer producer, SoundSource soundSource, float volume, float priority)
{
sounds.add(new Sound(producer, soundSource, volume, priority));
}
public void update(float alpha)
{
for (Iterator it = sounds.iterator(); it.hasNext();)
{
Sound sound = (Sound) it.next();
sound.update(soundListener, alpha);
if (!sound.isLive())
{
it.remove();
}
}
}
@SuppressWarnings("unchecked")
public float read(float[] leftBuf, float[] rightBuf, int readRate)
{
if (buf.length != leftBuf.length) buf = new float[leftBuf.length];
if (sounds.size() > maxChannels)
{
Collections.sort(sounds);
}
Arrays.fill(leftBuf, 0);
Arrays.fill(rightBuf, 0);
float maxAmplitude = 0;
for (int i = 0; i < sounds.size(); i++)
{
Sound sound = (Sound) sounds.get(i);
if (i < maxChannels)
{
sound.read(buf, readRate);
float rp = (sound.pan<0?1:1-sound.pan)*sound.amplitude;
float lp = (sound.pan>0?1:1+sound.pan)*sound.amplitude;
for (int j = 0; j < leftBuf.length; j++)
{
leftBuf[j] += buf[j]*lp;
rightBuf[j] += buf[j]*rp;
if (leftBuf[j]>maxAmplitude) maxAmplitude = leftBuf[j];
if (rightBuf[j]>maxAmplitude) maxAmplitude = rightBuf[j];
}
}
else
{
sound.skip(leftBuf.length, readRate);
}
}
return maxAmplitude;
}
public void skip(int samplesToSkip, int readRate)
{
for (int i = 0; i < sounds.size(); i++)
{
Sound sound = (Sound) sounds.get(i);
sound.skip(samplesToSkip, readRate);
}
}
}

View File

@@ -0,0 +1,84 @@
package dk.itu.mario.engine.sonar.mixer;
import dk.itu.mario.engine.sonar.SoundListener;
import dk.itu.mario.engine.sonar.SoundProducer;
import dk.itu.mario.engine.sonar.SoundSource;
public class Sound implements Comparable
{
private static final double l10 = Math.log(10);
private SoundProducer producer;
private SoundSource source;
private float volume;
private float priority;
private float x, y, z;
private float score = 0;
public float pan;
public float amplitude;
public Sound(SoundProducer producer, SoundSource source, float volume, float priority)
{
this.producer = producer;
this.source = source;
this.volume = volume;
this.priority = priority;
}
public void update(SoundListener listener, float alpha)
{
x = source.getX(alpha)-listener.getX(alpha);
y = source.getY(alpha)-listener.getY(alpha);
float distSqr = x*x+y*y+z*z;
float dist = (float)Math.sqrt(distSqr);
float REFERENCE_DISTANCE = 1;
float ROLLOFF_FACTOR = 2;
// float dB = (float)(volume + (20 * (Math.log(1.0 / distSqr) / l10)));
float dB = (float)(volume - 20*Math.log(1 + ROLLOFF_FACTOR*(dist-REFERENCE_DISTANCE)/REFERENCE_DISTANCE )/ l10);
dB = Math.min(dB, +6);
// dB = Math.max(dB, MIN_GAIN);
score = dB*priority;
// double angle = WMath.atan2(y, x);
float p = -x/320.0f;
if (p<-1) p = -1;
if (p>1) p = 1;
float dd = distSqr/16;
if (dd>1) dd = 1;
pan =(p*dd);
amplitude = volume*1f;
}
public void read(float[] buf, int readRate)
{
producer.read(buf, readRate);
}
public void skip(int samplesToSkip, int readRate)
{
producer.skip(samplesToSkip, readRate);
}
public boolean isLive()
{
return producer.isLive();
}
public int compareTo(Object o)
{
Sound s = (Sound)o;
if (s.score>score) return 1;
if (s.score<score) return -1;
return 0;
}
}

Binary file not shown.

View File

@@ -0,0 +1,114 @@
package dk.itu.mario.engine.sonar.sample;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.UnsupportedAudioFileException;
public class SampleLoader
{
/**
* Loads a sample from an url
*/
public static SonarSample loadSample(String resourceName) throws UnsupportedAudioFileException, IOException
{
// Hack to prevent "mark/reset not supported" on some systems
InputStream in=SampleLoader.class.getResourceAsStream(resourceName);
byte[] d = rip(in);
AudioInputStream ais = AudioSystem.getAudioInputStream(new ByteArrayInputStream(d));
return buildSample(rip(ais), ais.getFormat());
}
/**
* Rips the entire contents of an inputstream into a byte array
*/
private static byte[] rip(InputStream in) throws IOException
{
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] b = new byte[4096];
int read = 0;
while ((read = in.read(b)) > 0)
{
bos.write(b, 0, read);
}
bos.close();
return bos.toByteArray();
}
/**
* Reorganizes audio sample data into the intenal sonar format
*/
private static SonarSample buildSample(byte[] b, AudioFormat af) throws UnsupportedAudioFileException
{
// Rip audioformat data
int channels = af.getChannels();
int sampleSize = af.getSampleSizeInBits();
float rate = af.getFrameRate();
boolean signed = af.getEncoding() == AudioFormat.Encoding.PCM_SIGNED;
// Sanity checking
if (channels != 1) throw new UnsupportedAudioFileException("Only mono samples are supported");
if (!(sampleSize == 8 || sampleSize == 16 || sampleSize == 32)) throw new UnsupportedAudioFileException("Unsupported sample size");
if (!(af.getEncoding() == AudioFormat.Encoding.PCM_UNSIGNED || af.getEncoding() == AudioFormat.Encoding.PCM_SIGNED)) throw new UnsupportedAudioFileException("Unsupported encoding");
// Wrap the data into a bytebuffer, and set up the byte order
ByteBuffer bb = ByteBuffer.wrap(b);
bb.order(af.isBigEndian() ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN);
int s = b.length / (sampleSize / 8);
float[] buf = new float[s];
// Six different cases for reordering the data. Can this be improved without slowing it down?
if (sampleSize == 8)
{
if (signed)
{
for (int i = 0; i < s; i++)
buf[i] = bb.get() / (float)0x80;
}
else
{
for (int i = 0; i < s; i++)
buf[i] = ((bb.get()&0xFF)-0x80) / (float)0x80;
}
}
else if (sampleSize == 16)
{
if (signed)
{
for (int i = 0; i < s; i++)
buf[i] = bb.getShort() / (float)0x8000;
}
else
{
for (int i = 0; i < s; i++)
buf[i] = ((bb.getShort()&0xFFFF)-0x8000) / (float)0x8000;
}
}
else if (sampleSize == 32)
{
if (signed)
{
for (int i = 0; i < s; i++)
buf[i] = bb.getInt() / (float)0x80000000;
}
else
{
// Nasty.. check this.
for (int i = 0; i < s; i++)
buf[i] = ((bb.getInt()&0xFFFFFFFFl)-0x80000000l) / (float)0x80000000;
}
}
// Return the completed sample
return new SonarSample(buf, rate);
}
}

View File

@@ -0,0 +1,57 @@
package dk.itu.mario.engine.sonar.sample;
import dk.itu.mario.engine.sonar.SoundProducer;
public class SamplePlayer implements SoundProducer
{
private SonarSample sample;
private float pos = 0;
public boolean alive = true;
private float rate;
public SamplePlayer(SonarSample sample, float rate)
{
this.rate = rate;
this.sample = sample;
}
public float read(float[] buf, int readRate)
{
float step = (sample.rate*rate)/readRate;
for (int i=0; i<buf.length; i++)
{
if (pos>=sample.buf.length)
{
buf[i] = 0;
alive = false;
}
else
{
buf[i]=sample.buf[(int)(pos)];
}
pos+=step;
}
return 1;
}
public void skip(int samplesToSkip, int readRate)
{
float step = sample.rate/readRate;
pos+=step*samplesToSkip;
if (pos>=sample.buf.length)
{
alive = false;
}
}
public boolean isLive()
{
return alive;
}
}

View File

@@ -0,0 +1,13 @@
package dk.itu.mario.engine.sonar.sample;
public class SonarSample
{
public final float[] buf;
public final float rate;
public SonarSample(float[] buf, float rate)
{
this.buf = buf;
this.rate = rate;
}
}