Soundux/mainwindow.cpp

551 lines
16 KiB
C++
Raw Normal View History

2019-09-15 01:44:12 +02:00
#include "mainwindow.h"
2019-11-10 02:37:54 +01:00
#include "ui_mainwindow.h"
2019-09-15 01:44:12 +02:00
static vector<QHotkey *> hotkeys;
static string configFolder;
static string soundFilesConfig;
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
2019-09-15 01:44:12 +02:00
{
ui->setupUi(this);
2019-11-10 02:37:54 +01:00
2019-10-08 00:31:44 +02:00
ui->tabWidget->setTabsClosable(true);
ui->tabWidget->setMovable(true);
ui->stopButton->setDisabled(true);
2019-09-15 01:44:12 +02:00
ui->remoteVolumeSlider->setStyle(new ClickableSliderStyle(ui->remoteVolumeSlider->style()));
ui->localVolumeSlider->setStyle(new ClickableSliderStyle(ui->localVolumeSlider->style()));
// Set the config variables
configFolder = QStandardPaths::standardLocations(QStandardPaths::ConfigLocation)[0].toStdString() + "/" + windowTitle().toStdString();
2020-03-24 21:18:12 +01:00
// Create config folder
QString configFolderQ = QString::fromStdString(configFolder);
QDir dir;
QFile configFolderFile(configFolderQ);
if (!configFolderFile.exists()) {
dir.mkdir(configFolderQ);
}
2020-03-24 21:18:12 +01:00
2019-10-08 00:31:44 +02:00
soundFilesConfig = configFolder + "/sounds.json";
2019-11-10 02:37:54 +01:00
soundPlayback = new SoundPlayback(this, ui);
settingsDialog = new SettingsDialog(this, configFolder, soundPlayback);
2019-09-15 01:44:12 +02:00
// Disable resizing
this->setFixedSize(this->width(), this->height());
// We unload the modules first to remove any possible leftovers
2019-09-15 01:44:12 +02:00
//TODO: Only remove modules created by Soundboard
system("pacmd unload-module module-null-sink");
system("pacmd unload-module module-loopback");
// Create null sink
system("pacmd load-module module-null-sink sink_name=soundboard_sink sink_properties=device.description=Soundboard-Sink");
// get default input device
string defaultInput = "";
char cmd[] = "pacmd dump";
2019-11-10 02:37:54 +01:00
string result = soundPlayback->getCommandOutput(cmd);
regex reg(R"rgx(set-default-source (.+))rgx");
smatch sm;
regex_search(result, sm, reg);
defaultInput = sm[1].str();
2019-09-15 01:44:12 +02:00
// Create loopback for input
2019-10-08 00:31:44 +02:00
if (defaultInput != "")
{
cout << "Found default input device " << defaultInput << endl;
auto createLoopBack = "pacmd load-module module-loopback source=\"" + defaultInput + "\" sink=\"soundboard_sink\"";
system(createLoopBack.c_str());
}
2019-09-15 01:44:12 +02:00
loadSoundFiles();
2019-11-10 02:37:54 +01:00
soundPlayback->loadSources();
// we need to update the buttons if the program starts because the first tab may be a directory tab
this->on_tabWidget_currentChanged(0);
2019-09-15 01:44:12 +02:00
}
void MainWindow::closeEvent(QCloseEvent *event)
{
//TODO: Only remove modules created by Soundboard
system("pacmd unload-module module-null-sink");
system("pacmd unload-module module-loopback");
//TODO: Switch all recording streams back to default device
2019-09-15 01:44:12 +02:00
event->accept();
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::syncVolume(bool remote)
{
// Get volume from slider
int localValue = ui->localVolumeSlider->value();
int remoteValue = ui->remoteVolumeSlider->value();
if (ui->syncCheckBox->isChecked()) {
if (remote) {
ui->localVolumeSlider->setValue(remoteValue);
} else {
ui->remoteVolumeSlider->setValue(localValue);
}
}
// TODO: this is disabled until I find a good solution
/*
char cmd[] = "pacmd list-sink-inputs";
string result = getCommandOutput(cmd);
string delimiter = "\n";
size_t pos = 0;
string currentLine;
// Tell me if there is a better way to parse the pulseaudio output
regex reg(R"rgx(((index: (\d+)))|(driver: )(.*)|(state: )(.*)|(flags: )(.*)|(source: .*)(<(.*)>)|(muted: )(.{0,3})|([a-zA-Z-.0-9_]*)\ =\ (\"(.*)\"))rgx");
smatch sm;
PulseAudioPlaybackStream *current = nullptr;
while ((pos = result.find(delimiter)) != string::npos)
{
currentLine = result.substr(0, pos);
if (regex_search(currentLine, sm, reg))
{
auto index = sm[3];
if (index.length() > 0)
{
if (current)
{
2019-11-10 02:37:54 +01:00
soundPlayback->checkAndChangeVolume(current, localValue);
}
current = new PulseAudioPlaybackStream();
current->index = stoi(index);
}
else
{
auto propertyName = sm[15];
auto propertyValue = sm[17];
if (propertyName.length() > 0)
{
if (propertyName == "application.name")
{
current->applicationName = propertyValue.str();
}
}
}
}
result.erase(0, pos + delimiter.length());
}
2019-11-10 02:37:54 +01:00
soundPlayback->checkAndChangeVolume(current, localValue);
*/
}
// Sync volume when the slider value has changed
void MainWindow::on_localVolumeSlider_valueChanged(int value)
{
syncVolume(false);
}
void MainWindow::on_remoteVolumeSlider_valueChanged(int value)
{
syncVolume(true);
}
2019-09-15 01:44:12 +02:00
void MainWindow::on_refreshAppsButton_clicked()
{
2019-11-10 02:37:54 +01:00
soundPlayback->loadSources();
2019-09-15 01:44:12 +02:00
}
void MainWindow::on_stopButton_clicked()
{
2019-11-10 02:37:54 +01:00
soundPlayback->stopSound();
2019-09-15 01:44:12 +02:00
}
2019-10-09 22:14:11 +02:00
void MainWindow::on_addFolderButton_clicked()
{
auto selectedFolder = QFileDialog::getExistingDirectory(this, ("Select folder"), QDir::homePath());
if (selectedFolder != "")
{
QDir directory(selectedFolder);
QFileInfo fileInfo(selectedFolder);
auto created = createTab(fileInfo.fileName());
created->directory = directory.absolutePath().toStdString();
QStringList files = directory.entryList({"*.mp3", "*.wav", "*.ogg"}, QDir::Files);
2019-10-09 22:14:11 +02:00
for (auto fileName : files)
{
QFile file(directory.absoluteFilePath(fileName));
addSoundToView(file, created);
}
saveSoundFiles();
}
}
void MainWindow::addSoundToView(QFile &file, QListWidget *widget)
{
QFileInfo fileInfo(file);
auto path = fileInfo.absoluteFilePath().toStdString();
for (QListWidgetItem *item : widget->findItems("*", Qt::MatchWildcard))
{
// Check if Sound is already added
if (path == item->toolTip().toStdString())
{
auto already = "The sound " + item->text().toStdString() + " is already in the list";
QMessageBox::warning(this, "", tr(already.c_str()), QMessageBox::Ok);
2019-10-09 22:14:11 +02:00
return;
}
}
auto item = new SoundListWidgetItem();
2019-11-10 22:19:22 +01:00
item->setText(fileInfo.completeBaseName());
2019-10-09 22:14:11 +02:00
item->setToolTip(fileInfo.absoluteFilePath());
widget->addItem(item);
}
2019-09-15 01:44:12 +02:00
void MainWindow::on_addSoundButton_clicked()
{
2019-10-08 00:31:44 +02:00
if (!getActiveView())
{
createTab("Main");
}
QStringList selectedFiles = QFileDialog::getOpenFileNames(this, tr("Select file"), QDir::homePath(), tr("Sound files (*.mp3 *.wav *.ogg)"));
2019-10-08 00:31:44 +02:00
for (auto selectedFile : selectedFiles)
{
if (selectedFile != "")
{
2019-10-06 23:25:17 +02:00
QFile file(selectedFile);
2019-10-09 22:14:11 +02:00
addSoundToView(file, getActiveView());
2019-10-06 23:25:17 +02:00
}
2019-09-15 01:44:12 +02:00
}
2019-10-09 22:14:11 +02:00
saveSoundFiles();
2019-09-15 01:44:12 +02:00
}
2019-11-10 02:37:54 +01:00
void MainWindow::on_settingsButton_clicked()
{
settingsDialog->exec();
}
2019-10-08 00:31:44 +02:00
void MainWindow::on_soundsListWidget_itemDoubleClicked(QListWidgetItem *listWidgetItem)
{
if (listWidgetItem)
{
2019-11-10 02:37:54 +01:00
soundPlayback->playSound(listWidgetItem->toolTip().toStdString());
}
}
2019-09-15 01:44:12 +02:00
void MainWindow::on_removeSoundButton_clicked()
{
2019-10-08 00:31:44 +02:00
if (getActiveView())
{
SoundListWidgetItem *it = getSelectedItem();
2019-10-08 00:31:44 +02:00
if (it)
{
unregisterHotkey(it);
2019-10-08 00:31:44 +02:00
delete it;
2019-10-09 22:14:11 +02:00
saveSoundFiles();
2019-10-08 00:31:44 +02:00
}
}
2019-09-15 01:44:12 +02:00
}
void MainWindow::on_clearSoundsButton_clicked()
{
2019-10-08 00:31:44 +02:00
if (getActiveView())
{
QMessageBox::StandardButton resBtn = QMessageBox::question(this, "Clear sounds", tr("Are you sure?\n"), QMessageBox::No | QMessageBox::Yes, QMessageBox::Yes);
if (resBtn == QMessageBox::Yes)
{
clearSoundFiles();
saveSoundFiles();
}
}
2019-09-15 01:44:12 +02:00
}
SoundListWidgetItem *MainWindow::getSelectedItem()
2019-09-15 01:44:12 +02:00
{
2019-10-08 00:31:44 +02:00
if (getActiveView())
{
return (SoundListWidgetItem*) getActiveView()->item(getActiveView()->currentRow());
}
return nullptr;
}
void MainWindow::on_playSoundButton_clicked()
{
SoundListWidgetItem *it = getSelectedItem();
if (it)
{
2019-11-10 02:37:54 +01:00
soundPlayback->playSound(it->toolTip().toStdString());
2019-10-08 00:31:44 +02:00
}
}
void MainWindow::on_addTabButton_clicked()
{
bool ok;
QString text = QInputDialog::getText(this, "Add a tab", "Tab Text:", QLineEdit::Normal, "", &ok);
2019-10-08 00:31:44 +02:00
if (ok && !text.isEmpty())
{
createTab(text);
saveSoundFiles();
2019-09-15 01:44:12 +02:00
}
}
void MainWindow::on_setHotkeyButton_clicked()
{
SoundListWidgetItem *it = getSelectedItem();
if (it)
{
SetHotkeyDialog shd(this, it);
2019-11-22 21:05:57 +01:00
auto clicked = shd.exec();
if (clicked == 1)
{
auto given = shd.getSequence();
if (!given.isNull()) {
registerHotkey(it, given.toString());
} else {
unregisterHotkey(it);
}
2019-11-22 21:05:57 +01:00
saveSoundFiles();
}
}
}
void MainWindow::registerHotkey(SoundListWidgetItem* it, QString keys)
{
// Unregister previous hotkey
unregisterHotkey(it);
it->setHotkey(keys);
2019-11-22 20:11:34 +01:00
cout << "register " << keys.toStdString() << endl;
auto neger = QKeySequence(keys);
auto hotkey = new QHotkey(QKeySequence(keys), true, this);
if (hotkey->isRegistered())
{
hotkeys.push_back(hotkey);
auto toPlay = it->toolTip().toStdString();
connect(hotkey, &QHotkey::activated, this, [=]() {
2019-11-10 02:37:54 +01:00
soundPlayback->playSound(toPlay);
});
}
else
{
unregisterHotkey(it);
QMessageBox::warning(this, "Could not register " + keys, "Either the key combination is not valid or it's not possible to use this combination (Maybe another program is using it)", QMessageBox::Ok);
}
}
bool compareChar(char &c1, char &c2)
{
if (c1 == c2)
return true;
else if (toupper(c1) == toupper(c2))
return true;
return false;
}
bool caseInSensStringCompare(string &str1, string &str2)
{
return ((str1.size() == str2.size()) &&
equal(str1.begin(), str1.end(), str2.begin(), &compareChar));
}
void MainWindow::unregisterHotkey(SoundListWidgetItem *it)
{
auto previousHotkey = it->hotkey;
if (!previousHotkey.isNull())
{
auto previousHotkeyStr = previousHotkey.toString().toStdString();
2019-11-22 20:11:34 +01:00
cout << "unregister " << previousHotkeyStr << endl;
for (QHotkey *hotkey : hotkeys)
{
auto hotkeyStr = hotkey->shortcut().toString().toStdString();
if (caseInSensStringCompare(hotkeyStr, previousHotkeyStr))
{
delete hotkey;
}
}
// Reset Data
it->setHotkey(QVariant());
}
}
2019-10-08 00:31:44 +02:00
void MainWindow::on_tabWidget_tabBarDoubleClicked(int index)
{
bool ok;
QString text = QInputDialog::getText(this, "Rename tab", "Tab Text:", QLineEdit::Normal, ui->tabWidget->tabText(index), &ok);
2019-10-08 00:31:44 +02:00
if (ok && !text.isEmpty())
2019-09-15 01:44:12 +02:00
{
2019-10-08 00:31:44 +02:00
ui->tabWidget->setTabText(index, text);
saveSoundFiles();
2019-09-15 01:44:12 +02:00
}
}
2019-10-08 00:31:44 +02:00
void MainWindow::on_tabWidget_tabCloseRequested(int index)
{
QMessageBox::StandardButton resBtn = QMessageBox::question(this, "Delete tab", tr("Are you sure?\n"), QMessageBox::No | QMessageBox::Yes, QMessageBox::Yes);
if (resBtn == QMessageBox::Yes)
{
ui->tabWidget->removeTab(index);
saveSoundFiles();
}
}
QSoundsList *MainWindow::createTab(QString title)
2019-10-08 00:31:44 +02:00
{
auto soundsListWidget = new QSoundsList();
2019-10-08 00:31:44 +02:00
soundsListWidget->setObjectName(title);
connect(soundsListWidget, SIGNAL(itemDoubleClicked(QListWidgetItem *)), this, SLOT(on_soundsListWidget_itemDoubleClicked(QListWidgetItem *)));
ui->tabWidget->addTab(soundsListWidget, title);
return soundsListWidget;
}
void MainWindow::clearSoundFiles()
{
if (getActiveView())
{
while (getActiveView()->count() > 0)
{
getActiveView()->takeItem(0);
}
}
}
QSoundsList *MainWindow::getActiveView()
2019-10-08 00:31:44 +02:00
{
return (QSoundsList *)ui->tabWidget->widget(ui->tabWidget->currentIndex());
2019-10-08 00:31:44 +02:00
}
void MainWindow::saveSoundFiles()
{
json jsonTabs = json::array();
for (auto i = 0; i < ui->tabWidget->count(); i++)
{
auto title = ui->tabWidget->tabText(i).toStdString();
QSoundsList *listWidget = (QSoundsList *)ui->tabWidget->widget(i);
2019-10-08 00:31:44 +02:00
json tabJson;
// if it is a directory we just save the path and update the sounds from there later
if (listWidget->directory.length() > 0) {
tabJson[title] = listWidget->directory;
} else {
json tabJsonSounds = json::array();
for (auto *_item : listWidget->findItems("*", Qt::MatchWildcard))
{
auto item = (SoundListWidgetItem*) _item;
json j;
j["name"] = item->text().toStdString();
j["path"] = item->toolTip().toStdString();
auto hotkey = item->hotkey;
if (!hotkey.isNull())
{
auto hotkeyStr = hotkey.toString().toStdString();
j["hotkey"] = hotkeyStr;
}
tabJsonSounds.push_back(j);
}
tabJson[title] = tabJsonSounds;
2019-10-08 00:31:44 +02:00
}
jsonTabs.push_back(tabJson);
2019-09-15 01:44:12 +02:00
}
ofstream myfile;
myfile.open(soundFilesConfig);
2019-10-08 00:31:44 +02:00
myfile << jsonTabs.dump();
2019-09-15 01:44:12 +02:00
myfile.close();
}
2019-10-08 00:31:44 +02:00
void MainWindow::loadSoundFiles()
{
ifstream fileIn(soundFilesConfig);
if (fileIn.is_open())
{
2019-10-09 22:14:11 +02:00
clearSoundFiles();
2019-09-15 01:44:12 +02:00
2019-10-09 22:14:11 +02:00
string content((istreambuf_iterator<char>(fileIn)), istreambuf_iterator<char>());
2019-09-15 01:44:12 +02:00
json j = json::parse(content);
2019-10-08 00:31:44 +02:00
for (auto item : j.get<vector<json>>())
{
for (auto object : item.items())
{
auto tabName = object.key().c_str();
auto soundsListWidget = createTab(tabName);
if (strcmp(object.value().type_name(), "array") == 0) {
auto childItems = object.value().get<vector<json>>();
for (auto _child : childItems)
{
auto soundName = _child["name"];
auto soundPath = _child["path"];
remove(soundPath.begin(), soundPath.end(), '"');
auto item = new SoundListWidgetItem();
item->setText(QString::fromStdString(soundName));
item->setToolTip(QString::fromStdString(soundPath));
auto soundHotkey = _child["hotkey"];
if (!soundHotkey.is_null())
{
// Set hotkey back
registerHotkey(item, QString::fromStdString(soundHotkey));
}
soundsListWidget->addItem(item);
}
} else if (strcmp(object.value().type_name(), "string") == 0) {
// it is a directory category so we update add the files from the directory
string directoryPath = object.value();
soundsListWidget->directory = directoryPath;
QDir directory(QString::fromStdString(directoryPath));
QStringList files = directory.entryList({"*.mp3", "*.wav", "*.ogg"}, QDir::Files);
for (auto fileName : files)
2019-10-08 00:31:44 +02:00
{
QFile file(directory.absoluteFilePath(fileName));
addSoundToView(file, soundsListWidget);
2019-10-08 00:31:44 +02:00
}
2019-10-08 00:31:44 +02:00
}
2019-09-15 01:44:12 +02:00
}
}
fileIn.close();
}
}
// we need this to update the remove/add/clear/refresh button if the tab switched to is a directory tab or not
void MainWindow::on_tabWidget_currentChanged(int index)
{
QSoundsList* switchedTo = (QSoundsList *)ui->tabWidget->widget(index);
if (switchedTo) {
this->ui->addSoundButton->setEnabled(switchedTo->directory.length() <= 0);
this->ui->removeSoundButton->setEnabled(switchedTo->directory.length() <= 0);
this->ui->clearSoundsButton->setEnabled(switchedTo->directory.length() <= 0);
}
}