diff --git a/.gitmodules b/.gitmodules index 03b4c9c..094663a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "miniaudio"] path = miniaudio url = https://github.com/mackron/miniaudio.git +[submodule "Log"] + path = Log + url = https://git.okaestne.de/okaestne/Log.git diff --git a/Log b/Log new file mode 160000 index 0000000..96d10d6 --- /dev/null +++ b/Log @@ -0,0 +1 @@ +Subproject commit 96d10d6e72ddaffd8b688db5ef2705e38aff3a75 diff --git a/include/config.h b/include/config.h new file mode 100644 index 0000000..a57b273 --- /dev/null +++ b/include/config.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#include +#include + +class Config { +public: + Config(); + ~Config(); + + bool hasChanged(); + void load(); + + struct RootConfig { + struct AudioConfig { + std::vector devices; + } audio; + + struct ButtonConfig { + std::string name; + std::string file; + std::string key; + uint64_t offset; + uint64_t length; + + bool isValid() const; + }; + + std::vector buttons; + std::string audioPath; + } rootConfig; + +private: + std::string file = "soundboard.json"; +}; \ No newline at end of file diff --git a/include/mainwindow.h b/include/mainwindow.h index 7929798..37647ed 100644 --- a/include/mainwindow.h +++ b/include/mainwindow.h @@ -2,13 +2,14 @@ #define MAINWINDOW_H #include -#include -#include "qxtglobalshortcut.h" +#include +#include "config.h" #include "sound.h" +#include "soundbutton.h" QT_BEGIN_NAMESPACE -namespace Ui { class MainWindow; } +namespace Ui { class Soundboard; } QT_END_NAMESPACE class MainWindow : public QMainWindow @@ -20,17 +21,15 @@ public: ~MainWindow(); public slots: - void buttonPressed(unsigned int id); - void buttonPressed0(); - void buttonPressed1(); - void shortcut(); - void shortcut2(); + void reloadConfig(); + +signals: + void newStatusMessage(const QString&); private: - void playSound(unsigned int id); - Ui::MainWindow *ui; - QShortcut* sc; - QxtGlobalShortcut* globalShortcut; + Ui::Soundboard *ui; + std::vector soundbuttons; + Config config; }; #endif // MAINWINDOW_H diff --git a/include/sound.h b/include/sound.h index 0143b8e..db214ea 100644 --- a/include/sound.h +++ b/include/sound.h @@ -18,6 +18,7 @@ public: bool addDefaultDevice(); bool addDeviceWithName(const std::string& name); void stopAll(); + void reset(); const static std::string FOLDER; private: diff --git a/include/soundbutton.h b/include/soundbutton.h new file mode 100644 index 0000000..a9fff00 --- /dev/null +++ b/include/soundbutton.h @@ -0,0 +1,35 @@ +#pragma once + +#include +#include +#include +#include + +#include "qxtglobalshortcut.h" + +class SoundButton : public QObject { + Q_OBJECT + +public: + SoundButton(const std::string& filename, const std::string& name_ = "", const std::string& keycombo = ""); + ~SoundButton(); + + const std::string& getName() const; + const std::string& getFile() const; + const std::string& getKeyCombo() const; + + QPushButton* getButton(); + +public slots: + void play(); + +private: + static uint64_t nextid; + uint64_t id; + std::string name; + std::string file; + std::string keycombo; + + QxtGlobalShortcut* globalShortcut = nullptr; + QPushButton* button = nullptr; +}; \ No newline at end of file diff --git a/src/config.cpp b/src/config.cpp new file mode 100644 index 0000000..d32f058 --- /dev/null +++ b/src/config.cpp @@ -0,0 +1,60 @@ +#include "config.h" + +#include +#include +#include + +#include +using json = nlohmann::json; + +template +static void readVector(std::vector& v, const json& j) { + v.clear(); + v.reserve(j.size()); + std::copy(j.begin(), j.end(), std::insert_iterator>(v, v.begin())); +} + +Config::Config() { +} + +Config::~Config() { +} + +void from_json(const json& j, Config::RootConfig::AudioConfig& ac) { + readVector(ac.devices, j.value("devices", json::array())); +} + +void from_json(const json& j, Config::RootConfig::ButtonConfig& bc) { + bc.name = j.value("name", ""); + bc.file = j.value("file", ""); + bc.key = j.value("key", ""); + bc.offset = j.value("offset", 0); + bc.length = j.value("length", 0); +} + +void from_json(const json& j, Config::RootConfig& rc) { + rc.audio = j.value("audio", {}); + json barr = j.value("buttons", json::array()); + if(barr.is_array() && !barr.empty()) { + readVector(rc.buttons, barr); + } + rc.audioPath = j.value("audioPath", ""); +} + +bool Config::RootConfig::ButtonConfig::isValid() const { + return !file.empty(); +} + +void Config::load() { + std::ifstream stream(file); + json json; + if(stream) { + try { + stream >> json; + } catch(nlohmann::detail::parse_error& pe) { + std::cout << "json error: " << pe.what() << std::endl; + return; + } + } + rootConfig = json; +} diff --git a/src/main.cpp b/src/main.cpp index 1cd93da..a110fcc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2,8 +2,14 @@ #include +#include + int main(int argc, char *argv[]) { + Log::init(); + Log::setColoredOutput(true); + Log::setConsoleLogLevel(Log::Level::TRACE); + QApplication a(argc, argv); MainWindow w; w.show(); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 2b5d38e..4efcfbe 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -4,63 +4,56 @@ #include #include -MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { +#include + +MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::Soundboard) { ui->setupUi(this); - Sound& sound = Sound::instance(); // init sound - sound.addDeviceWithName("VirtualMic"); - sound.addDefaultDevice(); + reloadConfig(); - QPushButton* btn = ui->pushButton; - QObject::connect(btn, SIGNAL( clicked() ), this, SLOT( buttonPressed0() )); + // init Buttons + SoundButton* sb; + sb = new SoundButton("Uwu_voice-xjrU3N8M4eo-251.mp3", "UwU", "Shift+F1"); + ui->gridLayout->addWidget(sb->getButton(), 0, 0); + soundbuttons.push_back(sb); + sb = new SoundButton("bonk.wav", "Bonk"); + ui->gridLayout->addWidget(sb->getButton(), 0, 1); + soundbuttons.push_back(sb); - QPushButton* btn2 = ui->pushButton2; - QObject::connect(btn2, SIGNAL( clicked() ), this, SLOT( buttonPressed1() )); - - sc = new QShortcut(QKeySequence("Shift+1"), this, 0, 0, Qt::ApplicationShortcut); - QObject::connect(sc, SIGNAL( activated() ), this, SLOT( shortcut() )); - - globalShortcut = new QxtGlobalShortcut(QKeySequence("Shift+F1")); - QObject::connect(globalShortcut, SIGNAL(activated()), this, SLOT( shortcut2() )); + QObject::connect(ui->reloadButton, SIGNAL( clicked() ), this, SLOT( reloadConfig() )); + QObject::connect(this, SIGNAL( newStatusMessage(const QString&) ), ui->statusbar, SLOT( showMessage(const QString&) )); } -static const std::string SOUNDS[2] {"Uwu_voice-xjrU3N8M4eo-251.mp3", "bonk.wav"}; -static const unsigned int SOUNDSSIZE = 2; - MainWindow::~MainWindow() { delete ui; Sound::deinit(); } -void MainWindow::buttonPressed(unsigned int id) { - std::cout << "Button Pressed: " << id << std::endl; +void MainWindow::reloadConfig() { + Log::info << "realodConfig()"; - playSound(id); -} + QString loaded = QString::fromStdString("loading config"); + emit newStatusMessage(loaded); -void MainWindow::buttonPressed0() { - buttonPressed(0); -} + config.load(); -void MainWindow::buttonPressed1() { - buttonPressed(1); -} + Sound& sound = Sound::instance(); // init sound + sound.reset(); + const std::vector& devices = config.rootConfig.audio.devices; + for(const std::string& device : devices) { + Log::note << "loadAudio device: \"" << device << '"'; -void MainWindow::shortcut() { - std::cout << "Shortcut Pressed" << std::endl; - - // playSound(); -} - -void MainWindow::shortcut2() { - std::cout << "Shortcut2 Pressed!!" << std::endl; - - // playSound(); -} - -void MainWindow::playSound(unsigned int soundid) { - if(soundid < SOUNDSSIZE) { - Sound::instance().addPlayback(SOUNDS[soundid]); + if(!sound.addDeviceWithName(device)) { + Log::warn << "AudioDevice could not be loaded: \"" << device << '"'; + QString done = QString::fromStdString("Sound Device: " + device + " not found!"); + emit newStatusMessage(done); + return; + } } + + QString done = QString::fromStdString("config loaded"); + emit newStatusMessage(done); + + Log::info << "realodConfig() done"; } diff --git a/src/sound.cpp b/src/sound.cpp index 4da63d5..5f84ddf 100644 --- a/src/sound.cpp +++ b/src/sound.cpp @@ -1,7 +1,7 @@ // Diese Datei ist stark an den examples von miniaudio orientiert (vor allem simple_mixing.c). Die Struktur ähnelt sich daher möglicherweise. -#define MA_DEBUG_OUTPUT +//#define MA_DEBUG_OUTPUT //#define MA_LOG_LEVEL MA_LOG_LEVEL_VERBOSE #ifdef WIN32 @@ -42,6 +42,7 @@ #include #include +#include Sound* Sound::inst = nullptr; const std::string Sound::FOLDER = "/home/yannis/prog/ts3/soundboardsounds/"; @@ -60,7 +61,7 @@ void Sound::deinit() { inst = nullptr; } - std::cout << "Sound deinited" << std::endl; + Log::info << "Sound deinited"; } void Sound::addPlayback(const std::string& name, float volume) { @@ -78,7 +79,7 @@ bool Sound::addDefaultDevice() { } bool Sound::addDeviceWithName(const std::string& name) { - SoundDevice* sd = SoundDevice::createDevice(&context, name); + SoundDevice* sd = (name.empty() ? SoundDevice::createDevice(&context) : SoundDevice::createDevice(&context, name)); if(sd) { devices.push_back(sd); } @@ -91,9 +92,28 @@ void Sound::stopAll() { } } +void Sound::reset() { + Log::info << "ResetAudio"; + + // deinit + stopAll(); + for(SoundDevice* sd : devices) { + delete sd; + } + devices.clear(); + ma_context_uninit(&context); + + // reinit + if (ma_context_init(NULL, 0, NULL, &context) != MA_SUCCESS) { + Log::error << "Failed to initialize Sound context."; + throw new std::exception(); + } + Log::info << "ResetAudio done"; +} + Sound::Sound() { if (ma_context_init(NULL, 0, NULL, &context) != MA_SUCCESS) { - std::cout << "Failed to initialize Sound context." << std::endl; + Log::error << "Failed to initialize Sound context."; throw new std::exception(); } } diff --git a/src/soundbutton.cpp b/src/soundbutton.cpp new file mode 100644 index 0000000..7d267c2 --- /dev/null +++ b/src/soundbutton.cpp @@ -0,0 +1,43 @@ +#include "soundbutton.h" + +#include "sound.h" + +uint64_t SoundButton::nextid = 0; + +SoundButton::SoundButton(const std::string& filename, const std::string& name_, const std::string& keycombo) : id(nextid++), file(filename), keycombo(keycombo) { + name = (name_.empty() ? filename : name_); + + button = new QPushButton(QString::fromStdString(name)); + button->setObjectName(QString::fromStdString("soundButton" + std::to_string(id))); + button->setGeometry(QRect(10, 10, 100, 27)); + + globalShortcut = new QxtGlobalShortcut(QString::fromStdString(keycombo)); + + QObject::connect(globalShortcut, SIGNAL( activated() ), this, SLOT( play() )); + QObject::connect(button, SIGNAL( clicked() ), this, SLOT( play() )); +} + +SoundButton::~SoundButton() { + delete globalShortcut; + delete button; +} + +const std::string& SoundButton::getName() const { + return name; +} + +const std::string& SoundButton::getFile() const { + return file; +} + +const std::string& SoundButton::getKeyCombo() const { + return keycombo; +} + +QPushButton* SoundButton::getButton() { + return button; +} + +void SoundButton::play() { + Sound::instance().addPlayback(file); +} \ No newline at end of file diff --git a/src/sounddevice.cpp b/src/sounddevice.cpp index 91d332c..52d8511 100644 --- a/src/sounddevice.cpp +++ b/src/sounddevice.cpp @@ -2,6 +2,7 @@ #include #include +#include #include "sound.h" @@ -87,9 +88,9 @@ SoundDevice* SoundDevice::createDevice(ma_context* ctx, const std::string& name) ma_result result = ma_context_get_devices(ctx, &pPlaybackDeviceInfos, &playbackDeviceCount, NULL, 0); int8_t choosenDevice = -1; - std::cout << " " << playbackDeviceCount << " playback devices found:" << std::endl; + Log::info << " " << playbackDeviceCount << " playback devices found:"; for(uint8_t i = 0; i < playbackDeviceCount; i++) { - std::cout << " " << (int) i << ": " << pPlaybackDeviceInfos[i].name << std::endl; + Log::info << " " << (int) i << ": " << pPlaybackDeviceInfos[i].name; if(pPlaybackDeviceInfos[i].name == name) { choosenDevice = i; @@ -97,7 +98,7 @@ SoundDevice* SoundDevice::createDevice(ma_context* ctx, const std::string& name) } if(choosenDevice == -1) { - std::cout << "Device \"" << name << "\" not found!" << std::endl; + Log::info << "Device \"" << name << "\" not found!"; return nullptr; } @@ -119,13 +120,13 @@ SoundDevice* SoundDevice::createDevice(ma_context* ctx, const ma_device_id* did) deviceConfig.pUserData = sd; if (ma_device_init(ctx, &deviceConfig, &sd->device) != MA_SUCCESS) { - std::cout << "Failed to open sound device." << std::endl; + Log::error << "Failed to open sound device."; delete sd; return nullptr; } - std::cout << "Sound playback device \"" << sd->device.playback.name << "\" initilized." << std::endl; + Log::info << "Sound playback device \"" << sd->device.playback.name << "\" initilized."; return sd; } @@ -142,7 +143,7 @@ void SoundDevice::stop() { ma_mutex_unlock(data.mutex); if(deviceRunning) - std::cout << "Sound playback device \"" << device.playback.name << "\" stopped." << std::endl; + Log::info << "Sound playback device \"" << device.playback.name << "\" stopped."; deviceRunning = false; } @@ -153,7 +154,7 @@ void SoundDevice::addPlayback(const std::string& name, float volume) { Playback* pb = new Playback(); if (ma_decoder_init_file((Sound::FOLDER + name).c_str(), NULL, &(pb->decoder)) != MA_SUCCESS) { - std::cout << "Sound datei: " << name << " konnte nicht geladen werden!" << std::endl; + Log::info << "Sound datei: " << name << " konnte nicht geladen werden!"; throw std::exception(); } @@ -173,7 +174,7 @@ void SoundDevice::startDevice() { if (deviceRunning) return; if (ma_device_start(&device) != MA_SUCCESS) { - std::cout << "Failed to start sound device \"" << device.playback.name << "\"" << std::endl; + Log::error << "Failed to start sound device \"" << device.playback.name << "\""; stop(); ma_device_uninit(&device); throw std::exception(); @@ -181,7 +182,7 @@ void SoundDevice::startDevice() { deviceRunning = true; - std::cout << "Sound device \"" << device.playback.name << "\" started." << std::endl; + Log::info << "Sound device \"" << device.playback.name << "\" started."; } void SoundDevice::cleanupDecoders() { @@ -202,5 +203,5 @@ void SoundDevice::cleanupDecoders() { ma_mutex_unlock(data.mutex); if (count) - std::cout << "removed " << count << " decoder" << std::endl; + Log::debug << "removed " << count << " decoder"; } diff --git a/testqt.pro b/testqt.pro index 95a0dd3..841a151 100644 --- a/testqt.pro +++ b/testqt.pro @@ -21,13 +21,19 @@ SOURCES += \ src/main.cpp \ src/mainwindow.cpp \ src/sound.cpp \ - src/sounddevice.cpp + src/sounddevice.cpp \ + src/soundbutton.cpp \ + src/config.cpp \ + Log/Log.cpp HEADERS += \ include/mainwindow.h \ include/sound.h \ include/sounddevice.h \ - miniaudio/miniaudio.h + include/soundbutton.h \ + miniaudio/miniaudio.h \ + include/config.h \ + Log/Log.h MOC_DIR = generated/ UI_DIR = ui/ @@ -40,7 +46,8 @@ TRANSLATIONS += \ testqt_de_DE.ts INCLUDEPATH += $$PWD/include/ \ - $$PWD/miniaudio/ + $$PWD/miniaudio/ \ + $$PWD/Log/ # Default rules for deployment. qnx: target.path = /tmp/$${TARGET}/bin diff --git a/ui/mainwindow.ui b/ui/mainwindow.ui index 806b9ff..aa2efa8 100644 --- a/ui/mainwindow.ui +++ b/ui/mainwindow.ui @@ -1,56 +1,45 @@ - MainWindow - + Soundboard + 0 0 - 778 - 613 + 824 + 675 - MainWindow + Soundboard - + - 330 + 10 + 40 + 791 + 561 + + + + + QLayout::SetMaximumSize + + + + + + + 10 10 - 151 - 19 + 141 + 27 - Hello, world! - - - - - - 0 - 30 - 371 - 531 - - - - UwU - - - - - - 400 - 30 - 381 - 531 - - - - Bonk + reload Config @@ -59,7 +48,7 @@ 0 0 - 778 + 824 24