aboutsummaryrefslogblamecommitdiff
path: root/visualization/renderer/qmpvisrendercore.cpp
blob: c4d107962b895df0eed5accc92a772b6a38e5a2e (plain) (tree)
1
2
3
4
5
6
7
8





                               
                    
                  





                                   
                  
      

                   
                             



                                                 
                                                                                       




                                       
                  


                                                                                                                                                                                                     
                                                 
 

















                                                                                                      




                                                                             



                                                                                                         
                                                                            
                   
                                 




                                      
                    







                                                   







                                                                            
                 
                                                                                  

                                 





                                                                         







                                                   
































                                                                                                                                                                            


                      



















                                                                                                                              
                 
                                                                                                     

                       
                                                          



                                                        










                                                                                         

                                                

                                        


                                                

 
                                                                                     
 


                           
                          
                                     
         














                                              
                 




                                             







                                              
                 




                                             


                                            

                                                        





                                            
                                                        
                         





















                                                                                
                         

                 







                                                                                 
 
#include "qmpvisrendercore.hpp"
#include "qmppluginapistub.hpp"
#include "qmpsettingsro.hpp"
#include "qmpmidiplay.hpp"
#include "qmpcorepublic.hpp"

#include <algorithm>
#include <cassert>
#ifdef _WIN32
#include <windows.h>
#define dlopen(a,b) LoadLibraryW(a)
#define dlsym GetProcAddress
#define dlclose FreeLibrary
#else
#include <dlfcn.h>
#endif

#include <QProcess>
#include <QCommandLineParser>
#include <QDebug>
#include <QThread>
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<std::wstring> libpath={
		QCoreApplication::applicationDirPath().toStdWString()+L"/plugins/libvisualization.dll"
		L"libvisualization.dll",
		L"../libvisualization.dll"//for debugging only...?
	};
#else
	std::vector<std::string> 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<GetInterface_func>(dlsym(mp,"qmpPluginGetInterface"));
	SwitchMode_func switchmode=reinterpret_cast<SwitchMode_func>(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"<<QString("%1x%2").arg(msettings->getOptionInt("Visualization/wwidth")).arg(msettings->getOptionInt("Visualization/wheight"))
				<<"-framerate"<<QString::number(msettings->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<const char*>(px),static_cast<qint64>(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<int,QProcess::ExitStatus>::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<QChar,QVariant> subst)
{
	QStringList ret;
	QString buf;
	bool escaped=false;
	bool substi=false;
	for(int i=0;i<a.length();++i)
	{
		if(a[i]=='%')
		{
			if(escaped)
			{
				buf+='%';
				escaped=false;
			}
			else if(substi)
			{
				buf+='%';
				substi=false;
			}
			else substi=true;
		}
		else if(a[i]=='\\')
		{
			if(substi)
			{
				buf+='%';
				substi=false;
			}
			if(escaped)
			{
				buf+='\\';
				escaped=false;
			}
			else escaped=true;
		}
		else if(a[i]==' ')
		{
			if(substi)
			{
				buf+='%';
				substi=false;
			}
			if(escaped)buf+=' ';
			else
			{
				if(buf.length())
					ret.append(buf);
				buf.clear();
			}
			escaped=false;
		}
		else
		{
			if(substi&&subst.contains(a[i]))
			{
				if(subst[a[i]].canConvert(QMetaType::QString))
					buf+=subst[a[i]].toString();
				else
				{
					if(buf.length())
					{
						ret.append(buf);
						buf.clear();
					}
					for(auto &it:subst[a[i]].toStringList())
						ret.append(it);
				}
				substi=false;
			}
			else
			{
				if(escaped)
				{
					buf+='\\';
					escaped=false;
				}
				buf+=a[i];
			}
		}
	}
	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);
}