Change pitch by semitones
This commit is contained in:
parent
96eb1425f8
commit
5514616372
|
@ -9,6 +9,7 @@ import android.os.Bundle;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.CheckBox;
|
import android.widget.CheckBox;
|
||||||
|
import android.widget.RelativeLayout;
|
||||||
import android.widget.SeekBar;
|
import android.widget.SeekBar;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
@ -37,6 +38,7 @@ public class PlaybackParameterDialog extends DialogFragment {
|
||||||
|
|
||||||
private static final double DEFAULT_TEMPO = 1.00f;
|
private static final double DEFAULT_TEMPO = 1.00f;
|
||||||
private static final double DEFAULT_PITCH = 1.00f;
|
private static final double DEFAULT_PITCH = 1.00f;
|
||||||
|
private static final int DEFAULT_SEMITONES = 0;
|
||||||
private static final double DEFAULT_STEP = STEP_TWENTY_FIVE_PERCENT_VALUE;
|
private static final double DEFAULT_STEP = STEP_TWENTY_FIVE_PERCENT_VALUE;
|
||||||
private static final boolean DEFAULT_SKIP_SILENCE = false;
|
private static final boolean DEFAULT_SKIP_SILENCE = false;
|
||||||
|
|
||||||
|
@ -64,9 +66,11 @@ public class PlaybackParameterDialog extends DialogFragment {
|
||||||
|
|
||||||
private double initialTempo = DEFAULT_TEMPO;
|
private double initialTempo = DEFAULT_TEMPO;
|
||||||
private double initialPitch = DEFAULT_PITCH;
|
private double initialPitch = DEFAULT_PITCH;
|
||||||
|
private int initialSemitones = DEFAULT_SEMITONES;
|
||||||
private boolean initialSkipSilence = DEFAULT_SKIP_SILENCE;
|
private boolean initialSkipSilence = DEFAULT_SKIP_SILENCE;
|
||||||
private double tempo = DEFAULT_TEMPO;
|
private double tempo = DEFAULT_TEMPO;
|
||||||
private double pitch = DEFAULT_PITCH;
|
private double pitch = DEFAULT_PITCH;
|
||||||
|
private int semitones = DEFAULT_SEMITONES;
|
||||||
private double stepSize = DEFAULT_STEP;
|
private double stepSize = DEFAULT_STEP;
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
|
@ -78,6 +82,8 @@ public class PlaybackParameterDialog extends DialogFragment {
|
||||||
@Nullable
|
@Nullable
|
||||||
private TextView tempoStepUpText;
|
private TextView tempoStepUpText;
|
||||||
@Nullable
|
@Nullable
|
||||||
|
private RelativeLayout pitchControl;
|
||||||
|
@Nullable
|
||||||
private SeekBar pitchSlider;
|
private SeekBar pitchSlider;
|
||||||
@Nullable
|
@Nullable
|
||||||
private TextView pitchCurrentText;
|
private TextView pitchCurrentText;
|
||||||
|
@ -86,9 +92,23 @@ public class PlaybackParameterDialog extends DialogFragment {
|
||||||
@Nullable
|
@Nullable
|
||||||
private TextView pitchStepUpText;
|
private TextView pitchStepUpText;
|
||||||
@Nullable
|
@Nullable
|
||||||
|
private RelativeLayout semitoneControl;
|
||||||
|
@Nullable
|
||||||
|
private SeekBar semitoneSlider;
|
||||||
|
@Nullable
|
||||||
|
private TextView semitoneCurrentText;
|
||||||
|
@Nullable
|
||||||
|
private TextView semitoneStepDownText;
|
||||||
|
@Nullable
|
||||||
|
private TextView semitoneStepUpText;
|
||||||
|
@Nullable
|
||||||
|
private View separatorStepSizeSelector;
|
||||||
|
@Nullable
|
||||||
private CheckBox unhookingCheckbox;
|
private CheckBox unhookingCheckbox;
|
||||||
@Nullable
|
@Nullable
|
||||||
private CheckBox skipSilenceCheckbox;
|
private CheckBox skipSilenceCheckbox;
|
||||||
|
@Nullable
|
||||||
|
private CheckBox adjustBySemitonesCheckbox;
|
||||||
|
|
||||||
public static PlaybackParameterDialog newInstance(final double playbackTempo,
|
public static PlaybackParameterDialog newInstance(final double playbackTempo,
|
||||||
final double playbackPitch,
|
final double playbackPitch,
|
||||||
|
@ -101,6 +121,7 @@ public class PlaybackParameterDialog extends DialogFragment {
|
||||||
|
|
||||||
dialog.tempo = playbackTempo;
|
dialog.tempo = playbackTempo;
|
||||||
dialog.pitch = playbackPitch;
|
dialog.pitch = playbackPitch;
|
||||||
|
dialog.semitones = dialog.percentToSemitones(playbackPitch);
|
||||||
|
|
||||||
dialog.initialSkipSilence = playbackSkipSilence;
|
dialog.initialSkipSilence = playbackSkipSilence;
|
||||||
return dialog;
|
return dialog;
|
||||||
|
@ -127,9 +148,11 @@ public class PlaybackParameterDialog extends DialogFragment {
|
||||||
if (savedInstanceState != null) {
|
if (savedInstanceState != null) {
|
||||||
initialTempo = savedInstanceState.getDouble(INITIAL_TEMPO_KEY, DEFAULT_TEMPO);
|
initialTempo = savedInstanceState.getDouble(INITIAL_TEMPO_KEY, DEFAULT_TEMPO);
|
||||||
initialPitch = savedInstanceState.getDouble(INITIAL_PITCH_KEY, DEFAULT_PITCH);
|
initialPitch = savedInstanceState.getDouble(INITIAL_PITCH_KEY, DEFAULT_PITCH);
|
||||||
|
initialSemitones = percentToSemitones(initialPitch);
|
||||||
|
|
||||||
tempo = savedInstanceState.getDouble(TEMPO_KEY, DEFAULT_TEMPO);
|
tempo = savedInstanceState.getDouble(TEMPO_KEY, DEFAULT_TEMPO);
|
||||||
pitch = savedInstanceState.getDouble(PITCH_KEY, DEFAULT_PITCH);
|
pitch = savedInstanceState.getDouble(PITCH_KEY, DEFAULT_PITCH);
|
||||||
|
semitones = percentToSemitones(pitch);
|
||||||
stepSize = savedInstanceState.getDouble(STEP_SIZE_KEY, DEFAULT_STEP);
|
stepSize = savedInstanceState.getDouble(STEP_SIZE_KEY, DEFAULT_STEP);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -160,9 +183,11 @@ public class PlaybackParameterDialog extends DialogFragment {
|
||||||
.setView(view)
|
.setView(view)
|
||||||
.setCancelable(true)
|
.setCancelable(true)
|
||||||
.setNegativeButton(R.string.cancel, (dialogInterface, i) ->
|
.setNegativeButton(R.string.cancel, (dialogInterface, i) ->
|
||||||
setPlaybackParameters(initialTempo, initialPitch, initialSkipSilence))
|
setPlaybackParameters(initialTempo, initialPitch,
|
||||||
|
initialSemitones, initialSkipSilence))
|
||||||
.setNeutralButton(R.string.playback_reset, (dialogInterface, i) ->
|
.setNeutralButton(R.string.playback_reset, (dialogInterface, i) ->
|
||||||
setPlaybackParameters(DEFAULT_TEMPO, DEFAULT_PITCH, DEFAULT_SKIP_SILENCE))
|
setPlaybackParameters(DEFAULT_TEMPO, DEFAULT_PITCH,
|
||||||
|
DEFAULT_SEMITONES, DEFAULT_SKIP_SILENCE))
|
||||||
.setPositiveButton(R.string.ok, (dialogInterface, i) ->
|
.setPositiveButton(R.string.ok, (dialogInterface, i) ->
|
||||||
setCurrentPlaybackParameters());
|
setCurrentPlaybackParameters());
|
||||||
|
|
||||||
|
@ -176,12 +201,47 @@ public class PlaybackParameterDialog extends DialogFragment {
|
||||||
private void setupControlViews(@NonNull final View rootView) {
|
private void setupControlViews(@NonNull final View rootView) {
|
||||||
setupHookingControl(rootView);
|
setupHookingControl(rootView);
|
||||||
setupSkipSilenceControl(rootView);
|
setupSkipSilenceControl(rootView);
|
||||||
|
setupAdjustBySemitonesControl(rootView);
|
||||||
|
|
||||||
setupTempoControl(rootView);
|
setupTempoControl(rootView);
|
||||||
setupPitchControl(rootView);
|
setupPitchControl(rootView);
|
||||||
|
setupSemitoneControl(rootView);
|
||||||
|
|
||||||
|
togglePitchSliderType(rootView);
|
||||||
|
|
||||||
setStepSize(stepSize);
|
setStepSize(stepSize);
|
||||||
setupStepSizeSelector(rootView);
|
}
|
||||||
|
|
||||||
|
private void togglePitchSliderType(@NonNull final View rootView) {
|
||||||
|
|
||||||
|
pitchControl = rootView.findViewById(R.id.pitchControl);
|
||||||
|
semitoneControl = rootView.findViewById(R.id.semitoneControl);
|
||||||
|
|
||||||
|
separatorStepSizeSelector = rootView.findViewById(R.id.separatorStepSizeSelector);
|
||||||
|
final RelativeLayout.LayoutParams params =
|
||||||
|
(RelativeLayout.LayoutParams) separatorStepSizeSelector.getLayoutParams();
|
||||||
|
if (pitchControl != null && semitoneControl != null && unhookingCheckbox != null) {
|
||||||
|
if (getCurrentAdjustBySemitones()) {
|
||||||
|
// replaces pitchControl slider with semitoneControl slider
|
||||||
|
pitchControl.setVisibility(View.GONE);
|
||||||
|
semitoneControl.setVisibility(View.VISIBLE);
|
||||||
|
params.addRule(RelativeLayout.BELOW, R.id.semitoneControl);
|
||||||
|
|
||||||
|
// forces unhook for semitones
|
||||||
|
unhookingCheckbox.setChecked(true);
|
||||||
|
unhookingCheckbox.setEnabled(false);
|
||||||
|
|
||||||
|
setupTempoStepSizeSelector(rootView);
|
||||||
|
} else {
|
||||||
|
semitoneControl.setVisibility(View.GONE);
|
||||||
|
pitchControl.setVisibility(View.VISIBLE);
|
||||||
|
params.addRule(RelativeLayout.BELOW, R.id.pitchControl);
|
||||||
|
|
||||||
|
// (re)enables hooking selection
|
||||||
|
unhookingCheckbox.setEnabled(true);
|
||||||
|
setupCombinedStepSizeSelector(rootView);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupTempoControl(@NonNull final View rootView) {
|
private void setupTempoControl(@NonNull final View rootView) {
|
||||||
|
@ -234,23 +294,40 @@ public class PlaybackParameterDialog extends DialogFragment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setupSemitoneControl(@NonNull final View rootView) {
|
||||||
|
semitoneSlider = rootView.findViewById(R.id.semitoneSeekbar);
|
||||||
|
semitoneCurrentText = rootView.findViewById(R.id.semitoneCurrentText);
|
||||||
|
semitoneStepDownText = rootView.findViewById(R.id.semitoneStepDown);
|
||||||
|
semitoneStepUpText = rootView.findViewById(R.id.semitoneStepUp);
|
||||||
|
|
||||||
|
if (semitoneCurrentText != null) {
|
||||||
|
semitoneCurrentText.setText(getSignedSemitonesString(semitones));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (semitoneSlider != null) {
|
||||||
|
setSemitoneSlider(semitones);
|
||||||
|
semitoneSlider.setOnSeekBarChangeListener(getOnSemitoneChangedListener());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private void setupHookingControl(@NonNull final View rootView) {
|
private void setupHookingControl(@NonNull final View rootView) {
|
||||||
unhookingCheckbox = rootView.findViewById(R.id.unhookCheckbox);
|
unhookingCheckbox = rootView.findViewById(R.id.unhookCheckbox);
|
||||||
if (unhookingCheckbox != null) {
|
if (unhookingCheckbox != null) {
|
||||||
// restore whether pitch and tempo are unhooked or not
|
// restores whether pitch and tempo are unhooked or not
|
||||||
unhookingCheckbox.setChecked(PreferenceManager
|
unhookingCheckbox.setChecked(PreferenceManager
|
||||||
.getDefaultSharedPreferences(requireContext())
|
.getDefaultSharedPreferences(requireContext())
|
||||||
.getBoolean(getString(R.string.playback_unhook_key), true));
|
.getBoolean(getString(R.string.playback_unhook_key), true));
|
||||||
|
|
||||||
unhookingCheckbox.setOnCheckedChangeListener((compoundButton, isChecked) -> {
|
unhookingCheckbox.setOnCheckedChangeListener((compoundButton, isChecked) -> {
|
||||||
// save whether pitch and tempo are unhooked or not
|
// saves whether pitch and tempo are unhooked or not
|
||||||
PreferenceManager.getDefaultSharedPreferences(requireContext())
|
PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||||
.edit()
|
.edit()
|
||||||
.putBoolean(getString(R.string.playback_unhook_key), isChecked)
|
.putBoolean(getString(R.string.playback_unhook_key), isChecked)
|
||||||
.apply();
|
.apply();
|
||||||
|
|
||||||
if (!isChecked) {
|
if (!isChecked) {
|
||||||
// when unchecked, slide back to the minimum of current tempo or pitch
|
// when unchecked, slides back to the minimum of current tempo or pitch
|
||||||
final double minimum = Math.min(getCurrentPitch(), getCurrentTempo());
|
final double minimum = Math.min(getCurrentPitch(), getCurrentTempo());
|
||||||
setSliders(minimum);
|
setSliders(minimum);
|
||||||
setCurrentPlaybackParameters();
|
setCurrentPlaybackParameters();
|
||||||
|
@ -268,6 +345,42 @@ public class PlaybackParameterDialog extends DialogFragment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setupAdjustBySemitonesControl(@NonNull final View rootView) {
|
||||||
|
adjustBySemitonesCheckbox = rootView.findViewById(R.id.adjustBySemitonesCheckbox);
|
||||||
|
if (adjustBySemitonesCheckbox != null) {
|
||||||
|
// restores whether semitone adjustment is used or not
|
||||||
|
adjustBySemitonesCheckbox.setChecked(PreferenceManager
|
||||||
|
.getDefaultSharedPreferences(requireContext())
|
||||||
|
.getBoolean(getString(R.string.playback_adjust_by_semitones_key), true));
|
||||||
|
|
||||||
|
// stores whether semitone adjustment is used or not
|
||||||
|
adjustBySemitonesCheckbox.setOnCheckedChangeListener((compoundButton, isChecked) -> {
|
||||||
|
PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||||
|
.edit()
|
||||||
|
.putBoolean(getString(R.string.playback_adjust_by_semitones_key), isChecked)
|
||||||
|
.apply();
|
||||||
|
togglePitchSliderType(rootView);
|
||||||
|
if (isChecked) {
|
||||||
|
setPlaybackParameters(
|
||||||
|
getCurrentTempo(),
|
||||||
|
getCurrentPitch(),
|
||||||
|
percentToSemitones(getCurrentPitch()),
|
||||||
|
getCurrentSkipSilence()
|
||||||
|
);
|
||||||
|
setSemitoneSlider(percentToSemitones(getCurrentPitch()));
|
||||||
|
} else {
|
||||||
|
setPlaybackParameters(
|
||||||
|
getCurrentTempo(),
|
||||||
|
semitonesToPercent(getCurrentSemitones()),
|
||||||
|
getCurrentSemitones(),
|
||||||
|
getCurrentSkipSilence()
|
||||||
|
);
|
||||||
|
setPitchSlider(semitonesToPercent(getCurrentSemitones()));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void setupStepSizeSelector(@NonNull final View rootView) {
|
private void setupStepSizeSelector(@NonNull final View rootView) {
|
||||||
final TextView stepSizeOnePercentText = rootView.findViewById(R.id.stepSizeOnePercent);
|
final TextView stepSizeOnePercentText = rootView.findViewById(R.id.stepSizeOnePercent);
|
||||||
final TextView stepSizeFivePercentText = rootView.findViewById(R.id.stepSizeFivePercent);
|
final TextView stepSizeFivePercentText = rootView.findViewById(R.id.stepSizeFivePercent);
|
||||||
|
@ -310,6 +423,22 @@ public class PlaybackParameterDialog extends DialogFragment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setupTempoStepSizeSelector(@NonNull final View rootView) {
|
||||||
|
final TextView playbackStepTypeText = rootView.findViewById(R.id.playback_step_type);
|
||||||
|
if (playbackStepTypeText != null) {
|
||||||
|
playbackStepTypeText.setText(R.string.playback_tempo_step);
|
||||||
|
}
|
||||||
|
setupStepSizeSelector(rootView);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupCombinedStepSizeSelector(@NonNull final View rootView) {
|
||||||
|
final TextView playbackStepTypeText = rootView.findViewById(R.id.playback_step_type);
|
||||||
|
if (playbackStepTypeText != null) {
|
||||||
|
playbackStepTypeText.setText(R.string.playback_step);
|
||||||
|
}
|
||||||
|
setupStepSizeSelector(rootView);
|
||||||
|
}
|
||||||
|
|
||||||
private void setStepSize(final double stepSize) {
|
private void setStepSize(final double stepSize) {
|
||||||
this.stepSize = stepSize;
|
this.stepSize = stepSize;
|
||||||
|
|
||||||
|
@ -344,6 +473,20 @@ public class PlaybackParameterDialog extends DialogFragment {
|
||||||
setCurrentPlaybackParameters();
|
setCurrentPlaybackParameters();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (semitoneStepDownText != null) {
|
||||||
|
semitoneStepDownText.setOnClickListener(view -> {
|
||||||
|
onSemitoneSliderUpdated(getCurrentSemitones() - 1);
|
||||||
|
setCurrentPlaybackParameters();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (semitoneStepUpText != null) {
|
||||||
|
semitoneStepUpText.setOnClickListener(view -> {
|
||||||
|
onSemitoneSliderUpdated(getCurrentSemitones() + 1);
|
||||||
|
setCurrentPlaybackParameters();
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -398,6 +541,33 @@ public class PlaybackParameterDialog extends DialogFragment {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private SeekBar.OnSeekBarChangeListener getOnSemitoneChangedListener() {
|
||||||
|
return new SeekBar.OnSeekBarChangeListener() {
|
||||||
|
@Override
|
||||||
|
public void onProgressChanged(final SeekBar seekBar, final int progress,
|
||||||
|
final boolean fromUser) {
|
||||||
|
// semitone slider supplies values 0 to 25, subtraction by 12 is required
|
||||||
|
final int currentSemitones = progress - 12;
|
||||||
|
if (fromUser) { // this change is first in chain
|
||||||
|
onSemitoneSliderUpdated(currentSemitones);
|
||||||
|
// line below also saves semitones as pitch percentages
|
||||||
|
onPitchSliderUpdated(semitonesToPercent(currentSemitones));
|
||||||
|
setCurrentPlaybackParameters();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStartTrackingTouch(final SeekBar seekBar) {
|
||||||
|
// Do Nothing.
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStopTrackingTouch(final SeekBar seekBar) {
|
||||||
|
// Do Nothing.
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private void onTempoSliderUpdated(final double newTempo) {
|
private void onTempoSliderUpdated(final double newTempo) {
|
||||||
if (unhookingCheckbox == null) {
|
if (unhookingCheckbox == null) {
|
||||||
return;
|
return;
|
||||||
|
@ -420,6 +590,13 @@ public class PlaybackParameterDialog extends DialogFragment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void onSemitoneSliderUpdated(final int newSemitone) {
|
||||||
|
if (unhookingCheckbox == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setSemitoneSlider(newSemitone);
|
||||||
|
}
|
||||||
|
|
||||||
private void setSliders(final double newValue) {
|
private void setSliders(final double newValue) {
|
||||||
setTempoSlider(newValue);
|
setTempoSlider(newValue);
|
||||||
setPitchSlider(newValue);
|
setPitchSlider(newValue);
|
||||||
|
@ -439,25 +616,49 @@ public class PlaybackParameterDialog extends DialogFragment {
|
||||||
pitchSlider.setProgress(strategy.progressOf(newPitch));
|
pitchSlider.setProgress(strategy.progressOf(newPitch));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setSemitoneSlider(final int newSemitone) {
|
||||||
|
if (semitoneSlider == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
semitoneSlider.setProgress(newSemitone + 12);
|
||||||
|
}
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Helper
|
// Helper
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
private void setCurrentPlaybackParameters() {
|
private void setCurrentPlaybackParameters() {
|
||||||
setPlaybackParameters(getCurrentTempo(), getCurrentPitch(), getCurrentSkipSilence());
|
if (getCurrentAdjustBySemitones()) {
|
||||||
|
setPlaybackParameters(
|
||||||
|
getCurrentTempo(),
|
||||||
|
semitonesToPercent(getCurrentSemitones()),
|
||||||
|
getCurrentSemitones(),
|
||||||
|
getCurrentSkipSilence()
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
setPlaybackParameters(
|
||||||
|
getCurrentTempo(),
|
||||||
|
getCurrentPitch(),
|
||||||
|
percentToSemitones(getCurrentPitch()),
|
||||||
|
getCurrentSkipSilence()
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setPlaybackParameters(final double newTempo, final double newPitch,
|
private void setPlaybackParameters(final double newTempo, final double newPitch,
|
||||||
final boolean skipSilence) {
|
final int newSemitones, final boolean skipSilence) {
|
||||||
if (callback != null && tempoCurrentText != null && pitchCurrentText != null) {
|
if (callback != null && tempoCurrentText != null
|
||||||
|
&& pitchCurrentText != null && semitoneCurrentText != null) {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, "Setting playback parameters to "
|
Log.d(TAG, "Setting playback parameters to "
|
||||||
+ "tempo=[" + newTempo + "], "
|
+ "tempo=[" + newTempo + "], "
|
||||||
+ "pitch=[" + newPitch + "]");
|
+ "pitch=[" + newPitch + "], "
|
||||||
|
+ "semitones=[" + newSemitones + "]");
|
||||||
}
|
}
|
||||||
|
|
||||||
tempoCurrentText.setText(PlayerHelper.formatSpeed(newTempo));
|
tempoCurrentText.setText(PlayerHelper.formatSpeed(newTempo));
|
||||||
pitchCurrentText.setText(PlayerHelper.formatPitch(newPitch));
|
pitchCurrentText.setText(PlayerHelper.formatPitch(newPitch));
|
||||||
|
semitoneCurrentText.setText(getSignedSemitonesString(newSemitones));
|
||||||
callback.onPlaybackParameterChanged((float) newTempo, (float) newPitch, skipSilence);
|
callback.onPlaybackParameterChanged((float) newTempo, (float) newPitch, skipSilence);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -470,6 +671,11 @@ public class PlaybackParameterDialog extends DialogFragment {
|
||||||
return pitchSlider == null ? pitch : strategy.valueOf(pitchSlider.getProgress());
|
return pitchSlider == null ? pitch : strategy.valueOf(pitchSlider.getProgress());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int getCurrentSemitones() {
|
||||||
|
// semitoneSlider is absolute, that's why - 12
|
||||||
|
return semitoneSlider == null ? semitones : semitoneSlider.getProgress() - 12;
|
||||||
|
}
|
||||||
|
|
||||||
private double getCurrentStepSize() {
|
private double getCurrentStepSize() {
|
||||||
return stepSize;
|
return stepSize;
|
||||||
}
|
}
|
||||||
|
@ -478,6 +684,10 @@ public class PlaybackParameterDialog extends DialogFragment {
|
||||||
return skipSilenceCheckbox != null && skipSilenceCheckbox.isChecked();
|
return skipSilenceCheckbox != null && skipSilenceCheckbox.isChecked();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean getCurrentAdjustBySemitones() {
|
||||||
|
return adjustBySemitonesCheckbox != null && adjustBySemitonesCheckbox.isChecked();
|
||||||
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private static String getStepUpPercentString(final double percent) {
|
private static String getStepUpPercentString(final double percent) {
|
||||||
return STEP_UP_SIGN + getPercentString(percent);
|
return STEP_UP_SIGN + getPercentString(percent);
|
||||||
|
@ -493,8 +703,21 @@ public class PlaybackParameterDialog extends DialogFragment {
|
||||||
return PlayerHelper.formatPitch(percent);
|
return PlayerHelper.formatPitch(percent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private static String getSignedSemitonesString(final int semitones) {
|
||||||
|
return semitones > 0 ? "+" + semitones : "" + semitones;
|
||||||
|
}
|
||||||
|
|
||||||
public interface Callback {
|
public interface Callback {
|
||||||
void onPlaybackParameterChanged(float playbackTempo, float playbackPitch,
|
void onPlaybackParameterChanged(float playbackTempo, float playbackPitch,
|
||||||
boolean playbackSkipSilence);
|
boolean playbackSkipSilence);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public double semitonesToPercent(final int inSemitones) {
|
||||||
|
return Math.pow(2, inSemitones / 12.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int percentToSemitones(final double inPercent) {
|
||||||
|
return (int) Math.round(12 * Math.log(inPercent) / Math.log(2));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -261,11 +261,121 @@
|
||||||
tools:text="+5%" />
|
tools:text="+5%" />
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:id="@+id/semitoneControl"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:layout_below="@id/pitchControlText"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<org.schabi.newpipe.views.NewPipeTextView
|
||||||
|
android:id="@+id/semitoneStepDown"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_alignParentStart="true"
|
||||||
|
android:layout_alignParentLeft="true"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:background="?attr/selectableItemBackground"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="♭"
|
||||||
|
android:textColor="?attr/colorAccent"
|
||||||
|
android:textSize="24sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
tools:ignore="HardcodedText"
|
||||||
|
tools:text="♭" />
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:id="@+id/semitoneDisplay"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginLeft="4dp"
|
||||||
|
android:layout_marginRight="4dp"
|
||||||
|
android:layout_toStartOf="@+id/semitoneStepUp"
|
||||||
|
android:layout_toLeftOf="@+id/semitoneStepUp"
|
||||||
|
android:layout_toEndOf="@+id/semitoneStepDown"
|
||||||
|
android:layout_toRightOf="@+id/semitoneStepDown"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<org.schabi.newpipe.views.NewPipeTextView
|
||||||
|
android:id="@+id/semitoneMinimumText"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentStart="true"
|
||||||
|
android:layout_alignParentLeft="true"
|
||||||
|
android:layout_marginStart="4dp"
|
||||||
|
android:layout_marginLeft="4dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="-12"
|
||||||
|
android:textColor="?attr/colorAccent"
|
||||||
|
tools:ignore="HardcodedText"
|
||||||
|
tools:text="-12" />
|
||||||
|
|
||||||
|
<org.schabi.newpipe.views.NewPipeTextView
|
||||||
|
android:id="@+id/semitoneCurrentText"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_centerHorizontal="true"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="--"
|
||||||
|
android:textColor="?attr/colorAccent"
|
||||||
|
android:textStyle="bold"
|
||||||
|
tools:ignore="HardcodedText"
|
||||||
|
tools:text="0" />
|
||||||
|
|
||||||
|
<org.schabi.newpipe.views.NewPipeTextView
|
||||||
|
android:id="@+id/semitoneMaximumText"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:layout_alignParentRight="true"
|
||||||
|
android:layout_marginEnd="4dp"
|
||||||
|
android:layout_marginRight="4dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="+12"
|
||||||
|
android:textColor="?attr/colorAccent"
|
||||||
|
tools:ignore="HardcodedText"
|
||||||
|
tools:text="+12" />
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatSeekBar
|
||||||
|
android:id="@+id/semitoneSeekbar"
|
||||||
|
style="@style/Widget.AppCompat.SeekBar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_below="@+id/semitoneCurrentText"
|
||||||
|
android:max="24"
|
||||||
|
android:paddingBottom="4dp"
|
||||||
|
android:progress="12" />
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
<org.schabi.newpipe.views.NewPipeTextView
|
||||||
|
android:id="@+id/semitoneStepUp"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:layout_alignParentRight="true"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:layout_marginEnd="4dp"
|
||||||
|
android:layout_marginRight="4dp"
|
||||||
|
android:background="?attr/selectableItemBackground"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="♯"
|
||||||
|
android:textColor="?attr/colorAccent"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
tools:ignore="HardcodedText"
|
||||||
|
tools:text="♯" />
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
<View
|
<View
|
||||||
android:id="@+id/separatorStepSizeSelector"
|
android:id="@+id/separatorStepSizeSelector"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="1dp"
|
android:layout_height="1dp"
|
||||||
android:layout_below="@+id/pitchControl"
|
android:layout_below="@+id/semitoneControl"
|
||||||
android:layout_marginStart="12dp"
|
android:layout_marginStart="12dp"
|
||||||
android:layout_marginTop="6dp"
|
android:layout_marginTop="6dp"
|
||||||
android:layout_marginEnd="12dp"
|
android:layout_marginEnd="12dp"
|
||||||
|
@ -280,6 +390,7 @@
|
||||||
android:orientation="horizontal">
|
android:orientation="horizontal">
|
||||||
|
|
||||||
<org.schabi.newpipe.views.NewPipeTextView
|
<org.schabi.newpipe.views.NewPipeTextView
|
||||||
|
android:id="@+id/playback_step_type"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
|
@ -380,6 +491,18 @@
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:text="@string/skip_silence_checkbox" />
|
android:text="@string/skip_silence_checkbox" />
|
||||||
|
|
||||||
|
<CheckBox
|
||||||
|
android:id="@+id/adjustBySemitonesCheckbox"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_below="@id/skipSilenceCheckbox"
|
||||||
|
android:layout_centerHorizontal="true"
|
||||||
|
android:checked="false"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:text="@string/adjust_by_semitones_checkbox" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<!-- END HERE -->
|
<!-- END HERE -->
|
||||||
|
|
|
@ -258,6 +258,7 @@
|
||||||
<string name="enable_playback_resume_key" translatable="false">enable_playback_resume</string>
|
<string name="enable_playback_resume_key" translatable="false">enable_playback_resume</string>
|
||||||
<string name="enable_playback_state_lists_key" translatable="false">enable_playback_state_lists</string>
|
<string name="enable_playback_state_lists_key" translatable="false">enable_playback_state_lists</string>
|
||||||
<string name="playback_unhook_key" translatable="false">playback_unhook_key</string>
|
<string name="playback_unhook_key" translatable="false">playback_unhook_key</string>
|
||||||
|
<string name="playback_adjust_by_semitones_key" translatable="false">playback_adjust_by_semitones_key</string>
|
||||||
<string name="playback_speed_key" translatable="false">playback_speed_key</string>
|
<string name="playback_speed_key" translatable="false">playback_speed_key</string>
|
||||||
<string name="playback_pitch_key" translatable="false">playback_pitch_key</string>
|
<string name="playback_pitch_key" translatable="false">playback_pitch_key</string>
|
||||||
<string name="playback_skip_silence_key" translatable="false">playback_skip_silence_key</string>
|
<string name="playback_skip_silence_key" translatable="false">playback_skip_silence_key</string>
|
||||||
|
|
|
@ -503,7 +503,9 @@
|
||||||
<string name="playback_pitch">Pitch</string>
|
<string name="playback_pitch">Pitch</string>
|
||||||
<string name="unhook_checkbox">Unhook (may cause distortion)</string>
|
<string name="unhook_checkbox">Unhook (may cause distortion)</string>
|
||||||
<string name="skip_silence_checkbox">Fast-forward during silence</string>
|
<string name="skip_silence_checkbox">Fast-forward during silence</string>
|
||||||
|
<string name="adjust_by_semitones_checkbox">Adjust pitch by musical semitones</string>
|
||||||
<string name="playback_step">Step</string>
|
<string name="playback_step">Step</string>
|
||||||
|
<string name="playback_tempo_step">Tempo step</string>
|
||||||
<string name="playback_reset">Reset</string>
|
<string name="playback_reset">Reset</string>
|
||||||
<!-- GDPR dialog -->
|
<!-- GDPR dialog -->
|
||||||
<string name="start_accept_privacy_policy">In order to comply with the European General Data Protection Regulation (GDPR), we herby draw your attention to NewPipe\'s privacy policy. Please read it carefully.\nYou must accept it to send us the bug report.</string>
|
<string name="start_accept_privacy_policy">In order to comply with the European General Data Protection Regulation (GDPR), we herby draw your attention to NewPipe\'s privacy policy. Please read it carefully.\nYou must accept it to send us the bug report.</string>
|
||||||
|
|
Loading…
Reference in New Issue