在 Android 上从 MP3 获取 PCM 数据

问题描述 投票:0回答:2

我有兴趣在Android平台上做一些音乐分析。为此,我想将任意 MP3 解析为 PCM 数据。我环顾四周,似乎没有一个简单的方法可以做到这一点。我尝试过的一种解决方案是使用jLayer。这可行,但速度非常慢,在播放歌曲的同时解码歌曲。

我知道 Android 上存在 MP3 解码器,谷歌在支持的媒体类型下也这么说。有谁知道如何使用 Android 解码器来解码 MP3 而无需实际播放它?我想做的就是将这些位从 DAC 中转移出来,并将它们存储在某个缓冲区中。

或者,有没有人使用 NDK 和类似 MAD 之类的东西取得过成功?性能提升有那么好吗?

java android audio mp3 decode
2个回答
2
投票

可以编译并使用LAME解码器。在ARM上运行得很好。而且由于是C/C++,性能会更好。

大多数 ARM 平台上的硬件解码器都是面向播放的。所以不会给你完美的PCM。例如,为了避免抖动,如果负载较高,它会跳过一些数据。

编译LAME的说明为:

Lame MP3 编码器为 Android 编译


0
投票

这个为我做到了。 之后,我通过将结果文件作为原始数据导入来在 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();
    }
© www.soinside.com 2019 - 2024. All rights reserved.