Future versions.

Coordinator
Sep 6, 2012 at 11:09 AM

I have some addtional code that I need to get around to publishing.

this includes fixes:

  • unloading mods not removing samples
  • much improved test tool
  • Addtional loaders
  • and other things I've forgotten

 

I will conintue to work on improving the compatability of the mod playing engine but apart from me actually remembering to publish my changes what else would you like to see in the lib?

Jun 9, 2013 at 10:00 PM
Edited Jun 9, 2013 at 10:10 PM
Hello TheGouldfish,

first of all thank you so much for this great lib! It sounds really good in comparison to sharpmod and seems to be bugfree.
It would be cool if you would form this lib as tiny and smart as possible. It is also cool that you encode the modfile to a whole
wave file including the Header, which most People leave aside as NAudio doesn´t Need a waveheader and Plays the raw wavdata.

This is so cool, as you can Play the Output with a simple api call. You safe so much space with excluding NAudio!!!

So here are my requests, if you meet them I´ll definately use and recommend it:
  • The commands for the end user should look like this:
'I use VB .NET for my Projects. Here is what I am looking for.


Imports SharpMik


Public Class Form1


'On Startup load and Play the mod file.


    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        ModDriver.MikMod_Init(STREAM INSTANCE HERE)              'I would prefer a stream Argument here, as the user can now decide to Play from any Location.
        ModDriver.MikMod_Play(AUTOREPEAT BOOLEAN HERE)     'I would prefer a boolean repeat mode (on/off) here. Smart and cool!

    End Sub


'A Button should pause and resume the Sound.

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        If Button1.Text = "♫" Then
            ModDriver.MikMod_Resume()
            Button1.Text = "l l"
            GoTo 1
        End If

        If Button1.Text = "l l" Then
            ModDriver.MikMod_Pause()
            Button1.Text = "♫"
            GoTo 1
        End If
1:
        Exit Sub

    End Sub


'On leave stop and unload the mod file.


    Private Sub Form1_FormClosing(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing

        ModDriver.MikMod_Stop()

  End Sub
    
  • Other things like volume control and Position searching are also appreciated, but not that important.
  • A simple FFT spectrum analyser would be also optional but great. Sth. like this perhaps.
  • Please Keep it going pure .NET as no acceptable pure managed lib exists so far.
If you can achieve this, your lib would rock dude. I would help you with coding, but I am not so familiar with C# as with VB. If you like to, I would Support you via Skype (I´ll send you my username with a private Message).

Cu,

Freefall
Jun 9, 2013 at 10:16 PM
Edited Jun 9, 2013 at 10:16 PM
Here is my VB .NET test Player source (stored on my ftp):

Download Link
Coordinator
Jun 10, 2013 at 1:34 PM
I've not worked in VB, but I get the basic idea of what you are after.

The resulting .dll should be as small as possible, but the solution looks alot bigger then its actually is due to the various different platforms that it currently supports.

I can't simplify the setup API too much as both the C and the C# source needs a "driver" to take the raw byte data and pass it onto something, now it should be possible to create a stream driver that would be similar the wav driver but extends the stream class.

There should already be an API call to turn mod looping on or off (I think its exposed in the current version, if not its defiantly in the work in progress version I've got).

You should be able to change the volume via a ModDriver static and if you look in the mikmod class (which is a helper class to make using the library easier) you should see how I've worked out a very basic but usable way of telling how far though the track you are.


Due to how the library was written (a direct hand written port of the pure C library mikmod) the API is very C like and have some odd quirks and idea's that don't sit well with a more managed language and way of thinking. My plan is to slowly move to a more cleaner and more C# way of doing things but this will take time.
Jun 10, 2013 at 8:57 PM
Edited Jun 10, 2013 at 9:01 PM
Thanks for your quick reply.

The most requirement for me is to make playback possible without a temp wave file. That means, the driver takes a stream or a bytearray as Argument and the wav Reader writes the file also into a stream / bytearray to make sure, that everything is statically in the program. It is ok to write everything to a file for testing, but now it should only Return a wav Array or stream or better a full playback api. I will use this lib in my next Project if everything can be statically bound into the executable.
The resulting .dll should be as small as possible, but the solution looks alot bigger then its actually is due to the various different platforms that it currently supports.
Well, perhaps you could split your lib into seperate libs that meet the Special platform concerns. I think most People, just as me, use it only on Windows and argue about the code size for other platforms. Also, I think it would be better to only Support *.xm Fasttracker II files to save the space for the other loaders. You can convert the modfiles into xm with modplug, so you can also listen the others as well. Perhaps you could move more in the ufmod direction, to bring a unique alternative for all .NET users. :)
Coordinator
Jun 11, 2013 at 9:11 AM
The bonus of the way the lib is layed out at the moment is that you can remove any loaders / drivers very easy.
Just make a new dll project and only include the files you need (only the drivers and loaders can be cut down).
The solution is already broken up into smaller lib's, each project is its own library and each only includes what it needs.
If you want the library built into your next project you just need to copy it into your project with just the drivers / loaders that you need.

The Drivers are only included in the projects that need them and are not compiled or included in the other projects so the only extra code is found in the solution and not in the dll.

Removing mod loaders makes no sense the resulting dll size would barely be any different and they are written and work really well.

I really think you need to look into the source of the wav driver and memory stream driver to see how you get data out of the playback engine, its just raw byte data so you should be able to make a driver that does what you need.
Coordinator
Jun 11, 2013 at 9:36 AM
    public class StreamDriver : VirtualSoftwareDriver
    {
        sbyte[] m_Audiobuffer;
        static int BUFFERSIZE = 32768;

        StreamDelegate m_Stream;


        public Stream Stream
        {
            get { return m_Stream; }
        }

        public StreamDriver()
        {
            m_Next = null;
            m_Name = "Stream Driver";
            m_Version = "Stream Driver";
            m_HardVoiceLimit = 0;
            m_SoftVoiceLimit = 255;
            m_AutoUpdating = true;

            m_Stream.ReadData = ReadData;
        }

        private int ReadData(byte[] buffer, int count)
        {
            int amount = count < BUFFERSIZE ? count : BUFFERSIZE;

            int done = (int)VC_WriteBytes(m_Audiobuffer, amount);
            Buffer.BlockCopy(m_Audiobuffer, 0, buffer, 0, done);

            return done;
        }

        public override void CommandLine(string command)
        {
        }

        public override bool IsPresent()
        {
            return true;
        }

        public override bool Init()
        {
            m_Audiobuffer = new sbyte[BUFFERSIZE];

            return base.Init();
        }

        public override void PlayStop()
        {
            base.PlayStop();
        }

        public override bool PlayStart()
        {
            return base.PlayStart();
        }

        public override void Exit()
        {

        }
    }

    public class StreamDelegate : Stream
    {

        public delegate int ReadDataDelegate(byte[] buffer, int count);
        public ReadDataDelegate ReadData;


        public override int Read(byte[] buffer, int offset, int count)
        {
            if (ReadData != null)
            {
                return ReadData(buffer, count);
            }

            return 0;
        }


    }
This is a quick stream driver I just coded up, I've not got the rest of the source here with me so I can't tell if it compiles or works but it should give you an idea of what is needed.
StreamDriver driver = ModDriver.LoadDriver<StreamDriver>();
ModDriver.Init("");
Stream stream = driver.Stream;
Now if you needed the wav reader on the file then you could add that on, but you might run into some issues due to how the wav format works.
The Wave header TEXT needs to know the size of the data which is put into 2 places in the header (one is the total size of the file and the other is the size of the data).
Jun 11, 2013 at 3:30 PM
Edited Jun 11, 2013 at 3:31 PM
Imports SharpMik
Imports SharpMik.Player
Imports SharpMik.Drivers
Imports System.IO

Public Class Form1

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Dim driver As MemoryStreamDriver = ModDriver.LoadDriver(Of MemoryStreamDriver)()
        ModDriver.MikMod_Init("")
        Dim stream As Stream = driver.MemoryStream


        Dim modfile As [Module] = Nothing
        modfile = ModuleLoader.Load(My.Resources.Arachno___Alone_in_the_Dark, 32, 1)

        ModPlayer.Player_Start(modfile)

        While ModPlayer.Player_Active()
            ModDriver.MikMod_Update()
        End While



        ModPlayer.Player_Stop()
        ModuleLoader.UnLoad(modfile)

        ModDriver.MikMod_Exit()
        My.Computer.Audio.Play(stream, AudioPlayMode.BackgroundLoop)
    End Sub


End Class
This doesn´t work :( What´s the mistake?
Coordinator
Jun 11, 2013 at 3:49 PM
Edited Jun 11, 2013 at 3:56 PM
You want to grab the stream after the Player_Start, every time you call start on the memory stream driver it creates a new stream, that could be changed quite easily.

Again remember that this will be a raw audio data (pcm audio, aka wav) but with out the header and could easily take 50+ mb of data depending on the length of the mod.
Jun 11, 2013 at 5:05 PM
Ok, if the Header is missing I will be forced to use NAudio for playback.
You want to grab the stream after the Player_Start, every time you call start on the memory stream driver it creates a new stream, that could be changed quite easily.
So I Need to take the stream at before Player_Start?? But I think it is empty until the end of the while Loop? Could you please Show me how to get a stream or bytearray containing the Audio data?
Jun 11, 2013 at 5:08 PM
Why is it not possible to get the full wave file as bytearray/ stream instead of a file?
Coordinator
Jun 12, 2013 at 10:20 AM
To use the MemoryStreamDriver you would need to use it like this:
Imports SharpMik
Imports SharpMik.Player
Imports SharpMik.Drivers
Imports System.IO

Public Class Form1

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Dim driver As MemoryStreamDriver = ModDriver.LoadDriver(Of MemoryStreamDriver)()
        ModDriver.MikMod_Init("")


        Dim modfile As [Module] = Nothing
        modfile = ModuleLoader.Load(My.Resources.Arachno___Alone_in_the_Dark, 32, 1)

        ModPlayer.Player_Start(modfile)

        While ModPlayer.Player_Active()
            ModDriver.MikMod_Update()
        End While

        Dim stream As Stream = driver.MemoryStream

        ModPlayer.Player_Stop()
        ModuleLoader.UnLoad(modfile)

        ModDriver.MikMod_Exit()
        My.Computer.Audio.Play(stream, AudioPlayMode.BackgroundLoop)
    End Sub

End Class
As the stream isn't created till after the mod is started.

It its possible to get it to save the wav to memory, it just needs a new driver to be created, that uses a memory stream rather then the binary writer.
Here is a quick a dirty driver that should work (again I don't have the rest of the source to hand to test)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using SharpMilkLib.Player;
using SharpMilkLib.Extentions;
using System.Diagnostics;

namespace SharpMilkLib.Drivers
{
    public class WavToMemoryDriver : VirtualDriver1
    {
        MemoryStream m_MemoryStream;
        BinaryWriter m_FileStream;

        sbyte[] m_Audiobuffer;

        public static uint BUFFERSIZE = 32768;
        uint dumpsize;
        
        public Stream Stream
        {       
            Get {return m_MemoryStream;}
        }
        
        public WavToMemory()
        {
            m_Next = null;
            m_Name = "Memory Wav Writer";
            m_Version = "Memory disk writer v1";
            m_HardVoiceLimit = 0;
            m_SoftVoiceLimit = 255;
            m_AutoUpdating = false;
        }

        public override void CommandLine(string command)
        {   
        }

        public override bool IsPresent()
        {
            return true;
        }

        public override bool Init()
        {
            try
            {
                m_MemoryStream = new MemoryStream();
                m_FileStream = new BinaryWriter(m_MemoryStream);
                m_Audiobuffer = new sbyte[BUFFERSIZE];

                ModDriver.Mode = (ushort)( ModDriver.Mode | Common.DMODE_SOFT_MUSIC | Common.DMODE_SOFT_SNDFX);

                if (!base.Init())
                {
                    // close it all down?
                }

                putheader();

                return false;
            }
            catch (System.Exception ex)
            {
                throw ex;
            }

            return true;
        }

        public override void Exit()
        {
            try
            {
                putheader();
                Console.WriteLine("put {0} bytes into the wav", dumpsize);
                base.Exit();
                m_MemoryStream.Seek(0, SeekOrigin.Begin);
                //putheader();
                m_MemoryStream.
                m_FileStream = null;
            }
            catch (System.Exception ex)
            {
                throw ex;
            }

        }

        public override void Update()
        {
            uint done = VC_WriteBytes(m_Audiobuffer, BUFFERSIZE);
            m_FileStream.Write(m_Audiobuffer,0,(int)done);
            
            dumpsize += done;
        }


        void putheader()
        {
            m_FileStream.Seek(0,SeekOrigin.Begin);
            m_FileStream.Write("RIFF".ToCharArray());
            m_FileStream.Write((uint)(dumpsize + 44));
            m_FileStream.Write("WAVEfmt ".ToCharArray());
            m_FileStream.Write((uint)16);
            m_FileStream.Write((ushort)1);
            ushort channelCount = (ushort)((ModDriver.Mode & Common.DMODE_STEREO) == Common.DMODE_STEREO ? 2 : 1);
            ushort numberOfBytes = (ushort)((ModDriver.Mode & Common.DMODE_16BITS) == Common.DMODE_16BITS ? 2 : 1 );

            m_FileStream.Write(channelCount);
            m_FileStream.Write((uint)ModDriver.MixFreq);
            int blah = ModDriver.MixFreq * channelCount * numberOfBytes;
            m_FileStream.Write((uint)(blah));
            m_FileStream.Write((ushort)(channelCount * numberOfBytes));
            m_FileStream.Write((ushort)((ModDriver.Mode & Common.DMODE_16BITS) == Common.DMODE_16BITS ? 16 : 8));
            m_FileStream.Write("data".ToCharArray());
            m_FileStream.Write((uint)dumpsize);         
        }
    }
}
If you add that class to the library in the drivers folder you should then be able to use it with the following:
Imports SharpMik
Imports SharpMik.Player
Imports SharpMik.Drivers
Imports System.IO

Public Class Form1

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Dim driver As WavToMemoryDriver= ModDriver.LoadDriver(Of WavToMemoryDriver)()
        ModDriver.MikMod_Init("")
        Dim stream As Stream = driver.Stream


        Dim modfile As [Module] = Nothing
        modfile = ModuleLoader.Load(My.Resources.Arachno___Alone_in_the_Dark, 32, 1)

        ModPlayer.Player_Start(modfile)

        While ModPlayer.Player_Active()
            ModDriver.MikMod_Update()
        End While



        ModPlayer.Player_Stop()
        ModuleLoader.UnLoad(modfile)

        ModDriver.MikMod_Exit()
        My.Computer.Audio.Play(stream, AudioPlayMode.BackgroundLoop)
    End Sub


End Class
Jun 12, 2013 at 1:08 PM
Great you got it working! :)
Jun 13, 2013 at 8:51 PM
Thank you so much for this good work. I´ll definately use it in my next release!