GHI Electronics Music Module Play method using FileStream or MemoryStream

If you have used the GHI Electronics Music Module, you have probably noticed that audio files larger than 600 K run out of memory and throw an exception.  The current driver implements a Play method that does not support playing audio from a FileStream or MemoryStream. The existing method takes a byte[] array parameter that requires the entire file to be loaded in memory.

Mischa Boender has contributed the MUSIC MODULE DRIVER ACCEPTING STREAMS, which works fine for large audio files on an SD card or downloaded from the Internet.  You have to find a workable bandwidth for streaming from the Internet, but it is feasible with this driver.  You can build the driver from the GHI source code, as adapted by Mischa Boender, at Tiny CLR (same as previous link).  The .NET Gadgeteer Builder Templates make it pretty easy to build the new driver that implements a Play method that accepts a Stream parameter.

How to Implement a Driver for a .NET Gadgeteer Module from the Module Builder Template

Install the .NET Gadgeteer Builder Templates, and Create a .NET Gadgeteer module project in Visual Studio.  I’d suggest naming the project Music to save some renaming hassles later.

New Module Project

New .NET Gadgeteer Module Project

The project opens to a Readme file that provides complete instructions for driver implementation, including instructions for building the installer and where to get WiX 3.5 as required to build the installer.

Download the driver code from the Tiny CLR site as shown in the following illustration.

Download Driver Code

Download Driver Code

Since this code is an adaptation of the GHI Electronics source code, you can simply paste the entire download over the template code in the Music.cs file (the name of this file depends on the name you supplied when the project was created).  Save the file with the new code, and go to the Readme.txt file with the instructions.

As specified by the instructions, change the ManufacturerName in the project settings.  The assembly name should be GTM.GHIElectronics.Music and the default namespace Gadgeteer.Modules.GHIElectronics.  Follow step 2. to search for and replace ManufacturerName in [ModuleName].cs, common.wxi, GadgeteerHardware.xml, and AssemblyInfo.cs.  Again, ManufacturerName should be GHIElectronics.  Step 3. is done already, because we implemented the driver by pasting in the code from the download.  Step 4. is optional.  Our whole scenario is a test.

Step 5. is to edit the GadgeteerHardware.xml file, which includes specifying the sockets and pins that can be used for the module.  Because this module is already working, we know that it can use either socket 6 or 9 and that these sockets have common functionality on pin 3.  It took a couple of runs to get the socket orientation right for the image in the designer.  The HelpUrl, in this case, is a link to this blog post.

My GadgeteerHardware.xml file is shown in the following code block.

<?xml version="1.0" encoding="utf-8" ?>
<GadgeteerDefinitions xmlns="http://schemas.microsoft.com/Gadgeteer/2011/Hardware">
  <ModuleDefinitions>
    <!-- This module definition should be filled in.  Mouse over any attribute name to get more help about that attribute. -->
    <!-- The Unique ID is auto-generated and does not usually need to be modified. -->
    <!-- For file paths, do not use forward slash (/), use backslash (\) or it fails in Windows XP. -->
    <ModuleDefinition Name="Music"
                  UniqueId="92e3ba89-ba10-4bb6-8385-68f8bff5e2f7"
                  Manufacturer="Integral Design"
                  Description="A Music module"
                  InstanceName="Music"
                  Type="Gadgeteer.Modules.IntegralDesign.Music"
                  ModuleSuppliesPower="false"
                  HardwareVersion="1.0"
                  Image="Resources\Image.jpg"
                  BoardHeight="40"
                  BoardWidth="51"
                  MinimumGadgeteerCoreVersion="2.42.500"
                  HelpUrl="https://mikedodaro.net/2012/02/15/ghi-electronics-music-module-play-method-filestream-or-memorystream/"
      			>

      <!-- Assemblies listed here are added to the project as references by the designer whenever this module is instantiated.  -->
      <!-- This functionality is used to -->
      <!--   (1) include the output assemblies of this template -->
      <!--   (2) include other custom assemblies that the module relies on (NB you must edit msm.wxs so these are installed too) -->
      <!--   (3) indicate a reliance on a Gadgeteer assembly other than Gadgeteer.dll (which is always referenced) -->
      <!-- Some examples are provided inline: -->
      <Assemblies>
        <!-- This lists the assemblies which provides the API to this module, i.e. the output assemblies of this template. ((1) in the list above) -->
        <!-- If you do not provide support for all of NETMF 4.1, 4.2 and 4.3, the relevant lines should be removed -->
        <!-- NB you should use the same name for the same assembly's NETMF 4.1, NETMF 4.2 and NETMF 4.3 versions, since this will make it easier for users to change framework versions -->
        <Assembly MFVersion="4.1" Name="GTM.IntegralDesign.Music"/>
        <Assembly MFVersion="4.2" Name="GTM.IntegralDesign.Music"/>
        <!--<Assembly MFVersion="4.3" Name="GTM.IntegralDesign.Music"/>-->

      </Assemblies>

      <!--
      This is an example socket specification with two sockets on the board.
      The socket positions are specified in mm from the top left corner of the board, with the orientation 90 indicating the socket notches are pointing upwards (0=right, 180=left, 270=down)
      -->
      <Sockets>

        <!-- This example socket is compatible with socket type S, it is optional, and it has electrical connections to pins 3,4,5,7,8,9, with 7,8,9 being shareable (SPI bus) -->
        <Socket Left="30" Top="10" Orientation="90" ConstructorOrder="2" TypesLabel="S" Optional="true">
          <Types>
            <Type>S</Type>
          </Types>
          <Pins>
            <Pin Shared="false">3</Pin>
          </Pins>
        </Socket>

      </Sockets>

    </ModuleDefinition>
  </ModuleDefinitions>
</GadgeteerDefinitions>

In Step 6. we can copy the existing image of the module from Designer, save it as Image.jpg file  in Paint, and then copy the file into the Resources folder over the blank Image.jpg file.  I had to resize the file to BoardHeight=”40″ and BoardWidth=”51″, as in the xml specification above.

Name changes to the common.wxi file are specified by Step 7  as shown in the following block.  Set SafeModuleName to Music, SafeManufacturerName and FullManufacturerName to GHIElectronics. Change ModuleSoftwareVersion = “1.1.0.0” so the installer can upgrade to the new driver.  This driver will replace the driver installed with the GHI Electronics Spider Kit, but all the functionality of the existiing driver still works, and you will have the traditional overload of the Play method that accepts a Stream parameter.

<?xml version="1.0" encoding="utf-8"?>
<Include>
  <!-- Change this whenever building a new installer.  The fourth number is ignored, so change one of the top three.
  Otherwise, users will not be able to upgrade properly; Windows Installer will exit with an error instead of upgrading. -->
  <!-- Also change the version numbers in Properties/AssemblyInfo.cs for each NETMF version supported -->
  <?define ModuleSoftwareVersion = "1.1.0.0" ?>

  <!-- Alternatively, to synchronize this mainboard version to a kit version, uncomment the below lines, edit the pathname
  to point to your kit, and comment out the line above.  You also need to edit AssemblyInfo.cs.  See the kit readme.txt
  for more details of how and why to do this. -->
  <!--
  <?include $(var.SourcePath)..\..\..\..\..\Kits\IntegralDesign\KitName\Software\KitName\version.wxi ?>
  <?define ModuleSoftwareVersion = "$(var.KitVersion)" ?>
  -->

  <!-- SafeModuleName should match the name printed on the module PCB, without any spaces/punctuation. -->
  <?define SafeModuleName = "Music" ?>

  <!-- SafeManufacturer should match the manufacturer name printed on the module PCB, without any spaces/punctuation. -->
  <?define SafeManufacturer = "IntegralDesign" ?>

  <!-- FullManufacturer can have spaces, e.g. Microsoft Research.  This shows up as the manufacturer name in add/remove programs. -->
  <?define FullManufacturer = "Integral Design" ?>

  <!-- These define which versions of NETMF are supported - comment out or delete any unsupported NETMF versions. -->
  <?define NETMF41Supported ?>
  <?define NETMF42Supported ?>
  <!--<?define NETMF43Supported ?>-->

  <!-- No need to change anything below this line. -->
  <?define OutputFileName = "GTM.$(var.SafeManufacturer).$(var.SafeModuleName)"?>
  <?define ProjectPath = "Music" ?>

  <!-- These two should be copied over if upgrading from an older template -->
  <?define guid_msi_upgrade_code =  "b3a97773-e601-4fcb-bfa4-7e04024e12f1" ?>
  <?define guid_msm_package_id = "212c48cb-e040-415e-a65e-c4ac1a9bd823" ?>

  <?define guid_msm_comp_resources_id = "56fd52f7-f3c8-4598-a328-7afff9e8635f" ?>
  <?define guid_msm_comp_registry_id = "9190340e-39d7-4b1c-b532-0c545e28d527" ?>
  <?define guid_msm_comp_gadgeteerhardware_id = "9f03d147-932b-4a39-89d0-09ce347ea6b3"?>

  <?define guid_msm_comp_files_id_41 = "cd151811-4fae-46ca-911c-375da5d5da17" ?>
  <?define guid_msm_comp_files_be_id_41 = "1b1bcc09-fb6a-4169-986c-79b6ec143908" ?>
  <?define guid_msm_comp_files_le_id_41 = "5b106671-7655-4e67-abf0-87f76052450e" ?>

  <?define guid_msm_comp_files_id_42 = "3cc8cd2d-8401-4cdd-a2ca-13416e69589f" ?>
  <?define guid_msm_comp_files_be_id_42 = "0f611354-d79a-4e76-a99b-bbe50f08aab4" ?>
  <?define guid_msm_comp_files_le_id_42 = "65f94a32-a744-4214-99e2-fab6d547abca" ?>

  <!--<?define guid_msm_comp_files_id_43 = "aedec00b-b73b-4cb7-89f6-3adffdddf752" ?>
  <?define guid_msm_comp_files_be_id_43 = "4c3f1fdf-03de-4acd-8c88-53dbfb5d5bdc" ?>
  <?define guid_msm_comp_files_le_id_43 = "08916797-6eae-4270-a58c-1b9facdba617" ?>-->
</Include>

Installing and Using the Module

Build the project in Release mode.  This will dump the Music.msi installer into bin\Release\Installer\.  Double click the installer to install the DLL and designer files.  Then you should be able to create a new project, add the Music module to the designer display, and connect the module to a mainboard as shown in the following illustration.  You’ll find that the Music module does not show in the toolbox with the rest of the GHI Spider Kit modules.  It shows in a separate list depending on the way you named your manufacturer.

Music Module in Designer

Music Module in Designer

After these procedures, the new driver provides an overload for the Play method that takes a Stream object–FileStream or MemoryStream–instead of a byte array.  This is a very useful addition because it enables the music module to play audio files that are too large for the entire contents of the file to be loaded into the memory of Gadgeteer mainboards.

With the following code I have been able to play 128 kbs mp3 files from the SDCard module and 32 kbs wma files from a Web server.  This bandwidth works fine on my system; higher bandwidths tend to stutter. This code blocks the dispatcher, so you won’t be able to do anything else while the audio plays, such as adjusting the volume or stopping the player.

using System.Threading;
using Microsoft.SPOT;

using GT = Gadgeteer;
using Gadgeteer.Modules.GHIElectronics;

using System.IO;
using System.Net;

namespace MusicModuleMischaDriver
{
    public partial class Program
    {
        // This method is run when the mainboard is powered up or reset.
        void ProgramStarted()
        {
            button.ButtonPressed += new Button.ButtonEventHandler(button_ButtonPressed);
            music.MusicFinished += new Music.MusicFinishedPlayingEventHandler(music_MusicFinished);
            // Use Debug.Print to show messages in Visual Studio's "Output" window during debugging.
            Debug.Print(&quot;Program Started&quot;);
        }

        void music_MusicFinished(Music sender)
        {
            Debug.Print(&quot;music finished&quot;);
        }

        void button_ButtonPressed(Button sender, Button.ButtonState state)
        {
            if (button.IsLedOn)
            {
                button.TurnLEDOff();

                sdCard.MountSDCard();
                GT.StorageDevice storage = sdCard.GetStorageDevice();
                FileStream stream = storage.Open("\\Audio\\NewYorkNewYork_128kbs.mp3", FileMode.Open, FileAccess.Read);

                music.SetVolume(200);
                music.Play(stream);
                while (music.IsBusy)  // You can't call music.StopPlaying or set volume with this loop running.
                    Thread.Sleep(10);
            }
            else
            {
                button.TurnLEDOn();

                HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create("http://integral-data.com/NewYorkNewYork_32kbs.wma");

                music.SetVolume(200);
                music.Play(httpWebRequest.GetResponse().GetResponseStream());
                while (music.IsBusy)
                    Thread.Sleep(10);
                httpWebRequest.Dispose();

            }

        }
    }
}

If you want to avoid the implementation of the .NET Gadgeteer module, try this: http://integral-data.com/Music.zip  Run the Music.msi installer in the \bin\Release\Installer folder.  It should work for 4.1 and 4.2.

, , , , ,

  1. #1 by Roman on March 1, 2012 - 7:50 AM

    work it for FEZ Panada II?

  2. #3 by ShapingStuff on September 5, 2012 - 11:52 AM

    Hi. In step 5 were can the XML file for the project be found? I also don’t have a resources folder or am I looking in the wrong place? Thanks.

    • #4 by Michael Dodaro on September 5, 2012 - 1:05 PM

      You have to install the .NET Gadgeteer Builder Templates, and then you can create a .NET Gadgeteer module project in Visual Studio. Then follow the instructions in the readme file of the new module project.

      • #5 by ShapingStuff on September 6, 2012 - 3:34 AM

        I’ve installed the Templates and am using the module project but for some reasons there is no read me, xml or resources. I’ll look into it. Great blog by the way so much well documented gadgeteer stuff.

  3. #6 by Michael Dodaro on September 6, 2012 - 8:18 AM

    Strange! Do any other module projects work? Thanks for reading.

  4. #7 by Stephen Cardinale on May 3, 2013 - 4:32 AM

    Mike,, I am new to Gadgeteer and C#. Coming from a VB.net world, I am not able to implement your code due to the bit shift operators not being implemented in the VBNetMF Core. Can you supply a compiled version of the Music Module Driver that supports playing from streams. It would be a lifesaver at this point. TIA.

  5. #9 by Stephen Cardinale on May 3, 2013 - 5:12 PM

    Thanks Mike it worked like a charm.

    • #10 by Michael Dodaro on May 3, 2013 - 5:38 PM

      Stephen. Good to hear! The code blocks in this post were garbled by the blog software. Just as well that I had another look at it today.

  6. #11 by Domenic H. on October 1, 2013 - 2:52 AM

    Hi Mike
    I don’t know why but on 4.2 it doesn’t work with a 128kbps mp3 file. Did you test it on 4.2 as well?
    If I try to play a 128kbps file it is stottering enourmously…

    I used your compiled 4.2 assembly on a Spider. What was your testing environment?

  7. #13 by Mike on June 17, 2014 - 2:07 AM

    hi Mike,
    I have the music module running and playing from a stream – however I want to stop whats playing and skip to the next track – music.StopPlaying() locks the device.

    • #14 by Michael Dodaro on June 17, 2014 - 8:24 AM

      Sorry, it’s been too long since I used the music module. I don’t know what the problem could be
      . Try the GHI Electronics forum.

  1. Mp3 Player « Disruption: Design Interaction
  2. .NET Gadgeteer Music Module | Eduardo Velloso

Leave a reply to Michael Dodaro Cancel reply