capturing internal audio java - java

i will record the sound from my programs. I use Ubuntu 14.04 and PulseAudio.
Now i try to record from pulseaudio but currently i'm only recording from my microphone.
How can i record the sound from pulseaudio instead of my microphone?
public static void captureAudio() {
try {
final AudioFormat format = getFormat();
DataLine.Info info = new DataLine.Info(TargetDataLine.class, format);
final TargetDataLine line = (TargetDataLine) AudioSystem.getLine(info);
line.open(format);
line.start();
Runnable runnable = new Runnable() {
int bufferSize = (int)format.getSampleRate() * format.getFrameSize();
byte buffer[] = new byte[bufferSize];
public void run() {
out = new ByteArrayOutputStream();
running = true;
try {
while (running) {
int count = line.read(buffer, 0, buffer.length);
if (count > 0) {
out.write(buffer, 0, count);
}
}
out.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
};
Thread captureThread = new Thread(runnable);
captureThread.start();
} catch (LineUnavailableException ex) {
ex.printStackTrace();
}
}
I tried some things to change this in my code:
Mixer mixer = AudioSystem.getMixer(null);
And then:
final TargetDataLine line = (TargetDataLine) mixer.getLine(info);
Hope anyone have a solution.
Greetings
Daniel

This problem cannot be solved from within Java alone. Java sees only the devices which are already there, as the following Java program demonstrates:
import javax.sound.sampled.*;
public class ListDevices {
public static void main(final String... args) throws Exception {
for (final Mixer.Info info : AudioSystem.getMixerInfo())
System.out.format("%s: %s %s %s %s%n", info, info.getName(), info.getVendor(), info.getVersion(), info.getDescription());
}
}
What you need to do is create a loopback device for your audio system. The following post shows how to do that: https://askubuntu.com/questions/257992/how-can-i-use-pulseaudio-virtual-audio-streams-to-play-music-over-skype The purpose was different, but it should be adaptable for your situation, as your situation seems simpler to me than the situation described in that post.
It should be possible to run those pactl commands from Java using Process.

Related

How to change source data line for an audio stream?

I am trying to parse audio from a file onto my computer to a discord bot that can play the audio in a voice channel. I am unable to change where it is being sent to though and am unsure how to proceed.
public void play(String filePath) {
final File file = new File(filePath);
try (final AudioInputStream in = getAudioInputStream(file)) {
//gets the file type
final AudioFormat outFormat = getOutFormat(in.getFormat());
final Info info = new Info(SourceDataLine.class, outFormat);
// I think I want to edit this part to change where the sound is output to
try (final SourceDataLine line = (SourceDataLine) AudioSystem.getLine(info)) {
if (line != null) {
line.open(outFormat);
line.start();
stream(getAudioInputStream(outFormat, in), line);
line.drain();
line.stop();
}
}
} catch (UnsupportedAudioFileException
| LineUnavailableException
| IOException e) {
throw new IllegalStateException(e);
}
}
and I want to parse the audio to connectedChannel
public void onSlashCommandInteraction(#NotNull SlashCommandInteractionEvent event) {
super.onSlashCommandInteraction(event);
if (event.getName().equals("raid")) {
OptionMapping option = event.getOption("raid");
if (option != null) {
OptionMapping raidChannel = event.getOption("raid");
OptionMapping audioNumber = event.getOption("audio");
if (raidChannel != null) {
AudioFilePlayer player = new AudioFilePlayer();
player.play("filepath");
System.out.println("Audio file finished!");
//Connecting to discord voice channel
AudioManager audioManager = event.getGuild().getAudioManager();
VoiceChannel connectedChannel = raidChannel.getAsVoiceChannel();
audioManager.openAudioConnection(connectedChannel);
event.getGuild().getAudioManager().closeAudioConnection();
event.reply("Raided!").setEphemeral(true).queue();
}
}
}
}
I can't seem to get it working.

javax.sound.sampled.LineUnavailableException: line with format PCM_SIGNED 16000.0 Hz, 16 bit, mono, 2 bytes/frame

I'm creating a run-able jar from a project in Eclipse, which consist of a basic sample code for speech recognition using cloud speech. Anyhow the problem is when I run the code from Eclipse it works fine, but when I create a run-able jar and execute it, it throws me the following exception:
" javax.sound.sampled.LineUnavailableException: line with format
PCM_SIGNED 16000.0 Hz, 16 bit, mono, 2 bytes/frame, little-endian not
supported. ".
Hers's my code:
public static void streamingMicRecognize() throws Exception {
ResponseObserver<StreamingRecognizeResponse> responseObserver = null;
try (SpeechClient client = SpeechClient.create()) {
responseObserver =
new ResponseObserver<StreamingRecognizeResponse>() {
ArrayList<StreamingRecognizeResponse> responses = new ArrayList<>();
public void onStart(StreamController controller) {}
public void onResponse(StreamingRecognizeResponse response) {
responses.add(response);
}
public void onComplete() {
for (StreamingRecognizeResponse response : responses) {
StreamingRecognitionResult result = response.getResultsList().get(0);
SpeechRecognitionAlternative alternative = result.getAlternativesList().get(0);
System.out.printf("Transcript : %s\n", alternative.getTranscript());
}
}
public void onError(Throwable t) {
System.out.println(t);
}
};
ClientStream<StreamingRecognizeRequest> clientStream =
client.streamingRecognizeCallable().splitCall(responseObserver);
RecognitionConfig recognitionConfig =
RecognitionConfig.newBuilder()
.setEncoding(RecognitionConfig.AudioEncoding.LINEAR16)
.setLanguageCode("en-US")
.setSampleRateHertz(16000)
.build();
StreamingRecognitionConfig streamingRecognitionConfig =
StreamingRecognitionConfig.newBuilder().setConfig(recognitionConfig).build();
StreamingRecognizeRequest request =
StreamingRecognizeRequest.newBuilder()
.setStreamingConfig(streamingRecognitionConfig)
.build(); // The first request in a streaming call has to be a config
clientStream.send(request);
// SampleRate:16000Hz, SampleSizeInBits: 16, Number of channels: 1, Signed: true,
// bigEndian: false
AudioFormat audioFormat = new AudioFormat(16000, 16, 1, true, false);
DataLine.Info targetInfo =
new Info(
TargetDataLine.class,
audioFormat); // Set the system information to read from the microphone audio stream
if (!AudioSystem.isLineSupported(targetInfo)) {
System.out.println("Microphone not supported");
System.exit(0);
}
// Target data line captures the audio stream the microphone produces.
TargetDataLine targetDataLine = (TargetDataLine) AudioSystem.getLine(targetInfo);
targetDataLine.open(audioFormat);
targetDataLine.start();
System.out.println("Start speaking");
long startTime = System.currentTimeMillis();
// Audio Input Stream
AudioInputStream audio = new AudioInputStream(targetDataLine);
while (true) {
long estimatedTime = System.currentTimeMillis() - startTime;
byte[] data = new byte[6400];
audio.read(data);
if (estimatedTime > 60000) { // 60 seconds
System.out.println("Stop speaking.");
targetDataLine.stop();
targetDataLine.close();
break;
}
request =
StreamingRecognizeRequest.newBuilder()
.setAudioContent(ByteString.copyFrom(data))
.build();
clientStream.send(request);
}
} catch (Exception e) {
System.out.println(e);
}
responseObserver.onComplete();
}
public static void main(String[] args) {
LiveStream ls = new LiveStream();
try {ls.streamingMicRecognize();} catch (Exception e) {e.printStackTrace();}
}
This is a simple code for recognizing speech through streaming audio.
the issue got resolved after i built a jar using maven by using package as goal.Thanks for the help :-D

Why AudioInputStream read never returns -1?

I'm reading an AudioInputStream and using it to make a chunked POST request to a server in order to stream audio. I don't understand why read() method on the stream always returns 0, also after having called stop() and close() on TargetDataLine. I'm expecting at some point to have -1 since the stream is closed and there is no more data on it (EOF). This is causing me problems in Apache HTTP Client call that is making the POST, because at some point it expects -1 to terminate writing the output stream and in this way never terminates the write loop.
Here is the snippet:
public class MicrophoneTest {
// record duration, in milliseconds
static final long RECORD_TIME = 5000;
private static AudioFormat audioFormat = new AudioFormat(48000, 16, 1, true, false);
public static void main(String[] args) throws URISyntaxException, IOException {
final Microphone recorder = new Microphone(audioFormat);
recorder.open();
// creates a new thread that waits for a specified
// of time before stopping
Thread stopper = new Thread(new Runnable() {
public void run() {
try {
Thread.sleep(RECORD_TIME);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
recorder.close();
}
});
stopper.start();
// start recording
AudioInputStream inputStream = recorder.start();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1000];
int read = -1;
while ((read = inputStream.read(buffer)) != -1) {
System.out.println(read);
outputStream.write(buffer);
}
// Never gets here!
}
}
public class Microphone {
private static final Logger LOGGER = LoggerFactory.getLogger(Microphone.class);
// format of audio file
private static final AudioFileFormat.Type fileType = AudioFileFormat.Type.WAVE;
// the line from which audio data is captured
private TargetDataLine line;
private AudioFormat audioFormat;
public Microphone(AudioFormat audioFormat) {
this.audioFormat = audioFormat;
}
/**
* Prepare the line for recording
*
* #return
*/
public boolean open() {
try {
Info info = AudioSystem.getMixerInfo()[4];
line = (TargetDataLine) AudioSystem.getTargetDataLine(audioFormat, info);
line.open(audioFormat);
} catch (LineUnavailableException ex) {
LOGGER.error(ex.toString(), ex);
return false;
}
return true;
}
/**
* Captures the sound and return the stream
*/
public AudioInputStream start() {
if (line != null) {
line.start(); // start capturing
LOGGER.info("Start recording...");
return new AudioInputStream(line);
// AudioSystem.write(ais, fileType, outputStream);
} else {
throw new IllegalStateException("Line has not created. Cannot start recording");
}
}
/**
* Stops the recording process
*/
public void stop() {
line.stop();
LOGGER.info("Stop recording...");
}
/**
* Closes the target data line to finish capturing and recording *
*/
public void close() {
line.stop();
line.close();
LOGGER.info("Data line closed");
}
}
The microphone line never reaches the end of a file. It is either on or off. It is not reading a file from a disk or memory location.
I think you need to change your while loop to something like the following:
while(isRecording)
{
read = inputStream.read(buffer);
outputStream.write(buffer, 0, read);
}
and have your stop() method include
isRecording = false;
and your start() method include
isRecording = true;
etc. That sort of thing.

Java TargetDataLine and SourceDataLine reopen doesn't work

My setup is as followed:
I have a Java applet running in a browser which records and plays audio.
My problem is:
When I refresh the browser, the SourceDataLine is reopening properly after refresh, while the TargetDataLine isn't reopening itself.
public void init() {
try {
DataLine.Info sourceDataLineInfo = new DataLine.Info(
SourceDataLine.class, audioFormat);
DataLine.Info targetDataLineInfo = new DataLine.Info(
TargetDataLine.class, audioFormat);
// Setup a Line.Info instance specifically of the TargetDataLine class.
Line.Info targetDLInfo = new Line.Info(TargetDataLine.class);
Mixer.Info[] mixerInfo = AudioSystem.getMixerInfo();
Mixer currentMixer = null;
try {
for(int cnt = 0; cnt < mixerInfo.length; cnt++) {
// Get a temporary instance of the current mixer
currentMixer = AudioSystem.getMixer(mixerInfo[cnt]);
if( currentMixer.isLineSupported(targetDLInfo) ) {
Log.log("Found mixer:" + mixerInfo[cnt].getName());
System.out.println(mixerInfo[cnt].getName());
break;
}
//currentMixer = null;
}
} catch(Exception e) {
Log.log("Found no mixer");
}
if(!Client.refresh) {
try {
sourceDataLine = (SourceDataLine) AudioSystem
.getLine(sourceDataLineInfo);
}catch(Exception e){
Log.log("Unable to stream audio not starting playthread");
}
play = new PlayThread();
if(sourceDataLine != null) {
sourceDataLine.open(audioFormat);
sourceDataLine.start();
play.start();
}
try {
targetDataLine = (TargetDataLine) currentMixer.getLine(targetDataLineInfo);
}catch(Exception e) {
connection.addMessage("[WARNING] Your microphone is not working.");
}
capture = new CaptureThread();
if(currentMixer != null) {
if(targetDataLine != null) {
targetDataLine.open(audioFormat);
targetDataLine.start();
capture.start();
}
}else {
connection.addMessage("[WARNING] No compatible microphone found.");
Log.log("Not able to record data since no mixer was found");
}
} else {
sourceDataLine.open(audioFormat);
sourceDataLine.start();
targetDataLine.open(audioFormat);
targetDataLine.start();
}
} catch (Exception e) {
Log.log("An exception occured when trying to startup the audio");
}
}
What is wrong with my code?
As Andrew said, you need to close the opened sourceDataline and TargetData line. As you refresh browser the init() is called again as it's new instance of your applet. If datalines are already open and you are trying to open it again it throws you exception kinda "Line is not supported:audioFormat". As destroy() is called every time you ends the instance of an applet, you need to handle closing of opened datalines in it.

Playing sound in a Java Desktop application

How do we play sound (a music file of any format like .wma, .mp3 ) in a Java desktop application? (not an applet)
I have used the following code (taken from another question on Stack Overflow) but it throws an Exception.
public class playsound {
public static void main(String[] args) {
s s=new s();
s.start();
}
}
class s extends Thread{
public void run(){
try{
InputStream in = new FileInputStream("C:\\Users\\srgf\\Desktop\\s.wma");
AudioStream as = new AudioStream(in); //line 26
AudioPlayer.player.start(as);
}
catch(Exception e){
e.printStackTrace();
System.exit(1);
}
}
}
The program when run throws the following Exception:
java.io.IOException: could not create audio stream from input stream
at sun.audio.AudioStream.<init>(AudioStream.java:82)
at s.run(delplaysound.java:26)
Use this library:
http://www.javazoom.net/javalayer/javalayer.html
public void play() {
String song = "http://www.ntonyx.com/mp3files/Morning_Flower.mp3";
Player mp3player = null;
BufferedInputStream in = null;
try {
in = new BufferedInputStream(new URL(song).openStream());
mp3player = new Player(in);
mp3player.play();
} catch (MalformedURLException ex) {
} catch (IOException e) {
} catch (JavaLayerException e) {
} catch (NullPointerException ex) {
}
}
Hope that helps everyone with a similar question :-)
Hmmm. This might look like advertisement for my stuff, but you could use my API here:
https://github.com/s4ke/HotSound
playback is quite easy with this one.
Alternative: use Java Clips (prebuffering)
... code ...
// specify the sound to play
File soundFile = new File("pathToYouFile");
//this does the conversion stuff for you if you have the correct SPIs installed
AudioInputStream inputStream =
getSupportedAudioInputStreamFromInputStream(new FileInputStream(soundFile));
// load the sound into memory (a Clip)
DataLine.Info info = new DataLine.Info(Clip.class, inputStream.getFormat());
Clip clip = (Clip) AudioSystem.getLine(info);
clip.open(sound);
// due to bug in Java Sound, explicitly exit the VM when
// the sound has stopped.
clip.addLineListener(new LineListener() {
public void update(LineEvent event) {
if (event.getType() == LineEvent.Type.STOP) {
event.getLine().close();
System.exit(0);
}
}
});
// play the sound clip
clip.start();
... code ...
Then you need this method:
public static AudioInputStream getSupportedAudioInputStreamFromInputStream(InputStream pInputStream) throws UnsupportedAudioFileException,
IOException {
AudioInputStream sourceAudioInputStream = AudioSystem
.getAudioInputStream(pInputStream);
AudioInputStream ret = sourceAudioInputStream;
AudioFormat sourceAudioFormat = sourceAudioInputStream.getFormat();
DataLine.Info supportInfo = new DataLine.Info(SourceDataLine.class,
sourceAudioFormat,
AudioSystem.NOT_SPECIFIED);
boolean directSupport = AudioSystem.isLineSupported(supportInfo);
if(!directSupport) {
float sampleRate = sourceAudioFormat.getSampleRate();
int channels = sourceAudioFormat.getChannels();
AudioFormat newFormat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
sampleRate,
16,
channels,
channels * 2,
sampleRate,
false);
AudioInputStream convertedAudioInputStream = AudioSystem
.getAudioInputStream(newFormat, sourceAudioInputStream);
sourceAudioFormat = newFormat;
ret = convertedAudioInputStream;
}
return ret;
}
Source for the Clip example (with little changes by me): http://www.java2s.com/Code/Java/Development-Class/AnexampleofloadingandplayingasoundusingaClip.htm
SPIs are added via adding their .jars to the classpath
for mp3 these are:
http://www.javazoom.net/mp3spi/mp3spi.html
http://www.javazoom.net/javalayer/javalayer.html
http://www.tritonus.org/plugins.html (tritonus_share.jar)
Using JavaFX (which is bundled with your JDK) is pretty simple.
You will need the following imports:
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.util.Duration;
import java.nio.file.Paths;
Steps:
Initialize JavaFX:
new JFXPanel();
Create a Media (sound):
Media media = new Media(Paths.get(filename).toUri().toString());
Create a MediaPlayer to play the sound:
MediaPlayer player = new MediaPlayer(media);
And play the Media:
player.play();
You can set the start/stop times as well with MediaPlayer.setStartTime() and MediaPlayer.setStopTime():
player.setStartTime(new Duration(Duration.ZERO)); // Start at the beginning of the sound file
player.setStopTime(1000); // Stop one second (1000 milliseconds) into the playback
Or, you can stop playing with MediaPlayer.stop().
A sample function to play audio:
public static void playAudio(String name, double startMillis, double stopMillis) {
Media media = new Media(Paths.get(name).toUri().toString());
MediaPlayer player = new MediaPlayer(media);
player.setStartTime(new Duration(startMillis));
player.setStopTime(new Duration(stopMillis));
player.play();
}
More info can be found at the JavaFX javadoc.

Categories