【问题标题】:Understanding Android RingDroid WAV Calculations了解 Android RingDroid WAV 计算
【发布时间】:2014-06-06 06:27:03
【问题描述】:

我一直在研究 RingDroid 的源代码,试图弄清楚如何在 Android 设备上绘制波形。但是,我被困在CheapWAV.java 上关于阅读 WAV 文件的部分。

public void ReadFile(File inputFile)
        throws java.io.FileNotFoundException,
               java.io.IOException {
    super.ReadFile(inputFile);
    mFileSize = (int)mInputFile.length();

    if (mFileSize < 128) {
        throw new java.io.IOException("File too small to parse");
    }

    FileInputStream stream = new FileInputStream(mInputFile);
    byte[] header = new byte[12];
    stream.read(header, 0, 12);
    mOffset += 12;
    if (header[0] != 'R' ||
        header[1] != 'I' ||
        header[2] != 'F' ||
        header[3] != 'F' ||
        header[8] != 'W' ||
        header[9] != 'A' ||
        header[10] != 'V' ||
        header[11] != 'E') {
        throw new java.io.IOException("Not a WAV file");
    }

    mChannels = 0;
    mSampleRate = 0;
    while (mOffset + 8 <= mFileSize) {
        byte[] chunkHeader = new byte[8];
        stream.read(chunkHeader, 0, 8);
        mOffset += 8;

        int chunkLen =
            ((0xff & chunkHeader[7]) << 24) |
            ((0xff & chunkHeader[6]) << 16) |
            ((0xff & chunkHeader[5]) << 8) |
            ((0xff & chunkHeader[4]));

        if (chunkHeader[0] == 'f' &&
            chunkHeader[1] == 'm' &&
            chunkHeader[2] == 't' &&
            chunkHeader[3] == ' ') {
            if (chunkLen < 16 || chunkLen > 1024) {
                throw new java.io.IOException(
                    "WAV file has bad fmt chunk");
            }

            byte[] fmt = new byte[chunkLen];
            stream.read(fmt, 0, chunkLen);
            mOffset += chunkLen;

            int format =
                ((0xff & fmt[1]) << 8) |
                ((0xff & fmt[0]));
            mChannels =
                ((0xff & fmt[3]) << 8) |
                ((0xff & fmt[2]));
            mSampleRate =
                ((0xff & fmt[7]) << 24) |
                ((0xff & fmt[6]) << 16) |
                ((0xff & fmt[5]) << 8) |
                ((0xff & fmt[4]));

            if (format != 1) {
                throw new java.io.IOException(
                    "Unsupported WAV file encoding");
            }

        } else if (chunkHeader[0] == 'd' &&
                   chunkHeader[1] == 'a' &&
                   chunkHeader[2] == 't' &&
                   chunkHeader[3] == 'a') {
            if (mChannels == 0 || mSampleRate == 0) {
                throw new java.io.IOException(
                    "Bad WAV file: data chunk before fmt chunk");
            }

            int frameSamples = (mSampleRate * mChannels) / 50;
            mFrameBytes = frameSamples * 2;

            mNumFrames = (chunkLen + (mFrameBytes - 1)) / mFrameBytes;
            mFrameOffsets = new int[mNumFrames];
            mFrameLens = new int[mNumFrames];
            mFrameGains = new int[mNumFrames];

            byte[] oneFrame = new byte[mFrameBytes];

            int i = 0;
            int frameIndex = 0;
            while (i < chunkLen) {
                int oneFrameBytes = mFrameBytes;
                if (i + oneFrameBytes > chunkLen) {
                    i = chunkLen - oneFrameBytes;
                }

                stream.read(oneFrame, 0, oneFrameBytes);

                int maxGain = 0;
                for (int j = 1; j < oneFrameBytes; j += 4 * mChannels) {
                    int val = java.lang.Math.abs(oneFrame[j]);
                    if (val > maxGain) {
                        maxGain = val;
                    }
                }

                mFrameOffsets[frameIndex] = mOffset;
                mFrameLens[frameIndex] = oneFrameBytes;
                mFrameGains[frameIndex] = maxGain;

                frameIndex++;
                mOffset += oneFrameBytes;
                i += oneFrameBytes;

                if (mProgressListener != null) {
                    boolean keepGoing = mProgressListener.reportProgress(
                        i * 1.0 / chunkLen);
                    if (!keepGoing) {
                        break;
                    }
                }
            }

        } else {
            stream.skip(chunkLen);
            mOffset += chunkLen;
        }
    }
}

在我到达之前,一切似乎都是直截了当的

int frameSamples = (mSampleRate * mChannels) / 50;
mFrameBytes = frameSamples * 2;

mNumFrames = (chunkLen + (mFrameBytes - 1)) / mFrameBytes;

第一季度。 50这个神奇的数字是从哪里来的?是否只是假设帧持续时间为 50?

第二季度。为什么 mFrameBytes = frameSample * 2?是否假设每个样本是 2 个字节?但是为什么呢?

for (int j = 1; j < oneFrameBytes; j += 4 * mChannels) {
    int val = java.lang.Math.abs(oneFrame[j]);
    if (val > maxGain) {
        maxGain = val;
    }
}

第三季度。为什么 j 增加 4 * mChannels? 4 是如何证明的?

第四季度。 frameGains 究竟是什么意思?我已经浏览过诸如

之类的文章/博客
  1. https://ccrma.stanford.edu/courses/422/projects/WaveFormat/2
  2. http://blogs.msdn.com/b/dawate/archive/2009/06/23/intro-to-audio-programming-part-2-demystifying-the-wav-format.aspx
  3. http://www.speakingcode.com/2011/12/31/primer-on-digital-audio-and-pulse-code-modulation-pcm/

但我没有看到任何地方提到过这个词。

希望有人能对此有所了解。谢谢。

【问题讨论】:

    标签: android audio wav waveform


    【解决方案1】:

    第一季度。 50这个神奇的数字是从哪里来的?是否只是假设帧持续时间为 50?

    A1。这计算 1/50 秒作为一帧。因此,如果每秒有音频数据,应用程序将不得不处理 50 个帧缓冲区。

    第二季度。为什么 mFrameBytes = frameSample * 2?是否假设每个样本是 2 个字节?但为什么呢?

    A2。我猜这是因为他假设的是 16 位样本。

    第三季度。为什么 j 增加 4 * mChannels? 4 是如何证明的?

    A3。我认为这里的关键是要注意它从偏移量 1 开始。这意味着他只对样本的高位字节进行采样。 4 可能只是一个优化,所以他只处理一半的缓冲区(记住他假设每个样本 2 个字节)

    第四季度。 frameGains 究竟是什么意思?

    嗯,这正是它所说的。这是该帧的增益(1/50 秒)请参阅 http://en.m.wikipedia.org/wiki/Gain 或 Google 以获取:音频增益。

    这也应该有帮助:https://ccrma.stanford.edu/courses/422/projects/WaveFormat/

    【讨论】:

    • 谢谢。这是非常有用的。还有一个问题,当说音频增益时,它只是指由 Vout/Vin 确定的信号强度吗?
    • 他正在获得整个帧的峰值幅度。
    猜你喜欢
    • 1970-01-01
    • 2012-07-28
    • 1970-01-01
    • 2021-06-01
    • 1970-01-01
    • 2019-06-08
    • 1970-01-01
    • 2013-02-25
    • 1970-01-01
    相关资源
    最近更新 更多