Skip to content

Commit

Permalink
Tidy by removing reference semantics for DataFile and using maps to s…
Browse files Browse the repository at this point in the history
…tore

Using map allows lookup by file and keeps them sorted
As DataFile is POD and small, using value semantics is simpler
  • Loading branch information
rsjbailey committed Feb 21, 2024
1 parent ece9fe4 commit 66b5bc0
Show file tree
Hide file tree
Showing 4 changed files with 146 additions and 119 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,6 @@
#include <bear/api.hpp>

namespace {
bool operator==(const ear::plugin::DataFileManager::DataFile& lhs,
const ear::plugin::DataFileManager::DataFile& rhs) {
return (lhs.filename == rhs.filename) && (lhs.fullPath == rhs.fullPath) &&
(lhs.isBearRelease == rhs.isBearRelease) && (lhs.label == rhs.label) &&
(lhs.description == rhs.description);
}

juce::File getBearDataFileDirectory() {
auto vstPath = juce::File::getSpecialLocation(
juce::File::SpecialLocationType::currentExecutableFile);
Expand Down Expand Up @@ -37,114 +30,134 @@ juce::File getCustomDataFileDirectory() {

namespace ear {
namespace plugin {

DataFileManager::DataFileManager() {
bearReleaseFiles_.add(
getBearDataFileDirectory().getChildFile(BEAR_DATA_FILE));
updateAvailableFiles();
}

namespace {
void setFromMetadata(DataFileManager::DataFile& dataFile) {
try {
auto md = bear::DataFileMetadata::read_from_file(
dataFile.fullPath.getFullPathName().toStdString());
if (md.has_metadata()) {
dataFile.label = md.get_label();
dataFile.description = md.get_description();
dataFile.isBearRelease = md.is_released();
}
}
catch(std::exception const&) {
// What are we (not) handling here?
}
}

void updateFile(juce::File const& file, DataFileManager::FileMap& file_map,
bool released) {
auto [it, success] = file_map.insert(
{file, DataFileManager::DataFile{.filename = file.getFileName(),
.fullPath = file,
.label = "",
.description = "",
.isBearRelease = released}});
if (success) { // not already added
setFromMetadata(it->second);
}
}
} // namespace

bool DataFileManager::onlyContainsDefault() const {
return customDataFiles.empty() && releasedDataFiles.size() == 1u;
}

void DataFileManager::updateAvailableFiles() {
// Start a new vec and copy existing shared_ptrs of unchanged files
// This retains shared_ptrs unlike clear() and rebuild
std::vector<std::shared_ptr<DataFileManager::DataFile>> dfs;
// Lookup tfs where we expect to find custom files
auto files = getCustomDataFileDirectory().findChildFiles(
juce::File::TypesOfFileToFind::findFiles,
false, "*.tf");
// add our expected released files
for(auto const& bearReleaseFile : bearReleaseFiles_) {
if (bearReleaseFile.existsAsFile()) {
// note that in win, the released bear dir is the same as the custom
// dir, so we might have already found this with findChildFiles
files.addIfNotAlreadyThere(bearReleaseFile);
for (auto const& file : bearReleaseFiles_) {
if (file.existsAsFile()) {
updateFile(file, releasedDataFiles, true);
}
}
for (const auto& file : files) {
auto newDf = std::make_shared<DataFile>();
newDf->fullPath = file;
newDf->filename = file.getFileName();
try {
auto md = bear::DataFileMetadata::read_from_file(
file.getFullPathName().toStdString());
if (md.has_metadata()) {
newDf->label = md.get_label();
newDf->description = md.get_description();
newDf->isBearRelease = md.is_released();
}
} catch (std::exception) {
}
// Use the existing struct where possible (exists and is identical)
// -- this retains the same inst and shared_ptr
auto existingDf = getDataFileInfo(file.getFileName());
if (existingDf && *existingDf == *newDf) {
dfs.push_back(existingDf);
} else {
dfs.push_back(newDf);

auto custom_files = getCustomDataFileDirectory().findChildFiles(
juce::File::TypesOfFileToFind::findFiles, false, "*.tf");
for (const auto& file : custom_files) {
if (!releasedDataFiles.contains(file)) { // on windows the custom / release
// dirs are the same
updateFile(file, customDataFiles, false);
}
}
availableDataFiles_ = dfs;
}

std::shared_ptr<DataFileManager::DataFile>
DataFileManager::getSelectedDataFileInfo() {
std::optional<DataFileManager::DataFile>
DataFileManager::getSelectedDataFileInfo() const {
return selectedDataFile_;
}

std::vector<std::shared_ptr<DataFileManager::DataFile>>
DataFileManager::getAvailableDataFiles() {
return availableDataFiles_;
std::vector<DataFileManager::DataFile>
DataFileManager::getAvailableDataFiles() const {
std::vector<DataFile> files;
files.reserve(getAvailableDataFilesCount());
auto fileExtractor = [](auto const& file) { return file.second; };
std::transform(releasedDataFiles.begin(), releasedDataFiles.end(), std::back_inserter(files), fileExtractor);
std::transform(customDataFiles.begin(), customDataFiles.end(), std::back_inserter(files), fileExtractor);
return files;
}

int DataFileManager::getAvailableDataFilesCount() {
return availableDataFiles_.size();
std::size_t DataFileManager::getAvailableDataFilesCount() const {
return releasedDataFiles.size() + customDataFiles.size();
}

void DataFileManager::setSelectedDataFile(DataFile const& file) {
if(!selectedDataFile_ || file != *selectedDataFile_) {
selectedDataFile_ = file;
selectedDataFileChangeCallback_(file);
}
}

bool DataFileManager::setSelectedDataFile(const juce::String& fullPath) {
return setSelectedDataFile(juce::File(fullPath));
}

bool DataFileManager::setSelectedDataFile(const juce::File& fullPath) {
auto found = getDataFileInfo(fullPath);
if (found == nullptr) return false;
if (found != selectedDataFile_) {
selectedDataFile_ = found;
if (selectedDataFileChangeCallback_) {
selectedDataFileChangeCallback_(selectedDataFile_);
}
auto const found = getDataFileInfo(fullPath);
if (found) {
setSelectedDataFile(*found);
}
return true;
return static_cast<bool>(found);
}

bool DataFileManager::setSelectedDataFileDefault() {
updateAvailableFiles();
// look for any released by order
for (auto const& bearReleaseFile : bearReleaseFiles_) {
if (setSelectedDataFile(bearReleaseFile)) {
return true;
}
auto found = false;
if(!releasedDataFiles.empty()) {
setSelectedDataFile(releasedDataFiles.begin()->second);
found = true;
}
return false;
return found;
}

std::shared_ptr<DataFileManager::DataFile> DataFileManager::getDataFileInfo(
const juce::String& fullPath) {
return getDataFileInfo(juce::File(fullPath));
namespace {
std::optional<DataFileManager::DataFile> getDataFile(DataFileManager::FileMap const& map, juce::File const& file) {
std::optional<DataFileManager::DataFile> dataFile;
if(auto it = map.find(file); it != map.end()) {
dataFile = it->second;
}
return dataFile;
}
}

std::shared_ptr<DataFileManager::DataFile> DataFileManager::getDataFileInfo(
const juce::File& fullPath) {
auto it = std::find_if(availableDataFiles_.begin(), availableDataFiles_.end(),
[&fullPath](const std::shared_ptr<DataFile>& elm) {
return elm->fullPath == fullPath;
});

return it == availableDataFiles_.end() ? nullptr : *it;
std::optional<DataFileManager::DataFile> DataFileManager::getDataFileInfo(
const juce::File& fullPath) const {
auto file = getDataFile(releasedDataFiles, fullPath);
if(!file) {
file = getDataFile(customDataFiles, fullPath);
}
return file;
}

void DataFileManager::onSelectedDataFileChange(
std::function<void(std::shared_ptr<DataFile>)> callback) {
selectedDataFileChangeCallback_ = callback;
std::function<void(DataFile const&)> callback) {
selectedDataFileChangeCallback_ = std::move(callback);
}

} // namespace plugin
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#pragma once
#include "JuceHeader.h"
#include <memory>
#include <vector>
#include <functional>
#include <boost/container/flat_map.hpp>

namespace ear {
namespace plugin {
Expand All @@ -17,25 +17,38 @@ class DataFileManager {
juce::String label;
juce::String description;
bool isBearRelease{false};
friend bool operator<(DataFile const& lhs, DataFile const& rhs){
return lhs.fullPath.getFileName().compareIgnoreCase(rhs.fullPath.getFileName()) < 0;
}
friend bool operator==(DataFile const& lhs, DataFile const& rhs) {
return lhs.fullPath.getFileName().compareIgnoreCase(rhs.fullPath.getFileName()) == 0;
}
friend bool operator!=(DataFile const& lhs, DataFile const& rhs) {
return !(lhs == rhs);
}
};

bool onlyContainsDefault() const;
void updateAvailableFiles();
std::shared_ptr<DataFile> getSelectedDataFileInfo();
std::vector<std::shared_ptr<DataFile>> getAvailableDataFiles();
int getAvailableDataFilesCount();
std::optional<DataFile> getSelectedDataFileInfo() const;
std::vector<DataFile> getAvailableDataFiles() const;
bool setSelectedDataFile(const juce::String& fullPath);
bool setSelectedDataFile(const juce::File& fullPath);
bool setSelectedDataFileDefault();
std::shared_ptr<DataFile> getDataFileInfo(const juce::String& fullPath);
std::shared_ptr<DataFile> getDataFileInfo(const juce::File& fullPath);
void onSelectedDataFileChange(
std::function<void(std::shared_ptr<DataFile>)> callback);
std::function<void(DataFile const&)> callback);
using FileMap = boost::container::flat_map<juce::File, DataFile>;

private:
void setSelectedDataFile(DataFile const& file);
std::size_t getAvailableDataFilesCount() const;
bool setSelectedDataFile(const juce::File& fullPath);
std::optional<DataFile> getDataFileInfo(const juce::File& fullPath) const;
juce::Array<juce::File> bearReleaseFiles_;
std::shared_ptr<DataFile> selectedDataFile_;
std::vector<std::shared_ptr<DataFile>> availableDataFiles_;
std::function<void(std::shared_ptr<DataFile>)> selectedDataFileChangeCallback_;
std::optional<DataFile> selectedDataFile_;
FileMap releasedDataFiles;
FileMap customDataFiles;

std::function<void(DataFile const&)> selectedDataFileChangeCallback_;
};

} // namespace plugin
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,51 +188,52 @@ void BinauralMonitoringJuceFrontendConnector::setDataFileComponent(
std::shared_ptr<Component> comp) {
dataFileComponent_ = comp;
p_->dataFileManager.updateAvailableFiles();
auto dfs = p_->dataFileManager.getAvailableDataFiles();
auto df = p_->dataFileManager.getSelectedDataFileInfo();
if (dfs.size() == 1 && df && dfs[0] == df && df->isBearRelease) {
// Only 1 data file and it is a bear release. Don't show options.
if (p_->dataFileManager.onlyContainsDefault()) {
comp->setVisible(false);
} else {
comp->setVisible(true);
}
}

void BinauralMonitoringJuceFrontendConnector::setDataFileComboBox(
std::shared_ptr<EarComboBox> comboBox) {
comboBox->addListener(this);
dataFileComboBox_ = comboBox;
p_->dataFileManager.updateAvailableFiles();
comboBox->clearEntries();
auto dfs = p_->dataFileManager.getAvailableDataFiles();
std::sort(
dfs.begin(), dfs.end(),
[](const std::shared_ptr<ear::plugin::DataFileManager::DataFile>& a,
const std::shared_ptr<ear::plugin::DataFileManager::DataFile>& b) {
if (a->isBearRelease != b->isBearRelease) {
return a->isBearRelease;
}
return a->filename.compareIgnoreCase(b->filename) < 0;
});
for (const auto& df : dfs) {
namespace {
juce::String getDataFileLabelText(DataFileManager::DataFile const& file) {
juce::String txt;
if (!df->isBearRelease) {
if (!file.isBearRelease) {
txt = "[CUSTOM] ";
}
if (df->label.isEmpty()) {
if (file.label.isEmpty()) {
// use filename - will be an unusual case in future
txt += df->filename;
txt += file.filename;
} else {
txt += df->label;
txt += " (" + df->filename + ")";
txt += file.label;
txt += " (" + file.filename + ")";
}
return txt;
}

juce::StringArray getDataFileLabelSubText(DataFileManager::DataFile const& file) {
juce::StringArray subtexts;
if (!df->description.isEmpty()) {
subtexts.add(df->description);
if (!file.description.isEmpty()) {
subtexts.add(file.description);
}
subtexts.add(juce::String(df->fullPath.getFullPathName()));
auto entry = comboBox->addTextWithSubtextEntry(txt, subtexts, df->fullPath.getFullPathName());
entry->setLightFont(!df->isBearRelease);
subtexts.add(juce::String(file.fullPath.getFullPathName()));
return subtexts;
}
}

void BinauralMonitoringJuceFrontendConnector::setDataFileComboBox(
std::shared_ptr<EarComboBox> comboBox) {
comboBox->addListener(this);
dataFileComboBox_ = comboBox;
p_->dataFileManager.updateAvailableFiles();
comboBox->clearEntries();
auto dfs = p_->dataFileManager.getAvailableDataFiles();
for (const auto& df : dfs) {
auto entry = comboBox->addTextWithSubtextEntry(
getDataFileLabelText(df),
getDataFileLabelSubText(df),
df.fullPath.getFullPathName());
entry->setLightFont(!df.isBearRelease);
}
if (auto df = p_->dataFileManager.getSelectedDataFileInfo()) {
comboBox->setSelectedId(df->fullPath.getFullPathName(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ EarBinauralMonitoringAudioProcessor::EarBinauralMonitoringAudioProcessor()
: AudioProcessor(_getBusProperties()) {

dataFileManager.onSelectedDataFileChange(
[this](std::shared_ptr<ear::plugin::DataFileManager::DataFile> df) {
[this](ear::plugin::DataFileManager::DataFile const& df) {
writeConfigFile();
restartBearProcessor();
});
Expand Down

0 comments on commit 66b5bc0

Please sign in to comment.