Welcome to Pete Brown's 10rem.net

First time here? If you are a developer or are interested in Microsoft tools and technology, please consider subscribing to the latest posts.

You may also be interested in my blog archives, the articles section, or some of my lab projects such as the C64 emulator written in Silverlight.

(hide this)

Generating sound at runtime using XAudio2 in a Windows Store app

Pete Brown - 29 November 2012

In this post, I'll briefly explain how to generate sound in real time in a Windows Store C++ app using XAudio2.

I've always liked messing around with synthesizers. Quite some time ago, I built a simple synthesizer using Silverlight. It was cool, but very processor intensive. All the sound was generated in real time, and was then pushed into buffers which were read by the media pipeline. The pipeline was never meant for that type of sound generation so although it worked well, it had a fair bit of lag.

For a project I'm working on in my spare time, I need to be able to generate sound in real time for a Windows Store app. The user interface for this app also has to be really intuitive. It may have some interesting visualizations, but it'll be mostly standard UI type controls. I figured this was a good time to try out C++ with XAML in the Windows Store.

The project

So, I created a new Windows Store C++ plus XAML plus DirectX app. There are a couple different audio pipelines I could use. I decided to go with XAudio2 rather than WASAPI, as XAudio2 is much easier to get into, and has decent performance. Is XAudio2 fast enough for real-time sound generation? It has been shown to be so for many apps. In fact, the MorphWiz and Tachyon apps that Jordan Rudess played before Build 2012 used XAudio2 (although the developer may have converted to WASAPI by now in order to eek out some extra performance).

I added a single class named "Synthesizer". It's a bit much for me to call this a "synthesizer", but hey. :)

Keep in mind that I'm not a good C++ programmer; I'm still re-learning. The code here is shown just as a proof of concept, not best practices or anything like that.

The Synthesizer header file

#pragma once

//#include <wrl.h>
#include <pch.h>
#include <xaudio2.h>
#include <xaudio2fx.h>
//#include <mmreg.h>

#define VOICE_BUFFER_SAMPLE_COUNT (44100 * 2)

ref class Synthesizer sealed
{
private:
int m_sampleBits;

IXAudio2MasteringVoice* m_masterVoice; // cannot use ComPtr for this
IXAudio2* m_audioEngine;


// this is temp stuff which will be rolled into another class
IXAudio2SourceVoice* m_voice;
XAUDIO2_BUFFER m_buffer;
float m_bufferData[VOICE_BUFFER_SAMPLE_COUNT];

public:
Synthesizer(uint32 sampleRate);

};

There's nothing overly exciting going on in that class right now. The thing that took me the longest to figure out was that I couldn't reference any of the IXAudio2* interfaces (other than the main one) using a ComPtr. The error was suitably cryptic for a noob like me.

The Synthesizer Class has a single XAudio2 voice (you can have multiple voices). And no: no destructor, I don't even clean up after myself. Remember what I said about this not showing best practices?

Synthesizer class file

#include <pch.h>
#include <math.h>
#include <Synthesizer.h>


Synthesizer::Synthesizer(uint32 sampleRate)
{
m_sampleBits = 32; // 32 for IEEE float

// initialize XAudio 2
DX::ThrowIfFailed(XAudio2Create(&m_audioEngine, 0, XAUDIO2_DEFAULT_PROCESSOR));
DX::ThrowIfFailed(m_audioEngine->CreateMasteringVoice(&m_masterVoice, XAUDIO2_DEFAULT_CHANNELS, sampleRate));

// 2 channels because this is stereo
int channels = 2;

// set up wave format using my good friend WAVEFORMATEX
WAVEFORMATEX wfx;
wfx.wBitsPerSample = m_sampleBits;
wfx.nAvgBytesPerSec = sampleRate * channels * m_sampleBits / 8;
wfx.nChannels = channels;
wfx.nBlockAlign = channels * m_sampleBits / 8;
wfx.wFormatTag = WAVE_FORMAT_IEEE_FLOAT; // or could use WAVE_FORMAT_PCM
wfx.nSamplesPerSec = sampleRate;
wfx.cbSize = 0; // set to zero for PCM or IEEE float

DX::ThrowIfFailed(m_audioEngine->CreateSourceVoice(&m_voice, (WAVEFORMATEX*)&wfx));

//float noteFrequency = 22.5; // A0
float noteFrequency = 55; // A1
//float noteFrequency = 110; // A2
//float noteFrequency = 220; // A3
//float noteFrequency = 440; // A4

// there's probably a #define for this somewhere
float pi = 3.14159265;

// fill the buffer
for (int i = 0; i < VOICE_BUFFER_SAMPLE_COUNT; i+=2)
{
m_bufferData[i] = sin(i * 2 * pi * noteFrequency/sampleRate);

// stereo buffer. Slight detuning of the second tone
m_bufferData[i+1] = sin(i * 2 * pi * (noteFrequency + 2)/sampleRate);
}

// the buffer will be looped infinitely
m_buffer.AudioBytes = VOICE_BUFFER_SAMPLE_COUNT * m_sampleBits / 8;
m_buffer.PlayBegin = 0;
m_buffer.PlayLength = 0; // play entire buffer
m_buffer.LoopBegin = 0;
m_buffer.LoopLength = 0; // loop entire buffer
m_buffer.LoopCount = XAUDIO2_LOOP_INFINITE;
m_buffer.pAudioData = (BYTE *)&m_bufferData;
m_buffer.pContext = NULL;

// wire up the buffer
DX::ThrowIfFailed(m_voice->SubmitSourceBuffer(&m_buffer));

// start playing sound. Yeah, this is in the constructor. Yeah, I know.
DX::ThrowIfFailed(m_voice->Start(0));
}

The code is commented in-line and should be reasonably easy to follow. Basically all I do is initialize XAudio2, fill a buffer with samples of a sine wave, then tell XAudio2 to play that buffer, looping it forever.

References

Then, I added a synth instance to the header file for the main XAML page, and then from the main XAML page's constructor, I simply created an instance:

DirectXPage.xaml.cpp header addition

#pragma once

#include "DirectXPage.g.h"
#include "SimpleTextRenderer.h"
#include "BasicTimer.h"
#include <Synthesizer.h>

namespace PeteBrown_DroneSynthApp
{
/// <summary>
/// A DirectX page that can be used on its own. Note that it may not be used within a Frame.
/// </summary>
[Windows::Foundation::Metadata::WebHostHidden]
public ref class DirectXPage sealed
{
public:
...

private:
...

Synthesizer^ m_synth;
};
}

DirectXPage.xaml.cpp constructor addition

DirectXPage::DirectXPage() :
m_renderNeeded(true),
m_lastPointValid(false)
{
InitializeComponent();

...

m_synth = ref new Synthesizer(44100);
}

Once again, yeah, I know I'm doing all this from the constructor. Don't shoot me :)

I then ran the app, and got the wonderful DX + XAML UI (two bits of text) with what sounds like a hearing test playing in the background. Clearly you can take this much further and do all sorts of neat things with audio generation in real time.  For me, this was just a little proof-of-concept I had to work through before investing any real time in this side project. Concept proven, I'll move on to some other parts of the app.

For more information on XAudio2 on Windows, see MSDN. XAudio2 is a technology shared with the Windows Store (x86, 64, and ARM), the desktop, Xbox 360, and with Windows Phone 8, so once you learn it, you can apply it in all three places. For more information on XAudio2 on Windows Phone 8, see this MSDN page.

     
posted by Pete Brown on Thursday, November 29, 2012
filed under:      

10 comments for “Generating sound at runtime using XAudio2 in a Windows Store app”

  1. Simon Ferquelsays:
    Nice post :)
    For a slightly safer (and more elegant) C++, consider using ComPtr<> smart pointer for com objects, and std::shared_ptr<> or std::unique_ptr<> for standard C++ objects. For objects like IXaudio2*Voice that need to be destroyed using a member function, you can create a custom deleter class that will be called when the smart pointer decides it's time to delete.
  2. John Stovinsays:
    I'm working on something similar. One pain is that Win8 will not let you call CreateMasteringVoice() with a StreamCategory parameter of AudioCategory_BackgroundCapableMedia. This means that you can't create a synth that continues playing in the background after you switch tasks. The only way to do that seems to be to use the media player control, but that only allows you to play pre-recorded audio from a file.

    I think can understand why - they don't want a background process consuming high levels of resources to dynamically generate audio, and thereby accidentally suck all the life out of the battery on your portable device.

    But it does mean that trying to do anything beyond "toy" synth apps is probably a no-go for the Windows Store platform.

    I wonder if you could use PlayTo to chain synths/effects together on the same device?

    Also, were you aware of the SharpDX.XAudio2 project on NuGet? https://nuget.org/packages/SharpDX.XAudio2 Haven't tried it, but it might save having to re-learn C++ :)
  3. Petesays:
    @Simon

    I tried, but the code wouldn't compile if I used ComPtr with the IXAudio2 interfaces. It complained about the lack of a public Release method as code in Wrl/client tried to call Release. That took me forever to figure out, as I went with ComPtr to start. Any suggestions there?

    @John

    Not sure what the options are there, but I recall someone talking to me about this problem some time ago.

    It does limit the scenarios, but I don't think it limits you to "toy" synths. I've seen a number of performance and synth apps (most not yet in the store) which do quite well front and center without background capabilities.

    Pete
  4. Pete Brownsays:
    @TheAgent

    As Kevin pointed out, yes, you can use XAudio2, but you'll need to either use it from C++ or use something like SharpDX.

    For lowest possible latency with audio (If that's a goal) you'll need to use WASAPI and C++.

    Pete

Comment on this Post

Remember me