From 14d9f9a4e4b935b04c9866e5fa8f7712e09860a1 Mon Sep 17 00:00:00 2001 From: Daniel Gruss Date: Mon, 20 May 2024 14:40:16 +0200 Subject: [PATCH] add karaoke mode via #INSTRUMENTALS tag, key to toggle karaoke mode is K while playing. --- game/languages/English.ini | 1 + src/base/UMusic.pas | 3 +- src/base/USong.pas | 15 ++++- src/media/UAudioPlaybackBase.pas | 67 +++++++++++++++++-- src/media/UMedia_dummy.pas | 9 ++- src/screens/UScreenCredits.pas | 8 +-- src/screens/UScreenEditSub.pas | 6 +- src/screens/UScreenJukebox.pas | 2 +- src/screens/UScreenScore.pas | 2 +- src/screens/UScreenSong.pas | 2 +- .../controllers/UScreenSingController.pas | 7 +- 11 files changed, 98 insertions(+), 24 deletions(-) diff --git a/game/languages/English.ini b/game/languages/English.ini index b10ae0f76..6e4d37f31 100644 --- a/game/languages/English.ini +++ b/game/languages/English.ini @@ -1455,6 +1455,7 @@ Q = Quit UltraStar Deluxe SEC_002 = Playback SPACE = Pause/unpause song P = Pause/unpause song +K = Toggle karaoke mode (if file is available via the #INSTRUMENTAL tag) R = Restart song from beginning V = Toggle video/visualization/background (if available) W = Toggle webcam on/off (if available) diff --git a/src/base/UMusic.pas b/src/base/UMusic.pas index 1ce41323e..b3ad1e79a 100644 --- a/src/base/UMusic.pas +++ b/src/base/UMusic.pas @@ -473,10 +473,11 @@ TAudioOutputDevice = class function Finished: boolean; function Length: real; - function Open(const Filename: IPath): boolean; // true if succeed + function Open(const Filename: IPath; const FilenameKaraoke: IPath): boolean; // true if succeed procedure Close; procedure Play; + procedure ToggleKaraoke; procedure Pause; procedure Stop; diff --git a/src/base/USong.pas b/src/base/USong.pas index d68669c8a..7d6890f2b 100644 --- a/src/base/USong.pas +++ b/src/base/USong.pas @@ -129,6 +129,7 @@ TSong = class Mp3: IPath; Background: IPath; Video: IPath; + Karaoke: IPath; // sorting methods Genre: UTF8String; @@ -264,6 +265,7 @@ constructor TSong.Create(); Self.FileName := PATH_NONE(); Self.Cover := PATH_NONE(); Self.Mp3 := PATH_NONE(); + Self.Karaoke := PATH_NONE(); Self.Background:= PATH_NONE(); Self.Video := PATH_NONE(); end; @@ -860,7 +862,10 @@ function TSong.ReadTXTHeader(SongFile: TTextFileStream; ReadCustomTags: Boolean) if (Self.Path.Append(EncFile).IsFile) then begin self.Mp3 := EncFile; - + if (not Assigned(self.Karaoke)) or (self.Karaoke = PATH_NONE()) then + begin + self.Karaoke := EncFile; + end; //Add Mp3 Flag to Done Done := Done or 4; end @@ -919,6 +924,14 @@ function TSong.ReadTXTHeader(SongFile: TTextFileStream; ReadCustomTags: Boolean) Log.LogError('Can''t find video file in song: ' + FullFileName); end + // Karaoke Mp3 + else if (Identifier = 'INSTRUMENTAL') then + begin + EncFile := DecodeFilename(Value); + if (self.Path.Append(EncFile).IsFile) then + self.Karaoke := EncFile; + end + // Video Gap else if (Identifier = 'VIDEOGAP') then begin diff --git a/src/media/UAudioPlaybackBase.pas b/src/media/UAudioPlaybackBase.pas index 87f698f85..e8f33fafc 100644 --- a/src/media/UAudioPlaybackBase.pas +++ b/src/media/UAudioPlaybackBase.pas @@ -49,6 +49,8 @@ TAudioPlaybackBase = class(TInterfacedObject, IAudioPlayback) OutputDeviceList: TAudioOutputDeviceList; MusicStream: TAudioPlaybackStream; + KaraokeMusicStream: TAudioPlaybackStream; + KaraokeMode: boolean; IReplayGain: FReplayGain; @@ -64,11 +66,12 @@ TAudioPlaybackBase = class(TInterfacedObject, IAudioPlayback) constructor Create(); virtual; function GetName: string; virtual; abstract; - function Open(const Filename: IPath): boolean; // true if succeed + function Open(const Filename: IPath; const FilenameKaraoke: IPath): boolean; // true if succeed procedure Close; procedure Play; procedure Pause; + procedure ToggleKaraoke; procedure Stop; procedure FadeIn(Time: real; TargetVolume: single); procedure Fade(Time: real; TargetVolume: single); @@ -135,6 +138,7 @@ implementation constructor TAudioPlaybackBase.Create(); begin inherited; + KaraokeMode := false; end; { TAudioPlaybackBase } @@ -142,14 +146,16 @@ constructor TAudioPlaybackBase.Create(); function TAudioPlaybackBase.FinalizePlayback: boolean; begin FreeAndNil(MusicStream); + FreeAndNil(KaraokeMusicStream); ClearOutputDeviceList(); Result := true; end; -function TAudioPlaybackBase.Open(const Filename: IPath): boolean; +function TAudioPlaybackBase.Open(const Filename: IPath; const FilenameKaraoke: IPath): boolean; begin // free old MusicStream MusicStream.Free; + KaraokeMusicStream.Free; MusicStream := OpenStream(Filename); if not assigned(MusicStream) then @@ -158,7 +164,19 @@ function TAudioPlaybackBase.Open(const Filename: IPath): boolean; Exit; end; - if assigned(IReplayGain) and IReplayGain.CanEnable then MusicStream.AddSoundFX(IReplayGain.Create()); + if assigned(FilenameKaraoke) then + KaraokeMusicStream := OpenStream(FilenameKaraoke); + + if assigned(IReplayGain) and IReplayGain.CanEnable then begin + MusicStream.AddSoundFX(IReplayGain.Create()); + if assigned(KaraokeMusicStream) then + KaraokeMusicStream.AddSoundFX(IReplayGain.Create()); + end; + + KaraokeMode := false; + if assigned(KaraokeMusicStream) then begin + KaraokeMusicStream.Volume := 0; + end; Result := true; end; @@ -166,6 +184,7 @@ function TAudioPlaybackBase.Open(const Filename: IPath): boolean; procedure TAudioPlaybackBase.Close; begin FreeAndNil(MusicStream); + FreeAndNil(KaraokeMusicStream); end; function TAudioPlaybackBase.OpenDecodeStream(const Filename: IPath): TAudioDecodeStream; @@ -251,18 +270,38 @@ procedure TAudioPlaybackBase.Play; begin if assigned(MusicStream) then MusicStream.Play(); + if assigned(KaraokeMusicStream) then + KaraokeMusicStream.Play(); end; procedure TAudioPlaybackBase.Pause; begin if assigned(MusicStream) then MusicStream.Pause(); + if assigned(KaraokeMusicStream) then + KaraokeMusicStream.Pause(); +end; + +procedure TAudioPlaybackBase.ToggleKaraoke; +begin + KaraokeMode := not KaraokeMode; + if KaraokeMode and assigned(KaraokeMusicStream) and assigned(MusicStream) then begin + KaraokeMusicStream.Volume := MusicStream.Volume; + MusicStream.Volume := 0; + end; + if (not KaraokeMode) and assigned(KaraokeMusicStream) and assigned(MusicStream) then begin + MusicStream.Volume := KaraokeMusicStream.Volume; + KaraokeMusicStream.Volume := 0; + end; end; procedure TAudioPlaybackBase.Stop; begin if assigned(MusicStream) then MusicStream.Stop(); + if assigned(KaraokeMusicStream) then + KaraokeMusicStream.Stop(); + KaraokeMode := false; end; function TAudioPlaybackBase.Length: real; @@ -289,12 +328,16 @@ procedure TAudioPlaybackBase.SetPosition(Time: real); begin if assigned(MusicStream) then MusicStream.Position := Time; + if assigned(KaraokeMusicStream) then + KaraokeMusicStream.Position := Time; end; procedure TAudioPlaybackBase.SetSyncSource(SyncSource: TSyncSource); begin if assigned(MusicStream) then MusicStream.SetSyncSource(SyncSource); + if assigned(KaraokeMusicStream) then + KaraokeMusicStream.SetSyncSource(SyncSource); end; procedure TAudioPlaybackBase.Rewind; @@ -312,26 +355,34 @@ function TAudioPlaybackBase.Finished: boolean; procedure TAudioPlaybackBase.SetVolume(Volume: single); begin - if assigned(MusicStream) then + if (not KaraokeMode) and assigned(MusicStream) then MusicStream.Volume := Volume; + if KaraokeMode and assigned(KaraokeMusicStream) then + KaraokeMusicStream.Volume := Volume; end; procedure TAudioPlaybackBase.FadeIn(Time: real; TargetVolume: single); begin - if assigned(MusicStream) then + if (not KaraokeMode) and assigned(MusicStream) then MusicStream.FadeIn(Time, TargetVolume); + if KaraokeMode and assigned(KaraokeMusicStream) then + KaraokeMusicStream.FadeIn(Time, TargetVolume); end; procedure TAudioPlaybackBase.Fade(Time: real; TargetVolume: single); begin - if assigned(MusicStream) then + if (not KaraokeMode) and assigned(MusicStream) then MusicStream.Fade(Time, TargetVolume); + if KaraokeMode and assigned(KaraokeMusicStream) then + KaraokeMusicStream.Fade(Time, TargetVolume); end; procedure TAudioPlaybackBase.SetLoop(Enabled: boolean); begin if assigned(MusicStream) then MusicStream.Loop := Enabled; + if assigned(KaraokeMusicStream) then + KaraokeMusicStream.Loop := Enabled; end; // Equalizer @@ -347,8 +398,10 @@ procedure TAudioPlaybackBase.GetFFTData(var data: TFFTData); *} function TAudioPlaybackBase.GetPCMData(var data: TPCMData): Cardinal; begin - if assigned(MusicStream) then + if (not KaraokeMode) and assigned(MusicStream) then Result := MusicStream.GetPCMData(data) + else if KaraokeMode and assigned(KaraokeMusicStream) then + Result := KaraokeMusicStream.GetPCMData(data) else Result := 0; end; diff --git a/src/media/UMedia_dummy.pas b/src/media/UMedia_dummy.pas index 0b6eff247..0811a68b0 100644 --- a/src/media/UMedia_dummy.pas +++ b/src/media/UMedia_dummy.pas @@ -54,10 +54,11 @@ TAudio_Dummy = class( TInterfacedObject, IAudioPlayback, IAudioInput ) function Init(): boolean; function Finalize(): boolean; - function Open(const aFileName: IPath): boolean; // true if succeed + function Open(const aFileName: IPath; const aFileNameKaraoke: IPath): boolean; // true if succeed procedure Close; procedure Play; + procedure ToggleKaraoke; procedure Pause; procedure Stop; @@ -187,7 +188,7 @@ function TAudio_Dummy.Finalize(): boolean; Result := true; end; -function TAudio_Dummy.Open(const aFileName : IPath): boolean; // true if succeed +function TAudio_Dummy.Open(const aFileName : IPath; const aFileNameKaraoke : IPath): boolean; // true if succeed begin Result := false; end; @@ -200,6 +201,10 @@ procedure TAudio_Dummy.Play; begin end; +procedure TAudio_Dummy.ToggleKaraoke; +begin +end; + procedure TAudio_Dummy.Pause; begin end; diff --git a/src/screens/UScreenCredits.pas b/src/screens/UScreenCredits.pas index 9db1102f7..0b546c0ca 100644 --- a/src/screens/UScreenCredits.pas +++ b/src/screens/UScreenCredits.pas @@ -368,7 +368,7 @@ procedure TScreenCredits.OnShow; Credits_X := 580; { open credits tune, we play it after initial delay } - AudioPlayback.Open(soundpath.Append('wome-credits-tune.mp3')); // thank you wetue + AudioPlayback.Open(soundpath.Append('wome-credits-tune.mp3'),nil); // thank you wetue { reset twinkling stars } GoldenRec.KillAll; @@ -893,7 +893,7 @@ procedure TScreenCredits.DrawOutro; begin CTime_hold := 0; AudioPlayback.Stop; - AudioPlayback.Open(SoundPath.Append('credits-outro-tune.mp3')); + AudioPlayback.Open(SoundPath.Append('credits-outro-tune.mp3'),nil); AudioPlayback.SetVolume(0.2); AudioPlayback.SetLoop(true); AudioPlayback.Play; @@ -1268,10 +1268,6 @@ procedure Effect_Twinkle_Down (const Tex: TTexture; Progress: double); GoldenRec.Spawn(NameX + RandomRange(-NameW div 2, NameW div 2), NameY - NameH/2 + (1 - Progress) * NameH, 1, 16, 0, -1, PerfectLineTwinkle, 5); end; -{ beat detection algorithm - based on a tutorial from Frédéric Patin on gamedev.net - http://www.gamedev.net/reference/programming/features/beatdetection/default.asp } - { calculates average value of a history buffer } function Average(History: TEnergyHistory): single; var I: integer; diff --git a/src/screens/UScreenEditSub.pas b/src/screens/UScreenEditSub.pas index 408b23e38..205314531 100644 --- a/src/screens/UScreenEditSub.pas +++ b/src/screens/UScreenEditSub.pas @@ -2792,7 +2792,7 @@ function TScreenEditSub.ParseMouse(MouseButton: Integer; BtnDown: boolean; X, Y: SelectsS[Interactions[nBut].Num].SelectedOption := SelectsS[Interactions[nBut].Num].SelectedOption -1; CurrentSong.Mp3 := Path(SelectsS[Interactions[nBut].Num].TextOptT[SelectsS[Interactions[nBut].Num].SelectedOption]); AudioPlayback.Close; - AudioPlayback.Open(CurrentSong.Path.Append(CurrentSong.Mp3)); + AudioPlayback.Open(CurrentSong.Path.Append(CurrentSong.Mp3),nil); end; if ((Mp3SlideId = Interactions[nBut].Num) and (Action = maRight) and (SelectsS[Interactions[nBut].Num].SelectedOption < Length(SelectsS[Interactions[nBut].Num].TextOptT)-1)) then @@ -2801,7 +2801,7 @@ function TScreenEditSub.ParseMouse(MouseButton: Integer; BtnDown: boolean; X, Y: SelectsS[Interactions[nBut].Num].SelectedOption := SelectsS[Interactions[nBut].Num].SelectedOption +1; CurrentSong.Mp3 := Path(SelectsS[Interactions[nBut].Num].TextOptT[SelectsS[Interactions[nBut].Num].SelectedOption]); AudioPlayback.Close(); - AudioPlayback.Open(CurrentSong.Path.Append(CurrentSong.Mp3)); + AudioPlayback.Open(CurrentSong.Path.Append(CurrentSong.Mp3),nil); end; if (((VolumeAudioSlideId = Interactions[nBut].Num) or (VolumeMidiSlideId = Interactions[nBut].Num) or (VolumeClickSlideId = Interactions[nBut].Num)) @@ -4933,7 +4933,7 @@ procedure TScreenEditSub.OnShow; Tracks[TrackIndex].Lines[0].Notes[0].Color := P1_INVERTED; end; - AudioPlayBack.Open(CurrentSong.Path.Append(CurrentSong.Mp3)); + AudioPlayBack.Open(CurrentSong.Path.Append(CurrentSong.Mp3),nil); //Set Down Music Volume for Better hearability of Midi Sounds //Music.SetVolume(0.4); diff --git a/src/screens/UScreenJukebox.pas b/src/screens/UScreenJukebox.pas index 4383605a6..c596a6a8b 100644 --- a/src/screens/UScreenJukebox.pas +++ b/src/screens/UScreenJukebox.pas @@ -2174,7 +2174,7 @@ procedure TScreenJukebox.Play(); var I: integer; begin - AudioPlayback.Open(CurrentSong.Path.Append(CurrentSong.Mp3)); + AudioPlayback.Open(CurrentSong.Path.Append(CurrentSong.Mp3),nil); AudioPlayback.SetVolume(1.0); //AudioPlayback.Position := CurrentSong.Start; diff --git a/src/screens/UScreenScore.pas b/src/screens/UScreenScore.pas index 60dce71a1..0575568c6 100644 --- a/src/screens/UScreenScore.pas +++ b/src/screens/UScreenScore.pas @@ -1827,7 +1827,7 @@ procedure TScreenSCore.StartPreview; begin AudioPlayback.Close; - if AudioPlayback.Open(CatSongs.Song[select].Path.Append(CatSongs.Song[select].Mp3)) then + if AudioPlayback.Open(CatSongs.Song[select].Path.Append(CatSongs.Song[select].Mp3),nil) then begin if (CatSongs.Song[select].PreviewStart > 0) then AudioPlayback.Position := CatSongs.Song[select].PreviewStart diff --git a/src/screens/UScreenSong.pas b/src/screens/UScreenSong.pas index bc1ac2e5d..26de591d0 100644 --- a/src/screens/UScreenSong.pas +++ b/src/screens/UScreenSong.pas @@ -3776,7 +3776,7 @@ procedure TScreenSong.StartMusicPreview(); Exit; PlayMidi := false; - if AudioPlayback.Open(Song.Path.Append(Song.Mp3)) then + if AudioPlayback.Open(Song.Path.Append(Song.Mp3),nil) then begin PreviewOpened := Interaction; diff --git a/src/screens/controllers/UScreenSingController.pas b/src/screens/controllers/UScreenSingController.pas index af2fccb1c..2c7be17b0 100644 --- a/src/screens/controllers/UScreenSingController.pas +++ b/src/screens/controllers/UScreenSingController.pas @@ -538,6 +538,11 @@ function TScreenSingController.ParseInput(PressedKey: Cardinal; CharCode: UCS4Ch Pause; end; + SDLK_K: + begin + AudioPlayback.ToggleKaraoke; + end; + SDLK_RIGHT: begin if (SDL_ModState = KMOD_LCTRL) then // seek 5 seconds forward @@ -857,7 +862,7 @@ procedure TScreenSingController.onShowFinish; PlayMidi := false; MidiFadeIn := false; - AudioPlayback.Open(CurrentSong.Path.Append(CurrentSong.Mp3)); + AudioPlayback.Open(CurrentSong.Path.Append(CurrentSong.Mp3),CurrentSong.Path.Append(CurrentSong.Karaoke)); if ScreenSong.Mode = smMedley then AudioPlayback.SetVolume(0.1) else