Recording Sounds
Using the XNA libraries, you can also record sound. To accomplish this, you can use the Microphone
class (in the Microsoft.Xna.Framework.Audio
namespace), which provides access to the phone’s microphone. The Microphone
class has a static property (called Default
)
that returns the default microphone. This should be the only microphone
that a phone has, so you should be able to reliably use this
microphone. You will also need to store the results of the recording.
The easiest way to do this is to use a Stream
to store the information. In this case, a MemoryStream
is perfect for just storing the recording in memory:
public partial class MainPage : PhoneApplicationPage
{
MemoryStream _recording = null;
DispatcherTimer _updateTimer = new DispatcherTimer();
// Constructor
public MainPage()
{
InitializeComponent();
// Set up timer to call the XNA Dispatcher
//(for example, Game Loop)
_updateTimer.Interval = TimeSpan.FromMilliseconds(50);
_updateTimer.Tick += (s, e) =>
{
FrameworkDispatcher.Update();
};
}
Note that I am using the DispatcherTimer
to ensure we can use the XNA libraries (as detailed previously). Next, you need to turn the microphone on and off:
private void recordButton_Click(object sender, RoutedEventArgs e)
{
// Create a new Memory Stream for the data
_recording = new MemoryStream();
// Start the timer to create the 'game loop'
_updateTimer.Start();
// Start Recording
Microphone.Default.Start();
}
private void stopButton_Click(object sender, RoutedEventArgs e)
{
// Stop Recording
Microphone.Default.Stop();
// Stop the 'game loop'
_updateTimer.Stop();
}
You can see these event handlers are starting
and stopping not only the microphone, but also the “game loop,” which
enables the recording APIs to work. When a new recording is started, a
new MemoryStream
is created to store the recording so that we get a new one for every recording.
The Microphone
class has a BufferReady
event that can accept the data from the microphone. As the buffer fills
with the recording, the event will fire with a small amount of sound
data you need to store:
public partial class MainPage : PhoneApplicationPage
{
MemoryStream _recording = new MemoryStream();
DispatcherTimer _updateTimer = new DispatcherTimer();
// Constructor
public MainPage()
{
InitializeComponent();
// Set up timer to call the XNA Dispatcher
//(for example, Game Loop)
_updateTimer.Interval = TimeSpan.FromMilliseconds(50);
_updateTimer.Tick += (s, e) =>
{
FrameworkDispatcher.Update();
};
// Wire up an event to get the data from the Microphone
Microphone.Default.BufferReady +=
new EventHandler<EventArgs>(_mic_BufferReady);
}
void _mic_BufferReady(object sender, EventArgs e)
{
// Grab the Mic
var mic = Microphone.Default;
// Determine the #/bites needed for our sample
var bufferSize = mic.GetSampleSizeInBytes(mic.BufferDuration);
// Create the buffer
byte[] buffer = new byte[bufferSize];
// Get the Data (and return the number of bytes recorded)
var size = Microphone.Default.GetData(buffer);
// Write the data to our MemoryStream
_recording.Write(buffer, 0, size);
}
As you can see, the BufferReady
event handler first retrieves the size of the buffer (by asking the Microphone
class to get the sample size in bytes). Next, it creates a new buffer of bytes to store the data. Then it calls the Microphone
class to get the data (it takes the data, copies it into the buffer
that is passed in, and returns the number of bytes that were used).
Lastly, it uses the MemoryStream
we
created earlier and writes the new data into the stream. This event will
be called enough times to store the data as it is being recorded.
After the recording is complete, you can do whatever you want with the stream of sound. The Microphone
class returns the data as a .wav file, so you can store it in isolated storage and use it later or just play it back using the SoundEffect
API mentioned earlier:
private void playBackButton_Click(object sender, RoutedEventArgs e)
{
if (Microphone.Default.State == MicrophoneState.Stopped &&
_recording != null)
{
// Load the SoundEffect
SoundEffect effect = new SoundEffect(_recording.ToArray(),
Microphone.Default.SampleRate,
AudioChannels.Mono);
// Tell the XNA Libraries to continue to run
FrameworkDispatcher.Update();
// Play the Sound
effect.Play();
}
}
You can see here that the Microphone
class also has a State
property you can use to ensure that you use the data only after the recording is over. Then, instead of loading the SoundEffect
object from a stream (as shown earlier), this code creates a new SoundEffect
passing in the contents of the stream, the sample rate (which the Microphone
class contains), and the number of audio channels. Otherwise, this code is just like playing any other sound effect.