Sigrid Solveig Haflínudóttirbeep boopf627f682 years agoHistory
300 lines · 6.8 KB · C++
RawDownload
#include <QAction>#include <QAnyStringView>#include <QClipboard>#include <QCloseEvent>#include <QCoreApplication>#include <QElapsedTimer>#include <QEvent>#include <QGuiApplication>#include <QIcon>#include <QIODeviceBase>#include <QJsonDocument>#include <QJsonObject>#include <QMenu>#include <QSettings>#include <AL/al.h>#include <AL/alc.h>#include <AL/alext.h>#include "window.hpp" static int alcheckerr(const char *file, int line);static ALCcontext *ctx;static ALCdevice *dev;static ALuint albuf, alsrc;static QElapsedTimer underrun; typedef ALsizei (*nalBufferCallbackTypeSOFT)(ALvoid *aux, ALvoid *data, ALsizei numbytes);typedef void (*nalBufferCallbackSOFTType)(ALuint buf, ALenum fmt, ALsizei freq, nalBufferCallbackTypeSOFT cb, ALvoid *aux); static nalBufferCallbackSOFTType nalBufferCallbackSOFT; #define ALERR() alcheckerr(__FILE__, __LINE__) // Generalstatic const QAnyStringView windowVisible = "windowVisible";// Speechstatic const QAnyStringView speechGroup = "Speech",	speechModel = "model",	speechVolume = "volume",	speechLengthScale = "lengthScale",	speechUseCUDA = "useCUDA"; Window::Window(){	settings = new QSettings(this);	quitAction = new QAction(tr("&Quit"), this);	connect(quitAction, &QAction::triggered, this, &Window::quit); 	showAction = new QAction(tr("&Configure"), this);	connect(showAction, &QAction::triggered, this, &Window::makeVisible); 	tMenu = new QMenu(this);	tMenu->addAction(showAction);	tMenu->addAction(quitAction); 	tIcon = new QSystemTrayIcon(this);	tIcon->setContextMenu(tMenu);	connect(tIcon, &QSystemTrayIcon::activated, this, &Window::iconActivated);	tIcon->setIcon(QIcon(":/res/systray.svg"));	tIcon->show();	setWindowTitle(tr("Nublyat")); 	if(settings->value(windowVisible, true).toBool())		show(); 	clip = QGuiApplication::clipboard();	connect(clip, &QClipboard::selectionChanged, this, &Window::selectionChanged);	playing = false;	textChanged = false; 	if((piper = startPiper(nullptr)) == nullptr ||	   (extra = startPiper(nullptr)) == nullptr ||	   !startAudio())		qApp->quit();} static ALsizeiaudiocb(ALvoid *aux, ALvoid *data, ALsizei numbytes){	QProcess *in = (QProcess*)aux;	char *b; 	ALsizei n;	for(b = (char*)data; numbytes > 0; b += n, numbytes -= n){		n = in->read(b, numbytes);		if(n <= 0){			if(!underrun.isValid())				underrun.start();			memset(b, 0, numbytes);			b += numbytes;			break;		}	}	return b - (char*)data;} boolWindow::startAudio(){	ALCint attrs[] = {0};	int e; 	dev = nullptr;	ctx = nullptr;	dev = alcOpenDevice(alcGetString(nullptr, ALC_ALL_DEVICES_SPECIFIER)); ALERR();	if(dev == nullptr)		return false;	ctx = alcCreateContext(dev, attrs);	if(ctx == nullptr){		ALERR();closedev:		alcCloseDevice(dev); ALERR();		dev = nullptr;		return false;	}	e = alcMakeContextCurrent(ctx); ALERR();	if(!e){closectx: 		alcDestroyContext(ctx); ALERR(); 		ctx = nullptr;		goto closedev;	} 	if(!alIsExtensionPresent("AL_SOFT_callback_buffer")){		qCritical("no AL_SOFT_callback_buffer available");		goto closectx;	} 	settings->beginGroup(speechGroup);	alListenerf(AL_GAIN, settings->value(speechVolume, 1.0f).toFloat()); ALERR();	settings->endGroup();	alDistanceModel(AL_LINEAR_DISTANCE_CLAMPED); ALERR(); 	nalBufferCallbackSOFT = (nalBufferCallbackSOFTType)alGetProcAddress("alBufferCallbackSOFT"); ALERR();	alsrc = albuf = 0;	return true;} voidWindow::stop(){	QProcess *tmp;	alDeleteSources(1, &alsrc); ALERR(); alsrc = 0;	alDeleteBuffers(1, &albuf); ALERR(); albuf = 0;	tmp = extra;	extra = startPiper(piper);	piper = tmp;	playing = false;} voidWindow::play(){	piper->waitForReadyRead(-1);	underrun.invalidate();	alGenSources(1, &alsrc); ALERR();	alSourcei(alsrc, AL_SOURCE_SPATIALIZE_SOFT, AL_FALSE); ALERR();	alGenBuffers(1, &albuf); ALERR();	nalBufferCallbackSOFT(albuf, AL_FORMAT_MONO16, 22050, audiocb, piper); ALERR();	alSourcei(alsrc, AL_BUFFER, albuf); ALERR();	alSourcePlay(alsrc); ALERR();	playing = true;} voidWindow::makeVisible(){	settings->setValue(windowVisible, true);	show();} QProcess *Window::startPiper(QProcess *piper){	bool result = false; 	settings->beginGroup(speechGroup);	if(piper != nullptr){		piper->terminate();		piper->waitForFinished(-1);	}else{		piper = new QProcess(this);	} 	QString model = settings->value(speechModel).toString();	if(!model.isEmpty()){		QStringList args;		args << "--output-raw"			<< "--json-input"			<< "-m" << model			<< "--length_scale" << settings->value(speechLengthScale, 1.0).toString();		if(settings->value(speechUseCUDA).toBool())			args << "--use-cuda";		piper->setProcessChannelMode(QProcess::ForwardedErrorChannel);		piper->start("piper", args, QIODeviceBase::ReadWrite);		result = true;	}else {		qWarning("no model configured, can't speak");	}	settings->endGroup(); 	return result ? piper : nullptr;} voidWindow::selectionChanged(){	QString newText = clip->text(QClipboard::Selection);	if((textChanged = !(text == newText)))		text = newText;} voidWindow::closeEvent(QCloseEvent *ev){	if(ev->spontaneous()){		settings->setValue(windowVisible, false);		hide();		ev->ignore();	}} voidWindow::quit(){	piper->terminate();	piper->waitForFinished(-1);	qApp->quit();} voidWindow::iconActivated(QSystemTrayIcon::ActivationReason reason){	switch(reason){	case QSystemTrayIcon::Trigger:		if(playing){			stop();			if(!textChanged)				break;		}		textChanged = false;		if(!text.isEmpty()){			for(auto part : text.split('\n', Qt::SkipEmptyParts)) {				QJsonObject j;				j["text"] = part;				qInfo(QJsonDocument(j).toJson(QJsonDocument::Compact));				piper->write(QJsonDocument(j).toJson(QJsonDocument::Compact));				piper->write("\n");			}			play();		}		break;	case QSystemTrayIcon::MiddleClick:		if(playing)			stop();		break;	default:		break;	}} static intalcheckerr(const char *file, int line){	int e, ret;	const char *s;	char tmp[32]; 	ret = 0;	if(ctx != nullptr && (e = alGetError()) != AL_NO_ERROR){		switch(e){		case AL_INVALID_NAME: s = "invalid name"; break;		case AL_INVALID_ENUM: s = "invalid enum"; break;		case AL_INVALID_VALUE: s = "invalid value"; break;		case AL_INVALID_OPERATION: s = "invalid operation"; break;		case AL_OUT_OF_MEMORY: s = "out of memory"; break;		default:			snprintf(tmp, sizeof(tmp), "unknown (0x%x)", e);			s = tmp;			break;		}		ret |= e;		qWarning("AL: %s:%d: %s", file, line, s);	}	if(dev != nullptr && (e = alcGetError(dev)) != ALC_NO_ERROR){		switch(e){		case ALC_INVALID_DEVICE: s = "invalid device"; break;		case ALC_INVALID_ENUM: s = "invalid enum"; break;		case ALC_INVALID_VALUE: s = "invalid value"; break;		case ALC_INVALID_CONTEXT: s = "invalid context"; break;		case ALC_OUT_OF_MEMORY: s = "out of memory"; break;		default:			snprintf(tmp, sizeof(tmp), "unknown error (0x%x)", e);			s = tmp;			break;		}		ret |= e;		qWarning("ALC: %s:%d: %s\n", file, line, s);	} 	return ret;}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t