Mobile Apps

Adjust Your Ring Volume For Ambient Noise

Chris Mitchell

This article discusses:

  • Pocket PC development
  • Measuring loudness
  • Custom installers
  • Using P/Invoke
This article uses the following technologies:
.NET Compact Framework

Code download available at:NoiseDetection2007_10.exe(193 KB)

Contents

The Approach
Reference Values
The Implementation
How to Handle Installation
Results

You're sitting in the local coffee shop on a sunny afternoon, peacefully enjoying a quiet moment, when your phone starts to ring. It's set, of course, to full volume and the ring is so loud you nearly spill your coffee. So you turn it down.

Later that day, you're waiting at a bar for a friend, but he's nowhere to be seen. He'd forgotten which bar you were meeting at and called to see where you were. Unfortunately, you missed the call because you didn't hear your phone ring. The problem: the ring volume was still turned down and wasn't loud enough to hear in the noisy bar.

This is fairly ridiculous when you consider that current mobile phones have as much processing power as the first PC I used for multi-channel audio recording. It would seem that with today's intelligent mobile devices, which are capable of doing so many things and accessing so much information, this problem should not exist. However, everyone who has a mobile phone has experienced this problem in one form or another.

The obvious way to solve the problem is to remember to change your volume settings. The option to change the ring volume is presumably to deal with the acoustic differences in various environments, but the functionality does not really do that. Devices should be able to act with a little more common sense. It's simple: I just want my mobile phone not to ring too loudly in a quiet environment and to ring audibly in a loud place.

This article addresses the possibilities and practicality of a Windows Mobile® application that automatically adjusts the Pocket PC's ring level based on the ambient noise in its current environment. The functionality of this app prevents the phone from ringing too loudly or softly, saving me from embarrassment and missed calls. In this article, I'll use this problem as an introduction to Windows Mobile development using Visual Studio® 2005 and C#.

Embarrassed by your phone ringing too loudly?

Embarrassed by your phone ringing too loudly?  

The Approach

The plan is to use the sensors of a mobile phone to obtain a better understanding of the current environment. I want to use the microphone to measure environmental noise and then adjust the ring level accordingly.

I need to address a series of specific problems to make this work, as Figure 1 indicates. How do I obtain audio samples from the microphone that is built into the device? What measure should I use to define loudness? How do I change the ring level of the mobile device in response to this information? I'll discuss the first part of the system, collecting environmental information from a microphone, in a moment. First, however, I need to define a measure of loudness.

Figure 1 High-Level Steps to Adjusting Ring Volume

Figure 1** High-Level Steps to Adjusting Ring Volume **

Loudness is subjective and is typically represented with an equal-loudness chart. (For more information on the subjective perception of sound, I recommend you look up some articles on the study of psychoacoustics.) These plots have frequency on one axis and a measure of sound pressure on another, normally in decibels (dB). The charts display the relationship between frequency and our perception of equal loudness.

To determine the relationship values, studies presented individuals with tones that increased in 10dB increments at various frequencies. Study participants were asked to judge when the sounds were of equal loudness compared to a 1KHz reference signal. These relationships were then used to construct tables and plot the equal-loudness chart, which could be used, together with frequency information obtained from the phone's microphone, to create a measure of loudness.

Another approach is to compare a measure of the sound pressure level to reference values and adjust the ring level if a substantial change occurs. What constitutes a change can then be defined by the profile of the phone. This overcomes problems with variations among microphones and speakers on different phones. Simply changing reference values enables the software to operate effectively with various phone models. This also means the system doesn't have to obtain frequency information from the microphone, saving valuable battery power.

Design complexity in the system is simplified through the redundancy of frequency decomposition and dB measurement. However, a measure of relative loudness is required for my application. In audio signal processing, root-mean-square (RMS) can be used as a rudimentary measure of loudness. (I'll explain RMS in more detail when I describe the implementation stage.) Although the frequency attributes still affect the perceived loudness, the RMS values should be sufficient for accuracy needs—this is especially true considering Pocket PCs have only five volume settings. The RMS function can be used to obtain values necessary to adjust ringing volume.

Reference Values

Once the samples are obtained from the microphone and fed into the RMS function, the output needs to be mapped to one of five volume states. What is the mapping function and how do the settings relate to reference values discussed earlier? The values are specific to each device and define when the ringing volume of the mobile phone changes. For example, the ring level should adjust appropriately when the RMS function indicates the environment is similar to a coffee shop or a noisy bar.

If I were going to scale this application up to support multiple device profiles, I would provide an interface for setting up the application. For this article, however, I am not going to implement such an interface; I will leave that up to someone else.

The process for generating reference values for a device profile is not very complicated. First, you use the RMS reporting capabilities of the application to obtain values in multiple reference situations suited to your needs. I used four common environments: a residential area at night (45dB); a quiet indoor restaurant or office (55dB); a typical indoor restaurant (65dB); and busy traffic about 16 feet away (75dB). These values will be the references for mapping to the five states of volume on a Pocket PC. (Obviously, a tool that can be used to obtain the various RMS values in these locations is needed.)

Then, once you have obtained an average value for all of your locations, you will use these values as upper and lower limits in the application's code.

The Implementation

I chose to build my solution in Visual C#® 2005, due to the integrated mobile development platform. This has made Pocket PC development a lot easier, although there is still no built-in wizard to create a desktop installer that can deploy your Pocket PC application via the desktop. There are some good free tools that fill the installer and there are methods for creating your own custom installer. (I'll discuss building a custom installer later in this article.)

For now, I want to get back to the application, which I'll name TooLoud. There are three parts to creating my application:

  1. To capture and process audio, I'll use the P/Invoke service of the Microsoft® .NET Compact Framework.
  2. I'll write a function to calculate RMS values.
  3. I'll write a function to access and set the volume using the registry on the Pocket PC, given an average RMS value.

You can use a number of libraries and ActiveX® controls to access the microphone to record the audio from a Pocket PC device. My example builds on the P/Invoke library (see msdn2.microsoft.com/aa446550.aspx), which includes a WaveIn class for accessing a device's microphone. This class contains a structure capable of storing samples at a set rate and bit resolution. The P/Invoke library also provides a helper class named Wave, which is a common interface to the .wav audio format. The above link provides a list of all implemented classes and functions in the original WaveIn and Wave classes.

The version of the P/Invoke library included in my solution only contains the WaveIn, Memory, and Registry classes. All other classes have been removed for the sake of brevity. The library shown in Figure 2 has been extended by providing extra functions: one in WaveIn (TooLoud)—AutoSetVolume—and the other in Wave—CalcRMS.

Figure 2 Part of the P/Invoke Library Displayed with TooLoud's Class Diagram

Figure 2** Part of the P/Invoke Library Displayed with TooLoud's Class Diagram **
(Click the image for a larger view)

The application runs AutoSetVolume, which calls CalcRMS as part of its procedure to set the volume. TestProc can be called repeatedly or just once depending on the mode selected in the user interface. TestProc calls six functions:

  • AutoSetVolume is used to achieve the end goal.
  • NumDevices gets the number of input devices.
  • GetDeviceName gets the name of the specified recording device.
  • Preload preloads the buffers for a given size and maximum amount of audio in milliseconds. (Start initiates the recording time, currently set to one second, and Stop ends the current recording.)
  • AutoSetVolume calculates the new volume setting.
  • Dispose clears the buffers.

Preload sets up an instance of a waveformatex, a header to describe the data, with a Pulse Code Modulation (PCM) format tag, 8-bit sample resolution, a single channel, and a sampling rate of 11025 Hz.

Now that I can obtain blocks of audio data for a period of time, I can use this data to calculate an RMS value. The calculation for root mean square is defined in Figure 3.

Figure 3 Part of the Loudness Calculation

Figure 3** Part of the Loudness Calculation **(Click the image for a larger view)

The corresponding code, shown in Figure 4, can now be presented to define the method of calculating the RMS values using the sample values from the microphone. Due to the sample values being stored in byte format (0-255), the midpoint of 127.5 is subtracted to provide -127.5 to +127.5 normalized audio signals.

Figure 4 RMS Calculation

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);
}

The mapping function takes the RMS value and defines the value that will be used to set the Pocket PC's current ring level. Although this could be done more empirically, a simple profile of a mobile phone's capabilities will do for the demonstration.

As I discussed, the disadvantage of this technique is that a profile will probably be needed for each model of phone that has significantly different speaker and microphone capabilities. You can solve this problem by creating a configuration interface that lets the user set reference values for various locations, or you could provide a Web site that serves up the various profiles. Here, I will only include the means to input one set of reference values. With this in mind, however, I'll show you how to obtain the mapping values so they can be reproduced for any phone's profile.

The RMS values were generated by recording a number of common sound environments associated with certain dB levels. Ideally, this should be done over a substantial period of time (in case, say, somebody drops a plate in the coffee shop at the exact moment you're sampling), and it should include the typical environments where you expect your phone to be. The average reading from each environment can then be used as the upper and lower limits for volume setting.

Once the reference values have been calculated, an adaptive profile must be constructed. This simply defines how the mobile phone will adapt its ring level according to the values. There are many different strategies you can consider. You could, for example, even have vibrate turn on if the phone is in a very noisy environment. For my implementation, I will simply have the reference values map the microphone input onto the five volumes.

The ring level of a Pocket PC can be set using the registry. The registry value is in HKEY_CURRENT_USER under ControlPanel\SoundCategories\Ring, and the value to change is InitVol, which has a range from 0 to 5.

The code in Figure 5 can be used to set the ring volume values using the standard registry write functions. The mapping decides on the right value for the ringer given an upper and lower limit, implemented as several conditional statements.

Figure 5 Setting the Current Ring Level

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();
    }
}

How to Handle Installation

Now that I've created the TooLoud application code, I must pick from among several different options to create installers for the Pocket PC. You can transfer the application using the Windows Vista® Mobile Device Center if you're impatient and want to play with this right away—which is what I did. However, this is less than ideal for the end user. Another approach is to create a CAB file and then create a Microsoft Installer (MSI) installer that can be run from the PC to set up the application on the Pocket PC. Once the CAB file has been generated, you can use a program that allows you to quickly generate a setup file complete with end-user license. But you may prefer to build a custom installer to handle the deployment to the mobile phone from the desktop, offering more sophisticated techniques. The end of this section looks at how to create a custom installer, a smart device CAB file, and a deployment project using Visual C# 2005. If you have Visual Studio 2003, you can find a guide for implementing similar functionality at msdn2.micro- soft.com/ms838273.

The complete project, which is available for you to download from the MSDN® Magazine Web site, contains four main components: the TooLoud application, SmartDeviceCab builder, CustomInstaller, and Setup installer.

In Visual Studio 2005, creating a smart device CAB is straightforward. To add a smart device CAB project to the solution, simply click on the solution in the Solution Explorer, select Add | New Project, select Other Project Types | Setup and Deployment, select the Smart Device CAB Project template, and click OK. Now in Solution Explorer, select the smart device Cab project you just added and add the primary project output from the TooLoud application.

There are several properties you can set for the SmartDeviceCab project; Figure 6 shows the properties page in Visual Studio 2005. While some of the properties are straightforward, a few are not. For instance, the OSversionMin property defines whether the application supports screen orientation awareness if set to 4.21. However, this value will prevent the application from installing on Pocket PCs based on Windows Mobile 2003 and earlier. For Pocket PC solutions based on Windows® 2003SE and earlier, the Compress property and NoUninstall device deployment property must be false. (Note that if you have the .NET Compact Framework 2.0 installed, this latter property can be set to True.)

Figure 6 SmartDeviceCab Properties

Figure 6** SmartDeviceCab Properties **(Click the image for a larger view)

The CE Setup DLL specifies the file that will run postinstall operations, such as configuring registry settings or installing the application as a service. This latter case is useful if, for example, you want the application to run in the background as a service making intelligent decisions about when the application runs.

Once the CAB file is built, it is capable of deploying the application to a mobile phone, but there is still the problem of how to trigger the CAB file to be copied and installed onto a mobile phone. So I will create a deployment project that will install the CAB file onto the system desktop and then trigger the application manager (see Figure 7). The application manager, which runs under the Windows Mobile Device Center, is the program on your desktop that controls the applications that are installed onto your mobile phone.

Figure 7 Mobile Device Center and Application Manager

Figure 7** Mobile Device Center and Application Manager **(Click the image for a larger view)

My deployment project is the same as the desktop deployment project contained within the Visual Studio templates. However, I must extend the functionality to include the task of launching the application manager to install the CAB file. I do this by creating a custom installer class to extend the normal installer. The custom installer class overrides two of the base class's methods, AfterInstall and AfterUninstall, and adds a method to launch the application manager. You can find the location of the application manager by accessing a registry value on the desktop machine.

Figure 7 shows the application manager (the foreground Window), which has been launched by my custom installer. The code for my custom installer is shown in Figure 8.

Figure 8 Custom Installer Code

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 :-(");
}
}

Figure 9 The Entire Solution

Figure 9** The Entire Solution **

Now the deployment project is created in the usual way, by adding a standard desktop setup project to the solution. After the deployment template has been added, I then add the custominstaller.dll and the smartdevicecab.cab to the setup project. To reference the custom installer, I right-click the setup project and select View | Custom Actions. Then I add the custominstaller.dll to the Install and Uninstall custom actions of the deployment project.

My deployment project needs one last file—the setup.ini file:

[CEAppManager] 
Version = 1.0 
Component = App [App] 
Description = TooLoud Pocket PC application. 
CabFiles = SmartDeviceCab.cab

This tells the application manager which CAB to load. Figure 9 shows the layout of my solution. (The build order should be set to TooLoud—SmartDeviceCab—CustomInstall—Setup.) My project can now be built and deployed using a desktop machine that has application manager on it.

Results

This article outlines a specific example I wrote for Visual Studio 2005 using version 1.x of the .NET Compact Framework. It demonstrates the underlying principles and general techniques involved in designing an application that will use the hardware on a Windows Mobile device to monitor the surrounding environment and automatically adjust the ring volume accordingly. Of course, this code can be refactored to use the latest technologies, such as version 2.0 of the .NET Compact Framework.

To see how well my app was working, I did both lab-based testing and environmental testing. The lab-based test was to ensure the phone would only ring 2-5dB louder than the environment (6dB is roughly twice as loud). The dB values were measured with a dB meter with a tone as the test signal. Environmental testing was done in two locations—in a coffee shop and in an airport in Los Angeles. I used a very scientific form of measurement for this test—I counted the number of people who turned around when my phone rang.

The application provides a sound approach (sorry, this pun is intended) to monitoring and adapting the ring level of a mobile phone. Like all applications, it does have its limitations, such as the need to configure the reference values for each model of mobile phone. Although this is not an unmanageable task, the current user interface doesn't support it. The other major limitation is that this approach consumes a lot of power. However, this can be easily solved by, say, having the process activate only when the phone is about to ring.

These issues aside, the application makes a lot of sense—more so, in my opinion, than the current approach of having a ring level that only changes when you manually alter the settings. There are still some improvements to be made, so feel free to start adapting the code and adding any functionality you think would be useful. I'm sure you can think of a lot of features and capabilities that I haven't discussed here.

I would like to thank Brian Richardson for the artwork at the opening of the article.

Chris Mitchell finished his PhD in machine learning and music/sound signal processing, and is currently a Kauffman/NCGE fellow. He was a part-time lecturer at Anglia Ruskin University, Cambridge, UK, which, along with some network consulting, helped finance his studies. You can contact Chris at c.mitchell at anglia.ac.uk. A passion for music technology led him to explore some of the fundamentals of audio programming, and he is always happy to hear about anything in this area.