Android:如何为我的应用播放的任何音乐文件创建淡入/淡出音效?
Posted
技术标签:
【中文标题】Android:如何为我的应用播放的任何音乐文件创建淡入/淡出音效?【英文标题】:Android: How to create fade-in/fade-out sound effects for any music file that my app plays? 【发布时间】:2011-10-16 14:36:57 【问题描述】:我正在开发的应用程序播放音乐文件。如果计时器到期,我希望音乐淡出。我怎么做。我正在使用 MediaPlayer 播放音乐,并且音乐文件存在于我的应用程序的原始文件夹中。
【问题讨论】:
【参考方案1】:这是我的整个 android MediaPlayer 处理程序类。查看 play() 和 pause() 函数。两者都包含褪色或不褪色的能力。 updateVolume() 函数是让声音线性增加/减少的关键。
package com.***.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);
【讨论】:
【参考方案2】:一种方法是使用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()
或FadeOut()
。该方法不需要使用 deltaTime,但它会更好,因为它会在所有设备上以相同的速率降低音量。
【讨论】:
这是我不想做的事情,但由于缺乏更好的解决方案而最终还是做了。我一直在寻找更好、更优雅的解决方案,但似乎没有。所以我会接受这个答案。 您能再添加一些示例代码吗?我想我理解你在这个例子中的意图,但它仍然没有为我点击。比如,“deltaTime”从何而来?你会在一个循环中完成这一切吗? @Chris 如果我想同时为两首歌曲使用 FadeOut 和 FadeIn 怎么办?意味着我想同时对歌曲 1 使用 FadeOut,对歌曲 2 使用 FadeIn。那么它是如何工作的????你能帮我解决这个问题吗?!! 你是怎么做到的?有什么线索吗? 在哪里调用淡出,在哪里调用淡入?尝试从 onPreparedListener 调用淡入和从 onCompletionListener 淡出,但它不起作用。有什么想法吗?【参考方案3】:这是一个非常好的类 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();
【讨论】:
【参考方案4】:我一直在努力,希望对您有所帮助: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;
【讨论】:
如果我想同时使用 FadeOut 和 FadeIn 怎么办。我的意思是我正在播放一首歌曲,然后我按下淡入淡出按钮,然后播放歌曲音量缓慢下降,同时下一首歌曲音量增加。当第一首歌曲音量变为 0 时,它会停止播放,而第二首歌曲继续播放。【参考方案5】:这是我对股票 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;
【讨论】:
以上是关于Android:如何为我的应用播放的任何音乐文件创建淡入/淡出音效?的主要内容,如果未能解决你的问题,请参考以下文章
Jetpack Compose 如何为 LazyColumn 懒惰地获取音乐文件及其元数据
Android:尝试使用输入播放列表从我的应用启动音乐播放器