Thanks! Here’s the code…
Program.cs:
using System;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Collections;
using System.Threading;
using System.IO;
using Microsoft.SPOT;
using Microsoft.SPOT.IO;
using Gadgeteer.Networking;
using GT = Gadgeteer;
using GTM = Gadgeteer.Modules;
using Gadgeteer.Modules.GHIElectronics;
namespace MP3Stream
{
public partial class Program
{
void ProgramStarted()
{
sdCard.SDCardMounted += new SDCard.SDCardMountedEventHandler(sdCard_SDCardMounted);
}
private void PlayNetworkMP3()
{
try
{
bool hasHeader = false;
int totalRead = 0;
music.InitPlayback();
music.SetVolume(255);
const Int32 c_microsecondsPerSecond = 1000000;
using (Socket serverSocket = ConnectSocket("192.168.1.160", 8888))
{
String request = "GET /mp3.aspx HTTP/1.1\r\nHost: 192.168.1.160\r\nConnection: Close\r\n\r\n";
Byte[] bytesToSend = Encoding.UTF8.GetBytes(request);
serverSocket.Send(bytesToSend, bytesToSend.Length, 0);
Byte[] buffer = new Byte[4096];
while (serverSocket.Poll(5 * c_microsecondsPerSecond,
SelectMode.SelectRead))
{
if (serverSocket.Available > 0)
{
Array.Clear(buffer, 0, buffer.Length);
int bytesRead = serverSocket.Receive(buffer, 4096, SocketFlags.None);
totalRead += bytesRead;
if (!hasHeader)
{
int i = GetHeaderOffset(buffer);
if (i > 0)
{
Debug.Print("Header Length: " + i);
byte[] tmp = new byte[buffer.Length - i];
Array.Copy(buffer, i, tmp, 0, buffer.Length - i);
music.PlayBlock(tmp);
hasHeader = true;
}
}
else
{
music.PlayBlock(buffer);
}
Debug.Print("Read: " + totalRead);
}
else
{
break;
}
}
}
while (music.IsBusy)
{
Thread.Sleep(10);
}
music.StopPlaying();
}
catch (Exception ex)
{
Debug.Print("PlayNetworkMP3.Exception: " + ex.Message);
}
}
bool PlayLocalStream(string fileName)
{
music.SetVolume(255);
byte[] data = new byte[2048];
int size = 0;
FileStream fs = null;
try
{
fs = new FileStream(fileName, FileMode.Open);
while ((size = fs.Read(data, 0, data.Length)) > 0)
{
music.PlayBlock(data);
}
return true;
}
catch
{
return false;
}
finally
{
if (fs != null)
{
fs.Dispose();
}
}
}
void sdCard_SDCardMounted(SDCard sender, GT.StorageDevice SDCard)
{
VolumeInfo VolInfo = SDCard.Volume;
string DevInfo = VolInfo.Name + " format details:\r\n" +
"Device Flags = " + VolInfo.DeviceFlags + "\r\n" +
"FileSystem = " + VolInfo.FileSystem + "\r\n" +
"FileSystemFlags = " + VolInfo.FileSystemFlags + "\r\n" +
"SerialNumber = " + VolInfo.SerialNumber + "\r\n" +
"TotalFreeSpace = " + VolInfo.TotalFreeSpace + "\r\n" +
"TotalSize = " + VolInfo.TotalSize + "\r\n" +
"VolumeID = " + VolInfo.VolumeID + "\r\n" +
"VolumeLabel = " + VolInfo.VolumeLabel + "\r\n\r\n";
Debug.Print(DevInfo);
PlayLocalStream(SDCard.Volume.RootDirectory + "\\test2.mp3");
//PlayNetworkMP3();
}
int GetHeaderOffset(byte[] buf)
{
for (int i = 0; i < buf.Length - 4; i++)
{
if (buf[i] == '\r' && buf[i + 1] == '\n' && buf[i + 2] == '\r' && buf[i + 3] == '\n')
{
return i + 3;
}
}
return -1;
}
private Socket ConnectSocket(String server, Int32 port)
{
IPHostEntry hostEntry = Dns.GetHostEntry(server);
Socket socket = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
socket.Connect(new IPEndPoint(hostEntry.AddressList[0], port));
return socket;
}
}
}
StreamMusicDriver.cs
using System;
using System.IO;
using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using GT = Gadgeteer;
using GTM = Gadgeteer.Modules;
using GTI = Gadgeteer.Interfaces;
namespace Gadgeteer.Modules.GHIElectronics
{
/// <summary>
/// A Music module for Microsoft .NET Gadgeteer
///
/// Original source from:
/// http://gadgeteer.codeplex.com/SourceControl/changeset/view/14634
///
/// Edited by Mischa Boender
/// added Play(Stream dataStream) overload
/// added PlayBackStreamThread function
///
/// Don't forget to remove the default reference to
/// GTM.GHIElectronics.Music when using this driver
/// </summary>
public class Music : GTM.Module
{
// This example implements a driver in managed code for a simple Gadgeteer module. The module uses a
// single GTI.InterruptInput to interact with a sensor that can be in either of two states: low or high.
// The example code shows the recommended code pattern for exposing the property (IsHigh).
// The example also uses the recommended code pattern for exposing two events: MusicHigh, MusicLow.
// The triple-slash "///" comments shown will be used in the build process to create an XML file named
// GTM.GHIElectronics.Music. This file will provide Intellisense and documention for the
// interface and make it easier for developers to use the Music module.
/////////////////////////////////////////////////////////////////////////////////////////////////////
// Members
/////////////////////////////////////////////////////////////////////////////////////////////////////
private Thread m_recordingThread;
private Thread m_playbackThread;
private InputPort m_dreq;
// Define SPI Configuration for MP3 decoder
SPI m_SPI;
private SPI.Configuration m_dataConfig;
private SPI.Configuration m_cmdConfig;
/// <summary>
/// Playback buffer
/// </summary>
private byte[] m_playBackData = null;
/// <summary>
/// Command buffer
/// </summary>
private byte[] m_cmdBuffer = new byte[4];
bool _stopPlayingRequested = false;
bool _isPlaying = false;
Stream m_recordingStream;
Stream m_playblackStream;
bool m_isRecording = false;
bool m_stopRecordingRequested = false;
ushort[] m_oggPatch;
bool m_bPlayLocked = false;
private byte[] m_sampleBuffer = new byte[] { CMD_READ, SCI_HDAT0 };
static byte[] m_recordingBuffer = new byte[1024];
byte m_leftChannelVolume;
public byte LeftChannelVolume
{
get { return m_leftChannelVolume; }
set { m_leftChannelVolume = value; }
}
byte m_rightChannelVolume;
public byte RightChannelVolume
{
get { return m_rightChannelVolume; }
set { m_rightChannelVolume = value; }
}
/////////////////////////////////////////////////////////////////////////////////////////////////////
// Constant Values
/////////////////////////////////////////////////////////////////////////////////////////////////////
const byte CMD_WRITE = 0x02;
const byte CMD_READ = 0x03;
#region SCI_MODE bits
const ushort SM_RESET = 0x04;
const ushort SM_CANCEL = 0x10;
const ushort SM_TESTS = 0x20;
const ushort SM_SDINEW = 0x800;
const ushort SM_ADPCM = 0x1000;
const ushort SM_LINE1 = 0x4000;
#endregion
#region Registers
/// <summary>
/// Mode control
/// R/W
/// </summary>
const int SCI_MODE = 0x00;
/// <summary>
/// Status of VS1053b
/// R/W
/// </summary>
const int SCI_STATUS = 0x01;
/// <summary>
/// Built-in bass/treble control
/// R/W
/// </summary>
const int SCI_BASS = 0x02;
/// <summary>
/// Clock freq + multiplier
/// R/W
/// </summary>
const int SCI_CLOCKF = 0x03;
/// <summary>
/// Volume control
/// R/W
/// </summary>
const int SCI_WRAM = 0x06;
/// <summary>
/// Volume control
/// R/W
/// </summary>
const int SCI_WRAMADDR = 0x07;
/// <summary>
/// Stream header data 0
/// R
/// </summary>
const int SCI_HDAT0 = 0x08;
/// <summary>
/// Stream header data 1
/// R
/// </summary>
const int SCI_HDAT1 = 0x09;
/// <summary>
/// Volume control
/// R/W
/// </summary>
const int SCI_AIADDR = 0x0A;
/// <summary>
/// Volume control
/// R/W
/// </summary>
const int SCI_VOL = 0x0B;
/// <summary>
/// Application control register 0
/// R/W
/// </summary>
const int SCI_AICTRL0 = 0x0C;
/// <summary>
/// Application control register 1
/// R/W
/// </summary>
const int SCI_AICTRL1 = 0x0D;
/// <summary>
/// Application control register 2
/// R/W
/// </summary>
const int SCI_AICTRL2 = 0x0E;
/// <summary>
/// Application control register 3
/// R/W
/// </summary>
const int SCI_AICTRL3 = 0x0F;
#endregion
#region Recording constants
const int PCM_MODE_JOINTSTEREO = 0x00;
const int PCM_MODE_DUALCHANNEL = 0x01;
const int PCM_MODE_LEFTCHANNEL = 0x02;
const int PCM_MODE_RIGHTCHANNEL = 0x03;
const int PCM_ENC_ADPCM = 0x00;
const int PCM_ENC_PCM = 0x04;
#endregion
/////////////////////////////////////////////////////////////////////////////////////////////////////
#region Command Register operations
/// <summary>
/// Reads 16bit value from a register
/// </summary>
/// <param name="register">Source register</param>
/// <returns>16bit value from the source register</returns>
private ushort CommandRead(byte register)
{
ushort temp;
if (m_bPlayLocked)
{
m_bPlayLocked = false;
// be safe
Thread.Sleep(50);
}
while (m_dreq.Read() == false)
Thread.Sleep(1);
m_SPI.Config = m_cmdConfig;
m_cmdBuffer[0] = CMD_READ;
m_cmdBuffer[1] = register;
m_cmdBuffer[2] = 0;
m_cmdBuffer[3] = 0;
m_SPI.WriteRead(m_cmdBuffer, m_cmdBuffer, 2);
temp = m_cmdBuffer[0];
temp <<= 8;
temp += m_cmdBuffer[1];
m_bPlayLocked = true;
return temp;
}
/// <summary>
/// Writes 16bit value to a register
/// </summary>
/// <param name="register">target register</param>
/// <param name="data">data to write</param>
private void CommandWrite(byte register, ushort data)
{
if (m_bPlayLocked)
{
m_bPlayLocked = false;
// be safe
Thread.Sleep(50);
}
while (m_dreq.Read() == false)
Thread.Sleep(1);
m_SPI.Config = m_cmdConfig;
m_cmdBuffer[0] = CMD_WRITE;
m_cmdBuffer[1] = register;
m_cmdBuffer[2] = (byte)(data >> 8);
m_cmdBuffer[3] = (byte)data;
m_SPI.Write(m_cmdBuffer);
m_bPlayLocked = true;
}
#endregion
#region VS1053b Tests
/// <summary>
/// Run sine test
/// </summary>
public void SineTest()
{
byte[] buf = { 0 };
CommandWrite(SCI_MODE, SM_SDINEW | SM_TESTS | SM_RESET);
byte[] start = { 0x53, 0xEF, 0x6E, 0x7E, 0x00, 0x00, 0x00, 0x00 };
byte[] end = { 0x45, 0x78, 0x69, 0x74, 0x00, 0x00, 0x00, 0x00 };
m_SPI.Config = m_dataConfig;
//Write start sequence
for (int i = 0; i < start.Length; i++)
{
buf[0] = start[i];
m_SPI.Write(buf);
while (m_dreq.Read() == false)
;
}
//Play for 2 seconds
Thread.Sleep(2000);
//Write stop sequence
for (int i = 0; i < end.Length; i++)
{
buf[0] = end[i];
m_SPI.Write(buf);
while (m_dreq.Read() == false)
;
}
}
#endregion
// Note: A constructor summary is auto-generated by the doc builder.
/// <summary></summary>
/// <param name="socketNumber">The socket that this module is plugged in to.</param>
public Music(int socketNumber)
{
// This finds the Socket instance from the user-specified socket number.
// This will generate user-friendly error messages if the socket is invalid.
// If there is more than one socket on this module, then instead of "null" for the last parameter,
// put text that identifies the socket to the user (e.g. "S" if there is a socket type S)
Socket socket = Socket.GetSocket(socketNumber, true, this, null);
socket.EnsureTypeIsSupported(new char[] { 'S' }, this);
// Set up our SPI
m_dataConfig = new SPI.Configuration(socket.CpuPins[5], false, 0, 0, false, true, 2000, socket.SPIModule, socket.CpuPins[3], false);
m_cmdConfig = new SPI.Configuration(socket.CpuPins[6], false, 0, 0, false, true, 2000, socket.SPIModule, socket.CpuPins[3], false);
m_dreq = new InputPort(socket.CpuPins[3], false, Port.ResistorMode.PullUp);
m_SPI = new SPI(m_dataConfig);
Reset();
CommandWrite(SCI_MODE, SM_SDINEW);
CommandWrite(SCI_CLOCKF, 0xa000);
CommandWrite(SCI_VOL, 0x0101); // highest volume -1
if (CommandRead(SCI_VOL) != (0x0101))
{
throw new Exception("Failed to initialize MP3 Decoder.");
}
SetVolume(200, 200);
// This creates an GTI.InterruptInput interface. The interfaces under the GTI namespace provide easy ways to build common modules.
// This also generates user-friendly error messages automatically, e.g. if the user chooses a socket incompatible with an interrupt input.
//this.input = new GTI.InterruptInput(socket, GT.Socket.Pin.Three, GTI.GlitchFilterMode.On, GTI.ResistorMode.PullUp, GTI.InterruptMode.RisingAndFallingEdge, this);
// This registers a handler for the interrupt event of the interrupt input (which is below)
//this.input.Interrupt += new GTI.InterruptInput.InterruptEventHandler(this._input_Interrupt);
}
/// <summary>
/// Performs soft reset
/// </summary>
private void Reset()
{
while (m_dreq.Read() == false)
Thread.Sleep(1);
CommandWrite(SCI_MODE, SM_SDINEW | SM_RESET);
while (m_dreq.Read() == false)
Thread.Sleep(1);
Thread.Sleep(1);
CommandWrite(SCI_CLOCKF, 0xa000);
}
/// <summary>
/// Set volume for both channels. Valid values 0-255
/// </summary>
/// <param name="leftChannelVolume">0 - silence, 255 - loudest</param>
/// <param name="rightChannelVolume">0 - silence, 255 - loudest</param>
public void SetVolume(byte leftChannelVolume, byte rightChannelVolume)
{
//ErrorPrint("asdf");
// Cap the volume
if (leftChannelVolume > 255)
m_leftChannelVolume = 255;
else if (leftChannelVolume < 0)
m_leftChannelVolume = 0;
else
m_leftChannelVolume = leftChannelVolume;
if (rightChannelVolume > 255)
m_rightChannelVolume = 255;
else if (rightChannelVolume < 0)
m_rightChannelVolume = 0;
else
m_rightChannelVolume = rightChannelVolume;
CommandWrite(SCI_VOL, (ushort)((255 - leftChannelVolume) << 8 | (255 - rightChannelVolume)));
}
/// <summary>
/// Set volume for both channels. Valid values 0-255
/// </summary>
/// <param name="_dualChannelVolume">0 - silence, 255 - loudest</param>
public void SetVolume(byte _dualChannelVolume)
{
// Cap the volume
if (_dualChannelVolume > 255)
m_leftChannelVolume = m_rightChannelVolume = 255;
else if (_dualChannelVolume < 0)
m_leftChannelVolume = m_rightChannelVolume = 0;
else
m_leftChannelVolume = m_rightChannelVolume = _dualChannelVolume;
CommandWrite(SCI_VOL, (ushort)((255 - m_leftChannelVolume) << 8 | (255 - m_rightChannelVolume)));
}
/// <summary>
/// Returns true is shield is playing or recording audio
/// </summary>
public bool IsBusy
{
get
{
return _isPlaying || m_isRecording;
}
}
private void WritePatchFromArray(ushort[] patch)
{
m_SPI.Config = m_cmdConfig;
m_cmdBuffer[0] = 0x02;
int count = 0;
int i = 0;
ushort value = 0;
while (i < patch.Length)
{
m_cmdBuffer[1] = (byte)patch[i++];
count = patch[i++];
if ((count & 0x8000) != 0)
{
count &= 0x7FFF;
value = patch[i++];
m_cmdBuffer[2] = (byte)(value >> 8);
m_cmdBuffer[3] = (byte)value;
while (count-- > 0)
{
m_SPI.Write(m_cmdBuffer);
while (m_dreq.Read() == false)
;
}
}
else
{
while (count-- > 0)
{
value = patch[i++];
m_cmdBuffer[2] = (byte)(value >> 8);
m_cmdBuffer[3] = (byte)value;
m_SPI.Write(m_cmdBuffer);
while (m_dreq.Read() == false)
;
}
}
}
}
private void WritePatchFromStream(Stream s)
{
m_SPI.Config = m_cmdConfig;
m_cmdBuffer[0] = 0x02;
byte highByte = 0;
byte lowByte = 0;
int count = 0;
while (true)
{
while (m_dreq.Read() == false)
Thread.Sleep(1);
//register address
m_cmdBuffer[1] = (byte)s.ReadByte();
s.ReadByte();
lowByte = (byte)s.ReadByte();
highByte = (byte)s.ReadByte();
count = (highByte & 0x7F) << 8 | lowByte;
if ((highByte & 0x80) != 0)
{
m_cmdBuffer[3] = (byte)s.ReadByte();
m_cmdBuffer[2] = (byte)s.ReadByte();
while (count-- > 0)
{
m_SPI.Write(m_cmdBuffer);
}
}
else
{
while (count-- > 0)
{
m_cmdBuffer[3] = (byte)s.ReadByte();
m_cmdBuffer[2] = (byte)s.ReadByte();
m_SPI.Write(m_cmdBuffer);
}
}
}
}
#region Playback
/// <summary>
/// Play samples from byte array async
/// </summary>
/// <param name="data"></param>
public void Play(byte[] data)
{
_isPlaying = true;
_stopPlayingRequested = false;
m_playBackData = data;
m_playbackThread = new Thread(new ThreadStart(this.PlayBackThreadFunction));
m_playbackThread.Start();
}
/// <summary>
/// Play from stream
/// </summary>
/// <param name="dataStream"></param>
public void Play(Stream dataStream)
{
_isPlaying = true;
_stopPlayingRequested = false;
m_playblackStream = dataStream;
PlayBackStreamThreadFunction();
m_playbackThread = new Thread(new ThreadStart(this.PlayBackStreamThreadFunction));
m_playbackThread.Start();
}
/// <summary>
/// Init block level playback
/// </summary>
/// <param name="dataStream"></param>
public void InitPlayback()
{
_isPlaying = true;
_stopPlayingRequested = false;
}
/// <summary>
/// Play fragment from byte[] source
/// </summary>
/// <param name="dataStream"></param>
public void PlayBlock(byte[] data)
{
byte[] block = new byte[32];
int size = data.Length - data.Length % 32;
m_SPI.Config = m_dataConfig;
for (int i = 0; i < size; i += 32)
{
while (m_dreq.Read() == false)
{
Thread.Sleep(1); // wait till done
}
Array.Copy(data, i, block, 0, 32);
m_SPI.Write(block);
}
}
/// <summary>
/// Playback thread function
/// </summary>
private void PlayBackThreadFunction()
{
byte[] block = new byte[32];
int size = m_playBackData.Length - m_playBackData.Length % 32;
m_SPI.Config = m_dataConfig;
for (int i = 0; i < size; i += 32)
{
if (_stopPlayingRequested)
break;
Array.Copy(m_playBackData, i, block, 0, 32);
while (m_dreq.Read() == false)
Thread.Sleep(1); // wait till done
// fake spinlock is fake
while (true)
{
if (!m_bPlayLocked)
{
//someone still has the spi
Thread.Sleep(1);
}
else
{
// we can have the spi back
m_SPI.Config = m_dataConfig;
break;
}
}
// pause goes here
//while(paused)
//sleep(1)
m_SPI.Write(block);
}
this.OnMusicFinished(this);
Reset();
m_playBackData = null;
_isPlaying = false;
}
/// <summary>
/// Playback stream thread function
/// </summary>
private void PlayBackStreamThreadFunction()
{
byte[] block = new byte[32];
m_SPI.Config = m_dataConfig;
while (m_playblackStream.Read(block, 0, 32) > 0)
{
if (_stopPlayingRequested)
break;
while (m_dreq.Read() == false)
{
Thread.Sleep(1); // wait till done
}
m_SPI.Write(block);
}
this.OnMusicFinished(this);
if (m_playblackStream != null)
{
m_playblackStream.Close();
m_playblackStream = null;
}
Reset();
_isPlaying = false;
}
public delegate void MusicFinishedPlayingEventHandler(Music sender);
public event MusicFinishedPlayingEventHandler MusicFinished;
private MusicFinishedPlayingEventHandler _MusicFinished;
protected void OnMusicFinished(Music sender)
{
if (this.MusicFinished == null)
this.MusicFinished = new MusicFinishedPlayingEventHandler(this.OnMusicFinished);
this.MusicFinished(sender);
}
public void StopPlaying()
{
_stopPlayingRequested = true;
this.OnMusicFinished(this);
}
#endregion
/// <summary>
/// Optimized CommandRead for SCI_HDAT0
/// </summary>
/// <param name="nSamples"></param>
private void ReadData(int nSamples)
{
int i = 0;
m_SPI.Config = m_cmdConfig;
while (i < nSamples)
{
m_SPI.WriteRead(m_sampleBuffer, 0, 2, m_recordingBuffer, i * 2, 2, 2);
i++;
}
}
/// <summary>
/// Request recording to stop
/// </summary>
public void StopRecording()
{
if (!m_stopRecordingRequested)
m_stopRecordingRequested = true;
}
#region Ogg Vorbis Recording
public void RecordOggVorbis(Stream recordingStream, ushort[] oggPatch)
{
m_isRecording = true;
m_stopRecordingRequested = false;
m_oggPatch = oggPatch;
m_recordingStream = recordingStream;
m_recordingThread = new Thread(new ThreadStart(this.RecordOggVorbisThreadFunction));
m_recordingThread.Start();
}
private void RecordOggVorbisThreadFunction()
{
Reset();
SetVolume(255, 255);
CommandWrite(SCI_CLOCKF, 0xc000);
CommandWrite(SCI_BASS, 0x0000);
CommandWrite(SCI_AIADDR, 0x0000);
CommandWrite(SCI_WRAMADDR, 0xC01A);
CommandWrite(SCI_WRAM, 0x0002);
//Load Ogg Vorbis Encoder
WritePatchFromArray(m_oggPatch);
CommandWrite(SCI_MODE, (ushort)(CommandRead(SCI_MODE) | SM_ADPCM | SM_LINE1));
CommandWrite(SCI_AICTRL1, 0);
CommandWrite(SCI_AICTRL2, 4096);
////0x8000 - MONO
////0x8080 - STEREO
CommandWrite(SCI_AICTRL0, 0x0000);
CommandWrite(SCI_AICTRL3, 0);
//CommandWrite(SCI_AICTRL3, 0x40);
CommandWrite(SCI_AIADDR, 0x0034);
while (m_dreq.Read() == false)
;
int totalSamples = 0;
bool stopRecording = false;
bool stopRecordingRequestInProgress = false;
int samples = 0;
while (!stopRecording)
{
if (m_stopRecordingRequested && !stopRecordingRequestInProgress)
{
CommandWrite(SCI_AICTRL3, 0x0001);
m_stopRecordingRequested = false;
stopRecordingRequestInProgress = true;
}
if (stopRecordingRequestInProgress)
{
stopRecording = ((CommandRead(SCI_AICTRL3) & 0x0002) != 0);
}
samples = CommandRead(SCI_HDAT1);
if (samples > 0)
{
totalSamples = samples > 512 ? 512 : samples;
ReadData(totalSamples);
if (m_recordingStream != null)
m_recordingStream.Write(m_recordingBuffer, 0, totalSamples << 1);
//Debug.Print("I have: " + samples.ToString() + " samples");
}
//Debug.Print("no data");
}
samples = CommandRead(SCI_HDAT1);
while (samples > 0)
{
totalSamples = samples > 512 ? 512 : samples;
ReadData(totalSamples);
if (m_recordingStream != null)
m_recordingStream.Write(m_recordingBuffer, 0, totalSamples << 1);
Debug.Print("I have: " + samples.ToString() + " samples");
samples = CommandRead(SCI_HDAT1);
}
if (m_recordingStream != null)
{
m_recordingStream.Close();
m_recordingStream = null;
}
Reset();
m_isRecording = false;
m_oggPatch = null;
}
#endregion
private void _input_Interrupt(GTI.InterruptInput input, bool value)
{
this.OnMusicEvent(this, value ? MusicState.Low : MusicState.High);
}
private GTI.InterruptInput input;
/// <summary>
/// Gets a value that indicates whether the state of this Music is high.
/// </summary>
public bool IsHigh
{
get
{
return this.input.Read();
}
}
/// <summary>
/// Represents the state of the <see cref="Music"/> object.
/// </summary>
public enum MusicState
{
/// <summary>
/// The state of Music is low.
/// </summary>
Low = 0,
/// <summary>
/// The state of Music is high.
/// </summary>
High = 1
}
/// <summary>
/// Represents the delegate that is used to handle the <see cref="MusicHigh"/>
/// and <see cref="MusicLow"/> events.
/// </summary>
/// <param name="sender">The <see cref="Music"/> object that raised the event.</param>
/// <param name="state">The state of the Music</param>
public delegate void MusicEventHandler(Music sender, MusicState state);
/// <summary>
/// Raised when the state of <see cref="Music"/> is high.
/// </summary>
/// <remarks>
/// Implement this event handler and the <see cref="MusicLow"/> event handler
/// when you want to provide an action associated with Music activity.
/// The state of the Music is passed to the <see cref="MusicEventHandler"/> delegate,
/// so you can use the same event handler for both Music states.
/// </remarks>
public event MusicEventHandler MusicHigh;
/// <summary>
/// Raised when the state of <see cref="Music"/> is low.
/// </summary>
/// <remarks>
/// Implement this event handler and the <see cref="MusicHigh"/> event handler
/// when you want to provide an action associated with Music activity.
/// Since the state of the Music is passed to the <see cref="MusicEventHandler"/> delegate,
/// you can use the same event handler for both Music states.
/// </remarks>
public event MusicEventHandler MusicLow;
private MusicEventHandler onMusic;
/// <summary>
/// Raises the <see cref="MusicHigh"/> or <see cref="MusicLow"/> event.
/// </summary>
/// <param name="sender">The <see cref="Music"/> that raised the event.</param>
/// <param name="MusicState">The state of the Music.</param>
protected virtual void OnMusicEvent(Music sender, MusicState MusicState)
{
if (this.onMusic == null)
{
this.onMusic = new MusicEventHandler(this.OnMusicEvent);
}
if (Program.CheckAndInvoke((MusicState == MusicState.High ? this.MusicHigh : this.MusicLow), this.onMusic, sender, MusicState))
{
switch (MusicState)
{
case MusicState.High:
this.MusicHigh(sender, MusicState);
break;
case MusicState.Low:
this.MusicLow(sender, MusicState);
break;
}
}
}
}
}