Problem with MP3+Network on Hydra

I have a Hydra with the latest Ethernet firmware that I’m trying to stream MP3s over the network with. They start playing fine, but within a few seconds, playback stops abruptly and my LCD screen has register addresses on it. No exceptions are thrown. In an attempt to determine if there was an issue with with how I was reading the data over the network or with the mp3 playback itself, I also tried reading a file from an SD card and the same thing happened. Several 128kbps MP3s have been tried to rule out a problem with the file itself, both over the network and on the SD card.

I got it working this evening from the SD card, and then realized that what I forgot to do is actually connect the Ethernet cable. If it’s unplugged, it plays fine. Plugged in, it fails within seconds. The problem seems to be tied to having an active network connection.

My configuration is USB power in socket 2, ENC28 jack in socket 3, MP3 shield in socket 4, SD card shield in socket 8, and the LCD screen in sockets 10, 11, 12, and 13 (all GHI Gadgeteer parts). I did also try running from a 9VDC 650mA source in case it was a power problem.

Thanks,
David

Hi leapis,

Can you provide source code for your project so we can test it? From what you are saying it sounds as if it is writing to invalid memory and it is in essence crashing.

On a brighter note, welcome to the community.

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;
                }
            }
        }
    }
}

Welcome to the community David. :wink:

Way too much code to be able ot debug and help. Can you please simplify it to something we can work with?

A shorter version of the 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);
        }

        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(Exception ex)
            {
                return false;
            }
            finally
            {
                if (fs != null)
                {
                    fs.Dispose();
                }
            }
        }

        void sdCard_SDCardMounted(SDCard sender, GT.StorageDevice SDCard)
        {
            PlayLocalStream(SDCard.Volume.RootDirectory + "\\test.mp3");
        }
    }
}

The StreamMusicDriver.cs is from CodePlex, with just my addition of the PlayBlock() method, shown below:


   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);
            }
        }

Thanks for your help,
D

I did want to mention that the code that interfaces the MP3 module is an almost direct port of the existing FEZ MP3 shield code with additional logic for OGG recording. To simplify research, I have reduced it to the minimum code necessary below:


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
{
    public class Music : GTM.Module
    {
        private InputPort m_dreq;

        SPI m_SPI;
        private SPI.Configuration m_dataConfig;
        private SPI.Configuration m_cmdConfig;

        private byte[] m_cmdBuffer = new byte[4];
        bool m_bPlayLocked = false;

        private byte[] m_sampleBuffer = new byte[] { CMD_READ, SCI_HDAT0 };

        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 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

        public Music(int socketNumber)
        {
            Socket socket = Socket.GetSocket(socketNumber, true, this, null);
            socket.EnsureTypeIsSupported(new char[] { 'S' }, this);

            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.");
            }
        }

        private void Wait()
        {
            while (m_dreq.Read() == false)
            {
                Thread.Sleep(1);
            }
        }

        private void Reset()
        {
            Wait();
            CommandWrite(SCI_MODE, SM_SDINEW | SM_RESET);
            Wait();
            Thread.Sleep(1);
            CommandWrite(SCI_CLOCKF, 0xa000);
        }
 
        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)
            {
                Wait();
                Array.Copy(data, i, block, 0, 32);
                m_SPI.Write(block);
            }
        }
    }
}

Per the instructions included with it, I did remove the default GTM.GHIElectronics.Music reference that was added when I added the shield to the project. The code does behave the same even if the reference remains.

Please let me know if there is anything additional I can provide.

I can confirm that there is an issue using the music module with the networking firmware.

The problem appears to be with the RaiseMusicFinished event.

The same Game Slate code that works fine without networking crashes w/ networking enables the second music module tries to raise it’s finished event.

Hope that helps.

I have same project and same problem… Any idea?

My project http://code.tinyclr.com/project/473/internet-radio/

We are looking into it

I know you guys are hard at work on the 4.2 code. Just curious if you ever figured anything out with this?