windows phone 7中的多线程与windows中的多线程编程没什么区别,参照C#中thread类的用法,很容易实现多线程。
我遇到的问题是:wp7需要从音频口读取单片机传过来的数据(详见前面的博客),同时利用右声道向外播放声音(特定的波形),从而为单片机板子供电(供电这块还不知道好不好使,目前只是做手机这边,看能不能发出波形来)。
之前测试过了,录音的程序好使,需要做的就是在这个程序的基础上,利用thread类增加一个线程,使得程序启动之后就不断地向右声道发送波形!
首先:手机的音频口怎么向外发波形呢?我采用了最笨的办法,利用soundeffect的实例从声音buffer中读取数据并play(这一部分在录音全纪录那篇博客中讲的很清楚了)。而buffer中的数据是我自己构造的。声音经过麦克采集,AD转换之后,得到的原始数据实际上是麦克的电压值与32767~-32768之间的线性映射,在存储的时候,把每个数分成高8位和低8位,分别存储。我需要音频口输出方波,且要求峰峰值尽量的大,这样就可以组织如下的数组:
private byte[] wave = {255,127,255,127,255,127,255,127,255,127,255,127,255,127,255,127,
0,128,0,128,0,128,0,128,0,128,0,128,0,128,0,128,
255,127,255,127,255,127,255,127,255,127,255,127,255,127,255,127,
0,128,0,128,0,128,0,128,0,128,0,128,0,128,0,128}
其中,“255,127”实际上是一个值的低八位和高八位,即7FFF(32767),同理“0,128”是8000(-32768),很明显,之所以选这两个数是因为这两个数代表了最大和最小。这样就在输出时经过DA变换,出来的波形就是一个方波,上面的数组仅两个周期,一般要多得多。手机麦克的采样频率是16000Hz,那么如果要得到1KHz的方波,每个周期被采样16次,每个数又分为两个数来存,这样一共需要32000个数才能形成1秒钟的波形。当然我们不用真的写一个32000数的大数组,因为我们有更好的方法:多线程。
创建一个线程,使得程序在开始运行之后即不断地播放声音,如果声音buffer里的数很少,加入有64个(上面那个数组),则这个线程很快就会结束,这样设置一个定时器,设置很短的定时,比如33ms,没到定时器中断时,就检查这个线程的状态,如果已经结束,就再次创建并启动。循环往复,看上去就像是不断地播放波形!
在mainpage的构造函数最后加上以下语句
Thread energyThread = new Thread(new ThreadStart(energySound));//创建一个线程
energyThread.Start();//启动
energyIsPlaying = true;//将播放状态进行置位,目的是在定时器到时间时判断是否停下来了。
这是energySound的代码:
private void energySound()
{
Energy energy_output = new Energy();//Energy类实例化
energy_output.joint();
SoundEffect energy_sound = new SoundEffect((byte[])energy_output.target, microphone.SampleRate, AudioChannels.Mono);
energy_soundInstance = energy_sound.CreateInstance();
energyIsPlaying = true;
energy_soundInstance.Play();//播放
}
定时器的设置:
DispatcherTimer dt = new DispatcherTimer();
dt.Interval = TimeSpan.FromMilliseconds(33);//设置为33ms,每33ms就更新一次UI元素
dt.Tick += new EventHandler(dt_Tick);//当定时器到时间的时候,时间处理函数为dt_Tick
dt.Start();//启动定时器
dt_Tick的代码:
void dt_Tick(object sender, EventArgs e)
{
try
{
FrameworkDispatcher.Update();
}
catch { }
if (energy_soundInstance.State != SoundState.Playing)
{
energyIsPlaying = false;//如果能量播放已经结束了,就将播放标志置为false
}
if (energyIsPlaying == false)//一旦标志为false,就重新创建线程,重新启动,循环往复
{
Thread energyThread = new Thread(new ThreadStart(energySound));
energyThread.Start();
energyIsPlaying = true;
}
}
Energy类的代码:
public class Energy
{
private byte[] wave = {255,127,255,127,255,127,255,127,255,127,255,127,255,127,255,127,
0,128,0,128,0,128,0,128,0,128,0,128,0,128,0,128,
255,127,255,127,255,127,255,127,255,127,255,127,255,127,255,127,
0,128,0,128,0,128,0,128,0,128,0,128,0,128,0,128,
255,127,255,127,255,127,255,127,255,127,255,127,255,127,255,127,
0,128,0,128,0,128,0,128,0,128,0,128,0,128,0,128,
255,127,255,127,255,127,255,127,255,127,255,127,255,127,255,127,
0,128,0,128,0,128,0,128,0,128,0,128,0,128,0,128,
255,127,255,127,255,127,255,127,255,127,255,127,255,127,255,127,
0,128,0,128,0,128,0,128,0,128,0,128,0,128,0,128,
255,127,255,127,255,127,255,127,255,127,255,127,255,127,255,127,
0,128,0,128,0,128,0,128,0,128,0,128,0,128,0,128,
255,127,255,127,255,127,255,127,255,127,255,127,255,127,255,127,
0,128,0,128,0,128,0,128,0,128,0,128,0,128,0,128,
255,127,255,127,255,127,255,127,255,127,255,127,255,127,255,127,
0,128,0,128,0,128,0,128,0,128,0,128,0,128,0,128,
255,127,255,127,255,127,255,127,255,127,255,127,255,127,255,127,
0,128,0,128,0,128,0,128,0,128,0,128,0,128,0,128,
255,127,255,127,255,127,255,127,255,127,255,127,255,127,255,127,
0,128,0,128,0,128,0,128,0,128,0,128,0,128,0,128,
255,127,255,127,255,127,255,127,255,127,255,127,255,127,255,127,
0,128,0,128,0,128,0,128,0,128,0,128,0,128,0,128,
255,127,255,127,255,127,255,127,255,127,255,127,255,127,255,127,
0,128,0,128,0,128,0,128,0,128,0,128,0,128,0,128,
255,127,255,127,255,127,255,127,255,127,255,127,255,127,255,127,
0,128,0,128,0,128,0,128,0,128,0,128,0,128,0,128,
255,127,255,127,255,127,255,127,255,127,255,127,255,127,255,127,
0,128,0,128,0,128,0,128,0,128,0,128,0,128,0,128,
255,127,255,127,255,127,255,127,255,127,255,127,255,127,255,127,
0,128,0,128,0,128,0,128,0,128,0,128,0,128,0,128};
public byte[] Wave
{
get
{
return wave;
}
}
public void joint()
{
source = wave;
for (int cnt = 0; cnt < 660; cnt++)
{
source.CopyTo(target, cnt*480);
}
//return target;
}
Array source = Array.CreateInstance(typeof(byte), 480);
public Array target = Array.CreateInstance(typeof(byte), 320000);
}
这是我最开始写的代码,如上所述,没必要把这个数组整的这么大,写得小一点就行,后面的数值相应的改动。joint方法是为了获得一个更大更大的数组,呵呵。这看上去很笨,但很容易理解。
经过测试,在不影响录音和数据处理的情况下,实现了左右声道波形的输出。
存在的问题:
1、波形噪声很大。我在PC上制作了一个方波音频文件,同步到手机上用音乐播放器播放,示波器中观察到的波形非常干净、稳定。而我用这种方法播放出来的噪声却很大。
2、波形的幅值也很小。
3、左右声道都有波形,还不知道怎么设置。在soundEffect中用mono,是单声道,但测试的时候两个声道都有信号,另外,麦克里的信号对声道的影响也非常大,总之噪声非常大。