我有兴趣在Android平台上做一些音乐分析。为此,我想将任意 MP3 解析为 PCM 数据。我环顾四周,似乎没有一个简单的方法可以做到这一点。我尝试过的一种解决方案是使用jLayer。这可行,但速度非常慢,在播放歌曲的同时解码歌曲。
我知道 Android 上存在 MP3 解码器,谷歌在支持的媒体类型下也这么说。有谁知道如何使用 Android 解码器来解码 MP3 而无需实际播放它?我想做的就是将这些位从 DAC 中转移出来,并将它们存储在某个缓冲区中。
或者,有没有人使用 NDK 和类似 MAD 之类的东西取得过成功?性能提升有那么好吗?
可以编译并使用LAME解码器。在ARM上运行得很好。而且由于是C/C++,性能会更好。
大多数 ARM 平台上的硬件解码器都是面向播放的。所以不会给你完美的PCM。例如,为了避免抖动,如果负载较高,它会跳过一些数据。
编译LAME的说明为:
这个为我做到了。 之后,我通过将结果文件作为原始数据导入来在 Audacity 中验证结果。
public static void decodeMp3ToPCM(String inputMp3Path, String outputPcmPath) throws IOException {
GeneralUtils.log(TAG, "decodeMp3ToPCM");
MediaExtractor extractor = new MediaExtractor();
extractor.setDataSource(inputMp3Path);
int audioTrackIndex = -1;
MediaFormat format = null;
for (int i = 0; i < extractor.getTrackCount(); i++) {
format = extractor.getTrackFormat(i);
String mime = format.getString(MediaFormat.KEY_MIME);
if (mime.startsWith("audio/")) {
audioTrackIndex = i;
extractor.selectTrack(i);
break;
}
}
if (audioTrackIndex == -1) {
throw new IOException("No audio track found in " + inputMp3Path);
}
MediaCodec codec = MediaCodec.createDecoderByType(Objects.requireNonNull(format.getString(MediaFormat.KEY_MIME)));
codec.configure(format, null, null, 0);
codec.start();
ByteBuffer[] inputBuffers = codec.getInputBuffers();
ByteBuffer[] outputBuffers = codec.getOutputBuffers();
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
boolean isInputEOF = false;
boolean isOutputEOF = false;
FileOutputStream pcmFile = new FileOutputStream(outputPcmPath);
while (!isOutputEOF) {
if (!isInputEOF) {
int inputBufferIndex = codec.dequeueInputBuffer(10000);
if (inputBufferIndex >= 0) {
ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
int sampleSize = extractor.readSampleData(inputBuffer, 0);
if (sampleSize < 0) {
codec.queueInputBuffer(inputBufferIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
isInputEOF = true;
} else {
codec.queueInputBuffer(inputBufferIndex, 0, sampleSize, extractor.getSampleTime(), 0);
extractor.advance();
}
}
}
int outputBufferIndex = codec.dequeueOutputBuffer(bufferInfo, 10000);
if (outputBufferIndex >= 0) {
ByteBuffer outputBuffer = outputBuffers[outputBufferIndex];
outputBuffer.position(bufferInfo.offset);
outputBuffer.limit(bufferInfo.offset + bufferInfo.size);
byte[] pcmData = new byte[bufferInfo.size];
outputBuffer.get(pcmData);
pcmFile.write(pcmData);
codec.releaseOutputBuffer(outputBufferIndex, false);
if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
isOutputEOF = true;
}
}
}
pcmFile.close();
codec.stop();
codec.release();
extractor.release();
}