This article is compatible with the latest version of Silverlight for Windows Phone 7.
Windows Phone 7 platform provides two different frameworks for developing applications: Silverlight and XNA. XNA is a game-specific framework. If you want to write a 3D game, or a loop-based game with a lot of effects, textures and motion, you should choose XNA. So why should a SilverlightShow reader care about XNA? Because the two platforms are more-or-less compatible with each other, meaning you can use XNA assemblies in your Windows Phone 7 Silverlight applications. Here’s a brief introduction to the Silverlight-XNA integration.
First of all, you can download source codes for
- MicDemo – recording with microphone
- SoundEffectDemo – MSDN code modified for Silverlight: playing and applying 3D positioning effects to a SoundEffect
- SongDemo – playing back a song
- PictureDemo – loading and saving pictures with MediaLibrary
XNA assemblies, limitations
In a Silverlight WP7 application we can use all the XNA assemblies except Microsoft.Xna.Framework.Game and Microsoft.Xna.Framework.Graphics. Sadly, this kind of kills the concept of a game with Silverlight menus or a Silverlight application with some 3D XNA graphic.
Enabling XNA
When you first run your Silverlight application that uses the XNA Framework, you might get an exception like this:
The FrameworkDispatcher.Update method triggers event processing in the XNA Framework. It is automatically called whenever Game.Update is called. In Silverlight though, we don’t have a Game.Update, thus we have to call the FrameworkDispatcher.Update method manually.
The easiest way is to create a class like this:
public class XNAAsyncDispatcher : IApplicationService
{
private DispatcherTimer frameworkDispatcherTimer;
public XNAAsyncDispatcher(TimeSpan dispatchInterval)
{
this.frameworkDispatcherTimer = new DispatcherTimer();
this.frameworkDispatcherTimer.Tick += new EventHandler(frameworkDispatcherTimer_Tick);
this.frameworkDispatcherTimer.Interval = dispatchInterval;
}
void IApplicationService.StartService(ApplicationServiceContext context)
{
this.frameworkDispatcherTimer.Start();
}
void IApplicationService.StopService()
{
this.frameworkDispatcherTimer.Stop();
}
void frameworkDispatcherTimer_Tick(object sender, EventArgs e)
{
FrameworkDispatcher.Update();
}
}
and add it to ApplicationLifetimeObjects collection:
// Constructor
public App()
{
...
this.ApplicationLifetimeObjects.Add(
new XNAAsyncDispatcher(TimeSpan.FromTicks(333333))
);
}
The standard rendering rate for XNA is 30fps so we configure the DispatcherTimer according to that.
Audio and Media
The Microsoft.Xna.Framework.Audio and Media namespaces offer a lot of options. Playing back sound effects, dynamic sound effects, recording audio with the microphone, applying 3D effects to audio, and the list goes on…
SoundEffect
The SoundEffect class can be useful in a Silverlight game where you have a lot of sounds, often playing at the same time. Using the SoundEffect class to play a wave sound should be better performance-wise than using Silverlight’s MediaElement. Here’s how to load and play a sound file in a loop with XNA:
SoundEffect se = SoundEffect.FromStream(TitleContainer.OpenStream("hand-clap-1.wav"));
soundEffectInstance = se.CreateInstance();
soundEffectInstance.IsLooped = true;
soundEffectInstance.Play();
The TitleContainer static class provides filestream access in XNA. In the Play method we can also pass volume, pitch and panning values.
It’s very easy to apply positional effects to the sound, by calling the Apply3D method on the SoundEffectInstance, and passing one or more AudioListener instances and an AudioEmitter.
Microphone
Windows Phone 7 provides access to the microphone via the Microsoft.Xna.Audio.Microphone class. The quality is 16-bit Mono.
Microphone mic = Microphone.Default;
MemoryStream micStream = new MemoryStream();
byte[] buffer;
SoundEffect recordedSE;
// Constructor
public MainPage()
{
InitializeComponent();
mic.BufferReady += new EventHandler<EventArgs>(mic_BufferReady);
mic.BufferDuration = TimeSpan.FromMilliseconds(1000);
buffer = new byte[mic.GetSampleSizeInBytes(mic.BufferDuration)];
}
void mic_BufferReady(object sender, EventArgs e)
{
mic.GetData(buffer);
if (isRecording)
{
micStream.Write(buffer, 0, buffer.Length);
}
}
Now we can start recording by calling the mic.Start() method, and stop recording with mic.Stop().
We can play it back:
SoundEffect recSE = new SoundEffect(micStream.ToArray(), mic.SampleRate, AudioChannels.Mono);
recSE.Play();
Or save it to the application’s isolated storage:
using (var isf = IsolatedStorageFile.GetUserStoreForApplication())
{
using (var isfstream = new IsolatedStorageFileStream("record.pcm", FileMode.Create, isf))
{
isfstream.Write(micStream.ToArray(), 0, (int)micStream.Length);
}
}
Media
The Microsoft.Xna.Framework.Media namespace provides access to the Media Library. With the MediaLibrary class we can play back songs and load pictures from the user’s collection on the device. Saving pictures to the MediaLibrary is also allowed, but this is not true for songs. Another limitation is that we can’t get access to DRM protected music.
To load a picture from the media library, we must select the device’s library source. In the following code snippet I’m loading the first picture of the user’s library:
foreach (var source in MediaSource.GetAvailableMediaSources())
{
if (source.MediaSourceType == MediaSourceType.LocalDevice)
{
ml = new MediaLibrary(source);
if (ml.Pictures.Count > 0)
{
var bitmap = new BitmapImage();
bitmap.SetSource(ml.Pictures[0].GetImage());
img.Source = bitmap;
}
break;
}
}
And we can write to the library using the MediaLibrary.SavePicture method:
ml.SavePicture("TestPic", TitleContainer.OpenStream("Chrysanthemum.jpg"));
Regarding the emulator’s Songs library, it is empty, unlike the Pictures, which contains some sample images. To make things worse, even on the unlocked emulator I couldn’t find any way to upload some mp3s to the emulator’s library.
Still, the Song class is useful, we can load our files (or download, save to isolated storage and then load), and play them as background music. The Song class has also properties for artist, rating, playcount etc. They can be played using the MediaPlayer static class.
Song song = Song.FromUri("I Will Not Bow", new Uri("02.I Will Not Bow.mp3", UriKind.Relative));
if (MediaPlayer.GameHasControl)
{
MediaPlayer.Play(song);
}
In XNA for Windows or Xbox, MediaPlayer can provide visualization data, so you can display audio visualization easily (sound waves, bars etc) via the MediaPlayer.GetVisualizationData method. In Windows Phone 7 it is not available (it won’t throw an exception if we call it, but will only return a bunch of zeros), so you will have to find your own way to give graphical feedback.
MathHelper, Vectors
XNA contains a lot of classes required by game developing including a Vector class with 2,3 or 4 components, Rectangle class with intersect checking and a MathHelper static class. This gives somewhat an out-of-the-box solution for checking collisions, playground etc.
GameServices
The GameServices assembly gives you access to Xbox Live parts, but i’m not sure if it’s usable in a Silverlight based game. Even if you are developing an XNA game, you will need to be a registered Xbox Live developer to gain access to the GameServices.AchievementCollection for example.
Summary
Well, I think I covered the most promising scenarios, but I’m also sure, that a lot of new ideas will come about. On the audio level the integration is good, and apart from a few missing things, it’s definitely usable. The absence of the Game and Graphics assemblies really hurts, with them the Silverlight-XNA integration would reach a whole new level. We don’t have to go too far to imagine an XNA game with Silverlight menus, scoreboard, etc. or a Silverlight application with 3D models loaded with XNA. I hope in the next versions of Windows Phone 7 this level will be reached, and until then we can use the non-graphical components in our Silverlight applications.