Files
McSchneidarandFlorian Ostertag 4ee6689ff4 Seamless audio loops and own credits music (#945)
Loops
- Looping is now done by the playback backends instead of restarting
  the stream with Stop/Play. Loop can be toggled at runtime (SetLoop in
  ISound/IPlayback/IAudioStream), so the repeat button keeps working.
- GStreamer: gapless loops via playbin's about-to-finish signal.
- PortAudio/OpenAL: the decoder is reopened at the end of the file
  (Acinerella's seek does not reset FFmpeg after EOF) while the buffered
  data keeps playing, so the loop has no gap. The reported position wraps
  correctly.

Credits screen
- New event music player in CBackgroundMusic that replaces the current
  background music and resumes it afterwards.
- The credits screen loops Sounds/CreditsMusic.mp3
  ("Atmosphere Calm Background" by lvymusic, Pixabay Content License).
- As the credits screen was touched anyway, its content was updated as
  well.

Fixes for PortAudio/OpenAL playback found on the way
- Acinerella delivers 32 bit float samples for mp3, so every file was
  rejected with "Unsupported BitDepth". Float samples are converted to
  16 bit now.
- Acinerella reports a missing frame (EAGAIN) like the end of the file,
  so decoding is retried a few times before treating it as the end.
- OpenAL stopped after half a second because the decoder thread was
  never woken up to refill its buffer. A source that ran out of data is
  restarted as well.

Part of #677

Co-authored-by: Florian Ostertag <flokuep@users.noreply.github.com>
2026-10-27 09:43:02 +01:00

351 lines
11 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.Collections.ObjectModel;
using System.Drawing;
using VocaluxeLib.Draw;
using VocaluxeLib.Game;
using VocaluxeLib.Menu;
using VocaluxeLib.Profile;
using VocaluxeLib.Songs;
namespace VocaluxeLib
{
public delegate void OnSongMenuChanged();
public interface IConfig
{
EOffOn GetSaveModifiedSongs();
void SetBackgroundMusicVolume(int newVolume);
int GetMusicVolume(EMusicType type);
EBackgroundMusicOffOn GetBackgroundMusicStatus();
int GetPreviewMusicVolume();
EOffOn GetVideosToBackground();
EOffOn GetVideoBackgrounds();
EOffOn GetVideoPreview();
ESongMenu GetSongMenuType();
EHighscoreStyle GetHighscoreStyle();
EOffOn GetDrawNoteLines();
EOffOn GetDrawToneHelper();
EOffOn GetTileCaption();
int GetCoverSize();
IEnumerable<string> GetSongFolders();
ESongSorting GetSongSorting();
EOffOn GetTabs();
EOffOn GetAutoplayPreviews();
int GetAutoplayPreviewDelay();
EOffOn GetIgnoreArticles();
bool IsMicConfigured(int playerNr);
int GetMaxNumMics();
void AddSongMenuListener(OnSongMenuChanged onSongMenuChanged);
void RemoveSongMenuListener(OnSongMenuChanged onSongMenuChanged);
}
public interface ISettings
{
int GetRenderW();
int GetRenderH();
SRectF GetRenderRect();
bool IsTabNavigation();
float GetZFar();
float GetZNear();
EProgramState GetProgramState();
int GetToneMin();
int GetToneMax();
int GetNumNoteLines();
int GetMaxNumPlayer();
int GetMaxNumScreens();
float GetDefaultMedleyFadeInTime();
float GetDefaultMedleyFadeOutTime();
int GetMedleyMinSeriesLength();
float GetMedleyMinDuration();
string GetFolderProfiles();
string GetDataPath();
float GetSlideShowImageTime();
float GetSlideShowFadeTime();
float GetSoundPlayerFadeTime();
}
public interface IThemes
{
string GetThemeScreensPath(int partyModeId);
CTextureRef GetSkinTexture(string textureName, int partyModeId);
CVideoStream GetSkinVideo(string videoName, int partyModeId, bool loop);
bool GetColor(string colorName, int partyModeId, out SColorF color);
SColorF GetPlayerColor(int playerNr);
void Reload();
}
public interface IBackgroundMusic
{
bool IsDisabled();
bool IsPlaying();
bool IsPlayingPreview();
bool SongHasVideo();
bool VideoEnabled();
float GetLength();
void SetDisabled(bool disabled);
void Next();
void Previous();
void Pause();
void Play();
void Stop();
CTextureRef GetVideoTexture();
void LoadPreview(CSong song, float start = -1f);
void StopPreview();
void SetPlayingPreview(bool playPreview);
}
public interface IDrawing
{
void DrawTexture(CTextureRef texture, SRectF rect);
void DrawTexture(CTextureRef texture, SRectF rect, SColorF color, bool allMonitors = true);
void DrawTexture(CTextureRef texture, SRectF rect, SColorF color, SRectF bounds, bool mirrored = false, bool allMonitors = true);
void DrawTexture(CTextureRef textureRef, SRectF bounds, EAspect aspect);
void DrawTexture(CTextureRef textureRef, SRectF bounds, EAspect aspect, SColorF color);
void DrawTextureReflection(CTextureRef texture, SRectF rect, SColorF color, SRectF bounds, float reflectionSpace, float reflectionHeight, bool allMonitors = true);
CTextureRef AddTexture(string fileName);
CTextureRef EnqueueTexture(string fileName);
void RemoveTexture(ref CTextureRef texture);
void DrawRect(SColorF color, SRectF rect, bool allMonitors = true);
void DrawRectReflection(SColorF color, SRectF rect, float space, float height);
void PushTransform(float scale, float centerX, float centerY, float offsetY, float alpha);
void PopTransform();
}
public interface IGraphics
{
void ReloadTheme();
void SaveTheme();
void FadeTo(EScreen nextScreen);
void FadeTo(IMenu nextScreen);
float GetGlobalAlpha();
IMenu GetNextScreen();
EScreen GetNextScreenType();
IMenu GetScreen(EScreen screen);
}
public interface IFonts
{
RectangleF GetTextBounds(CText text);
void DrawText(string text, CFont font, float x, float y, float z, SColorF color, bool allMonitors = true);
void DrawTextReflection(string text, CFont font, float x, float y, float z, SColorF color, float reflectionSpace, float reflectionHeight);
void DrawText(string text, CFont font, float x, float y, float z, SColorF color, float begin, float end);
}
public interface ILanguage
{
string Translate(string keyWord);
string Translate(string keyWord, int partyModeId);
bool TranslationExists(string keyWord);
}
public interface IGame
{
int GetNumPlayer();
void SetNumPlayer(int numPlayer);
SPlayer[] GetPlayers();
CPoints GetPoints();
int GetCurrentBeat();
float GetMidRecordedBeat();
int GetRecordedBeat();
int GetRandom(int max);
double GetRandomDouble();
float GetTimeFromBeats(float beat, float bpm);
float GetBeatFromTime(float time, float bpm, float gap);
void AddSong(int songId, EGameMode gameMode);
void AddSong(int songId, EGameMode gameMode, EAudioMode audioMode);
void Reset();
void ClearSongs();
int GetNumSongs();
CSong GetSong();
CSong GetSong(int round);
}
public interface IRecording
{
int GetToneAbs(int player);
void AnalyzeBuffer(int player);
float GetMaxVolume(int player);
float[] ToneWeigth(int player);
bool Start();
bool Stop();
}
public interface IProfiles
{
CProfile[] GetProfiles();
int GetNum();
EGameDifficulty GetDifficulty(Guid profileId);
string GetPlayerName(Guid profileId, int playerNum = 0);
CTextureRef GetAvatar(Guid profileId);
CAvatar GetAvatarByFilename(string fileName);
bool IsProfileIdValid(Guid profileId);
bool IsGuest(Guid profileId);
void AddProfileChangedCallback(ProfileChangedCallback notification);
}
public interface ISongs
{
int GetNumSongs();
int GetNumSongsVisible();
int GetNumCategories();
int GetNumSongsInCategory(int categoryIndex);
int GetNumSongsNotSungInCategory(int categoryIndex);
bool IsInCategory();
int GetCurrentCategoryIndex();
EOffOn GetTabs();
string GetSearchFilter();
void SetCategory(int categoryIndex);
void UpdateRandomSongList();
CSong GetVisibleSong(int visibleIndex);
CSong GetSongById(int songId);
ReadOnlyCollection<CSong> GetSongs();
ReadOnlyCollection<CSong> GetVisibleSongs();
CCategory GetCategory(int index);
void AddPartySongSung(int songId);
void ResetSongSung(int catIndex = -1);
void SortSongs(ESongSorting sorting, EOffOn tabs, EOffOn ignoreArticles, String searchString, EDuetOptions duetOptions, int playlistId);
void NextCategory();
void PrevCategory();
}
public interface IVideo
{
CVideoStream Load(string videoFileName);
bool Skip(CVideoStream stream, float startPosition, float videoGap);
bool GetFrame(CVideoStream stream, float time);
bool IsFinished(CVideoStream stream);
void Close(ref CVideoStream stream);
void SetLoop(CVideoStream stream, bool loop = true);
void Resume(CVideoStream stream);
void Pause(CVideoStream stream);
}
public interface ISound
{
int Load(string soundFile, bool loop = false, bool prescan = false);
void SetPosition(int streamId, float newPosition);
void Play(int streamId);
void Fade(int streamId, int targetVolume, float duration, EStreamAction afterFadeAction = EStreamAction.Nothing);
void Close(int streamId);
bool IsFinished(int streamId);
float GetPosition(int streamId);
float GetLength(int streamId);
void SetStreamVolume(int streamId, int volume);
void SetGlobalVolume(int volume);
bool IsPaused(int streamId);
void SetLoop(int streamId, bool loop);
}
public interface ICover
{
CTextureRef GetNoCover();
CTextureRef GenerateCover(string text, ECoverGeneratorType type, CSong firstSong);
}
public interface IDataBase
{
bool GetCover(string fileName, ref CTextureRef texture, int coverSize);
bool GetDataBaseSongInfos(string artist, string title, out int numPlayed, out DateTime dateAdded, out int highscoreId);
}
public interface IControllers
{
void SetRumble(float duration);
}
public interface IPlaylist
{
bool Exists(int playlistId);
string GetName(int playlistId);
List<int> GetIds();
List<string> GetNames();
void SetName(int playlistId, string name);
void Delete(int playlistId);
void Save(int playlistId);
int GetNumPlaylists();
void AddSong(int playlistId, int songId);
void AddSong(int playlistId, int songId, EGameMode gameMode);
void InsertSong(int playlistId, int positionIndex, int songId, EGameMode gameMode);
void MoveSong(int playlistId, int sourceIndex, int destIndex);
void MoveSongDown(int playlistId, int songIndex);
void MoveSongUp(int playlistId, int songIndex);
void DeleteSong(int playlistId, int songIndex);
int GetSongCount(int playlistId);
CPlaylistSong GetSong(int playlistId, int songIndex);
bool ContainsSong(int playlistId, int songIndex);
}
public interface IPreviewPlayer
{
void Play(float start = -1);
void Load(CSong song, float start = 0f);
void Stop();
void TogglePause();
CTextureRef GetCover();
CTextureRef GetVideoTexture();
bool IsPlaying();
float GetLength();
}
}