[19990] | 1 | /*
|
---|
| 2 | * GPLv2 or 3, Copyright (c) 2010 Andrzej Zaborowski
|
---|
| 3 | */
|
---|
| 4 | package wmsturbochallenge;
|
---|
| 5 |
|
---|
| 6 | import java.util.Timer;
|
---|
| 7 | import java.util.TimerTask;
|
---|
| 8 |
|
---|
[32322] | 9 | import javax.sound.sampled.AudioFormat;
|
---|
[19990] | 10 | import javax.sound.sampled.AudioSystem;
|
---|
| 11 | import javax.sound.sampled.DataLine;
|
---|
| 12 | import javax.sound.sampled.SourceDataLine;
|
---|
| 13 |
|
---|
[33342] | 14 | /**
|
---|
| 15 | * This class simulates a car engine. What does a car engine do? It
|
---|
| 16 | * makes a pc-speaker-like buzz. The PC Speaker could only emit
|
---|
| 17 | * a (nearly) square wave and we simulate it here for maximum realism.
|
---|
| 18 | */
|
---|
[32322] | 19 | class EngineSound {
|
---|
[33342] | 20 | EngineSound() {
|
---|
[23190] | 21 | rpm = 0.0;
|
---|
| 22 | }
|
---|
[19990] | 23 |
|
---|
[23190] | 24 | public void start() {
|
---|
| 25 | rpm = 0.3;
|
---|
| 26 | speed = 0.0;
|
---|
| 27 | n = 0;
|
---|
[19990] | 28 |
|
---|
[23190] | 29 | if (output != null)
|
---|
| 30 | stop();
|
---|
[19990] | 31 |
|
---|
[23190] | 32 | AudioFormat output_format =
|
---|
[32914] | 33 | new AudioFormat(S_RATE, 16, 1, true, true);
|
---|
[23190] | 34 | DataLine.Info info =
|
---|
[32914] | 35 | new DataLine.Info(SourceDataLine.class, output_format);
|
---|
[19990] | 36 |
|
---|
[23190] | 37 | /* Get the data line, open it and initialise the device */
|
---|
| 38 | try {
|
---|
| 39 | output = (SourceDataLine) AudioSystem.getLine(info);
|
---|
| 40 | output.open(output_format);
|
---|
| 41 | output.start();
|
---|
| 42 | frames_written = 0;
|
---|
| 43 | reschedule(0);
|
---|
| 44 | } catch (Exception e) {
|
---|
| 45 | output = null;
|
---|
| 46 | System.out.println("Audio not available: " +
|
---|
| 47 | e.getClass().getSimpleName());
|
---|
| 48 | }
|
---|
| 49 | }
|
---|
[19990] | 50 |
|
---|
[23190] | 51 | public void stop() {
|
---|
| 52 | rpm = 0.0;
|
---|
| 53 | n = 0;
|
---|
[19990] | 54 |
|
---|
[23190] | 55 | if (output == null)
|
---|
| 56 | return;
|
---|
[19990] | 57 |
|
---|
[23190] | 58 | tick.cancel();
|
---|
| 59 | tick.purge();
|
---|
[19990] | 60 |
|
---|
[23190] | 61 | output.stop();
|
---|
| 62 | output.flush();
|
---|
| 63 | output.close();
|
---|
| 64 | output = null;
|
---|
| 65 | }
|
---|
[19990] | 66 |
|
---|
[23190] | 67 | public void set_speed(double speed) {
|
---|
| 68 | /* This engine is equipped with an automatic gear box that
|
---|
| 69 | * switches gears when the RPM becomes too high or too low. */
|
---|
| 70 | double new_speed = Math.abs(speed);
|
---|
| 71 | double accel = new_speed - this.speed;
|
---|
| 72 | this.speed = new_speed;
|
---|
[19990] | 73 |
|
---|
[23190] | 74 | if (accel > 0.05)
|
---|
| 75 | accel = 0.05;
|
---|
| 76 | else if (accel < -0.05)
|
---|
| 77 | accel = -0.05;
|
---|
| 78 | rpm += accel;
|
---|
[19990] | 79 |
|
---|
[23190] | 80 | if (accel > 0.0 && rpm > 1.0 + n * 0.2 && speed > 0.0) {
|
---|
| 81 | rpm = 0.3 + n * 0.2;
|
---|
[33342] | 82 | n++;
|
---|
[23190] | 83 | } else if (accel < 0.0 && rpm < 0.3) {
|
---|
| 84 | if (n > 0) {
|
---|
| 85 | rpm = 0.7 + n * 0.1;
|
---|
[33342] | 86 | n--;
|
---|
[23190] | 87 | } else
|
---|
| 88 | rpm = 0.2;
|
---|
| 89 | }
|
---|
| 90 | if (speed < 2.0)
|
---|
| 91 | n = 0;
|
---|
| 92 | }
|
---|
[19990] | 93 |
|
---|
[23190] | 94 | public boolean is_on() {
|
---|
| 95 | return output != null;
|
---|
| 96 | }
|
---|
[19990] | 97 |
|
---|
[23190] | 98 | protected double speed;
|
---|
| 99 | protected double rpm;
|
---|
| 100 | protected int n;
|
---|
[19990] | 101 |
|
---|
[23190] | 102 | protected SourceDataLine output = null;
|
---|
| 103 | protected long frames_written;
|
---|
| 104 | protected Timer tick = new Timer();
|
---|
[19990] | 105 |
|
---|
[23190] | 106 | /* Audio parameters. */
|
---|
| 107 | protected static final int S_RATE = 44100;
|
---|
| 108 | protected static final int MIN_BUFFER = 4096;
|
---|
| 109 | protected static final double volume = 0.3;
|
---|
[19990] | 110 |
|
---|
[23190] | 111 | protected class audio_task extends TimerTask {
|
---|
[32914] | 112 | @Override
|
---|
[23190] | 113 | public void run() {
|
---|
| 114 | if (output == null)
|
---|
| 115 | return;
|
---|
[19990] | 116 |
|
---|
[23190] | 117 | /* If more than a two buffers left to play,
|
---|
| 118 | * reschedule and try to wake up closer to the
|
---|
| 119 | * end of already written data. */
|
---|
| 120 | long frames_current = output.getLongFramePosition();
|
---|
| 121 | if (frames_current < frames_written - MIN_BUFFER * 2) {
|
---|
| 122 | reschedule(frames_current);
|
---|
| 123 | return;
|
---|
| 124 | }
|
---|
[19990] | 125 |
|
---|
[23190] | 126 | /* Build a new buffer */
|
---|
| 127 | /* double freq = 20 * Math.pow(1.3, rpm * 5.0); */
|
---|
| 128 | double freq = (rpm - 0.1) * 160.0;
|
---|
| 129 | int wavelen = (int) (S_RATE / freq);
|
---|
| 130 | int bufferlen = MIN_BUFFER - (MIN_BUFFER % wavelen) +
|
---|
[32914] | 131 | wavelen;
|
---|
[23190] | 132 | int value = (int) (0x7fff * volume);
|
---|
[19990] | 133 |
|
---|
[23190] | 134 | bufferlen *= 2;
|
---|
| 135 | byte[] buffer = new byte[bufferlen];
|
---|
[33342] | 136 | for (int b = 0; b < bufferlen;) {
|
---|
[23190] | 137 | int j;
|
---|
[33342] | 138 | for (j = wavelen / 2; j > 0; j--) {
|
---|
| 139 | buffer[b++] = (byte) (value >> 8);
|
---|
| 140 | buffer[b++] = (byte) (value & 0xff);
|
---|
[23190] | 141 | }
|
---|
| 142 | value = 0x10000 - value;
|
---|
[33342] | 143 | for (j = wavelen - wavelen / 2; j > 0; j--) {
|
---|
| 144 | buffer[b++] = (byte) (value >> 8);
|
---|
| 145 | buffer[b++] = (byte) (value & 0xff);
|
---|
[23190] | 146 | }
|
---|
| 147 | value = 0x10000 - value;
|
---|
| 148 | }
|
---|
[19990] | 149 |
|
---|
[23190] | 150 | frames_written +=
|
---|
[32914] | 151 | output.write(buffer, 0, bufferlen) / 2;
|
---|
[19990] | 152 |
|
---|
[23190] | 153 | reschedule(frames_current);
|
---|
| 154 | }
|
---|
| 155 | }
|
---|
[19990] | 156 |
|
---|
[23190] | 157 | protected void reschedule(long frames) {
|
---|
| 158 | /* Send a new buffer as close to the end of the
|
---|
| 159 | * currently playing buffer as possible (aim at
|
---|
| 160 | * about half into the last frame). */
|
---|
| 161 | long delay = (frames_written - frames - MIN_BUFFER / 2) *
|
---|
[32914] | 162 | 1000 / S_RATE;
|
---|
[23190] | 163 | if (delay < 0)
|
---|
| 164 | delay = 0;
|
---|
| 165 | tick.schedule(new audio_task(), delay);
|
---|
| 166 | }
|
---|
[19990] | 167 | }
|
---|