センサーとサウンドの処理を組み込んでアプリを完成させる
処理ページのプログラミング
参照の追加
このアプリでは、センサーを利用するためにMicrosoft.Devices.Sensors名前空間を、サウンドの再生のためにMicrosoft.Xna.Framework名前空間を使うため、「プロジェクト/参照の追加」から、それらへの参照を追加しておきます(図15)。
図15:Microsoft.Devices.Sensors とMicrosoft.Xna.Frameworkへの参照を追加する(クリックで拡大) |
処理ページのロジック(MyClinometer.xaml.vb)
ここで、処理ページのプログラミングに移ります(リスト10)。
センサーとXna.Frameworkの名前空間をインポートして、レンジ設定、サウンド再生、針やメッセージの表示、加速度センサーの実行に必要な変数を宣言しておきます。
その上で、次の処理を記述します。
(1)初期設定
このページがロードされた時、レンジを「1」、サウンドを「ベル」に設定します。これをデフォルトの設定とします。加速度センサーがサポートされていない機種に対してメッセージを表示します。
(2)レンジ設定
レンジ設定スライダーの値が変化した時、設定した角度を表示し、レンジ横のWindows Phoneの絵を回転させて、設定角度を視覚的に表現します。
(3)サウンド設定
ベル、小鳥、歌の3つのボタンがそれぞれタップされた時、選択されたボタンは再タップできないようにして、他の2つのボタンはタップ可能にします。
(4)ボリューム設定
ボリュームのデフォルトは「0.3」とし、ボリューム調整スライダーの値が変化した時、ボリュームを設定し、ボリュームを表す数字を表示しているTextBlockをツマミの位置に合わせて移動させます。
(5)「Start」ボタンがタップされた時の処理
「Start」ボタンがタップされた時、Accelerometer クラスの新しいインスタンスを作成します。
傾斜表示の元になる角度、4個のTextBlockへの表示用情報、針の角度を初期化した上で、設定画面を非表示にして、結果表示ウィンドウを表示します。
加速度センサーでの情報取得を開始します。
(6)加速度センサーから取得する情報が変化した時の処理
加速度センサーからのデータを取得し、逆三角関数で角度をもとめ、プラスマイナスの付いた状態で変数MyChannelAngleX、MyChannelAngleYに格納しておきます。
4個のTextBlockに表示する数値は絶対値にして、水平より傾いている(下がっている)方向に表示します。
プラスマイナスの付いた角度から傾きの状態が分かり、再生させるサウンドを特定できるので、サウンドファイル名の部分を変数に代入しておきます。また、設定画面で設定したテーマをフォルダ名として、再生するサウンドファイル名を変数MySoundFileNameに代入します。
X方向またはY方向の角度が設定したレンジの角度以上に傾いた時、加速度取得を停止します。
音声ファイル名から針の角度を決定し、どちらの方向を上げた方がよいか(設定したレンジに対してどちらの方向が下がりすぎたか)を示すメッセージを変数MyMessageTextに代入し、針を回転させて表示します。
サウンドファイルを再生し、メッセージを表示して、「OK」ボタンを表示します。
(7)「OK」ボタンがタップされた時の処理
サウンドを停止してインスタンスを破棄し、結果表示ウィンドウを非表示にして設定画面を表示します。結果表示ウィンドウ内のパーツを非表示にして値を初期化します。
(8)ページがアンロードされた時の処理
取得される角度を代入する変数の値を初期化します。
リスト10 完成した処理ページのロジック・コード(MyClinometer.xaml.vb
Option Strict On Imports Microsoft.Devices.Sensors Imports Microsoft.Xna.Framework Imports Microsoft.Xna.Framework.Audio Partial Public Class MyClinometer Inherits PhoneApplicationPage Public Sub New() InitializeComponent() End Sub '■レンジ設定用 Dim MySetAngle As Integer '●レンジの設定角度 '■サウンド再生用 Dim MySoundDirectory As String '●サウンドファイルのディレクトリ名 Dim MySoundTitle As String = String.Empty '●サウンドファイル名(共通) Dim SoundPropertyX As String = String.Empty '●サウンドファイル名のX部分(Right/Center/Left) Dim SoundPropertyY As String = String.Empty '●サウンドファイル名のY部分(High/Middle/Low) Dim MySoundFileName As String = String.Empty '●パス付きサウンドファイル名 Dim MySound As SoundEffect = Nothing Dim MySoundInstance As SoundEffectInstance '■針表示用 Dim MyAngleX, MyAngleY As Double '●丸めた傾斜角度の絶対値 Dim MyChannelAngleX, MyChannelAngleY As Double '●丸めた傾斜角度 Dim MyHandAngle As Integer = 0 '●描画する針の角度 '■メッセージ表示用 Dim MyMessageText As String = String.Empty '●メッセージの内容 '■加速度センサー Dim MyAccelerometer As Accelerometer '●加速度センサー
(1)初期設定
Private Sub MyAccelerometer_Loaded(sender As Object, e As System.Windows.RoutedEventArgs) Handles MyBase.Loaded MySetAngle = 1 MySoundDirectory = "Bell" '●加速度センサーがサポートされていない機種への対応 If Accelerometer.IsSupported = False Then MessageBox.Show("Device does not support accelerometer.") StartButton.IsEnabled = False Exit Sub End If End Sub
(2)レンジ設定
Private Sub AngleSlider_ValueChanged(sender As Object, e As System.Windows.RoutedPropertyChangedEventArgs(Of Double)) Handles AngleSlider.ValueChanged MySetAngle = CInt(AngleSlider.Value) AngleValueText.Text = CStr(MySetAngle) & "°" myPhoneProjection.RotationZ = -MySetAngle End Sub
(3)サウンド設定
Private Sub TweetButton_Tap(sender As Object, e As System.Windows.Input.GestureEventArgs) Handles TweetButton.Tap MySoundDirectory = "Tweet" TweetButton.Opacity = 0.3 TweetButton.IsEnabled = False BellButton.Opacity = 1.0 BellButton.IsEnabled = True VoiceButton.Opacity = 1.0 VoiceButton.IsEnabled = True End Sub Private Sub BellButton_Tap(sender As Object, e As System.Windows.Input.GestureEventArgs) Handles BellButton.Tap MySoundDirectory = "Bell" TweetButton.Opacity = 1.0 TweetButton.IsEnabled = True BellButton.Opacity = 0.3 BellButton.IsEnabled = False VoiceButton.Opacity = 1.0 VoiceButton.IsEnabled = True End Sub Private Sub VoiceButton_Tap(sender As Object, e As System.Windows.Input.GestureEventArgs) Handles VoiceButton.Tap MySoundDirectory = "Voice" TweetButton.Opacity = 1.0 TweetButton.IsEnabled = True BellButton.Opacity = 1.0 BellButton.IsEnabled = True VoiceButton.Opacity = 0.3 VoiceButton.IsEnabled = False End Sub
(4)ボリューム設定
Dim MyVolume As Double '●音声のボリューム。デフォルトは0.3とする Private Sub VolumeSlider_ValueChanged(sender As Object, e As System.Windows.RoutedPropertyChangedEventArgs(Of Double)) Handles VolumeSlider.ValueChanged ShowVolume.Text = CStr(CInt(VolumeSlider.Value)) ShowVolume.Margin = New Thickness(VolumeSlider.Value * 32.5 - 99, 0, 0, 0) MyVolume = CInt(VolumeSlider.Value) / 10 End Sub
(5)「Start」ボタンがタップされた時の処理
Private Sub StartButton_Tap(sender As Object, e As System.Windows.Input.GestureEventArgs) Handles StartButton.Tap '●Accelerometer クラスの新しいインスタンスを作成 MyAccelerometer = New Accelerometer '●傾斜表示の元になる角度を初期化 MyAngleX = 0 MyAngleY = 0 '●TextBlockへの表示用情報を初期化 Xplus.Text = String.Empty Xminus.Text = String.Empty Yplus.Text = String.Empty Yminus.Text = String.Empty '●針の角度を初期化 MyHandAngle = 0 myAngleProjection.RotationZ = MyHandAngle '●設定画面を非表示にして、結果表示ウィンドウを表示 PreferencesPanel.Visibility = Windows.Visibility.Collapsed StatePanel.Visibility = Windows.Visibility.Visible '●加速度センサーでの情報取得を開始 AddHandler MyAccelerometer.CurrentValueChanged, Sub(senderValue As Object, eValue As SensorReadingEventArgs(Of AccelerometerReading)) Deployment.Current.Dispatcher.BeginInvoke(Sub() CurrentValueChanged(eValue.SensorReading)) End Sub Try MyAccelerometer.Start() Catch MessageBox.Show("Unable to start accelerometer.") Exit Sub End Try End Sub
(6)加速度センサーから取得する情報が変化した時の処理
Private Sub CurrentValueChanged(myReading As AccelerometerReading) Try '●加速度センサーからのデータを取得 Dim MyAcceleration = myReading.Acceleration '●誤差を防ぐため上下をカット If MyAcceleration.X > 1 Then MyAcceleration.X = 1 ElseIf MyAcceleration.X < -1 Then MyAcceleration.X = -1 End If If MyAcceleration.Y > 1 Then MyAcceleration.Y = 1 ElseIf MyAcceleration.Y < -1 Then MyAcceleration.Y = -1 End If '●プラスマイナスの付いた角度を格納 MyChannelAngleX = Math.Round(Math.Asin(MyAcceleration.X) * 180 / Math.PI) MyChannelAngleY = Math.Round(Math.Asin(MyAcceleration.Y) * 180 / Math.PI) '●表示用には絶対値の角度にする MyAngleX = Math.Abs(MyChannelAngleX) MyAngleY = Math.Abs(MyChannelAngleY) '●4方向のTextBlock中傾斜のある方向に絶対値の角度を表示 '●サウンドファイル名の部分を設定 If MyChannelAngleX > 0 Then SoundPropertyX = "Right" Xplus.Text = CStr(MyAngleX) & "°" Xminus.Text = String.Empty ElseIf MyChannelAngleX = 0 Then SoundPropertyX = "Center" Xplus.Text = String.Empty Xminus.Text = String.Empty ElseIf MyChannelAngleX < 0 Then SoundPropertyX = "Left" Xplus.Text = String.Empty Xminus.Text = CStr(MyAngleX) & "°" End If If MyChannelAngleY > 0 Then SoundPropertyY = "High" Yplus.Text = CStr(MyAngleY) & "°" Yminus.Text = String.Empty ElseIf MyChannelAngleY = 0 Then SoundPropertyY = "Middle" Yplus.Text = String.Empty Yminus.Text = String.Empty ElseIf MyChannelAngleY < 0 Then SoundPropertyY = "Low" Yplus.Text = String.Empty Yminus.Text = CStr(MyAngleY) & "°" End If '●再生するサウンドファイル名を設定 MySoundTitle = SoundPropertyX & SoundPropertyY MySoundFileName = MySoundDirectory & "/" & MySoundTitle & ".wav" '●X方向またはY方向の角度がレンジ設定の角度以上に傾いた時の処理 If MyAngleX >= MySetAngle OrElse MyAngleY >= MySetAngle Then '●センサーの加速度取得を停止 MyAccelerometer.Stop() MyAccelerometer = Nothing '●角度と音声ファイル名を取得してセンサーを停止した後、 '●表示と音声再生に利用するプラスマイナス付きの角度を初期化 MyChannelAngleX = 0 MyChannelAngleY = 0 '●音声ファイル名によって、針の角度を決定 Select Case MySoundTitle Case "RightMiddle" MyHandAngle = 0 MyMessageText = "Slightly lift the right side." HandImage.Visibility = Windows.Visibility.Visible CenterMiddleImage.Visibility = Windows.Visibility.Collapsed Exit Select Case "RightHigh" If MyAngleX > MyAngleY Then MyHandAngle = 30 Else MyHandAngle = 60 End If MyMessageText = "Slightly lift the top and the right side." HandImage.Visibility = Windows.Visibility.Visible CenterMiddleImage.Visibility = Windows.Visibility.Collapsed Exit Select Case "CenterHigh" MyHandAngle = 90 MyMessageText = "Slightly lift the top." HandImage.Visibility = Windows.Visibility.Visible CenterMiddleImage.Visibility = Windows.Visibility.Collapsed Exit Select Case "LeftHigh" If MyAngleX < MyAngleY Then MyHandAngle = 120 Else MyHandAngle = 150 End If MyMessageText = "Slightly lift the top and the left side." HandImage.Visibility = Windows.Visibility.Visible CenterMiddleImage.Visibility = Windows.Visibility.Collapsed Exit Select Case "LeftMiddle" MyHandAngle = 180 MyMessageText = "Slightly lift the left side." HandImage.Visibility = Windows.Visibility.Visible CenterMiddleImage.Visibility = Windows.Visibility.Collapsed Exit Select Case "LeftLow" If MyAngleX > MyAngleY Then MyHandAngle = 210 Else MyHandAngle = 240 End If MyMessageText = "Slightly lift the bottom and the left side." HandImage.Visibility = Windows.Visibility.Visible CenterMiddleImage.Visibility = Windows.Visibility.Collapsed Exit Select Case "CenterLow" MyHandAngle = 270 MyMessageText = "Slightly lift the bottom." HandImage.Visibility = Windows.Visibility.Visible CenterMiddleImage.Visibility = Windows.Visibility.Collapsed Exit Select Case "RightLow" If MyAngleX < MyAngleY Then MyHandAngle = 300 Else MyHandAngle = 330 End If MyMessageText = "Slightly lift the bottom and the right side." HandImage.Visibility = Windows.Visibility.Visible CenterMiddleImage.Visibility = Windows.Visibility.Collapsed Exit Select Case "CenterMiddle" MyHandAngle = 0 MyMessageText = "OK. It's the origin." HandImage.Visibility = Windows.Visibility.Collapsed CenterMiddleImage.Visibility = Windows.Visibility.Visible Exit Select End Select '●決定した角度で針を表示 myAngleProjection.RotationZ = MyHandAngle '●サウンドファイルを再生 Dim MySoundFileUri As System.Windows.Resources.StreamResourceInfo MySoundFileUri = App.GetResourceStream(New Uri(MySoundFileName, UriKind.Relative)) MySound = SoundEffect.FromStream(MySoundFileUri.Stream) MySoundInstance = MySound.CreateInstance() SoundEffect.MasterVolume = CSng(MyVolume) MySoundInstance.Play() '●メッセージを表示して「OK」ボタンを表示 MessageTextBlock.Text = MyMessageText FinishButton.Visibility = Windows.Visibility.Visible FinishButton.IsEnabled = True End If Catch Exit Sub End Try End Sub
(7)「OK」ボタンがタップされた時の処理
Private Sub FinishButton_Tap(sender As Object, e As System.Windows.Input.GestureEventArgs) Handles FinishButton.Tap '●サウンドを停止してインスタンスを破棄 MySoundInstance.Stop() MySound.Dispose() '●「OK」ボタンを非表示にする FinishButton.Visibility = Windows.Visibility.Collapsed FinishButton.IsEnabled = False '●結果表示ウィンドウを非表示にして設定画面を表示 StatePanel.Visibility = Windows.Visibility.Collapsed PreferencesPanel.Visibility = Windows.Visibility.Visible '●結果表示ウィンドウ内のパーツを非表示にして値を初期化 HandImage.Visibility = Windows.Visibility.Collapsed MyHandAngle = 0 CenterMiddleImage.Visibility = Windows.Visibility.Collapsed MessageTextBlock.Text = String.Empty MyChannelAngleX = 0 MyChannelAngleY = 0 End Sub
(8)ページがアンロードされた時の処理
Private Sub MyAccelerometer_Unloaded(sender As Object, e As System.Windows.RoutedEventArgs) Handles MyBase.Unloaded MyChannelAngleX = 0 MyChannelAngleY = 0 End Sub End Class<
動作確認
処理が完成したところで、ヘルプページを作成する前に動作確認を行います。まず、エミュレーターで確認します。
任意のレンジを設定し、ヘッドフォンを接続し、サウンドやボリュームを設定して、センサーのシミュレーターを表示します(図16)。この例では、レンジに「35°」を設定し、サウンドは「Tweet(森の中の小鳥)」、ボリュームは「8」に設定しています。
図16:エミュレーターのセンサー・シミュレーターを表示する(クリックで拡大) |
実機の頭側を上にして見降ろした形でのシミュレートができる「Portrait Flat」モードを選択します(図17)。
図17:「Portrait Flat」モードを選択する(クリックで拡大) |
「START」ボタンをクリックして、シミュレーター内のWindows Phoneを動かします。花の絵のX軸、Y軸の傾いている方向に角度が表示されます。上下左右のいずれかの方向に、レンジに設定した角度よりも大きく傾いた時、青い花びらが傾いた方向を指し、メッセージが表示されます。
この図の例では、前掲の図17でレンジに「35°」を設定しており、左側に35°以上傾いたので、傾いた方向を青い花びらが指し、「こころもち左上を上げてください」というメッセージが表示されています。この例では、サウンドに「Tweet」を選択しており、左上に傾いているので、左チャンネルで高いピッチのTweetが再生されます。つまり、左耳に、高い小鳥のさえずりが聴こえます(図18)。
図18:エミュレーターでWindows Phoneを動かして動作を確認する(クリックで拡大) |
シミュレーターのWindows Phoneの向きをリセットし(図19)、「OK」ボタンをクリックして設定画面に戻ります。
設定はそのままで再度「START」をクリックして、いろいろな方向に傾けて確認します。また、いろいろなレンジ、いろいろなサウンドを指定して確認します。
図19:シミュレーターのWindows Phoneの向きをリセットする(クリックで拡大) |
エミュレーターで確認できたら、実機で確認します。実機をPCに接続し、ZUNEが起動して実機の電源がONになっていることを確認して、「Windows Phone」を選択してデバッグします(図20)。確認できたら、ZUNEを閉じて「ハードウェアの安全な取り外し」でIS12Tを取り外し、持ち歩いて、床や階段などの傾斜も測ってみてください。ちなみに筆者のデスクの上を測ったところ、右へ2°、手前に1°傾いていました。
図20:エミュレーターで確認し、実機でも確認する(クリックで拡大) |
ヘルプの作成と申請
最後にヘルプページ(MyClinometerHelp.xaml)をPanorama Pageで作成します。
リスト7 要素の兄要素に背景画像を指定する(MyClinometerHelp.xaml)
<Grid x:Name="LayoutRoot" Background="#FFFFFFFF"> <Image Height="768" HorizontalAlignment="Left" Margin="0,0,0,0" Name="HeloBackground" Stretch="Fill" VerticalAlignment="Top" Width="480" Source="/SoundClinometer;component/Image/HelpBack.png" /> <controls:Panorama Height="750" HorizontalAlignment="Left" Margin="10,10,0,0" Name="Panorama1" Title="" VerticalAlignment="Top" >
図21:1枚の背景画像を設定しておけば、フリックしてどのページが表示されても同じ背景になる(クリックで拡大) |
このヘルプには、テキストでの説明を補うため、図を多用しています。なぜなら、マーケットプレイス認定条件にのっとった提出用のキャプチャ方法では、エミュレーターで直接キャプチャできない説明用の図は使えないからです。
そこで、「第2回 Windows Phoneアプリを申請する流れを確認しておこう」の図12のように、処理中の画面ではなく、ヘルプの画面をキャプチャして申請しました。この方法ならば、ヘルプのページもアプリの一画面ですから、問題なく認定されます。
なお、先に解説した2本の俳句アプリは「エンターテイメント」カテゴリで申請していますが、今回は、「ツール」カテゴリで申請しました。現在のところ、海外で毎日少しずつコンスタントにダウンロードされています。
以上で、3本のアプリの解説は終わりです。Windows Phone アプリの開発の流れは、把握していただけたことと思います。皆さんも、ぜひ、気軽にアプリを開発して公開してみてください。また、今回紹介したアプリはSilverlightですが、Xna Frameworkを使ったゲームを発信するのも有望だと思います。
最終回の次回は、この12回で伝えきれなかった雑多な話題を取り上げます。
【関連リンク】
開発に役立つリンク集を紹介します。
- ■開発者向け技術情報サイト MSDN
- 全ての情報は、ここから。
- ■App Hub / Windows Phone マーケットプレイス Windows Phone フォーラム
- App Hub の利用や Windows Phone マーケットプレイスに関する話題や情報交換の場です。
- ■UX-TV
- アプリケーション開発に関する情報をエバンジェリストの方々が定期的に発信しています。Windows Phone開発に関する番組が充実しています。
- ■Windows Phone公式サイト(Microsoft)
- ■Windows Phone 開発者向け技術情報
- ■facebook 公式 Fan Page「Windows Phone Japan」
- ■Windows Phone 関連ブログ
- ■コミュニティ: Windows Phone Arch
<リンク先最終アクセス:2012.04>
<編集部より>リスト5とリスト6の名称を修正しました(処理には影響ありません)。(2012.05.14)
「Sound Clinometer Ver.0.8」プロジェクト一式