I haven't experience to use javascript. But I want to demonstrate sound reduction or cancellation to high school students by using single frequency sound in class. I've searched sound generator & detection code in website. Now I can find out frequency but I cannot make phase shifting sound to reduce sound. Could you help me advices to make phase shifting sound to reduce sound?
//Single frequency sound generator
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
public class MakeSound {
public static void main(String[] args) throws LineUnavailableException {
System.out.println("Generate Noise!");
byte[] buf = new byte[2];
int samplingsize = 44100;
AudioFormat af = new AudioFormat((float) samplingsize, 16, 1, true, false);
SourceDataLine sdl = AudioSystem.getSourceDataLine(af);
sdl.open();
sdl.start();
int duration = 500000; // noise generating duration [ms]
int noise_frequency = 315; // noise frequency
System.out.println("Noise Frequency:"+noise_frequency+"Hz");
for (int i = 0; i < duration*(float) 44100/1000; i++) {
float numberOfSamplesToRepresentFullSin = (float) samplingsize / noise_frequency;
double angle = i / (numberOfSamplesToRepresentFullSin/ 2.0) * Math.PI;
short a = (short) (Math.sin(angle) * 32767); //32767 - max value for sample to take (-32767 to 32767)
buf[0] = (byte) (a & 0xFF);
buf[1] = (byte) (a >> 8);
sdl.write(buf, 0, 2);
}
sdl.drain();
sdl.stop();
}
}
//Frequency detection & phase shifting sound generator
package fft_1;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.TargetDataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
import org.apache.commons.math3.complex.Complex;
import org.apache.commons.math3.transform.DftNormalization;
import org.apache.commons.math3.transform.FastFourierTransformer;
import org.apache.commons.math3.transform.TransformType;
@SuppressWarnings("unused")
public class AudioInput {
TargetDataLine microphone;
final int audioFrames= 8192; //power ^ 2
final float sampleRate= 8000.0f;
final int bitsPerRecord= 16;
final int channels= 1;
final boolean bigEndian = true;
final boolean signed= true;
byte byteData[]; // length=audioFrames * 2
double doubleData[]; // length=audioFrames only reals needed for apache lib.
AudioFormat format;
FastFourierTransformer transformer;
public AudioInput () {
byteData= new byte[audioFrames * 2]; //two bytes per audio frame, 16 bits
doubleData= new double[audioFrames * 2]; // real & imaginary
doubleData= new double[audioFrames]; // only real for apache
transformer = new FastFourierTransformer(DftNormalization.STANDARD);
System.out.print("Microphone initialization\n");
format = new AudioFormat(sampleRate, bitsPerRecord, channels, signed, bigEndian);
DataLine.Info info = new DataLine.Info(TargetDataLine.class, format); // format is an AudioFormat object
if (!AudioSystem.isLineSupported(info)) {
System.err.print("isLineSupported failed");
System.exit(1);
}
try {
microphone = (TargetDataLine) AudioSystem.getLine(info);
microphone.open(format);
System.out.print("Microphone opened with format: "+format.toString()+"\n");
microphone.start();
}
catch(Exception ex){
System.out.println("Microphone failed: "+ex.getMessage());
System.exit(1);
}
}
public int readPcm(){
int numBytesRead=
microphone.read(byteData, 0, byteData.length);
if(numBytesRead!=byteData.length){
System.out.println("Warning: read less bytes than buffer size");
System.exit(1);
}
return numBytesRead;
}
@SuppressWarnings({ })
public void byteToDouble(){
ByteBuffer buf= ByteBuffer.wrap(byteData);
buf.order(ByteOrder.BIG_ENDIAN);
int i=0;
while(buf.remaining()>2){
short s = buf.getShort();
doubleData[ i ] = (new Short(s)).doubleValue();
++i;
}
System.out.println("Parsed "+i+" doubles from "+byteData.length+" bytes");
}
public void findFrequency() throws LineUnavailableException{
float frequency;
Complex[] cmplx= transformer.transform(doubleData, TransformType.FORWARD);
double real = 0;
double im = 0;
double mag[] = new double[cmplx.length];
byte[] buf = new byte[2];
int samplingsize = 44100;
AudioFormat af = new AudioFormat((float) samplingsize, 16, 1, true, false);
SourceDataLine sdl = AudioSystem.getSourceDataLine(af);
sdl.open();
sdl.start();
for(int i = 0; i < cmplx.length; i++){
real = cmplx[i].getReal();
im = cmplx[i].getImaginary();
mag[i] = Math.sqrt((real * real) + (im*im));
}
double peak = -1.0;
int index=-1;
for(int i = 0; i < cmplx.length; i++){
if(peak < mag[i]){
index=i;
peak= mag[i];
}
}
frequency = (sampleRate * index) / audioFrames;
System.out.print("Index: "+index+", Frequency: "+frequency+"\n");
int duration = 3000; // duration millisecond
int beatpersec = (int) Math.round(frequency);
for (int i = 0; i < frequency/2 ; i++) {
System.out.println("i"+i);
}
for (int i = 0; i < duration*(float) 44100/1000; i++) {
float numberOfSamplesToRepresentFullSin = (float) samplingsize / beatpersec;
double angle = i / (numberOfSamplesToRepresentFullSin/ 2.0) * Math.PI;
short a = (short) (Math.sin(angle) * 32767); //32767 - max value for sample to take (-32767 to 32767)
buf[0] = (byte) (a & 0xFF);
buf[1] = (byte) (a >> 8);
sdl.write(buf, 0, 2);
}
sdl.drain();
sdl.stop();
}
public void printFreqs(){
for (int i=0; i<audioFrames/4; i++){
//System.out.println("bin "+i+", freq: "+(sampleRate*i)/audioFrames);
System.out.println("End");
}
}
public static void main(String[] args) throws LineUnavailableException {
AudioInput ai= new AudioInput();
int turns=1;
while(turns-- > 0){
ai.readPcm();
ai.byteToDouble();
ai.findFrequency();
}
ai.printFreqs();
}
}
I'm just looking at the MakeSound
class. I'm assuming if we had a controlled way of altering its phase, that would be sufficient for your needs.
First off, include a slider control in the project. It's output should go from 0 to one full period, depending on your "angle" units. If it's degrees, it could be 0 to 359.
Put the sdl.write
method in its own thread, inside a while
loop and keep it running continuously.
Make a class or function that provides the "next" block of sine data on demand. If you need an array size for the write, something like 4K might be a good starting guess. In my experience, anything from 1k to 8k works fine. The while loop holding the sdl calls this function once per each write operation.
Now, your angle
value in your data on-demand function needs to be determined by adding two parts: (1) the part that cycles on pitch continously (similar to what you are already doing), (2) a "phase" variable that holds an angle value that can range from 0 to one full period.
Have the slider tied to the "phase" variable. Probably some form of loose coupling would be good, to prevent the changes to the "phase" variable from blocking the sine-wave calculation.
A couple of cautions, though. For one, as you move the slider, you will likely create some clicks unless you build in a function to spread out the changes in the "phase" value over, say, 128 PCM values. Secondly, the volumes have to match for true cancellation, so a volume slider as well as the phase slider might be needed. The "volume" slider can range from 0 to 1, creating a factor that you multiply against the PCM values that you are holding in the short
array.
The main thing, since there is a single starting point for this continuous signal, (thanks to running the sdl continuously in the while loop), there should be some point on the slide that best corresponds to the cancellation. It will be a different point on the slider each time, of course.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.