Chris Lacy's Software Engineering Blog
Saturday Jun 26, 2010
music test
package net.chrislacy.music.test;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.InputStreamReader;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
public class MusicTest {
private static final double[] NOTES = { 261.626, 293.665, 329.628, 349.228, 391.995, 440.000, 493.883, 523.251 };
private static final Set<Integer> INCLUDE = new HashSet<Integer>();
private static final double VOLUME = 0.03;
private static final long LENGTH_MS = 1500;
static {
INCLUDE.add(1);
INCLUDE.add(3);
//INCLUDE.add(4);
INCLUDE.add(5);
//INCLUDE.add(8);
}
public static void main(String[] args) throws Throwable {
// TODO add fails to "more often test" list
// print solution after x (3) wrong guesses
while(true) {
List<Integer> notes = playRandomPhrase(3, VOLUME);
InputStreamReader converter = new InputStreamReader(System.in);
BufferedReader in = new BufferedReader(converter);
boolean shouldRepeat = true;
while(shouldRepeat) {
String line = in.readLine();
int index = 0;
boolean passed = true;
inner:
for(Integer n : notes) {
if(line.length() != notes.size() || Integer.parseInt(line.charAt(index++) + "") != n) {
System.out.println("nope");
playPhrase(VOLUME, createSimilarNotes(LENGTH_MS, 100, hertzFromOneBased(notes)));
passed = false;
break inner;
}
}
shouldRepeat = !passed;
}
}
}
private static double[] hertzFromOneBased(List<Integer> oneBased) {
double[] hertzs = new double[oneBased.size()];
int index = 0;
for(Integer oneB : oneBased) {
hertzs[index++] = NOTES[oneB - 1];
}
return hertzs;
}
private static List<Integer> playRandomPhrase(int length, double volume) {
List<Integer> result = new LinkedList<Integer>();
double[] hertzs = new double[length];
result.add(1);
hertzs[0] = NOTES[0];
for(int ii=1; ii<length; ii++) {
int cur = randomIntLessThan(NOTES.length);
int oneBasedCur = cur + 1;
while(!INCLUDE.contains(oneBasedCur)) {
cur = randomIntLessThan(NOTES.length);
oneBasedCur = cur + 1;
}
result.add(oneBasedCur);
hertzs[ii] = NOTES[cur];
}
playPhrase(volume, createSimilarNotes(LENGTH_MS, 100, hertzs));
return result;
}
private static int randomIntLessThan(int max) {
double r = Math.random();
return (int) Math.round(r * max);
}
private static ComplexNote[] createSimilarNotes(long length, int relativeVolume, double... hertz) {
ComplexNote[] result = new ComplexNote[hertz.length];
for(int ii=0; ii<hertz.length; ii++) {
result[ii] = new ComplexNote(hertz[ii], length, relativeVolume);
}
return result;
}
private static byte[] createOneWave(int numberOfBytes, double maxHeight) {
byte[] oneWave = new byte[numberOfBytes];
for(int i=0; i<numberOfBytes; i++) {
double fractionalPos = ((double)i*2) / numberOfBytes;
double angle = fractionalPos * Math.PI;
double sineVal = Math.sin(angle);
Double volSine = sineVal * maxHeight;
byte b = volSine.byteValue();
oneWave[i] = b;
}
return oneWave;
}
private static byte[] makeWaveTwoChannel(byte[] oneWave) {
byte[] stereoWave = new byte[oneWave.length * 2];
for(int i=0; i<oneWave.length; i++) {
int stereoPos = i*2;
stereoWave[stereoPos] = stereoWave[stereoPos+1] = oneWave[i];
}
return stereoWave;
}
private static byte[] repeatWave(byte[] original, int numberOfWaves) {
byte[] multipleStereoWaves = new byte[original.length * numberOfWaves];
for(int i=0; i < numberOfWaves; i++) {
System.arraycopy(original, 0, multipleStereoWaves, original.length * i, original.length);
}
return multipleStereoWaves;
}
private static byte[] createMultipleStereoWaves(int bytesPerWave, double volume, int numberOfWaves) {
byte[] oneWave = createOneWave(bytesPerWave, volume);
byte[] stereoWave = makeWaveTwoChannel(oneWave);
return repeatWave(stereoWave, numberOfWaves);
}
private static Clip createClip(AudioInputStream ais) {
Clip clip;
try {
clip = AudioSystem.getClip();
clip.open(ais);
clip.setFramePosition(0);
}
catch (Exception e) {
throw new RuntimeException(e);
}
return clip;
}
/**
* @param volume 1.000104 min vol headphones @ 220 hz
*/
private static AudioInputStream createAudioInputStream(double volume, ComplexNote... notes) {
float bytesPerSecond = 48000f;
AudioFormat af = new AudioFormat(bytesPerSecond,
8, // sample size in bits
2, // channels
true, // signed
false // bigendian
);
List<byte[]> waveList = new LinkedList<byte[]>();
int finalLength = 0;
for(ComplexNote note : notes) {
Double bpw = bytesPerSecond/note.getHertz();
int bytesPerWave = bpw.intValue();
int numberOfWaves = 800/bpw.intValue() + 5; // 763
byte[] multipleStereoWaves = createMultipleStereoWaves(bytesPerWave, note.getRelativeVolume() * volume, numberOfWaves);
int numBytesNeeded = ((Float) (bytesPerSecond * (note.getLength()/1000))).intValue();
byte[] noteBuffer = new byte[numBytesNeeded];
int repeatNum = numBytesNeeded / multipleStereoWaves.length;
int ii=0;
for(; ii<repeatNum; ii++) {
System.arraycopy(multipleStereoWaves, 0, noteBuffer, multipleStereoWaves.length * ii, multipleStereoWaves.length);
}
System.arraycopy(multipleStereoWaves, 0, noteBuffer, multipleStereoWaves.length * ii, numBytesNeeded % multipleStereoWaves.length);
waveList.add(noteBuffer);
finalLength += noteBuffer.length;
}
byte[] finalBa = new byte[finalLength];
int index = 0;
for(byte[] cur : waveList) {
System.arraycopy(cur, 0, finalBa, index, cur.length);
index = index + cur.length;;
}
return new AudioInputStream(new ByteArrayInputStream(finalBa), af, finalBa.length/2);
}
private static void playPhrase(double volume, ComplexNote... notes) {
Clip clip = createClip(createAudioInputStream(volume, notes));
clip.loop(0);
clip.drain();
clip.flush();
clip.close();
}
}
Posted at 12:31PM Jun 26, 2010 by chris in Java |
Comments: