Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions sources/Adapters/adv/gui/SerialDebugUI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "../system/advSystem.h"
#include "Adapters/adv/audio/record.h"
#include "Application/Model/Config.h"
#include "Application/Utils/MemoryPool.h"
#include "System/FileSystem/FileSystem.h"
#include "System/FileSystem/I_File.h"
#include "System/System/System.h"
Expand Down Expand Up @@ -195,11 +196,13 @@ void SerialDebugUI::catFile(const char *path) {

void SerialDebugUI::listFiles(const char *path) {
auto fs = FileSystem::GetInstance();
auto fileIndexList = MemoryPool::getFileIndexList(this);

if (!fs->chdir(path)) {
Trace::Error("failed to ls files path:%s", path);
}
etl::vector<int, MAX_FILE_INDEX_SIZE> fileIndexes;
fs->list(&fileIndexes, "", false);

fs->list(&(*fileIndexList), "", false);

// No need to actually do the printing below for now as the current debug code
// in PicoFileSystem class is already printing all the files fetched when the
Expand Down
15 changes: 11 additions & 4 deletions sources/Adapters/picoTracker/filesystem/picoTrackerFileSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
*/

#include "picoTrackerFileSystem.h"
#include "Application/Utils/MemoryPool.h"
#include "Externals/etl/include/etl/pool.h"
#include "pico/multicore.h"
#include <cstring>
Expand Down Expand Up @@ -277,21 +278,27 @@ bool picoTrackerFileSystem::CopyFile(const char *srcFilename,
auto fSrc = sd.open(srcFilename, O_READ);
auto fDest = sd.open(destFilename, O_WRITE | O_CREAT);

auto buffer = MemoryPool::getBuffer();
char *fileBuffer = buffer.data();

int n = 0;
int bufferSize = sizeof(fileBuffer_);

while (true) {
n = fSrc.read(fileBuffer_, bufferSize);
n = fSrc.read(fileBuffer, MEMORYPOOL_SCRATCH_SIZE);
// check for read error and only write if no error
if (n >= 0) {
fDest.write(fileBuffer_, n);
fDest.write(fileBuffer, n);
} else {
Trace::Error("Failed to read file: %s", srcFilename);
fSrc.close();
fDest.close();
return false;
}
if (n < bufferSize) {
if ((size_t)n < MEMORYPOOL_SCRATCH_SIZE) {
break;
}
}

fSrc.close();
fDest.close();
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,6 @@ class picoTrackerFileSystem : public FileSystem {
private:
SdFs sd;
void tolowercase(char *temp);
// buffer needs to be allocated here as too big for allocation as local
// variable on the stack
uint8_t fileBuffer_[512];
};

// Concrete implementation of PI_File for picoTracker
Expand Down
12 changes: 7 additions & 5 deletions sources/Adapters/picoTracker/gui/SerialDebugUI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include "SerialDebugUI.h"
#include "Application/Model/Config.h"
#include "Application/Utils/MemoryPool.h"
#include "System/FileSystem/FileSystem.h"
#include "System/FileSystem/I_File.h"
#include "hardware/uart.h"
Expand Down Expand Up @@ -131,16 +132,17 @@ void SerialDebugUI::catFile(const char *path) {

void SerialDebugUI::listFiles(const char *path) {
auto fs = FileSystem::GetInstance();
auto fileIndexList = MemoryPool::getFileIndexList(this);
if (!fs->chdir(path)) {
Trace::Error("failed to ls files path:%s", path);
}
etl::vector<int, MAX_FILE_INDEX_SIZE> fileIndexes;
fs->list(&fileIndexes, "", false);

fs->list(&(*fileIndexList), "", false);

char name[PFILENAME_SIZE];
for (size_t i = 0; i < fileIndexes.size(); i++) {
fs->getFileName(fileIndexes[i], name, PFILENAME_SIZE);
if (fs->getFileType(fileIndexes[i]) == PFT_FILE) {
for (size_t i = 0; i < fileIndexList->size(); i++) {
fs->getFileName((*fileIndexList)[i], name, PFILENAME_SIZE);
if (fs->getFileType((*fileIndexList)[i]) == PFT_FILE) {
printf("[file] %s\n", name);
} else {
printf("[dir] %s\n", name);
Expand Down
2 changes: 0 additions & 2 deletions sources/Application/AppWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,8 +152,6 @@ AppWindow::AppWindow(I_GUIWindowImp &imp, const char *projectName)

// Init all members

_statusLine[0] = 0;
Comment thread
n1LS marked this conversation as resolved.

_currentView = nullptr;
_closeProject = false;
_lastA = 0;
Expand Down
1 change: 0 additions & 1 deletion sources/Application/AppWindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,6 @@ class AppWindow : public GUIWindow, I_Observer, Status {
unsigned short _mask;
unsigned long _lastA;
unsigned long _lastB;
char _statusLine[80];

bool lowBatteryState_;
bool lowBatteryMessageShown_;
Expand Down
12 changes: 6 additions & 6 deletions sources/Application/Audio/AudioFileStreamer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

#include "AudioFileStreamer.h"
#include "Application/Model/Config.h"
#include "Application/Utils/MemoryPool.h"
#include "Application/Utils/fixed.h"
#include "Services/Audio/Audio.h"
#include "System/Console/Trace.h"
Expand Down Expand Up @@ -46,17 +47,16 @@ bool AudioFileStreamer::Start(const char *name, int startSample, bool looping) {
return false;
}

name_ = name;
position_ = (startSample > 0) ? float(startSample) : 0.0f;
#ifndef ADV
stopRequested_ = false;
#endif

wav_.Close();
Trace::Log("", "wave open:%s", name_.c_str());
auto res = wav_.Open(name_.c_str());
Trace::Log("", "wave open:%s", name);
auto res = wav_.Open(name);
if (!res) {
Trace::Error("Failed to open streaming of file:%s", name_.c_str());
Trace::Error("Failed to open streaming of file:%s", name);
mode_ = AFSM_STOPPED;
return false;
}
Expand Down Expand Up @@ -95,7 +95,7 @@ bool AudioFileStreamer::Start(const char *name, int startSample, bool looping) {
fpSpeed_ = fl2fp(ratio);
Trace::Debug("AudioFileStreamer: File '%s' - Sample Rate: %d Hz, Channels: "
"%d",
name_.c_str(), fileSampleRate_, channels);
name, fileSampleRate_, channels);
Trace::Debug("Size: %ld samples, Speed: %d", size, fp2i(fpSpeed_));

// Load the entire buffer for single cycle waveforms
Expand Down Expand Up @@ -203,7 +203,7 @@ bool AudioFileStreamer::Render(fixed *buffer, int samplecount) {
}
// look if we have the file loaded
if (!wav_.IsOpen()) {
Trace::Error("Failed to open streaming of file:%s", name_.c_str());
Trace::Error("Failed to open streaming of file");
mode_ = AFSM_STOPPED;
return false;
}
Expand Down
1 change: 0 additions & 1 deletion sources/Application/Audio/AudioFileStreamer.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ class AudioFileStreamer : public AudioModule {

protected:
AudioFileStreamerMode mode_;
etl::string<PFILENAME_SIZE - 1> name_;
WavFile wav_;
float position_;
Project *project_;
Expand Down
12 changes: 7 additions & 5 deletions sources/Application/Instruments/SamplePool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "Application/Model/Config.h"
#include "Application/Persistency/PersistencyService.h"
#include "Application/Utils/DrawUtils.h"
#include "Application/Utils/MemoryPool.h"
#include "Externals/SRC/common.h"
#include "Externals/etl/include/etl/string.h"
#include "Externals/etl/include/etl/string_stream.h"
Expand Down Expand Up @@ -57,10 +58,11 @@ void SamplePool::Load(const char *projectName) {
PROJECT_SAMPLES_DIR);
}
// First, find all wav files
etl::vector<int, MAX_FILE_INDEX_SIZE> fileIndexes;
fs->list(&fileIndexes, ".wav", false);
auto fileIndexList = MemoryPool::getFileIndexList(this);

fs->list(&(*fileIndexList), ".wav", false);
char name[PFILENAME_SIZE];
uint totalSamples = fileIndexes.size();
uint totalSamples = fileIndexList->size();

// store for ui updates
importCount = totalSamples;
Expand All @@ -69,8 +71,8 @@ void SamplePool::Load(const char *projectName) {
importIndex = i;
importName = name;

fs->getFileName(fileIndexes[i], name, PFILENAME_SIZE);
if (fs->getFileType(fileIndexes[i]) == PFT_FILE) {
fs->getFileName((*fileIndexList)[i], name, PFILENAME_SIZE);
if (fs->getFileType((*fileIndexList)[i]) == PFT_FILE) {
// Check if the filename exceeds the maximum allowed length
if (strlen(name) > MAX_INSTRUMENT_FILENAME_LENGTH) {
Trace::Error(
Expand Down
7 changes: 7 additions & 0 deletions sources/Application/Utils/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
add_library(application_utils
MemoryPool.cpp
HexBuffers.cpp
char.cpp
fixed.cpp
Expand All @@ -9,6 +10,12 @@ target_link_libraries(application_utils PUBLIC
etl
)

if(NOT ADV)
target_link_libraries(application_utils PUBLIC
pico_sync
)
endif()

target_include_directories(application_utils PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})

include_directories(${PROJECT_SOURCE_DIR})
30 changes: 30 additions & 0 deletions sources/Application/Utils/MemoryPool.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* SPDX-License-Identifier: BSD-3-Clause
*
* Copyright (c) 2026 xiphonics, inc.
*
* This file is part of the picoTracker firmware
*/

#include "MemoryPool.h"

#ifdef ADV
#include "Adapters/adv/mutex/advMutex.h"
using Mutex = advMutex;
#else
#include "Adapters/picoTracker/mutex/picoTrackerMutex.h"
using Mutex = picoTrackerMutex;
#endif

// globally available 1k scratch buffer for temporary use, e.g. for file
// loading, string rendering, file sorting
char MemoryPool::buffer_[MEMORYPOOL_SCRATCH_SIZE];
static Mutex s_scratchBufferMutex;
SysMutex *MemoryPool::scratchBufferMutex_ = &s_scratchBufferMutex;

// globally available list of file indexes for the current directory
etl::vector<int, MAX_FILE_INDEX_SIZE> MemoryPool::fileIndexList_;
static Mutex s_fileIndexListMutex;
SysMutex *MemoryPool::fileIndexListMutex_ = &s_fileIndexListMutex;
const void *MemoryPool::fileIndexCurrentKey_ = nullptr;
int MemoryPool::fileIndexLockDepth_ = 0;
105 changes: 105 additions & 0 deletions sources/Application/Utils/MemoryPool.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
* SPDX-License-Identifier: BSD-3-Clause
*
* Copyright (c) 2026 xiphonics, inc.
*
* This file is part of the picoTracker firmware
*/

#pragma once

#include "System/FileSystem/FileSystem.h"

#include "ScopedLock.h"

constexpr size_t MEMORYPOOL_SCRATCH_SIZE = 1024;

class MemoryPool {
public:
template <typename T> class ScopedRef {
public:
ScopedRef(T &resource, SysMutex &mutex)
: resource_(resource), lock_(mutex) {}
T *operator->() { return &resource_; }
T &operator*() { return resource_; }
ScopedRef(const ScopedRef &) = delete;
ScopedRef &operator=(const ScopedRef &) = delete;

private:
T &resource_;
ScopedLock lock_;
};

// Scoped accessor for the raw scratch buffer.
//
// CORRECT:
// auto buf = MemoryPool::getBuffer();
// char *p = buf.data(); // lock held until 'buf' goes out of scope
//
// WRONG - lock released immediately, p is unprotected:
// char *p = MemoryPool::getBuffer().data();
class ScopedBuffer {
public:
ScopedBuffer(char *buf, SysMutex &m) : buf_(buf), lock_(m) {}
char *data() { return buf_; }
ScopedBuffer(const ScopedBuffer &) = delete;
ScopedBuffer &operator=(const ScopedBuffer &) = delete;

private:
char *buf_;
ScopedLock lock_;
};

// Scoped accessor for the file index list with key-based recursive locking.
//
// Pass 'this' as the key so the same object can re-enter without deadlocking:
// auto list = MemoryPool::getFileIndexList(this);
//
// A different key (or the first caller) acquires the mutex; the same key
// simply increments a depth counter and skips re-locking. The mutex is
// released only when the depth reaches zero (outermost scope exits).
class KeyedScopedRef {
public:
using VecType = etl::vector<int, MAX_FILE_INDEX_SIZE>;

KeyedScopedRef(VecType &resource, SysMutex &mutex)
: resource_(resource), mutex_(mutex) {}

~KeyedScopedRef() {
if (--fileIndexLockDepth_ == 0) {
fileIndexCurrentKey_ = nullptr;
mutex_.Unlock();
}
}

VecType *operator->() { return &resource_; }
VecType &operator*() { return resource_; }
KeyedScopedRef(const KeyedScopedRef &) = delete;
KeyedScopedRef &operator=(const KeyedScopedRef &) = delete;

private:
VecType &resource_;
SysMutex &mutex_;
};

[[nodiscard]] static KeyedScopedRef getFileIndexList(const void *key) {
if (fileIndexCurrentKey_ != key) {
fileIndexListMutex_->Lock();
fileIndexCurrentKey_ = key;
}
++fileIndexLockDepth_;
return {fileIndexList_, *fileIndexListMutex_};
}

[[nodiscard]] static ScopedBuffer getBuffer() {
return {buffer_, *scratchBufferMutex_};
}

private:
static char buffer_[MEMORYPOOL_SCRATCH_SIZE];
static etl::vector<int, MAX_FILE_INDEX_SIZE> fileIndexList_;
static SysMutex *fileIndexListMutex_;
static SysMutex *scratchBufferMutex_;
static const void *fileIndexCurrentKey_;
static int fileIndexLockDepth_;
};
24 changes: 24 additions & 0 deletions sources/Application/Utils/ScopedLock.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* SPDX-License-Identifier: BSD-3-Clause
*
* Copyright (c) 2026 xiphonics, inc.
*
* This file is part of the picoTracker firmware
*/

#pragma once

#include "System/Process/SysMutex.h"

// minimal RAII lock guard using the SysMutex interface ************************

class ScopedLock {
public:
explicit ScopedLock(SysMutex &m) : m_(m) { m_.Lock(); }
~ScopedLock() { m_.Unlock(); }
ScopedLock(const ScopedLock &) = delete;
ScopedLock &operator=(const ScopedLock &) = delete;

private:
SysMutex &m_;
};
Loading