Windows Phone - 擷取麥克風輸入的音量觸發任務
在<Windows Phone 7 – 寫一個會Record Audio的錄音程式>這篇介紹過怎麼操作麥克風來做到錄音的功能。
如果忘記了Microsoft.Xna.Framework.Audio.Microphone該類別的一些特性,可以透過上述該篇內容加以了解。
本篇則介紹如何透過麥克風取得目前輸入的音量來觸發任務。為什麼會想介紹這個呢?主要是因為很好奇
看到很多透過麥克風呼氣或尖叫音量來觸發遊戲或特別的任務,因此,也來看看在WP裡如何實作吧。
[步驟]
0. 至WMAppManifest.xml開啟「ID_CAP_MICROPHONE」的權限;
1. 設定MainPage.xaml畫面,用來呈現音量變化有一個音量軸的高度變化與數值呈現,如下圖:
那麼他的XAML檔設定如下:
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<Grid.RowDefinitions>
<RowDefinition Height="30*" />
<RowDefinition Height="70*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="30*" />
<ColumnDefinition Width="70*" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2">
<Button x:Name="btnStart"
Content="Start"
Click="recordButton_Click" />
<Button x:Name="btnStop"
Content="Stop"
Click="stopButton_Click" />
<StackPanel Orientation="Horizontal">
<TextBlock Text="Volume: "
FontSize="30"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Margin="30,0, 30, 0" />
<TextBlock Name="txtVolume"
FontSize="30"
HorizontalAlignment="Center"
Text="0" />
</StackPanel>
</StackPanel>
<!-- 音量呈現軸 -->
<Rectangle
Grid.Column="0"
Grid.Row="1"
Fill="#FFF90606"
HorizontalAlignment="Center"
Height="5"
Stroke="Black"
VerticalAlignment="Bottom"
Width="100"
x:Name="rectVolume">
</Rectangle>
<!-- 放置擷取的數值 -->
<ListBox
Grid.Row="1"
Grid.Column="1"
Height="415"
x:Name="lstVolume" />
</Grid>
宣告了一個<Rectangle />用來呈現音量軸,所以當用戶輸入的音量達到我們最低標記時,則會調整該軸的高度來呈現效果。
2. 參考<How to access the microphone in a Windows Phone app>的內容建立一個可以操作麥克風的應用程式;
2-1. 準備必要的參數與Namespaces宣告;
//宣告必要的Namespaces
using System.Windows.Threading;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using System.Windows.Media;
public partial class MainPage : PhoneApplicationPage
{
//宣告麥克風元件
Microphone tMicrophone = Microphone.Default;
//放入可緩衝用的byte array
byte[] buffer;
//放入可將byte寫入Stream
MemoryStream stream = new MemoryStream();
//播放所錄製聲音的元件
SoundEffect sound;
//宣告音量所達的最低指標,用於觸發任務
private int minimumThreshold = 300;
}
2-1. 註冊每固定時間更新,以確保錄音的功能持續的更新狀態與進行擷取動作,並註冊麥克風元件的BufferReady事件;
// Constructor
public MainPage()
{
InitializeComponent();
// Timer to o simulate the XNA Game Studio game
// loop (Microphone is from XNA Game Studio)
DispatcherTimer dt = new DispatcherTimer();
dt.Interval = TimeSpan.FromMilliseconds(33);
dt.Tick += delegate
{
try { FrameworkDispatcher.Update(); }
catch { }
};
dt.Start();
//註冊擷取錄音緩衝的事件
tMicrophone.BufferReady += new EventHandler<EventArgs>(microphone_BufferReady);
}
2-2. 註冊開啟/關閉麥克風的事件處理;
private void recordButton_Click(object sender, RoutedEventArgs e)
{
//識別如果目前麥克風是關閉的,則打開。
if (tMicrophone.State == MicrophoneState.Stopped)
{
try
{
//指定麥克風元件緩衝多少區間,則觸發BufferReady事件
//設定愈短,其效果比較明顯,但不一定比較好,請依需求調整。
tMicrophone.BufferDuration = TimeSpan.FromMilliseconds(100);
//設定緩衝用的byte array需要保留多少空間
buffer = new byte[
tMicrophone.GetSampleSizeInBytes(tMicrophone.BufferDuration)];
//開始錄音
tMicrophone.Start();
System.Diagnostics.Debug.WriteLine("Threshold setted to:" + minimumThreshold);
}
catch (Exception ex)
{
string tMsg = ex.Message;
}
btnStart.IsEnabled = false;
btnStop.IsEnabled = true;
}
}
private void stopButton_Click(object sender, RoutedEventArgs e)
{
//識別如果目前麥克風為開啟,則關閉
if (tMicrophone.State == MicrophoneState.Started)
{
tMicrophone.Stop();
}
btnStart.IsEnabled = true;
btnStop.IsEnabled = false;
rectVolume.Height = 5;
txtVolume.Text = "0";
lstVolume.Items.Clear();
}
3. 註冊處理BufferReady的事件,從中識別輸入的音量是否超過設定的最低標記值,有則執行音量軸的變化;
為什麼需要最低標記值(minimumThreshold)?根據<How to access and manage the Microphone raw data in WP>的介紹,
最主要是為了讓低於最低標記值的音量變化不出現反應,這樣可以控制讓用戶的輸入一定要達我們的需求,
避免不必要的資訊被記錄到而反應邏輯或效果的判斷。
void microphone_BufferReady(object sender, EventArgs e)
{
HandleVolume();
}
private void HandleVolume()
{
// Retrieve audio data
tMicrophone.GetData(buffer);
// RMS Method
double rms = 0;
ushort byte1 = 0;
ushort byte2 = 0;
short value = 0;
int volume = 0;
rms = (short)(byte1 | (byte2 << 8));
for (int i = 0; i < buffer.Length - 1; i += 2)
{
byte1 = buffer[i]; byte2 = buffer[i + 1];
value = (short)(byte1 | (byte2 << 8));
rms += Math.Pow(value, 2);
}
//取得RMS值與Volumne
rms /= (double)(buffer.Length / 2);
volume = (int)Math.Floor(Math.Sqrt(rms));
//識別是否超過最低標記值
if ((volume > minimumThreshold))
{
Dispatcher.BeginInvoke(() =>
{
txtVolume.Text = volume.ToString();
lstVolume.Items.Add(string.Format("{0},{1}", buffer.Length.ToString(), volume.ToString()));
//更新音量軸
rectVolume.Height = (volume - minimumThreshold) * .6;
});
System.Diagnostics.Debug.WriteLine("Threshold exceeded");
System.Diagnostics.Debug.WriteLine("buffer.Length" + buffer.Length + " Volume:" + volume);
}
else
{
//沒有則還原畫面值
Dispatcher.BeginInvoke(() =>
{
rectVolume.Height = 5;
txtVolume.Text = "0";
lstVolume.Items.Clear();
});
}
}
其計算的公式採用:RMS method來加以算出Volume。
[範例程式]
======
以上是分享怎麼從過Microphone類別的BufferReady取得音量的變化,進一步就可以依需求來設定反應的對象。
希望對大家有所幫助。
References:
‧How to access the microphone in a Windows Phone app
‧Windows Phone SDK 8.0 Extensions for XNA Game Studio 4.0
‧How to access and manage the Microphone raw data in WP & Using QMicrophone
‧Saving Microphone stream to wave format in Windows Phone
‧XNA from Silverlight on Windows Phone 7 – The Microphone
‧Windows Phone 7 – 寫一個會Record Audio的錄音程式
‧Part 24 - Windows Phone 7 - Microphone Repeater