Skip to content

Instantly share code, notes, and snippets.

@BrianMacIntosh
Last active May 28, 2018 04:47
Show Gist options
  • Save BrianMacIntosh/a7957ab765d5b5417d2878ac11f23cc7 to your computer and use it in GitHub Desktop.
Save BrianMacIntosh/a7957ab765d5b5417d2878ac11f23cc7 to your computer and use it in GitHub Desktop.
This class provides easy access to the Windows Phone microphone hardware with XNA.
using System;
using System.IO;
using Microsoft.Xna.Framework.Audio;
/*
* XNA Windows Phone Easy Microphone Access
* by Brian MacIntosh for ThunderFish Entertainment/BoneFish Studios
*
* Version: 1.1
*
* 11/23/2011: Version 1.0
* - Created
*
* 1/5/2012: Version 1.1
* - Adjusted intensity calculation
* - Added frequency field
*
* 11/21/12
* - Distributed
*
* This library is provided free of charge for commercial and noncommercial use.
* No warranty of fitness for any purpose, express or implied, is given.
*/
/*
* This class provides easy access to the Windows Phone microphone
* hardware. Simply initialize the microphone with ThunderFish.
* TMicrophone.Initialize() when the app is started (OnNavigateTo)
* and deinitialize it with ThunderFish.TMicrophone.Deinitialize()
* when you are done (OnNavigateFrom).
*
* You can then record audio from the microphone using StartRecording()
* and StopRecording().
*
* You can record a specific length of audio from the microphone using
* StartRecording(duration). The TimedRecordingDone event will be invoked
* when the recording is done.
*
* You can monitor the intensity of the microphone readings with the
* Intesity field. The microphone must be recording for the field to
* be updated - you can use StartMonitoring and StopMonitoring to
* "fake" recording for this purpose.
*
* See method descriptions for more information.
*/
namespace Thunderfish
{
/// <summary>
/// This class provides methods for accessing the phone microphone.
/// </summary>
public static class TMicrophone
{
private static Microphone mic;
private static MemoryStream currentData;
private static TimeSpan targetRecordTime;
private static bool isTimed;
private static TimeSpan defaultBufferTime;
/// <summary>
/// Returns true if the microphone is currently recording.
/// </summary>
public static bool IsRecording { get { return mic.State == MicrophoneState.Started; } }
/// <summary>
/// Returns the friendly name of the current microphone.
/// </summary>
public static string MicrophoneName { get { return mic.Name; } }
/// <summary>
/// Returns the sample rate (bytes per second) of the microphone.
/// </summary>
public static int SampleRate { get { return mic.SampleRate; } }
/// <summary>
/// Returns true if the current microphone is a headset.
/// </summary>
public static bool IsHeadset { get { return mic.IsHeadset; } }
public delegate void TimedRecordingEvent(SoundEffect record);
/// <summary>
/// Event that occurs when a timed recording has finished.
/// </summary>
public static event TimedRecordingEvent TimedRecordingDone;
/// <summary>
/// Set up the microphone.
/// </summary>
public static void Initialize()
{
mic = Microphone.Default;
defaultBufferTime = mic.BufferDuration;
mic.BufferReady += BufferSound;
}
/// <summary>
/// Unload the microphone.
/// </summary>
public static void Deinitialize()
{
if (IsRecording)
mic.Stop();
mic.BufferReady -= BufferSound;
if (currentData != null) currentData.Close();
}
/// <summary>
/// Switches to recording from a headset microphone if one is
/// available.
/// </summary>
/// <returns>True if a headset mic was found</returns>
public static bool UseHeadset()
{
if (IsRecording) throw new MicrophoneAlreadyRecordingException();
foreach (Microphone m in Microphone.All)
{
if (m.IsHeadset)
{
mic = m;
defaultBufferTime = mic.BufferDuration;
return true;
}
}
return false;
}
/// <summary>
/// Switches to recording from the built-in microphone.
/// </summary>
/// <returns>True if the built-in mic was found</returns>
public static bool UseBuiltIn()
{
if (IsRecording) throw new MicrophoneAlreadyRecordingException();
foreach (Microphone m in Microphone.All)
{
if (!m.IsHeadset)
{
mic = m;
defaultBufferTime = mic.BufferDuration;
return true;
}
}
return false;
}
/// <summary>
/// Get the last sound intensity reading from the mic.
/// Background noise is about 0.25, significant noises are 0.3 up to 0.5
/// </summary>
public static float Intensity { get; private set; }
/// <summary>
/// Get the frequency of the last reading from the mic (hz)
/// </summary>
//TODO: public static float Frequency { get; private set; }
/// <summary>
/// Start the mic to monitor for sound
/// </summary>
public static void StartMonitoring()
{
mic.BufferDuration = mic.GetSampleDuration(1024);
mic.Start();
}
/// <summary>
/// Stop monitoring for sound
/// </summary>
public static void StopMonitoring()
{
mic.Stop();
mic.BufferDuration = defaultBufferTime;
}
/// <summary>
/// Begin asynchronously recording data from the microphone.
/// </summary>
public static void StartRecording()
{
mic.BufferDuration = defaultBufferTime;
currentData = new MemoryStream();
mic.Start();
isTimed = false;
}
/// <summary>
/// Begin asynchronously recording data from the microphone.
/// Recording will stop automatically after the specified duration
/// and the TimedRecordingDone event will occur.
/// </summary>
/// <param name="duration"></param>
public static void StartRecording(TimeSpan duration)
{
mic.BufferDuration = defaultBufferTime;
currentData = new MemoryStream();
mic.Start();
targetRecordTime = duration;
isTimed = true;
}
/// <summary>
/// Stops recording and returns a SoundEffect of the recorded data.
/// </summary>
/// <returns>A SoundEffect of the recorded data</returns>
public static SoundEffect StopRecording()
{
if (!IsRecording) throw new MicrophoneNotStartedException();
mic.Stop();
SoundEffect ret =
new SoundEffect(currentData.ToArray(), mic.SampleRate, AudioChannels.Mono);
currentData.Close();
currentData = null;
return ret;
}
private const int intensityTakeTop = 5;
private static void BufferSound(object sender, EventArgs e)
{
byte[] buffer = new byte[mic.GetSampleSizeInBytes(mic.BufferDuration)];
int bytesRead = 0;
while ((bytesRead = mic.GetData(buffer, 0, buffer.Length)) > 0)
{
//Record intensity (average of top intensityTakeTop values)
int[] best = new int[intensityTakeTop];
for (int c = 0; c < bytesRead; c++)
for (int d = 0; d < best.Length; d++)
if (best[d] < Math.Abs(buffer[c] - 128))
{
int hold = best[d];
best[d] = Math.Abs(buffer[c] - 128);
if (d > 0)
for (int f = d - 1; d >= 0; d--)
{
int temp = best[f];
best[f] = hold;
hold = temp;
}
break;
}
int total = 0;
foreach (int i in best) total += i;
Intensity = (total / (float)best.Length);
//Record frequency
/*SpeedTest.FFT2 fft = new SpeedTest.FFT2();
fft.init(10);
fft.*/
if (currentData != null)
{
if (isTimed
&& currentData.Length + bytesRead >= mic.GetSampleSizeInBytes(targetRecordTime))
{
currentData.Write(buffer, 0,
mic.GetSampleSizeInBytes(targetRecordTime) - (int)currentData.Length);
TimedRecordingDone(StopRecording());
}
else
currentData.Write(buffer, 0, bytesRead);
}
}
}
private class MicrophoneAlreadyRecordingException : Exception
{
public override string Message
{
get
{
return "Attempted to change microphones while recording.";
}
}
}
private class MicrophoneNotStartedException : Exception
{
public override string Message
{
get
{
return "Attempted to get recording data when microphone was not recording.";
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment