From a76d8c24fb333a7bf9585399a47b4ab61c917f36 Mon Sep 17 00:00:00 2001 From: saker Date: Fri, 13 Sep 2024 21:41:15 -0400 Subject: [PATCH 01/19] Add interface for SampleDatabase --- include/SampleDatabase.h | 82 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 include/SampleDatabase.h diff --git a/include/SampleDatabase.h b/include/SampleDatabase.h new file mode 100644 index 00000000000..d0b1bc85486 --- /dev/null +++ b/include/SampleDatabase.h @@ -0,0 +1,82 @@ +/* + * SampleDatabase.h + * + * Copyright (c) 2024 saker + * + * This file is part of LMMS - https://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef LMMS_SAMPLE_DATABASE_H +#define LMMS_SAMPLE_DATABASE_H + +#include +#include +#include +#include + +#include "SampleBuffer.h" + +namespace lmms { +class SampleDatabase +{ +public: + /** + Fetches a sample from the database through a path to an audio file, + and returns the stored buffer. + + If `path` exists in the database, its creation time and last write time + is checked with what is currently in the database. If there is a mismatch, the sample is reloaded from disk, its + entry in the database is updated, and the sample is returned. + + If `path` does not exist in the database, the sample is loaded from disk and + then returned. + + If `path` does not exist on disk, an empty buffer is returned. + */ + auto fetch(const QString& path) -> std::shared_ptr; + + /** + Fetches a sample from the database through a Base64 string and a sample rate + and returns the stored buffer. + + If an entry for a `base64` string with a certain `sampleRate` exists in the database, the stored sample is + returned. Otherwise, if it does not exist in the database, the sample is loaded and then returned. + */ + auto fetch(const std::string& base64, int sampleRate) -> std::shared_ptr; + +private: + struct AudioFileEntry + { + std::filesystem::path path; + std::filesystem::file_time_type creationTime; + std::filesystem::file_time_type lastWriteTime; + }; + + struct Base64Entry + { + std::string base64; + int sampleRate; + }; + + std::unordered_map> m_audioFileMap; + std::unordered_map> m_base64Map; +}; +} // namespace lmms + +#endif // LMMS_SAMPLE_DATABASE_H From 2ca2f25ab8b8dcf288cee940f144f2f8ad964096 Mon Sep 17 00:00:00 2001 From: saker Date: Fri, 13 Sep 2024 23:18:23 -0400 Subject: [PATCH 02/19] Add implementation for SampleDatabase --- include/FileSystemHelpers.h | 55 ++++++++++++++++++++++++++++++++ include/SampleDatabase.h | 38 ++++++++++++++++++----- src/core/CMakeLists.txt | 1 + src/core/SampleDatabase.cpp | 62 +++++++++++++++++++++++++++++++++++++ 4 files changed, 148 insertions(+), 8 deletions(-) create mode 100644 include/FileSystemHelpers.h create mode 100644 src/core/SampleDatabase.cpp diff --git a/include/FileSystemHelpers.h b/include/FileSystemHelpers.h new file mode 100644 index 00000000000..6e607dfd3b2 --- /dev/null +++ b/include/FileSystemHelpers.h @@ -0,0 +1,55 @@ +/* + * FileSystemHelpers.h + * + * Copyright (c) 2024 saker + * + * This file is part of LMMS - https://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef LMMS_FILE_SYSTEM_HELPERS_H +#define LMMS_FILE_SYSTEM_HELPERS_H + +#include +#include + +namespace lmms { +class FileSystemHelpers +{ +public: + static std::filesystem::path pathFromQString(const QString& path) + { +#ifdef _WIN32 + return std::filesystem::path{path.toStdWString()}; +#else + return std::filesystem::path{path.toStdString()}; +#endif + } + + static QString qStringFromPath(const std::filesystem::path& path) + { +#ifdef _WIN32 + return QString::fromStdWString(path.generic_wstring()); +#else + return QString::fromStdString(path.native()); +#endif + } +}; +} // namespace lmms + +#endif // LMMS_FILE_SYSTEM_HELPERS_H diff --git a/include/SampleDatabase.h b/include/SampleDatabase.h index d0b1bc85486..246c8a21d2f 100644 --- a/include/SampleDatabase.h +++ b/include/SampleDatabase.h @@ -40,16 +40,16 @@ class SampleDatabase Fetches a sample from the database through a path to an audio file, and returns the stored buffer. - If `path` exists in the database, its creation time and last write time - is checked with what is currently in the database. If there is a mismatch, the sample is reloaded from disk, its - entry in the database is updated, and the sample is returned. + If `path` exists in the database, its last write time is checked with what is currently in the database. If + there is a mismatch, the sample is reloaded from disk, its entry in the database is updated, and the sample is + returned. If `path` does not exist in the database, the sample is loaded from disk and then returned. If `path` does not exist on disk, an empty buffer is returned. */ - auto fetch(const QString& path) -> std::shared_ptr; + static auto fetch(const std::filesystem::path& path) -> std::shared_ptr; /** Fetches a sample from the database through a Base64 string and a sample rate @@ -58,24 +58,46 @@ class SampleDatabase If an entry for a `base64` string with a certain `sampleRate` exists in the database, the stored sample is returned. Otherwise, if it does not exist in the database, the sample is loaded and then returned. */ - auto fetch(const std::string& base64, int sampleRate) -> std::shared_ptr; + static auto fetch(const std::string& base64, int sampleRate) -> std::shared_ptr; private: struct AudioFileEntry { + friend bool operator==(const AudioFileEntry& first, const AudioFileEntry& second) noexcept + { + return first.path == second.path && first.lastWriteTime == second.lastWriteTime; + } + std::filesystem::path path; - std::filesystem::file_time_type creationTime; std::filesystem::file_time_type lastWriteTime; }; struct Base64Entry { + friend bool operator==(const Base64Entry& first, const Base64Entry& second) noexcept + { + return first.base64 == second.base64 && first.sampleRate == second.sampleRate; + } + std::string base64; int sampleRate; }; - std::unordered_map> m_audioFileMap; - std::unordered_map> m_base64Map; + struct Hash + { + std::size_t operator()(const AudioFileEntry& entry) const noexcept + { + return std::hash{}(entry.path); + } + + std::size_t operator()(const Base64Entry& entry) const noexcept + { + return std::hash{}(entry.base64); + } + }; + + inline static std::unordered_map, Hash> s_audioFileMap; + inline static std::unordered_map, Hash> s_base64Map; }; } // namespace lmms diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 1e2c4f3cfdb..8016d54abbe 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -67,6 +67,7 @@ set(LMMS_SRCS core/RenderManager.cpp core/RingBuffer.cpp core/Sample.cpp + core/SampleDatabase.cpp core/SampleBuffer.cpp core/SampleClip.cpp core/SampleDecoder.cpp diff --git a/src/core/SampleDatabase.cpp b/src/core/SampleDatabase.cpp new file mode 100644 index 00000000000..81f3b597074 --- /dev/null +++ b/src/core/SampleDatabase.cpp @@ -0,0 +1,62 @@ +/* + * SampleDatabase.cpp + * + * Copyright (c) 2024 saker + * + * This file is part of LMMS - https://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include "SampleDatabase.h" + +#include + +#include "FileSystemHelpers.h" +#include "SampleBuffer.h" + +namespace lmms { +auto SampleDatabase::fetch(const std::filesystem::path& path) -> std::shared_ptr +{ + const auto entry = AudioFileEntry{path, std::filesystem::last_write_time(path)}; + const auto it = s_audioFileMap.find(entry); + + if (it == s_audioFileMap.end()) + { + const auto buffer = std::make_shared(FileSystemHelpers::qStringFromPath(path)); + s_audioFileMap.insert(std::make_pair(entry, buffer)); + return buffer; + } + + return it->second.lock(); +} + +auto SampleDatabase::fetch(const std::string& base64, int sampleRate) -> std::shared_ptr +{ + const auto entry = Base64Entry{base64, sampleRate}; + const auto it = s_base64Map.find(entry); + + if (it == s_base64Map.end()) + { + const auto buffer = std::make_shared(QString::fromStdString(base64), sampleRate); + s_base64Map.insert(std::make_pair(entry, buffer)); + return buffer; + } + + return it->second.lock(); +} +} // namespace lmms From badde844e8744a67e8c249d88f9ced0d2605b2a0 Mon Sep 17 00:00:00 2001 From: saker Date: Fri, 13 Sep 2024 23:32:54 -0400 Subject: [PATCH 03/19] Rename SampleLoader::create* to SampleLoader::load* --- include/SampleLoader.h | 4 ++-- plugins/AudioFileProcessor/AudioFileProcessor.cpp | 4 ++-- plugins/SlicerT/SlicerT.cpp | 6 +++--- plugins/TripleOscillator/TripleOscillator.cpp | 4 ++-- src/core/EnvelopeAndLfoParameters.cpp | 2 +- src/core/LfoController.cpp | 2 +- src/core/SampleClip.cpp | 4 ++-- src/gui/FileBrowser.cpp | 2 +- src/gui/LfoControllerDialog.cpp | 2 +- src/gui/SampleLoader.cpp | 4 ++-- src/gui/clips/SampleClipView.cpp | 4 ++-- src/gui/instrument/EnvelopeAndLfoView.cpp | 4 ++-- src/gui/widgets/Graph.cpp | 2 +- 13 files changed, 22 insertions(+), 22 deletions(-) diff --git a/include/SampleLoader.h b/include/SampleLoader.h index fd8f1135725..b35afbb3a51 100644 --- a/include/SampleLoader.h +++ b/include/SampleLoader.h @@ -37,8 +37,8 @@ class LMMS_EXPORT SampleLoader public: static QString openAudioFile(const QString& previousFile = ""); static QString openWaveformFile(const QString& previousFile = ""); - static std::shared_ptr createBufferFromFile(const QString& filePath); - static std::shared_ptr createBufferFromBase64( + static std::shared_ptr loadBufferFromFile(const QString& filePath); + static std::shared_ptr loadBufferFromBase64( const QString& base64, int sampleRate = Engine::audioEngine()->outputSampleRate()); private: static void displayError(const QString& message); diff --git a/plugins/AudioFileProcessor/AudioFileProcessor.cpp b/plugins/AudioFileProcessor/AudioFileProcessor.cpp index 4cc14ba9cdb..9de09bd4846 100644 --- a/plugins/AudioFileProcessor/AudioFileProcessor.cpp +++ b/plugins/AudioFileProcessor/AudioFileProcessor.cpp @@ -224,7 +224,7 @@ void AudioFileProcessor::loadSettings(const QDomElement& elem) } else if (auto sampleData = elem.attribute("sampledata"); !sampleData.isEmpty()) { - m_sample = Sample(gui::SampleLoader::createBufferFromBase64(sampleData)); + m_sample = Sample(gui::SampleLoader::loadBufferFromBase64(sampleData)); } m_loopModel.loadSettings(elem, "looped"); @@ -317,7 +317,7 @@ void AudioFileProcessor::setAudioFile(const QString& _audio_file, bool _rename) } // else we don't touch the track-name, because the user named it self - m_sample = Sample(gui::SampleLoader::createBufferFromFile(_audio_file)); + m_sample = Sample(gui::SampleLoader::loadBufferFromFile(_audio_file)); loopPointChanged(); emit sampleUpdated(); } diff --git a/plugins/SlicerT/SlicerT.cpp b/plugins/SlicerT/SlicerT.cpp index dcfbf6bc551..f2173812255 100644 --- a/plugins/SlicerT/SlicerT.cpp +++ b/plugins/SlicerT/SlicerT.cpp @@ -319,7 +319,7 @@ std::vector SlicerT::getMidi() void SlicerT::updateFile(QString file) { - if (auto buffer = gui::SampleLoader::createBufferFromFile(file)) { m_originalSample = Sample(std::move(buffer)); } + if (auto buffer = gui::SampleLoader::loadBufferFromFile(file)) { m_originalSample = Sample(std::move(buffer)); } findBPM(); findSlices(); @@ -359,7 +359,7 @@ void SlicerT::loadSettings(const QDomElement& element) { if (QFileInfo(PathUtil::toAbsolute(srcFile)).exists()) { - auto buffer = gui::SampleLoader::createBufferFromFile(srcFile); + auto buffer = gui::SampleLoader::loadBufferFromFile(srcFile); m_originalSample = Sample(std::move(buffer)); } else @@ -370,7 +370,7 @@ void SlicerT::loadSettings(const QDomElement& element) } else if (auto sampleData = element.attribute("sampledata"); !sampleData.isEmpty()) { - auto buffer = gui::SampleLoader::createBufferFromBase64(sampleData); + auto buffer = gui::SampleLoader::loadBufferFromBase64(sampleData); m_originalSample = Sample(std::move(buffer)); } diff --git a/plugins/TripleOscillator/TripleOscillator.cpp b/plugins/TripleOscillator/TripleOscillator.cpp index f04cee81835..280d2b4647b 100644 --- a/plugins/TripleOscillator/TripleOscillator.cpp +++ b/plugins/TripleOscillator/TripleOscillator.cpp @@ -140,7 +140,7 @@ void OscillatorObject::oscUserDefWaveDblClick() auto af = gui::SampleLoader::openWaveformFile(); if( af != "" ) { - m_sampleBuffer = gui::SampleLoader::createBufferFromFile(af); + m_sampleBuffer = gui::SampleLoader::loadBufferFromFile(af); m_userAntiAliasWaveTable = Oscillator::generateAntiAliasUserWaveTable(m_sampleBuffer.get()); // TODO: //m_usrWaveBtn->setToolTip(m_sampleBuffer->audioFile()); @@ -287,7 +287,7 @@ void TripleOscillator::loadSettings( const QDomElement & _this ) { if (QFileInfo(PathUtil::toAbsolute(userWaveFile)).exists()) { - m_osc[i]->m_sampleBuffer = gui::SampleLoader::createBufferFromFile(userWaveFile); + m_osc[i]->m_sampleBuffer = gui::SampleLoader::loadBufferFromFile(userWaveFile); m_osc[i]->m_userAntiAliasWaveTable = Oscillator::generateAntiAliasUserWaveTable(m_osc[i]->m_sampleBuffer.get()); } else { Engine::getSong()->collectError(QString("%1: %2").arg(tr("Sample not found"), userWaveFile)); } diff --git a/src/core/EnvelopeAndLfoParameters.cpp b/src/core/EnvelopeAndLfoParameters.cpp index a3c3bcf9184..80a4dfd3e41 100644 --- a/src/core/EnvelopeAndLfoParameters.cpp +++ b/src/core/EnvelopeAndLfoParameters.cpp @@ -389,7 +389,7 @@ void EnvelopeAndLfoParameters::loadSettings( const QDomElement & _this ) { if (QFileInfo(PathUtil::toAbsolute(userWaveFile)).exists()) { - m_userWave = gui::SampleLoader::createBufferFromFile(_this.attribute("userwavefile")); + m_userWave = gui::SampleLoader::loadBufferFromFile(_this.attribute("userwavefile")); } else { Engine::getSong()->collectError(QString("%1: %2").arg(tr("Sample not found"), userWaveFile)); } } diff --git a/src/core/LfoController.cpp b/src/core/LfoController.cpp index 96ea71f7b50..11f99de5b2a 100644 --- a/src/core/LfoController.cpp +++ b/src/core/LfoController.cpp @@ -243,7 +243,7 @@ void LfoController::loadSettings( const QDomElement & _this ) { if (QFileInfo(PathUtil::toAbsolute(userWaveFile)).exists()) { - m_userDefSampleBuffer = gui::SampleLoader::createBufferFromFile(_this.attribute("userwavefile")); + m_userDefSampleBuffer = gui::SampleLoader::loadBufferFromFile(_this.attribute("userwavefile")); } else { Engine::getSong()->collectError(QString("%1: %2").arg(tr("Sample not found"), userWaveFile)); } } diff --git a/src/core/SampleClip.cpp b/src/core/SampleClip.cpp index 5ef001e20d1..86f8a4868c6 100644 --- a/src/core/SampleClip.cpp +++ b/src/core/SampleClip.cpp @@ -150,7 +150,7 @@ void SampleClip::setSampleFile(const QString& sf) if (!sf.isEmpty()) { //Otherwise set it to the sample's length - m_sample = Sample(gui::SampleLoader::createBufferFromFile(sf)); + m_sample = Sample(gui::SampleLoader::loadBufferFromFile(sf)); length = sampleLength(); } @@ -309,7 +309,7 @@ void SampleClip::loadSettings( const QDomElement & _this ) auto sampleRate = _this.hasAttribute("sample_rate") ? _this.attribute("sample_rate").toInt() : Engine::audioEngine()->outputSampleRate(); - auto buffer = gui::SampleLoader::createBufferFromBase64(_this.attribute("data"), sampleRate); + auto buffer = gui::SampleLoader::loadBufferFromBase64(_this.attribute("data"), sampleRate); m_sample = Sample(std::move(buffer)); } changeLength( _this.attribute( "len" ).toInt() ); diff --git a/src/gui/FileBrowser.cpp b/src/gui/FileBrowser.cpp index e5168fac8bb..c414d538071 100644 --- a/src/gui/FileBrowser.cpp +++ b/src/gui/FileBrowser.cpp @@ -743,7 +743,7 @@ void FileBrowserTreeWidget::previewFileItem(FileItem* file) embed::getIconPixmap("sample_file", 24, 24), 0); // TODO: this can be removed once we do this outside the event thread qApp->processEvents(QEventLoop::ExcludeUserInputEvents); - if (auto buffer = SampleLoader::createBufferFromFile(fileName)) + if (auto buffer = SampleLoader::loadBufferFromFile(fileName)) { auto s = new SamplePlayHandle(new lmms::Sample{std::move(buffer)}); s->setDoneMayReturnTrue(false); diff --git a/src/gui/LfoControllerDialog.cpp b/src/gui/LfoControllerDialog.cpp index 559ac13360c..c492d455165 100644 --- a/src/gui/LfoControllerDialog.cpp +++ b/src/gui/LfoControllerDialog.cpp @@ -216,7 +216,7 @@ void LfoControllerDialog::askUserDefWave() auto lfoModel = dynamic_cast(model()); auto& buffer = lfoModel->m_userDefSampleBuffer; - buffer = SampleLoader::createBufferFromFile(fileName); + buffer = SampleLoader::loadBufferFromFile(fileName); m_userWaveBtn->setToolTip(buffer->audioFile()); } diff --git a/src/gui/SampleLoader.cpp b/src/gui/SampleLoader.cpp index f2340852d77..af70ffc241c 100644 --- a/src/gui/SampleLoader.cpp +++ b/src/gui/SampleLoader.cpp @@ -88,7 +88,7 @@ QString SampleLoader::openWaveformFile(const QString& previousFile) previousFile.isEmpty() ? ConfigManager::inst()->factorySamplesDir() + "waveforms/10saw.flac" : previousFile); } -std::shared_ptr SampleLoader::createBufferFromFile(const QString& filePath) +std::shared_ptr SampleLoader::loadBufferFromFile(const QString& filePath) { if (filePath.isEmpty()) { return SampleBuffer::emptyBuffer(); } @@ -103,7 +103,7 @@ std::shared_ptr SampleLoader::createBufferFromFile(const QSt } } -std::shared_ptr SampleLoader::createBufferFromBase64(const QString& base64, int sampleRate) +std::shared_ptr SampleLoader::loadBufferFromBase64(const QString& base64, int sampleRate) { if (base64.isEmpty()) { return SampleBuffer::emptyBuffer(); } diff --git a/src/gui/clips/SampleClipView.cpp b/src/gui/clips/SampleClipView.cpp index a7251be8de6..025e8db059b 100644 --- a/src/gui/clips/SampleClipView.cpp +++ b/src/gui/clips/SampleClipView.cpp @@ -123,7 +123,7 @@ void SampleClipView::dropEvent( QDropEvent * _de ) } else if( StringPairDrag::decodeKey( _de ) == "sampledata" ) { - m_clip->setSampleBuffer(SampleLoader::createBufferFromBase64(StringPairDrag::decodeValue(_de))); + m_clip->setSampleBuffer(SampleLoader::loadBufferFromBase64(StringPairDrag::decodeValue(_de))); m_clip->updateLength(); update(); _de->accept(); @@ -190,7 +190,7 @@ void SampleClipView::mouseDoubleClickEvent( QMouseEvent * ) } else { - auto sampleBuffer = SampleLoader::createBufferFromFile(selectedAudioFile); + auto sampleBuffer = SampleLoader::loadBufferFromFile(selectedAudioFile); if (sampleBuffer != SampleBuffer::emptyBuffer()) { m_clip->setSampleBuffer(sampleBuffer); diff --git a/src/gui/instrument/EnvelopeAndLfoView.cpp b/src/gui/instrument/EnvelopeAndLfoView.cpp index 1b639e6c326..cfca2bbfc2c 100644 --- a/src/gui/instrument/EnvelopeAndLfoView.cpp +++ b/src/gui/instrument/EnvelopeAndLfoView.cpp @@ -242,7 +242,7 @@ void EnvelopeAndLfoView::dropEvent( QDropEvent * _de ) QString value = StringPairDrag::decodeValue( _de ); if( type == "samplefile" ) { - m_params->m_userWave = SampleLoader::createBufferFromFile(value); + m_params->m_userWave = SampleLoader::loadBufferFromFile(value); m_userLfoBtn->model()->setValue( true ); m_params->m_lfoWaveModel.setValue(static_cast(EnvelopeAndLfoParameters::LfoShape::UserDefinedWave)); _de->accept(); @@ -254,7 +254,7 @@ void EnvelopeAndLfoView::dropEvent( QDropEvent * _de ) auto file = dataFile.content(). firstChildElement().firstChildElement(). firstChildElement().attribute("src"); - m_params->m_userWave = SampleLoader::createBufferFromFile(file); + m_params->m_userWave = SampleLoader::loadBufferFromFile(file); m_userLfoBtn->model()->setValue( true ); m_params->m_lfoWaveModel.setValue(static_cast(EnvelopeAndLfoParameters::LfoShape::UserDefinedWave)); _de->accept(); diff --git a/src/gui/widgets/Graph.cpp b/src/gui/widgets/Graph.cpp index 0781d4f1113..3a48c9e40e1 100644 --- a/src/gui/widgets/Graph.cpp +++ b/src/gui/widgets/Graph.cpp @@ -592,7 +592,7 @@ QString graphModel::setWaveToUser() QString fileName = gui::SampleLoader::openWaveformFile(); if( fileName.isEmpty() == false ) { - auto sampleBuffer = gui::SampleLoader::createBufferFromFile(fileName); + auto sampleBuffer = gui::SampleLoader::loadBufferFromFile(fileName); for( int i = 0; i < length(); i++ ) { m_samples[i] = Oscillator::userWaveSample(sampleBuffer.get(), i / static_cast(length())); From 6a6ecfa31420b8e3e539183673249d8adec9c2e0 Mon Sep 17 00:00:00 2001 From: saker Date: Sat, 14 Sep 2024 00:06:21 -0400 Subject: [PATCH 04/19] Use SampleDatabase in SampleLoader --- include/SampleDatabase.h | 6 ++---- src/core/SampleDatabase.cpp | 13 +++++++------ src/gui/SampleLoader.cpp | 6 +++--- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/include/SampleDatabase.h b/include/SampleDatabase.h index 246c8a21d2f..fc0c1886412 100644 --- a/include/SampleDatabase.h +++ b/include/SampleDatabase.h @@ -46,10 +46,8 @@ class SampleDatabase If `path` does not exist in the database, the sample is loaded from disk and then returned. - - If `path` does not exist on disk, an empty buffer is returned. */ - static auto fetch(const std::filesystem::path& path) -> std::shared_ptr; + static auto fetch(const QString& path) -> std::shared_ptr; /** Fetches a sample from the database through a Base64 string and a sample rate @@ -58,7 +56,7 @@ class SampleDatabase If an entry for a `base64` string with a certain `sampleRate` exists in the database, the stored sample is returned. Otherwise, if it does not exist in the database, the sample is loaded and then returned. */ - static auto fetch(const std::string& base64, int sampleRate) -> std::shared_ptr; + static auto fetch(const QString& base64, int sampleRate) -> std::shared_ptr; private: struct AudioFileEntry diff --git a/src/core/SampleDatabase.cpp b/src/core/SampleDatabase.cpp index 81f3b597074..d60edaa4c09 100644 --- a/src/core/SampleDatabase.cpp +++ b/src/core/SampleDatabase.cpp @@ -30,14 +30,15 @@ #include "SampleBuffer.h" namespace lmms { -auto SampleDatabase::fetch(const std::filesystem::path& path) -> std::shared_ptr +auto SampleDatabase::fetch(const QString& path) -> std::shared_ptr { - const auto entry = AudioFileEntry{path, std::filesystem::last_write_time(path)}; + const auto fsPath = FileSystemHelpers::pathFromQString(path); + const auto entry = AudioFileEntry{fsPath, std::filesystem::last_write_time(fsPath)}; const auto it = s_audioFileMap.find(entry); if (it == s_audioFileMap.end()) { - const auto buffer = std::make_shared(FileSystemHelpers::qStringFromPath(path)); + const auto buffer = std::make_shared(path); s_audioFileMap.insert(std::make_pair(entry, buffer)); return buffer; } @@ -45,14 +46,14 @@ auto SampleDatabase::fetch(const std::filesystem::path& path) -> std::shared_ptr return it->second.lock(); } -auto SampleDatabase::fetch(const std::string& base64, int sampleRate) -> std::shared_ptr +auto SampleDatabase::fetch(const QString& base64, int sampleRate) -> std::shared_ptr { - const auto entry = Base64Entry{base64, sampleRate}; + const auto entry = Base64Entry{base64.toStdString(), sampleRate}; const auto it = s_base64Map.find(entry); if (it == s_base64Map.end()) { - const auto buffer = std::make_shared(QString::fromStdString(base64), sampleRate); + const auto buffer = std::make_shared(base64, sampleRate); s_base64Map.insert(std::make_pair(entry, buffer)); return buffer; } diff --git a/src/gui/SampleLoader.cpp b/src/gui/SampleLoader.cpp index af70ffc241c..02c348000e6 100644 --- a/src/gui/SampleLoader.cpp +++ b/src/gui/SampleLoader.cpp @@ -32,8 +32,8 @@ #include "FileDialog.h" #include "GuiApplication.h" #include "PathUtil.h" +#include "SampleDatabase.h" #include "SampleDecoder.h" -#include "Song.h" namespace lmms::gui { QString SampleLoader::openAudioFile(const QString& previousFile) @@ -94,7 +94,7 @@ std::shared_ptr SampleLoader::loadBufferFromFile(const QStri try { - return std::make_shared(filePath); + return SampleDatabase::fetch(filePath); } catch (const std::runtime_error& error) { @@ -109,7 +109,7 @@ std::shared_ptr SampleLoader::loadBufferFromBase64(const QSt try { - return std::make_shared(base64, sampleRate); + return SampleDatabase::fetch(base64, sampleRate); } catch (const std::runtime_error& error) { From 247955ebd43537f6fc1c5e544ae1e621ada2e1d6 Mon Sep 17 00:00:00 2001 From: saker Date: Sat, 14 Sep 2024 00:22:31 -0400 Subject: [PATCH 05/19] Fix segmentation fault on null entries --- src/core/SampleDatabase.cpp | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/core/SampleDatabase.cpp b/src/core/SampleDatabase.cpp index d60edaa4c09..bfff740f61d 100644 --- a/src/core/SampleDatabase.cpp +++ b/src/core/SampleDatabase.cpp @@ -43,7 +43,15 @@ auto SampleDatabase::fetch(const QString& path) -> std::shared_ptr return buffer; } - return it->second.lock(); + const auto entryLock = it->second.lock(); + if (!entryLock) + { + const auto buffer = std::make_shared(path); + s_audioFileMap[entry] = buffer; + return buffer; + } + + return entryLock; } auto SampleDatabase::fetch(const QString& base64, int sampleRate) -> std::shared_ptr @@ -58,6 +66,14 @@ auto SampleDatabase::fetch(const QString& base64, int sampleRate) -> std::shared return buffer; } - return it->second.lock(); + const auto entryLock = it->second.lock(); + if (!entryLock) + { + const auto buffer = std::make_shared(base64, sampleRate); + s_base64Map[entry] = buffer; + return buffer; + } + + return entryLock; } } // namespace lmms From 121b327591ab528578d68496ce8e0b9b6c5bd96c Mon Sep 17 00:00:00 2001 From: saker Date: Sat, 14 Sep 2024 00:50:43 -0400 Subject: [PATCH 06/19] Fix duplication --- include/SampleDatabase.h | 23 ++++++++++++++++++++++ src/core/SampleDatabase.cpp | 38 ++----------------------------------- 2 files changed, 25 insertions(+), 36 deletions(-) diff --git a/include/SampleDatabase.h b/include/SampleDatabase.h index fc0c1886412..69b82f01a66 100644 --- a/include/SampleDatabase.h +++ b/include/SampleDatabase.h @@ -94,6 +94,29 @@ class SampleDatabase } }; + template + static auto get(const T& entry, std::unordered_map, Hash>& map, Args... args) + { + const auto it = map.find(entry); + + if (it == map.end()) + { + const auto buffer = std::make_shared(std::forward(args)...); + map.insert(std::make_pair(entry, buffer)); + return buffer; + } + + const auto entryLock = it->second.lock(); + if (!entryLock) + { + const auto buffer = std::make_shared(std::forward(args)...); + map[entry] = buffer; + return buffer; + } + + return entryLock; + } + inline static std::unordered_map, Hash> s_audioFileMap; inline static std::unordered_map, Hash> s_base64Map; }; diff --git a/src/core/SampleDatabase.cpp b/src/core/SampleDatabase.cpp index bfff740f61d..07aa1f0ebe1 100644 --- a/src/core/SampleDatabase.cpp +++ b/src/core/SampleDatabase.cpp @@ -34,46 +34,12 @@ auto SampleDatabase::fetch(const QString& path) -> std::shared_ptr { const auto fsPath = FileSystemHelpers::pathFromQString(path); const auto entry = AudioFileEntry{fsPath, std::filesystem::last_write_time(fsPath)}; - const auto it = s_audioFileMap.find(entry); - - if (it == s_audioFileMap.end()) - { - const auto buffer = std::make_shared(path); - s_audioFileMap.insert(std::make_pair(entry, buffer)); - return buffer; - } - - const auto entryLock = it->second.lock(); - if (!entryLock) - { - const auto buffer = std::make_shared(path); - s_audioFileMap[entry] = buffer; - return buffer; - } - - return entryLock; + return get(entry, s_audioFileMap, path); } auto SampleDatabase::fetch(const QString& base64, int sampleRate) -> std::shared_ptr { const auto entry = Base64Entry{base64.toStdString(), sampleRate}; - const auto it = s_base64Map.find(entry); - - if (it == s_base64Map.end()) - { - const auto buffer = std::make_shared(base64, sampleRate); - s_base64Map.insert(std::make_pair(entry, buffer)); - return buffer; - } - - const auto entryLock = it->second.lock(); - if (!entryLock) - { - const auto buffer = std::make_shared(base64, sampleRate); - s_base64Map[entry] = buffer; - return buffer; - } - - return entryLock; + return get(entry, s_base64Map, base64, sampleRate); } } // namespace lmms From 99f29a961653b5ecaa2306fffc2efd05b8212ce7 Mon Sep 17 00:00:00 2001 From: saker Date: Sat, 14 Sep 2024 02:03:53 -0400 Subject: [PATCH 07/19] Fix CI attempt 1 --- include/SampleDatabase.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/SampleDatabase.h b/include/SampleDatabase.h index 69b82f01a66..3bd813ace28 100644 --- a/include/SampleDatabase.h +++ b/include/SampleDatabase.h @@ -85,12 +85,12 @@ class SampleDatabase { std::size_t operator()(const AudioFileEntry& entry) const noexcept { - return std::hash{}(entry.path); + return std::hash()(entry.path); } std::size_t operator()(const Base64Entry& entry) const noexcept { - return std::hash{}(entry.base64); + return std::hash()(entry.base64); } }; From 35026ddead1c65c45d0a7914720ab3adbf24fa82 Mon Sep 17 00:00:00 2001 From: saker Date: Sat, 14 Sep 2024 02:39:57 -0400 Subject: [PATCH 08/19] CI fix attempt 2 --- include/SampleDatabase.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/SampleDatabase.h b/include/SampleDatabase.h index 3bd813ace28..7125712e5ad 100644 --- a/include/SampleDatabase.h +++ b/include/SampleDatabase.h @@ -85,7 +85,7 @@ class SampleDatabase { std::size_t operator()(const AudioFileEntry& entry) const noexcept { - return std::hash()(entry.path); + return std::filesystem::hash_value(entry.path); } std::size_t operator()(const Base64Entry& entry) const noexcept From 777e9c8fca95e995928bad8449dc61dc543c2a07 Mon Sep 17 00:00:00 2001 From: saker Date: Tue, 12 Nov 2024 19:35:29 -0500 Subject: [PATCH 09/19] Move file system helper functions into PathUtil --- include/FileSystemHelpers.h | 55 ------------------------------------- include/PathUtil.h | 7 +++++ src/core/PathUtil.cpp | 18 ++++++++++++ src/core/SampleDatabase.cpp | 4 +-- 4 files changed, 27 insertions(+), 57 deletions(-) delete mode 100644 include/FileSystemHelpers.h diff --git a/include/FileSystemHelpers.h b/include/FileSystemHelpers.h deleted file mode 100644 index 6e607dfd3b2..00000000000 --- a/include/FileSystemHelpers.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * FileSystemHelpers.h - * - * Copyright (c) 2024 saker - * - * This file is part of LMMS - https://lmms.io - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program (see COPYING); if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301 USA. - * - */ - -#ifndef LMMS_FILE_SYSTEM_HELPERS_H -#define LMMS_FILE_SYSTEM_HELPERS_H - -#include -#include - -namespace lmms { -class FileSystemHelpers -{ -public: - static std::filesystem::path pathFromQString(const QString& path) - { -#ifdef _WIN32 - return std::filesystem::path{path.toStdWString()}; -#else - return std::filesystem::path{path.toStdString()}; -#endif - } - - static QString qStringFromPath(const std::filesystem::path& path) - { -#ifdef _WIN32 - return QString::fromStdWString(path.generic_wstring()); -#else - return QString::fromStdString(path.native()); -#endif - } -}; -} // namespace lmms - -#endif // LMMS_FILE_SYSTEM_HELPERS_H diff --git a/include/PathUtil.h b/include/PathUtil.h index 9b410d014a0..43c06074e0a 100644 --- a/include/PathUtil.h +++ b/include/PathUtil.h @@ -28,6 +28,7 @@ #include "lmms_export.h" #include +#include namespace lmms::PathUtil { @@ -68,6 +69,12 @@ namespace lmms::PathUtil //! Defaults to an absolute path if all bases fail. QString LMMS_EXPORT toShortestRelative(const QString & input, bool allowLocal = false); + //! Converts a QString path to a STL filesystem path. + std::filesystem::path LMMS_EXPORT pathFromQString(const QString& path); + + //! Converts an STL filesystem path to a QString path. + QString LMMS_EXPORT qStringFromPath(const std::filesystem::path& path); + } // namespace lmms::PathUtil #endif // LMMS_PATHUTIL_H diff --git a/src/core/PathUtil.cpp b/src/core/PathUtil.cpp index 03ec465a929..d564a167a93 100644 --- a/src/core/PathUtil.cpp +++ b/src/core/PathUtil.cpp @@ -188,4 +188,22 @@ namespace lmms::PathUtil return basePrefix(shortestBase) + relativeOrAbsolute(absolutePath, shortestBase); } + std::filesystem::path pathFromQString(const QString& path) + { +#ifdef _WIN32 + return std::filesystem::path{path.toStdWString()}; +#else + return std::filesystem::path{path.toStdString()}; +#endif + } + + QString qStringFromPath(const std::filesystem::path& path) + { +#ifdef _WIN32 + return QString::fromStdWString(path.generic_wstring()); +#else + return QString::fromStdString(path.native()); +#endif + } + } // namespace lmms::PathUtil diff --git a/src/core/SampleDatabase.cpp b/src/core/SampleDatabase.cpp index 07aa1f0ebe1..077cf595faf 100644 --- a/src/core/SampleDatabase.cpp +++ b/src/core/SampleDatabase.cpp @@ -26,13 +26,13 @@ #include -#include "FileSystemHelpers.h" +#include "PathUtil.h" #include "SampleBuffer.h" namespace lmms { auto SampleDatabase::fetch(const QString& path) -> std::shared_ptr { - const auto fsPath = FileSystemHelpers::pathFromQString(path); + const auto fsPath = PathUtil::pathFromQString(path); const auto entry = AudioFileEntry{fsPath, std::filesystem::last_write_time(fsPath)}; return get(entry, s_audioFileMap, path); } From 9d3045751fa96e9aed64c05312eddb29548f3668 Mon Sep 17 00:00:00 2001 From: saker Date: Tue, 12 Nov 2024 20:27:56 -0500 Subject: [PATCH 10/19] Move SampleLoader into the core namespace and add SampleFilePicker class --- include/SampleFilePicker.h | 40 +++++++++ include/SampleLoader.h | 16 ++-- .../AudioFileProcessor/AudioFileProcessor.cpp | 4 +- .../AudioFileProcessorView.cpp | 4 +- plugins/SlicerT/SlicerT.cpp | 6 +- plugins/SlicerT/SlicerTView.cpp | 6 +- plugins/TripleOscillator/TripleOscillator.cpp | 8 +- src/core/EnvelopeAndLfoParameters.cpp | 2 +- src/core/LfoController.cpp | 2 +- src/core/SampleClip.cpp | 4 +- src/gui/CMakeLists.txt | 1 + src/gui/LfoControllerDialog.cpp | 3 +- src/gui/SampleFilePicker.cpp | 85 +++++++++++++++++++ src/gui/SampleLoader.cpp | 72 +++------------- src/gui/clips/SampleClipView.cpp | 4 +- src/gui/widgets/Graph.cpp | 6 +- 16 files changed, 166 insertions(+), 97 deletions(-) create mode 100644 include/SampleFilePicker.h create mode 100644 src/gui/SampleFilePicker.cpp diff --git a/include/SampleFilePicker.h b/include/SampleFilePicker.h new file mode 100644 index 00000000000..ae40b6385ca --- /dev/null +++ b/include/SampleFilePicker.h @@ -0,0 +1,40 @@ +/* + * SampleFilePicker.h + * + * Copyright (c) 2024 saker + * + * This file is part of LMMS - https://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef LMMS_GUI_SAMPLE_FILE_PICKER_H +#define LMMS_GUI_SAMPLE_FILE_PICKER_H + +#include +#include "lmms_export.h" + +namespace lmms::gui { +class LMMS_EXPORT SampleFilePicker +{ +public: + static QString openAudioFile(const QString& previousFile = ""); + static QString openWaveformFile(const QString& previousFile = ""); +}; +} // namespace lmms::gui + +#endif // LMMS_GUI_SAMPLE_FILE_PICKER_H diff --git a/include/SampleLoader.h b/include/SampleLoader.h index b35afbb3a51..147cfa913cc 100644 --- a/include/SampleLoader.h +++ b/include/SampleLoader.h @@ -1,7 +1,7 @@ /* - * SampleLoader.h - Load audio and waveform files + * SampleLoader.h * - * Copyright (c) 2023 saker + * Copyright (c) 2024 saker * * This file is part of LMMS - https://lmms.io * @@ -22,8 +22,8 @@ * */ -#ifndef LMMS_GUI_SAMPLE_LOADER_H -#define LMMS_GUI_SAMPLE_LOADER_H +#ifndef LMMS_SAMPLE_LOADER_H +#define LMMS_SAMPLE_LOADER_H #include #include @@ -31,18 +31,14 @@ #include "SampleBuffer.h" #include "lmms_export.h" -namespace lmms::gui { +namespace lmms { class LMMS_EXPORT SampleLoader { public: - static QString openAudioFile(const QString& previousFile = ""); - static QString openWaveformFile(const QString& previousFile = ""); static std::shared_ptr loadBufferFromFile(const QString& filePath); static std::shared_ptr loadBufferFromBase64( const QString& base64, int sampleRate = Engine::audioEngine()->outputSampleRate()); -private: - static void displayError(const QString& message); }; } // namespace lmms::gui -#endif // LMMS_GUI_SAMPLE_LOADER_H +#endif // LMMS_SAMPLE_LOADER_H diff --git a/plugins/AudioFileProcessor/AudioFileProcessor.cpp b/plugins/AudioFileProcessor/AudioFileProcessor.cpp index 9de09bd4846..82f188fd198 100644 --- a/plugins/AudioFileProcessor/AudioFileProcessor.cpp +++ b/plugins/AudioFileProcessor/AudioFileProcessor.cpp @@ -224,7 +224,7 @@ void AudioFileProcessor::loadSettings(const QDomElement& elem) } else if (auto sampleData = elem.attribute("sampledata"); !sampleData.isEmpty()) { - m_sample = Sample(gui::SampleLoader::loadBufferFromBase64(sampleData)); + m_sample = Sample(SampleLoader::loadBufferFromBase64(sampleData)); } m_loopModel.loadSettings(elem, "looped"); @@ -317,7 +317,7 @@ void AudioFileProcessor::setAudioFile(const QString& _audio_file, bool _rename) } // else we don't touch the track-name, because the user named it self - m_sample = Sample(gui::SampleLoader::loadBufferFromFile(_audio_file)); + m_sample = Sample(SampleLoader::loadBufferFromFile(_audio_file)); loopPointChanged(); emit sampleUpdated(); } diff --git a/plugins/AudioFileProcessor/AudioFileProcessorView.cpp b/plugins/AudioFileProcessor/AudioFileProcessorView.cpp index b7d5802dc44..35678b9e56c 100644 --- a/plugins/AudioFileProcessor/AudioFileProcessorView.cpp +++ b/plugins/AudioFileProcessor/AudioFileProcessorView.cpp @@ -33,7 +33,7 @@ #include "DataFile.h" #include "gui_templates.h" #include "PixmapButton.h" -#include "SampleLoader.h" +#include "SampleFilePicker.h" #include "Song.h" #include "StringPairDrag.h" #include "Track.h" @@ -257,7 +257,7 @@ void AudioFileProcessorView::sampleUpdated() void AudioFileProcessorView::openAudioFile() { - QString af = SampleLoader::openAudioFile(); + QString af = SampleFilePicker::openAudioFile(); if (af.isEmpty()) { return; } castModel()->setAudioFile(af); diff --git a/plugins/SlicerT/SlicerT.cpp b/plugins/SlicerT/SlicerT.cpp index f2173812255..2bf445d1a77 100644 --- a/plugins/SlicerT/SlicerT.cpp +++ b/plugins/SlicerT/SlicerT.cpp @@ -319,7 +319,7 @@ std::vector SlicerT::getMidi() void SlicerT::updateFile(QString file) { - if (auto buffer = gui::SampleLoader::loadBufferFromFile(file)) { m_originalSample = Sample(std::move(buffer)); } + if (auto buffer = SampleLoader::loadBufferFromFile(file)) { m_originalSample = Sample(std::move(buffer)); } findBPM(); findSlices(); @@ -359,7 +359,7 @@ void SlicerT::loadSettings(const QDomElement& element) { if (QFileInfo(PathUtil::toAbsolute(srcFile)).exists()) { - auto buffer = gui::SampleLoader::loadBufferFromFile(srcFile); + auto buffer = SampleLoader::loadBufferFromFile(srcFile); m_originalSample = Sample(std::move(buffer)); } else @@ -370,7 +370,7 @@ void SlicerT::loadSettings(const QDomElement& element) } else if (auto sampleData = element.attribute("sampledata"); !sampleData.isEmpty()) { - auto buffer = gui::SampleLoader::loadBufferFromBase64(sampleData); + auto buffer = SampleLoader::loadBufferFromBase64(sampleData); m_originalSample = Sample(std::move(buffer)); } diff --git a/plugins/SlicerT/SlicerTView.cpp b/plugins/SlicerT/SlicerTView.cpp index bbdb53ccbc2..39d71ef10f3 100644 --- a/plugins/SlicerT/SlicerTView.cpp +++ b/plugins/SlicerT/SlicerTView.cpp @@ -29,11 +29,9 @@ #include "Clipboard.h" #include "DataFile.h" -#include "Engine.h" #include "InstrumentTrack.h" -#include "SampleLoader.h" +#include "SampleFilePicker.h" #include "SlicerT.h" -#include "Song.h" #include "StringPairDrag.h" #include "Track.h" #include "embed.h" @@ -130,7 +128,7 @@ void SlicerTView::exportMidi() void SlicerTView::openFiles() { - const auto audioFile = SampleLoader::openAudioFile(); + const auto audioFile = SampleFilePicker::openAudioFile(); if (audioFile.isEmpty()) { return; } m_slicerTParent->updateFile(audioFile); } diff --git a/plugins/TripleOscillator/TripleOscillator.cpp b/plugins/TripleOscillator/TripleOscillator.cpp index 280d2b4647b..9c7f9602269 100644 --- a/plugins/TripleOscillator/TripleOscillator.cpp +++ b/plugins/TripleOscillator/TripleOscillator.cpp @@ -29,7 +29,6 @@ #include "TripleOscillator.h" #include "AudioEngine.h" #include "AutomatableButton.h" -#include "debug.h" #include "Engine.h" #include "InstrumentTrack.h" #include "Knob.h" @@ -39,6 +38,7 @@ #include "PixmapButton.h" #include "SampleBuffer.h" #include "SampleLoader.h" +#include "SampleFilePicker.h" #include "Song.h" #include "embed.h" #include "plugin_export.h" @@ -137,10 +137,10 @@ OscillatorObject::OscillatorObject( Model * _parent, int _idx ) : void OscillatorObject::oscUserDefWaveDblClick() { - auto af = gui::SampleLoader::openWaveformFile(); + auto af = gui::SampleFilePicker::openWaveformFile(); if( af != "" ) { - m_sampleBuffer = gui::SampleLoader::loadBufferFromFile(af); + m_sampleBuffer = SampleLoader::loadBufferFromFile(af); m_userAntiAliasWaveTable = Oscillator::generateAntiAliasUserWaveTable(m_sampleBuffer.get()); // TODO: //m_usrWaveBtn->setToolTip(m_sampleBuffer->audioFile()); @@ -287,7 +287,7 @@ void TripleOscillator::loadSettings( const QDomElement & _this ) { if (QFileInfo(PathUtil::toAbsolute(userWaveFile)).exists()) { - m_osc[i]->m_sampleBuffer = gui::SampleLoader::loadBufferFromFile(userWaveFile); + m_osc[i]->m_sampleBuffer = SampleLoader::loadBufferFromFile(userWaveFile); m_osc[i]->m_userAntiAliasWaveTable = Oscillator::generateAntiAliasUserWaveTable(m_osc[i]->m_sampleBuffer.get()); } else { Engine::getSong()->collectError(QString("%1: %2").arg(tr("Sample not found"), userWaveFile)); } diff --git a/src/core/EnvelopeAndLfoParameters.cpp b/src/core/EnvelopeAndLfoParameters.cpp index 80a4dfd3e41..8c7ad659894 100644 --- a/src/core/EnvelopeAndLfoParameters.cpp +++ b/src/core/EnvelopeAndLfoParameters.cpp @@ -389,7 +389,7 @@ void EnvelopeAndLfoParameters::loadSettings( const QDomElement & _this ) { if (QFileInfo(PathUtil::toAbsolute(userWaveFile)).exists()) { - m_userWave = gui::SampleLoader::loadBufferFromFile(_this.attribute("userwavefile")); + m_userWave = SampleLoader::loadBufferFromFile(_this.attribute("userwavefile")); } else { Engine::getSong()->collectError(QString("%1: %2").arg(tr("Sample not found"), userWaveFile)); } } diff --git a/src/core/LfoController.cpp b/src/core/LfoController.cpp index 11f99de5b2a..a65930557d9 100644 --- a/src/core/LfoController.cpp +++ b/src/core/LfoController.cpp @@ -243,7 +243,7 @@ void LfoController::loadSettings( const QDomElement & _this ) { if (QFileInfo(PathUtil::toAbsolute(userWaveFile)).exists()) { - m_userDefSampleBuffer = gui::SampleLoader::loadBufferFromFile(_this.attribute("userwavefile")); + m_userDefSampleBuffer = SampleLoader::loadBufferFromFile(_this.attribute("userwavefile")); } else { Engine::getSong()->collectError(QString("%1: %2").arg(tr("Sample not found"), userWaveFile)); } } diff --git a/src/core/SampleClip.cpp b/src/core/SampleClip.cpp index 86f8a4868c6..6fe3f22586b 100644 --- a/src/core/SampleClip.cpp +++ b/src/core/SampleClip.cpp @@ -150,7 +150,7 @@ void SampleClip::setSampleFile(const QString& sf) if (!sf.isEmpty()) { //Otherwise set it to the sample's length - m_sample = Sample(gui::SampleLoader::loadBufferFromFile(sf)); + m_sample = Sample(SampleLoader::loadBufferFromFile(sf)); length = sampleLength(); } @@ -309,7 +309,7 @@ void SampleClip::loadSettings( const QDomElement & _this ) auto sampleRate = _this.hasAttribute("sample_rate") ? _this.attribute("sample_rate").toInt() : Engine::audioEngine()->outputSampleRate(); - auto buffer = gui::SampleLoader::loadBufferFromBase64(_this.attribute("data"), sampleRate); + auto buffer = SampleLoader::loadBufferFromBase64(_this.attribute("data"), sampleRate); m_sample = Sample(std::move(buffer)); } changeLength( _this.attribute( "len" ).toInt() ); diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 4195ec58c1a..278c9ee1abf 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -33,6 +33,7 @@ SET(LMMS_SRCS gui/PluginBrowser.cpp gui/ProjectNotes.cpp gui/RowTableView.cpp + gui/SampleFilePicker.cpp gui/SampleLoader.cpp gui/SampleTrackWindow.cpp gui/SampleWaveform.cpp diff --git a/src/gui/LfoControllerDialog.cpp b/src/gui/LfoControllerDialog.cpp index c492d455165..7e88b5c9ce9 100644 --- a/src/gui/LfoControllerDialog.cpp +++ b/src/gui/LfoControllerDialog.cpp @@ -32,6 +32,7 @@ #include "TempoSyncKnob.h" #include "PixmapButton.h" #include "SampleLoader.h" +#include "SampleFilePicker.h" namespace lmms::gui { @@ -211,7 +212,7 @@ LfoControllerDialog::~LfoControllerDialog() void LfoControllerDialog::askUserDefWave() { - const auto fileName = SampleLoader::openWaveformFile(); + const auto fileName = SampleFilePicker::openWaveformFile(); if (fileName.isEmpty()) { return; } auto lfoModel = dynamic_cast(model()); diff --git a/src/gui/SampleFilePicker.cpp b/src/gui/SampleFilePicker.cpp new file mode 100644 index 00000000000..e18992dc80d --- /dev/null +++ b/src/gui/SampleFilePicker.cpp @@ -0,0 +1,85 @@ +/* + * SampleFilePicker.cpp + * + * Copyright (c) 2024 saker + * + * This file is part of LMMS - https://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include "SampleFilePicker.h" + +#include "ConfigManager.h" +#include "FileDialog.h" +#include "PathUtil.h" +#include "SampleDecoder.h" + +namespace lmms::gui { +QString SampleFilePicker::openAudioFile(const QString& previousFile) +{ + auto openFileDialog = FileDialog(nullptr, QObject::tr("Open audio file")); + auto dir = !previousFile.isEmpty() ? PathUtil::toAbsolute(previousFile) : ConfigManager::inst()->userSamplesDir(); + + // change dir to position of previously opened file + openFileDialog.setDirectory(dir); + openFileDialog.setFileMode(FileDialog::ExistingFiles); + + // set filters + auto fileTypes = QStringList{}; + auto allFileTypes = QStringList{}; + auto nameFilters = QStringList{}; + const auto& supportedAudioTypes = SampleDecoder::supportedAudioTypes(); + + for (const auto& audioType : supportedAudioTypes) + { + const auto name = QString::fromStdString(audioType.name); + const auto extension = QString::fromStdString(audioType.extension); + const auto displayExtension = QString{"*.%1"}.arg(extension); + fileTypes.append(QString{"%1 (%2)"}.arg(FileDialog::tr("%1 files").arg(name), displayExtension)); + allFileTypes.append(displayExtension); + } + + nameFilters.append(QString{"%1 (%2)"}.arg(FileDialog::tr("All audio files"), allFileTypes.join(" "))); + nameFilters.append(fileTypes); + nameFilters.append(QString("%1 (*)").arg(FileDialog::tr("Other files"))); + + openFileDialog.setNameFilters(nameFilters); + + if (!previousFile.isEmpty()) + { + // select previously opened file + openFileDialog.selectFile(QFileInfo{previousFile}.fileName()); + } + + if (openFileDialog.exec() == QDialog::Accepted) + { + if (openFileDialog.selectedFiles().isEmpty()) { return ""; } + + return PathUtil::toShortestRelative(openFileDialog.selectedFiles()[0]); + } + + return ""; +} + +QString SampleFilePicker::openWaveformFile(const QString& previousFile) +{ + return openAudioFile( + previousFile.isEmpty() ? ConfigManager::inst()->factorySamplesDir() + "waveforms/10saw.flac" : previousFile); +} + +} // namespace lmms::gui \ No newline at end of file diff --git a/src/gui/SampleLoader.cpp b/src/gui/SampleLoader.cpp index 02c348000e6..71fca0b5360 100644 --- a/src/gui/SampleLoader.cpp +++ b/src/gui/SampleLoader.cpp @@ -28,65 +28,17 @@ #include #include -#include "ConfigManager.h" -#include "FileDialog.h" #include "GuiApplication.h" -#include "PathUtil.h" #include "SampleDatabase.h" -#include "SampleDecoder.h" -namespace lmms::gui { -QString SampleLoader::openAudioFile(const QString& previousFile) -{ - auto openFileDialog = FileDialog(nullptr, QObject::tr("Open audio file")); - auto dir = !previousFile.isEmpty() ? PathUtil::toAbsolute(previousFile) : ConfigManager::inst()->userSamplesDir(); - - // change dir to position of previously opened file - openFileDialog.setDirectory(dir); - openFileDialog.setFileMode(FileDialog::ExistingFiles); - - // set filters - auto fileTypes = QStringList{}; - auto allFileTypes = QStringList{}; - auto nameFilters = QStringList{}; - const auto& supportedAudioTypes = SampleDecoder::supportedAudioTypes(); - - for (const auto& audioType : supportedAudioTypes) - { - const auto name = QString::fromStdString(audioType.name); - const auto extension = QString::fromStdString(audioType.extension); - const auto displayExtension = QString{"*.%1"}.arg(extension); - fileTypes.append(QString{"%1 (%2)"}.arg(FileDialog::tr("%1 files").arg(name), displayExtension)); - allFileTypes.append(displayExtension); - } +namespace lmms { - nameFilters.append(QString{"%1 (%2)"}.arg(FileDialog::tr("All audio files"), allFileTypes.join(" "))); - nameFilters.append(fileTypes); - nameFilters.append(QString("%1 (*)").arg(FileDialog::tr("Other files"))); - - openFileDialog.setNameFilters(nameFilters); - - if (!previousFile.isEmpty()) - { - // select previously opened file - openFileDialog.selectFile(QFileInfo{previousFile}.fileName()); - } - - if (openFileDialog.exec() == QDialog::Accepted) - { - if (openFileDialog.selectedFiles().isEmpty()) { return ""; } - - return PathUtil::toShortestRelative(openFileDialog.selectedFiles()[0]); - } - - return ""; -} - -QString SampleLoader::openWaveformFile(const QString& previousFile) +namespace { +void displayError(const QString& message) { - return openAudioFile( - previousFile.isEmpty() ? ConfigManager::inst()->factorySamplesDir() + "waveforms/10saw.flac" : previousFile); + QMessageBox::critical(nullptr, QObject::tr("Error loading sample"), message); } +} // namespace std::shared_ptr SampleLoader::loadBufferFromFile(const QString& filePath) { @@ -98,7 +50,8 @@ std::shared_ptr SampleLoader::loadBufferFromFile(const QStri } catch (const std::runtime_error& error) { - if (getGUI()) { displayError(QString::fromStdString(error.what())); } + // TODO: Remove calls to `gui::getGUI` from core and use something to signal back to the GUI instead + if (gui::getGUI()) { displayError(QString::fromStdString(error.what())); } return SampleBuffer::emptyBuffer(); } } @@ -113,14 +66,9 @@ std::shared_ptr SampleLoader::loadBufferFromBase64(const QSt } catch (const std::runtime_error& error) { - if (getGUI()) { displayError(QString::fromStdString(error.what())); } + // TODO: Remove calls to `gui::getGUI` from core and use something to signal back to the GUI instead + if (gui::getGUI()) { displayError(QString::fromStdString(error.what())); } return SampleBuffer::emptyBuffer(); } } - -void SampleLoader::displayError(const QString& message) -{ - QMessageBox::critical(nullptr, QObject::tr("Error loading sample"), message); -} - -} // namespace lmms::gui +} // namespace lmms diff --git a/src/gui/clips/SampleClipView.cpp b/src/gui/clips/SampleClipView.cpp index 025e8db059b..02ea034483a 100644 --- a/src/gui/clips/SampleClipView.cpp +++ b/src/gui/clips/SampleClipView.cpp @@ -23,7 +23,6 @@ */ #include "SampleClipView.h" - #include #include #include @@ -33,6 +32,7 @@ #include "embed.h" #include "PathUtil.h" #include "SampleClip.h" +#include "SampleFilePicker.h" #include "SampleLoader.h" #include "SampleWaveform.h" #include "Song.h" @@ -180,7 +180,7 @@ void SampleClipView::mouseReleaseEvent(QMouseEvent *_me) void SampleClipView::mouseDoubleClickEvent( QMouseEvent * ) { - const QString selectedAudioFile = SampleLoader::openAudioFile(); + const QString selectedAudioFile = SampleFilePicker::openAudioFile(); if (selectedAudioFile.isEmpty()) { return; } diff --git a/src/gui/widgets/Graph.cpp b/src/gui/widgets/Graph.cpp index 3a48c9e40e1..87ce7ef7827 100644 --- a/src/gui/widgets/Graph.cpp +++ b/src/gui/widgets/Graph.cpp @@ -26,9 +26,9 @@ #include #include "Graph.h" +#include "SampleFilePicker.h" #include "SampleLoader.h" #include "StringPairDrag.h" -#include "SampleBuffer.h" #include "Oscillator.h" namespace lmms @@ -589,10 +589,10 @@ void graphModel::setWaveToNoise() QString graphModel::setWaveToUser() { - QString fileName = gui::SampleLoader::openWaveformFile(); + QString fileName = gui::SampleFilePicker::openWaveformFile(); if( fileName.isEmpty() == false ) { - auto sampleBuffer = gui::SampleLoader::loadBufferFromFile(fileName); + auto sampleBuffer = SampleLoader::loadBufferFromFile(fileName); for( int i = 0; i < length(); i++ ) { m_samples[i] = Oscillator::userWaveSample(sampleBuffer.get(), i / static_cast(length())); From 053ba61af680b3a6e039b54732eb93f97038f9e4 Mon Sep 17 00:00:00 2001 From: saker Date: Tue, 12 Nov 2024 20:31:26 -0500 Subject: [PATCH 11/19] Add asserts that ensure only the main thread is requesting samples from the database --- src/core/SampleDatabase.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/core/SampleDatabase.cpp b/src/core/SampleDatabase.cpp index 077cf595faf..0d6c6518498 100644 --- a/src/core/SampleDatabase.cpp +++ b/src/core/SampleDatabase.cpp @@ -32,6 +32,10 @@ namespace lmms { auto SampleDatabase::fetch(const QString& path) -> std::shared_ptr { + // To ensure that only the main thread is requesting samples from the database + // Can be removed if proven that other threads are requesting samples besides the main thread + assert(QThread::currentThread() == QCoreApplication::instance()->thread()); + const auto fsPath = PathUtil::pathFromQString(path); const auto entry = AudioFileEntry{fsPath, std::filesystem::last_write_time(fsPath)}; return get(entry, s_audioFileMap, path); @@ -39,6 +43,10 @@ auto SampleDatabase::fetch(const QString& path) -> std::shared_ptr auto SampleDatabase::fetch(const QString& base64, int sampleRate) -> std::shared_ptr { + // To ensure that only the main thread is requesting samples from the database + // Can be removed if proven that other threads are requesting samples besides the main thread + assert(QThread::currentThread() == QCoreApplication::instance()->thread()); + const auto entry = Base64Entry{base64.toStdString(), sampleRate}; return get(entry, s_base64Map, base64, sampleRate); } From b1c1a9a430c892bc87021c9bcf647a2331059f4b Mon Sep 17 00:00:00 2001 From: saker Date: Tue, 12 Nov 2024 20:37:03 -0500 Subject: [PATCH 12/19] Rebrand SampleDatabase to SampleCache --- include/{SampleDatabase.h => SampleCache.h} | 24 +++++++++---------- src/core/CMakeLists.txt | 2 +- .../{SampleDatabase.cpp => SampleCache.cpp} | 12 +++++----- src/gui/SampleLoader.cpp | 6 ++--- 4 files changed, 22 insertions(+), 22 deletions(-) rename include/{SampleDatabase.h => SampleCache.h} (81%) rename src/core/{SampleDatabase.cpp => SampleCache.cpp} (86%) diff --git a/include/SampleDatabase.h b/include/SampleCache.h similarity index 81% rename from include/SampleDatabase.h rename to include/SampleCache.h index 7125712e5ad..9777ff759c1 100644 --- a/include/SampleDatabase.h +++ b/include/SampleCache.h @@ -1,5 +1,5 @@ /* - * SampleDatabase.h + * SampleCache.h * * Copyright (c) 2024 saker * @@ -22,8 +22,8 @@ * */ -#ifndef LMMS_SAMPLE_DATABASE_H -#define LMMS_SAMPLE_DATABASE_H +#ifndef LMMS_SAMPLE_CACHE_H +#define LMMS_SAMPLE_CACHE_H #include #include @@ -33,28 +33,28 @@ #include "SampleBuffer.h" namespace lmms { -class SampleDatabase +class SampleCache { public: /** - Fetches a sample from the database through a path to an audio file, + Fetches a sample from the cache through a path to an audio file, and returns the stored buffer. - If `path` exists in the database, its last write time is checked with what is currently in the database. If - there is a mismatch, the sample is reloaded from disk, its entry in the database is updated, and the sample is + If `path` exists in the cache, its last write time is checked with what is currently in the cache. If + there is a mismatch, the sample is reloaded from disk, its entry in the cache is updated, and the sample is returned. - If `path` does not exist in the database, the sample is loaded from disk and + If `path` does not exist in the cache, the sample is loaded from disk and then returned. */ static auto fetch(const QString& path) -> std::shared_ptr; /** - Fetches a sample from the database through a Base64 string and a sample rate + Fetches a sample from the cache through a Base64 string and a sample rate and returns the stored buffer. - If an entry for a `base64` string with a certain `sampleRate` exists in the database, the stored sample is - returned. Otherwise, if it does not exist in the database, the sample is loaded and then returned. + If an entry for a `base64` string with a certain `sampleRate` exists in the cache, the stored sample is + returned. Otherwise, if it does not exist in the cache, the sample is loaded and then returned. */ static auto fetch(const QString& base64, int sampleRate) -> std::shared_ptr; @@ -122,4 +122,4 @@ class SampleDatabase }; } // namespace lmms -#endif // LMMS_SAMPLE_DATABASE_H +#endif // LMMS_SAMPLE_CACHE_H diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 8016d54abbe..c388dbae2a3 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -67,7 +67,7 @@ set(LMMS_SRCS core/RenderManager.cpp core/RingBuffer.cpp core/Sample.cpp - core/SampleDatabase.cpp + core/SampleCache.cpp core/SampleBuffer.cpp core/SampleClip.cpp core/SampleDecoder.cpp diff --git a/src/core/SampleDatabase.cpp b/src/core/SampleCache.cpp similarity index 86% rename from src/core/SampleDatabase.cpp rename to src/core/SampleCache.cpp index 0d6c6518498..9c4750ebf7f 100644 --- a/src/core/SampleDatabase.cpp +++ b/src/core/SampleCache.cpp @@ -1,5 +1,5 @@ /* - * SampleDatabase.cpp + * SampleCache.cpp * * Copyright (c) 2024 saker * @@ -22,7 +22,7 @@ * */ -#include "SampleDatabase.h" +#include "SampleCache.h" #include @@ -30,9 +30,9 @@ #include "SampleBuffer.h" namespace lmms { -auto SampleDatabase::fetch(const QString& path) -> std::shared_ptr +auto SampleCache::fetch(const QString& path) -> std::shared_ptr { - // To ensure that only the main thread is requesting samples from the database + // To ensure that only the main thread is requesting samples from the cache // Can be removed if proven that other threads are requesting samples besides the main thread assert(QThread::currentThread() == QCoreApplication::instance()->thread()); @@ -41,9 +41,9 @@ auto SampleDatabase::fetch(const QString& path) -> std::shared_ptr return get(entry, s_audioFileMap, path); } -auto SampleDatabase::fetch(const QString& base64, int sampleRate) -> std::shared_ptr +auto SampleCache::fetch(const QString& base64, int sampleRate) -> std::shared_ptr { - // To ensure that only the main thread is requesting samples from the database + // To ensure that only the main thread is requesting samples from the cache // Can be removed if proven that other threads are requesting samples besides the main thread assert(QThread::currentThread() == QCoreApplication::instance()->thread()); diff --git a/src/gui/SampleLoader.cpp b/src/gui/SampleLoader.cpp index 71fca0b5360..25d28a715a0 100644 --- a/src/gui/SampleLoader.cpp +++ b/src/gui/SampleLoader.cpp @@ -29,7 +29,7 @@ #include #include "GuiApplication.h" -#include "SampleDatabase.h" +#include "SampleCache.h" namespace lmms { @@ -46,7 +46,7 @@ std::shared_ptr SampleLoader::loadBufferFromFile(const QStri try { - return SampleDatabase::fetch(filePath); + return SampleCache::fetch(filePath); } catch (const std::runtime_error& error) { @@ -62,7 +62,7 @@ std::shared_ptr SampleLoader::loadBufferFromBase64(const QSt try { - return SampleDatabase::fetch(base64, sampleRate); + return SampleCache::fetch(base64, sampleRate); } catch (const std::runtime_error& error) { From 5810d3d9206a9941ce88e85ac65d9f6329aec966 Mon Sep 17 00:00:00 2001 From: saker Date: Tue, 12 Nov 2024 20:47:59 -0500 Subject: [PATCH 13/19] Remove ::gui suffix --- include/SampleLoader.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/SampleLoader.h b/include/SampleLoader.h index 147cfa913cc..d4dd52b2ea5 100644 --- a/include/SampleLoader.h +++ b/include/SampleLoader.h @@ -39,6 +39,6 @@ class LMMS_EXPORT SampleLoader static std::shared_ptr loadBufferFromBase64( const QString& base64, int sampleRate = Engine::audioEngine()->outputSampleRate()); }; -} // namespace lmms::gui +} // namespace lmms #endif // LMMS_SAMPLE_LOADER_H From f9d79684516f4af05d5739b65c5d54473cf646c2 Mon Sep 17 00:00:00 2001 From: saker Date: Wed, 20 Nov 2024 21:28:23 -0500 Subject: [PATCH 14/19] Improve get function --- include/SampleCache.h | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/include/SampleCache.h b/include/SampleCache.h index 9777ff759c1..6e5ccc1ace2 100644 --- a/include/SampleCache.h +++ b/include/SampleCache.h @@ -98,23 +98,16 @@ class SampleCache static auto get(const T& entry, std::unordered_map, Hash>& map, Args... args) { const auto it = map.find(entry); + const auto item = it == map.end() ? nullptr : it->second.lock(); - if (it == map.end()) + if (!item) { const auto buffer = std::make_shared(std::forward(args)...); - map.insert(std::make_pair(entry, buffer)); + map.emplace(entry, buffer); return buffer; } - const auto entryLock = it->second.lock(); - if (!entryLock) - { - const auto buffer = std::make_shared(std::forward(args)...); - map[entry] = buffer; - return buffer; - } - - return entryLock; + return item; } inline static std::unordered_map, Hash> s_audioFileMap; From 826cbf7ed7df9a2c3568596d5c192c979072019d Mon Sep 17 00:00:00 2001 From: saker Date: Thu, 21 Nov 2024 00:57:34 -0500 Subject: [PATCH 15/19] Use insert_or_assign instead of emplace and make args an rvalue reference --- include/SampleCache.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/SampleCache.h b/include/SampleCache.h index 6e5ccc1ace2..3e7e37054b3 100644 --- a/include/SampleCache.h +++ b/include/SampleCache.h @@ -95,7 +95,7 @@ class SampleCache }; template - static auto get(const T& entry, std::unordered_map, Hash>& map, Args... args) + static auto get(const T& entry, std::unordered_map, Hash>& map, Args&&... args) { const auto it = map.find(entry); const auto item = it == map.end() ? nullptr : it->second.lock(); @@ -103,7 +103,7 @@ class SampleCache if (!item) { const auto buffer = std::make_shared(std::forward(args)...); - map.emplace(entry, buffer); + map.insert_or_assign(std::move(entry), std::move(buffer)); return buffer; } From e9a5914a9fac065696854d7f30b6c6a6342b1191 Mon Sep 17 00:00:00 2001 From: saker Date: Thu, 21 Nov 2024 01:19:20 -0500 Subject: [PATCH 16/19] Do not move buffer into map We need to return it back to the caller as well --- include/SampleCache.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/SampleCache.h b/include/SampleCache.h index 3e7e37054b3..22a0debc958 100644 --- a/include/SampleCache.h +++ b/include/SampleCache.h @@ -103,7 +103,7 @@ class SampleCache if (!item) { const auto buffer = std::make_shared(std::forward(args)...); - map.insert_or_assign(std::move(entry), std::move(buffer)); + map.insert_or_assign(std::move(entry), buffer); return buffer; } From 8cd2472a4bccc5aa96e0e13a19ef53bf11b3cf62 Mon Sep 17 00:00:00 2001 From: saker Date: Thu, 21 Nov 2024 03:56:33 -0500 Subject: [PATCH 17/19] Are we there yet --- include/SampleCache.h | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/include/SampleCache.h b/include/SampleCache.h index 22a0debc958..2d71c4aa8a4 100644 --- a/include/SampleCache.h +++ b/include/SampleCache.h @@ -95,19 +95,18 @@ class SampleCache }; template - static auto get(const T& entry, std::unordered_map, Hash>& map, Args&&... args) + static auto get(T entry, std::unordered_map, Hash>& map, Args&&... args) { - const auto it = map.find(entry); - const auto item = it == map.end() ? nullptr : it->second.lock(); + auto& value = map[std::move(entry)]; + auto buffer = value.lock(); - if (!item) + if (!buffer) { - const auto buffer = std::make_shared(std::forward(args)...); - map.insert_or_assign(std::move(entry), buffer); - return buffer; + buffer = std::make_shared(std::forward(args)...); + value = buffer; } - return item; + return buffer; } inline static std::unordered_map, Hash> s_audioFileMap; From ffdf92ca8f88d69f6da5854c1927872f65ac433b Mon Sep 17 00:00:00 2001 From: saker Date: Thu, 21 Nov 2024 12:48:07 -0500 Subject: [PATCH 18/19] Update SampleCache.cpp Co-authored-by: Dalton Messmer --- src/core/SampleCache.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/SampleCache.cpp b/src/core/SampleCache.cpp index 9c4750ebf7f..5fd7f8a7f24 100644 --- a/src/core/SampleCache.cpp +++ b/src/core/SampleCache.cpp @@ -37,8 +37,8 @@ auto SampleCache::fetch(const QString& path) -> std::shared_ptr assert(QThread::currentThread() == QCoreApplication::instance()->thread()); const auto fsPath = PathUtil::pathFromQString(path); - const auto entry = AudioFileEntry{fsPath, std::filesystem::last_write_time(fsPath)}; - return get(entry, s_audioFileMap, path); + auto entry = AudioFileEntry{fsPath, std::filesystem::last_write_time(fsPath)}; + return get(std::move(entry), s_audioFileMap, path); } auto SampleCache::fetch(const QString& base64, int sampleRate) -> std::shared_ptr From 85f03e534a5a59094e74fbe1cfd16da8ea53cd39 Mon Sep 17 00:00:00 2001 From: saker Date: Thu, 21 Nov 2024 12:48:20 -0500 Subject: [PATCH 19/19] Update SampleCache.cpp Co-authored-by: Dalton Messmer --- src/core/SampleCache.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/SampleCache.cpp b/src/core/SampleCache.cpp index 5fd7f8a7f24..723c39142ae 100644 --- a/src/core/SampleCache.cpp +++ b/src/core/SampleCache.cpp @@ -47,7 +47,7 @@ auto SampleCache::fetch(const QString& base64, int sampleRate) -> std::shared_pt // Can be removed if proven that other threads are requesting samples besides the main thread assert(QThread::currentThread() == QCoreApplication::instance()->thread()); - const auto entry = Base64Entry{base64.toStdString(), sampleRate}; - return get(entry, s_base64Map, base64, sampleRate); + auto entry = Base64Entry{base64.toStdString(), sampleRate}; + return get(std::move(entry), s_base64Map, base64, sampleRate); } } // namespace lmms