Created
February 6, 2024 09:31
-
-
Save bc-lee/826e9ecc482188341f63665c584ae69b to your computer and use it in GitHub Desktop.
Check Audio Effect
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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