[ai] quickwin memory optimiziations, wip

This commit is contained in:
Stefan Ostermann 2025-11-02 20:31:05 +01:00
parent 465e34e919
commit 3a34b1b8d0
2 changed files with 52 additions and 43 deletions

View File

@ -37,19 +37,20 @@ const std::vector<String> &DirectoryNode::getMP3Files() const
return mp3Files; return mp3Files;
} }
void DirectoryNode::setCurrentPlaying(const String mp3File) void DirectoryNode::setCurrentPlaying(const String &mp3File)
{ {
currentPlaying = mp3File; currentPlaying = mp3File;
for (int i = 0; i < mp3Files.size(); i++) for (size_t i = 0; i < mp3Files.size(); i++)
{ {
if (mp3Files[i] == mp3File && ids.size() > i) if (mp3Files[i] == mp3File && ids.size() > i)
{ {
currentPlayingId = ids[i]; currentPlayingId = ids[i];
break;
} }
} }
} }
const String DirectoryNode::getCurrentPlaying() const const String &DirectoryNode::getCurrentPlaying() const
{ {
return currentPlaying; return currentPlaying;
} }
@ -82,7 +83,7 @@ void DirectoryNode::setSecondsPlayed(const uint32_t seconds)
secondsPlayed = seconds; secondsPlayed = seconds;
} }
uint32_t DirectoryNode::getSecondsPlayed() uint32_t DirectoryNode::getSecondsPlayed() const
{ {
return secondsPlayed; return secondsPlayed;
} }
@ -97,6 +98,9 @@ void DirectoryNode::buildDirectoryTree(const char *currentPath)
subdirectories.clear(); subdirectories.clear();
mp3Files.clear(); mp3Files.clear();
ids.clear(); ids.clear();
subdirectories.shrink_to_fit();
mp3Files.shrink_to_fit();
ids.shrink_to_fit();
// First collect entries so we can sort them alphabetically // First collect entries so we can sort them alphabetically
std::vector<String> dirNames; std::vector<String> dirNames;
@ -113,27 +117,33 @@ void DirectoryNode::buildDirectoryTree(const char *currentPath)
if (entry.isDirectory() && entry.name()[0] != '.' && strcmp(entry.name(), sys_dir)) if (entry.isDirectory() && entry.name()[0] != '.' && strcmp(entry.name(), sys_dir))
{ {
dirNames.push_back(String(entry.name())); dirNames.emplace_back(entry.name());
} }
else else
{ {
String entryName = entry.name(); String entryName = entry.name();
if (entryName.endsWith(".mp3") || entryName.endsWith(".MP3")) if (entryName.endsWith(".mp3") || entryName.endsWith(".MP3"))
{ {
fileNames.push_back(entryName); fileNames.push_back(std::move(entryName));
} }
} }
entry.close(); entry.close();
} }
rootDir.close(); rootDir.close();
// Case-insensitive alphabetical sort // Case-insensitive alphabetical sort without allocations
auto ciLess = [](const String &a, const String &b) { auto ciLess = [](const String &a, const String &b) {
String al = a; const char* pa = a.c_str();
String bl = b; const char* pb = b.c_str();
al.toLowerCase(); while (*pa && *pb) {
bl.toLowerCase(); char ca = *pa++;
return al.compareTo(bl) < 0; char cb = *pb++;
if (ca >= 'A' && ca <= 'Z') ca += 'a' - 'A';
if (cb >= 'A' && cb <= 'Z') cb += 'a' - 'A';
if (ca < cb) return true;
if (ca > cb) return false;
}
return *pa < *pb;
}; };
std::sort(dirNames.begin(), dirNames.end(), ciLess); std::sort(dirNames.begin(), dirNames.end(), ciLess);
std::sort(fileNames.begin(), fileNames.end(), ciLess); std::sort(fileNames.begin(), fileNames.end(), ciLess);
@ -165,7 +175,7 @@ void DirectoryNode::buildDirectoryTree(const char *currentPath)
fullPath += "/"; fullPath += "/";
fullPath += fileName; fullPath += fileName;
mp3Files.push_back(fullPath); mp3Files.push_back(std::move(fullPath));
ids.push_back(getNextId()); ids.push_back(getNextId());
} }
} }
@ -174,7 +184,7 @@ void DirectoryNode::printDirectoryTree(int level) const
{ {
for (int i = 0; i < level; i++) for (int i = 0; i < level; i++)
{ {
Serial.print(" "); Serial.print(F(" "));
} }
Serial.println(name); Serial.println(name);
@ -182,7 +192,7 @@ void DirectoryNode::printDirectoryTree(int level) const
{ {
for (int i = 0; i <= level; i++) for (int i = 0; i <= level; i++)
{ {
Serial.print(" "); Serial.print(F(" "));
} }
Serial.println(mp3File); Serial.println(mp3File);
} }
@ -256,15 +266,16 @@ DirectoryNode *DirectoryNode::advanceToMP3(const uint16_t id)
} }
// If we get here, no song with this ID was found // If we get here, no song with this ID was found
Serial.println("advanceToMP3: No song found for ID: " + String(id)); Serial.print(F("advanceToMP3: No song found for ID: "));
Serial.println(id);
return nullptr; return nullptr;
} }
DirectoryNode *DirectoryNode::advanceToMP3(const String songName) DirectoryNode *DirectoryNode::advanceToMP3(const String &songName)
{ {
if (songName.isEmpty()) if (songName.isEmpty())
{ {
Serial.println("advanceToMP3: songName is empty"); Serial.println(F("advanceToMP3: songName is empty"));
return nullptr; return nullptr;
} }
@ -279,8 +290,6 @@ DirectoryNode *DirectoryNode::advanceToMP3(const String songName)
// Lowercased copies for case-insensitive comparisons (FAT can uppercase names) // Lowercased copies for case-insensitive comparisons (FAT can uppercase names)
String lowTarget = songName; String lowTarget = songName;
lowTarget.toLowerCase(); lowTarget.toLowerCase();
String lowNormPath = normalizedPath;
lowNormPath.toLowerCase();
// First, search in the current directory's MP3 files // First, search in the current directory's MP3 files
for (size_t i = 0; i < mp3Files.size(); i++) for (size_t i = 0; i < mp3Files.size(); i++)
@ -359,7 +368,8 @@ DirectoryNode *DirectoryNode::advanceToMP3(const String songName)
} }
// If we get here, no matching song was found // If we get here, no matching song was found
Serial.println("advanceToMP3: No song found for: " + songName); Serial.print(F("advanceToMP3: No song found for: "));
Serial.println(songName);
return nullptr; return nullptr;
} }
@ -376,14 +386,14 @@ DirectoryNode *DirectoryNode::goToPreviousMP3(uint32_t thresholdSeconds)
// Safety check for null pointer // Safety check for null pointer
if (currentPlaying.isEmpty()) if (currentPlaying.isEmpty())
{ {
Serial.println("goToPreviousMP3: currentPlaying is empty"); Serial.println(F("goToPreviousMP3: currentPlaying is empty"));
return nullptr; return nullptr;
} }
// If we've been playing for more than threshold seconds, restart current song // If we've been playing for more than threshold seconds, restart current song
if (secondsPlayed > thresholdSeconds) if (secondsPlayed > thresholdSeconds)
{ {
Serial.println("goToPreviousMP3: Restarting current song (played > threshold)"); Serial.println(F("goToPreviousMP3: Restarting current song (played > threshold)"));
return this; return this;
} }
@ -401,7 +411,7 @@ DirectoryNode *DirectoryNode::goToPreviousMP3(uint32_t thresholdSeconds)
// If current song found and not the first song, move to previous // If current song found and not the first song, move to previous
if (currentIndex > 0) if (currentIndex > 0)
{ {
Serial.print("goToPreviousMP3: Moving to previous song in same directory: "); Serial.print(F("goToPreviousMP3: Moving to previous song in same directory: "));
Serial.println(mp3Files[currentIndex - 1]); Serial.println(mp3Files[currentIndex - 1]);
setCurrentPlaying(mp3Files[currentIndex - 1]); setCurrentPlaying(mp3Files[currentIndex - 1]);
return this; return this;
@ -409,15 +419,15 @@ DirectoryNode *DirectoryNode::goToPreviousMP3(uint32_t thresholdSeconds)
// If we're at the first song or song not found in current directory, // If we're at the first song or song not found in current directory,
// we need to find the previous song globally // we need to find the previous song globally
Serial.println("goToPreviousMP3: At first song or song not found, looking for previous globally"); Serial.println(F("goToPreviousMP3: At first song or song not found, looking for previous globally"));
return nullptr; // Let the caller handle global previous logic return nullptr; // Let the caller handle global previous logic
} }
DirectoryNode *DirectoryNode::findPreviousMP3Globally(const String currentGlobal) DirectoryNode *DirectoryNode::findPreviousMP3Globally(const String &currentGlobal)
{ {
if (currentGlobal.isEmpty()) if (currentGlobal.isEmpty())
{ {
Serial.println("findPreviousMP3Globally: currentGlobal is null"); Serial.println(F("findPreviousMP3Globally: currentGlobal is null"));
return nullptr; return nullptr;
} }
@ -444,23 +454,25 @@ DirectoryNode *DirectoryNode::findPreviousMP3Globally(const String currentGlobal
DirectoryNode *prevNode = allMP3s[currentGlobalIndex - 1].first; DirectoryNode *prevNode = allMP3s[currentGlobalIndex - 1].first;
int prevFileIndex = allMP3s[currentGlobalIndex - 1].second; int prevFileIndex = allMP3s[currentGlobalIndex - 1].second;
Serial.print("findPreviousMP3Globally: Moving to previous song globally: "); Serial.print(F("findPreviousMP3Globally: Moving to previous song globally: "));
Serial.println(prevNode->mp3Files[prevFileIndex]); Serial.println(prevNode->mp3Files[prevFileIndex]);
prevNode->setCurrentPlaying(prevNode->mp3Files[prevFileIndex]); prevNode->setCurrentPlaying(prevNode->mp3Files[prevFileIndex]);
return prevNode; return prevNode;
} }
Serial.println("findPreviousMP3Globally: No previous song found globally"); Serial.println(F("findPreviousMP3Globally: No previous song found globally"));
return nullptr; return nullptr;
} }
void DirectoryNode::buildFlatMP3List(std::vector<std::pair<DirectoryNode *, int>> &allMP3s) void DirectoryNode::buildFlatMP3List(std::vector<std::pair<DirectoryNode *, int>> &allMP3s)
{ {
// Pre-reserve to reduce reallocations
allMP3s.reserve(allMP3s.size() + mp3Files.size());
// Add all MP3 files from this directory // Add all MP3 files from this directory
for (size_t i = 0; i < mp3Files.size(); i++) for (size_t i = 0; i < mp3Files.size(); i++)
{ {
allMP3s.push_back(std::make_pair(this, i)); allMP3s.emplace_back(this, i);
} }
// Recursively add MP3 files from subdirectories // Recursively add MP3 files from subdirectories
@ -470,12 +482,12 @@ void DirectoryNode::buildFlatMP3List(std::vector<std::pair<DirectoryNode *, int>
} }
} }
const size_t DirectoryNode::getNumOfFiles() size_t DirectoryNode::getNumOfFiles() const
{ {
return subdirectories.size(); return subdirectories.size();
} }
DirectoryNode *DirectoryNode::advanceToNextMP3(const String currentGlobal) DirectoryNode *DirectoryNode::advanceToNextMP3(const String &currentGlobal)
{ {
bool useFirst = false; bool useFirst = false;
Serial.println(currentGlobal.c_str()); Serial.println(currentGlobal.c_str());
@ -534,7 +546,7 @@ DirectoryNode *DirectoryNode::advanceToNextMP3(const String currentGlobal)
// If we get here, there were no MP3 files or subdirectories left to check // If we get here, there were no MP3 files or subdirectories left to check
currentPlaying = ""; currentPlaying = "";
Serial.println("no more nodes found"); Serial.println(F("no more nodes found"));
return this; return this;
} }
@ -581,6 +593,3 @@ void DirectoryNode::streamDirectoryHTML(Print &out) const {
yield(); // Final yield before completing yield(); // Final yield before completing
} }
} }

View File

@ -34,14 +34,14 @@ public:
const std::vector<DirectoryNode*>& getSubdirectories() const; const std::vector<DirectoryNode*>& getSubdirectories() const;
const std::vector<String>& getMP3Files() const; const std::vector<String>& getMP3Files() const;
const size_t getNumOfFiles(); size_t getNumOfFiles() const;
void setCurrentPlaying(const String mp3File); void setCurrentPlaying(const String& mp3File);
const String getCurrentPlaying() const; const String& getCurrentPlaying() const;
const uint16_t getCurrentPlayingId() const; const uint16_t getCurrentPlayingId() const;
void setSecondsPlayed(const uint32_t seconds); void setSecondsPlayed(const uint32_t seconds);
uint32_t getSecondsPlayed(); uint32_t getSecondsPlayed() const;
uint16_t getNextId(); uint16_t getNextId();
@ -49,10 +49,10 @@ public:
void addMP3File(const String& mp3File); void addMP3File(const String& mp3File);
void buildDirectoryTree(const char* currentPath); void buildDirectoryTree(const char* currentPath);
void printDirectoryTree(int level = 0) const; void printDirectoryTree(int level = 0) const;
DirectoryNode* advanceToMP3(const String songName); DirectoryNode* advanceToMP3(const String& songName);
DirectoryNode* advanceToNextMP3(const String currentGlobal); DirectoryNode* advanceToNextMP3(const String& currentGlobal);
DirectoryNode* goToPreviousMP3(uint32_t thresholdSeconds = 3); DirectoryNode* goToPreviousMP3(uint32_t thresholdSeconds = 3);
DirectoryNode* findPreviousMP3Globally(const String currentGlobal); DirectoryNode* findPreviousMP3Globally(const String& currentGlobal);
void buildFlatMP3List(std::vector<std::pair<DirectoryNode*, int>>& allMP3s); void buildFlatMP3List(std::vector<std::pair<DirectoryNode*, int>>& allMP3s);
DirectoryNode* advanceToMP3(const uint16_t id); DirectoryNode* advanceToMP3(const uint16_t id);
void advanceToFirstMP3InThisNode(); void advanceToFirstMP3InThisNode();