/*
* GPLv2 or 3, Copyright (c) 2010 Andrzej Zaborowski
*
* This class simulates a car engine. What does a car engine do? It
* makes a pc-speaker-like buzz. The PC Speaker could only emit
* a (nearly) square wave and we simulate it here for maximum realism.
*/
package wmsturbochallenge;
import java.util.Timer;
import java.util.TimerTask;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.SourceDataLine;
class EngineSound {
public EngineSound() {
rpm = 0.0;
}
public void start() {
rpm = 0.3;
speed = 0.0;
n = 0;
if (output != null)
stop();
AudioFormat output_format =
new AudioFormat(S_RATE, 16, 1, true, true);
DataLine.Info info =
new DataLine.Info(SourceDataLine.class, output_format);
/* Get the data line, open it and initialise the device */
try {
output = (SourceDataLine) AudioSystem.getLine(info);
output.open(output_format);
output.start();
frames_written = 0;
reschedule(0);
} catch (Exception e) {
output = null;
System.out.println("Audio not available: " +
e.getClass().getSimpleName());
}
}
public void stop() {
rpm = 0.0;
n = 0;
if (output == null)
return;
tick.cancel();
tick.purge();
output.stop();
output.flush();
output.close();
output = null;
}
public void set_speed(double speed) {
/* This engine is equipped with an automatic gear box that
* switches gears when the RPM becomes too high or too low. */
double new_speed = Math.abs(speed);
double accel = new_speed - this.speed;
this.speed = new_speed;
if (accel > 0.05)
accel = 0.05;
else if (accel < -0.05)
accel = -0.05;
rpm += accel;
if (accel > 0.0 && rpm > 1.0 + n * 0.2 && speed > 0.0) {
rpm = 0.3 + n * 0.2;
n ++;
} else if (accel < 0.0 && rpm < 0.3) {
if (n > 0) {
rpm = 0.7 + n * 0.1;
n --;
} else
rpm = 0.2;
}
if (speed < 2.0)
n = 0;
}
public boolean is_on() {
return output != null;
}
protected double speed;
protected double rpm;
protected int n;
protected SourceDataLine output = null;
protected long frames_written;
protected Timer tick = new Timer();
/* Audio parameters. */
protected static final int S_RATE = 44100;
protected static final int MIN_BUFFER = 4096;
protected static final double volume = 0.3;
protected class audio_task extends TimerTask {
@Override
public void run() {
if (output == null)
return;
/* If more than a two buffers left to play,
* reschedule and try to wake up closer to the
* end of already written data. */
long frames_current = output.getLongFramePosition();
if (frames_current < frames_written - MIN_BUFFER * 2) {
reschedule(frames_current);
return;
}
/* Build a new buffer */
/* double freq = 20 * Math.pow(1.3, rpm * 5.0); */
double freq = (rpm - 0.1) * 160.0;
int wavelen = (int) (S_RATE / freq);
int bufferlen = MIN_BUFFER - (MIN_BUFFER % wavelen) +
wavelen;
int value = (int) (0x7fff * volume);
bufferlen *= 2;
byte[] buffer = new byte[bufferlen];
for (int b = 0; b < bufferlen; ) {
int j;
for (j = wavelen / 2; j > 0; j --) {
buffer[b ++] = (byte) (value >> 8);
buffer[b ++] = (byte) (value & 0xff);
}
value = 0x10000 - value;
for (j = wavelen - wavelen / 2; j > 0; j --) {
buffer[b ++] = (byte) (value >> 8);
buffer[b ++] = (byte) (value & 0xff);
}
value = 0x10000 - value;
}
frames_written +=
output.write(buffer, 0, bufferlen) / 2;
reschedule(frames_current);
}
}
protected void reschedule(long frames) {
/* Send a new buffer as close to the end of the
* currently playing buffer as possible (aim at
* about half into the last frame). */
long delay = (frames_written - frames - MIN_BUFFER / 2) *
1000 / S_RATE;
if (delay < 0)
delay = 0;
tick.schedule(new audio_task(), delay);
}
}