mirror of
https://gitlab.com/ultrasonic/ultrasonic.git
synced 2025-05-17 15:56:36 +03:00
Modernize EQ
This commit is contained in:
parent
8c15b0394a
commit
e1741e9a83
@ -1,186 +0,0 @@
|
||||
/*
|
||||
This file is part of Subsonic.
|
||||
|
||||
Subsonic 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.
|
||||
|
||||
Subsonic 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.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Subsonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2011 (C) Sindre Mehus
|
||||
*/
|
||||
package org.moire.ultrasonic.audiofx;
|
||||
|
||||
import android.content.Context;
|
||||
import android.media.MediaPlayer;
|
||||
import android.media.audiofx.Equalizer;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
import org.moire.ultrasonic.util.FileUtil;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Backward-compatible wrapper for {@link Equalizer}, which is API Level 9.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
* @version $Id$
|
||||
*/
|
||||
public class EqualizerController
|
||||
{
|
||||
private static Boolean available = null;
|
||||
private static final MutableLiveData<EqualizerController> instance = new MutableLiveData<>();
|
||||
|
||||
private Context context;
|
||||
public Equalizer equalizer;
|
||||
private int audioSessionId;
|
||||
|
||||
/**
|
||||
* Retrieves the EqualizerController as LiveData
|
||||
*/
|
||||
public static LiveData<EqualizerController> get()
|
||||
{
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the EqualizerController instance with a MediaPlayer
|
||||
*/
|
||||
public static void create(Context context, MediaPlayer mediaPlayer)
|
||||
{
|
||||
if (mediaPlayer == null) return;
|
||||
if (!isAvailable()) return;
|
||||
|
||||
EqualizerController controller = new EqualizerController();
|
||||
controller.context = context;
|
||||
|
||||
try
|
||||
{
|
||||
controller.audioSessionId = mediaPlayer.getAudioSessionId();
|
||||
controller.equalizer = new Equalizer(0, controller.audioSessionId);
|
||||
controller.loadSettings();
|
||||
|
||||
instance.postValue(controller);
|
||||
}
|
||||
catch (Throwable x)
|
||||
{
|
||||
Timber.w(x, "Failed to create equalizer.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases the EqualizerController instance when the underlying MediaPlayer is no longer available
|
||||
*/
|
||||
public static void release()
|
||||
{
|
||||
EqualizerController controller = instance.getValue();
|
||||
if (controller == null) return;
|
||||
|
||||
controller.equalizer.release();
|
||||
instance.postValue(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the {@link Equalizer} class is available.
|
||||
*/
|
||||
private static boolean isAvailable()
|
||||
{
|
||||
if (available != null) return available;
|
||||
try
|
||||
{
|
||||
Class.forName("android.media.audiofx.Equalizer");
|
||||
available = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Timber.i(ex, "CheckAvailable received an exception getting class for the Equalizer");
|
||||
available = false;
|
||||
}
|
||||
return available;
|
||||
}
|
||||
|
||||
public void saveSettings()
|
||||
{
|
||||
if (!available) return;
|
||||
try
|
||||
{
|
||||
FileUtil.serialize(context, new EqualizerSettings(equalizer), "equalizer.dat");
|
||||
}
|
||||
catch (Throwable x)
|
||||
{
|
||||
Timber.w(x, "Failed to save equalizer settings.");
|
||||
}
|
||||
}
|
||||
|
||||
public void loadSettings()
|
||||
{
|
||||
if (!available) return;
|
||||
try
|
||||
{
|
||||
EqualizerSettings settings = FileUtil.deserialize(context, "equalizer.dat");
|
||||
|
||||
if (settings != null)
|
||||
{
|
||||
settings.apply(equalizer);
|
||||
}
|
||||
}
|
||||
catch (Throwable x)
|
||||
{
|
||||
Timber.w(x, "Failed to load equalizer settings.");
|
||||
}
|
||||
}
|
||||
|
||||
private static class EqualizerSettings implements Serializable
|
||||
{
|
||||
private static final long serialVersionUID = 626565082425206061L;
|
||||
private final short[] bandLevels;
|
||||
private short preset;
|
||||
private final boolean enabled;
|
||||
|
||||
public EqualizerSettings(Equalizer equalizer)
|
||||
{
|
||||
enabled = equalizer.getEnabled();
|
||||
bandLevels = new short[equalizer.getNumberOfBands()];
|
||||
|
||||
for (short i = 0; i < equalizer.getNumberOfBands(); i++)
|
||||
{
|
||||
bandLevels[i] = equalizer.getBandLevel(i);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
preset = equalizer.getCurrentPreset();
|
||||
}
|
||||
catch (Exception x)
|
||||
{
|
||||
preset = -1;
|
||||
}
|
||||
}
|
||||
|
||||
public void apply(Equalizer equalizer)
|
||||
{
|
||||
for (short i = 0; i < bandLevels.length; i++)
|
||||
{
|
||||
equalizer.setBandLevel(i, bandLevels[i]);
|
||||
}
|
||||
|
||||
if (preset >= 0 && preset < equalizer.getNumberOfPresets())
|
||||
{
|
||||
equalizer.usePreset(preset);
|
||||
}
|
||||
|
||||
equalizer.setEnabled(enabled);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,275 +0,0 @@
|
||||
package org.moire.ultrasonic.fragment;
|
||||
|
||||
import android.media.audiofx.Equalizer;
|
||||
import android.os.Bundle;
|
||||
import android.view.ContextMenu;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.SeekBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.lifecycle.Observer;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.moire.ultrasonic.R;
|
||||
import org.moire.ultrasonic.audiofx.EqualizerController;
|
||||
import org.moire.ultrasonic.util.Util;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
/**
|
||||
* Displays the Equalizer
|
||||
*/
|
||||
public class EqualizerFragment extends Fragment {
|
||||
|
||||
private static final int MENU_GROUP_PRESET = 100;
|
||||
|
||||
private final Map<Short, SeekBar> bars = new HashMap<>();
|
||||
private EqualizerController equalizerController;
|
||||
private Equalizer equalizer;
|
||||
private LinearLayout equalizerLayout;
|
||||
private View presetButton;
|
||||
private CheckBox enabledCheckBox;
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
Util.applyTheme(this.getContext());
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.equalizer, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
FragmentTitle.Companion.setTitle(this, R.string.equalizer_label);
|
||||
equalizerLayout = view.findViewById(R.id.equalizer_layout);
|
||||
presetButton = view.findViewById(R.id.equalizer_preset);
|
||||
enabledCheckBox = view.findViewById(R.id.equalizer_enabled);
|
||||
|
||||
EqualizerController.get().observe(getViewLifecycleOwner(), new Observer<EqualizerController>() {
|
||||
@Override
|
||||
public void onChanged(EqualizerController controller) {
|
||||
if (controller != null) {
|
||||
Timber.d("EqualizerController Observer.onChanged received controller");
|
||||
equalizerController = controller;
|
||||
equalizer = controller.equalizer;
|
||||
setup();
|
||||
} else {
|
||||
Timber.d("EqualizerController Observer.onChanged has no controller");
|
||||
equalizerController = null;
|
||||
equalizer = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause()
|
||||
{
|
||||
super.onPause();
|
||||
if (equalizerController == null) return;
|
||||
equalizerController.saveSettings();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateContextMenu(@NotNull ContextMenu menu, @NotNull View view, ContextMenu.ContextMenuInfo menuInfo)
|
||||
{
|
||||
super.onCreateContextMenu(menu, view, menuInfo);
|
||||
if (equalizer == null) return;
|
||||
|
||||
short currentPreset;
|
||||
try
|
||||
{
|
||||
currentPreset = equalizer.getCurrentPreset();
|
||||
}
|
||||
catch (Exception x)
|
||||
{
|
||||
currentPreset = -1;
|
||||
}
|
||||
|
||||
for (short preset = 0; preset < equalizer.getNumberOfPresets(); preset++)
|
||||
{
|
||||
MenuItem menuItem = menu.add(MENU_GROUP_PRESET, preset, preset, equalizer.getPresetName(preset));
|
||||
if (preset == currentPreset)
|
||||
{
|
||||
menuItem.setChecked(true);
|
||||
}
|
||||
}
|
||||
menu.setGroupCheckable(MENU_GROUP_PRESET, true, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onContextItemSelected(@NotNull MenuItem menuItem)
|
||||
{
|
||||
if (equalizer == null) return true;
|
||||
try
|
||||
{
|
||||
short preset = (short) menuItem.getItemId();
|
||||
equalizer.usePreset(preset);
|
||||
updateBars();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
//TODO: Show a dialog?
|
||||
Timber.i(ex, "An exception has occurred in EqualizerFragment onContextItemSelected");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void setup()
|
||||
{
|
||||
initEqualizer();
|
||||
|
||||
registerForContextMenu(presetButton);
|
||||
presetButton.setOnClickListener(new View.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(View view)
|
||||
{
|
||||
presetButton.showContextMenu();
|
||||
}
|
||||
});
|
||||
|
||||
enabledCheckBox.setChecked(equalizer.getEnabled());
|
||||
enabledCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener()
|
||||
{
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton compoundButton, boolean b)
|
||||
{
|
||||
setEqualizerEnabled(b);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setEqualizerEnabled(boolean enabled)
|
||||
{
|
||||
if (equalizer == null) return;
|
||||
equalizer.setEnabled(enabled);
|
||||
updateBars();
|
||||
}
|
||||
|
||||
private void updateBars()
|
||||
{
|
||||
if (equalizer == null) return;
|
||||
try
|
||||
{
|
||||
for (Map.Entry<Short, SeekBar> entry : bars.entrySet())
|
||||
{
|
||||
short band = entry.getKey();
|
||||
SeekBar bar = entry.getValue();
|
||||
bar.setEnabled(equalizer.getEnabled());
|
||||
short minEQLevel = equalizer.getBandLevelRange()[0];
|
||||
bar.setProgress(equalizer.getBandLevel(band) - minEQLevel);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
//TODO: Show a dialog?
|
||||
Timber.i(ex, "An exception has occurred in EqualizerFragment updateBars");
|
||||
}
|
||||
}
|
||||
|
||||
private void initEqualizer()
|
||||
{
|
||||
if (equalizer == null) return;
|
||||
|
||||
try
|
||||
{
|
||||
short[] bandLevelRange = equalizer.getBandLevelRange();
|
||||
short numberOfBands = equalizer.getNumberOfBands();
|
||||
|
||||
final short minEQLevel = bandLevelRange[0];
|
||||
final short maxEQLevel = bandLevelRange[1];
|
||||
|
||||
for (short i = 0; i < numberOfBands; i++)
|
||||
{
|
||||
final short band = i;
|
||||
|
||||
View bandBar = LayoutInflater.from(getContext()).inflate(R.layout.equalizer_bar, equalizerLayout, false);
|
||||
TextView freqTextView;
|
||||
|
||||
if (bandBar != null)
|
||||
{
|
||||
freqTextView = (TextView) bandBar.findViewById(R.id.equalizer_frequency);
|
||||
final TextView levelTextView = (TextView) bandBar.findViewById(R.id.equalizer_level);
|
||||
SeekBar bar = (SeekBar) bandBar.findViewById(R.id.equalizer_bar);
|
||||
|
||||
freqTextView.setText(String.format(Locale.getDefault(), "%d Hz", equalizer.getCenterFreq(band) / 1000));
|
||||
|
||||
bars.put(band, bar);
|
||||
bar.setMax(maxEQLevel - minEQLevel);
|
||||
short level = equalizer.getBandLevel(band);
|
||||
bar.setProgress(level - minEQLevel);
|
||||
bar.setEnabled(equalizer.getEnabled());
|
||||
updateLevelText(levelTextView, level);
|
||||
|
||||
bar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener()
|
||||
{
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser)
|
||||
{
|
||||
short level = (short) (progress + minEQLevel);
|
||||
if (fromUser)
|
||||
{
|
||||
try
|
||||
{
|
||||
equalizer.setBandLevel(band, level);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
//TODO: Show a dialog?
|
||||
Timber.i(ex, "An exception has occurred in Equalizer onProgressChanged");
|
||||
}
|
||||
}
|
||||
updateLevelText(levelTextView, level);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar)
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar)
|
||||
{
|
||||
}
|
||||
});
|
||||
|
||||
equalizerLayout.addView(bandBar);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
//TODO: Show a dialog?
|
||||
Timber.i(ex, "An exception has occurred while initializing Equalizer");
|
||||
}
|
||||
}
|
||||
|
||||
private static void updateLevelText(TextView levelTextView, short level)
|
||||
{
|
||||
if (levelTextView != null)
|
||||
{
|
||||
levelTextView.setText(String.format(Locale.getDefault(), "%s%d dB", level > 0 ? "+" : "", level / 100));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,124 @@
|
||||
/*
|
||||
* EqualizerController.kt
|
||||
* Copyright (C) 2009-2022 Ultrasonic developers
|
||||
*
|
||||
* Distributed under terms of the GNU GPLv3 license.
|
||||
*/
|
||||
package org.moire.ultrasonic.audiofx
|
||||
|
||||
import android.media.audiofx.Equalizer
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import java.io.Serializable
|
||||
import java.lang.Exception
|
||||
import org.moire.ultrasonic.app.UApp
|
||||
import org.moire.ultrasonic.util.FileUtil.deserialize
|
||||
import org.moire.ultrasonic.util.FileUtil.serialize
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
* Wrapper for [Equalizer] with automatic restoration of presets and settings.
|
||||
*
|
||||
* TODO: Maybe store the settings in the DB?
|
||||
*/
|
||||
class EqualizerController {
|
||||
|
||||
@JvmField
|
||||
var equalizer: Equalizer? = null
|
||||
private var audioSessionId = 0
|
||||
|
||||
fun saveSettings() {
|
||||
if (equalizer == null) return
|
||||
try {
|
||||
serialize(UApp.applicationContext(), EqualizerSettings(equalizer!!), "equalizer.dat")
|
||||
} catch (all: Throwable) {
|
||||
Timber.w(all, "Failed to save equalizer settings.")
|
||||
}
|
||||
}
|
||||
|
||||
fun loadSettings() {
|
||||
if (equalizer == null) return
|
||||
try {
|
||||
val settings = deserialize<EqualizerSettings>(
|
||||
UApp.applicationContext(), "equalizer.dat"
|
||||
)
|
||||
settings?.apply(equalizer!!)
|
||||
} catch (all: Throwable) {
|
||||
Timber.w(all, "Failed to load equalizer settings.")
|
||||
}
|
||||
}
|
||||
|
||||
private class EqualizerSettings(equalizer: Equalizer) : Serializable {
|
||||
private val bandLevels: ShortArray
|
||||
private var preset: Short = 0
|
||||
private val enabled: Boolean
|
||||
|
||||
fun apply(equalizer: Equalizer) {
|
||||
for (i in bandLevels.indices) {
|
||||
equalizer.setBandLevel(i.toShort(), bandLevels[i])
|
||||
}
|
||||
if (preset >= 0 && preset < equalizer.numberOfPresets) {
|
||||
equalizer.usePreset(preset)
|
||||
}
|
||||
equalizer.enabled = enabled
|
||||
}
|
||||
|
||||
init {
|
||||
enabled = equalizer.enabled
|
||||
bandLevels = ShortArray(equalizer.numberOfBands.toInt())
|
||||
for (i in 0 until equalizer.numberOfBands) {
|
||||
bandLevels[i] = equalizer.getBandLevel(i.toShort())
|
||||
}
|
||||
preset = try {
|
||||
equalizer.currentPreset
|
||||
} catch (ignored: Exception) {
|
||||
-1
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val serialVersionUID = 6269873247206061L
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val instance = MutableLiveData<EqualizerController?>()
|
||||
|
||||
/**
|
||||
* Retrieves the EqualizerController as LiveData
|
||||
*/
|
||||
@JvmStatic
|
||||
fun get(): LiveData<EqualizerController?> {
|
||||
return instance
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the EqualizerController instance with a Session
|
||||
*
|
||||
* @param sessionId
|
||||
* @return the new controller
|
||||
*/
|
||||
fun create(sessionId: Int): EqualizerController? {
|
||||
val controller = EqualizerController()
|
||||
return try {
|
||||
controller.audioSessionId = sessionId
|
||||
controller.equalizer = Equalizer(0, controller.audioSessionId)
|
||||
controller.loadSettings()
|
||||
instance.postValue(controller)
|
||||
controller
|
||||
} catch (all: Throwable) {
|
||||
Timber.w(all, "Failed to create equalizer.")
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases the EqualizerController instance when the underlying MediaPlayer is no longer available
|
||||
*/
|
||||
fun release() {
|
||||
val controller = instance.value ?: return
|
||||
controller.equalizer!!.release()
|
||||
instance.postValue(null)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,232 @@
|
||||
/*
|
||||
* EqualizerFragment.kt
|
||||
* Copyright (C) 2009-2022 Ultrasonic developers
|
||||
*
|
||||
* Distributed under terms of the GNU GPLv3 license.
|
||||
*/
|
||||
|
||||
package org.moire.ultrasonic.fragment
|
||||
|
||||
import android.media.audiofx.Equalizer
|
||||
import android.os.Bundle
|
||||
import android.view.ContextMenu
|
||||
import android.view.ContextMenu.ContextMenuInfo
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.CheckBox
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.SeekBar
|
||||
import android.widget.SeekBar.OnSeekBarChangeListener
|
||||
import android.widget.TextView
|
||||
import androidx.fragment.app.Fragment
|
||||
import java.lang.Exception
|
||||
import java.util.HashMap
|
||||
import java.util.Locale
|
||||
import org.moire.ultrasonic.R
|
||||
import org.moire.ultrasonic.audiofx.EqualizerController
|
||||
import org.moire.ultrasonic.fragment.FragmentTitle.Companion.setTitle
|
||||
import org.moire.ultrasonic.util.Util.applyTheme
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
* Displays the Equalizer
|
||||
*/
|
||||
class EqualizerFragment : Fragment() {
|
||||
private val bars: MutableMap<Short, SeekBar> = HashMap()
|
||||
private var equalizerController: EqualizerController? = null
|
||||
private var equalizer: Equalizer? = null
|
||||
private var equalizerLayout: LinearLayout? = null
|
||||
private var presetButton: View? = null
|
||||
private var enabledCheckBox: CheckBox? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
applyTheme(this.context)
|
||||
super.onCreate(savedInstanceState)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(R.layout.equalizer, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
setTitle(this, R.string.equalizer_label)
|
||||
equalizerLayout = view.findViewById(R.id.equalizer_layout)
|
||||
presetButton = view.findViewById(R.id.equalizer_preset)
|
||||
enabledCheckBox = view.findViewById(R.id.equalizer_enabled)
|
||||
|
||||
// Subscribe to changes in the active controller
|
||||
EqualizerController.get().observe(viewLifecycleOwner) { controller ->
|
||||
if (controller != null) {
|
||||
Timber.d("EqualizerController Observer.onChanged received controller")
|
||||
equalizerController = controller
|
||||
equalizer = controller.equalizer
|
||||
setup()
|
||||
} else {
|
||||
Timber.d("EqualizerController Observer.onChanged has no controller")
|
||||
equalizerController = null
|
||||
equalizer = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
equalizerController?.saveSettings()
|
||||
}
|
||||
|
||||
override fun onCreateContextMenu(menu: ContextMenu, view: View, menuInfo: ContextMenuInfo?) {
|
||||
super.onCreateContextMenu(menu, view, menuInfo)
|
||||
|
||||
if (equalizer == null) return
|
||||
val currentPreset: Short = try {
|
||||
equalizer!!.currentPreset
|
||||
} catch (ignored: Exception) {
|
||||
-1
|
||||
}
|
||||
for (preset in 0 until equalizer!!.numberOfPresets) {
|
||||
val menuItem = menu.add(
|
||||
MENU_GROUP_PRESET, preset, preset,
|
||||
equalizer!!.getPresetName(
|
||||
preset.toShort()
|
||||
)
|
||||
)
|
||||
if (preset == currentPreset.toInt()) {
|
||||
menuItem.isChecked = true
|
||||
}
|
||||
}
|
||||
menu.setGroupCheckable(MENU_GROUP_PRESET, true, true)
|
||||
}
|
||||
|
||||
override fun onContextItemSelected(menuItem: MenuItem): Boolean {
|
||||
if (equalizer == null) return true
|
||||
try {
|
||||
val preset = menuItem.itemId.toShort()
|
||||
equalizer!!.usePreset(preset)
|
||||
updateBars()
|
||||
} catch (all: Exception) {
|
||||
// TODO: Show a dialog?
|
||||
Timber.i(all, "An exception has occurred in EqualizerFragment onContextItemSelected")
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun setup() {
|
||||
initEqualizer()
|
||||
registerForContextMenu(presetButton!!)
|
||||
presetButton!!.setOnClickListener { presetButton!!.showContextMenu() }
|
||||
enabledCheckBox!!.isChecked = equalizer!!.enabled
|
||||
enabledCheckBox!!.setOnCheckedChangeListener { _, b -> setEqualizerEnabled(b) }
|
||||
}
|
||||
|
||||
private fun setEqualizerEnabled(enabled: Boolean) {
|
||||
equalizer?.enabled = enabled
|
||||
updateBars()
|
||||
}
|
||||
|
||||
private fun updateBars() {
|
||||
if (equalizer == null) return
|
||||
try {
|
||||
for ((band, bar) in bars) {
|
||||
bar.isEnabled = equalizer!!.enabled
|
||||
val minEQLevel = equalizer!!.bandLevelRange[0]
|
||||
bar.progress = equalizer!!.getBandLevel(band) - minEQLevel
|
||||
}
|
||||
} catch (all: Exception) {
|
||||
// TODO: Show a dialog?
|
||||
Timber.i(all, "An exception has occurred in EqualizerFragment updateBars")
|
||||
}
|
||||
}
|
||||
|
||||
private fun initEqualizer() {
|
||||
if (equalizer == null) return
|
||||
try {
|
||||
val bandLevelRange = equalizer!!.bandLevelRange
|
||||
val numberOfBands = equalizer!!.numberOfBands
|
||||
|
||||
val minEQLevel = bandLevelRange[0]
|
||||
val maxEQLevel = bandLevelRange[1]
|
||||
|
||||
for (i in 0 until numberOfBands) {
|
||||
val bandBar = createSeekBarForBand(i, maxEQLevel, minEQLevel)
|
||||
equalizerLayout!!.addView(bandBar)
|
||||
}
|
||||
} catch (all: Exception) {
|
||||
// TODO: Show a dialog?
|
||||
Timber.i(all, "An exception has occurred while initializing Equalizer")
|
||||
}
|
||||
}
|
||||
|
||||
private fun createSeekBarForBand(index: Int, maxEQLevel: Short, minEQLevel: Short): View {
|
||||
val band = index.toShort()
|
||||
val bandBar = LayoutInflater.from(context)
|
||||
.inflate(R.layout.equalizer_bar, equalizerLayout, false)
|
||||
|
||||
val freqTextView: TextView =
|
||||
bandBar.findViewById<View>(R.id.equalizer_frequency) as TextView
|
||||
val levelTextView = bandBar.findViewById<View>(R.id.equalizer_level) as TextView
|
||||
val bar = bandBar.findViewById<View>(R.id.equalizer_bar) as SeekBar
|
||||
|
||||
val range = equalizer!!.getBandFreqRange(band)
|
||||
|
||||
freqTextView.text = String.format(
|
||||
Locale.getDefault(),
|
||||
"%d - %d Hz",
|
||||
range[0] / 1000, range[1] / 1000
|
||||
)
|
||||
|
||||
bars[band] = bar
|
||||
bar.max = maxEQLevel - minEQLevel
|
||||
val bandLevel = equalizer!!.getBandLevel(band)
|
||||
bar.progress = bandLevel - minEQLevel
|
||||
bar.isEnabled = equalizer!!.enabled
|
||||
updateLevelText(levelTextView, bandLevel)
|
||||
|
||||
bar.setOnSeekBarChangeListener(object : OnSeekBarChangeListener {
|
||||
override fun onProgressChanged(
|
||||
seekBar: SeekBar,
|
||||
progress: Int,
|
||||
fromUser: Boolean
|
||||
) {
|
||||
val level = (progress + minEQLevel).toShort()
|
||||
if (fromUser) {
|
||||
try {
|
||||
equalizer!!.setBandLevel(band, level)
|
||||
} catch (all: Exception) {
|
||||
// TODO: Show a dialog?
|
||||
Timber.i(
|
||||
all,
|
||||
"An exception has occurred in Equalizer onProgressChanged"
|
||||
)
|
||||
}
|
||||
}
|
||||
updateLevelText(levelTextView, level)
|
||||
}
|
||||
|
||||
override fun onStartTrackingTouch(seekBar: SeekBar) {}
|
||||
override fun onStopTrackingTouch(seekBar: SeekBar) {}
|
||||
})
|
||||
|
||||
return bandBar
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val MENU_GROUP_PRESET = 100
|
||||
private fun updateLevelText(levelTextView: TextView?, level: Short) {
|
||||
if (levelTextView != null) {
|
||||
levelTextView.text = String.format(
|
||||
Locale.getDefault(),
|
||||
"%s%d dB",
|
||||
if (level > 0) "+" else "",
|
||||
level / 100
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -25,6 +25,7 @@ import okhttp3.OkHttpClient
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.moire.ultrasonic.activity.NavigationActivity
|
||||
import org.moire.ultrasonic.app.UApp
|
||||
import org.moire.ultrasonic.audiofx.EqualizerController
|
||||
import org.moire.ultrasonic.data.ActiveServerProvider
|
||||
import org.moire.ultrasonic.service.MusicServiceFactory.getMusicService
|
||||
import org.moire.ultrasonic.service.RxBus
|
||||
@ -36,6 +37,7 @@ import timber.log.Timber
|
||||
class PlaybackService : MediaLibraryService(), KoinComponent {
|
||||
private lateinit var player: ExoPlayer
|
||||
private lateinit var mediaLibrarySession: MediaLibrarySession
|
||||
private var equalizer: EqualizerController? = null
|
||||
|
||||
private lateinit var librarySessionCallback: MediaLibrarySession.Callback
|
||||
|
||||
@ -128,6 +130,8 @@ class PlaybackService : MediaLibraryService(), KoinComponent {
|
||||
.setSeekForwardIncrementMs(Settings.seekInterval.toLong())
|
||||
.build()
|
||||
|
||||
equalizer = EqualizerController.create(player.audioSessionId)
|
||||
|
||||
// Enable audio offload
|
||||
if (Settings.useHwOffload)
|
||||
player.experimentalSetOffloadSchedulingEnabled(true)
|
||||
|
Loading…
x
Reference in New Issue
Block a user