Microphone Streaming is Not Clear - java

I'm trying to stream microphone over UDP but my output is so noisy, it's not able to understand the input audio. Here is my code:
Server Side:
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.Mixer;
import javax.sound.sampled.Port;
import javax.sound.sampled.TargetDataLine;
public class MicPlayer {
private static final String IP_TO_STREAM_TO = "localhost" ;
private static final int PORT_TO_STREAM_TO = 8888 ;
/** Creates a new instance of MicPlayer */
public MicPlayer() {
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
Mixer.Info minfo[] = AudioSystem.getMixerInfo() ;
for( int i = 0 ; i < minfo.length ; i++ )
{
System.out.println( minfo[i] ) ;
}
if (AudioSystem.isLineSupported(Port.Info.MICROPHONE)) {
try {
DataLine.Info dataLineInfo = new DataLine.Info(
TargetDataLine.class , getAudioFormat() ) ;
final TargetDataLine targetDataLine = (TargetDataLine)AudioSystem.getLine( dataLineInfo ) ;
targetDataLine.open( getAudioFormat() );
targetDataLine.start();
byte tempBuffer[] = new byte[targetDataLine.getBufferSize() / 5] ;
int cnt = 0 ;
while( true )
{
targetDataLine.read( tempBuffer , 0 , tempBuffer.length );
sendThruUDP( tempBuffer ) ;
}
}
catch(Exception e )
{
System.out.println(" not correct " ) ;
System.exit(0) ;
}
}
}
public static AudioFormat getAudioFormat(){
float sampleRate = 8000.0F;
//8000,11025,16000,22050,44100
int sampleSizeInBits = 16;
//8,16
int channels = 1;
//1,2
boolean signed = true;
//true,false
boolean bigEndian = false;
//true,false
return new AudioFormat( sampleRate, sampleSizeInBits, channels, signed, bigEndian );
}
public static void sendThruUDP( byte soundpacket[] )
{
try
{
DatagramSocket sock = new DatagramSocket() ;
sock.send( new DatagramPacket( soundpacket , soundpacket.length , InetAddress.getByName( IP_TO_STREAM_TO ) , PORT_TO_STREAM_TO ) ) ;
sock.close() ;
}
catch( Exception e )
{
e.printStackTrace() ;
System.out.println(" Unable to send soundpacket using UDP " ) ;
}
}
}
I don't think client-side has problems but here is the code;
Client Side:
import java.io.DataInputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.SourceDataLine;
public class RadioReceiver extends Thread {
private static final String IP_TO_STREAM_TO = "localhost" ;
private static final int PORT_TO_STREAM_TO = 8888 ;
/** Creates a new instance of RadioReceiver */
public RadioReceiver() {
}
public void run()
{
byte b[] = null ;
while( true )
{
b = receiveThruUDP() ;
toSpeaker( b ) ;
}
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
RadioReceiver r = new RadioReceiver() ;
r.start() ;
}
public static byte[] receiveThruUDP()
{
try
{
DatagramSocket sock = new DatagramSocket(PORT_TO_STREAM_TO) ;
byte soundpacket[] = new byte[1230] ;
DatagramPacket datagram = new DatagramPacket( soundpacket , soundpacket.length , InetAddress.getByName( IP_TO_STREAM_TO ) , PORT_TO_STREAM_TO ) ;
sock.receive( datagram ) ;
sock.close() ; return datagram.getData() ; // soundpacket ;
}
catch( Exception e )
{
System.out.println(" Unable to send soundpacket using UDP " ) ;
return null ;
}
}
public static void toSpeaker( byte soundbytes[] )
{
try{
DataLine.Info dataLineInfo = new DataLine.Info( SourceDataLine.class , getAudioFormat() ) ;
SourceDataLine sourceDataLine = (SourceDataLine)AudioSystem.getLine( dataLineInfo );
sourceDataLine.open( getAudioFormat() ) ;
sourceDataLine.start();
int cnt = 0;
sourceDataLine.write( soundbytes , 0, soundbytes.length );
sourceDataLine.drain() ;
sourceDataLine.close() ;
}
catch(Exception e )
{
System.out.println("not working in speakers " ) ;
}
}
public static AudioFormat getAudioFormat()
{
float sampleRate = 8000.0F;
//8000,11025,16000,22050,44100
int sampleSizeInBits = 16;
//8,16
int channels = 1;
//1,2
boolean signed = true;
//true,false
boolean bigEndian = false;
//true,false
return new AudioFormat( sampleRate, sampleSizeInBits, channels, signed, bigEndian );
}
}
I'm sure that my connection is OK but i don't have any idea why my output is so noisy. It's getting me crazy i'm working on it till 1 week, please help me. Thank You.

The reason is probably that your datagram packets are too small, which causes you to send a whole bunch of packages that creates a lot of overhead. This might result in a huge packet loss rate and make them arrive in the wrong order.
So, make your buffer size bigger:
byte tempBuffer[] = new byte[8192] ;
And this comes from the DatagramSocket.receive() JavaDoc:
This method blocks until a datagram is received. The length field of the datagram packet object contains the length of the received message. If the message is longer than the packet's length, the message is truncated.
This might be a problem as well. Try to use the same size for both sending and receiving packets.
byte soundpacket[] = new byte[8192];
Also, do not continiously open and close the AudioLine to the speakers. Do also not continuously create DatagramSockets. Create one, and keep it.

Related

I can't change the speed of a music file without avoiding that a disturbing noise appear

I am trying to change the speed of an audio file, if I do it with unsigned values all is all right, but once I start using double values things get messy, for instance, my code works with all the "x.5" numbers but it doesn't with any other number with decimals and in my case, I want to increase the speed by 1.3 points. But all I get is a file where you can't barely hear nothing but an annoying noise.
Here is the code that I am using:
import javax.swing.JOptionPane;
import javax.sound.sampled.*;
import java.net.URL;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.ByteArrayInputStream;
import java.util.Date;
class AcceleratePlayback {
public static void main(String[] args) throws Exception {
//double playBackSpeed = 1.5; Works
//double playBackSpeed = 1.3; Doesn't work
File file1= new File("Sample2.wav");
File file2= new File("DEF.wav");
AudioInputStream ais = AudioSystem.getAudioInputStream(file1);
AudioFormat af = ais.getFormat();
int frameSize = af.getFrameSize();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] b = new byte[2^16];
int read = 1;
while( read>-1 ) {
read = ais.read(b);
if (read>0) {
baos.write(b, 0, read);
}
}
byte[] b1 = baos.toByteArray();
byte[] b2 = new byte[(int)(b1.length/playBackSpeed)];
for (int i=0; i<(b2.length/frameSize); i++) {
for (int j=0; j<frameSize; j++) {
b2[(i*frameSize)+j] = b1[(int)((i*frameSize*playBackSpeed)+j)];
}
}
ByteArrayInputStream bais = new ByteArrayInputStream(b2);
AudioInputStream aisAccelerated =
new AudioInputStream(bais, af, b2.length/frameSize);
AudioSystem.write(aisAccelerated, AudioFileFormat.Type.WAVE, file2);
}
}
Your reading-from falls on odd barriers: that is because of truncation the read-from byte starts at an odd location. Use the following to start from even location:
for (i=0; i<(b2.length/frameSize); i++) {
int ind=(int)((i*frameSize*playBackSpeed));
if((ind%2)==1) ind++;
for (j=0; j<frameSize; j++) {
b2[(i*frameSize)+j] = b1[ind+j];
}
}
Or you can change the jump to 4:
if((ind%4)>0) ind+=(4-(ind%4));

My app "find frequency of audio input from microphone" always crashs. But why?

At the end you will find the error message!!
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.TargetDataLine;
Math3 import to use fft.
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;
public class AudioInput {
TargetDataLine microphone;
final int audioFrames= 8196; //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;
}
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(){
double frequency;
Complex[] cmplx= transformer.transform(doubleData, TransformType.FORWARD);
double real;
double im;
double mag[] = new double[cmplx.length];
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");
}
/*
* Print the first frequency bins to know about the resolution we have
*/
public void printFreqs(){
for (int i=0; i<audioFrames/4; i++){
System.out.println("bin "+i+", freq: "+(sampleRate*i)/audioFrames);
}
}
public static void main(String[] args) {
AudioInput ai= new AudioInput();
int turns=10000;
while(turns-- > 0){
ai.readPcm();
ai.byteToDouble();
ai.findFrequency();
}
//ai.printFreqs();
}
}
Here is the content from the console with the error message:
Microphone initialization
Microphone opened with format: PCM_SIGNED 8000.0 Hz, 16 bit, mono, 2 bytes/frame, big-endian
Exception in thread "main" org.apache.commons.math3.exception.MathIllegalArgumentException: 8.196 is not a power of 2, consider padding for fix
at org.apache.commons.math3.transform.FastFourierTransformer.transformInPlace(FastFourierTransformer.java:229)
at org.apache.commons.math3.transform.FastFourierTransformer.transform(FastFourierTransformer.java:375)
at AudioInput.findFrequency(AudioInput.java:88)
at AudioInput.main(AudioInput.java:126)
8196 is NOT a power of 2. Try to change the variable audioFrames to final int audioFrames= 8192;. The FFT algorithm can only handle arrays with size power of two (2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, ...)

Loud Noise on Encrypted Voice Chat in Java Through UDP

We had to built a encrypted/decrypted voice chat through UDP. Chat is working without encryption but when I add AES code to encrypt, i hear very loud noise which is continuous periodic beep signal but at the same time I also hear decrypted conversations which is fine. I need to eliminate this noise.
We will be so grateful for your help. Thank you
Sending
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import javax.sound.sampled.*;
public class MicPlayer {
private static final String IP_TO_STREAM_TO = "localhost" ;
private static final int PORT_TO_STREAM_TO = 1234 ;
/** Creates a new instance of MicPlayer */
public MicPlayer() {
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
Mixer.Info minfo[] = AudioSystem.getMixerInfo() ;
for( int i = 0 ; i < minfo.length ; i++ )
{
System.out.println( minfo[i] ) ;
}
if (AudioSystem.isLineSupported(Port.Info.MICROPHONE)) {
try {
DataLine.Info dataLineInfo = new DataLine.Info( TargetDataLine.class , getAudioFormat() ) ;
TargetDataLine targetDataLine = (TargetDataLine)AudioSystem.getLine( dataLineInfo ) ;
targetDataLine.open( getAudioFormat() );
targetDataLine.start();
byte tempBuffer[] = new byte[8192] ;
while( true )
{
targetDataLine.read( tempBuffer , 0 , tempBuffer.length );
byte[] encrypt = AES.encrypt(tempBuffer);
sendThruUDP(encrypt) ;
}
}
catch(Exception e )
{
System.out.println(" not correct " ) ;
System.exit(0) ;
}
}
}
public static AudioFormat getAudioFormat(){
float sampleRate = 8000.0F;
//8000,11025,16000,22050,44100
int sampleSizeInBits = 16;
//8,16
int channels = 1;
//1,2
boolean signed = true;
//true,false
boolean bigEndian = false;
//true,false
return new AudioFormat( sampleRate, sampleSizeInBits, channels, signed, bigEndian );
}
public static void sendThruUDP( byte soundpacket[] )
{
try
{
DatagramSocket sock = new DatagramSocket() ;
sock.send( new DatagramPacket( soundpacket , soundpacket.length , InetAddress.getByName( IP_TO_STREAM_TO ) , PORT_TO_STREAM_TO ) ) ;
sock.close() ;
}
catch( Exception e )
{
e.printStackTrace() ;
System.out.println(" Unable to send soundpacket using UDP " ) ;
}
}
}
Receiving
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.SourceDataLine;
public class RadioReceiver extends Thread {
private static final String IP_TO_STREAM_TO = "localhost" ;
private static final int PORT_TO_STREAM_TO = 1234;
/** Creates a new instance of RadioReceiver */
public RadioReceiver() {
}
public void run()
{
byte b[] = null ;
while( true )
{
b = receiveThruUDP() ;
toSpeaker( b ) ;
}
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
RadioReceiver r = new RadioReceiver() ;
r.start() ;
}
public static byte[] receiveThruUDP()
{
try
{
DatagramSocket sock = new DatagramSocket(PORT_TO_STREAM_TO) ;
byte soundpacket[] = new byte[8192] ;
DatagramPacket datagram = new DatagramPacket( soundpacket , soundpacket.length , InetAddress.getByName( IP_TO_STREAM_TO ) , PORT_TO_STREAM_TO ) ;
sock.receive( datagram ) ;
sock.close() ;
return AES.decrypt(datagram.getData()); // soundpacket ;
}
catch( Exception e )
{
System.out.println(" Unable to send soundpacket using UDP " ) ;
return null ;
}
}
public static void toSpeaker( byte soundbytes[] )
{
try{
DataLine.Info dataLineInfo = new DataLine.Info( SourceDataLine.class , getAudioFormat() ) ;
SourceDataLine sourceDataLine = (SourceDataLine)AudioSystem.getLine( dataLineInfo );
sourceDataLine.open( getAudioFormat() ) ;
sourceDataLine.start();
sourceDataLine.write( soundbytes , 0, soundbytes.length );
sourceDataLine.drain() ;
sourceDataLine.close() ;
}
catch(Exception e )
{
System.out.println("not working in speakers " ) ;
}
}
public static AudioFormat getAudioFormat()
{
float sampleRate = 44100.0F;
//8000,11025,16000,22050,44100
int sampleSizeInBits = 16;
//8,16
int channels = 1;
//1,2
boolean signed = true;
//true,false
boolean bigEndian = false;
//true,false
return new AudioFormat( sampleRate, sampleSizeInBits, channels, signed, bigEndian );
}
}
AES
import java.security.MessageDigest;
import java.util.Arrays;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class AES {
static String IV = "AAAAAAAAAAAAAAAA";
static String encryptionKey = "0123456789abcdef";
public static byte[] encrypt(byte[] inputcum) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding", "SunJCE");
SecretKeySpec key = new SecretKeySpec(encryptionKey.getBytes("UTF-8"), "AES");
cipher.init(Cipher.ENCRYPT_MODE, key,new IvParameterSpec(IV.getBytes("UTF-8")));
return cipher.doFinal(inputcum);
}
public static byte[] decrypt(byte[] cipherSound) throws Exception{
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding", "SunJCE");
SecretKeySpec key = new SecretKeySpec(encryptionKey.getBytes("UTF-8"), "AES");
cipher.init(Cipher.DECRYPT_MODE, key,new IvParameterSpec(IV.getBytes("UTF-8")));
return cipher.doFinal(cipherSound);
}
}
The problem is not related to UDP or encryption. Each call to TargetDataLine.read(byte[]) fills only a part of array with remaining part being filled with leftovers from the previous calls, but you are encrypting and sending the whole array each time.
A call to TargetDataLine.read(byte[]) behaves similar to InputStream.read(byte[]) - it returns the actual number of bytes transferred into the byte array. This value must not be ignored.
For the minimum working process the code should be modified along the following guidelines:
When sending:
while( true ) {
int read = targetDataLine.read( tempBuffer , 0 , tempBuffer.length );
byte[] encrypt = AES.encrypt(tempBuffer, 0, read);
sendThruUDP(encrypt) ;
}
When encrypting (notice that the padding is changed to PKCS5Padding to allow for input length which is not a multiple of the AES block size):
public static byte[] encrypt(byte[] plainData, int offset, int length) throws Exception
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding", "SunJCE");
SecretKeySpec key = new SecretKeySpec(encryptionKey.getBytes("UTF-8"), "AES");
cipher.init(Cipher.ENCRYPT_MODE, key,new IvParameterSpec(IV.getBytes("UTF-8")));
return cipher.doFinal(plainData, offset, length);
}
The decrypt() method should be modified to use the same padding.
Other most obvious improvements:
Generate new random IV for each data block and send a packet that contains both the IV and the encrypted data. It will require some copying between a number of byte arrays both on the sending+encrypting and receiving+decrypting side, but reusing the same IV for multiple cipher operations on the same key is absolutely unsafe from the cryptography point of view.
Use the proper key derivation function (search for PBKDF2) instead of simply converting a password string to bytes.
Get an instance of Cipher once and then just reinitialize it with the key and a new IV. That will save a bit of CPU and memory.
#Oleg Estekhin said "The problem is not related to UDP or encryption. You are ignoring the return value of targetDataLine.read( tempBuffer , 0 , tempBuffer.length ). I am pretty sure that most of the time it reads less than the buffer size, with remaining data being garbage from previous calls"
I agree and use a CTR or GCM as a mode of operation not CBC because CBC is slower.

ObjectInputStream being corrupted during byte array read

I appear to be getting corruption when reading an ObjectInputStream. The attached snippet throws an exception prior to completion. I fixed the example to call oos.writeObject( p1 ) as suggested.
The Exception stack is as follows:
java.lang.OutOfMemoryError: Java heap space
at test.POJO.readExternal(STest.java:82)
at java.io.ObjectInputStream.readExternalData(Unknown Source)
at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
at java.io.ObjectInputStream.readObject0(Unknown Source)
at java.io.ObjectInputStream.readObject(Unknown Source)
at test.STest.test(STest.java:37)
I believe this OutOfMemoryError exception to be misleading. I added a print statement showing the readExternal(..) behavior and am seeing a large value being pulled from ObjectInputStream, this does not correlate to what was written. If DIM is set to 5 it works if set to 15 I get the above exception. If I lower the number of bytes written per array element I get more successful iterations.
package test;
import static org.junit.Assert.*;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import org.junit.Test;
public class STest
{
#Test
public void test() throws Exception
{
POJO p1 = new POJO();
POJO p2 = new POJO();
// Initialize and serialize POJO 1
// --------------------------------
p1.hydrate();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream( baos );
oos.writeObject( p1 );
oos.flush();
oos.close();
byte [] baSerialized = baos.toByteArray();
// Parse POJO 2
// -------------
ObjectInputStream ois = new ObjectInputStream( new ByteArrayInputStream( baSerialized ) );
p2 = (POJO)ois.readObject();
// Test Result
// ------------
byte [][] baa1 = p1._baa;
byte [][] baa2 = p2._baa;
for ( int i=0; i < baa1.length; i++ )
{
String str1 = new String( baa1[ i ] );
String str2 = new String( baa2[ i ] );
assertTrue( str1.equals( str2 ) );
}
}
}
class POJO implements Externalizable
{
protected static final int DIM = 5;
protected byte [][] _baa = null;
public POJO()
{
}
public void hydrate()
{
_baa = new byte[ DIM ][];
for ( int i = 0; i < _baa.length; i++ )
{
_baa[ i ] = ("This is a serialize and parse test, it will be interesting to see if it completes without exception, I suspect not as there appears be a bug in the JRE - " + i).getBytes();
}
}
#Override
public void readExternal( ObjectInput oi ) throws IOException, ClassNotFoundException
{
int iDim = oi.readInt();
_baa = new byte[ iDim ][];
for ( int i=0; i < iDim; i++ )
{
int iSize = oi.readInt();
System.out.println( iSize );
byte [] ba = new byte[ iSize ];
oi.read( ba );
_baa[ i ] = ba;
}
}
#Override
public void writeExternal( ObjectOutput oo ) throws IOException
{
oo.writeInt( _baa.length );
for ( int i=0; i < _baa.length; i++ )
{
oo.writeInt( _baa[ i ].length );
oo.write( _baa[ i ] );
}
}
}
p1.writeExternal(o);
That should be:
oo.writeObject(p1);
You aren't supposed to call your own writeExternal() method directly. The ObjectOutputStream does that.

java DSP synth strange behaviour

I am trying to play a signal saved on a byte array, using javax.sound.sampled.SourceDataLine.
I am trying for a start to play a simple sine wave.
For some frequencies (for instances 1000Hz, 400Hz) it works well, but for others (1001, 440)
I am only getting an almost pitchless buzz.
The sampling rate is definitly high enough to prevent aliasing (16Khz).
Any ideas ?
Cheers.
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.SourceDataLine;
public class Player
{
private static float SAMPLE_RATE = 16000;
public static void main(String[] args)
{
playSound();
}
private static void playSound()
{
try
{
final AudioFormat audioFormat = new AudioFormat( SAMPLE_RATE, 8, 1, true, true );
SourceDataLine line = AudioSystem.getSourceDataLine( audioFormat );
line.open( audioFormat );
line.start();
/* the last argument here is the frequency in Hz. works well with 1000, but with 1001 I get week and pitchless clicking sound sound*/
byte[] signal = smpSin( 1, 1, 1000 );
play( line, signal );
line.drain();
line.close();
}
catch (Exception e)
{
e.printStackTrace();
}
}
private static byte[] smpSin(double lenInSec, double amp, double signalFreq)
{
int len = (int)(SAMPLE_RATE * lenInSec);
byte[] out = new byte[len];
for (int i = 0; i < out.length; i++)
{
out[i] = (byte)(amp * Math.sin( ((2.0 * Math.PI * signalFreq) * ((double)i)) / SAMPLE_RATE ));
}
return out;
}
private static void play(SourceDataLine line, byte[] array)
{
line.write( array, 0, array.length );
}
}
You aren't saving the phase of the sinewave between buffer calls. Thus any phase discontinuity will cause a buzz at the rate play() is called. Frequencies where there is no buzz just happen to end at your default beginning phase.

Categories