I have looked everywhere to fix my problem but i just cant seem to get it going.
How do i make seekbar automatically slide with song play ?
this is what i have so far.
ArrayList<String> arrlist = new ArrayList<String>(20);
private Handler seekHandler = new Handler();
ImageButton next, playPause, previous;
SeekBar seekBar;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout);
getSupportActionBar().hide();
getInit();
}
public void getInit() {
songCurrentDurationLabel = (TextView) findViewById(R.id.startTime);
songTotalDurationLabel = (TextView) findViewById(R.id.endTime);
mediaPlayer = new MediaPlayer();
mediaPlayer = MediaPlayer.create(this, R.raw.firstsong);
seekBar = (SeekBar) findViewById(R.id.seekBar1);
seekBar.setMax(mediaPlayer.getDuration());
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
// remove message Handler from updating progress bar
seekHandler.removeCallbacks(mUpdateTimeTask);
int totalDuration = mediaPlayer.getDuration();
int currentPosition = progressToTimer(seekBar.getProgress(), totalDuration);
// forward or backward to certain seconds
mediaPlayer.seekTo(currentPosition);
// update timer progress again
updateProgressBar();
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
// remove message Handler from updating progress bar
seekHandler.removeCallbacks(mUpdateTimeTask);
}
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (mediaPlayer != null && fromUser) {
mediaPlayer.seekTo(progress);
// mediaPlayer.seekTo(progress * 1000);
}
}
});
spinner = ((Spinner) findViewById(R.id.spinner1));
spinner.setAdapter(songAdapter);
previous = ((ImageButton) findViewById(R.id.previous));
playPause = ((ImageButton) findViewById(R.id.play));
next = ((ImageButton) findViewById(R.id.next));
spinner.setOnItemSelectedListener(this);
previous.setOnClickListener(this);
playPause.setOnClickListener(this);
next.setOnClickListener(this);
totalDuration = mediaPlayer.getDuration();
currentDuration = mediaPlayer.getCurrentPosition() / 1000;
// Displaying Total Duration time
songTotalDurationLabel.setText("" + milliSecondsToTimer(totalDuration));
// Displaying time completed playing
songCurrentDurationLabel.setText("" + milliSecondsToTimer(currentDuration));
}
public void updateProgressBar() {
seekHandler.postDelayed(mUpdateTimeTask, 100);
}
private Runnable mUpdateTimeTask = new Runnable() {
#Override
public void run() {
long totalDuration = mediaPlayer.getDuration();
long currentDuration = mediaPlayer.getCurrentPosition() / 1000;
int progress = (int) getProgressPercentage(currentDuration, totalDuration);
// Updating progress bar
seekBar.setProgress(progress);
// Running this thread after 100 milliseconds
seekHandler.postDelayed(this, 100);
}
};
public int progressToTimer(int progress, int totalDuration) {
int currentDuration = 0;
totalDuration = (int) (totalDuration / 1000);
currentDuration = (int) ((((double) progress) / 100) * totalDuration);
// return current duration in milliseconds
return currentDuration * 1000;
}
public int getProgressPercentage(long currentDuration1, long totalDuration1) {
Double percentage = (double) 0;
long currentSeconds = (int) (currentDuration1 / 1000);
long totalSeconds = (int) (totalDuration1 / 1000);
// calculating percentage
percentage = (((double) currentSeconds) / totalSeconds) * 100;
// return percentage
return percentage.intValue();
}
public String milliSecondsToTimer(long milliseconds) {
String finalTimerString = "";
String secondsString = "";
// Convert total duration into time
int hours = (int) (milliseconds / (1000 * 60 * 60));
int minutes = (int) (milliseconds % (1000 * 60 * 60)) / (1000 * 60);
int seconds = (int) ((milliseconds % (1000 * 60 * 60)) % (1000 * 60) / 1000);
// Add hours if there
if (hours > 0) {
finalTimerString = hours + ":";
}
// Prepending 0 to seconds if it is one digit
if (seconds < 10) {
secondsString = "0" + seconds;
} else {
secondsString = "" + seconds;
}
finalTimerString = finalTimerString + minutes + ":" + secondsString;
// return timer string
return finalTimerString;
}
}
I didnt include the song list and all the other stuff as i dont see why it would be necesary to put here. But anyways, when i do what i have here i dont get no error or anything, the seekbar just doesnt automatically move and when i try to move it manually to a position it goes right back to 0.
First of all you should define a Runnable object that will be triggered each second. For your situation, in everysecond that class will be triggered.
I will paste some example code. Here is the runnable class.
Runnable timerRunnable = new Runnable() {
public void run() {
// Get mediaplayer time and set the value
// This will trigger itself every one second.
updateHandler.postDelayed(this, 1000);
}
};
And you should also have a Handler that will trigger Runnable instance.
Handler updateHandler = new Handler();
updateHandler.postDelayed(timerRunnable, 1000);
I hope that sample will help you.
I am using a Runnable and it looks like this:
private Runnable mUpdateTimeTask = new Runnable()
{
public void run()
{
long currentDuration = mediaPlayer.getCurrentPosition();
long elapsedDuration = mediaPlayer.getDuration() - currentDuration;
// Displaying current song progress
// playing
tvProgressLeft.setText("" + utils.milliSecondsToTimer(currentDuration));
// Displaying remaining time
tvProgressRight.setText("" + utils.milliSecondsToTimer(elapsedDuration));
// Updating progress bar
int progress = (int) (utils.getProgressPercentage(currentDuration,
elapsedDuration));
// Log.d("Progress", ""+progress);
songProgressBar.setProgress(progress);
// Running this thread after 100
// milliseconds
handler.postDelayed(this, 100);
}
};
/***/
But better yet here is a link to the sample that I have adopted.
http://www.androidhive.info/2012/03/android-building-audio-player-tutorial/
Regarding the tutorial the only thing that will not work is retrieving and filtering the song from the given file path. Somehow I couldn't get it to work. But I found another solution which was to write your own FileFilter and use it instead of the default File Filter.
Related
I want to make a random number from 0-9 with animation.
I try to use with ValueAnimator, but no luck.
Here is my code without ValueAnimator (works):
final int min = 0;
final int max = 9;
final int random = new Random().nextInt((max - min) + 1) + min;
randomSting = String.valueOf(random);
mNumberView.setText(randomSting);
Here is with ValueAnimator that doesn't work (not works):
animator = ValueAnimator.ofInt(0, 9);
final int min = 0;
final int max = 9;
final int random = new Random().nextInt((max - min) + 1) + min;
randomSting = String.valueOf(random);
cTimer = new CountDownTimer(3000, 1000) {
public void onTick(long millisUntilFinished) {
animator.setDuration(3000);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
public void onAnimationUpdate(ValueAnimator animation) {
mNumberView.setText(animation.getAnimatedValue().toString());
}
});
animator.start();
}
public void onFinish() {
animator.cancel();
mNumberView.setText(randomSting);
}
};
cTimer.start();
mNumberView cannot set text to randomString.
If possible to make number animator with random numbers, not using ValueAnimator
this code will generate random number between 9 and 0 every 1 second.
final int min = 0;
final int max = 9;
final int duration= 1000;
final Handler handler = new Handler();
final Runnable runnable = new Runnable() {
#Override
public void run() {
int random = new Random().nextInt(max - min + 1) + min;
handler.postDelayed(this,duration);
}
};
handler.post(runnable);
I'm developing a music streaming app which has to have a crossfade function e.g. when there are 5s left to the end of the first song it should start fading out and the next song should start fading in in the very same moment so that for a moment there will be two tracks playing at once.
I was trying to use two MediaPlayer objects but always only one plays. The fade out and fade in effects work perfectly fine if I play just one track.
I check when to start the crossfade in this runnable:
private Runnable mUpdateTimeTask = new Runnable() {
public void run() {
long totalDuration = 0;
long currentDuration = 0;
if(musicPlayer.isPlaying()) {
totalDuration = musicPlayer.getDuration();
currentDuration = musicPlayer.getCurrentPosition();
// Updating progress bar
int progress = (utils.getProgressPercentage(currentDuration, totalDuration));
trackPb.setProgress(progress);
if (!trackDownloaded && currentDuration > 100) {
Log.i(TagsContainer.MUSIC_PLAYER_TAG,"next track download started");
trackDownloaded = true;
new TrackLoader().execute();
}
long crossFadeValue = currentDuration + CROSSFADE_DURATION;
if (crossFadeValue > totalDuration && !fadeStarted && currentDuration > 100) {
fadeStarted = true;
crossFade();
}
// Running this thread after 100 milliseconds
}
mHandler.postDelayed(this, 100);
}
};
Crossfade's functions look like this:
private void crossFade() {
fadeOut(musicPlayer, CROSSFADE_DURATION);
fadeIn(musicPlayer2, CROSSFADE_DURATION);
}
public void fadeOut(final MediaPlayer _player, final int duration) {
final float deviceVolume = getDeviceVolume();
final Handler h = new Handler();
h.postDelayed(new Runnable() {
private float time = duration;
private float volume = 0.0f;
#Override
public void run() {
if (!_player.isPlaying())
_player.start();
// can call h again after work!
time -= 100;
volume = (deviceVolume * time) / duration;
_player.setVolume(volume, volume);
if (time > 0)
h.postDelayed(this, 100);
else {
_player.stop();
_player.release();
}
}
}, 100); // 1 second delay (takes millis)
}
public void fadeIn(final MediaPlayer _player, final int duration) {
final float deviceVolume = getDeviceVolume();
final Handler h = new Handler();
h.postDelayed(new Runnable() {
private float time = 0.0f;
private float volume = 0.0f;
#Override
public void run() {
if (!_player.isPlaying())
_player.start();
// can call h again after work!
time += 100;
volume = (deviceVolume * time) / duration;
_player.setVolume(volume, volume);
if (time < duration)
h.postDelayed(this, 100);
}
}, 100); // 1 second delay (takes millis)
}
I defined different lock positions:
private static final int[] gears = new int[] { 0, 33, 66, 100 };
In the onStopTrackingTouch i calculate which of my lock positions is the closest to the progress state of my SeekBar:
#Override
public void onStopTrackingTouch(final SeekBar seekBar) {
int progress = seekBar.getProgress();
int distance = Math.abs(gears[0] - progress);
int index = 0;
for(int i = 1; i < gears.length; i++) {
int tempDistance = Math.abs(gears[i] - progress);
if(tempDistance < distance) {
index = i;
distance = tempDistance;
}
}
int lock = gears[index];
}
I now need to somehow set the progress state of the SeekBar to my lock position using a smooth animation.
How can i impelement that animation?
EDIT
Thanks to the answer of #bwoogie here the complete working example using a VerticalSeekBar
#Override
public void onStopTrackingTouch(final SeekBar seekBar) {
int progress = seekBar.getProgress();
int distance = Math.abs(gears[0] - progress);
int index = 0;
for(int i = 1; i < gears.length; i++) {
int tempDistance = Math.abs(gears[i] - progress);
if(tempDistance < distance) {
index = i;
distance = tempDistance;
}
}
final int lock = gears[index];
final int tick = 50;
final VerticalSeekBar verticalSeekBar = (VerticalSeekBar)seekBar;
new Thread(new Runnable() {
long now = System.currentTimeMillis();
public void run() {
while(seekBar.getProgress() != lock) {
if(now + tick < System.currentTimeMillis()) {
if(seekBar.getProgress() > lock) {
getActivity().runOnUiThread(new Runnable() {
public void run() {
verticalSeekBar.setMax(100);
verticalSeekBar.setProgressAndThumb(seekBar.getProgress() - 1);
}
});
} else if(seekBar.getProgress() < lock) {
getActivity().runOnUiThread(new Runnable() {
public void run() {
verticalSeekBar.setMax(100);
verticalSeekBar.setProgressAndThumb(seekBar.getProgress() + 1);
}
});
} else {
break;
}
now = System.currentTimeMillis();
}
}
}
}).start();
}
What I would do is create a loop (in a new thread) that checks every few milliseconds if enough time has passed and if so then we can update the seekBar. Here is the basic idea, just throw it into a thread and implement the updateSeekBar() method since its in a thread you wont be able to access the UI thread directly.
int newPos; // the new position we are seeking to
int ticks = 50; //how many milliseconds
long now = System.getCurrentTimeMillis();
while(seekBar.getPosition() != newPos) {
if(now + ticks < System.getCurrentTimeMillis()) {
if(seekBar.getPosition() > newPos) {
updateSeekbar(seekBar.getPosition()--);}
elseif(seekBar.getPosition() < newPos{
updateSeekbar(seekBar.getPosition()++;}
else {
break; // we've reached out goal
}
now = System.getCurrentTimeMillis(); //Edit - forgot to update the time!!
}
}
The application that I am working on plays music files. If a timer expires I want the music to fade out. How do I do that. I am using MediaPlayer to play music and music files are present in raw folder of my application.
This is my entire handler class for Android MediaPlayer. Look at the play() and pause() functions. Both contain the ability to either fade or not. The updateVolume() function was the key to let the sound increase/decrease linearly.
package com.stackoverflow.utilities;
import java.io.File;
import java.util.Timer;
import java.util.TimerTask;
import android.content.Context;
import android.media.MediaPlayer;
import android.net.Uri;
public class MusicHandler {
private MediaPlayer mediaPlayer;
private Context context;
private int iVolume;
private final static int INT_VOLUME_MAX = 100;
private final static int INT_VOLUME_MIN = 0;
private final static float FLOAT_VOLUME_MAX = 1;
private final static float FLOAT_VOLUME_MIN = 0;
public MusicHandler(Context context) {
this.context = context;
}
public void load(String path, boolean looping) {
mediaPlayer = MediaPlayer.create(context, Uri.fromFile(new File(path)));
mediaPlayer.setLooping(looping);
}
public void load(int address, boolean looping) {
mediaPlayer = MediaPlayer.create(context, address);
mediaPlayer.setLooping(looping);
}
public void play(int fadeDuration) {
// Set current volume, depending on fade or not
if (fadeDuration > 0)
iVolume = INT_VOLUME_MIN;
else
iVolume = INT_VOLUME_MAX;
updateVolume(0);
// Play music
if (!mediaPlayer.isPlaying())
mediaPlayer.start();
// Start increasing volume in increments
if (fadeDuration > 0) {
final Timer timer = new Timer(true);
TimerTask timerTask = new TimerTask() {
#Override
public void run() {
updateVolume(1);
if (iVolume == INT_VOLUME_MAX) {
timer.cancel();
timer.purge();
}
}
};
// calculate delay, cannot be zero, set to 1 if zero
int delay = fadeDuration / INT_VOLUME_MAX;
if (delay == 0)
delay = 1;
timer.schedule(timerTask, delay, delay);
}
}
public void pause(int fadeDuration) {
// Set current volume, depending on fade or not
if (fadeDuration > 0)
iVolume = INT_VOLUME_MAX;
else
iVolume = INT_VOLUME_MIN;
updateVolume(0);
// Start increasing volume in increments
if (fadeDuration > 0) {
final Timer timer = new Timer(true);
TimerTask timerTask = new TimerTask() {
#Override
public void run() {
updateVolume(-1);
if (iVolume == INT_VOLUME_MIN) {
// Pause music
if (mediaPlayer.isPlaying())
mediaPlayer.pause();
timer.cancel();
timer.purge();
}
}
};
// calculate delay, cannot be zero, set to 1 if zero
int delay = fadeDuration / INT_VOLUME_MAX;
if (delay == 0)
delay = 1;
timer.schedule(timerTask, delay, delay);
}
}
private void updateVolume(int change) {
// increment or decrement depending on type of fade
iVolume = iVolume + change;
// ensure iVolume within boundaries
if (iVolume < INT_VOLUME_MIN)
iVolume = INT_VOLUME_MIN;
else if (iVolume > INT_VOLUME_MAX)
iVolume = INT_VOLUME_MAX;
// convert to float value
float fVolume = 1 - ((float) Math.log(INT_VOLUME_MAX - iVolume) / (float) Math.log(INT_VOLUME_MAX));
// ensure fVolume within boundaries
if (fVolume < FLOAT_VOLUME_MIN)
fVolume = FLOAT_VOLUME_MIN;
else if (fVolume > FLOAT_VOLUME_MAX)
fVolume = FLOAT_VOLUME_MAX;
mediaPlayer.setVolume(fVolume, fVolume);
}
}
One way to do it is to use MediaPlayer.setVolume(right, left) and have these values decrement after every iteration..here is a rough idea
float volume = 1;
float speed = 0.05f;
public void FadeOut(float deltaTime)
{
mediaPlayer.setVolume(volume, volume);
volume -= speed* deltaTime
}
public void FadeIn(float deltaTime)
{
mediaPlayer.setVolume(volume, volume);
volume += speed* deltaTime
}
The FadeIn() or FadeOut() should be called once this timer of yours has expired. The method doesn't need to take the deltaTime, but it's better as it will lower the volume at the same rate across all devices.
It is a very good class sngreco.
To make it more complete I will add stop() function to stop the player with fade, and stopAndRelease() to stop the player and release the resources securely, very useful to use when you call Activity methods like onStop() or onDestroy().
The two methods:
public void stop(int fadeDuration)
{
try {
// Set current volume, depending on fade or not
if (fadeDuration > 0)
iVolume = INT_VOLUME_MAX;
else
iVolume = INT_VOLUME_MIN;
updateVolume(0);
// Start increasing volume in increments
if (fadeDuration > 0)
{
final Timer timer = new Timer(true);
TimerTask timerTask = new TimerTask()
{
#Override
public void run()
{
updateVolume(-1);
if (iVolume == INT_VOLUME_MIN)
{
// Pause music
mediaPlayer.stop();
timer.cancel();
timer.purge();
}
}
};
// calculate delay, cannot be zero, set to 1 if zero
int delay = fadeDuration / INT_VOLUME_MAX;
if (delay == 0)
delay = 1;
timer.schedule(timerTask, delay, delay);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void stopAndRelease(int fadeDuration) {
try {
final Timer timer = new Timer(true);
TimerTask timerTask = new TimerTask()
{
#Override
public void run()
{
updateVolume(-1);
if (iVolume == INT_VOLUME_MIN)
{
// Stop and Release player after Pause music
mediaPlayer.stop();
mediaPlayer.release();
timer.cancel();
timer.purge();
}
}
};
timer.schedule(timerTask, fadeDuration);
} catch (Exception e) {
e.printStackTrace();
}
}
I have been working on this I hope it helps :D :
private static void crossFade() {
MediaPlayerManager.fadeOut(currentPlayer, 2000);
MediaPlayerManager.fadeIn(auxPlayer, 2000);
currentPlayer = auxPlayer;
auxPlayer = null;
}
public static void fadeOut(final MediaPlayer _player, final int duration) {
final float deviceVolume = getDeviceVolume();
final Handler h = new Handler();
h.postDelayed(new Runnable() {
private float time = duration;
private float volume = 0.0f;
#Override
public void run() {
if (!_player.isPlaying())
_player.start();
// can call h again after work!
time -= 100;
volume = (deviceVolume * time) / duration;
_player.setVolume(volume, volume);
if (time > 0)
h.postDelayed(this, 100);
else {
_player.stop();
_player.release();
}
}
}, 100); // 1 second delay (takes millis)
}
public static void fadeIn(final MediaPlayer _player, final int duration) {
final float deviceVolume = getDeviceVolume();
final Handler h = new Handler();
h.postDelayed(new Runnable() {
private float time = 0.0f;
private float volume = 0.0f;
#Override
public void run() {
if (!_player.isPlaying())
_player.start();
// can call h again after work!
time += 100;
volume = (deviceVolume * time) / duration;
_player.setVolume(volume, volume);
if (time < duration)
h.postDelayed(this, 100);
}
}, 100); // 1 second delay (takes millis)
}
public static float getDeviceVolume() {
int volumeLevel = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
int maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
return (float) volumeLevel / maxVolume;
}
Here is my simplified adaptation of stock Android Alarm Clock's fade in implementation.
Rather than defining the number of steps/increments and then increasing the volume step by step (as in other answers to this question), it adjusts volume every 50ms (configurable value) working out steps/increments on the scale between -40dB (near silent) and 0dB (max; relative to the stream volume) based on:
Preset effect duration (can be hard-coded or set by user)
Elapsed time since the playback started
See computeVolume() below for the juicy bits.
Full original code can be found here: Google Source
private MediaPlayer mMediaPlayer;
private long mCrescendoDuration = 0;
private long mCrescendoStopTime = 0;
// Default settings
private static final boolean DEFAULT_CRESCENDO = true;
private static final int CRESCENDO_DURATION = 1;
// Internal message codes
private static final int EVENT_VOLUME = 3;
// Create a message Handler
#SuppressLint("HandlerLeak")
private Handler mHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
...
case EVENT_VOLUME:
if (adjustVolume()) {
scheduleVolumeAdjustment();
}
break;
...
}
}
};
// Obtain user preferences
private void getPrefs() {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
...
final boolean crescendo = prefs.getBoolean(SettingsActivity.KEY_CRESCENDO, DEFAULT_CRESCENDO);
if (crescendo) {
// Convert mins to millis
mCrescendoDuration = CRESCENDO_DURATION * 1000 * 60;
} else {
mCrescendoDuration = 0;
}
...
}
// Start the playback
private void play(Alarm alarm) {
...
// Check to see if we are already playing
stop();
// Obtain user preferences
getPrefs();
// Check if crescendo is enabled. If it is, set alarm volume to 0.
if (mCrescendoDuration > 0) {
mMediaPlayer.setVolume(0, 0);
}
mMediaPlayer.setDataSource(this, alarm.alert);
startAlarm(mMediaPlayer);
...
}
// Do the common stuff when starting the alarm.
private void startAlarm(MediaPlayer player) throws java.io.IOException, IllegalArgumentException, IllegalStateException {
final AudioManager audioManager = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
// Do not play alarms if stream volume is 0
// (typically because ringer mode is silent).
if (audioManager.getStreamVolume(AudioManager.STREAM_ALARM) != 0) {
player.setAudioStreamType(AudioManager.STREAM_ALARM);
player.setLooping(true);
player.prepare();
player.start();
// Schedule volume adjustment
if (mCrescendoDuration > 0) {
mCrescendoStopTime = System.currentTimeMillis() + mCrescendoDuration;
scheduleVolumeAdjustment();
}
}
}
// Stop the playback
public void stop() {
...
if (mMediaPlayer != null) {
mMediaPlayer.stop();
mMediaPlayer.release();
mMediaPlayer = null;
}
mCrescendoDuration = 0;
mCrescendoStopTime = 0;
...
}
// Schedule volume adjustment 50ms in the future.
private void scheduleVolumeAdjustment() {
// Ensure we never have more than one volume adjustment queued.
mHandler.removeMessages(EVENT_VOLUME);
// Queue the next volume adjustment.
mHandler.sendMessageDelayed( mHandler.obtainMessage(EVENT_VOLUME, null), 50);
}
// Adjusts the volume of the ringtone being played to create a crescendo effect.
private boolean adjustVolume() {
// If media player is absent or not playing, ignore volume adjustment.
if (mMediaPlayer == null || !mMediaPlayer.isPlaying()) {
mCrescendoDuration = 0;
mCrescendoStopTime = 0;
return false;
}
// If the crescendo is complete set the volume to the maximum; we're done.
final long currentTime = System.currentTimeMillis();
if (currentTime > mCrescendoStopTime) {
mCrescendoDuration = 0;
mCrescendoStopTime = 0;
mMediaPlayer.setVolume(1, 1);
return false;
}
// The current volume of the crescendo is the percentage of the crescendo completed.
final float volume = computeVolume(currentTime, mCrescendoStopTime, mCrescendoDuration);
mMediaPlayer.setVolume(volume, volume);
// Schedule the next volume bump in the crescendo.
return true;
}
/**
* #param currentTime current time of the device
* #param stopTime time at which the crescendo finishes
* #param duration length of time over which the crescendo occurs
* #return the scalar volume value that produces a linear increase in volume (in decibels)
*/
private static float computeVolume(long currentTime, long stopTime, long duration) {
// Compute the percentage of the crescendo that has completed.
final float elapsedCrescendoTime = stopTime - currentTime;
final float fractionComplete = 1 - (elapsedCrescendoTime / duration);
// Use the fraction to compute a target decibel between -40dB (near silent) and 0dB (max).
final float gain = (fractionComplete * 40) - 40;
// Convert the target gain (in decibels) into the corresponding volume scalar.
final float volume = (float) Math.pow(10f, gain/20f);
//LOGGER.v("Ringtone crescendo %,.2f%% complete (scalar: %f, volume: %f dB)", fractionComplete * 100, volume, gain);
return volume;
}
I'm making a simple trivia game, and running a looping ~1 min mp3 file when the user arrives at the main menu. The sound is set to stop when user clicks any of the buttons on the menu (i.e. Play Game).
My problem is that when the sound stops, its kind of a jarring cut off. Rather than do .pause() or .stop(), is there a way to make the sound slowly fade out after a button is pressed?
Thanks
EDIT (3/13/13): Updated with new QA'd code
This is my entire handler class for Android MediaPlayer. Look at the play() and pause() functions. Both contain the ability to either fade or not. The updateVolume() function was the key to let the sound increase/decrease linearly.
public class MusicHandler
{
private MediaPlayer mediaPlayer;
private Context context;
private int iVolume;
private final static int INT_VOLUME_MAX = 100;
private final static int INT_VOLUME_MIN = 0;
private final static float FLOAT_VOLUME_MAX = 1;
private final static float FLOAT_VOLUME_MIN = 0;
public MusicHandler(Context context)
{
this.context = context;
}
public void load(String path, boolean looping)
{
mediaPlayer = MediaPlayer.create(context, Uri.fromFile(new File(path)));
mediaPlayer.setLooping(looping);
}
public void load(int address, boolean looping)
{
mediaPlayer = MediaPlayer.create(context, address);
mediaPlayer.setLooping(looping);
}
public void play(int fadeDuration)
{
//Set current volume, depending on fade or not
if (fadeDuration > 0)
iVolume = INT_VOLUME_MIN;
else
iVolume = INT_VOLUME_MAX;
updateVolume(0);
//Play music
if(!mediaPlayer.isPlaying()) mediaPlayer.start();
//Start increasing volume in increments
if(fadeDuration > 0)
{
final Timer timer = new Timer(true);
TimerTask timerTask = new TimerTask()
{
#Override
public void run()
{
updateVolume(1);
if (iVolume == INT_VOLUME_MAX)
{
timer.cancel();
timer.purge();
}
}
};
// calculate delay, cannot be zero, set to 1 if zero
int delay = fadeDuration/INT_VOLUME_MAX;
if (delay == 0) delay = 1;
timer.schedule(timerTask, delay, delay);
}
}
public void pause(int fadeDuration)
{
//Set current volume, depending on fade or not
if (fadeDuration > 0)
iVolume = INT_VOLUME_MAX;
else
iVolume = INT_VOLUME_MIN;
updateVolume(0);
//Start increasing volume in increments
if(fadeDuration > 0)
{
final Timer timer = new Timer(true);
TimerTask timerTask = new TimerTask()
{
#Override
public void run()
{
updateVolume(-1);
if (iVolume == INT_VOLUME_MIN)
{
//Pause music
if (mediaPlayer.isPlaying()) mediaPlayer.pause();
timer.cancel();
timer.purge();
}
}
};
// calculate delay, cannot be zero, set to 1 if zero
int delay = fadeDuration/INT_VOLUME_MAX;
if (delay == 0) delay = 1;
timer.schedule(timerTask, delay, delay);
}
}
private void updateVolume(int change)
{
//increment or decrement depending on type of fade
iVolume = iVolume + change;
//ensure iVolume within boundaries
if (iVolume < INT_VOLUME_MIN)
iVolume = INT_VOLUME_MIN;
else if (iVolume > INT_VOLUME_MAX)
iVolume = INT_VOLUME_MAX;
//convert to float value
float fVolume = 1 - ((float) Math.log(INT_VOLUME_MAX - iVolume) / (float) Math.log(INT_VOLUME_MAX));
//ensure fVolume within boundaries
if (fVolume < FLOAT_VOLUME_MIN)
fVolume = FLOAT_VOLUME_MIN;
else if (fVolume > FLOAT_VOLUME_MAX)
fVolume = FLOAT_VOLUME_MAX;
mediaPlayer.setVolume(fVolume, fVolume);
}
}
https://stackoverflow.com/a/29246026/922514
private static void crossFade() {
MediaPlayerManager.fadeOut(currentPlayer, 2000);
MediaPlayerManager.fadeIn(auxPlayer, 2000);
currentPlayer = auxPlayer;
auxPlayer = null;
}
public static void fadeOut(final MediaPlayer _player, final int duration) {
final float deviceVolume = getDeviceVolume();
final Handler h = new Handler();
h.postDelayed(new Runnable() {
private float time = duration;
private float volume = 0.0f;
#Override
public void run() {
if (!_player.isPlaying())
_player.start();
// can call h again after work!
time -= 100;
volume = (deviceVolume * time) / duration;
_player.setVolume(volume, volume);
if (time > 0)
h.postDelayed(this, 100);
else {
_player.stop();
_player.release();
}
}
}, 100); // 1 second delay (takes millis)
}
public static void fadeIn(final MediaPlayer _player, final int duration) {
final float deviceVolume = getDeviceVolume();
final Handler h = new Handler();
h.postDelayed(new Runnable() {
private float time = 0.0f;
private float volume = 0.0f;
#Override
public void run() {
if (!_player.isPlaying())
_player.start();
// can call h again after work!
time += 100;
volume = (deviceVolume * time) / duration;
_player.setVolume(volume, volume);
if (time < duration)
h.postDelayed(this, 100);
}
}, 100); // 1 second delay (takes millis)
}
public static float getDeviceVolume() {
int volumeLevel = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
int maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
return (float) volumeLevel / maxVolume;
}
You can use AudioManager:
public static final int STEP_DOWN = 5; // how far each step goes down
// later on, and in a backgroud thread like an AsyncTask
AudioManager am = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
int targetVol = 0; // or whatever you wanted.
int currentVol = am.getStreamVolume(AudioManager.STREAM_MUSIC);
while(currentVol > targetVol)
{
am.setStreamVolume(AudioManager.STREAM_MUSIC, currentVol - STEP_DOWN, 0);
currentVol = am.getStreamVolume(AudioManager.STREAM_MUSIC);
thread.sleep(100);
}
You probably also want to record the original volume they had media set to, and reset it to that after you fade out to mute and stop your music.
That code may not be exact, I don't currently have access to any way of testing it, but I hope it leads you in the right direction....