Невозможно пройти тесты, в окне с вопросами пусто |
Акселерометр, микрофон, панель приложения
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-программы. Выберите один из сенсоров или аппаратных устройств и реализуйте приложение, использующее его возможности.