[ai] memory optimizations

This commit is contained in:
2025-11-03 22:49:35 +01:00
parent b97eb79b91
commit c14624ef92
5 changed files with 186 additions and 119 deletions

View File

@@ -1,6 +1,10 @@
#include "DirectoryNode.h"
#include "globals.h"
#include <algorithm>
#include <cstring> // strlen, strlcpy, strlcat
#include <strings.h> // strcasecmp
char DirectoryNode::buffer[DirectoryNode::buffer_size];
DirectoryNode::DirectoryNode(const String &nodeName)
: name(nodeName), currentPlaying("")
@@ -42,14 +46,7 @@ const String &DirectoryNode::getDirPath() const
return dirPath;
}
String DirectoryNode::getFullPathByIndex(size_t index) const
{
if (index < mp3Files.size())
{
return buildFullPath(mp3Files[index]);
}
return String();
}
String DirectoryNode::buildFullPath(const String &fileName) const
{
@@ -65,11 +62,49 @@ String DirectoryNode::buildFullPath(const String &fileName) const
return p;
}
bool DirectoryNode::comparePathWithString(const char* path, const String& target) const
{
// Convert target to char* for comparison
const char* targetStr = target.c_str();
// Case-insensitive string comparison
return strcasecmp(path, targetStr) == 0;
}
void DirectoryNode::buildFullPath(const String &fileName, char* out, size_t n) const
{
if (n == 0) return;
out[0] = '\0';
if (dirPath == "/")
{
strlcat(out, "/", n);
}
else
{
strlcpy(out, dirPath.c_str(), n);
strlcat(out, "/", n);
}
strlcat(out, fileName.c_str(), n);
}
void DirectoryNode::setCurrentPlaying(const String &mp3File)
{
bool isAbs = (mp3File.length() > 0) && (mp3File.charAt(0) == '/');
const String &fileName = isAbs ? mp3File.substring(mp3File.lastIndexOf('/') + 1) : mp3File;
currentPlaying = isAbs ? mp3File : buildFullPath(fileName);
if (isAbs)
{
currentPlaying = mp3File; // Already absolute path
}
else
{
// Use buffer for building relative path
buildFullPath(fileName, buffer, buffer_size);
currentPlaying = String(buffer); // Convert back to String for assignment
}
for (size_t i = 0; i < mp3Files.size(); i++)
{
if (mp3Files[i] == fileName && ids.size() > i)
@@ -128,9 +163,6 @@ void DirectoryNode::buildDirectoryTree(const char *currentPath)
subdirectories.clear();
mp3Files.clear();
ids.clear();
subdirectories.shrink_to_fit();
mp3Files.shrink_to_fit();
ids.shrink_to_fit();
// Set directory path for this node (normalize: keep "/" or remove trailing slash)
String path = String(currentPath);
@@ -145,6 +177,12 @@ void DirectoryNode::buildDirectoryTree(const char *currentPath)
std::vector<String> fileNames;
File rootDir = SD.open(currentPath);
if (!rootDir)
{
Serial.print(F("buildDirectoryTree: failed to open path: "));
Serial.println(currentPath);
return;
}
while (true)
{
File entry = rootDir.openNextFile();
@@ -153,7 +191,7 @@ void DirectoryNode::buildDirectoryTree(const char *currentPath)
break;
}
if (entry.isDirectory() && entry.name()[0] != '.' && strcmp(entry.name(), sys_dir))
if (entry.isDirectory() && entry.name()[0] != '.' && strcmp(entry.name(), sys_dir) != 0)
{
dirNames.emplace_back(entry.name());
}
@@ -195,12 +233,26 @@ void DirectoryNode::buildDirectoryTree(const char *currentPath)
for (const String &dirName : dirNames)
{
DirectoryNode *newNode = new DirectoryNode(dirName);
if (!newNode)
{
Serial.println(F("buildDirectoryTree: OOM creating DirectoryNode"));
continue;
}
subdirectories.push_back(newNode);
String childPath = String(currentPath);
if (!childPath.endsWith("/"))
String childPath;
childPath.reserve(dirPath.length() + 1 + dirName.length());
if (dirPath == "/")
{
childPath = "/";
childPath += dirName;
}
else
{
childPath = dirPath;
childPath += "/";
childPath += dirName;
childPath += dirName;
}
newNode->buildDirectoryTree(childPath.c_str());
}
@@ -220,16 +272,19 @@ void DirectoryNode::printDirectoryTree(int level) const
Serial.print(F(" "));
}
Serial.println(name);
for (const String &mp3File : mp3Files)
{
for (int i = 0; i <= level; i++)
{
Serial.print(F(" "));
}
Serial.println(buildFullPath(mp3File));
// Use buffer for building path
buildFullPath(mp3File, buffer, buffer_size);
Serial.println(buffer);
}
for (DirectoryNode *childNode : subdirectories)
{
childNode->printDirectoryTree(level + 1);
@@ -274,7 +329,8 @@ DirectoryNode *DirectoryNode::advanceToMP3(const uint16_t id)
if (id == ids[i])
{
// Found the current MP3 file
currentPlaying = buildFullPath(mp3Files[i]);
buildFullPath(mp3Files[i], buffer, buffer_size);
currentPlaying = String(buffer); // Convert back to String for assignment
currentPlayingId = id;
return this;
}
@@ -329,7 +385,9 @@ DirectoryNode *DirectoryNode::advanceToMP3(const String &songName)
{
if (isAbsolutePath)
{
if (buildFullPath(mp3Files[i]).equalsIgnoreCase(songName))
// Use static buffer for path building and comparison
buildFullPath(mp3Files[i], buffer, buffer_size);
if (comparePathWithString(buffer, songName))
{
setCurrentPlaying(mp3Files[i]);
return this;
@@ -337,7 +395,9 @@ DirectoryNode *DirectoryNode::advanceToMP3(const String &songName)
}
else
{
String f = mp3Files[i];
// Use static buffer for case conversion and comparison
buildFullPath(mp3Files[i], buffer, buffer_size);
String f = String(buffer);
f.toLowerCase();
if (f.endsWith(lowTarget))
{
@@ -431,7 +491,8 @@ DirectoryNode *DirectoryNode::goToPreviousMP3(uint32_t thresholdSeconds)
int currentIndex = -1;
for (size_t i = 0; i < mp3Files.size(); i++)
{
if (currentPlaying == buildFullPath(mp3Files[i]))
buildFullPath(mp3Files[i], buffer, buffer_size);
if (comparePathWithString(buffer, currentPlaying))
{
currentIndex = i;
break;
@@ -442,7 +503,7 @@ DirectoryNode *DirectoryNode::goToPreviousMP3(uint32_t thresholdSeconds)
if (currentIndex > 0)
{
Serial.print(F("goToPreviousMP3: Moving to previous song in same directory: "));
Serial.println(buildFullPath(mp3Files[currentIndex - 1]));
Serial.println(mp3Files[currentIndex - 1]);
setCurrentPlaying(mp3Files[currentIndex - 1]);
return this;
}
@@ -471,7 +532,9 @@ DirectoryNode *DirectoryNode::findPreviousMP3Globally(const String &currentGloba
{
DirectoryNode *node = allMP3s[i].first;
int fileIndex = allMP3s[i].second;
if (node->buildFullPath(node->mp3Files[fileIndex]) == currentGlobal)
node->buildFullPath(node->mp3Files[fileIndex], buffer, buffer_size);
if (comparePathWithString(buffer, currentGlobal))
{
currentGlobalIndex = i;
break;
@@ -483,10 +546,12 @@ DirectoryNode *DirectoryNode::findPreviousMP3Globally(const String &currentGloba
{
DirectoryNode *prevNode = allMP3s[currentGlobalIndex - 1].first;
int prevFileIndex = allMP3s[currentGlobalIndex - 1].second;
prevNode->buildFullPath(prevNode->mp3Files[prevFileIndex], buffer, buffer_size);
Serial.print(F("findPreviousMP3Globally: Moving to previous song globally: "));
Serial.println(prevNode->buildFullPath(prevNode->mp3Files[prevFileIndex]));
Serial.println(buffer);
prevNode->setCurrentPlaying(prevNode->mp3Files[prevFileIndex]);
return prevNode;
}
@@ -525,7 +590,8 @@ DirectoryNode *DirectoryNode::advanceToNextMP3(const String &currentGlobal)
{
for (size_t i = 0; i < mp3Files.size(); i++)
{
if (currentGlobal == buildFullPath(mp3Files[i]))
buildFullPath(mp3Files[i], buffer, buffer_size);
if (currentGlobal == String(buffer))
{
// Found the current playing MP3 file
if (i < mp3Files.size() - 1)
@@ -555,7 +621,8 @@ DirectoryNode *DirectoryNode::advanceToNextMP3(const String &currentGlobal)
// Have each subdirectory advance its song
for (size_t i = 0; i < subdir->mp3Files.size(); i++)
{
if (currentGlobal == subdir->buildFullPath(subdir->mp3Files[i]))
subdir->buildFullPath(subdir->mp3Files[i], buffer, buffer_size);
if (currentGlobal == String(buffer))
{
// Found the current playing MP3 file
if (i < subdir->mp3Files.size() - 1)
@@ -603,10 +670,11 @@ void DirectoryNode::streamDirectoryHTML(Print &out) const {
out.print(F("<li data-id=\""));
out.print(ids[i]);
out.print(F("\">"));
out.print(buildFullPath(mp3Files[i]));
buildFullPath(mp3Files[i], buffer, buffer_size);
out.print(buffer);
out.println(F("</li>"));
#ifdef DEBUG
Serial.printf("stream song: %s\n", buildFullPath(mp3Files[i]).c_str());
Serial.printf("stream song: %s\n", buffer);
#endif
// Yield every few items to allow the async web server to send buffered data
if (i % 5 == 4) {
@@ -614,6 +682,8 @@ void DirectoryNode::streamDirectoryHTML(Print &out) const {
}
}
out.flush();
for (DirectoryNode* child : subdirectories) {
child->streamDirectoryHTML(out);
}