add: Audio Info in NowPlayingPanel.cpp ; Includes: Samplerate, bits/sample, and codec
change: opengl dll for windows now is not included, cause it is too large refactor: Spectrogram colors for bars are now in a Header (GradientPresetHeader.h), with accompaning methods
This commit is contained in:
+6
-5
@@ -63,6 +63,7 @@ set(HEADERS
|
||||
src/gui/CoverArtWidget.h
|
||||
src/gui/SpectrogramWidget.h
|
||||
src/gui/GradientDialog.h
|
||||
src/gui/GradientPresetHeader.h
|
||||
src/gui/EqualizerWidget.h
|
||||
src/gui/EqualizerPresetManager.h
|
||||
src/gui/FreqCurveWidget.h
|
||||
@@ -87,10 +88,10 @@ set(HEADERS
|
||||
src/engine/encoder/StageBuffer.h
|
||||
src/engine/encoder/FfmpegGuards.h
|
||||
src/engine/encoder/SampleRateUtils.h
|
||||
src/engine/encoder/CodecUtils.h # new: pickSampleFormat/pickChannelCount/clampBitrate/ChannelLayoutGuard
|
||||
src/engine/encoder/OutputPathBuilder.h # new: buildOutputPath
|
||||
src/engine/encoder/DecoderPipeline.h # new: input open + decoder context
|
||||
src/engine/encoder/EncoderPipeline.h # new: encoder + muxer + SWR + encode frame
|
||||
src/engine/encoder/CodecUtils.h
|
||||
src/engine/encoder/OutputPathBuilder.h
|
||||
src/engine/encoder/DecoderPipeline.h
|
||||
src/engine/encoder/EncoderPipeline.h
|
||||
# ffmpeg wrapper
|
||||
src/engine/ffmpeg/FormatContext.h
|
||||
src/engine/ffmpeg/CodecContext.h
|
||||
@@ -131,7 +132,7 @@ target_include_directories(SubWave PRIVATE
|
||||
src/config
|
||||
src/controllers
|
||||
src/engine
|
||||
src/engine/encoder # covers CodecUtils.h, OutputPathBuilder.h, DecoderPipeline.h, EncoderPipeline.h
|
||||
src/engine/encoder
|
||||
src/engine/ffmpeg
|
||||
src/playlists
|
||||
src/gui
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
# completely written by chatgpt btw. dont bother for winslop
|
||||
# except one part, which is the dll deletion!
|
||||
Set-StrictMode -Version Latest
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
# this is bullshit, only needed cause QtCreator is needed to compile the App ; which is also bullshit
|
||||
$BuildDir = Join-Path $env:USERPROFILE "Documents\SubWave\build\Desktop_Qt_6_10_2_MinGW_64_bit-Release"
|
||||
$ZipOutDir = Join-Path $env:USERPROFILE "Documents\SubWave\build"
|
||||
$QtBin = "C:\Qt\6.10.2\mingw_64\bin"
|
||||
@@ -11,7 +13,7 @@ $AppExe = Join-Path $BuildDir "SubWave.exe"
|
||||
|
||||
$StagingDir = Join-Path $env:TEMP "SubWave_staging"
|
||||
$ZipPath = Join-Path $ZipOutDir "SubWave.zip"
|
||||
$UnpackedDir = Join-Path $ZipOutDir "SubWave_uncompressed"
|
||||
$UnpackedDir = Join-Path $ZipOutDir "SubWave"
|
||||
|
||||
function Copy-Dll($src, $dst) {
|
||||
if (Test-Path $src) {
|
||||
@@ -125,7 +127,8 @@ $dllsToRemove = @(
|
||||
"avdevice-*.dll","libavdevice-*.dll",
|
||||
"swscale-*.dll","libswscale-*.dll",
|
||||
"postproc-*.dll","libpostproc-*.dll",
|
||||
"libfftw3f-3.dll","libfftw3l-3.dll"
|
||||
"libfftw3f-3.dll","libfftw3l-3.dll",
|
||||
"opengl32sw.dll" # opengl is usually shipped withwindows, so we can safely delete it, mesa is not needed
|
||||
)
|
||||
|
||||
foreach ($pattern in $dllsToRemove) {
|
||||
|
||||
@@ -21,6 +21,11 @@ AudioEngine::State AudioEngine::state() const { return m_state.load(); }
|
||||
qint64 AudioEngine::positionFrames() const { return m_posFrames.load(); }
|
||||
qint64 AudioEngine::totalFrames() const { return m_totalFrames.load(); }
|
||||
int AudioEngine::sampleRate() const { return m_sampleRate.load(); }
|
||||
int AudioEngine::bitsPerSample() const { return m_bitsPerSample.load(); }
|
||||
QString AudioEngine::codecName() const {
|
||||
QMutexLocker lock(&m_metaMtx);
|
||||
return m_codecName;
|
||||
}
|
||||
|
||||
void AudioEngine::setVolume(float v) {
|
||||
m_volume = std::clamp(v, 0.f, 1.f);
|
||||
@@ -32,6 +37,11 @@ void AudioEngine::play(const engine::Track &track) {
|
||||
m_stopReq = false;
|
||||
m_pauseReq = false;
|
||||
m_seekPending = false;
|
||||
m_bitsPerSample = 0;
|
||||
{
|
||||
QMutexLocker lock(&m_metaMtx);
|
||||
m_codecName.clear();
|
||||
}
|
||||
m_track = track;
|
||||
m_thread = QThread::create([this]{ playbackLoop(); });
|
||||
m_thread->setObjectName("subwave-audio");
|
||||
@@ -99,6 +109,11 @@ void AudioEngine::playbackLoop() {
|
||||
}
|
||||
|
||||
m_sampleRate = decoder.sampleRate();
|
||||
m_bitsPerSample = decoder.bitsPerSample();
|
||||
{
|
||||
QMutexLocker lock(&m_metaMtx);
|
||||
m_codecName = decoder.codecName();
|
||||
}
|
||||
m_eq.setSampleRate(m_sampleRate);
|
||||
m_eq.reset();
|
||||
|
||||
|
||||
@@ -27,6 +27,8 @@ public:
|
||||
qint64 positionFrames() const;
|
||||
qint64 totalFrames() const;
|
||||
int sampleRate() const;
|
||||
int bitsPerSample() const;
|
||||
QString codecName() const;
|
||||
|
||||
void setVolume(float v);
|
||||
void play(const engine::Track &track);
|
||||
@@ -55,6 +57,7 @@ private:
|
||||
std::atomic<qint64> m_posFrames { 0 };
|
||||
std::atomic<qint64> m_totalFrames { 0 };
|
||||
std::atomic<int> m_sampleRate { 44100 };
|
||||
std::atomic<int> m_bitsPerSample { 0 };
|
||||
std::atomic<bool> m_stopReq { false };
|
||||
std::atomic<bool> m_pauseReq { false };
|
||||
std::atomic<bool> m_seekPending { false };
|
||||
@@ -67,8 +70,10 @@ private:
|
||||
|
||||
QMutex m_pauseMtx;
|
||||
QWaitCondition m_pauseCv;
|
||||
mutable QMutex m_metaMtx;
|
||||
|
||||
engine::Track m_track;
|
||||
QString m_codecName;
|
||||
engine::EqProcessor m_eq;
|
||||
|
||||
float m_eqGainDb[EQ_BANDS] {};
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
extern "C" {
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavutil/samplefmt.h>
|
||||
}
|
||||
|
||||
namespace engine::ffmpeg {
|
||||
@@ -64,4 +65,18 @@ int CodecContext::channels() const {
|
||||
return m_ctx ? m_ctx->ch_layout.nb_channels : 0;
|
||||
}
|
||||
|
||||
int CodecContext::bitsPerSample() const {
|
||||
if (!m_ctx) return 0;
|
||||
|
||||
if (m_ctx->bits_per_raw_sample > 0)
|
||||
return m_ctx->bits_per_raw_sample;
|
||||
|
||||
if (m_ctx->sample_fmt != AV_SAMPLE_FMT_NONE && m_ctx->sample_fmt != AV_SAMPLE_FMT_NB) {
|
||||
const int bytesPerSample = av_get_bytes_per_sample(m_ctx->sample_fmt);
|
||||
return bytesPerSample > 0 ? bytesPerSample * 8 : 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace engine::ffmpeg
|
||||
|
||||
@@ -27,6 +27,7 @@ public:
|
||||
bool isOpen() const { return m_ctx != nullptr; }
|
||||
int sampleRate()const;
|
||||
int channels() const;
|
||||
int bitsPerSample() const;
|
||||
|
||||
const AVCodecContext *get() const { return m_ctx.get(); }
|
||||
|
||||
|
||||
@@ -61,6 +61,28 @@ void FFmpegDecoder::seek(double fraction) {
|
||||
m_codec.flush();
|
||||
}
|
||||
|
||||
QString FFmpegDecoder::codecName() const {
|
||||
const AVCodecParameters *par = m_format.audioCodecPar();
|
||||
if (!par) return {};
|
||||
|
||||
const AVCodecDescriptor *desc = avcodec_descriptor_get(par->codec_id);
|
||||
if (!desc) {
|
||||
const char *fallback = avcodec_get_name(par->codec_id);
|
||||
return fallback ? QString::fromUtf8(fallback).toUpper() : QString();
|
||||
}
|
||||
|
||||
QString name = QString::fromUtf8(desc->long_name && desc->long_name[0] ? desc->long_name : desc->name);
|
||||
const int parenPos = name.indexOf(" (");
|
||||
if (parenPos >= 0)
|
||||
name.truncate(parenPos);
|
||||
|
||||
return name.trimmed().toUpper();
|
||||
}
|
||||
|
||||
int FFmpegDecoder::bitsPerSample() const {
|
||||
return m_codec.bitsPerSample();
|
||||
}
|
||||
|
||||
bool FFmpegDecoder::decodeNext(AudioBuffer &buf) {
|
||||
if (!m_format.isOpen()) return false;
|
||||
|
||||
|
||||
@@ -24,6 +24,8 @@ public:
|
||||
int channels() const { return m_codec.channels(); }
|
||||
double durationSeconds() const { return m_format.durationSecs(); }
|
||||
qint64 totalFrames() const { return m_totalFrames; }
|
||||
QString codecName() const;
|
||||
int bitsPerSample() const;
|
||||
|
||||
private:
|
||||
FormatContext m_format;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "GradientDialog.h"
|
||||
#include "GradientPresetHeader.h"
|
||||
#include <QHBoxLayout>
|
||||
#include <QVBoxLayout>
|
||||
#include <QPushButton>
|
||||
@@ -8,47 +9,6 @@
|
||||
#include <QPixmap>
|
||||
#include <QComboBox>
|
||||
|
||||
static QVector<QColor> makePreset4(const QColor &c0, const QColor &c1,
|
||||
const QColor &c2, const QColor &c3,
|
||||
const QColor &c4)
|
||||
{
|
||||
return { c0, c2, c3, c4 };
|
||||
}
|
||||
|
||||
struct Preset {
|
||||
QString name;
|
||||
QVector<QColor> colors;
|
||||
};
|
||||
|
||||
// Presets are defined in a Vector for simplicity of adding new ones
|
||||
static const QVector<Preset> PRESETS = {
|
||||
{ "Default",
|
||||
{ QColor("#078D70"), QColor("#26CEAA"), QColor("#98E8C1"), QColor("#FFFFFF") } },
|
||||
|
||||
{ "Pastels",
|
||||
makePreset4(QColor("#92ccdd"), QColor("#c7eff0"),
|
||||
QColor("#f5d5fd"), QColor("#fdc4ec"), QColor("#ffc2cb")) },
|
||||
|
||||
{ "Raddish",
|
||||
makePreset4(QColor("#ff0000"), QColor("#b40000"),
|
||||
QColor("#890000"), QColor("#540000"), QColor("#240000")) },
|
||||
|
||||
{ "Blau",
|
||||
makePreset4(QColor("#72c8dd"), QColor("#43b5d2"),
|
||||
QColor("#14a3c7"), QColor("#1293b3"), QColor("#10829f")) },
|
||||
|
||||
{ "Turquoise",
|
||||
makePreset4(QColor("#d8f2f6"), QColor("#8bd9ef"),
|
||||
QColor("#3ec1d5"), QColor("#319aaa"), QColor("#25737f")) },
|
||||
|
||||
{ "Aqua",
|
||||
makePreset4(QColor("#2278FB"), QColor("#3A9AF0"),
|
||||
QColor("#53BDE6"), QColor("#6BDFDB"), QColor("#1A5BB8")) },
|
||||
|
||||
{ "Peaches",
|
||||
makePreset4(QColor("#FFCAA6"), QColor("#FDA8A0"),
|
||||
QColor("#FA879A"), QColor("#F86594"), QColor("#FFE0CC")) }
|
||||
};
|
||||
|
||||
static int findPresetIndex(const QVector<QColor> &stops)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
#include <QVector>
|
||||
#include <QColor>
|
||||
#include <QString>
|
||||
|
||||
/** preset function */
|
||||
inline QVector<QColor> makePreset4(const QColor &c0, const QColor &c1,
|
||||
const QColor &c2, const QColor &c3,
|
||||
const QColor &c4)
|
||||
{
|
||||
// if you dont define c1 as #FFFF worry, cause it does NOT get used!
|
||||
return { c0, c2, c3, c4 };
|
||||
}
|
||||
|
||||
/** Preset Struct */
|
||||
struct Preset {
|
||||
QString name;
|
||||
QVector<QColor> colors;
|
||||
};
|
||||
|
||||
/** Defined Presets */
|
||||
inline const QVector<Preset> PRESETS = {
|
||||
{ "Default",
|
||||
{ QColor("#078D70"), QColor("#26CEAA"), QColor("#98E8C1"), QColor("#FFFFFF") } },
|
||||
|
||||
{ "Pastels",
|
||||
makePreset4(QColor("#92ccdd"), QColor("#c7eff0"),
|
||||
QColor("#f5d5fd"), QColor("#fdc4ec"), QColor("#ffc2cb")) },
|
||||
|
||||
{ "Raddish",
|
||||
makePreset4(QColor("#ff0000"), QColor("#b40000"),
|
||||
QColor("#890000"), QColor("#540000"), QColor("#240000")) },
|
||||
|
||||
{ "Blau",
|
||||
makePreset4(QColor("#72c8dd"), QColor("#43b5d2"),
|
||||
QColor("#14a3c7"), QColor("#1293b3"), QColor("#10829f")) },
|
||||
|
||||
{ "Turquoise",
|
||||
makePreset4(QColor("#d8f2f6"), QColor("#8bd9ef"),
|
||||
QColor("#3ec1d5"), QColor("#319aaa"), QColor("#25737f")) },
|
||||
|
||||
{ "Aqua",
|
||||
makePreset4(QColor("#2278FB"), QColor("#3A9AF0"),
|
||||
QColor("#53BDE6"), QColor("#6BDFDB"), QColor("#1A5BB8")) },
|
||||
|
||||
{ "Peaches",
|
||||
makePreset4(QColor("#FFCAA6"), QColor("#FDA8A0"),
|
||||
QColor("#FA879A"), QColor("#F86594"), QColor("#FFE0CC")) },
|
||||
|
||||
{ "Windows Green",
|
||||
makePreset4(QColor("#BFFFE3"), QColor("#7FFFC2"),
|
||||
QColor("#43FFAC"), QColor("#27FF97"), QColor("#0EDB7D")) },
|
||||
};
|
||||
@@ -48,6 +48,10 @@ MainWindow::MainWindow(QWidget *parent)
|
||||
e->positionFrames(),
|
||||
e->totalFrames(),
|
||||
float(e->sampleRate()));
|
||||
m_nowPlaying->updateAudioInfo(
|
||||
e->sampleRate(),
|
||||
e->bitsPerSample(),
|
||||
e->codecName());
|
||||
|
||||
if (e->state() == AudioEngine::State::Stopped)
|
||||
m_nowPlaying->setPlaybackActive(false);
|
||||
|
||||
@@ -60,11 +60,22 @@ void NowPlayingPanel::buildLayout()
|
||||
transport->addWidget(new QLabel("Volume:"));
|
||||
transport->addWidget(m_volSlider);
|
||||
|
||||
auto *metaRow = new QHBoxLayout;
|
||||
metaRow->setSpacing(8);
|
||||
metaRow->addWidget(new QLabel("Sample rate:"));
|
||||
metaRow->addWidget(m_lblSampleRate);
|
||||
metaRow->addWidget(new QLabel("Bits/Sample:"));
|
||||
metaRow->addWidget(m_lblBitsPerSample);
|
||||
metaRow->addWidget(new QLabel("Codec:"));
|
||||
metaRow->addWidget(m_lblCodec);
|
||||
metaRow->addStretch();
|
||||
|
||||
auto *right = new QVBoxLayout;
|
||||
right->setSpacing(4);
|
||||
right->addWidget(m_lblTitle);
|
||||
right->addWidget(m_lblArtist);
|
||||
right->addWidget(m_lblAlbum);
|
||||
right->addLayout(metaRow);
|
||||
right->addLayout(seekRow);
|
||||
right->addLayout(transport);
|
||||
|
||||
@@ -73,14 +84,25 @@ void NowPlayingPanel::buildLayout()
|
||||
hlay->addLayout(right, 1);
|
||||
}
|
||||
|
||||
void NowPlayingPanel::clearAudioInfo()
|
||||
{
|
||||
m_lblSampleRate->setText("-");
|
||||
m_lblBitsPerSample->setText("-");
|
||||
m_lblCodec->setText("-");
|
||||
}
|
||||
|
||||
void NowPlayingPanel::updateTrack(const engine::Track *t)
|
||||
{
|
||||
if (!t) return;
|
||||
if (!t) {
|
||||
clearAudioInfo();
|
||||
return;
|
||||
}
|
||||
m_cover->setTrack(t);
|
||||
m_lblTitle->setText(t->displayTitle());
|
||||
m_lblArtist->setText(t->artist);
|
||||
m_lblAlbum->setText(t->album +
|
||||
(t->year > 0 ? " (" + QString::number(t->year) + ")" : ""));
|
||||
clearAudioInfo();
|
||||
m_seekBar->setValue(0);
|
||||
m_btnPlay->setText("Pause");
|
||||
}
|
||||
@@ -101,6 +123,13 @@ void NowPlayingPanel::updatePosition(qint64 posFrames, qint64 totalFrames, float
|
||||
m_seekBar->setValue(int(posFrames * 1000 / totalFrames));
|
||||
}
|
||||
|
||||
void NowPlayingPanel::updateAudioInfo(int sampleRate, int bitsPerSample, const QString &codec)
|
||||
{
|
||||
m_lblSampleRate->setText(sampleRate > 0 ? QString("%1 Hz").arg(sampleRate) : "-");
|
||||
m_lblBitsPerSample->setText(bitsPerSample > 0 ? QString("%1").arg(bitsPerSample) : "-");
|
||||
m_lblCodec->setText(!codec.isEmpty() ? codec : "-");
|
||||
}
|
||||
|
||||
void NowPlayingPanel::setPlaybackActive(bool playing)
|
||||
{
|
||||
m_btnPlay->setText(playing ? "Pause" : "Play");
|
||||
|
||||
@@ -20,7 +20,8 @@ public:
|
||||
public slots:
|
||||
void updateTrack(const engine::Track *track);
|
||||
void updatePosition(qint64 posFrames, qint64 totalFrames, float sampleRate);
|
||||
void setPlaybackActive(bool playing);
|
||||
void updateAudioInfo(int sampleRate, int bitsPerSample, const QString &codec);
|
||||
void setPlaybackActive(bool playing);
|
||||
|
||||
signals:
|
||||
void playPauseClicked();
|
||||
@@ -31,12 +32,16 @@ signals:
|
||||
|
||||
private:
|
||||
void buildLayout();
|
||||
void clearAudioInfo();
|
||||
|
||||
CoverArtWidget *m_cover;
|
||||
QLabel *m_lblTitle = new QLabel("No track selected");
|
||||
QLabel *m_lblArtist = new QLabel(" ");
|
||||
QLabel *m_lblAlbum = new QLabel(" ");
|
||||
QLabel *m_lblTime = new QLabel("0:00 / 0:00");
|
||||
QLabel *m_lblSampleRate = new QLabel("—");
|
||||
QLabel *m_lblBitsPerSample = new QLabel("-");
|
||||
QLabel *m_lblCodec = new QLabel("—");
|
||||
QSlider *m_seekBar = new QSlider(Qt::Horizontal);
|
||||
QPushButton *m_btnPrev = new QPushButton("|<");
|
||||
QPushButton *m_btnPlay = new QPushButton("Play");
|
||||
|
||||
Reference in New Issue
Block a user