I am writing a java program to record sound(using Java Sound API), When I run my code while headphones are plugged in, the program works correctly, but if I remove headphones in the middle the program is neither capturing the sound nor displaying a notification to show that capturing device is not found.
My aim here is to detect the removal of the external microphone and automatically switch to the built-in microphone of my laptop computer.
Here is my code:
public void capAud() {
try {
af = getAudioFormate();
DataLine.Info dataLineInfo = new DataLine.Info(TargetDataLine.class,af);
td = (TargetDataLine)AudioSystem.getLine(dataLineInfo);
td.addLineListener(new MyListener());
new newthread().start();
} catch(Exception e) {
e.printStackTrace();
}
}
And the "newThread" class
class newthread extends Thread {
public void run() {
baos=new ByteArrayOutputStream();
int len=32*1024;
try {
File file=new File("C:\\Users\\Programmer\\Desktop\\my.wav");
td.open(af);
td.start();
byte []temp=new byte[len];
while(td!=null) {
int num=td.read(temp, 0, len);
if(num>0) {
baos.write(temp, 0, num);
}
}
baos.close();
} catch(Exception e) {
e.printStackTrace();
}
}
}
Related
I am currently creating virtual drum kit. Kinect is recording my moves and when I hit the virtual drum the program needs to play the sound of this drum. Currently my code to play music looks like that:
void playInstrumentSound(InstrumentModel instrument) {
if (instrument.getMedia() != null) {
new Thread() {
public void run() {
instrument.setPlaying(true);
MediaPlayer player = new MediaPlayer(instrument.getMedia());
player.setVolume(1);
player.play();
try {
Thread.sleep(properties.getSleepLength());
} catch (InterruptedException e) {
e.printStackTrace();
}
instrument.setPlaying(false);
player.dispose();
}
}.start();
}
}
InstrumentModel is my class that contains Media object that is initialized by using pathToSound which is path to .wav file in resources folder:
if (this.pathToSound != null) {
media = new Media(new File(this.pathToSound).toURI().toString());
}
Right now this code doesn't reach my expectations, because if I hit a drum then I can't hit it again for the properties.getSleepLength() value of time (now it is about 200ms). If I don`t make Thread sleep then I don't hear full sound.
For example, if the .wav file duration is 300ms and I make Thread.sleep(300) inside playInstrumentSound() method, then I can hear full sound but I can't play different sound for this 300ms. But if I make Thread.sleep(50) then I can hit it again almost instantly but I hear only 50ms of the .wav file.
I would like to be able to hit the drum almost instantly but also hear full sound of it. How can I reach that? Thanks in advance.
EDIT:
I just got an idea to change order inside playInstrumentSoundMethod:
void playInstrumentSound(InstrumentModel instrument) {
if (instrument.getMedia() != null) {
instrument.setPlaying(true);
MediaPlayer player = new MediaPlayer(instrument.getMedia());
player.setVolume(1);
player.play();
new Thread() {
public void run() {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
instrument.setPlaying(false);
}
}.start();
new Thread() {
public void run() {
try {
Thread.sleep(250);
} catch (InterruptedException e) {
e.printStackTrace();
}
player.dispose();
}
}.start();
}
Now it should turn the sound off after 250ms and changes isPlaying flag after 50ms (if isPlaying flag is true then you can't hit a drum again). You think that it might work?
I have sound effects for a game. Upon the game frame opening, the first sound lags behind. After that sound plays, no more lag is experienced.
Here's my clip player:
public enum SoundEffect
{
WALL("ping_pong_8bit_plop"),
PADDLE("ping_pong_8bit_beeep"),
POINT("ping_pong_8bit_peeeeeep");
public static enum Volume
{
MUTE, UNMUTE
}
public static Volume volume = Volume.MUTE;
private Clip clip;
SoundEffect (String file)
{
try
{
AudioInputStream inputStream = AudioSystem.getAudioInputStream(this.getClass().getResource(file+".wav"));
AudioFormat format = inputStream.getFormat();
DataLine.Info info = new DataLine.Info(Clip.class, format);
clip = (Clip)AudioSystem.getLine(info);
clip.open(inputStream);
}
catch (UnsupportedAudioFileException uae)
{
uae.printStackTrace();
}
catch (IOException ioe)
{
ioe.printStackTrace();
}
catch (LineUnavailableException lue)
{
lue.printStackTrace();
}
}
public void play()
{
if (volume != Volume.MUTE)
{
if (clip.isRunning())
clip.stop();
clip.flush();
clip.setFramePosition(0);
clip.start();
}
}
static void init()
{
values();
}
}
So when I call SoundEffect.WALL.play() for example, it plays fine overall, but the very first time it plays there is a huge lag spike. What can I do to solve this, preferably still using Clips?
I had the same bug, the first play of a clip was about 1-2 sec delayed, no matter when the play()-method is called. What I did is I played a clip in the main() method which is a half seccond long and contains nothing (no sound). This means that the play() method of the actual sound isn't called the first time, and for me, it works. Hope it helped.
Use a separate Thread
Thread music = new Thread(new Runnable() {
#Override public void run() { your code }
};
music.start();
I am attempting to capture a video recording through an external camera, Logitec C922. Using java, I can make this possible through webcam api.
<dependency>
<groupId>com.github.sarxos</groupId>
<artifactId>webcam-capture</artifactId>
<version>0.3.10</version>
</dependency>
<dependency>
<groupId>xuggle</groupId>
<artifactId>xuggle-xuggler</artifactId>
<version>5.4</version>
</dependency>
However, for the life of me, I cannot make it record at 60FPS. The video randomly stutters when stored, and is not smooth at all.
I can connect to the camera, using the following details.
final List<Webcam> webcams = Webcam.getWebcams();
for (final Webcam cam : webcams) {
if (cam.getName().contains("C922")) {
System.out.println("### Logitec C922 cam found");
webcam = cam;
break;
}
}
I set the size of the cam to the following:
final Dimension[] nonStandardResolutions = new Dimension[] { WebcamResolution.HD720.getSize(), };
webcam.setCustomViewSizes(nonStandardResolutions);
webcam.setViewSize(WebcamResolution.HD720.getSize());
webcam.open(true);
And then I capture the images:
while (continueRecording) {
// capture the webcam image
final BufferedImage webcamImage = ConverterFactory.convertToType(webcam.getImage(),
BufferedImage.TYPE_3BYTE_BGR);
final Date timeOfCapture = new Date();
// convert the image and store
final IConverter converter = ConverterFactory.createConverter(webcamImage, IPixelFormat.Type.YUV420P);
final IVideoPicture frame = converter.toPicture(webcamImage,
(System.currentTimeMillis() - start) * 1000);
frame.setKeyFrame(false);
frame.setQuality(0);
writer.encodeVideo(0, frame);
}
My writer is defined as follows:
final Dimension size = WebcamResolution.HD720.getSize();
final IMediaWriter writer = ToolFactory.makeWriter(videoFile.getName());
writer.addVideoStream(0, 0, ICodec.ID.CODEC_ID_H264, size.width, size.height);
I am honestly not sure what in my code could be causing this. Given that I lower the resolution, I get no problems. ( 480p ) Could the issue be with the codes I am using?
As some of the comments mentioned, introducing queues does solve the problem. Here is the general logic to performs the needed steps. Note, I've setup my code for a lower resolution, as it allows me to capture 100FPS per sec. Adjust as needed.
Class to link the image/video capture and class to edit it :
public class WebcamRecorder {
final Dimension size = WebcamResolution.QVGA.getSize();
final Stopper stopper = new Stopper();
public void startRecording() throws Exception {
final Webcam webcam = Webcam.getDefault();
webcam.setViewSize(size);
webcam.open(true);
final BlockingQueue<CapturedFrame> queue = new LinkedBlockingQueue<CapturedFrame>();
final Thread recordingThread = new Thread(new RecordingThread(queue, webcam, stopper));
final Thread imageProcessingThread = new Thread(new ImageProcessingThread(queue, size));
recordingThread.start();
imageProcessingThread.start();
}
public void stopRecording() {
stopper.setStop(true);
}
}
RecordingThread :
public void run() {
try {
System.out.println("## capturing images began");
while (true) {
final BufferedImage webcamImage = ConverterFactory.convertToType(webcam.getImage(),
BufferedImage.TYPE_3BYTE_BGR);
final Date timeOfCapture = new Date();
queue.put(new CapturedFrame(webcamImage, timeOfCapture, false));
if (stopper.isStop()) {
System.out.println("### signal to stop capturing images received");
queue.put(new CapturedFrame(null, null, true));
break;
}
}
} catch (InterruptedException e) {
System.out.println("### threading issues during recording:: " + e.getMessage());
} finally {
System.out.println("## capturing images end");
if (webcam.isOpen()) {
webcam.close();
}
}
}
ImageProcessingThread:
public void run() {
writer.addVideoStream(0, 0, ICodec.ID.CODEC_ID_H264, size.width, size.height);
try {
int frameIdx = 0;
final long start = System.currentTimeMillis();
while (true) {
final CapturedFrame capturedFrame = queue.take();
if (capturedFrame.isEnd()) {
break;
}
final BufferedImage webcamImage = capturedFrame.getImage();
size.height);
// convert the image and store
final IConverter converter = ConverterFactory.createConverter(webcamImage, IPixelFormat.Type.YUV420P);
final long end = System.currentTimeMillis();
final IVideoPicture frame = converter.toPicture(webcamImage, (end - start) * 1000);
frame.setKeyFrame((frameIdx++ == 0));
frame.setQuality(0);
writer.encodeVideo(0, frame);
}
} catch (final InterruptedException e) {
System.out.println("### threading issues during image processing:: " + e.getMessage());
} finally {
if (writer != null) {
writer.close();
}
}
The way it works is pretty simple. The WebcamRecord class creates an instance of a queue that is shared between the video capture and the image processing. The RecordingThread sends bufferedImages to the queue ( in my case, it a pojo, called CapturedFrame ( which has a BufferedImage in it ) ). The ImageProcessingThread will listen and pull data from the queue. If it does not receive a signal that the writing should end, the loop never dies.
I've found a working Sound class online and am using it to play sound in a game I'm making. However, I wanted to continuously play the file so I decided just to use a Swing Timer and it works, the only problem is my game window freezes and won't even let you exit it out without using task manager.
Can you please take a look at it and tell me where I went wrong? By the way main is just my object variable for my Sound class, and maindelay is just my int for the delay.
public static void loopSound(){
Timer maintime = new Timer(maindelay, new ActionListener(){
public void actionPerformed(ActionEvent e){
main.run();
}
});
maintime.start();
}
The Sound class I've found:
public class Sound extends Thread {
private String filename;
private Position curPosition;
private final int EXTERNAL_BUFFER_SIZE = 524288; // 128Kb
enum Position {
LEFT, RIGHT, NORMAL
};
public Sound(String wavfile) {
filename = wavfile;
curPosition = Position.NORMAL;
}
public Sound(String wavfile, Position p) {
filename = wavfile;
curPosition = p;
}
public void run() {
File soundFile = new File(filename);
if (!soundFile.exists()) {
System.err.println("Wave file not found: " + filename);
return;
}
AudioInputStream audioInputStream = null;
try {
audioInputStream = AudioSystem.getAudioInputStream(soundFile);
} catch (UnsupportedAudioFileException e1) {
e1.printStackTrace();
return;
} catch (IOException e1) {
e1.printStackTrace();
return;
}
AudioFormat format = audioInputStream.getFormat();
SourceDataLine auline = null;
DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);
try {
auline = (SourceDataLine) AudioSystem.getLine(info);
auline.open(format);
} catch (LineUnavailableException e) {
e.printStackTrace();
return;
} catch (Exception e) {
e.printStackTrace();
return;
}
if (auline.isControlSupported(FloatControl.Type.PAN)) {
FloatControl pan = (FloatControl) auline
.getControl(FloatControl.Type.PAN);
if (curPosition == Position.RIGHT)
pan.setValue(1.0f);
else if (curPosition == Position.LEFT)
pan.setValue(-1.0f);
}
auline.start();
int nBytesRead = 0;
byte[] abData = new byte[EXTERNAL_BUFFER_SIZE];
try {
while (nBytesRead != -1) {
nBytesRead = audioInputStream.read(abData, 0, abData.length);
if (nBytesRead >= 0)
auline.write(abData, 0, nBytesRead);
}
} catch (IOException e) {
e.printStackTrace();
return;
} finally {
auline.drain();
auline.close();
}
}
}
I'm guessing the sound last longer than 1 second. The int you pass to Timer is in milliseconds. It would seem to me that you are creating the sounds quicker than they can be played and this is clogging your application and blocking the UI (hence the "freeze").
I think using a Timer is the wrong way to repeatedly play the sound. You need instead to modify the sound file.
// as Sound
class RepeatSound {
...
public void run() {
// modify this condition to something meaningful
while (true) {
// as original Sound.run() in here
...
}
}
...
}
And call RepeatSound.start(). You must change that while(true) to exit with your application e.g. make it while (myFrame.visible()) to stop playing once the window is closed.
Some hints to solve the problem:
Swing Timer API states that a timer will repeat the notification to its listeners by default. It means you have an endless loop caused by this timer. See isRepeats() javadoc.
Consequently in each iteration a new thread playing a song is created which is not correct at all.
However as you don't have any interaction between swing components and played song you don't need a Swing timer actually. Just run your Song class in a separate thread than the EDT (Event Dispatch Thread) and it should be all right.
However, I wanted to continuously play the file...
As far as I can see in SourceDataLine interface API it extends from Line interface which allows you add LineListeners to listen LineEvents. Having said all this you may add a listener to restart the data line when it stops. I'd say it should look like something like this:
public class Sound extends Thread {
...
public void run()
...
auline.addLineListener(new LineListener() {
#Override
public void update(LineEvent e) {
if(e.getType() == LineEvent.Type.STOP && e.getLine().isOpen()) {
e.getLine().start();
}
}
});
auline.start();
...
}
...
}
Note: I really don't have time enough to play with this API so it's up to you test if it works. However I made an MP3 player for my cellphone using Java Mobile Media API and the approach was quite similar when I wanted to repeat the same song over again when it finishes.
ok so im working on my program which will play a song once a button is clicked, i have the code down, and my audio is playing when the button is clicked,but the only problem now is that my program is going completely bonkers and just playing the audio when the button is clicked!!!! the program just freezes the audio keeps playing but the program will not do it's other functions! What am i doing wrong? and how do i fix it?
heres the code for the action listeners button
in myFrame.java
sound sound = new sound();
private File pokemonBattle = new File(".\\audio\\"+"sound.wav");
private void dualButtonActionPerformed(java.awt.event.ActionEvent evt)
{
// TODO add your handling code here:
for (int i = 0; i<1; i++){
c = deck.getNextCard();
p1hand[i] = c;
p1.setCard(c); //ignore all this
c = deck.getNextCard();
p2hand[i] = c;
p2.setCard(c);
}
pokemon1.setEnabled(true);
pokemon2.setEnabled(true);
pokemon1.setIcon(p1hand[0].getImage()); //ignore all this as well
pokemon2.setIcon(p2hand[0].getImage());
textArea.setText("Pokemon 1:"+p1hand[0].toString()+"\nPokemon 2:
"+p2hand[0].toString());
p1ResultLabel.setText("");
p2ResultLabel.setText("");
//this is where im calling my audio
sound.playAudio(pokemonBattle);//this is where im calling my play audio method
}
sound.java where i have playAudio()
import java.io.File;
import javax.sound.sampled.*;
public class sound {
public void playAudio(File sf){
AudioFormat audioFormat;
AudioInputStream audioInputStream;
SourceDataLine sourceDataLine;
try
{
audioInputStream = AudioSystem.getAudioInputStream(sf);
audioFormat = audioInputStream.getFormat();
System.out.println(audioFormat);
DataLine.Info dataLineInfo = new
DataLine.Info(SourceDataLine.class,audioFormat);
sourceDataLine =(SourceDataLine)AudioSystem.getLine(dataLineInfo);
byte tempBuffer[]=new byte[100000];
int cnt;
sourceDataLine.open(audioFormat);
sourceDataLine.start();
while((cnt=audioInputStream.read
(tempBuffer,0,tempBuffer.length))!=-1){
if(cnt>0){
sourceDataLine.write(tempBuffer,0,cnt);
}
}
}
catch (Exception e)
{
e.printStackTrace();
System.exit(0);
}
}
}
You need to play the audio in a separate thread. The program becomes unresponsive because it is playing your audio on the event dispatch thread instead of drawing the UI and responding to user interaction.
instead of:
sound.playAudio(pokemonBattle);
do:
Thread t = new Thread( new SoundPlayer( sound, pokemonBattle );
t.start();
and also:
public class SoundPlayer implements Runnable
{
private final sound sound;
private final File soundFile;
public SoundPlayer( sound sound, File soundFile )
{
this.sound = sound;
this.soundFile = soundFile;
}
public void run()
{
sound.playAudio( soundFile );
}
}
Joseph is right. But in this instance the problem is
while((cnt=audioInputStream.read
(tempBuffer,0,tempBuffer.length))!=-1){
if(cnt>0){
sourceDataLine.write(tempBuffer,0,cnt);
}
}
You while loop will run infinitely hence freezing up your application. Since you are reading and writing the entire buffer at once there is no need for the while loop. Do this outside of a loop and you will be fine.