Refactors the music player.

This commit is contained in:
Zimmermann Gyula
2015-06-26 02:45:43 +02:00
parent f5d7df621f
commit 60fafaa5a7
6 changed files with 178 additions and 69 deletions

View File

@@ -286,7 +286,6 @@ namespace OpenRA
Console.WriteLine("Loading mod: {0}", mod);
Settings.Game.Mod = mod;
Sound.StopMusic();
Sound.StopVideo();
Sound.Initialize();

View File

@@ -182,6 +182,7 @@
<Compile Include="Traits\Util.cs" />
<Compile Include="Traits\ValidateOrder.cs" />
<Compile Include="Traits\World\Country.cs" />
<Compile Include="Traits\World\MusicPlaylist.cs" />
<Compile Include="Traits\World\ResourceType.cs" />
<Compile Include="Traits\World\ScreenShaker.cs" />
<Compile Include="Traits\World\Shroud.cs" />

View File

@@ -0,0 +1,150 @@
#region Copyright & License Information
/*
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System;
using System.Linq;
using OpenRA.GameRules;
namespace OpenRA.Traits
{
[Desc("Trait for music handling. Attach this to the world actor.")]
public class MusicPlaylistInfo : ITraitInfo
{
public readonly string StartingMusic = null;
public readonly bool LoopStartingMusic = false;
public object Create(ActorInitializer init) { return new MusicPlaylist(init.World, this); }
}
public class MusicPlaylist : INotifyActorDisposing
{
readonly MusicInfo[] random;
readonly MusicInfo[] playlist;
public readonly bool IsMusicAvailable;
MusicInfo currentSong;
bool repeat;
public MusicPlaylist(World world, MusicPlaylistInfo info)
{
IsMusicAvailable = world.Map.Rules.InstalledMusic.Any();
playlist = world.Map.Rules.InstalledMusic.Select(a => a.Value).ToArray();
if (!IsMusicAvailable)
return;
random = playlist.Shuffle(Game.CosmeticRandom).ToArray();
if (Game.Settings.Sound.MapMusic
&& !string.IsNullOrEmpty(info.StartingMusic)
&& world.Map.Rules.Music.ContainsKey(info.StartingMusic)
&& world.Map.Rules.Music[info.StartingMusic].Exists)
{
currentSong = world.Map.Rules.Music[info.StartingMusic];
repeat = info.LoopStartingMusic;
}
else
{
currentSong = Game.Settings.Sound.Shuffle ? random.First() : playlist.First();
repeat = Game.Settings.Sound.Repeat;
}
Play();
}
public MusicInfo CurrentSong()
{
return currentSong;
}
public MusicInfo[] AvailablePlaylist()
{
// TO-DO: add filter options for Race-specific music
return playlist;
}
void Play()
{
if (currentSong == null || !IsMusicAvailable)
return;
Sound.PlayMusicThen(currentSong, () =>
{
if (!repeat)
currentSong = GetNextSong();
Play();
});
}
public void Play(MusicInfo music)
{
if (music == null || !IsMusicAvailable)
return;
currentSong = music;
repeat = Game.Settings.Sound.Repeat;
Sound.PlayMusicThen(music, () =>
{
if (!repeat)
currentSong = GetNextSong();
Play();
});
}
public void Play(MusicInfo music, Action onComplete)
{
if (music == null || !IsMusicAvailable)
return;
currentSong = music;
Sound.PlayMusicThen(music, onComplete);
}
public MusicInfo GetNextSong()
{
return GetSong(false);
}
public MusicInfo GetPrevSong()
{
return GetSong(true);
}
MusicInfo GetSong(bool reverse)
{
if (!IsMusicAvailable)
return null;
var songs = Game.Settings.Sound.Shuffle ? random : playlist;
return reverse ? songs.SkipWhile(m => m != currentSong)
.Skip(1).FirstOrDefault() ?? songs.FirstOrDefault() :
songs.Reverse().SkipWhile(m => m != currentSong)
.Skip(1).FirstOrDefault() ?? songs.Reverse().FirstOrDefault();
}
public void Stop()
{
currentSong = null;
Sound.StopMusic();
}
public void Disposing(Actor self)
{
if (currentSong != null)
Stop();
}
}
}

View File

@@ -388,7 +388,6 @@ namespace OpenRA
frameEndActions.Clear();
Sound.StopAudio();
Sound.StopMusic();
Sound.StopVideo();
// Dispose newer actors first, and the world actor last

View File

@@ -11,26 +11,28 @@
using System;
using System.Drawing;
using System.IO;
using System.Linq;
using Eluant;
using OpenRA.Effects;
using OpenRA.FileFormats;
using OpenRA.FileSystem;
using OpenRA.GameRules;
using OpenRA.Graphics;
using OpenRA.Mods.Common.Effects;
using OpenRA.Scripting;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Scripting
{
[ScriptGlobal("Media")]
public class MediaGlobal : ScriptGlobal
{
World world;
readonly World world;
readonly MusicPlaylist playlist;
public MediaGlobal(ScriptContext context)
: base(context)
{
world = context.World;
playlist = world.WorldActor.Trait<MusicPlaylist>();
}
[Desc("Play an announcer voice listed in notifications.yaml")]
@@ -51,23 +53,15 @@ namespace OpenRA.Mods.Common.Scripting
Sound.Play(file);
}
MusicInfo previousMusic;
Action onComplete;
[Desc("Play track defined in music.yaml or keep it empty for a random song.")]
public void PlayMusic(string track = null, LuaFunction func = null)
{
if (!Game.Settings.Sound.MapMusic)
return;
var music = world.Map.Rules.InstalledMusic.Select(a => a.Value).ToArray();
if (!music.Any())
if (!Game.Settings.Sound.MapMusic || !playlist.IsMusicAvailable)
return;
var musicInfo = !string.IsNullOrEmpty(track) ? world.Map.Rules.Music[track]
: Game.Settings.Sound.Repeat && previousMusic != null ? previousMusic
: Game.Settings.Sound.Shuffle ? music.Random(Game.CosmeticRandom)
: previousMusic == null ? music.First()
: music.SkipWhile(s => s != previousMusic).Skip(1).First();
: playlist.GetNextSong();
if (func != null)
{
@@ -84,19 +78,17 @@ namespace OpenRA.Mods.Common.Scripting
Context.FatalError(e.Message);
}
};
playlist.Play(musicInfo, onComplete);
}
else
onComplete = () => { };
Sound.PlayMusicThen(musicInfo, onComplete);
previousMusic = Sound.CurrentMusic;
playlist.Play(musicInfo);
}
[Desc("Stop the current song.")]
public void StopMusic()
{
Sound.StopMusic();
playlist.Stop();
}
Action onCompleteFullscreen;

View File

@@ -18,29 +18,24 @@ namespace OpenRA.Mods.Common.Widgets.Logic
{
public class MusicPlayerLogic
{
readonly Ruleset modRules;
readonly ScrollPanelWidget musicList;
readonly ScrollItemWidget itemTemplate;
bool installed;
readonly MusicPlaylist musicPlaylist;
MusicInfo currentSong = null;
MusicInfo[] music;
MusicInfo[] random;
ScrollPanelWidget musicList;
ScrollItemWidget itemTemplate;
[ObjectCreator.UseCtor]
public MusicPlayerLogic(Widget widget, Ruleset modRules, World world, Action onExit)
{
this.modRules = modRules;
var panel = widget.Get("MUSIC_PANEL");
musicList = panel.Get<ScrollPanelWidget>("MUSIC_LIST");
itemTemplate = musicList.Get<ScrollItemWidget>("MUSIC_TEMPLATE");
musicPlaylist = world.WorldActor.Trait<MusicPlaylist>();
BuildMusicTable();
Func<bool> noMusic = () => !installed;
Func<bool> noMusic = () => !musicPlaylist.IsMusicAvailable;
panel.Get("NO_MUSIC_LABEL").IsVisible = noMusic;
var playButton = panel.Get<ButtonWidget>("BUTTON_PLAY");
@@ -54,15 +49,15 @@ namespace OpenRA.Mods.Common.Widgets.Logic
pauseButton.IsVisible = () => Sound.MusicPlaying;
var stopButton = panel.Get<ButtonWidget>("BUTTON_STOP");
stopButton.OnClick = Sound.StopMusic;
stopButton.OnClick = () => { musicPlaylist.Stop(); };
stopButton.IsDisabled = noMusic;
var nextButton = panel.Get<ButtonWidget>("BUTTON_NEXT");
nextButton.OnClick = () => { currentSong = GetNextSong(); Play(); };
nextButton.OnClick = () => { currentSong = musicPlaylist.GetNextSong(); Play(); };
nextButton.IsDisabled = noMusic;
var prevButton = panel.Get<ButtonWidget>("BUTTON_PREV");
prevButton.OnClick = () => { currentSong = GetPrevSong(); Play(); };
prevButton.OnClick = () => { currentSong = musicPlaylist.GetPrevSong(); Play(); };
prevButton.IsDisabled = noMusic;
var shuffleCheckbox = panel.Get<CheckboxWidget>("SHUFFLE");
@@ -111,11 +106,13 @@ namespace OpenRA.Mods.Common.Widgets.Logic
public void BuildMusicTable()
{
music = modRules.InstalledMusic.Select(a => a.Value).ToArray();
random = music.Shuffle(Game.CosmeticRandom).ToArray();
currentSong = Sound.CurrentMusic;
if (!musicPlaylist.IsMusicAvailable)
return;
var music = musicPlaylist.AvailablePlaylist();
currentSong = musicPlaylist.CurrentSong();
if (currentSong == null && music.Any())
currentSong = Game.Settings.Sound.Shuffle ? random.First() : music.First();
currentSong = musicPlaylist.GetNextSong();
musicList.RemoveChildren();
foreach (var s in music)
@@ -124,8 +121,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
if (currentSong == null)
currentSong = song;
// TODO: We leak the currentSong MusicInfo across map load, so compare the Filename instead.
var item = ScrollItemWidget.Setup(song.Filename, itemTemplate, () => currentSong.Filename == song.Filename, () => { currentSong = song; Play(); }, () => { });
var item = ScrollItemWidget.Setup(song.Filename, itemTemplate, () => currentSong == song, () => { currentSong = song; Play(); }, () => { });
item.Get<LabelWidget>("TITLE").GetText = () => song.Title;
item.Get<LabelWidget>("LENGTH").GetText = () => SongLengthLabel(song);
musicList.AddChild(item);
@@ -133,8 +129,6 @@ namespace OpenRA.Mods.Common.Widgets.Logic
if (currentSong != null)
musicList.ScrollToItem(currentSong.Filename);
installed = modRules.InstalledMusic.Any();
}
void Play()
@@ -143,38 +137,12 @@ namespace OpenRA.Mods.Common.Widgets.Logic
return;
musicList.ScrollToItem(currentSong.Filename);
Sound.PlayMusicThen(currentSong, () =>
{
if (!Game.Settings.Sound.Repeat)
currentSong = GetNextSong();
Play();
});
musicPlaylist.Play(currentSong);
}
static string SongLengthLabel(MusicInfo song)
{
return "{0:D1}:{1:D2}".F(song.Length / 60, song.Length % 60);
}
MusicInfo GetNextSong()
{
if (!music.Any())
return null;
var songs = Game.Settings.Sound.Shuffle ? random : music;
return songs.SkipWhile(m => m != currentSong)
.Skip(1).FirstOrDefault() ?? songs.FirstOrDefault();
}
MusicInfo GetPrevSong()
{
if (!music.Any())
return null;
var songs = Game.Settings.Sound.Shuffle ? random : music;
return songs.Reverse().SkipWhile(m => m != currentSong)
.Skip(1).FirstOrDefault() ?? songs.Reverse().FirstOrDefault();
}
}
}