Android "unable to load sample (null)" - android

I am facing a vague issue with sound manager. Only the first sound that I load, works. The 2nd sound does not.
Initialization, here WRONG_SOUND does not load.
Globals.mSoundManager = new SoundManager();
Globals.mSoundManager.initSounds(getBaseContext());
Globals.mSoundManager.addSound(Globals.CORRECT_SOUND, R.raw.correct);
Globals.mSoundManager.addSound(Globals.WRONG_SOUND, R.raw.wrong);
If I reverse the loading, CORRECT_SOUND does not load
Globals.mSoundManager = new SoundManager();
Globals.mSoundManager.initSounds(getBaseContext());
Globals.mSoundManager.addSound(Globals.WRONG_SOUND, R.raw.wrong);
Globals.mSoundManager.addSound(Globals.CORRECT_SOUND, R.raw.correct);
Constructor of SoundManager
public void initSounds(Context theContext) {
mContext = theContext;
mSoundPool = new SoundPool(4, AudioManager.STREAM_MUSIC, 0);
mSoundPool.setOnLoadCompleteListener(this);
mSoundPoolMap = new HashMap<Integer, Integer>();
mAudioManager = (AudioManager) mContext
.getSystemService(Context.AUDIO_SERVICE);
}
addSound method
public synchronized void addSound(int Index, int SoundID) {
mSoundPoolMap.put(Index, mSoundPool.load(mContext, SoundID, 1));
synchronized(this) {
try {
this.wait(1000) ;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
SoundManager implements OnLoadCompleteListener
#Override
public void onLoadComplete(SoundPool arg0, int arg1, int arg2) {
synchronized (this) {
this.notifyAll();
}
}
I thought it was a load issue, so I added a OnLoadCompleteListener, but still the 2nd sound loaded fails to work.

To fix this I had to change the mp3 file, and it worked. Magically the mp3 file works on media player of Android, also on the mac. But for some reason this crazy change to a new mp3 was the only way to fix it. Many SO questions have this solution too.

Related

Android's Media Player: Why is an audio loop not staying synced with metronome playing at the same BPM?

I am using Android's MediaPlayer to loop audio files. I set the media player looping with
mMediaPlayer.setLooping(true);
After several repetitions, the loop starts earlier than it should do i.e. if I play the same loop on a computer, with a metronome running independently at the same BPM as the loop, Android's Media player and metronome stay synced for a few bars, but after a couple of loop, the loop played with the app starts too early.
I've read about other peoples complaining about this problem.
I reproduce this problem both on Android 4.4 and Android 6
Here is a minimal Android project for reproducing the problem:
https://drive.google.com/open?id=0B9FRLIzBQgR1WWdMU29ZcHdsMXc
In my project, I had the same issue and I found he solution in SO somewhere, I don't remember exactly where. But here what I've done:
You can try using a handler to set seek to beginning x milliseconds before the end of file
loopTimer = new Timer();
loopTask = new TimerTask() {
#Override public void run() {
mMediaPlayer.seekTo(0);
}
};
long waitingTime = mMediaPlayer.getDuration()-mLoopingPreview;
loopTimer.schedule(loopTask, waitingTime, waitingTime);
Now just set mLoopingPreview to some reasonable time value. I'm using 80ms.
I also faced this problem...in Mediaplayer
There is couple of solution:
1.use ogg format sound file (https://en.wikipedia.org/wiki/.ogg)
2.Can use soundpool it has a looping and caching facility for media play (https://developer.android.com/reference/android/media/SoundPool.html)
3.Also you can use the following class: it initialize a new mediaplayer as soon as the first one finishes..so the delay is less more obvious
public class LoopMediaPlayer {
public static final String TAG = LoopMediaPlayer.class.getSimpleName();
private Context mContext = null;
private int mResId = 0;
private int mCounter = 1;
private MediaPlayer mCurrentPlayer = null;
private MediaPlayer mNextPlayer = null;
public static LoopMediaPlayer create(Context context, int resId) {
return new LoopMediaPlayer(context, resId);
}
private LoopMediaPlayer(Context context, int resId) {
mContext = context;
mResId = resId;
mCurrentPlayer = MediaPlayer.create(mContext, mResId);
mCurrentPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mediaPlayer) {
mCurrentPlayer.start();
}
});
createNextMediaPlayer();
}
private void createNextMediaPlayer() {
mNextPlayer = MediaPlayer.create(mContext, mResId);
mCurrentPlayer.setNextMediaPlayer(mNextPlayer);
mCurrentPlayer.setOnCompletionListener(onCompletionListener);
}
private MediaPlayer.OnCompletionListener onCompletionListener = new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mediaPlayer) {
mediaPlayer.release();
mCurrentPlayer = mNextPlayer;
createNextMediaPlayer();
Log.d(TAG, String.format("Loop #%d", ++mCounter));
}
};
}
to use it just call
LoopMediaPlayer.create(context, R.raw.sound_file_name);
instead of mp.setLooping(true);

Disable Android's VideoView requestAudioFocus when playing a video?

I'm building an app that records and plays back video. I would like to do so without affecting background music playback, i.e. if I begin playing a video, I do not want to pause other apps' audio. However, on Lollipop, Android's VideoView class automatically requests audio focus when the private method VideoView.openVideo() is called:
AudioManager am = (AudioManager) super.getSystemService(name);
am.requestAudioFocus(null, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
Any suggestions on how to get around this?
Starting with Android SDK 26 you may want to use VideoView and
if(Build.VERSION.SDK_INT >= Build.Version_CODES.O){
//set this BEFORE start playback
videoView.setAudioFocusRequest(AudioManager.AUDIOFOCUS_NONE)
}
For older version, there's a workaround described here: https://stackoverflow.com/a/31569930/993439
Basically, copy source code of VideoView and uncomment following lines
AudioManager am = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
am.requestAudioFocus(null, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
I got around this with a stupid solution by copying whole source code of android.widget.VideoView of Lollipop and removing line you mentioned.
Make your own VideoView class. don't use extends VideoView since you can't override openVideo() method.
I don't recommend this as I thinking it's a temporary solution. VideoView Changed a lot between 4.1-5.0 so this can make RuntimeException on Android version other than Lollipop
Edit
I made approach MediaPlayer + SurfaceView as pinxue told us;
It respects aspect ratio within viewWidth and viewHeight.
final String finalFilePath = filePath;
final SurfaceHolder surfaceHolder = sv.getHolder();
final MediaPlayer mediaPlayer = new MediaPlayer();
final LinearLayout.LayoutParams svLayoutParams = new LinearLayout.LayoutParams(viewWidth,viewHeight);
surfaceHolder.addCallback(new SurfaceHolder.Callback(){
#Override
public void surfaceCreated(SurfaceHolder holder) {
try {
if(isDebug) {
System.out.println("setting VideoPath to VideoView: "+finalFilePath);
}
mediaPlayer.setDataSource(finalFilePath);
}catch (IOException ioe){
if(isDebug){
ioe.printStackTrace();
}
//mediaPlayer = null;
}
mediaPlayer.setDisplay(surfaceHolder);
mediaPlayer.prepareAsync();
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
if(isDebug){
System.out.println("Video is starting...");
}
// for compatibility, we adjust size based on aspect ratio
if ( mp.getVideoWidth() * svLayoutParams.height < svLayoutParams.width * mp.getVideoHeight() ) {
//Log.i("###", "image too wide, correcting");
svLayoutParams.width = svLayoutParams.height * mp.getVideoWidth() / mp.getVideoHeight();
} else if ( mp.getVideoWidth() * svLayoutParams.height > svLayoutParams.width * mp.getVideoHeight() ) {
//Log.i("###", "image too tall, correcting");
svLayoutParams.height = svLayoutParams.width * mp.getVideoHeight() / mp.getVideoWidth();
}
sv.post(new Runnable(){
#Override
public void run() {
sv.setLayoutParams(svLayoutParams);
}
});
mp.start();
}
});
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
if(isDebug){
System.out.println("surfaceChanged(holder, "+format+", "+width+", "+height+")");
}
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
try {
mediaPlayer.setDataSource("");
}catch (IOException ioe){
if(isDebug){
ioe.printStackTrace();
}
}
}
});
if(sv.post(new Runnable() {
#Override
public void run() {
sv.setLayoutParams(svLayoutParams);///
sv.setVisibility(View.VISIBLE);
}})){
if(isDebug) {
System.out.println("post Succeded");
}
}else{
if(isDebug) {
System.out.println("post Failed");
}
}
The accepted solution does not guarantee compatibility across all Android versions and is a dirty hack more than a true solution. I've tried all forms of hacks to get this working, yet none have worked to my satisfaction.
I have come up with a much better solution though - switch from a VideoView to a TextureView and load it with a MediaPlayer. There is no difference from the user's perspective, just no more audio stoppage.
Here's my use case for playing an MP4 looping:
private TextureView _introVideoTextureView;
private MediaPlayer _introMediaPlayer;
...
#Override
public void onCreate(...) {
_introVideoTextureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
#Override
public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) {
try {
destoryIntroVideo();
_introMediaPlayer = MediaPlayer.create(SignInActivity.this, R.raw.intro_video);
_introMediaPlayer.setSurface(new Surface(surfaceTexture));
_introMediaPlayer.setLooping(true);
_introMediaPlayer.setVideoScalingMode(MediaPlayer.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING);
_introMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mediaPlayer) {
mediaPlayer.start();
}
});
} catch (Exception e) {
System.err.println("Error playing intro video: " + e.getMessage());
}
}
#Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int i, int i1) {}
#Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
return false;
}
#Override
public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {}
});
}
#Override
public void onDestroy() {
super.onDestroy();
destoryIntroVideo();
}
private void destoryIntroVideo() {
if (_introMediaPlayer != null) {
_introMediaPlayer.stop();
_introMediaPlayer.release();
_introMediaPlayer = null;
}
}
You may use MediaPlayer + SurfaceView instead.
use audioManager.abandonAudioFocus(null)
If you look at the VideoView code you will notice it calls the method audioManager.requestAudioFocus with null for the OnAudioFocusChangeListener. When you register a listener with the AudioManager it uses this method to make an ID for the listener
private String getIdForAudioFocusListener(OnAudioFocusChangeListener l) {
if (l == null) {
return new String(this.toString());
} else {
return new String(this.toString() + l.toString());
}
}
which generates the same ID every time you use null. So if you call abandonAudioFocus with null it will remove any listener that was added with null as the parameter for the OnAudioFocusChangeListener

Not able to show spinner until mediaplayer is prepared

I tried various approaches but nothing is solving my problem. Hope someone here can help me.
Simple requirement is I want to show a spinner until media player window is shown. It sounds easy enough but it is not.
I am calling my mediaplayer class on "Listen" click of alertDialog. Here it is:
alertBox.setCancelable(false)
.setNegativeButton("Listen", new DialogInterface.OnClickListener(){
public void onClick(DialogInterface dialog,int id){
dialog.dismiss();
emp = new EasyMediaPlayer(mp3PopupLayout,buttonPlayPause,seekBarProgress,tv_mp3,downloadURL);
emp.startPlayingMP3();
}
}).show();
And startPlaying function calls chooseToStart(true) which in-turn runs the media player. This class is:
public class EasyMediaPlayer implements OnClickListener, OnTouchListener, OnCompletionListener, OnBufferingUpdateListener{
EasyMediaPlayer(View mp3PopupLayout,ImageView buttonPlayPause,SeekBar seekBarProgress,TextView tv_mp3, String MP3URL){
this.mp3PopupLayout = mp3PopupLayout;
this.buttonPlayPause = buttonPlayPause;
this.seekBarProgress = seekBarProgress;
this.tv_mp3 = tv_mp3;
this.MP3URL = MP3URL;
seekBarProgress.setOnTouchListener(this);
buttonPlayPause.setOnClickListener(this);
}
public void startPlayingMP3(){
mediaFileLengthInMilliseconds = 1;
tv_mp3.setText("");
buttonPlayPause.setImageResource(R.drawable.button_play);
buttonPlayPause.setVisibility(View.GONE);
seekBarProgress.setProgress(0);
seekBarProgress.setSecondaryProgress(0);
mediaPlayer = new MediaPlayer();
mediaPlayer.setOnBufferingUpdateListener(this);
mediaPlayer.setOnCompletionListener(this);
mediaPlayer.setOnErrorListener(new OnErrorListener() {
public boolean onError(MediaPlayer mp, int what, int extra) {
tv_mp3.setText("Error in playing file !!");
return true;
}
});
mp3DownloadWindow = new PopupWindow(mp3PopupLayout, LayoutParams.MATCH_PARENT,LayoutParams.WRAP_CONTENT, true);
mp3DownloadWindow.showAtLocation(mp3PopupLayout, Gravity.BOTTOM, 30, 0);
chooseToStart(true);
}
public void chooseToStart(boolean startFlag){
if(startFlag){
try {
mediaPlayer.setDataSource(this.MP3URL);
mediaPlayer.prepare(); // you must call this method after setup the datasource in setDataSource method. After calling prepare() the instance of MediaPlayer starts load data from URL to internal buffer.
mediaFileLengthInMilliseconds = mediaPlayer.getDuration(); // gets the song length in milliseconds from URL
} catch (Exception e) {
tv_mp3.setText(e.toString() + "\nClose it");
}
if(!mediaPlayer.isPlaying()){
spinner.cancel();
mediaPlayer.start();
buttonPlayPause.setImageResource(R.drawable.button_pause);
}else {
mediaPlayer.pause();
buttonPlayPause.setImageResource(R.drawable.button_play);
}
primarySeekBarProgressUpdater();
}
}
//other stuff
}
I need to know where and how I can use spinner so it will be shown until media player starts playing.
Any help will be appreciated. Thanks
Use prepareAsync() and setOnPreparedListener() instead of prepare().
mediaPlayer.setDataSource(this.MP3URL);
mediaPlayer.setOnPreparedListener(new OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
// Hide whatever you need, mediaPlayer is prepared
mp.start();
}
});
// Show whatever you need, mediaPlayer is preparing
mediaPlayer.prepareAsync();

Passing soundpool to multiple activities in Android

Basically, in a short explanation, I am making a sound board. Once I run the initial activity, it adds all sound clips to the soundpool. I then have two other activities that are to organize the sound clips, and I would like to use the previously loaded soundpool so that there isn't move load time when switching between activities. I am fairly new to this Android coding, so please make things in simple terms!
EDIT: Also, does anybody know how to stop the playback on a second button click? Not as in clicking a different button, I understand that, but if the same button is clicked, it will stop it?
My main activity:
private SoundManager mSoundManager;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
this.setVolumeControlStream(AudioManager.STREAM_MUSIC);
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.main);
mSoundManager = new SoundManager();
mSoundManager.initSounds(getBaseContext());
mSoundManager.addSound(0, R.raw.stop_playing);
mSoundManager.addSound(1, R.raw.sound1);
mSoundManager.addSound(2, R.raw.sound2);
mSoundManager.addSound(3, R.raw.sound3);
Dealing with soundpool stuff:
public void initSounds(Context theContext) {
mContext = theContext;
mSoundPool = new SoundPool(1, AudioManager.STREAM_MUSIC, 0);
mSoundPoolMap = new HashMap<Integer, Integer>();
mAudioManager = (AudioManager)mContext.getSystemService(Context.AUDIO_SERVICE);
}
public void addSound(int Index,int SoundID)
{
mSoundPoolMap.put(Index, mSoundPool.load(mContext, SoundID, 1));
}
public void pauseSound(){
mSoundPool.autoPause();
}
public void stopSound(){
mSoundPool.stop(playingNumber);
}
public int playSound(int index) {
int streamVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
int soundId = mSoundPool.play(mSoundPoolMap.get(index), streamVolume, streamVolume, 1, 0, 1f);
mSoundPool.play(mSoundPoolMap.get(index), streamVolume, streamVolume, 1, 0, 1f);
playingNumber = index;
return playingNumber;
}
}
Alwade create Application class, and create static SoundManager instance in the your application class, and add needed sounds to SoundManager in the Initial activity. Then you can use current Soundmanager in other activities. Change SoundManager class to static class.
For example:
public class MyApplication extends Application {
public static SoundManager soundManager;
public static SoundManager getSoundManager() {
return soundManager;
}
public static void setSoundManager(SoundManager soundManagerIns) {
soundManager = soundManagerIns;
}
}
and in the initial activity create instance and set it to :
mSoundManager = new SoundManager();
mSoundManager.initSounds(getApplicationContext);
mSoundManager.addSound(0, R.raw.stop_playing);
mSoundManager.addSound(1, R.raw.choo_choo);
mSoundManager.addSound(2, R.raw.all);
mSoundManager.addSound(3, R.raw.hearts);
MyApplication.setSoundManager(mSoundManager);
Then you can get this SoundManager in the other activities:
SoundManager sManager = MyApplication.getSoundManager();
This is my example project: SoundPoolExample

Soundpool different streamid

I have soundpool that will load file from sd-card and I used mstreamID to stop it and I use mstreamID in OnLoadCompleteListener so the files can load. I use 2 buttons to play soundpool diffrent sounds. But when I click first button and while it plays 1 sound I click second button and it stops the sound from first button.
Here is my code:
public class MainActivity extends Settings {
SoundPool mSoundPool;
int mSoundId;
int mStreamId = 0;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
this.setVolumeControlStream(AudioManager.STREAM_MUSIC);
mSoundPool = new SoundPool(5, AudioManager.STREAM_MUSIC, 0);
mSoundPool
.setOnLoadCompleteListener(new SoundPool.OnLoadCompleteListener() {
public void onLoadComplete(SoundPool soundPool,
int sampleId, int status) {
mStreamId = soundPool.play(sampleId, 1, 1, 1, 0, 1f);
}
});
}
//OnClick
public void button1(View view) {
if (mStreamId != 0) {
mSoundPool.stop(mStreamId);
}
// et1 is extended from Settings.java
String path = getFullFilePath(getApplicationContext(), et1.getText()
.toString());
mSoundId = mSoundPool.load(path, 1);
}
public void button2(View view) {
if (mStreamId != 0) {
mSoundPool.stop(mStreamId);
}
String path = getFullFilePath(getApplicationContext(), et2.getText()
.toString());
mSoundId = mSoundPool.load(path, 1);
}
Read doc please, and what is the thing you want to say?
do not load same file more than one time if you not unload it.
load all sound at start when use SoundPool.
streamId is increasing all the time, you never get same streamId.

Categories

Resources