Skip to content

Commit

Permalink
fix AntennaPod#4229 - configurable skip silence
Browse files Browse the repository at this point in the history
- followup feed-specific skip silence config (AntennaPod#6910)
- toggle buttons to configure
- medium 300ms gap
- aggressive 50ms gap
  • Loading branch information
MartinNowak committed May 17, 2024
1 parent 27aa5cb commit d30c25e
Show file tree
Hide file tree
Showing 14 changed files with 214 additions and 64 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,21 @@
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
import com.google.android.material.button.MaterialButtonToggleGroup;
import com.google.android.material.chip.Chip;
import com.google.android.material.snackbar.Snackbar;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.event.playback.SpeedChangedEvent;
import de.danoeh.antennapod.storage.preferences.UserPreferences;
import de.danoeh.antennapod.core.util.playback.PlaybackController;
import de.danoeh.antennapod.model.feed.FeedPreferences;
import de.danoeh.antennapod.view.ItemOffsetDecoration;
import de.danoeh.antennapod.view.PlaybackSpeedSeekBar;
import org.greenrobot.eventbus.EventBus;
Expand All @@ -30,14 +33,15 @@
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Set;

public class VariableSpeedDialog extends BottomSheetDialogFragment {
private SpeedSelectionAdapter adapter;
private PlaybackController controller;
private final List<Float> selectedSpeeds;
private PlaybackSpeedSeekBar speedSeekBar;
private Chip addCurrentSpeedChip;
private CheckBox skipSilenceCheckbox;
private MaterialButtonToggleGroup skipSilenceToggleGroup;

public VariableSpeedDialog() {
DecimalFormatSymbols format = new DecimalFormatSymbols(Locale.US);
Expand Down Expand Up @@ -75,8 +79,28 @@ public void updateSpeed(SpeedChangedEvent event) {
addCurrentSpeedChip.setText(String.format(Locale.getDefault(), "%1$.2f", event.getNewSpeed()));
}

public void updateSkipSilence(boolean skipSilence) {
skipSilenceCheckbox.setChecked(skipSilence);
public void updateSkipSilence(FeedPreferences.SkipSilence skipSilence) {
int id = View.NO_ID;
switch (skipSilence) {
case OFF: id = R.id.skipSilenceOff; break;
case MEDIUM: id = R.id.skipSilenceMedium; break;
case AGGRESSIVE: id = R.id.skipSilenceAggressive; break;
}
skipSilenceToggleGroup.check(id);
}

private void onSkipSilenceChanged(MaterialButtonToggleGroup group, int checkedId, boolean isChecked) {
FeedPreferences.SkipSilence opt;
final int id = group.getCheckedButtonId();
if (id == R.id.skipSilenceOff) opt = FeedPreferences.SkipSilence.OFF;
else if (id == R.id.skipSilenceMedium) opt = FeedPreferences.SkipSilence.MEDIUM;
else if (id == R.id.skipSilenceAggressive) opt = FeedPreferences.SkipSilence.AGGRESSIVE;
else opt = FeedPreferences.SkipSilence.GLOBAL;
if (UserPreferences.getSkipSilence() != opt)
UserPreferences.setSkipSilence(opt);
if (controller != null) {
controller.setSkipSilence(opt);
}
}

@Nullable
Expand Down Expand Up @@ -104,12 +128,10 @@ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup c
addCurrentSpeedChip.setCloseIconContentDescription(getString(R.string.add_preset));
addCurrentSpeedChip.setOnClickListener(v -> addCurrentSpeed());

skipSilenceCheckbox = root.findViewById(R.id.skipSilence);
skipSilenceCheckbox.setChecked(UserPreferences.isSkipSilence());
skipSilenceCheckbox.setOnCheckedChangeListener((buttonView, isChecked) -> {
UserPreferences.setSkipSilence(isChecked);
controller.setSkipSilence(isChecked);
});
skipSilenceToggleGroup = root.findViewById(R.id.skipSilence);
skipSilenceToggleGroup.addOnButtonCheckedListener(
(group, checkedId, isChecked) -> onSkipSilenceChanged(group, checkedId, isChecked));
updateSkipSilence(UserPreferences.getSkipSilence());
return root;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -246,16 +246,19 @@ private void setupPlaybackSpeedPreference() {
viewBinding.seekBar.setAlpha(isChecked ? 0.4f : 1f);
viewBinding.currentSpeedLabel.setAlpha(isChecked ? 0.4f : 1f);

viewBinding.skipSilenceFeed.setEnabled(!isChecked);
viewBinding.skipSilenceFeed.setAlpha(isChecked ? 0.4f : 1f);
viewBinding.skipSilence.setEnabled(!isChecked);
viewBinding.skipSilence.setAlpha(isChecked ? 0.4f : 1f);
});
float speed = feedPreferences.getFeedPlaybackSpeed();
FeedPreferences.SkipSilence skipSilence = feedPreferences.getFeedSkipSilence();
boolean isGlobal = speed == FeedPreferences.SPEED_USE_GLOBAL;
viewBinding.useGlobalCheckbox.setChecked(isGlobal);
viewBinding.seekBar.updateSpeed(isGlobal ? 1 : speed);
viewBinding.skipSilenceFeed.setChecked(!isGlobal
&& skipSilence == FeedPreferences.SkipSilence.AGGRESSIVE);
if (isGlobal) {
viewBinding.skipSilence.clearChecked();
} else {
viewBinding.skipSilence.check(skipSilence.code);
}
new MaterialAlertDialogBuilder(getContext())
.setTitle(R.string.playback_speed)
.setView(viewBinding.getRoot())
Expand All @@ -266,11 +269,11 @@ private void setupPlaybackSpeedPreference() {
FeedPreferences.SkipSilence newSkipSilence;
if (viewBinding.useGlobalCheckbox.isChecked()) {
newSkipSilence = FeedPreferences.SkipSilence.GLOBAL;
} else if (viewBinding.skipSilenceFeed.isChecked()) {
newSkipSilence = FeedPreferences.SkipSilence.AGGRESSIVE;
} else {
newSkipSilence = FeedPreferences.SkipSilence.OFF;
}
} else {
newSkipSilence = FeedPreferences.SkipSilence.fromCode(
viewBinding.skipSilence.getCheckedButtonId()
);
}
feedPreferences.setFeedSkipSilence(newSkipSilence);
DBWriter.setFeedPreferences(feedPreferences);
EventBus.getDefault().post(new SpeedPresetChangedEvent(
Expand Down
34 changes: 31 additions & 3 deletions app/src/main/res/layout/playback_speed_feed_setting_dialog.xml
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,38 @@

</LinearLayout>

<CheckBox
android:id="@+id/skipSilenceFeed"
<com.google.android.material.button.MaterialButtonToggleGroup
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/skipSilence"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/pref_skip_silence_title" />
android:weightSum="3"
app:singleSelection="true">

<Button
android:id="@+id/skipSilenceOff"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="Off"
style="@style/OutlinedButtonBetterContrast" />

<Button
android:id="@+id/skipSilenceMedium"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="Medium"
style="@style/OutlinedButtonBetterContrast" />

<Button
android:id="@+id/skipSilenceAggressive"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="Aggressive"
style="@style/OutlinedButtonBetterContrast" />

</com.google.android.material.button.MaterialButtonToggleGroup>

</LinearLayout>
39 changes: 37 additions & 2 deletions app/src/main/res/layout/speed_select_dialog.xml
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,45 @@
android:layout_width="match_parent"
android:layout_height="wrap_content" />

<CheckBox
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:text="@string/pref_skip_silence_title"
style="@style/AntennaPod.TextView.ListItemPrimaryTitle" />

<com.google.android.material.button.MaterialButtonToggleGroup
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/skipSilence"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/pref_skip_silence_title" />
android:weightSum="3"
app:singleSelection="true">

<Button
android:id="@+id/skipSilenceOff"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="Off"
style="@style/OutlinedButtonBetterContrast" />

<Button
android:id="@+id/skipSilenceMedium"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="Medium"
style="@style/OutlinedButtonBetterContrast" />

<Button
android:id="@+id/skipSilenceAggressive"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="Aggressive"
style="@style/OutlinedButtonBetterContrast" />

</com.google.android.material.button.MaterialButtonToggleGroup>

</LinearLayout>
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,7 @@ public static FeedPreferences.SkipSilence getCurrentSkipSilencePreference(Playab
}
}
if (skipSilence == FeedPreferences.SkipSilence.GLOBAL) {
skipSilence = UserPreferences.isSkipSilence()
? FeedPreferences.SkipSilence.AGGRESSIVE : FeedPreferences.SkipSilence.OFF;
skipSilence = UserPreferences.getSkipSilence();
}
return skipSilence;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,10 +182,9 @@ public static void setCurrentlyPlayingTemporaryPlaybackSpeed(float speed) {
editor.apply();
}

public static void setCurrentlyPlayingTemporarySkipSilence(boolean skipSilence) {
public static void setCurrentlyPlayingTemporarySkipSilence(FeedPreferences.SkipSilence skipSilence) {
SharedPreferences.Editor editor = prefs.edit();
editor.putInt(PREF_CURRENTLY_PLAYING_TEMPORARY_SKIP_SILENCE, skipSilence
? FeedPreferences.SkipSilence.AGGRESSIVE.code : FeedPreferences.SkipSilence.OFF.code);
editor.putInt(PREF_CURRENTLY_PLAYING_TEMPORARY_SKIP_SILENCE, skipSilence.code);
editor.apply();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@
import androidx.media3.exoplayer.SeekParameters;
import androidx.media3.exoplayer.ExoPlayer;
import androidx.media3.common.AudioAttributes;
import androidx.media3.exoplayer.audio.AudioCapabilities;
import androidx.media3.exoplayer.audio.AudioSink;
import androidx.media3.exoplayer.audio.DefaultAudioSink;
import androidx.media3.exoplayer.audio.DefaultAudioSink.DefaultAudioProcessorChain;
import androidx.media3.exoplayer.audio.SilenceSkippingAudioProcessor;
import androidx.media3.exoplayer.audio.SonicAudioProcessor;
import androidx.media3.exoplayer.source.MediaSource;
import androidx.media3.exoplayer.source.ProgressiveMediaSource;
import androidx.media3.exoplayer.source.TrackGroupArray;
Expand All @@ -49,6 +55,7 @@
import de.danoeh.antennapod.net.common.AntennapodHttpClient;
import de.danoeh.antennapod.net.common.HttpCredentialEncoder;
import de.danoeh.antennapod.net.common.NetworkUtils;
import de.danoeh.antennapod.model.feed.FeedPreferences;
import de.danoeh.antennapod.model.playback.Playable;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
Expand Down Expand Up @@ -81,6 +88,8 @@ public class ExoPlayerWrapper {
@Nullable
private LoudnessEnhancer loudnessEnhancer = null;

private FeedPreferences.SkipSilence skipSilence = FeedPreferences.SkipSilence.OFF;

ExoPlayerWrapper(Context context) {
this.context = context;
createPlayer();
Expand All @@ -101,7 +110,41 @@ private void createPlayer() {
DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS);
loadControl.setBackBuffer(UserPreferences.getRewindSecs() * 1000 + 500, true);
trackSelector = new DefaultTrackSelector(context);
exoPlayer = new ExoPlayer.Builder(context, new DefaultRenderersFactory(context))
final int skipSilenceDurationUs;
switch (skipSilence) {
case MEDIUM: skipSilenceDurationUs = 300_000; break;
case AGGRESSIVE: skipSilenceDurationUs = 50_000; break;
default: skipSilenceDurationUs = 0;
}
exoPlayer = new ExoPlayer.Builder(context, new DefaultRenderersFactory(context) {
@Override
protected AudioSink buildAudioSink(
Context context,
boolean enableFloatOutput,
boolean enableAudioTrackPlaybackParams,
boolean enableOffload) {
// https://github.com/google/ExoPlayer/blob/r2.14.2/library/core/src/main/java/com/google/android/exoplayer2/DefaultRenderersFactory.java#L637
return new DefaultAudioSink.Builder()
.setAudioCapabilities(AudioCapabilities.getCapabilities(context))
.setAudioProcessorChain(
new DefaultAudioProcessorChain(
new SilenceSkippingAudioProcessor(
skipSilenceDurationUs,
skipSilenceDurationUs,
SilenceSkippingAudioProcessor.DEFAULT_SILENCE_THRESHOLD_LEVEL
),
new SonicAudioProcessor()
)
)
.setEnableFloatOutput(enableFloatOutput)
.setEnableAudioTrackPlaybackParams(enableAudioTrackPlaybackParams)
.setOffloadMode(
enableOffload
? DefaultAudioSink.OFFLOAD_MODE_ENABLED_GAPLESS_REQUIRED
: DefaultAudioSink.OFFLOAD_MODE_DISABLED)
.build();
}
})
.setTrackSelector(trackSelector)
.setLoadControl(loadControl.build())
.build();
Expand Down Expand Up @@ -152,7 +195,8 @@ public void onAudioSessionIdChanged(int audioSessionId) {
initLoudnessEnhancer(audioSessionId);
}
});
simpleCache = new SimpleCache(new File(context.getCacheDir(), "streaming"),
if (simpleCache != null)
simpleCache = new SimpleCache(new File(context.getCacheDir(), "streaming"),
new LeastRecentlyUsedCacheEvictor(50 * 1024 * 1024), new StandaloneDatabaseProvider(context));
initLoudnessEnhancer(exoPlayer.getAudioSessionId());
}
Expand All @@ -165,8 +209,11 @@ public float getCurrentSpeedMultiplier() {
return playbackParameters.speed;
}

public boolean getCurrentSkipSilence() {
return exoPlayer.getSkipSilenceEnabled();
public FeedPreferences.SkipSilence getCurrentSkipSilence() {
if (!exoPlayer.getSkipSilenceEnabled()) {
return FeedPreferences.SkipSilence.OFF;
}
return this.skipSilence;
}

public int getDuration() {
Expand Down Expand Up @@ -266,9 +313,20 @@ public void setDisplay(SurfaceHolder sh) {
exoPlayer.setVideoSurfaceHolder(sh);
}

public void setPlaybackParams(float speed, boolean skipSilence) {
public void setPlaybackParams(float speed, FeedPreferences.SkipSilence skipSilence) {
playbackParameters = new PlaybackParameters(speed, playbackParameters.pitch);
exoPlayer.setSkipSilenceEnabled(skipSilence);
// update skip silence duration in audio pipeline
if (skipSilence != FeedPreferences.SkipSilence.OFF && skipSilence != this.skipSilence) {
this.skipSilence = skipSilence;
final boolean wasPlaying = isPlaying();
final int position = getCurrentPosition();
exoPlayer.release();
createPlayer();
if (mediaSource != null) prepare();
exoPlayer.seekTo(position);
if (wasPlaying) exoPlayer.play();
}
exoPlayer.setSkipSilenceEnabled(skipSilence != FeedPreferences.SkipSilence.OFF);
exoPlayer.setPlaybackParameters(playbackParameters);
}

Expand Down
Loading

0 comments on commit d30c25e

Please sign in to comment.