| Невозможно пройти тесты, в окне с вопросами пусто |
Акселерометр, микрофон, панель приложения
34.2. Работа с микрофоном, панель приложения
В основу этого примера лёг образец от Microsoft (http://msdn.microsoft.com/en-us/library/gg442302%28v=vs.92%29.aspx). Здесь создан пользовательский интерфейс на основе панели приложения. С помощью кнопок на панели приложения мы управляем записью звуков посредством микрофона и воспроизведением этих звуков.
Панель приложения обычно представлена набором кнопок в нижней части окна программы (при портретной ориентации). Кнопки располагаются достаточно компактно, если прикоснуться к области панели с тремя точками, панель развернётся, показывая подписи к кнопкам, и, возможно, пункты меню, позволяющие произвести какие-либо дополнительные действия. На рис. 34.2 показан пользовательский интерфейс нашей программы при запуске в эмуляторе.
Кнопки отображены пустыми значками, так как для них использованы пустые заготовки изображений, которые можно нарисовать самостоятельно, учитывая особенности оформления Windows Phone-приложений.
В листинге 34.3 приведен XAML-код страницы MainPage
<phone:PhoneApplicationPage
x:Class="P27_2.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="696"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
shell:SystemTray.IsVisible="True">
<!--LayoutRoot представляет корневую сетку, где размещается все содержимое страницы-->
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--TitlePanel содержит имя приложения и заголовок страницы-->
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
<TextBlock x:Name="ApplicationTitle" Text="МИКРОФОН"
Style="{StaticResource PhoneTextNormalStyle}"/>
<TextBlock x:Name="PageTitle" Text="микрофон" Margin="9,-7,0,0"
Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>
<!--ContentPanel — поместите здесь дополнительное содержимое-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<Image x:Name="StatusImage" Height="334" Width="366"
HorizontalAlignment="Center" VerticalAlignment="Center"
Source="Images/blank.png" Margin="45,79,45,18" />
<TextBlock Height="30" HorizontalAlignment="Left" Margin="12,6,0,0"
Name="UserHelp" Text="Нажмите кнопку Запись" VerticalAlignment="Top"
Width="414" />
</Grid>
</Grid>
<!--Пример кода, иллюстрирующий использование ApplicationBar-->
<phone:PhoneApplicationPage.ApplicationBar>
<shell:ApplicationBar IsVisible="True" IsMenuEnabled="False">
<shell:ApplicationBar.Buttons>
<shell:ApplicationBarIconButton x:Name="recordButton" Text="запись"
IconUri="/Images/record.png" Click="recordButton_Click" IsEnabled="True"/>
<shell:ApplicationBarIconButton x:Name="playButton" Text="проиграть"
IconUri="/Images/play.png" Click="playButton_Click" IsEnabled="False"/>
<shell:ApplicationBarIconButton x:Name="stopButton" Text="стоп"
IconUri="/Images/stop.png" Click="stopButton_Click" IsEnabled="False"/>
</shell:ApplicationBar.Buttons>
</shell:ApplicationBar>
</phone:PhoneApplicationPage.ApplicationBar>
</phone:PhoneApplicationPage>
Листинг
34.3.
Код страницы MainPage
Обратите внимание на описание ApplicationBar. Здесь задано три кнопки на панели приложения. Одна из них, кнопка записи, активирована по умолчанию, доступ к двум другим закрыт, так как сразу после запуска приложения единственное, что может сделать пользователь – это сделать новую запись с микрофона.
Для данного проекта мы должны подключить библиотеку Microsoft.Xna.Framework, объектное представление аппаратного микрофона представлено именно в ней. В листинге 34.4 вы можете видеть программный код, обеспечивающий работу примера. Он подробно прокомментирован.
using System;
using System.IO;
using System.Threading;
using System.Windows;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
namespace P27_2
{
public partial class MainPage : PhoneApplicationPage
{
// Объект, представляющий физический микрофон на устройстве
private Microphone microphone = Microphone.Default;
//Динамический буфер для получения аудиоданных с микрофона
private byte[] buffer;
//Хранение аудиоданных для последующего воспроизведения
private MemoryStream stream = new MemoryStream();
//Используется для воспроизведения звука
private SoundEffectInstance soundInstance;
//Флаг для отслеживания состояния проигрывания звука
private bool soundIsPlaying = false;
// Изображения, сигнализирующие о состоянии программы
private BitmapImage blankImage;
private BitmapImage microphoneImage;
private BitmapImage speakerImage;
/// <summary>
/// Конструктор
/// </summary>
public MainPage()
{
InitializeComponent();
// Таймер для организации игрового цикла XNA (обект Microphone взят
// из XNA Framework). Мы так же используем этот таймер для отслеживания
// состояния воспроизведения аудио, что позволяем нам обновлять
// состояние пользовательского интерфейса
DispatcherTimer dt = new DispatcherTimer();
dt.Interval = TimeSpan.FromMilliseconds(33);
dt.Tick += new EventHandler(dt_Tick);
dt.Start();
//Обработчик событя для получения аудиоданных, когда буфер заполнится
microphone.BufferReady += new EventHandler<EventArgs>(microphone_BufferReady);
//Подготовка ссылок на изображения
blankImage = new BitmapImage(new Uri("Images/blank.png", UriKind.RelativeOrAbsolute));
microphoneImage = new BitmapImage(new Uri("Images/microphone.png", UriKind.RelativeOrAbsolute));
speakerImage = new BitmapImage(new Uri("Images/speaker.png", UriKind.RelativeOrAbsolute));
}
/// <summary>
/// Вызвает XNA FrameworkDispatcher и проверяет, проигрывается ли звук.
/// Если проигрывание завершено, обновляет состояние пользовательского интерфейса.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void dt_Tick(object sender, EventArgs e)
{
//Организуем игровой цикл XNA
try { FrameworkDispatcher.Update(); }
catch { }
//Если звук проигрывается
if (true == soundIsPlaying)
{
//Если проигрывание завершено
if (soundInstance.State != SoundState.Playing)
{
// Проигрывание завершено
soundIsPlaying = false;
// Обновляет пользовательский интерфейс
// того, чтобы отразить, что прекращено проигрывание звука
SetButtonStates(true, true, false);
UserHelp.Text = "Нажмите кнопку Проиграть или Запись";
StatusImage.Source = blankImage;
}
}
}
/// <summary>
/// Обработчик события Microphone.BufferReady.
/// Получает аудиоданные с микрофона, сохраняет их в буфер,
/// затем записывает содержимое буфера в поток для последующего воспроизведения.
/// Любые действия в этом обработчике должны быть как можно более быстрыми!
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void microphone_BufferReady(object sender, EventArgs e)
{
// Получаем аудиоданные
microphone.GetData(buffer);
// Сохраняем данные в поток
stream.Write(buffer, 0, buffer.Length);
}
/// <summary>
/// Обрабатывает событие Click для кнопки record (запись).
/// Устанавливает микрофон и буферы данных для получения аудиоданных,
/// затем запускает микрофон. Кроме того, обновляет пользовательский интерфейс.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void recordButton_Click(object sender, EventArgs e)
{
// Получение аудиоданных участками по 1/2 секунды
microphone.BufferDuration = TimeSpan.FromMilliseconds(500);
// Выделяет память для хранения аудиоданных
buffer = new byte[microphone.GetSampleSizeInBytes(microphone.BufferDuration)];
// Сбрасывает поток в 0 для того случая, если он уже содержит
//какие-либо данные
stream.SetLength(0);
// Начало записи
microphone.Start();
// Обновление пользовательского интерфейса
SetButtonStates(false, false, true);
UserHelp.Text = "Идёт запись...";
StatusImage.Source = microphoneImage;
}
/// <summary>
/// Обрабатывает событие Click для кнопки stop (стоп).
/// Останавливает запись звука с микрофона и обновляет пользовательский интерфейс.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void stopButton_Click(object sender, EventArgs e)
{
//Если запись идёт
if (microphone.State == MicrophoneState.Started)
{
// В режиме записи пользователь нажимает на кнопку
// stop для того, чтобы прекратить запись
microphone.Stop();
}
// Если звук воспроизводится
else if (soundInstance.State == SoundState.Playing)
{
// В режиме воспроизведения пользоатель нажимает на кнопку
// stop для того, чтобы остановить воспроизведение
soundInstance.Stop();
}
// Обновляет пользовательский интерфейс
SetButtonStates(true, true, false);
UserHelp.Text = "Готово";
StatusImage.Source = blankImage;
}
/// <summary>
/// Обрабатывает событие Click для кнопки play (воспроизведение).
/// Проигрывает звук, записанный в прошлом сеансе записи с микрофона
/// и обновляет пользовательский интерфейс.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void playButton_Click(object sender, EventArgs e)
{
if (stream.Length > 0)
{
// Обновление пользовательского интефейса,
// указывающее на то, что звук проигрывается
SetButtonStates(false, false, true);
UserHelp.Text = "Воспроизведение записи";
StatusImage.Source = speakerImage;
// Проигрывает звук в новом потоке, что позволяет
//пользовательскому интерфейсу обновляться.
Thread soundThread = new Thread(new ThreadStart(playSound));
soundThread.Start();
}
}
/// <summary>
/// Воспроизводит звук, используя SoundEffectInstance
/// это позволяет нам отслеживать состояние проигрывания.
/// </summary>
private void playSound()
{
// Воспроизводит звук, используя SoundEffectInstance
// что позволяет нам отслеживать состояние воспроизведения
// и обновлять пользовательский интерфейс в обработчике dt_Tick, когда воспроизведение завершитсяg.
SoundEffect sound = new SoundEffect(stream.ToArray(), microphone.SampleRate, AudioChannels.Mono);
soundInstance = sound.CreateInstance();
soundIsPlaying = true;
soundInstance.Play();
}
/// <summary>
/// Вспомогательный метод для изменения свойства IsEnabled
/// для объектов ApplicationBarIconButtons (кнопок на панели приложения).
/// </summary>
/// <param name="recordEnabled">Новое состояние для кнопки record.</param>
/// <param name="playEnabled">Новое состояние для play .</param>
/// <param name="stopEnabled">Новое состояние для stop .</param>
private void SetButtonStates(bool recordEnabled, bool playEnabled, bool stopEnabled)
{
(ApplicationBar.Buttons[0] as ApplicationBarIconButton).IsEnabled = recordEnabled;
(ApplicationBar.Buttons[1] as ApplicationBarIconButton).IsEnabled = playEnabled;
(ApplicationBar.Buttons[2] as ApplicationBarIconButton).IsEnabled = stopEnabled;
}
}
}
Листинг
34.4.
Программный код страницы MainPage
Так как в работе мы пользуемся механизмами XNA, нам нужно организовать таймер, имитирующий игровой цикл XNA. Этот таймер мы запускаем в конструкторе, с его помощью мы, кроме того, отслеживаем состояние проигрывания записи и соответствующим образом обновляем пользовательский интерфейс.
В конструкторе же мы добавляем обработчик события BufferReady, которое происходит, когда буфер микрофона (то есть – записанные данные) готов к обработке.
По кнопке старта записи мы подготавливаем всё, что нужно для работы с данными микрофона и запускаем микрофон. По кнопке остановки, которая останавливает запись, если запись идёт, или останавливает воспроизведение при проигрывании записи, мы, в первом случае, останавливаем микрофон (после чего происходит событие BufferReady, данные с микрофона сохраняются), во втором – останавливаем воспроизведение.
При нажатии на кнопку воспроизведения мы проверяем, есть ли данные в потоке, хранящем данные последней записи, если они есть – запускаем проигрывание в отдельном потоке, пользуясь методом PlaySound().
Большинство операций, имеющих отношение к работе со звуком, сопровождаются настройками пользовательского интерфейса. Как правило, меняется сообщение для пользователя, которое выводится в текстовый блок UserHelp, кроме того, с помощью вспомогательного метода SetButtonStates настраивается доступность кнопок панели приложения.
34.3. Выводы
В данной лабораторной работе мы ознакомились с использованием показаний акселерометра для управления экранными объектами в Silverlight-приложениях, рассмотрели элемент управления Canvas и концепцию присоединенных свойств, научились работать с микрофоном и оснащать программу панелью приложения
34.4. Задание
Выясните, какими еще сенсорами и аппаратными устройствами телефона можно воспользоваться из Silverlight-программы. Выберите один из сенсоров или аппаратных устройств и реализуйте приложение, использующее его возможности.
