diff options
m--------- | visualization/SMELT | 0 | ||||
-rw-r--r-- | visualization/qmpvisualization.cpp | 27 | ||||
-rw-r--r-- | visualization/qmpvisualization.hpp | 6 | ||||
-rw-r--r-- | visualization/renderer/main.cpp | 21 | ||||
-rw-r--r-- | visualization/renderer/qmpsettingsro.cpp | 59 | ||||
-rw-r--r-- | visualization/renderer/qmpsettingsro.hpp | 1 | ||||
-rw-r--r-- | visualization/renderer/qmpvisrendercore.cpp | 126 | ||||
-rw-r--r-- | visualization/renderer/qmpvisrendercore.hpp | 15 |
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; }; |