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

View file

@ -6,7 +6,6 @@
#include <QMessageBox> #include <QMessageBox>
#include <QSplitter> #include <QSplitter>
#include "interleaf.h"
#include "siview/siview.h" #include "siview/siview.h"
using namespace si; using namespace si;
@ -20,18 +19,45 @@ MainWindow::MainWindow(QWidget *parent) :
this->setCentralWidget(splitter); this->setCentralWidget(splitter);
tree_ = new QTreeView(); tree_ = new QTreeView();
//tree_->setModel(&chunk_model_); tree_->setModel(&model_);
tree_->setContextMenuPolicy(Qt::CustomContextMenu); tree_->setContextMenuPolicy(Qt::CustomContextMenu);
connect(tree_->selectionModel(), &QItemSelectionModel::currentRowChanged, this, &MainWindow::SelectionChanged); connect(tree_->selectionModel(), &QItemSelectionModel::currentRowChanged, this, &MainWindow::SelectionChanged);
connect(tree_, &QTreeView::customContextMenuRequested, this, &MainWindow::ShowContextMenu); connect(tree_, &QTreeView::customContextMenuRequested, this, &MainWindow::ShowContextMenu);
splitter->addWidget(tree_); 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(); config_stack_ = new QStackedWidget();
splitter->addWidget(config_stack_); config_layout->addWidget(config_stack_);
panel_blank_ = new Panel(); panel_blank_ = new Panel();
config_stack_->addWidget(panel_blank_); config_stack_->addWidget(panel_blank_);
panel_wav_ = new WavPanel();
config_stack_->addWidget(panel_wav_);
panel_bmp_ = new BitmapPanel();
config_stack_->addWidget(panel_bmp_);
InitializeMenuBar(); InitializeMenuBar();
splitter->setSizes({99999, 99999}); splitter->setSizes({99999, 99999});
@ -43,8 +69,9 @@ void MainWindow::OpenFilename(const QString &s)
if (si.Read(s.toStdString())) { if (si.Read(s.toStdString())) {
SIViewDialog d(SIViewDialog::Import, &si, this); SIViewDialog d(SIViewDialog::Import, &si, this);
if (d.exec() == QDialog::Accepted) { if (d.exec() == QDialog::Accepted) {
Interleaf interleaf; model_.SetCore(nullptr);
interleaf.Parse(&si); interleaf_.Parse(&si);
model_.SetCore(&interleaf_);
} }
} else { } else {
QMessageBox::critical(this, QString(), tr("Failed to load Interleaf file.")); QMessageBox::critical(this, QString(), tr("Failed to load Interleaf file."));
@ -76,7 +103,7 @@ void MainWindow::InitializeMenuBar()
setMenuBar(menubar); 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()); auto current = static_cast<Panel*>(config_stack_->currentWidget());
current->SetData(nullptr); current->SetData(nullptr);
@ -84,6 +111,28 @@ void MainWindow::SetPanel(Panel *panel, si::Chunk *chunk)
config_stack_->setCurrentWidget(panel); config_stack_->setCurrentWidget(panel);
panel->SetData(chunk); panel->SetData(chunk);
last_set_data_ = 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() void MainWindow::OpenFile()
@ -97,10 +146,17 @@ void MainWindow::OpenFile()
void MainWindow::SelectionChanged(const QModelIndex &index) void MainWindow::SelectionChanged(const QModelIndex &index)
{ {
Panel *p = panel_blank_; Panel *p = panel_blank_;
Chunk *c = static_cast<Chunk*>(index.internalPointer()); Object *c = dynamic_cast<Object*>(static_cast<Core*>(index.internalPointer()));
if (c) { 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_) { if (p != config_stack_->currentWidget() || c != last_set_data_) {
@ -126,21 +182,13 @@ void MainWindow::ExtractSelectedItems()
} }
for (const QModelIndex &i : selected) { for (const QModelIndex &i : selected) {
if (Chunk *chunk = static_cast<Chunk*>(i.internalPointer())) { if (Object *obj = dynamic_cast<Object*>(static_cast<Core*>(i.internalPointer()))) {
QString filename(chunk->data("FileName")); ExtractObject(obj);
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); void MainWindow::ExtractClicked()
if (!s.isEmpty()) { {
//chunk->Export() ExtractObject(last_set_data_);
}
}
}
} }

View file

@ -1,12 +1,17 @@
#ifndef MAINWINDOW_H #ifndef MAINWINDOW_H
#define MAINWINDOW_H #define MAINWINDOW_H
#include <chunk.h> #include <interleaf.h>
#include <object.h>
#include <QGroupBox>
#include <QMainWindow> #include <QMainWindow>
#include <QStackedWidget> #include <QStackedWidget>
#include <QTreeView> #include <QTreeView>
#include "objectmodel.h"
#include "panel.h" #include "panel.h"
#include "viewer/bitmappanel.h"
#include "viewer/wavpanel.h"
class MainWindow : public QMainWindow class MainWindow : public QMainWindow
{ {
@ -23,15 +28,24 @@ private:
void InitializeMenuBar(); void InitializeMenuBar();
void SetPanel(Panel *panel, si::Chunk *chunk); void SetPanel(Panel *panel, si::Object *chunk);
void ExtractObject(si::Object *obj);
QStackedWidget *config_stack_; QStackedWidget *config_stack_;
QTreeView *tree_; 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: private slots:
void OpenFile(); void OpenFile();
@ -44,6 +58,8 @@ private slots:
void ExtractSelectedItems(); void ExtractSelectedItems();
void ExtractClicked();
}; };
#endif // MAINWINDOW_H #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 "objectmodel.h"
#include <chunk.h> #include <object.h>
#define super QAbstractItemModel #define super Model
using namespace si; using namespace si;
@ -16,49 +16,23 @@ int ObjectModel::columnCount(const QModelIndex &parent) const
return kColCount; 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 QVariant ObjectModel::data(const QModelIndex &index, int role) const
{ {
uint32_t offset; Core *c = GetCoreFromIndex(index);
Chunk *c = GetItem(index.row(), &offset);
switch (role) { switch (role) {
case Qt::DisplayRole: case Qt::DisplayRole:
switch (index.column()) { switch (index.column()) {
case kColIndex: case kColIndex:
return index.row(); if (Object *o = dynamic_cast<Object*>(c)) {
case kColOffset: return QString::number(o->id());
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"));
} }
//return index.row();
break;
case kColName:
if (Object *o = dynamic_cast<Object*>(c)) {
return QString::fromStdString(o->name());
} }
break; break;
} }
@ -75,8 +49,6 @@ QVariant ObjectModel::headerData(int section, Qt::Orientation orientation, int r
switch (section) { switch (section) {
case kColIndex: case kColIndex:
return tr("Index"); return tr("Index");
case kColOffset:
return tr("Offset");
case kColName: case kColName:
return tr("Name"); return tr("Name");
} }

View file

@ -1,9 +1,11 @@
#ifndef OBJECTMODEL_H #ifndef OBJECTMODEL_H
#define OBJECTMODEL_H #define OBJECTMODEL_H
#include <QAbstractItemModel> #include <interleaf.h>
class ObjectModel : public QAbstractItemModel #include "model.h"
class ObjectModel : public Model
{ {
Q_OBJECT Q_OBJECT
public: public:
@ -17,9 +19,6 @@ public:
explicit ObjectModel(QObject *parent = nullptr); explicit ObjectModel(QObject *parent = nullptr);
virtual int columnCount(const QModelIndex &parent = QModelIndex()) const override; 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 data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
virtual QVariant headerData(int section, Qt::Orientation orientation, 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: public:
explicit Panel(QWidget *parent = nullptr); explicit Panel(QWidget *parent = nullptr);
void *GetData() const { return data_; }
void SetData(void *data); void SetData(void *data);
signals: signals:

View file

@ -2,13 +2,12 @@
#include <iostream> #include <iostream>
#define super QAbstractItemModel #define super Model
using namespace si; using namespace si;
ChunkModel::ChunkModel(QObject *parent) : ChunkModel::ChunkModel(QObject *parent) :
super{parent}, super{parent}
chunk_(nullptr)
{ {
} }
@ -17,50 +16,9 @@ int ChunkModel::columnCount(const QModelIndex &parent) const
return kColCount; 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 QVariant ChunkModel::data(const QModelIndex &index, int role) const
{ {
Chunk *c = GetChunkFromIndex(index); Chunk *c = static_cast<Chunk*>(GetCoreFromIndex(index));
if (!c) { if (!c) {
return QVariant(); return QVariant();
} }
@ -108,19 +66,3 @@ QVariant ChunkModel::headerData(int section, Qt::Orientation orientation, int ro
return super::headerData(section, orientation, role); 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 #define CHUNKMODEL_H
#include <chunk.h> #include <chunk.h>
#include <QAbstractItemModel>
class ChunkModel : public QAbstractItemModel #include "model.h"
class ChunkModel : public Model
{ {
Q_OBJECT Q_OBJECT
public: public:
@ -21,22 +22,9 @@ public:
explicit ChunkModel(QObject *parent = nullptr); explicit ChunkModel(QObject *parent = nullptr);
virtual int columnCount(const QModelIndex &parent = QModelIndex()) const override; 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 data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
virtual QVariant headerData(int section, Qt::Orientation orientation, 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 #endif // CHUNKMODEL_H

View file

@ -71,6 +71,17 @@ MxChPanel::MxChPanel(QWidget *parent) :
data_sz_edit_->setMaximum(INT_MAX); data_sz_edit_->setMaximum(INT_MAX);
layout()->addWidget(data_sz_edit_, row, 1); 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(); FinishLayout();
} }
@ -99,6 +110,10 @@ void MxChPanel::OnClosingData(void *data)
chunk->data("Object") = u32(obj_edit_->value()); chunk->data("Object") = u32(obj_edit_->value());
chunk->data("Time") = u32(ms_offset_edit_->value()); chunk->data("Time") = u32(ms_offset_edit_->value());
chunk->data("DataSize") = u32(data_sz_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) void MxChPanel::FlagCheckBoxClicked(bool e)
@ -116,3 +131,15 @@ void MxChPanel::FlagCheckBoxClicked(bool e)
flag_edit_->setText(QString::number(current, 16)); 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 <QCheckBox>
#include <QLineEdit> #include <QLineEdit>
#include <QPushButton>
#include <QSpinBox> #include <QSpinBox>
#include <QTextEdit> #include <QTextEdit>
@ -23,12 +24,16 @@ private:
QSpinBox *obj_edit_; QSpinBox *obj_edit_;
QSpinBox *ms_offset_edit_; QSpinBox *ms_offset_edit_;
QSpinBox *data_sz_edit_; QSpinBox *data_sz_edit_;
QTextEdit *data_edit_;
QPushButton *show_data_btn_;
QVector<QCheckBox*> flag_checkboxes_; QVector<QCheckBox*> flag_checkboxes_;
private slots: private slots:
void FlagCheckBoxClicked(bool e); void FlagCheckBoxClicked(bool e);
void ShowDataField();
}; };
#endif // MXCHPANEL_H #endif // MXCHPANEL_H

View file

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