diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..fa7c91e --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "libs/libsndfile"] + path = libs/libsndfile + url = https://github.com/libsndfile/libsndfile diff --git a/inkbox.pro b/inkbox.pro index 61cfd4e..154b229 100644 --- a/inkbox.pro +++ b/inkbox.pro @@ -25,8 +25,12 @@ DEFINES += GIT_COMMIT=\\\"$$GIT_COMMIT\\\" SOURCES += \ src/apps/todo.cpp \ + src/audio/audiothread.cpp \ src/splash/alert.cpp \ src/apps/apps.cpp \ + src/widgets/dialogs/audio/audiodialog.cpp \ + src/widgets/dialogs/audio/audiofile.cpp \ + src/widgets/dialogs/audio/audiofilequeue.cpp \ src/widgets/dialogs/library/bookinfodialog.cpp \ src/widgets/dialogs/library/bookoptionsdialog.cpp \ src/widgets/dialogs/brightnessdialog.cpp \ @@ -69,9 +73,13 @@ SOURCES += \ src/settings/powerdaemonsettings.cpp HEADERS += \ + src/audio/audiothread.h \ src/apps/todo.h \ src/splash/alert.h \ src/apps/apps.h \ + src/widgets/dialogs/audio/audiodialog.h \ + src/widgets/dialogs/audio/audiofile.h \ + src/widgets/dialogs/audio/audiofilequeue.h \ src/widgets/dialogs/library/bookinfodialog.h \ src/widgets/dialogs/library/bookoptionsdialog.h \ src/widgets/dialogs/brightnessdialog.h \ @@ -117,6 +125,9 @@ FORMS += \ src/apps/todo.ui \ src/splash/alert.ui \ src/apps/apps.ui \ + src/widgets/dialogs/audio/audiodialog.ui \ + src/widgets/dialogs/audio/audiofile.ui \ + src/widgets/dialogs/audio/audiofilequeue.ui \ src/widgets/dialogs/library/bookinfodialog.ui \ src/widgets/dialogs/library/bookoptionsdialog.ui \ src/widgets/dialogs/brightnessdialog.ui \ @@ -163,3 +174,6 @@ RESOURCES += \ src/eink.qrc INCLUDEPATH += $$system(find ./ -type d -print -path ./.git -prune | grep -v "./.git") +INCLUDEPATH += $$PWD/libs/libsndfile/include/ +DEPENDPATH += $$PWD/libs/libsndfile/include/ +LIBS += -L$$PWD/libs/prebuilt -lsndfile diff --git a/libs/libsndfile b/libs/libsndfile new file mode 160000 index 0000000..59067eb --- /dev/null +++ b/libs/libsndfile @@ -0,0 +1 @@ +Subproject commit 59067eb60518fa63c8629372538075a54828328b diff --git a/libs/prebuilt/libsndfile.so b/libs/prebuilt/libsndfile.so new file mode 120000 index 0000000..948fb66 --- /dev/null +++ b/libs/prebuilt/libsndfile.so @@ -0,0 +1 @@ +libsndfile.so.1.0.35 \ No newline at end of file diff --git a/libs/prebuilt/libsndfile.so.1 b/libs/prebuilt/libsndfile.so.1 new file mode 120000 index 0000000..948fb66 --- /dev/null +++ b/libs/prebuilt/libsndfile.so.1 @@ -0,0 +1 @@ +libsndfile.so.1.0.35 \ No newline at end of file diff --git a/libs/prebuilt/libsndfile.so.1.0.35 b/libs/prebuilt/libsndfile.so.1.0.35 new file mode 100644 index 0000000..fb9322b Binary files /dev/null and b/libs/prebuilt/libsndfile.so.1.0.35 differ diff --git a/src/audio/audiothread.cpp b/src/audio/audiothread.cpp new file mode 100644 index 0000000..3164b09 --- /dev/null +++ b/src/audio/audiothread.cpp @@ -0,0 +1,137 @@ +#include "audiothread.h" +#include "functions.h" + +#include +// Socket things +#include +#include +#include +#include + +#include + +audiothread::audiothread() {} + +void audiothread::start() { + log("Audio thread monitor available", className); + + // QTimer doesn't work in such loops + int count = 0; + int delayMs = 100; + int secondsToCount = 1000 / delayMs; + int previousVolume = -1; + while(true) { + global::audio::audioMutex.lock(); + if(count == secondsToCount) { + count = 0; + if(monitorProgress == true) { + audioProgress(); + } + } + foreach(global::audio::Action action, global::audio::currentAction) { + // Order is important + if(action == global::audio::Action::Stop) { + // No need to call this before 'Play' + log("Stop action received", className); + sendInfo("pause:"); // Yea, only that + global::audio::paused = true; + global::audio::isSomethingCurrentlyPlaying = false; + global::audio::progressSeconds = -5; + monitorProgress = false; + } + if(action == global::audio::Action::Play) { + log("'Play' action received", className); + QString message = "play:\""; + QString betterPath = global::audio::queue[global::audio::itemCurrentlyPlaying].path; + betterPath.remove(0, 21); // Remove /mnt/onboard/onboard/ + log("The name of the song is: '" + global::audio::queue[global::audio::itemCurrentlyPlaying].name + "'", className); + log("The path to be sent is: '" + betterPath + "'", className); + + message.append(betterPath); + message.append('"'); + sendInfo(message); + global::audio::paused = false; + global::audio::isSomethingCurrentlyPlaying = true; + global::audio::progressSeconds = -5; + monitorProgress = true; + } + if(action == global::audio::Action::Pause) { + log("'Pause' action received", className); + QString message = "pause:"; + sendInfo(message); + global::audio::paused = true; + monitorProgress = false; + } + if(action == global::audio::Action::Continue) { + log("'Continue' action received", className); + QString message = "continue:"; + sendInfo(message); + global::audio::paused = false; + monitorProgress = true; + } + } + global::audio::currentAction.clear(); + if(global::audio::volumeLevel != previousVolume) { + previousVolume = global::audio::volumeLevel; + log("'Set volume' action detected", className); + QString message = "set_volume:" + QString::number(global::audio::volumeLevel); + sendInfo(message); + } + global::audio::audioMutex.unlock(); + count = count + 1; + QThread::msleep(delayMs); + } +} + +// I have no explanation as to why this needs to connect/disconnect every time. +// while (recv(client_sockfd, buffer_tmp, 1, 0) > 0) { +// ^ This line makes InkBox freeze +void audiothread::sendInfo(QString message) { + log("Sending message: *" + message + "*", className); + // Send + sockfd = socket(AF_UNIX, SOCK_STREAM, 0); + if (sockfd < 0) { + log("Error creating socket", className); + } + + // Connect to the socket + addr.sun_family = AF_UNIX; + strcpy(addr.sun_path, "/dev/iaudio.socket"); + res = ::connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)); + if (res < 0) { + log("Error connecting to socket", className); + } + + log("Sending message: " + message, className); + res = send(sockfd, message.toStdString().c_str(), strlen(message.toStdString().c_str()), 0); + if (res < 0) { + log("Error sending message to socket", className); + } + + log("Message sent, exiting", className); + close(sockfd); +} + +// Mutex managed outside of the function +void audiothread::audioProgress() { + global::audio::progressSeconds = global::audio::progressSeconds + 1; + // log("Progress, +1 sec: " + QString::number(global::audio::progressSeconds), className); + if(global::audio::progressSeconds >= global::audio::queue[global::audio::itemCurrentlyPlaying].lengths) { + if(global::audio::itemCurrentlyPlaying >= global::audio::queue.length() - 1) { + // It's the last item + log("Last item: stopping playback", className); + global::audio::isSomethingCurrentlyPlaying = false; + global::audio::paused = true; + monitorProgress = false; + global::audio::progressSeconds = -5; + global::audio::currentAction.append(global::audio::Action::Stop); + } + else { + // It's not the last item, continuing + log("Audio file changed", className); + global::audio::itemCurrentlyPlaying = global::audio::itemCurrentlyPlaying + 1; + global::audio::currentAction.append(global::audio::Action::Play); + global::audio::songChanged = true; + } + } +} diff --git a/src/audio/audiothread.h b/src/audio/audiothread.h new file mode 100644 index 0000000..e8547e9 --- /dev/null +++ b/src/audio/audiothread.h @@ -0,0 +1,27 @@ +#ifndef AUDIOTHREAD_H +#define AUDIOTHREAD_H + +#include +#include +#include + +class audiothread : public QObject +{ + Q_OBJECT + +public: + QString className = this->metaObject()->className(); + audiothread(); + void sendInfo(QString message); + int sockfd; + struct sockaddr_un addr; + int res; + bool monitorProgress = false; +public slots: + void start(); + void audioProgress(); + +private: +}; + +#endif // AUDIOTHREAD_H diff --git a/src/eink.qrc b/src/eink.qrc index 59b6c97..24db7e4 100644 --- a/src/eink.qrc +++ b/src/eink.qrc @@ -111,5 +111,13 @@ resources/checkbox-indeterminate.png resources/egg/0.jpg resources/egg/1.jpg + resources/music-note.png + resources/music-library.png + resources/next.png + resources/music-queue.png + resources/play.png + resources/previous.png + resources/pause.png + resources/clean.png diff --git a/src/functions.h b/src/functions.h index 3a4a605..8a72e48 100644 --- a/src/functions.h +++ b/src/functions.h @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -87,6 +88,7 @@ namespace global { namespace text { inline bool textBrowserDialog; inline QString textBrowserContents; + inline QString textBrowserTitle = ""; // At default: empty; "Information" will be displayed } namespace keyboard { inline bool keyboardDialog; @@ -196,6 +198,37 @@ namespace global { int signal; }; } + namespace audio { + inline bool enabled = false; + struct musicFile { + QString path; + QString name; // Cut path for easier use in names + int lengths; // length Seconds + QString length; // In minutes:seconds + int id; + }; + // 'None' is when 'currentAction' is empty + enum class Action { // Function will be called with this enum + Play, + Next, + Previous, + Pause, + Continue, + Stop, // Sets 'paused' to false, 'isSomethingCurrentlyPlaying' to false, and 'itemCurrentlyPlaying' to -1; also stops playing + SetVolume, + }; + inline QVector currentAction; + inline QVector queue; + inline QVector fileList; + inline int itemCurrentlyPlaying = -1; // Also indicates in the queue menu which a grey color which is playing + inline QMutex audioMutex; // These variables will be shared between threads, so here, it's to protect it + inline int progressSeconds = -5; // -5 at default to avoid cutting song too early... yea + inline bool paused = false; + inline bool isSomethingCurrentlyPlaying = false; // Pause and continue + inline bool firstScan = true; + inline int volumeLevel = 40; // Default save value + inline bool songChanged = false; + } inline QString systemInfoText; inline bool forbidOpenSearchDialog; inline bool isN705 = false; @@ -261,9 +294,8 @@ namespace { config.open(QIODevice::ReadOnly); QTextStream in (&config); const QString content = in.readAll(); - std::string contentstr = content.toStdString(); config.close(); - if(contentstr.find("true") != std::string::npos) { + if(content.contains("true")) { return true; } else { @@ -271,6 +303,7 @@ namespace { } } else { + QString function = __func__; log(function + ": Warning: File '" + file + "' doesn't exist, returning false", "functions"); return false; } return 0; @@ -1143,6 +1176,19 @@ namespace { // This can cause problems if someone names their directory with HTML tags, so stop here. Anki, which is a big project, also doesn't care about this return text.remove(QRegExp("<[^>]*>")); } + void bool_writeconfig(QString file, bool option) { + QString str; + if(option == true) { + str = "true"; + } + else { + str = "false"; + } + std::ofstream fhandler; + fhandler.open(file.toStdString()); + fhandler << str.toStdString(); + fhandler.close(); + } } #endif // FUNCTIONS_H diff --git a/src/homeWidget/mainwindow.cpp b/src/homeWidget/mainwindow.cpp index bf44345..e85ec58 100644 --- a/src/homeWidget/mainwindow.cpp +++ b/src/homeWidget/mainwindow.cpp @@ -5,6 +5,8 @@ #include "apps.h" #include "ui_apps.h" #include "functions.h" +#include "audiodialog.h" + #include #include #include @@ -28,6 +30,13 @@ MainWindow::MainWindow(QWidget *parent) , ui(new Ui::MainWindow) { ui->setupUi(this); + + // Needed, the screen is shifted? + // ~Szybet + if(global::deviceID == "n306\n") { + ui->centralwidget->layout()->setContentsMargins(4, 5, 8, 8); + } + ui->inkboxLabel->setFont(QFont("u001")); ui->settingsBtn->setProperty("type", "borderless"); @@ -39,6 +48,7 @@ MainWindow::MainWindow(QWidget *parent) ui->brightnessBtn->setProperty("type", "borderless"); ui->homeBtn->setProperty("type", "borderless"); ui->wifiBtn->setProperty("type", "borderless"); + ui->audioBtn->setProperty("type", "borderless"); ui->settingsBtn->setText(""); ui->appsBtn->setText(""); @@ -50,6 +60,7 @@ MainWindow::MainWindow(QWidget *parent) ui->homeBtn->setText(""); ui->quoteLabel->setText(""); ui->wifiBtn->setText(""); + ui->audioBtn->setText(""); ui->quotePictureLabel->setText(""); @@ -132,6 +143,17 @@ MainWindow::MainWindow(QWidget *parent) ui->brightnessBtn->setIcon(QIcon(":/resources/frontlight.png")); ui->brightnessBtn->setIconSize(QSize(brightnessIconWidth, brightnessIconHeight)); + if(global::audio::enabled == false) { + ui->audioBtn->hide(); + ui->audioLine->hide(); + } + else { + ui->audioBtn->setIcon(QIcon(":/resources/music-note.png")); + ui->audioBtn->setIconSize(QSize(wifiIconWidth, wifiIconHeight)); + ui->labelLine_1->hide(); + ui->labelLine_2->hide(); + } + updateWifiAble(); if(global::device::isWifiAble == true) { // Start Wi-Fi icon updater @@ -1055,3 +1077,9 @@ void MainWindow::setupHomePageWidget() { ui->homeStackedWidget->setCurrentIndex(2); global::mainwindow::tabSwitcher::homePageWidgetCreated = true; } + +void MainWindow::on_audioBtn_clicked() +{ + QDialog* newAudioDialog = new audioDialog(this); + newAudioDialog->exec(); +} diff --git a/src/homeWidget/mainwindow.h b/src/homeWidget/mainwindow.h index 3bc787a..4409298 100644 --- a/src/homeWidget/mainwindow.h +++ b/src/homeWidget/mainwindow.h @@ -102,6 +102,7 @@ private slots: void setupLocalLibraryWidget(); void setupHomePageWidget(); void launchOnlineLibrary(); + void on_audioBtn_clicked(); private: Ui::MainWindow * ui; diff --git a/src/homeWidget/mainwindow.ui b/src/homeWidget/mainwindow.ui index 1840d71..d5b5f50 100644 --- a/src/homeWidget/mainwindow.ui +++ b/src/homeWidget/mainwindow.ui @@ -6,7 +6,7 @@ 0 0 - 706 + 749 601 @@ -17,6 +17,37 @@ + + + + QFrame::Plain + + + Qt::Vertical + + + + + + + + + + + + + + batteryIcon + + + + + + + Home + + + @@ -30,23 +61,15 @@ - - - - - Inter - 11 - false - true - - + + - batt + Brightness - - + + QFrame::Plain @@ -55,10 +78,13 @@ - - - - batteryIcon + + + + QFrame::Plain + + + Qt::Vertical @@ -75,14 +101,60 @@ - - + + + + + Inter + 11 + false + true + + + + time + + + Qt::AlignCenter + + + + + + + QFrame::Plain + + + Qt::Vertical + + + + + - + + + + + Inter + 11 + false + true + + + + time + + + Qt::AlignCenter + + + + Wi-Fi @@ -107,62 +179,10 @@ - - + + - - - - - - - - Home - - - - - - - - Inter - 11 - false - true - - - - time - - - Qt::AlignCenter - - - - - - - Brightness - - - - - - - QFrame::Plain - - - Qt::Vertical - - - - - - - QFrame::Plain - - - Qt::Vertical + Audio diff --git a/src/main.cpp b/src/main.cpp index 6d4e3d6..7329ba6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -23,6 +23,7 @@ #include "encryptionmanager.h" #include "sleepthread.h" #include "sleepdialog.h" +#include "audiothread.h" #include #include @@ -103,6 +104,20 @@ int main(int argc, char *argv[]) QObject::connect(sleepThreadWindow, &sleepThread::stopDialog, sleepDialogWindow, &sleepDialog::hideSleepDialog); IPDThread->start(); + // Audio stuff + if(checkconfig(".config/e-2-audio/enabled") == true) { + log("Audio is enabled", "main"); + global::audio::enabled = true; + QThread * audioThread = new QThread(); + audiothread * audioObject = new audiothread(); + audioObject->moveToThread(audioThread); + QObject::connect(audioThread, &QThread::started, audioObject, &audiothread::start); + audioThread->start(); + } + else { + log("Audio is disabled", "main"); + } + if(checkconfig(".config/18-encrypted_storage/status") == true and checkconfig("/external_root/run/encfs_mounted") == false) { // Open Encryption Manager to unlock encrypted storage encryptionManager w; diff --git a/src/resources/clean.png b/src/resources/clean.png new file mode 100644 index 0000000..0711ab6 Binary files /dev/null and b/src/resources/clean.png differ diff --git a/src/resources/music-library.png b/src/resources/music-library.png new file mode 100644 index 0000000..a327f6e Binary files /dev/null and b/src/resources/music-library.png differ diff --git a/src/resources/music-note.png b/src/resources/music-note.png new file mode 100644 index 0000000..5a5bdb0 Binary files /dev/null and b/src/resources/music-note.png differ diff --git a/src/resources/music-queue.png b/src/resources/music-queue.png new file mode 100644 index 0000000..6edcd99 Binary files /dev/null and b/src/resources/music-queue.png differ diff --git a/src/resources/next.png b/src/resources/next.png new file mode 100644 index 0000000..5c123d5 Binary files /dev/null and b/src/resources/next.png differ diff --git a/src/resources/pause.png b/src/resources/pause.png new file mode 100644 index 0000000..091a123 Binary files /dev/null and b/src/resources/pause.png differ diff --git a/src/resources/play.png b/src/resources/play.png new file mode 100644 index 0000000..25f3916 Binary files /dev/null and b/src/resources/play.png differ diff --git a/src/resources/previous.png b/src/resources/previous.png new file mode 100644 index 0000000..dae26d4 Binary files /dev/null and b/src/resources/previous.png differ diff --git a/src/widgets/dialogs/audio/audiodialog.cpp b/src/widgets/dialogs/audio/audiodialog.cpp new file mode 100644 index 0000000..a617758 --- /dev/null +++ b/src/widgets/dialogs/audio/audiodialog.cpp @@ -0,0 +1,450 @@ +#include "audiodialog.h" +#include "ui_audiodialog.h" +#include "functions.h" +#include "audiofile.h" +#include "audiofilequeue.h" + +#include +#include +#include + +#include + +audioDialog::audioDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::audioDialog) +{ + ui->setupUi(this); + audioDialog::setFont(QFont("u001")); + + progress = new QTimer(this); + + // Size + QRect screenGeometry = QGuiApplication::screens()[0]->geometry(); + this->setFixedWidth(screenGeometry.width() / 1.1); + + int halfOfHalfHeight = ((screenGeometry.height() / 2) / 2) / 2; + int finalHeight = screenGeometry.height() - halfOfHalfHeight; + + this->setFixedHeight(finalHeight); + + // Centering dialog + int x = (screenGeometry.width() - this->width()) / 2; + int y = (screenGeometry.height() - this->height()) / 2; + this->move(x, y); + + ui->minusBtn->setProperty("type", "borderless"); + ui->plusBtn->setProperty("type", "borderless"); + ui->exitBtn->setProperty("type", "borderless"); + ui->nextBtn->setProperty("type", "borderless"); + ui->previousBtn->setProperty("type", "borderless"); + ui->playBtn->setProperty("type", "borderless"); + ui->libraryBtn->setProperty("type", "borderless"); + ui->queueBtn->setProperty("type", "borderless"); + ui->refreshBtn->setProperty("type", "borderless"); + + int topButtonsSize = 55; + int menuButtonsSize = 80; + int playButtonsSize = 50; + + ui->minusBtn->setIconSize(QSize{topButtonsSize,topButtonsSize}); + ui->plusBtn->setIconSize(QSize{topButtonsSize,topButtonsSize}); + ui->exitBtn->setIconSize(QSize{topButtonsSize,topButtonsSize}); + ui->refreshBtn->setIconSize(QSize{topButtonsSize,topButtonsSize}); + + ui->nextBtn->setIconSize(QSize{playButtonsSize,playButtonsSize}); + ui->previousBtn->setIconSize(QSize{playButtonsSize,playButtonsSize}); + ui->playBtn->setIconSize(QSize{playButtonsSize,playButtonsSize}); + + ui->libraryBtn->setIconSize(QSize{menuButtonsSize,menuButtonsSize}); + ui->queueBtn->setIconSize(QSize{menuButtonsSize,menuButtonsSize}); + + ui->fileNameLabel->setWordWrap(true); + + // Default "page" + ui->libraryBtn->setStyleSheet("background: grey;"); + ui->refreshBtn->setIcon(QIcon(":/resources/refresh-small.png")); + ui->refreshBtn->hide(); + ui->lineRefresh->hide(); + + if(global::audio::firstScan == true) { + global::audio::firstScan = false; + refreshFileList(); + log("Gathered files list", className); + } + + refreshAudioFileWidgets(); + + progressFuncManage(); + ui->progressSlider->setDisabled(true); + + int autoRepeatDelay = 400; + int autoRepeatInterval = 20; + + ui->plusBtn->setAutoRepeat(true); + ui->plusBtn->setAutoRepeatDelay(autoRepeatDelay); + ui->plusBtn->setAutoRepeatInterval(autoRepeatInterval); + + ui->minusBtn->setAutoRepeat(true); + ui->minusBtn->setAutoRepeatDelay(autoRepeatDelay); + ui->minusBtn->setAutoRepeatInterval(autoRepeatInterval); + + global::audio::audioMutex.lock(); + ui->soundLevelSlider->setValue(global::audio::volumeLevel); + + if(global::audio::isSomethingCurrentlyPlaying == true) { + ui->playBtn->setIcon(QIcon(":/resources/pause.png")); + } + global::audio::audioMutex.unlock(); + + finishedStartingUp = true; +} + +audioDialog::~audioDialog() +{ + delete ui; +} + +void audioDialog::changeMenu() { + log("Menu change requested", className); + if(currentMenu != Queue) { + currentMenu = Queue; + emit deleteItself(); + ui->refreshBtn->show(); + ui->lineRefresh->show(); + ui->refreshBtn->setIcon(QIcon(":/resources/clean.png")); + ui->libraryBtn->setStyleSheet("background: white;"); + ui->queueBtn->setStyleSheet("background: grey;"); + refreshAudioFileWidgetsQueue(); + } + else if(currentMenu != Library){ + currentMenu = Library; + emit deleteItself(); + ui->refreshBtn->hide(); + ui->lineRefresh->hide(); + ui->libraryBtn->setStyleSheet("background: grey;"); + ui->queueBtn->setStyleSheet("background: white;"); + refreshAudioFileWidgets(); + } + ui->scrollArea->verticalScrollBar()->setValue(ui->scrollArea->verticalScrollBar()->minimum()); +} + +void audioDialog::on_libraryBtn_clicked() +{ + if(currentMenu != Library) { + changeMenu(); + } +} + +void audioDialog::on_queueBtn_clicked() +{ + if(currentMenu != Queue) { + changeMenu(); + } +} + +// Only on launching dialog +void audioDialog::refreshFileList() { + log("Refreshing file list", className); + if(QFile(".config/e-2-audio/path").exists() == false) { + writeFile(".config/e-2-audio/path", "/mnt/onboard/onboard/music/"); + log("Music config file doesn't exist, creating it", className); + } + // For example in this path: /mnt/onboard/onboard/music/ ( with / at the end ) + QString path = readFile(".config/e-2-audio/path").replace("\n", ""); + log("Path for audio files: " + path, className); + QDir dir{path}; + // Other file formats could be added, by building more libraries + // https://github.com/arnavyc/sndfile-alsa-example/blob/main/src/sndfile-alsa.c + // https://github.com/libsndfile/libsndfile + // Is it easy to do? Yes. Does it take more space? Yes. Do I care? No, i have this fancy command: + // for i in *; do ffmpeg -i "$i" "${i%.*}.wav"; done + // (Szybet) + dir.setNameFilters(QStringList("*.wav")); + dir.setFilter(QDir::Files | QDir::NoDotAndDotDot | QDir::NoSymLinks); + + QStringList fileList = dir.entryList(); + log("Files count: " + QString::number(fileList.count()), className); + global::audio::audioMutex.lock(); + for (int i = 0; i < fileList.count(); i++) + { + log("Audio file: " + fileList[i], className); + global::audio::musicFile newMusicFile; + newMusicFile.path = path + fileList[i]; + log("File name path: " + newMusicFile.path, className); + QString tempName = fileList[i]; + tempName.chop(4); // File extension + newMusicFile.name = tempName; + log("File name length: " + QString::number(fileList[i].length()), className); + log("File name: " + tempName, className); + + // http://libsndfile.github.io/libsndfile/api#open_fd + /* + sf_count_t frames ; + int samplerate ; + int channels ; + int format ; + int sections ; + int seekable ; + */ + SF_INFO sfinfo; + SNDFILE *wavFile = ::sf_open(newMusicFile.path.toStdString().c_str(), SFM_READ, &sfinfo); + // http://soundfile.sapp.org/doc/WaveFormat/ + // https://stackoverflow.com/questions/53338925/get-the-audio-duration-using-libsndfile + int bareSeconds = static_cast(sfinfo.frames) / (sfinfo.samplerate); + sf_close(wavFile); + newMusicFile.lengths = bareSeconds; + int countMin = 0; + while(bareSeconds >= 60) { + countMin = countMin + 1; + bareSeconds = bareSeconds - 60; + } + QString min = QString::number(countMin); + if(min.length() < 2) { + min = '0' + min; + } + QString sec = QString::number(bareSeconds); + if(sec.length() < 2) { + sec = '0' + sec; + } + newMusicFile.length = min + ":" + sec; + // To avoid shifting the line + while(newMusicFile.length.length() < 4) { + newMusicFile.length = newMusicFile.length + " "; + } + log("File length: " + newMusicFile.length, className); + newMusicFile.id = i; + global::audio::fileList.append(newMusicFile); + } + global::audio::audioMutex.unlock(); + this->adjustSize(); +} + +void audioDialog::refreshAudioFileWidgets() { + global::audio::audioMutex.lock(); + emit deleteItself(); + // Doesn't work, freezes the app... + // QCoreApplication::processEvents(); + for(int i = 0; i < global::audio::fileList.size(); i++) { + log("Adding new item number: " + QString::number(i), className); + audiofile* newAudioFile = new audiofile(this); + newAudioFile->provideData(global::audio::fileList[i]); + QObject::connect(this, &audioDialog::deleteItself, newAudioFile, &audiofile::die); + QObject::connect(newAudioFile, &audiofile::playFileChild, this, &audioDialog::playFile); + ui->verticalLayout->addWidget(newAudioFile, Qt::AlignTop); + } + global::audio::audioMutex.unlock(); + this->adjustSize(); +} + +void audioDialog::refreshAudioFileWidgetsQueue() { + global::audio::audioMutex.lock(); + emit deleteItself(); + // Doesn't work, freezes the app... + // QCoreApplication::processEvents(); + log("global::audio::itemCurrentlyPlaying: " + QString::number(global::audio::itemCurrentlyPlaying), className); + log("global::audio::isSomethingCurrentlyPlaying: " + QString::number(global::audio::isSomethingCurrentlyPlaying), className); + + for(int i = 0; i < global::audio::queue.size(); i++) { + log("Adding new item: " + QString::number(i), className); + audiofilequeue* newAudioFileQueue = new audiofilequeue(this); + bool grey = false; + if(global::audio::isSomethingCurrentlyPlaying == true && global::audio::itemCurrentlyPlaying == i) { + grey = true; + } + global::audio::queue[i].id = i; // Give them invidual ID once more, because files can repeat + newAudioFileQueue->provideData(global::audio::queue[i], grey); + QObject::connect(this, &audioDialog::deleteItself, newAudioFileQueue, &audiofilequeue::die); + QObject::connect(newAudioFileQueue, &audiofilequeue::playFileChild, this, &audioDialog::playFile); + ui->verticalLayout->addWidget(newAudioFileQueue, Qt::AlignTop); + } + global::audio::audioMutex.unlock(); +} + +void audioDialog::on_refreshBtn_clicked() +{ + global::audio::audioMutex.lock(); + global::audio::currentAction.append(global::audio::Action::Stop); + global::audio::isSomethingCurrentlyPlaying = false; + global::audio::queue.clear(); + global::audio::audioMutex.unlock(); + refreshAudioFileWidgetsQueue(); +} + +void audioDialog::playFile(int itemInQueue) { + log("Called playFile", className); + + log("Calling stop to current actions to play a new file", className); + global::audio::audioMutex.lock(); + // All those variables will be changed in audio thread too - but too late for progressFuncManage + global::audio::itemCurrentlyPlaying = itemInQueue; + global::audio::isSomethingCurrentlyPlaying = true; + global::audio::paused = false; + global::audio::songChanged = true; + global::audio::currentAction.append(global::audio::Action::Play); + global::audio::audioMutex.unlock(); + + progress->stop(); + progressFuncManage(); + + ui->playBtn->setIcon(QIcon(":/resources/pause.png")); + + if(currentMenu == Queue) { + refreshAudioFileWidgetsQueue(); + } +} + +void audioDialog::progressFuncManage() { + global::audio::audioMutex.lock(); + // log("Called progress watcher", className); + + bool requestWatcher = false; + bool requestQueueRefresh = false; + + if(global::audio::isSomethingCurrentlyPlaying == true) { + if(ui->progressSlider->maximum() != global::audio::queue[global::audio::itemCurrentlyPlaying].lengths) { + ui->progressSlider->setMaximum(global::audio::queue[global::audio::itemCurrentlyPlaying].lengths); + } + // log("Changing slider position: " + QString::number(global::audio::progressSeconds), className); + ui->progressSlider->setSliderPosition(global::audio::progressSeconds); + if(global::audio::songChanged == true || finishedStartingUp == false) { + log("global::audio::queue.size(): " + QString::number(global::audio::queue.size()), className); + log("global::audio::itemCurrentlyPlaying: " + QString::number(global::audio::itemCurrentlyPlaying), className); + log("Setting song name...", className); + + QString currentName = global::audio::queue[global::audio::itemCurrentlyPlaying].name; + // log("New name: " + currentName, className); + ui->fileNameLabel->setText(currentName); + if(currentMenu == Queue) { + requestQueueRefresh = true; + } + global::audio::songChanged = false; + } + if(global::audio::paused == false) { + requestWatcher = true; + } + } + else { + ui->progressSlider->setSliderPosition(0); + ui->playBtn->setIcon(QIcon(":/resources/play.png")); + ui->fileNameLabel->setText("..."); + if(currentMenu == Queue) { + requestQueueRefresh = true; + } + // log("Exiting progress watcher", className); + } + global::audio::audioMutex.unlock(); + + if(requestQueueRefresh == true) { + refreshAudioFileWidgetsQueue(); + } + + if(requestWatcher == true) { + // log("Launching another watcher...", className); + progress->singleShot(450, this, SLOT(progressFuncManage())); // For better accuracy, set 50 + } +} + +void audioDialog::on_progressSlider_sliderPressed() +{ + ui->progressSlider->releaseMouse(); +} + +void audioDialog::on_soundLevelSlider_valueChanged(int value) +{ + log("Setting volume level: " + QString::number(value), className); + // It detects if volume changes + if(finishedStartingUp == true) { + global::audio::audioMutex.lock(); + global::audio::volumeLevel = value; + global::audio::audioMutex.unlock(); + } +} + +void audioDialog::on_plusBtn_clicked() +{ + ui->soundLevelSlider->setValue(ui->soundLevelSlider->value() + 1); +} + +void audioDialog::on_minusBtn_clicked() +{ + ui->soundLevelSlider->setValue(ui->soundLevelSlider->value() - 1); +} + +void audioDialog::on_playBtn_clicked() +{ + global::audio::audioMutex.lock(); + if(global::audio::isSomethingCurrentlyPlaying == true) { + if(global::audio::paused == false) { + global::audio::paused = true; + global::audio::currentAction.append(global::audio::Action::Pause); + ui->playBtn->setIcon(QIcon(":/resources/play.png")); + } + else { + global::audio::paused = false; + global::audio::currentAction.append(global::audio::Action::Continue); + ui->playBtn->setIcon(QIcon(":/resources/pause.png")); + global::audio::audioMutex.unlock(); + progressFuncManage(); + return void(); + } + } + else if(global::audio::queue.size() - 1 >= global::audio::itemCurrentlyPlaying && global::audio::itemCurrentlyPlaying > 0) { + int tmpInt = global::audio::itemCurrentlyPlaying; + global::audio::audioMutex.unlock(); + playFile(tmpInt); + ui->playBtn->setIcon(QIcon(":/resources/pause.png")); + return void(); + } + else { + ui->playBtn->setIcon(QIcon(":/resources/stop.png")); + } + global::audio::audioMutex.unlock(); +} + +void audioDialog::on_previousBtn_clicked() +{ + global::audio::audioMutex.lock(); + int item = global::audio::itemCurrentlyPlaying; + item = item - 1; + if(item >= 0 && global::audio::queue.size() >= 1) { + global::audio::audioMutex.unlock(); + playFile(item); + ui->previousBtn->setIcon(QIcon(":/resources/previous.png")); + ui->nextBtn->setIcon(QIcon(":/resources/next.png")); + return void(); + } else { + ui->previousBtn->setIcon(QIcon(":/resources/stop.png")); + } + global::audio::audioMutex.unlock(); +} + +void audioDialog::on_nextBtn_clicked() +{ + global::audio::audioMutex.lock(); + int item = global::audio::itemCurrentlyPlaying; + item = item + 1; + if(item >= 0 && item < global::audio::queue.size()) { + global::audio::audioMutex.unlock(); + playFile(item); + ui->previousBtn->setIcon(QIcon(":/resources/previous.png")); + ui->nextBtn->setIcon(QIcon(":/resources/next.png")); + return void(); + } else { + ui->nextBtn->setIcon(QIcon(":/resources/stop.png")); + } + global::audio::audioMutex.unlock(); +} + +void audioDialog::on_exitBtn_clicked() +{ + progress->stop(); + progress->deleteLater(); + + // Make sure the mutex is unlocked + global::audio::audioMutex.tryLock(); + global::audio::audioMutex.unlock(); + + this->deleteLater(); + this->close(); +} diff --git a/src/widgets/dialogs/audio/audiodialog.h b/src/widgets/dialogs/audio/audiodialog.h new file mode 100644 index 0000000..ff07a40 --- /dev/null +++ b/src/widgets/dialogs/audio/audiodialog.h @@ -0,0 +1,57 @@ +#ifndef AUDIODIALOG_H +#define AUDIODIALOG_H + +#include +#include + +namespace Ui { +class audioDialog; +} + +class audioDialog : public QDialog +{ + Q_OBJECT + +public: + explicit audioDialog(QWidget *parent = nullptr); + ~audioDialog(); + QString className = this->metaObject()->className(); + + enum audioMenu { + Library, + Queue, + }; + + bool currentMenu = Library; + void changeMenu(); + void refreshFileList(); + void refreshAudioFileWidgets(); + void refreshAudioFileWidgetsQueue(); + QTimer* progress; // Needs to be stopped at exit + bool finishedStartingUp = false; + +public slots: + void playFile(int itemInQueue); // Can be called from children + void progressFuncManage(); + +private slots: + void on_libraryBtn_clicked(); + void on_queueBtn_clicked(); + void on_refreshBtn_clicked(); + void on_progressSlider_sliderPressed(); + void on_soundLevelSlider_valueChanged(int value); + void on_plusBtn_clicked(); + void on_minusBtn_clicked(); + void on_playBtn_clicked(); + void on_previousBtn_clicked(); + void on_nextBtn_clicked(); + void on_exitBtn_clicked(); + +signals: + void deleteItself(); + +private: + Ui::audioDialog *ui; +}; + +#endif // AUDIODIALOG_H diff --git a/src/widgets/dialogs/audio/audiodialog.ui b/src/widgets/dialogs/audio/audiodialog.ui new file mode 100644 index 0000000..ecc5882 --- /dev/null +++ b/src/widgets/dialogs/audio/audiodialog.ui @@ -0,0 +1,384 @@ + + + audioDialog + + + + 0 + 0 + 603 + 489 + + + + Dialog + + + + + + QFrame::Box + + + 2 + + + + + + + + + + + + :/resources/previous.png:/resources/previous.png + + + + + + + + + + + :/resources/stop.png:/resources/stop.png + + + + + + + + + + + :/resources/next.png:/resources/next.png + + + + + + + + + ... + + + Qt::AlignCenter + + + + + + + Qt::NoFocus + + + 100 + + + 1 + + + Qt::Horizontal + + + + + + + + + + + + + 0 + 0 + + + + + + + + :/resources/music-library.png:/resources/music-library.png + + + + + + + QFrame::Plain + + + Qt::Vertical + + + + + + + + 0 + 0 + + + + + + + + :/resources/music-queue.png:/resources/music-queue.png + + + + + + + + + QFrame::NoFrame + + + QFrame::Plain + + + 0 + + + Qt::ScrollBarAlwaysOff + + + true + + + + + 0 + 0 + 585 + 301 + + + + + 0 + + + + + + + + Qt::Vertical + + + QSizePolicy::MinimumExpanding + + + + 20 + 40 + + + + + + + + + + + + QFrame::Plain + + + Qt::Horizontal + + + + + + + QLayout::SetMinimumSize + + + + + + 0 + 0 + + + + + + + + :/resources/minus.png:/resources/minus.png + + + + + + + 100 + + + 10 + + + 100 + + + Qt::Horizontal + + + + + + + + 0 + 0 + + + + + + + + :/resources/plus.png:/resources/plus.png + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 5 + 20 + + + + + + + + QFrame::Plain + + + Qt::Vertical + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 5 + 20 + + + + + + + + + 0 + 0 + + + + + + + + :/resources/refresh-small.png:/resources/refresh-small.png + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 5 + 20 + + + + + + + + QFrame::Plain + + + Qt::Vertical + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 5 + 20 + + + + + + + + + 0 + 0 + + + + + + + + :/resources/close.png:/resources/close.png + + + + + + + + + + + + diff --git a/src/widgets/dialogs/audio/audiofile.cpp b/src/widgets/dialogs/audio/audiofile.cpp new file mode 100644 index 0000000..98c6b0c --- /dev/null +++ b/src/widgets/dialogs/audio/audiofile.cpp @@ -0,0 +1,57 @@ +#include "audiofile.h" +#include "ui_audiofile.h" +#include "functions.h" + +audiofile::audiofile(QWidget *parent) : + QWidget(parent), + ui(new Ui::audiofile) +{ + ui->setupUi(this); + audiofile::setFont(QFont("u001")); + + ui->nameLabel->setWordWrap(true); +} + +audiofile::~audiofile() +{ + delete ui; +} + +void audiofile::provideData(global::audio::musicFile fileProvided) { + file = fileProvided; + ui->nameLabel->setText(file.name); + ui->timeLabel->setText(file.length); +} + +void audiofile::die() { + this->deleteLater(); + this->close(); +} + +void audiofile::on_addBtn_clicked() +{ + ui->addBtn->setDisabled(true); + ui->addBtn->setStyleSheet("background: black;"); + + log("Adding item (audio file) to queue", className); + global::audio::audioMutex.lock(); + global::audio::queue.append(file); + if(global::audio::isSomethingCurrentlyPlaying == false) { + log("Starting playback because nothing else is currently playing", className); + global::audio::isSomethingCurrentlyPlaying = true; + int tmpInt = global::audio::queue.length() - 1; + global::audio::audioMutex.unlock(); + QTimer::singleShot(700, this, SLOT(enableButton())); + emit playFileChild(tmpInt); + return void(); + } + global::audio::audioMutex.unlock(); + QTimer::singleShot(700, this, SLOT(enableButton())); +} + +void audiofile::enableButton() { + log("Enabling 'Back' button", className); + ui->addBtn->setEnabled(true); + ui->addBtn->setStyleSheet("background: white;"); + ui->addBtn->repaint(); +} diff --git a/src/widgets/dialogs/audio/audiofile.h b/src/widgets/dialogs/audio/audiofile.h new file mode 100644 index 0000000..c993d1c --- /dev/null +++ b/src/widgets/dialogs/audio/audiofile.h @@ -0,0 +1,38 @@ +#ifndef AUDIOFILE_H +#define AUDIOFILE_H + +#include "functions.h" + +#include + +namespace Ui { +class audiofile; +} + +class audiofile : public QWidget +{ + Q_OBJECT + +public: + explicit audiofile(QWidget *parent = nullptr); + ~audiofile(); + QString className = this->metaObject()->className(); + void provideData(global::audio::musicFile fileProvided); + global::audio::musicFile file; + +public slots: + void die(); + void enableButton(); + +private slots: + + void on_addBtn_clicked(); + +signals: + void playFileChild(int itemInQueue); + +private: + Ui::audiofile *ui; +}; + +#endif // AUDIOFILE_H diff --git a/src/widgets/dialogs/audio/audiofile.ui b/src/widgets/dialogs/audio/audiofile.ui new file mode 100644 index 0000000..e0265dd --- /dev/null +++ b/src/widgets/dialogs/audio/audiofile.ui @@ -0,0 +1,114 @@ + + + audiofile + + + + 0 + 0 + 316 + 53 + + + + + 0 + 0 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + 0 + 0 + + + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + + + + + + + + 0 + 0 + + + + + + + + :/resources/plus.png:/resources/plus.png + + + + + + + + + QFrame::Plain + + + Qt::Horizontal + + + + + + + + + + diff --git a/src/widgets/dialogs/audio/audiofilequeue.cpp b/src/widgets/dialogs/audio/audiofilequeue.cpp new file mode 100644 index 0000000..1fc089a --- /dev/null +++ b/src/widgets/dialogs/audio/audiofilequeue.cpp @@ -0,0 +1,84 @@ +#include "audiofilequeue.h" +#include "ui_audiofilequeue.h" + +audiofilequeue::audiofilequeue(QWidget *parent) : + QWidget(parent), + ui(new Ui::audiofilequeue) +{ + ui->setupUi(this); + audiofilequeue::setFont(QFont("u001")); + + ui->nameLabel->setWordWrap(true); +} + +audiofilequeue::~audiofilequeue() +{ + delete ui; +} + +void audiofilequeue::provideData(global::audio::musicFile fileProvided, bool grey) { + file = fileProvided; + ui->nameLabel->setText(file.name); + ui->timeLabel->setText(file.length); + if(grey == true) { + log("Setting background grey", className); + ui->deleteBtn->setStyleSheet("background: grey;"); + ui->playButton->setStyleSheet("background: grey;"); + isPlaying = true; + } +} + +void audiofilequeue::die() { + this->deleteLater(); + this->close(); +} + +void audiofilequeue::on_deleteBtn_clicked() +{ + int id = -1; + log("global::audio::queue.size(): " + QString::number(global::audio::queue.size()), className); + global::audio::audioMutex.lock(); + for(int i = 0; i < global::audio::queue.size(); i++) { + if(file.id == global::audio::queue[i].id) { + log("Found ID", className); + id = i; + } + } + + log("File ID is: " + QString::number(id), className); + global::audio::queue.remove(id); + // TODO: Don't change song if the removed item is not playing + // I will do it the moment I get annoyed about it + log("After removing global::audio::queue.size(): " + QString::number(global::audio::queue.size()), className); + if(id - 2 >= 0) { + global::audio::audioMutex.unlock(); + emit playFileChild(id - 2); + return void(); + } + else if(id < global::audio::queue.size()) { + global::audio::audioMutex.unlock(); + emit playFileChild(id); + return void(); + } + else { + global::audio::currentAction.append(global::audio::Action::Stop); + global::audio::audioMutex.unlock(); + return void(); + } + log("Something went wrong in deleting item that was in the query", className); + global::audio::audioMutex.unlock(); +} + +void audiofilequeue::on_playButton_clicked() +{ + global::audio::audioMutex.lock(); + for(int i = 0; i < global::audio::queue.size(); i++) { + if(global::audio::queue[i].id == file.id) { + global::audio::audioMutex.unlock(); + emit playFileChild(i); + return void(); + } + } + global::audio::audioMutex.unlock(); + log("Something went really wrong", className); +} diff --git a/src/widgets/dialogs/audio/audiofilequeue.h b/src/widgets/dialogs/audio/audiofilequeue.h new file mode 100644 index 0000000..d101cd5 --- /dev/null +++ b/src/widgets/dialogs/audio/audiofilequeue.h @@ -0,0 +1,38 @@ +#ifndef AUDIOFILEQUEUE_H +#define AUDIOFILEQUEUE_H + +#include +#include "functions.h" + +namespace Ui { +class audiofilequeue; +} + +class audiofilequeue : public QWidget +{ + Q_OBJECT + +public: + explicit audiofilequeue(QWidget *parent = nullptr); + ~audiofilequeue(); + QString className = this->metaObject()->className(); + bool isPlaying = false; + + void provideData(global::audio::musicFile fileProvided, bool grey); + global::audio::musicFile file; + +public slots: + void die(); + +private: + Ui::audiofilequeue *ui; + +signals: + void playFileChild(int itemInQueue); + +private slots: + void on_deleteBtn_clicked(); + void on_playButton_clicked(); +}; + +#endif // AUDIOFILEQUEUE_H diff --git a/src/widgets/dialogs/audio/audiofilequeue.ui b/src/widgets/dialogs/audio/audiofilequeue.ui new file mode 100644 index 0000000..40344a2 --- /dev/null +++ b/src/widgets/dialogs/audio/audiofilequeue.ui @@ -0,0 +1,134 @@ + + + audiofilequeue + + + + 0 + 0 + 284 + 53 + + + + + 0 + 0 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + 0 + 0 + + + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + + + + + + + + 0 + 0 + + + + + + + + :/resources/close.png:/resources/close.png + + + + + + + + 0 + 0 + + + + + + + + :/resources/chevron-right.png:/resources/chevron-right.png + + + + + + + + + QFrame::Plain + + + 2 + + + Qt::Horizontal + + + + + + + + + + diff --git a/src/widgets/dialogs/generaldialog.cpp b/src/widgets/dialogs/generaldialog.cpp index 47b7fc4..7d76043 100644 --- a/src/widgets/dialogs/generaldialog.cpp +++ b/src/widgets/dialogs/generaldialog.cpp @@ -142,7 +142,16 @@ generalDialog::generalDialog(QWidget *parent) : else if(global::text::textBrowserDialog == true) { textBrowserDialog = true; textwidgetWindow = new textwidget(); - ui->headerLabel->setText("Information"); + + if(global::text::textBrowserTitle.isEmpty() == true) { + ui->headerLabel->setText("Information"); + } + else { + ui->headerLabel->setText(global::text::textBrowserTitle); + } + // Important + ui->bodyLabel->setText(global::text::textBrowserContents); + ui->stackedWidget->setCurrentIndex(1); ui->mainStackedWidget->insertWidget(1, textwidgetWindow); ui->mainStackedWidget->setCurrentIndex(1); @@ -584,11 +593,13 @@ void generalDialog::on_acceptBtn_clicked() } if(textBrowserDialog == true) { global::text::textBrowserContents = ""; + global::text::textBrowserTitle = ""; global::text::textBrowserDialog = false; } if(appInfoDialog == true) { global::text::textBrowserContents = ""; + global::text::textBrowserTitle = ""; global::userApps::appInfoDialog = false; }