#include "qmpvisrendercore.hpp" #include "qmppluginapistub.hpp" #include "qmpsettingsro.hpp" #include "qmpmidiplay.hpp" #include "qmpcorepublic.hpp" #include #include #ifdef _WIN32 #include #define dlopen(a,b) LoadLibraryW(a) #define dlsym(a,b) GetProcAddress((HMODULE)a,b) #define dlclose(a) FreeLibrary((HMODULE)a) #else #include #endif #include #include #include #include qmpVisRenderCore *qmpVisRenderCore::inst=nullptr; qmpVisRenderCore::qmpVisRenderCore(QCommandLineParser *_clp):QObject(nullptr),clp(_clp) { inst=this; player=new CMidiPlayer(); api=new qmpPluginAPIStub(this); msettings=new qmpSettingsRO(); frameno=0; 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); } bool qmpVisRenderCore::loadVisualizationLibrary() { #ifdef _WIN32 std::vector libpath={ QCoreApplication::applicationDirPath().toStdWString()+L"/plugins/libvisualization.dll", L"libvisualization.dll", L"../libvisualization.dll"//for debugging only...? }; #else std::vector libpath={ QCoreApplication::applicationDirPath().toStdString()+"/plugins/libvisualization.so", QT_STRINGIFY(INSTALL_PREFIX)+std::string("/lib/qmidiplayer/libvisualization.so"), "../libvisualization.so"//for debugging only }; #endif for(auto&l:libpath) { mp=dlopen(l.c_str(),RTLD_LAZY); if(mp)break; } if(!mp) { fprintf(stderr,"failed to load the visualization module!\n"); return false; } GetInterface_func getintf=reinterpret_cast(dlsym(mp,"qmpPluginGetInterface")); SwitchMode_func switchmode=reinterpret_cast(dlsym(mp,"switchToRenderMode")); vf=nullptr; vp=getintf(api); switchmode(&qmpVisRenderCore::framefunc,!clp->isSet("show-window")); vp->init(); resetcb(nullptr,nullptr); if(clp->isSet("list-options")) { msettings->listopt(); exit(0); } return true; } void qmpVisRenderCore::unloadVisualizationLibrary() { vp->deinit(); 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()); continue; } 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); } void qmpVisRenderCore::startRender() { assert(vf); subst={ {'w',QString::number(msettings->getOptionInt("Visualization/wwidth"))}, {'h',QString::number(msettings->getOptionInt("Visualization/wheight"))}, {'r',QString::number(msettings->getOptionInt("Visualization/tfps"))}, {'i', QStringList() <<"-f"<<"rawvideo" <<"-pixel_format"<<"rgba" <<"-video_size"<getOptionInt("Visualization/wwidth")).arg(msettings->getOptionInt("Visualization/wheight")) <<"-framerate"<getOptionInt("Visualization/tfps")) <<"-i"<<"pipe:" }, {'o',clp->value("output-file")} }; if(clp->value("receiver-execution")=="per-frame") { subst['o']=clp->value("output-file").replace("%f",QString("%1").arg(frameno,6,10,QChar('0'))); oneshot=false; } else { oneshot=true; if(clp->value("receiver-execution")!="one-shot") qWarning("Invalid value set for --receiver-execution. Using default value."); } rxproc=new QProcess(); QStringList arguments=process_arguments(clp->value("receiver"),subst); assert(arguments.length()>0); rxproc->setProgram(arguments.front()); arguments.pop_front(); rxproc->setArguments(arguments); frameconn=connect(this,&qmpVisRenderCore::frameRendered,this, [this](void* px,size_t sz,uint32_t c,uint32_t t) { if(sz) { if(!oneshot) { subst['f']=QString("%1").arg(frameno,6,10,QChar('0')); subst['o']=clp->value("output-file").replace("%f",QString("%1").arg(frameno,6,10,QChar('0'))); frameno++; QStringList arguments=process_arguments(clp->value("receiver"),subst); arguments.pop_front(); rxproc->setArguments(arguments); rxproc->start(); rxproc->waitForStarted(); } if(!rxproc->isOpen())return; rxproc->write(static_cast(px),static_cast(sz)); while(rxproc->bytesToWrite()>(oneshot?(1<<26):0)) rxproc->waitForBytesWritten(); if(!oneshot) { rxproc->closeWriteChannel(); rxproc->waitForFinished(-1); } } fprintf(stderr,"Rendered tick %u of %u, %.2f%% done.\r",c,t,std::min(100.,100.*c/t)); if(c>t) { this->rxproc->closeWriteChannel(); disconnect(frameconn); qApp->exit(0); } },Qt::ConnectionType::BlockingQueuedConnection); connect(rxproc,QOverload::of(&QProcess::finished), [this](int x,QProcess::ExitStatus st){ qDebug("%s",this->rxproc->readAllStandardError().data()); if(oneshot) { disconnect(frameconn); if(x||st==QProcess::ExitStatus::CrashExit) qApp->exit(1); else qApp->exit(0); } }); QMetaObject::invokeMethod(this,[this](){ if(oneshot) rxproc->start(); vf->show(); startcb(nullptr,nullptr); },Qt::ConnectionType::QueuedConnection); } QStringList qmpVisRenderCore::process_arguments(QString a,QMap subst) { QStringList ret; QString buf; bool escaped=false; bool substi=false; for(int i=0;iframeRendered(px,sz,curf,totf); }