MediaPlayer in Android works with a delay [duplicate] - java

This question already has answers here:
Not able to achieve Gapless audio looping so far on Android
(9 answers)
Closed 4 years ago.
I'm trying to play very short wav files (around 0.5 seconds each) at a very precise moments.
I've loaded a wav file and tried to play it when it's looped:
private val player = MediaPlayer()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val afd = resources.openRawResourceFd(R.raw.sub_kick_36_045)
val fileDescriptor = afd.fileDescriptor
try {
player.setDataSource(
fileDescriptor, afd.startOffset,
afd.length
)
player.isLooping = true
player.prepare()
} catch (ex: IOException) {
Log.d("Activity", ex.message)
}
play.setOnClickListener {
player.start()
}
stop.setOnClickListener {
player.stop()
}
}
Sound is playing, however I have a significant delay when playing loops.
I've found an app which plays sound very accurately but it uses much more complicated process to play files and files itself are very peculiar(not wav)
https://github.com/tube42/drumon
Maybe you guys can advice me how I can play sounds (0.5 sec - 5 sec long) instantly, with a minimum delay. (using some java library or something)

you audio is very short,Maybe you could be to try "SoundPool".

Related

JavaFX MediaPlayer highly inaccurate seeking

I'm using 320 kbps roughly 1 hour long MP3 files. The project I'm working on would seek in a collection of music inside an MP3 file so that it can shuffle the songs. I would give the timestamps to the program and it would seek to the song. It would work if JavaFX's seek method wasn't highly inaccurate.
After using MediaPlayer.seek(duration) The MediaPlayer.getCurrentTime() returns the duration we seeked to as expected. However if we listen to the mp3 file(either without seeking or in an external mp3 player) we realize that the time reported and reality is very different, sometimes even seconds.
For example MediaPlayer.seek(Duration.millis(2000)) results seeking to 0 seconds. A 2 second failure rate is not acceptable.
With WAV it seems to work. Though it does not with MP3.
The two workarounds I think so far are possible:
Writing an MP3 Decoder and Player which doesn't have the bug
Using uncompressed WAV files
Does anyone know anything better?
If anyone needs the source code there isn't much more in it:
public static void main(String[] args) {
MediaPlayer player = null;
JFXPanel fxPanel = new JFXPanel(); //To initialize JavaFX
try {
String url = new File("E:\\Music\\test.mp3").toURI().toURL().toExternalForm();
player = new MediaPlayer(new Media(url));
System.out.println("File loaded!");
} catch (MalformedURLException e) {
//e.printStackTrace();
System.out.println("Error with filename!");
System.exit(0);
}
player.play();
System.out.println("Playing!");
while (true)
{
Scanner reader = new Scanner(System.in);
String string = reader.nextLine();
if (string.equals("Exit")) System.exit(0);
else if (string.equals("Seek"))
{
player.seek(Duration.millis(2000)); //this seeks to the beggining of the file
player.pause();
try {
Thread.sleep(100); //player.getCurrentTime() doesn't update immediately
} catch (InterruptedException e) { }
System.out.println("Time: " + player.getCurrentTime().toMillis() + " / " + player.getTotalDuration().toMillis());
player.play();
}
}
}
I would recommend using the javazoom library. It is an open source java library that already has this stuff written without errors(At least none that I found).
Source
http://www.javazoom.net/index.shtml
Place your call to the seek method off the UI thread or your UI will hang.
new Thread(() ->player.seek(Duration.millis(2000))).start();

Find the dB in android java

I'm trying to get the decibels of a sound in android. I use this method, but the "db" is "-Infinity". Can anybody help me?
public void startRecording() throws IOException {
recorder = new MediaRecorder();
recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
recorder.setOutputFile(Environment.getExternalStorageDirectory().getPath()+"/"+System.currentTimeMillis()+".3gp");
//recorder.setOutputFile("/dev/null");
recorder.prepare();
recorder.start();
double db = (double) 20*Math.log10(recorder.getMaxAmplitude()/700.0);
sun.setText(""+db);
}
The value -Infinity means, that the value of recorder.getMaxAmplitude() is 0. What does it mean? It means you have no sound. Why? Well, there are many reasons. Maybe you are testing on emulator and you didn't configure it properly. Or maybe at the last session you didn't call release(). You may want to check logcat for any messages.

Route MIC input through headset and AUDIO output through phone's speaker

is there a way to route microphone input through headset and use simultaneously the AUDIO output through smartphone's speaker ?
I've been watching around for hours now and I saw that it's clearly impossible on iOS but what about Android..
I'm using a Samsung Galaxy S4.
Here is a part of my code to route the audio output through the speaker even if the headset is plugged in :
AudioManager speakerOutput = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
speakerOutput.setMode(AudioManager.MODE_IN_CALL);
speakerOutput.setSpeakerphoneOn(true);
But then when I tried to control my app by the headset's microphone, nothing. So I tried with the smartphone's one and it's obviously working.
It don't seems like others AudioManager.MODEs route audio output only and leave the mic input to the headset. So what can I do now without having to build a new kernel, modify drivers, HAL, …etc ?
Thanks, C.
There is no way to loopback from headset Mic to Speaker if you do not change HAL or driver on Android. If u can change hardware, it possible works out.
If you use AudioManager.STREAM_RING, the audio should be routed to both the loudspeaker and the wired headset, just as when the ringtone is played for an incoming call (remove the setMode and setSpeakerphoneOn calls).
Yes, It's possible. I successfully do it a few days ago. There are 2 solutions for it:
Solution 1:
Suppose you are in a normal mood & Headset connected. We just need to call setPreferredDevice function to set your preferred audio input/output device. Our input source is already coming from the headset. So we only need to route the audio output source to the Phone Speaker. Here is my sample code by Media Player:
#RequiresApi(Build.VERSION_CODES.P)
private fun playAudioFromPhoneSpeaker(context: Context, audioResId: Int) {
val mMediaPlayer: MediaPlayer = MediaPlayer.create(context, audioResId)
val mCurrentDevice: AudioDeviceInfo? = mMediaPlayer.preferredDevice
val mDeviceSpeaker: AudioDeviceInfo? = getDeviceOutputSpeaker(context)
mDeviceSpeaker?.let { speaker ->
if (mCurrentDevice != speaker) {
val result: Boolean? = mMediaPlayer.setPreferredDevice(mDeviceSpeaker)
Log.i("PreferredDevice", "is set as speaker: $result")
}
}
mMediaPlayer.start()
mMediaPlayer.setOnCompletionListener { mediaPlayer ->
mediaPlayer.release()
}
}
#RequiresApi(Build.VERSION_CODES.M)
private fun getDeviceOutputSpeaker(context: Context): AudioDeviceInfo? {
val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
val mAudioDeviceOutputList = audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)
mAudioDeviceOutputList?.filterNotNull()?.forEach { speaker ->
if (speaker.type == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER) {
return speaker
}
}
return null
}
The positive thing about this solution is that we don't need to think about the input source. We only change the audio output source and it doesn't have any impact on the input source.
The problem is setPreferredDevice of MediaPlayer come from Android 9. So this solution is only supported from Android 9.
Solution 2:
In 2nd solution we turn on device speaker by Audio Manager. So after turn on the device speaker the audio input & output source will come to the device internal speaker & device mic. Now we have to route the mic to the headset. Sample code:
private val mAudioRecord: AudioRecord? = null
private fun RecordAudioFromHeadphone(context: Context) {
//Step 1: Turn on Speaker
val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
audioManager.isSpeakerphoneOn = true
//Step 2: Init AudioRecorder TODO for you
//Step 3: Route mic to headset
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
setPreferredInputMic(getWiredHeadPhoneMic(this))
}
}
#RequiresApi(Build.VERSION_CODES.M)
private fun setPreferredInputMic(mAudioDeviceInfo: AudioDeviceInfo?) {
val result: Boolean? = mAudioRecord?.setPreferredDevice(mAudioDeviceInfo)
}
#RequiresApi(Build.VERSION_CODES.M)
private fun getWiredHeadPhoneMic(context: Context): AudioDeviceInfo? {
val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
val mAudioDeviceOutputList = audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)
mAudioDeviceOutputList?.filterNotNull()?.forEach { device ->
if (device.type == AudioDeviceInfo.TYPE_WIRED_HEADSET) {
return device
}
}
return null
}
The positive think of this solution is supported from Android 6. And negative thing is it's little bit complex and you have to maintain the voice input source.
AudioRecorder, MediaRecorder, AudioTrack also support this kind of solution.
Thanks

Java VLCJ player, set audio track is not working

I am currently working on java media player that can play mkv format. I am using VLCJ, everything is working except when I try to change audio track which is not working.
here is the code
public class mediaplayer {
private static JFileChooser filechooser = new JFileChooser();
public mediaplayer() {
}
public static void main(String[] args) {
String vlcPath = "", mediaPath = "";
File ourfile;
filechooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
filechooser.showSaveDialog(null);
ourfile = filechooser.getSelectedFile();
mediaPath = ourfile.getAbsolutePath();
EmbeddedMediaPlayerComponent mediacom = new EmbeddedMediaPlayerComponent();
JFrame frame = new JFrame();
frame.setContentPane(mediacom);
frame.add(canvas);
frame.setLocation(100, 100);
frame.setSize(1050, 600);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
MediaPlayer mplayer = mediacom.getMediaPlayer();
mplayer.playMedia(mediaPath);
mplayer.setAudioTrack(1);
}
}
In libVLC versions before vlc 2.0.5 the native API call to set the audio track was bugged.
With the fix in libVLC 2.0.5, setting the audio track works reliably but you can not just assume a simple index from 0..N and you can not assume sequential track numbers - you must enumerate the audio tracks by calling mediaPlayer.getAudioDescriptions(). The returned TrackDescription objects contain an audio track identifier that should be used with mediaPlayer.setAudioTrack().
To disable audio, you can select the audio track identifier of the track with a a description of "Disable".
Also be aware that you might not be able to set the audio track immediately after calling mediaPlayer.playMedia(). Media is started asynchronously and you may need to wait until the media has actually started and/or has been parsed before the track information is available.
6 years later
The API have grown a lot
If you want to stop or choose an audio track you can use this code snippet and adapt it, the idea is to wait for the player to start using a process then switching to what you need, in my case is the audio disabling
new Thread(
() -> {
try {
while(!empc.mediaPlayer().status().isPlaying())Thread.sleep(500);
empc.mediaPlayer().audio().trackDescriptions().stream()
.filter(td -> td.description().equals("Disable"))
.forEach(t -> empc.mediaPlayer().audio().setTrack(t.id()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
).start();
empc is my media player instance {the embed one for more precision in the player component}

Start and stop sounds in Java

Similar to this question, I would like to play a WAV file in a Java application - however, I would also like the ability to pause, resume, and restart the sound. I'm guessing I can restart by pausing and then just creating a new sound, but how would I pause and resume in the first place?
Note that my sound is ~15minutes and 152.8mb. If there is a way to do this with an MP3 file (same length, 20.8mb) that would be even better.
For playing WAV files, see the answers to this question:
Problem with Javas Audio Clips on frequent playback of beep sounds
For playing MP3s, you can use JLayer which is a fairly small jar (100k I think, maybe smaller) that you can bundle with your application.
Here's a fairly decent example of how to use it:
MP3.java (from How to play an MP3 file in Java)
/*************************************************************************
* Compilation: javac -classpath .:jl1.0.jar MP3.java (OS X)
* javac -classpath .;jl1.0.jar MP3.java (Windows)
* Execution: java -classpath .:jl1.0.jar MP3 filename.mp3 (OS X / Linux)
* java -classpath .;jl1.0.jar MP3 filename.mp3 (Windows)
*
* Plays an MP3 file using the JLayer MP3 library.
*
* Reference: http://www.javazoom.net/javalayer/sources.html
*
*
* To execute, get the file jl1.0.jar from the website above or from
*
* http://www.cs.princeton.edu/introcs/24inout/jl1.0.jar
*
* and put it in your working directory with this file MP3.java.
*
*************************************************************************/
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import javazoom.jl.player.Player;
public class MP3 {
private String filename;
private Player player;
// constructor that takes the name of an MP3 file
public MP3(String filename) {
this.filename = filename;
}
public void close() { if (player != null) player.close(); }
// play the MP3 file to the sound card
public void play() {
try {
FileInputStream fis = new FileInputStream(filename);
BufferedInputStream bis = new BufferedInputStream(fis);
player = new Player(bis);
}
catch (Exception e) {
System.out.println("Problem playing file " + filename);
System.out.println(e);
}
// run in new thread to play in background
new Thread() {
public void run() {
try { player.play(); }
catch (Exception e) { System.out.println(e); }
}
}.start();
}
// test client
public static void main(String[] args) {
String filename = args[0];
MP3 mp3 = new MP3(filename);
mp3.play();
// do whatever computation you like, while music plays
int N = 4000;
double sum = 0.0;
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
sum += Math.sin(i + j);
}
}
System.out.println(sum);
// when the computation is done, stop playing it
mp3.close();
// play from the beginning
mp3 = new MP3(filename);
mp3.play();
}
}
Similar to this question, I would like to play a WAV file in a Java application - however, I would also like the ability to pause, resume, and restart the sound. I'm guessing I can restart by pausing and then just creating a new sound, but how would I pause and resume in the first place?
The javax.sound.sampled.Clip would be ideal for this, except for the fact that most implementations of Clip will not load more than 2 seconds of stereo, 16 bit, 44.1KHz sound! For that reason I developed BigClip. BigClip can handle sounds that are as big as the available memory.
Note that my sound is ~15minutes and 152.8mb. If there is a way to do this with an MP3 file (same length, 20.8mb) that would be even better.
Sure thing. As mentioned in the JavaSound tag info page..
MP3 decoding support
The Java Sound API does not support many formats of sampled sound internally. In a 1.6.0_24 Oracle JRE getAudioFileTypes() will generally return {WAVE, AU, AIFF}. An MP3 decoder at least, is close by. The mp3plugin.jar of the Java Media Framework supports decoding MP3s.
I currently use BigClip & the mp3plugin.jar Jar in the DukeBox player. Given 1024Meg of memory, it can easily load both the 17:12 of the 1812 Overture, & 15:38 of Bolero (the two longest tracks in my favorites play list). I mention 'both' since it will load the next track while playing the current one.
As an aside, beware of looking at code that mentions the sun.audio packages (mentioned in both linked threads). This package and/or it's classes might be moved or removed in the next release (at Oracle's discretion) & have not been necessary since Java 1.3.

Categories