分贝显示器,实时显示声音强度(附源码)

使用 摄像头、麦克风、扬声器测试程序 一文中提到的技术,我们可以基本实现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程序

 源码下载

时间: 2024-11-07 09:27:17

分贝显示器,实时显示声音强度(附源码)的相关文章

自定义ProgressDialog实现暂时隐藏进度值并显示等待状态(附源码下载)

有时,我们需要访问网络才能获取到需要操作的任务数(例如下载的文件数),而在服务器返回任务数之前要想隐藏进度百分比和进度数值,就需要我们自己重写ProgressDialog.等到获取到任务数后再把进度值和百分比显示出来.先上效果图: 关键代码: public class CustomProgressDialog extends ProgressDialog { private final String TAG = this.getClass().getSimpleName(); public Cu

史上最强Android 开启照相或者是从本地相册选中一张图片以后先裁剪在保存并显示的讲解附源码

整个程序的布局很简单 只在一个垂直方向上的线性布局里面有俩个按钮(Button)和一个显示图片的控件(ImageView)这里就不给出这部分的代码了 1.是打开系统的相册 Intent albumIntent = new Intent(Intent.ACTION_PICK, null); albumIntent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*"); startActivityFo

Android 音视频深入 十八 FFmpeg播放视频,有声音(附源码下载)

项目地址https://github.com/979451341/AudioVideoStudyCodeTwo/tree/master/FFmpegv%E6%92%AD%E6%94%BE%E8%A7%86%E9%A2%91%E6%9C%89%E5%A3%B0%E9%9F%B3%EF%BC%8C%E6%9A%82%E5%81%9C%EF%BC%8C%E9%87%8A%E6%94%BE%E3%80%81%E5%BF%AB%E8%BF%9B%E3%80%81%E9%80%80%E5%90%8E 这个项

超详细的php用户注册页面填写信息完整实例(附源码)

这篇文章主要介绍了一个超详细的php用户注册页面填写信息完整实例,内容包括邮箱自动匹配.密码强度验证以及防止表单重复等,小编特别喜欢这篇文章,推荐给大家. 注册页面是大多数网站必备的页面,所以很有必要对自己的注册页面做些精心的设计.下面三张图,第一张是注册的展示页面,第二张思维导图就一个简单的逻辑,第三张是通过firebug查看调用的JS文件. 一.给每个输入框写下说明 在用户看到这个输入框的时候,就能非常清晰的明白这个输入框是做啥用的,最大限度的降低他们产生疑惑的可能性.我们需要假设用户毫不了

C#中的WinFrom技术实现串口通讯助手(附源码)

C#中的WinFrom技术实现串口通讯助手(附源码) ??实现的功能: 1.实现自动加载可用串口. 2.打开串口,并且使用C#状态栏显示串口的状态. 3.实现了串口的接收数据和发送数据功能. 4.串口使用定时器进行定时发送数据. 5.可以打开文件夹,选择文件进行发送,并且将发送文件的内容显示在发送文本框中. 6.可以清空发送和接收文本框中的内容. 7.可以实时计算发送和接收的字节数. 8.实现打开文件夹保存发送和接收的文件内容(目前只支持.txt文件). 9.实时显示当前时间. ??功能演示 1

一组网页边栏过渡动画,创意无限!【附源码下载】

今天我们想与大家分享另一套过渡效果.这一次,我们将探讨如何实现侧边栏的过渡动画,就像我们已经在多级推出菜单中使用的.我们的想法是,以细微的 过渡动画显示一些隐藏的侧边栏,其余的内容也是.通常侧边栏滑入,把其他内容推到一边.这个可过程中可以加入很多微妙而奇特的效果,而今天这篇文章能够给 你一些启示. 温馨提示:为保证最佳的效果,请在 IE10+.Chrome.Firefox 和 Safari 等现代浏览器中浏览. 立即下载      在线演示 因为我们希望能够在一个页面上展现所有的效果,因此我们示

创意无限!一组网页边栏过渡动画【附源码下载】

今天我们想与大家分享另一套过渡效果.这一次,我们将探讨如何实现侧边栏的过渡动画,就像我们已经在多级推出菜单中使用的.我们的想法是,以细微的过渡动画显示一些隐藏的侧边栏,其余的内容也是.通常侧边栏滑入,把其他内容推到一边.这个可过程中可以加入很多微妙而奇特的效果,而今天这篇文章能够给你一些启示. 温馨提示:为保证最佳的效果,请在 IE10+.Chrome.Firefox 和 Safari 等现代浏览器中浏览. 立即下载      在线演示 因为我们希望能够在一个页面上展现所有的效果,因此我们示例的

Android学习笔记(十四)——在运行时添加碎片(附源码)

在运行时添加碎片 点击获取源码 将UI分割为多个可配置的部分是碎片的优势之一,但其真正强大之处在于可在运行时动态地把它们添加到活动中. 1.使用上一篇创建的Fragments项目,在main.xml文件中注释掉两个<fragment>元素: 2.在FragmentActivity.java中添加下面的代码: FragmentManager fragmentManager = getSupportFragmentManager();//向活动添加碎片 FragmentTransaction fr

Android学习笔记(十五)——碎片的生命周期(附源码)

碎片的生命周期 点击下载源码 与活动类似,碎片具有自己的生命周期.理解了碎片的生命周期后,我们可以在碎片被销毁时正确地保存其实例,在碎片被重建时将其还原到前一个状态. 1.使用上一篇的项目Fragments,在Fragment1.java文件中添加如下代码: package net.zenail.Fragments; import android.app.Activity; import android.os.Bundle; import android.support.v4.app.Fragm