Windows Phone 7 – 写一个会Record Audio的录音程序
过去在念书的时候总是会看到同学带著录音笔去参加meeting或是上课,担心会漏掉上课的种种内容,随着智能手机的发展,
有很多App都方便地透过内键的麦克风录下声音,不需在另外带一个设备来录音。然而录音的程序在Windows Mobile时代很容易做到,
透过一些内键的API与取得麦克风的Instance即可以完成功能,那么在WP7内要怎么实践呢?往下便仔细的来介绍有那些API可以使用。
〉Micorsoft.Xna.Framework.Audio:
在WP7里,要实践一个支持录音的程序,主要是透过Micorsoft.Xna.Framework.Audio里的Microphone类,
由于相关多媒体资源存取的功能都纳入在XNA系列里,相关图片、音乐、影片这些资源要使用到XNA的Dll就对了。
不过这有点扯远了,接下来在介绍撰写一个录音程序前,有几个重要的类我觉得要先认识一下:
a. Microphone Class:
该类提供了丰富的属性、方法与事件来透过手机麦克风来撷取声音。在使用前有几个重要的属性(均是只读):
类 | 名称 | 说明 |
属性 | All | 静态属性,回传目前设备可用的麦克风集合。例如:手机内键的麦克风、蓝牙耳机的麦克风或连接式耳麦。 |
Default | 静态属性,回传默认设备使用的麦克风类型。如果回传是null代表该设备不支持麦克风,默认麦克风即为手机的内键麦克风。 | |
BufferDuration | 设定/取得麦克风撷取声音时,录音数据写入缓冲区对应的容纳时间长度。换句话说,为撷取声音长度达到设定的缓冲大小时,代表已达容纳时间长度需触发BufferReady事件。 举个例子说明:当设定“Microphone.BufferDuration = TimeSpan.FromMilliseconds(1000)”代表当麦克风撷取声音长达1秒后,立刻触发BufferReady事件。 BufferDuration可设定容纳时间范围:100毫秒至1000毫秒(1秒)。超过或低于会发生ArgumentOutOfRangeException例外事件。 |
|
IsHeadset | 识别目前设备是否有外接麦克风,不管是接线式或蓝牙耳机,如果有为true;没有为false。 | |
SampleRate | 回传麦克风撷取声音数据的采样率,以Hertz (Hz)为单位。 | |
State | 回传麦克风目前运行的状态,值为MicrophoneState枚举,包括:Started、Stoped。 | |
方法 | GetData | 取得由麦克风撷取声音数据的最新缓冲区数据,以byte array为单位。 该方法为重载的方式,可以指定要撷取的范围(Byte[], start, length)或是整个Byte[]回传。 |
GetSampleDuration | 回传指定麦克风录音的数据大小为对应的时间长度。举例来说,录音声音byte array大小为3200,透过“TimeSpan duration = Microphone.GetSampleDuration(totalSize)”指定取得的回传值为约7秒。 | |
GetSampleSizeInBytes | 回传指定麦克风录音的容纳时间长度对应于Bytes的大小,该回传值代表该录音文件需要byte arrray大小。 | |
Start | 启动录音进程。 | |
Stop | 结束录音进程。 | |
事件 | BufferReady | 当撷取声音的缓冲被填满后,即触发该事件。代表实践该事件时,可以透过GetData方法取得目前已被写入缓冲的录音数据(byte array)。 |
由上表可以看出几个重要的属性:
BufferDuration:指定要录音的时间长度,透过TimSpan做为设定的时间长度,以毫秒为单位;
GetSampleSizeInBytes:根据指定的时间长度转换成对于byte arrary的大小;
BufferReady:代表该事件触发时,可以取得真正被录好的binary数据,将binary数据写成文件即可以进行播放。
[注意事项]
在使用Microphone类进行录音时,有一件事是需要注意的那就是需要去执行:“FrameworkDispatcher.Update();”。
为什么呢?由于在XNA中每33fp就会更新画面一次,但在Silverlight Application并没有这样的机制,为了确保录音的功能
持续的更新状态与进行撷取动作,因此,需要透过指定一个定期执行“FrameworkDispatcher.Update();”的事件。
详细说可参考
通常直接使用DispatcherTimer让它自己一个线程,定期调用上述命令,例如:
1: DispatcherTimer tDT = new DispatcherTimer();
2: //定期每33毫秒执行一次FrameworkDispatcher.Update(); 方法
3: tDT.Interval = TimeSpan.FromMilliseconds(33);
4: tDT.Tick += delegate { try { FrameworkDispatcher.Update(); } catch { } };
5: tDT.Start();
b. SoundEffect Class:
该类提供载入声音资源的功能,该类最常用于XNA Game中播放特定的音效,由于它不像MediaElement需指定特定的处理才能播放,
又支持非前景下进行。因此,在录音程序完成后通常也会配合该类将声音资源直接播放出来,等到确定保存时,再将数据保存为实例文件。
但使用该类要特别注意,SoundEffect针对载入的声音资源是写入内存中的,它配合多个SoundEffectInstance来播放多个声音,代表
手机内存的管理是需要特别被处理的。
c. 需注意的Exception:NoAudioHardwareException与NoMicrophoneConnectedException:
这二个Exception发生于当麦克风硬件或连接Microphone API出现问题时,例如:安装外接式麦克风后发生Driver不支持或无法撷取声音时。
由于我在是试用手机连接蓝牙耳机时所遇到的例外,所以特别标示出来。当时发生的情形是刚接上蓝牙后,启动App立即点击录音,API连接一
会后就出现该Exception了。
〉实践范例:
该实践范例主要是撰写透过麦克风录下正在播放的音乐,在录制20秒后结束并自动存成*.wav文件。往下便加以说明如何实践:
a. 在画面中加下几个录音与播放音乐用的控件:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17: 18: Grid.Column="0" Grid.Row="0" />19:20:21:22:23:
预览画面如图:
b. 实践录音的功能:
b-1. 先定义公用变量,记录microphone实例、缓冲用的byte[]、记录音档数据的stream与要播放的SoundEffect。
1: Microphone gMicrophone = Microphone.Default;2: byte[] gAudioBuffer;3: MemoryStream gStream = new MemoryStream();4:5: SoundEffect gSoundEffect;6: //定义记录播放20秒的token7: int gSpendTime = 0;8:9: public MainPage()10: {11: InitializeComponent();12:13: init();14: }
b-2. 撰写程序启动后,初始化的任务,包括:
(1) 定义要周期性调用FrameworkDispatcher.Update();
(2) 注册microphone的BufferReady事件处理;
1: //定义初始化副程序2: private void init()3: {4: DispatcherTimer tDT = new DispatcherTimer();5: //定期每33毫秒执行一次FrameworkDispatcher.Update(); 方法6: tDT.Interval = TimeSpan.FromMilliseconds(33);7: tDT.Tick += delegate { try { FrameworkDispatcher.Update(); } catch { } };8: tDT.Start();9: //注册BufferReady事件的处理者10: gMicrophone.BufferReady += new EventHandler(gMicrophone_BufferReady); 11: }12:13: void gMicrophone_BufferReady(object sender, EventArgs e)14: {15: //设定每1秒加1次目前录音花掉的时间16: gSpendTime += 1;17: Dispatcher.BeginInvoke(() =>18: {19: tblSpendTime.Text = string.Format("{0} seconds.", gSpendTime.ToString());20: });21:22: //每1秒到时,取出在缓冲的Buffer数据,并写入Stream中。23: gMicrophone.GetData(gAudioBuffer);24: gStream.Write(gAudioBuffer, 0, gAudioBuffer.Length);25:26: //满足20秒后自动调用关闭录音27: if (gSpendTime == 20)28: {29: btnStopRecord_Click(sender, new RoutedEventArgs());30: gSpendTime = 0;31: Dispatcher.BeginInvoke(() =>32: {33: tblSpendTime.Text = "Record finished!";34: });35: }36: }
b-3. 注册"启动录音”与”结束录音”的按钮事件:
在实践启动录音与结束录音,出现了WavHedaer的类功能,主要是用于将Stream中的数据按造Wav Format的格式进行
编译,为何需要这样呢?由于WP7目前还不支持将录好的Audio File直接转成mp3或wav,因此,如何将Byte Array转成指
定的音档格式,目前我找到的可以参考:
的做法,直接把Byte Array wav文件,并且写入Isolated Storage之中。
1: private void btnRecord_Click(object sender, RoutedEventArgs e)2: {3: //指定要将录音的数据转成WAV格式4: WavHeader.WriteWavHeader(gStream, gMicrophone.SampleRate);5:6: //指定录音的BufferDuration为1秒触发一次BufferReady事件7: gMicrophone.BufferDuration = TimeSpan.FromMilliseconds(1000);8:9: //透过指定的BufferDuration,转换出大约需要的byte array size10: gAudioBuffer = new byte[gMicrophone.GetSampleSizeInBytes(gMicrophone.BufferDuration)];11:12: //启动录音13: gMicrophone.Start();14: }15:16: private void btnStopRecord_Click(object sender, RoutedEventArgs e)17: {18: if (gMicrophone.State == MicrophoneState.Started)19: {20: //结束录音21: gMicrophone.Stop();22:23: //指定将录音的数据结束时补上特定标签24: WavHeader.UpdateWavHeader(gStream);25:26: //结束写入内存27: gStream.Close();28: }29: }
b-4. 注册播放录好音的声音数据按钮事件:
1: private void btnPlay_Click(object sender, RoutedEventArgs e)2: {3: //由于录好音的数据是记录在MemoryStream之中,4: //因此,直接使用SoundEffect配合是最好的作法,Stream.ToArray把数据转换出来。5: //设定SampleRate告诉SoundEffect音轨的频率,AudioChannels.Mono与频道的模式6: gSoundEffect = new SoundEffect(gStream.ToArray(),7: gMicrophone.SampleRate, AudioChannels.Mono);8: gSoundEffect.Play();9: }
c. 实践播放音乐的功能:
实践b步骤中的所有流程,即可以进行录音与播放录好的声音功能。接下来c的部分,主要透过MediaElement载入要播放的音乐。
1: private void btnPlayMusic_Click(object sender, RoutedEventArgs e)2: {3: if (media.CurrentState == MediaElementState.Closed)4: {5: //指定播放的音乐6: Uri tUri = new Uri("mms://bcr.media.hinet.net/RA000040",7: UriKind.Absolute);8: media.Source = tUri;9: media.Play();10: }11: }12:13: private void btnStopMusic_Click(object sender, RoutedEventArgs e)14: {15: if (media.CurrentState == MediaElementState.Playing)16: media.Stop();17: }
d. 实践写入Isolated Storage与播放WAV的功能的功能:
1: private void btnSaveFile_Click(object sender, RoutedEventArgs e)2: {3: //将文件存在IsolatedStorage之中4: WriteStraminIsolatedStorage("cusrecord.wav", gStream);5:6: //透过SoundEffect载入放在IsolatedStorage中的wav7: using (var tStore = IsolatedStorageFile.GetUserStoreForApplication())8: {9: using (var tFStream = new IsolatedStorageFileStream("cusrecord.wav", FileMode.Open, tStore))10: {11: //使用SoundEffect直接播放wav文件12: SoundEffect effect = SoundEffect.FromStream(tFStream);13: //记得在加上一次update,告诉mediaplayer要进行播放14: FrameworkDispatcher.Update();15: effect.Play();16: }17: }18: }19:20: private void WriteStraminIsolatedStorage(string pFileName, Stream pStream)21: {22: using (var tStore = IsolatedStorageFile.GetUserStoreForApplication())23: {24: //取得文件名称,并且检查是否已经有文件存在,如果有则先删除,再重新写入。25: if (tStore.FileExists(pFileName))26: {27: tStore.DeleteFile(pFileName);28: }29: //建立文件30: using (var tFStream = new IsolatedStorageFileStream(pFileName, FileMode.Create, tStore))31: {32: byte[] tByteInStream = gStream.ToArray();33: tFStream.Write(tByteInStream, 0, tByteInStream.Length);34: tFStream.Flush();35: }36: }37: }
e. 测试流程:
1. 点击[Play Music] ;
2. 点击[Start Record by Microphone],等录制20秒后会自动关闭录音出现“record finished”;
3. 点击[Save File],程序会自动产生*.wav与播放wav文件。
[范例程序]
======
以上是说明如何实践一个录音程序的功能。其实录音程序在XNA类里还有很多相关的应用,值得去研读。
不过目前范例中介绍的功能我觉得非常适用。不过由于目前WP7针对录音数据没有办法直接保存成mp3, wav或wma,
老实说真的不是很方便,希望能够快点支持才好。不然就要来找看看怎么把它转过去了。
References:
‧在App中读取Windows Phone 7手机内的音乐资源(Music Hub整合) (必读)
‧Sound Recording in Windows Phone 7
‧How to capture audio from your microphone in WP7
‧WP7 and Microsoft.Xna.Framework.Audio.Microphone
‧梦想成真 XNA (6) - 声音和音效 & 梦想成真XNA(6)-声音和音效(2)
‧Recording Audio in Silverlight on Windows Phone 7
‧How To Record Audio On Windows Phone 7 And Copy Recorded Files To Your PC ()
‧How to: Record Video in a Camera Application for Windows Phone
‧How to: Use the DeviceStatus Class for Windows Phone
‧Audio recorder Silverlight 4 sample & Silverlight 4 – WebCams.OnceMoreWithAudio()
‧Saving Microphone Stream to mp3 or wave & Storing WP7 recorded audio as WAV format streams (必读)
‧Upload Files from Windows Phone
‧Silverlight 4 Rough Notes: Camera and Microphone Support (重要)
‧Saving the microphone recording byte[] in a file ? & Aumplib: C# Namespace And Classes For Audio Conversion
‧silverlight中播放Wav文档 & Windows Phone7 播放WAV 和 MP3 & Playing .MP3 and .WMA Audio Files in XNA
‧Articles about Alvas.Audio
‧Video Recording in Windows Phone 7
‧Record Silverlight 4 MediaElement content & Steps to Record Voice & Podcast on Windows Phone 7
‧WPF save streamed video using MediaElement & MediaElement Record
Dotblogs Tags: Windows Phone 7
原文:大专栏 WP7 - 写一个会Record Audio的录音程序