androidvisualizer

using audio visualizer in android


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();

Solution

  • 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:

    <uses-permission android:name="android.permission.RECORD_AUDIO"/>
    

    And then request permission like this:

    ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.RECORD_AUDIO), 123 /* your request code */)
    

    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 to prepare and please remove the setOnPreparedListener. But if you want to play URLs from web servers for example please switch to prepareAsync and setOnPreparedListener. It's explained in the documentation:

    For files, it is OK to call prepare(), which blocks until MediaPlayer is ready for playback.