Added ability to set Playback speed and pitch
This commit is contained in:
parent
3a6645ab35
commit
943de60f23
12 changed files with 229 additions and 18 deletions
|
@ -155,3 +155,5 @@ const val WHITELIST_MUSIC = "whitelist_music"
|
|||
const val MATERIAL_YOU = "material_you"
|
||||
const val SNOWFALL = "snowfall"
|
||||
const val LYRICS_TYPE = "lyrics_type"
|
||||
const val PLAYBACK_SPEED = "playback_speed"
|
||||
const val PLAYBACK_PITCH = "playback_pitch"
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
package code.name.monkey.retromusic.dialogs
|
||||
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.databinding.DialogPlaybackSpeedBinding
|
||||
import code.name.monkey.retromusic.extensions.accent
|
||||
import code.name.monkey.retromusic.extensions.colorButtons
|
||||
import code.name.monkey.retromusic.extensions.materialDialog
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import com.google.android.material.slider.Slider
|
||||
|
||||
class PlaybackSpeedDialog : DialogFragment() {
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
val binding = DialogPlaybackSpeedBinding.inflate(layoutInflater)
|
||||
binding.playbackSpeedSlider.accent()
|
||||
binding.playbackPitchSlider.accent()
|
||||
binding.playbackSpeedSlider.addOnChangeListener(Slider.OnChangeListener { _, value, _ ->
|
||||
binding.speedValue.text = "$value"
|
||||
})
|
||||
binding.playbackPitchSlider.addOnChangeListener(Slider.OnChangeListener { _, value, _ ->
|
||||
binding.pitchValue.text = "$value"
|
||||
})
|
||||
binding.playbackSpeedSlider.value = PreferenceUtil.playbackSpeed
|
||||
binding.playbackPitchSlider.value = PreferenceUtil.playbackPitch
|
||||
|
||||
return materialDialog(R.string.playback_settings)
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.setPositiveButton(R.string.save) { _, _ ->
|
||||
updatePlaybackAndPitch(
|
||||
binding.playbackSpeedSlider.value,
|
||||
binding.playbackPitchSlider.value
|
||||
)
|
||||
}
|
||||
.setView(binding.root)
|
||||
.create()
|
||||
.colorButtons()
|
||||
}
|
||||
|
||||
private fun updatePlaybackAndPitch(speed: Float, pitch: Float) {
|
||||
PreferenceUtil.playbackSpeed = speed
|
||||
PreferenceUtil.playbackPitch = pitch
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun newInstance(): PlaybackSpeedDialog {
|
||||
return PlaybackSpeedDialog()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -43,6 +43,7 @@ import com.google.android.material.button.MaterialButton
|
|||
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
import com.google.android.material.progressindicator.CircularProgressIndicator
|
||||
import com.google.android.material.slider.Slider
|
||||
import com.google.android.material.textfield.TextInputLayout
|
||||
|
||||
fun Int.ripAlpha(): Int {
|
||||
|
@ -112,6 +113,12 @@ fun SeekBar.addAccentColor() {
|
|||
thumbTintList = colorState
|
||||
}
|
||||
|
||||
fun Slider.accent() {
|
||||
if (materialYou) return
|
||||
trackActiveTintList = context.accentColor().colorStateList
|
||||
trackInactiveTintList = context.accentColorVariant().colorStateList
|
||||
}
|
||||
|
||||
fun Button.accentTextColor() {
|
||||
if (materialYou) return
|
||||
setTextColor(ThemeStore.accentColor(App.getContext()))
|
||||
|
@ -269,6 +276,15 @@ fun Context.darkAccentColorVariant(): Int {
|
|||
)
|
||||
}
|
||||
|
||||
@ColorInt
|
||||
fun Context.accentColorVariant(): Int {
|
||||
return if (surfaceColor().isColorLight) {
|
||||
accentColor().darkerColor
|
||||
} else {
|
||||
accentColor().lighterColor
|
||||
}
|
||||
}
|
||||
|
||||
inline val @receiver:ColorInt Int.isColorLight
|
||||
get() = ColorUtil.isColorLight(this)
|
||||
|
||||
|
@ -277,3 +293,6 @@ inline val @receiver:ColorInt Int.lighterColor
|
|||
|
||||
inline val @receiver:ColorInt Int.darkerColor
|
||||
get() = ColorUtil.darkenColor(this)
|
||||
|
||||
inline val Int.colorStateList : ColorStateList
|
||||
get() = ColorStateList.valueOf(this)
|
|
@ -42,6 +42,7 @@ import androidx.viewpager.widget.ViewPager
|
|||
import code.name.monkey.appthemehelper.util.VersionUtils
|
||||
import code.name.monkey.retromusic.EXTRA_ALBUM_ID
|
||||
import code.name.monkey.retromusic.EXTRA_ARTIST_ID
|
||||
import code.name.monkey.retromusic.PLAYBACK_SPEED
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.activities.MainActivity
|
||||
import code.name.monkey.retromusic.activities.tageditor.AbsTagEditorActivity
|
||||
|
@ -79,6 +80,10 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme
|
|||
): Boolean {
|
||||
val song = MusicPlayerRemote.currentSong
|
||||
when (item.itemId) {
|
||||
R.id.action_playback_speed -> {
|
||||
PlaybackSpeedDialog.newInstance().show(childFragmentManager, "PLAYBACK_SETTINGS")
|
||||
return true
|
||||
}
|
||||
R.id.action_toggle_lyrics -> {
|
||||
PreferenceUtil.showLyrics = !item.isChecked
|
||||
item.isChecked = !item.isChecked
|
||||
|
|
|
@ -1,17 +1,3 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Hemanth Savarala.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3
|
||||
*
|
||||
* This is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
package code.name.monkey.retromusic.preferences
|
||||
|
||||
import android.app.Dialog
|
||||
|
|
|
@ -16,8 +16,10 @@ package code.name.monkey.retromusic.service;
|
|||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.media.AudioManager;
|
||||
import android.media.MediaPlayer;
|
||||
import android.media.PlaybackParams;
|
||||
import android.media.audiofx.AudioEffect;
|
||||
import android.net.Uri;
|
||||
import android.os.PowerManager;
|
||||
|
@ -26,7 +28,10 @@ import android.widget.Toast;
|
|||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
import code.name.monkey.appthemehelper.util.VersionUtils;
|
||||
import code.name.monkey.retromusic.ConstantsKt;
|
||||
import code.name.monkey.retromusic.R;
|
||||
import code.name.monkey.retromusic.service.playback.Playback;
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil;
|
||||
|
@ -35,7 +40,7 @@ import code.name.monkey.retromusic.util.PreferenceUtil;
|
|||
* @author Andrew Neal, Karim Abou Zeid (kabouzeid)
|
||||
*/
|
||||
public class MultiPlayer
|
||||
implements Playback, MediaPlayer.OnErrorListener, MediaPlayer.OnCompletionListener {
|
||||
implements Playback, MediaPlayer.OnErrorListener, MediaPlayer.OnCompletionListener, SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
public static final String TAG = MultiPlayer.class.getSimpleName();
|
||||
|
||||
private MediaPlayer mCurrentMediaPlayer = new MediaPlayer();
|
||||
|
@ -53,6 +58,7 @@ public class MultiPlayer
|
|||
MultiPlayer(final Context context) {
|
||||
this.context = context;
|
||||
mCurrentMediaPlayer.setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK);
|
||||
PreferenceManager.getDefaultSharedPreferences(context).registerOnSharedPreferenceChangeListener(this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -86,6 +92,7 @@ public class MultiPlayer
|
|||
} else {
|
||||
player.setDataSource(path);
|
||||
}
|
||||
setPlaybackSpeedPitch(player);
|
||||
player.setAudioStreamType(AudioManager.STREAM_MUSIC);
|
||||
player.prepare();
|
||||
} catch (Exception e) {
|
||||
|
@ -199,6 +206,7 @@ public class MultiPlayer
|
|||
if (mNextMediaPlayer != null) {
|
||||
mNextMediaPlayer.release();
|
||||
}
|
||||
PreferenceManager.getDefaultSharedPreferences(context).unregisterOnSharedPreferenceChangeListener(this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -346,4 +354,23 @@ public class MultiPlayer
|
|||
@Override
|
||||
public void setCrossFadeDuration(int duration) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
||||
if (key.equals(ConstantsKt.PLAYBACK_SPEED) || key.equals(ConstantsKt.PLAYBACK_PITCH)) {
|
||||
setPlaybackSpeedPitch(mCurrentMediaPlayer);
|
||||
}
|
||||
}
|
||||
|
||||
public void setPlaybackSpeedPitch(MediaPlayer mp) {
|
||||
if (VersionUtils.INSTANCE.hasMarshmallow()) {
|
||||
boolean wasPlaying = mp.isPlaying();
|
||||
mp.setPlaybackParams(new PlaybackParams()
|
||||
.setSpeed(PreferenceUtil.INSTANCE.getPlaybackSpeed())
|
||||
.setPitch(PreferenceUtil.INSTANCE.getPlaybackPitch()));
|
||||
if (!wasPlaying) {
|
||||
if (mp.isPlaying()) mp.pause();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -22,6 +22,7 @@ import static code.name.monkey.retromusic.ConstantsKt.BLURRED_ALBUM_ART;
|
|||
import static code.name.monkey.retromusic.ConstantsKt.CLASSIC_NOTIFICATION;
|
||||
import static code.name.monkey.retromusic.ConstantsKt.COLORED_NOTIFICATION;
|
||||
import static code.name.monkey.retromusic.ConstantsKt.CROSS_FADE_DURATION;
|
||||
import static code.name.monkey.retromusic.ConstantsKt.PLAYBACK_SPEED;
|
||||
import static code.name.monkey.retromusic.ConstantsKt.TOGGLE_HEADSET;
|
||||
import static code.name.monkey.retromusic.service.AudioFader.startFadeAnimator;
|
||||
|
||||
|
@ -940,6 +941,9 @@ public class MusicService extends MediaBrowserServiceCompat
|
|||
playingNotification.setPlaying(isPlaying());
|
||||
playingNotification.updateMetadata(getCurrentSong(), this::startForegroundOrNotify);
|
||||
break;
|
||||
case PLAYBACK_SPEED:
|
||||
updateMediaSessionPlaybackState();
|
||||
break;
|
||||
case TOGGLE_HEADSET:
|
||||
registerHeadsetEvents();
|
||||
break;
|
||||
|
@ -1334,7 +1338,7 @@ public class MusicService extends MediaBrowserServiceCompat
|
|||
.setState(
|
||||
isPlaying() ? PlaybackStateCompat.STATE_PLAYING : PlaybackStateCompat.STATE_PAUSED,
|
||||
getSongProgressMillis(),
|
||||
1);
|
||||
PreferenceUtil.INSTANCE.getPlaybackSpeed());
|
||||
|
||||
setCustomAction(stateBuilder);
|
||||
|
||||
|
|
|
@ -674,6 +674,16 @@ object PreferenceUtil {
|
|||
} else {
|
||||
LyricsType.OVER_LYRICS
|
||||
}
|
||||
|
||||
var playbackSpeed
|
||||
get() = sharedPreferences
|
||||
.getFloat(PLAYBACK_SPEED, 1F)
|
||||
set(value) = sharedPreferences.edit { putFloat(PLAYBACK_SPEED, value) }
|
||||
|
||||
var playbackPitch
|
||||
get() = sharedPreferences
|
||||
.getFloat(PLAYBACK_PITCH, 1F)
|
||||
set(value) = sharedPreferences.edit { putFloat(PLAYBACK_PITCH, value) }
|
||||
}
|
||||
enum class LyricsType {
|
||||
REPLACE_LYRICS, OVER_LYRICS
|
||||
|
|
12
app/src/main/res/drawable/ic_playback_speed.xml
Normal file
12
app/src/main/res/drawable/ic_playback_speed.xml
Normal file
|
@ -0,0 +1,12 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M4.027,13.011H2.014C2.201,14.887 2.914,16.672 4.071,18.158L5.504,16.718C4.706,15.628 4.197,14.352 4.027,13.011ZM22,12.002C21.996,9.511 21.073,7.111 19.407,5.264C17.741,3.417 15.451,2.254 12.98,2V4.035C15.581,4.366 17.856,5.953 19.071,8.283C20.285,10.613 20.285,13.391 19.071,15.72C17.856,18.05 15.581,19.637 12.98,19.968V21.986C15.448,21.733 17.736,20.573 19.402,18.73C21.067,16.886 21.993,14.49 22,12.002ZM6.869,18.192L5.447,19.618C6.999,20.964 8.925,21.796 10.966,22V19.981C9.458,19.789 8.036,19.168 6.869,18.192ZM5.49,7.282L4.057,5.846C2.9,7.332 2.187,9.117 2,10.993H4.014C4.184,9.65 4.692,8.373 5.49,7.282ZM10.966,4.036V2C8.926,2.206 6.998,3.038 5.446,4.382L6.869,5.808C8.038,4.838 9.46,4.223 10.966,4.036Z"
|
||||
android:fillColor="#fff"/>
|
||||
<path
|
||||
android:pathData="M15.6432,11.152C16.2699,11.5437 16.2699,12.4563 15.6432,12.848L10.53,16.0438C9.8639,16.46 9,15.9812 9,15.1958V8.8042C9,8.0188 9.8639,7.54 10.53,7.9563L15.6432,11.152Z"
|
||||
android:fillColor="#fff"/>
|
||||
</vector>
|
82
app/src/main/res/layout/dialog_playback_speed.xml
Normal file
82
app/src/main/res/layout/dialog_playback_speed.xml
Normal file
|
@ -0,0 +1,82 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright (c) 2019 Hemanth Savarala.
|
||||
~
|
||||
~ Licensed under the GNU General Public License v3
|
||||
~
|
||||
~ This is free software: you can redistribute it and/or modify it under
|
||||
~ the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation either version 3 of the License, or (at your option) any later version.
|
||||
~
|
||||
~ This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
~ without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
~ See the GNU General Public License for more details.
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:paddingVertical="16dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/playback_speed_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="3"
|
||||
android:text="@string/playback_speed"
|
||||
android:textAppearance="@style/TextViewBody1" />
|
||||
|
||||
<com.google.android.material.slider.Slider
|
||||
android:id="@+id/playback_speed_slider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="3"
|
||||
android:stepSize="0.05"
|
||||
android:valueFrom="0.25"
|
||||
android:valueTo="4.00"
|
||||
app:labelBehavior="gone"
|
||||
app:tickVisible="false" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/speed_value"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center"
|
||||
android:textAppearance="@style/TextViewBody1"
|
||||
tools:text="1.00" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/playback_pitch_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_weight="3"
|
||||
android:text="@string/playback_pitch"
|
||||
android:textAppearance="@style/TextViewBody1" />
|
||||
|
||||
<com.google.android.material.slider.Slider
|
||||
android:id="@+id/playback_pitch_slider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="3"
|
||||
android:stepSize="0.05"
|
||||
android:valueFrom="0.50"
|
||||
android:valueTo="2.00"
|
||||
app:labelBehavior="gone"
|
||||
app:tickVisible="false" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/pitch_value"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center"
|
||||
android:textAppearance="@style/TextViewBody1"
|
||||
tools:text="1.00" />
|
||||
</LinearLayout>
|
|
@ -1,6 +1,15 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
<item
|
||||
android:id="@+id/action_playback_speed"
|
||||
android:checkable="true"
|
||||
android:icon="@drawable/ic_playback_speed"
|
||||
android:orderInCategory="0"
|
||||
android:title="@string/playback_speed"
|
||||
app:showAsAction="always"
|
||||
tools:targetApi="23" />
|
||||
<item
|
||||
android:id="@+id/action_toggle_lyrics"
|
||||
android:checkable="true"
|
||||
|
|
|
@ -526,4 +526,7 @@
|
|||
<string name="you_have_to_select_at_least_one_category">You have to select at least one category.</string>
|
||||
<string name="you_will_be_forwarded_to_the_issue_tracker_website">You will be forwarded to the issue tracker website.</string>
|
||||
<string name="your_account_data_is_only_used_for_authentication">Your account data is only used for authentication.</string>
|
||||
<string name="playback_speed">Playback Speed</string>
|
||||
<string name="playback_pitch">Pitch</string>
|
||||
<string name="playback_settings">Playback Settings</string>
|
||||
</resources>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue