aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
m---------visualization/SMELT0
-rw-r--r--visualization/qmpvisualization.cpp27
-rw-r--r--visualization/qmpvisualization.hpp6
-rw-r--r--visualization/renderer/main.cpp21
-rw-r--r--visualization/renderer/qmpsettingsro.cpp59
-rw-r--r--visualization/renderer/qmpsettingsro.hpp1
-rw-r--r--visualization/renderer/qmpvisrendercore.cpp126
-rw-r--r--visualization/renderer/qmpvisrendercore.hpp15
8 files changed, 207 insertions, 48 deletions
diff --git a/visualization/SMELT b/visualization/SMELT
-Subproject 87a25aa8fd3cb5a67747bff63711338e0f88b7c
+Subproject b83fb431fdff9329dc3cf9457f1934b6156eedf
diff --git a/visualization/qmpvisualization.cpp b/visualization/qmpvisualization.cpp
index e4e4428..d63b1e3 100644
--- a/visualization/qmpvisualization.cpp
+++ b/visualization/qmpvisualization.cpp
@@ -208,7 +208,7 @@ void qmpVisualization::reset()
}
}
-void qmpVisualization::switchToRenderMode(void(*frameCallback)(void*,size_t),bool _hidewindow)
+void qmpVisualization::switchToRenderMode(void(*frameCallback)(void*,size_t,uint32_t,uint32_t),bool _hidewindow)
{
rendermode=true;
framecb=frameCallback;
@@ -709,23 +709,26 @@ bool qmpVisualization::update()
}
}
if(osdpos==4){sm->smRenderEnd();return shouldclose;}
+ uint32_t ltpc=~0u;
for(uint32_t i=elb;i<pool.size();++i)
{
if(pool[i]->tcs>ctk)break;
if(pool[i]->ch==998)cts=pool[i]->key;
if(pool[i]->ch==997)cks=pool[i]->key;
if(pool[i]->ch==996)
- {
- ctp=pool[i]->key;
- if(rendermode)
- {
- lstk=ctk;
- cfr=1;
- }
- }
+ ltpc=i;
if(pool[i]->ch==995)cpbr[pool[i]->vel]=pool[i]->key;
if(pool[i]->ch==994)cpw[pool[i]->vel]=pool[i]->key;
}
+ if(~ltpc&&ctp!=pool[ltpc]->key)
+ {
+ ctp=pool[ltpc]->key;
+ if(rendermode)
+ {
+ lstk=pool[ltpc]->tcs;
+ cfr=1;
+ }
+ }
int t,r;t=cks;r=(int8_t)((t>>8)&0xFF)+7;t&=0xFF;
std::wstring ts(t?minors:majors,2*r,2);
int step=int(1.33*fontsize);
@@ -777,11 +780,11 @@ bool qmpVisualization::update()
if(rendermode)
{
if(ctk>api->getMaxTick())
- framecb(nullptr,0);
+ framecb(nullptr,0,ctk,api->getMaxTick());
else
{
sm->smPixelCopy(0,0,wwidth,wheight,4*wwidth*wheight,fbcont);
- framecb(fbcont,4*wwidth*wheight);
+ framecb(fbcont,4*wwidth*wheight,ctk,api->getMaxTick());
}
}
return shouldclose;
@@ -813,6 +816,7 @@ qmpVisualization::qmpVisualization(qmpPluginAPI* _api)
api=_api;
inst=this;
rendermode=false;
+ hidewindow=false;
}
qmpVisualization::~qmpVisualization()
{
@@ -824,7 +828,6 @@ void qmpVisualization::init()
h=new CMidiVisualHandler(this);
closeh=new CloseHandler(this);
rendererTh=nullptr;playing=false;
- hidewindow=false;
memset(rpnid,0xFF,sizeof(rpnid));
memset(rpnval,0xFF,sizeof(rpnval));
memset(spectra,0,sizeof(spectra));
diff --git a/visualization/qmpvisualization.hpp b/visualization/qmpvisualization.hpp
index 824093a..8a8d832 100644
--- a/visualization/qmpvisualization.hpp
+++ b/visualization/qmpvisualization.hpp
@@ -46,7 +46,7 @@ class qmpVisualization:public qmpPluginIntf,public qmpFuncBaseIntf
bool rendermode,hidewindow;
int herh,heh,hfrf;
int uihb,uihs,uihp,uihr,uihk;
- void(*framecb)(void*,size_t);
+ void(*framecb)(void*,size_t,uint32_t,uint32_t);
DWORD* fbcont;
std::vector<std::pair<uint32_t,uint32_t>>tspool;
int traveld[16][128];bool notestatus[16][128],lastnotestatus[16][128];
@@ -69,7 +69,7 @@ class qmpVisualization:public qmpPluginIntf,public qmpFuncBaseIntf
void stop();
void pause();
void reset();
- void switchToRenderMode(void(*frameCallback)(void*,size_t),bool _hidewindow);
+ void switchToRenderMode(void(*frameCallback)(void*,size_t,uint32_t,uint32_t),bool _hidewindow);
void init();
void deinit();
@@ -109,7 +109,7 @@ extern "C"{
{return new qmpVisualization(api);}
EXPORTSYM const char* qmpPluginGetAPIRev()
{return QMP_PLUGIN_API_REV;}
- EXPORTSYM void switchToRenderMode(void(*frameCallback)(void*,size_t),bool hidewindow)
+ EXPORTSYM void switchToRenderMode(void(*frameCallback)(void*,size_t,uint32_t,uint32_t),bool hidewindow)
{
if(qmpVisualization::instance())
qmpVisualization::instance()->switchToRenderMode(frameCallback,hidewindow);
diff --git a/visualization/renderer/main.cpp b/visualization/renderer/main.cpp
index ec4dd1d..cca8a12 100644
--- a/visualization/renderer/main.cpp
+++ b/visualization/renderer/main.cpp
@@ -2,17 +2,32 @@
#include <QCommandLineParser>
#include "qmpvisrendercore.hpp"
+#include "qmpsettingsro.hpp"
int main(int argc,char **argv)
{
QCoreApplication::setApplicationName("qmpvisrender");
+ QCoreApplication::setApplicationVersion("0.0.0");
QCoreApplication a(argc,argv);
QCommandLineParser clp;
clp.setApplicationDescription("Renderer a visualization of a midi file.");
clp.addHelpOption();
- clp.parse(a.arguments());
- qmpVisRenderCore core;
- core.loadVisualizationLibrary();
+ clp.addVersionOption();
+ clp.addOption({{"f","output-file"},"File name of the output file.","filename","output.mp4"});
+ clp.addOption({"ffmpeg-args","Custom output option arguments for ffmpeg.","args","-vf vflip -pix_fmt yuv420p -c:v libx264 -preset slow -crf 22"});
+ clp.addOption({"ffmpeg-pre-args","Custom arguments passed to ffmpeg before input arguments.","args",""});
+ clp.addOption({"ffmpeg-exec","Specify the path to the ffmpeg executable.","exec","ffmpeg"});
+ clp.addOption({{"s","show-window"},"Do not hide the visualization window."});
+ clp.addOption({{"c","config"},"Load options from the configuration file.","qmprc file"});
+ clp.addOption({{"o","option"},"Set option for the visualization module.","key-value pair"});
+ clp.addPositionalArgument("file","MIDI file to render");
+ clp.process(a.arguments());
+ qmpVisRenderCore core(&clp);
+ if(clp.positionalArguments().empty())
+ clp.showHelp(1);
+ if(!core.loadVisualizationLibrary())
+ return 1;
+ core.loadSettings();
if(clp.positionalArguments().size())
core.setMIDIFile(clp.positionalArguments().front().toStdString().c_str());
core.startRender();
diff --git a/visualization/renderer/qmpsettingsro.cpp b/visualization/renderer/qmpsettingsro.cpp
index cc6e0bf..a66943b 100644
--- a/visualization/renderer/qmpsettingsro.cpp
+++ b/visualization/renderer/qmpsettingsro.cpp
@@ -165,3 +165,62 @@ void qmpSettingsRO::load(const char *path)
settings.insert(k,qsettings->value(k));
}
}
+
+void qmpSettingsRO::setopt(std::string key, std::string val)
+{
+ if(options.find(key)==options.end())
+ {
+ std::string nkey="Visualization/"+key;
+ if(options.find(nkey)==options.end())
+ {
+ qDebug("invalid option key %s",key.c_str());
+ return;
+ }
+ else key=nkey;
+ }
+ char *rptr;
+ switch(options[key].type)
+ {
+ case qmpOptionR::ParameterType::parameter_int:
+ {
+ long long v=strtoll(val.c_str(),&rptr,10);
+ if(rptr==val.c_str()||v>INT_MAX||v<INT_MIN)
+ qDebug("invalid value for option %s",key.c_str());
+ setOptionInt(key,static_cast<int>(v));
+ }
+ break;
+ case qmpOptionR::ParameterType::parameter_uint:
+ {
+ long long v=strtoll(val.c_str(),&rptr,10);
+ if(rptr==val.c_str()||v>UINT32_MAX||v<0)
+ qDebug("invalid value for option %s",key.c_str());
+ setOptionUint(key,static_cast<uint32_t>(v));
+ }
+ break;
+ case qmpOptionR::ParameterType::parameter_double:
+ {
+ errno=0;
+ double v=strtod(val.c_str(),&rptr);
+ if(rptr==val.c_str()||errno)
+ qDebug("invalid value for option %s",key.c_str());
+ setOptionDouble(key,v);
+ }
+ break;
+ case qmpOptionR::ParameterType::parameter_bool:
+ {
+ if(val!="true"&&val!="false")
+ qDebug("invalid value for option %s",key.c_str());
+ setOptionBool(key,val=="true");
+ }
+ break;
+ case qmpOptionR::ParameterType::parameter_str:
+ case qmpOptionR::ParameterType::parameter_url:
+ setOptionString(key,val);
+ break;
+ case qmpOptionR::ParameterType::parameter_enum:
+ setOptionEnumIntOptName(key,val);
+ break;
+ default:
+ break;
+ }
+}
diff --git a/visualization/renderer/qmpsettingsro.hpp b/visualization/renderer/qmpsettingsro.hpp
index 30ab9b6..5c5361d 100644
--- a/visualization/renderer/qmpsettingsro.hpp
+++ b/visualization/renderer/qmpsettingsro.hpp
@@ -64,6 +64,7 @@ public:
void setOptionEnumIntOptName(std::string key,std::string valname);
void load(const char* path);
+ void setopt(std::string key,std::string val);
private:
std::map<std::string,qmpOptionR> options;
std::vector<std::string> optionlist;
diff --git a/visualization/renderer/qmpvisrendercore.cpp b/visualization/renderer/qmpvisrendercore.cpp
index fb1a7ef..a2733a7 100644
--- a/visualization/renderer/qmpvisrendercore.cpp
+++ b/visualization/renderer/qmpvisrendercore.cpp
@@ -8,11 +8,12 @@
#include <dlfcn.h>
#include <QProcess>
+#include <QCommandLineParser>
#include <QDebug>
#include <QThread>
qmpVisRenderCore *qmpVisRenderCore::inst=nullptr;
-qmpVisRenderCore::qmpVisRenderCore():QObject(nullptr)
+qmpVisRenderCore::qmpVisRenderCore(QCommandLineParser *_clp):QObject(nullptr),clp(_clp)
{
inst=this;
player=new CMidiPlayer();
@@ -21,17 +22,21 @@ qmpVisRenderCore::qmpVisRenderCore():QObject(nullptr)
msettings->registerOptionEnumInt("MIDI","Text encoding","Midi/TextEncoding",{"Unicode","Big5","Big5-HKSCS","CP949","EUC-JP","EUC-KR","GB18030","KOI8-R","KOI8-U","Macintosh","Shift-JIS"},0);
}
-void qmpVisRenderCore::loadVisualizationLibrary()
+bool qmpVisRenderCore::loadVisualizationLibrary()
{
mp=dlopen("libvisualization.so",RTLD_LAZY);
- if(!mp)fprintf(stderr,"failed to load visualization module!\n");
+ if(!mp)
+ {
+ fprintf(stderr,"failed to load the visualization module!\n");
+ return false;
+ }
GetInterface_func getintf=reinterpret_cast<GetInterface_func>(dlsym(mp,"qmpPluginGetInterface"));
SwitchMode_func switchmode=reinterpret_cast<SwitchMode_func>(dlsym(mp,"switchToRenderMode"));
vf=nullptr;
vp=getintf(api);
- switchmode(&qmpVisRenderCore::framefunc,false);
+ switchmode(&qmpVisRenderCore::framefunc,!clp->isSet("show-window"));
vp->init();
- msettings->load("/home/chrisoft/.config/qmprc");
+ return true;
}
void qmpVisRenderCore::unloadVisualizationLibrary()
@@ -40,6 +45,21 @@ void qmpVisRenderCore::unloadVisualizationLibrary()
dlclose(mp);
}
+void qmpVisRenderCore::loadSettings()
+{
+ if(clp->isSet("config"))
+ msettings->load(clp->value("config").toStdString().c_str());
+ for(auto &o:clp->values("option"))
+ {
+ int sp=o.indexOf('=');
+ if(!~sp)
+ qDebug("invalid option pair: %s",o.toStdString().c_str());
+ QString key=o.left(sp);
+ QString value=o.mid(sp+1);
+ msettings->setopt(key.toStdString(),value.toStdString());
+ }
+}
+
void qmpVisRenderCore::setMIDIFile(const char *url)
{
player->playerLoadFile(url);
@@ -51,38 +71,92 @@ void qmpVisRenderCore::startRender()
ffmpegproc=new QProcess();
ffmpegproc->setProgram("ffmpeg");
QStringList arguments;
+ arguments.append(split_arguments(clp->value("ffmpeg-pre-args")));
arguments
<<"-f"<<"rawvideo"
<<"-pixel_format"<<"rgba"
- <<"-video_size"<<"1600x900"
- <<"-framerate"<<"60"
+ <<"-video_size"<<QString("%1x%2").arg(msettings->getOptionInt("Visualization/wwidth")).arg(msettings->getOptionInt("Visualization/wheight"))
+ <<"-framerate"<<QString::number(msettings->getOptionInt("Visualization/tfps"))
<<"-i"<<"pipe:";
- arguments
- <<"-vf"<<"vflip"
- <<"-pix_fmt"<<"yuv420p"
- <<"-c:v"<<"libx264"
- <<"-preset"<<"fast"
- <<"-crf"<<"22";
- arguments<<"output.mp4";
+ arguments.append(split_arguments(clp->value("ffmpeg-args")));
+ arguments<<clp->value("output-file");
ffmpegproc->setArguments(arguments);
- ffmpegproc->start();
+ QMetaObject::Connection frameconn=connect(this,&qmpVisRenderCore::frameRendered,this,
+ [this,&frameconn](void* px,size_t sz,uint32_t c,uint32_t t)
+ {
+ if(sz)
+ {
+ if(!ffmpegproc->isOpen())return;
+ ffmpegproc->write(static_cast<const char*>(px),static_cast<qint64>(sz));
+ while(ffmpegproc->bytesToWrite()>1<<26)
+ ffmpegproc->waitForBytesWritten();
+ }
+ fprintf(stderr,"Rendered tick %u of %u, %.2f%% done.\r",c,t,100.*c/t);
+ if(c>t)
+ {
+ this->ffmpegproc->closeWriteChannel();
+ disconnect(frameconn);
+ qApp->exit(0);
+ }
+ },Qt::ConnectionType::BlockingQueuedConnection);
connect(ffmpegproc,QOverload<int,QProcess::ExitStatus>::of(&QProcess::finished),
- [this](int x,QProcess::ExitStatus){qDebug("%d",x);qDebug()<<this->ffmpegproc->readAllStandardError();});
- vf->show();
- startcb(nullptr,nullptr);
+ [this,&frameconn](int x,QProcess::ExitStatus st){
+ qDebug("%s",this->ffmpegproc->readAllStandardError().data());
+ disconnect(frameconn);
+ if(x||st==QProcess::ExitStatus::CrashExit)
+ qApp->exit(1);
+ else
+ qApp->exit(0);
+ });
+ QMetaObject::invokeMethod(this,[this](){
+ ffmpegproc->start();
+ vf->show();
+ startcb(nullptr,nullptr);
+ },Qt::ConnectionType::QueuedConnection);
}
-void qmpVisRenderCore::framefunc(void *px, size_t sz)
+QStringList qmpVisRenderCore::split_arguments(QString a)
{
- if(sz)
+ QStringList ret;
+ QString buf;
+ bool escaped=false;
+ for(int i=0;i<a.length();++i)
{
- inst->ffmpegproc->write((const char*)px,sz);
- while(inst->ffmpegproc->bytesToWrite()>1<<26)
+ if(a[i]=='\\')
+ {
+ if(escaped)
+ {
+ buf+='\\';
+ escaped=false;
+ }
+ else escaped=true;
+ }
+ else if(a[i]==' ')
{
- inst->ffmpegproc->waitForBytesWritten();
- QThread::yieldCurrentThread();
+ if(escaped)buf+=' ';
+ else
+ {
+ ret.append(buf);
+ buf.clear();
+ }
+ escaped=false;
+ }
+ else
+ {
+ if(escaped)
+ {
+ buf+='\\';
+ escaped=false;
+ }
+ buf+=a[i];
}
}
- else
- inst->ffmpegproc->closeWriteChannel();
+ if(buf.length())
+ ret.append(buf);
+ return ret;
+}
+
+void qmpVisRenderCore::framefunc(void *px, size_t sz,uint32_t curf,uint32_t totf)
+{
+ emit inst->frameRendered(px,sz,curf,totf);
}
diff --git a/visualization/renderer/qmpvisrendercore.hpp b/visualization/renderer/qmpvisrendercore.hpp
index 0c024f1..27337ba 100644
--- a/visualization/renderer/qmpvisrendercore.hpp
+++ b/visualization/renderer/qmpvisrendercore.hpp
@@ -9,6 +9,7 @@
class qmpPluginAPIStub;
class CMidiPlayer;
class qmpSettingsRO;
+class QCommandLineParser;
class QProcess;
@@ -16,14 +17,18 @@ class qmpVisRenderCore:public QObject
{
Q_OBJECT
public:
- qmpVisRenderCore();
- void loadVisualizationLibrary();
+ qmpVisRenderCore(QCommandLineParser *_clp);
+ bool loadVisualizationLibrary();
void unloadVisualizationLibrary();
+ void loadSettings();
void setMIDIFile(const char* url);
void startRender();
qmpSettingsRO* settings(){return msettings;}
+signals:
+ void frameRendered(void* px,size_t sz,uint32_t current_tick,uint32_t total_ticks);
+
private:
qmpPluginIntf *vp;
qmpFuncBaseIntf *vf;
@@ -33,11 +38,13 @@ private:
CMidiPlayer *player;
qmpSettingsRO *msettings;
QProcess *ffmpegproc;
+ QCommandLineParser *clp;
+ QStringList split_arguments(QString a);
typedef qmpPluginIntf*(*GetInterface_func)(qmpPluginAPI*);
- typedef void(*SwitchMode_func)(void(*frameCallback)(void*,size_t),bool hidewindow);
+ typedef void(*SwitchMode_func)(void(*frameCallback)(void*,size_t,uint32_t,uint32_t),bool hidewindow);
friend class qmpPluginAPIStub;
- static void framefunc(void* px,size_t sz);
+ static void framefunc(void* px, size_t sz, uint32_t curf, uint32_t totf);
static qmpVisRenderCore *inst;
};