[ai] memory optimizations
This commit is contained in:
@@ -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 ¤tGloba
|
||||
{
|
||||
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 ¤tGloba
|
||||
{
|
||||
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 ¤tGlobal)
|
||||
{
|
||||
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 ¤tGlobal)
|
||||
// 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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user