Синтаксический анализ Wave-файла с помощью BinaryReader

#c# #file #wave #audio-processing

#c# #файл #wave #обработка звука

Вопрос:

В системе mscorlib сборки .NET.Пространство имен ввода-вывода, я использую метод 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 (если выборки находятся вместе), либо прочитать каналы отдельно, если данные для одного канала следуют за другим.

Комментарии:

1. каналы в 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:

преобразовал VB-код с помощью sharpdevelop в 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;
    }
}