了解Android RingDroid WAV计算

时间:2014-06-06 06:27:04

标签: android audio wav waveform

我一直在研究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;

Q1。 50魔法数字来自哪里?是假设帧持续时间是50?

Q2。为什么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;
    }
}

Q3。为什么j增加4 * mChannels? 4如何合理?

Q4。 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/
  4. 但是我没有看到任何地方提到这个词。

    希望有人可以对此有所了解。谢谢。

1 个答案:

答案 0 :(得分:2)

  

Q1。 50魔法数字来自哪里?是假设帧持续时间是50?

A1。计算1/50秒作为帧。因此,如果每秒音频数据,应用程序必须处理50帧缓冲区。

  

Q2。为什么mFrameBytes = frameSample * 2?是假设每个样本是2个字节?但为什么呢?

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

  

Q3。为什么j增加4 * mChannels? 4如何合理?

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

  

Q4。 frameGains实际意味着什么?

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

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