diff --git a/include/SampleClip.h b/include/SampleClip.h index c9e247328..d7abd1db7 100644 --- a/include/SampleClip.h +++ b/include/SampleClip.h @@ -25,7 +25,9 @@ #ifndef LMMS_SAMPLE_CLIP_H #define LMMS_SAMPLE_CLIP_H +#include #include "Clip.h" +#include "Sample.h" namespace lmms { @@ -52,7 +54,7 @@ public: SampleClip& operator=( const SampleClip& that ) = delete; void changeLength( const TimePos & _length ) override; - const QString & sampleFile() const; + QString sampleFile() const; void saveSettings( QDomDocument & _doc, QDomElement & _parent ) override; void loadSettings( const QDomElement & _this ) override; @@ -61,9 +63,9 @@ public: return "sampleclip"; } - SampleBuffer* sampleBuffer() + std::shared_ptr sample() { - return m_sampleBuffer; + return m_sample; } TimePos sampleLength() const; @@ -76,7 +78,7 @@ public: void setIsPlaying(bool isPlaying); public slots: - void setSampleBuffer( lmms::SampleBuffer* sb ); + void setSampleBuffer(SampleBuffer2* sb); void setSampleFile( const QString & _sf ); void updateLength(); void toggleRecord(); @@ -85,7 +87,7 @@ public slots: private: - SampleBuffer* m_sampleBuffer; + std::shared_ptr m_sample = std::make_shared(); BoolModel m_recordModel; bool m_isPlaying; diff --git a/include/SampleLoader.h b/include/SampleLoader.h index d23fa0820..b0a986edb 100644 --- a/include/SampleLoader.h +++ b/include/SampleLoader.h @@ -38,7 +38,7 @@ 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 createBufferFromBase64(const QString& base64, int sampleRate = Engine::audioEngine()->processingSampleRate()); private: static void displayError(const QString& message); }; diff --git a/include/SamplePlayHandle.h b/include/SamplePlayHandle.h index 31b4f0bd5..35b82c3d3 100644 --- a/include/SamplePlayHandle.h +++ b/include/SamplePlayHandle.h @@ -26,6 +26,7 @@ #ifndef LMMS_SAMPLE_PLAY_HANDLE_H #define LMMS_SAMPLE_PLAY_HANDLE_H +#include "Sample.h" #include "SampleBuffer.h" #include "AutomatableModel.h" #include "PlayHandle.h" @@ -43,7 +44,7 @@ class AudioPort; class LMMS_EXPORT SamplePlayHandle : public PlayHandle { public: - SamplePlayHandle( SampleBuffer* sampleBuffer , bool ownAudioPort = true ); + SamplePlayHandle(std::shared_ptr sampleBuffer , bool ownAudioPort = true); SamplePlayHandle( const QString& sampleFile ); SamplePlayHandle( SampleClip* clip ); ~SamplePlayHandle() override; @@ -81,11 +82,11 @@ public: private: - SampleBuffer * m_sampleBuffer; + std::shared_ptr m_sample; bool m_doneMayReturnTrue; f_cnt_t m_frame; - SampleBuffer::handleState m_state; + Sample::PlaybackState m_state; const bool m_ownAudioPort; diff --git a/include/SampleRecordHandle.h b/include/SampleRecordHandle.h index de1ca19ba..e24559021 100644 --- a/include/SampleRecordHandle.h +++ b/include/SampleRecordHandle.h @@ -29,6 +29,7 @@ #include #include "PlayHandle.h" +#include "SampleBuffer2.h" #include "TimePos.h" namespace lmms @@ -53,7 +54,7 @@ public: bool isFromTrack( const Track * _track ) const override; f_cnt_t framesRecorded() const; - void createSampleBuffer( SampleBuffer * * _sample_buf ); + void createSampleBuffer(SampleBuffer2** _sample_buf); private: diff --git a/src/core/SampleClip.cpp b/src/core/SampleClip.cpp index 592a63827..d88626094 100644 --- a/src/core/SampleClip.cpp +++ b/src/core/SampleClip.cpp @@ -28,6 +28,7 @@ #include "SampleBuffer.h" #include "SampleClipView.h" +#include "SampleLoader.h" #include "SampleTrack.h" #include "TimeLineWidget.h" @@ -38,7 +39,6 @@ namespace lmms SampleClip::SampleClip( Track * _track ) : Clip( _track ), - m_sampleBuffer( new SampleBuffer ), m_isPlaying( false ) { saveJournallingState( false ); @@ -90,10 +90,8 @@ SampleClip::SampleClip( Track * _track ) : SampleClip::SampleClip(const SampleClip& orig) : SampleClip(orig.getTrack()) { - // TODO: This creates a new SampleBuffer for the new Clip, eating up memory - // & eventually causing performance issues. Letting tracks share buffers - // when they're identical would fix this, but isn't possible right now. - *m_sampleBuffer = *orig.m_sampleBuffer; + // TODO C++20: Deprecated, use std::atomic instead + std::atomic_store(&m_sample, orig.m_sample); m_isPlaying = orig.m_isPlaying; } @@ -107,9 +105,6 @@ SampleClip::~SampleClip() { sampletrack->updateClips(); } - Engine::audioEngine()->requestChangeInModel(); - sharedObject::unref( m_sampleBuffer ); - Engine::audioEngine()->doneChangeInModel(); } @@ -123,19 +118,18 @@ void SampleClip::changeLength( const TimePos & _length ) -const QString & SampleClip::sampleFile() const +QString SampleClip::sampleFile() const { - return m_sampleBuffer->audioFile(); + return m_sample->buffer()->audioFile(); } -void SampleClip::setSampleBuffer( SampleBuffer* sb ) +void SampleClip::setSampleBuffer( SampleBuffer2* sb ) { - Engine::audioEngine()->requestChangeInModel(); - sharedObject::unref( m_sampleBuffer ); - Engine::audioEngine()->doneChangeInModel(); - m_sampleBuffer = sb; + // TODO C++20: Deprecated, use std::atomic instead + auto buffer = std::shared_ptr(sb); + std::atomic_store(&m_sample, std::make_shared(buffer)); updateLength(); emit sampleChanged(); @@ -154,7 +148,9 @@ void SampleClip::setSampleFile( const QString & _sf ) } else { //Otherwise set it to the sample's length - m_sampleBuffer->setAudioFile( _sf ); + auto buffer = gui::SampleLoader::createBufferFromFile(_sf); + // TODO C++20: Deprecated, use std::atomic instead + std::atomic_store(&m_sample, std::make_shared(std::move(buffer))); length = sampleLength(); } changeLength(length); @@ -225,7 +221,7 @@ void SampleClip::updateLength() TimePos SampleClip::sampleLength() const { - return (int)( m_sampleBuffer->frames() / Engine::framesPerTick() ); + return static_cast(m_sample->playbackSize() / Engine::framesPerTick()); } @@ -233,7 +229,7 @@ TimePos SampleClip::sampleLength() const void SampleClip::setSampleStartFrame(f_cnt_t startFrame) { - m_sampleBuffer->setStartFrame( startFrame ); + m_sample->setStartFrame(startFrame); } @@ -241,7 +237,7 @@ void SampleClip::setSampleStartFrame(f_cnt_t startFrame) void SampleClip::setSamplePlayLength(f_cnt_t length) { - m_sampleBuffer->setEndFrame( length ); + m_sample->setEndFrame(length); } @@ -264,15 +260,15 @@ void SampleClip::saveSettings( QDomDocument & _doc, QDomElement & _this ) if( sampleFile() == "" ) { QString s; - _this.setAttribute( "data", m_sampleBuffer->toBase64( s ) ); + _this.setAttribute("data", m_sample->buffer()->toBase64()); } - _this.setAttribute( "sample_rate", m_sampleBuffer->sampleRate()); + _this.setAttribute("sample_rate", m_sample->buffer()->sampleRate()); if( usesCustomClipColor() ) { _this.setAttribute( "color", color().name() ); } - if (m_sampleBuffer->reversed()) + if (m_sample->reversed()) { _this.setAttribute("reversed", "true"); } @@ -291,11 +287,12 @@ void SampleClip::loadSettings( const QDomElement & _this ) setSampleFile( _this.attribute( "src" ) ); if( sampleFile().isEmpty() && _this.hasAttribute( "data" ) ) { - m_sampleBuffer->loadFromBase64( _this.attribute( "data" ) ); - if (_this.hasAttribute("sample_rate")) - { - m_sampleBuffer->setSampleRate(_this.attribute("sample_rate").toInt()); - } + auto sampleRate = _this.hasAttribute("sample_rate") ? _this.attribute("sample_rate").toInt() : + Engine::audioEngine()->processingSampleRate(); + + auto buffer = gui::SampleLoader::createBufferFromBase64(_this.attribute("data"), sampleRate); + // TODO C++20: Deprecated, use std::atomic instead + std::atomic_store(&m_sample, std::make_shared(std::move(buffer))); } changeLength( _this.attribute( "len" ).toInt() ); setMuted( _this.attribute( "muted" ).toInt() ); @@ -313,7 +310,7 @@ void SampleClip::loadSettings( const QDomElement & _this ) if(_this.hasAttribute("reversed")) { - m_sampleBuffer->setReversed(true); + m_sample->setReversed(true); emit wasReversed(); // tell SampleClipView to update the view } } diff --git a/src/core/SamplePlayHandle.cpp b/src/core/SamplePlayHandle.cpp index ea27146cb..e7fc0c49b 100644 --- a/src/core/SamplePlayHandle.cpp +++ b/src/core/SamplePlayHandle.cpp @@ -35,9 +35,9 @@ namespace lmms { -SamplePlayHandle::SamplePlayHandle( SampleBuffer* sampleBuffer , bool ownAudioPort ) : +SamplePlayHandle::SamplePlayHandle(std::shared_ptr sample, bool ownAudioPort) : PlayHandle( Type::SamplePlayHandle ), - m_sampleBuffer( sharedObject::ref( sampleBuffer ) ), + m_sample(sample), m_doneMayReturnTrue( true ), m_frame( 0 ), m_ownAudioPort( ownAudioPort ), @@ -56,16 +56,15 @@ SamplePlayHandle::SamplePlayHandle( SampleBuffer* sampleBuffer , bool ownAudioPo SamplePlayHandle::SamplePlayHandle( const QString& sampleFile ) : - SamplePlayHandle( new SampleBuffer( sampleFile ) , true) + SamplePlayHandle(std::make_shared(sampleFile), true) { - sharedObject::unref( m_sampleBuffer ); } SamplePlayHandle::SamplePlayHandle( SampleClip* clip ) : - SamplePlayHandle( clip->sampleBuffer() , false) + SamplePlayHandle(clip->sample(), false) { m_track = clip->getTrack(); setAudioPort( ( (SampleTrack *)clip->getTrack() )->audioPort() ); @@ -76,7 +75,6 @@ SamplePlayHandle::SamplePlayHandle( SampleClip* clip ) : SamplePlayHandle::~SamplePlayHandle() { - sharedObject::unref( m_sampleBuffer ); if( m_ownAudioPort ) { delete audioPort(); @@ -110,12 +108,12 @@ void SamplePlayHandle::play( sampleFrame * buffer ) if( !( m_track && m_track->isMuted() ) && !(m_patternTrack && m_patternTrack->isMuted())) { -/* StereoVolumeVector v = +/* StereoVolumeVector v { { m_volumeModel->value() / DefaultVolume, m_volumeModel->value() / DefaultVolume } };*/ // SamplePlayHandle always plays the sample at its original pitch; // it is used only for previews, SampleTracks and the metronome. - if (!m_sampleBuffer->play(workingBuffer, &m_state, frames, DefaultBaseFreq)) + if (!m_sample->play(workingBuffer, &m_state, frames, DefaultBaseFreq)) { memset(workingBuffer, 0, frames * sizeof(sampleFrame)); } @@ -145,8 +143,8 @@ bool SamplePlayHandle::isFromTrack( const Track * _track ) const f_cnt_t SamplePlayHandle::totalFrames() const { - return ( m_sampleBuffer->endFrame() - m_sampleBuffer->startFrame() ) * - ( Engine::audioEngine()->processingSampleRate() / m_sampleBuffer->sampleRate() ); + return (m_sample->endFrame() - m_sample->startFrame()) * + (static_cast(Engine::audioEngine()->processingSampleRate()) / m_sample->buffer()->sampleRate()); } diff --git a/src/core/SampleRecordHandle.cpp b/src/core/SampleRecordHandle.cpp index 10e970b8f..05ea2915f 100644 --- a/src/core/SampleRecordHandle.cpp +++ b/src/core/SampleRecordHandle.cpp @@ -53,9 +53,9 @@ SampleRecordHandle::~SampleRecordHandle() { if( !m_buffers.empty() ) { - SampleBuffer* sb; + SampleBuffer2* sb; createSampleBuffer( &sb ); - m_clip->setSampleBuffer( sb ); + m_clip->setSampleBuffer(sb); } while( !m_buffers.empty() ) @@ -111,7 +111,7 @@ f_cnt_t SampleRecordHandle::framesRecorded() const -void SampleRecordHandle::createSampleBuffer( SampleBuffer** sampleBuf ) +void SampleRecordHandle::createSampleBuffer(SampleBuffer2** sampleBuf) { const f_cnt_t frames = framesRecorded(); // create buffer to store all recorded buffers in @@ -130,8 +130,7 @@ void SampleRecordHandle::createSampleBuffer( SampleBuffer** sampleBuf ) data_ptr += ( *it ).second; } // create according sample-buffer out of big buffer - *sampleBuf = new SampleBuffer( data, frames ); - ( *sampleBuf)->setSampleRate( Engine::audioEngine()->inputSampleRate() ); + *sampleBuf = new SampleBuffer2(data, frames, Engine::audioEngine()->inputSampleRate()); delete[] data; } diff --git a/src/gui/SampleLoader.cpp b/src/gui/SampleLoader.cpp index 4f3f7440e..e8fd87ba6 100644 --- a/src/gui/SampleLoader.cpp +++ b/src/gui/SampleLoader.cpp @@ -95,7 +95,7 @@ std::unique_ptr SampleLoader::createBufferFromFile(const QString& catch (const std::runtime_error& error) { displayError(QString::fromStdString(error.what())); - return nullptr; + return std::make_unique(); } } @@ -108,7 +108,7 @@ std::unique_ptr SampleLoader::createBufferFromBase64(const QStrin catch (const std::runtime_error& error) { displayError(QString::fromStdString(error.what())); - return nullptr; + return std::make_unique(); } } diff --git a/src/gui/clips/SampleClipView.cpp b/src/gui/clips/SampleClipView.cpp index e21a7e30b..d6edd0676 100644 --- a/src/gui/clips/SampleClipView.cpp +++ b/src/gui/clips/SampleClipView.cpp @@ -28,6 +28,7 @@ #include #include +#include "SampleLoader.h" #include "embed.h" #include "PathUtil.h" #include "SampleBuffer.h" @@ -60,8 +61,8 @@ void SampleClipView::updateSample() update(); // set tooltip to filename so that user can see what sample this // sample-clip contains - setToolTip(m_clip->m_sampleBuffer->audioFile() != "" ? - PathUtil::toAbsolute(m_clip->m_sampleBuffer->audioFile()) : + setToolTip(m_clip->m_sample->buffer()->audioFile() != "" ? + PathUtil::toAbsolute(m_clip->m_sample->buffer()->audioFile()) : tr( "Double-click to open sample" ) ); } @@ -112,8 +113,7 @@ void SampleClipView::dropEvent( QDropEvent * _de ) } else if( StringPairDrag::decodeKey( _de ) == "sampledata" ) { - m_clip->m_sampleBuffer->loadFromBase64( - StringPairDrag::decodeValue( _de ) ); + auto buffer = SampleLoader::createBufferFromBase64(StringPairDrag::decodeValue(_de)); m_clip->updateLength(); update(); _de->accept(); @@ -171,12 +171,12 @@ void SampleClipView::mouseReleaseEvent(QMouseEvent *_me) void SampleClipView::mouseDoubleClickEvent( QMouseEvent * ) { - QString af = m_clip->m_sampleBuffer->openAudioFile(); + QString af = gui::SampleLoader::openAudioFile(); if ( af.isEmpty() ) {} //Don't do anything if no file is loaded - else if ( af == m_clip->m_sampleBuffer->audioFile() ) + else if (af == m_clip->m_sample->buffer()->audioFile()) { //Instead of reloading the existing file, just reset the size - int length = (int) ( m_clip->m_sampleBuffer->frames() / Engine::framesPerTick() ); + int length = static_cast(m_clip->m_sample->buffer()->size() / Engine::framesPerTick()); m_clip->changeLength(length); } else @@ -263,9 +263,9 @@ void SampleClipView::paintEvent( QPaintEvent * pe ) float offset = m_clip->startTimeOffset() / ticksPerBar * pixelsPerBar(); QRect r = QRect( offset, spacing, qMax( static_cast( m_clip->sampleLength() * ppb / ticksPerBar ), 1 ), rect().bottom() - 2 * spacing ); - m_clip->m_sampleBuffer->visualize( p, r, pe->rect() ); + m_clip->m_sample->visualize(p, r); - QString name = PathUtil::cleanName(m_clip->m_sampleBuffer->audioFile()); + QString name = PathUtil::cleanName(m_clip->m_sample->buffer()->audioFile()); paintTextLabel(name, p); // disable antialiasing for borders, since its not needed @@ -318,7 +318,7 @@ void SampleClipView::paintEvent( QPaintEvent * pe ) void SampleClipView::reverseSample() { - m_clip->sampleBuffer()->setReversed(!m_clip->sampleBuffer()->reversed()); + m_clip->sample()->setReversed(!m_clip->sample()->reversed()); Engine::getSong()->setModified(); update(); } diff --git a/src/tracks/SampleTrack.cpp b/src/tracks/SampleTrack.cpp index 876cb307f..e661e0ebf 100644 --- a/src/tracks/SampleTrack.cpp +++ b/src/tracks/SampleTrack.cpp @@ -108,10 +108,10 @@ bool SampleTrack::play( const TimePos & _start, const fpp_t _frames, { if( sClip->isPlaying() == false && _start >= (sClip->startPosition() + sClip->startTimeOffset()) ) { - auto bufferFramesPerTick = Engine::framesPerTick (sClip->sampleBuffer ()->sampleRate ()); + auto bufferFramesPerTick = Engine::framesPerTick(sClip->sample()->buffer()->sampleRate()); f_cnt_t sampleStart = bufferFramesPerTick * ( _start - sClip->startPosition() - sClip->startTimeOffset() ); f_cnt_t clipFrameLength = bufferFramesPerTick * ( sClip->endPosition() - sClip->startPosition() - sClip->startTimeOffset() ); - f_cnt_t sampleBufferLength = sClip->sampleBuffer()->frames(); + f_cnt_t sampleBufferLength = sClip->sample()->buffer()->size(); //if the Clip smaller than the sample length we play only until Clip end //else we play the sample to the end but nothing more f_cnt_t samplePlayLength = clipFrameLength > sampleBufferLength ? sampleBufferLength : clipFrameLength;