文章目录

在Windows Phone 8上使用MediaElement的时候发现一个很奇怪的现象。按照MSDN的说法,MediaElement的Volume属性可以通过设置0~1之间的值来设置媒体音量(0为最小音量,1为最大音量),其默认值为0.5。然而在实际的使用中发现其默认值并不是如MSDN中所说的0.5而是1,且音量并不随着Volume从0到1均匀变化。在0~0.7之间声音很小几乎听不见,0.7之后开始有明显的声音,且在0.75~1.0这个区间内改变Volume的值音量随Volume的变化明显。

由此看来,音量(z)随Volume属性的值(y)的变化的函数z = g(y)的函数图像近似于指数函数图像沿y轴负方向移动一个单位(因为g(0) = 0)。对于用户来说通过滑动一个范围为0~1的音量条来调节音量,通常用户所期望的是音量(z)能随着音量条的值(x)以一次函数z=h(x)均匀变化,而非以指数函数变化。

由于要改变MediaElement的音量只能通过设置Volume属性来达到,因此需要找到一个合适的转换函数f(x) = y满足z = g(y) = g(f(x)) = h(x) = kx,(其中k为大于1的常数)。

因为g(y)的图像近似于指数函数图像沿y轴负方向移动一个单位,所以设g(y)=a^y – 1 = (e^(lna))^y – 1 = e^(ylna) – 1。
因为y的定义域为[0,1],所以z = h(x) = g(y)的值域为[0,e^lna – 1]。
因为h(x) = kx,且k>1,x的定义域为[0,1]。所以h(1) = e^(lna) – 1 = k。即h(x) = x(e^(lna) – 1)。
因为 h(x) = g(y)
=> x(e^(lna) – 1) = e^(ylna) – 1
=> x(e^(lna) – 1) + 1 = e^(ylna)
=> x(e^(lna) – 1) + 1 = e^(f(x)lna)
两边以e为底取对数
=> ln(x(e^(lna) – 1) + 1) = f(x)lna
=> f(x) = ln(x(e^(lna) – 1) + 1) / lna
令lna = c
=> f(x) = ln(x(e^c – 1) + 1) / c

至此便求得函数f(x),要确定常量c的值,需能够测得和量化音量z的最大值zmax。ln(zmax)即为c。若不具备此条件可以通过尝试不同的常数来得到一个较为合适的值。本文以c = 9.21045为例,对应的zmax约为10000。

1
2
3
4
5
6
#include <iostream>
#include <cmath>
#include <iomanip>

const double e = 2.7182818;
const double c = 9.21045; //lna

f(x)和g(x)的函数定义如下:

1
2
3
4
5
6
7
8
9
double g(double y)
{

return std::pow(e, y * c) - 1;
}

double f(double x)
{

return std::log(x * (std::pow(e, c) - 1) + 1) / c;
}

以x = 1.0 0.9 0.8 … 0.1 0.0求g(f(x))进行验证。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int main()
{

std::cout << std::fixed;
for(int i = 10; i >= 0; --i) {
auto x = static_cast<double>(i) / 10;
std::cout << "x = "
<< x
<< " f(x) = "
<< f(x)
<< " g(f(x)) = "
<< g(f(x))
<< std::endl;
}
return 0;
}

输出

1
2
3
4
5
6
7
8
9
10
11
x = 1.000000 f(x) = 1.000000 g(f(x)) = 10000.094412
x = 0.900000 f(x) = 0.988562 g(f(x)) = 9000.084980
x = 0.800000 f(x) = 0.975775 g(f(x)) = 8000.075548
x = 0.700000 f(x) = 0.961280 g(f(x)) = 7000.066114
x = 0.600000 f(x) = 0.944546 g(f(x)) = 6000.056679
x = 0.500000 f(x) = 0.924754 g(f(x)) = 5000.047242
x = 0.400000 f(x) = 0.900532 g(f(x)) = 4000.037803
x = 0.300000 f(x) = 0.869307 g(f(x)) = 3000.028361
x = 0.200000 f(x) = 0.825303 g(f(x)) = 2000.018916
x = 0.100000 f(x) = 0.750101 g(f(x)) = 1000.009465
x = 0.000000 f(x) = 0.000000 g(f(x)) = 0.000000

从输出可以看出所求得的函数f(x)使得音量z随着x以一次函数均匀变化。

在Windows Phone 8中应用这种转换。
XAML

1
2
<MediaElement x:Name="mediaElement"/>
<Slider Minimum="0.0" Maximum="1.0" ValueChanged="OnVolumeSliderValueChanged"/>

C#

1
2
3
4
5
private void OnVolumeSliderValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{

const double c = 9.21045;
this.mediaElement.Volume = Math.Log(e.NewValue * (Math.Pow(Math.E, c) - 1) + 1) / c;
}

文章目录