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