Play-/tools/PsfPlayer/Source/ui_qt/mainwindow.cpp
2023-06-05 09:26:01 -04:00

352 lines
8.7 KiB
C++

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "PsfLoader.h"
#include "PsfTags.h"
#include "AppConfig.h"
#ifdef WIN32
#include "ui_win32/SH_WaveOut.h"
#else
#include "SH_OpenAL.h"
#endif
#include <QFileDialog>
#include <QMessageBox>
#include <QDateTime>
#include <chrono>
#define PREFERENCE_UI_LASTFOLDER "ui.lastfolder"
MainWindow::MainWindow(QWidget* parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
, model(this)
{
ui->setupUi(this);
m_virtualMachine = new CPsfVm();
#ifdef WIN32
m_virtualMachine->SetSpuHandler(&CSH_WaveOut::HandlerFactory);
#else
m_virtualMachine->SetSpuHandler(&CSH_OpenAL::HandlerFactory);
#endif
m_OnNewFrameConnection = m_virtualMachine->OnNewFrame.Connect([&]() { OnNewFrame(); });
m_debugger = std::make_unique<DebuggerWindow>(*m_virtualMachine);
model.setHeaderData(0, Qt::Orientation::Horizontal, QVariant("Game"), Qt::DisplayRole);
model.setHeaderData(1, Qt::Orientation::Horizontal, QVariant("Title"), Qt::DisplayRole);
model.setHeaderData(2, Qt::Orientation::Horizontal, QVariant("Length"), Qt::DisplayRole);
ui->tableView->setModel(&model);
ui->tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
ui->tableView->verticalHeader()->setSectionResizeMode(QHeaderView::Fixed);
CAppConfig::GetInstance().RegisterPreferenceString(PREFERENCE_UI_LASTFOLDER, QDir::homePath().toStdString().c_str());
m_path = CAppConfig::GetInstance().GetPreferenceString(PREFERENCE_UI_LASTFOLDER);
// used as workaround to avoid direct ui access from a thread
connect(this, SIGNAL(ChangeRow(int)), ui->tableView, SLOT(selectRow(int)));
m_running = true;
m_thread = std::thread(&MainWindow::UiUpdateLoop, this);
}
void MainWindow::UiUpdateLoop()
{
while(m_running)
{
auto end = std::chrono::steady_clock::now() + std::chrono::milliseconds(16);
if(m_frames > m_trackLength)
{
m_frames = 0;
on_nextButton_clicked();
}
else if((m_frames >= m_fadePosition) && (m_trackLength != m_fadePosition))
{
float currentRatio = static_cast<float>(m_trackLength - m_fadePosition) / static_cast<float>(m_trackLength - m_frames);
float currentVolume = (1 / currentRatio) * m_volumeAdjust;
m_virtualMachine->SetVolumeAdjust(currentVolume);
}
else if(m_trackLength > 0 && m_frames > 0 && m_fadePosition > 0)
{
float currentRatio = 1 / (static_cast<float>(m_trackLength - m_fadePosition) / static_cast<float>(m_frames));
if(currentRatio < 1.0f)
{
float currentVolume = currentRatio * m_volumeAdjust;
m_virtualMachine->SetVolumeAdjust(currentVolume);
}
}
std::this_thread::sleep_until(end);
}
}
MainWindow::~MainWindow()
{
m_running = false;
if(m_thread.joinable()) m_thread.join();
m_debugger.reset();
if(m_virtualMachine != nullptr)
{
m_virtualMachine->Pause();
delete m_virtualMachine;
m_virtualMachine = nullptr;
}
delete ui;
}
void MainWindow::on_actionOpen_triggered()
{
QFileDialog dialog(this);
dialog.setFileMode(QFileDialog::ExistingFiles);
dialog.setDirectory(QString(m_path.c_str()));
dialog.setNameFilter(tr("PSF files (*.psf *.psf2 *.psfp *.minipsf *.minipsf2 *.minipsfp)"));
if(dialog.exec())
{
if(m_virtualMachine != nullptr)
{
m_virtualMachine->Pause();
m_virtualMachine->Reset();
int index = model.rowCount();
foreach(QString file, dialog.selectedFiles())
{
AddFileToPlaylist(file.toStdString());
}
m_path = QFileInfo(model.at(index)->path.c_str()).absolutePath().toStdString();
CAppConfig::GetInstance().SetPreferenceString(PREFERENCE_UI_LASTFOLDER, m_path.c_str());
PlayTrackIndex(index);
}
}
}
void MainWindow::AddFileToPlaylist(const fs::path& filePath)
{
try
{
//TODO: Use PlaylistDiscoveryService
auto streamProvider = CreatePsfStreamProvider(fs::path());
std::unique_ptr<Framework::CStream> inputStream(streamProvider->GetStreamForPath(filePath.native()));
CPsfBase psfFile(*inputStream);
auto tags = CPsfTags::TagMap(psfFile.GetTagsBegin(), psfFile.GetTagsEnd());
model.addPlaylistItem(filePath.string(), tags);
}
catch(const std::exception& e)
{
QMessageBox messageBox;
messageBox.critical(0, "Error", e.what());
messageBox.show();
}
}
void MainWindow::ShowDebugger()
{
m_debugger->showMaximized();
m_debugger->raise();
m_debugger->activateWindow();
}
void MainWindow::UpdateTrackDetails(CPsfBase::TagMap& tags)
{
auto tag = CPsfTags(tags);
ui->current_game->setText(QString::fromWCharArray(tag.GetTagValue("game").c_str()));
ui->current_total_length->setText(QString::fromWCharArray(tag.GetTagValue("length").c_str()));
ui->current_title->setText(QString::fromWCharArray(tag.GetTagValue("title").c_str()));
ui->current_artist->setText(QString::fromWCharArray(tag.GetTagValue("artist").c_str()));
ui->current_cp->setText(QString::fromWCharArray(tag.GetTagValue("copyright").c_str()));
ui->current_year->setText(QString::fromWCharArray(tag.GetTagValue("year").c_str()));
m_volumeAdjust = 1.0f;
try
{
m_volumeAdjust = stof(tag.GetTagValue("volume"));
}
catch(...)
{
}
double dlength = CPsfTags::ConvertTimeString(tag.GetTagValue("length").c_str());
double dfade = CPsfTags::ConvertTimeString(tag.GetTagValue("fade").c_str());
m_trackLength = static_cast<uint64>(dlength * 60.0);
m_fadePosition = m_trackLength - static_cast<uint64>(dfade * 60.0);
m_frames = 0;
m_virtualMachine->SetVolumeAdjust((dfade > 0) ? 0 : m_volumeAdjust);
ChangeRow(m_currentindex);
}
void MainWindow::OnNewFrame()
{
m_frames++;
ui->current_time->setText(QDateTime::fromTime_t(m_frames / 60).toUTC().toString("mm:ss"));
}
void MainWindow::on_tableView_customContextMenuRequested(const QPoint& pos)
{
auto item = ui->tableView->indexAt(pos);
if(item.isValid())
{
QMenu* menu = new QMenu(this);
auto playAct = new QAction("Play", this);
auto delAct = new QAction("Remove", this);
menu->addAction(playAct);
menu->addAction(delAct);
menu->popup(ui->tableView->viewport()->mapToGlobal(pos));
auto index = item.row();
connect(playAct, &QAction::triggered, std::bind(&MainWindow::PlayTrackIndex, this, index));
connect(delAct, &QAction::triggered, std::bind(&MainWindow::DeleteTrackIndex, this, index));
}
}
void MainWindow::UnloadCurrentTrack()
{
if(m_currentindex == -1)
{
return;
}
m_virtualMachine->Pause();
#ifdef DEBUGGER_INCLUDED
auto item = model.at(m_currentindex);
auto itemPath = fs::path(item->path);
auto tagPackageName = itemPath.stem().string();
m_virtualMachine->SaveDebugTags(tagPackageName.c_str());
#endif
m_currentindex = -1;
}
void MainWindow::PlayTrackIndex(int index)
{
UnloadCurrentTrack();
m_virtualMachine->Reset();
m_currentindex = index;
CPsfBase::TagMap tags;
auto item = model.at(m_currentindex);
auto itemPath = fs::path(item->path);
CPsfLoader::LoadPsf(*m_virtualMachine, itemPath.native(), "", &tags);
UpdateTrackDetails(tags);
#ifdef DEBUGGER_INCLUDED
auto tagPackageName = itemPath.stem().string();
m_virtualMachine->LoadDebugTags(tagPackageName.c_str());
m_debugger->Reset();
#else
m_virtualMachine->Resume();
#endif
}
void MainWindow::closeEvent(QCloseEvent* event)
{
UnloadCurrentTrack();
#ifdef DEBUGGER_INCLUDED
m_debugger->close();
#endif
}
void MainWindow::on_tableView_doubleClicked(const QModelIndex& index)
{
PlayTrackIndex(index.row());
}
void MainWindow::DeleteTrackIndex(int index)
{
auto playlistsize = model.removePlaylistItem(index);
if(index == m_currentindex)
{
if(playlistsize >= m_currentindex)
{
PlayTrackIndex(m_currentindex);
}
else if(m_currentindex > 0 && playlistsize == m_currentindex - 1)
{
PlayTrackIndex(--m_currentindex);
}
else
{
if(m_virtualMachine->GetStatus() == CVirtualMachine::STATUS::RUNNING)
{
m_virtualMachine->Pause();
m_virtualMachine->Reset();
}
m_currentindex--;
}
}
else if(index < m_currentindex)
{
m_currentindex--;
}
}
void MainWindow::on_playpauseButton_clicked()
{
if(m_currentindex > -1)
{
if(m_virtualMachine != nullptr)
{
if(m_virtualMachine->GetStatus() == CVirtualMachine::STATUS::RUNNING)
{
m_virtualMachine->Pause();
}
else
{
m_virtualMachine->Resume();
}
}
}
else
{
on_actionOpen_triggered();
}
}
void MainWindow::on_nextButton_clicked()
{
if(model.rowCount() < 1) return;
int currentindex = m_currentindex;
if(m_currentindex < model.rowCount() - 1)
{
m_currentindex += 1;
}
else
{
m_currentindex = 0;
}
if(currentindex == m_currentindex) return;
PlayTrackIndex(m_currentindex);
}
void MainWindow::on_prevButton_clicked()
{
if(model.rowCount() < 1) return;
if(m_currentindex > 0)
{
m_currentindex -= 1;
}
else
{
m_currentindex = model.rowCount() - 1;
}
PlayTrackIndex(m_currentindex);
}
void MainWindow::on_actionPlayPause_triggered()
{
on_playpauseButton_clicked();
}
void MainWindow::on_actionPrev_triggered()
{
on_prevButton_clicked();
}
void MainWindow::on_actionNext_triggered()
{
on_nextButton_clicked();
}