From 427dfe81201dec0f92f712ca774eaf301fae61bf Mon Sep 17 00:00:00 2001 From: Daniel Gruss Date: Thu, 27 Nov 2025 23:33:07 +0100 Subject: [PATCH 1/5] report progress about how many songs have been parsed --- src/base/USongs.pas | 149 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 120 insertions(+), 29 deletions(-) diff --git a/src/base/USongs.pas b/src/base/USongs.pas index 63223ef5c..2a2fed101 100644 --- a/src/base/USongs.pas +++ b/src/base/USongs.pas @@ -93,8 +93,17 @@ TSongs = class(TThread) private fParseSongDirectory: boolean; fProcessing: boolean; + fTotalSongsToLoad: integer; + fSongsLoaded: integer; + fLoadingBaseTemplate: UTF8String; + fLoadingBaseText: UTF8String; + fLastProgressRedrawTicks: cardinal; + function CollectSongFiles: TPathDynArray; procedure int_LoadSongList; procedure DoDirChanged(Sender: TObject); + procedure LoadSongFromFile(const FilePath: IPath); + procedure UpdateLoadingProgress; + procedure RedrawLoadingScreen; protected procedure Execute; override; public @@ -105,9 +114,7 @@ TSongs = class(TThread) destructor Destroy(); override; procedure LoadSongList; // load all songs - procedure FindFilesByExtension(const Dir: IPath; const Ext: IPath; Recursive: Boolean; var Files: TPathDynArray); - procedure BrowseDir(Dir: IPath); // should return number of songs in the future - procedure BrowseTXTFiles(Dir: IPath); + procedure FindFilesByExtension(const Dir: IPath; const Ext: IPath; Recursive: Boolean; var Files: TPathDynArray); procedure Sort(Order: TSortingType); property Processing: boolean read fProcessing; end; @@ -160,9 +167,12 @@ implementation uses StrUtils, + SDL2, UCovers, UFiles, UGraphic, + UDisplay, + UMenuText, UMain, UPathUtils, UNote, @@ -217,15 +227,31 @@ procedure TSongs.Execute(); procedure TSongs.int_LoadSongList; var I: integer; + SongFiles: TPathDynArray; begin + SetLength(SongFiles, 0); try fProcessing := true; Log.LogStatus('Searching For Songs', 'SongList'); - // browse directories - for I := 0 to SongPaths.Count-1 do - BrowseDir(SongPaths[I] as IPath); + fSongsLoaded := 0; + fTotalSongsToLoad := 0; + fLoadingBaseTemplate := ''; + fLoadingBaseText := ''; + UpdateLoadingProgress; + + SongFiles := CollectSongFiles; + + fTotalSongsToLoad := Length(SongFiles); + UpdateLoadingProgress; + + for I := 0 to High(SongFiles) do + begin + LoadSongFromFile(SongFiles[I]); + Inc(fSongsLoaded); + UpdateLoadingProgress; + end; if assigned(CatSongs) then CatSongs.Refresh; @@ -243,6 +269,7 @@ procedure TSongs.int_LoadSongList; end; finally + SetLength(SongFiles, 0); Log.LogStatus('Search Complete', 'SongList'); fParseSongDirectory := false; @@ -257,11 +284,6 @@ procedure TSongs.LoadSongList; Resume(); end; -procedure TSongs.BrowseDir(Dir: IPath); -begin - BrowseTXTFiles(Dir); -end; - procedure TSongs.FindFilesByExtension(const Dir: IPath; const Ext: IPath; Recursive: Boolean; var Files: TPathDynArray); var Iter: IFileIterator; @@ -290,38 +312,107 @@ procedure TSongs.FindFilesByExtension(const Dir: IPath; const Ext: IPath; Recurs Log.LogDebug('Found file ' + Dir.Append(FileName).ToWide, 'TSongs.FindFilesByExtension'); SetLength(Files, Length(Files)+1); Files[High(Files)] := Dir.Append(FileName); + fTotalSongsToLoad := fTotalSongsToLoad + 1; + UpdateLoadingProgress; end; end; end; end; -procedure TSongs.BrowseTXTFiles(Dir: IPath); +function TSongs.CollectSongFiles: TPathDynArray; var - I: integer; - Files: TPathDynArray; - Song: TSong; - //CloneSong: TSong; + DirIndex, FileIndex, AppendIndex, AdditionalCount: integer; + DirFiles: TPathDynArray; Extension: IPath; + DirPath: IPath; begin - Log.LogDebug('Searching directory ' + Dir.ToWide + ' for txt files', 'TSongs.BrowseTXTFiles'); - SetLength(Files, 0); + SetLength(Result, 0); + + if SongPaths = nil then + Exit; + Extension := Path('.txt'); - FindFilesByExtension(Dir, Extension, true, Files); - for I := 0 to High(Files) do + for DirIndex := 0 to SongPaths.Count - 1 do begin - Song := TSong.Create(Files[I]); - - if Song.Analyse then - SongList.Add(Song) - else - begin - Log.LogError('AnalyseFile failed for "' + Files[I].ToNative + '".'); - FreeAndNil(Song); + DirPath := SongPaths[DirIndex] as IPath; + Log.LogDebug('Searching directory ' + DirPath.ToWide + ' for txt files', 'TSongs.CollectSongFiles'); + SetLength(DirFiles, 0); + FindFilesByExtension(DirPath, Extension, true, DirFiles); + AdditionalCount := Length(DirFiles); + if AdditionalCount = 0 then + Continue; + + AppendIndex := Length(Result); + SetLength(Result, AppendIndex + AdditionalCount); + for FileIndex := 0 to AdditionalCount - 1 do begin + Result[AppendIndex + FileIndex] := DirFiles[FileIndex]; + fTotalSongsToLoad := Length(Result); + UpdateLoadingProgress; end; + + SetLength(DirFiles, 0); + end; +end; + +procedure TSongs.LoadSongFromFile(const FilePath: IPath); +var + Song: TSong; +begin + Song := TSong.Create(FilePath); + + if Song.Analyse then + SongList.Add(Song) + else + begin + Log.LogError('AnalyseFile failed for "' + FilePath.ToNative + '".'); + FreeAndNil(Song); end; +end; + +procedure TSongs.UpdateLoadingProgress; +var + Percent: integer; + ProgressText: UTF8String; + LoadingText: TText; + NowTicks: cardinal; +begin + if (ScreenLoading = nil) or (Length(ScreenLoading.Text) = 0) then + Exit; + + NowTicks := SDL_GetTicks(); + if (NowTicks - fLastProgressRedrawTicks < 100) and (fLastProgressRedrawTicks <> 0) then + Exit; + + LoadingText := ScreenLoading.Text[0]; + if LoadingText = nil then + Exit; + + if fLoadingBaseTemplate = '' then + fLoadingBaseTemplate := LoadingText.Text; + + if fLoadingBaseText = '' then + fLoadingBaseText := fLoadingBaseTemplate; + + ProgressText := fLoadingBaseText + UTF8String(' [' + IntToStr(fSongsLoaded) + '/' + IntToStr(fTotalSongsToLoad) + ']'); + + LoadingText.Text := ProgressText; + + RedrawLoadingScreen; + fLastProgressRedrawTicks := NowTicks; +end; + +procedure TSongs.RedrawLoadingScreen; +begin + if (ScreenLoading = nil) or (Display = nil) then + Exit; + + if Display.CurrentScreen <> @ScreenLoading then + Exit; - SetLength(Files, 0); + ScreenLoading.Draw; + Display.Draw; + SwapBuffers; end; (* From e7e9b4654d31c8ba2dd9aa60fca423a634b493b9 Mon Sep 17 00:00:00 2001 From: Daniel Gruss Date: Mon, 15 Dec 2025 18:48:18 +0100 Subject: [PATCH 2/5] speed up file discovery --- src/base/USongs.pas | 75 +++++++++++++++++++++++++++++++++------------ 1 file changed, 55 insertions(+), 20 deletions(-) diff --git a/src/base/USongs.pas b/src/base/USongs.pas index 2a2fed101..146216404 100644 --- a/src/base/USongs.pas +++ b/src/base/USongs.pas @@ -286,34 +286,69 @@ procedure TSongs.LoadSongList; procedure TSongs.FindFilesByExtension(const Dir: IPath; const Ext: IPath; Recursive: Boolean; var Files: TPathDynArray); var + DirList: TPathDynArray; + DirCount, DirIdx: Integer; Iter: IFileIterator; FileInfo: TFileInfo; FileName: IPath; -begin - // search for all files and directories - Iter := FileSystem.FileFind(Dir.Append('*'), faAnyFile); - while (Iter.HasNext) do + function CollectDirectories(const StartDir: IPath): TPathDynArray; + var + LocalDirs: TPathDynArray; + LocalIter: IFileIterator; + LocalFileInfo: TFileInfo; + LocalFileName: IPath; + SubDirs: TPathDynArray; + SubDirCount, SubDirIdx: Integer; begin - // the debug statements in this function have exactly the same message length before it prints the path - FileInfo := Iter.Next; - FileName := FileInfo.Name; - if ((FileInfo.Attr and faDirectory) <> 0) then + SetLength(LocalDirs, 1); + LocalDirs[0] := StartDir; + if not Recursive then + Exit(LocalDirs); + + // Only search for directories + LocalIter := FileSystem.FileFind(StartDir.Append('*'), faDirectory); + while (LocalIter.HasNext) do begin - if Recursive and (not FileName.Equals('.')) and (not FileName.Equals('..')) and (not FileName.Equals('')) then begin - Log.LogDebug('Recursing: ' + Dir.Append(FileName).ToWide, 'TSongs.FindFilesByExtension'); - FindFilesByExtension(Dir.Append(FileName), Ext, true, Files); + LocalFileInfo := LocalIter.Next; + LocalFileName := LocalFileInfo.Name; + if ((LocalFileInfo.Attr and faDirectory) <> 0) and + (not LocalFileName.Equals('.')) and (not LocalFileName.Equals('..')) and (not LocalFileName.Equals('')) then + begin + // Add subdirectory and recurse + SubDirs := CollectDirectories(StartDir.Append(LocalFileName)); + SubDirCount := Length(SubDirs); + if SubDirCount > 0 then + begin + SetLength(LocalDirs, Length(LocalDirs) + SubDirCount); + for SubDirIdx := 0 to SubDirCount - 1 do + LocalDirs[High(LocalDirs) - SubDirCount + 1 + SubDirIdx] := SubDirs[SubDirIdx]; + end; end; - end - else + end; + Exit(LocalDirs); + end; +begin + // First, collect all directories (including Dir itself) + DirList := CollectDirectories(Dir); + DirCount := Length(DirList); + + for DirIdx := 0 to DirCount - 1 do + begin + Iter := FileSystem.FileFind(DirList[DirIdx].Append('*.txt'), 0); + while (Iter.HasNext) do begin - // do not load files which either have wrong extension or start with a point - if (Ext.Equals(FileName.GetExtension(), true) and not (FileName.ToUTF8()[1] = '.')) then + FileInfo := Iter.Next; + FileName := FileInfo.Name; + if ((FileInfo.Attr and faDirectory) = 0) then begin - Log.LogDebug('Found file ' + Dir.Append(FileName).ToWide, 'TSongs.FindFilesByExtension'); - SetLength(Files, Length(Files)+1); - Files[High(Files)] := Dir.Append(FileName); - fTotalSongsToLoad := fTotalSongsToLoad + 1; - UpdateLoadingProgress; + if (Ext.Equals(FileName.GetExtension(), true)) then + begin + Log.LogDebug('Found file ' + DirList[DirIdx].Append(FileName).ToWide, 'TSongs.FindFilesByExtension'); + SetLength(Files, Length(Files)+1); + Files[High(Files)] := DirList[DirIdx].Append(FileName); + fTotalSongsToLoad := fTotalSongsToLoad + 1; + UpdateLoadingProgress; + end; end; end; end; From ea9ec9a7117c87ac9c9b7e3acf5a7922adf72796 Mon Sep 17 00:00:00 2001 From: Daniel Gruss Date: Sun, 12 Apr 2026 13:06:51 +0200 Subject: [PATCH 3/5] copied progress bar idea and sdl updates from 1226, thanks to g3n35i5 --- src/base/USongs.pas | 225 +++++++++++++++++---------------- src/screens/UScreenLoading.pas | 156 ++++++++++++++++++++++- 2 files changed, 270 insertions(+), 111 deletions(-) diff --git a/src/base/USongs.pas b/src/base/USongs.pas index 146216404..8d8991902 100644 --- a/src/base/USongs.pas +++ b/src/base/USongs.pas @@ -93,17 +93,16 @@ TSongs = class(TThread) private fParseSongDirectory: boolean; fProcessing: boolean; + fDiscoveryDirCount: integer; + fDiscoveryDirsScanned: integer; fTotalSongsToLoad: integer; fSongsLoaded: integer; - fLoadingBaseTemplate: UTF8String; - fLoadingBaseText: UTF8String; - fLastProgressRedrawTicks: cardinal; function CollectSongFiles: TPathDynArray; procedure int_LoadSongList; procedure DoDirChanged(Sender: TObject); procedure LoadSongFromFile(const FilePath: IPath); - procedure UpdateLoadingProgress; - procedure RedrawLoadingScreen; + procedure UpdateDiscoveryProgress(SongsFound: integer; Force: boolean = false); + procedure UpdateSongLoadingProgress(Force: boolean = false); protected procedure Execute; override; public @@ -171,14 +170,55 @@ implementation UCovers, UFiles, UGraphic, - UDisplay, - UMenuText, UMain, UPathUtils, UNote, UFilesystem, UUnicodeUtils; +procedure PumpLoadingEvents; +var + Event: TSDL_Event; +begin + while SDL_PollEvent(@Event) <> 0 do + ; +end; + +function CollectDirectories(const StartDir: IPath; Recursive: Boolean): TPathDynArray; +var + Iter: IFileIterator; + FileInfo: TFileInfo; + FileName: IPath; + SubDirs: TPathDynArray; + SubDirCount, SubDirIdx: Integer; +begin + SetLength(Result, 1); + Result[0] := StartDir; + if not Recursive then + Exit; + + Iter := FileSystem.FileFind(StartDir.Append('*'), faDirectory); + while Iter.HasNext do + begin + FileInfo := Iter.Next; + FileName := FileInfo.Name; + if ((FileInfo.Attr and faDirectory) <> 0) and + (not FileName.Equals('.')) and + (not FileName.Equals('..')) and + (not FileName.Equals('')) then + begin + SubDirs := CollectDirectories(StartDir.Append(FileName), true); + SubDirCount := Length(SubDirs); + if SubDirCount > 0 then + begin + SetLength(Result, Length(Result) + SubDirCount); + for SubDirIdx := 0 to SubDirCount - 1 do + Result[High(Result) - SubDirCount + 1 + SubDirIdx] := SubDirs[SubDirIdx]; + end; + end; + end; +end; + constructor TSongs.Create(); begin // do not start thread BEFORE initialization (suspended = true) @@ -235,24 +275,34 @@ procedure TSongs.int_LoadSongList; Log.LogStatus('Searching For Songs', 'SongList'); + fDiscoveryDirCount := 0; + fDiscoveryDirsScanned := 0; fSongsLoaded := 0; fTotalSongsToLoad := 0; - fLoadingBaseTemplate := ''; - fLoadingBaseText := ''; - UpdateLoadingProgress; + if Assigned(ScreenLoading) then + begin + ScreenLoading.SetDiscoveryProgress(0, 0, 0); + ScreenLoading.SetSongLoadingProgress(0, 0); + ScreenLoading.RefreshProgress(true); + end; SongFiles := CollectSongFiles; fTotalSongsToLoad := Length(SongFiles); - UpdateLoadingProgress; + UpdateDiscoveryProgress(fTotalSongsToLoad, true); + UpdateSongLoadingProgress(true); for I := 0 to High(SongFiles) do begin LoadSongFromFile(SongFiles[I]); Inc(fSongsLoaded); - UpdateLoadingProgress; + UpdateSongLoadingProgress; + PumpLoadingEvents; end; + if fTotalSongsToLoad > 0 then + UpdateSongLoadingProgress(true); + if assigned(CatSongs) then CatSongs.Refresh; @@ -287,68 +337,31 @@ procedure TSongs.LoadSongList; procedure TSongs.FindFilesByExtension(const Dir: IPath; const Ext: IPath; Recursive: Boolean; var Files: TPathDynArray); var DirList: TPathDynArray; - DirCount, DirIdx: Integer; + DirIndex: Integer; Iter: IFileIterator; FileInfo: TFileInfo; FileName: IPath; - function CollectDirectories(const StartDir: IPath): TPathDynArray; - var - LocalDirs: TPathDynArray; - LocalIter: IFileIterator; - LocalFileInfo: TFileInfo; - LocalFileName: IPath; - SubDirs: TPathDynArray; - SubDirCount, SubDirIdx: Integer; +begin + if Recursive then + DirList := CollectDirectories(Dir, true) + else begin - SetLength(LocalDirs, 1); - LocalDirs[0] := StartDir; - if not Recursive then - Exit(LocalDirs); - - // Only search for directories - LocalIter := FileSystem.FileFind(StartDir.Append('*'), faDirectory); - while (LocalIter.HasNext) do - begin - LocalFileInfo := LocalIter.Next; - LocalFileName := LocalFileInfo.Name; - if ((LocalFileInfo.Attr and faDirectory) <> 0) and - (not LocalFileName.Equals('.')) and (not LocalFileName.Equals('..')) and (not LocalFileName.Equals('')) then - begin - // Add subdirectory and recurse - SubDirs := CollectDirectories(StartDir.Append(LocalFileName)); - SubDirCount := Length(SubDirs); - if SubDirCount > 0 then - begin - SetLength(LocalDirs, Length(LocalDirs) + SubDirCount); - for SubDirIdx := 0 to SubDirCount - 1 do - LocalDirs[High(LocalDirs) - SubDirCount + 1 + SubDirIdx] := SubDirs[SubDirIdx]; - end; - end; - end; - Exit(LocalDirs); + SetLength(DirList, 1); + DirList[0] := Dir; end; -begin - // First, collect all directories (including Dir itself) - DirList := CollectDirectories(Dir); - DirCount := Length(DirList); - for DirIdx := 0 to DirCount - 1 do + for DirIndex := 0 to High(DirList) do begin - Iter := FileSystem.FileFind(DirList[DirIdx].Append('*.txt'), 0); - while (Iter.HasNext) do + Iter := FileSystem.FileFind(DirList[DirIndex].Append('*.txt'), 0); + while Iter.HasNext do begin FileInfo := Iter.Next; FileName := FileInfo.Name; - if ((FileInfo.Attr and faDirectory) = 0) then + if ((FileInfo.Attr and faDirectory) = 0) and Ext.Equals(FileName.GetExtension(), true) then begin - if (Ext.Equals(FileName.GetExtension(), true)) then - begin - Log.LogDebug('Found file ' + DirList[DirIdx].Append(FileName).ToWide, 'TSongs.FindFilesByExtension'); - SetLength(Files, Length(Files)+1); - Files[High(Files)] := DirList[DirIdx].Append(FileName); - fTotalSongsToLoad := fTotalSongsToLoad + 1; - UpdateLoadingProgress; - end; + Log.LogDebug('Found file ' + DirList[DirIndex].Append(FileName).ToWide, 'TSongs.FindFilesByExtension'); + SetLength(Files, Length(Files) + 1); + Files[High(Files)] := DirList[DirIndex].Append(FileName); end; end; end; @@ -356,12 +369,15 @@ procedure TSongs.FindFilesByExtension(const Dir: IPath; const Ext: IPath; Recurs function TSongs.CollectSongFiles: TPathDynArray; var - DirIndex, FileIndex, AppendIndex, AdditionalCount: integer; + DirIndex, FileIndex, AppendIndex, AdditionalCount, ScanIndex: integer; + DirList: TPathDynArray; + AllDirs: TPathDynArray; DirFiles: TPathDynArray; Extension: IPath; DirPath: IPath; begin SetLength(Result, 0); + SetLength(AllDirs, 0); if SongPaths = nil then Exit; @@ -372,20 +388,36 @@ function TSongs.CollectSongFiles: TPathDynArray; begin DirPath := SongPaths[DirIndex] as IPath; Log.LogDebug('Searching directory ' + DirPath.ToWide + ' for txt files', 'TSongs.CollectSongFiles'); + DirList := CollectDirectories(DirPath, true); + AdditionalCount := Length(DirList); + if AdditionalCount > 0 then + begin + AppendIndex := Length(AllDirs); + SetLength(AllDirs, AppendIndex + AdditionalCount); + for FileIndex := 0 to AdditionalCount - 1 do + AllDirs[AppendIndex + FileIndex] := DirList[FileIndex]; + end; + end; + + fDiscoveryDirCount := Length(AllDirs); + UpdateDiscoveryProgress(0, true); + + for ScanIndex := 0 to High(AllDirs) do + begin SetLength(DirFiles, 0); - FindFilesByExtension(DirPath, Extension, true, DirFiles); + FindFilesByExtension(AllDirs[ScanIndex], Extension, false, DirFiles); AdditionalCount := Length(DirFiles); - if AdditionalCount = 0 then - Continue; - - AppendIndex := Length(Result); - SetLength(Result, AppendIndex + AdditionalCount); - for FileIndex := 0 to AdditionalCount - 1 do begin - Result[AppendIndex + FileIndex] := DirFiles[FileIndex]; - fTotalSongsToLoad := Length(Result); - UpdateLoadingProgress; + if AdditionalCount > 0 then + begin + AppendIndex := Length(Result); + SetLength(Result, AppendIndex + AdditionalCount); + for FileIndex := 0 to AdditionalCount - 1 do + Result[AppendIndex + FileIndex] := DirFiles[FileIndex]; end; + Inc(fDiscoveryDirsScanned); + UpdateDiscoveryProgress(Length(Result)); + PumpLoadingEvents; SetLength(DirFiles, 0); end; end; @@ -405,49 +437,24 @@ procedure TSongs.LoadSongFromFile(const FilePath: IPath); end; end; -procedure TSongs.UpdateLoadingProgress; -var - Percent: integer; - ProgressText: UTF8String; - LoadingText: TText; - NowTicks: cardinal; +procedure TSongs.UpdateDiscoveryProgress(SongsFound: integer; Force: boolean = false); begin - if (ScreenLoading = nil) or (Length(ScreenLoading.Text) = 0) then - Exit; - - NowTicks := SDL_GetTicks(); - if (NowTicks - fLastProgressRedrawTicks < 100) and (fLastProgressRedrawTicks <> 0) then - Exit; - - LoadingText := ScreenLoading.Text[0]; - if LoadingText = nil then + if not Assigned(ScreenLoading) then Exit; - if fLoadingBaseTemplate = '' then - fLoadingBaseTemplate := LoadingText.Text; - - if fLoadingBaseText = '' then - fLoadingBaseText := fLoadingBaseTemplate; - - ProgressText := fLoadingBaseText + UTF8String(' [' + IntToStr(fSongsLoaded) + '/' + IntToStr(fTotalSongsToLoad) + ']'); - - LoadingText.Text := ProgressText; - - RedrawLoadingScreen; - fLastProgressRedrawTicks := NowTicks; + ScreenLoading.SetDiscoveryProgress(fDiscoveryDirsScanned, fDiscoveryDirCount, SongsFound); + if Force then + ScreenLoading.RefreshProgress(true); end; -procedure TSongs.RedrawLoadingScreen; +procedure TSongs.UpdateSongLoadingProgress(Force: boolean = false); begin - if (ScreenLoading = nil) or (Display = nil) then - Exit; - - if Display.CurrentScreen <> @ScreenLoading then + if not Assigned(ScreenLoading) then Exit; - ScreenLoading.Draw; - Display.Draw; - SwapBuffers; + ScreenLoading.SetSongLoadingProgress(fSongsLoaded, fTotalSongsToLoad); + if Force then + ScreenLoading.RefreshProgress(true); end; (* diff --git a/src/screens/UScreenLoading.pas b/src/screens/UScreenLoading.pas index e8099b9f4..7a352ee1a 100644 --- a/src/screens/UScreenLoading.pas +++ b/src/screens/UScreenLoading.pas @@ -35,13 +35,28 @@ interface uses UMenu, - sdl2, + UMenuStatic, SysUtils, UThemes, - dglOpenGL; + dglOpenGL, + sdl2; type TScreenLoading = class(TMenu) + private + DiscoveryBarBaseIndex: integer; + DiscoveryBarFillIndex: integer; + LoadingBarBaseIndex: integer; + LoadingBarFillIndex: integer; + StatusTextIndex: integer; + StatusTextBase: UTF8String; + DiscoveryBarAlpha: real; + LoadingBarAlpha: real; + LastRedrawTicks: cardinal; + function ClampProgress(Current, Total: integer): real; + function AddProgressOverlay(SourceBarIndex: integer; Alpha: real): integer; + procedure UpdateBar(BaseBarIndex, FillBarIndex: integer; Progress, BaseAlpha, FillAlpha: real); + procedure UpdateStatus(const Value: UTF8String); public Fadeout: boolean; TextDescription: integer; @@ -49,12 +64,16 @@ TScreenLoading = class(TMenu) constructor Create; override; procedure OnShow; override; function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override; + procedure SetDiscoveryProgress(Current, Total, SongsFound: integer); + procedure SetSongLoadingProgress(Current, Total: integer); + procedure RefreshProgress(Force: boolean = false); end; implementation uses UGraphic, + UDisplay, UTime; function TScreenLoading.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; @@ -68,6 +87,48 @@ constructor TScreenLoading.Create; LoadFromTheme(Theme.Loading); + if Length(Statics) > 0 then + begin + DiscoveryBarBaseIndex := 0; + DiscoveryBarAlpha := Statics[DiscoveryBarBaseIndex].Texture.Alpha; + DiscoveryBarFillIndex := AddProgressOverlay(DiscoveryBarBaseIndex, DiscoveryBarAlpha); + end + else + begin + DiscoveryBarBaseIndex := -1; + DiscoveryBarFillIndex := -1; + DiscoveryBarAlpha := 1; + end; + + if Length(Statics) > 1 then + begin + LoadingBarBaseIndex := 1; + LoadingBarAlpha := Statics[LoadingBarBaseIndex].Texture.Alpha; + LoadingBarFillIndex := AddProgressOverlay(LoadingBarBaseIndex, LoadingBarAlpha); + end + else + begin + LoadingBarBaseIndex := -1; + LoadingBarFillIndex := -1; + LoadingBarAlpha := 1; + end; + + if Length(Text) > 0 then + begin + StatusTextIndex := 0; + StatusTextBase := Text[0].Text; + end + else + begin + StatusTextIndex := -1; + StatusTextBase := ''; + end; + + LastRedrawTicks := 0; + UpdateBar(DiscoveryBarBaseIndex, DiscoveryBarFillIndex, 0, DiscoveryBarAlpha * 0.5, DiscoveryBarAlpha); + UpdateBar(LoadingBarBaseIndex, LoadingBarFillIndex, 0, LoadingBarAlpha * 0.5, LoadingBarAlpha); + UpdateStatus(''); + RefreshProgress(true); Fadeout := false; end; @@ -76,4 +137,95 @@ procedure TScreenLoading.OnShow; inherited; end; +function TScreenLoading.ClampProgress(Current, Total: integer): real; +begin + if Total <= 0 then + Result := 0 + else + Result := Current / Total; + + if Result < 0 then + Result := 0 + else if Result > 1 then + Result := 1; +end; + +function TScreenLoading.AddProgressOverlay(SourceBarIndex: integer; Alpha: real): integer; +var + StaticNum: integer; +begin + if (SourceBarIndex < 0) or (SourceBarIndex >= Length(Statics)) then + Exit(-1); + + StaticNum := Length(Statics); + SetLength(Statics, StaticNum + 1); + Statics[StaticNum] := TStatic.Create(Statics[SourceBarIndex].Texture); + Statics[StaticNum].Texture.Alpha := Alpha; + Statics[StaticNum].Texture.ScaleW := 0; + Statics[StaticNum].Visible := Statics[SourceBarIndex].Visible; + Statics[StaticNum].Reflection := Statics[SourceBarIndex].Reflection; + Statics[StaticNum].ReflectionSpacing := Statics[SourceBarIndex].ReflectionSpacing; + Result := StaticNum; +end; + +procedure TScreenLoading.UpdateBar(BaseBarIndex, FillBarIndex: integer; Progress, BaseAlpha, FillAlpha: real); +begin + if (BaseBarIndex >= 0) and (BaseBarIndex < Length(Statics)) then + begin + Statics[BaseBarIndex].Texture.ScaleW := 1; + Statics[BaseBarIndex].Texture.Alpha := BaseAlpha; + end; + + if (FillBarIndex >= 0) and (FillBarIndex < Length(Statics)) then + begin + Statics[FillBarIndex].Texture.ScaleW := Progress; + Statics[FillBarIndex].Texture.Alpha := FillAlpha; + end; +end; + +procedure TScreenLoading.UpdateStatus(const Value: UTF8String); +begin + if (StatusTextIndex >= 0) and (StatusTextIndex < Length(Text)) then + begin + if Value = '' then + Text[StatusTextIndex].Text := StatusTextBase + else + Text[StatusTextIndex].Text := StatusTextBase + ' ' + Value; + end; +end; + +procedure TScreenLoading.SetDiscoveryProgress(Current, Total, SongsFound: integer); +begin + UpdateBar(DiscoveryBarBaseIndex, DiscoveryBarFillIndex, ClampProgress(Current, Total), DiscoveryBarAlpha * 0.5, DiscoveryBarAlpha); + UpdateStatus(Format('0 / %5d', [SongsFound])); + RefreshProgress; +end; + +procedure TScreenLoading.SetSongLoadingProgress(Current, Total: integer); +begin + UpdateBar(LoadingBarBaseIndex, LoadingBarFillIndex, ClampProgress(Current, Total), LoadingBarAlpha * 0.5, LoadingBarAlpha); + if Total > 0 then + UpdateStatus(Format('%5d / %5d', [Current, Total])) + else + UpdateStatus(''); + RefreshProgress; +end; + +procedure TScreenLoading.RefreshProgress(Force: boolean); +var + NowTicks: cardinal; +begin + if (Display = nil) or (Display.CurrentScreen <> @ScreenLoading) then + Exit; + + NowTicks := SDL_GetTicks; + if not Force and (LastRedrawTicks <> 0) and (NowTicks - LastRedrawTicks < 100) then + Exit; + + Self.Draw; + Display.Draw; + SwapBuffers; + LastRedrawTicks := NowTicks; +end; + end. From 8597b018094c338717de2d32c27f441e319a079e Mon Sep 17 00:00:00 2001 From: Daniel Gruss Date: Mon, 27 Apr 2026 16:09:19 +0200 Subject: [PATCH 4/5] restored debug line for recursing into directories --- src/base/USongs.pas | 1 + 1 file changed, 1 insertion(+) diff --git a/src/base/USongs.pas b/src/base/USongs.pas index 8d8991902..30d539c7f 100644 --- a/src/base/USongs.pas +++ b/src/base/USongs.pas @@ -207,6 +207,7 @@ function CollectDirectories(const StartDir: IPath; Recursive: Boolean): TPathDyn (not FileName.Equals('..')) and (not FileName.Equals('')) then begin + Log.LogDebug('Recursing: ' + StartDir.Append(FileName).ToWide, 'TSongs.FindFilesByExtension'); SubDirs := CollectDirectories(StartDir.Append(FileName), true); SubDirCount := Length(SubDirs); if SubDirCount > 0 then From 62c6c66665dbfe22e1d586e5ed7e7f0a8dedde4e Mon Sep 17 00:00:00 2001 From: Daniel Gruss Date: Mon, 27 Apr 2026 17:27:48 +0200 Subject: [PATCH 5/5] more SDL event pumping and timestamps for the log file --- src/base/ULog.pas | 15 ++++++++++----- src/screens/UScreenSong.pas | 4 ++++ 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/base/ULog.pas b/src/base/ULog.pas index 56947478e..752e0a2ad 100644 --- a/src/base/ULog.pas +++ b/src/base/ULog.pas @@ -293,6 +293,12 @@ procedure TLog.LogBenchmark(const Text: string; Number: integer); end; procedure TLog.LogToFile(const Text: string); + + procedure WriteLogLine(const Line: string); + begin + WriteLn(LogFile, FormatDateTime('yyyy-mm-dd hh:nn:ss.zzz', Now) + ' ' + Line); + end; + begin EnterCriticalSection(Lock); if (FileOutputEnabled and not LogFileOpened) then @@ -307,9 +313,9 @@ procedure TLog.LogToFile(const Text: string); //If File is opened write Date to Error File if (LogFileOpened) then begin - WriteLn(LogFile, Title + ' Error Log'); - WriteLn(LogFile, 'Date: ' + DatetoStr(Now) + ' Time: ' + TimetoStr(Now)); - WriteLn(LogFile, '-------------------'); + WriteLogLine(Title + ' Error Log'); + WriteLogLine('Date: ' + DatetoStr(Now) + ' Time: ' + TimetoStr(Now)); + WriteLogLine('-------------------'); Flush(LogFile); end; @@ -318,7 +324,7 @@ procedure TLog.LogToFile(const Text: string); if LogFileOpened then begin try - WriteLn(LogFile, Text); + WriteLogLine(Text); Flush(LogFile); except LogFileOpened := false; @@ -582,4 +588,3 @@ procedure TLog.LogConsole(const Text: string); end. - diff --git a/src/screens/UScreenSong.pas b/src/screens/UScreenSong.pas index 6004599aa..0bfed8e3f 100644 --- a/src/screens/UScreenSong.pas +++ b/src/screens/UScreenSong.pas @@ -2025,6 +2025,7 @@ procedure TScreenSong.GenerateThumbnails(); Cover: TCover; CoverFile: IPath; Song: TSong; + Event: TSDL_Event; begin if (Length(CatSongs.Song) <= 0) then Exit; @@ -2035,6 +2036,9 @@ procedure TScreenSong.GenerateThumbnails(); // create all buttons for I := 0 to High(CatSongs.Song) do begin + while SDL_PollEvent(@Event) <> 0 do + ; + CoverButton := nil; // create a clickable cover