MOD Player for Defold Engine

MOD Player

Mod Player can load and play good old .XM and .MOD file formats.

As a dinosaur developer, I miss my C64 and Amiga days. When developing this simple extension I remembered those days a lot.

There are some hitch-ups, but extension supports MacOS, Windows, Linux, Android, iOS and HTML5. Unfortunately, HTML5 bundle is little tricky . There is a little py script which simplify things for now. Please read the details on Github page.

Defold Asset Portal
Github
Simple Examples
Nanowar Example (All credits goes to @benjames171 )

Installation

Installation requires a few steps.

1- Add Dependency

You can use the ModPlayer extension in your own project by adding this project as a Defold library dependency.
Open your game.project file and in the dependencies field under project add:

https://github.com/selimanac/defold-modplayer/archive/master.zip

2- Bundle Resources Path

Open your game.project file and in the Bundle Resources field under project add:

/res

Bundle

3- Create Folders

Create /res/common/assets folders in your project root. Then you can place your .xm and .mod files here. You can add subfolders as you like; /res/common/assets/musics

Bundle

Notes & Known Issues

  • Loading and parsing is blocker. It will block the main thread (UI thread). Since the mod files are small it is better to load them when bootstraping or preloading. It may cause a pause on UI.
  • Loading and parsing XM files much more faster then mod files. Use XM if possible. (Tested with same tracker file as .mod and .xm)
  • Not %100 compatible with every MOD or XM files.
  • I couldn’t find a way to retrieve build path when developing on Defold Editor. You have to provide a full path to player.build_path("<FULL_PATH>/res/common/assets/") function for working on Defold Editor only. It doesn’t required when bundling.
  • Different platform bundles didn’t tested very well.
    • MacOS: Long run.
    • iOS: Long run.
    • Windows: Short run. Tested on Wine
    • Android: Short run.
    • Linux: I couldn’t manage to have sound on my VMs. But app is successfully load the files and run on Debian and Ubuntu
  • Hashtable is limited to 10 elements. I think it is more than enough. It is a bad practice to load or play more than two music files at the same time.
  • Currently, it is not possible to Build HTML5 on the Defold Editor with mod music. You can build it for testing, but can’t load the the musics.

Example

See the example folder for simple usage.
For more examples: https://github.com/selimanac/defold-modplayer-examples
Nanowar game example: https://github.com/selimanac/nanowar-modplayer


player.master_volume(1.0) -- Set master volume for musics ( 0.0 -> 1.0 is max level)
local music = player.load_music("bb.xm") -- Load mod file and assign it is ID
player.play_music(music) -- Play mod file
player.music_volume(music, 0.5) -- Set volume for music ( 0.0 -> 1.0 is max level)
player.music_pitch(music, 1.0) -- Set pitch for a music (1.0 is base level)
print("Music length: ", player.music_lenght(music)) -- Get music time length (in seconds)

HTML5 Bundle

Unfortunately, it is not possible to build HTML5 on the Defold Editor with mod music(You can build it for testing, but can’t load the musics). But you can bundle as HTML5 from the Editor with mod music.

Bundling for HTML5 is require of editing archive_files.json file. More info about the issue is here..
You can use a script or you can edit it manually.

- Python Script

I wrote a small python script for updating archive_files.jsonfile with music files data. You can find it here: https://github.com/selimanac/modplayer-html5-example/blob/master/mod_file_parser.py

  • Put this file alongside the index.html in the root of your HTML5 Bundle
  • Run it with /assets/ path
    > python mod_file_parser.py /assets/audio
  • It will update the /archive/archive_files.json file with music data

- Manual

  • Bundle you project as usual by using Project > Bundle > HTML5 from the Defold Editor
  • Open archive/archive_files.json file from bundled folder
  • Add your music files into archive_files.json file with their names and sizes.
{
    "name": "level_1.xm", 	<- Name of your file for loading from Defold
    "size": 42940,			<- Actual size of the file (bytes) 
    "pieces": [
        {
            "name": "../assets/audio/level_1.xm", <- Relative path to your mod files
            "offset": 0
        }
    ]
}

Example HTML5 project is here and example archive_files.json is here.

API

player.build_path(full_path:string)

!- Don’t set this when bundling -!

Only required when developing on Defold Editor. <FULL_PATH> (absolute path) is the full path of your project folder/directory.
Don’t forget to add trailing /.


player.build_path("<FULL_PATH>/res/common/assets/") -- Set build path when working on Editor only 

Examples

Windows:
player.build_path("C:/Users/user_name/your_project_path/res/common/assets/")

*nix:
player.build_path("/Users/user_name/your_project_path/res/common/assets/")

We don’t need it when bundling:


local is_development = false -- If you are building on Defold Editor then set it true. If you are bundling set it false
if is_development then
	player.build_path("<FULL_PATH>/res/common/assets/") -- Set build path only for Defold Editor
end

player.master_volume(volume:double)

Set master volume for musics ( 0.0 -> 1.0 is max level)


player.master_volume(1.0)

player.load_music(file_name:string)

Load and parse mod file into memory. Returns ID.


local music = player.load_music("your_file_name.xm") -- Load mod file and assign it is ID[int] 

player.play_music(id:int)

Start music playing.


player.play_music(music) 

player.pause_music(id:int)

Pause music playing.


player.pause_music(music) 

player.resume_music(id:int)

Resume playing “paused” music


player.resume_music(music)

player.music_volume(id:int, volume:double)

Set volume for music ( 0.0 -> 1.0 is max level)


player.music_volume(music, 0.5)

player.music_pitch(id:int, pitch:double)

Set pitch for a music (1.0 is base level).


player.music_pitch(music, 1.0) 

player.music_lenght(id:int)

Get music time length (in seconds)


print("Music length: ", player.music_lenght(music))

player.music_played(id:int)

Get current music time played (in seconds)


print("Played : ", player.music_played(music))

player.music_loop(loop:int)

Set music loop count (loop repeats) NOTE: If set to -1, means infinite loop. Default is -1 (infinite)


player.music_loop(music, 1)

player.is_music_playing(id:int)

Check if music is playing. Also returns false if music is not loaded or unloaded.


print("is Playing:", player.is_music_playing(music)) 

player.stop_music(id:int)

Stop music playing


player.stop_music(music) 

player.unload_music(id:int)

Unload music from memory


player.unload_music(music)

player.xm_volume(id:int, volume:double, amplification:double)

Only for XM files. You can change the samples volume, but it may cause a clipping. You can balance it with amplification. Some bad modules may still clip. Default values; volume 1.0, amplification 0.25. Use it with caution!


player.xm_volume(music, 2.5, 0.15)

Dependencies

Thanks to all Defold team for their great support.