SFML community forums

General => SFML projects => Topic started by: zorexx on February 24, 2012, 07:18:33 pm

Title: sfMidi 1.1 - Play MIDI in SFML
Post by: zorexx on February 24, 2012, 07:18:33 pm
What is sfMidi?
sfMidi is a static library for playing MIDI files with SoundFont 2 in SFML.
sfMidi uses FluidSynth (http://sourceforge.net/apps/trac/fluidsynth/ (http://sourceforge.net/apps/trac/fluidsynth/)) to load SoundFont 2 and MIDI files.

GitHub repository:
https://github.com/eXpl0it3r/sfMidi (https://github.com/eXpl0it3r/sfMidi)

Hand over to new maintainer:
After a long period of inactivity, I have decided to hand over this project to eXpl0it3r (https://en.sfml-dev.org/forums/index.php?action=profile;u=3414).
Please checkout the GitHub repository for the latest updates on this project.

(click to show/hide)
Title: sfMidi - Play MIDI in SFML
Post by: Laurent on February 24, 2012, 08:24:36 pm
Have you tried to render the MIDI music as audio samples instead of playing it directly? This way you could use it with the audio API (sf::Music, sf::Sound), and you wouldn't lose any feature.

http://fluidsynth.sourceforge.net/api/index.html#UsingSynth
Title: sfMidi - Play MIDI in SFML
Post by: zorexx on February 24, 2012, 08:33:17 pm
That is what sfMidi does, audio is played with an sf::SoundStream subclass, sf::Midi.

Edit:
Or did you mean rendering the whole midi file into memory first, then playing it from there? I thought of that before, but then I got the impression that, that would be very memory intensive. But I just did the calculation and it doesn't seem that bad actually, so maybe I'll add a PreloadMidi function or something tomorrow.
Title: sfMidi - Play MIDI in SFML
Post by: Laurent on February 24, 2012, 09:21:28 pm
Ok sorry, I should have looked at your code first, or at least read more carefully your description of the limitation :oops:

It's interesting, that makes it a good candidate for a future plugin system. I would even have integrated it directly if the license of FluidSynth was not LGPL (which implies an extra DLL on Windows).
Title: sfMidi - Play MIDI in SFML
Post by: StormWingDelta on February 25, 2012, 12:27:34 am
I was wondering when someone would try to set this up.  Thanks! :)

By the way.. Where are you getting your Sound Fonts from?  Just wondering since I've never had to use sound fonts before in this way.  The only time I've worked with Midi files is in Java and I've never had to use sound fonts for it.  :?

An idea would be to have a default sound font in your project that doesn't change things too much.  This way someone won't have to download sound fonts if they don't want to or are easily confused like me. :)
Title: sfMidi - Play MIDI in SFML
Post by: zorexx on February 25, 2012, 02:25:11 am
@Laurent: No problem. About integrating it into SFML, yes, the GPL family is quite a pain, but the only good alternative to this I can find is TiMIDIty++, which is GPL, there's also libjdkmidi but I didn't really look into it because it seems dead, and it's GPL again.

Actually, if the project does not use the MIDI functions, you won't have to include the dll file along with it, right?

@StormWingDelta: I got the sound font from a link in FluidSynth's website, @Stormhttp://sourceforge.net/apps/trac/fluidsynth/wiki/SoundFont
I do think you should include the SoundFont in your project, instead of expecting the user to have one, since they probably don't.

Also, before deciding to use MIDI in your project, do consider using mp3/ogg, or module files (tracker music, check out my other project, sfMod:http://www.sfml-dev.org/forum/viewtopic.php?t=6178) instead, SoundFonts are huge, putting the size on disk aside, take a look at the memory it takes up when you load it, and they are slow to load as well, so I honestly don't really recommend using MIDI in your projects unless you have to.

Edit:
Added the preloading feature and released as version 1.0.1.
Takes quite some time to preload, but you get to unload the sound font file when you're done preloading. Playing, stopping, seeking, and looping will work as if it's a normal sf::Music with preloaded data. Preloaded data is about 10MB per minute.
Title: sfMidi - Play MIDI in SFML
Post by: Laurent on February 25, 2012, 09:36:14 am
Quote
Actually, if the project does not use the MIDI functions, you won't have to include the dll file along with it, right?

If it's compiled inside sfml-audio you will have to provide the DLL. That's why it has to be a plugin, so that people that don't need MIDI can just ignore it.
Title: sfMidi - Play MIDI in SFML
Post by: zorexx on February 25, 2012, 09:50:24 am
Hmm, let's say if I'm using sfml-audio to play music, but the file handling and data feeding is done through my own program via SoundStream, in other words, I'm not using the audio file loading capability of sfml, which I presume uses libsndfile.
In this case, I do not need to include the dll of libsndfile in my project, or is this a different case?
Title: sfMidi - Play MIDI in SFML
Post by: Laurent on February 25, 2012, 09:56:22 am
Quote
In this case, I do not need to include the dll of libsndfile in my project

You do, because sfml-audio is built with a dependency to libsndfile.dll. It would be optional if it was a dynamically loaded plugin.
Title: sfMidi - Play MIDI in SFML
Post by: zorexx on February 25, 2012, 10:11:34 am
Really? I did not include libsndfile.dll in both sfMod and sfMidi example project, and they both work just fine. Or am I misunderstanding something?
Title: sfMidi - Play MIDI in SFML
Post by: Laurent on February 25, 2012, 02:20:47 pm
Can you upload one of these examples?
Title: sfMidi - Play MIDI in SFML
Post by: zorexx on February 25, 2012, 02:52:41 pm
It is already included in the full version:
http://www.zorexxlkl.com/files/downloads/sfMidi-1.0.1-full.zip

sfMidiTest folder

for the sfMod one:
http://www.zorexxlkl.com/files/downloads/sfMod-1.0.2-full.zip

sfModTest folder
Title: sfMidi - Play MIDI in SFML
Post by: Laurent on February 25, 2012, 03:01:07 pm
With your examples I get "missing DLL" errors for openal32.dll and libsndfile-1.dll, as expected.

If you don't, it means that you've put these DLLs in a directory which is in your PATH environment variable.
Title: sfMidi - Play MIDI in SFML
Post by: zorexx on February 25, 2012, 03:20:09 pm
Really? That's odd, because I even tried with libfluidsynth, which I just compiled, and I'm very sure it's not in any directory under my PATH environment variable.

I did this test:
In the sfMidi project, run as usual, works, delete libfluidsynth, does not work (missing dll), edit the file to exclude all the sfMidi functions, (actually, i put those in an if (false) scope), compile, now it works, no missing dll error.

Just wondering, did you recompile the project or run it directly?


Edit:
I just tested it on my sister's laptop, she does not do any sort of programming, and the only missing dlls are msvcp100.dll and msvcr100.dll (runs fine without libfluidsynth.dll as long as I don't call any function from sfMidi)
Title: sfMidi - Play MIDI in SFML
Post by: Laurent on February 25, 2012, 04:41:51 pm
You can check which DLLs an executable or DLL depends on with Dependency Walker (http://www.dependencywalker.com/).

And I find a little strange that your example can run without openal32.dll too -- this one is definitely needed ;)

Quote
Just wondering, did you recompile the project or run it directly?

I ran it directly.

Quote
I did this test:
In the sfMidi project, run as usual, works, delete libfluidsynth, does not work (missing dll), edit the file to exclude all the sfMidi functions, (actually, i put those in an if (false) scope), compile, now it works, no missing dll error.

It depends how you build sfMidi and the application that uses it. I haven't looked, so I cannot say anything about it.

But anyway, sfml-audio calls functions of libsndfile and openal, so even if what you say was true it wouldn't change anything.

EDIT: do you link SFML statically or dynamically?
Title: sfMidi - Play MIDI in SFML
Post by: zorexx on February 25, 2012, 06:26:53 pm
About openal32, yes, I should need this, I left it out by accident (same goes to libsndfile actually, I only realized this when I tried to test it just now), but for openal32, there's a possibility that it is in a directory in PATH, but it shouldn't be the case for libfluidsynth, since I'm quite positive that I do not have it in any PATH directory, and my sister wouldn't even have such dll on her computer.

I link to SFML dynamically.


Edit:
Ah, I know what's going on now, there's a flaw in my test program, probably due to compiler optimization or so. If I put if (false), the compiler probably removed all the code under it, since I would never reach there, however, if I put if (static_cast<unsigned int>(time(NULL)) < 1000), which would almost always be false, obviously, but since there is a possibility that it may be true, so the compiler does not remove the code under it, and I get the missing dll error. I can also avoid the compiler optimization by compiling under debug, or just disable it, so it's my mistake that I did not tried testing under debug.

As for openal32.dll and libsndfile-1.dll, I found that they're in my PATH variable, under MinGW. That explains why.

Anyway, thanks for clearing this up for me, seems like we have to be careful of compiler optimization.

Now time to add the 2 missing dlls into the example files. >.<

On the other hand, have you thought of integrating sfMod? It relies on libmodplug, but libmodplug is under public domain, and I have it statically linked (no, actually I just added the source files into the sfMod project). It would be great if SFML supports module files natively. Also sfMod is a cleaner piece of code than sfMidi, without all the workarounds since everything I need is available in libmodplug.
Title: sfMidi - Play MIDI in SFML
Post by: greeniekin on March 02, 2012, 04:13:06 am
This is something I thought mentioning.

There is a library called rtMidi. The license is not restrictive. Apart from if there are any changes to rtMidi post them to the creator. It does not synthesise but deals with receiving and sending midi messages. Which would make seeking, pausing and dynamic music easy. With no large sound fonts.

On windows this means you can use the built in midi synth. Which actually sounds pretty good.

On linux of course the problem is linux can have nothing. There are packages that give midi synth and that can be installed and it should work.

Mac I have no idea about. Well they can always plug into an external synth ><.

Obviously your method has less requirements(regarding operating system). Code reuse from sfml sound would not really be an issue as very little code is need to make rtMidi work.
Title: sfMidi - Play MIDI in SFML
Post by: zorexx on March 02, 2012, 05:21:33 am
Yes, I am aware of that library.

But it seems to me that it does not support loading .mid files. It's only handles midi input and output, but not actually loading the files. At least I can't find anything about loading midi files on the website.

Also, one of the main reason I chose Fluidsynth, was for the SoundFont 2 support. It was meant to rely on sound fonts.

On the other hand, I found another 2 libraries, PortMidi, and PortSMF, one for realtime midi i/o, probably like rtMidi, one for midi file i/o.
Perhaps I can add this into sfMidi as an option to play Midi files without sound fonts.
Title: sfMidi - Play MIDI in SFML
Post by: greeniekin on March 02, 2012, 02:07:46 pm
That is true. It does not have support for loading midi files.
I do think your current method is better for just playing midi files. More reliable cross platform.

I had not heard of portMidi.
They look like really good libraries.
The license may actually better. In that your not required to send changes back to the creator.

Plus the fact that it can be used with portsmf(midi file reader/writer/data-structure) and scorealign(aligning audo and midi). plus the portmedia project which these are apart of has stuff for audio api and vidio api.

I know you know this zorexx. Just wanted to mention this for other readers.
Title: sfMidi - Play MIDI in SFML
Post by: zorexx on March 02, 2012, 03:54:15 pm
Ok, I will look into PortMidi when I manage to find the time to do so.  :D
Title: sfMidi - Play MIDI in SFML
Post by: Tenry on March 11, 2012, 03:57:53 pm
I am getting undefined reference errors, even though I am linking to the sfMidi library :(


My .bat file:
Code: [Select]
g++ -o main.o -c main.cpp -IsfMidi-1.0/include -ID:/dev/SFML-1.6/include
g++ -o test.exe main.o -LsfMidi-1.0/lib -LD:/dev/SFML-1.6/lib -lsfMidi -lsfml-audio
@pause


some error messages:
Quote
undefined reference to `sfMidi::Midi::Midi()'
undefined reference to `sfMidi::Midi::LoadMidiFromFile(std::string const&)'
Title: sfMidi - Play MIDI in SFML
Post by: zorexx on March 11, 2012, 05:17:34 pm
The compiled libs I provided are compiled with SFML 2 and will not work with SFML1.6. You'll have to recompile them yourself.

I also realize that there was a bug when compiling with SFML1.6, so I fixed it and uploaded it as version 1.0.2.

I also compiled a SFML1.6 version for you:
http://www.zorexxlkl/files/downloads/sfMidi-1.0.2-sfml1.6.zip
Title: Re: sfMidi 1.1 - Play MIDI in SFML
Post by: zorexx on April 05, 2012, 07:35:11 am
I'm finally done adapting sfMidi to the latest SFML naming conventions.

I also added a new error handling system (the same one as sfTheora and sfMod).

Dropped SFML 1.6 support as well, since SFML 2.0 will be released as a stable version soon.

See the changelog for the full list of changes.

On a side note, about PortMidi, I've decided to not implement it in sfMidi.
However, there is a chance that I will write another library (sfPortMidi), which will utilize PortMidi.
The main reason is because things will get pretty confusing and/or messy if I introduce PortMidi into sfMidi.
There will be 2 Midi classes, one which uses FluidSynth, and another which uses PortMidi, thus, a name clash (2 Midi classes).
I thought of renaming the current Midi class to SfMidi (SoundFont Midi), but then that will cause confusion in the Music and Loader classes.
I also thought of using the same class, and if you don't load a SoundFont, it will use PortMidi, otherwise it will use FluidSynth, but then every function will have to check if a SoundFont is loaded, then decide what to do, which will create some unnecessary overhead.
Either way, there isn't a clean and simple way to introduce PortMidi in sfMidi.
Hence, I will either drop it or write a new library.
If I do have the time to do so, I will try to write sfPortMidi, but I can't promise anything.
Title: Re: sfMidi 1.1 - Play MIDI in SFML
Post by: Nexus on April 05, 2012, 09:11:48 am
I also thought of using the same class, and if you don't load a SoundFont, it will use PortMidi, otherwise it will use FluidSynth, but then every function will have to check if a SoundFont is loaded, then decide what to do, which will create some unnecessary overhead.
While I have no idea about the MIDI libraries you've mentioned (actually MIDI at all), wouldn't it be possible to write an interface providing the necessary operations, that actually points to a class of one library? So the frontend code would just call virtual functions, no case differentiations are necessary.

This design is often used, for example in 3D graphic engines to implement OpenGL and DirectX backends or in SFML to abstract platform-dependent code.
Title: Re: sfMidi 1.1 - Play MIDI in SFML
Post by: zorexx on April 05, 2012, 09:32:45 am
I'm not sure if I understand exactly what you mean, but how does that solve the problem of differentiation being necessary. Since one library requires SoundFonts to work while the other does not, even if the user don't have to choose between which to use, the internal code will still have to do so somewhere.

Also, since probably nobody will ever use both in the same project (to use SoundFont and to not), there shouldn't be have both in the same library (forcing the user to include both dlls, or recompiling them without either 1).

To my understanding, SFML to abstract platform-dependant code is done during compile time, meaning that it will adapt to whichever platform it is compiled on, not run on, so once it's compiled, it no longer needs to differentiate between which backend to use.
However, in sfMidi's case, sfMidi will have to decide which backend to use depending on whether the programmer provides SoundFont or not, the user may end up using both backends in 1 project for all we know.

Hence, in this case, it would be much better to provide 2 separate libraries.
Title: Re: sfMidi 1.1 - Play MIDI in SFML
Post by: Laurent on April 05, 2012, 09:39:39 am
I think he was referring to this kind of solution:

class MidiBackend
{
    // whatever is required as virtual functions
};

class FluidSynthBackend : public MidiBackend
{
    // implement virtual functions
};

class PortMidiBackend : public MidiBackend
{
    // implement virtual functions
};

class sfMidi
{
public:

    void loadFromFile(std::string filename)
    {
        delete m_backend;
        m_backend = new PortMidiBackend(filename);
        ...
    }

    void loadFromFile(std::string filename, std::string soundfont)
    {
        delete m_backend;
        m_backend = new FluidSynthBackend(filename, soundfont);
        ...
    }

private:

    MidiBackend* m_backend;
};
Title: Re: sfMidi 1.1 - Play MIDI in SFML
Post by: zorexx on April 05, 2012, 09:47:42 am
Oh right, I was stuck with the thought that I have to provide a loadSoundFontFromFile function.
But I do think that function is pretty important, since SoundFonts are reusable, you can unload the midi file and load another midi file without having to reload the SoundFont, and this is pretty important because SoundFont takes a long time to load.

I still think it's cleaner to separate it into 2 libraries, since they work quite differently, and then again there is the multiple dlls problem.
It will also make things easier to manage in the future.  :)

Edit: Actually, I just realize my first point (in this reply) doesn't change anything since I could make the decision of which backend to use when the loadMidiFromFile function is called, but I still don't see why I should not separate them into 2 libraries.
Title: Re: sfMidi - Play MIDI in SFML
Post by: Tenry on April 10, 2012, 07:05:36 pm
The compiled libs I provided are compiled with SFML 2 and will not work with SFML1.6. You'll have to recompile them yourself.

I also realize that there was a bug when compiling with SFML1.6, so I fixed it and uploaded it as version 1.0.2.

I also compiled a SFML1.6 version for you:
http://www.zorexxlkl/files/downloads/sfMidi-1.0.2-sfml1.6.zip (http://www.zorexxlkl/files/downloads/sfMidi-1.0.2-sfml1.6.zip)

I've switched to SFML 2.0 and downloaded sfMidi 1.1. Still undefined references :(

midiTest>midiTest>g++ -c main.cpp -o main.o -IsfMidi-1.1/include -ID:/dev/SFML-2.0/include

midiTest>g++ main.o -o test.exe -LsfMidi-1.1/lib -LD:/dev/SFML-2.0/lib -lsfMidi -lsfml-audio
main.o:main.cpp:(.text+0x1b): undefined reference to `sfmidi::Midi::Midi()'
main.o:main.cpp:(.text+0x57): undefined reference to `sfmidi::Midi::loadMidiFrom
File(std::string const&)'
main.o:main.cpp:(.text+0x93): undefined reference to `sfmidi::Midi::play()'
main.o:main.cpp:(.text+0xc1): undefined reference to `sfmidi::Midi::~Midi()'
main.o:main.cpp:(.text+0xdd): undefined reference to `sfmidi::Midi::~Midi()'
collect2: ld returned 1 exit status
Title: Re: sfMidi 1.1 - Play MIDI in SFML
Post by: zorexx on April 11, 2012, 05:05:03 am
Oh, I didn't realize you were using g++ earlier. >.<
Since the libraries were compiled under MSVC++, I don't think it'll work for g++.
g++ uses libraries which are under the extension .a if I remember correctly. MSVC++ uses .lib files.

Have you tried the CMake version of sfMidi?
Title: Re: sfMidi 1.1 - Play MIDI in SFML
Post by: Tenry on April 11, 2012, 03:22:04 pm
Oh, I didn't realize you were using g++ earlier. >.<
Since the libraries were compiled under MSVC++, I don't think it'll work for g++.
g++ uses libraries which are under the extension .a if I remember correctly. MSVC++ uses .lib files.

Have you tried the CMake version of sfMidi?
Okay, that's the reason I think. There's another object oriented library compiled with MSVC++, and the oo code only works with MSVC++. Other users have to use the C style version without any classes :(

I tried cmake and everything seems fine. But for compiling I finally need fluidsynth, right?
I never got fluidsynth compiled yet (and there seems not to be a precompiled version), and even with just having the header files only on my system sfMidi can't locate fluidsynth.h >.<

error: fluidsynth.h: No such file or directory

I put the includes to D:/dev/fluidsynth-1.1.5/include and rebuilt everything with cmake before compiling. Nothing changed :(
Title: Re: sfMidi 1.1 - Play MIDI in SFML
Post by: zorexx on April 11, 2012, 04:14:57 pm
yes, you'll have to compile fluidsynth as well if there's no precompiled one available. but if I recall correctly, fluidsynth uses cmake as well, so you shouldn't have problems compiling.
Title: Re: sfMidi 1.1 - Play MIDI in SFML
Post by: Tenry on April 11, 2012, 05:21:49 pm
yes, you'll have to compile fluidsynth as well if there's no precompiled one available. but if I recall correctly, fluidsynth uses cmake as well, so you shouldn't have problems compiling.
But as far as I remember I had trouble somehow :(
Cmake worked fine for SFML-2.0, don't know why I trouble with fluidsynth. Maybe it has got additional dependencies I have to install or something. But for that I should ask in the fluidsynth community, and not here ;)
Title: Re: sfMidi 1.1 - Play MIDI in SFML
Post by: zorexx on April 11, 2012, 06:16:36 pm
Oh, I just remembered, after clicking configure in CMake for the 1st time, when the red options appear, you'll have to change certain settings, I unchecked everything except for enable-aufile (and BUILD_SHARED_LIBS of course), that's pretty much the bare minimum, which means you don't have anything to play the sound (since we have SFML for that), and enable-aufile allows fluidsynth to load MIDI files, which is all we need.
Trying using that same setting and see if it works.
Title: Re: sfMidi 1.1 - Play MIDI in SFML
Post by: Tenry on April 11, 2012, 10:02:06 pm
Oh, I just remembered, after clicking configure in CMake for the 1st time, when the red options appear, you'll have to change certain settings, I unchecked everything except for enable-aufile (and BUILD_SHARED_LIBS of course), that's pretty much the bare minimum, which means you don't have anything to play the sound (since we have SFML for that), and enable-aufile allows fluidsynth to load MIDI files, which is all we need.
Trying using that same setting and see if it works.
I tried now, too, but when making I get a lot of error messages.

F:\Benutzer\Simon\Desktop\fluidsynth-1.1.5\build>make
Scanning dependencies of target libfluidsynth
[  2%] Building C object src/CMakeFiles/libfluidsynth.dir/fluid_dll.c.obj
[  5%] Building C object src/CMakeFiles/libfluidsynth.dir/drivers/fluid_dsound.c
.obj
f:/Benutzer/Simon/Desktop/fluidsynth-1.1.5/src/drivers/fluid_dsound.c:30:20: err
or: dsound.h: No such file or directory
f:/Benutzer/Simon/Desktop/fluidsynth-1.1.5/src/drivers/fluid_dsound.c:46: error:
 expected specifier-qualifier-list before 'LPDIRECTSOUND'
f:/Benutzer/Simon/Desktop/fluidsynth-1.1.5/src/drivers/fluid_dsound.c: In functi
on 'fluid_dsound_audio_driver_settings':
f:/Benutzer/Simon/Desktop/fluidsynth-1.1.5/src/drivers/fluid_dsound.c:93: error:
 'LPDSENUMCALLBACK' undeclared (first use in this function)
f:/Benutzer/Simon/Desktop/fluidsynth-1.1.5/src/drivers/fluid_dsound.c:93: error:
 (Each undeclared identifier is reported only once

and many, many more :(
Title: Re: sfMidi 1.1 - Play MIDI in SFML
Post by: zorexx on April 12, 2012, 04:22:47 am
Have you tried following the instructions here?
http://sourceforge.net/apps/trac/fluidsynth/wiki/BuildingWithCMake (http://sourceforge.net/apps/trac/fluidsynth/wiki/BuildingWithCMake)
Title: Re: sfMidi 1.1 - Play MIDI in SFML
Post by: Tenry on April 12, 2012, 01:59:29 pm
Have you tried following the instructions here?
http://sourceforge.net/apps/trac/fluidsynth/wiki/BuildingWithCMake (http://sourceforge.net/apps/trac/fluidsynth/wiki/BuildingWithCMake)
Okay, thanks. I haven't found that page before, I think.
But I will not try it now, as I have to download several things, changing the system paths etc.
I have to think about all that carefully later, and I'm sure, then everything will work.
Otherwise, I should contact the fluidsynth community, and not spam an SFML thread :P