Files
Vocaluxe/Vocaluxe/Base/CBackgroundMusic.cs
T

444 lines
14 KiB
C#

#region license
// This file is part of Vocaluxe.
//
// Vocaluxe is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Vocaluxe is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Vocaluxe. If not, see <http://www.gnu.org/licenses/>.
#endregion
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks;
using VocaluxeLib;
using VocaluxeLib.Songs;
using VocaluxeLib.Draw;
using VocaluxeLib.Utils.Player;
namespace Vocaluxe.Base
{
static class CBackgroundMusic
{
private static bool _Initialized;
private static readonly CSongPlayer _BGPlayer = new CSongPlayer();
private static readonly CSongPlayer _PreviewPlayer = new CSongPlayer(true);
private static CSongPlayer _CurPlayer;
private static bool _OwnSongsAvailable;
private static EBackgroundMusicSource _MusicSource;
private static CPlaylistElement _CurrentPlaylistElement; //Currently played music
private static int _PreviousMusicIndex = -1;
private static readonly List<CPlaylistElement> _BGMusicFiles = new List<CPlaylistElement>(); //Background music files
private static readonly List<CPlaylistElement> _NotPlayedFiles = new List<CPlaylistElement>(); //Not played files
private static readonly List<CPlaylistElement> _PreviousFiles = new List<CPlaylistElement>(); //Played files
private static bool _OwnMusicAdded;
private static bool _BackgroundMusicAdded;
private static bool _Disabled;
private static Task _PreviewStartHelperTask;
private static int _PreviewStartWaitCounter;
private const int _PreviewStartWaitMaxTries = 10;
public static bool VideoEnabled
{
get { return _CurPlayer.VideoEnabled; }
set { _CurPlayer.VideoEnabled = value; }
}
public static bool CanSing
{
get
{
if (IsPlayingPreview)
return false;
return !_BGMusicFiles.Contains(_CurrentPlaylistElement);
}
}
public static bool Disabled
{
get { return _Disabled; }
set
{
if (Disabled == value)
return;
_Disabled = value;
if (_Disabled)
Pause();
else
Play();
}
}
/// <summary>
/// Repeat the background song (preview songs are always repeating!)
/// </summary>
public static bool RepeatSong
{
get { return _BGPlayer.Loop; }
set { _BGPlayer.Loop = value; }
}
public static int SongID
{
get { return _CurPlayer.SongID; }
}
public static bool SongHasVideo
{
get { return _CurPlayer.SongHasVideo; }
}
public static bool IsPlaying
{
get { return _CurPlayer.IsPlaying; }
}
public static bool IsPlayingPreview
{
get { return _CurPlayer == _PreviewPlayer; }
set
{
if (IsPlayingPreview == value)
return;
Pause();
_CurPlayer = value ? _PreviewPlayer : _BGPlayer;
Play();
}
}
public static string ArtistAndTitle
{
get { return _CurPlayer.ArtistAndTitle; }
}
public static float Length
{
get { return _CurPlayer.Length; }
}
public static CTextureRef Cover
{
get { return _CurPlayer.Cover; }
}
//Use this to set whether own songs are available for access
public static bool OwnSongsAvailable
{
get { return _OwnSongsAvailable; }
set
{
_OwnSongsAvailable = value;
if (_OwnSongsAvailable && _MusicSource != EBackgroundMusicSource.TR_CONFIG_NO_OWN_MUSIC)
_AddOwnMusic();
}
}
/// <summary>
/// Initializes the background music with values from config
/// Has to be called before any other method is used
/// </summary>
public static void Init()
{
if (_Initialized)
return;
List<string> soundFiles = CHelper.ListSoundFiles(CSettings.FolderNameBackgroundMusic, true, true);
foreach (string path in soundFiles)
_BGMusicFiles.Add(new CPlaylistElement(path));
_CurPlayer = _BGPlayer;
//Set a default to have a consistent starting point, use SetMusicSource afterwards
_MusicSource = EBackgroundMusicSource.TR_CONFIG_NO_OWN_MUSIC;
_AddBackgroundMusic();
SetMusicSource(CConfig.Config.Sound.BackgroundMusicSource);
_Initialized = true;
}
public static void Close()
{
_BGPlayer.Close();
_PreviewPlayer.Close();
_BGMusicFiles.Clear();
_NotPlayedFiles.Clear();
_PreviousFiles.Clear();
_Initialized = false;
}
public static void Play()
{
if (!IsPlayingPreview && CConfig.Config.Sound.BackgroundMusic != EBackgroundMusicOffOn.TR_CONFIG_ON)
return;
if (IsPlayingPreview || _BGPlayer.SoundLoaded)
_CurPlayer.Play();
else
Next();
}
public static void Stop()
{
_CurPlayer.Stop();
if (!IsPlayingPreview)
_CurrentPlaylistElement = null;
}
public static void Pause()
{
_CurPlayer.Pause();
}
public static void Next()
{
if (IsPlayingPreview)
return;
Stop(); //stop last song if any
if (_PreviousMusicIndex < _PreviousFiles.Count - 2)
{
//We are in the previous list and next element exists
_PreviousMusicIndex++;
_CurrentPlaylistElement = _PreviousFiles[_PreviousMusicIndex];
}
else
{
//We are not in the previous list (anymore)
if (_NotPlayedFiles.Count == 0)
{
if (_PreviousFiles.Count == 0)
return; //No songs to play
_NotPlayedFiles.AddRange(_PreviousFiles);
}
_CurrentPlaylistElement = _NotPlayedFiles[CGame.Rand.Next(_NotPlayedFiles.Count)];
_NotPlayedFiles.Remove(_CurrentPlaylistElement);
_PreviousFiles.Add(_CurrentPlaylistElement);
_PreviousMusicIndex = _PreviousFiles.Count - 1;
}
_StartSong();
}
public static void Previous()
{
if (IsPlayingPreview)
return;
if (_PreviousMusicIndex < 0)
return;
if (_CurrentPlaylistElement == null || (_BGPlayer.Position <= 1.5f && _PreviousMusicIndex > 0))
{
Stop(); //stop last song
_PreviousMusicIndex--;
_CurrentPlaylistElement = _PreviousFiles[_PreviousMusicIndex];
}
_StartSong();
}
public static void Update()
{
if (_PreviewStartHelperTask != null && ( _PreviewPlayer.Length > 0 || _PreviewStartWaitCounter++ >= _PreviewStartWaitMaxTries))
{
_PreviewStartHelperTask.RunSynchronously(TaskScheduler.FromCurrentSynchronizationContext());
_PreviewStartHelperTask.Dispose();
_PreviewStartHelperTask = null;
_PreviewStartWaitCounter = 0;
}
if (!IsPlaying)
return;
_CurPlayer.Update();
if (!IsPlayingPreview && _BGPlayer.IsFinished)
Next();
}
public static CTextureRef GetVideoTexture()
{
return _CurPlayer.GetVideoTexture();
}
public static void SetMusicSource(EBackgroundMusicSource source)
{
if (_MusicSource == source)
return;
_MusicSource = source;
switch (_MusicSource)
{
case EBackgroundMusicSource.TR_CONFIG_NO_OWN_MUSIC:
_AddBackgroundMusic();
_RemoveOwnMusic();
break;
case EBackgroundMusicSource.TR_CONFIG_ONLY_OWN_MUSIC:
_AddOwnMusic();
_RemoveBackgroundMusic();
break;
case EBackgroundMusicSource.TR_CONFIG_OWN_MUSIC:
_AddBackgroundMusic();
_AddOwnMusic();
break;
}
}
public static void LoadPreview(CSong song, float start = -1f)
{
if (song == null)
throw new ArgumentNullException("song");
if (!IsPlayingPreview)
{
Pause();
CSound.SetGlobalVolume(CConfig.PreviewMusicVolume);
}
bool songChanged = _CurPlayer.SongID != song.ID;
_PreviewPlayer.Load(song);
//Change song position only if song is changed or near to end
if (songChanged || _CurPlayer.Position + 30 < _CurPlayer.Length)
{
_PreviewStartHelperTask = new Task(() =>
{
float length = _PreviewPlayer.Length;
if (length < 1)
length = 30; // If length is unknow or invalid assume a length of 30s
if (start < 0)
start = (song.Preview.Source == EDataSource.None) ? length / 4f : song.Preview.StartTime;
if (start > length - 5f)
start = Math.Max(0f, Math.Min(length / 4f, length - 5f));
if (start >= 0.5f)
start -= 0.5f;
_PreviewPlayer.Position = start;
Play();
});
_CurPlayer = _PreviewPlayer;
}
else
{
_PreviewStartHelperTask.Dispose();
_PreviewStartHelperTask = null;
_PreviewPlayer.Position = _CurPlayer.Position;
_CurPlayer = _PreviewPlayer;
Play();
}
}
public static void StopPreview()
{
if (!IsPlayingPreview)
return;
Stop();
_CurPlayer = _BGPlayer;
if (_MusicSource != EBackgroundMusicSource.TR_CONFIG_NO_OWN_MUSIC)
{
_CurPlayer.Load(CSongs.GetSong(_PreviewPlayer.SongID));
_CurPlayer.Position = _PreviewPlayer.Position;
}
CSound.SetGlobalVolume(CConfig.BackgroundMusicVolume);
}
/// <summary>
/// (Re-)Starts the _CurrentPlaylistElement
/// </summary>
private static void _StartSong()
{
Debug.Assert(_CurrentPlaylistElement != null);
//If current song same as loaded restart only
if (_BGPlayer.SoundLoaded && _CurrentPlaylistElement.MusicFilePath == _BGPlayer.FilePath)
{
_BGPlayer.Stop();
_BGPlayer.Play();
return;
}
//otherwhise load
if (!_CurrentPlaylistElement.HasMetaData)
_BGPlayer.Load(_CurrentPlaylistElement.MusicFilePath, 0f, true);
else
{
//Seek to #Start-Tag, if found
float start = 0f;
if (_CurrentPlaylistElement.Start > 0.001 && CConfig.Config.Sound.BackgroundMusicUseStart == EOffOn.TR_CONFIG_ON)
start = _CurrentPlaylistElement.Start;
_BGPlayer.Load(_CurrentPlaylistElement.Song, start, true);
}
}
private static void _AddOwnMusic()
{
if (_OwnMusicAdded || !_OwnSongsAvailable)
return;
foreach (CSong song in CSongs.AllSongs)
_NotPlayedFiles.Add(new CPlaylistElement(song));
_OwnMusicAdded = true;
}
private static void _RemoveOwnMusic()
{
if (!_OwnMusicAdded)
return;
_NotPlayedFiles.RemoveAll(el => el.SongID >= 0);
_PreviousFiles.RemoveAll(el => el.SongID >= 0);
_PreviousMusicIndex = _PreviousFiles.IndexOf(_CurrentPlaylistElement);
if (IsPlaying && !_IsBackgroundFile(_CurrentPlaylistElement))
Next();
_OwnMusicAdded = false;
}
private static void _AddBackgroundMusic()
{
if (_BackgroundMusicAdded)
return;
_NotPlayedFiles.AddRange(_BGMusicFiles);
_BackgroundMusicAdded = true;
}
private static void _RemoveBackgroundMusic()
{
if (!_BackgroundMusicAdded)
return;
_NotPlayedFiles.RemoveAll(el => el.SongID < 0);
_PreviousFiles.RemoveAll(el => el.SongID < 0);
_PreviousMusicIndex = _PreviousFiles.IndexOf(_CurrentPlaylistElement);
if (IsPlaying && _IsBackgroundFile(_CurrentPlaylistElement))
Next();
_BackgroundMusicAdded = false;
}
private static bool _IsBackgroundFile(CPlaylistElement element)
{
return _BGMusicFiles.Contains(element);
}
}
}