I'm trying to have a SoundPool play while a view object is being clicked.
I have successfully implemented it but found out that the sound of the click will lag.
I've been researching on StackOverFlow and on the internet for solutions and the best solution seems to be to have the SoundPool on a separate Thread.
I'm very new to the whole Thread thing and so I don't really know what to do at the moment.
I have followed THIS guide on creating a SoundPool thread, however, I don't know how to access it, or even to assign the clips that I wished to play.
Can someone please give me an example of how I should call the SoundPool thread to play my clip while an object in the view is being clicked.
Please find my onClick code below:
#Override
public boolean onTouch(View v, MotionEvent event) {
int actionPerformed = event.getAction();
switch(actionPerformed) {
case MotionEvent.ACTION_DOWN: {
if (Math.pow((event.getX() - a_Ball.getX()),2)
+ Math.pow((event.getY() - a_Ball.getY()), 2)
<= Math.pow(a_Ball.getDiameter(),2)) {
//pCheck = !pCheck;
if (pauseBall == false) {
SoundPool soundPool = new SoundPool(1,AudioManager.STREAM_MUSIC,1);
clickSound = soundPool.load(MainActivity.this, R.raw.click, 1);
/*soundPool.setOnLoadCompleteListener(new OnLoadCompleteListener() {
public void onLoadComplete(SoundPool soundPool, int sampleId,int status) {
soundPool.play(clickSound,1.0f, 1.0f, 0, 0, 1.0f);
}
});*/
//Increase Ball Speed
sIncrease = true;
}
} else {
if (pauseBall == false) {
//set minus health when not touch on ball
health--;
TextView txtHealth = (TextView)findViewById(R.id.health);
String tempHealth = txtHealth.getText().toString();
tempHealth = getResources().getString(R.string.health) + String.valueOf(health);
txtHealth.setText(tempHealth);
}
}
break;
}
default:
return false;
}
return true;
} //onTouch end
Thanks in advance.
You don't need threads to make sounds without lag. The lag comes from the loading of the sound file, try create the SoundPool and load the sound (soundPool.load) in advance (e.g. onCreate), then just call soundPool.play in your onTouch method.
Ok, so instead of doing it in another class, I decided to do it in one class and created a new thread by myself. Seems to be working well enough.
I'll mark this thread as answer.
Related
im trying to do a Soundboard for a school proyect, but I have problems when I pulse the buttons, If I pulse a button twice, the sound does not reproduce again, and, even if the condition fullfills, the audio doesnt loop. I have different buttons, and all of they call a method (each one with different parameters on the call
btn1.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
reproducir(mp1, motionEvent, btn1, colorin);
return false;
}
});
public void reproducir(MediaPlayer mp, MotionEvent motionEvent, Button btn, String colorin ){
switch(motionEvent.getAction()){
case MotionEvent.ACTION_DOWN:
mp.start();
btn.setBackgroundColor(getResources().getColor(R.color.Blanco));
if(instrumento.getText().equals("piano")) {
mp.setLooping(true);
}
break;
case MotionEvent.ACTION_UP:
mp.stop();
ponerColor(colorin);
}
}
The last line calls to a method to put again the original color of the button, thanks a lot
Your Code is Fine Only One tiny Mistake:
You should replace mp.stop(); with mp.pause();
That's All.
Better to it Like this though:
if (mp.isPlaying()){
mp.pause();
}
....
if (!mp.isPlaying()){
mp.start();
}
...
if (!mp.isLooping()){
mp.setLooping(true);
}
I should also let you know that mp.stop() Stops the media, it's Good, But Stop is only to be used in case You want the Whole MediaPlayer Forget what song is being played and we just wanna change The Audio, reset MEdia player and start another Audio.
Hope you find this useful
I am trying to implement a musical keyboard application that will play string sounds. String sounds are needed to be played till the user releases the key.
I am using a small sample of 1 second with the idea of looping the sample using SoundPool on
MotionEvent.ACTION_DOWN,
and stopping it as
ACTION_UP
gets called. But looping does not seem to work. I can put a "long enough" sound sample assuming no user would keep the button pressed for that long, but that is not quite the way I want the app work.
What should I do?
Try this code:
inside onCreate()
mp=MediaPlayer.create(MainActivity.this,R.raw.beep);
b.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
mp.setLooping(true);
mp.start();
}
if (event.getAction() == MotionEvent.ACTION_UP) {
mp.stop();
mp=MediaPlayer.create(MainActivity.this,R.raw.beep);
}
return false;
}
});
I am attempting to create a simple Morse code application. When the user presses a button the Morse sound should start before ending when released.
The problem is the latency- the Morse sound does not start until roughly 400ms after the user presses the button. I am not sure why this is exactly but after researching the problem i think it is the way my code is structured. The file i am trying to play is in Mp3 format and is located in the raw folder.
I had this completed using the Media Player but i ran into the same problem in regard it not being responsive enough so i opted to try and use Sound pool. Does anyone have any advice/suggestions as to how i can speed up the operation? This is new territory to me in regards development.
public int S1 = R.raw.morse;
private SoundPool soundPool;
private boolean loaded;
static int x;
public void initSounds(Context context) {
soundPool = new SoundPool(10, AudioManager.STREAM_MUSIC, 0);
final int y = soundPool.load(context, R.raw.morse, 1);
soundPool.setOnLoadCompleteListener(new SoundPool.OnLoadCompleteListener() {
#Override
public void onLoadComplete(SoundPool soundPool, int sampleId,
int status) {
loaded = true;
playSound(y);
}
});
}
public void playSound(int soundID) {
if(loaded) {
x = soundPool.play(soundID, 0.5f, 0.5f, 1, 0, 1f);
}
}
//Calling code
pad.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN)
{
//State of a toggle button
if(audioOnOff==true) {
//Sound pool object is created and initialized as a global variabe
sp.initSounds(getApplicationContext());
}
}
It looks to me like what's happening here is that each time the button is pressed, you're loading the sound, waiting for it to finish loading, then playing it. What you actually want is to load the sound once, then just play it each time the button is pressed.
So instead of calling initSounds in your onTouchListener, call initSounds somewhere else before the button is pressed. Then in your onTouchListener you just call your playSound method.
Finally, make sure you remove the playSound method call from your onLoadCompleteListener so you don't get a mysterious noise when you initially load up the sound ;)
Using the Vitamio media player, I do not see a constant for when the video actually starts rendering (as there has been for the normal android MediaPlayer since api 17). onPreparedListeners do not detect when the rendering physically starts, and, as a result, the black screen prior to the video starting is seemingly unavoidable.
Is there any way to detect when the video has actually started rendering in Vitamio?
Though it's a bit of a hack, I found that this way works wonders:
Create a boolean, defaulted to false, which determines whether or not buffering has completed for the first time.
Set an onInfoListener on your Vitamio VideoView.
Look for MediaPlayer.MEDIA_INFO_BUFFERING_END
If the boolean you created is false, then set it to true and wait, with a while loop, until yourVideoView.getCurrentPosition() != 0. Note that 0 may be too low -- sometimes getCurrentPosition will return a number higher than zero before it has started, but typically the returned value will be no higher than 1000 / (the fps of your video). I used 40.
Execute desired code (remove other views to make the VideoView visible, or add the VideoView to the layout).
In an OnCompletionListener, set the created boolean back to false.
public class AnimationCanvas extends VideoView{
public static AnimationCanvas animationCanvas;
private static boolean bufferedOnce = false;
public AnimationCanvas(Context context, AttributeSet attrs){
super(context, attrs);
animationCanvas = this;
getHolder().addCallback(this);
this.setOnInfoListener(new MediaPlayer.OnInfoListener() {
#Override
public boolean onInfo(MediaPlayer mp, int what, int extra) {
if (!bufferedOnce && what == MediaPlayer.MEDIA_INFO_BUFFERING_END) {
bufferedOnce = true;
while (getCurrentPosition() < 40) {
try {
Thread.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
MainActivity.frameLayout.removeView(MainCanvas.mainCanvas);
return true;
}
return false;
}
});
this.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
bufferedOnce = false;
MainActivity.frameLayout.addView(MainCanvas.mainCanvas);
MainActivity.frameLayout.removeView(animationCanvas);
}
});
}
EDIT: Note that another option is to create a separate Runnable which does the waiting (while(vv.getCurrentPosition() < 40){}) and then, in the same Runnable, call runOnUIThread() to run a second runnable which alters / removes / adds views if needed (Views can only be touched by the thread in which they were created.) This way, there is no need for an onInfoListener -- just start the first Runnable in an onPreparedListener.
Here's the problem, I want to change play button to pause button when the video stream starts playing in videoview but I don't know how to detect that event?
There is a great article about MediaPlayer in here - http://www.malmstein.com/blog/2014/08/09/how-to-use-a-textureview-to-display-a-video-with-custom-media-player-controls/
You can set infoListener on your VideoView
setOnInfoListener(new MediaPlayer.OnInfoListener() {
#Override
public boolean onInfo(MediaPlayer mp, int what, int extra) {
if (what == MediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START) {
// Here the video starts
return true;
}
return false;
}
I ended up using VideoView.setOnPreparedListener. This was enough to cover my problem (play button drawable change to pause)
accepted answer here is not 100% accurate.
sometimes onprepared is call 3 seconds before first frame is being rendered. i suggest having a callback on that event (MediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START)
mMediaPlayer.setOnInfoListener(new MediaPlayer.OnInfoListener() {
#Override
public boolean onInfo(MediaPlayer mediaPlayer, int i, int i1) {
if (i == MediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START){
//first frame was bufered - do your stuff here
}
return false;
}
});
see media info documantaion for more callbacks of info/warning:
https://developer.android.com/reference/android/media/MediaPlayer.html#MEDIA_INFO_VIDEO_RENDERING_START
As far as I know, there is no event sent when video start playing in VideoView, but I can think of two options:
Create a version of VideoView by yourself, that sends event in those cases.
Use MediaController (which is default in VideoView)
If you want to follow option one - you can get the source of VideoView from here
isPlaying() can be called to test whether the MediaPlayer object is in the Started
Android MediaPlayer.isPlaying()
Another way to detect if videoview started is using videoView.getCurrentPosition(). getCurrentPosition() returns 0 if streaming not started.
protected Runnable playingCheck = new Runnable() {
public void run() {
while (true) {
if (vw.getCurrentPosition() != 0) {
// do what you want when playing started
break;
} else {
try {
Thread.sleep(250);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
};
Then call:
new Thread(playingCheck).start();
Please try this :
final Handler h = new Handler();
h.postDelayed( new Runnable() {
public void run() {
if (videoView.getCurrentPosition() != 0) {
((ProgressBar) rootView.findViewById(R.id.pgStreaming)).setVisibility(View.GONE);
} else {
h.postDelayed(this, 250);
}
}
}, 250);