Shuffle songs in music player - android

I’m just starting Android development and Java, and this is my first app to get acquainted with Android development. I’m almost done with the app, the only thing left is shuffling songs. I tried many steps to get it right, and I’ve scoured the web and SO for related question, yet I still can’t get it right.
This snippet of code is in my Service class, Playlist is passed from Main Activity:
public void setPlayList(ArrayList<SongModel> playlist) {
playList = playlist;
//Arraylist of integer to hold the number of indices
list = new ArrayList<Integer>();
for(int i =0; i <= playList.size();i++){
list.add(i);
}
Collections.shuffle(list, new Random(System.nanoTime()));
}
The code below is how songs are played, this snippet is in configPlayBack() method that plays the song from the song id:
long item = 0;
item = playList.get(MusicPref.isShuffle(this)? list.get(position): position).getSongId();
the snippet for playing next song is:
public void playNext() {
position++;
if (position >= playList.size()) {
position = 0;
}
configPlayBack();
}
But the songs are still playing serially.
EDIT:
public void configPlayBack(){
prepared = true;
player.reset();
if ( playList.size()>0){
long item = 0;
item = playList.get(MusicPref.isShuffle(this)? list.get(position):position).getSongId();
playItem(item);
}
}
public void playItem(long item){
Uri base = android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
Uri trackUri = ContentUris.withAppendedId(base, item);
try{
player.setDataSource(getApplicationContext(), trackUri);
}
catch(Exception e){
Log.e(TAG, "Errror setting data source", e);
}
try{
player.prepare();
}
catch(Exception ee)
{
ee.printStackTrace();
Log.e(TAG, "Error setting data source", ee);
Toast.makeText(getApplicationContext(), "Song corrupt or not supported", Toast.LENGTH_SHORT).show();
ee.printStackTrace();
isReady = false;
}
player.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
isReady = true;
setPlayState();
getAudioFocus();
mp.start();
updateNotificationPlayer();
updatePlayback();
updateSeek();
player.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
isReady = false;
mp.reset();
playNext();
}
});
}
});
}

Okay you are shuffling your newly created list with
Collections.shuffle(list, new Random(System.nanoTime()));
but you are still using the old playlist
item = playList.get(MusicPref.isShuffle(this)? list.get(position): position).getSongId();
And I am not sure why you are using that line at all. Change it simply to
item = list.get(position).getSongId();

Related

Android Media Player crash when changing datasource

I`m using a service to play the music in the background. the user have a list of songs that he can listen online or downloading them and listen offline.
the problem appears when the user is in offline mode. if he downloads a song he can play it with no problems, but if he skip to the next one(not downloaded) and there is not internet, it does not work as expected and this is not a problem. the problem appears when the user skip to previous one which is downloaded, this time its not playing
private void initMediaPlayer() {
int sura_number = activeSuraModel.getSura_number();
String formatted_sura_number = String.format("%03d", sura_number); //001
String path = activeSuraModel.getReciter_server() + "/" + formatted_sura_number + ".mp3";
mediaPlayer = new MediaPlayer();
//Set up MediaPlayer event listeners
mediaPlayer.setOnCompletionListener(this);
mediaPlayer.setOnErrorListener(this);
mediaPlayer.setOnPreparedListener(this);
mediaPlayer.setOnBufferingUpdateListener(this);
mediaPlayer.setOnSeekCompleteListener(this);
mediaPlayer.setOnInfoListener(this);
//Reset so that the MediaPlayer is not pointing to another data source
mediaPlayer.reset();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
try {
String downloadedFilePath = downloadedFiles.getDownloadedFilePath(downloaded_files_map, activeSuraModel, reciterName);
if (downloadedFilePath == null) {
mediaPlayer.setDataSource(path); // get online
} else {
mediaPlayer.setDataSource(downloadedFilePath);// get from storage
}
} catch (Exception e) {
Log.i("pat", "error " + e.getMessage());
stopSelf();
}
mediaPlayer.prepareAsync();
}
private void playMedia() {
if (!mediaPlayer.isPlaying()) {
mediaPlayer.start();
}
}
private void skipToNext() {
if (suraIndex == suraModels.size() - 1) {
//if last in playlist
suraIndex = 0;
activeSuraModel = suraModels.get(suraIndex);
} else {
//get next in playlist
activeSuraModel = suraModels.get(++suraIndex);
}
//Update stored index
new StorageUtil(getApplicationContext()).storeAudioIndex(suraIndex);
stopMedia();
//reset mediaPlayer
mediaPlayer.reset();
initMediaPlayer();
}
private void skipToPrevious() {
if (suraIndex == 0) {
//if first in playlist
//set index to the last of audioList
suraIndex = suraModels.size() - 1;
activeSuraModel = suraModels.get(suraIndex);
} else {
//get previous in playlist
activeSuraModel = suraModels.get(--suraIndex);
}
//Update stored index
new StorageUtil(getApplicationContext()).storeAudioIndex(suraIndex);
stopMedia();
//reset mediaPlayer
mediaPlayer.reset();
initMediaPlayer();
}
#Override
public void onCompletion(MediaPlayer mp) {
if (isRepeat) {
mp.start();
} else if (isShuffle) {
Random random = new Random();
int random_number = random.nextInt(suraModels.size());
activeSuraModel = suraModels.get(random_number);
initMediaPlayer();
} else {// play next song
skipToNext();
}
}
private void stopMedia() {
if (mediaPlayer == null) return;
if (mediaPlayer.isPlaying()) {
mediaPlayer.stop();
}
}

MediaPlayer skips files if previous music file is short in length

I have a music service class in which I have a method called playNext().
public void playNext() {
setSong(songPosn + 1);
playSong();
}
and the playSong() method is,
public void playSong() {
if(player != null){
if(player.isPlaying()){
player.stop();
}
}
//play a song
isPaused = false;
isComplete = false;
isPrepared = false;
player = new MediaPlayer();
player.setAudioStreamType(AudioManager.STREAM_MUSIC);
player.setOnPreparedListener(this);
player.setOnCompletionListener(this);
//get song
Song playSong = songs.get(songPosn);
//get id
long currSong = playSong.getID();
//set uri
Uri trackUri = ContentUris.withAppendedId(
android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
currSong);
try {
player.setDataSource(getApplicationContext(), trackUri);
} catch (Exception e) {
Log.e("MUSIC SERVICE", "Error setting data source", e);
}
try {
player.prepare();
} catch (IOException e) {
e.printStackTrace();
}
}
The problem is when a song is played which has a very short length, player skips more than 1 song in the songs list. Even though, only 1 is incremented in the playNext() method.
Note: It doesn't happen every time. But most of the time. I cannot figure out the problem. And that short music file is a Facebook pop up sound which is less than 0 second in duration.

arraylist to play with playback

I am getting list of mp3 files(as a ArrayList(Path)) from local by choosing one by one but I could not able to make them to play one by one play if finished move to next and if finished all list loop again. any help?
I solved issue thansk to : http://www.helloandroid.com/tutorials/musicdroid-audio-player-part-i
ofcourse I did a little bit modification, like:
private void playSong() {
try {
mp.reset();
mp.setDataSource(playlist.get(currentPosition));
mp.prepare();
mp.start();
// Setup listener so next song starts automatically
mp.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
public void onCompletion(MediaPlayer arg0) {
nextSong();
}
});
} catch (IOException e) {
Log.v(getString(R.string.app_name), e.getMessage());
}
}
private void nextSong() {
if (++currentPosition >= playlist.size()) {
// Last song, just reset currentPosition
currentPosition = 0;
} else {
// Play next song
playSong(/*MEDIA_PATH + playlist.get(currentPosition)*/);
}
}

Why does this loop fail to get past my first MediaPlayer

I have 3 MediaPlayers: Track 0, Track 1, and Track 2. I want each track to start once the one before it finishes, and once that last track finishes, I want to go back to song 0 and start the cycle over. Here is the loop I made that is failing to do this:
public void myMusic() {
while (y > 0) {
music.get(track).start();
if (music.get(track).isPlaying() == false) {
music.get(track).stop();
track++;
if (track == 3) {
track = 0;
for (int i = 0; i < 3; i++) {
try {
music.get(i).prepare();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
music.get(i).seekTo(0);
}
}
}
y--;
}
}
I think you have to close the first if block befor you check if track==3. But this is just a suggestion i dont try it.
Frankly I'd abandon the whole loop concept and go with an OnCompletionListener. Something like this might work -- I haven't tested this, just drew this out of my crazy head, but take it as conceptual:
private class LoopingCompletionListener implements OnCompletionListener {
private int index;
private int count;
private int[] ids;
private Resources res;
public LoopingCompletionListener(int[] musicIds, Resources res) {
//start on the second ID since you start the player with
//the first one
index = 1;
count = musicIds.length();
this.ids = ids;
this.res = res;
}
#Override
public void onCompletion(MediaPlayer mp) {
//Basically, just keep it between 0-2 -- this might need tweaking
index = ((index + 2) % count) - 1;
AssetFileDescriptor afd = res.openRawResourceFd(ids[index]);
FileDescriptor fd = afd.getFileDescriptor();
mp.reset();
mp.setDataSource(fd, afd.getStartOffset(), afd.getLength());
mp.prepare();
mp.start();
}
}
//Meanwhile, in onCreate()...
int[] musicIds = { R.raw.track01, R.raw.track02, R.raw.track03 };
MediaPlayer mediaPlayer = MediaPlayer.create(this, musicIds[0]);
mediaPlayer.setOnCompletionListener(
new LoopingCompletionListener(musicIds, getResources());
mediaPlayer.start();
If I had to guess, isPlaying() is only false when you set the MediaPlayer state to pause or stop. In other words, you can't simply rely on it to determine when the music has reached the end of the stream to determine when it's done.
Also, from the documentation:
Note that the transition from the Started state to the Paused state and vice versa happens asynchronously in the player engine. It may take some time before the state is updated in calls to isPlaying(), and it can be a number of seconds in the case of streamed content.
So it is an unreliable indicator for a use case such as this.
What you could do instead is take advantage of the MediaPlayer.OnCompletionListener. Register one for each MediaPlayer available whereby, upon completion, you advance to the next MediaPlayer.
So something like this:
public class PlayUponCompleteListener implements MediaPlayer.OnCompletionListener {
WeakReference<MediaPlayer> mNextPlayer;
public PlayUponCompleteListener(MediaPlayer mp) {
mNextPlayer = new WeakReference<MediaPlayer>(mp);
}
#Override
public void onCompletion(MediaPlayer mp) {
mp.stop();
mp.reset();
if(mNextPlayer.get() != null) {
mNextPlayer.start();
}
}
}
Then, register them like so:
mp1.setOnCompletionListener(new CompletionListener(mp2));
mp2.setOnCompletionListener(new CompletionListener(mp3));
mp3.setOnCompletionListener(new CompletionListener(mp1));

Android: Stop sound files playing at once

In the application I am currently writing, a user is able to select an entry from the database and play the contents of that entry: an entry is made up of a number of sound files (without a limit). In my application, I return the URI locations of the sound files of an entry (which have been stored in my database) in a List. The code is as follows:
public void audioPlayer() {
// set up MediaPlayer
MediaPlayer mp = new MediaPlayer();
DatabaseHandler db = new DatabaseHandler(this);
Entry retrieveEntry = new Entry();
retrieveEntry = db.getEntry();
List<String> path = retrieveEntry.getAudioUri();
path.size();
System.out.println("PATH SIZE: " +path.size());
System.out.println("FILEZ: " + path);
Iterator<String> i = path.iterator();
String myAudio;
int count = 0;
while (i.hasNext()) {
System.out.println(count);
myAudio = i.next();
System.out.println("MY AUDIO: " + myAudio);
MediaPlayer player = MediaPlayer.create(this, Uri.parse(myAudio));
player.start();
player.stop();
player = MediaPlayer.create(this, Uri.parse(myAudio));
player.start();
count++;
}
}
My users require that there be user input for playing a file - is there a way to play the first file, then wait for the user to press the button, then play the second file, then wait for the user to press the button, etc.? At the moment, when the play button is pressed, all of the sound files that have been returned get played at the same time, rather than one after the other.
Thanks in advance for any help provided!
You can use this class to play a playlist. This will start one audio, when that audio finishes, it will start playing next audio till the end of the list. If you want to play the playlist in looping i.e start first audio after reaching end, then pass isLooping=true in startPlayingPlaylist(list,looping)
AudioPlayer player = new AudioPlayer();
player.startPlayingPlaylist(list, false);
Class
public class AudioPlayer{
MediaPlayer player = null;
ArrayList<String> playlist = null;
int position = 0;
public AudioPlayer() {
super();
// TODO Auto-generated constructor stub
}
public void startPlayingPlaylist(ArrayList<String> list, boolean looping){
playlist = list;
if(player!=null){
player.release();
}
if(playlist!=null && playlist.size()>0){
player = MediaPlayer.create(LMApplicaton.getInstance(),Uri.parse(playlist.get(position)));
player.setWakeMode(LMApplicaton.getInstance(), PowerManager.PARTIAL_WAKE_LOCK);
player.setLooping(looping);
player.start();
// Set onCompletion listener
player.setOnCompletionListener(new OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
position = position+1;
if(position<playlist.size()){
try {
player.reset();
player.setDataSource(playlist.get(position));
player.prepareAsync();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
} else if(player.isLooping()==true){
position = position%playlist.size();
try {
player.reset();
player.setDataSource(playlist.get(position));
player.prepareAsync();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
else if(player.isLooping()==false){
player.release();
player = null;
}
}
});
player.setOnPreparedListener(new OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
player.start();
}
});
}
}
public void pause(){
if(player!=null && player.isPlaying()){
player.pause();
}
}
public void play(){
if(player!=null && player.isPlaying()==false){
player.start();
}
}
public boolean isPlaying(){
return player.isPlaying();
}
public void release(){
if(player!=null){
player.release();
}
}
}
Edit:
The class below receives a list of audios, then plays first Audio. It plays next audio when user calls startNextAudio() You can use any one of these according to your requirements
public class AudioPlayer{
MediaPlayer player = null;
ArrayList playlist = null;
int position = 0;
public AudioPlayer() {
super();
// TODO Auto-generated constructor stub
}
public void startPlayingPlaylist(ArrayList<String> list){
playlist = list;
if(player!=null){
player.release();
}
if(playlist!=null && playlist.size()>0){
player = MediaPlayer.create(LMApplicaton.getInstance(),Uri.parse(playlist.get(position)));
player.setWakeMode(LMApplicaton.getInstance(), PowerManager.PARTIAL_WAKE_LOCK);
player.start();
// Set onCompletion listener
player.setOnPreparedListener(new OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
player.start();
}
});
}
}
public void startNextAudio(){
position = position+1;
if(position<playlist.size()){
try {
player.reset();
player.setDataSource(playlist.get(position));
player.prepareAsync();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
} else if(player.isLooping()==true){
position = position%playlist.size();
try {
player.reset();
player.setDataSource(playlist.get(position));
player.prepareAsync();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}else{
Log.i("AudioPlayer","Playlist reached at the end");
}
}
public void pause(){
if(player!=null && player.isPlaying()){
player.pause();
}
}
public void play(){
if(player!=null && player.isPlaying()==false){
player.start();
}
}
public boolean isPlaying(){
return player.isPlaying();
}
public void release(){
if(player!=null){
player.release();
}
}
}
One approach would be to implement the MediaPlayer.OnCompletionListener interface. This gives you the MediaPlayer.onCompletion() callback method which you could use like so:
#Override
public void onCompletion(MediaPlayer mp) {
if (i.hasNext) {
// ...hand mp the next file
// ...show the user the 'play next' button
}
}
Note you also will need to call the MediaPlayer.setOnCompletionListener() method in your setup.

Categories

Resources