Browse Source

Rework the way noise is handled.

master
James Childers 13 years ago
parent
commit
3ec1c31379
  1. 73
      Java/src/nl/captcha/audio/AudioCaptcha.java
  2. 11
      Java/src/nl/captcha/audio/Mixer.java
  3. 7
      Java/src/nl/captcha/audio/Sample.java
  4. 4
      Java/src/nl/captcha/audio/noise/NoiseProducer.java
  5. 42
      Java/src/nl/captcha/audio/noise/RandomNoiseProducer.java
  6. 6
      Java/src/nl/captcha/text/producer/NumbersAnswerProducer.java
  7. 6
      Java/src/nl/captcha/util/FileUtil.java
  8. BIN
      sounds/noises/walkie_talkie_garble.wav

73
Java/src/nl/captcha/audio/AudioCaptcha.java

@ -9,7 +9,7 @@ import nl.captcha.audio.noise.RandomNoiseProducer;
import nl.captcha.audio.noise.NoiseProducer;
import nl.captcha.audio.producer.RandomNumberVoiceProducer;
import nl.captcha.audio.producer.VoiceProducer;
import nl.captcha.text.producer.NumberAnswerProducer;
import nl.captcha.text.producer.NumbersAnswerProducer;
import nl.captcha.text.producer.TextProducer;
public final class AudioCaptcha {
@ -26,15 +26,17 @@ public final class AudioCaptcha {
public static class Builder {
private String _answer = "";
private List<VoiceProducer> _vProds;
private NoiseProducer _noiseProd;
private Sample _challenge;
private List<VoiceProducer> _voiceProds;
private List<NoiseProducer> _noiseProds;
public Builder() {
_vProds = new ArrayList<VoiceProducer>();
_voiceProds = new ArrayList<VoiceProducer>();
_noiseProds = new ArrayList<NoiseProducer>();
}
public Builder addAnswer() {
return addAnswer(new NumberAnswerProducer());
return addAnswer(new NumbersAnswerProducer());
}
public Builder addAnswer(TextProducer ansProd) {
@ -43,8 +45,14 @@ public final class AudioCaptcha {
return this;
}
public Builder addVoice() {
_voiceProds.add(new RandomNumberVoiceProducer());
return this;
}
public Builder addVoice(VoiceProducer vProd) {
_vProds.add(vProd);
_voiceProds.add(vProd);
return this;
}
@ -54,17 +62,43 @@ public final class AudioCaptcha {
}
public Builder addNoise(NoiseProducer noiseProd) {
_noiseProd = noiseProd;
_noiseProds.add(noiseProd);
return this;
}
public AudioCaptcha build() {
// Make sure there is at least one voice producer
if (_vProds.size() == 0) {
_vProds.add(new RandomNumberVoiceProducer());
// Make sure we have at least one voiceProducer
if (_voiceProds.size() == 0) {
addVoice();
}
// Convert answer to an array
char[] ansAry = _answer.toCharArray();
// Make a List of Samples for each character
VoiceProducer vProd;
List<Sample> samples = new ArrayList<Sample>();
Sample sample;
for (int i = 0; i < ansAry.length; i++) {
// Create Sample for this character from one of the
// VoiceProducers
vProd = _voiceProds.get(RAND.nextInt(_voiceProds.size()));
sample = vProd.getVocalization(ansAry[i]);
samples.add(sample);
}
// 3. Add noise, if any, and return the result
if (_noiseProds.size() > 0) {
NoiseProducer nProd = _noiseProds.get(RAND.nextInt(_noiseProds
.size()));
_challenge = nProd.addNoise(samples);
return new AudioCaptcha(this);
}
_challenge = Mixer.append(samples);
return new AudioCaptcha(this);
}
@ -87,24 +121,7 @@ public final class AudioCaptcha {
}
public Sample getChallenge() {
// 1. Convert answer to an array
char[] ansAry = getAnswer().toCharArray();
VoiceProducer vProd;
List<Sample> samples = new ArrayList<Sample>();
for (int i = 0; i < ansAry.length; i++) {
vProd = _builder._vProds.get(RAND.nextInt(_builder._vProds.size()));
samples.add(vProd.getVocalization(ansAry[i]));
}
// 2. Append the voices one to the other
Sample appended = Mixer.append(samples);
// 3. Add noise
if (_builder._noiseProd != null) {
appended = _builder._noiseProd.addNoise(appended);
}
return appended;
return _builder._challenge;
}
@Override public String toString() {

11
Java/src/nl/captcha/audio/Mixer.java

@ -32,11 +32,13 @@ public class Mixer {
return buildSample(sampleCount, appended);
}
public final static Sample mix(Sample sample1, Sample sample2) {
public final static Sample mix(Sample sample1, double volAdj1,
Sample sample2, double volAdj2) {
double[] s1_ary = sample1.getInterleavedSamples();
double[] s2_ary = sample2.getInterleavedSamples();
double[] mixed = mix(s1_ary, s2_ary);
//
double[] mixed = mix(s1_ary, volAdj1, s2_ary, volAdj2);
return buildSample(sample1.getSampleCount(), mixed);
}
@ -55,13 +57,14 @@ public class Mixer {
return result;
}
private static final double[] mix(double[] sample1, double[] sample2) {
private static final double[] mix(double[] sample1, double volAdj1, double[] sample2,
double volAdj2) {
for (int i = 0; i < sample1.length; i++) {
if (i >= sample2.length) {
sample1[i] = 0;
break;
}
sample1[i] = (sample1[i] + (sample2[i] * 2));
sample1[i] = (sample1[i] * volAdj1) + (sample2[i] * volAdj2);
}
return sample1;
}

7
Java/src/nl/captcha/audio/Sample.java

@ -178,9 +178,10 @@ public class Sample {
/**
* Helper method to convert a double[] to a byte[] in a format that can be
* used by <code>AudioInputStream</code>. Typically, this will be used with
* a <code>sample</code> that has been modified from its original.
*
* used by {@link AudioInputStream}. Typically this will be used with
* a {@link Sample} that has been modified from its original.
*
*
* @see <a href="http://en.wiktionary.org/wiki/yak_shaving">Yak Shaving</a>
*
* @return

4
Java/src/nl/captcha/audio/noise/NoiseProducer.java

@ -1,7 +1,9 @@
package nl.captcha.audio.noise;
import java.util.List;
import nl.captcha.audio.Sample;
public interface NoiseProducer {
public Sample addNoise(Sample target);
public Sample addNoise(List<Sample> target);
}

42
Java/src/nl/captcha/audio/noise/RandomNoiseProducer.java

@ -1,6 +1,7 @@
package nl.captcha.audio.noise;
import java.security.SecureRandom;
import java.util.List;
import java.util.Random;
import nl.captcha.audio.Mixer;
@ -11,29 +12,44 @@ import nl.captcha.util.FileUtil;
* Adds noise to a {@link Sample} from one of the given <code>noiseFiles</code>.
*
* @author <a href="mailto:james.childers@gmail.com">James Childers</a>
*
*
*/
public class RandomNoiseProducer implements NoiseProducer {
private static final Random RAND = new SecureRandom();
private static final String[] DEFAULT_NOISES = { "/sounds/noises/restaurant.wav",
"/sounds/noises/zombie.wav",
};
private static final String[] DEFAULT_NOISES = {
"/sounds/noises/radio_tuning.wav",
"/sounds/noises/restaurant.wav",
"/sounds/noises/swimming.wav", };
private final String _noiseFiles[];
private final String _noiseFile;
public RandomNoiseProducer() {
this(DEFAULT_NOISES);
}
public RandomNoiseProducer(String[] noiseFiles) {
_noiseFiles = noiseFiles;
_noiseFile = _noiseFiles[RAND.nextInt(_noiseFiles.length)];
}
@Override public Sample addNoise(Sample target) {
String file = _noiseFiles[RAND.nextInt(_noiseFiles.length)];
Sample noiseSample = FileUtil.readSample(file);
return Mixer.mix(target, noiseSample);
@Override public Sample addNoise(List<Sample> samples) {
Sample appended = Mixer.append(samples);
Sample noise = FileUtil.readSample(_noiseFile);
// Decrease the volume of the noise to make sure the voices can be heard
return Mixer.mix(appended, 1.0, noise, 0.6);
}
@Override public String toString() {
StringBuffer sb = new StringBuffer();
sb.append("[File: ");
sb.append(_noiseFile);
sb.append("][Files to choose from: ");
sb.append(_noiseFiles);
sb.append("]");
return sb.toString();
}
}

6
Java/src/nl/captcha/text/producer/NumberAnswerProducer.java → Java/src/nl/captcha/text/producer/NumbersAnswerProducer.java

@ -7,7 +7,7 @@ package nl.captcha.text.producer;
* @author <a href="mailto:james.childers@gmail.com">James Childers</a>
*
*/
public class NumberAnswerProducer implements TextProducer {
public class NumbersAnswerProducer implements TextProducer {
private static final int DEFAULT_LENGTH = 5;
private static final char[] NUMBERS = { '0', '1', '2', '3', '4', '5', '6', '7',
@ -15,11 +15,11 @@ public class NumberAnswerProducer implements TextProducer {
private final TextProducer _txtProd;
public NumberAnswerProducer() {
public NumbersAnswerProducer() {
this(DEFAULT_LENGTH);
}
public NumberAnswerProducer(int length) {
public NumbersAnswerProducer(int length) {
_txtProd = new DefaultTextProducer(length, NUMBERS);
}

6
Java/src/nl/captcha/util/FileUtil.java

@ -2,6 +2,7 @@ package nl.captcha.util;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
@ -18,6 +19,11 @@ public class FileUtil {
*/
public static final InputStream readResource(String filename) {
InputStream jarIs = FileUtil.class.getResourceAsStream(filename);
if (jarIs == null) {
throw new RuntimeException(new FileNotFoundException("File '"
+ filename + "' not found."));
}
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
byte[] data = new byte[16384];

BIN
sounds/noises/walkie_talkie_garble.wav

Loading…
Cancel
Save