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