Skip to content

Commit

Permalink
Implement sample caching (LMMS#6390)
Browse files Browse the repository at this point in the history
This implements a framework for the caching/sharing of samples.
Reintegration still needs to occur, so not everything is complete.
This is just to simplify the changes.

SampleBufferV2 is where samples and any pertaining data to that sample
is stored. From there, Sample's use that buffer but do not change it.
They do that by storing a std::shared_ptr<const SampleBufferV2>,
which gives read-only access to the buffer.

SampleBufferCache tracks the samples used in a project from a file or
from Base64. If something needs a certain sample that is tracked by
this cache, it will just create a std::shared_ptr from that tracked
sample.

The tracking is done with std::weak_ptr's within the SampleBufferCache.
  • Loading branch information
sakertooth committed May 19, 2022
1 parent 3964c53 commit 3b74f03
Show file tree
Hide file tree
Showing 18 changed files with 1,322 additions and 387 deletions.
9 changes: 7 additions & 2 deletions include/Engine.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
#include <QString>
#include <QObject>


#include "lmmsconfig.h"
#include "lmms_export.h"
#include "lmms_basics.h"
Expand All @@ -40,7 +39,7 @@ class PatternStore;
class ProjectJournal;
class Song;
class Ladspa2LMMS;

class SampleBufferCache;

// Note: This class is called 'LmmsCore' instead of 'Engine' because of naming
// conflicts caused by ZynAddSubFX. See https://github.com/LMMS/lmms/issues/2269
Expand Down Expand Up @@ -87,6 +86,11 @@ class LMMS_EXPORT LmmsCore : public QObject
return s_projectJournal;
}

static SampleBufferCache* sampleBufferCache()
{
return s_sampleBufferCache;
}

static bool ignorePluginBlacklist();

#ifdef LMMS_HAVE_LV2
Expand Down Expand Up @@ -143,6 +147,7 @@ class LMMS_EXPORT LmmsCore : public QObject
static AudioEngine *s_audioEngine;
static Mixer * s_mixer;
static Song * s_song;
static SampleBufferCache * s_sampleBufferCache;
static PatternStore * s_patternStore;
static ProjectJournal * s_projectJournal;

Expand Down
119 changes: 119 additions & 0 deletions include/Sample.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/*
* Sample.h - a SampleBuffer with its own characteristics
*
* Copyright (c) 2022 sakertooth <sakertooth@gmail.com>
*
* 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 SAMPLE_H
#define SAMPLE_H

#include <QObject>
#include <QPainter>
#include <QRect>
#include <QString>
#include <memory>
#include <samplerate.h>

#include "Note.h"
#include "SampleBufferCache.h"
#include "SampleBufferV2.h"
#include "lmms_basics.h"

class Sample
{
public:
enum class PlaybackType
{
Regular,
LoopPoints,
PingPong
};

Sample() = default;
Sample(const QString& strData, SampleBufferV2::StrDataType dataType);
Sample(const sampleFrame* data, const f_cnt_t numFrames);
explicit Sample(const SampleBufferV2* buffer);
explicit Sample(const f_cnt_t numFrames);

Sample(const Sample& other);
Sample& operator=(const Sample& other);

Sample(Sample&& other);
Sample& operator=(Sample&& other);

bool play(sampleFrame* dst, const fpp_t numFrames, const float freq);
void visualize(QPainter& painter, const QRect& drawingRect, f_cnt_t fromFrame = 0, f_cnt_t toFrame = 0);

QString sampleFile() const;
std::shared_ptr<const SampleBufferV2> sampleBuffer() const;
sample_rate_t sampleRate() const;
float amplification() const;
float frequency() const;
bool reversed() const;
bool varyingPitch() const;
int interpolationMode() const;
f_cnt_t startFrame() const;
f_cnt_t endFrame() const;
f_cnt_t loopStartFrame() const;
f_cnt_t loopEndFrame() const;
f_cnt_t frameIndex() const;
f_cnt_t numFrames() const;
PlaybackType playback() const;

void setSampleData(const QString& str, SampleBufferV2::StrDataType dataType);
void setSampleBuffer(const SampleBufferV2* buffer);
void setAmplification(float amplification);
void setFrequency(float frequency);
void setReversed(bool reversed);
void setVaryingPitch(bool varyingPitch);
void setInterpolationMode(int interpolationMode);
void setStartFrame(f_cnt_t start);
void setEndFrame(f_cnt_t end);
void setLoopStartFrame(f_cnt_t loopStart);
void setLoopEndFrame(f_cnt_t loopEnd);
void setFrameIndex(f_cnt_t frameIndex);
void setPlayback(PlaybackType playback);

void loadAudioFile(const QString& audioFile);
void loadBase64(const QString& base64);

void resetMarkers();
int calculateTickLength() const;
QString openSample();

private:
std::shared_ptr<const SampleBufferV2> m_sampleBuffer = nullptr;
float m_amplification = 1.0f;
float m_frequency = DefaultBaseFreq;
bool m_reversed = false;
bool m_varyingPitch = false;
bool m_pingPongBackwards = false;
int m_interpolationMode = SRC_LINEAR;
f_cnt_t m_startFrame = 0;
f_cnt_t m_endFrame = 0;
f_cnt_t m_loopStartFrame = 0;
f_cnt_t m_loopEndFrame = 0;
f_cnt_t m_frameIndex = 0;
PlaybackType m_playback = PlaybackType::Regular;
SRC_STATE* resampleState = nullptr;
};

#endif
44 changes: 44 additions & 0 deletions include/SampleBufferCache.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* SampleBufferCache.h - Used to cache sample buffers
*
* Copyright (c) 2022 sakertooth <sakertooth@gmail.com>
*
* 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 SAMPLE_BUFFER_CACHE_H
#define SAMPLE_BUFFER_CACHE_H

#include <QHash>
#include <QString>
#include <memory>

#include "SampleBufferV2.h"

class SampleBufferCache
{
public:
std::shared_ptr<const SampleBufferV2> get(const QString& id);
std::shared_ptr<const SampleBufferV2> add(const QString& id, const SampleBufferV2* buffer);

private:
QHash<QString, std::weak_ptr<const SampleBufferV2>> m_hash;
};

#endif
78 changes: 78 additions & 0 deletions include/SampleBufferV2.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* SampleBufferV2.h - container class for immutable sample data
*
* Copyright (c) 2022 sakertooth <sakertooth@gmail.com>
*
* 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 SAMPLE_BUFFER_V2_H
#define SAMPLE_BUFFER_V2_H

#include "Engine.h"
#include "AudioEngine.h"
#include "lmms_basics.h"

#include <QObject>
#include <QString>
#include <memory>
#include <vector>


class SampleBufferV2
{
public:
enum class StrDataType
{
AudioFile,
Base64
};

SampleBufferV2(const QString& strData, StrDataType dataType);
SampleBufferV2(const sampleFrame* data, const f_cnt_t numFrames);
explicit SampleBufferV2(const f_cnt_t numFrames);

SampleBufferV2(const SampleBufferV2& other) = delete;
SampleBufferV2& operator=(const SampleBufferV2& other) = delete;

SampleBufferV2(SampleBufferV2&& other);
SampleBufferV2& operator=(SampleBufferV2&& other);

const std::vector<sampleFrame>& sampleData() const;
sample_rate_t originalSampleRate() const;
f_cnt_t numFrames() const;

const QString& filePath() const;
bool hasFilePath() const;

QString toBase64() const;

private:
void loadFromAudioFile(const QString& audioFilePath);
void loadFromDrumSynthFile(const QString& drumSynthFilePath);
void loadFromBase64(const QString& str);
void resample(const sample_rate_t oldSampleRate, const sample_rate_t newSampleRate);

private:
std::vector<sampleFrame> m_sampleData;
sample_rate_t m_originalSampleRate;
QString m_filePath;
};

#endif
55 changes: 19 additions & 36 deletions include/SampleClip.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,72 +21,55 @@
* Boston, MA 02110-1301 USA.
*
*/

#ifndef SAMPLE_CLIP_H
#define SAMPLE_CLIP_H

#include "Clip.h"

class SampleBuffer;

#include "Sample.h"

class SampleClip : public Clip
{
Q_OBJECT
mapPropertyFromModel(bool,isRecord,setRecord,m_recordModel);
public:
SampleClip( Track * _track );
SampleClip( const SampleClip& orig );
virtual ~SampleClip();
mapPropertyFromModel(bool, isRecord, setRecord, m_recordModel);

SampleClip& operator=( const SampleClip& that ) = delete;
public:
explicit SampleClip(Track* _track);
~SampleClip() override;

void changeLength( const TimePos & _length ) override;
const QString & sampleFile() const;
void changeLength(const TimePos& _length) override;
QString sampleFile() const;

void saveSettings( QDomDocument & _doc, QDomElement & _parent ) override;
void loadSettings( const QDomElement & _this ) override;
inline QString nodeName() const override
{
return "sampleclip";
}
void saveSettings(QDomDocument& _doc, QDomElement& _parent) override;
void loadSettings(const QDomElement& _this) override;

SampleBuffer* sampleBuffer()
{
return m_sampleBuffer;
}
void loadSample(const QString& strData, SampleBufferV2::StrDataType dataType);

QString nodeName() const override;
Sample& sample();
TimePos sampleLength() const;
void setSampleStartFrame( f_cnt_t startFrame );
void setSamplePlayLength( f_cnt_t length );
ClipView * createView( TrackView * _tv ) override;


ClipView* createView(TrackView* _tv) override;

bool isPlaying() const;
void setIsPlaying(bool isPlaying);

std::unique_ptr<SampleClip> clone();

public slots:
void setSampleBuffer( SampleBuffer* sb );
void setSampleFile( const QString & _sf );
void updateLength();
void toggleRecord();
void playbackPositionChanged();
void updateTrackClips();


private:
SampleBuffer* m_sampleBuffer;
Sample m_sample;
BoolModel m_recordModel;
bool m_isPlaying;

friend class SampleClipView;


signals:
void sampleChanged();
void wasReversed();
} ;



};
#endif
Loading

0 comments on commit 3b74f03

Please sign in to comment.