Skip to content

Commit c1902d9

Browse files
committed
feat(memory): implement auto-eviction, zoom limits and fix track loading bug
- Added AnalyzePath to TrackState for on-demand waveform reloading - Implemented RB_ReloadWaveform in rekordbox_reader - Added auto-eviction of idle waveform buffers to save RAM - Restricted max zoom-out on low-memory devices - Fixed track selection bug for MIDI/Keyboard direct loads
1 parent faef831 commit c1902d9

7 files changed

Lines changed: 92 additions & 2 deletions

File tree

src/library/rekordbox_reader.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -539,3 +539,48 @@ extern "C" void RB_LoadTrackData(RBTrack* track, const char* rootPath) {
539539
RB_ParseAnlz(extPath, track);
540540
}
541541
}
542+
543+
extern "C" void RB_ReloadWaveform(const char* path, unsigned char** outData, int* outLen, int* outType) {
544+
if (!path || !outData || !outLen || !outType) return;
545+
546+
std::ifstream is(path, std::ios::binary);
547+
if (!is.is_open()) return;
548+
549+
try {
550+
kaitai::kstream ks(&is);
551+
rekordbox_anlz_t anlz(&ks);
552+
553+
for (auto& section : *anlz.sections()) {
554+
auto tag = section->fourcc();
555+
if (tag == rekordbox_anlz_t::SECTION_TAGS_WAVE_SCROLL || tag == rekordbox_anlz_t::SECTION_TAGS_WAVE_COLOR_SCROLL || tag == rekordbox_anlz_t::SECTION_TAGS_WAVE_3BAND_SCROLL) {
556+
std::string data;
557+
int type = 0;
558+
if (tag == rekordbox_anlz_t::SECTION_TAGS_WAVE_SCROLL) {
559+
auto ws = static_cast<rekordbox_anlz_t::wave_scroll_tag_t*>(section->body());
560+
data = ws->entries();
561+
type = 1;
562+
} else if (tag == rekordbox_anlz_t::SECTION_TAGS_WAVE_COLOR_SCROLL) {
563+
auto wc = static_cast<rekordbox_anlz_t::wave_color_scroll_tag_t*>(section->body());
564+
data = wc->entries();
565+
type = 2;
566+
} else {
567+
auto w3 = static_cast<rekordbox_anlz_t::wave_3band_scroll_tag_t*>(section->body());
568+
data = w3->entries();
569+
type = 3;
570+
}
571+
572+
*outLen = data.length();
573+
*outType = type;
574+
if (*outLen > 0) {
575+
*outData = new (std::nothrow) unsigned char[*outLen];
576+
if (*outData) {
577+
memcpy(*outData, data.data(), *outLen);
578+
} else {
579+
*outLen = 0;
580+
}
581+
}
582+
return; // Found it
583+
}
584+
}
585+
} catch (...) {}
586+
}

src/library/rekordbox_reader.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,9 @@ void RB_FreeDatabase(RBDatabase* db);
105105
// Loads analysis data for a specific track
106106
void RB_LoadTrackData(RBTrack* track, const char* rootPath);
107107

108+
// Low-level reload for memory management (re-parses waveform sections into existing pointers)
109+
void RB_ReloadWaveform(const char* path, unsigned char** outData, int* outLen, int* outType);
110+
108111
#ifdef __cplusplus
109112
}
110113
#endif

src/ui/browser/browser.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,7 @@ static int Browser_Update(Component *base) {
492492
return 0;
493493

494494
int loadToDeck = -1;
495+
int targetIdx = s->ScrollOffset + s->CursorPos;
495496
bool triggerEnter = false;
496497

497498
// Dropdown and Search Box Interaction
@@ -1034,7 +1035,7 @@ static int Browser_Update(Component *base) {
10341035
return 0; // Prevent load
10351036
}
10361037

1037-
int idx = s->PopupTrackIdx;
1038+
int idx = targetIdx;
10381039
if (s->DatabaseType == 0) { // Rekordbox
10391040
if (idx < s->ActiveTrackCount && s->TrackPointers[idx]) {
10401041
RBTrack *t = s->TrackPointers[idx];
@@ -1112,6 +1113,11 @@ static int Browser_Update(Component *base) {
11121113
}
11131114

11141115
memset(newTrack, 0, sizeof(TrackState));
1116+
1117+
if (s->SelectedStorage) {
1118+
snprintf(newTrack->AnalyzePath, sizeof(newTrack->AnalyzePath), "%s/%s", s->SelectedStorage->Path, t->AnalyzePath);
1119+
}
1120+
11151121
newTrack->StaticWaveformLen = t->StaticWaveformLen;
11161122
newTrack->StaticWaveformType = t->StaticWaveformType;
11171123
memcpy(newTrack->StaticWaveform, t->StaticWaveform,

src/ui/player/player_state.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ typedef struct TrackState {
5555
int KindID;
5656
} Phrases[64];
5757
int PhraseCount;
58+
char AnalyzePath[512]; // Path to .ANLZ/EXT file for reloading
5859
} TrackState;
5960

6061
typedef struct DeckState {

src/ui/player/waveform.c

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "ui/player/waveform.h"
22
#include "core/memory_guard.h"
3+
#include "core/logger.h"
34
#include "audio/engine.h"
45
#include "core/logic/quantize.h"
56
#include "rlgl.h"
@@ -48,6 +49,32 @@ static int Waveform_Update(Component *base) {
4849
bool inWaveform = (mouse.x >= wfLeft && mouse.x <= wfRight &&
4950
mouse.y >= wfY && mouse.y <= wfY + waveH);
5051

52+
// AUTO RELOAD: If focused and buffer was evicted, reload it
53+
bool isInteracting = (inWaveform || r->State->IsPlaying || r->State->IsTouching);
54+
if (isInteracting) r->lastInteractionTime = GetTime();
55+
56+
if (r->cachedTrack && r->cachedTrack->DynamicWaveform == NULL && r->cachedTrack->DynamicWaveformLen > 0) {
57+
// Reload if we are interacting OR we are master
58+
if (isInteracting || r->State->IsMaster) {
59+
UNX_LOG_INFO("[WAVE] Reloading evicted buffer for Deck %c", r->ID == 0 ? 'A' : 'B');
60+
RB_ReloadWaveform(r->cachedTrack->AnalyzePath,
61+
&r->cachedTrack->DynamicWaveform,
62+
&r->cachedTrack->DynamicWaveformLen,
63+
&r->cachedTrack->WaveformType);
64+
}
65+
}
66+
67+
// AUTO EVICT: If idle and memory is low, clear buffer
68+
if (r->cachedTrack && r->cachedTrack->DynamicWaveform != NULL) {
69+
if (!isInteracting && !r->State->IsPlaying && !r->State->IsMaster) {
70+
if (MemoryGuard_GetLevel() >= MEM_MODE_LITE && (GetTime() - r->lastInteractionTime > 5.0)) {
71+
UNX_LOG_INFO("[WAVE] Evicting idle buffer for Deck %c to save RAM", r->ID == 0 ? 'A' : 'B');
72+
free(r->cachedTrack->DynamicWaveform);
73+
r->cachedTrack->DynamicWaveform = NULL;
74+
}
75+
}
76+
}
77+
5178
// Zoom & Jog Interaction Logic
5279
int gesture = GetGestureDetected();
5380
if (inWaveform) {
@@ -86,7 +113,13 @@ static int Waveform_Update(Component *base) {
86113

87114
// Clamp the index
88115
if (currentIndex < 0) currentIndex = 0;
89-
if (currentIndex >= NUM_ZOOM_LEVELS) currentIndex = NUM_ZOOM_LEVELS - 1;
116+
117+
int maxZoomIndex = NUM_ZOOM_LEVELS - 1;
118+
// ECO MODE: Limit maximum zoom out to prevent heavy rendering
119+
if (MemoryGuard_GetLevel() >= MEM_MODE_ECO) {
120+
maxZoomIndex = (NUM_ZOOM_LEVELS / 2) + 1; // Limit to mid-zoom
121+
}
122+
if (currentIndex > maxZoomIndex) currentIndex = maxZoomIndex;
90123

91124
r->State->ZoomScale = ZOOM_LEVELS[currentIndex];
92125

@@ -815,4 +848,5 @@ void WaveformRenderer_Init(WaveformRenderer *r, int id, DeckState *state,
815848
r->cachedTrack = NULL;
816849
r->dynWfmFrames = 480;
817850
r->lastMouseX = 0;
851+
r->lastInteractionTime = GetTime();
818852
}

src/ui/player/waveform.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ struct WaveformRenderer {
1515
int dynWfmFrames;
1616
float dataDensity;
1717
float lastMouseX;
18+
double lastInteractionTime;
1819
};
1920

2021
static const float ZOOM_LEVELS[] = {

tools/bin2c.exe

0 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)