Android: ¿Cómo crear efectos de sonido de fundido de entrada / salida para cualquier archivo de música que reproduzca mi aplicación?

La aplicación en la que estoy trabajando reproduce archivos de música. Si un temporizador expira, quiero que la música se desvanezca. Cómo puedo hacer eso. Estoy usando MediaPlayer para reproducir música y los archivos de música están presentes en la carpeta sin formato de mi aplicación.

Una forma de hacerlo es usar MediaPlayer.setVolume (derecha, izquierda) y hacer que estos valores disminuyan después de cada iteración. Aquí hay una idea aproximada

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 } 

Debes llamar a FadeIn o FadeOut cuando este temporizador haya expirado, el método no necesita tomar el deltaTime, pero es una buena práctica, ya que reducirá el volumen a la misma velocidad en todos los dispositivos.

Esta es toda mi clase de controlador para Android MediaPlayer. Mira las funciones play () y pause (). Ambos contienen la capacidad de desaparecer o no. La función updateVolume () fue la clave para permitir que el sonido aumente / disminuya linealmente.

 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); } } 

Es una muy buena clase de sngreco.

Para hacerlo más completo, agregaré la función stop () para detener el reproductor con fade y stopAndRelease () para detener el reproductor y liberar los recursos de forma segura, muy útil para usar cuando llames a métodos Activity como onStop () o onDestroy () .

Los dos métodos:

  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(); } } 

He estado trabajando en esto, espero que ayude: 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; } 

Aquí está mi adaptación simplificada de stock Despertador Android Alarm Clock en implementación.

En lugar de definir el número de pasos / incrementos y luego boost el volumen paso a paso (como en otras respuestas a esta pregunta), ajusta el volumen cada 50 ms (valor configurable) resolviendo pasos / incrementos en la escala entre -40dB (casi silencioso) ) y 0dB (máximo, en relación con el volumen de la secuencia) en función de:

  • Duración del efecto preestablecido (puede ser codificado o configurado por el usuario)
  • Tiempo transcurrido desde que comenzó la reproducción

Ver computeVolume() continuación para los bits jugosos.

El código original completo se puede encontrar aquí: 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; }