2019-09-18 19:56:50 +02:00
|
|
|
|
2019-09-15 01:44:12 +02:00
|
|
|
#include "mainwindow.h"
|
|
|
|
#include "./ui_mainwindow.h"
|
|
|
|
|
|
|
|
/*
|
|
|
|
*
|
|
|
|
* TODO: Find another way how to play it for myself and others (maybe just loopback the default output to the sink monitor)
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
static vector<PulseAudioRecordingStream *> streams;
|
|
|
|
|
2019-10-06 01:28:46 +02:00
|
|
|
static string configFolder;
|
|
|
|
static string soundFilesConfig;
|
|
|
|
|
2019-09-15 01:44:12 +02:00
|
|
|
MainWindow::MainWindow(QWidget *parent)
|
2019-10-08 00:31:44 +02:00
|
|
|
: QMainWindow(parent), ui(new Ui::MainWindow)
|
2019-09-15 01:44:12 +02:00
|
|
|
{
|
|
|
|
ui->setupUi(this);
|
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
|
|
|
|
2019-10-07 01:43:35 +02:00
|
|
|
// Set the config variables
|
2019-10-06 01:28:46 +02:00
|
|
|
configFolder = QStandardPaths::standardLocations(QStandardPaths::ConfigLocation)[0].toStdString() + "/" + windowTitle().toStdString();
|
2019-10-08 00:31:44 +02:00
|
|
|
if (!filesystem::exists(configFolder))
|
|
|
|
{
|
2019-10-06 01:28:46 +02:00
|
|
|
filesystem::create_directory(configFolder);
|
|
|
|
}
|
2019-10-08 00:31:44 +02:00
|
|
|
soundFilesConfig = configFolder + "/sounds.json";
|
2019-10-06 01:28:46 +02:00
|
|
|
|
2019-09-15 01:44:12 +02:00
|
|
|
// Disable resizing
|
|
|
|
this->setFixedSize(this->width(), this->height());
|
|
|
|
|
|
|
|
//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");
|
|
|
|
|
|
|
|
// Create loopback for output devices (so that you can hear it)
|
|
|
|
//system("pacmd load-module module-loopback source=\"soundboard_sink.monitor\"");
|
|
|
|
|
2019-09-15 02:50:45 +02:00
|
|
|
// get default input device
|
|
|
|
string defaultInput = "";
|
|
|
|
char cmd[] = "pacmd dump";
|
|
|
|
string result = 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 != "")
|
|
|
|
{
|
2019-10-06 01:28:46 +02:00
|
|
|
cout << "Found default input device " << defaultInput << endl;
|
2019-09-15 02:50:45 +02:00
|
|
|
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();
|
|
|
|
loadSources();
|
|
|
|
}
|
|
|
|
|
|
|
|
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");
|
2019-10-06 01:32:51 +02:00
|
|
|
//TODO: Switch all recording streams back to default device
|
2019-09-15 01:44:12 +02:00
|
|
|
event->accept();
|
|
|
|
}
|
|
|
|
|
2019-10-08 00:31:44 +02:00
|
|
|
string MainWindow::getCommandOutput(char cmd[])
|
|
|
|
{
|
2019-09-15 01:44:12 +02:00
|
|
|
array<char, 1028> buffer;
|
|
|
|
string result;
|
|
|
|
unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd, "r"), pclose);
|
2019-10-08 00:31:44 +02:00
|
|
|
if (!pipe)
|
|
|
|
{
|
2019-09-15 01:44:12 +02:00
|
|
|
throw runtime_error("popen() failed!");
|
|
|
|
}
|
2019-10-08 00:31:44 +02:00
|
|
|
while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr)
|
|
|
|
{
|
2019-09-15 01:44:12 +02:00
|
|
|
result += buffer.data();
|
|
|
|
}
|
2019-09-15 02:50:45 +02:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2019-10-08 00:31:44 +02:00
|
|
|
bool MainWindow::isValidDevice(PulseAudioRecordingStream *stream)
|
|
|
|
{
|
2019-09-15 02:50:45 +02:00
|
|
|
return !strstr(stream->source.c_str(), ".monitor") && !strstr(stream->flags.c_str(), "DONT_MOVE");
|
|
|
|
}
|
|
|
|
|
2019-10-08 00:31:44 +02:00
|
|
|
bool MainWindow::loadSources()
|
|
|
|
{
|
2019-09-15 02:50:45 +02:00
|
|
|
// Save previously selected applicaton
|
|
|
|
auto previouslySelected = ui->outputApplication->currentText();
|
|
|
|
|
|
|
|
streams.clear();
|
|
|
|
ui->outputApplication->clear();
|
|
|
|
|
|
|
|
char cmd[] = "pacmd list-source-outputs";
|
|
|
|
string result = getCommandOutput(cmd);
|
2019-09-15 01:44:12 +02:00
|
|
|
string delimiter = "\n";
|
|
|
|
size_t pos = 0;
|
|
|
|
string currentLine;
|
|
|
|
|
2019-10-17 23:17:10 +02:00
|
|
|
// Tell me if there is a better way to parse the pulseaudio output
|
2019-09-15 01:44:12 +02:00
|
|
|
regex reg(R"rgx(((index: (\d+)))|(driver: )(.*)|(state: )(.*)|(flags: )(.*)|(source: .*)(<(.*)>)|(muted: )(.{0,3})|([a-zA-Z-.0-9_]*)\ =\ (\"(.*)\"))rgx");
|
|
|
|
smatch sm;
|
|
|
|
|
2019-10-08 00:31:44 +02:00
|
|
|
PulseAudioRecordingStream *current = nullptr;
|
2019-09-15 01:44:12 +02:00
|
|
|
|
2019-10-08 00:31:44 +02:00
|
|
|
while ((pos = result.find(delimiter)) != string::npos)
|
|
|
|
{
|
2019-09-15 01:44:12 +02:00
|
|
|
currentLine = result.substr(0, pos);
|
2019-10-08 00:31:44 +02:00
|
|
|
if (regex_search(currentLine, sm, reg))
|
|
|
|
{
|
2019-09-15 01:44:12 +02:00
|
|
|
auto index = sm[3];
|
2019-10-08 00:31:44 +02:00
|
|
|
if (index.length() > 0)
|
|
|
|
{
|
2019-09-15 01:44:12 +02:00
|
|
|
|
2019-10-08 00:31:44 +02:00
|
|
|
if (current && isValidDevice(current))
|
|
|
|
{
|
2019-09-15 01:44:12 +02:00
|
|
|
streams.push_back(current);
|
|
|
|
}
|
|
|
|
|
|
|
|
current = new PulseAudioRecordingStream();
|
|
|
|
current->index = stoi(index);
|
2019-10-08 00:31:44 +02:00
|
|
|
}
|
|
|
|
else if (current)
|
|
|
|
{
|
2019-09-15 01:44:12 +02:00
|
|
|
|
|
|
|
auto driver = sm[5];
|
|
|
|
auto state = sm[7];
|
|
|
|
auto flags = sm[9];
|
|
|
|
auto source = sm[12];
|
|
|
|
auto muted = sm[14];
|
|
|
|
auto propertyName = sm[15];
|
|
|
|
auto propertyValue = sm[17];
|
|
|
|
|
2019-10-08 00:31:44 +02:00
|
|
|
if (driver.length() > 0)
|
|
|
|
{
|
2019-09-15 01:44:12 +02:00
|
|
|
current->driver = driver.str();
|
|
|
|
}
|
2019-10-08 00:31:44 +02:00
|
|
|
if (state.length() > 0)
|
|
|
|
{
|
2019-09-15 01:44:12 +02:00
|
|
|
current->state = state.str();
|
|
|
|
}
|
2019-10-08 00:31:44 +02:00
|
|
|
if (flags.length() > 0)
|
|
|
|
{
|
2019-09-15 01:44:12 +02:00
|
|
|
current->flags = flags.str();
|
|
|
|
}
|
2019-10-08 00:31:44 +02:00
|
|
|
if (source.length() > 0)
|
|
|
|
{
|
2019-09-15 01:44:12 +02:00
|
|
|
current->source = source.str();
|
|
|
|
}
|
2019-10-08 00:31:44 +02:00
|
|
|
if (muted.length() > 0)
|
|
|
|
{
|
2019-09-15 01:44:12 +02:00
|
|
|
current->muted = muted == "yes" ? true : false;
|
|
|
|
}
|
2019-10-08 00:31:44 +02:00
|
|
|
if (propertyName.length() > 0)
|
|
|
|
{
|
|
|
|
if (propertyName == "application.name")
|
|
|
|
{
|
2019-09-15 01:44:12 +02:00
|
|
|
current->applicationName = propertyValue.str();
|
|
|
|
}
|
2019-10-08 00:31:44 +02:00
|
|
|
if (propertyName == "application.process.id")
|
|
|
|
{
|
2019-09-15 01:44:12 +02:00
|
|
|
current->processId = stoi(propertyValue);
|
|
|
|
}
|
2019-10-08 00:31:44 +02:00
|
|
|
if (propertyName == "application.process.binary")
|
|
|
|
{
|
2019-09-15 01:44:12 +02:00
|
|
|
current->processBinary = propertyValue.str();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
result.erase(0, pos + delimiter.length());
|
|
|
|
}
|
2019-10-08 00:31:44 +02:00
|
|
|
if (isValidDevice(current))
|
|
|
|
{
|
2019-09-15 01:44:12 +02:00
|
|
|
streams.push_back(current);
|
|
|
|
}
|
|
|
|
|
2019-10-08 00:31:44 +02:00
|
|
|
for (auto stream : streams)
|
|
|
|
{
|
|
|
|
if (stream->driver == "<protocol-native.c>")
|
|
|
|
{
|
2019-09-15 01:44:12 +02:00
|
|
|
ui->outputApplication->addItem(QString(stream->processBinary.c_str()));
|
|
|
|
}
|
|
|
|
}
|
2019-09-15 02:50:45 +02:00
|
|
|
|
|
|
|
// This automatically sets the selected item to the previous one. if it does not exists it does nothing
|
|
|
|
ui->outputApplication->setCurrentText(previouslySelected);
|
|
|
|
|
|
|
|
// Return if the output was not changed
|
|
|
|
return ui->outputApplication->currentText() == previouslySelected;
|
2019-09-15 01:44:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
MainWindow::~MainWindow()
|
|
|
|
{
|
|
|
|
delete ui;
|
|
|
|
}
|
|
|
|
|
2019-10-08 00:31:44 +02:00
|
|
|
void MainWindow::playSound(string path)
|
|
|
|
{
|
|
|
|
//TODO: Remove this and stop old playback or enable multiple sounds at once (maybe create a setting for it)
|
2019-10-09 22:14:11 +02:00
|
|
|
if (ui->stopButton->isEnabled())
|
|
|
|
{
|
2019-10-08 00:31:44 +02:00
|
|
|
return;
|
|
|
|
}
|
2019-09-15 01:44:12 +02:00
|
|
|
|
2019-09-15 02:50:45 +02:00
|
|
|
// Don't play the sound if the app changed (previous one no longer available)
|
2019-10-08 00:31:44 +02:00
|
|
|
if (!loadSources())
|
|
|
|
{
|
2019-09-15 02:50:45 +02:00
|
|
|
QMessageBox::warning(this, "", tr("Output stream no longer available...\nAborting\n"), QMessageBox::Ok);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-10-17 23:30:50 +02:00
|
|
|
bool isMP3 = strstr(path.c_str(), ".mp3");
|
|
|
|
|
|
|
|
if (isMP3)
|
|
|
|
{
|
|
|
|
ostringstream mpg123Check;
|
2019-10-17 23:32:06 +02:00
|
|
|
mpg123Check << "which mpg123 >/dev/null 2>&1";
|
2019-10-17 23:30:50 +02:00
|
|
|
bool canPlayMP3 = (system(mpg123Check.str().c_str()) == 0);
|
|
|
|
if (!canPlayMP3)
|
|
|
|
{
|
|
|
|
QMessageBox::critical(this, "", tr("Can't play mp3 file!\nmpg123 is not installed\nPlease install it and restart the program\n"), QMessageBox::Ok);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-15 01:44:12 +02:00
|
|
|
// Get selected application
|
|
|
|
string selectedApp = ui->outputApplication->currentText().toStdString();
|
2019-10-08 00:31:44 +02:00
|
|
|
PulseAudioRecordingStream *selected = nullptr;
|
|
|
|
for (auto stream : streams)
|
|
|
|
{
|
|
|
|
if (stream->processBinary == selectedApp)
|
|
|
|
{
|
2019-09-15 01:44:12 +02:00
|
|
|
selected = stream;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-08 00:31:44 +02:00
|
|
|
if (selected)
|
|
|
|
{
|
2019-09-15 01:44:12 +02:00
|
|
|
int index = selected->index;
|
|
|
|
string source = selected->source;
|
|
|
|
|
2019-10-06 01:28:46 +02:00
|
|
|
cout << "Source before was " << source << endl;
|
|
|
|
|
2019-09-15 01:44:12 +02:00
|
|
|
auto moveToSink = "pacmd move-source-output " + to_string(index) + " soundboard_sink.monitor";
|
|
|
|
auto moveBack = "pacmd move-source-output " + to_string(index) + " " + source;
|
|
|
|
|
2019-10-08 00:31:44 +02:00
|
|
|
// Switch recording stream device to game sink
|
2019-09-15 01:44:12 +02:00
|
|
|
system(moveToSink.c_str());
|
|
|
|
|
2019-10-17 23:17:10 +02:00
|
|
|
auto forMe = std::thread([=]() {
|
|
|
|
auto cmdForMe = "paplay \"" + path + "\"";
|
2019-10-17 23:30:50 +02:00
|
|
|
if (isMP3)
|
2019-10-17 23:17:10 +02:00
|
|
|
{
|
|
|
|
cmdForMe = "mpg123 -o pulse \"" + path + "\"";
|
|
|
|
}
|
2019-10-06 01:28:46 +02:00
|
|
|
system(cmdForMe.c_str());
|
2019-09-15 01:44:12 +02:00
|
|
|
});
|
2019-10-17 23:17:10 +02:00
|
|
|
forMe.detach();
|
2019-09-15 01:44:12 +02:00
|
|
|
|
2019-10-17 23:17:10 +02:00
|
|
|
auto forOthers = std::thread([=]() {
|
2019-10-08 00:31:44 +02:00
|
|
|
ui->stopButton->setDisabled(false);
|
2019-10-17 23:17:10 +02:00
|
|
|
auto cmdForOthers = "paplay -d soundboard_sink \"" + path + "\"";
|
2019-10-17 23:30:50 +02:00
|
|
|
if (isMP3)
|
2019-10-17 23:17:10 +02:00
|
|
|
{
|
|
|
|
cmdForOthers = "mpg123 -o pulse -a soundboard_sink \"" + path + "\"";
|
|
|
|
}
|
2019-10-06 01:28:46 +02:00
|
|
|
system(cmdForOthers.c_str());
|
|
|
|
// Switch recording stream device back
|
|
|
|
system(moveBack.c_str());
|
2019-10-08 00:31:44 +02:00
|
|
|
ui->stopButton->setDisabled(true);
|
2019-10-17 23:17:10 +02:00
|
|
|
// Repeat when the check box is checked
|
|
|
|
if (ui->repeatCheckBox->isChecked())
|
|
|
|
{
|
|
|
|
playSound(path);
|
|
|
|
}
|
2019-10-06 01:28:46 +02:00
|
|
|
});
|
2019-10-17 23:17:10 +02:00
|
|
|
forOthers.detach();
|
2019-09-15 01:44:12 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-18 00:03:05 +02:00
|
|
|
void MainWindow::checkAndChangeVolume(PulseAudioPlaybackStream *stream, int value)
|
|
|
|
{
|
|
|
|
// TODO: Only set it when this was created by Soundboard
|
|
|
|
// Set the volume if the application is paplay or mpg123
|
|
|
|
if (stream->applicationName == "paplay" || stream->applicationName == "mpg123")
|
|
|
|
{
|
|
|
|
system(("pacmd set-sink-input-volume " + to_string(stream->index) + " " + to_string(value)).c_str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-17 23:17:10 +02:00
|
|
|
void MainWindow::syncVolume()
|
|
|
|
{
|
|
|
|
// Get volume from slider
|
|
|
|
int value = ui->volumeSlider->value();
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
2019-10-18 00:03:05 +02:00
|
|
|
PulseAudioPlaybackStream *current = nullptr;
|
2019-10-17 23:17:10 +02:00
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2019-10-18 00:03:05 +02:00
|
|
|
if (current)
|
|
|
|
{
|
|
|
|
checkAndChangeVolume(current, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
current = new PulseAudioPlaybackStream();
|
|
|
|
current->index = stoi(index);
|
2019-10-17 23:17:10 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
auto propertyName = sm[15];
|
|
|
|
auto propertyValue = sm[17];
|
|
|
|
if (propertyName.length() > 0)
|
|
|
|
{
|
|
|
|
if (propertyName == "application.name")
|
|
|
|
{
|
2019-10-18 00:03:05 +02:00
|
|
|
current->applicationName = propertyValue.str();
|
2019-10-17 23:17:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
result.erase(0, pos + delimiter.length());
|
|
|
|
}
|
2019-10-18 00:03:05 +02:00
|
|
|
checkAndChangeVolume(current, value);
|
2019-10-17 23:17:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Sync volume when the slider value has changed
|
|
|
|
void MainWindow::on_volumeSlider_valueChanged(int value)
|
|
|
|
{
|
|
|
|
syncVolume();
|
|
|
|
}
|
|
|
|
|
2019-09-15 01:44:12 +02:00
|
|
|
void MainWindow::on_refreshAppsButton_clicked()
|
|
|
|
{
|
|
|
|
loadSources();
|
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::on_stopButton_clicked()
|
|
|
|
{
|
2019-10-17 23:17:10 +02:00
|
|
|
// Fix continuous playback
|
|
|
|
if (ui->repeatCheckBox->isChecked())
|
|
|
|
{
|
|
|
|
ui->repeatCheckBox->setChecked(false);
|
|
|
|
}
|
|
|
|
//TODO: Only kill players started from Soundboard
|
2019-10-06 01:28:46 +02:00
|
|
|
system("killall mpg123");
|
2019-10-17 23:17:10 +02:00
|
|
|
system("killall paplay");
|
2019-10-08 00:31:44 +02:00
|
|
|
ui->stopButton->setDisabled(true);
|
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());
|
|
|
|
|
2019-10-17 23:17:10 +02:00
|
|
|
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())
|
|
|
|
{
|
2019-10-17 23:17:10 +02:00
|
|
|
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 QListWidgetItem();
|
|
|
|
item->setText(fileInfo.baseName());
|
|
|
|
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");
|
|
|
|
}
|
2019-10-17 23:17:10 +02:00
|
|
|
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-10-08 00:31:44 +02:00
|
|
|
void MainWindow::on_soundsListWidget_itemDoubleClicked(QListWidgetItem *listWidgetItem)
|
|
|
|
{
|
|
|
|
if (listWidgetItem)
|
|
|
|
{
|
2019-10-07 01:43:35 +02:00
|
|
|
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())
|
|
|
|
{
|
|
|
|
QListWidgetItem *it = getActiveView()->takeItem(getActiveView()->currentRow());
|
|
|
|
if (it)
|
|
|
|
{
|
|
|
|
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 02:50:45 +02:00
|
|
|
}
|
2019-09-15 01:44:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::on_playSoundButton_clicked()
|
|
|
|
{
|
2019-10-08 00:31:44 +02:00
|
|
|
if (getActiveView())
|
|
|
|
{
|
|
|
|
QListWidgetItem *it = getActiveView()->item(getActiveView()->currentRow());
|
|
|
|
if (it)
|
|
|
|
{
|
|
|
|
playSound(it->toolTip().toStdString());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::on_addTabButton_clicked()
|
|
|
|
{
|
|
|
|
bool ok;
|
|
|
|
QString text = QInputDialog::getText(0, "Add a tab", "Tab Text:", QLineEdit::Normal, "", &ok);
|
|
|
|
if (ok && !text.isEmpty())
|
|
|
|
{
|
|
|
|
createTab(text);
|
2019-10-09 22:44:06 +02:00
|
|
|
saveSoundFiles();
|
2019-09-15 01:44:12 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-08 00:31:44 +02:00
|
|
|
void MainWindow::on_tabWidget_tabBarDoubleClicked(int index)
|
|
|
|
{
|
|
|
|
bool ok;
|
|
|
|
QString text = QInputDialog::getText(0, "Rename tab", "Tab Text:", QLineEdit::Normal, ui->tabWidget->tabText(index), &ok);
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
QListWidget *MainWindow::createTab(QString title)
|
|
|
|
{
|
|
|
|
auto soundsListWidget = new QListWidget();
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
QListWidget *MainWindow::getActiveView()
|
|
|
|
{
|
|
|
|
return (QListWidget *)ui->tabWidget->widget(ui->tabWidget->currentIndex());
|
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::saveSoundFiles()
|
|
|
|
{
|
|
|
|
json jsonTabs = json::array();
|
|
|
|
|
|
|
|
for (auto i = 0; i < ui->tabWidget->count(); i++)
|
|
|
|
{
|
|
|
|
auto title = ui->tabWidget->tabText(i).toStdString();
|
|
|
|
QListWidget *listWidget = (QListWidget *)ui->tabWidget->widget(i);
|
|
|
|
|
|
|
|
json tabJson;
|
|
|
|
json tabJsonSounds = json::array();
|
|
|
|
|
|
|
|
for (QListWidgetItem *item : listWidget->findItems("*", Qt::MatchWildcard))
|
|
|
|
{
|
|
|
|
json j;
|
|
|
|
j[item->text().toStdString()] = item->toolTip().toStdString();
|
|
|
|
tabJsonSounds.push_back(j);
|
|
|
|
}
|
|
|
|
|
|
|
|
tabJson[title] = tabJsonSounds;
|
|
|
|
jsonTabs.push_back(tabJson);
|
2019-09-15 01:44:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
ofstream myfile;
|
2019-10-06 01:28:46 +02:00
|
|
|
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);
|
|
|
|
|
|
|
|
auto childItems = object.value().get<vector<json>>();
|
|
|
|
for (auto _child : childItems)
|
|
|
|
{
|
|
|
|
for (auto child : _child.items())
|
|
|
|
{
|
|
|
|
auto soundName = child.key();
|
|
|
|
auto soundPath = child.value();
|
|
|
|
remove(soundPath.begin(), soundPath.end(), '"');
|
|
|
|
|
|
|
|
auto item = new QListWidgetItem();
|
|
|
|
item->setText(QString::fromStdString(soundName));
|
|
|
|
item->setToolTip(QString::fromStdString(soundPath));
|
|
|
|
soundsListWidget->addItem(item);
|
|
|
|
}
|
|
|
|
}
|
2019-09-15 01:44:12 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fileIn.close();
|
|
|
|
}
|
2019-10-08 00:31:44 +02:00
|
|
|
}
|