技术开发 频道

移动应用程序 调整铃声音量,避免环境噪音

 【IT168技术文档】在某个阳光明媚的午后,您坐在当地一家咖啡馆里,心情平和地享受着宁静的时光,这时,您的电话响了。而且它被设置成最大音量,铃声如此之响,差点让您把咖啡打翻。所以您将声音调小了。

 那天晚些时候,您在酒吧等一位朋友,但是他迟迟不来。实际上,他是忘记你们要见面的酒吧了,并打电话询问您在什么地方。不幸的是,您没有接到电话,因为您没有听见电话铃声。原来:铃声音量仍然处在之前调得很小的状态,而在嘈杂的酒吧里面根本听不见这么小的铃声。

 如果您认为现在的移动电话处理功能可以与我用于多声道音频录音的第一台 PC 相媲美,那么这将是非常可笑的想法。如今的智能移动设备看起来拥有诸多功能,能访问诸多信息,因此这种问题不应该存在。但是,拥有移动电话的人都或多或少遇到过这种问题。

 要解决该问题,显而易见的方法是记得更改音量设置。更改铃声音量的一个方案可能是区别对待不同环境中的声音差异,但实际功能并没有做到这一点。这些设备的反应能力应该多结合一点常识。其实很简单:我只要我的移动电话在安静的环境中不要发出太响的铃声,在嘈杂的地方又能够让我听得见铃声。

 本文将探讨 Windows Mobile? 应用程序根据其当前环境中的环境噪音自动调整 Pocket PC 的铃声级别的可能性和实用性。此应用程序的功能将防止电话铃声太响或太轻,从而避免我陷入尴尬的境地和错过电话。在本文中,我将从此问题切入,着手介绍使用 Visual Studio 2005 和 C# 进行 Windows Mobile 开发。

 电话铃声太响使您陷入尴尬?

 解决方法

 办法是利用移动电话的传感器获取对当前环境的更好了解。我想使用麦克风来测量环境噪音,然后相应地调整铃声水平。

 我需要解决一系列具体问题才能使此方法行得通,如图 1 所示。我如何从内置于该设备的麦克风中获取音频采样呢?我该使用什么度量来定义响度?我该如何更改移动设备的铃声水平对这一响度作出反应?我将马上讨论此系统的第一部分,即从麦克风中收集环境信息。但是首先,我需要定义响度的度量。

 图 1 调整铃声音量的高级步骤

 响度是主观性的,通常由等响度图表表示。(有关声音主观感知的详细信息,我建议您查询一些有关心理声学研究的文章。) 这些图的一个轴上是频率,另一个轴上是声压度量,通常以分贝 (dB) 为单位。这些图表显示了频率和人们对等响度的感知之间的关系。

 为了确定关系值,科学研究向参与者提供了很多音调,这些音调按不同频率,以 10dB 递增。研究参与者被要求判断何时声音具有与 1KHz 参考信号相比的等响度。然后,这些关系被用来构造表格和绘制等响度图表,它们与从电话麦克风中获取的频率信息一起,可用于创建响度的度量。

 另一个方法是将声压水平的度量与参考值进行比较,并在发生重大变化时调整铃声水平。然后可以通过电话的配置文件来定义构成变化的条件。该方法克服了不同电话上不同麦克风和扬声器之间存在差异这个问题。只需更改参考值即可使软件能够针对各种电话型号有效地操作。这还意味着系统不必从麦克风中获取频率信息,从而节省宝贵的电池电量。

 系统中的设计复杂性通过频率分解和 dB 测量的冗余得到了简化。但是,我的应用程序必须使用相对响度的度量。在音频信号处理中,可以使用均方根 (RMS) 作为响度的基本度量。(我将在说明实现阶段时详细介绍 RMS。) 尽管频率属性仍然影响着感知到的响度,但是对于准确性需求来说,RMS 值应该已经足够了——鉴于 Pocket PC 只有五个音量设置,那更是如此。RMS 函数可用于获取调整铃声音量所必需的值。

 参考值

 一旦从麦克风中获取采样并输入到 RMS 函数,输出必须映射到五个音量状态之一。什么是映射函数?这些设置如何与上文讨论的参考值关联起来?参考值对于每个设备都是特定的,它们用于定义何时移动电话铃声音量发生变化。例如,当 RMS 函数指出环境类似于某个咖啡馆或嘈杂的酒吧时,铃声水平应进行相应调整。

 如果我打算扩展此应用程序,以支持多个设备的配置文件,我将提供一个界面以设置该应用程序。但是在本文中,我并未打算实现此类界面,让其他人来做吧。

 为某个设备配置文件生成参考值的过程并不是很复杂。首先,使用应用程序的 RMS 报告功能获取适合您需要的多种参考情况下的参考值。我使用的是四种常见环境:晚间的住宅区 (45dB)、安静的室内餐厅或办公室 (55dB)、普通的室内餐厅 (65dB) 及约 16 英尺远的繁忙的交通区 (75dB)。这些值将是映射到 Pocket PC 上五个音量状态的参考。(很明显,我们需要一种工具来获取这些地点的各种 RMS 值。)

 接着,在您得到所有这些地点的平均值后,您将使用这些值作为应用程序代码中的上限和下限。

 实现

 由于 Visual C#? 2005 具有集成的移动开发平台,因此我选择它来构建我的解决方案。虽然仍旧没有用于创建桌面安装程序(可通过桌面部署 Pocket PC 应用程序)的内置向导,但这已经大大简化了 Pocket PC 的开发。市面上有一些优秀的免费工具可充当安装程序,也有一些方法可供您创建自定义的安装程序。(我将在本文稍后讨论如何构建自定义安装程序。)

 现在,让我们回到应用程序,我会将它命名为 TooLoud。创建这个应用程序须由三个部分组成:

 捕获和处理音频,我将使用 Microsoft? .NET Compact Framework 的 P/Invoke 服务。

 我将编写一个函数来计算 RMS 值。

 如果给定了平均 RMS 值,我将编写一个函数,使用 Pocket PC 上的注册表来访问和设置音量。

 您可以使用许多库和 ActiveX? 控件来访问麦克风,以记录 Pocket PC 设备的音频。我的示例构建在 P/Invoke 库(请参阅 msdn2.microsoft.com/aa446550.aspx)之上,该库包含用于访问设备麦克风的 WaveIn 类。此类包含一个结构,能够以设定的速率和位分辨率存储采样结果。P/Invoke 库还提供名为 Wave 的帮助器类,这是到 .wav 音频格式的一个通用接口。上述链接提供了在原始 WaveIn 和 Wave 类中所有已实现类和函数的列表。

 在我的解决方案中,P/Invoke 库的版本仅包含 WaveIn、Memory 和 Registry 类。为了简洁起见,所有其他类均已删除。图 2 中所示的库已通过提供额外函数而得到了扩展:一个函数位于 WaveIn (TooLoud)—AutoSetVolume—另一个函数位于 Wave—CalcRMS。

 图 2 与 TooLoud 类关系图一同显示的部分 P/Invoke 库

 应用程序运行 AutoSetVolume,后者调用 CalcRMS 作为其过程的一部分以设置音量。TestProc 可重复调用,或只调用一次,具体取决于用户界面中选择的模式。TestProc 调用六个函数:

 AutoSetVolume 用于实现最终目标。

 NumDevices 用于获取输入设备的数量。

 GetDeviceName 用于获取指定录音设备的名称。

 Preload 为给定大小和最大的音频数量(以毫秒计)预加载缓冲区。(Start 用于初始化录音时间,目前设置为一秒,Stop 用于结束当前录音。)

 AutoSetVolume 用于计算新的音量设置。

 Dispose 用于清除缓冲区。

 Preload 将设置 waveformatex 的实例、用于说明数据的标头、脉冲编码调制 (PCM) 格式标签、8 位采样分辨率、单一声道和 11025 Hz 的采样率。

 既然我能获取一段时间的音频数据块,那么我可以使用此数据来计算 RMS 值。均方根的计算见图 3 中的定义。

 图 3 响度计算的一部分

 图 4 中所示的相应代码现在可用于定义使用从麦克风中获取的采样值来计算 RMS 值的方法。由于采样值以字节格式 (0-255) 存储,因此,减去中间值 127.5 可提供 -127.5 到 +127.5 的规范化音频信号。

 Figure 4 RMS 计算

 public double CalcRMS(MainTest.DisplayLineDelegate ShowLine)

 {

 byte[] data = new byte[dwBytesRecorded];

 Marshal.Copy(lpData, data, 0, data.Length);

 double temp = 0, sum = 0;

 int j = 0;

 ShowLine(data.Length.ToString());

 for (int i = 0; i < data.Length; i++)

 {

 temp = (double)data[i] - 127.50;

 sum = sum + (temp * temp);

 j++;

 }

 return Math.Sqrt(sum / j);

 }

 映射函数取 RMS 值并定义将用于设置 Pocket PC 当前铃声水平的值。虽然完成它需要更多的经验,但是移动电话功能的简单配置文件对演示就足够了。

 正如我先前所述,此技术的缺点在于,扬声器和麦克风功能有显著差异的每种电话型号都将可能需要一个配置文件。解决这个问题的办法是通过创建配置界面,让用户设置适合各种地点的参考值,或者可以提供一个网站,在上面提供各种配置文件。在本文中,我将仅介绍输入一组参考值的方法。然而,怀着这个目的,我将向您介绍如何获取映射值,以便可以为任何电话的配置文件复制它们。

 RMS 值是通过录制许多与特定 dB 水平关联的常见声音环境而生成的。理论上,这项录制工作须经过很长一段时间才能完成(例如,有人在咖啡馆掉了一个盘子,而此时您正在采样),而且它应包括您认为可能需要接电话的普通环境。然后,可以将每种环境的平均读数用作音量设置的上限和下限。

 一旦参考值计算好,就必须即刻创建自适应配置文件。这只是定义了移动电话将如何基于不同的值来自调铃声水平。您可以考虑许多其他策略。例如,如果电话处于一个非常吵闹的环境中,您甚至可以开启振动。对于我的演示,我将仅用参考值将麦克风输入映射到五个音量。

 可以使用注册表来设置 Pocket PC 的铃声水平。注册表值位于 ControlPanel\SoundCategories\Ring 下的 HKEY_CURRENT_USER,要更改的值是 InitVol,该值范围是 0 到 5。

 代码使用标准注册表写函数来设置铃声音量值。给定了上限和下限后,映射可以确定铃声的适当值,具体实现为若干个条件语句。

 public Wave.MMSYSERR AutoSetVolume(MainTest.DisplayLineDelegate ShowLine)

 {

 double AverageRMS = 0.00;

 if (!m_inited)xreturn Wave.MMSYSERR.ERROR;

 if (m_recording) Stop();

 try

 {

 // Write the data recorded to each buffer.

 // Get RMS value for each block and take the average

 // across all blocks.

 for (int i = 0; i < m_numBlocks; i++)

 {

 if (m_whdr[i] != null)

 {

 AverageRMS = AverageRMS + m_whdr[i].CalcRMS(ShowLine);

 }

 }

 AverageRMS = AverageRMS / m_numBlocks;

 ShowLine("Average RMS: " + AverageRMS);

 // Select volume based on reference values.

 uint Volume = 0;

 if (AverageRMS > 35.00)

 {

 //Range of this value for the volume control is from

 //0x0000 (min) non to 0x0005 (max).

 Volume = 5;

 }

 else if ((AverageRMS <= 35.00) && (AverageRMS > 30.00))

 {

 //Range of this value for the volume control is from

 //0x0000 (min) non to 0x0005 (max).

 Volume = 4;

 }

 else if ((AverageRMS <= 30.00) && (AverageRMS > 20.00))

 {

 //Range of this value for the volume control is from

 //0x0000 (min) non to 0x0005 (max).

 Volume = 3;

 }

 else if ((AverageRMS <= 20.00) && (AverageRMS > 10.00))

 {

 //Range of this value for the volume control is from

 //0x0000 (min) non to 0x0005 (max).

 Volume = 2;

 }

 else

 {

 //Range of this value for the volume control is from

 //0x0000 (min) non to 0x0005 (max).

 Volume = 1;

 }

 ShowLine("Set Volume to: " + Volume.ToString());

 PInvokeLibrary.Registry.CreateValueDWORD(

 "ControlPanel\\SoundCategories\\Ring", "InitVol",

 Volume);

 return Wave.MMSYSERR.NOERROR;

 }

 catch { return Wave.MMSYSERR.ERROR; }

 finally { FreeWaveBuffers(); }

 }

 如何处理安装

 现在我已经创建了 TooLoud 应用程序代码,要为 Pocket PC 创建安装程序,我必须从若干不同选项中作出选择。如果您是急性子,想要立即尝尝鲜(我当初就是这样),那么您可以使用 Windows Vista? Mobile 设备中心来传输此应用程序。但是,这对于最终用户而言并非是理想做法。另外一种方法是创建 CAB 文件,然后创建 Microsoft Installer (MSI) 安装程序,该安装程序可以从 PC 运行,以便在 Pocket PC 上安装此应用程序。一旦 CAB 文件生成后,您便可以使用一个程序快速生成安装文件,并配备最终用户许可证。但是,您可能更愿意构建一个自定义安装程序,以便从桌面处理对移动电话的部署,从而提供更高级的技术。本部分的结尾将探讨如何使用 Visual C# 2005 来创建自定义安装程序、智能设备 CAB 文件以及部署项目。如果您拥有 Visual Studio 2003,则可以在 msdn2.micro- soft.com/ms838273 找到一份关于实现类似功能的指南。

 《MSDN? 杂志》网站上可供您下载完整的项目,其中包括四个主要组件:TooLoud 应用程序、SmartDeviceCab 生成器、CustomInstaller 和 Setup 安装程序。

 在 Visual Studio 2005 中,创建智能设备 CAB 的过程很简单。要将智能设备 CAB 项目添加到解决方案,只需单击“解决方案资源管理器”中的解决方案,然后依次选择“添加”|“新项目”、“其他项目类型”|“安装和部署”、“智能设备 CAB 项目”模板,然后单击“确定”即可。现在在“解决方案资源管理器”中,选择您刚刚添加的智能设备 Cab 项目,然后添加 TooLoud 应用程序的主项目输出。

 您可以为 SmartDeviceCab 项目设置多个属性;图 6 显示了 Visual Studio 2005 中的属性页。虽然有些属性很简单,但有些则并非如此。例如,OSversionMin 属性如果设置为 4.21,则定义应用程序是否支持屏幕方向感知。但是,此值将不允许在基于 Windows Mobile 2003 和早期版本的 Pocket PC 上安装应用程序。对基于 Windows? 2003SE 和早期版本的 Pocket PC 解决方案来说,Compress 属性和 NoUninstall 设备部署属性必须为 false。(请注意,如果您安装了 .NET Compact Framework 2.0,则 NoUninstall 设备部署属性可以设置为 True。)

 图 6 SmartDeviceCab 属性 (单击该图像获得较大视图)

 CE Setup DLL 指定了运行安装后操作的文件,这些操作包括配置注册表设置或将应用程序安装为服务等。这后一种情况非常有用,例如,您希望应用程序在后台作为服务运行,能够做出何时运行此应用程序的智能决定。

 构建 CAB 文件后,它能够将此应用程序部署到移动电话上,但是关于如何触发复制 CAB 文件并将其安装到移动电话上仍然存在问题。因此,我将创建一个部署项目,该项目会将 CAB 文件安装到系统桌面,然后触发应用程序管理器(参见图 7)。应用程序管理器在 Windows Mobile 设备中心下运行,它是您桌面上的程序,控制安装到移动电话的应用程序。

 图 7 Mobile 设备中心和应用程序管理器

 我的部署项目与 Visual Studio 模板中所含的桌面部署项目相同。但是,我必须扩展功能,以包括启动应用程序管理器以安装 CAB 文件的任务。我的实现方法是创建一个自定义安装程序类来扩展普通安装程序。该自定义安装程序类会重写两个基类方法 AfterInstall 和 AfterUninstall,然后添加一个方法以启动应用程序管理器。您可以通过访问台式计算机上的注册表来查找应用程序管理器的位置。

 图 7 显示了已由我的自定义安装程序启动的应用程序管理器(前台窗口)。我的自定义安装程序的代码如图 8 所示。

 Figure 8 自定义安装程序代码

 private void Installer_AfterInstall(

 object sender, System.Configuration.Install.InstallEventArgs e)

 {

 // Get fullpath to .ini file.

 string arg = Path.Combine(

 Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location),

 "Setup.ini");

 // Run WinCE App Manager to install .cab file on device.

 RunAppManager(arg);

 }

 private void Installer_AfterUninstall(

 object sender, System.Configuration.Install.InstallEventArgs e)

 {

 // Run app manager in uninstall mode (without any arguments).

 RunAppManager(null);

 }

 private void RunAppManager(string arg)

 {

 // Get path to the application manager.

 const string RegPath = @"Software\Microsoft\Windows\" +

 @"CurrentVersion\App Paths\CEAppMgr.exe";

 RegistryKey key = Registry.LocalMachine.OpenSubKey(RegPath);

 string appManager = key.GetValue("") as string;

 if (appManager != null)

 {

 // Launch the application manager.

 Process.Start(string.Format("\"{0}\"", appManager),

 (arg == null) ? "" : string.Format("\"{0}\"", arg));

 }

 else

 {

 // Could not locate application manager.

 MessageBox.Show(

 "Could not launch the WinCE Application Manager :-(");

 }

 }

图 9 完整解决方案

 现在,部署项目是按普通方法创建的,即向解决方案添加一个标准桌面安装项目。添加了部署模板之后,我将 custominstaller.dll 和 smartdevicecab.cab 添加到安装项目。为了引用自定义安装程序,右键单击安装项目,然后选择“查看”|“自定义操作”。接下来,我将 custominstaller.dll 添加到部署项目的“安装”和“卸载”自定义操作中。

 我的部署项目需要最后一个文件——setup.ini 文件:

 [CEAppManager]

 Version      = 1.0

 Component    = App

 [App]

 Description  = TooLoud Pocket PC

 application.

 CabFiles     = SmartDeviceCab.cab

 

 它告诉应用程序管理器该加载哪个 CAB。图 9 显示了我的解决方案的布局。(生成顺序应设为 TooLoud—SmartDeviceCab—CustomInstall—Setup。) 现在,可以使用装有应用程序管理器的台式计算机构建和部署我的项目了。

 结束语

 本文概述了我使用 .NET Compact Framework 的 1.x 版本为 Visual Studio 2005 编写的一个特定示例。它说明了设计一个将使用 Windows Mobile 设备上的硬件来监控周围环境并相应自动调整铃声音量的应用程序时,所涉及的基本原则和一般技巧。当然,可以重构此代码以使用最新的技术,如 .NET Compact Framework 2.0 版本。

 为了检查这个应用程序的运行状况,我同时进行了基于实验室的测试和环境测试。基于实验室的测试是为了确保电话铃声仅超过环境噪音 2-5dB(6dB 大约是两倍响度)。分贝值是通过分贝计,利用音调作为测试信号测量出来的。环境测试在两个地点进行——洛杉矶的一个咖啡馆和一个机场。我为此测试采用了非常科学的测量方式——我记下了当我的电话响起时回头的人数。

 此应用程序提供了一种声音法/可靠的方法(抱歉,sound approach 这一双关语是有意使用的)来监控和自调移动电话的铃声水平。与所有应用程序一样,它也有自己的局限性,如需要为移动电话的每个型号配置参考值。尽管这不是一个不可管理的任务,但是当前用户界面并不支持它。另外一个主要限制是此方法会消耗很多电量。但是,耗电问题非常容易解决,例如,只在电话将要响起时才启用进程。

 除了这些问题,此应用程序比目前仅在手动更改设置后铃声水平才能发生变化的方法更具有意义,这在我看来尤其如此。但是,还是有一些需要改进的地方,因此请根据需要自调代码并添加您认为有用的任何功能。我肯定您能想到本文中尚未探讨的很多特性和功能。

查看原文地址

0
相关文章