Compare commits

...
Sign in to create a new pull request.

28 commits

Author SHA1 Message Date
snt
9c559e8a8f wip audio setup 2024-12-01 13:30:10 +01:00
snt
e68b46f3b7 remove selection mode in activeCueList 2024-07-27 02:10:34 +02:00
snt
6da193c50d show max/min button in show player window 2024-07-27 01:50:50 +02:00
snt
f665ccfd7d shortcuts acts in entire app, although the focus is set on the main window
cueTrackWidget and cueTrackListWidget cosmetics
2024-07-26 22:47:24 +02:00
snt
f4bf71dbcf stop reset to cursor when finish fade out 2024-07-26 20:59:45 +02:00
snt
f6eb15f13c save splitters state. Moved restore and save geometry to ui and
showplayer constructors/destructors
2024-07-26 20:16:21 +02:00
snt
11bd76a458 filter bank HPF Ui 2024-07-26 15:42:06 +02:00
snt
8716004c5e cue controls in CueTrackWidget 2024-07-09 20:48:20 +02:00
snt
eed68d1817 panic resotre buttons. copy/cut/paste connects. Load systems settings,
if not, default xml file, if not, generate a generic conf (2 layers,
    no dmx, path ../media, two audiodevices)
2024-07-09 17:11:53 +02:00
snt
46b7624fb5 fix playing folder modes in ui. read/write settings and cue list at
start/exit.shortcuts.
2024-07-08 20:23:25 +02:00
snt
b4988a1307 fade out when stop or pause 2024-07-07 18:05:37 +02:00
snt
aa713fd9be fades in pan, pitch, bus1, bus2 2024-07-07 02:31:05 +02:00
snt
d35b166ce5 quita de la lista las cues en stop o paused cuando acaben su ejeución. 2024-07-02 00:30:03 +02:00
snt
14a51c88e9 multi-cue, nuevo campo en cuetrack para lanzar la siguiente cue de forma
inmediata, sin esperar a los tiempos de la cue actual.
Cambiado el visor de cue a QWidgetList, ahora muestra la última cue
reproducida en cada capa.
2024-07-01 18:31:47 +02:00
snt
307d9c4d43 wip cuetrackwidget 2024-06-27 15:25:07 +02:00
snt
e39d885f6f fade in time 2024-06-26 20:14:51 +02:00
snt
6056778856 active indicator working 2024-06-22 00:57:19 +02:00
snt
0795157d5f wip en active, hacer lista con las cues activas? 2024-06-21 20:39:07 +02:00
snt
86e866c996 iconos de botones en fichero de recursos 2024-06-21 18:56:02 +02:00
snt
d9e755cd32 fixes, funcionalidad básica y nuevos botones (no cargan iconos).
graba/carga cuelists en archivo xml
2024-06-21 18:12:46 +02:00
snt
14a8aab0a4 Fixes cue list order and filename in widget 2024-06-20 18:57:43 +02:00
snt
84702c5e44 cambio a un vector de CueTrack en vex de CueTrackWidget.
CueTrackListWidget ahora posee el contenedor de CueTracks y muestra un
QTableWidget con info de las CueTracks.
2024-06-20 14:56:15 +02:00
snt
3b98be8b14 wip - antes de cambiar a TableWidget 2024-06-19 15:20:53 +02:00
snt
bd9b07f000 wip edit cues 2024-06-18 20:23:19 +02:00
snt
333d7c7b49 wip settings 2024-06-18 14:59:31 +02:00
snt
52b44a4d7c wip cue list básica en una capa. 2024-06-17 19:12:05 +02:00
snt
647b75f168 mend 2024-06-16 18:17:47 +02:00
snt
9c97f20470 wip show player 2024-06-16 18:15:27 +02:00
51 changed files with 92590 additions and 202 deletions

2
.gitignore vendored
View file

@ -44,3 +44,5 @@ log/**
*~ *~
*.autosave *.autosave
*.vscode*

View file

@ -1,8 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<lmsAudio ui="1" layersNumber="4" path="../media/sound" >
<audioDevice devicesNumber="2" id0="3" id1="4"/>
<layer id="0" dmx="1" universe="1" audioDevice="0" />
<layer id="1" dmx="17" universe="1" audioDevice="0" />
<layer id="2" dmx="33" universe="1" audioDevice="1" />
<layer id="3" dmx="49" universe="1" audioDevice="1"/>
</lmsAudio>

8
docs/lms-audio.xml Normal file
View file

@ -0,0 +1,8 @@
<?xml version='1.0' encoding='UTF-8'?>
<lmsAudio ui="1" dmxActive="0" showPlayerActive="1" layersNumber="4" path="../media/sound" >
<audioDevice devicesNumber="2" id0="4" id1="3"/>
<layer id="0" dmx="1" universe="1" audioDevice="0" />
<layer id="1" dmx="26" universe="1" audioDevice="0" />
<layer id="2" dmx="51" universe="1" audioDevice="1" />
<layer id="3" dmx="76" universe="1" audioDevice="1"/>
</lmsAudio>

View file

@ -34,7 +34,6 @@ v 0.3.0
- Rose noise and sine generator. - Rose noise and sine generator.
- Logs, verbosity, timestamp. - Logs, verbosity, timestamp.
- New play mode without pitch control, it saves resources. MA_SOUND_FLAG_NO_PITCH - New play mode without pitch control, it saves resources. MA_SOUND_FLAG_NO_PITCH
- SettingsDialog.
- Load/save conf file. - Load/save conf file.
- ¿Exit Point? is it needed? - ¿Exit Point? is it needed?
- Hardening: check return errors, try/catch exceptions, i'm too happy.... - Hardening: check return errors, try/catch exceptions, i'm too happy....
@ -42,3 +41,9 @@ v 0.3.0
- ampliar writer para recibir un número n de entradas y escribirlas cada una en un buffer - ampliar writer para recibir un número n de entradas y escribirlas cada una en un buffer
- aislar miniaudio del callback dmx tal como hemos hecho con la Ui, al menos las operaciones lentas como cargar medios. - aislar miniaudio del callback dmx tal como hemos hecho con la Ui, al menos las operaciones lentas como cargar medios.
- en load media usar un fence para actualizar mediaLoaded. - en load media usar un fence para actualizar mediaLoaded.
- Video
v 0.2.1 showplayer
- SettingsDialog, configuración gráfica.

View file

@ -2,12 +2,16 @@ TEMPLATE = app
TARGET = libremediaserver-audio TARGET = libremediaserver-audio
QT += webkitwidgets widgets QT += webkitwidgets widgets
HEADERS += src/libremediaserver-audio.h \ HEADERS += src/libremediaserver-audio.h \
src/cuetrackwidget.h \
src/editcuetrackwidget.h \
src/cuetracklistwidget.h \
src/showplayer.h \
src/clickabledoublespinbox.h \ src/clickabledoublespinbox.h \
src/clickablelabel.h \ src/clickablelabel.h \
src/clickableslider.h \ src/clickableslider.h \
src/dmxwidget.h \ src/dmxwidget.h \
src/filterbankwidget.h \ src/filterbankwidget.h \
src/libremediaserver-audio-gui.h \ src/libremediaserver-audio-ui.h \
src/ma_writer_node.h \ src/ma_writer_node.h \
src/main.h \ src/main.h \
src/medialibrary.h \ src/medialibrary.h \
@ -20,14 +24,20 @@ HEADERS += src/libremediaserver-audio.h \
src/audiowidget.h \ src/audiowidget.h \
src/defines.h \ src/defines.h \
src/settings.h \ src/settings.h \
src/slidergroup.h src/slidergroup.h \
src/dialgroup.h \
src/audiofade.h
SOURCES += src/main.cpp \ SOURCES += src/main.cpp \
src/cuetrackwidget.cpp \
src/editcuetrackwidget.cpp \
src/cuetracklistwidget.cpp \
src/showplayer.cpp \
src/clickabledoublespinbox.cpp \ src/clickabledoublespinbox.cpp \
src/clickablelabel.cpp \ src/clickablelabel.cpp \
src/clickableslider.cpp \ src/clickableslider.cpp \
src/dmxwidget.cpp \ src/dmxwidget.cpp \
src/filterbankwidget.cpp \ src/filterbankwidget.cpp \
src/libremediaserver-audio-gui.cpp \ src/libremediaserver-audio-ui.cpp \
src/libremediaserver-audio.cpp \ src/libremediaserver-audio.cpp \
src/medialibrary.cpp \ src/medialibrary.cpp \
src/miniaudio.c \ src/miniaudio.c \
@ -37,17 +47,22 @@ SOURCES += src/main.cpp \
src/audiolayerwidget.cpp \ src/audiolayerwidget.cpp \
src/audiowidget.cpp \ src/audiowidget.cpp \
src/settings.cpp \ src/settings.cpp \
src/slidergroup.cpp src/slidergroup.cpp \
FORMS += src/libremediaserver-audio-gui.ui src/dialgroup.cpp
FORMS += \
src/cuetrackwidget.ui \
src/showplayer.ui \
src/libremediaserver-audio-ui.ui
CCFLAG += -msse2 -mavx2 CCFLAG += -msse2 -mavx2
QMAKE_CXXFLAGS += $$(CXXFLAG) QMAKE_CXXFLAGS += $$(CXXFLAG)
QMAKE_CFLAGS += $$(CCFLAG) QMAKE_CFLAGS += $$(CCFLAG)
QMAKE_LFLAGS += $$(LDFLAG) QMAKE_LFLAGS += $$(LDFLAG)
LIBS += -lola -lolacommon -ldl -lpthread -lm LIBS += -lola -lolacommon -ldl -lpthread -lm
# -lcitp
OTHER_FILES += \ OTHER_FILES += \
LICENSE.txt \ LICENSE.txt \
docs/compiling.txt \ docs/compiling.txt \
docs/changelog.txt \ docs/changelog.txt \
docs/lms-audio.xlm \ docs/lms-audio.xlm \
docs/roadmap.txt docs/roadmap.txt
RESOURCES += \
lms-resources.qrc

16
lms-resources.qrc Normal file
View file

@ -0,0 +1,16 @@
<RCC>
<qresource prefix="/buttons">
<file>resources/copy_button.png</file>
<file>resources/cut_button.png</file>
<file>resources/delete_button.png</file>
<file>resources/edit_button.png</file>
<file>resources/load_button.png</file>
<file>resources/new_button.png</file>
<file>resources/paste_button.png</file>
<file>resources/save_button.png</file>
<file>resources/icon.png</file>
<file>resources/panic_button.jpg</file>
<file>resources/go_button.jpeg</file>
<file>resources/restore_button.jpg</file>
</qresource>
</RCC>

BIN
resources/copy_button.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 871 B

BIN
resources/cut_button.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
resources/delete_button.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
resources/edit_button.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

BIN
resources/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

BIN
resources/load_button.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

BIN
resources/new_button.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
resources/panic_button.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

BIN
resources/paste_button.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

BIN
resources/save_button.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

23
src/.qmake.stash Normal file
View file

@ -0,0 +1,23 @@
QMAKE_CXX.QT_COMPILER_STDCXX = 201703L
QMAKE_CXX.QMAKE_GCC_MAJOR_VERSION = 11
QMAKE_CXX.QMAKE_GCC_MINOR_VERSION = 4
QMAKE_CXX.QMAKE_GCC_PATCH_VERSION = 0
QMAKE_CXX.COMPILER_MACROS = \
QT_COMPILER_STDCXX \
QMAKE_GCC_MAJOR_VERSION \
QMAKE_GCC_MINOR_VERSION \
QMAKE_GCC_PATCH_VERSION
QMAKE_CXX.INCDIRS = \
/usr/include/c++/11 \
/usr/include/x86_64-linux-gnu/c++/11 \
/usr/include/c++/11/backward \
/usr/lib/gcc/x86_64-linux-gnu/11/include \
/usr/local/include \
/usr/include/x86_64-linux-gnu \
/usr/include
QMAKE_CXX.LIBDIRS = \
/usr/lib/gcc/x86_64-linux-gnu/11 \
/usr/lib/x86_64-linux-gnu \
/usr/lib \
/lib/x86_64-linux-gnu \
/lib

View file

@ -275,10 +275,10 @@ void AudioLayerWidget::setMediaFile(QString file)
int size = list.size(); int size = list.size();
if (size >= 2) { if (size >= 2) {
QString tmp = list.at(size - 2); QString tmp = list.at(size - 2);
tmp.truncate(60); tmp.truncate(64);
m_folderValue->setText(tmp); m_folderValue->setText(tmp);
tmp = list.at(size - 1); tmp = list.at(size - 1);
tmp.truncate(40); tmp.truncate(64);
m_fileValue->setText(tmp); m_fileValue->setText(tmp);
} }
} }

View file

@ -19,7 +19,6 @@ public:
explicit AudioLayerWidget(QWidget *parent = 0, int layer = 0); explicit AudioLayerWidget(QWidget *parent = 0, int layer = 0);
~AudioLayerWidget(); ~AudioLayerWidget();
// From OLA -> LibreMediaServer -> AudioWidget
void setMediaFile(QString file); void setMediaFile(QString file);
void setDuration(float dur); void setDuration(float dur);
void setCurrentTime(float progress); void setCurrentTime(float progress);

View file

@ -5,13 +5,5 @@
ClickableDoubleSpinBox::ClickableDoubleSpinBox(QWidget *parent) ClickableDoubleSpinBox::ClickableDoubleSpinBox(QWidget *parent)
: QDoubleSpinBox(parent) : QDoubleSpinBox(parent)
{ {
setFocusPolicy(Qt::NoFocus);
setButtonSymbols(QAbstractSpinBox::NoButtons);
setValue(-1);
setDecimals(1);
setAlignment(Qt::AlignHCenter);
setContentsMargins(0, 0, 0, 0);
setMaximumWidth(66);
setMinimumWidth(25);
} }

View file

@ -10,11 +10,20 @@ class ClickableDoubleSpinBox : public QDoubleSpinBox
{ {
Q_OBJECT Q_OBJECT
public: public:
bool active = false;
explicit ClickableDoubleSpinBox(QWidget *parent = nullptr); explicit ClickableDoubleSpinBox(QWidget *parent = nullptr);
protected: protected:
void mousePressEvent ( QMouseEvent * event ) { void mousePressEvent ( QMouseEvent * event ) {
if (event->button() == Qt::LeftButton) { if (event->button() == Qt::LeftButton) {
emit click(); emit click();
active = true;
//this->setBackgroundRole(QPalette::BrightText);
} else if (event->button() == Qt::RightButton) {
emit click();
active = false;
//this->setBackgroundRole(QPalette::Shadow);
} }
event->accept(); event->accept();
} }

605
src/cuetracklistwidget.cpp Normal file
View file

@ -0,0 +1,605 @@
#include <algorithm>
#include <iostream>
#include <string>
#include <fstream>
#include <QWidget>
#include <QScrollArea>
#include <QEvent>
#include <QKeyEvent>
#include <QShortcut>
#include <QVBoxLayout>
#include <QTableWidget>
#include <QTableWidgetItem>
#include <QFile>
#include <QXmlStreamReader>
#include <QMessageBox>
#include "cuetracklistwidget.h"
#include "editcuetrackwidget.h"
CueTrackListWidget::CueTrackListWidget(QWidget *parent)
{
layout = new QVBoxLayout(this);
tableWidget = new QTableWidget();
tableWidget->setSortingEnabled(false);
layout->addWidget(tableWidget);
setLayout(layout);
tableWidget->installEventFilter(parent);
QShortcut *shortcut_up = new QShortcut(QKeySequence("Up"), parent);
QObject::connect(shortcut_up, SIGNAL(activated()), this , SLOT(key_up()));
QShortcut *shortcut_down = new QShortcut(QKeySequence("Down"), parent);
QObject::connect(shortcut_down, SIGNAL(activated()), this, SLOT(key_down()));
QShortcut *shortcut_copy = new QShortcut(QKeySequence("Ctrl+C"), parent);
QObject::connect(shortcut_copy, SIGNAL(activated()), this, SLOT(copyCueTrack()));
QShortcut *shortcut_cut = new QShortcut(QKeySequence("Ctrl+X"), parent);
QObject::connect(shortcut_cut, SIGNAL(activated()), this, SLOT(cutCueTrack()));
QShortcut *shortcut_paste = new QShortcut(QKeySequence("Ctrl+V"), parent);
QObject::connect(shortcut_paste, SIGNAL(activated()), this, SLOT(pasteCueTrack()));
QShortcut *shortcut_delete = new QShortcut(QKeySequence("Delete"), parent);
QObject::connect(shortcut_delete, SIGNAL(activated()), this, SLOT(deleteCueTrack()));
QShortcut *shortcut_insert = new QShortcut(QKeySequence("Insert"), parent);
QObject::connect(shortcut_insert, SIGNAL(activated()), this, SLOT(createNewCueTrack()));
QShortcut *shortcut_edit = new QShortcut(QKeySequence("e"), parent);
QObject::connect(shortcut_edit, SIGNAL(activated()), this, SLOT(editCueTrack()));
}
void CueTrackListWidget::addCueTrack(CueTrack* cue) {
cueTracks.push_back(cue);
displayCueTrackInTable(cue, -1);
m_size++;
}
void CueTrackListWidget::removeCueTrack(int index) {
if (index >= m_size) return;
cueTracks.erase(cueTracks.begin() + index);
tableWidget->removeRow(index);
m_size--;
}
CueTrack* CueTrackListWidget::getTrackAtIndex(int index) {
if (index < m_size) {
return cueTracks.at(index);
}
return nullptr;
}
CueTrack* CueTrackListWidget::getSelectedTrack(bool advance) {
CueTrack* track = getTrackAtIndex(selectedIndex);
if (advance)
key_down();
return track;
}
void CueTrackListWidget::key_up() {
if (m_size > 0 && selectedIndex > 0) {
updateSelectedCueTrack(false);
selectedIndex--;
updateSelectedCueTrack(true);
tableWidget->scrollToItem(tableWidget->item(selectedIndex, 0));
}
emit changeSelectedIndex(selectedIndex);
}
void CueTrackListWidget::key_down() {
if (selectedIndex < m_size - 1 && m_size > 0) {
updateSelectedCueTrack(false);
selectedIndex++;
updateSelectedCueTrack(true);
tableWidget->scrollToItem(tableWidget->item(selectedIndex, 0));
}
emit changeSelectedIndex(selectedIndex);
}
void CueTrackListWidget::displayCueTrackInTable(CueTrack *cueTrack, int index) {
if (tableWidget->columnCount() == 0) {
tableWidget->setColumnCount(7);
QStringList headers = {"Active", "Cue","Chan", "Name", "Vol", "State", "File"};
tableWidget->setHorizontalHeaderLabels(headers);
}
if (index == -1)
index = tableWidget->rowCount();
tableWidget->insertRow(index);
tableWidget->setItem(index, 0, new QTableWidgetItem(cueTrack->active ? "Yes" : "No"));
tableWidget->setItem(index, 1, new QTableWidgetItem(QStringLiteral("%1").arg(cueTrack->userNumber, 3, 10, QLatin1Char('0'))));
tableWidget->setItem(index, 2, new QTableWidgetItem(QString::number(cueTrack->audioLayer)));
tableWidget->setItem(index, 3, new QTableWidgetItem(cueTrack->name.data()));
tableWidget->setItem(index, 4, new QTableWidgetItem(QString::number(cueTrack->volume)));
QString statusStr;
statusStr = statusToString(cueTrack->status);
tableWidget->setItem(index, 5, new QTableWidgetItem(statusStr));
tableWidget->setItem(index, 6, new QTableWidgetItem(*getFileName(cueTrack->filePath)));
}
void CueTrackListWidget::updateSelectedCueTrack(bool highlightRow) {
if (selectedIndex >= 0 && selectedIndex < tableWidget->rowCount()) {
QBrush backgroundBrush(highlightRow ? QColor(248, 248, 200) : Qt::white);
for (int column = 0; column < tableWidget->columnCount(); ++column) {
QTableWidgetItem *item = tableWidget->item(selectedIndex, column);
if (!item) {
item = new QTableWidgetItem();
tableWidget->setItem(selectedIndex, column, item);
}
item->setBackground(backgroundBrush);
}
tableWidget->resizeColumnsToContents();
tableWidget->resizeRowsToContents();
}
}
void CueTrackListWidget::cueTrackLoadDefaults(CueTrack * t)
{
t->active = false;
t->audioLayer = 0;
t->bus1 = 100;
t->bus2 = 100;
t->entryPoint = 0;
t->exitPoint = 255;
t->fadeIn = 2;
t->fadeOut = 2;
t->waitIn = 0;
t->waitOut = 0;
t->pan = 0;
t->pitch = 1;
t->status = Status::PlayingOnce;
lastUserCueNumber += 10;
t->userNumber = lastUserCueNumber;
t->volume = 80;
t->stopAtEnd = true;
t->filePath = "";
t->duration = 0;
t->multi = false;
}
void CueTrackListWidget::createNewCueTrack()
{
CueTrack *t = new CueTrack;
cueTrackLoadDefaults(t);
EditCueTrackWidget dialog(t, this);
if (dialog.exec() == QDialog::Accepted) {
t->active = false;
addCueTrack(t);
if (m_size == 1)
{
updateSelectedCueTrack(true);
emit changeSelectedIndex(0);
} else
redrawCueTrackList();
if (lastUserCueNumber < t->userNumber)
lastUserCueNumber = t->userNumber;
} else
delete (t);
}
void CueTrackListWidget::editCueTrack()
{
CueTrack *current = getTrackAtIndex(selectedIndex);
EditCueTrackWidget dialog(current, this);
if (dialog.exec() == QDialog::Accepted) {
copyCueTrack(dialog.cueTrack, current);
redrawCueTrackList();
emit changeSelectedIndex(selectedIndex);
}
if (lastUserCueNumber < current->userNumber)
lastUserCueNumber = current->userNumber;
}
void CueTrackListWidget::deleteCueTrack()
{
QMessageBox::StandardButton reply;
reply = QMessageBox::question(this, "Delete Cue Track", "Are you sure you want to delete this cue track?",
QMessageBox::Yes | QMessageBox::No);
if (reply == QMessageBox::Yes) {
removeCueTrack(selectedIndex);
if (selectedIndex >= m_size)
{
selectedIndex = m_size - 1;
emit changeSelectedIndex(selectedIndex);
}
redrawCueTrackList();
}
}
void CueTrackListWidget::copyCueTrack(CueTrack *src, CueTrack *dst)
{
dst->active = src->active;
dst->audioLayer = src->audioLayer;
dst->bus1 = src->bus1;
dst->bus2 = src->bus2;
dst->entryPoint = src->entryPoint;
dst->exitPoint = src->exitPoint;
dst->fadeIn = src->fadeIn;
dst->fadeOut = src->fadeOut;
dst->waitIn = src->waitIn;
dst->waitOut = src->waitOut;
dst->pan = src->pan;
dst->pitch = src->pitch;
dst->status = src->status;
dst->userNumber = src->userNumber;
dst->volume = src->volume;
dst->stopAtEnd = src->stopAtEnd;
dst->name = src->name;
dst->description = src->description;
dst->filePath = src->filePath;
dst->duration = src->duration;
dst->multi = src->multi;
}
QString *CueTrackListWidget::getFileName(std::string s)
{
size_t bar = s.rfind("/");
std::string tmp = s.substr(bar + 1, strlen(s.data()));
QString *ret = new QString(tmp.data());
ret->truncate(64);
return ret;
}
void CueTrackListWidget::sortCueTrackList()
{
std::sort(cueTracks.begin(), cueTracks.end(), [](CueTrack *a, CueTrack *b) {
return a->userNumber < b->userNumber;
});
}
void CueTrackListWidget::redrawCueTrackList()
{
if (!m_size)
return;
int selected = cueTracks.at(selectedIndex)->userNumber;
clearTableWidget();
tableWidget->setColumnCount(7);
QStringList headers = {"Active", "Cue","Chan", "Name", "Vol", "State", "File"};
tableWidget->setHorizontalHeaderLabels(headers);
sortCueTrackList();
selectedIndex = 0;
for (int i = 0; i < m_size; i++)
{
displayCueTrackInTable(cueTracks.at(i), i);
}
selectedIndex = 0;
while (cueTracks.at(selectedIndex)->userNumber != selected)
{
selectedIndex++;
}
updateSelectedCueTrack(true);
tableWidget->resizeColumnsToContents();
tableWidget->resizeRowsToContents();
tableWidget->scrollToItem(tableWidget->item(selectedIndex, 0));
tableWidget->blockSignals(false);
emit changeSelectedIndex(selectedIndex);
}
void CueTrackListWidget::loadCueTrackList(std::string filename)
{
qDebug() << "loading cue list from " << filename.data();
QFile file(QString::fromStdString(filename));
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
{
std::cerr << "No se pudo abrir el archivo para lectura" << std::endl;
return;
}
QXmlStreamReader xmlReader(&file);
clearCueTrackList();
lastUserCueNumber = 0;
while (!xmlReader.atEnd() && !xmlReader.hasError())
{
QXmlStreamReader::TokenType token = xmlReader.readNext();
if (token == QXmlStreamReader::StartElement)
{
if (xmlReader.name() == "CueTrack")
{
CueTrack *t = new CueTrack;
while (!(xmlReader.tokenType() == QXmlStreamReader::EndElement && xmlReader.name() == "CueTrack"))
{
if (xmlReader.tokenType() == QXmlStreamReader::StartElement)
{
QString elementName = xmlReader.name().toString();
xmlReader.readNext();
if (xmlReader.tokenType() == QXmlStreamReader::Characters)
{
if (elementName == "filePath") {
t->filePath = xmlReader.text().toString().toStdString();
}
else if (elementName == "volume") {
t->volume = xmlReader.text().toInt();
}
else if (elementName == "pan") {
t->pan = xmlReader.text().toInt();
}
else if (elementName == "pitch") {
t->pitch = xmlReader.text().toInt();
}
else if (elementName == "bus1") {
t->bus1 = xmlReader.text().toInt();
}
else if (elementName == "bus2") {
t->bus2 = xmlReader.text().toInt();
}
else if (elementName == "status") {
QString tmp = xmlReader.text().toString();
t->status = stringToStatus(&tmp);
}
else if (elementName == "fadeOut") {
t->fadeOut = xmlReader.text().toInt();
}
else if (elementName == "fadeIn") {
t->fadeIn = xmlReader.text().toInt();
}
else if (elementName == "waitIn") {
t->waitIn = xmlReader.text().toInt();
}
else if (elementName == "waitOut") {
t->waitOut = xmlReader.text().toInt();
}
else if (elementName == "stopAtEnd") {
QString tmp = xmlReader.text().toString().toLower();
if (tmp.compare("false"))
t->stopAtEnd = true;
else {
t->stopAtEnd = false;
}
}
else if (elementName == "name") {
t->name = xmlReader.text().toString().toStdString();
}
else if (elementName == "description") {
t->description = xmlReader.text().toString().toStdString();
}
else if (elementName == "userNumber") {
t->userNumber = xmlReader.text().toInt();
if (t->userNumber > lastUserCueNumber)
{
lastUserCueNumber = t->userNumber;
}
}
else if (elementName == "entryPoint") {
t->entryPoint = xmlReader.text().toInt();
}
else if (elementName == "exitPoint") {
t->exitPoint = xmlReader.text().toInt();
}
else if (elementName == "audioLayer") {
t->audioLayer = xmlReader.text().toInt();
}
else if (elementName == "duration") {
t->duration = xmlReader.text().toInt();
}
else if (elementName == "multi") {
QString tmp = xmlReader.text().toString().toLower();
if (tmp.compare("false"))
t->multi = true;
else {
t->multi = false;
}
}
t->active = false;
}
}
xmlReader.readNext();
}
addCueTrack(t);
}
}
}
if (xmlReader.hasError())
{
std::cerr << "Error al leer el archivo XML: " << xmlReader.errorString().toStdString() << std::endl;
}
file.close();
redrawCueTrackList();
}
void CueTrackListWidget::saveCueTrackList(std::string filename)
{
qDebug() << "saving cue list to " << filename.data();
std::ofstream file(filename);
if (!file.is_open())
{
return;
}
file << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
file << "<CueTrackList>\n";
for (int i = 0; i < m_size; i++)
{
file << cueTrackToXml(*cueTracks.at(i)) << std::endl;
}
file << "</CueTrackList>\n";
file.close();
}
std::string CueTrackListWidget::cueTrackToXml(const CueTrack& cueTrack)
{
std::string xml = " <CueTrack>\n";
xml += " <filePath>" + cueTrack.filePath + "</filePath>\n";
xml += " <volume>" + std::to_string(cueTrack.volume) + "</volume>\n";
xml += " <pan>" + std::to_string(cueTrack.pan) + "</pan>\n";
xml += " <pitch>" + std::to_string(cueTrack.pitch) + "</pitch>\n";
xml += " <bus1>" + std::to_string(cueTrack.bus1) + "</bus1>\n";
xml += " <bus2>" + std::to_string(cueTrack.bus2) + "</bus2>\n";
xml += " <status>";
xml += statusToString(cueTrack.status);
xml += "</status>\n";
xml += " <fadeOut>" + std::to_string(cueTrack.fadeOut) + "</fadeOut>\n";
xml += " <fadeIn>" + std::to_string(cueTrack.fadeIn) + "</fadeIn>\n";
xml += " <waitIn>" + std::to_string(cueTrack.waitIn) + "</waitIn>\n";
xml += " <waitOut>" + std::to_string(cueTrack.waitOut) + "</waitOut>\n";
xml += " <stopAtEnd>";
xml += (cueTrack.stopAtEnd ? "true" : "false");
xml += "</stopAtEnd>\n";
xml += " <name>" + cueTrack.name + "</name>\n";
xml += " <description>" + cueTrack.description + "</description>\n";
xml += " <userNumber>" + std::to_string(cueTrack.userNumber) + "</userNumber>\n";
xml += " <entryPoint>" + std::to_string(cueTrack.entryPoint) + "</entryPoint>\n";
xml += " <exitPoint>" + std::to_string(cueTrack.exitPoint) + "</exitPoint>\n";
xml += " <audioLayer>" + std::to_string(cueTrack.audioLayer) + "</audioLayer>\n";
xml += " <duration>" + std::to_string(cueTrack.duration) + "</duration>\n";
xml += " <multi>";
xml += (cueTrack.multi ? "true" : "false");
xml += "</multi>\n";
xml += " </CueTrack>\n";
return xml;
}
void CueTrackListWidget::clearCueTrackList()
{
for (int i = 0; i < m_size; i++)
{
delete cueTracks.at(i);
}
cueTracks.clear();
m_size = 0;
selectedIndex = 0;
}
#include <QFile>
#include <QXmlStreamWriter>
void saveCueTrackToXml(const CueTrack& cueTrack, const QString& filename) {
QFile file(filename);
if (!file.open(QIODevice::WriteOnly)) {
qInfo() << "Can not open file " << filename << "\n";
return;
}
QXmlStreamWriter xmlWriter(&file);
xmlWriter.setAutoFormatting(true);
xmlWriter.writeStartDocument();
xmlWriter.writeStartElement("CueTrack");
xmlWriter.writeTextElement("filePath", QString::fromStdString(cueTrack.filePath));
xmlWriter.writeTextElement("volume", QString::number(cueTrack.volume));
xmlWriter.writeTextElement("pan", QString::number(cueTrack.pan));
xmlWriter.writeTextElement("pitch", QString::number(cueTrack.pitch));
xmlWriter.writeTextElement("bus1", QString::number(cueTrack.bus1));
xmlWriter.writeTextElement("bus2", QString::number(cueTrack.bus2));
xmlWriter.writeTextElement("status", statusToString(cueTrack.status));
xmlWriter.writeTextElement("fadeOut", QString::number(cueTrack.fadeOut));
xmlWriter.writeTextElement("fadeIn", QString::number(cueTrack.fadeIn));
xmlWriter.writeTextElement("waitIn", QString::number(cueTrack.waitIn));
xmlWriter.writeTextElement("waitOut", QString::number(cueTrack.waitOut));
xmlWriter.writeTextElement("stopAtEnd", cueTrack.stopAtEnd ? "true" : "false");
xmlWriter.writeTextElement("name", QString::fromStdString(cueTrack.name));
xmlWriter.writeTextElement("description", QString::fromStdString(cueTrack.description));
xmlWriter.writeTextElement("userNumber", QString::number(cueTrack.userNumber));
xmlWriter.writeTextElement("entryPoint", QString::number(cueTrack.entryPoint));
xmlWriter.writeTextElement("exitPoint", QString::number(cueTrack.exitPoint));
xmlWriter.writeTextElement("audioLayer", QString::number(cueTrack.audioLayer));
xmlWriter.writeEndElement();
xmlWriter.writeEndDocument();
file.close();
}
#include <QFile>
#include <QXmlStreamReader>
#include <QString>
#include <string>
CueTrack loadCueTrackFromXml(const QString& filename) {
QFile file(filename);
if (!file.open(QIODevice::ReadOnly)) {
throw std::runtime_error("No se pudo abrir el archivo");
}
QXmlStreamReader xmlReader(&file);
CueTrack cueTrack;
while (!xmlReader.atEnd() && !xmlReader.hasError()) {
QXmlStreamReader::TokenType token = xmlReader.readNext();
if (token == QXmlStreamReader::StartElement) {
const QString elementName = xmlReader.name().toString();
if (elementName == "filePath") {
cueTrack.filePath = xmlReader.readElementText().toStdString();
} else if (elementName == "vol") {
cueTrack.volume = xmlReader.readElementText().toInt();
} else if (elementName == "pan") {
cueTrack.pan = xmlReader.readElementText().toInt();
} else if (elementName == "pitch") {
cueTrack.pitch = xmlReader.readElementText().toInt();
} else if (elementName == "bus1") {
cueTrack.bus1 = xmlReader.readElementText().toInt();
} else if (elementName == "bus2") {
cueTrack.bus2 = xmlReader.readElementText().toInt();
} else if (elementName == "state") {
QString tmp = xmlReader.readElementText();
cueTrack.status = stringToStatus(&tmp);
} else if (elementName == "fadeOut") {
cueTrack.fadeOut = xmlReader.readElementText().toInt();
} else if (elementName == "fadeIn") {
cueTrack.fadeIn = xmlReader.readElementText().toInt();
} else if (elementName == "waitIn") {
cueTrack.waitIn = xmlReader.readElementText().toInt();
} else if (elementName == "waitOut") {
cueTrack.waitOut = xmlReader.readElementText().toInt();
} else if (elementName == "stopAtEnd") {
cueTrack.stopAtEnd = xmlReader.readElementText() == "true";
} else if (elementName == "name") {
cueTrack.name = xmlReader.readElementText().toStdString();
} else if (elementName == "description") {
cueTrack.description = xmlReader.readElementText().toStdString();
} else if (elementName == "userNumber") {
cueTrack.userNumber = xmlReader.readElementText().toInt();
} else if (elementName == "entryPoint") {
cueTrack.entryPoint = xmlReader.readElementText().toInt();
} else if (elementName == "exitPoint") {
cueTrack.exitPoint = xmlReader.readElementText().toInt();
} else if (elementName == "audioLayer") {
cueTrack.audioLayer = xmlReader.readElementText().toInt();
} else if (elementName == "duration") {
cueTrack.duration = xmlReader.readElementText().toInt();
}
}
}
if (xmlReader.hasError()) {
throw std::runtime_error("Error al parsear el archivo XML");
}
file.close();
return cueTrack;
}
void CueTrackListWidget::clearTableWidget()
{
for (int i = 0; i < m_size; i++)
{
tableWidget->removeRow(i);
}
tableWidget->clear();
tableWidget->setRowCount(0);
}
void CueTrackListWidget::cueTrackAtEnd(int layer)
{
for (int i = 0; i < m_size; i++)
{
CueTrack * cur = cueTracks.at(i);
if (cur->audioLayer == layer)
{
cur->active = false;
}
}
redrawCueTrackList();
}
void CueTrackListWidget::copyCueTrack() {
if (selectedIndex >= 0 && selectedIndex < m_size) {
delete copiedCue;
copiedCue = new CueTrack(*cueTracks.at(selectedIndex));
}
}
void CueTrackListWidget::cutCueTrack() {
if (selectedIndex >= 0 && selectedIndex < m_size) {
delete copiedCue;
copiedCue = new CueTrack(*cueTracks.at(selectedIndex));
removeCueTrack(selectedIndex);
}
}
void CueTrackListWidget::pasteCueTrack() {
if (copiedCue != nullptr) {
CueTrack* newCue = new CueTrack(*copiedCue);
addCueTrack(newCue);
}
}

66
src/cuetracklistwidget.h Normal file
View file

@ -0,0 +1,66 @@
#ifndef CUETRACKLISTWIDGET_H
#define CUETRACKLISTWIDGET_H
#include <vector>
#include <QWidget>
#include <QScrollArea>
#include <QEvent>
#include <QKeyEvent>
#include <QShortcut>
#include <QVBoxLayout>
#include <QTableWidget>
#include <QTableWidgetItem>
#include "defines.h"
class CueTrackListWidget : public QWidget {
Q_OBJECT
public:
explicit CueTrackListWidget(QWidget *parent = nullptr);
public slots:
CueTrack* getSelectedTrack(bool advance);
void createNewCueTrack();
void editCueTrack();
void deleteCueTrack();
int getSelectedIndex() { return selectedIndex; };
CueTrack* getTrackAtIndex(int index);
QString *getFileName(std::string s);
void loadCueTrackList(std::string filename);
void saveCueTrackList(std::string filename);
void clearCueTrackList();
void setLastUserCueNumber(size_t n) { lastUserCueNumber = n; }
size_t getLastUserCueNumber() { return lastUserCueNumber; }
void cueTrackAtEnd(int layer);
void redrawCueTrackList();
private:
std::vector<CueTrack *> cueTracks;
QVBoxLayout* layout;
QTableWidget* tableWidget;
int m_size = 0;
int size() { return m_size; }
int selectedIndex = 0;
int lastUserCueNumber = 0;
CueTrack* copiedCue = nullptr;
private slots:
void addCueTrack(CueTrack* cue);
void removeCueTrack(int index);
void key_up();
void key_down();
void displayCueTrackInTable(CueTrack *cueTrack, int index);
void updateSelectedCueTrack(bool highlightRow);
void cueTrackLoadDefaults(CueTrack * t);
void copyCueTrack(CueTrack *src, CueTrack *dst);
void sortCueTrackList();
void clearTableWidget();
std::string cueTrackToXml(const CueTrack& cueTrack);
void copyCueTrack();
void pasteCueTrack();
void cutCueTrack();
signals:
void changeSelectedIndex(int index);
void goAction(int channel);
};
#endif

148
src/cuetrackwidget.cpp Normal file
View file

@ -0,0 +1,148 @@
#include <QFileInfo>
#include "cuetrackwidget.h"
#include "ui_cuetrackwidget.h"
CueTrackWidget::CueTrackWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::CueTrackWidget)
{
ui->setupUi(this);
tlFade.setFrameRange(0, 100);
tlWaitIn.setFrameRange(0, 100);
tlWaitOut.setFrameRange(0, 100);
connect(&tlFade, &QTimeLine::frameChanged, ui->cueProgressBar, &QProgressBar::setValue);
connect(&tlWaitIn, &QTimeLine::frameChanged, ui->cueProgressBar, &QProgressBar::setValue);
connect(&tlWaitOut, &QTimeLine::frameChanged, ui->cueProgressBar, &QProgressBar::setValue);
connect(&tlFade, SIGNAL(finished()), this, SLOT(fadeSlot()));
connect(&tlWaitIn, SIGNAL(finished()), this, SLOT(waitInSlot()));
connect(&tlWaitOut, SIGNAL(finished()), this, SLOT(waitOutSlot()));
connect(ui->playButton, SIGNAL(clicked()), this, SLOT(play()));
connect(ui->stopButton, SIGNAL(clicked()), this, SLOT(stop()));
connect(ui->pauseButton, SIGNAL(clicked()), this, SLOT(pause()));
connect(ui->fadeInButton, SIGNAL(clicked()), this, SLOT(fadeIn()));
connect(ui->fadeOutButton, SIGNAL(clicked()), this, SLOT(fadeOut()));
connect(ui->muteButton, SIGNAL(clicked()), this, SLOT(mute()));
}
CueTrackWidget::~CueTrackWidget()
{
delete ui;
}
void CueTrackWidget::loadCueTrack(CueTrack* track)
{
ui->cueName->setText(track->name.data());
ui->cueNumber->display(track->userNumber);
ui->vol->display(track->volume);
ui->pan->display(track->pan);
ui->pitch->display(track->pitch);
ui->bus1->display(track->bus1);
ui->bus2->display(track->bus2);
ui->fade->display(track->fadeIn);
ui->waitIn->display(track->waitIn);
ui->waitOut->display(track->waitOut);
ui->halt->setChecked(track->stopAtEnd);
QFileInfo fileInfo(track->filePath.data());
fileInfo.fileName().truncate(64);
ui->mediaFileName->setText(fileInfo.fileName());
ui->status->setText(statusToString(track->status));
ui->audioLayer->display(track->audioLayer);
ui->multi->setChecked(track->multi);
ui->fadeOut->display(track->fadeOut);
}
void CueTrackWidget::go()
{
tlFade.stop();
tlWaitIn.stop();
tlWaitOut.stop();
ui->cueProgressBar->setValue(0);
if (ui->waitIn->value() > 0)
{
tlWaitIn.setDuration(ui->waitIn->value() * 1000);
tlWaitIn.start();
ui->cueProgressBar->setStyleSheet("QProgressBar::chunk { background-color: #FF0000; }");
} else {
waitInSlot();
}
}
void CueTrackWidget::waitInSlot()
{
emit goAction(ui->audioLayer->intValue());
if (ui->fade->value() > 0) {
tlFade.setDuration(ui->fade->value() * 1000);
tlFade.start();
ui->cueProgressBar->setStyleSheet("QProgressBar::chunk { background-color: #00FF00; }");
} else {
fadeSlot();
}
}
void CueTrackWidget::fadeSlot()
{
if (ui->waitOut->value() > 0) {
tlWaitOut.setDuration(ui->waitOut->value() * 1000);
tlWaitOut.start();
ui->cueProgressBar->setStyleSheet("QProgressBar::chunk { background-color: #FF0000; }");
} else {
waitOutSlot();
}
}
void CueTrackWidget::waitOutSlot()
{
ui->cueProgressBar->setStyleSheet("QProgressBar::chunk { background-color: #0000FF; }");
emit cueFinished(ui->audioLayer->intValue());
}
void CueTrackWidget::setNextCue()
{
ui->cueProgressBar->setDisabled(true);
ui->cueProgressBar->hide();
ui->audioControlLayout->setEnabled(false);
QList<int> list = { 1, 0 };
ui->cueValues->setSizes(list);
}
void CueTrackWidget::play()
{
QString t = ui->status->text();
emit changeStatus(ui->audioLayer->intValue(), stringToStatus(&t));
}
void CueTrackWidget::stop()
{
emit changeStatus(ui->audioLayer->intValue(), Status::Stopped);
}
void CueTrackWidget::pause()
{
emit changeStatus(ui->audioLayer->intValue(), Status::Paused);
}
void CueTrackWidget::fadeIn()
{
emit changeVol(ui->audioLayer->intValue(), Slider::Volume, ui->vol->value() * 655.35, ui->fade->value() * 1000, ui->fadeOut->value() * 1000);
muted = false;
}
void CueTrackWidget::fadeOut()
{
emit changeVol(ui->audioLayer->intValue(), Slider::Volume, 0, ui->fade->value() * 1000, ui->fadeOut->value() * 1000);
muted = true;
}
void CueTrackWidget::mute()
{
if (muted)
{
emit changeVol(ui->audioLayer->intValue(), Slider::Volume, ui->vol->value() * 655.35, 0, 0);
muted = false;
}
else {
emit changeVol(ui->audioLayer->intValue(), Slider::Volume, 0, 0, 0);
muted = true;
}
}

52
src/cuetrackwidget.h Normal file
View file

@ -0,0 +1,52 @@
#ifndef CUETRACKWIDGET_H
#define CUETRACKWIDGET_H
#include <QWidget>
#include <QTimeLine>
#include "defines.h"
#include "dialgroup.h"
namespace Ui {
class CueTrackWidget;
}
class CueTrackWidget : public QWidget
{
Q_OBJECT
public:
explicit CueTrackWidget(QWidget *parent = nullptr);
~CueTrackWidget();
public slots:
void loadCueTrack(CueTrack* track);
void go();
void setNextCue();
private:
Ui::CueTrackWidget *ui;
QTimeLine tlFade;
QTimeLine tlWaitIn;
QTimeLine tlWaitOut;
bool muted = false;
private slots:
void fadeSlot();
void waitInSlot();
void waitOutSlot();
void play();
void stop();
void pause();
void fadeIn();
void fadeOut();
void mute();
signals:
void cueFinished(int layer);
void goAction(int layer);
void changeStatus(int layer, Status status);
void changeVol(int layer, Slider s, int value, int fadein, int fadeout);
};
#endif // CUETRACKWIDGET_H

1142
src/cuetrackwidget.ui Normal file

File diff suppressed because it is too large Load diff

View file

@ -4,7 +4,7 @@
#define VERSION "LibreMediaServerAudio v0.2.0 Antigona" #define VERSION "LibreMediaServerAudio v0.2.0 Antigona"
#define COPYRIGHT "(C) 2014-2024 Santi Noreña <lms@criptomart.net>" #define COPYRIGHT "(C) 2014-2024 Santi Noreña <lms@criptomart.net>"
#define LICENSE "GPL3 Licensed. See LICENSE.txt." #define LICENSE "GPL3 Licensed. See LICENSE.txt."
#define DEFAULT_FILE "lms-audio.xlm" #define DEFAULT_FILE "lms-audio.xml"
#define MAX_LAYERS 4 #define MAX_LAYERS 4
#define MAX_AUDIODEVICES 8 #define MAX_AUDIODEVICES 8
#define FORMAT ma_format_f32 /* Must always be f32. */ #define FORMAT ma_format_f32 /* Must always be f32. */
@ -57,12 +57,25 @@ constexpr const char* statusToString(Status e) noexcept
case Status::Iddle: return "Iddle"; case Status::Iddle: return "Iddle";
case Status::PlayingFolder: return "Play Folder"; case Status::PlayingFolder: return "Play Folder";
case Status::PlayingFolderLoop: return "Play Folder Loop"; case Status::PlayingFolderLoop: return "Play Folder Loop";
case Status::PlayingFolderRandom: return "Playing Folder Random"; case Status::PlayingFolderRandom: return "Play Folder Random";
default: return "--++--"; default: return "--++--";
} }
} }
#include <QString> #include <QString>
static Status stringToStatus(QString *statusStr) {
if (!statusStr->compare("Stop")) return Stopped;
else if (!statusStr->compare("Paused") ) return Paused;
else if (!statusStr->compare("Play 1")) return PlayingOnce;
else if (!statusStr->compare("Play Loop")) return PlayingLoop;
else if (!statusStr->compare("Iddle")) return Iddle;
else if (!statusStr->compare("Play Folder")) return PlayingFolder;
else if (!statusStr->compare("Play Folder Loop")) return PlayingFolderLoop;
else if (!statusStr->compare("Play Folder Random")) return PlayingFolderRandom;
else return Iddle;
}
struct layerData { struct layerData {
QString media; QString media;
Status status; Status status;
@ -78,6 +91,33 @@ struct layerData {
int bus1Vol; int bus1Vol;
int bus2Vol; int bus2Vol;
float level; float level;
int fadeIn;
int fadeOut;
}; };
struct CueTrack {
std::string filePath;
int volume = 0; // 0 - 65535 ToDo: change to db -85 - 0
int pan = 128; // 0 - 255
int pitch = 128; // 0 - 255
int bus1 = 255; // 0 - 255
int bus2 = 255; // 0 - 255
Status status;
int fadeOut = 3; // ToDo: change to float or milliseconds
int fadeIn = 3;
int waitIn = 0;
int waitOut = 0;
bool stopAtEnd = false; // trigger next cue when this is executed
std::string name;
std::string description;
int userNumber; // cue user id
int entryPoint; // 0 - 255
int exitPoint; // 0 - 255
int audioLayer; // internal audio layer used when cue is loaded
bool active; // the cue is playing
int duration; // media duration in milliseconds
bool multi; // launch next cue at same time, not waiting for this to finish.
};
#endif // __cplusplus #endif // __cplusplus
#endif // DEFINES_H #endif // DEFINES_H

67
src/dialgroup.cpp Normal file
View file

@ -0,0 +1,67 @@
#include <cmath>
#include <QWidget>
#include <QVBoxLayout>
#include "dialgroup.h"
DialGroup::DialGroup(QString name,
int min,
int max,
int decimals,
QWidget *parent)
: QWidget(parent)
{
QVBoxLayout *layout = new QVBoxLayout;
layout->setAlignment(Qt::AlignHCenter);
layout->setContentsMargins(0, 0, 0, 0);
slider.setFocusPolicy(Qt::NoFocus);
slider.setRange(min, max);
slider.setValue(min + max / 2);
slider.setToolTip(name);
slider.setContentsMargins(0, 0, 0, 0);
/*
slider.setMinimumHeight(0);
slider.setSingleStep(1);
slider.setMinimumWidth(50);
slider.setStyleSheet("QDial {"
"border: 1px solid #aa8895;"
"background: #20182d;"
"margin: 0px;}"
"QSlider::groove:vertical {"
"border: 1px solid #999999;"
"width: 25px;}"
"QDial::tickmarks:vertical {"
"background-color: black;"
"background: red;"
"color: white;}"
*/
valueBox.setFocusPolicy(Qt::NoFocus);
valueBox.setButtonSymbols(QAbstractSpinBox::NoButtons);
valueBox.setMinimumWidth(50);
valueBox.setRange(min, max);
valueBox.setValue(0);
valueBox.setDecimals(decimals);
valueBox.setObjectName(name);
valueBox.setToolTip(name);
valueBox.setAlignment(Qt::AlignHCenter);
valueBox.setContentsMargins(0, 0, 0, 0);
layout->addWidget(&slider);
layout->addWidget(&valueBox);
/* this->setStyleSheet("border: 1px solid #998090;"
"background-color: black;"
"margin: 1px;"
);*/
layout->setSpacing(0);
layout->setContentsMargins(0, 0, 0, 0);
this->setLayout(layout);
slider.setObjectName(name);
}
void DialGroup::setValue(float value)
{
slider.blockSignals(true);
valueBox.blockSignals(true);
slider.setValue(value);
valueBox.setValue(value);
slider.blockSignals(false);
valueBox.blockSignals(false);
}

30
src/dialgroup.h Normal file
View file

@ -0,0 +1,30 @@
#ifndef DIALGROUP_H
#define DIALGROUP_H
#include <QObject>
#include <QWidget>
#include <QDebug>
#include <QDial>
#include "clickabledoublespinbox.h"
class DialGroup : public QWidget
{
Q_OBJECT
public:
explicit DialGroup(QString name,
int min,
int max,
int decimals,
QWidget *parent = nullptr);
public slots:
void setValue(float value);
private:
QDial slider;
ClickableDoubleSpinBox valueBox;
};
#endif

145
src/editcuetrackwidget.cpp Normal file
View file

@ -0,0 +1,145 @@
#include "editcuetrackwidget.h"
#include <QLineEdit>
#include <QFormLayout>
#include <QVBoxLayout>
#include <QFileDialog>
EditCueTrackWidget::EditCueTrackWidget(CueTrack *cueTrack, QWidget *parent)
: QDialog(parent) {
setupUi();
connectSignals();
this->cueTrack = cueTrack;
loadCueTrack(*cueTrack);
}
void EditCueTrackWidget::setupUi() {
QFormLayout *layout = new QFormLayout(this);
userNumberSpin = new QSpinBox(this);
userNumberSpin->setRange(0, 9999);
layout->addRow("User Number", userNumberSpin);
nameEdit = new QLineEdit(this);
layout->addRow("Name", nameEdit);
audioLayerSpin = new QSpinBox(this);
audioLayerSpin->setRange(0, MAX_LAYERS - 1);
layout->addRow("Audio Layer", audioLayerSpin);
filePathEdit = new QLineEdit(this);
layout->addRow("File Path", filePathEdit);
browseButton = new QPushButton("Browse...", this);
layout->addRow(browseButton);
statusCombo = new QComboBox(this);
layout->addRow("Status", statusCombo);
volumeSpin = new QDoubleSpinBox(this);
volumeSpin->setRange(0, 100.00f);
layout->addRow("Volume", volumeSpin);
panSpin = new QDoubleSpinBox(this);
panSpin->setRange(-1, 1);
layout->addRow("Pan", panSpin);
pitchSpin = new QDoubleSpinBox(this);
pitchSpin->setRange(0, 2);
layout->addRow("Pitch", pitchSpin);
bus1Spin = new QDoubleSpinBox(this);
bus1Spin->setRange(0, 100.00f);
layout->addRow("Bus 1", bus1Spin);
bus2Spin = new QDoubleSpinBox(this);
bus2Spin->setRange(0, 100.00f);
layout->addRow("Bus 2", bus2Spin);
fadeInSpin = new QSpinBox(this);
layout->addRow("Fade In", fadeInSpin);
fadeOutSpin = new QSpinBox(this);
fadeOutSpin->setRange(0, 60 * 60 * 24);
layout->addRow("Fade Out", fadeOutSpin);
waitInSpin = new QSpinBox(this);
layout->addRow("Wait In", waitInSpin);
waitOutSpin = new QSpinBox(this);
layout->addRow("Wait Out", waitOutSpin);
stopAtEndCheck = new QCheckBox(this);
layout->addRow("Stop At End", stopAtEndCheck);
descriptionEdit = new QLineEdit(this);
layout->addRow("Description", descriptionEdit);
entryPointSpin = new QSpinBox(this);
layout->addRow("Entry Point", entryPointSpin);
exitPointSpin = new QSpinBox(this);
layout->addRow("Exit Point", exitPointSpin);
multiCheck = new QCheckBox(this);
layout->addRow("Multi Cue", multiCheck);
statusCombo->addItem(statusToString(Status::Stopped));
statusCombo->addItem(statusToString(Status::Paused));
statusCombo->addItem(statusToString(Status::PlayingOnce));
statusCombo->addItem(statusToString(Status::PlayingLoop));
statusCombo->addItem(statusToString(Status::Iddle));
statusCombo->addItem(statusToString(Status::PlayingFolder));
statusCombo->addItem(statusToString(Status::PlayingFolderLoop));
statusCombo->addItem(statusToString(Status::PlayingFolderRandom));
saveButton = new QPushButton("Save", this);
cancelButton = new QPushButton("Cancel", this);
layout->addRow(saveButton);
layout->addRow(cancelButton);
}
void EditCueTrackWidget::loadCueTrack(CueTrack cueTrack) {
filePathEdit->setText(cueTrack.filePath.data());
volumeSpin->setValue(cueTrack.volume);
panSpin->setValue(cueTrack.pan);
pitchSpin->setValue(cueTrack.pitch);
bus1Spin->setValue(cueTrack.bus1);
bus2Spin->setValue(cueTrack.bus2);
fadeInSpin->setValue(cueTrack.fadeIn);
fadeOutSpin->setValue(cueTrack.fadeOut);
waitInSpin->setValue(cueTrack.waitIn);
waitOutSpin->setValue(cueTrack.waitOut);
stopAtEndCheck->setChecked(cueTrack.stopAtEnd);
nameEdit->setText(cueTrack.name.data());
descriptionEdit->setText(cueTrack.description.data());
userNumberSpin->setValue(cueTrack.userNumber);
entryPointSpin->setValue(cueTrack.entryPoint);
exitPointSpin->setValue(cueTrack.exitPoint);
audioLayerSpin->setValue(cueTrack.audioLayer);
QString tmp = statusToString(cueTrack.status);
statusCombo->setCurrentIndex(statusCombo->findText(tmp));
multiCheck->setChecked(cueTrack.multi);
}
CueTrack EditCueTrackWidget::saveCueTrack() {
CueTrack cueTrack;
cueTrack.userNumber = userNumberSpin->value();
cueTrack.name.append(nameEdit->text().toUtf8().constData());
cueTrack.audioLayer = audioLayerSpin->value();
cueTrack.filePath.append(filePathEdit->text().toUtf8().constData());
cueTrack.status = static_cast<Status>(statusCombo->currentIndex());
cueTrack.fadeIn = fadeInSpin->value();
cueTrack.fadeOut = fadeOutSpin->value();
cueTrack.waitIn = waitInSpin->value();
cueTrack.waitOut = waitOutSpin->value();
cueTrack.stopAtEnd = stopAtEndCheck->isChecked();
cueTrack.entryPoint = entryPointSpin->value();
cueTrack.exitPoint = exitPointSpin->value();
cueTrack.volume = volumeSpin->value();
cueTrack.bus1 = bus1Spin->value();
cueTrack.bus2 = bus2Spin->value();
cueTrack.pan = panSpin->value();
cueTrack.pitch = pitchSpin->value();
cueTrack.description.append(descriptionEdit->text().toUtf8().constData());
cueTrack.multi = multiCheck->isChecked();
return cueTrack;
}
void EditCueTrackWidget::accept() {
*cueTrack = saveCueTrack();
QDialog::accept();
}
void EditCueTrackWidget::connectSignals() {
connect(saveButton, &QPushButton::clicked, this, &EditCueTrackWidget::accept);
connect(cancelButton, &QPushButton::clicked, this, &EditCueTrackWidget::reject);
connect(browseButton, &QPushButton::clicked, this, &EditCueTrackWidget::onBrowseButtonClicked);
}
void EditCueTrackWidget::onBrowseButtonClicked() {
QString filePath = QFileDialog::getOpenFileName(this, tr("Select File"), Settings::getInstance()->getPathMedia(), tr("Audio Files (*.mp3 *.wav *.flac)"));
if (!filePath.isEmpty()) {
filePathEdit->setText(filePath);
QDir dir;
Settings::getInstance()->setPathMedia(dir.absoluteFilePath(filePath));
}
}

55
src/editcuetrackwidget.h Normal file
View file

@ -0,0 +1,55 @@
#ifndef EDITCUETRACKWIDGET_H
#define EDITCUETRACKWIDGET_H
#include <QDialog>
#include <QLineEdit>
#include <QSpinBox>
#include <QCheckBox>
#include <QPushButton>
#include <QFormLayout>
#include <QDoubleSpinBox>
#include <QComboBox>
#include "settings.h"
#include "defines.h"
class EditCueTrackWidget : public QDialog
{
Q_OBJECT
public:
explicit EditCueTrackWidget(CueTrack *cueTrack, QWidget *parent = nullptr);
CueTrack *cueTrack;
void loadCueTrack(CueTrack cueTrack);
CueTrack saveCueTrack();
private:
void setupUi();
void connectSignals();
void onBrowseButtonClicked();
void accept() override;
QLineEdit *filePathEdit;
QDoubleSpinBox *volumeSpin;
QDoubleSpinBox *panSpin;
QDoubleSpinBox *pitchSpin;
QDoubleSpinBox *bus1Spin;
QDoubleSpinBox *bus2Spin;
QSpinBox *fadeInSpin;
QSpinBox *fadeOutSpin;
QSpinBox *waitInSpin;
QSpinBox *waitOutSpin;
QCheckBox *stopAtEndCheck;
QLineEdit *nameEdit;
QLineEdit *descriptionEdit;
QSpinBox *userNumberSpin;
QSpinBox *entryPointSpin;
QSpinBox *exitPointSpin;
QComboBox *statusCombo;
QSpinBox *audioLayerSpin;
QCheckBox *multiCheck;
QPushButton *browseButton;
QPushButton *saveButton;
QPushButton *cancelButton;
};
#endif // EDITCUETRACKWIDGET_H

View file

@ -36,6 +36,14 @@ FilterBankWidget::FilterBankWidget(QWidget *parent)
"font-size: 8px;}"); "font-size: 8px;}");
connect(m_bypass, SIGNAL(stateChanged(int)), this, SLOT(bypassChanged(int))); connect(m_bypass, SIGNAL(stateChanged(int)), this, SLOT(bypassChanged(int)));
master->addWidget(fb[0]); master->addWidget(fb[0]);
fb[0]->setFocusPolicy(Qt::NoFocus);
fb[0]->setButtonSymbols(QAbstractSpinBox::NoButtons);
fb[0]->setValue(-1);
fb[0]->setDecimals(1);
fb[0]->setAlignment(Qt::AlignHCenter);
fb[0]->setContentsMargins(0, 0, 0, 0);
fb[0]->setMaximumWidth(66);
fb[0]->setMinimumWidth(25);
layout->addLayout(master); layout->addLayout(master);
for (int i = 1; i < 13;) { for (int i = 1; i < 13;) {
QVBoxLayout *filterLayout= new QVBoxLayout; QVBoxLayout *filterLayout= new QVBoxLayout;
@ -47,6 +55,14 @@ FilterBankWidget::FilterBankWidget(QWidget *parent)
} else { } else {
fb[j]->setRange(-50, 50); fb[j]->setRange(-50, 50);
} }
fb[j]->setFocusPolicy(Qt::NoFocus);
fb[j]->setButtonSymbols(QAbstractSpinBox::NoButtons);
fb[j]->setValue(-1);
fb[j]->setDecimals(1);
fb[j]->setAlignment(Qt::AlignHCenter);
fb[j]->setContentsMargins(0, 0, 0, 0);
fb[j]->setMaximumWidth(66);
fb[j]->setMinimumWidth(25);
filterLayout->insertWidget(j, fb[j]); filterLayout->insertWidget(j, fb[j]);
} }
filterLayout->setSpacing(0); filterLayout->setSpacing(0);

View file

@ -1,57 +0,0 @@
/*
Libre Media Server Audio - An Open source Media Server for arts and performing.
(c) Criptomart - Santiago Noreña 2012-2024 <lms@criptomart.net>
https://git.criptomart.net/libremediaserver
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "libremediaserver-audio-gui.h"
libreMediaServerAudioUi::libreMediaServerAudioUi(QWidget *parent)
: QMainWindow(parent)
{
ui.setupUi(this);
this->setWindowTitle(VERSION);
m_aw = new AudioWidget(this);
setCentralWidget(m_aw);
m_dmxWidget = new dmxWidget(this);
QDockWidget *topWidget = new QDockWidget(tr("Master"), this);
topWidget->setAllowedAreas(Qt::TopDockWidgetArea);
topWidget->setWidget(m_dmxWidget);
topWidget->setContentsMargins(0, 0, 0, 0);
addDockWidget(Qt::TopDockWidgetArea, topWidget);
connect(ui.actionLaunch_OLA_Setup, SIGNAL(triggered()), this, SLOT(olasetup()));
this->setContentsMargins(0, 0, 0, 0);
this->setStyleSheet(
"margin: 0px;"
"color: white;"
"background-color: #3f3038;"
"selection-color: blue;"
"selection-background-color: green"
);
}
libreMediaServerAudioUi::~libreMediaServerAudioUi()
{
}
void libreMediaServerAudioUi::olasetup()
{
QWebView *view = new QWebView();
view->load(QUrl("http://localhost:9090/ola.html"));
view->show();
}

View file

@ -0,0 +1,106 @@
/*
Libre Media Server Audio - An Open source Media Server for arts and performing.
(c) Criptomart - Santiago Noreña 2012-2024 <lms@criptomart.net>
https://git.criptomart.net/libremediaserver
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "libremediaserver-audio-ui.h"
libreMediaServerAudioUi::libreMediaServerAudioUi(QWidget *parent)
: QMainWindow(parent)
{
ui.setupUi(this);
this->setWindowTitle(VERSION);
m_aw = new AudioWidget(this);
setCentralWidget(m_aw);
m_dmxWidget = new dmxWidget(this);
QDockWidget *topWidget = new QDockWidget(tr("Master"), this);
topWidget->setAllowedAreas(Qt::TopDockWidgetArea);
topWidget->setWidget(m_dmxWidget);
topWidget->setContentsMargins(0, 0, 0, 0);
addDockWidget(Qt::TopDockWidgetArea, topWidget);
connect(ui.actionLaunch_OLA_Setup, SIGNAL(triggered()), this, SLOT(olasetup()));
connect(ui.actionLaunchShowPlayer, SIGNAL(triggered()), this, SLOT(launchShowPlayerWindow()));
connect(ui.actionAudioSetup, SIGNAL(triggered()), this, SLOT(audioSetupWindow));
this->setContentsMargins(0, 0, 0, 0);
this->setStyleSheet(
"margin: 0px;"
"color: white;"
"background-color: #3f3038;"
"selection-color: blue;"
"selection-background-color: green"
);
m_showPlayer = new ShowPlayer();
m_showPlayer->setFocus();
QShortcut *shortcut_space = new QShortcut(QKeySequence(" "), this);
QObject::connect(shortcut_space, SIGNAL(activated()), m_showPlayer, SLOT(go()));
QShortcut *shortcut_up = new QShortcut(QKeySequence("Up"), this);
QObject::connect(shortcut_up, SIGNAL(activated()), m_showPlayer->ui->cueListWidget, SLOT(key_up()));
QShortcut *shortcut_down = new QShortcut(QKeySequence("Down"), this);
QObject::connect(shortcut_down, SIGNAL(activated()), m_showPlayer->ui->cueListWidget, SLOT(key_down()));
QShortcut *shortcut_copy = new QShortcut(QKeySequence("Ctrl+C"), this);
QObject::connect(shortcut_copy, SIGNAL(activated()), m_showPlayer->ui->cueListWidget, SLOT(copyCueTrack()));
QShortcut *shortcut_cut = new QShortcut(QKeySequence("Ctrl+X"), this);
QObject::connect(shortcut_cut, SIGNAL(activated()), m_showPlayer->ui->cueListWidget, SLOT(cutCueTrack()));
QShortcut *shortcut_paste = new QShortcut(QKeySequence("Ctrl+V"), this);
QObject::connect(shortcut_paste, SIGNAL(activated()), m_showPlayer->ui->cueListWidget, SLOT(pasteCueTrack()));
QShortcut *shortcut_delete = new QShortcut(QKeySequence("Delete"), this);
QObject::connect(shortcut_delete, SIGNAL(activated()), m_showPlayer->ui->cueListWidget, SLOT(deleteCueTrack()));
QShortcut *shortcut_insert = new QShortcut(QKeySequence("Insert"), this);
QObject::connect(shortcut_insert, SIGNAL(activated()), m_showPlayer->ui->cueListWidget, SLOT(createNewCueTrack()));
QShortcut *shortcut_edit = new QShortcut(QKeySequence("e"), this);
QObject::connect(shortcut_edit, SIGNAL(activated()), m_showPlayer->ui->cueListWidget, SLOT(editCueTrack()));
Settings::getInstance()->beginGroup("mainwindow");
auto geometry = Settings::getInstance()->value("geometry", QByteArray()).toByteArray();
if (!geometry.isEmpty())
restoreGeometry(geometry);
geometry = Settings::getInstance()->value("showPlayerGeometry", QByteArray()).toByteArray();
if (!geometry.isEmpty())
m_showPlayer->restoreGeometry(geometry);
Settings::getInstance()->endGroup();
}
libreMediaServerAudioUi::~libreMediaServerAudioUi()
{
Settings::getInstance()->beginGroup("mainwindow");
Settings::getInstance()->setValue("size", size());
Settings::getInstance()->setValue("fullScreen", isFullScreen());
Settings::getInstance()->setValue("geometry", saveGeometry());
Settings::getInstance()->setValue("showPlayer", m_showPlayer->isVisible());
Settings::getInstance()->setValue("showPlayerSize", m_showPlayer->size());
Settings::getInstance()->setValue("showPlayerGeometry", m_showPlayer->saveGeometry());
Settings::getInstance()->endGroup();
delete m_showPlayer;
}
void libreMediaServerAudioUi::olasetup()
{
QWebView *view = new QWebView();
view->load(QUrl("http://localhost:9090/ola.html"));
view->show();
}
void libreMediaServerAudioUi::launchShowPlayerWindow()
{
m_showPlayer->show();
}
void libreMediaServerAudioUi::audioSetupWindow()
{
}

View file

@ -26,7 +26,8 @@
#include "audiowidget.h" #include "audiowidget.h"
#include "dmxwidget.h" #include "dmxwidget.h"
#include "defines.h" #include "defines.h"
#include "ui_libremediaserver-audio-gui.h" #include "showplayer.h"
#include "ui_libremediaserver-audio-ui.h"
class libreMediaServerAudioUi : public QMainWindow class libreMediaServerAudioUi : public QMainWindow
{ {
@ -37,12 +38,15 @@ public:
virtual ~libreMediaServerAudioUi(); virtual ~libreMediaServerAudioUi();
AudioWidget *m_aw; AudioWidget *m_aw;
dmxWidget *m_dmxWidget; dmxWidget *m_dmxWidget;
ShowPlayer *m_showPlayer;
private: private:
Ui::LibreMediaServerAudio ui; Ui::LibreMediaServerAudioUi ui;
private slots: private slots:
void olasetup(); void olasetup();
void launchShowPlayerWindow();
void audioSetupWindow();
}; };
#endif // LIBREMEDIASERVERAUDIOUI_H #endif // LIBREMEDIASERVERAUDIOUI_H

View file

@ -1,21 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0"> <ui version="4.0">
<author>Santi Noreña lms@criptomart.net</author> <author>Santi Noreña lms@criptomart.net</author>
<class>LibreMediaServerAudio</class> <class>LibreMediaServerAudioUi</class>
<widget class="QMainWindow" name="LibreMediaServerAudio"> <widget class="QMainWindow" name="LibreMediaServerAudioUi">
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>400</width> <width>640</width>
<height>400</height> <height>800</height>
</rect> </rect>
</property> </property>
<property name="font"> <property name="font">
<font> <font>
<family>Unifont</family> <family>Unifont</family>
<pointsize>12</pointsize> <pointsize>12</pointsize>
<weight>75</weight>
<bold>true</bold> <bold>true</bold>
</font> </font>
</property> </property>
@ -23,8 +22,8 @@
<string>LibreMediaServer</string> <string>LibreMediaServer</string>
</property> </property>
<property name="windowIcon"> <property name="windowIcon">
<iconset> <iconset resource="../lms-resources.qrc">
<normaloff>../../../../criptomart/artwork/logo_v2_criptomart.net.png</normaloff>../../../../criptomart/artwork/logo_v2_criptomart.net.png</iconset> <normaloff>:/buttons/resources/icon.png</normaloff>:/buttons/resources/icon.png</iconset>
</property> </property>
<widget class="QWidget" name="centralwidget"/> <widget class="QWidget" name="centralwidget"/>
<widget class="QMenuBar" name="menuBar"> <widget class="QMenuBar" name="menuBar">
@ -32,15 +31,17 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>400</width> <width>640</width>
<height>21</height> <height>21</height>
</rect> </rect>
</property> </property>
<widget class="QMenu" name="menuFile"> <widget class="QMenu" name="menuFile">
<property name="title"> <property name="title">
<string>File</string> <string>Setup</string>
</property> </property>
<addaction name="actionLaunch_OLA_Setup"/> <addaction name="actionLaunch_OLA_Setup"/>
<addaction name="actionLaunchShowPlayer"/>
<addaction name="actionAudioSetup"/>
</widget> </widget>
<addaction name="menuFile"/> <addaction name="menuFile"/>
</widget> </widget>
@ -49,7 +50,19 @@
<string>OLA Setup</string> <string>OLA Setup</string>
</property> </property>
</action> </action>
<action name="actionLaunchShowPlayer">
<property name="text">
<string>Show Player</string>
</property>
</action>
<action name="actionAudioSetup">
<property name="text">
<string>Audio</string>
</property>
</action>
</widget> </widget>
<resources/> <resources>
<include location="../lms-resources.qrc"/>
</resources>
<connections/> <connections/>
</ui> </ui>

View file

@ -23,13 +23,10 @@
libreMediaServerAudio::libreMediaServerAudio() libreMediaServerAudio::libreMediaServerAudio()
{ {
m_settings = Settings::getInstance(); m_settings = Settings::getInstance(this);
m_settings->readFile(); //m_settings->readFile();
m_ui = m_settings->getShowUi(); m_ui = m_settings->getShowUi();
m_layersQty = m_settings->getLayersNumber(); m_layersQty = m_settings->getLayersNumber();
m_dmxSettings = m_settings->getDmxSettings();
m_mediaLibrary = new MediaLibrary;
m_mediaLibrary->initMediaLibrary();
for (uint i = 0; i < m_layersQty; i++) { for (uint i = 0; i < m_layersQty; i++) {
m_currentMedia[i] = ""; m_currentMedia[i] = "";
m_currentStatus[i] = Status::Iddle; m_currentStatus[i] = Status::Iddle;
@ -44,6 +41,10 @@ libreMediaServerAudio::libreMediaServerAudio()
cout << "Can not start Audio Engine!" << endl; cout << "Can not start Audio Engine!" << endl;
exit(-1); exit(-1);
} }
if (m_settings->getDmxActive()) {
m_dmxSettings = m_settings->getDmxSettings();
m_mediaLibrary = new MediaLibrary;
m_mediaLibrary->initMediaLibrary();
m_ola = new olaThread(this, m_layersQty); m_ola = new olaThread(this, m_layersQty);
Q_CHECK_PTR(m_ola); Q_CHECK_PTR(m_ola);
m_ola->blockSignals(true); m_ola->blockSignals(true);
@ -52,13 +53,21 @@ libreMediaServerAudio::libreMediaServerAudio()
m_ola->start(QThread::TimeCriticalPriority ); m_ola->start(QThread::TimeCriticalPriority );
#endif #endif
m_ola->blockSignals(false); m_ola->blockSignals(false);
} else {
qDebug() << "DMX inactive";
}
if (!m_settings->getShowPlayerActive())
qDebug() << "ShowPlayer inactive";
cout << "Core init Complete." << endl; cout << "Core init Complete." << endl;
} }
libreMediaServerAudio::~libreMediaServerAudio() libreMediaServerAudio::~libreMediaServerAudio()
{ {
if (m_settings->getDmxActive())
m_ola->stop(); m_ola->stop();
m_mae.stopEngine(); m_mae.stopEngine();
delete m_lmsUi;
delete m_settings;
sleep(1); sleep(1);
cout << "bye!" << endl; cout << "bye!" << endl;
exit(0); exit(0);
@ -139,8 +148,6 @@ void libreMediaServerAudio::dmxInput(int layer, int channel, int value)
#ifndef NOGUI #ifndef NOGUI
if (m_ui) { if (m_ui) {
m_lmsUi->m_aw->filterParamChanged(layer, channel, value); m_lmsUi->m_aw->filterParamChanged(layer, channel, value);
m_played.clear();
m_played.append(m_ola->getValue(layer, DMX_FILE));
} }
#endif #endif
} }
@ -172,40 +179,106 @@ void libreMediaServerAudio::refreshUi() {
} }
m_lmsUi->m_aw->levelChanged(i, m_mae.getLevel(i)); m_lmsUi->m_aw->levelChanged(i, m_mae.getLevel(i));
if (m_mae.getAtEnd(i)) { if (m_mae.getAtEnd(i)) {
if (m_currentStatus[i] == Status::PlayingOnce) { if (m_settings->getDmxActive())
endMediaDMX(i);
else
endMediaUi(i);
}
}
}
int libreMediaServerAudio::getFileIndexInFolder(const QString& currentFilePath) {
QDir directory = QFileInfo(currentFilePath).absoluteDir();
QStringList filters;
filters << "*.mp3" << "*.wav" << "*.flac";
QStringList fileList = directory.entryList(filters, QDir::Files, QDir::Name);
m_folderSize = fileList.size();
return fileList.indexOf(QFileInfo(currentFilePath).fileName());
}
QString libreMediaServerAudio::getNextFile(const QString& currentFilePath, bool loop, bool random) {
QDir directory = QFileInfo(currentFilePath).absoluteDir();
QStringList filters;
filters << "*.mp3" << "*.wav" << "*.flac";
QStringList fileList = directory.entryList(filters, QDir::Files, QDir::Name);
m_folderSize = fileList.size();
int currentIndex = fileList.indexOf(QFileInfo(currentFilePath).fileName());
if (currentIndex >= 0 && currentIndex < m_folderSize - 1 && !random) {
return directory.absoluteFilePath(fileList.at(currentIndex + 1));
} else if (loop && fileList.size() > 0 && !random) {
return directory.absoluteFilePath(fileList.at(0));
} else if (random && m_folderSize > 0) {
if (m_played.size() >= m_folderSize )
m_played.clear();
int r = rand() % fileList.size();
while (m_played.contains(r)) {
r = rand() % fileList.size();
}
return directory.absoluteFilePath(fileList.at(r));
}
return QString();
}
void libreMediaServerAudio::endMediaUi(int i)
{
QString nextFile;
if (m_currentStatus[i] == Status::PlayingOnce || m_currentStatus[i] == Status::Stopped) {
m_currentStatus[i] = Status::Stopped; m_currentStatus[i] = Status::Stopped;
m_lmsUi->m_aw->playbackChanged(i, Status::Stopped); m_lmsUi->m_aw->playbackChanged(i, Status::Stopped);
m_lmsUi->m_showPlayer->cueTrackAtEnd(i);
} else if (m_currentStatus[i] == Status::PlayingFolder) {
nextFile = getNextFile(m_currentMedia[i], false, false);
} else if (m_currentStatus[i] == Status::PlayingFolderLoop) {
nextFile = getNextFile(m_currentMedia[i], true, false);
} else if (m_currentStatus[i] == Status::PlayingFolderRandom) {
nextFile = getNextFile(m_currentMedia[i], false, true);
} }
if (m_currentStatus[i] == Status::PlayingFolder) { if (!nextFile.isEmpty()) {
uint last = m_played.last(); this->uiLoadMedia(i, nextFile);
m_mae.playbackChanged(i, m_currentStatus[i]);
} else {
m_currentStatus[i] = Status::Stopped;
m_lmsUi->m_aw->playbackChanged(i, Status::Stopped);
m_lmsUi->m_showPlayer->cueTrackAtEnd(i);
m_played.clear();
}
}
void libreMediaServerAudio::endMediaDMX(int i)
{
if (m_currentStatus[i] == Status::PlayingOnce || m_currentStatus[i] == Status::Stopped) {
m_currentStatus[i] = Status::Stopped;
m_lmsUi->m_aw->playbackChanged(i, Status::Stopped);
m_lmsUi->m_showPlayer->cueTrackAtEnd(i);
} else if (m_currentStatus[i] == Status::PlayingFolder) {
uint last = 0;
if (!m_played.isEmpty())
last = m_played.last() + 1;
int folder = m_ola->getValue(i, DMX_FOLDER); int folder = m_ola->getValue(i, DMX_FOLDER);
last++; QString nextFile = getNextFile(m_currentMedia[i], false, false);
if (last < m_mediaLibrary->getMediaFolderCount(folder)) { if (!nextFile.isEmpty()) {
this->loadMedia(i, folder, last); this->loadMedia(i, folder, last);
m_mae.playbackChanged(i, Status::PlayingFolder); m_mae.playbackChanged(i, Status::PlayingFolder);
} } else {
else {
m_currentStatus[i] = Status::Stopped; m_currentStatus[i] = Status::Stopped;
m_lmsUi->m_aw->playbackChanged(i, Status::Stopped); m_lmsUi->m_aw->playbackChanged(i, Status::Stopped);
m_lmsUi->m_showPlayer->cueTrackAtEnd(i);
} }
} } else if (m_currentStatus[i] == Status::PlayingFolderLoop) {
else if (m_currentStatus[i] == Status::PlayingFolderLoop) {
uint last = m_played.last(); uint last = m_played.last();
int folder = m_ola->getValue(i, DMX_FOLDER); int folder = m_ola->getValue(i, DMX_FOLDER);
last++; last++;
if (last >= m_mediaLibrary->getMediaFolderCount(folder)) { QString nextFile = getNextFile(m_currentMedia[i], true, false);
if (!nextFile.isEmpty()) {
this->loadMedia(i, folder, last);
m_mae.playbackChanged(i, Status::PlayingFolder);
} else {
this->loadMedia(i, folder, 0); this->loadMedia(i, folder, 0);
m_mae.playbackChanged(i, Status::PlayingFolderLoop); m_mae.playbackChanged(i, Status::PlayingFolderLoop);
} else {
this->loadMedia(i, folder, last);
m_mae.playbackChanged(i, Status::PlayingFolder);
} }
} } else if (m_currentStatus[i] == Status::PlayingFolderRandom) {
else if (m_currentStatus[i] == Status::PlayingFolderRandom) {
int last = -1; int last = -1;
int folder = m_ola->getValue(i, DMX_FOLDER); int folder = m_ola->getValue(i, DMX_FOLDER);
if (uint(abs(m_played.size())) >= m_mediaLibrary->getMediaFolderCount(folder))
m_played.clear();
while (last == -1) { while (last == -1) {
last = rand() % m_mediaLibrary->getMediaFolderCount(folder); last = rand() % m_mediaLibrary->getMediaFolderCount(folder);
if (m_played.contains(last)) if (m_played.contains(last))
@ -214,49 +287,65 @@ void libreMediaServerAudio::refreshUi() {
this->loadMedia(i, folder, last); this->loadMedia(i, folder, last);
m_mae.playbackChanged(i, Status::PlayingFolderRandom); m_mae.playbackChanged(i, Status::PlayingFolderRandom);
} }
}
}
} }
void libreMediaServerAudio::setUi(libreMediaServerAudioUi *lmsUi) void libreMediaServerAudio::setUi(libreMediaServerAudioUi *lmsUi)
{ {
m_lmsUi = lmsUi; m_lmsUi = lmsUi;
m_ui = true; m_ui = true;
connect(m_ola, SIGNAL(universeReceived(int)), m_lmsUi->m_dmxWidget, SLOT(updateWatchDMX(int)));
connect(m_lmsUi->m_aw, SIGNAL(uiSliderChanged(int, Slider, int)), this, SLOT(uiSliderChanged(int, Slider, int))); connect(m_lmsUi->m_aw, SIGNAL(uiSliderChanged(int, Slider, int)), this, SLOT(uiSliderChanged(int, Slider, int)));
connect(m_lmsUi->m_aw, SIGNAL(uiPlaybackChanged(int, Status)), this, SLOT(uiPlaybackChanged(int, Status))); connect(m_lmsUi->m_aw, SIGNAL(uiPlaybackChanged(int, Status)), this, SLOT(uiPlaybackChanged(int, Status)));
connect(m_lmsUi->m_aw, SIGNAL(uiLoadMedia(int, QString)), this, SLOT(uiLoadMedia(int, QString))); connect(m_lmsUi->m_aw, SIGNAL(uiLoadMedia(int, QString)), this, SLOT(uiLoadMedia(int, QString)));
m_refreshUi = new QTimer(this); connect(m_lmsUi->m_showPlayer, SIGNAL(uiSliderChanged(int, Slider, int)), this, SLOT(uiSliderChanged(int, Slider, int)));
connect(m_refreshUi, SIGNAL(timeout()), this, SLOT(refreshUi())); connect(m_lmsUi->m_showPlayer, SIGNAL(uiSliderChangedFaded(int, Slider, int, int, int)), this, SLOT(uiSliderChangedFaded(int, Slider, int, int, int)));
m_refreshUi->start(UI_REFRESH_TIME); connect(m_lmsUi->m_showPlayer, SIGNAL(uiPlaybackChanged(int, Status)), this, SLOT(uiPlaybackChanged(int, Status)));
connect(m_lmsUi->m_showPlayer, SIGNAL(uiLoadMedia(int, QString)), this, SLOT(uiLoadMedia(int, QString)));
if (m_settings->getDmxActive()) {
connect(m_ola, SIGNAL(universeReceived(int)), m_lmsUi->m_dmxWidget, SLOT(updateWatchDMX(int)));
m_ola->start(QThread::TimeCriticalPriority ); m_ola->start(QThread::TimeCriticalPriority );
} else {
m_lmsUi->m_dmxWidget->hide();
}
for (uint i = 0; i < m_settings->getAudioDeviceQty(); i++) { for (uint i = 0; i < m_settings->getAudioDeviceQty(); i++) {
char *name = m_mae.getDeviceName(i); char *name = m_mae.getDeviceName(i);
m_lmsUi->m_aw->busNameChanged(i, name); m_lmsUi->m_aw->busNameChanged(i, name);
} }
if (m_settings->getShowPlayerActive()) {
m_lmsUi->m_showPlayer->loadCueTrackList("lastshow.xml");
m_lmsUi->m_showPlayer->show();
}
m_refreshUi = new QTimer(this);
connect(m_refreshUi, SIGNAL(timeout()), this, SLOT(refreshUi()));
m_refreshUi->start(UI_REFRESH_TIME);
}; };
// From Ui widgets // From Ui widgets and ShowPlayer
void libreMediaServerAudio::uiSliderChanged(int layer, Slider s, int value) void libreMediaServerAudio::uiSliderChanged(int layer, Slider s, int value)
{ {
switch (s){ switch (s){
case Slider::Volume: case Slider::Volume:
m_mae.volChanged(layer, value); m_mae.volChanged(layer, value);
m_updateUi[layer][0] = value;
break; break;
case Slider::Pan: case Slider::Pan:
m_mae.panChanged(layer, value); m_mae.panChanged(layer, value);
m_updateUi[layer][1] = value;
break; break;
case Slider::Pitch: case Slider::Pitch:
m_mae.pitchChanged(layer, value); m_mae.pitchChanged(layer, value);
m_updateUi[layer][2] = value;
break; break;
case Slider::Bypass: case Slider::Bypass:
// ToDo: sigsev when no dmx, resolve this whitout dmxSettings, is it needed=
m_mae.setBypass(m_dmxSettings.at(layer).audioDevice, layer, value); m_mae.setBypass(m_dmxSettings.at(layer).audioDevice, layer, value);
break; break;
case Slider::Bus1: case Slider::Bus1:
m_mae.filterParamChanged(layer, SEND1, value / 255.0f); m_mae.filterParamChanged(layer, SEND1, value / 255.0f);
m_lmsUi->m_aw->filterParamChanged(layer, SEND1, value / 255.0f);
break; break;
case Slider::Bus2: case Slider::Bus2:
m_mae.filterParamChanged(layer, SEND2, value / 255.0f); m_mae.filterParamChanged(layer, SEND2, value / 255.0f);
m_lmsUi->m_aw->filterParamChanged(layer, SEND2, value / 255.0f);
break; break;
} }
} }
@ -264,27 +353,59 @@ void libreMediaServerAudio::uiSliderChanged(int layer, Slider s, int value)
void libreMediaServerAudio::uiPlaybackChanged(int layer, Status s) void libreMediaServerAudio::uiPlaybackChanged(int layer, Status s)
{ {
ma_result result; ma_result result;
QString tmp;
result = m_mae.playbackChanged(layer, s); result = m_mae.playbackChanged(layer, s);
if (result == MA_SUCCESS) { if (result == MA_SUCCESS) {
m_currentStatus[layer] = s; m_currentStatus[layer] = s;
m_lmsUi->m_aw->playbackChanged(layer, s);
} else { } else {
qWarning() << "ui playback change error " << result << " status " << statusToString(s) << "layer" << layer; qWarning() << "ui playback change error " << result << " status " << statusToString(s) << "layer" << layer;
} }
switch (s) {
case Status::PlayingOnce:
case Status::PlayingLoop:
case Status::Iddle:
case Status::Stopped:
m_played.clear();
break;
case Status::Paused:
case Status::PlayingFolder:
case Status::PlayingFolderLoop:
break;
case Status::PlayingFolderRandom:
tmp = getNextFile(m_currentMedia[layer], false, true);
uiLoadMedia(layer, tmp);
break;
}
} }
void libreMediaServerAudio::uiLoadMedia(int layer, QString mediaFile) void libreMediaServerAudio::uiLoadMedia(int layer, QString mediaFile)
{ {
ma_result result; ma_result result;
if (strcmp(mediaFile.toLatin1().constData(), m_currentMedia[layer].toLatin1().constData()) == 0) if (mediaFile.isEmpty())
return ;
if (mediaFile.compare(m_currentMedia[layer]) == 0)
return; return;
result = m_mae.loadMedia(layer, mediaFile.toLatin1().data()); result = m_mae.loadMedia(layer, mediaFile.toLatin1().data());
if (result == MA_SUCCESS) { if (result == MA_SUCCESS) {
m_currentMedia[layer] = mediaFile; m_currentMedia[layer] = mediaFile;
m_lmsUi->m_aw->mediaLoaded(layer, mediaFile, m_mae.getDuration(layer)); m_lmsUi->m_aw->mediaLoaded(layer, mediaFile, m_mae.getDuration(layer));
m_played.append(getFileIndexInFolder(mediaFile));
} else { } else {
qWarning() << "ui load media error" << result << "file" << mediaFile << "layer" << layer; qWarning() << "ui load media error" << result << "file" << mediaFile << "layer" << layer;
} }
} }
void libreMediaServerAudio::uiSliderChangedFaded(int layer, Slider s, int value, int fadeIn, int fadeOut)
{
switch (s){
case Slider::Volume:
m_mae.volChanged(layer, value, fadeIn, fadeOut);
m_updateUi[layer][0] = value;
break;
}
}
#endif #endif

View file

@ -30,7 +30,7 @@ using namespace std;
#include "settings.h" #include "settings.h"
#include "defines.h" #include "defines.h"
#ifndef NOGUI #ifndef NOGUI
#include "libremediaserver-audio-gui.h" #include "libremediaserver-audio-ui.h"
#endif #endif
class libreMediaServerAudio : public QObject class libreMediaServerAudio : public QObject
@ -62,12 +62,18 @@ private:
QTimer *m_refreshUi; QTimer *m_refreshUi;
libreMediaServerAudioUi *m_lmsUi; libreMediaServerAudioUi *m_lmsUi;
float m_updateUi[MAX_LAYERS][4]; float m_updateUi[MAX_LAYERS][4];
int m_folderSize;
private slots: private slots:
void refreshUi(); void refreshUi();
void uiSliderChanged(int layer, Slider s, int value); void uiSliderChanged(int layer, Slider s, int value);
void uiSliderChangedFaded(int layer, Slider s, int value, int fadeIn, int fadeOut);
void uiPlaybackChanged(int layer, Status s); void uiPlaybackChanged(int layer, Status s);
void uiLoadMedia(int layer, QString s); void uiLoadMedia(int layer, QString s);
void endMediaDMX(int layer);
void endMediaUi(int layer);
QString getNextFile(const QString& currentFilePath, bool loop, bool random);
int getFileIndexInFolder(const QString& currentFilePath);
#endif #endif
}; };

View file

@ -33,6 +33,13 @@ int main(int argc, char *argv[])
{ {
QApplication app(argc, argv); QApplication app(argc, argv);
libreMediaServerAudio lms; libreMediaServerAudio lms;
app.setApplicationName("LibreMediaServerAudio");
app.setOrganizationName("Criptomart");
app.setOrganizationDomain("criptomart.net");
QCoreApplication::setOrganizationName("Criptomart");
QCoreApplication::setOrganizationDomain("criptomart.net");
QCoreApplication::setApplicationName("LibreMediaServerAudio");
app.setApplicationVersion(VERSION);
#ifndef NOGUI #ifndef NOGUI
if (hasUi(argc, argv) || lms.getShowUi()) if (hasUi(argc, argv) || lms.getShowUi())
{ {

View file

@ -8,7 +8,7 @@
#include "olathread.h" #include "olathread.h"
#include "settings.h" #include "settings.h"
#include "libremediaserver-audio.h" #include "libremediaserver-audio.h"
#include "libremediaserver-audio-gui.h" #include "libremediaserver-audio-ui.h"
#include "defines.h" #include "defines.h"
olaThread *m_ola; olaThread *m_ola;

81196
src/miniaudio.c Normal file

File diff suppressed because it is too large Load diff

7766
src/miniaudio.h Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,3 +1,4 @@
#include <QTimer>
#include "miniaudioengine.h" #include "miniaudioengine.h"
#include "dmxPersonality.h" #include "dmxPersonality.h"
@ -58,7 +59,9 @@ bool MiniAudioEngine::startEngine(uint layers, uint* audioDevicesId, uint audioD
m_mae.currentStatus[i].pitch = 128; m_mae.currentStatus[i].pitch = 128;
m_mae.currentStatus[i].vol = 0.0f; m_mae.currentStatus[i].vol = 0.0f;
m_mae.currentStatus[i].cursor = 0; m_mae.currentStatus[i].cursor = 0;
m_mae.currentStatus[i].updated = false; m_mae.currentStatus[i].updated = MA_FALSE;
m_mae.currentStatus[i].fadeIn = FADE_TIME;
m_mae.currentStatus[i].fadeOut = FADE_TIME;
} }
result = this->startContext(); result = this->startContext();
if (result != MA_SUCCESS) return false; if (result != MA_SUCCESS) return false;
@ -283,7 +286,7 @@ ma_result MiniAudioEngine::startDevices()
engineConfig.noAutoStart = MA_TRUE; engineConfig.noAutoStart = MA_TRUE;
for (uint internalId = 0; internalId < m_mae.audioDevicesQty; internalId++) { for (uint internalId = 0; internalId < m_mae.audioDevicesQty; internalId++) {
deviceConfig.capture.pDeviceID = &m_mae.pPlaybackDeviceInfos[m_mae.audioDevicesId[internalId]].id; //deviceConfig.capture.pDeviceID = &m_mae.pPlaybackDeviceInfos[m_mae.audioDevicesId[internalId]].id;
deviceConfig.playback.pDeviceID = &m_mae.pPlaybackDeviceInfos[m_mae.audioDevicesId[internalId]].id; deviceConfig.playback.pDeviceID = &m_mae.pPlaybackDeviceInfos[m_mae.audioDevicesId[internalId]].id;
deviceConfig.pUserData = &m_mae.engines[internalId]; deviceConfig.pUserData = &m_mae.engines[internalId];
result = ma_device_init(&m_mae.context, &deviceConfig, &m_mae.devices[internalId]); result = ma_device_init(&m_mae.context, &deviceConfig, &m_mae.devices[internalId]);
@ -351,6 +354,8 @@ ma_result MiniAudioEngine::loadMedia(int layer, char *file)
{ {
ma_result result; ma_result result;
if (!file || !*file)
return MA_INVALID_ARGS;
if (m_mae.mediaLoaded[layer] == MA_TRUE) if (m_mae.mediaLoaded[layer] == MA_TRUE)
{ {
m_mae.mediaLoaded[layer] = MA_FALSE; m_mae.mediaLoaded[layer] = MA_FALSE;
@ -372,7 +377,7 @@ ma_result MiniAudioEngine::loadMedia(int layer, char *file)
return result; return result;
} }
m_mae.currentStatus[layer].media = file; m_mae.currentStatus[layer].media = file;
m_mae.currentStatus[layer].updated = true; m_mae.currentStatus[layer].updated = MA_TRUE;
m_mae.mediaLoaded[layer] = MA_TRUE; m_mae.mediaLoaded[layer] = MA_TRUE;
this->refreshValues(layer); this->refreshValues(layer);
return result; return result;
@ -432,8 +437,18 @@ ma_result MiniAudioEngine::printFormatInfo(int layer)
// Expects between 0 and 65535 vol value // Expects between 0 and 65535 vol value
void MiniAudioEngine::volChanged(int layer, int vol) void MiniAudioEngine::volChanged(int layer, int vol)
{
volChanged(layer, vol, FADE_TIME, FADE_TIME);
}
// Expects between 0 and 65535 vol value and fade time in milliseconds
void MiniAudioEngine::volChanged(int layer, int vol, int fadeIn, int fadeOut)
{ {
m_mae.currentStatus[layer].vol = vol; m_mae.currentStatus[layer].vol = vol;
m_mae.currentStatus[layer].fadeIn = fadeIn;
m_mae.currentStatus[layer].fadeOut = fadeOut;
if (m_mae.mediaLoaded[layer] != MA_TRUE)
return;
if (m_mae.mediaLoaded[layer] == MA_FALSE && m_mae.currentStatus[layer].updated) if (m_mae.mediaLoaded[layer] == MA_FALSE && m_mae.currentStatus[layer].updated)
return; return;
float db = ((float)vol / 771.0f) - 85.0f; float db = ((float)vol / 771.0f) - 85.0f;
@ -441,7 +456,7 @@ void MiniAudioEngine::volChanged(int layer, int vol)
db = 0; db = 0;
} else } else
db = ma_volume_db_to_linear(db); db = ma_volume_db_to_linear(db);
ma_sound_set_fade_in_milliseconds(&m_mae.sounds[layer], -1, db, FADE_TIME); ma_sound_set_fade_in_milliseconds(&m_mae.sounds[layer], -1, db, fadeIn);
} }
void MiniAudioEngine::panChanged(int layer, float value) void MiniAudioEngine::panChanged(int layer, float value)
@ -449,7 +464,7 @@ void MiniAudioEngine::panChanged(int layer, float value)
float result; float result;
m_mae.currentStatus[layer].pan = value; m_mae.currentStatus[layer].pan = value;
if (m_mae.mediaLoaded[layer] == false) if (m_mae.mediaLoaded[layer] != MA_TRUE)
return; return;
result = (value / 128.0) - 1.0; result = (value / 128.0) - 1.0;
ma_sound_group_set_pan(&m_mae.sounds[layer], result); ma_sound_group_set_pan(&m_mae.sounds[layer], result);
@ -460,7 +475,7 @@ void MiniAudioEngine::pitchChanged(int layer, float value)
float pitch; float pitch;
m_mae.currentStatus[layer].pitch = value; m_mae.currentStatus[layer].pitch = value;
if (m_mae.mediaLoaded[layer] == false) if (m_mae.mediaLoaded[layer] != MA_TRUE)
return; return;
pitch = value / 128.0; pitch = value / 128.0;
ma_sound_group_set_pitch(&m_mae.sounds[layer], pitch); ma_sound_group_set_pitch(&m_mae.sounds[layer], pitch);
@ -471,18 +486,23 @@ ma_result MiniAudioEngine::playbackChanged(int layer, Status status)
ma_result result = MA_SUCCESS; ma_result result = MA_SUCCESS;
float db = 0; float db = 0;
bool loop = false; bool loop = false;
bool playAtBegin = false;
if (status == Status::Stopped)
playAtBegin = true;
m_mae.currentStatus[layer].status = status; m_mae.currentStatus[layer].status = status;
if (m_mae.mediaLoaded[layer] == MA_FALSE) if (m_mae.mediaLoaded[layer] != MA_TRUE)
return MA_DOES_NOT_EXIST; return MA_DOES_NOT_EXIST;
m_mae.currentStatus[layer].updated = false; m_mae.currentStatus[layer].updated = false;
switch (status) { switch (status) {
case Status::Paused: case Status::Paused:
result = ma_sound_stop_with_fade_in_milliseconds(&m_mae.sounds[layer], FADE_TIME); result = ma_sound_stop_with_fade_in_milliseconds(&m_mae.sounds[layer], m_mae.currentStatus[layer].fadeOut);
break; break;
case Status::Stopped: case Status::Stopped:
ma_sound_stop_with_fade_in_milliseconds(&m_mae.sounds[layer], 0.0f); ma_sound_stop_with_fade_in_milliseconds(&m_mae.sounds[layer], m_mae.currentStatus[layer].fadeOut);
result = this->seekToCursor(layer, m_mae.currentStatus[layer].cursor); QTimer::singleShot(m_mae.currentStatus[layer].fadeOut, [this, layer] {
this->seekToCursor(layer, m_mae.currentStatus[layer].cursor);
});
break; break;
case Status::PlayingLoop: case Status::PlayingLoop:
loop = true; loop = true;
@ -493,6 +513,8 @@ ma_result MiniAudioEngine::playbackChanged(int layer, Status status)
case Status::PlayingFolder: case Status::PlayingFolder:
case Status::PlayingFolderLoop: case Status::PlayingFolderLoop:
case Status::PlayingFolderRandom: case Status::PlayingFolderRandom:
if (playAtBegin)
this->seekToCursor(layer, 0);
ma_sound_set_looping(&m_mae.sounds[layer], loop); ma_sound_set_looping(&m_mae.sounds[layer], loop);
if (ma_sound_is_playing(&m_mae.sounds[layer])) break; if (ma_sound_is_playing(&m_mae.sounds[layer])) break;
ma_sound_set_stop_time_in_milliseconds(&m_mae.sounds[layer], ~(ma_uint64)0); ma_sound_set_stop_time_in_milliseconds(&m_mae.sounds[layer], ~(ma_uint64)0);
@ -502,8 +524,8 @@ ma_result MiniAudioEngine::playbackChanged(int layer, Status status)
result = ma_sound_start(&m_mae.sounds[layer]); result = ma_sound_start(&m_mae.sounds[layer]);
ma_sound_set_fade_in_milliseconds(&m_mae.sounds[layer], 0.000001f, 0.000000f, FADE_TIME); ma_sound_set_fade_in_milliseconds(&m_mae.sounds[layer], 0.000001f, 0.000000f, FADE_TIME);
if (m_mae.currentStatus[layer].cursor > 0) if (m_mae.currentStatus[layer].cursor > 0)
usleep(FADE_TIME * 1500); // avoid glitch when load when seeking usleep(FADE_TIME * 1500); // avoid glitch when seeking
ma_sound_set_fade_in_milliseconds(&m_mae.sounds[layer], 0, db, FADE_TIME * 2); ma_sound_set_fade_in_milliseconds(&m_mae.sounds[layer], 0, db, m_mae.currentStatus[layer].fadeIn);
default: default:
break; break;
} }

View file

@ -57,17 +57,14 @@ typedef struct
class MiniAudioEngine class MiniAudioEngine
{ {
friend class libreMediaServerAudio;
public: public:
static void audioDataCallback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount); static void audioDataCallback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount);
protected:
MiniAudioEngine(); MiniAudioEngine();
void stopEngine(); void stopEngine();
bool startEngine(uint layersQty, uint* audioDevicesID, uint audioDevicesQty); bool startEngine(uint layersQty, uint* audioDevicesID, uint audioDevicesQty);
ma_result loadMedia(int layer, char *media); ma_result loadMedia(int layer, char *media);
void volChanged(int layer, int vol); void volChanged(int layer, int vol);
void volChanged(int layer, int vol, int fadeIn, int fadeOut);
void panChanged(int layer, float pan); void panChanged(int layer, float pan);
void pitchChanged(int layer, float pitch); void pitchChanged(int layer, float pitch);
ma_result playbackChanged(int layer, Status status); ma_result playbackChanged(int layer, Status status);

View file

@ -2,38 +2,131 @@
Settings *Settings::_instance = 0; Settings *Settings::_instance = 0;
Settings *Settings::getInstance() { Settings *Settings::getInstance(QObject *parent) {
if (_instance == 0) { if (_instance == 0) {
_instance = new Settings(); _instance = new Settings(parent);
Q_CHECK_PTR(_instance); Q_CHECK_PTR(_instance);
} }
return _instance; return _instance;
} }
Settings::Settings(QObject *parent) : Settings::Settings(QObject *parent) :
QObject(parent) QSettings("Criptomart", "LibreMediaServer", parent)
{ {
m_layersNumber = 0; m_layersNumber = 0;
m_ui = false; m_ui = false;
m_audioDeviceQty = 0;
for (uint i = 0; i < MAX_AUDIODEVICES; i++)
m_audioDeviceId[i] = -1;
if (QFile::exists(DEFAULT_FILE))
readFromFile(DEFAULT_FILE);
else if (QFile::exists(fileName()))
settingsLoader();
else {
qDebug() << "Can not read initial settings, making some standard";
setValue("lmsAudio/ui", 1);
setValue("lmsAudio/dmxActive", 0);
setValue("lmsAudio/showPlayerActive", 1);
setValue("lmsAudio/layersNumber", 2);
setValue("lmsAudio/path", "../media/");
setValue("audioDevice/devicesNumber", 2);
setValue("audioDevice/id0", 4);
setValue("audioDevice/id1", 3);
setValue("layers/layer0/dmx", 1);
setValue("layers/layer0/universe", 1);
setValue("layers/layer0/id", 0);
setValue("layers/layer0/audioDevice", 0);
setValue("layers/layer1/dmx", 26);
setValue("layers/layer1/universe", 1);
setValue("layers/layer1/id", 1);
setValue("layers/layer1/audioDevice", 0);
sync();
settingsLoader();
}
} }
// Read the dmx settings for dmx.xml At the moment we need: Settings::~Settings() {
settingsSaver();
}
void Settings::settingsLoader()
{
qDebug() << "loading settings from system";
beginGroup("lmsAudio");
m_ui = value("ui", 0).toBool();
m_dmxActive = value("dmxActive", 0).toBool();
m_showPlayerActive = value("showPlayerActive", 0).toBool();
m_layersNumber = value("layersNumber", 0).toInt();
m_pathmedia = value("path", "").toString();
endGroup();
beginGroup("audioDevice");
m_audioDeviceQty = value("devicesNumber", 0).toInt();
for (uint i = 0; i < m_audioDeviceQty; i++)
m_audioDeviceId[i] = value(QString("id%1").arg(i), -1).toInt();
endGroup();
beginGroup("layers");
for (int i = 0; i < m_layersNumber; i++) {
beginGroup(QString("layer%1").arg(i));
dmxSetting temp;
temp.address = value("dmx", 0).toInt();
temp.universe = value("universe", 0).toInt();
temp.layer = value("id", 0).toInt();
temp.audioDevice = value("audioDevice", 0).toInt();
m_settings.append(temp);
if (!m_universe.contains(temp.universe)) {
m_universe.insert(temp.universe);
}
endGroup();
}
endGroup();
printSettings();
}
void Settings::settingsSaver()
{
beginGroup("lmsAudio");
setValue("ui", m_ui);
setValue("dmxActive", m_dmxActive);
setValue("showPlayerActive", m_showPlayerActive);
setValue("layersNumber", m_layersNumber);
setValue("path", m_pathmedia);
endGroup();
beginGroup("audioDevice");
setValue("devicesNumber", m_audioDeviceQty);
for (uint i = 0; i < m_audioDeviceQty; i++)
setValue(QString("id%1").arg(i), m_audioDeviceId[i]);
endGroup();
beginGroup("layers");
for (int i = 0; i < m_layersNumber; i++) {
beginGroup(QString("layer%1").arg(i));
setValue("dmx", m_settings[i].address);
setValue("universe", m_settings[i].universe);
setValue("id", m_settings[i].layer);
setValue("audioDevice", m_settings[i].audioDevice);
endGroup();
}
endGroup();
sync();
qDebug() << "writed settings to " << fileName();
}
// Read the dmx settings. At the moment we need:
// - The path to the medias folder tree // - The path to the medias folder tree
// - The number of sources/layers controlled by DMX // - The number of sources/layers controlled by DMX
// - The first DMX channel of each source/layer // - The first DMX channel of each source/layer
// - The universe to bind in OLA // - dmxActive and The universe to bind in OLA
// - Audio device id // - Audio device id
// - Show the Ui or not // - Show Ui, showPlayer
void Settings::readFromFile(QString file) { void Settings::readFromFile(QString file) {
qDebug() << "loading settings from xml file " << file;
QFile* xmlFile = new QFile(file); QFile* xmlFile = new QFile(file);
if (!xmlFile->open(QIODevice::ReadOnly | QIODevice::Text)) { if (!xmlFile->open(QIODevice::ReadOnly | QIODevice::Text)) {
QMessageBox::critical(NULL,"Load XML File Problem", QMessageBox::critical(NULL,"Load XML File Problem",
QString("Couldn't open %1 to load settings").arg(file), QString("Couldn't open %1 to load settings").arg(file),
QMessageBox::Ok); QMessageBox::Ok);
// Instead exit give the oportunity to load another file or define the settings
qCritical("Load XML File Problem"); qCritical("Load XML File Problem");
exit(1); return;
} }
QXmlStreamReader* xmlReader = new QXmlStreamReader(xmlFile); QXmlStreamReader* xmlReader = new QXmlStreamReader(xmlFile);
while(!xmlReader->atEnd() && !xmlReader->hasError()) { while(!xmlReader->atEnd() && !xmlReader->hasError()) {
@ -44,6 +137,8 @@ void Settings::readFromFile(QString file) {
if(token == QXmlStreamReader::StartElement) { if(token == QXmlStreamReader::StartElement) {
if(xmlReader->name() == "lmsAudio") { if(xmlReader->name() == "lmsAudio") {
m_ui = xmlReader->attributes().value("ui").toLocal8Bit().toInt(); m_ui = xmlReader->attributes().value("ui").toLocal8Bit().toInt();
m_dmxActive = xmlReader->attributes().value("dmxActive").toInt();
m_showPlayerActive = xmlReader->attributes().value("showPlayerActive").toInt();
m_layersNumber = xmlReader->attributes().value("layersNumber").toLocal8Bit().toInt(); m_layersNumber = xmlReader->attributes().value("layersNumber").toLocal8Bit().toInt();
m_pathmedia = xmlReader->attributes().value("path").toLocal8Bit(); m_pathmedia = xmlReader->attributes().value("path").toLocal8Bit();
continue; continue;
@ -80,6 +175,7 @@ void Settings::readFromFile(QString file) {
void Settings::printSettings() { void Settings::printSettings() {
qInfo() << "Settings readed:\nShow Ui:" << m_ui << "Layers:" << m_layersNumber << "Path:" << m_pathmedia <<"Audio Device qty:" << m_audioDeviceQty; qInfo() << "Settings readed:\nShow Ui:" << m_ui << "Layers:" << m_layersNumber << "Path:" << m_pathmedia <<"Audio Device qty:" << m_audioDeviceQty;
qInfo() << "dmx: " << m_dmxActive << " showPlayer: " << m_showPlayerActive;
for (uint i = 0; i < m_audioDeviceQty; i++) for (uint i = 0; i < m_audioDeviceQty; i++)
qInfo() << "Audio device internal id:" << i << "system id:" << m_audioDeviceId[i]; qInfo() << "Audio device internal id:" << i << "system id:" << m_audioDeviceId[i];
for (int i = 0; i < m_layersNumber; i++) for (int i = 0; i < m_layersNumber; i++)

View file

@ -6,28 +6,35 @@
#include <QMessageBox> #include <QMessageBox>
#include <QSet> #include <QSet>
#include <QDebug> #include <QDebug>
#include <QSettings>
#include "medialibrary.h" #include "medialibrary.h"
#include "audiowidget.h" #include "audiowidget.h"
#include "defines.h" #include "defines.h"
class Settings : public QObject class Settings : public QSettings
{ {
Q_OBJECT Q_OBJECT
public: public:
Settings(QObject *parent = 0); Settings(QObject *parent = nullptr);
static Settings *getInstance(); ~Settings();
static Settings *getInstance(QObject *parent = nullptr);
inline QSet<int> getUniverses() { return m_universe; } inline QSet<int> getUniverses() { return m_universe; }
inline QString getPathMedia() { return m_pathmedia; } inline QString getPathMedia() { return m_pathmedia; }
inline void setPathMedia(QString new_path) { m_pathmedia = new_path; };
inline QList<dmxSetting> getDmxSettings() { return m_settings; } inline QList<dmxSetting> getDmxSettings() { return m_settings; }
inline int getLayersNumber() { return m_layersNumber; } inline int getLayersNumber() { return m_layersNumber; }
inline uint *getAudioDeviceId() { return m_audioDeviceId; } inline uint *getAudioDeviceId() { return m_audioDeviceId; }
inline uint getAudioDeviceQty() { return m_audioDeviceQty; } inline uint getAudioDeviceQty() { return m_audioDeviceQty; }
inline bool getShowUi() { return m_ui; } inline bool getShowUi() { return m_ui; }
inline bool getDmxActive() { return m_dmxActive; }
inline bool getShowPlayerActive() { return m_showPlayerActive; }
void readFile(); void readFile();
void readFromFile(QString file); void readFromFile(QString file);
void printSettings(); void printSettings();
void settingsLoader();
void settingsSaver();
private: private:
static Settings *_instance; static Settings *_instance;
@ -38,6 +45,8 @@ private:
QSet<int> m_universe; QSet<int> m_universe;
int m_layersNumber; int m_layersNumber;
bool m_ui; bool m_ui;
bool m_dmxActive;
bool m_showPlayerActive;
}; };
#endif // SETTINGS_H #endif // SETTINGS_H

223
src/showplayer.cpp Normal file
View file

@ -0,0 +1,223 @@
#include "showplayer.h"
ShowPlayer::ShowPlayer(QWidget *parent) :
QDialog(parent, Qt::Window)
, ui(new Ui::ShowPlayer)
{
ui->setupUi(this);
connect(ui->NewCue, SIGNAL(clicked()), this, SLOT(onAddTrack()));
connect(ui->EditCue, SIGNAL(clicked()), ui->cueListWidget, SLOT(editCueTrack()));
connect(ui->RemoveCue, SIGNAL(clicked()), ui->cueListWidget, SLOT(deleteCueTrack()));
connect(ui->SaveCueList, SIGNAL(clicked()), this, SLOT(saveCueTrackList()));
connect(ui->LoadCueList, SIGNAL(clicked()), this, SLOT(loadCueTrackList()));
connect(ui->goButton, SIGNAL(clicked()), this, SLOT(go()));
connect(ui->cueListWidget, SIGNAL(changeSelectedIndex(int)), this, SLOT(updateIndex(int)));
connect(ui->panicButton, SIGNAL(clicked()), this, SLOT(panicButtonClicked()));
connect(ui->restoreButton, SIGNAL(clicked()), this, SLOT(restoreButtonClicked()));
connect(ui->CopyCue, SIGNAL(clicked()), ui->cueListWidget, SLOT(copyCueTrack()));
connect(ui->PasteCue, SIGNAL(clicked()), ui->cueListWidget, SLOT(pasteCueTrack()));
connect(ui->CutCue, SIGNAL(clicked()), ui->cueListWidget, SLOT(cutCueTrack()));
currentStatus = Status::Iddle;
for(int i = 0; i < MAX_LAYERS; i++) {
layersUsed[i] = -1;
cueTrackWidgetPlaying[i] = nullptr;
current[i] = nullptr;
prevCue[i] = nullptr;
}
ui->nextCue->setNextCue();
Settings::getInstance()->beginGroup("showPlayerSplittersState");
ui->splitter_main->restoreState(Settings::getInstance()->value("mainSplitter").toByteArray());
ui->splitter_controls->restoreState(Settings::getInstance()->value("controlsSplitter").toByteArray());
ui->splitter_cues->restoreState(Settings::getInstance()->value("cueSplitter").toByteArray());
Settings::getInstance()->endGroup();
ui->activeCueList->setSelectionMode(QAbstractItemView::NoSelection);
}
ShowPlayer::~ShowPlayer()
{
saveCueTrackList("lastshow.xml");
Settings::getInstance()->beginGroup("showPlayerSplittersState");
Settings::getInstance()->setValue("mainSplitter", ui->splitter_main->saveState());
Settings::getInstance()->setValue("controlsSplitter", ui->splitter_controls->saveState());
Settings::getInstance()->setValue("cueSplitter", ui->splitter_cues->saveState());
Settings::getInstance()->endGroup();
delete ui;
}
void ShowPlayer::onAddTrack() {
ui->cueListWidget->createNewCueTrack();
}
void ShowPlayer::go()
{
CueTrack *goCue = ui->cueListWidget->getSelectedTrack(true);
if (!goCue)
return;
prevCue[goCue->audioLayer] = current[goCue->audioLayer];
current[goCue->audioLayer] = goCue;
removeCueTrackWidget(goCue->audioLayer);
CueTrackWidget *widget = addCueTrackWidget(goCue);
widget->go();
if (goCue->multi)
go();
}
void ShowPlayer::goAction(int c)
{
if (!current[c]->filePath.empty()) {
emit uiLoadMedia(current[c]->audioLayer, current[c]->filePath.data());
ui->cueListWidget->cueTrackAtEnd(c);
}
updateTrackStateInEngine(current[c]);
emit uiPlaybackChanged(current[c]->audioLayer, current[c]->status);
switch (current[c]->status) {
case Status::PlayingOnce:
case Status::PlayingLoop:
case Status::PlayingFolder:
case Status::PlayingFolderLoop:
case Status::PlayingFolderRandom:
current[c]->active = true;
layersUsed[current[c]->audioLayer] = current[c]->userNumber;
break;
default:
current[c]->active = false;
}
ui->cueListWidget->redrawCueTrackList();
}
void ShowPlayer::fade(Slider s, int start, CueTrack *track) {
AudioFade *fade = new AudioFade(s, start, track, this);
connect(fade, SIGNAL(uiSliderChanged(int, Slider, int)), this, SIGNAL(uiSliderChanged(int, Slider, int)));
fade->start();
}
void ShowPlayer::updateTrackStateInEngine(CueTrack *track) {
emit uiSliderChangedFaded(track->audioLayer, Slider::Volume, track->volume * 655.35, track->fadeIn * 1000, track->fadeOut * 1000);
if (track->fadeIn != 0 && prevCue[track->audioLayer] != nullptr) {
if (prevCue[track->audioLayer]->pan != track->pan)
fade(Slider::Pan, prevCue[track->audioLayer]->pan, track);
if (prevCue[track->audioLayer]->pitch != track->pitch)
fade(Slider::Pitch, prevCue[track->audioLayer]->pitch, track);
if (prevCue[track->audioLayer]->bus1 != track->bus1)
fade(Slider::Bus1, prevCue[track->audioLayer]->bus1, track);
if (prevCue[track->audioLayer]->bus2 != track->bus2)
fade(Slider::Bus2, prevCue[track->audioLayer]->bus2, track);
} else {
emit uiSliderChanged(track->audioLayer, Slider::Pan, (track->pan + 1) * 128);
emit uiSliderChanged(track->audioLayer, Slider::Pitch, track->pitch * 128);
emit uiSliderChanged(track->audioLayer, Slider::Bus1, track->bus1 * 255 * 2.55);
emit uiSliderChanged(track->audioLayer, Slider::Bus2, track->bus2 * 255 * 2.55);
}
}
void ShowPlayer::loadCueTrackList()
{
QString fileName = QFileDialog::getOpenFileName(this, tr("Open File"), "", tr("XML Files (*.xml)"));
if (fileName.isEmpty())
return;
ui->cueListWidget->loadCueTrackList(fileName.toStdString());
}
void ShowPlayer::saveCueTrackList()
{
QString fileName = QFileDialog::getSaveFileName(this, tr("Save File"), "", tr("XML Files (*.xml)"));
if (fileName.isEmpty())
return;
ui->cueListWidget->saveCueTrackList(fileName.toStdString());
}
void ShowPlayer::cueTrackAtEnd(int layer)
{
current[layer]->active = false;
ui->cueListWidget->cueTrackAtEnd(layer);
removeCueTrackWidget(layer);
}
void ShowPlayer::cueFinished(int c)
{
if (!current[c])
return;
if (!current[c]->stopAtEnd)
this->go();
if (current[c]->status == Status::Stopped || current[c]->status == Status::Paused)
cueTrackAtEnd(c);
}
CueTrackWidget *ShowPlayer::addCueTrackWidget(CueTrack* track) {
QListWidgetItem* item = new QListWidgetItem(ui->activeCueList);
CueTrackWidget* widget = new CueTrackWidget();
widget->loadCueTrack(track);
connect(widget, SIGNAL(goAction(int)), this, SLOT(goAction(int)));
connect(widget, SIGNAL(cueFinished(int)), this, SLOT(cueFinished(int)));
connect(widget, SIGNAL(changeStatus(int, Status)), this, SIGNAL(uiPlaybackChanged(int, Status)));
connect(widget, SIGNAL(changeVol(int, Slider, int, int, int)), this, SIGNAL(uiSliderChangedFaded(int, Slider, int, int, int)));
cueTrackWidgetPlaying[track->audioLayer] = widget;
item->setSizeHint(widget->sizeHint());
ui->activeCueList->setItemWidget(item, widget);
return (widget);
}
void ShowPlayer::playCueTrack(CueTrack* track) {
removeCueTrackWidget(track->audioLayer);
CueTrackWidget *widget = addCueTrackWidget(track);
widget->go();
}
void ShowPlayer::removeCueTrackWidget(int audioLayer) {
CueTrackWidget* widgetToRemove = cueTrackWidgetPlaying[audioLayer];
if (widgetToRemove != NULL && ui->activeCueList->count() > 0) {
for (int i = 0; i < ui->activeCueList->count(); ++i) {
QListWidgetItem* item = ui->activeCueList->item(i);
QWidget* widget = ui->activeCueList->itemWidget(item);
if (widget == widgetToRemove) {
ui->activeCueList->removeItemWidget(item);
delete item;
delete widgetToRemove;
break;
}
}
cueTrackWidgetPlaying[audioLayer] = nullptr;
}
}
void ShowPlayer::updateIndex(int index) {
if (index < 0)
return;
CueTrack *track = ui->cueListWidget->getSelectedTrack(false);
if (track) {
ui->nextCue->loadCueTrack(track);
}
}
void ShowPlayer::loadCueTrackList(QString path) {
ui->cueListWidget->loadCueTrackList(path.toStdString());
}
void ShowPlayer::saveCueTrackList(QString path) {
ui->cueListWidget->saveCueTrackList(path.toStdString());
}
void ShowPlayer::panicButtonClicked() {
for (int i = 0; i < MAX_LAYERS; i++) {
if (layersUsed[i] != -1) {
CueTrack* track = current[i];
if (track && track->active) {
emit uiSliderChanged(track->audioLayer, Slider::Volume, 0);
emit uiPlaybackChanged(track->audioLayer, Status::Paused);
}
}
}
}
void ShowPlayer::restoreButtonClicked() {
for (int i = 0; i < MAX_LAYERS; i++) {
if (layersUsed[i] != -1) {
CueTrack* track = current[i];
if (track) {
emit uiSliderChangedFaded(track->audioLayer, Slider::Volume, track->volume * 655.35, track->fadeIn, track->fadeOut);
emit uiPlaybackChanged(track->audioLayer, track->status);
}
}
}
}

67
src/showplayer.h Normal file
View file

@ -0,0 +1,67 @@
#ifndef SHOWPLAYER_H
#define SHOWPLAYER_H
#include <vector>
#include <string>
#include <QDialog>
#include <QFileDialog>
#include "defines.h"
#include "cuetracklistwidget.h"
#include "cuetrackwidget.h"
#include "dialgroup.h"
#include "settings.h"
#include "audiofade.h"
#include "ui_showplayer.h"
namespace Ui {
class ShowPlayer;
}
class ShowPlayer : public QDialog
{
Q_OBJECT
public:
explicit ShowPlayer(QWidget *parent = nullptr);
~ShowPlayer();
Ui::ShowPlayer *ui;
public slots:
void cueTrackAtEnd(int layer);
void loadCueTrackList(QString path);
void saveCueTrackList(QString path);
private:
Status currentStatus = Status::Iddle;
int layersUsed[MAX_LAYERS];
CueTrack *current[MAX_LAYERS];
CueTrack *prevCue[MAX_LAYERS];
CueTrackWidget *cueTrackWidgetPlaying[MAX_LAYERS];
CueTrackWidget *addCueTrackWidget(CueTrack *track);
std::map<int, std::pair<Status, double>> prePanicState;
private slots:
void updateTrackStateInEngine(CueTrack *track);
void onAddTrack();
void go();
void loadCueTrackList();
void saveCueTrackList();
void cueFinished(int channel);
void removeCueTrackWidget(int index);
void playCueTrack(CueTrack *track);
void goAction(int channel);
void updateIndex(int index);
void fade(Slider s, int start, CueTrack *track);
void panicButtonClicked();
void restoreButtonClicked();
signals:
void uiPlaybackChanged(int layer, Status s);
void uiSliderChanged(int layer, Slider s, int value);
void uiSliderChangedFaded(int layer, Slider s, int value, int fadeIn, int fadeOut);
void uiLoadMedia(int layer, QString s);
};
#endif // SHOWPLAYER_H

385
src/showplayer.ui Normal file
View file

@ -0,0 +1,385 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ShowPlayer</class>
<widget class="QDialog" name="ShowPlayer">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>802</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Show Player</string>
</property>
<property name="windowIcon">
<iconset resource="../lms-resources.qrc">
<normaloff>:/buttons/resources/icon.png</normaloff>:/buttons/resources/icon.png</iconset>
</property>
<property name="autoFillBackground">
<bool>true</bool>
</property>
<property name="sizeGripEnabled">
<bool>false</bool>
</property>
<property name="modal">
<bool>false</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QSplitter" name="splitter_main">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<widget class="QWidget" name="horizontalLayoutWidget">
<layout class="QHBoxLayout" name="ButtonToolBarLayout" stretch="0,0,0,0,0,0,0,0">
<property name="spacing">
<number>0</number>
</property>
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<item>
<widget class="QToolButton" name="LoadCueList">
<property name="toolTip">
<string>Load Cue List</string>
</property>
<property name="text">
<string>Load CueList</string>
</property>
<property name="icon">
<iconset resource="../lms-resources.qrc">
<normaloff>:/buttons/resources/load_button.png</normaloff>:/buttons/resources/load_button.png</iconset>
</property>
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="SaveCueList">
<property name="toolTip">
<string>Save Cue List</string>
</property>
<property name="text">
<string>Save CueList.</string>
</property>
<property name="icon">
<iconset resource="../lms-resources.qrc">
<normaloff>:/buttons/resources/save_button.png</normaloff>:/buttons/resources/save_button.png</iconset>
</property>
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="CopyCue">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>32</horstretch>
<verstretch>32</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Copy Cue</string>
</property>
<property name="text">
<string>Copy Cue</string>
</property>
<property name="icon">
<iconset resource="../lms-resources.qrc">
<normaloff>:/buttons/resources/copy_button.png</normaloff>:/buttons/resources/copy_button.png</iconset>
</property>
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="CutCue">
<property name="toolTip">
<string>Cut Cue</string>
</property>
<property name="accessibleName">
<string>Load Cue List</string>
</property>
<property name="text">
<string>Cut Cue</string>
</property>
<property name="icon">
<iconset resource="../lms-resources.qrc">
<normaloff>:/buttons/resources/cut_button.png</normaloff>:/buttons/resources/cut_button.png</iconset>
</property>
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="PasteCue">
<property name="toolTip">
<string>Paste Cue</string>
</property>
<property name="text">
<string>Paste Cue</string>
</property>
<property name="icon">
<iconset resource="../lms-resources.qrc">
<normaloff>:/buttons/resources/paste_button.png</normaloff>:/buttons/resources/paste_button.png</iconset>
</property>
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="NewCue">
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="toolTip">
<string>New Cue</string>
</property>
<property name="text">
<string>New Cue</string>
</property>
<property name="icon">
<iconset resource="../lms-resources.qrc">
<normaloff>:/buttons/resources/new_button.png</normaloff>:/buttons/resources/new_button.png</iconset>
</property>
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="EditCue">
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="toolTip">
<string>Edit Cue</string>
</property>
<property name="text">
<string>Edit Cue</string>
</property>
<property name="icon">
<iconset resource="../lms-resources.qrc">
<normaloff>:/buttons/resources/edit_button.png</normaloff>:/buttons/resources/edit_button.png</iconset>
</property>
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="RemoveCue">
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="toolTip">
<string>Remove Cue</string>
</property>
<property name="text">
<string>Delete Cue</string>
</property>
<property name="icon">
<iconset resource="../lms-resources.qrc">
<normaloff>:/buttons/resources/delete_button.png</normaloff>:/buttons/resources/delete_button.png</iconset>
</property>
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QSplitter" name="splitter_controls">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="QPushButton" name="goButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="focusPolicy">
<enum>Qt::ClickFocus</enum>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../lms-resources.qrc">
<normaloff>:/buttons/resources/go_button.jpeg</normaloff>:/buttons/resources/go_button.jpeg</iconset>
</property>
<property name="iconSize">
<size>
<width>120</width>
<height>120</height>
</size>
</property>
<property name="shortcut">
<string>Space</string>
</property>
<property name="autoDefault">
<bool>true</bool>
</property>
<property name="flat">
<bool>false</bool>
</property>
</widget>
<widget class="CueTrackWidget" name="nextCue" native="true"/>
<widget class="QToolButton" name="panicButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="focusPolicy">
<enum>Qt::ClickFocus</enum>
</property>
<property name="toolTip">
<string>Panic!</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../lms-resources.qrc">
<normaloff>:/buttons/resources/panic_button.jpg</normaloff>:/buttons/resources/panic_button.jpg</iconset>
</property>
<property name="iconSize">
<size>
<width>120</width>
<height>120</height>
</size>
</property>
<property name="shortcut">
<string>P</string>
</property>
</widget>
<widget class="QPushButton" name="restoreButton">
<property name="toolTip">
<string>Restore</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../lms-resources.qrc">
<normaloff>:/buttons/resources/restore_button.jpg</normaloff>:/buttons/resources/restore_button.jpg</iconset>
</property>
<property name="iconSize">
<size>
<width>120</width>
<height>120</height>
</size>
</property>
</widget>
</widget>
<widget class="QSplitter" name="splitter_cues">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="CueTrackListWidget" name="cueListWidget" native="true">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Cue List&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
<widget class="QListWidget" name="activeCueList">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Cues active&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="resizeMode">
<enum>QListView::Adjust</enum>
</property>
<property name="layoutMode">
<enum>QListView::SinglePass</enum>
</property>
</widget>
</widget>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>CueTrackListWidget</class>
<extends>QWidget</extends>
<header>src/cuetracklistwidget.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>CueTrackWidget</class>
<extends>QWidget</extends>
<header>src/cuetrackwidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>NewCue</tabstop>
<tabstop>EditCue</tabstop>
<tabstop>RemoveCue</tabstop>
</tabstops>
<resources>
<include location="../lms-resources.qrc"/>
</resources>
<connections/>
</ui>

View file

@ -38,16 +38,16 @@ SliderGroup::SliderGroup(QString name,
"margin: 0px;}" "margin: 0px;}"
"QSlider::groove:vertical {" "QSlider::groove:vertical {"
"border: 1px solid #999999;" "border: 1px solid #999999;"
"width: 25px;" "width: 25px;}"
"margin: -4px;}"
"QSlider::handle:vertical {" "QSlider::handle:vertical {"
"background: white;" "background: white;"
"border: 1px solid #5c5c5c;" "border: 2px solid #5c5c5c;"
"width: 29px;" "width: 29px;"
"height: 7px;" "height: 10px;"
"margin: -2px;"
"border-radius: 2px;}" "border-radius: 2px;}"
"Qslider::tickmarks:vertical {background: white;" "Qslider::tickmarks:vertical {"
"background-color: black;"
"background: red;"
"color: white;}" "color: white;}"
"QSlider::add-page:vertical {background: blue;}" "QSlider::add-page:vertical {background: blue;}"
"QSlider::sub-page:vertical {background: #20182d;}"); "QSlider::sub-page:vertical {background: #20182d;}");