0
0

Hi guys!

This afternoon I wrapped the FMOD DLL calls into a .NET class library using P/Invoke. The conversion is NOT 100% complete and mostly untested. Only the FMUSIC callback is working at the moment. You cannot provide your own memory alloc and file routines. The DSP stuff is not working and the EAX stuff (Reverb) is also disabled, at the moment. Basically any function call, which somehow involves dealing with raw memory or passing non-simple structures is currently not done. I might finish them off tomorrow … depends on how easy it will be to nail down this stuff in the P/Invoke Marshalling documentation.

So, how does it work. I tried to keep the API very close to FMOD. In .NET all functions have to live inside classes, though. Instead of having to force everyone to type stuff like FmodNet.FSOUND_Init(), I chose to organize this stuff in several classes … all living under the FmodNet namespace. These classes are called FMOD (only the FMOD_VERSION lives there at the), FSOUND and FMUSIC. The general idea is. If you had a call such as FSOUND_Init() …. it will now be FSOUND.Init() (note the period). The only exception to this rule, at the moment are the structure names, such as FMUSIC_MODULE or FSOUND_STREAM. Also, instead of using the TRUE/FALSE macros and signed chars the functions use the .NET bool type.

REMINDER: This stuff is not 100% finished. I would say it is about 70-80% done. It is also widely untested, so problems in the marshalling could always occur 😕

The C# source code is located here:

[url:3gekehx1]http://www.uni-koblenz.de/~koegler/fmod/fmodnet_src.zip[/url:3gekehx1]

The DLL can be found here (also contains FMOD3.62 DLL with stdcall calling convention):

[url:3gekehx1]http://www.uni-koblenz.de/~koegler/fmod/fmodnet.zip[/url:3gekehx1]

Here’s how a little C# program looks with this (don’t forget to add the fmodnet.dll reference):

[code:3gekehx1]
using System;
using FmodNet;

namespace fmodtest
{
class MainClass
{
public static void RowCallback(FMUSIC_MODULE mod, char param)
{
Console.WriteLine("RowCallback");
}

    public static void OrderCallback(FMUSIC_MODULE mod, char param)
    {
        Console.WriteLine("OrderCallback");
    }

    [STAThread]
    static void Main(string[] args)
    {
        Console.WriteLine ("FmodNet Demo\n");
        Console.WriteLine ("Using FMOD Version: {0}", FSOUND.GetVersion());

        FSOUND.Init(44100, 32, 0);

        FMUSIC_MODULE mod = FMUSIC.LoadSong("test.xm");

        // set callbacks
        FMUSIC.SetRowCallback(mod, new FMUSIC.CALLBACK(RowCallback), 1);
        FMUSIC.SetOrderCallback(mod, new FMUSIC.CALLBACK(OrderCallback), 1);

        FMUSIC.PlaySong(mod);

        //FSOUND_DSPUNIT fftDSP = FSOUND.DSP_GetFFTUnit();
        //FSOUND.DSP_SetActive(fftDSP, true);

        while (!FMUSIC.IsFinished(mod))
        {
        }

        FMUSIC.FreeSong(mod);

        FSOUND.Close();
    }
}

}
[/code:3gekehx1]

As you can see, it even supports simple callbacks through delegates. 8)

Phew, anyways … hope this is helpful for someone out there. I am not entirely satisfied with this approach though. I think, in the long run a ManagedFMOD wrapper should be done (in Managed C++). This wrapper should also clearly use the advantages provided by the .NET framework much better. It is probably too much effort for FMOD 3.xx, though, with FMOD 4 lurking about.

Happy Coding,
Marco

  • You must to post comments
0
0

BTW, the next version will also have the structures in the different class. So, instead of FMUSIC_MODULE, you will also have FMUSIC.MODULE … it is more consistent that way.

-Marco

  • You must to post comments
0
0

<snip>
I am not entirely satisfied with this approach though. I think, in the long run a ManagedFMOD wrapper should be done (in Managed C++).
</snip>

Marco,
Glad to see you got callbacks working, I’ve been trying to understand that for some time and investigating your source should be helpful.

Anyways, I’m not experienced with C++, therefore prefer the P/Invoke method of wrapping fmod.dll. As a person who wants to use fmod.dll in a .NET application (and therefore does not care how fmod was wrapped) can you tell me the benefits of using a Managed component versus a wrapped P/Invoke .dll?

Another issue I’ve been having is whether to make all the fmod methods static calls as you’ve done, or to wrap them in namespace/class hierarchies. The fmod documentation provides a good starting point for structure

Heres a rundown of what I had in mind:

FMODNet (root namespace)
– FMOD (singleton class for startup and shutdown of digital output, info methods)
– FChannel (abstract, common methods for FStream, FSound, FMusic)
– FStream (instance, inherits FChannel)
– FMusic (same as above)

so that something along these lines is done:

FStream _mp3File = new FStream( “myfile.mp3” );
_mp3File.Play();

My concern with that is I’m putting an extra layer over the fmod API, something a person would have to learn from documentation other than the fmod docs. Exposing static methods with naming conventions similiar to fmod would make use of existing documentation and source code and I prefer that to OOP.

I’ve looked at a .NET implementation of another audio sdk, and learning how to P/Invoke the win32 api was easier than learning the .NET wrappered api. I don’t mean to take anything away from the creator of the wrapper, its just my experience with it.

Thanks for any info
Ron

  • You must to post comments
0
0

Hi!

Here’s a snippet of a mail I wrote to Brett two days ago (haven’t received a reply):

[quote:3ark3xl3]
You create an FMOD object (maybe a singleton). Construction of this initializes FMOD. Using this FMOD object, you would then be able to create a Sample, Stream, Music object etc.. Each of these objects would maintain a reference to the FMOD object. So, FMOD would only be garbage collected once all secondary objects are gone, etc..

A lot of the Get/Set functions should be implemented as Properties. This means, that the thing feels like a variable, but actually uses a get/set function internally (where you can call FMOD functions). So, a more appropriate example would be target C# code, which looks like this:

FMOD.SetMaxHardwareChannels(32);

FMOD fmod = new FMOD(44100, 32, 0);

// use a property for example to wrap FSOUND_GetMaxChannels
Console.WriteLine(“MaxChannels: ” + fmod.MaxChannels);

// provide some very convenient functions for the ‘dumbest average user’
FStream myStream = fmod.CreateStream(“myfunky.ogg”);
// another use of a property, wrapping FSOUND_Stream_SetTime
myStream.Time = 2000; // skip two seconds

FChannel myChannel = myStream.Play();
myChannel.Volume = 128;

// … PLAY FOR SOME TIME …

// basically ‘delete’ the stream, this closes the stream and removes the refernce to the fmod object, which created it
myStream.Dispose();
[/quote:3ark3xl3]

As you can tell, what you propose is very similar to what I had in mind. As you say, the FMOD library is structured in a way, which really lends itself to this kind of design.

I think a ManagedFMOD wrapper (basically an object oriented layer on top of FMOD using .NET features) is just the ‘natural’ way to expose this stuff to anybody. C# (and other .NET languages, but IMHO C# is coolest in terms of syntax and ease of use) really provides lots of infrastructure for ‘object oriented coolness’ and it should be used. I mean, if you make the decision to use .NET … why would you want to use a more cumbersome C-like API? Also, some API features (using custom memory allocation and File I/O) just don’t lend themselves to a straight port. It just does not make sense to do it. I would much rather have people register a .NET File-Interface and stuff :)

You can implement the ManagedFMOD using any .NET language … Managed just means it is ‘.NET’. It does not imply using Managed C++. Anyways, I like the approach of using Managed C++, because as a developer I have tighter control over what I do inside the wrapper functions. You can very easily mix and match native functions (FMOD functions) and managed functions. You also have to take care of some of the Marshalling yourself … yes, it is more work, but you know where you lose performance (buffers have to copied, etc…). Another benefit would be, that you can directly link the FMOD source code into the managed C++ project, meaning that you only have a single DLL instead of requiring the correct version of FMOD. For example, callbacks in .NET require the STDCALL convention version of the fmod.dll. But, if you also do normal C++ dev, you might get FMOD versions mixed up … this can get very annoying. So, having a standalone fmodnet.dll which contains EVERYTHING would be very, very nice.

With this release I just wanted to kinda prove, that it is very easy to get FMOD working with .NET … the code in the ZIP file took me like 3-4 hours to write. But, it was mostly the sheer number of functions, which slowed me down :) I mean, once you know P/Invoke 90% of the process is basically ‘automatic’.

In your case, I would not worry about putting another layer over the FMOD API …. whoever wants the C-like API could use what I posted :D! Nevertheless, I also believe that lots of people wrap the FMOD functions into objects anyways (I would). So, having an object-oriented layer is the future. The only question for me is … should it be done for FMOD 3.xx or just for version 4.xx? I hope to get at least some more functions working in my wrapper. Once that is fully working, I would start with the object-oriented version, as then basically all marshalling problems are solved and I can just focus on good object-oriented design.

It’s good to know, that I’m not alone in this …
-Marco

  • You must to post comments
0
0

Marco,

Thanks for the reply. That helped clear alot of issues I was having concerning how to approach FMOD with .NET.

I especially like the point you mentioned about how most people might wrap the FMOD API in objects regardless of the language they use. I suppose that is whats great about the FMOD API, its easy to adapt to the project, whether its straight C calls or OOP methodology.

Looking forward to seeing other posts regarding peoples involvement with FMOD and .NET,

Ron

  • You must to post comments
Showing 4 results
Your Answer

Please first to submit.