Android: Как создать звуковые эффекты постепенного появления / исчезновения звука для любого музыкального файла, который воспроизводит мое приложение? - PullRequest
21 голосов
/ 30 июля 2011

Приложение, над которым я работаю, воспроизводит музыкальные файлы. Если таймер истекает, я хочу, чтобы музыка исчезала. Как я это сделал. Я использую MediaPlayer для воспроизведения музыки, а музыкальные файлы присутствуют в сырой папке моего приложения.

Ответы [ 5 ]

25 голосов
/ 13 марта 2013

Это весь мой класс обработчиков для 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);
    }
}
22 голосов
/ 30 июля 2011

Один из способов сделать это - использовать MediaPlayer.setVolume (справа, слева) и уменьшать эти значения после каждой итерации. Есть грубая идея

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, но это хорошая практика, так как он будет с одинаковой скоростью уменьшать громкость на всех устройствах

8 голосов
/ 03 января 2014

Это очень хороший класс sngreco.

Чтобы сделать его более полным, я добавлю stop () функцию, чтобы остановить игрока с исчезновением, и stopAndRelease () , чтобы остановить игрока и безопасно высвободить ресурсы, очень полезно использовать при вызове методов Activity, таких как onStop () или onDestroy ().

Два метода:

    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();
    }
}
3 голосов
/ 25 марта 2015

Я работал над этим, надеюсь, это поможет: 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;
}
0 голосов
/ 10 июня 2018

Вот моя упрощенная адаптация стандартного будильника Android в реализации.

Вместо того, чтобы определять количество шагов / приращений и затем шаг за шагом увеличивать громкость (как в других ответах на этот вопрос),он регулирует громкость каждые 50 мс (настраиваемое значение), вычисляя шаги / приращения по шкале от -40 дБ (почти бесшумно) до 0 дБ (макс. относительно объема потока) на основе:

  • Заданная продолжительность эффекта (может быть жестко задана или задана пользователем)
  • Истекшее время с начала воспроизведения

См. Ниже 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;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...