Last active
May 28, 2018 04:47
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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