
visualizer是android音频框架提供的一个强大工具,它允许开发者访问正在播放的音频流的波形(waveform)或快速傅里叶变换(fft)数据。通过这些数据,开发者可以创建各种音频可视化效果,如频谱分析仪、声波图等。visualizer通过监听特定音频会话(audio session)的输出,捕获实时的音频数据。
Visualizer对象在使用前必须经过正确的初始化和状态转换。其生命周期中包含不同的状态,例如未初始化、已初始化、已启用等。如果在一个不正确的状态下调用了某个方法,就会抛出IllegalStateException。
IllegalStateException: getFft() called in wrong state: 1 是使用Visualizer时非常常见的错误。这里的“state: 1”通常指的是Visualizer对象处于“已初始化但未启用”的状态。getFft()或getWaveForm()这类数据获取方法要求Visualizer必须处于“已启用”(Enabled)状态才能正常工作。
解决方案的核心在于,在尝试获取FFT或波形数据之前,必须显式地调用visualizer.setEnabled(true)来激活Visualizer。
以下是使用Visualizer获取FFT数据的详细步骤和相应的代码示例。
在AndroidManifest.xml文件中添加RECORD_AUDIO权限。尽管Visualizer只用于读取音频输出,但其内部实现可能涉及到与音频输入相关的权限。
<uses-permission android:name="android.permission.RECORD_AUDIO" />
Visualizer需要绑定到一个特定的音频会话。这通常通过MediaPlayer或AudioTrack实例获取。
// 假设你有一个MediaPlayer实例 MediaPlayer mediaPlayer = new MediaPlayer(); // ... 设置数据源、准备等操作 ... int audioSessionId = mediaPlayer.getAudioSessionId();
使用获取到的audioSessionId来实例化Visualizer对象。
import android.media.audiofx.Visualizer;
Visualizer audioVisualizer;
try {
audioVisualizer = new Visualizer(audioSessionId);
} catch (UnsupportedOperationException e) {
// 设备不支持Visualizer或AudioFX
e.printStackTrace();
return;
} catch (RuntimeException e) {
// 其他初始化错误,例如权限不足
e.printStackTrace();
return;
}在启用Visualizer之前,可以设置数据捕获的参数,例如捕获尺寸。FFT的捕获尺寸必须是2的幂,并且在getFftCaptureSizeRange()返回的范围内。通常,我们会选择最大允许的FFT捕获尺寸以获取更详细的频谱信息。
// 设置FFT捕获尺寸为最大值 // getFftCaptureSizeRange() 返回一个包含最小和最大尺寸的数组 int maxCaptureSize = Visualizer.getFftCaptureSizeRange()[1]; audioVisualizer.setCaptureSize(maxCaptureSize); // 可选:设置数据捕获的采样率 // int maxCaptureRate = Visualizer.getMaxCaptureRate(); // audioVisualizer.setSamplingRate(maxCaptureRate);
这是解决IllegalStateException的关键。在尝试获取数据之前,必须将Visualizer设置为启用状态。
audioVisualizer.setEnabled(true);
现在可以安全地调用getFft()方法来获取FFT数据。需要注意的是,getFft()方法需要一个预先分配好大小的byte数组作为参数,用于存储捕获到的数据。如果传入null或大小不正确的数组,可能会导致JNI DETECTED ERROR IN APPLICATION: jarray was NULL这类JNI错误。数组的大小应与之前设置的captureSize相同。
// 确保byte数组已初始化且大小正确
byte[] fftBuffer = new byte[audioVisualizer.getCaptureSize()];
try {
int result = audioVisualizer.getFft(fftBuffer);
if (result == Visualizer.SUCCESS) {
// FFT数据已成功写入fftBuffer
// fftBuffer中的数据格式是实部和虚部交替存储的,
// 例如:[R0, I0, R1, I1, ..., Rn-1, In-1, Rn]
// 其中,R0是直流分量,I0始终为0,Rn是奈奎斯特频率分量。
// 可以根据这些数据进行可视化处理。
// 例如,计算每个频段的幅度:magnitude = sqrt(real*real + imag*imag)
} else {
// 处理获取失败的情况
System.err.println("Failed to get FFT data: " + result);
}
} catch (IllegalStateException e) {
// 再次检查是否已启用
e.printStackTrace();
}获取到的fftBuffer包含原始的FFT数据。通常,byte数组的前半部分是实部,后半部分是虚部。每个频段的能量(幅度)可以通过magnitude = sqrt(real*real + imag*imag)计算得出。
当不再需要Visualizer时,务必调用release()方法来释放其占用的资源,避免内存泄漏和系统资源耗尽。
// 在Activity/Fragment的onDestroy()或不再需要时调用
if (audioVisualizer != null) {
audioVisualizer.release();
audioVisualizer = null;
}import android.Manifest;
import android.content.pm.PackageManager;
import android.media.MediaPlayer;
import android.media.audiofx.Visualizer;
import android.os.Bundle;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
public class AudioVisualizerActivity extends AppCompatActivity {
private static final String TAG = "AudioVisualizerActivity";
private static final int REQUEST_RECORD_AUDIO_PERMISSION = 200;
private MediaPlayer mediaPlayer;
private Visualizer audioVisualizer;
private byte[] fftBuffer;
// 请求权限的回调
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQUEST_RECORD_AUDIO_PERMISSION) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
initVisualizer();
} else {
Log.e(TAG, "RECORD_AUDIO permission denied.");
// 可以在这里提示用户或禁用相关功能
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); // 假设你有一个主布局
// 检查并请求RECORD_AUDIO权限
if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.RECORD_AUDIO}, REQUEST_RECORD_AUDIO_PERMISSION);
} else {
initVisualizer();
}
}
private void initVisualizer() {
// 1. 初始化MediaPlayer并获取AudioSessionId
mediaPlayer = MediaPlayer.create(this, R.raw.sample_audio); // 假设你有一个名为sample_audio的音频文件
if (mediaPlayer == null) {
Log.e(TAG, "MediaPlayer initialization failed.");
return;
}
mediaPlayer.setLooping(true); // 循环播放
mediaPlayer.start(); // 开始播放
int audioSessionId = mediaPlayer.getAudioSessionId();
Log.d(TAG, "Audio Session ID: " + audioSessionId);
// 2. 初始化Visualizer
try {
audioVisualizer = new Visualizer(audioSessionId);
Log.d(TAG, "Visualizer initialized.");
} catch (UnsupportedOperationException e) {
Log.e(TAG, "Device does not support Visualizer or AudioFX: " + e.getMessage());
return;
} catch (RuntimeException e) {
Log.e(TAG, "Visualizer initialization failed due to RuntimeException (e.g., permissions): " + e.getMessage());
return;
}
// 3. 设置捕获参数
int maxCaptureSize = Visualizer.getFftCaptureSizeRange()[1];
audioVisualizer.setCaptureSize(maxCaptureSize);
Log.d(TAG, "FFT Capture Size set to: " + maxCaptureSize);
// 初始化FFT数据缓冲区
fftBuffer = new byte[audioVisualizer.getCaptureSize()];
// 4. 启用Visualizer (核心步骤)
try {
audioVisualizer.setEnabled(true);
Log.d(TAG, "Visualizer enabled.");
} catch (IllegalStateException e) {
Log.e(TAG, "Failed to enable Visualizer: " + e.getMessage());
return;
}
// 5. 设置数据捕获监听器 (推荐方式,自动获取数据)
// 也可以手动调用getFft(),但监听器更适合实时可视化
audioVisualizer.setDataCaptureListener(new Visualizer.OnDataCaptureListener() {
@Override
public void onWaveFormDataCapture(Visualizer visualizer, byte[] waveform, int samplingRate) {
// 不使用波形数据时,可以留空或返回
}
@Override
public void onFftDataCapture(Visualizer visualizer, byte[] fft, int samplingRate) {
// FFT数据已在fft数组中
// 可以在这里更新UI或进行其他处理
// Log.d(TAG, "FFT Data Captured. Length: " + fft.length);
// For demonstration, print first few values
// if (fft.length > 0) {
// StringBuilder sb = new StringBuilder("FFT: [");
// for (int i = 0; i < Math.min(10, fft.length); i++) {
// sb.append(fft[i]).append(", ");
// }
// sb.append("...]");
// Log.d(TAG, sb.toString());
// }
}
}, Visualizer.getMaxCaptureRate(), false, true); // false for waveform, true for fft
// 如果想手动调用getFft()而不是通过监听器,可以这样做:
// new Thread(() -> {
// while (audioVisualizer != null && audioVisualizer.getEnabled()) {
// try {
// int result = audioVisualizer.getFft(fftBuffer);
// if (result == Visualizer.SUCCESS) {
// // Process fftBuffer
// // Log.d(TAG, "Manually got FFT data. Length: " + fftBuffer.length);
// }
// Thread.sleep(100); // 控制获取频率
// } catch (IllegalStateException | InterruptedException e) {
// Log.e(TAG, "Error getting FFT manually: " + e.getMessage());
// break;
// }
// }
// }).start();
}
@Override
protected void onPause() {
super.onPause();
// 暂停Visualizer和MediaPlayer
if (audioVisualizer != null) {
audioVisualizer.setEnabled(false);
}
if (mediaPlayer != null) {
mediaPlayer.pause();
}
}
@Override
protected void onResume() {
super.onResume();
// 恢复Visualizer和MediaPlayer
if (audioVisualizer != null) {
audioVisualizer.setEnabled(true);
}
if (mediaPlayer != null) {
mediaPlayer.start();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
// 释放资源
if (mediaPlayer != null) {
mediaPlayer.release();
mediaPlayer = null;
}
if (audioVisualizer != null) {
audioVisualizer.release();
audioVisualizer = null;
}
Log.d(TAG, "Resources released.");
}
}通过遵循上述步骤和最佳实践,开发者可以有效地解决Visualizer的IllegalStateException,并成功地从Android音频流中获取FFT频谱数据。理解Visualizer的状态管理和正确的资源释放机制是构建稳定、高性能音频可视化应用的关键。利用Visualizer捕获到的数据,开发者可以进一步进行复杂的信号处理和图形渲染,为用户提供丰富的音频交互体验。
以上就是Android Visualizer 获取FFT频谱数据:状态管理与实践指南的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号