clean structure
This commit is contained in:
+23
-19
@@ -26,20 +26,21 @@ pkg_check_modules(FFTW REQUIRED IMPORTED_TARGET fftw3)
|
|||||||
# sources
|
# sources
|
||||||
set(SOURCES
|
set(SOURCES
|
||||||
src/main.cpp
|
src/main.cpp
|
||||||
src/MetadataReader.cpp
|
src/config/Config.cpp
|
||||||
src/Config.cpp
|
|
||||||
src/MusicLibrary.cpp
|
src/gui/CoverArtWidget.cpp
|
||||||
src/CoverArtWidget.cpp
|
src/gui/SpectrogramWidget.cpp
|
||||||
src/SpectrogramWidget.cpp
|
src/gui/GradientDialog.cpp
|
||||||
src/GradientDialog.cpp
|
src/gui/EqualizerWidget.cpp
|
||||||
src/EqualizerWidget.cpp
|
src/gui/TrackDelegate.cpp
|
||||||
src/TrackDelegate.cpp
|
src/gui/MainWindow.cpp
|
||||||
src/MainWindow.cpp
|
|
||||||
|
|
||||||
# engine
|
# engine
|
||||||
src/engine/AudioEngine.cpp
|
src/engine/AudioEngine.cpp
|
||||||
src/engine/EqProcessor.cpp
|
src/engine/EqProcessor.cpp
|
||||||
src/engine/SpectrumAnalyzer.cpp
|
src/engine/SpectrumAnalyzer.cpp
|
||||||
|
src/engine/MusicLibrary.cpp
|
||||||
|
src/engine/MetadataReader.cpp
|
||||||
|
|
||||||
# ffmpeg
|
# ffmpeg
|
||||||
src/engine/ffmpeg/FormatContext.cpp
|
src/engine/ffmpeg/FormatContext.cpp
|
||||||
@@ -55,22 +56,23 @@ set(SOURCES
|
|||||||
)
|
)
|
||||||
|
|
||||||
set(HEADERS
|
set(HEADERS
|
||||||
src/Track.h
|
src/config/Config.h
|
||||||
src/MetadataReader.h
|
|
||||||
src/Config.h
|
src/gui/CoverArtWidget.h
|
||||||
src/MusicLibrary.h
|
src/gui/SpectrogramWidget.h
|
||||||
src/CoverArtWidget.h
|
src/gui/GradientDialog.h
|
||||||
src/SpectrogramWidget.h
|
src/gui/EqualizerWidget.h
|
||||||
src/GradientDialog.h
|
src/gui/TrackDelegate.h
|
||||||
src/EqualizerWidget.h
|
src/gui/MainWindow.h
|
||||||
src/TrackDelegate.h
|
|
||||||
src/MainWindow.h
|
|
||||||
|
|
||||||
# engine
|
# engine
|
||||||
src/engine/AudioEngine.h
|
src/engine/AudioEngine.h
|
||||||
src/engine/EqProcessor.h
|
src/engine/EqProcessor.h
|
||||||
src/engine/SpectrumAnalyzer.h
|
src/engine/SpectrumAnalyzer.h
|
||||||
src/engine/AudioBuffer.h
|
src/engine/AudioBuffer.h
|
||||||
|
src/engine/Track.h
|
||||||
|
src/engine/MetadataReader.h
|
||||||
|
src/engine/MusicLibrary.h
|
||||||
|
|
||||||
# ffmpeg wrapper
|
# ffmpeg wrapper
|
||||||
src/engine/ffmpeg/FormatContext.h
|
src/engine/ffmpeg/FormatContext.h
|
||||||
@@ -97,9 +99,11 @@ add_executable(SubWave ${SOURCES} ${HEADERS} ${RESOURCES})
|
|||||||
# headers
|
# headers
|
||||||
target_include_directories(SubWave PRIVATE
|
target_include_directories(SubWave PRIVATE
|
||||||
src
|
src
|
||||||
|
src/config
|
||||||
src/engine
|
src/engine
|
||||||
src/engine/ffmpeg
|
src/engine/ffmpeg
|
||||||
src/playlists
|
src/playlists
|
||||||
|
src/gui
|
||||||
)
|
)
|
||||||
|
|
||||||
# link to qt and ffmpeg and fftw
|
# link to qt and ffmpeg and fftw
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "Track.h"
|
|
||||||
|
|
||||||
class MetadataReader {
|
|
||||||
public:
|
|
||||||
static void populate(Track &track);
|
|
||||||
};
|
|
||||||
@@ -27,7 +27,7 @@ void AudioEngine::setVolume(float v) {
|
|||||||
if (m_sink) m_sink->setVolume(m_volume);
|
if (m_sink) m_sink->setVolume(m_volume);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioEngine::play(const Track &track) {
|
void AudioEngine::play(const engine::Track &track) {
|
||||||
stop();
|
stop();
|
||||||
m_stopReq = false;
|
m_stopReq = false;
|
||||||
m_pauseReq = false;
|
m_pauseReq = false;
|
||||||
@@ -197,3 +197,4 @@ void AudioEngine::playbackLoop() {
|
|||||||
if (natural)
|
if (natural)
|
||||||
emit trackEnded();
|
emit trackEnded();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ public:
|
|||||||
int sampleRate() const;
|
int sampleRate() const;
|
||||||
|
|
||||||
void setVolume(float v);
|
void setVolume(float v);
|
||||||
void play(const Track &track);
|
void play(const engine::Track &track);
|
||||||
void togglePause();
|
void togglePause();
|
||||||
void stop();
|
void stop();
|
||||||
void seekFraction(double fraction);
|
void seekFraction(double fraction);
|
||||||
@@ -68,7 +68,7 @@ private:
|
|||||||
QMutex m_pauseMtx;
|
QMutex m_pauseMtx;
|
||||||
QWaitCondition m_pauseCv;
|
QWaitCondition m_pauseCv;
|
||||||
|
|
||||||
Track m_track;
|
engine::Track m_track;
|
||||||
engine::EqProcessor m_eq;
|
engine::EqProcessor m_eq;
|
||||||
|
|
||||||
float m_eqGainDb[EQ_BANDS] {};
|
float m_eqGainDb[EQ_BANDS] {};
|
||||||
|
|||||||
@@ -5,7 +5,9 @@ extern "C" {
|
|||||||
#include <libavutil/dict.h>
|
#include <libavutil/dict.h>
|
||||||
}
|
}
|
||||||
|
|
||||||
void MetadataReader::populate(Track &track) {
|
namespace engine {
|
||||||
|
|
||||||
|
void MetadataReader::populate(engine::Track &track) {
|
||||||
AVFormatContext *fmt = nullptr;
|
AVFormatContext *fmt = nullptr;
|
||||||
if (avformat_open_input(&fmt, track.filePath.toUtf8().constData(),
|
if (avformat_open_input(&fmt, track.filePath.toUtf8().constData(),
|
||||||
nullptr, nullptr) < 0) return;
|
nullptr, nullptr) < 0) return;
|
||||||
@@ -74,4 +76,5 @@ void MetadataReader::populate(Track &track) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
avformat_close_input(&fmt);
|
avformat_close_input(&fmt);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "Track.h"
|
||||||
|
|
||||||
|
namespace engine {
|
||||||
|
|
||||||
|
class MetadataReader {
|
||||||
|
public:
|
||||||
|
static void populate(engine::Track &track);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -3,9 +3,11 @@
|
|||||||
#include <QDirIterator>
|
#include <QDirIterator>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
|
|
||||||
|
namespace engine {
|
||||||
|
|
||||||
MusicLibrary::MusicLibrary(QObject *parent) : QObject(parent) {}
|
MusicLibrary::MusicLibrary(QObject *parent) : QObject(parent) {}
|
||||||
|
|
||||||
const QList<Track> &MusicLibrary::tracks() const { return m_tracks; }
|
const QList<engine::Track> &MusicLibrary::tracks() const { return m_tracks; }
|
||||||
const QString &MusicLibrary::rootPath() const { return m_root; }
|
const QString &MusicLibrary::rootPath() const { return m_root; }
|
||||||
|
|
||||||
void MusicLibrary::setRootPath(const QString &path) { m_root = path; }
|
void MusicLibrary::setRootPath(const QString &path) { m_root = path; }
|
||||||
@@ -27,7 +29,7 @@ void MusicLibrary::scan() {
|
|||||||
|
|
||||||
while (it.hasNext() && !QThread::currentThread()->isInterruptionRequested()) {
|
while (it.hasNext() && !QThread::currentThread()->isInterruptionRequested()) {
|
||||||
QString path = it.next();
|
QString path = it.next();
|
||||||
Track t;
|
engine::Track t;
|
||||||
t.filePath = path;
|
t.filePath = path;
|
||||||
QString ext = QFileInfo(path).suffix().toLower();
|
QString ext = QFileInfo(path).suffix().toLower();
|
||||||
t.format = ext.toUpper();
|
t.format = ext.toUpper();
|
||||||
@@ -48,7 +50,7 @@ void MusicLibrary::scan() {
|
|||||||
|
|
||||||
void MusicLibrary::sortByArtistTitle() {
|
void MusicLibrary::sortByArtistTitle() {
|
||||||
QMutexLocker lk(&m_mtx);
|
QMutexLocker lk(&m_mtx);
|
||||||
std::sort(m_tracks.begin(), m_tracks.end(), [](const Track &a, const Track &b){
|
std::sort(m_tracks.begin(), m_tracks.end(), [](const engine::Track &a, const engine::Track &b){
|
||||||
int c = a.artist.compare(b.artist, Qt::CaseInsensitive);
|
int c = a.artist.compare(b.artist, Qt::CaseInsensitive);
|
||||||
return c != 0 ? c < 0 : a.title.compare(b.title, Qt::CaseInsensitive) < 0;
|
return c != 0 ? c < 0 : a.title.compare(b.title, Qt::CaseInsensitive) < 0;
|
||||||
});
|
});
|
||||||
@@ -56,4 +58,6 @@ void MusicLibrary::sortByArtistTitle() {
|
|||||||
|
|
||||||
void MusicLibrary::shutdown() {
|
void MusicLibrary::shutdown() {
|
||||||
if (m_scanThread) { m_scanThread->requestInterruption(); m_scanThread->wait(); }
|
if (m_scanThread) { m_scanThread->requestInterruption(); m_scanThread->wait(); }
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -5,6 +5,8 @@
|
|||||||
#include <QMutex>
|
#include <QMutex>
|
||||||
#include "Track.h"
|
#include "Track.h"
|
||||||
|
|
||||||
|
namespace engine {
|
||||||
|
|
||||||
class MusicLibrary : public QObject {
|
class MusicLibrary : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
@@ -12,7 +14,7 @@ public:
|
|||||||
{ "mp3", "flac", "wav", "ogg", "aac", "m4a", "opus", "wma" };
|
{ "mp3", "flac", "wav", "ogg", "aac", "m4a", "opus", "wma" };
|
||||||
|
|
||||||
explicit MusicLibrary(QObject *parent = nullptr);
|
explicit MusicLibrary(QObject *parent = nullptr);
|
||||||
const QList<Track> &tracks() const;
|
const QList<engine::Track> &tracks() const;
|
||||||
const QString &rootPath() const;
|
const QString &rootPath() const;
|
||||||
void setRootPath(const QString &path);
|
void setRootPath(const QString &path);
|
||||||
void scan();
|
void scan();
|
||||||
@@ -21,12 +23,14 @@ public:
|
|||||||
|
|
||||||
signals:
|
signals:
|
||||||
void scanStarted();
|
void scanStarted();
|
||||||
void trackFound(const Track &track);
|
void trackFound(const engine::Track &track);
|
||||||
void scanComplete(int count);
|
void scanComplete(int count);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString m_root;
|
QString m_root;
|
||||||
QList<Track> m_tracks;
|
QList<engine::Track> m_tracks;
|
||||||
QThread *m_scanThread { nullptr };
|
QThread *m_scanThread { nullptr };
|
||||||
QMutex m_mtx;
|
QMutex m_mtx;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -3,6 +3,8 @@
|
|||||||
#include <QImage>
|
#include <QImage>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
|
|
||||||
|
namespace engine {
|
||||||
|
|
||||||
struct Track {
|
struct Track {
|
||||||
QString filePath;
|
QString filePath;
|
||||||
QString title;
|
QString title;
|
||||||
@@ -28,4 +30,6 @@ struct Track {
|
|||||||
QString toString() const {
|
QString toString() const {
|
||||||
return artist + " \u2014 " + title;
|
return artist + " \u2014 " + title;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -7,7 +7,7 @@ CoverArtWidget::CoverArtWidget(QWidget *parent) : QWidget(parent) {
|
|||||||
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoverArtWidget::setTrack(const Track *t) {
|
void CoverArtWidget::setTrack(const engine::Track *t) {
|
||||||
m_cover = (t && !t->coverArt.isNull()) ? t->coverArt : QImage{};
|
m_cover = (t && !t->coverArt.isNull()) ? t->coverArt : QImage{};
|
||||||
m_title = t ? t->displayTitle() : "No track";
|
m_title = t ? t->displayTitle() : "No track";
|
||||||
m_artist = t ? t->artist : QString{};
|
m_artist = t ? t->artist : QString{};
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
#include <QImage>
|
#include <QImage>
|
||||||
#include "Track.h"
|
#include "engine/Track.h"
|
||||||
|
|
||||||
class CoverArtWidget : public QWidget {
|
class CoverArtWidget : public QWidget {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit CoverArtWidget(QWidget *parent = nullptr);
|
explicit CoverArtWidget(QWidget *parent = nullptr);
|
||||||
void setTrack(const Track *t);
|
void setTrack(const engine::Track *t);
|
||||||
protected:
|
protected:
|
||||||
void paintEvent(QPaintEvent *) override;
|
void paintEvent(QPaintEvent *) override;
|
||||||
private:
|
private:
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
#include "MainWindow.h"
|
#include "MainWindow.h"
|
||||||
#include "AudioEngine.h"
|
#include "AudioEngine.h"
|
||||||
#include "MusicLibrary.h"
|
#include "engine/MusicLibrary.h"
|
||||||
#include "PlaylistModel.h"
|
#include "PlaylistModel.h"
|
||||||
#include "TrackDelegate.h"
|
#include "TrackDelegate.h"
|
||||||
#include "CoverArtWidget.h"
|
#include "CoverArtWidget.h"
|
||||||
@@ -31,7 +31,7 @@ MainWindow::MainWindow(QWidget *parent)
|
|||||||
setMinimumSize(700, 760);
|
setMinimumSize(700, 760);
|
||||||
|
|
||||||
m_engine = new AudioEngine(this);
|
m_engine = new AudioEngine(this);
|
||||||
m_library = new MusicLibrary(this);
|
m_library = new engine::MusicLibrary(this);
|
||||||
m_model = new PlaylistModel(this);
|
m_model = new PlaylistModel(this);
|
||||||
m_library->setRootPath(Config::loadMusicRoot());
|
m_library->setRootPath(Config::loadMusicRoot());
|
||||||
|
|
||||||
@@ -258,7 +258,7 @@ void MainWindow::wireSignals()
|
|||||||
connect(m_engine, &AudioEngine::durationKnown,
|
connect(m_engine, &AudioEngine::durationKnown,
|
||||||
this, [this](qint64 secs){
|
this, [this](qint64 secs){
|
||||||
if (m_currentIndex >= 0) {
|
if (m_currentIndex >= 0) {
|
||||||
Track *t = m_model->trackAt(m_currentIndex);
|
engine::Track *t = m_model->trackAt(m_currentIndex);
|
||||||
if (t && t->durationSecs == 0) {
|
if (t && t->durationSecs == 0) {
|
||||||
t->durationSecs = secs;
|
t->durationSecs = secs;
|
||||||
m_model->refreshRow(m_currentIndex);
|
m_model->refreshRow(m_currentIndex);
|
||||||
@@ -267,15 +267,15 @@ void MainWindow::wireSignals()
|
|||||||
}, Qt::QueuedConnection);
|
}, Qt::QueuedConnection);
|
||||||
|
|
||||||
// Library scanning
|
// Library scanning
|
||||||
connect(m_library, &MusicLibrary::scanStarted, this, [this]{
|
connect(m_library, &engine::MusicLibrary::scanStarted, this, [this]{
|
||||||
m_model->setTracks({});
|
m_model->setTracks({});
|
||||||
m_lblStatus->setText("Scanning...");
|
m_lblStatus->setText("Scanning...");
|
||||||
});
|
});
|
||||||
connect(m_library, &MusicLibrary::trackFound, this, [this](const Track &t){
|
connect(m_library, &engine::MusicLibrary::trackFound, this, [this](const engine::Track &t){
|
||||||
m_model->appendTrack(t);
|
m_model->appendTrack(t);
|
||||||
m_lblStatus->setText(QString("%1 tracks found").arg(m_model->rowCount({})));
|
m_lblStatus->setText(QString("%1 tracks found").arg(m_model->rowCount({})));
|
||||||
}, Qt::QueuedConnection);
|
}, Qt::QueuedConnection);
|
||||||
connect(m_library, &MusicLibrary::scanComplete, this, [this](int n){
|
connect(m_library, &engine::MusicLibrary::scanComplete, this, [this](int n){
|
||||||
m_model->setTracks(m_library->tracks()); // sorted version
|
m_model->setTracks(m_library->tracks()); // sorted version
|
||||||
m_lblStatus->setText(QString("%1 tracks.").arg(n));
|
m_lblStatus->setText(QString("%1 tracks.").arg(n));
|
||||||
}, Qt::QueuedConnection);
|
}, Qt::QueuedConnection);
|
||||||
@@ -290,7 +290,7 @@ void MainWindow::reloadLibrary()
|
|||||||
|
|
||||||
void MainWindow::playTrack(int index)
|
void MainWindow::playTrack(int index)
|
||||||
{
|
{
|
||||||
Track *t = m_model->trackAt(index);
|
engine::Track *t = m_model->trackAt(index);
|
||||||
if (!t) return;
|
if (!t) return;
|
||||||
m_currentIndex = index;
|
m_currentIndex = index;
|
||||||
m_model->setCurrentIndex(index);
|
m_model->setCurrentIndex(index);
|
||||||
@@ -351,15 +351,14 @@ void MainWindow::onUiTimer()
|
|||||||
qint64 posF = m_engine->positionFrames();
|
qint64 posF = m_engine->positionFrames();
|
||||||
qint64 totF = m_engine->totalFrames();
|
qint64 totF = m_engine->totalFrames();
|
||||||
qint64 posSec = qint64(posF / sr);
|
qint64 posSec = qint64(posF / sr);
|
||||||
|
|
||||||
|
engine::Track *currentTrack = (m_currentIndex >= 0) ? m_model->trackAt(m_currentIndex) : nullptr;
|
||||||
qint64 totSec = totF > 0 ? qint64(totF / sr)
|
qint64 totSec = totF > 0 ? qint64(totF / sr)
|
||||||
: (m_currentIndex >= 0
|
: (currentTrack ? currentTrack->durationSecs : 0);
|
||||||
? (m_model->trackAt(m_currentIndex)
|
|
||||||
? m_model->trackAt(m_currentIndex)->durationSecs : 0)
|
|
||||||
: 0);
|
|
||||||
|
|
||||||
// Write back duration if we now know it
|
// Write back duration if we now know it
|
||||||
if (totF > 0 && m_currentIndex >= 0) {
|
if (totF > 0 && m_currentIndex >= 0) {
|
||||||
Track *t = m_model->trackAt(m_currentIndex);
|
engine::Track *t = m_model->trackAt(m_currentIndex);
|
||||||
if (t && t->durationSecs == 0) {
|
if (t && t->durationSecs == 0) {
|
||||||
t->durationSecs = totSec;
|
t->durationSecs = totSec;
|
||||||
m_model->refreshRow(m_currentIndex);
|
m_model->refreshRow(m_currentIndex);
|
||||||
@@ -9,10 +9,13 @@
|
|||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QTabWidget>
|
#include <QTabWidget>
|
||||||
#include <QGroupBox>
|
#include <QGroupBox>
|
||||||
#include <QHBoxLayout> // needed for buildTransportRow() return type
|
#include <QHBoxLayout>
|
||||||
|
|
||||||
|
// Forward declarations
|
||||||
class AudioEngine;
|
class AudioEngine;
|
||||||
class MusicLibrary;
|
namespace engine {
|
||||||
|
class MusicLibrary;
|
||||||
|
}
|
||||||
class PlaylistModel;
|
class PlaylistModel;
|
||||||
class CoverArtWidget;
|
class CoverArtWidget;
|
||||||
class SpectrogramWidget;
|
class SpectrogramWidget;
|
||||||
@@ -50,9 +53,9 @@ private:
|
|||||||
void loadPlaylistIntoModel(const QString &playlistName);
|
void loadPlaylistIntoModel(const QString &playlistName);
|
||||||
|
|
||||||
// Core
|
// Core
|
||||||
AudioEngine *m_engine;
|
AudioEngine *m_engine;
|
||||||
MusicLibrary *m_library;
|
engine::MusicLibrary *m_library;
|
||||||
PlaylistModel *m_model;
|
PlaylistModel *m_model;
|
||||||
|
|
||||||
// Transport – plain ASCII labels now
|
// Transport – plain ASCII labels now
|
||||||
QLabel *m_lblTitle = new QLabel("No track selected");
|
QLabel *m_lblTitle = new QLabel("No track selected");
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "SpectrogramWidget.h"
|
#include "SpectrogramWidget.h"
|
||||||
#include "GradientDialog.h"
|
#include "GradientDialog.h"
|
||||||
|
#include <QTimer>
|
||||||
#include <QSurfaceFormat>
|
#include <QSurfaceFormat>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QMouseEvent>
|
#include <QMouseEvent>
|
||||||
@@ -27,10 +28,15 @@ SpectrogramWidget::SpectrogramWidget(QWidget *parent)
|
|||||||
setMinimumSize(400, 200);
|
setMinimumSize(400, 200);
|
||||||
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||||
|
|
||||||
m_barHeights .resize(BAR_COUNT);
|
m_barHeights.resize(BAR_COUNT);
|
||||||
m_barPeaksVec.resize(BAR_COUNT);
|
m_barPeaksVec.resize(BAR_COUNT);
|
||||||
|
|
||||||
loadBarColors();
|
loadBarColors();
|
||||||
|
|
||||||
|
auto *timer = new QTimer(this);
|
||||||
|
connect(timer, &QTimer::timeout,
|
||||||
|
this, QOverload<>::of(&SpectrogramWidget::update));
|
||||||
|
timer->start(16);
|
||||||
}
|
}
|
||||||
|
|
||||||
SpectrogramWidget::~SpectrogramWidget()
|
SpectrogramWidget::~SpectrogramWidget()
|
||||||
@@ -287,6 +293,4 @@ void SpectrogramWidget::paintGL()
|
|||||||
glBindVertexArray(0);
|
glBindVertexArray(0);
|
||||||
m_barsProg.release();
|
m_barsProg.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
update();
|
|
||||||
}
|
}
|
||||||
@@ -77,7 +77,7 @@ void PlaylistCreatorDialog::onAddFiles()
|
|||||||
|
|
||||||
// Check if already in playlist
|
// Check if already in playlist
|
||||||
bool alreadyAdded = false;
|
bool alreadyAdded = false;
|
||||||
for (const Track &t : m_playlist.tracks) {
|
for (const engine::Track &t : m_playlist.tracks) {
|
||||||
if (t.filePath == filePath) {
|
if (t.filePath == filePath) {
|
||||||
alreadyAdded = true;
|
alreadyAdded = true;
|
||||||
break;
|
break;
|
||||||
@@ -85,7 +85,7 @@ void PlaylistCreatorDialog::onAddFiles()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!alreadyAdded) {
|
if (!alreadyAdded) {
|
||||||
Track track;
|
engine::Track track;
|
||||||
track.filePath = filePath;
|
track.filePath = filePath;
|
||||||
|
|
||||||
// Set default metadata
|
// Set default metadata
|
||||||
@@ -96,7 +96,7 @@ void PlaylistCreatorDialog::onAddFiles()
|
|||||||
track.album = "Unknown Album";
|
track.album = "Unknown Album";
|
||||||
|
|
||||||
// Extract actual metadata and cover art
|
// Extract actual metadata and cover art
|
||||||
MetadataReader::populate(track);
|
engine::MetadataReader::populate(track);
|
||||||
|
|
||||||
m_playlist.tracks.append(track);
|
m_playlist.tracks.append(track);
|
||||||
}
|
}
|
||||||
@@ -145,7 +145,7 @@ void PlaylistCreatorDialog::updateTrackList()
|
|||||||
{
|
{
|
||||||
m_trackList->clear();
|
m_trackList->clear();
|
||||||
|
|
||||||
for (const Track &track : m_playlist.tracks) {
|
for (const engine::Track &track : m_playlist.tracks) {
|
||||||
QString display = track.displayTitle();
|
QString display = track.displayTitle();
|
||||||
if (!track.artist.isEmpty()) {
|
if (!track.artist.isEmpty()) {
|
||||||
display = track.artist + " - " + display;
|
display = track.artist + " - " + display;
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ QStringList PlaylistManager::listPlaylists()
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlaylistManager::addTrackToPlaylist(Playlist &playlist, const Track &track)
|
void PlaylistManager::addTrackToPlaylist(Playlist &playlist, const engine::Track &track)
|
||||||
{
|
{
|
||||||
playlist.tracks.append(track);
|
playlist.tracks.append(track);
|
||||||
}
|
}
|
||||||
@@ -144,7 +144,7 @@ PlaylistManager::Playlist PlaylistManager::parsePlaylistJson(const QString &json
|
|||||||
for (int i = 0; i < tracksArray.size(); ++i) {
|
for (int i = 0; i < tracksArray.size(); ++i) {
|
||||||
QJsonObject trackObj = tracksArray[i].toObject();
|
QJsonObject trackObj = tracksArray[i].toObject();
|
||||||
|
|
||||||
Track track;
|
engine::Track track;
|
||||||
track.filePath = trackObj["filePath"].toString();
|
track.filePath = trackObj["filePath"].toString();
|
||||||
track.title = trackObj["title"].toString();
|
track.title = trackObj["title"].toString();
|
||||||
track.artist = trackObj["artist"].toString();
|
track.artist = trackObj["artist"].toString();
|
||||||
@@ -157,7 +157,7 @@ PlaylistManager::Playlist PlaylistManager::parsePlaylistJson(const QString &json
|
|||||||
if (!track.filePath.isEmpty()) {
|
if (!track.filePath.isEmpty()) {
|
||||||
// Re-extract metadata and cover art from the file
|
// Re-extract metadata and cover art from the file
|
||||||
// in case it has changed or cover art wasn't saved
|
// in case it has changed or cover art wasn't saved
|
||||||
MetadataReader::populate(track);
|
engine::MetadataReader::populate(track);
|
||||||
playlist.tracks.append(track);
|
playlist.tracks.append(track);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -171,7 +171,7 @@ QString PlaylistManager::playlistToJson(const Playlist &playlist)
|
|||||||
QJsonObject root;
|
QJsonObject root;
|
||||||
QJsonArray tracksArray;
|
QJsonArray tracksArray;
|
||||||
|
|
||||||
for (const Track &track : playlist.tracks) {
|
for (const engine::Track &track : playlist.tracks) {
|
||||||
QJsonObject trackObj;
|
QJsonObject trackObj;
|
||||||
trackObj["filePath"] = track.filePath;
|
trackObj["filePath"] = track.filePath;
|
||||||
trackObj["title"] = track.title;
|
trackObj["title"] = track.title;
|
||||||
|
|||||||
@@ -2,13 +2,13 @@
|
|||||||
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QList>
|
#include <QList>
|
||||||
#include "Track.h"
|
#include "engine/Track.h"
|
||||||
|
|
||||||
class PlaylistManager {
|
class PlaylistManager {
|
||||||
public:
|
public:
|
||||||
struct Playlist {
|
struct Playlist {
|
||||||
QString name;
|
QString name;
|
||||||
QList<Track> tracks;
|
QList<engine::Track> tracks;
|
||||||
QString filePath;
|
QString filePath;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@ public:
|
|||||||
static QStringList listPlaylists();
|
static QStringList listPlaylists();
|
||||||
|
|
||||||
// Track management
|
// Track management
|
||||||
static void addTrackToPlaylist(Playlist &playlist, const Track &track);
|
static void addTrackToPlaylist(Playlist &playlist, const engine::Track &track);
|
||||||
static void removeTrackFromPlaylist(Playlist &playlist, int index);
|
static void removeTrackFromPlaylist(Playlist &playlist, int index);
|
||||||
static void clearPlaylist(Playlist &playlist);
|
static void clearPlaylist(Playlist &playlist);
|
||||||
|
|
||||||
|
|||||||
@@ -3,13 +3,13 @@
|
|||||||
PlaylistModel::PlaylistModel(QObject *parent)
|
PlaylistModel::PlaylistModel(QObject *parent)
|
||||||
: QAbstractListModel(parent) {}
|
: QAbstractListModel(parent) {}
|
||||||
|
|
||||||
void PlaylistModel::setTracks(const QList<Track> &tracks) {
|
void PlaylistModel::setTracks(const QList<engine::Track> &tracks) {
|
||||||
beginResetModel();
|
beginResetModel();
|
||||||
m_tracks = tracks;
|
m_tracks = tracks;
|
||||||
endResetModel();
|
endResetModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlaylistModel::appendTrack(const Track &t) {
|
void PlaylistModel::appendTrack(const engine::Track &t) {
|
||||||
beginInsertRows({}, m_tracks.size(), m_tracks.size());
|
beginInsertRows({}, m_tracks.size(), m_tracks.size());
|
||||||
m_tracks.append(t);
|
m_tracks.append(t);
|
||||||
endInsertRows();
|
endInsertRows();
|
||||||
@@ -27,11 +27,17 @@ void PlaylistModel::refreshRow(int idx) {
|
|||||||
emit dataChanged(index(idx), index(idx));
|
emit dataChanged(index(idx), index(idx));
|
||||||
}
|
}
|
||||||
|
|
||||||
Track *PlaylistModel::trackAt(int idx) {
|
engine::Track *PlaylistModel::trackAt(int idx) {
|
||||||
if (idx < 0 || idx >= m_tracks.size()) return nullptr;
|
if (idx < 0 || idx >= m_tracks.size()) return nullptr;
|
||||||
return &m_tracks[idx];
|
return &m_tracks[idx];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const engine::Track &PlaylistModel::trackAt(int idx) const {
|
||||||
|
static const engine::Track s_invalid;
|
||||||
|
if (idx < 0 || idx >= m_tracks.size()) return s_invalid;
|
||||||
|
return m_tracks[idx];
|
||||||
|
}
|
||||||
|
|
||||||
int PlaylistModel::rowCount(const QModelIndex &) const {
|
int PlaylistModel::rowCount(const QModelIndex &) const {
|
||||||
return m_tracks.size();
|
return m_tracks.size();
|
||||||
}
|
}
|
||||||
@@ -39,7 +45,7 @@ int PlaylistModel::rowCount(const QModelIndex &) const {
|
|||||||
QVariant PlaylistModel::data(const QModelIndex &idx, int role) const {
|
QVariant PlaylistModel::data(const QModelIndex &idx, int role) const {
|
||||||
if (!idx.isValid() || idx.row() >= m_tracks.size())
|
if (!idx.isValid() || idx.row() >= m_tracks.size())
|
||||||
return {};
|
return {};
|
||||||
const Track &t = m_tracks[idx.row()];
|
const engine::Track &t = m_tracks[idx.row()];
|
||||||
switch (role) {
|
switch (role) {
|
||||||
case Qt::DisplayRole:
|
case Qt::DisplayRole:
|
||||||
return t.displayTitle();
|
return t.displayTitle();
|
||||||
|
|||||||
@@ -1,22 +1,23 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <QAbstractListModel>
|
#include <QAbstractListModel>
|
||||||
#include <QList>
|
#include <QList>
|
||||||
#include "Track.h"
|
#include "engine/Track.h"
|
||||||
|
|
||||||
class PlaylistModel : public QAbstractListModel {
|
class PlaylistModel : public QAbstractListModel {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit PlaylistModel(QObject *parent=nullptr);
|
explicit PlaylistModel(QObject *parent=nullptr);
|
||||||
void setTracks(const QList<Track> &tracks);
|
void setTracks(const QList<engine::Track> &tracks);
|
||||||
void appendTrack(const Track &t);
|
void appendTrack(const engine::Track &t);
|
||||||
void setCurrentIndex(int idx);
|
void setCurrentIndex(int idx);
|
||||||
void refreshRow(int idx);
|
void refreshRow(int idx);
|
||||||
Track *trackAt(int idx);
|
engine::Track *trackAt(int idx);
|
||||||
|
const engine::Track &trackAt(int idx) const;
|
||||||
int rowCount(const QModelIndex &) const override;
|
int rowCount(const QModelIndex &) const override;
|
||||||
QVariant data(const QModelIndex &idx, int role) const override;
|
QVariant data(const QModelIndex &idx, int role) const override;
|
||||||
int currentIndex() const;
|
int currentIndex() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QList<Track> m_tracks;
|
QList<engine::Track> m_tracks;
|
||||||
int m_current { -1 };
|
int m_current { -1 };
|
||||||
};
|
};
|
||||||
Reference in New Issue
Block a user