Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add sample thumbnails #7366

Open
wants to merge 37 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
5ad7845
Experimental sample thumbnail
khoidauminh Apr 23, 2024
795ad7f
Rename some classes and type aliases. Make some type declarations exp…
khoidauminh Apr 25, 2024
140006b
Use a combination of audioFile name and shared_ptrs to track samples;…
khoidauminh Apr 25, 2024
14d1e91
That weird line at the end of the sample is now gone
khoidauminh Apr 25, 2024
71284b0
Small changes to the code; Add comments
khoidauminh Apr 26, 2024
4553283
Add missing word to comment; Fix typo
khoidauminh Apr 26, 2024
7a6e2aa
Track `SharedSampleThumbnailList`s instead
khoidauminh May 2, 2024
f281961
Major refactor; implement thumbnailing for SlicerT, AFP and Automatio…
khoidauminh Jul 3, 2024
e03dacd
Code clean up, renames and documenting
khoidauminh Jul 3, 2024
4a1689e
Merge branch 'LMMS:master' into sample-thumbnail
khoidauminh Jul 4, 2024
82c51e0
Add the namespace lmms comments
khoidauminh Jul 4, 2024
35589f9
More code updates and documentation
khoidauminh Jul 4, 2024
92b9c15
Fix error in comment
khoidauminh Jul 4, 2024
fa24cfe
Comment out `qDebug`s
khoidauminh Jul 5, 2024
101199e
Fix formatting
khoidauminh Jul 5, 2024
b903d9f
Use alternative initialization of `SampleThumbnailVisualizeParameters`
khoidauminh Jul 5, 2024
7a393ef
Remove commented code
khoidauminh Jul 12, 2024
4e39c16
Fix style and simplify code
sakertooth Sep 10, 2024
9880d83
Use auto
sakertooth Sep 10, 2024
69cc6ba
Draw lines using floating point
sakertooth Sep 10, 2024
d7f89ac
Merge the classes into one nested class
sakertooth Sep 10, 2024
9605dec
Fix comparison of different signedness
sakertooth Sep 10, 2024
7f3d43f
Include memory header
sakertooth Sep 10, 2024
3c41384
Fix a logic error when selecting samples; Rename a const
khoidauminh Sep 10, 2024
5f70d59
Fix more issues with signedness
sakertooth Sep 10, 2024
4f9f353
Fix sample drawing error in `visualizeOriginal`
khoidauminh Sep 11, 2024
12bc9cc
Only render regions in view of the sample
khoidauminh Sep 11, 2024
0a210ee
Allow partial repaints of sample clips
khoidauminh Sep 11, 2024
d6a3638
Remove unused variable
khoidauminh Sep 11, 2024
305baee
Limit most of the painting to the visible region
khoidauminh Sep 11, 2024
9392493
Revert back to using rect() in some places
khoidauminh Sep 11, 2024
4bc9dae
Partial rendering for AutomationEditor
khoidauminh Sep 11, 2024
5cd947b
Don't redraw painted regions; allowHighResolution; remove `visualizeO…
khoidauminh Sep 12, 2024
daf45cc
Add s_sampleThumbnailCacheMap back for testing convenience
khoidauminh Sep 12, 2024
2c2202f
Minor change to the way `thumbnailSizeDivisor` is calculated
khoidauminh Sep 12, 2024
0317e80
Extend update region by an amount
khoidauminh Sep 12, 2024
bfb5367
forgot about this
khoidauminh Sep 12, 2024
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
3 changes: 3 additions & 0 deletions include/AutomationEditor.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
#include "SampleClip.h"
#include "TimePos.h"
#include "lmms_basics.h"
#include "SampleThumbnail.h"

class QPainter;
class QPixmap;
Expand Down Expand Up @@ -288,6 +289,8 @@ protected slots:
QColor m_ghostNoteColor;
QColor m_detuningNoteColor;
QColor m_ghostSampleColor;

SampleThumbnail m_sampleThumbnail;

friend class AutomationEditorWindow;

Expand Down
4 changes: 4 additions & 0 deletions include/SampleClipView.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@

#include "ClipView.h"

#include "SampleThumbnail.h"

namespace lmms
{

Expand Down Expand Up @@ -63,7 +65,9 @@ public slots:

private:
SampleClip * m_clip;
SampleThumbnail m_sampleThumbnail;
QPixmap m_paintPixmap;
QRect m_paintPixmapDrawnRegion;
bool splitClip( const TimePos pos ) override;
} ;

Expand Down
121 changes: 121 additions & 0 deletions include/SampleThumbnail.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/*
* SampleThumbnail.h
*
* Copyright (c) 2024 Khoi Dau <casboi86@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 LMMS_SAMPLE_THUMBNAIL_H
#define LMMS_SAMPLE_THUMBNAIL_H

#include <QPainter>
#include <memory>

#include "lmms_export.h"

namespace lmms {

class Sample;
class SampleFrame;

/**
Insert this into your class when you want to implement
thumbnails. This is optional, but without this class, there will be no
indication that this thumbnail list is being used and it will be destroyed
when you generate thumbnails somewhere else, and have to be regenerated
before being rendered, which is slower than rendering the sample directly.
*/
class LMMS_EXPORT SampleThumbnail
{
public:
static constexpr auto MinThumbnailSize = 1;
static constexpr auto MaxThumbnailSize = 32768;

struct Bit
{
Bit() = default;
Bit(const SampleFrame&);

void merge(const Bit&);
void merge(const SampleFrame&);

float max = -100.0f;
float min = 100.0f;
float rms = 0.0f;
};

struct VisualizeParameters
{
bool allowHighResolution = false;

float amplification = 1.0f; //!< The amount of amplification to apply to the waveform.
bool reversed = false; //!< Determines if the waveform is drawn in reverse or not.

float sampleStart = 0.0f; //!< Where the sample begins for drawing.
float sampleEnd = 1.0f; //!< Where the sample ends for drawing.

long x = 0; //!< Starting X position for the waveform.
long y = 0; //!< Starting Y position for the waveform.

/**
If the left side of the sample clip is out of view,
this field will specify the x position of left most
pixels of the sample clip that's still in view.
*/
long viewX = 0;

long width = 0; //!< The width of the rectangle to draw into.
long height = 0; //!< The height of the rectangle to draw into.

/**
Song editor clips shorter than the sample length (measuring
from the start of the sample) can specify this field so
rendering cuts off early, reducing computation cost.
*/
long clipWidthSinceSampleStart = std::numeric_limits<long>::max();
};

using Thumbnail = std::vector<Bit>;
using ThumbnailCache = std::vector<Thumbnail>;

SampleThumbnail() = default;
SampleThumbnail(const Sample& sample);

void visualize(const VisualizeParameters& parameters, QPainter& painter) const;
void visualizeOriginal(const VisualizeParameters& parameters, QPainter& painter) const;

bool selectFromGlobalThumbnailMap(const Sample&);
static void cleanUpGlobalThumbnailMap();

private:
static void draw(QPainter& painter, const Bit& bit, float lineX, int centerY, float scalingFactor,
const QColor& color, const QColor& rmsColor);

static Thumbnail generate(const size_t thumbnailSize, const SampleFrame* buffer, const size_t size);

std::shared_ptr<ThumbnailCache> m_thumbnailCache = nullptr;

/* DEPRECATED; functionality is kept for testing conveniences */
inline static std::map<const QString, std::shared_ptr<ThumbnailCache>> s_sampleThumbnailCacheMap;
};

} // namespace lmms

#endif // LMMS_SAMPLE_THUMBNAIL_H
24 changes: 18 additions & 6 deletions plugins/AudioFileProcessor/AudioFileProcessorWaveView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@
#include "AudioFileProcessorWaveView.h"

#include "ConfigManager.h"
#include "Sample.h"
#include "gui_templates.h"
#include "SampleWaveform.h"
#include "SampleThumbnail.h"

#include <QPainter>
#include <QMouseEvent>
Expand Down Expand Up @@ -81,7 +82,8 @@ AudioFileProcessorWaveView::AudioFileProcessorWaveView(QWidget* parent, int w, i
m_isDragging(false),
m_reversed(false),
m_framesPlayed(0),
m_animation(ConfigManager::inst()->value("ui", "animateafp").toInt())
m_animation(ConfigManager::inst()->value("ui", "animateafp").toInt()),
m_sampleThumbnail(*buf)
{
setFixedSize(w, h);
setMouseTracking(true);
Expand Down Expand Up @@ -333,10 +335,20 @@ void AudioFileProcessorWaveView::updateGraph()
QPainter p(&m_graph);
p.setPen(QColor(255, 255, 255));

const auto rect = QRect{0, 0, m_graph.width(), m_graph.height()};
const auto waveform = SampleWaveform::Parameters{
m_sample->data() + m_from, static_cast<size_t>(range()), m_sample->amplification(), m_sample->reversed()};
SampleWaveform::visualize(waveform, p, rect);
m_sampleThumbnail = SampleThumbnail{*m_sample};

auto param = SampleThumbnail::VisualizeParameters{};
param.allowHighResolution = true;
param.amplification = m_sample->amplification();
param.reversed = m_sample->reversed();
param.sampleStart = static_cast<float>(m_from) / m_sample->sampleSize();
param.sampleEnd = static_cast<float>(m_to) / m_sample->sampleSize();
param.x = 0;
param.y = 0;
param.width = m_graph.width();
param.height = m_graph.height();

m_sampleThumbnail.visualize(param, p);
}

void AudioFileProcessorWaveView::zoom(const bool out)
Expand Down
2 changes: 2 additions & 0 deletions plugins/AudioFileProcessor/AudioFileProcessorWaveView.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@


#include "Knob.h"
#include "SampleThumbnail.h"


namespace lmms
Expand Down Expand Up @@ -142,6 +143,7 @@ public slots:
bool m_reversed;
f_cnt_t m_framesPlayed;
bool m_animation;
SampleThumbnail m_sampleThumbnail;

friend class AudioFileProcessorView;

Expand Down
39 changes: 30 additions & 9 deletions plugins/SlicerT/SlicerTWaveform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

#include <QBitmap>

#include "SampleWaveform.h"
#include "SampleThumbnail.h"
#include "SlicerT.h"
#include "SlicerTView.h"
#include "embed.h"
Expand Down Expand Up @@ -90,9 +90,17 @@ void SlicerTWaveform::drawSeekerWaveform()
brush.setPen(s_waveformColor);

const auto& sample = m_slicerTParent->m_originalSample;
const auto waveform = SampleWaveform::Parameters{sample.data(), sample.sampleSize(), sample.amplification(), sample.reversed()};
const auto rect = QRect(0, 0, m_seekerWaveform.width(), m_seekerWaveform.height());
SampleWaveform::visualize(waveform, brush, rect);

m_sampleThumbnail = SampleThumbnail{sample};

auto param = SampleThumbnail::VisualizeParameters{};
param.amplification = sample.amplification();
param.reversed = sample.reversed();
param.x = 0;
param.y = 0;
param.width = m_seekerWaveform.width();
param.height = m_seekerWaveform.height();
m_sampleThumbnail.visualize(param, brush);

// increase brightness in inner color
QBitmap innerMask = m_seekerWaveform.createMaskFromColor(s_waveformMaskColor, Qt::MaskMode::MaskOutColor);
Expand Down Expand Up @@ -141,15 +149,28 @@ void SlicerTWaveform::drawEditorWaveform()

QPainter brush(&m_editorWaveform);
size_t startFrame = m_seekerStart * m_slicerTParent->m_originalSample.sampleSize();
size_t endFrame = m_seekerEnd * m_slicerTParent->m_originalSample.sampleSize();
size_t endFrame = m_seekerEnd * m_slicerTParent->m_originalSample.sampleSize();

brush.setPen(s_waveformColor);
float zoomOffset = (m_editorHeight - m_zoomLevel * m_editorHeight) / 2;
long zoomOffset = (m_editorHeight - m_zoomLevel * m_editorHeight) / 2;

// Visualize
const auto& sample = m_slicerTParent->m_originalSample;
const auto waveform = SampleWaveform::Parameters{sample.data() + startFrame, endFrame - startFrame, sample.amplification(), sample.reversed()};
const auto rect = QRect(0, zoomOffset, m_editorWidth, m_zoomLevel * m_editorHeight);
SampleWaveform::visualize(waveform, brush, rect);

m_sampleThumbnail = SampleThumbnail{sample};

auto param = SampleThumbnail::VisualizeParameters{};
param.allowHighResolution = true;
param.amplification = sample.amplification();
param.reversed = sample.reversed();
param.sampleStart = static_cast<float>(startFrame) / sample.sampleSize();
param.sampleEnd = static_cast<float>(endFrame) / sample.sampleSize();
param.x = 0;
param.y = zoomOffset;
param.width = m_editorWidth;
param.height = static_cast<long>(m_zoomLevel * m_editorHeight);

m_sampleThumbnail.visualize(param, brush);

// increase brightness in inner color
QBitmap innerMask = m_editorWaveform.createMaskFromColor(s_waveformMaskColor, Qt::MaskMode::MaskOutColor);
Expand Down
3 changes: 3 additions & 0 deletions plugins/SlicerT/SlicerTWaveform.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@

#include "Instrument.h"
#include "SampleBuffer.h"
#include "SampleThumbnail.h"

namespace lmms {

Expand Down Expand Up @@ -108,6 +109,8 @@ public slots:
QPixmap m_editorWaveform;
QPixmap m_sliceEditor;
QPixmap m_emptySampleIcon;

SampleThumbnail m_sampleThumbnail;

SlicerT* m_slicerTParent;

Expand Down
1 change: 1 addition & 0 deletions src/gui/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ SET(LMMS_SRCS
gui/RowTableView.cpp
gui/SampleLoader.cpp
gui/SampleTrackWindow.cpp
gui/SampleThumbnail.cpp
gui/SampleWaveform.cpp
gui/SendButtonIndicator.cpp
gui/SideBar.cpp
Expand Down
Loading
Loading