如何防止数字因波浪而变大

本文关键字:何防止 数字 | 更新日期: 2023-09-27 18:34:16

所以解释起来可能会变得过于复杂,但我会尽量保持简单但信息丰富。 我的程序是用 C#.net 编写的,它会监视麦克风 2 秒钟,并从样本中返回最大值。我不太精通 winmm.dll 如何生成声音等,但我的程序松散地基于 NAudio 和 CodeProject 的另一个项目来可视化波浪。我使用的波形格式是这样的

    //WaveIn.cs
    private WaveFormat Format= new WaveFormat(8000, 16,1);
    //waveFormat.cs
[StructLayout(LayoutKind.Sequential)] 
public class WaveFormat
{
    public short wFormatTag;
    public short nChannels;
    public int nSamplesPerSec;
    public int nAvgBytesPerSec;
    public short nBlockAlign;
    public short wBitsPerSample;
    public short cbSize;
    public WaveFormat(int rate, int bits, short channels)
    {
        wFormatTag = (short)WaveFormats.Pcm;
        nChannels = channels;
        nSamplesPerSec = rate;
        wBitsPerSample = (short)bits;
        cbSize = 0;
        nBlockAlign = (short)(nChannels * (wBitsPerSample / 8));
        nAvgBytesPerSec = nSamplesPerSec * nBlockAlign;
    }

(我想我可能刚刚通过发布这个找到了我的问题,但我仍然会问(

因此,我在 Wavein 文件中设置了一个最大声级事件。如果我正确理解源代码,它会在缓冲区已满时触发。这是代码

    private void CallBack(IntPtr waveInHandle, WaveMessage message, int userData, ref WaveHeader waveHeader, IntPtr reserved)
    {
        if (message == WaveMessage.WIM_DATA)
        {
            GCHandle hBuffer = (GCHandle)waveHeader.dwUser;
            WaveInBuffer buffer = (WaveInBuffer)hBuffer.Target;
            Exception exception = null;
            if (DataAvailable != null)
            {
                DataAvailable(buffer.Data, buffer.BytesRecorded);
            }
            if (MaxSoundLevel != null) //FOLLOW THIS ONE
            {
                byte[] waveStream = new byte[buffer.BytesRecorded];
                Marshal.Copy(buffer.Data, waveStream, 0, buffer.BytesRecorded);
                MaxSoundLevel(GetMaxSound(GetWaveChannels(waveStream)));
            }
            if (recording)
            {
                try
                {
                    buffer.Reuse();
                }
                catch (Exception e)
                {
                    recording = false;
                    exception = e;
                }
            }
        }
    }
    private short[] GetWaveChannels(byte[] waveStream)
    {
        short[] monoWave = new short[waveStream.Length/2];
        int h=0;
        for (int i = 0 ; i < waveStream.Length; i += 2)
        {
            monoWave[h] = BitConverter.ToInt16(waveStream, i);
            h++;
        }
        return monoWave;
    }
    private int GetMaxSound(short[] wave)
    {
        int maxSound = 0;
        for (int i = 0; i < wave.Length; i++)
        {
            maxSound = Math.Max(maxSound, Math.Abs(wave[i]));
        }
        return maxSound;
    }

因此,当我从此测试中监视它时,如果我将声级保持在"正常",它就不会崩溃

    [Test]
    public void TestSound()
    {
        var waveIn = new WaveIn();
        waveIn.MaxSoundLevel += new WaveIn.MaxSoundHandler(waveIn_MaxSoundLevel);
        waveIn.StartRecording();
        Console.WriteLine("Starting to record");
        Thread.Sleep(4800); //record for 4.8 seconds.
        waveIn.StopRecording();
        Console.WriteLine("Done Recording");
    }
    void waveIn_MaxSoundLevel(int MaxSound)
    {
        Console.WriteLine("MaxSound:{0}", MaxSound);
    }

这是我的输出

最大音效:28最大音效:24最大音效:31最大音效:17最大音效:18760

未处理的异常:

系统。溢出异常:否定二进制补码数的最小值无效。

我曾经得到它给我MaxSound:32767(0x7FFF(。

所以我认为我的问题在于试图将 32 位数字转换为 16 位数字,这就是我将 GetMaxSound 从短切换到 int 的原因。所以我不知道。我被难住了。那么为什么我会遇到这个问题呢?难道我的波浪不是表明它的最大值是 32,767 并且 winmm.dll 会知道这一点并且不会超过那个吗?而且由于它只是将 2 字节的数据转换为短数据,它不应该遇到这个问题吗?请帮忙:)

如何防止数字因波浪而变大

对于那些可能正在研究这个问题的人来说,我的解决方案本质上相当简单。16 位有符号数字的最大正值为 32767。它的最大负数是 -32768。如果采用绝对值 32768 并尝试将其放入 16 位数字,则会导致引发溢出异常。因此,解决方案是在我尝试获取其绝对值之前将短值转换为 32 位数字。这是更正的功能

    private int GetMaxSound(short[] wave)
    {
        int maxSound = 0;
        for (int i = 0; i < wave.Length; i++)
        {
            maxSound = Math.Max(maxSound, Math.Abs((int)wave[i]));
        }
        return maxSound;
    }

我可能也可以通过使用ushort来坚持使用无符号数字,但是Math.Abs确实如此。