我正在开发的应用程序播放音乐文件。如果计时器到期,我希望音乐淡出。我怎么做。我正在使用 MediaPlayer 播放音乐,并且音乐文件存在于我的应用程序的原始文件夹中。
5 回答
这是我的整个 Android MediaPlayer 处理程序类。查看 play() 和 pause() 函数。两者都包含褪色或不褪色的能力。updateVolume() 函数是让声音线性增加/减少的关键。
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);
}
}
一种方法是MediaPlayer.setVolume(right, left)
在每次迭代后使用并减少这些值。这是一个粗略的想法
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
}
一旦您的计时器到期,应该调用FadeIn()
or 。FadeOut()
该方法不需要使用 deltaTime,但它会更好,因为它会在所有设备上以相同的速率降低音量。
这是一个非常好的类 sngreco。
为了使它更完整,我将添加stop()函数以通过淡入淡出停止播放器,并添加 stopAndRelease()以停止播放器并安全地释放资源,当您调用诸如 onStop() 或 onDestroy() 之类的 Activity 方法时非常有用.
两种方法:
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();
}
}
我一直在努力,希望对您有所帮助: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;
}
这是我对股票 Android 闹钟淡入实现的简化改编。
它不是定义步进/增量的数量,然后逐步增加音量(如该问题的其他答案),它每 50 毫秒(可配置值)调整音量,在-40dB(接近静音)的范围内计算步进/增量) 和0dB(最大值;相对于流音量)基于:
- 预设效果持续时间(可以硬编码或由用户设置)
- 自播放开始以来经过的时间
computeVolume()
有关多汁的部分,请参见下文。
完整的原始代码可以在这里找到: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;
}