Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
169 changes: 168 additions & 1 deletion include/SampleBuffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,85 @@ class QRect;
// may need to be higher - conversely, to optimize, some may work with lower values
const f_cnt_t MARGIN[] = { 64, 64, 64, 4, 4 };


class ControlPoint
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we have a separate header for that and CubicBezier? Maybe one for each is a overkill but we could get them both in a single separate header. I can see it being reused in other places like AutomationPattern for example!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. In the meantime I've noticed that @PhysSong has been working on Bezier curves for automation tracks in #4674. Maybe we can work on the Bezier header together?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if he is actively working on it at the moment, but as long as the Bezier header is done in a way that makes it usable by any other class that need to calculate Bezier curves it should be fine!

{
public:
ControlPoint(float x, float y)
{
this->setX(x);
this->setY(y);
}

void setX(float x)
{
m_x = adjustPoint(x);
}

void setY(float y)
{
m_y = adjustPoint(y);
}

const float x() const
{
return m_x;
}

const float y() const
{
return m_y;
}

private:
float m_x;
float m_y;
inline float adjustPoint(float val)
{
/*
Constrain the control points to lie in the interval [0.0, 1.0].
This ensures that the fade function has a unique value at each sample
and that the fade function can not go above 1.0 and below 0.0.
*/
return std::min(std::max(val, 0.0f), 1.0f);
}
};


class CubicBezier
{
/*
Very comprehensive overview of Bezier curves:
https://pomax.github.io/bezierinfo/
*/
public:
CubicBezier(float p1x, float p1y, float p2x, float p2y)
{
m_p1 = ControlPoint(p1x, p1y);
m_p2 = ControlPoint(p2x, p2y);
};

float evaluate(float);
ControlPoint m_p1 = ControlPoint(0.0f, 0.0f);
ControlPoint m_p2 = ControlPoint(0.0f, 0.0f);

private:
float cubicBezier(float t, float p1, float p2);
float cubicBezierDerivative(float t, float p1, float p2);
float cubicBezierLastSegmentP1(float t, float p1, float p2);
float cubicBezierLastSegmentP2(float t, float p1, float p2);
// Solve with Newton's method: Very fast, but t might lie outside [0.0, 1.0]
float solveXforTNewton(float x, bool useCache);
// Solve with Bisection method: Slow, but t is guaranteed to lie inside [0.0, 1.0]
float solveXforTBisection(float x);
float solveXforT(float x)
{
return solveXforTBisection(x);
}
float m_tCache = -1.0f;
};


class LMMS_EXPORT SampleBuffer : public QObject, public sharedObject
{
Q_OBJECT
Expand Down Expand Up @@ -273,6 +352,75 @@ class LMMS_EXPORT SampleBuffer : public QObject, public sharedObject
}


// Fading related members

float fadingVal(f_cnt_t pos, bool isLeft);

void setTcoStartTime(float startTime)
{
m_TcoStartTime = startTime;
}

void setTcoFrameLength(float len)
{
m_TcoFrameLength = len;
}

float leftFaderPos()
{
return m_leftFader;
}

float rightFaderPos()
{
return m_rightFader;
}

ControlPoint getLeftP1()
{
return m_leftBezierFade.m_p1;
}

ControlPoint getLeftP2()
{
return m_leftBezierFade.m_p2;
}

ControlPoint getRightP1()
{
return m_rightBezierFade.m_p1;
}

ControlPoint getRightP2()
{
return m_rightBezierFade.m_p2;
}

void setLeftP1(float x, float y)
{
m_leftBezierFade.m_p1 = ControlPoint(x, y);
}

void setLeftP2(float x, float y)
{
m_leftBezierFade.m_p2 = ControlPoint(x, y);
}

void setRightP1(float x, float y)
{
m_rightBezierFade.m_p1 = ControlPoint(1.0f - x, y);
}

void setRightP2(float x, float y)
{
m_rightBezierFade.m_p2 = ControlPoint(1.0f - x, y);
}

void setLeftFader(float lfader);
void setRightFader(float rfader);
void checkFadingActive();


public slots:
void setAudioFile(const QString & audioFile);
void loadFromBase64(const QString & data);
Expand Down Expand Up @@ -340,11 +488,30 @@ public slots:
f_cnt_t getLoopedIndex(f_cnt_t index, f_cnt_t startf, f_cnt_t endf) const;
f_cnt_t getPingPongIndex(f_cnt_t index, f_cnt_t startf, f_cnt_t endf) const;

// Fading related members
bool m_fading = false;
float m_leftFader = 0.0f;
float m_rightFader = 1.0f;

f_cnt_t m_cachePos = -mixerSampleRate();
float m_cacheAmp = 1.0f;


CubicBezier m_leftBezierFade = CubicBezier(0.5f, 0.0f, 0.5f, 1.0f);
CubicBezier m_rightBezierFade = CubicBezier(0.5f, 0.0f, 0.5f, 1.0f);

float bezierFade(float, bool);
typedef float(SampleBuffer::*fadingFunc)(float, bool);
fadingFunc fadefuncPtr = &SampleBuffer::bezierFade;
float fadefunc(f_cnt_t);

float m_TcoStartTime = 0.0f;
float m_TcoFrameLength = 0.0f;


signals:
void sampleUpdated();

} ;


#endif
46 changes: 44 additions & 2 deletions include/SampleTrack.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@

#include <QDialog>
#include <QLayout>
#include <memory>

#include "AudioPort.h"
#include "FadeButton.h"
Expand All @@ -39,6 +40,7 @@
class EffectRackView;
class Knob;
class SampleBuffer;
class ControlPoint;
class SampleTrackWindow;
class TrackLabelButton;
class QLineEdit;
Expand Down Expand Up @@ -79,6 +81,8 @@ class SampleTCO : public TrackContentObject
bool isPlaying() const;
void setIsPlaying(bool isPlaying);

void setStartTimeOffset(const TimePos &startTimeOffset) override;

public slots:
void setSampleBuffer( SampleBuffer* sb );
void setSampleFile( const QString & _sf );
Expand All @@ -92,7 +96,12 @@ public slots:
SampleBuffer* m_sampleBuffer;
BoolModel m_recordModel;
bool m_isPlaying;
bool m_changed = false;
TimePos m_lenOld;
TimePos m_startTimeOffsetOld;

void saveFaderSettings(QDomElement&);
void loadFaderSettings(const QDomElement&);

friend class SampleTCOView;

Expand All @@ -116,22 +125,55 @@ class SampleTCOView : public TrackContentObjectView
public slots:
void updateSample();
void reverseSample();


void setSFade();
void setCosFade();
void setLinearFade();
void setManualFade();

protected:
void contextMenuEvent( QContextMenuEvent * _cme ) override;
void mousePressEvent( QMouseEvent * _me ) override;
void mouseReleaseEvent( QMouseEvent * _me ) override;
void mouseMoveEvent(QMouseEvent* e) override;
void dragEnterEvent( QDragEnterEvent * _dee ) override;
void dropEvent( QDropEvent * _de ) override;
void mouseDoubleClickEvent( QMouseEvent * ) override;
void paintEvent( QPaintEvent * ) override;
void enterEvent(QEvent *e) override;
void leaveEvent(QEvent *e) override;


private:
SampleTCO * m_tco;
QPixmap m_paintPixmap;

// Members related to fading
bool m_moveLeftCorner = false;
bool m_moveRightCorner = false;
bool m_leftFadeClicked = false;
bool m_rightFadeClicked = false;
bool m_leftFadeManual = false;
bool m_rightFadeManual = false;
std::unique_ptr<QRectF> m_leftHandle;
std::unique_ptr<QRectF> m_rightHandle;
std::unique_ptr<QRectF> m_leftP1;
std::unique_ptr<QRectF> m_leftP2;
std::unique_ptr<QRectF> m_rightP1;
std::unique_ptr<QRectF> m_rightP2;
bool m_moveLeftP1 = false;
bool m_moveLeftP2 = false;
bool m_moveRightP1 = false;
bool m_moveRightP2 = false;
QVector<QPointF> m_leftPath;
QVector<QPointF> m_rightPath;
bool m_inside = false;
void drawFadePoints(bool drawLeft, QVector<QPointF>& points);
void drawFades();
void checkCornersClicked(const QMouseEvent *e);
void checkFadesClicked(const QMouseEvent *e);
void mouseMoveFadingPos(const QMouseEvent *e);
void mouseMoveLeftControlPoints(const QMouseEvent *e);
void mouseMoveRightControlPoints(const QMouseEvent *e);
} ;


Expand Down
2 changes: 1 addition & 1 deletion include/TrackContentObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ class LMMS_EXPORT TrackContentObject : public Model, public JournallingObject
static bool comparePosition(const TrackContentObject* a, const TrackContentObject* b);

TimePos startTimeOffset() const;
void setStartTimeOffset( const TimePos &startTimeOffset );
virtual void setStartTimeOffset(const TimePos &startTimeOffset);

void updateColor();

Expand Down
1 change: 1 addition & 0 deletions include/TrackContentObjectView.h
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ public slots:
}

float pixelsPerBar();
int m_textLabelHeight;


DataFile createTCODataFiles(const QVector<TrackContentObjectView *> & tcos) const;
Expand Down
Loading