使用 BinaryReader 解析 Wave 文件
Posted
技术标签:
【中文标题】使用 BinaryReader 解析 Wave 文件【英文标题】:Parsing Through Wave File with BinaryReader 【发布时间】:2011-05-12 19:28:43 【问题描述】:在 .NET Assembly mscorlib System.IO 命名空间中,我使用 ReadInt16() 方法循环音频数据字节并将有符号整数值转储到文本文件中。如何解释与一个采样率相关的两个值?也就是说,如果我有一秒钟的单声道数据将有 88200 个字节,因此使用 ReadInt16() 返回 88200 个离散整数。这信息太多了,我应该只有 44100 个整数。所以我是否需要使用不同的方法,或者每次迭代将循环提前 1 次。
非常感谢........米奇
【问题讨论】:
【参考方案1】:using System;
using System.IO;
public struct WaveFormat
private short m_FormatTag; // most often PCM = 1
private short m_nChannels; // number of channels
private int m_SamplesPerSecond; // samples per second eg 44100
private int m_AvgBytesPerSecond; // bytes per second eg 176000
private short m_BlockAlign; // blockalign (byte per sample) eg 4 bytes
private short m_BitsPerSample; // bits per sample, 8, 16, 24
public WaveFormat(byte BPS, int SPS, byte nChn)
m_FormatTag = 1; //PCM
m_nChannels = nChn;
m_SamplesPerSecond = SPS;
m_BitsPerSample = BPS;
m_BlockAlign = (short)(m_nChannels * m_BitsPerSample / 8);
m_AvgBytesPerSecond = (int)(m_BlockAlign * m_SamplesPerSecond);
public short FormatTag
get return m_FormatTag;
set m_FormatTag = value;
public short Channels
get return m_nChannels;
public int SamplesPerSecond
get return m_SamplesPerSecond;
public int AvgBytesPerSecond
get return m_AvgBytesPerSecond;
public short BlockAlign
get return m_BlockAlign;
public short BitsPerSample
get return m_BitsPerSample;
public void Read(BinaryReader br)
m_FormatTag = br.ReadInt16();
m_nChannels = br.ReadInt16();
m_SamplesPerSecond = br.ReadInt32();
m_AvgBytesPerSecond = br.ReadInt32();
m_BlockAlign = br.ReadInt16();
m_BitsPerSample = br.ReadInt16();
public void Write(BinaryWriter bw)
bw.Write(m_FormatTag);
bw.Write(m_nChannels);
bw.Write(m_SamplesPerSecond);
bw.Write(m_AvgBytesPerSecond);
bw.Write(m_BlockAlign);
bw.Write(m_BitsPerSample);
public override string ToString()
System.Text.StringBuilder sb = new System.Text.StringBuilder();
sb.AppendLine("FormatTag: " + m_FormatTag.ToString());
sb.AppendLine("nChannels: " + m_nChannels.ToString());
sb.AppendLine("SamplesPerSecond: " + m_SamplesPerSecond.ToString());
sb.AppendLine("AvgBytesPerSecond: " + m_AvgBytesPerSecond.ToString());
sb.AppendLine("BlockAlign: " + m_BlockAlign.ToString());
sb.AppendLine("BitsPerSample: " + m_BitsPerSample.ToString());
return sb.ToString();
【讨论】:
【参考方案2】:通常,当您读取数据数组时,您的代码应如下所示:
for(int i = 0; i < totalNumberOfEntries; i++)
// read all data for this entry
var component1 = reader.ReadXXX();
var component2 = reader.ReadXXX();
// deal with data for this entry
someEntryStroage.Add(new Entry(component1, component2);
最有可能(我不知道 Wave 文件格式)在您的情况下,您要么需要读取 Int16 值对(如果样本在一起),要么如果一个通道的数据一个接一个,则单独读取通道。
【讨论】:
wav文件中的通道是“多路复用的”,这意味着:sample1_chnl_1,sample1_chnl_2,sample2_chnl_1,sample2_chnl_2,sample3_chnl_1,sample3_chnl_2,sample4_chnl_1,sample4_chnl_2...【参考方案3】:你必须阅读chunkinfos。数据块告诉您必须读取多少字节。 WaveFormat 会告诉您平均每秒多少字节,等等。我有一些 VB 代码...
【讨论】:
【参考方案4】:已经将带有 sharpdevelop 的 VB 代码转换为 C# 可能会有所帮助...
using System;
using System.IO;
public class ChunkInfo
private byte[] m_Header;
private long m_Length;
private long m_OffSet;
public ChunkInfo(string Header)
m_Header = new byte[Header.Length];
for (int i = 0; i <= m_Header.GetUpperBound(0); i++)
m_Header[i] = (byte)Header[i];
public ChunkInfo(byte[] Header)
m_Header = Header;
public void Read(BinaryReader br)
m_OffSet = SearchOffset(br);
if (m_OffSet >= 0)
br.BaseStream.Position = m_OffSet + m_Header.Length;
m_Length = br.ReadInt32();
public void Write(BinaryWriter bw)
bw.Write(m_Header);
bw.Write(m_Length);
public long Length
get return m_Length;
public long OffSet
get return m_OffSet;
private long SearchOffset(BinaryReader br)
byte[] haystack = null;
bool found = false;
long offset = 0;
long basepos = 0;
int hlength = 260;
long basepos_grow = hlength - m_Header.Length;
while (!(found || (basepos >= br.BaseStream.Length)))
br.BaseStream.Position = basepos;
haystack = br.ReadBytes(hlength);
offset = BoyerMooreHorspool.find(haystack, m_Header);
found = offset >= 0;
if (found)
offset += basepos;
break;
else
basepos += basepos_grow;
return offset;
public static class BoyerMooreHorspool
//detects a needle in the haystack
const int UBYTE_MAX = 255;
static int[] bad_char_skip4 = new int[UBYTE_MAX + 3];
static int[] bad_char_skip8 = new int[UBYTE_MAX + 3];
static bool IsInitialized = false;
public static void init()
//little optimization for needles with length 4 or 8
for (int i = 0; i <= UBYTE_MAX + 2; i++)
bad_char_skip4[i] = 4;
bad_char_skip8[i] = 8;
IsInitialized = true;
public static int find(byte[] haystack, byte[] needle, int start = 0)
if (!IsInitialized) init();
int i_n = 0;
//needle index
int n_n = needle.Length;
int[] bad_char_skip = null;
switch (n_n)
case 4:
bad_char_skip = bad_char_skip4;
break;
case 8:
bad_char_skip = bad_char_skip8;
break;
default:
bad_char_skip = new int[UBYTE_MAX + 3];
for (i_n = 0; i_n <= UBYTE_MAX + 2; i_n++)
bad_char_skip[i_n] = n_n;
break;
int ifind = -1;
//if not found then return - 1
int i_h = start;
//haystack index
int n_h = haystack.Length;
if (n_n > n_h)
throw new ArgumentOutOfRangeException("needle", "needle is to long");
int last = n_n - 1;
for (i_n = 0; i_n <= last - 1; i_n++)
bad_char_skip[needle[i_n]] = last - i_n;
byte bcs = 0;
int bhs = 0;
while ((n_h - start) >= n_n)
i_n = last;
while (haystack[i_h + i_n] == needle[i_n])
i_n -= 1;
if (i_n == 0)
ifind = i_h;
break;
bhs = haystack[i_h + last];
bcs = (byte)(bad_char_skip[bhs]);
n_h -= bcs;
i_h += bcs;
return ifind;
【讨论】:
以上是关于使用 BinaryReader 解析 Wave 文件的主要内容,如果未能解决你的问题,请参考以下文章
BinaryReader.PeekChar()读取了多少位?
为啥在 BinaryReader 上调用 Dispose() 会导致编译错误?
如果我正在读取的字节还不存在,BinaryReader 会做啥?