Audio effect: equalizer and visualizer

Audio effect: equalizer and visualizer
pull/166/head
xufulong 4 years ago
parent d52b84ef84
commit ec4f9ee641
  1. 1
      app/build.gradle
  2. 47
      app/src/main/AndroidManifest.xml
  3. 255
      app/src/main/java/com/frank/ffmpeg/activity/AudioEffectActivity.kt
  4. 6
      app/src/main/java/com/frank/ffmpeg/activity/MainActivity.java
  5. 89
      app/src/main/java/com/frank/ffmpeg/adapter/EqualizerAdapter.kt
  6. 35
      app/src/main/java/com/frank/ffmpeg/format/AudioVisualizer.java
  7. 5
      app/src/main/java/com/frank/ffmpeg/listener/OnSeeBarListener.kt
  8. 168
      app/src/main/java/com/frank/ffmpeg/view/VisualizerView.kt
  9. 80
      app/src/main/res/layout/activity_audio_effect.xml
  10. 7
      app/src/main/res/layout/activity_main.xml
  11. 18
      app/src/main/res/layout/adapter_equalizer.xml
  12. 6
      app/src/main/res/values-en/strings.xml
  13. 6
      app/src/main/res/values/strings.xml

@ -56,6 +56,7 @@ dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs') implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation "androidx.appcompat:appcompat:$rootProject.appcompatVersion" implementation "androidx.appcompat:appcompat:$rootProject.appcompatVersion"
implementation "androidx.recyclerview:recyclerview:$rootProject.recyclerviewVersion" implementation "androidx.recyclerview:recyclerview:$rootProject.recyclerviewVersion"
implementation 'androidx.appcompat:appcompat:1.2.0'
testImplementation 'junit:junit:4.12' testImplementation 'junit:junit:4.12'
implementation project(':Live') implementation project(':Live')
implementation "androidx.core:core-ktx:$rootProject.core_ktx" implementation "androidx.core:core-ktx:$rootProject.core_ktx"

@ -8,57 +8,54 @@
<uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/> <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-feature android:name="android.hardware.camera" /> <uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" /> <uses-feature android:name="android.hardware.camera.autofocus" />
<application <application
android:name=".FFmpegApplication" android:name=".FFmpegApplication"
tools:replace="android:name"
android:allowBackup="true" android:allowBackup="true"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:label="@string/app_name" android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:screenOrientation="landscape" android:screenOrientation="landscape"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/AppTheme"> android:theme="@style/AppTheme"
tools:replace="android:name">
<uses-library android:name="org.apache.http.legacy" android:required="false" /> <uses-library
android:name="org.apache.http.legacy"
android:required="false" />
<activity android:name=".activity.MainActivity"> <activity android:name=".activity.MainActivity">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<action android:name="android.intent.action.VIEW"/> <action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
</activity> </activity> <!-- audio handle -->
<!-- audio handle --> <activity android:name=".activity.AudioHandleActivity" /> <!-- media handle -->
<activity android:name=".activity.AudioHandleActivity" /> <activity android:name=".activity.MediaHandleActivity" /> <!-- video handle -->
<!-- media handle --> <activity android:name=".activity.VideoHandleActivity" /> <!-- media player -->
<activity android:name=".activity.MediaHandleActivity" />
<!-- video handle -->
<activity android:name=".activity.VideoHandleActivity" />
<!-- media player -->
<activity <activity
android:name=".activity.MediaPlayerActivity" android:name=".activity.MediaPlayerActivity"
android:screenOrientation="landscape" /> android:screenOrientation="landscape" /> <!-- local pusher -->
<!-- local pusher --> <activity android:name=".activity.PushActivity" /> <!-- real time pusher -->
<activity
android:name=".activity.PushActivity" />
<!-- real time pusher -->
<activity <activity
android:name=".activity.LiveActivity" android:name=".activity.LiveActivity"
android:screenOrientation="portrait" /> android:screenOrientation="portrait" /> <!-- filter effect -->
<!-- filter effect -->
<activity <activity
android:name=".activity.FilterActivity" android:name=".activity.FilterActivity"
android:screenOrientation="landscape" /> android:screenOrientation="landscape" /> <!-- video preview -->
<!-- video preview --> <activity
<activity android:name=".activity.VideoPreviewActivity" android:name=".activity.VideoPreviewActivity"
android:screenOrientation="portrait" /> <!-- probe media format -->
<activity
android:name=".activity.ProbeFormatActivity"
android:screenOrientation="portrait" /> android:screenOrientation="portrait" />
<!-- probe media format --> <activity android:name=".activity.AudioEffectActivity"
<activity android:name=".activity.ProbeFormatActivity"
android:screenOrientation="portrait" /> android:screenOrientation="portrait" />
</application> </application>

@ -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()
}
}

@ -30,7 +30,8 @@ public class MainActivity extends BaseActivity {
R.id.btn_live, R.id.btn_live,
R.id.btn_filter, R.id.btn_filter,
R.id.btn_preview, R.id.btn_preview,
R.id.btn_probe R.id.btn_probe,
R.id.btn_audio_effect
); );
} }
@ -65,6 +66,9 @@ public class MainActivity extends BaseActivity {
case R.id.btn_probe://probe media format case R.id.btn_probe://probe media format
intent.setClass(MainActivity.this, ProbeFormatActivity.class); intent.setClass(MainActivity.this, ProbeFormatActivity.class);
break; break;
case R.id.btn_audio_effect://audio effect
intent.setClass(MainActivity.this, AudioEffectActivity.class);
break;
default: default:
break; break;
} }

@ -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>

@ -76,6 +76,13 @@
android:text="@string/media_probe" android:text="@string/media_probe"
android:layout_marginTop="10dp" /> android:layout_marginTop="10dp" />
<Button
android:id="@+id/btn_audio_effect"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/audio_effect"
android:layout_marginTop="10dp" />
</LinearLayout> </LinearLayout>
</ScrollView> </ScrollView>

@ -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>

@ -9,6 +9,12 @@
<string name="audio_encode">Audio encode</string> <string name="audio_encode">Audio encode</string>
<string name="pcm_concat">PCM concat</string> <string name="pcm_concat">PCM concat</string>
<string name="audio_speed">Audio speed</string> <string name="audio_speed">Audio speed</string>
<string name="audio_effect">Audio effect</string>
<string name="audio_equalizer">Audio equalizer:</string>
<string name="audio_style">Audio style:</string>
<string name="audio_reverb">Audio reverb:</string>
<string name="audio_bass_boost">Bass boost:</string>
<string name="audio_voice_enhancer">Voice enhancer:</string>
<string name="audio_handle">Audio handle</string> <string name="audio_handle">Audio handle</string>
<string name="media_handle">Media handle</string> <string name="media_handle">Media handle</string>

@ -9,6 +9,12 @@
<string name="audio_encode">音频编码</string> <string name="audio_encode">音频编码</string>
<string name="pcm_concat">PCM合并</string> <string name="pcm_concat">PCM合并</string>
<string name="audio_speed">音频速度</string> <string name="audio_speed">音频速度</string>
<string name="audio_effect">音乐均衡器</string>
<string name="audio_equalizer">均衡器:</string>
<string name="audio_style">风格:</string>
<string name="audio_reverb">混响:</string>
<string name="audio_bass_boost">重低音:</string>
<string name="audio_voice_enhancer">声音增强:</string>
<string name="audio_handle">音频处理</string> <string name="audio_handle">音频处理</string>
<string name="media_handle">音视频处理</string> <string name="media_handle">音视频处理</string>

Loading…
Cancel
Save