Modding Music

From PMDOWiki

This page goes through the process of how to add new music to a PMDO mod.

Adding Files

Add music by placing the file in the Content/Music folder of your mod. It must be an OGG file:

It will appear in the music list of the Map or Ground Editor on the next refresh:

Looping

If you added an ogg, chances are it’s not set up to loop properly. You can set the loop points by editing the tags of the file. Use an editing software like MP3Tag.

You will need to edit the extended tags to include a LOOPSTART and LOOPLENGTH:

LOOPSTART specifies the start of the song’s loop in samples. (Samples are the smallest unit of time for an OGG)

LOOPLENGTH specifies the length of the song’s loop in samples. This is actually optional if the music file itself stops at the loop’s end.

You can find these loop points by opening the song in Audacity, playing through it and carefully pinpointing the times you want to loop together.

This can be an imperfect and laborious process. If the song you’re trying to use is popular enough, chances are it’s on Smas Custom Music. Using songs from this site makes the process much easier.

Looping BRSTMs with Smash Custom Music

Smash Custom Music is a site that hosts popular custom songs from various games. It’s particularly useful because it comes with loop points listed on the song page itself:

What’s even more convenient is that there exists programs to convert BRSTMs to OGGs while fully preserving the loop points in the tags. One such tool is found here.


Download and run the program, and you should see the following UI:

Choose to output as Looping OGG and select any downloaded BRSTM:

Once converted, you will get a .logg file in the output folder:

You can rename the extension to .ogg and it will work just fine.


If you inspect the tags, you’ll find them automatically generated:

Looping Using PyMusicLooper

Looping can also be done via arkrow's PyMusicLooper Tool

For installation on Windows: First, make sure you have the Visual Studio Runtime installed. It can be downloaded here: https://aka.ms/vs/17/release/vc_redist.x64.exe

The next thing we need to do is open PowerShell as a user and run the following commands:

# Say yes
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
# This will install the Scoop package manager for Windows
Invoke-RestMethod -Uri https://get.scoop.sh | Invoke-Expression

After those two commands are run, you should now be able to use Scoop from PowerShell.

Now we are going to install quite a few packages via Scoop, which includes the correct version of Python, along with FFmpeg, Git, and pipx. If you have Python 3.12 or newer installed, make sure it is uninstalled, otherwise, pipx will use that version instead of the one that works with PyMusicLooper. That Python version is 3.11.

If you have Git and FFmpeg already installed you can skip the first two scoop install commands.

scoop install git
scoop install ffmpeg
scoop bucket add versions
scoop install versions/python311
scoop install pipx

You may need to logout and login after this step for all changes to apply. Now that pipx is installed along with its dependencies in Windows, we can continue to the normal install.

Install PyMusicLooper using pipx:

# Normal install
# (follows the official releases on https://pypi.org/project/pymusiclooper/)
pipx install pymusiclooper
pipx ensurepath

If you get the following error when installing pymusiclooper on Windows

ERROR: Could not find a version that satisfies the requirement pyloopermusic (from versions: none)
ERROR: No matching distribution found for pyloopermusic

Then you will need to install using the old method of installing packages via pip:

scoop install pip
pip install pyloopermusic

This is not recommended and should only be used as a last resort since pymusiclooper could break if any of its dependencies get overwritten by another package.

Now that PyMusicLooper is installed, run the following command to find loop points.

pymusiclooper -i play --path "path/to/my_song.ogg"  

You may be presented with multiple indexes to choose from, try out different indexes, and decide on which index is best for you. After deciding on the index, run the following command with the same song as before:

pymusiclooper -i export-points --path "path/to/my_song.ogg"  

Select the index that has the same loop parameters you chose earlier. You should now get something like this:

Loop points for "my_song.ogg":
LOOP_START: XXXXXXX
LOOP_END: XXXXXXXX

LOOP_START is the start of the loop in samples. Where LOOP_END is the end of the loop in samples. To find the length of the loop, you will need to take the end of the loop and subtract the start of the loop.

LOOPLENGTH = LOOP_END - LOOP_START

For further documentation visit https://github.com/arkrow/PyMusicLooper

Additional Data

If you speak to Kriketune and open the music menu, you may find that your song is missing data for its Title, Origin, and Artist, unlike the other songs:


In order to remedy this, simply add the data to the Title, Album, and Artist fields, respectively.

Spoilers

Sometimes, you don’t want a song to be playable in the menu until the player has reached a certain point in the game. You can spoiler your music track by adding the SPOILER tag to the oggs, and give it a name you want to track:

As a result, the track will be hidden from the music menu when accessed.

To make it such that the song is unlocked, you will need to pass in the string to the function call used to open the music menu, likely by a condition checking a save variable. For example:

  local unlocks = {}
  if SV.temporal_pinnacle.ExpositionComplete then
    table.insert(unlocks, "FINAL_BATTLE")
  end
  UI:ShowMusicMenu(unlocks)

Cross Fading

Tracks can be specified as a part of a group. When switching between songs of the same group, the music will cross-fade instead of a regular fade. Support for this feature is currently TENTATIVE and the responsible tag may change in the future.

Within the song's tags, add a "RELATIVE" tag and specify the other songs in the group. This must be done for each song in the group. Also there is currently only support for one song. Ex.

  • Song A.ogg: RELATIVE is set to Song B.ogg
  • Song B.ogg: RELATIVE is set to Song A.ogg