Seamlessy-looping music is important for many games, but MonoGame's built-in music player isn't able to consistently loop music - more often than not, it lags, adding a short, but noticeable delay before looping.
PlayPlayMini.NAudio
allows you to use NAudio to play music, resolving this issue, and adding support for playing multiple songs at once, and cross-fading between songs!
Hey, Listen! PlayPlayMini.NAudio
is in early release; the API may change dramatically even between minor version numbers.
- Install this package.
dotnet add package BenMakesGames.PlayPlayMini.NAudio
- Do NOT add your songs to the MCGB content pipeline tool (remove them if they're already there); instead, ensure your songs are set to "copy if newer" in the project's properties.
- You can add something like the following to your
.csproj
file to automatically include all songs; change the path as needed, of course:<ItemGroup> <None Update="Content\Music\**\*.*"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </None> </ItemGroup>
- You can add something like the following to your
- When adding assets to your game's
GameStateManagerBuilder
, usenew NAudioSongMeta(...)
instead ofnew SongMeta(...)
. When usingnew NAudioSongMeta(...)
, you must specify the extension of the song.- For example,
new NAudioSongMeta("TitleTheme", "Content/Music/TitleTheme.mp3")
.
- For example,
- Update your
.AddServices(...)
call as follows:.AddServices((s, c, serviceWatcher) => { ... // WaveOutEvent is only supported by Windows; for other OSes, replace WaveOutEvent with alternatives from this thread: https://github.com/naudio/NAudio/issues/585 s.RegisterType<NAudioMusicPlayer<WaveOutEvent>>().As<INAudioMusicPlayer>() .SingleInstance() .OnActivating(audio => serviceWatcher.RegisterService(audio.Instance)); })
- In your startup state, where you wait for content loaders to finish loading, get an
INAudioMusicPlayer
, and wait for it to be.FullyLoaded
, too.
Hey, Listen! All songs you load must have the same sample rate (typically 44.1khz) and channel count (typically 2). When songs are loading, an error will be logged if they do not all match, and not all songs will be loaded.
.mp3
, .aiff
, and .wav
files are supported out of the box. For other formats, you will need to install additional NAudio packages and do a little extra configuration:
Add the NAudio.Vorbis
package to your project.
In your game's .AddServices(...)
configuration, add the following line:
s.RegisterInstance(new NAudioFileLoader("ogg", f => new VorbisWaveReader(f)));
Add the NAudio.Flac
package to your project.
In your game's .AddServices(...)
configuration, add the following line:
s.RegisterInstance(new NAudioFileLoader("flac", f => new FlacReader(f)));
In your game state or services, get an INAudioMusicPlayer
via the constructor (just as you would any other service), and use it to play and stop songs.
Example:
NAudioMusicPlayer
.StopAllSongs(1000) // stop all songs, fading them out over 1 second
.PlaySong("TitleTheme", 0); // start the TitleTheme with no fade-in time
Refer to the reference, below, for a list of available methods.
Hey, Listen! Negative fade-in and fade-out times are treated as 0.
Starts playing the specific song, fading it in over the specified number of milliseconds.
Songs which are already playing will not be stopped! You must explicitly stop them using StopAllSongs
or StopSong
(below).
If the song is already playing, it will not be played again (you cannot currently play two copies of the same song at the same time). If the song is fading in, its fade-in time will not be changed.
Stops all songs, fading them out over the specified number of milliseconds.
Songs which are already fading out will not have their fade-out time changed. A fade-out time of 0 will always immediately stops all songs, however.
To cross-fade between songs, you can chain StopSongs
and PlaySong
calls. For example:
musicPlayer // an instance of INAudioMusicPlayer
.StopAllSongs(1000)
.PlaySong("TitleTheme");
Works like StopAllSongs
(above), but does NOT stop the songs named in songsToContinue
.
Works like StopAllSongs
(above), but does NOT stop the named song.
Like StopAllSongs
(above), but stops only the named song.
Changes the volume for all songs.
Returns true
if the specific song is currently playing.
Returns an array of the names of all songs currently playing.