Skip to content

Instantly share code, notes, and snippets.

@bc-lee
Created February 6, 2024 09:31
Show Gist options
  • Save bc-lee/826e9ecc482188341f63665c584ae69b to your computer and use it in GitHub Desktop.
Save bc-lee/826e9ecc482188341f63665c584ae69b to your computer and use it in GitHub Desktop.
Check Audio Effect
package com.example.checkaudioeffect
import android.Manifest
import android.content.pm.PackageManager
import android.media.AudioFormat
import android.media.AudioRecord
import android.media.MediaRecorder
import android.media.audiofx.AudioEffect
import android.os.Build
import android.os.Bundle
import android.util.Log
import android.widget.TextView
import android.widget.Toast
import androidx.annotation.RequiresPermission
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.AppCompatButton
import androidx.core.app.ActivityCompat
import com.example.checkaudioeffect.databinding.ActivityMainBinding
import java.util.UUID
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var startButton: AppCompatButton
private lateinit var textView: TextView
private var cachedEffects: Array<AudioEffect.Descriptor>? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
startButton = binding.startButton
textView = binding.textView
startButton.setOnClickListener {
onStartButtonClicked()
}
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<String>,
grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (grantResults.all { result -> result == PackageManager.PERMISSION_GRANTED }) {
// To prevent reentrant call, post to UI thread
runOnUiThread {
onStartButtonClicked()
}
return
} else {
Toast.makeText(
this,
"Failed to get required permissions",
Toast.LENGTH_LONG)
.show()
}
}
private fun onStartButtonClicked() {
if (ActivityCompat.checkSelfPermission(
this,
Manifest.permission.RECORD_AUDIO
) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.RECORD_AUDIO),
0
)
return
}
var message = ""
// Android Version, Brand, Model, SOC Manufacturer
message += "Android Version: ${Build.VERSION.RELEASE}\n"
message += "Brand: ${Build.BRAND}\n"
message += "Model: ${Build.MODEL}\n"
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
message += "SOC: ${Build.SOC_MANUFACTURER} ${Build.SOC_MODEL}\n"
}
val audioRecord = createAudioRecord()
if (audioRecord == null) {
message += "Failed to create AudioRecord\n"
textView.text = message
return
}
try {
val effects = availableEffects()
if (effects == null) {
message += "Failed to get available effects\n"
textView.text = message
return
}
val aecList = mutableListOf<AudioEffect.Descriptor>()
val nsList = mutableListOf<AudioEffect.Descriptor>()
for (effect in effects) {
if (effect.type == AudioEffect.EFFECT_TYPE_AEC) {
aecList.add(effect)
} else if (effect.type == AudioEffect.EFFECT_TYPE_NS) {
nsList.add(effect)
}
}
if (aecList.isEmpty()) {
message += "No AEC found\n"
} else {
message += "AEC:\n"
for (aec in aecList) {
message += " ${aec.name} ${aec.implementor} {${aec.uuid}}"
if (aec.uuid == AOSP_ACOUSTIC_ECHO_CANCELER) {
message += " from AOSP"
}
message += "\n"
}
}
if (nsList.isEmpty()) {
message += "No NS found\n"
} else {
message += "NS:\n"
for (ns in nsList) {
message += " ${ns.name} ${ns.implementor} {${ns.uuid}}"
if (ns.uuid == AOSP_NOISE_SUPPRESSOR) {
message += " from AOSP"
}
message += "\n"
}
}
Log.d(TAG, message)
textView.text = message
} finally {
audioRecord.release()
}
}
@RequiresPermission(Manifest.permission.RECORD_AUDIO)
private fun createAudioRecord(): AudioRecord? {
val audioSource = MediaRecorder.AudioSource.MIC
val sampleRateInHz = 48000
val channelConfig = AudioFormat.CHANNEL_IN_MONO
val audioFormat = AudioFormat.ENCODING_PCM_16BIT
val bufferSizeInBytes = AudioRecord.getMinBufferSize(
sampleRateInHz,
channelConfig,
audioFormat
)
return try {
AudioRecord(
audioSource,
sampleRateInHz,
channelConfig,
audioFormat,
bufferSizeInBytes
)
} catch (e: IllegalArgumentException) {
Log.e(TAG, "Failed to create AudioRecord", e)
null
}
}
private fun availableEffects(): Array<AudioEffect.Descriptor>? {
if (cachedEffects == null) {
cachedEffects = AudioEffect.queryEffects()
}
return cachedEffects
}
companion object {
private const val TAG = "CheckAudioEffect"
// UUIDs for Software Audio Effects that we want to avoid using.
// The implementor field will be set to "The Android Open Source Project".
private val AOSP_ACOUSTIC_ECHO_CANCELER =
UUID.fromString("bb392ec0-8d4d-11e0-a896-0002a5d5c51b")
private val AOSP_NOISE_SUPPRESSOR = UUID.fromString("c06c8400-8e06-11e0-9cb6-0002a5d5c51b")
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment