parent
d52b84ef84
commit
ec4f9ee641
@ -0,0 +1,255 @@ |
||||
package com.frank.ffmpeg.activity |
||||
|
||||
import android.Manifest |
||||
import android.media.MediaPlayer |
||||
import android.media.audiofx.* |
||||
import android.os.Build |
||||
import androidx.appcompat.app.AppCompatActivity |
||||
import android.os.Bundle |
||||
import android.os.Environment |
||||
import android.util.Log |
||||
import android.util.Pair |
||||
import android.view.View |
||||
import android.widget.* |
||||
import androidx.annotation.RequiresApi |
||||
import androidx.recyclerview.widget.LinearLayoutManager |
||||
import androidx.recyclerview.widget.RecyclerView |
||||
import com.frank.ffmpeg.R |
||||
import com.frank.ffmpeg.adapter.EqualizerAdapter |
||||
import com.frank.ffmpeg.format.AudioVisualizer |
||||
import com.frank.ffmpeg.listener.OnSeeBarListener |
||||
import com.frank.ffmpeg.view.VisualizerView |
||||
import java.io.IOException |
||||
import java.util.ArrayList |
||||
|
||||
/** |
||||
* Audio effect: equalizer, enhancer, visualizer |
||||
* Created by frank on 2020/10/20. |
||||
*/ |
||||
class AudioEffectActivity : AppCompatActivity(), OnSeeBarListener { |
||||
|
||||
companion object { |
||||
private val TAG = AudioEffectActivity::class.java.simpleName |
||||
|
||||
private val AUDIO_PATH = Environment.getExternalStorageDirectory().path + "/heart.mp3" |
||||
} |
||||
|
||||
private var mPlayer: MediaPlayer? = null |
||||
private var mEqualizer: Equalizer? = null |
||||
private var mBass: BassBoost? = null |
||||
private var mPresetReverb: PresetReverb? = null |
||||
private val reverbValues = ArrayList<String>() |
||||
private var seekBarList: List<SeekBar>? = ArrayList() |
||||
private var bands: Short = 0 |
||||
private var minEQLevel: Short = 0 |
||||
private var spinnerStyle: Spinner? = null |
||||
private var spinnerReverb: Spinner? = null |
||||
private var barBassBoost: SeekBar? = null |
||||
private var equalizerAdapter: EqualizerAdapter? = null |
||||
private val enableEqualizer = true |
||||
private var loudnessEnhancer: LoudnessEnhancer? = null |
||||
private var barEnhancer: SeekBar? = null |
||||
private var visualizerView: VisualizerView? = null |
||||
private var mVisualizer: AudioVisualizer? = null |
||||
|
||||
private val presetReverb = arrayOf("None", "SmallRoom", "MediumRoom", "LargeRoom", |
||||
"MediumHall", "LargeHall", "Plate") |
||||
|
||||
private val permissions = arrayOf( |
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE, |
||||
Manifest.permission.RECORD_AUDIO, |
||||
Manifest.permission.MODIFY_AUDIO_SETTINGS) |
||||
|
||||
private val onPreparedListener = MediaPlayer.OnPreparedListener { |
||||
setupEqualizer() |
||||
setupPresetStyle() |
||||
setupReverberation() |
||||
setupBassBoost() |
||||
setLoudnessEnhancer() |
||||
setupVisualizer() |
||||
|
||||
mPlayer!!.start() |
||||
} |
||||
|
||||
public override fun onCreate(savedInstanceState: Bundle?) { |
||||
super.onCreate(savedInstanceState) |
||||
|
||||
checkPermission() |
||||
setContentView(R.layout.activity_audio_effect) |
||||
initView() |
||||
initPlayer() |
||||
} |
||||
|
||||
private fun initView() { |
||||
spinnerStyle = findViewById(R.id.spinner_style) |
||||
spinnerReverb = findViewById(R.id.spinner_reverb) |
||||
barBassBoost = findViewById(R.id.bar_bassboost) |
||||
barEnhancer = findViewById(R.id.bar_enhancer) |
||||
visualizerView = findViewById(R.id.visualizer_view) |
||||
val equalizerView = findViewById<RecyclerView>(R.id.equalizer_view) |
||||
val layoutManager = LinearLayoutManager(this) |
||||
layoutManager.orientation = RecyclerView.VERTICAL |
||||
equalizerView.layoutManager = layoutManager |
||||
equalizerAdapter = EqualizerAdapter(this, this) |
||||
equalizerView.adapter = equalizerAdapter |
||||
} |
||||
|
||||
private fun checkPermission() { |
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { |
||||
requestPermissions(permissions, 456) |
||||
} |
||||
} |
||||
|
||||
private fun initPlayer() { |
||||
try { |
||||
mPlayer = MediaPlayer() |
||||
mPlayer!!.setDataSource(AUDIO_PATH) |
||||
mPlayer!!.setOnPreparedListener(onPreparedListener) |
||||
mPlayer!!.prepareAsync() |
||||
} catch (e: IOException) { |
||||
e.printStackTrace() |
||||
} |
||||
} |
||||
|
||||
override fun onProgress(index: Int, progress: Int) { |
||||
mEqualizer!!.setBandLevel(index.toShort(), (progress + minEQLevel).toShort()) |
||||
} |
||||
|
||||
private fun setupEqualizer() { |
||||
val equalizerList = ArrayList<Pair<*, *>>() |
||||
mEqualizer = Equalizer(0, mPlayer!!.audioSessionId) |
||||
mEqualizer!!.enabled = enableEqualizer |
||||
// band level: min and max |
||||
minEQLevel = mEqualizer!!.bandLevelRange[0]//min level |
||||
val maxEQLevel = mEqualizer!!.bandLevelRange[1] // max level |
||||
bands = mEqualizer!!.numberOfBands |
||||
for (i in 0 until bands) { |
||||
val centerFreq = (mEqualizer!!.getCenterFreq(i.toShort()) / 1000).toString() + " Hz" |
||||
val pair = Pair.create(centerFreq, mEqualizer!!.getBandLevel(i.toShort()) - minEQLevel) |
||||
equalizerList.add(pair) |
||||
} |
||||
if (equalizerAdapter != null) { |
||||
equalizerAdapter!!.setMaxProgress(maxEQLevel - minEQLevel) |
||||
equalizerAdapter!!.setEqualizerList(equalizerList) |
||||
} |
||||
} |
||||
|
||||
private fun setupPresetStyle() { |
||||
for (i in 0 until mEqualizer!!.numberOfPresets) { |
||||
reverbValues.add(mEqualizer!!.getPresetName(i.toShort())) |
||||
} |
||||
|
||||
spinnerStyle!!.adapter = ArrayAdapter(this, android.R.layout.simple_spinner_item, reverbValues) |
||||
spinnerStyle!!.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { |
||||
override fun onItemSelected(arg0: AdapterView<*>, arg1: View, arg2: Int, arg3: Long) { |
||||
try { |
||||
mEqualizer!!.usePreset(arg2.toShort()) |
||||
if (equalizerAdapter != null) { |
||||
seekBarList = equalizerAdapter!!.getSeekBarList() |
||||
} |
||||
if (bands > 0 && seekBarList != null && mEqualizer != null) { |
||||
for (band in 0 until bands) { |
||||
seekBarList!![band].progress = mEqualizer!!.getBandLevel(band.toShort()) - minEQLevel |
||||
} |
||||
} |
||||
|
||||
} catch (e: Exception) { |
||||
Log.e(TAG, "preset style error=$e") |
||||
} |
||||
|
||||
} |
||||
override fun onNothingSelected(arg0: AdapterView<*>) {} |
||||
} |
||||
} |
||||
|
||||
private fun setupReverberation() { |
||||
mPresetReverb = PresetReverb(0, mPlayer!!.audioSessionId) |
||||
mPresetReverb!!.enabled = enableEqualizer |
||||
mPlayer!!.attachAuxEffect(mPresetReverb!!.id) |
||||
//sendLevel:0-1 |
||||
mPlayer!!.setAuxEffectSendLevel(1.0f) |
||||
|
||||
spinnerReverb!!.adapter = ArrayAdapter(this, android.R.layout.simple_spinner_item, presetReverb) |
||||
spinnerReverb!!.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { |
||||
override fun onItemSelected(arg0: AdapterView<*>, arg1: View, arg2: Int, arg3: Long) { |
||||
try { |
||||
mPresetReverb!!.preset = arg2.toShort() |
||||
} catch (e: Exception) { |
||||
Log.e(TAG, "preset reverberation error=$e") |
||||
} |
||||
} |
||||
override fun onNothingSelected(arg0: AdapterView<*>) {} |
||||
} |
||||
} |
||||
|
||||
private fun setupBassBoost() { |
||||
mBass = BassBoost(0, mPlayer!!.audioSessionId) |
||||
mBass!!.enabled = enableEqualizer |
||||
// 0--1000 |
||||
barBassBoost!!.max = 1000 |
||||
barBassBoost!!.progress = 0 |
||||
barBassBoost!!.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener { |
||||
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) { |
||||
// set the strength of bass boost |
||||
mBass!!.setStrength(progress.toShort()) |
||||
} |
||||
|
||||
override fun onStartTrackingTouch(seekBar: SeekBar) {} |
||||
|
||||
override fun onStopTrackingTouch(seekBar: SeekBar) {} |
||||
}) |
||||
} |
||||
|
||||
@RequiresApi(Build.VERSION_CODES.KITKAT) |
||||
private fun setLoudnessEnhancer() { |
||||
loudnessEnhancer = LoudnessEnhancer(mPlayer!!.audioSessionId) |
||||
loudnessEnhancer!!.enabled = true |
||||
// Unit: mB |
||||
loudnessEnhancer!!.setTargetGain(500) |
||||
barEnhancer!!.max = 1000 |
||||
barEnhancer!!.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener { |
||||
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) { |
||||
if (fromUser) { |
||||
loudnessEnhancer!!.setTargetGain(progress) |
||||
} |
||||
} |
||||
|
||||
override fun onStartTrackingTouch(seekBar: SeekBar?) { |
||||
} |
||||
|
||||
override fun onStopTrackingTouch(seekBar: SeekBar?) { |
||||
} |
||||
}) |
||||
} |
||||
|
||||
|
||||
private fun setupVisualizer() { |
||||
mVisualizer = AudioVisualizer() |
||||
mVisualizer?.initVisualizer(mPlayer!!.audioSessionId, false, true, object: Visualizer.OnDataCaptureListener{ |
||||
override fun onFftDataCapture(visualizer: Visualizer?, fft: ByteArray?, samplingRate: Int) { |
||||
if (visualizerView != null && fft != null) { |
||||
visualizerView!!.post { visualizerView!!.setWaveData(fft) } |
||||
} |
||||
} |
||||
|
||||
override fun onWaveFormDataCapture(visualizer: Visualizer?, waveform: ByteArray?, samplingRate: Int) { |
||||
} |
||||
}) |
||||
} |
||||
|
||||
private fun releaseVisualizer() { |
||||
mVisualizer?.releaseVisualizer() |
||||
} |
||||
|
||||
override fun onDestroy() { |
||||
super.onDestroy() |
||||
|
||||
mEqualizer!!.release() |
||||
mPresetReverb!!.release() |
||||
mBass!!.release() |
||||
loudnessEnhancer!!.release() |
||||
releaseVisualizer() |
||||
mPlayer!!.release() |
||||
} |
||||
|
||||
} |
@ -0,0 +1,89 @@ |
||||
package com.frank.ffmpeg.adapter |
||||
|
||||
import android.content.Context |
||||
import android.util.Pair |
||||
import android.view.LayoutInflater |
||||
import android.view.View |
||||
import android.view.ViewGroup |
||||
import android.widget.SeekBar |
||||
import android.widget.TextView |
||||
import androidx.recyclerview.widget.RecyclerView |
||||
|
||||
import com.frank.ffmpeg.R |
||||
import com.frank.ffmpeg.listener.OnSeeBarListener |
||||
|
||||
import java.util.ArrayList |
||||
|
||||
/** |
||||
* @author frank |
||||
* @date 2020-10-19 0:19 |
||||
* @desc Adapter of equalizer |
||||
*/ |
||||
|
||||
class EqualizerAdapter(private val context: Context, private val onSeeBarListener: OnSeeBarListener?) : RecyclerView.Adapter<RecyclerView.ViewHolder>() { |
||||
|
||||
private var equalizerList: List<Pair<*, *>>? = ArrayList() |
||||
private val seekBarList = ArrayList<SeekBar>() |
||||
private var maxProgress: Int = 0 |
||||
|
||||
override fun onCreateViewHolder(viewGroup: ViewGroup, i: Int): RecyclerView.ViewHolder { |
||||
return EqualizerHolder(LayoutInflater.from(context).inflate(R.layout.adapter_equalizer, null)) |
||||
} |
||||
|
||||
override fun onBindViewHolder(viewHolder: RecyclerView.ViewHolder, i: Int) { |
||||
val holder = viewHolder as EqualizerHolder |
||||
if (equalizerList != null) { |
||||
val centerFreq = equalizerList!![i].first as String |
||||
holder.txtFrequency.text = centerFreq |
||||
} |
||||
seekBarList.add(holder.barEqualizer) |
||||
holder.barEqualizer.max = maxProgress |
||||
val currentProgress = equalizerList!![i].second as Int |
||||
holder.barEqualizer.progress = currentProgress |
||||
|
||||
holder.barEqualizer.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener { |
||||
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) { |
||||
if (fromUser) { |
||||
onSeeBarListener?.onProgress(i, progress) |
||||
} |
||||
} |
||||
|
||||
override fun onStartTrackingTouch(seekBar: SeekBar) { |
||||
|
||||
} |
||||
|
||||
override fun onStopTrackingTouch(seekBar: SeekBar) { |
||||
|
||||
} |
||||
}) |
||||
} |
||||
|
||||
override fun getItemCount(): Int { |
||||
return if (equalizerList != null) equalizerList!!.size else 0 |
||||
} |
||||
|
||||
private inner class EqualizerHolder internal constructor(itemView: View) : RecyclerView.ViewHolder(itemView) { |
||||
|
||||
val txtFrequency: TextView = itemView.findViewById(R.id.txt_frequency) |
||||
val barEqualizer: SeekBar = itemView.findViewById(R.id.bar_equalizer) |
||||
|
||||
} |
||||
|
||||
fun setMaxProgress(maxProgress: Int) { |
||||
if (maxProgress > 0) { |
||||
this.maxProgress = maxProgress |
||||
} |
||||
} |
||||
|
||||
fun setEqualizerList(equalizerList: List<Pair<*, *>>?) { |
||||
if (equalizerList != null) { |
||||
this.equalizerList = equalizerList |
||||
notifyDataSetChanged() |
||||
} |
||||
} |
||||
|
||||
fun getSeekBarList(): List<SeekBar> { |
||||
return seekBarList |
||||
} |
||||
|
||||
} |
@ -0,0 +1,35 @@ |
||||
package com.frank.ffmpeg.format; |
||||
|
||||
import android.media.audiofx.Visualizer; |
||||
import android.os.Build; |
||||
|
||||
/** |
||||
* Visualizer of Audio frequency |
||||
* Created by frank on 2020/10/20. |
||||
*/ |
||||
public class AudioVisualizer { |
||||
|
||||
private Visualizer visualizer; |
||||
|
||||
public void initVisualizer(int audioSession, boolean waveform, boolean fft, Visualizer.OnDataCaptureListener dataCaptureListener) { |
||||
visualizer = new Visualizer(audioSession); |
||||
int captureSize = Visualizer.getCaptureSizeRange()[1]; |
||||
int captureRate = Visualizer.getMaxCaptureRate() / 2; |
||||
|
||||
visualizer.setCaptureSize(captureSize); |
||||
visualizer.setDataCaptureListener(dataCaptureListener, captureRate, waveform, fft); |
||||
visualizer.setScalingMode(Visualizer.SCALING_MODE_NORMALIZED); |
||||
visualizer.setEnabled(true); |
||||
} |
||||
|
||||
public void releaseVisualizer() { |
||||
if (visualizer != null) { |
||||
int captureRate = Visualizer.getMaxCaptureRate() / 2; |
||||
visualizer.setEnabled(false); |
||||
visualizer.setDataCaptureListener(null, captureRate, false, false); |
||||
visualizer.release(); |
||||
visualizer = null; |
||||
} |
||||
} |
||||
|
||||
} |
@ -0,0 +1,5 @@ |
||||
package com.frank.ffmpeg.listener |
||||
|
||||
interface OnSeeBarListener { |
||||
fun onProgress(index: Int, progress: Int) |
||||
} |
@ -0,0 +1,168 @@ |
||||
package com.frank.ffmpeg.view |
||||
|
||||
import android.content.Context |
||||
import android.graphics.Canvas |
||||
import android.graphics.Color |
||||
import android.graphics.Paint |
||||
import android.graphics.Path |
||||
import android.graphics.Point |
||||
import android.util.AttributeSet |
||||
import android.view.View |
||||
|
||||
import java.util.ArrayList |
||||
|
||||
/** |
||||
* the custom view of audio visualizer |
||||
* Created by frank on 2020/10/19. |
||||
*/ |
||||
class VisualizerView : View { |
||||
|
||||
private var upShowStyle = ShowStyle.STYLE_HOLLOW_LUMP |
||||
|
||||
private var waveData: ByteArray? = null |
||||
private var pointList: MutableList<Point>? = null |
||||
|
||||
private var lumpPaint: Paint? = null |
||||
private var wavePath = Path() |
||||
|
||||
|
||||
constructor(context: Context) : super(context) { |
||||
init() |
||||
} |
||||
|
||||
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) { |
||||
init() |
||||
} |
||||
|
||||
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { |
||||
init() |
||||
} |
||||
|
||||
private fun init() { |
||||
lumpPaint = Paint() |
||||
lumpPaint!!.isAntiAlias = true |
||||
lumpPaint!!.color = LUMP_COLOR |
||||
|
||||
lumpPaint!!.strokeWidth = 2f |
||||
lumpPaint!!.style = Paint.Style.STROKE |
||||
} |
||||
|
||||
fun setWaveData(data: ByteArray) { |
||||
this.waveData = readyData(data) |
||||
genSamplingPoint(data) |
||||
invalidate() |
||||
} |
||||
|
||||
|
||||
fun setStyle(upShowStyle: ShowStyle) { |
||||
this.upShowStyle = upShowStyle |
||||
} |
||||
|
||||
override fun onDraw(canvas: Canvas) { |
||||
super.onDraw(canvas) |
||||
wavePath.reset() |
||||
|
||||
for (i in 0 until LUMP_COUNT) { |
||||
if (waveData == null) { |
||||
canvas.drawRect(((LUMP_WIDTH + LUMP_SPACE) * i).toFloat(), |
||||
(LUMP_MAX_HEIGHT - LUMP_MIN_HEIGHT).toFloat(), |
||||
((LUMP_WIDTH + LUMP_SPACE) * i + LUMP_WIDTH).toFloat(), |
||||
LUMP_MAX_HEIGHT.toFloat(), |
||||
lumpPaint!!) |
||||
continue |
||||
} |
||||
|
||||
when (upShowStyle) { |
||||
ShowStyle.STYLE_HOLLOW_LUMP -> drawLump(canvas, i, false) |
||||
ShowStyle.STYLE_WAVE -> drawWave(canvas, i, false) |
||||
else -> { |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
private fun drawWave(canvas: Canvas, i: Int, reversal: Boolean) { |
||||
if (pointList == null || pointList!!.size < 2) { |
||||
return |
||||
} |
||||
val ratio = SCALE * if (reversal) -1 else 1 |
||||
if (i < pointList!!.size - 2) { |
||||
val point = pointList!![i] |
||||
val nextPoint = pointList!![i + 1] |
||||
val midX = point.x + nextPoint.x shr 1 |
||||
if (i == 0) { |
||||
wavePath.moveTo(point.x.toFloat(), LUMP_MAX_HEIGHT - point.y * ratio) |
||||
} |
||||
wavePath.cubicTo(midX.toFloat(), LUMP_MAX_HEIGHT - point.y * ratio, |
||||
midX.toFloat(), LUMP_MAX_HEIGHT - nextPoint.y * ratio, |
||||
nextPoint.x.toFloat(), LUMP_MAX_HEIGHT - nextPoint.y * ratio) |
||||
|
||||
canvas.drawPath(wavePath, lumpPaint!!) |
||||
} |
||||
} |
||||
|
||||
private fun drawLump(canvas: Canvas, i: Int, reversal: Boolean) { |
||||
val minus = if (reversal) -1 else 1 |
||||
val top = LUMP_MAX_HEIGHT - (LUMP_MIN_HEIGHT + waveData!![i] * SCALE) * minus |
||||
|
||||
canvas.drawRect((LUMP_SIZE * i).toFloat(), |
||||
top, |
||||
(LUMP_SIZE * i + LUMP_WIDTH).toFloat(), |
||||
LUMP_MAX_HEIGHT.toFloat(), |
||||
lumpPaint!!) |
||||
} |
||||
|
||||
/** |
||||
* generate the waveform of sampling points |
||||
* |
||||
* @param data data |
||||
*/ |
||||
private fun genSamplingPoint(data: ByteArray) { |
||||
if (upShowStyle != ShowStyle.STYLE_WAVE) { |
||||
return |
||||
} |
||||
if (pointList == null) { |
||||
pointList = ArrayList() |
||||
} else { |
||||
pointList!!.clear() |
||||
} |
||||
pointList!!.add(Point(0, 0)) |
||||
var i = WAVE_SAMPLING_INTERVAL |
||||
while (i < LUMP_COUNT) { |
||||
pointList!!.add(Point(LUMP_SIZE * i, waveData!![i].toInt())) |
||||
i += WAVE_SAMPLING_INTERVAL |
||||
} |
||||
pointList!!.add(Point(LUMP_SIZE * LUMP_COUNT, 0)) |
||||
} |
||||
|
||||
enum class ShowStyle { |
||||
STYLE_HOLLOW_LUMP, |
||||
STYLE_WAVE, |
||||
STYLE_NOTHING |
||||
} |
||||
|
||||
companion object { |
||||
|
||||
private const val LUMP_COUNT = 128 |
||||
private const val LUMP_WIDTH = 6 |
||||
private const val LUMP_SPACE = 2 |
||||
private const val LUMP_MIN_HEIGHT = LUMP_WIDTH |
||||
private const val LUMP_MAX_HEIGHT = 200 |
||||
private const val LUMP_SIZE = LUMP_WIDTH + LUMP_SPACE |
||||
private val LUMP_COLOR = Color.parseColor("#6de6f6") |
||||
|
||||
private const val WAVE_SAMPLING_INTERVAL = 3 |
||||
|
||||
private const val SCALE = (LUMP_MAX_HEIGHT / LUMP_COUNT).toFloat() |
||||
|
||||
private fun readyData(fft: ByteArray): ByteArray { |
||||
val newData = ByteArray(LUMP_COUNT) |
||||
var abs: Byte |
||||
for (i in 0 until LUMP_COUNT) { |
||||
abs = kotlin.math.abs(fft[i].toInt()).toByte() |
||||
newData[i] = if (abs < 0) 127 else abs |
||||
} |
||||
return newData |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,80 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<LinearLayout |
||||
xmlns:android="http://schemas.android.com/apk/res/android" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="match_parent" |
||||
android:layout_marginStart="16dp" |
||||
android:orientation="vertical"> |
||||
|
||||
<TextView |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
android:layout_marginTop="10dp" |
||||
android:text="@string/audio_equalizer"> |
||||
</TextView> |
||||
|
||||
<androidx.recyclerview.widget.RecyclerView |
||||
android:id="@+id/equalizer_view" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content"> |
||||
</androidx.recyclerview.widget.RecyclerView> |
||||
|
||||
<TextView |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
android:layout_marginTop="10dp" |
||||
android:text="@string/audio_style"/> |
||||
|
||||
<Spinner |
||||
android:id="@+id/spinner_style" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
android:layout_marginTop="5dp"> |
||||
</Spinner> |
||||
|
||||
<TextView |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
android:layout_marginTop="10dp" |
||||
android:text="@string/audio_reverb"/> |
||||
|
||||
<Spinner |
||||
android:id="@+id/spinner_reverb" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
android:layout_marginTop="5dp"> |
||||
</Spinner> |
||||
|
||||
<TextView |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
android:layout_marginTop="10dp" |
||||
android:text="@string/audio_bass_boost"/> |
||||
|
||||
<SeekBar |
||||
android:id="@+id/bar_bassboost" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
android:layout_marginTop="5dp"> |
||||
</SeekBar> |
||||
|
||||
<TextView |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
android:layout_marginTop="10dp" |
||||
android:text="@string/audio_voice_enhancer"/> |
||||
|
||||
<SeekBar |
||||
android:id="@+id/bar_enhancer" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
android:layout_marginTop="5dp"> |
||||
</SeekBar> |
||||
|
||||
<com.frank.ffmpeg.view.VisualizerView |
||||
android:id="@+id/visualizer_view" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="180dp" |
||||
android:layout_marginEnd="16dp"/> |
||||
|
||||
</LinearLayout> |
@ -0,0 +1,18 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="match_parent"> |
||||
|
||||
<TextView |
||||
android:id="@+id/txt_frequency" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:layout_centerHorizontal="true" /> |
||||
|
||||
<SeekBar |
||||
android:id="@+id/bar_equalizer" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
android:layout_below="@+id/txt_frequency"/> |
||||
|
||||
</RelativeLayout> |
Loading…
Reference in new issue