zoukankan      html  css  js  c++  java
  • 分贝显示器,实时显示声音强度(附源码)

    使用 摄像头、麦克风、扬声器测试程序 一文中提到的技术,我们可以基本实现QQ的语音视频测试向导的功能了。但是,我觉得语音测试这块的体验还可以做得更好一点,就像QQ语音测试一样,实时显示麦克风采集到的声音的强度:

           

    接下来,我们做个小demo,来实现类似的功能。先上demo运行起来的截图:

            

    (界面确实比较丑,我们这里的重点在于技术方面如何实现,如果你愿意花点时间,可以将其美化得跟QQ的那个一样漂亮^_^)

     

    1.实现思路

    实现这个小例子的主要思路如下:

    (1)使用OMCS采集和播放从麦克风的输入数据(PCM)。

    (2)对采集到的数据进行傅立叶变换,变换的结果就可以反应声音的强度。

    (3)使用ProgressBar控件来实时显示声音的强度信息。

    2.具体实现

    (1)傅立叶变换算法 

    复制代码
        public static class FourierTransformer
        {
            public static double[] FFTDb(double[] source)
            {          
                int sourceLen = source.Length;
                int nu = (int)(Math.Log(sourceLen) / Math.Log(2));
                int halfSourceLen = sourceLen / 2;
                int nu1 = nu - 1;
                double[] xre = new double[sourceLen];
                double[] xim = new double[sourceLen];
                double[] decibel = new double[halfSourceLen];
                double tr, ti, p, arg, c, s;
                for (int i = 0; i < sourceLen; i++)
                {
                    xre[i] = source[i];
                    xim[i] = 0.0f;
                }
                int k = 0;
                for (int l = 1; l <= nu; l++)
                {
                    while (k < sourceLen)
                    {
                        for (int i = 1; i <= halfSourceLen; i++)
                        {
                            p = BitReverse(k >> nu1, nu);
                            arg = 2 * (double)Math.PI * p / sourceLen;
                            c = (double)Math.Cos(arg);
                            s = (double)Math.Sin(arg);
                            tr = xre[k + halfSourceLen] * c + xim[k + halfSourceLen] * s;
                            ti = xim[k + halfSourceLen] * c - xre[k + halfSourceLen] * s;
                            xre[k + halfSourceLen] = xre[k] - tr;
                            xim[k + halfSourceLen] = xim[k] - ti;
                            xre[k] += tr;
                            xim[k] += ti;
                            k++;
                        }
                        k += halfSourceLen;
                    }
                    k = 0;
                    nu1--;
                    halfSourceLen = halfSourceLen / 2;
                }
                k = 0;
                int r;
                while (k < sourceLen)
                {
                    r = BitReverse(k, nu);
                    if (r > k)
                    {
                        tr = xre[k];
                        ti = xim[k];
                        xre[k] = xre[r];
                        xim[k] = xim[r];
                        xre[r] = tr;
                        xim[r] = ti;
                    }
                    k++;
                }
                for (int i = 0; i < sourceLen / 2; i++)
                {
                    decibel[i] = 10.0 * Math.Log10((float)(Math.Sqrt((xre[i] * xre[i]) + (xim[i] * xim[i]))));
                }
    
                return decibel;
            }
    
            private static int BitReverse(int j, int nu)
            {
                int j2;
                int j1 = j;
                int k = 0;
                for (int i = 1; i <= nu; i++)
                {
                    j2 = j1 / 2;
                    k = 2 * k + j1 - 2 * j2;
                    j1 = j2;
                }
                return k;
            }
        }
    复制代码

     至于傅立叶变换与分贝有什么关系,网上有很多相关的资料,可以baidu一下。对有兴趣的童鞋,强烈推荐阅读这篇文章 -- 分贝是个什么东西?

    (2)初始化OMCS服务器、设备管理器、麦克风设备 

    复制代码
            //获取麦克风列表
             IList<MicrophoneInformation> microphones = SoundDevice.GetMicrophones();
            this.comboBox2.DataSource = microphones;
            if (microphones.Count > 0)
            {
                this.comboBox2.SelectedIndex = 0;
            }
    
            //初始化OMCS服务器
             OMCSConfiguration configuration = new OMCSConfiguration(10, 1, EncodingQuality.High, 16000, 800, 600);
            this.multimediaServer = new MultimediaServer(9000, new DefaultUserVerifier(), configuration, false, null);
    
            this.multimediaManager.DeviceErrorOccurred += new CbGeneric<MultimediaDeviceType, string>(multimediaManager_DeviceErrorOccurred);
            this.multimediaManager.AudioCaptured += new CbGeneric<byte[]>(multimediaManager_AudioCaptured);
            this.microphoneConnector1.ConnectEnded += new CbGeneric<ConnectResult>(microphoneConnector1_ConnectEnded);
    复制代码

    (3)连接麦克风,开始采集

    复制代码
        if (!SoundDevice.IsSoundCardInstalled())
        {
            this.label_error.Visible = true;
            this.label_error.Text = "声卡没有安装";
        }
    
        //初始化多媒体管理器 
        this.multimediaManager.MicrophoneDeviceIndex = this.comboBox2.SelectedIndex;                  
        this.multimediaManager.Initialize("tester", "", "127.0.0.1", 9000); //与OMCS服务器建立连接,并登录
    
        //尝试连接麦克风              
        this.microphoneConnector1.BeginConnect("tester");
    复制代码

    首先,初始化本地多媒体设备管理器,然后使用麦克风连接器连接到当前登录用户“tester”(即“自己”)麦克风设备。如果连接成功,多媒体管理器将会触发AudioCaptured事件,我们通过这个事件来截获音频数据。

    (4)处理采集到的音频数据,并显示结果 

    复制代码
            void multimediaManager_AudioCaptured(byte[] data)
            {
                double[] wave = new double[data.Length / 2];
                int h = 0;
                for (int i = 0; i < wave.Length; i += 2)
                {
                    wave[h] = (double)BitConverter.ToInt16(data, i); //采样位数为16bit
                    ++h;
                }
    
                double[] res = FourierTransformer.FFTDb(wave);
    
                double kk = 0;
                foreach (double dd in res)
                {
                    kk += dd;
                }
                if (kk < 0)
                {
                    kk = 0;
                }
                this.showResult(kk / res.Length);
            }
            
            private void showResult(double rs)
            {
                if (this.InvokeRequired)
                {
                    this.BeginInvoke(new CbGeneric<double>(this.showResult), rs);
                }
                else
                {
                    int rss = (int)(rs * 2);
                       if (rss < 40)
                    {
                        rss = 40;
                    }
                    if (rss > 100)
                    {
                        rss = 100;
                    }
    
                    this.progressBar1.Value = rss;
                }
            }
    复制代码

    注意:由于OMCS音频采样的位数为16bit,这样,一个单位的语音样本的字节数为2个字节。所以,傅立叶变换前,先要将原始的PCM数据(byte[])转为Int16的数组。

    在显示分贝强度时,我偷了下懒,直接使用了ProgressBar控件,体验不是很好,勉强能表达出意思吧。

    3.Demo程序

        源码下载


  • 相关阅读:
    An error happened during template parsing (template: "class path resource [templates/index.html]")(有效转)
    Netty线程模型及EventLoop
    社交网络大时代背景下的店铺社交群
    Shiro
    从n个数中取出m个最大数(复杂度最低)的最好的算法是什么?
    Shiro(基本)
    Shiro(转)
    一:Spring Boot 的配置文件 application.properties
    Spring Boot 基础,理论,简介
    收集整理Idea常用配置及插件
  • 原文地址:https://www.cnblogs.com/riskyer/p/3283581.html
Copyright © 2011-2022 走看看