Вот моя упрощенная адаптация стандартного будильника 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;
}