app: implement deweaving and preview

This commit is contained in:
itsmattkc 2022-07-11 01:48:50 -07:00
parent 4465f26c38
commit cb53beb445
17 changed files with 509 additions and 152 deletions

View file

@ -1,7 +1,7 @@
find_package(Qt5)
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets)
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets Multimedia)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets Multimedia)
set(PROJECT_SOURCES
siview/chunkmodel.cpp
@ -19,11 +19,18 @@ set(PROJECT_SOURCES
siview/siview.cpp
siview/siview.h
viewer/bitmappanel.cpp
viewer/bitmappanel.h
viewer/wavpanel.cpp
viewer/wavpanel.h
main.cpp
mainwindow.cpp
mainwindow.h
#objectmodel.cpp
#objectmodel.h
model.cpp
model.h
objectmodel.cpp
objectmodel.h
panel.cpp
panel.h
vector3edit.cpp
@ -43,7 +50,7 @@ else()
)
endif()
target_link_libraries(si-edit PRIVATE Qt${QT_VERSION_MAJOR}::Widgets libweaver)
target_link_libraries(si-edit PRIVATE Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Multimedia libweaver)
target_include_directories(si-edit PRIVATE "${CMAKE_SOURCE_DIR}/lib")
set_target_properties(si-edit PROPERTIES

View file

@ -6,7 +6,6 @@
#include <QMessageBox>
#include <QSplitter>
#include "interleaf.h"
#include "siview/siview.h"
using namespace si;
@ -20,18 +19,45 @@ MainWindow::MainWindow(QWidget *parent) :
this->setCentralWidget(splitter);
tree_ = new QTreeView();
//tree_->setModel(&chunk_model_);
tree_->setModel(&model_);
tree_->setContextMenuPolicy(Qt::CustomContextMenu);
connect(tree_->selectionModel(), &QItemSelectionModel::currentRowChanged, this, &MainWindow::SelectionChanged);
connect(tree_, &QTreeView::customContextMenuRequested, this, &MainWindow::ShowContextMenu);
splitter->addWidget(tree_);
auto config_area = new QWidget();
splitter->addWidget(config_area);
auto config_layout = new QVBoxLayout(config_area);
action_grp_ = new QGroupBox();
config_layout->addWidget(action_grp_);
auto action_layout = new QHBoxLayout(action_grp_);
action_layout->addStretch();
auto extract_btn = new QPushButton(tr("Extract"));
action_layout->addWidget(extract_btn);
connect(extract_btn, &QPushButton::clicked, this, &MainWindow::ExtractClicked);
auto replace_btn = new QPushButton(tr("Replace"));
action_layout->addWidget(replace_btn);
action_layout->addStretch();
config_stack_ = new QStackedWidget();
splitter->addWidget(config_stack_);
config_layout->addWidget(config_stack_);
panel_blank_ = new Panel();
config_stack_->addWidget(panel_blank_);
panel_wav_ = new WavPanel();
config_stack_->addWidget(panel_wav_);
panel_bmp_ = new BitmapPanel();
config_stack_->addWidget(panel_bmp_);
InitializeMenuBar();
splitter->setSizes({99999, 99999});
@ -43,8 +69,9 @@ void MainWindow::OpenFilename(const QString &s)
if (si.Read(s.toStdString())) {
SIViewDialog d(SIViewDialog::Import, &si, this);
if (d.exec() == QDialog::Accepted) {
Interleaf interleaf;
interleaf.Parse(&si);
model_.SetCore(nullptr);
interleaf_.Parse(&si);
model_.SetCore(&interleaf_);
}
} else {
QMessageBox::critical(this, QString(), tr("Failed to load Interleaf file."));
@ -76,7 +103,7 @@ void MainWindow::InitializeMenuBar()
setMenuBar(menubar);
}
void MainWindow::SetPanel(Panel *panel, si::Chunk *chunk)
void MainWindow::SetPanel(Panel *panel, si::Object *chunk)
{
auto current = static_cast<Panel*>(config_stack_->currentWidget());
current->SetData(nullptr);
@ -84,6 +111,28 @@ void MainWindow::SetPanel(Panel *panel, si::Chunk *chunk)
config_stack_->setCurrentWidget(panel);
panel->SetData(chunk);
last_set_data_ = chunk;
action_grp_->setEnabled(chunk);
}
void MainWindow::ExtractObject(si::Object *obj)
{
QString filename = QString::fromStdString(obj->filename());
if (filename.isEmpty()) {
filename = QString::fromStdString(obj->name());
filename.append(QStringLiteral(".bin"));
}
QString s = QFileDialog::getSaveFileName(this, tr("Export Object"), filename);
if (!s.isEmpty()) {
QFile f(s);
if (f.open(QFile::WriteOnly)) {
f.write(obj->data().data(), obj->data().size());
f.close();
} else {
QMessageBox::critical(this, QString(), tr("Failed to write to file \"%1\".").arg(s));
}
}
}
void MainWindow::OpenFile()
@ -97,10 +146,17 @@ void MainWindow::OpenFile()
void MainWindow::SelectionChanged(const QModelIndex &index)
{
Panel *p = panel_blank_;
Chunk *c = static_cast<Chunk*>(index.internalPointer());
Object *c = dynamic_cast<Object*>(static_cast<Core*>(index.internalPointer()));
if (c) {
// HECK
switch (c->filetype()) {
case MxOb::WAV:
p = panel_wav_;
break;
case MxOb::STL:
p = panel_bmp_;
break;
}
}
if (p != config_stack_->currentWidget() || c != last_set_data_) {
@ -126,21 +182,13 @@ void MainWindow::ExtractSelectedItems()
}
for (const QModelIndex &i : selected) {
if (Chunk *chunk = static_cast<Chunk*>(i.internalPointer())) {
QString filename(chunk->data("FileName"));
if (filename.isEmpty()) {
filename = QString(chunk->data("Name"));
filename.append(QStringLiteral(".bin"));
}
if (filename.isEmpty()) {
filename = QStringLiteral("%1_%2.bin").arg(QString::fromLatin1((const char *) &chunk->id(), sizeof(uint32_t)),
QString::number(chunk->offset(), 16));
}
QString s = QFileDialog::getSaveFileName(this, tr("Export Object"), filename);
if (!s.isEmpty()) {
//chunk->Export()
}
if (Object *obj = dynamic_cast<Object*>(static_cast<Core*>(i.internalPointer()))) {
ExtractObject(obj);
}
}
}
void MainWindow::ExtractClicked()
{
ExtractObject(last_set_data_);
}

View file

@ -1,12 +1,17 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <chunk.h>
#include <interleaf.h>
#include <object.h>
#include <QGroupBox>
#include <QMainWindow>
#include <QStackedWidget>
#include <QTreeView>
#include "objectmodel.h"
#include "panel.h"
#include "viewer/bitmappanel.h"
#include "viewer/wavpanel.h"
class MainWindow : public QMainWindow
{
@ -23,15 +28,24 @@ private:
void InitializeMenuBar();
void SetPanel(Panel *panel, si::Chunk *chunk);
void SetPanel(Panel *panel, si::Object *chunk);
void ExtractObject(si::Object *obj);
QStackedWidget *config_stack_;
QTreeView *tree_;
Panel *panel_blank_;
QGroupBox *action_grp_;
si::Chunk *last_set_data_;
Panel *panel_blank_;
WavPanel *panel_wav_;
BitmapPanel *panel_bmp_;
ObjectModel model_;
si::Interleaf interleaf_;
si::Object *last_set_data_;
private slots:
void OpenFile();
@ -44,6 +58,8 @@ private slots:
void ExtractSelectedItems();
void ExtractClicked();
};
#endif // MAINWINDOW_H

67
app/model.cpp Normal file
View file

@ -0,0 +1,67 @@
#include "model.h"
#define super QAbstractItemModel
Model::Model(QObject *parent) :
super(parent),
core_(nullptr)
{
}
void Model::SetCore(si::Core *c)
{
beginResetModel();
core_ = c;
endResetModel();
}
si::Core *Model::GetCoreFromIndex(const QModelIndex &index) const
{
if (!index.isValid()) {
return core_;
} else {
return static_cast<si::Core*>(index.internalPointer());
}
}
QModelIndex Model::index(int row, int column, const QModelIndex &parent) const
{
si::Core *c = GetCoreFromIndex(parent);
if (!c) {
return QModelIndex();
}
return createIndex(row, column, c->GetChildAt(row));
}
QModelIndex Model::parent(const QModelIndex &index) const
{
si::Core *child = GetCoreFromIndex(index);
if (!child) {
return QModelIndex();
}
si::Core *parent = child->GetParent();
if (!parent) {
return QModelIndex();
}
si::Core *grandparent = parent->GetParent();
if (!grandparent) {
return QModelIndex();
}
size_t row = grandparent->IndexOfChild(parent);
return createIndex(int(row), 0, parent);
}
int Model::rowCount(const QModelIndex &parent) const
{
si::Core *c = GetCoreFromIndex(parent);
if (!c) {
return 0;
}
return int(c->GetChildCount());
}

27
app/model.h Normal file
View file

@ -0,0 +1,27 @@
#ifndef MODEL_H
#define MODEL_H
#include <core.h>
#include <QAbstractItemModel>
class Model : public QAbstractItemModel
{
public:
explicit Model(QObject *parent = nullptr);
si::Core *GetCore() const { return core_; }
void SetCore(si::Core *c);
virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
virtual QModelIndex parent(const QModelIndex &index) const override;
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override;
protected:
si::Core *GetCoreFromIndex(const QModelIndex &index) const;
private:
si::Core *core_;
};
#endif // MODEL_H

View file

@ -1,8 +1,8 @@
#include "objectmodel.h"
#include <chunk.h>
#include <object.h>
#define super QAbstractItemModel
#define super Model
using namespace si;
@ -16,49 +16,23 @@ int ObjectModel::columnCount(const QModelIndex &parent) const
return kColCount;
}
QModelIndex ObjectModel::index(int row, int column, const QModelIndex &parent) const
{
return createIndex(row, column, GetItem(row));
}
QModelIndex ObjectModel::parent(const QModelIndex &index) const
{
return QModelIndex();
}
int ObjectModel::rowCount(const QModelIndex &parent) const
{
if (parent.isValid()) {
return 0;
} else {
Chunk *mxof = GetMxOf();
if (!mxof) {
return 0;
}
return int(mxof->data("Offsets").size() / sizeof(uint32_t));
}
}
QVariant ObjectModel::data(const QModelIndex &index, int role) const
{
uint32_t offset;
Chunk *c = GetItem(index.row(), &offset);
Core *c = GetCoreFromIndex(index);
switch (role) {
case Qt::DisplayRole:
switch (index.column()) {
case kColIndex:
return index.row();
case kColOffset:
return QStringLiteral("0x%1").arg(QString::number(offset, 16).toUpper());
case kColName:
if (c) {
Chunk *mxob = c->FindChildWithType(Chunk::TYPE_MxOb);
if (mxob) {
return QString(mxob->data("Name"));
if (Object *o = dynamic_cast<Object*>(c)) {
return QString::number(o->id());
}
//return index.row();
break;
case kColName:
if (Object *o = dynamic_cast<Object*>(c)) {
return QString::fromStdString(o->name());
}
break;
}
@ -75,8 +49,6 @@ QVariant ObjectModel::headerData(int section, Qt::Orientation orientation, int r
switch (section) {
case kColIndex:
return tr("Index");
case kColOffset:
return tr("Offset");
case kColName:
return tr("Name");
}

View file

@ -1,9 +1,11 @@
#ifndef OBJECTMODEL_H
#define OBJECTMODEL_H
#include <QAbstractItemModel>
#include <interleaf.h>
class ObjectModel : public QAbstractItemModel
#include "model.h"
class ObjectModel : public Model
{
Q_OBJECT
public:
@ -17,9 +19,6 @@ public:
explicit ObjectModel(QObject *parent = nullptr);
virtual int columnCount(const QModelIndex &parent = QModelIndex()) const override;
virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
virtual QModelIndex parent(const QModelIndex &index) const override;
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override;
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;

View file

@ -10,6 +10,7 @@ class Panel : public QWidget
public:
explicit Panel(QWidget *parent = nullptr);
void *GetData() const { return data_; }
void SetData(void *data);
signals:

View file

@ -2,13 +2,12 @@
#include <iostream>
#define super QAbstractItemModel
#define super Model
using namespace si;
ChunkModel::ChunkModel(QObject *parent) :
super{parent},
chunk_(nullptr)
super{parent}
{
}
@ -17,50 +16,9 @@ int ChunkModel::columnCount(const QModelIndex &parent) const
return kColCount;
}
QModelIndex ChunkModel::index(int row, int column, const QModelIndex &parent) const
{
Chunk *c = GetChunkFromIndex(parent);
if (!c) {
return QModelIndex();
}
return createIndex(row, column, c->GetChildAt(row));
}
QModelIndex ChunkModel::parent(const QModelIndex &index) const
{
Chunk *child = GetChunkFromIndex(index);
if (!child) {
return QModelIndex();
}
Core *parent = child->GetParent();
if (!parent) {
return QModelIndex();
}
Core *grandparent = parent->GetParent();
if (!grandparent) {
return QModelIndex();
}
size_t row = grandparent->IndexOfChild(parent);
return createIndex(int(row), 0, parent);
}
int ChunkModel::rowCount(const QModelIndex &parent) const
{
Chunk *c = GetChunkFromIndex(parent);
if (!c) {
return 0;
}
return int(c->GetChildCount());
}
QVariant ChunkModel::data(const QModelIndex &index, int role) const
{
Chunk *c = GetChunkFromIndex(index);
Chunk *c = static_cast<Chunk*>(GetCoreFromIndex(index));
if (!c) {
return QVariant();
}
@ -108,19 +66,3 @@ QVariant ChunkModel::headerData(int section, Qt::Orientation orientation, int ro
return super::headerData(section, orientation, role);
}
void ChunkModel::SetChunk(si::Chunk *c)
{
beginResetModel();
chunk_ = c;
endResetModel();
}
si::Chunk *ChunkModel::GetChunkFromIndex(const QModelIndex &index) const
{
if (!index.isValid()) {
return chunk_;
} else {
return static_cast<Chunk*>(index.internalPointer());
}
}

View file

@ -2,9 +2,10 @@
#define CHUNKMODEL_H
#include <chunk.h>
#include <QAbstractItemModel>
class ChunkModel : public QAbstractItemModel
#include "model.h"
class ChunkModel : public Model
{
Q_OBJECT
public:
@ -21,22 +22,9 @@ public:
explicit ChunkModel(QObject *parent = nullptr);
virtual int columnCount(const QModelIndex &parent = QModelIndex()) const override;
virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
virtual QModelIndex parent(const QModelIndex &index) const override;
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override;
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
void SetChunk(si::Chunk *c);
protected:
si::Chunk *GetChunkFromIndex(const QModelIndex &index) const;
si::Chunk *root() const { return chunk_; }
private:
si::Chunk *chunk_;
};
#endif // CHUNKMODEL_H

View file

@ -71,6 +71,17 @@ MxChPanel::MxChPanel(QWidget *parent) :
data_sz_edit_->setMaximum(INT_MAX);
layout()->addWidget(data_sz_edit_, row, 1);
row++;
show_data_btn_ = new QPushButton(tr("Show Data"));
connect(show_data_btn_, &QPushButton::clicked, this, &MxChPanel::ShowDataField);
layout()->addWidget(show_data_btn_, row, 0, 1, 2);
data_edit_ = new QTextEdit();
data_edit_->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
layout()->addWidget(data_edit_, row, 0, 1, 2);
data_edit_->hide();
FinishLayout();
}
@ -99,6 +110,10 @@ void MxChPanel::OnClosingData(void *data)
chunk->data("Object") = u32(obj_edit_->value());
chunk->data("Time") = u32(ms_offset_edit_->value());
chunk->data("DataSize") = u32(data_sz_edit_->value());*/
show_data_btn_->show();
data_edit_->clear();
data_edit_->hide();
}
void MxChPanel::FlagCheckBoxClicked(bool e)
@ -116,3 +131,15 @@ void MxChPanel::FlagCheckBoxClicked(bool e)
flag_edit_->setText(QString::number(current, 16));
}
}
void MxChPanel::ShowDataField()
{
show_data_btn_->hide();
data_edit_->show();
Chunk *chunk = static_cast<Chunk*>(GetData());
const Data &data = chunk->data("Data");
QByteArray ba(data.data(), int(data.size()));
data_edit_->setPlainText(ba.toHex());
}

View file

@ -3,6 +3,7 @@
#include <QCheckBox>
#include <QLineEdit>
#include <QPushButton>
#include <QSpinBox>
#include <QTextEdit>
@ -23,12 +24,16 @@ private:
QSpinBox *obj_edit_;
QSpinBox *ms_offset_edit_;
QSpinBox *data_sz_edit_;
QTextEdit *data_edit_;
QPushButton *show_data_btn_;
QVector<QCheckBox*> flag_checkboxes_;
private slots:
void FlagCheckBoxClicked(bool e);
void ShowDataField();
};
#endif // MXCHPANEL_H

View file

@ -20,7 +20,7 @@ SIViewDialog::SIViewDialog(Mode mode, Chunk *riff, QWidget *parent) :
layout->addWidget(splitter);
auto tree = new QTreeView();
chunk_model_.SetChunk(riff);
chunk_model_.SetCore(riff);
tree->setModel(&chunk_model_);
tree->setContextMenuPolicy(Qt::CustomContextMenu);
connect(tree->selectionModel(), &QItemSelectionModel::currentRowChanged, this, &SIViewDialog::SelectionChanged);
@ -53,6 +53,7 @@ SIViewDialog::SIViewDialog(Mode mode, Chunk *riff, QWidget *parent) :
btn_layout->addStretch();
auto accept_btn = new QPushButton(mode == Import ? tr("De-Weave") : tr("Weave"));
accept_btn->setDefault(true);
connect(accept_btn, &QPushButton::clicked, this, &SIViewDialog::accept);
btn_layout->addWidget(accept_btn);

View file

@ -0,0 +1,39 @@
#include "bitmappanel.h"
#include <object.h>
#include <QBuffer>
#include <QGroupBox>
BitmapPanel::BitmapPanel(QWidget *parent)
{
int row = 0;
auto preview_grp = new QGroupBox(tr("Bitmap"));
layout()->addWidget(preview_grp, row, 0, 1, 2);
auto preview_layout = new QVBoxLayout(preview_grp);
img_lbl_ = new QLabel();
img_lbl_->setAlignment(Qt::AlignHCenter);
preview_layout->addWidget(img_lbl_);
desc_lbl_ = new QLabel();
desc_lbl_->setAlignment(Qt::AlignHCenter);
preview_layout->addWidget(desc_lbl_);
FinishLayout();
}
void BitmapPanel::OnOpeningData(void *data)
{
si::Object *o = static_cast<si::Object*>(data);
QByteArray b(o->data().data(), o->data().size());
QBuffer buf(&b);
QImage img;
img.load(&buf, "BMP");
img_lbl_->setPixmap(QPixmap::fromImage(img));
desc_lbl_->setText(tr("%1x%2").arg(QString::number(img.width()), QString::number(img.height())));
}

24
app/viewer/bitmappanel.h Normal file
View file

@ -0,0 +1,24 @@
#ifndef BITMAPPANEL_H
#define BITMAPPANEL_H
#include <panel.h>
#include <QLabel>
class BitmapPanel : public Panel
{
Q_OBJECT
public:
BitmapPanel(QWidget *parent = nullptr);
protected:
virtual void OnOpeningData(void *data) override;
//virtual void OnClosingData(void *data) override;
private:
QLabel *img_lbl_;
QLabel *desc_lbl_;
};
#endif // BITMAPPANEL_H

146
app/viewer/wavpanel.cpp Normal file
View file

@ -0,0 +1,146 @@
#include "wavpanel.h"
#include <iostream>
#include <object.h>
#include <QGroupBox>
#include <QHBoxLayout>
WavPanel::WavPanel(QWidget *parent) :
Panel(parent),
audio_out_(nullptr)
{
int row = 0;
auto wav_group = new QGroupBox(tr("Playback"));
layout()->addWidget(wav_group, row, 0, 1, 2);
auto wav_layout = new QHBoxLayout(wav_group);
playhead_slider_ = new QSlider(Qt::Horizontal);
playhead_slider_->setMinimum(0);
connect(playhead_slider_, &QSlider::valueChanged, this, &WavPanel::SliderMoved);
wav_layout->addWidget(playhead_slider_);
play_btn_ = new QPushButton(tr("Play"));
play_btn_->setCheckable(true);
connect(play_btn_, &QPushButton::clicked, this, &WavPanel::Play);
wav_layout->addWidget(play_btn_);
FinishLayout();
buffer_.setBuffer(&array_);
playback_timer_ = new QTimer(this);
playback_timer_->setInterval(100);
connect(playback_timer_, &QTimer::timeout, this, &WavPanel::TimerUpdate);
}
void WavPanel::OnOpeningData(void *data)
{
si::Object *o = static_cast<si::Object*>(data);
// Find fmt and data
auto dat = o->data().data();
for (size_t i=0; i<o->data().size(); i++) {
if (!memcmp(dat+i, "fmt ", 4)) {
header_ = *(si::WAVFormatHeader *)(dat+i+8);
break;
}
}
for (size_t i=0; i<o->data().size(); i++) {
if (!memcmp(dat+i, "data", 4)) {
uint32_t sz = *(uint32_t*)(dat+i+4);
playhead_slider_->setMaximum(sz/GetSampleSize());
break;
}
}
}
void WavPanel::OnClosingData(void *data)
{
Play(false);
playhead_slider_->setValue(0);
}
int WavPanel::GetSampleSize() const
{
return (header_.BitsPerSample/8) * header_.Channels;
}
void WavPanel::Play(bool e)
{
if (audio_out_) {
audio_out_->stop();
//audio_out_->deleteLater();
delete audio_out_;
audio_out_ = nullptr;
}
buffer_.close();
playback_timer_->stop();
if (e) {
const si::bytearray &lib = static_cast<si::Object*>(GetData())->data();
array_ = QByteArray(lib.data(), lib.size());
buffer_.open(QBuffer::ReadOnly);
size_t start = 0;
si::WAVFormatHeader header;
// Find fmt and data
auto dat = lib.data();
for (size_t i=0; i<lib.size(); i++) {
if (!memcmp(dat+i, "fmt ", 4)) {
header = *(si::WAVFormatHeader *)(dat+i+8);
break;
}
}
for (size_t i=0; i<lib.size(); i++) {
if (!memcmp(dat+i, "data", 4)) {
start = i + 8;
break;
}
}
buffer_start_ = start;
if (playhead_slider_->value() < playhead_slider_->maximum()) {
start += playhead_slider_->value() * GetSampleSize();
}
buffer_.seek(start);
QAudioFormat audio_fmt;
audio_fmt.setSampleRate(header.SampleRate);
audio_fmt.setChannelCount(header.Channels);
audio_fmt.setSampleSize(header.BitsPerSample);
audio_fmt.setByteOrder(QAudioFormat::LittleEndian);
audio_fmt.setCodec(QStringLiteral("audio/pcm"));
audio_fmt.setSampleType(QAudioFormat::SignedInt);
audio_out_ = new QAudioOutput(audio_fmt, this);
connect(audio_out_, &QAudioOutput::stateChanged, this, &WavPanel::OutputChanged);
audio_out_->start(&buffer_);
playback_timer_->start();
}
}
void WavPanel::TimerUpdate()
{
playhead_slider_->setValue((buffer_.pos() - buffer_start_) / GetSampleSize());
}
void WavPanel::OutputChanged(QAudio::State state)
{
if (state != QAudio::ActiveState) {
Play(false);
play_btn_->setChecked(false);
}
}
void WavPanel::SliderMoved(int i)
{
if (buffer_.isOpen()) {
buffer_.seek(buffer_start_ + i * GetSampleSize());
}
}

48
app/viewer/wavpanel.h Normal file
View file

@ -0,0 +1,48 @@
#ifndef WAVPANEL_H
#define WAVPANEL_H
#include <sitypes.h>
#include <QAudioOutput>
#include <QBuffer>
#include <QByteArray>
#include <QPushButton>
#include <QSlider>
#include <QTimer>
#include "panel.h"
class WavPanel : public Panel
{
Q_OBJECT
public:
WavPanel(QWidget *parent = nullptr);
protected:
virtual void OnOpeningData(void *data) override;
virtual void OnClosingData(void *data) override;
private:
int GetSampleSize() const;
QSlider *playhead_slider_;
QPushButton *play_btn_;
QAudioOutput *audio_out_;
QBuffer buffer_;
QByteArray array_;
size_t buffer_start_;
QTimer *playback_timer_;
si::WAVFormatHeader header_;
private slots:
void Play(bool e);
void TimerUpdate();
void OutputChanged(QAudio::State state);
void SliderMoved(int i);
};
#endif // WAVPANEL_H