diff --git a/include/LfoController.h b/include/LfoController.h index 1c63ba698..adf78abeb 100644 --- a/include/LfoController.h +++ b/include/LfoController.h @@ -86,7 +86,7 @@ protected: sample_t (*m_sampleFunction)( const float ); private: - SampleBuffer * m_userDefSampleBuffer; + std::shared_ptr m_userDefSampleBuffer; protected slots: void updatePhase(); diff --git a/include/Oscillator.h b/include/Oscillator.h index dab0b948d..74464d6bc 100644 --- a/include/Oscillator.h +++ b/include/Oscillator.h @@ -29,13 +29,14 @@ #include #include #include +#include "interpolation.h" #include "Engine.h" #include "lmms_constants.h" #include "lmmsconfig.h" #include "AudioEngine.h" #include "OscillatorConstants.h" -#include "SampleBuffer.h" +#include "SampleBuffer2.h" namespace lmms { @@ -46,7 +47,6 @@ class IntModel; class LMMS_EXPORT Oscillator { - MM_OPERATORS public: enum class WaveShape { @@ -91,14 +91,14 @@ public: static void waveTableInit(); static void destroyFFTPlans(); - static void generateAntiAliasUserWaveTable(SampleBuffer* sampleBuffer); + static std::unique_ptr generateAntiAliasUserWaveTable(const SampleBuffer2 *sampleBuffer); inline void setUseWaveTable(bool n) { m_useWaveTable = n; } - inline void setUserWave( const SampleBuffer * _wave ) + inline void setUserWave(std::shared_ptr _wave) { m_userWave = _wave; } @@ -164,9 +164,18 @@ public: return 1.0f - fast_rand() * 2.0f / FAST_RAND_MAX; } - inline sample_t userWaveSample( const float _sample ) const + static inline sample_t userWaveSample(const SampleBuffer2* buffer, const float sample) { - return m_userWave->userWaveSample( _sample ); + if (buffer == nullptr || buffer->size() == 0) { return 0; } + const auto frames = buffer->size(); + const auto frame = sample * frames; + auto f1 = static_cast(frame) % frames; + if (f1 < 0) + { + f1 += frames; + } + + return linearInterpolate(buffer->data()[f1][0], buffer->data()[(f1 + 1) % frames][0], fraction(frame)); } struct wtSampleControl { @@ -203,7 +212,7 @@ public: table[control.band][control.f2], fraction(control.frame)); } - inline sample_t wtSample(const std::unique_ptr& table, const float sample) const + inline sample_t wtSample(const OscillatorConstants::waveform_t* table, const float sample) const { assert(table != nullptr); wtSampleControl control = getWtSampleControl(sample); @@ -247,7 +256,8 @@ private: Oscillator * m_subOsc; float m_phaseOffset; float m_phase; - const SampleBuffer * m_userWave; + std::shared_ptr m_userWave; + std::shared_ptr m_userAntiAliasWaveTable; bool m_useWaveTable; // There are many update*() variants; the modulator flag is stored as a member variable to avoid // adding more explicit parameters to all of them. Can be converted to a parameter if needed. diff --git a/include/SampleBuffer2.h b/include/SampleBuffer2.h index f5869f02f..fbee21217 100644 --- a/include/SampleBuffer2.h +++ b/include/SampleBuffer2.h @@ -22,8 +22,8 @@ * */ -#ifndef LMMS_SAMPLE_BUFFER_H -#define LMMS_SAMPLE_BUFFER_H +#ifndef LMMS_SAMPLE_BUFFER2_H +#define LMMS_SAMPLE_BUFFER2_H #include #include @@ -80,4 +80,4 @@ private: } // namespace lmms -#endif // LMMS_SAMPLE_BUFFER_H +#endif // LMMS_SAMPLE_BUFFER2_H diff --git a/include/SampleLoader.h b/include/SampleLoader.h index 4a8ec7af1..d23fa0820 100644 --- a/include/SampleLoader.h +++ b/include/SampleLoader.h @@ -28,7 +28,7 @@ #include #include -#include "SampleBuffer.h" +#include "SampleBuffer2.h" #include "lmms_export.h" namespace lmms::gui { @@ -37,8 +37,8 @@ class LMMS_EXPORT SampleLoader public: static QString openAudioFile(const QString& previousFile = ""); static QString openWaveformFile(const QString& previousFile = ""); - static std::unique_ptr createBufferFromFile(const QString& filePath); - static std::unique_ptr createBufferFromBase64(const QString& base64, int sampleRate); + static std::unique_ptr createBufferFromFile(const QString& filePath); + static std::unique_ptr createBufferFromBase64(const QString& base64, int sampleRate); private: static void displayError(const QString& message); }; diff --git a/plugins/TripleOscillator/TripleOscillator.cpp b/plugins/TripleOscillator/TripleOscillator.cpp index f2340d3d6..c355b84e4 100644 --- a/plugins/TripleOscillator/TripleOscillator.cpp +++ b/plugins/TripleOscillator/TripleOscillator.cpp @@ -26,6 +26,7 @@ #include +#include "SampleLoader.h" #include "TripleOscillator.h" #include "AudioEngine.h" #include "AutomatableButton.h" @@ -92,7 +93,7 @@ OscillatorObject::OscillatorObject( Model * _parent, int _idx ) : tr( "Modulation type %1" ).arg( _idx+1 ) ), m_useWaveTableModel(true), - m_sampleBuffer( new SampleBuffer ), + m_sampleBuffer( new SampleBuffer2 ), m_volumeLeft( 0.0f ), m_volumeRight( 0.0f ), m_detuningLeft( 0.0f ), @@ -136,17 +137,17 @@ OscillatorObject::OscillatorObject( Model * _parent, int _idx ) : -OscillatorObject::~OscillatorObject() -{ - sharedObject::unref( m_sampleBuffer ); -} void OscillatorObject::oscUserDefWaveDblClick() { - QString af = m_sampleBuffer->openAndSetWaveformFile(); + QString af = gui::SampleLoader::openWaveformFile(); + auto buffer = gui::SampleLoader::createBufferFromFile(af); + // TODO C++20: Deprecated, use std::atomic instead + std::atomic_store(&m_sampleBuffer, std::shared_ptr(std::move(buffer))); + if( af != "" ) { // TODO: @@ -289,8 +290,10 @@ void TripleOscillator::loadSettings( const QDomElement & _this ) "modalgo" + QString::number( i+1 ) ); m_osc[i]->m_useWaveTableModel.loadSettings( _this, "useWaveTable" + QString::number (i+1 ) ); - m_osc[i]->m_sampleBuffer->setAudioFile( _this.attribute( - "userwavefile" + is ) ); + + auto buffer = gui::SampleLoader::createBufferFromFile(_this.attribute("userwavefile" + is)); + // TODO C++20: Deprecated, use std::atomic instead + std::atomic_store(&m_osc[i]->m_sampleBuffer, std::shared_ptr(std::move(buffer))); } } diff --git a/plugins/TripleOscillator/TripleOscillator.h b/plugins/TripleOscillator/TripleOscillator.h index f3290153b..c66028a98 100644 --- a/plugins/TripleOscillator/TripleOscillator.h +++ b/plugins/TripleOscillator/TripleOscillator.h @@ -29,6 +29,7 @@ #include "Instrument.h" #include "InstrumentView.h" #include "AutomatableModel.h" +#include "SampleBuffer2.h" namespace lmms { @@ -57,9 +58,6 @@ class OscillatorObject : public Model Q_OBJECT public: OscillatorObject( Model * _parent, int _idx ); - ~OscillatorObject() override; - - private: FloatModel m_volumeModel; FloatModel m_panModel; @@ -71,7 +69,7 @@ private: IntModel m_waveShapeModel; IntModel m_modulationAlgoModel; BoolModel m_useWaveTableModel; - SampleBuffer* m_sampleBuffer; + std::shared_ptr m_sampleBuffer; float m_volumeLeft; float m_volumeRight; diff --git a/src/core/LfoController.cpp b/src/core/LfoController.cpp index 23621b847..4cf0850eb 100644 --- a/src/core/LfoController.cpp +++ b/src/core/LfoController.cpp @@ -28,6 +28,7 @@ #include "LfoController.h" #include "AudioEngine.h" +#include "SampleLoader.h" #include "Song.h" @@ -48,7 +49,7 @@ LfoController::LfoController( Model * _parent ) : m_phaseOffset( 0 ), m_currentPhase( 0 ), m_sampleFunction( &Oscillator::sinSample ), - m_userDefSampleBuffer( new SampleBuffer ) + m_userDefSampleBuffer(std::make_shared()) { setSampleExact( true ); connect( &m_waveModel, SIGNAL(dataChanged()), @@ -74,7 +75,6 @@ LfoController::LfoController( Model * _parent ) : LfoController::~LfoController() { - sharedObject::unref( m_userDefSampleBuffer ); m_baseModel.disconnect( this ); m_speedModel.disconnect( this ); m_amountModel.disconnect( this ); @@ -107,7 +107,7 @@ void LfoController::updateValueBuffer() { const float currentSample = m_sampleFunction != nullptr ? m_sampleFunction( phase ) - : m_userDefSampleBuffer->userWaveSample( phase ); + : Oscillator::userWaveSample(m_userDefSampleBuffer.get(), phase); f = std::clamp(m_baseModel.value() + (*amountPtr * currentSample / 2.0f), 0.0f, 1.0f); @@ -211,7 +211,10 @@ void LfoController::loadSettings( const QDomElement & _this ) m_phaseModel.loadSettings( _this, "phase" ); m_waveModel.loadSettings( _this, "wave" ); m_multiplierModel.loadSettings( _this, "multiplier" ); - m_userDefSampleBuffer->setAudioFile( _this.attribute("userwavefile" ) ); + + auto buffer = gui::SampleLoader::createBufferFromFile(_this.attribute("userwavefile")); + // TODO C++20: Deprecated, use std::atomic instead + std::atomic_store(&m_userDefSampleBuffer, std::shared_ptr(std::move(buffer))); updateSampleFunction(); } diff --git a/src/core/Oscillator.cpp b/src/core/Oscillator.cpp index 06033b63e..2393040fa 100644 --- a/src/core/Oscillator.cpp +++ b/src/core/Oscillator.cpp @@ -182,19 +182,20 @@ void Oscillator::generateFromFFT(int bands, sample_t* table) normalize(s_sampleBuffer.data(), table, OscillatorConstants::WAVETABLE_LENGTH, 2*OscillatorConstants::WAVETABLE_LENGTH + 1); } -void Oscillator::generateAntiAliasUserWaveTable(SampleBuffer *sampleBuffer) +std::unique_ptr Oscillator::generateAntiAliasUserWaveTable(const SampleBuffer2* sampleBuffer) { - if (sampleBuffer->m_userAntiAliasWaveTable == nullptr) {return;} - + auto userAntiAliasWaveTable = std::make_unique(); for (int i = 0; i < OscillatorConstants::WAVE_TABLES_PER_WAVEFORM_COUNT; ++i) { for (int i = 0; i < OscillatorConstants::WAVETABLE_LENGTH; ++i) { - s_sampleBuffer[i] = sampleBuffer->userWaveSample((float)i / (float)OscillatorConstants::WAVETABLE_LENGTH); + s_sampleBuffer[i] = Oscillator::userWaveSample(sampleBuffer, (float)i / (float)OscillatorConstants::WAVETABLE_LENGTH); } fftwf_execute(s_fftPlan); - Oscillator::generateFromFFT(OscillatorConstants::MAX_FREQ / freqFromWaveTableBand(i), (*(sampleBuffer->m_userAntiAliasWaveTable))[i].data()); + Oscillator::generateFromFFT(OscillatorConstants::MAX_FREQ / freqFromWaveTableBand(i), (*(userAntiAliasWaveTable))[i].data()); } + + return userAntiAliasWaveTable; } @@ -807,13 +808,13 @@ template<> inline sample_t Oscillator::getSample( const float _sample ) { - if (m_useWaveTable && !m_isModulator) + if (m_useWaveTable && m_userAntiAliasWaveTable && !m_isModulator) { - return wtSample(m_userWave->m_userAntiAliasWaveTable, _sample); + return wtSample(m_userAntiAliasWaveTable.get(), _sample); } else { - return userWaveSample(_sample); + return userWaveSample(m_userWave.get(), _sample); } } diff --git a/src/core/SampleBuffer.cpp b/src/core/SampleBuffer.cpp index 775db125b..75d69bc95 100644 --- a/src/core/SampleBuffer.cpp +++ b/src/core/SampleBuffer.cpp @@ -361,7 +361,6 @@ void SampleBuffer::update(bool keepSettings) { m_userAntiAliasWaveTable = std::make_unique(); } - Oscillator::generateAntiAliasUserWaveTable(this); if (fileLoadError) { diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 9f940c035..24d97aa97 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/SampleLoader.cpp gui/SampleTrackWindow.cpp gui/SendButtonIndicator.cpp gui/SideBar.cpp diff --git a/src/gui/LfoControllerDialog.cpp b/src/gui/LfoControllerDialog.cpp index 77362b169..4a03be8e3 100644 --- a/src/gui/LfoControllerDialog.cpp +++ b/src/gui/LfoControllerDialog.cpp @@ -31,6 +31,7 @@ #include "Knob.h" #include "TempoSyncKnob.h" #include "PixmapButton.h" +#include "SampleLoader.h" namespace lmms::gui { @@ -210,9 +211,13 @@ LfoControllerDialog::~LfoControllerDialog() void LfoControllerDialog::askUserDefWave() { - SampleBuffer * sampleBuffer = dynamic_cast(this->model())-> - m_userDefSampleBuffer; - QString fileName = sampleBuffer->openAndSetWaveformFile(); + auto sampleBuffer = dynamic_cast(this->model())->m_userDefSampleBuffer; + QString fileName = SampleLoader::openWaveformFile(); + + auto buffer = SampleLoader::createBufferFromFile(fileName); + // TODO C++20: Deprecated, use std::atomic instead + std::atomic_store(&sampleBuffer, std::shared_ptr(std::move(buffer))); + if( fileName.isEmpty() == false ) { // TODO: diff --git a/src/gui/SampleLoader.cpp b/src/gui/SampleLoader.cpp index abcc046e9..4f3f7440e 100644 --- a/src/gui/SampleLoader.cpp +++ b/src/gui/SampleLoader.cpp @@ -86,11 +86,11 @@ QString SampleLoader::openWaveformFile(const QString& previousFile) previousFile.isEmpty() ? ConfigManager::inst()->factorySamplesDir() + "waveforms/10saw.flac" : previousFile); } -std::unique_ptr SampleLoader::createBufferFromFile(const QString& filePath) +std::unique_ptr SampleLoader::createBufferFromFile(const QString& filePath) { try { - return std::make_unique(filePath); + return std::make_unique(filePath); } catch (const std::runtime_error& error) { @@ -99,11 +99,11 @@ std::unique_ptr SampleLoader::createBufferFromFile(const QString& } } -std::unique_ptr SampleLoader::createBufferFromBase64(const QString& base64, int sampleRate) +std::unique_ptr SampleLoader::createBufferFromBase64(const QString& base64, int sampleRate) { try { - return std::make_unique(base64.toUtf8().toBase64(), sampleRate); + return std::make_unique(base64.toUtf8().toBase64(), sampleRate); } catch (const std::runtime_error& error) {