
Sound Effects in Windows Phone 8 Applications

Referring to the Windows Phone Silverlight documentation, you can find the following Media for Windows Phone and Playing a sound effect articles . Based on the content of the documents, one can come to the conclusion that there are only two ways to reproduce effects in applications: use MediaElement or XNA. Consider each of these methods in more detail.
Play sound effects using MediaElement
The easiest and most native way to play a sound effect is to use the MediaElement control. This control provides ample opportunities for playing audio and video content and can be used for our purposes.
To use the control, you must add it to the visual tree of the current page:
To do this, add the following code to the XAML of the page:
Or create a control from the cedebehind page using the following code:
private MediaElement _mediaElement;
private void CreateMediaElement()
{
_mediaElement = new MediaElement
{
AutoPlay = true
};
LayoutRoot.Children.Add(_mediaElement);
}
You can notice from the code that the control has the AutoPlay property set, this switches the control to the mode of instant playback of the transferred content.
To play a sound file, you must pass its address to the Source property of the control.
private void PlaySoundClick(object sender, RoutedEventArgs e)
{
viewMediaElement.Source = new Uri("/Assets/sound.wav", UriKind.Relative);
}
You can use XAML data binding to set the value of the Source property, which completely eliminates codebehind content.
Advantages of using this method of reproducing sound effects:
- Ease of implementation and use
- Play file contents in any of the formats supported by the operating system
Disadvantages:
- The control must be added to the visual tree of the application.
- In this way, you can play only one sound at a time, the documentation indicates that only one control is allowed.
- Sound playback is tied to the user interface and requires synchronization with the UI stream.
- Unable to organize seamless loop playback.
Play sound effects with XNA
To play sound effects, you can use the XNA library. Despite the fact that this library was developed to work within the framework of the XNA infrastructure when developing game projects, it can be used in Windows Phone applications to play sounds.
To do this is quite simple:
private void PlaySoundClick(object sender, RoutedEventArgs e)
{
var stream = TitleContainer.OpenStream("Assets/sound.wav");
var soundEffect = SoundEffect.FromStream(stream);
var instance = soundEffect.CreateInstance();
FrameworkDispatcher.Update();
instance.Play();
}
Pay attention to two interesting points. It is not necessary to call the CreateInstance method for the sound effect, you can play it simply by calling the Play () method on the SoundEffect instance, however only the explicitly created instance of the effect allows you to fully control the effect and fine tune it. Ignorance of this point led to the widespread misconception of WP SL programmers that SoundEffect is uncontrollable after playback is started.
A call to the FrameworkDispatcher.Update () method is necessary to emulate the XNA game loop, which is required for the XNA library to work correctly.
Advantages of playing sound using XNA SoundEffect:
- Ease of use.
- Advanced settings for sound effects, such as volume, speed, repetition, etc.
Disadvantages:
- Deprecated technology that is no longer supported by Microsoft.
- Play only WAV files in PCM format, which consumes a large amount of memory.
- The need for additional manipulations, in the form of a game cycle emulation.
Using XAudio2 to play sound effects
Unfortunately, there are no other ways to play sound effects for WP 8 applications out of the box. However, for native applications, the low-level XAudio2 sound effects technology is available. This technology is positioned as a technology for reproducing sound effects in games, but it can be used to play sound effects in Windows Phone 8.
Since it is impossible to use XAudio2 directly from a WP application. We’ll develop a simple Windows Runtime component that gives WP Silverlight the ability to play sound effects.
Windows Runtime components for the Windows Phone 8 operating system are developed only using the C ++ language.
Add a new Windows Runtime Component project (Windows Phone 8.0)

For the correct assembly of the project, it is necessary to indicate to the linker that it is necessary to use the xaudio2.lib library. To do this, open the project properties, on the Linker -> Input tab, and add the xaudio2.lib library to the Additional Dependencies list

Define the class of the created component:
public ref class SoundEffect sealed
{
private:
std::shared_ptr _waveData;
Microsoft::WRL::ComPtr _audioEngine;
IXAudio2MasteringVoice* _masteringVoice;
IXAudio2SourceVoice* _sourceVoice;
public:
SoundEffect(const Array^ source);
void Play();
void Stop();
};
The class constructor accepts a single parameter, an array of bytes with the contents of the audio file in PCM or ADPCM format. To control the sound effect are the Play and Stop methods. When the Play method is called, the effect should be played from the beginning; when Stop is called, the effect should stop playing.
Class fields are intended for storing references to XAudio2 components and descriptions of the WaveData sound file. The IXAudio2 component is a COM object; therefore, the Microsoft :: WRL :: ComPrt container is used to store it, which ensures the correct operation of the link counting mechanism and the release of the object. The rest of the pointers to XAudio2 objects do not use ComPtr and the reference count for their lifetime is controlled by the IXAudio2 object.
Initializing XAudio2
SoundEffect::SoundEffect(const Array^ source)
{
_waveData = std::make_shared(source);
auto hr = XAudio2Create(&_audioEngine);
if (FAILED(hr))
{
throw ref new Platform::Exception(hr);
}
hr = _audioEngine->CreateMasteringVoice(&_masteringVoice);
if (FAILED(hr))
{
throw ref new Platform::Exception(hr);
}
hr = _audioEngine->CreateSourceVoice(&_sourceVoice, _waveData->Format);
if (FAILED(hr))
{
throw ref new Platform::Exception(hr);
}
hr = _audioEngine->StartEngine();
if (FAILED(hr))
{
throw ref new Platform::Exception(hr);
}
}
The WaveData class will be discussed below, now it’s enough to know that it extracts information about the audio data format and the audio data themselves from the sound file in wav format.
The first step is to call the XAudio2Create function to get a link to an object that implements the IXAudio2 interface.
The next step is to create an object that implements the IXAudioMasteringVoice interface, this object is responsible for combining data from the original voices into a single audio stream. In fact, this object is a sound mixer.
By calling the CreateSourceVoice method, create an original voice representing the sound stream from the sound effect file. When creating the original voice, you must specify the format of the audio data that will be transferred to it for playback.
Read more about XAudio2 voices atXAudio2 Voices .
The final step in initializing XAudio2 is to call the method, StartEngine, which starts processing the sound effects with the core of the library.
Methods for starting and ending an effect
void SoundEffect::Play()
{
Stop();
XAUDIO2_BUFFER playBuffer = { 0 };
playBuffer.AudioBytes = _waveData->Length;
playBuffer.pAudioData = _waveData->Data;
playBuffer.Flags = XAUDIO2_END_OF_STREAM;
auto hr = _sourceVoice->SubmitSourceBuffer(&playBuffer);
if (SUCCEEDED(hr))
{
hr = _sourceVoice->Start(0, XAUDIO2_COMMIT_NOW);
}
}
void SoundEffect::Stop()
{
auto hr = _sourceVoice->Stop();
if (SUCCEEDED(hr))
{
_sourceVoice->FlushSourceBuffers();
}
}
Before starting playback, you need to fill out the XAUDIO2_BUFER structure with the sound file data and set it using the SubmitSourceBuffer method as a data source for the original voice.
And the last step is to launch the voice for playback, using the Start method;
In fact, you can set the voice data source once, then only start playback using the Play method, however, I met the situation of the Stop method that worked incorrectly and, as a result, subsequent errors when trying to start playback. Setting the source buffer at each play solved this problem.
Preparing and downloading audio files
XAudio2 library on WP 8 can play audio data in PCM and ADPCM formats. To prepare the ADPCM files, you need to use the adpcmencode.exe utility; its use guarantees the compatibility of the received file with the XAudio2 library.
In fact, I could not prepare a single file in ADPCM format using third-party utilities, all of them turned out to be incompatible, despite the formal compliance with the requirements.
Two required components must be extracted from the wav sound file: the audio data format and directly the audio stream data in PCM or ADPCM format. To get these components, you need to parse the wav file.
A wav file is a Riff container, more about which you can read: WIKI RIFF ,Resource Interchange File Format Services and ADPCM RIFF .
The WavData class is responsible for extracting data from a wav file and its subsequent storage.
private struct WaveData
{
private:
const Array^ _buffer;
ChunkInfo FindChunk(const Array^ buffer, const RiffType& chunkType) const;
void Read(const Array^ buffer);
public:
WaveData(const Array^ buffer);
const WAVEFORMATEX* Format;
const byte* Data;
uint32 Length;
};
When parsing a file, the class checks:
- file contains wave data
- the file contains the frm (format) part and that its size is not less than the size of the WAVEFORMATEX structure
- PCM or ADPCM audio data format
- file contains data part
Pointers to the data obtained in paragraphs 2 and 4 are stored for future use.
Using the generated component
Connect the created component to the WP Silverlight application and use it to play the sound effect:
private async void PlaySoundClick(object sender, RoutedEventArgs e)
{
var soundEffect = await CreateSoundEffectFromFile("ms-appx:///Assets/sound.wav");
soundEffect.Play();
}
private async Task CreateSoundEffectFromFile(string fileUri)
{
if (string.IsNullOrWhiteSpace(fileUri))
{
return null;
}
try
{
var file = await StorageFile.GetFileFromApplicationUriAsync(new Uri(fileUri));
var buffer = await ReadFile(file);
return new XAudio2SoundEffect.SoundEffect(buffer);
}
catch (Exception ex)
{
return null;
}
}
private async Task ReadFile(StorageFile file)
{
using (var stream = await file.OpenReadAsync())
using (var reader = new DataReader(stream))
{
var buffer = new byte[stream.Size];
await reader.LoadAsync((uint)stream.Size);
reader.ReadBytes(buffer);
return buffer;
}
}
Instead of a conclusion
Having created a component that uses the XAudio2 library to play sound effects, we got rid of obsolete XNA libraries and got rid of the disadvantages inherent in MediaElement.
The created component implements the simplest sound effect control functions. All features provided by XAudio2 are available to the developer, such as: volume control, playback speed control, loop playback, sound effects and much more.
The source code for the test project is available on Github: https://github.com/Viacheslav01/WPSoundEffect