C#基于NAudio的声音识别(一)——录制与切割

准备做个非特定的声音识别程序,卡在录音和切割一小段时间,C#下基于DirectX的声音处理真的超级麻烦,后来找到了NAudio,非常好用,安利给大家,顺带把几个小坑稍微描述下,希望帮到类似需求的人。

项目下载:

NAudio官方地址:http://naudio.codeplex.com/

GitHub地址:https://github.com/naudio/NAudio

下载后是完整的工程文件,直接运行应该没问题,官方的demo都写成自定义控件的形式,界面写的不错,我也就懒得慢慢摘出来了,照猫画虎的去掉了demo里面选择控件的部分,直接显示在tabControl里,效果如下图。

C#基于NAudio的声音识别(一)——录制与切割_第1张图片

private void MainForm_Load(object sender, EventArgs e)
        {
            var demos = ReflectionHelper.CreateAllInstancesOf().OrderBy(d => d.Name);
            var plugin1 = (INAudioDemoPlugin)demos.Last();
            var control1 = plugin1.CreatePanel();
            control1.Dock = DockStyle.Fill;
            panel1.Controls.Add(control1);


            var plugin2 = (INAudioDemoPlugin)demos.First();
            var control2 = plugin2.CreatePanel();
            control2.Dock = DockStyle.Fill;
            panel2.Controls.Add(control2);
        }

demos是项目下所有自定控件的集合,我这里删的大部分,只留下了一个录音和一个播放,所以只有last和first。

至此,录音和播放功能完全实现了,而后我的工作是希望将声音切割,判断依据是dB(分贝),

C#基于NAudio的声音识别(一)——录制与切割_第2张图片

就是上图中垂直的绿色条条和黄色图中的波形,达到某个值就切割出来,静音部分不要(做监听咯,懂的,没声音就pass掉)

void OnPostVolumeMeter(object sender, StreamVolumeEventArgs e)
        {
            // we know it is stereo
            volumeMeter1.Amplitude = e.MaxSampleValues[0];
            volumeMeter2.Amplitude = e.MaxSampleValues[1];

            if (e.MaxSampleValues[0] > 0.01 && StartOREnd == false)
            {
                
                StartOREnd = true;

                DateTime starttime = DateTime.ParseExact(this.labelCurrentTime.Text,"mm:ss:fff",null);
                int StartPoint = starttime.Minute * 60 * 1000 + starttime.Second * 1000 + starttime.Millisecond;
                this.richTextBox1.Text += "\r\n开始:" + StartPoint.ToString();
                CutInfo.Add(StartPoint);
            }
            if (e.MaxSampleValues[0] < 0.01 && StartOREnd == true)
            {
                
                StartOREnd = false;

                DateTime endtime = DateTime.ParseExact(this.labelCurrentTime.Text, "mm:ss:fff", null);
                int EndPoint = endtime.Minute * 60 * 1000 + endtime.Second * 1000 + endtime.Millisecond;
                this.richTextBox1.Text += "\r\n结束:" + EndPoint.ToString();
                CutInfo.Add(EndPoint);
            }


        }

我定义了一个CutInfo的list,坦白说这里的e.MaxSampleValuesde的值代表什么意思我没搞明白,但是有声音数值就大,没声音数值就小,所以我这就当时dB来用了,按照阈值定义起点和终点,用于后期的切割。

切割分两部分,mp3和wav,其中mp3直接就能按照时间进行切割,wav得换算为字节长度稍微麻烦一点点。

public static void TrimMp3File(string inputPath, string outputPath, TimeSpan? begin, TimeSpan? end)
        {
            if (begin.HasValue && end.HasValue && begin > end)
                throw new ArgumentOutOfRangeException("end", "end should be greater than begin");

            using (var reader = new Mp3FileReader(inputPath))
            using (var writer = File.Create(outputPath))
            {
                Mp3Frame frame;
                while ((frame = reader.ReadNextFrame()) != null)
                    if (reader.CurrentTime >= begin || !begin.HasValue)
                    {
                        if (reader.CurrentTime <= end || !end.HasValue)
                            writer.Write(frame.RawData, 0, frame.RawData.Length);
                        else break;
                    }
            }
        }

        public static void TrimWavFile(string inPath, string outPath, TimeSpan cutFromStart, TimeSpan cutFromEnd)
        {
            using (WaveFileReader reader = new WaveFileReader(inPath))
            {
                using (WaveFileWriter writer = new WaveFileWriter(outPath, reader.WaveFormat))
                {
                    int bytesPerMillisecond = reader.WaveFormat.AverageBytesPerSecond/1000;

                    int startPos = (int)cutFromStart.TotalMilliseconds * bytesPerMillisecond;
                    startPos = startPos - startPos % reader.WaveFormat.BlockAlign;

                    int endPos = (int)cutFromEnd.TotalMilliseconds * bytesPerMillisecond;
                    endPos = endPos - endPos % reader.WaveFormat.BlockAlign;
                    TrimWavFile(reader, writer, startPos, endPos);
                }
            }
        }

        private static void TrimWavFile(WaveFileReader reader, WaveFileWriter writer, int startPos, int endPos)
        {
            reader.Position = startPos;
            byte[] buffer = new byte[1024];
            while (reader.Position < endPos)
            {
                int bytesRequired = (int)(endPos - reader.Position);
                if (bytesRequired > 0)
                {
                    int bytesToRead = Math.Min(bytesRequired, buffer.Length);
                    int bytesRead = reader.Read(buffer, 0, bytesToRead);
                    if (bytesRead > 0)
                    {
                        writer.WriteData(buffer, 0, bytesRead);
                    }
                }
            }
        }

C#基于NAudio的声音识别(一)——录制与切割_第3张图片

我每段测试音频都念了五个音,测试MP3和WAV都成功,效果如下图:

C#基于NAudio的声音识别(一)——录制与切割_第4张图片

黄色波形图前5个波是一段,后面的是切割后单独放的5个。切割的目的是识别,下一篇写识别。

新开博客,目的为学习交流,本人QQ:273651820。

你可能感兴趣的:(C#基于NAudio的声音识别(一)——录制与切割)