web-dev-qa-db-ja.com

Android:アプリで再生する音楽ファイルのフェードイン/フェードアウトサウンドエフェクトを作成するにはどうすればよいですか?

私が取り組んでいるアプリケーションは、音楽ファイルを再生します。タイマーが切れたら、音楽をフェードアウトさせます。それ、どうやったら出来るの。 MediaPlayerを使用して音楽を再生しています。音楽ファイルはアプリケーションのrawフォルダーにあります。

23
binW

これを行う1つの方法は、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をとる必要はありませんが、すべてのデバイスで同じレートで音量を下げるのでより良い方法です。

22
Chris

これは、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);
    }
}
27
sngreco

それは非常に良いクラスのsngrecoです。

より完全にするために、フェードでプレーヤーを停止するstop()関数を追加し、プレーヤーを停止してリソースを安全に解放するstopAndRelease()を追加します。 onStop()やonDestroy()などのActivityメソッドを呼び出すときに使用します。

2つの方法:

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

私はこれに取り組んできました: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;
}
4
Thram

これは、ストックの簡略化した適応ですAndroid目覚まし時計のフェードイン実装。

ステップ/インクリメントの数を定義し、(この質問に対する他の回答のように)段階的に音量を上げるのではなく、50msごとにボリュームを調整し(設定可能な値)、次のスケールでステップ/インクリメントを計算します-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;
}
0
Maksim Ivanov