From ed0f288c8acb67dba32b48afc5e777fbfb11afb2 Mon Sep 17 00:00:00 2001 From: Sotonye Atemie Date: Sun, 4 Jan 2026 21:18:37 -0500 Subject: [PATCH] Remove `SampleLoader` (#8186) Removes `SampleLoader`. File dialog functions were moved into `FileDialog`. Creation functions were moved into `SampleBuffer`. --------- Co-authored-by: Dalton Messmer --- include/FileDialog.h | 2 + include/Sample.h | 2 - include/SampleBuffer.h | 8 +- include/SampleLoader.h | 48 ------- .../AudioFileProcessor/AudioFileProcessor.cpp | 5 +- .../AudioFileProcessorView.cpp | 4 +- plugins/SlicerT/SlicerT.cpp | 7 +- plugins/SlicerT/SlicerTView.cpp | 4 +- plugins/TripleOscillator/TripleOscillator.cpp | 8 +- src/core/EnvelopeAndLfoParameters.cpp | 3 +- src/core/LfoController.cpp | 3 +- src/core/Sample.cpp | 18 --- src/core/SampleBuffer.cpp | 94 +++++++++---- src/core/SampleClip.cpp | 5 +- src/core/SamplePlayHandle.cpp | 2 +- src/gui/CMakeLists.txt | 1 - src/gui/FileBrowser.cpp | 3 +- src/gui/LfoControllerDialog.cpp | 6 +- src/gui/SampleLoader.cpp | 125 ------------------ src/gui/clips/SampleClipView.cpp | 8 +- src/gui/instrument/EnvelopeAndLfoView.cpp | 5 +- src/gui/modals/FileDialog.cpp | 56 +++++++- src/gui/widgets/Graph.cpp | 6 +- 23 files changed, 159 insertions(+), 264 deletions(-) delete mode 100644 include/SampleLoader.h delete mode 100644 src/gui/SampleLoader.cpp diff --git a/include/FileDialog.h b/include/FileDialog.h index 67ef1d8eb..3e0147257 100644 --- a/include/FileDialog.h +++ b/include/FileDialog.h @@ -50,6 +50,8 @@ public: const QString &directory = QString(), const QString &filter = QString(), QString *selectedFilter = 0); + static QString openAudioFile(const QString& previousFile = ""); + static QString openWaveformFile(const QString& previousFile = ""); void clearSelection(); }; diff --git a/include/Sample.h b/include/Sample.h index b631e6d9e..c24505548 100644 --- a/include/Sample.h +++ b/include/Sample.h @@ -69,11 +69,9 @@ public: Sample() = default; - Sample(const QByteArray& base64, int sampleRate = Engine::audioEngine()->outputSampleRate()); Sample(const SampleFrame* data, size_t numFrames, int sampleRate = Engine::audioEngine()->outputSampleRate()); Sample(const Sample& other); Sample(Sample&& other) noexcept; - explicit Sample(const QString& audioFile); explicit Sample(std::shared_ptr buffer); auto operator=(const Sample&) -> Sample&; diff --git a/include/SampleBuffer.h b/include/SampleBuffer.h index 4e799c47c..1fe41985a 100644 --- a/include/SampleBuffer.h +++ b/include/SampleBuffer.h @@ -49,9 +49,7 @@ public: using const_reverse_iterator = std::vector::const_reverse_iterator; SampleBuffer() = default; - explicit SampleBuffer(const QString& audioFile); - SampleBuffer(const QString& base64, int sampleRate); - SampleBuffer(std::vector data, int sampleRate); + SampleBuffer(std::vector data, int sampleRate, const QString& audioFile = ""); SampleBuffer( const SampleFrame* data, size_t numFrames, int sampleRate = Engine::audioEngine()->outputSampleRate()); @@ -85,6 +83,10 @@ public: static auto emptyBuffer() -> std::shared_ptr; + static std::shared_ptr fromFile(const QString& path); + static std::shared_ptr fromBase64( + const QString& str, int sampleRate = Engine::audioEngine()->outputSampleRate()); + private: std::vector m_data; QString m_audioFile; diff --git a/include/SampleLoader.h b/include/SampleLoader.h deleted file mode 100644 index fd8f11357..000000000 --- a/include/SampleLoader.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * SampleLoader.h - Load audio and waveform files - * - * Copyright (c) 2023 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_LOADER_H -#define LMMS_GUI_SAMPLE_LOADER_H - -#include -#include - -#include "SampleBuffer.h" -#include "lmms_export.h" - -namespace lmms::gui { -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( - const QString& base64, int sampleRate = Engine::audioEngine()->outputSampleRate()); -private: - static void displayError(const QString& message); -}; -} // namespace lmms::gui - -#endif // LMMS_GUI_SAMPLE_LOADER_H diff --git a/plugins/AudioFileProcessor/AudioFileProcessor.cpp b/plugins/AudioFileProcessor/AudioFileProcessor.cpp index e78cdfdf4..96f358979 100644 --- a/plugins/AudioFileProcessor/AudioFileProcessor.cpp +++ b/plugins/AudioFileProcessor/AudioFileProcessor.cpp @@ -27,7 +27,6 @@ #include "InstrumentTrack.h" #include "PathUtil.h" -#include "SampleLoader.h" #include "Song.h" #include "LmmsTypes.h" @@ -225,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(SampleBuffer::fromBase64(sampleData)); } m_loopModel.loadSettings(elem, "looped"); @@ -319,7 +318,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(SampleBuffer::fromFile(_audio_file)); loopPointChanged(); reverseModelChanged(); emit sampleUpdated(); diff --git a/plugins/AudioFileProcessor/AudioFileProcessorView.cpp b/plugins/AudioFileProcessor/AudioFileProcessorView.cpp index 0658dc651..60fb8e4dd 100644 --- a/plugins/AudioFileProcessor/AudioFileProcessorView.cpp +++ b/plugins/AudioFileProcessor/AudioFileProcessorView.cpp @@ -31,9 +31,9 @@ #include "ComboBox.h" #include "DataFile.h" +#include "FileDialog.h" #include "FontHelper.h" #include "PixmapButton.h" -#include "SampleLoader.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 = FileDialog::openAudioFile(); if (af.isEmpty()) { return; } castModel()->setAudioFile(af); diff --git a/plugins/SlicerT/SlicerT.cpp b/plugins/SlicerT/SlicerT.cpp index 3b872ffaa..a22506f21 100644 --- a/plugins/SlicerT/SlicerT.cpp +++ b/plugins/SlicerT/SlicerT.cpp @@ -31,7 +31,6 @@ #include "Engine.h" #include "InstrumentTrack.h" #include "PathUtil.h" -#include "SampleLoader.h" #include "SlicerTView.h" #include "Song.h" #include "embed.h" @@ -307,7 +306,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 = SampleBuffer::fromFile(file)) { m_originalSample = Sample(std::move(buffer)); } findBPM(); findSlices(); @@ -352,7 +351,7 @@ void SlicerT::loadSettings(const QDomElement& element) { if (QFileInfo(PathUtil::toAbsolute(srcFile)).exists()) { - auto buffer = gui::SampleLoader::createBufferFromFile(srcFile); + auto buffer = SampleBuffer::fromFile(srcFile); m_originalSample = Sample(std::move(buffer)); } else @@ -363,7 +362,7 @@ void SlicerT::loadSettings(const QDomElement& element) } else if (auto sampleData = element.attribute("sampledata"); !sampleData.isEmpty()) { - auto buffer = gui::SampleLoader::createBufferFromBase64(sampleData); + auto buffer = SampleBuffer::fromBase64(sampleData); m_originalSample = Sample(std::move(buffer)); } diff --git a/plugins/SlicerT/SlicerTView.cpp b/plugins/SlicerT/SlicerTView.cpp index 83e7ac6ef..841dd735f 100644 --- a/plugins/SlicerT/SlicerTView.cpp +++ b/plugins/SlicerT/SlicerTView.cpp @@ -31,11 +31,11 @@ #include "Clipboard.h" #include "ComboBox.h" #include "DataFile.h" +#include "FileDialog.h" #include "InstrumentView.h" #include "Knob.h" #include "LcdSpinBox.h" #include "PixmapButton.h" -#include "SampleLoader.h" #include "SlicerT.h" #include "SlicerTWaveform.h" #include "StringPairDrag.h" @@ -158,7 +158,7 @@ void SlicerTView::exportMidi() void SlicerTView::openFiles() { - const auto audioFile = SampleLoader::openAudioFile(); + const auto audioFile = FileDialog::openAudioFile(); if (audioFile.isEmpty()) { return; } m_slicerTParent->updateFile(audioFile); } diff --git a/plugins/TripleOscillator/TripleOscillator.cpp b/plugins/TripleOscillator/TripleOscillator.cpp index a2d4ca199..1d28ba198 100644 --- a/plugins/TripleOscillator/TripleOscillator.cpp +++ b/plugins/TripleOscillator/TripleOscillator.cpp @@ -30,6 +30,7 @@ #include "AudioEngine.h" #include "AutomatableButton.h" #include "Engine.h" +#include "FileDialog.h" #include "InstrumentTrack.h" #include "Knob.h" #include "NotePlayHandle.h" @@ -37,7 +38,6 @@ #include "PathUtil.h" #include "PixmapButton.h" #include "SampleBuffer.h" -#include "SampleLoader.h" #include "Song.h" #include "embed.h" #include "plugin_export.h" @@ -136,10 +136,10 @@ OscillatorObject::OscillatorObject( Model * _parent, int _idx ) : void OscillatorObject::oscUserDefWaveDblClick() { - auto af = gui::SampleLoader::openWaveformFile(); + auto af = gui::FileDialog::openWaveformFile(); if( af != "" ) { - m_sampleBuffer = gui::SampleLoader::createBufferFromFile(af); + m_sampleBuffer = SampleBuffer::fromFile(af); m_userAntiAliasWaveTable = Oscillator::generateAntiAliasUserWaveTable(m_sampleBuffer.get()); // TODO: //m_usrWaveBtn->setToolTip(m_sampleBuffer->audioFile()); @@ -284,7 +284,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 = SampleBuffer::fromFile(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 a3c3bcf91..158c8f531 100644 --- a/src/core/EnvelopeAndLfoParameters.cpp +++ b/src/core/EnvelopeAndLfoParameters.cpp @@ -31,7 +31,6 @@ #include "Engine.h" #include "Oscillator.h" #include "PathUtil.h" -#include "SampleLoader.h" #include "Song.h" namespace lmms @@ -389,7 +388,7 @@ void EnvelopeAndLfoParameters::loadSettings( const QDomElement & _this ) { if (QFileInfo(PathUtil::toAbsolute(userWaveFile)).exists()) { - m_userWave = gui::SampleLoader::createBufferFromFile(_this.attribute("userwavefile")); + m_userWave = SampleBuffer::fromFile(_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 17290a5d3..097921e78 100644 --- a/src/core/LfoController.cpp +++ b/src/core/LfoController.cpp @@ -31,7 +31,6 @@ #include "AudioEngine.h" #include "Oscillator.h" #include "PathUtil.h" -#include "SampleLoader.h" #include "Song.h" namespace lmms @@ -244,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 = SampleBuffer::fromFile(_this.attribute("userwavefile")); } else { Engine::getSong()->collectError(QString("%1: %2").arg(tr("Sample not found"), userWaveFile)); } } diff --git a/src/core/Sample.cpp b/src/core/Sample.cpp index 81bc10ead..b535aa187 100644 --- a/src/core/Sample.cpp +++ b/src/core/Sample.cpp @@ -26,24 +26,6 @@ namespace lmms { -Sample::Sample(const QString& audioFile) - : m_buffer(std::make_shared(audioFile)) - , m_startFrame(0) - , m_endFrame(m_buffer->size()) - , m_loopStartFrame(0) - , m_loopEndFrame(m_buffer->size()) -{ -} - -Sample::Sample(const QByteArray& base64, int sampleRate) - : m_buffer(std::make_shared(base64, sampleRate)) - , m_startFrame(0) - , m_endFrame(m_buffer->size()) - , m_loopStartFrame(0) - , m_loopEndFrame(m_buffer->size()) -{ -} - Sample::Sample(const SampleFrame* data, size_t numFrames, int sampleRate) : m_buffer(std::make_shared(data, numFrames, sampleRate)) , m_startFrame(0) diff --git a/src/core/SampleBuffer.cpp b/src/core/SampleBuffer.cpp index 3283a7142..e04a63205 100644 --- a/src/core/SampleBuffer.cpp +++ b/src/core/SampleBuffer.cpp @@ -23,8 +23,12 @@ */ #include "SampleBuffer.h" + +#include +#include #include +#include "GuiApplication.h" #include "PathUtil.h" #include "SampleDecoder.h" @@ -36,35 +40,9 @@ SampleBuffer::SampleBuffer(const SampleFrame* data, size_t numFrames, int sample { } -SampleBuffer::SampleBuffer(const QString& audioFile) -{ - if (audioFile.isEmpty()) { throw std::runtime_error{"Failure loading audio file: Audio file path is empty."}; } - const auto absolutePath = PathUtil::toAbsolute(audioFile); - - if (auto decodedResult = SampleDecoder::decode(absolutePath)) - { - auto& [data, sampleRate] = *decodedResult; - m_data = std::move(data); - m_sampleRate = sampleRate; - m_audioFile = PathUtil::toShortestRelative(audioFile); - return; - } - - throw std::runtime_error{ - "Failed to decode audio file: Either the audio codec is unsupported, or the file is corrupted."}; -} - -SampleBuffer::SampleBuffer(const QString& base64, int sampleRate) - : m_sampleRate(sampleRate) -{ - // TODO: Replace with non-Qt equivalent - const auto bytes = QByteArray::fromBase64(base64.toUtf8()); - m_data.resize(bytes.size() / sizeof(SampleFrame)); - std::memcpy(reinterpret_cast(m_data.data()), bytes, m_data.size() * sizeof(SampleFrame)); -} - -SampleBuffer::SampleBuffer(std::vector data, int sampleRate) +SampleBuffer::SampleBuffer(std::vector data, int sampleRate, const QString& audioFile) : m_data(std::move(data)) + , m_audioFile(audioFile) , m_sampleRate(sampleRate) { } @@ -92,4 +70,64 @@ auto SampleBuffer::emptyBuffer() -> std::shared_ptr return s_buffer; } +std::shared_ptr SampleBuffer::fromFile(const QString& filePath) +{ + if (filePath.isEmpty()) { return SampleBuffer::emptyBuffer(); } + + const auto absolutePath = PathUtil::toAbsolute(filePath); + const auto storedPath = PathUtil::toShortestRelative(filePath); + + auto result = SampleDecoder::decode(absolutePath); + + if (!result) + { + // TODO: Improve error handling. We dont always want to show a message box on failure when there is a GUI (e.g. + // when loading the project), and this function also shouldn't be concerned with handling the error. + if (gui::getGUI()) + { + QMessageBox::warning(nullptr, QObject::tr("Failed to load sample"), + QObject::tr("The sample may be corrupted or unsupported.")); + } + else + { + qWarning() << QObject::tr( + "Failed to load sample at path %1, the file may not exist, be corrupted, or is unsupported.") + .arg(absolutePath); + } + + return SampleBuffer::emptyBuffer(); + } + + auto& [data, sampleRate] = *result; + return std::make_shared(std::move(data), sampleRate, storedPath); +} + +std::shared_ptr SampleBuffer::fromBase64(const QString& str, int sampleRate) +{ + if (str.isEmpty()) { return SampleBuffer::emptyBuffer(); } + + const auto bytes = QByteArray::fromBase64(str.toUtf8()); + + if (bytes.size() % sizeof(SampleFrame) != 0) + { + // TODO: Improve error handling. We dont always want to show a message box on failure when there is a GUI (e.g. + // when loading the project), and this function also shouldn't be concerned with handling the error. + if (gui::getGUI()) + { + QMessageBox::warning( + nullptr, QObject::tr("Failed to load sample"), QObject::tr("The sample size is invalid.")); + } + else + { + qWarning() << QObject::tr("Failed to load Base64 sample, invalid size"); + } + + return SampleBuffer::emptyBuffer(); + } + + auto data = std::vector(bytes.size() / sizeof(SampleFrame)); + std::memcpy(reinterpret_cast(data.data()), bytes, bytes.size()); + return std::make_shared(std::move(data), sampleRate); +} + } // namespace lmms diff --git a/src/core/SampleClip.cpp b/src/core/SampleClip.cpp index 9e0092b11..e279f7623 100644 --- a/src/core/SampleClip.cpp +++ b/src/core/SampleClip.cpp @@ -29,7 +29,6 @@ #include "PathUtil.h" #include "SampleClipView.h" -#include "SampleLoader.h" #include "SampleTrack.h" #include "Song.h" @@ -158,7 +157,7 @@ void SampleClip::setSampleFile(const QString& sf) setStartTimeOffset(0); if (!sf.isEmpty()) { - m_sample = Sample(gui::SampleLoader::createBufferFromFile(sf)); + m_sample = Sample(SampleBuffer::fromFile(sf)); updateLength(); } else @@ -321,7 +320,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 = SampleBuffer::fromBase64(_this.attribute("data"), sampleRate); m_sample = Sample(std::move(buffer)); } changeLength( _this.attribute( "len" ).toInt() ); diff --git a/src/core/SamplePlayHandle.cpp b/src/core/SamplePlayHandle.cpp index d5410e7f4..e4dc69b0a 100644 --- a/src/core/SamplePlayHandle.cpp +++ b/src/core/SamplePlayHandle.cpp @@ -56,7 +56,7 @@ SamplePlayHandle::SamplePlayHandle(Sample* sample, bool ownAudioBusHandle) : SamplePlayHandle::SamplePlayHandle( const QString& sampleFile ) : - SamplePlayHandle(new Sample(sampleFile), true) + SamplePlayHandle(new Sample(SampleBuffer::fromFile(sampleFile)), true) { } diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 030eab0c9..061d05eb4 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -35,7 +35,6 @@ SET(LMMS_SRCS gui/PluginBrowser.cpp gui/ProjectNotes.cpp gui/RowTableView.cpp - gui/SampleLoader.cpp gui/SampleTrackWindow.cpp gui/SampleThumbnail.cpp gui/SendButtonIndicator.cpp diff --git a/src/gui/FileBrowser.cpp b/src/gui/FileBrowser.cpp index 6b20d3f66..9106c7215 100644 --- a/src/gui/FileBrowser.cpp +++ b/src/gui/FileBrowser.cpp @@ -60,7 +60,6 @@ #include "PresetPreviewPlayHandle.h" #include "Sample.h" #include "SampleClip.h" -#include "SampleLoader.h" #include "SamplePlayHandle.h" #include "SampleTrack.h" #include "Song.h" @@ -767,7 +766,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 = SampleBuffer::fromFile(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 ae206ff0c..c16875fc3 100644 --- a/src/gui/LfoControllerDialog.cpp +++ b/src/gui/LfoControllerDialog.cpp @@ -24,6 +24,7 @@ */ +#include "FileDialog.h" #include "embed.h" @@ -32,7 +33,6 @@ #include "Knob.h" #include "TempoSyncKnob.h" #include "PixmapButton.h" -#include "SampleLoader.h" namespace lmms::gui { @@ -208,12 +208,12 @@ LfoControllerDialog::~LfoControllerDialog() void LfoControllerDialog::askUserDefWave() { - const auto fileName = SampleLoader::openWaveformFile(); + const auto fileName = FileDialog::openWaveformFile(); if (fileName.isEmpty()) { return; } auto lfoModel = dynamic_cast(model()); auto& buffer = lfoModel->m_userDefSampleBuffer; - buffer = SampleLoader::createBufferFromFile(fileName); + buffer = SampleBuffer::fromFile(fileName); m_userWaveBtn->setToolTip(buffer->audioFile()); } diff --git a/src/gui/SampleLoader.cpp b/src/gui/SampleLoader.cpp deleted file mode 100644 index 7bd67b6ba..000000000 --- a/src/gui/SampleLoader.cpp +++ /dev/null @@ -1,125 +0,0 @@ -/* - * SampleLoader.cpp - Static functions that open audio files - * - * Copyright (c) 2023 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 "SampleLoader.h" - -#include -#include -#include - -#include "ConfigManager.h" -#include "FileDialog.h" -#include "GuiApplication.h" -#include "PathUtil.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() ? QFileInfo(PathUtil::toAbsolute(previousFile)).absolutePath() : 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 SampleLoader::openWaveformFile(const QString& previousFile) -{ - return openAudioFile( - previousFile.isEmpty() ? ConfigManager::inst()->factorySamplesDir() + "waveforms/10saw.flac" : previousFile); -} - -std::shared_ptr SampleLoader::createBufferFromFile(const QString& filePath) -{ - if (filePath.isEmpty()) { return SampleBuffer::emptyBuffer(); } - - try - { - return std::make_shared(filePath); - } - catch (const std::runtime_error& error) - { - if (getGUI()) { displayError(QString::fromStdString(error.what())); } - return SampleBuffer::emptyBuffer(); - } -} - -std::shared_ptr SampleLoader::createBufferFromBase64(const QString& base64, int sampleRate) -{ - if (base64.isEmpty()) { return SampleBuffer::emptyBuffer(); } - - try - { - return std::make_shared(base64, sampleRate); - } - catch (const std::runtime_error& error) - { - if (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 diff --git a/src/gui/clips/SampleClipView.cpp b/src/gui/clips/SampleClipView.cpp index 1751949d2..f9aaa6468 100644 --- a/src/gui/clips/SampleClipView.cpp +++ b/src/gui/clips/SampleClipView.cpp @@ -28,12 +28,12 @@ #include #include +#include "FileDialog.h" #include "GuiApplication.h" #include "AutomationEditor.h" #include "embed.h" #include "PathUtil.h" #include "SampleClip.h" -#include "SampleLoader.h" #include "SampleThumbnail.h" #include "Song.h" #include "StringPairDrag.h" @@ -129,7 +129,7 @@ void SampleClipView::dropEvent( QDropEvent * _de ) } else if( StringPairDrag::decodeKey( _de ) == "sampledata" ) { - m_clip->setSampleBuffer(SampleLoader::createBufferFromBase64(StringPairDrag::decodeValue(_de))); + m_clip->setSampleBuffer(SampleBuffer::fromBase64(StringPairDrag::decodeValue(_de))); m_clip->updateLength(); update(); _de->accept(); @@ -188,13 +188,13 @@ void SampleClipView::mouseDoubleClickEvent( QMouseEvent * ) { if (m_trackView->trackContainerView()->knifeMode()) { return; } - const QString selectedAudioFile = SampleLoader::openAudioFile(); + const QString selectedAudioFile = FileDialog::openAudioFile(); if (selectedAudioFile.isEmpty()) { return; } if (!m_clip->hasSampleFileLoaded(selectedAudioFile)) { - auto sampleBuffer = SampleLoader::createBufferFromFile(selectedAudioFile); + auto sampleBuffer = SampleBuffer::fromFile(selectedAudioFile); if (sampleBuffer != SampleBuffer::emptyBuffer()) { m_clip->setSampleBuffer(sampleBuffer); diff --git a/src/gui/instrument/EnvelopeAndLfoView.cpp b/src/gui/instrument/EnvelopeAndLfoView.cpp index 9bf1e3b2a..6fd0e5a3a 100644 --- a/src/gui/instrument/EnvelopeAndLfoView.cpp +++ b/src/gui/instrument/EnvelopeAndLfoView.cpp @@ -33,7 +33,6 @@ #include "EnvelopeGraph.h" #include "LfoGraph.h" #include "EnvelopeAndLfoParameters.h" -#include "SampleLoader.h" #include "Knob.h" #include "LedCheckBox.h" #include "DataFile.h" @@ -242,7 +241,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 = SampleBuffer::fromFile(value); m_userLfoBtn->model()->setValue( true ); m_params->m_lfoWaveModel.setValue(static_cast(EnvelopeAndLfoParameters::LfoShape::UserDefinedWave)); _de->accept(); @@ -254,7 +253,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 = SampleBuffer::fromFile(file); m_userLfoBtn->model()->setValue( true ); m_params->m_lfoWaveModel.setValue(static_cast(EnvelopeAndLfoParameters::LfoShape::UserDefinedWave)); _de->accept(); diff --git a/src/gui/modals/FileDialog.cpp b/src/gui/modals/FileDialog.cpp index 5895c5214..6fc6f691c 100644 --- a/src/gui/modals/FileDialog.cpp +++ b/src/gui/modals/FileDialog.cpp @@ -30,6 +30,8 @@ #include #include "ConfigManager.h" +#include "PathUtil.h" +#include "SampleDecoder.h" #include "FileDialog.h" namespace lmms::gui @@ -142,5 +144,57 @@ void FileDialog::clearSelection() view->clearSelection(); } +QString FileDialog::openAudioFile(const QString& previousFile) +{ + auto openFileDialog = FileDialog(nullptr, tr("Open audio file")); + auto dir = !previousFile.isEmpty() ? QFileInfo(PathUtil::toAbsolute(previousFile)).absolutePath() : ConfigManager::inst()->userSamplesDir(); -} // namespace lmms::gui \ No newline at end of file + // change dir to position of previously opened file + openFileDialog.setDirectory(dir); + openFileDialog.setFileMode(QFileDialog::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(tr("%1 files").arg(name), displayExtension)); + allFileTypes.append(displayExtension); + } + + nameFilters.append(QString{"%1 (%2)"}.arg(tr("All audio files"), allFileTypes.join(" "))); + nameFilters.append(fileTypes); + nameFilters.append(QString("%1 (*)").arg(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 FileDialog::openWaveformFile(const QString& previousFile) +{ + return openAudioFile( + previousFile.isEmpty() ? ConfigManager::inst()->factorySamplesDir() + "waveforms/10saw.flac" : previousFile); +} + + +} // namespace lmms::gui diff --git a/src/gui/widgets/Graph.cpp b/src/gui/widgets/Graph.cpp index 4ce504400..359207338 100644 --- a/src/gui/widgets/Graph.cpp +++ b/src/gui/widgets/Graph.cpp @@ -27,7 +27,7 @@ #include "Graph.h" #include "DeprecationHelper.h" -#include "SampleLoader.h" +#include "FileDialog.h" #include "StringPairDrag.h" #include "Oscillator.h" @@ -585,10 +585,10 @@ void graphModel::setWaveToNoise() QString graphModel::setWaveToUser() { - QString fileName = gui::SampleLoader::openWaveformFile(); + QString fileName = gui::FileDialog::openWaveformFile(); if( fileName.isEmpty() == false ) { - auto sampleBuffer = gui::SampleLoader::createBufferFromFile(fileName); + auto sampleBuffer = SampleBuffer::fromFile(fileName); for( int i = 0; i < length(); i++ ) { m_samples[i] = Oscillator::userWaveSample(sampleBuffer.get(), i / static_cast(length()));