I'm trying to get the Waveform visualisation for an audio in my local device, and I'm trying to use the Visualizer package (import android.media.audiofx.Visualizer) , but, it continuously crashes giving me the following logs:
E/AudioEffect: set(): AudioFlinger could not create effect e46b26a0-dddd-11db-8afd-0002a5d5c51b / NULL, status: -1
E/visualizers-JNI: Visualizer initCheck failed -3
E/Visualizer-JAVA: Error code -3 when initializing Visualizer.
D/AndroidRuntime: Shutting down VM
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.musicit.player, PID: 7181
java.lang.IllegalStateException: Could not execute method for android:onClick
at androidx.appcompat.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:473)
at android.view.View.performClick(View.java:7750)
at android.view.View.performClickInternal(View.java:7727)
at android.view.View.access$3700(View.java:858)
at android.view.View$PerformClick.run(View.java:29115)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:210)
at android.os.Looper.loop(Looper.java:299)
at android.app.ActivityThread.main(ActivityThread.java:8168)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:556)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1037)
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Method.invoke(Native Method)
at androidx.appcompat.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:468)
at android.view.View.performClick(View.java:7750)
at android.view.View.performClickInternal(View.java:7727)
at android.view.View.access$3700(View.java:858)
at android.view.View$PerformClick.run(View.java:29115)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:210)
at android.os.Looper.loop(Looper.java:299)
at android.app.ActivityThread.main(ActivityThread.java:8168)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:556)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1037)
Caused by: java.lang.RuntimeException: Cannot initialize Visualizer engine, error: -3
at android.media.audiofx.Visualizer.<init>(Visualizer.java:238)
at com.musicit.player.playerVisualizer.setAudioSessionId(playerVisualizer.kt:49)
at com.musicit.player.MainActivity.playSong(MainActivity.kt:89)
at java.lang.reflect.Method.invoke(Native Method)
at androidx.appcompat.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:468)
at android.view.View.performClick(View.java:7750)
at android.view.View.performClickInternal(View.java:7727)
at android.view.View.access$3700(View.java:858)
at android.view.View$PerformClick.run(View.java:29115)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:210)
at android.os.Looper.loop(Looper.java:299)
at android.app.ActivityThread.main(ActivityThread.java:8168)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:556)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1037)
The way I did it is very straightforward, just to see how it works:
I have this :
class MainActivity : AppCompatActivity() {
fun onPlayClick(view: View) {
var song = '/path/that/definitelyexists.mp3';
playMusic(song);
}
fun playMusic(song: String){
val mediaPlayerInstance = MediaPlayer();
mediaPlayerInstance.setAudioAttributes(
AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA)
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.build()
);
mediaPlayerInstance.setDataSource(audioPath);
mediaPlayerInstance.prepare();
mediaPlayerInstance.start();
val visualiz = findViewById<MVisualizer>(R.id.musicitviz);
visualiz.setAudioSessionId(mediaPlayerInstance.audioSessionId);
}
}
<com.musicit.music.Visualizer
android:id="@+id/musicitviz"
android:layout_width="267dp"
android:layout_height="258dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
class Visualizer(context: Context?, attrs: AttributeSet?) : View(context, attrs) {
private var paint = Paint();
private var visualizer: Visualizer? = null;
private var waveformByteArray: ByteArray? = null;
init {
paint.color = Color.rgb(244, 40, 30);
}
override fun draw(canvas: Canvas?) {
super.draw(canvas);
waveformByteArray?.let { waveformByteArray ->
if(waveformByteArray.isEmpty()) {
Log.d(": ", "waveformbytearray is null.");
return;
}else{
Log.d(": ", "waveformbytearray is not null.");
}
}
}
fun setAudioSessionId(audioSessionId: Int) {
if(visualizer != null){
visualizer?.release();
}
val newVisualizer = Visualizer(audioSessionId);
newVisualizer.enabled = true;
newVisualizer.captureSize = Visualizer.getCaptureSizeRange()[1];
newVisualizer.setDataCaptureListener(object: Visualizer.OnDataCaptureListener {
override fun onWaveFormDataCapture(
visualizer: Visualizer?,
waveform: ByteArray?,
samplingRate: Int
) {
waveformByteArray = waveform;
Log.d("TAG: ", "I am updating the waveform")
invalidate();
}
override fun onFftDataCapture(
visualizer: Visualizer?,
fft: ByteArray?,
samplingRate: Int
) {
}
}, Visualizer.getMaxCaptureRate() / 2, true, false);
newVisualizer.enabled = true;
visualizer = newVisualizer;
}
}
I've tried adding permission:
<uses-permission android:name="android.permission.RECORD_AUDIO"/> <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
restarting my device, and also updating the api level to >=29:
android {
compileSdk 33
defaultConfig {
applicationId "com.musicit.music"
minSdk 29
targetSdk 33
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
namespace 'com.musicit.music'
}
as suggested in posts with similar problem but, nothing really works, wonder how I can solve the problem.
EDIT: I also tried setting on prepare listener, but to no avail, the error message is still the same, with Error code -3:
val mediaPlayerInstance = MediaPlayer();
mediaPlayerInstance.setAudioAttributes(
AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA)
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.build()
);
mediaPlayerInstance.setDataSource(audioPath);
mediaPlayerInstance.setOnPreparedListener(object: MediaPlayer.OnPreparedListener {
override fun onPrepared(mp: MediaPlayer?) {
if(mp == null) {
return;
}
val visualiz = findViewById<Visualizer>(R.id.musicitviz);
visualiz.setAudioSessionId(mp.audioSessionId);
visualiz.enableVisualization();
mp.start();
}
});
mediaPlayerInstance.prepare();
I'm glad I can tell you I solved the issue. I tried to run your code and got the same error. I found the solution here. They say you need to have permission RECORD_AUDIO. After requesting this permission the error was gone! You have to declare this is in the manifest:
And then request permission like this:
That's all. When OnRequestPermissionsResultCallback is called you can create the
android.media.audiofx.Visualizer. But not before.I also told you to use
setOnPreparedListener. That wasn't completely correct. If you only play local files you can stick toprepareand please remove thesetOnPreparedListener. But if you want to play URLs from web servers for example please switch toprepareAsyncandsetOnPreparedListener. It's explained in the documentation: