WP7 - 写一个会Record Audio的录音程序

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:NoAudioHardwareExceptionNoMicrophoneConnectedException

    这二个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: 


预览画面如图:

000

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秒的token
   7: 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 size
  10:     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中的wav
   7:     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的录音程序


猜你喜欢

转载自www.cnblogs.com/petewell/p/11452654.html