diff --git a/docs/LibreMediaServer_Audio.hed b/docs/LibreMediaServer_Audio.hed
index 5da3a28..fda3e2d 100644
--- a/docs/LibreMediaServer_Audio.hed
+++ b/docs/LibreMediaServer_Audio.hed
@@ -1,76 +1,79 @@
ް׆ˌĠ鿰맫͓Ԕ
ҊÅЁ䍡
ӽ
-䉭ėĈ
-ϛ
-Ӧ
-ڑ
-ė
-
-ݍ
-Ȫ
-웣傪Ⓨ֚
-ކՂ
-߸
-Ŷ
-ѝ
-ωƀ
-
-
-
-
-ŕ
-ʷí
-̙
-
-ԥѼ܅
-𗹣َ
-̚
-Რ
-
-Ņ
-⽬
-̫
-喤쏎ʁǏ
-
-߬ػс
-썎ޛ
-
-쏎
-
-
-
-Ճ
-
-尨
-ގ
-
-ލ
-ƕ
-
-ŗ
-
-
-
-
-
-ˤ
-
-
-
-
-
-쏐
-
-
-
-쏎
-
-
-
-
-ÛƘ
-
-
-
-
+Ꭸʆ
+ɑ
+ɬ
+
+Ԑɉ
+
+ٿդϨ҃
+ڔ
+ڑŌ
+
+
+
+
+
+
+
+
+
+쏎ބ
+힇ݳ
+
+Ԓlj
+
+Ŷ§
+ÐÊ
+𡭟Ē
+
+܊
+
+зм
+
+ҭ
+ܻۏ
+ڑן
+ޏ
+
+Ƴ
+
+
+
+Ǖ
+
+𓎍Ԏ
+
+
+Ξ
+ֆ
+
+ց
+
+ᱯ
+݈
+
+؈
+
+馹
+۴
+𓎍
+
+
+
+
+
+𓎑
+
+
+
+Ɨ
+
+
+
+Ӌ
+쏎
+
+
+
diff --git a/docs/changelog.txt b/docs/changelog.txt
index 48b0ddf..5c7e0b6 100644
--- a/docs/changelog.txt
+++ b/docs/changelog.txt
@@ -4,39 +4,45 @@ Libre Media Server Audio - An Open source Media Server for arts and performing.
https://git.criptomart.net/libremediaserver
*******************************************************************************
-Lbre Media Server ChangeLog
+Libre Media Server ChangeLog
-v 0.2.0 Antigona (24/04/2024)
-+ change engine to miniaudio because is imposible pan in SFML and it has not access to low API and audio processing.
-+ Refactor all audio methods to MiniAudioEngine.
+v 0.2.0 Antígona (26/05/2024)
++ Change audio engine to miniaudio because is imposible pan in SFML and it has not access to low API and audio processing.
++ Refactor all audio methods to MiniAudioEngine class.
+ Select sound device output.
-+ pan.
-+ Show faders values.
-+ play offset.
++ Pan.
++ Show faders values. New SliderGroup class.
++ Entry Point 16 bits.
+ Refactor AudioMasterWidget to AudioDMXReceptionWidget.
-+ mp3, flac, wav (mp3 has given some errors seeking cursor...).
-+ settings dialog not working, only read the conf file at startup.
-+ variable number of layers.
-+ olathread, send double channels only once for each dmx frame buffer.
++ Read mp3, flac, wav (mp3 has given some errors seeking cursor...).
++ Removed settings dialog, only read xml conf file at startup.
++ Real dynamic variable number of layers based on conf file setting.
++ OlaThread send double channels (volume, entry point, load media) only once for each dmx frame buffer.
++ Terminal mode without graphical interface. All audio methods has been refactorized out of QWidget world.
++ Compilation without GUI (-DNOGUI).
++ New Status "Iddle" in playbacks if is not loaded.
++ New DMX personality version, better sort for audio needs (first load media, set vol, pan, etc, last playback order);
++ Refresh layer values when it loads a new sound file.
++ No QtSignals for sending data, better performance about 20% in my machine. Now, libremediaserver only updates values in AudioWidget, ui refresh is doing with a timer in audiowidget, so there is not problems between graphical and ola thread (the callback).
++ Load media files from ui clicking in the media labels.
-v 0.1.3 Unreleased (19/04/2024)
+v 0.1.3 Leúcade (19/04/2024)
+ Ubuntu 22.04 jammy.
-+ Use SFML as audio engine.
+ Qt 5.15.3.
-+ pitch.
-+ loop.
++ Pitch.
++ Loop.
v 0.1.2 Mayordomo (12/08/2015)
-- GUI config
-- Several bugs tested in real world
-- variable layers
-- SFML as audio engine
+- GUI config.
+- Several bugs tested in real world.
+- Variable layers.
+- SFML as audio engine.
v 0.1.1 Pascual (24/09/2014)
-+ First Version: 4 layers playing .ogg
-+ Needs Open Lighting Arquitecture => 0.9.0
-+ Pure Data as audio engine
++ First Version: 4 layers playing .ogg.
++ Needs Open Lighting Arquitecture => 0.9.0.
++ Pure Data as audio engine.
diff --git a/docs/lms-audio.xlm b/docs/lms-audio.xlm
index cc498ca..4274267 100644
--- a/docs/lms-audio.xlm
+++ b/docs/lms-audio.xlm
@@ -1,8 +1,8 @@
-
-
-
-
-
-
-
+
+
+
+
+
+
+
diff --git a/docs/roadmap.txt b/docs/roadmap.txt
index 8ff57d7..0771257 100644
--- a/docs/roadmap.txt
+++ b/docs/roadmap.txt
@@ -5,7 +5,6 @@ https://git.criptomart.net/libremediaserver
*******************************************************************************
Libre Media Server Roadmap
-(en continuo crecimiento...)
v 0.2.x
- skin, UI/UX
@@ -20,8 +19,8 @@ v 0.2.2
+ hay que empaquetar OLA, incluirlo en el binario, o implementar sACN y linkarlo estáticamente.
+ https://github.com/ETCLabs/sACN
- Qt6.
-- audio processing (eq, rev, compresor, ...) por master y capa.
-- CIPT/MSex, send icons play-pause-stop.
+- Audio processing (eq, rev, compresor, ...) by master and layer.
+- CIPT/MSex, send icons play/pause/stop.
- Rasp build.
- Octopus Sound Card support (6 outputs - 8 inputs).
@@ -41,15 +40,15 @@ v 0.2.1
- audio device linked, outputs will be redirected there.
- dmx address + universe settings.
- Rose noise and sine generator in menu to test system.
-- Keyboards strokes, load media files from ui.
-- Dar la opción clickeando en el widget de tiempo de poner una cuenta atrás en vez de hacia delante.
+- Ui/Ux; Keyboards strokes.
+- Ui/Ux: Dar la opción clickeando en el widget de tiempo de poner una cuenta atrás en vez de hacia delante.
- Logs, verbosity, timestamp.
-- Bufgix: depurar errores cuando no carga la librería de medias, cambia el númmero de capas, cambia el universo, etc.
-- New control mode without pitch control, it saves resources. MA_SOUND_FLAG_NO_PITCH
-- Vumeter or indicator about audio output in layer and master.
+- New play mode without pitch control, it saves resources. MA_SOUND_FLAG_NO_PITCH
- SettingsDialog.
- Load/save conf file.
-- ¿stop offset? is it needed?
-- decouple MiniAudioEngine from AudioWidget, starts whith no gui or with audio in a dedicated thread.
-- New Status "Iddle" in playbacks if is not loaded.
-- check return errors, we are too happy....
+- ¿Exit Point? is it needed?
+- Hardening: check return errors, try/catch exceptions, i'm too happy....
+- Tests: errors on wrong conf file.
+
+v0.2.0:
+- Vumeter or indicator about audio output in layer and master, add to sliderGroup.
diff --git a/libremediaserver-audio.pro b/libremediaserver-audio.pro
index a33e231..41d6ecd 100644
--- a/libremediaserver-audio.pro
+++ b/libremediaserver-audio.pro
@@ -2,7 +2,10 @@ TEMPLATE = app
TARGET = libremediaserver-audio
QT += webkitwidgets widgets
HEADERS += src/libremediaserver-audio.h \
+ src/clickablelabel.h \
src/dmxwidget.h \
+ src/libremediaserver-audio-gui.h \
+ src/main.h \
src/miniaudio.h \
src/medialibrary.h \
src/miniaudioengine.h \
@@ -14,7 +17,9 @@ HEADERS += src/libremediaserver-audio.h \
src/settings.h \
src/slidergroup.h
SOURCES += src/main.cpp \
+ src/clickablelabel.cpp \
src/dmxwidget.cpp \
+ src/libremediaserver-audio-gui.cpp \
src/miniaudio.c \
src/libremediaserver-audio.cpp \
src/medialibrary.cpp \
@@ -24,10 +29,10 @@ SOURCES += src/main.cpp \
src/audiowidget.cpp \
src/settings.cpp \
src/slidergroup.cpp
-FORMS += src/libremediaserver-audio.ui
-CCFLAG += -msse2 -mavx2 #-fsanitize=address -g -O0
+FORMS += src/libremediaserver-audio-gui.ui
+CCFLAG += -msse2 -mavx2 #-fsanitize=address -g3 -O0
QMAKE_CXXFLAGS += $$(CXXFLAG)
-#QMAKE_CXXFLAGS += -fsanitize=address -g -O0
+#QMAKE_CXXFLAGS += -fsanitize=address -g3 -O0
QMAKE_CFLAGS += $$(CCFLAG)
QMAKE_LFLAGS += $$(LDFLAG)
LIBS += -lola -lolacommon -ldl -lpthread -lm
diff --git a/src/audiolayerwidget.cpp b/src/audiolayerwidget.cpp
index 99e1996..e8d22bd 100644
--- a/src/audiolayerwidget.cpp
+++ b/src/audiolayerwidget.cpp
@@ -1,64 +1,89 @@
#include "audiolayerwidget.h"
+#include
-
-AudioLayerWidget::AudioLayerWidget(QWidget *parent, QString name, int layer):
- QGroupBox(parent)
+AudioLayerWidget::AudioLayerWidget(QWidget *parent, int layer):
+ QWidget(parent)
, m_layer(layer)
, m_suspendResumeButton(0)
{
- this->setTitle(name);
QVBoxLayout *layout = new QVBoxLayout;
- QHBoxLayout *progressTime = new QHBoxLayout;
- m_progressTime = new QTimeEdit;
- m_progressTime->text();
- m_progressTime->setDisplayFormat("h:mm:ss:zzz");
- m_progressTime->setReadOnly(true);
- m_progressTime->setButtonSymbols(QAbstractSpinBox::NoButtons);
- m_progressTime->setMaximumWidth(90);
- m_progressTime->setFocusPolicy(Qt::NoFocus);
- progressTime->addWidget(m_progressTime);
- m_totalTimeValue = new QTimeEdit;
- m_totalTimeValue->setDisplayFormat("h:mm:ss:zzz");
- m_totalTimeValue->setReadOnly(true);
- m_totalTimeValue->setButtonSymbols(QAbstractSpinBox::NoButtons);
- m_totalTimeValue->setMaximumWidth(90);
- m_totalTimeValue->setFocusPolicy(Qt::NoFocus);
- progressTime->addWidget(m_totalTimeValue);
- layout->addLayout(progressTime);
-
- m_progressSlider = new QSlider(Qt::Horizontal);
- m_progressSlider->setFocusPolicy(Qt::NoFocus);
- layout->addWidget(m_progressSlider);
-
- QGridLayout *status = new QGridLayout;
- m_statusValue = new QLabel;
- status->addWidget(m_statusValue, 0, 0);
- m_folderValue = new QLabel;
- m_folderValue->setMaximumWidth(200);
- status->addWidget(m_folderValue, 1, 0);
- m_fileValue = new QLabel;
- m_fileValue->setMaximumWidth(200);
- status->addWidget(m_fileValue, 2, 0);
- layout->addLayout(status);
-
- QHBoxLayout *volumeBox = new QHBoxLayout;
- m_volume = new SliderGroup("Vol", 0 , 100, 2, NULL);
- volumeBox->addWidget(m_volume);
- connect(m_volume, SIGNAL(valueChanged(float)), this, SLOT(volumeChanged(float)));
- m_pan = new SliderGroup("Pan", 0 , 255, 0, NULL);
- volumeBox->addWidget(m_pan);
- connect(m_pan, SIGNAL(valueChanged(float)), this, SLOT(panChanged(float)));
- m_pitch = new SliderGroup("Pitch", 0 , 255, 0, NULL);
- volumeBox->addWidget(m_pitch);
- connect(m_pitch, SIGNAL(valueChanged(float)), this, SLOT(pitchChanged(float)));
- layout->addLayout(volumeBox);
+ QVBoxLayout *playback = new QVBoxLayout;
+ m_folderValue = new ClickableLabel;
+ m_folderValue->setMaximumWidth(160);
+ m_folderValue->setAlignment(Qt::AlignLeft);
+ m_folderValue->setStyleSheet(
+ "color: white;"
+ "background-color: black;"
+ );
+ playback->addWidget(m_folderValue);
+ m_fileValue = new ClickableLabel;
+ connect(m_fileValue, SIGNAL(clicked()), this, SLOT(openMediaDialog()));
+ connect(m_folderValue, SIGNAL(clicked()), this, SLOT(openMediaDialog()));
+ m_fileValue->setMaximumWidth(160);
+ m_fileValue->setAlignment(Qt::AlignLeft);
+ m_fileValue->setStyleSheet(
+ "color: white;"
+ "background-color: black;"
+ );
+ playback->addWidget(m_fileValue);
+ playback->setSpacing(0);
+ playback->setContentsMargins(0, 0, 0, 0);
+ layout->addLayout(playback);
m_suspendResumeButton = new QPushButton(this);
- m_suspendResumeButton->setText(StatusStr[Status::Stopped]);
+ m_suspendResumeButton->setText(StatusStr[Status::Iddle]);
connect(m_suspendResumeButton, SIGNAL(clicked()), SLOT(toggleSuspendResume()));
layout->addWidget(m_suspendResumeButton);
+ m_progress = new QProgressBar(this);
+ m_progress->setOrientation(Qt::Horizontal);
+ m_progress->setRange(0, 0);
+ m_progress->setValue(0);
+ m_progress->setFormat("%v / %m");
+ layout->addWidget(m_progress);
+
+ m_progressTime = new QTimeEdit;
+ m_progressTime->setToolTip("Current Time");
+ m_progressTime->setObjectName("Current Time");
+ m_progressTime->setDisplayFormat("mm:ss:zz");
+ m_progressTime->setReadOnly(true);
+ m_progressTime->setButtonSymbols(QAbstractSpinBox::NoButtons);
+ m_progressTime->setMinimumWidth(80);
+ m_progressTime->setMaximumWidth(80);
+ m_progressTime->setFocusPolicy(Qt::NoFocus);
+ m_progressTime->setAlignment(Qt::AlignHCenter);
+ m_progressTime->setContentsMargins(0,0,0,0);
+ m_totalTimeValue = new QTimeEdit;
+ m_totalTimeValue->setObjectName("Track Length");
+ m_totalTimeValue->setToolTip("Track Length");
+ m_totalTimeValue->setDisplayFormat("mm:ss:zzz");
+ m_totalTimeValue->setReadOnly(true);
+ m_totalTimeValue->setButtonSymbols(QAbstractSpinBox::NoButtons);
+ m_totalTimeValue->setMinimumWidth(80);
+ m_totalTimeValue->setFocusPolicy(Qt::NoFocus);
+ m_totalTimeValue->setAlignment(Qt::AlignHCenter);
+ m_totalTimeValue->setContentsMargins(0,0,0,0);
+ QHBoxLayout *status = new QHBoxLayout;
+ status->addWidget(m_progressTime);
+ status->addWidget(m_totalTimeValue);
+ layout->addLayout(status);
+ QHBoxLayout *volumeBox = new QHBoxLayout;
+ m_volume = new SliderGroup("Vol", 0 , 100, 2, NULL);
+ volumeBox->addWidget(m_volume);
+ connect(m_volume, SIGNAL(valueChanged(int)), this, SLOT(volumeChanged(int)));
+ m_pan = new SliderGroup("Pan", 0 , 255, 0, NULL);
+ volumeBox->addWidget(m_pan);
+ connect(m_pan, SIGNAL(valueChanged(int)), this, SLOT(panChanged(int)));
+ m_pitch = new SliderGroup("Pitch", 0 , 255, 0, NULL);
+ volumeBox->addWidget(m_pitch);
+ volumeBox->setSpacing(0);
+ volumeBox->setContentsMargins(0, 0, 0, 0);
+ connect(m_pitch, SIGNAL(valueChanged(int)), this, SLOT(pitchChanged(int)));
+ layout->addLayout(volumeBox);
+ layout->setAlignment(Qt::AlignHCenter);
+ layout->setSpacing(0);
+ layout->setContentsMargins(2, 2, 2, 2);
this->setLayout(layout);
}
@@ -68,17 +93,17 @@ AudioLayerWidget::~AudioLayerWidget()
}
// From UI.
-void AudioLayerWidget::volumeChanged(float value)
+void AudioLayerWidget::volumeChanged(int value)
{
emit(uiSliderChanged(m_layer, Slider::Volume, value));
}
-void AudioLayerWidget::panChanged(float value)
+void AudioLayerWidget::panChanged(int value)
{
emit(uiSliderChanged(m_layer, Slider::Pan, value));
}
-void AudioLayerWidget::pitchChanged(float value)
+void AudioLayerWidget::pitchChanged(int value)
{
emit(uiSliderChanged(m_layer, Slider::Pitch, value));
}
@@ -93,12 +118,28 @@ void AudioLayerWidget::toggleSuspendResume()
break;
case Status::Paused:
case Status::Stopped:
- this->setPlaybackStatus(Status::PlayingOnce);
- emit uiPlaybackChanged(m_layer, Status::PlayingOnce);
+ this->setPlaybackStatus(Status::PlayingLoop);
+ emit uiPlaybackChanged(m_layer, Status::PlayingLoop);
+ case Status::Iddle:
break;
}
}
+void AudioLayerWidget::openMediaDialog()
+{
+ QFileDialog dialog(this);
+ dialog.setFileMode(QFileDialog::ExistingFile);
+ dialog.setNameFilter(tr("Sound Tracks (*.mp3 *.mp3 *.flac *.wav"));
+ dialog.setViewMode(QFileDialog::Detail);
+ dialog.setDirectory(Settings::getInstance()->getPathMedia());
+ if (!dialog.exec())
+ return;
+ QStringList fileNames;
+ fileNames = dialog.selectedFiles();
+ emit uiLoadMedia(m_layer, fileNames.at(0));
+ this->setMediaFile(fileNames.at(0));
+}
+
// from DMX signals
void AudioLayerWidget::setVol(float vol)
{
@@ -121,7 +162,7 @@ void AudioLayerWidget::setPitch(int pitch)
m_pitch->blockSignals(false);
}
-void AudioLayerWidget::fileLoaded(QString file)
+void AudioLayerWidget::setMediaFile(QString file)
{
QStringList list = file.split("/");
int size = list.size();
@@ -131,41 +172,36 @@ void AudioLayerWidget::fileLoaded(QString file)
}
}
-void AudioLayerWidget::setPlaybackStatus(Status status)
+void AudioLayerWidget::setPlaybackStatus(Status s)
{
- m_status = status;
- if (status == Status::Stopped)
- m_progressTime->setTime(QTime::fromMSecsSinceStartOfDay(0));
- m_statusValue->blockSignals(true);
+ Status status = static_cast(s);
m_suspendResumeButton->blockSignals(true);
- m_statusValue->setText(StatusStr[status]);
+ m_status = status;
m_suspendResumeButton->setText(StatusStr[status]);
- switch (m_status) {
- case Status::Paused:
- m_statusValue->setStyleSheet("QLabel { color : red; }");
- break;
- case Status::PlayingLoop:
- case Status::PlayingOnce:
- m_statusValue->setStyleSheet("QLabel { color : green; }");
- break;
- case Status::Stopped:
- m_statusValue->setStyleSheet("QLabel { color : red; }");
- break;
- }
- m_statusValue->blockSignals(false);
m_suspendResumeButton->blockSignals(false);
}
-void AudioLayerWidget::durationChanged(float dur)
+void AudioLayerWidget::setDuration(float dur)
{
- dur *= 1000;
- m_progressSlider->setMaximum(dur);
+ m_progress->blockSignals(true);
+ m_progressTime->blockSignals(true);
+ m_totalTimeValue->blockSignals(true);
+ m_progress->setRange(0, dur);
+ m_progress->setValue(0);
+ m_progressTime->setTime(QTime::fromMSecsSinceStartOfDay(0));
m_totalTimeValue->setTime(QTime::fromMSecsSinceStartOfDay(dur));
+ m_progress->blockSignals(false);
+ m_progressTime->blockSignals(false);
+ m_totalTimeValue->blockSignals(false);
}
-void AudioLayerWidget::refreshUi(float progress)
+void AudioLayerWidget::setCurrentTime(float progress)
{
progress *= 1000;
- m_progressSlider->setValue(progress);
+ m_progress->blockSignals(true);
+ m_progressTime->blockSignals(true);
+ m_progress->setValue(progress);
m_progressTime->setTime(QTime::fromMSecsSinceStartOfDay(progress));
+ m_progress->blockSignals(false);
+ m_progressTime->blockSignals(false);
}
diff --git a/src/audiolayerwidget.h b/src/audiolayerwidget.h
index 53401fb..9380b81 100644
--- a/src/audiolayerwidget.h
+++ b/src/audiolayerwidget.h
@@ -3,53 +3,57 @@
#include
#include
-#include
+#include
+#include
#include "defines.h"
#include "slidergroup.h"
+#include "clickablelabel.h"
+#include "settings.h"
-class AudioLayerWidget : public QGroupBox
+class AudioLayerWidget : public QWidget
{
Q_OBJECT
public:
- explicit AudioLayerWidget(QWidget *parent = 0, QString name = "Layer", int layer = 0);
+ explicit AudioLayerWidget(QWidget *parent = 0, int layer = 0);
~AudioLayerWidget();
- void setVol(float vol);
- void resume();
- void setPan(int pan);
- void setPitch(int pitch);
- void setLoop(bool on);
- void setPlaybackStatus(Status status);
- inline Status getPlaybackStatus() { return m_status; }
private:
Status m_status;
int m_layer;
QPushButton *m_suspendResumeButton;
- QLabel * m_statusValue;
- QLabel *m_fileValue;
- QLabel * m_folderValue;
+ ClickableLabel *m_fileValue;
+ ClickableLabel * m_folderValue;
SliderGroup *m_volume;
SliderGroup *m_pan;
SliderGroup *m_pitch;
- QSlider *m_progressSlider;
QTimeEdit *m_progressTime;
QTimeEdit *m_totalTimeValue;
+ QProgressBar *m_progress;
+// From DMX
public slots:
+ void setMediaFile(QString file);
+ void setDuration(float dur);
+ void setCurrentTime(float progress);
+ void setPlaybackStatus(Status status);
+ void setVol(float vol);
+ void setPan(int pan);
+ void setPitch(int pitch);
+
+// From Ui
+private slots:
+ void openMediaDialog();
void toggleSuspendResume();
- void volumeChanged(float vol);
- void panChanged(float pan);
- void pitchChanged(float pitch);
- void fileLoaded(QString file);
- void durationChanged(float dur);
- void refreshUi(float progress);
+ void volumeChanged(int vol);
+ void panChanged(int pan);
+ void pitchChanged(int pitch);
signals:
void uiPlaybackChanged(int layer, Status s);
void uiSliderChanged(int layer, Slider s, int value);
-
+ void uiLoadMedia(int layer, QString s);
};
#endif // AUDIOLAYERWIDGET_H
diff --git a/src/audiowidget.cpp b/src/audiowidget.cpp
index 4b35a5c..8bd077c 100644
--- a/src/audiowidget.cpp
+++ b/src/audiowidget.cpp
@@ -1,121 +1,100 @@
#include "audiowidget.h"
+#include
-
-AudioWidget::AudioWidget() :
- m_layout(new QHBoxLayout())
- , m_refreshUi(new QTimer(this))
+AudioWidget::AudioWidget(QWidget *parent) :
+ QWidget(parent)
+ , m_layout(new QHBoxLayout())
{
for (int i= 0; i < Settings::getInstance()->getLayersNumber(); i++ ) {
- AudioLayerWidget *alw = new AudioLayerWidget(this, tr("Layer %1").arg(i + 1), i);
+ AudioLayerWidget *alw = new AudioLayerWidget(this, i);
m_layout->insertWidget(i, alw);
- connect(alw, SIGNAL(uiSliderChanged(int, Slider, int)), this, SLOT(uiSliderAction(int, Slider, int)));
- connect(alw, SIGNAL(uiPlaybackChanged(int, Status)), this, SLOT(uiChangePlaybackStatus(int, Status)));
+ connect(alw, SIGNAL(uiSliderChanged(int, Slider, int)), this, SIGNAL(uiSliderChanged(int, Slider, int)));
+ connect(alw, SIGNAL(uiPlaybackChanged(int, Status)), this, SIGNAL(uiPlaybackChanged(int, Status)));
+ connect(alw, SIGNAL(uiLoadMedia(int, QString)), this, SIGNAL(uiLoadMedia(int, QString)));
+ m_layerUpdate[i].status = Status::Iddle;
+ m_layerUpdate[i].duration = 0;
+ m_layerUpdate[i].media = "";
+ m_layerUpdate[i].vol = 0;
+ m_layerUpdate[i].pan = 128;
+ m_layerUpdate[i].pitch = 128;
+ m_layerUpdate[i].cursor = 0;
}
+ m_layout->setSpacing(0);
+ m_layout->setContentsMargins(1, 1, 1, 1);
setLayout(m_layout);
+ m_refreshUi = new QTimer(this);
connect(m_refreshUi, SIGNAL(timeout()), this, SLOT(refreshUi()));
- m_refreshUi->start(UI_REFRESH_TIME);
+ m_refreshUi->start(UI_REFRESH_TIME * 1.5);
}
-bool AudioWidget::startEngine(int id)
+void AudioWidget::mediaLoaded(int layer, QString file, float duration)
{
- return (m_mae.startEngine(id));
-}
-
-bool AudioWidget::startEngine()
-{
- return (m_mae.startEngine(Settings::getInstance()->getAudioDeviceId()));
-}
-
-void AudioWidget::stopEngine()
-{
- m_mae.stopEngine();
-}
-
-void AudioWidget::mediaLoaded(int layer, QString file)
-{
- ma_result result;
-
- if (m_currentMedia[layer].compare(file) == 0 ) {
- return;
- }
- if (!QFile::exists(file)) {
- qWarning("Can not access to file %s", file.toLatin1().constData());
- return;
- }
- result = m_mae.loadMedia(layer, file.toLatin1().data());
- if (result != MA_SUCCESS) {
- qWarning("can not open file %s", file.toLatin1().constData());
- return;
- }
- m_currentMedia[layer] = file;
- float pLength = m_mae.getDuration(layer);
- qInfo("File loaded: %s - Duration: %f secs", file.toLatin1().constData(), pLength);
- m_mae.printFormatInfo(layer);
- QLayoutItem * const item = m_layout->itemAt(layer);
- dynamic_cast(item->widget())->fileLoaded(file);
- dynamic_cast(item->widget())->durationChanged(pLength);
+ m_layerUpdate[layer].media = file;
+ m_layerUpdate[layer].duration = duration;
+ m_layerUpdate[layer].updated = true;
}
void AudioWidget::volChanged(int layer, float vol) {
- m_mae.volChanged(layer, vol);
- QLayoutItem * const item = m_layout->itemAt(layer);
- dynamic_cast(item->widget())->setVol(vol);
+ m_layerUpdate[layer].vol = vol;
+ m_layerUpdate[layer].updated = true;
}
void AudioWidget::panChanged(int layer, int pan) {
- m_mae.panChanged(layer, pan);
- QLayoutItem * const item = m_layout->itemAt(layer);
- dynamic_cast(item->widget())->setPan(pan);
+ m_layerUpdate[layer].pan = pan;
+ m_layerUpdate[layer].updated = true;
}
void AudioWidget::pitchChanged(int layer, int pitch) {
- m_mae.pitchChanged(layer, pitch);
- QLayoutItem * const item = m_layout->itemAt(layer);
- dynamic_cast(item->widget())->setPitch(pitch);
+
+ m_layerUpdate[layer].pitch = pitch;
+ m_layerUpdate[layer].updated = true;
}
void AudioWidget::playbackChanged(int layer, Status status)
{
- m_mae.playbackChanged(layer, status);
- QLayoutItem * const item = m_layout->itemAt(layer);
- dynamic_cast(item->widget())->setPlaybackStatus(status);
+ m_layerUpdate[layer].status = status;
+ m_layerUpdate[layer].updated = true;
}
-void AudioWidget::entryPointChanged(int layer, int cursor)
+void AudioWidget::cursorChanged(int layer, float cursor)
{
- m_mae.setCursor(layer, cursor);
- QLayoutItem * const item = m_layout->itemAt(layer);
- AudioLayerWidget *aw = dynamic_cast(item->widget());
- aw->refreshUi(m_mae.getCursor(layer));
+ m_layerUpdate[layer].cursor = cursor;
+ m_layerUpdate[layer].updated = true;
}
-void AudioWidget::refreshUi() {
- for (int i= 0; i < Settings::getInstance()->getLayersNumber(); i++ ) {
- QLayoutItem * const item = m_layout->itemAt(i);
- AudioLayerWidget *aw = dynamic_cast(item->widget());
- Status s = aw->getPlaybackStatus();
- if (s == Status::PlayingOnce || s == Status::PlayingLoop) {
- aw->refreshUi(m_mae.getCursor(i));
+void AudioWidget::refreshUi()
+{
+ for (int i = 0; i < MAX_LAYERS; i++)
+ {
+ if (m_layerUpdate[i].updated) {
+ QLayoutItem * const item = m_layout->itemAt(i);
+ AudioLayerWidget *alw = dynamic_cast(item->widget());
+ if (m_layerUpdate[i].vol > -1) {
+ alw->setVol(m_layerUpdate[i].vol);
+ m_layerUpdate[i].vol = -1;
+ }
+ if (m_layerUpdate[i].cursor > -1) {
+ alw->setCurrentTime(m_layerUpdate[i].cursor);
+ m_layerUpdate[i].cursor = -1;
+ }
+ if (m_layerUpdate[i].pan > -1) {
+ alw->setPan(m_layerUpdate[i].pan);
+ m_layerUpdate[i].pan = -1;
+ }
+ if (m_layerUpdate[i].pitch > -1) {
+ alw->setPitch(m_layerUpdate[i].pitch);
+ m_layerUpdate[i].pitch = -1;
+ }
+ if (m_layerUpdate[i].status != Status::Iddle) {
+ alw->setPlaybackStatus(m_layerUpdate[i].status);
+ m_layerUpdate[i].status = Status::Iddle;
+ }
+ if (m_layerUpdate[i].duration > -1) {
+ alw->setMediaFile(m_layerUpdate[i].media);
+ alw->setDuration(m_layerUpdate[i].duration);
+ m_layerUpdate[i].duration = -1;
+ }
+ m_layerUpdate[i].updated = false;
}
}
}
-
-void AudioWidget::uiSliderAction(int layer, Slider s, int value)
-{
- switch (s){
- case Slider::Volume:
- m_mae.volChanged(layer, value);
- break;
- case Slider::Pan:
- m_mae.panChanged(layer, value);
- break;
- case Slider::Pitch:
- m_mae.pitchChanged(layer, value);
- break;
- }
-}
-
-void AudioWidget::uiChangePlaybackStatus(int layer, Status s) {
- m_mae.playbackChanged(layer, s);
-}
-
diff --git a/src/audiowidget.h b/src/audiowidget.h
index 254e59e..dd9b1f1 100644
--- a/src/audiowidget.h
+++ b/src/audiowidget.h
@@ -5,7 +5,6 @@
#include "audiolayerwidget.h"
#include "settings.h"
-#include "miniaudioengine.h"
#include "defines.h" // MAX_LAYERS
class AudioWidget : public QWidget
@@ -13,29 +12,27 @@ class AudioWidget : public QWidget
Q_OBJECT
public:
- AudioWidget();
- bool startEngine();
- bool startEngine(int id);
- void stopEngine();
- void mediaLoaded(int layer, QString media );
- void volChanged(int layer, float vol);
- void panChanged(int layer, int pan);
- void pitchChanged(int layer, int pitch);
- void playbackChanged(int layer, Status status);
- void entryPointChanged(int layer, int cursor);
+ AudioWidget(QWidget *parent = nullptr);
private:
- MiniAudioEngine m_mae;
- QString m_currentMedia[MAX_LAYERS];
QHBoxLayout *m_layout;
+ layerData m_layerUpdate[MAX_LAYERS];
QTimer *m_refreshUi;
public slots:
- void uiSliderAction(int layer, Slider s, int value);
- void uiChangePlaybackStatus(int layer, Status s);
+ void volChanged(int layer, float vol);
+ void panChanged(int layer, int pan);
+ void pitchChanged(int layer, int pitch);
+ void cursorChanged(int layer, float cursor);
+ void mediaLoaded(int layer, QString media, float duration);
+ void playbackChanged(int layer, Status status);
private slots:
void refreshUi();
+signals:
+ void uiPlaybackChanged(int layer, Status s);
+ void uiSliderChanged(int layer, Slider s, int vol);
+ void uiLoadMedia(int layer, QString s);
};
#endif // AUDIOWIDGET_H
diff --git a/src/clickablelabel.cpp b/src/clickablelabel.cpp
new file mode 100644
index 0000000..623493d
--- /dev/null
+++ b/src/clickablelabel.cpp
@@ -0,0 +1,14 @@
+#include "clickablelabel.h"
+
+ClickableLabel::ClickableLabel(QWidget *parent, Qt::WindowFlags f)
+ : QLabel{parent}
+{
+ Q_UNUSED(f);
+}
+
+ClickableLabel::~ClickableLabel() {}
+
+void ClickableLabel::mousePressEvent(QMouseEvent* event) {
+ Q_UNUSED(event);
+ emit clicked();
+}
diff --git a/src/clickablelabel.h b/src/clickablelabel.h
new file mode 100644
index 0000000..e75aae7
--- /dev/null
+++ b/src/clickablelabel.h
@@ -0,0 +1,22 @@
+#ifndef CLICKABLELABEL_H
+#define CLICKABLELABEL_H
+
+#include
+#include
+#include
+
+class ClickableLabel : public QLabel
+{
+ Q_OBJECT
+public:
+ explicit ClickableLabel(QWidget *parent = Q_NULLPTR, Qt::WindowFlags f = Qt::WindowFlags());
+ ~ClickableLabel();
+
+signals:
+ void clicked();
+
+protected:
+ void mousePressEvent(QMouseEvent* event);
+};
+
+#endif // CLICKABLELABEL_H
diff --git a/src/defines.h b/src/defines.h
index 68675c2..ff299c1 100644
--- a/src/defines.h
+++ b/src/defines.h
@@ -1,15 +1,15 @@
#ifndef DEFINES_H
#define DEFINES_H
-//#define VERSION "LibreMediaServerAudio 0.2.0 Antigona Release"
-#define VERSION "Kike Substitutor - No AI required - v0.2.0"
+#define VERSION "LibreMediaServerAudio 0.2.0 Antigona Release"
#define COPYRIGHT "(C) 2014-2024 Santi Noreña "
-#define LICENSE "GPL 3 Licensed. See LICENSE.txt.\nSound guys are not allowed to use this software."
+#define LICENSE "GPL 3 Licensed. See LICENSE.txt."
#define DEFAULT_FILE "lms-audio.xlm"
-#define MAX_LAYERS 16
-#define UI_REFRESH_TIME 200
+#define MAX_LAYERS 4
+#define MAX_AUDIODEVICES 8
+#define UI_REFRESH_TIME 66
+#define FADE_TIME 25 // DMX Frame time, 40 fps, avoid clicks
-// struct where save the DMX settings for each layer
struct dmxSetting {
int address;
unsigned int universe;
@@ -22,6 +22,10 @@ enum Status
Paused,
PlayingOnce,
PlayingLoop,
+ Iddle,
+ PlayingFolder,
+ PlayingFolderLoop,
+ PlayingFolderRandom
};
static const char* StatusStr[] =
@@ -29,7 +33,11 @@ static const char* StatusStr[] =
"Stop",
"Pause",
"Playing One",
- "Playing Loop",
+ "Playing One Loop",
+ "Iddle",
+ "Playing Folder",
+ "Playing Folder Loop",
+ "Playing Folder Random",
0x0
};
@@ -40,5 +48,18 @@ enum Slider
Pitch,
};
+#include
+struct layerData {
+ QString media;
+ Status status;
+ bool updated;
+ float vol;
+ float cursor;
+ int pan;
+ int pitch;
+ float duration;
+ int address;
+ unsigned int universe;
+ int device;
+};
#endif // DEFINES_H
-
diff --git a/src/dmxPersonality.h b/src/dmxPersonality.h
index 1e42df7..82f9468 100644
--- a/src/dmxPersonality.h
+++ b/src/dmxPersonality.h
@@ -1,39 +1,15 @@
#ifndef DMXPERSONALITY_H
#define DMXPERSONALITY_H
-/** Define the DMX personality to avoid dealing with
- * numbers and change it easyly in case
- *
-1 - Volumen Coarse
-2 - Pan
-3 - Folder
-4 - File
-5 - Playback
- 0-24 : Play once.
- 25-49: Stop. Returns to start of file.
- 50-74: Pause. It keeps the time of reproductions.
- 75-99: Play loop.
-6 - Control - Reservado, sin uso en este momento.
-7 - Volume Fine
-8 - Entry Point Coarse - Punto de entrada de reproducción.
-9 - Entry Point Fine - El valor de estos dos canales en centésimas de segundo.
-10 - Pan
-11 - Pitch
-*/
-
-// ToDo: Mejor inicializacion, primero folder, file, después params, ultimo playback.7
-// quitar CONTROL no usado
-#define VOLUME_COARSE 0
-#define PAN 1
-#define DMX_FOLDER 2
-#define DMX_FILE 3
-#define PLAYBACK 4
-#define CONTROL 5
-#define VOLUME_FINE 6
-#define ENTRY_POINT_COARSE 7
-#define ENTRY_POINT_FINE 8
-#define PITCH 9
-
-#define LAYER_CHANNELS 10
+#define VOLUME_COARSE 3
+#define PAN 6
+#define DMX_FOLDER 0
+#define DMX_FILE 1
+#define PLAYBACK 8
+#define VOLUME_FINE 2
+#define ENTRY_POINT_COARSE 5
+#define ENTRY_POINT_FINE 4
+#define PITCH 7
+#define LAYER_CHANNELS 9
#endif // DMXPERSONALITY_H
diff --git a/src/dmxwidget.cpp b/src/dmxwidget.cpp
index 60d4206..c5b4150 100644
--- a/src/dmxwidget.cpp
+++ b/src/dmxwidget.cpp
@@ -9,6 +9,8 @@ dmxWidget::dmxWidget(QWidget *parent) :
QVBoxLayout *vbox = new QVBoxLayout;
m_receiveDMX->setText("DMX Signal");
vbox->addWidget(m_receiveDMX);
+ vbox->setSpacing(1);
+ vbox->setContentsMargins(1, 1, 1, 1);
this->setLayout(vbox);
connect(m_watchDMX, SIGNAL(timeout()),
this, SLOT(watchDMXExpired()));
diff --git a/src/libremediaserver-audio-gui.cpp b/src/libremediaserver-audio-gui.cpp
new file mode 100644
index 0000000..b769679
--- /dev/null
+++ b/src/libremediaserver-audio-gui.cpp
@@ -0,0 +1,56 @@
+/*
+
+ Libre Media Server Audio - An Open source Media Server for arts and performing.
+ (c) Criptomart - Santiago Noreña 2012-2024
+ 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 .
+*/
+
+#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(5, 5, 5, 5);
+ this->setStyleSheet(
+ "color: white;"
+ "background-color: #4f4048;"
+ "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();
+}
diff --git a/src/libremediaserver-audio-gui.h b/src/libremediaserver-audio-gui.h
new file mode 100644
index 0000000..50b4d8b
--- /dev/null
+++ b/src/libremediaserver-audio-gui.h
@@ -0,0 +1,48 @@
+/*
+ Libre Media Server Audio - An Open source Media Server for arts and performing.
+ (c) Criptomart - Santiago Noreña 2012-2024
+ 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 .
+*/
+
+#ifndef LIBREMEDIASERVERAUDIOUI_H
+#define LIBREMEDIASERVERAUDIOUI_H
+
+#include
+#include
+
+#include "audiowidget.h"
+#include "dmxwidget.h"
+#include "defines.h"
+#include "ui_libremediaserver-audio-gui.h"
+
+class libreMediaServerAudioUi : public QMainWindow
+{
+ Q_OBJECT
+
+public:
+ libreMediaServerAudioUi(QWidget *parent = 0);
+ virtual ~libreMediaServerAudioUi();
+ AudioWidget *m_aw;
+ dmxWidget *m_dmxWidget;
+
+private:
+ Ui::LibreMediaServerAudio ui;
+
+private slots:
+ void olasetup();
+};
+
+#endif // LIBREMEDIASERVERAUDIOUI_H
diff --git a/src/libremediaserver-audio-gui.ui b/src/libremediaserver-audio-gui.ui
new file mode 100644
index 0000000..c0f11ad
--- /dev/null
+++ b/src/libremediaserver-audio-gui.ui
@@ -0,0 +1,55 @@
+
+
+ Santi Noreña lms@criptomart.net
+ LibreMediaServerAudio
+
+
+
+ 0
+ 0
+ 500
+ 400
+
+
+
+
+ Unifont
+ 12
+ 75
+ true
+
+
+
+ LibreMediaServer
+
+
+
+ ../../../../criptomart/artwork/logo_v2_criptomart.net.png../../../../criptomart/artwork/logo_v2_criptomart.net.png
+
+
+
+
+
+ OLA Setup
+
+
+
+
+
+
diff --git a/src/libremediaserver-audio.cpp b/src/libremediaserver-audio.cpp
index 0d595ec..a9868fd 100644
--- a/src/libremediaserver-audio.cpp
+++ b/src/libremediaserver-audio.cpp
@@ -21,106 +21,239 @@
#include "libremediaserver-audio.h"
-libreMediaServerAudio::libreMediaServerAudio(QStringList args, QWidget *parent)
- : QMainWindow(parent)
+libreMediaServerAudio::libreMediaServerAudio()
{
- Q_UNUSED(args);
- qDebug() << VERSION;
- qDebug() << COPYRIGHT;
- qDebug() << LICENSE;
- ui.setupUi(this);
- this->setWindowTitle(VERSION);
- Settings *set = Settings::getInstance();
- set->readFile();
+ m_settings = Settings::getInstance();
+ m_settings->readFile();
+ m_ui = m_settings->getShowUi();
m_mediaLibrary = new MediaLibrary;
m_mediaLibrary->initMediaLibrary();
- m_aw = new AudioWidget;
- setCentralWidget(m_aw);
- m_dmxWidget = new dmxWidget(this);
- QDockWidget *topWidget = new QDockWidget(tr("Master"), this);
- topWidget->setAllowedAreas(Qt::TopDockWidgetArea);
- topWidget->setWidget(m_dmxWidget);
- addDockWidget(Qt::TopDockWidgetArea, topWidget);
- m_ola = new olaThread(this, set->getLayersNumber());
+ for (int i = 0; i < MAX_LAYERS; i++) {
+ m_currentMedia[i] = "";
+ m_currentStatus[i] = Status::Iddle;
+#ifdef NOGUI
+ m_updateUi[i][0] = -1;
+ m_updateUi[i][1] = -1;
+ m_updateUi[i][2] = -1;
+ m_updateUi[i][3] = -1;
+#endif
+ }
+ m_ola = new olaThread(this, m_settings->getLayersNumber());
Q_CHECK_PTR(m_ola);
m_ola->blockSignals(true);
- connect(m_ola, SIGNAL (universeReceived(int)), m_dmxWidget, SLOT(updateWatchDMX(int)));
- connect(m_ola, SIGNAL(dmxOutput(int, int, int)), this, SLOT(dmxInput(int, int, int)));
m_ola->registerUniverse();
- connect(ui.actionLaunch_OLA_Setup, SIGNAL(triggered()), this, SLOT(olasetup()));
- m_aw->startEngine();
- qDebug("Init Complete.");
+ m_mae.startEngine(m_settings->getAudioDeviceId());
+ qDebug("Core init Complete. Start reading DMX.");
m_ola->blockSignals(false);
+#ifdef NOGUI
m_ola->start(QThread::TimeCriticalPriority );
+#endif
}
libreMediaServerAudio::~libreMediaServerAudio()
{
m_ola->stop();
- m_aw->stopEngine();
+ m_mae.stopEngine();
}
-void libreMediaServerAudio::olasetup()
+void libreMediaServerAudio::loadMedia(int layer, int folder, int file)
{
- QWebView *view = new QWebView();
- view->load(QUrl("http://localhost:9090/ola.html"));
- view->show();
+ QString mediaFile = m_mediaLibrary->requestNewFile(folder, file);
+ if (strcmp(mediaFile.toLatin1().constData(), m_currentMedia[layer].toLatin1().constData()) == 0)
+ return;
+ if (QFile::exists(mediaFile)){
+ m_mae.loadMedia(layer, mediaFile.toLatin1().data());
+ m_currentMedia[layer] = mediaFile;
+#ifndef NOGUI
+ if (m_ui)
+ m_lmsUi->m_aw->mediaLoaded(layer, mediaFile, m_mae.getDuration(layer));
+#endif
+ m_mae.printFormatInfo(layer);
+ }
+ if (m_currentStatus[layer] == Status::PlayingFolder \
+ || (m_currentStatus[layer] == Status::PlayingFolderLoop)\
+ || (m_currentStatus[layer] == Status::PlayingFolderRandom)) {
+ m_played.append(file);
+ } else if (m_currentStatus[layer] == Status::PlayingOnce \
+ || m_currentStatus[layer] == Status::PlayingLoop) {
+ m_played.clear();
+ }
}
void libreMediaServerAudio::dmxInput(int layer, int channel, int value)
{
- if (layer > LAYER_CHANNELS)
+ if (layer >= MAX_LAYERS || channel >= LAYER_CHANNELS)
return;
QString mediaFile = NULL;
int aux;
- switch(channel){
- case DMX_FOLDER:
- aux = m_ola->getValue(layer, DMX_FILE);
- mediaFile = m_mediaLibrary->requestNewFile(value, aux);
- if (QFile::exists(mediaFile))
- m_aw->mediaLoaded(layer, mediaFile);
- break;
- case DMX_FILE:
- aux = m_ola->getValue(layer, DMX_FOLDER);
- mediaFile = m_mediaLibrary->requestNewFile(aux, value);
- if (QFile::exists(mediaFile))
- m_aw->mediaLoaded(layer, mediaFile);
- break;
- case VOLUME_COARSE:
- case VOLUME_FINE:
- m_aw->volChanged(layer, (value / 65025.0f));
- break;
- case PAN:
- m_aw->panChanged(layer, value);
- break;
- case PITCH:
- m_aw->pitchChanged(layer, value);
- break;
- case ENTRY_POINT_COARSE:
- case ENTRY_POINT_FINE:
- m_aw->entryPointChanged(layer, value);
- break;
- case PLAYBACK:
- if (value == 0)
- break;
+ if (channel == VOLUME_COARSE || channel == VOLUME_FINE) {
+ float tmp = value / 65025.0f;
+ m_mae.volChanged(layer, tmp);
+ m_updateUi[layer][0] = tmp * 100.0f;
+ } else if (channel == PAN) {
+ m_mae.panChanged(layer, value);
+ m_updateUi[layer][1] = value;
+ } else if (channel == PITCH) {
+ m_mae.pitchChanged(layer, value);
+ m_updateUi[layer][2] = value;
+ } else if (channel == ENTRY_POINT_COARSE || channel == ENTRY_POINT_FINE) {
+ m_mae.setCursor(layer, value);
+ m_updateUi[layer][3] = value;
+ } else if (channel == PLAYBACK && value > 0) {
aux = value / 25;
- switch (aux) {
- case 0 :
- m_aw->playbackChanged(layer, PlayingOnce);
- break;
- case 1 :
- m_aw->playbackChanged(layer, Stopped);
- break;
- case 2 :
- m_aw->playbackChanged(layer, Paused);
- break;
- case 3 :
- m_aw->playbackChanged(layer, PlayingLoop);
- break;
- default :
- break;
+ Status s = m_currentStatus[layer];
+ if (aux == 0)
+ s = Status::PlayingOnce;
+ else if (aux == 1)
+ s = Status::Stopped;
+ else if (aux == 2)
+ s = Status::Paused;
+ else if (aux == 3)
+ s = Status::PlayingLoop;
+ else if (aux == 4)
+ s = Status::PlayingFolder;
+ else if (aux == 5)
+ s = Status::PlayingFolderLoop;
+ else if (aux == 6)
+ s = Status::PlayingFolderRandom;
+ m_mae.playbackChanged(layer, s);
+ m_currentStatus[layer] = s;
+ qInfo() << "Layer" << layer << StatusStr[s];
+#ifndef NOGUI
+ if (m_ui) {
+ m_lmsUi->m_aw->playbackChanged(layer, s);
+ //m_lmsUi->m_aw->cursorChanged(layer, m_mae.getCursor(layer));
}
- default:
+#endif
+ }
+}
+#ifndef NOGUI
+void libreMediaServerAudio::refreshUi() {
+ if (!m_ui) return;
+ for (int i= 0; i < m_settings->getLayersNumber(); i++ ) {
+ if (m_updateUi[i][0] >= 0) {
+ m_lmsUi->m_aw->volChanged(i, m_updateUi[i][0]);
+ m_updateUi[i][0] = -1;
+ }
+ if (m_updateUi[i][1] >= 0) {
+ m_lmsUi->m_aw->panChanged(i, m_updateUi[i][1]);
+ m_updateUi[i][1] = -1;
+ }
+ if (m_updateUi[i][2] >= 0) {
+ m_lmsUi->m_aw->pitchChanged(i, m_updateUi[i][2]);
+ m_updateUi[i][2] = -1;
+ }
+ if (m_updateUi[i][3] >= 0 \
+ || m_currentStatus[i] == Status::PlayingOnce\
+ || m_currentStatus[i] == Status::PlayingLoop\
+ || m_currentStatus[i] == Status::PlayingFolder\
+ || m_currentStatus[i] == Status::PlayingFolderLoop
+ || m_currentStatus[i] == Status::PlayingFolderRandom) {
+ m_lmsUi->m_aw->cursorChanged(i, m_mae.getCursor(i));
+ m_updateUi[i][3] = -1;
+ }
+ if (m_mae.getAtEnd(i)) {
+ if (m_played.isEmpty())
+ m_played.append(m_ola->getValue(i, DMX_FILE));
+ if (m_currentStatus[i] == Status::PlayingOnce) {
+ m_currentStatus[i] = Status::Stopped;
+ }
+ if (m_currentStatus[i] == Status::PlayingFolder) {
+ uint last = m_played.last();
+ int folder = m_ola->getValue(i, DMX_FOLDER);
+ last++;
+ if (last < m_mediaLibrary->getMediaFolderCount(folder)) {
+ this->loadMedia(i, folder, last);
+ m_mae.playbackChanged(i, Status::PlayingFolder);
+ }
+ else {
+ m_currentStatus[i] = Status::Stopped;
+ m_lmsUi->m_aw->playbackChanged(i, Status::Stopped);
+ }
+ }
+ if (m_currentStatus[i] == Status::PlayingFolderLoop) {
+ uint last = m_played.last();
+ int folder = m_ola->getValue(i, DMX_FOLDER);
+ last++;
+ if (last >= m_mediaLibrary->getMediaFolderCount(folder)) {
+ this->loadMedia(i, folder, 0);
+ m_mae.playbackChanged(i, Status::PlayingFolderLoop);
+ } else {
+ this->loadMedia(i, folder, last);
+ m_mae.playbackChanged(i, Status::PlayingFolder);
+ }
+ }
+ if (m_currentStatus[i] == Status::PlayingFolderRandom) {
+ int last = -1;
+ int folder = m_ola->getValue(i, DMX_FOLDER);
+ if (uint(abs(m_played.size())) >= m_mediaLibrary->getMediaFolderCount(folder))
+ m_played.clear();
+ while (last == -1) {
+ last = rand() % m_mediaLibrary->getMediaFolderCount(folder);
+ if (m_played.contains(last))
+ last = -1;
+ }
+ this->loadMedia(i, folder, last);
+ m_mae.playbackChanged(i, Status::PlayingFolderRandom);
+ }
+ }
+ }
+}
+
+void libreMediaServerAudio::setUi(libreMediaServerAudioUi *lmsUi)
+{
+ m_lmsUi = lmsUi;
+ 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(uiPlaybackChanged(int, Status)), this, SLOT(uiPlaybackChanged(int, Status)));
+ connect(m_lmsUi->m_aw, SIGNAL(uiLoadMedia(int, QString)), this, SLOT(uiLoadMedia(int, QString)));
+ m_refreshUi = new QTimer(this);
+ connect(m_refreshUi, SIGNAL(timeout()), this, SLOT(refreshUi()));
+ m_refreshUi->start(UI_REFRESH_TIME);
+ m_ola->start(QThread::TimeCriticalPriority );
+};
+
+// From Ui widgets
+void libreMediaServerAudio::uiSliderChanged(int layer, Slider s, int value)
+{
+ switch (s){
+ case Slider::Volume:
+ m_mae.volChanged(layer, float((value / 100.0f)));
+ break;
+ case Slider::Pan:
+ m_mae.panChanged(layer, value);
+ break;
+ case Slider::Pitch:
+ m_mae.pitchChanged(layer, value);
break;
}
}
+
+void libreMediaServerAudio::uiPlaybackChanged(int layer, Status s)
+{
+ ma_result result;
+
+ result = m_mae.playbackChanged(layer, s);
+ if (result == MA_SUCCESS) {
+ m_currentStatus[layer] = s;
+ } else {
+ qWarning() << "ui playback change error" << result << "status" << s << "layer" << layer;
+ }
+}
+
+void libreMediaServerAudio::uiLoadMedia(int layer, QString mediaFile)
+{
+ ma_result result;
+
+ if (strcmp(mediaFile.toLatin1().constData(), m_currentMedia[layer].toLatin1().constData()) == 0)
+ return;
+ result = m_mae.loadMedia(layer, mediaFile.toLatin1().data());
+ if (result == MA_SUCCESS) {
+ m_currentMedia[layer] = mediaFile;
+ m_lmsUi->m_aw->mediaLoaded(layer, mediaFile, m_mae.getDuration(layer));
+ } else {
+ qWarning() << "ui load media error" << result << "file" << mediaFile << "layer" << layer;
+ }
+}
+#endif
diff --git a/src/libremediaserver-audio.h b/src/libremediaserver-audio.h
index 9cd154b..d83446a 100644
--- a/src/libremediaserver-audio.h
+++ b/src/libremediaserver-audio.h
@@ -20,35 +20,51 @@
#ifndef LIBREMEDIASERVERAUDIO_H
#define LIBREMEDIASERVERAUDIO_H
-#include
-#include
-
-#include "audiowidget.h"
#include "medialibrary.h"
+#include "miniaudioengine.h"
#include "olathread.h"
#include "settings.h"
-#include "dmxwidget.h"
#include "defines.h"
-#include "ui_libremediaserver-audio.h"
+#ifndef NOGUI
+#include "libremediaserver-audio-gui.h"
+#endif
-class libreMediaServerAudio : public QMainWindow
+class libreMediaServerAudio : public QObject
{
Q_OBJECT
public:
- libreMediaServerAudio (QStringList args, QWidget *parent = 0);
+ libreMediaServerAudio();
virtual ~libreMediaServerAudio();
- Ui::LibreMediaServerAudio ui;
+ void dmxInput(int layer, int channel, int value);
+ void loadMedia(int layer, int folder, int file);
+#ifndef NOGUI
+ void setUi(libreMediaServerAudioUi *lmsUi);
+ bool inline getShowUi() { return m_settings->getShowUi(); }
+#endif
private:
- AudioWidget *m_aw;
- dmxWidget *m_dmxWidget;
olaThread *m_ola;
MediaLibrary *m_mediaLibrary;
+ MiniAudioEngine m_mae;
+ Settings *m_settings;
+ QString m_currentMedia[MAX_LAYERS];
+ Status m_currentStatus[MAX_LAYERS];
+ QList m_dmxSettings;
+ bool m_ui;
+ QList m_played;
+#ifndef NOGUI
+ QTimer *m_refreshUi;
+ libreMediaServerAudioUi *m_lmsUi;
+ float m_updateUi[MAX_LAYERS][4];
private slots:
- void olasetup();
- void dmxInput(int layer, int channel, int value);
+ void refreshUi();
+ void uiSliderChanged(int layer, Slider s, int value);
+ void uiPlaybackChanged(int layer, Status s);
+ void uiLoadMedia(int layer, QString s);
+
+#endif
};
#endif // LIBREMEDIASERVERAUDIO_H
diff --git a/src/libremediaserver-audio.ui b/src/libremediaserver-audio.ui
deleted file mode 100644
index 6ade744..0000000
--- a/src/libremediaserver-audio.ui
+++ /dev/null
@@ -1,81 +0,0 @@
-
-
- Santi Noreña belfegor@gmail.com
- LibreMediaServerAudio
-
-
-
- 0
- 0
- 114
- 218
-
-
-
- LibreMediaServer
-
-
-
-
-
- Exit
-
-
-
-
- Open Configuration...
-
-
-
-
- Save Configuration...
-
-
-
-
- Settings...
-
-
-
-
- false
-
-
- Init
-
-
-
-
- IP Address
-
-
-
-
- Make Thumbs
-
-
-
-
- OLA Setup...
-
-
-
-
-
-
diff --git a/src/main.cpp b/src/main.cpp
index bfadafe..79b7a91 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -18,34 +18,28 @@
along with this program. If not, see .
*/
-#include "libremediaserver-audio.h"
+#include "main.h"
+bool hasUi(int &argc, char *argv[])
+{
+ for (int i = 1; i < argc; ++i) {
+ if (!strcmp(argv[i], "--gui"))
+ return true;
+ }
+ return false;
+}
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
- QStringList args = app.arguments();
- if (args.size() > 1)
+ libreMediaServerAudio lms;
+#ifndef NOGUI
+ if (hasUi(argc, argv) || lms.getShowUi())
{
- if (args.contains("-v"))
- {
- qDebug() << VERSION;
- qDebug() << COPYRIGHT;
- qDebug() << LICENSE;
- return 0;
- }
- if (args.contains("-h"))
- {
- qDebug() << VERSION;
- qDebug() << COPYRIGHT;
- qDebug() << LICENSE;
- qDebug() << "Help for command line options:";
- qDebug() << "-v show the version and exits";
- qDebug() << "-h this help";
- return 0;
- }
+ libreMediaServerAudioUi *lmsUi = new libreMediaServerAudioUi();
+ lms.setUi(lmsUi);
+ lmsUi->show();
}
- libreMediaServerAudio libreMediaServerAudio(args);
- libreMediaServerAudio.show();
+#endif
return app.exec();
}
diff --git a/src/main.h b/src/main.h
new file mode 100644
index 0000000..1a96ff6
--- /dev/null
+++ b/src/main.h
@@ -0,0 +1,20 @@
+#ifndef MAIN_H
+#define MAIN_H
+
+#include
+#include
+
+#include "medialibrary.h"
+#include "olathread.h"
+#include "settings.h"
+#include "libremediaserver-audio.h"
+#include "libremediaserver-audio-gui.h"
+#include "defines.h"
+
+ olaThread *m_ola;
+ MediaLibrary *m_mediaLibrary;
+
+ // slots
+ void dmxInput(int layer, int channel, int value);
+
+#endif // MAIN_H
diff --git a/src/medialibrary.h b/src/medialibrary.h
index 5a0b55b..ea92bca 100644
--- a/src/medialibrary.h
+++ b/src/medialibrary.h
@@ -27,14 +27,11 @@ class MediaLibrary : public QObject
public:
MediaLibrary(QObject *parent = 0);
- /**
- * @brief request a new file from the media library
- * @param int folder - the folder required
- * @param int layer - file required
- * @return QString the file required with full path
- */
QString requestNewFile(int folder, int layer);
void initMediaLibrary();
+ inline uint getMediaFolderCount(int folder) {
+ return m_media->at(folder).m_ElementCount;
+ }
private:
QList *m_media;
diff --git a/src/miniaudioengine.cpp b/src/miniaudioengine.cpp
index 6d5490c..9204636 100644
--- a/src/miniaudioengine.cpp
+++ b/src/miniaudioengine.cpp
@@ -1,8 +1,15 @@
#include "miniaudioengine.h"
-
+#include //enum macro
MiniAudioEngine::MiniAudioEngine()
{
-
+ for (int i =0; i < MAX_LAYERS; i++) {
+ m_mediaLoaded[i] = false;
+ m_currentLayerValues[i].status = Status::Iddle;
+ m_currentLayerValues[i].pan = 128;
+ m_currentLayerValues[i].pitch = 128;
+ m_currentLayerValues[i].vol = 0;
+ m_currentLayerValues[i].cursor = 0;
+ }
}
void MiniAudioEngine::audioDataCallback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount)
@@ -20,25 +27,26 @@ void MiniAudioEngine::stopEngine()
ma_resource_manager_uninit(&resourceManager);
}
-bool MiniAudioEngine::startEngine(int n)
+bool MiniAudioEngine::startEngine(uint n)
{
ma_result result;
result = this->startContext();
- if (result != MA_SUCCESS) {
- return result;
- }
- this->getAllAudioDevices();
+ if (result != MA_SUCCESS) return result;
+ result = this->getAllAudioDevices();
+ if (result != MA_SUCCESS) return result;
result = this->startDevice(n);
return result;
}
-ma_result MiniAudioEngine::startDevice(int id)
+ma_result MiniAudioEngine::startDevice(uint id)
{
ma_result result;
ma_device_config deviceConfig;
ma_engine_config engineConfig;
+ if (id >= playbackDeviceCount)
+ id = playbackDeviceCount - 1;
deviceConfig = ma_device_config_init(ma_device_type_playback);
deviceConfig.playback.pDeviceID = &pPlaybackDeviceInfos[id].id;
deviceConfig.playback.format = resourceManager.config.decodedFormat;
@@ -66,7 +74,7 @@ ma_result MiniAudioEngine::startDevice(int id)
return result;
}
iChosenDevice = id;
- qInfo("Initialized audio device %d: %s", id, pPlaybackDeviceInfos[id].name);
+ qInfo("Initialized audio device %d : %s", id, pPlaybackDeviceInfos[id].name);
return result;
}
@@ -77,8 +85,8 @@ ma_result MiniAudioEngine::startContext()
resourceManagerConfig = ma_resource_manager_config_init();
resourceManagerConfig.decodedFormat = ma_format_f32; /* ma_format_f32 should almost always be used as that's what the engine (and most everything else) uses for mixing. */
resourceManagerConfig.decodedChannels = 0;
- resourceManagerConfig.decodedSampleRate = 0;
- resourceManagerConfig.jobThreadCount = 0;
+ resourceManagerConfig.decodedSampleRate = ma_standard_sample_rate_48000;
+ resourceManagerConfig.jobThreadCount = 4;
result = ma_resource_manager_init(&resourceManagerConfig, &resourceManager);
if (result != MA_SUCCESS) {
qCritical("Failed to initialize audio resource manager.");
@@ -102,7 +110,7 @@ ma_result MiniAudioEngine::getAllAudioDevices()
ma_context_uninit(&context);
return result;
}
- printf("Audio devices detected in system:\n");
+ printf("Audio devices available:\n");
for (ma_uint32 iAvailableDevice = 0; iAvailableDevice < playbackDeviceCount; iAvailableDevice += 1) {
qInfo("%d: : %s", iAvailableDevice, pPlaybackDeviceInfos[iAvailableDevice].name);
}
@@ -115,21 +123,20 @@ ma_result MiniAudioEngine::loadMedia(int layer, char *file)
if (m_mediaLoaded[layer] == true)
{
- qInfo("removing sound %i", layer);
ma_sound_uninit(&m_currentSound[layer]);
m_mediaLoaded[layer] = false;
}
result = ma_sound_init_from_file(&engine, file, \
MA_SOUND_FLAG_NO_SPATIALIZATION \
- /*| MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE \
- | MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC \
- | MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM \*/
+ | MA_SOUND_FLAG_DECODE \
+ /*| MA_SOUND_FLAG_NO_PITCH \*/
, NULL, NULL, &m_currentSound[layer]);
if (result != MA_SUCCESS)
qWarning("Failed to load file %s", file);
else {
m_mediaLoaded[layer] = true;
- this->volChanged(layer, 0);
+ this->refreshValues(layer);
+ m_currentLayerValues[layer].media = file;
}
return result;
}
@@ -137,16 +144,21 @@ ma_result MiniAudioEngine::loadMedia(int layer, char *file)
float MiniAudioEngine::getDuration(int layer)
{
ma_result result;
- float ret = 0;
+ ma_uint64 lengthInPCMFrames;
+ ma_uint32 sampleRate;
+ float ret;
if (m_mediaLoaded[layer] == false)
return MA_DOES_NOT_EXIST;
- result = ma_sound_get_length_in_seconds(&m_currentSound[layer], &ret);
- if (result != MA_SUCCESS)
- {
- qWarning("Can not get duration %i", layer);
- ret = 0;
+ result = ma_sound_get_length_in_pcm_frames(&m_currentSound[layer], &lengthInPCMFrames);
+ if (result != MA_SUCCESS) {
+ return result;
}
+ result = ma_sound_get_data_format(&m_currentSound[layer], NULL, NULL, &sampleRate, NULL, 0);
+ if (result != MA_SUCCESS) {
+ return MA_ERROR;
+ }
+ ret = 1000.0f * (lengthInPCMFrames / float(sampleRate));
return ret;
}
@@ -160,8 +172,8 @@ float MiniAudioEngine::getCursor(int layer)
result = ma_sound_get_cursor_in_seconds(&m_currentSound[layer], &ret);
if (result != MA_SUCCESS)
{
- qWarning("Can not get cursor %i", layer);
- ret = 0;
+ qWarning("%i can not get cursor error %i", layer, result);
+ ret = MA_ERROR;
}
return ret;
}
@@ -175,11 +187,10 @@ ma_result MiniAudioEngine::printFormatInfo(int layer)
if (m_mediaLoaded[layer] == false)
return MA_DOES_NOT_EXIST;
ma_result result = ma_sound_get_data_format(&m_currentSound[layer], &format, &channels, &sampleRate, NULL, 0);
- if (result != MA_SUCCESS) {
- qWarning("Failed to get data format %i\n", layer);
- return MA_INVALID_DATA;
- }
- qInfo("samples/sec: %u format: %u channels: %u", sampleRate, format, channels);
+ if (result != MA_SUCCESS)
+ qWarning("%i failed to get data format %i\n", layer, result);
+ else
+ qInfo() << "Layer:" << layer << m_currentLayerValues[layer].media << "samples/sec:" << sampleRate << "format:" << format << "channels:" << channels;
return result;
}
@@ -188,7 +199,10 @@ void MiniAudioEngine::volChanged(int layer, float vol)
{
if (m_mediaLoaded[layer] == false)
return;
- ma_sound_group_set_volume(&m_currentSound[layer], vol);
+ if (vol >= 1)
+ vol = 0.99f;
+ ma_sound_group_set_fade_in_milliseconds(&m_currentSound[layer], -1, vol, FADE_TIME);
+ m_currentLayerValues[layer].vol = vol;
}
void MiniAudioEngine::panChanged(int layer, float value)
@@ -199,48 +213,90 @@ void MiniAudioEngine::panChanged(int layer, float value)
return;
result = (value / 128.0) - 1.0;
ma_sound_group_set_pan(&m_currentSound[layer], result);
+ m_currentLayerValues[layer].pan = value;
}
void MiniAudioEngine::pitchChanged(int layer, float value)
{
- float result;
+ float pitch;
if (m_mediaLoaded[layer] == false)
return;
- result = value / 128.0;
- ma_sound_group_set_pitch(&m_currentSound[layer], result);
+ pitch = value / 128.0;
+ ma_sound_group_set_pitch(&m_currentSound[layer], pitch);
+ m_currentLayerValues[layer].pitch = value;
}
-void MiniAudioEngine::playbackChanged(int layer, Status status)
+ma_result MiniAudioEngine::playbackChanged(int layer, Status status)
{
+ ma_result result = MA_SUCCESS;
+
if (m_mediaLoaded[layer] == false)
- return;
+ return MA_DOES_NOT_EXIST;
switch (status) {
case Status::Paused:
- ma_sound_stop(&m_currentSound[layer]);
+ result = ma_sound_stop_with_fade_in_milliseconds(&m_currentSound[layer], FADE_TIME);
break;
case Status::Stopped:
- ma_sound_stop(&m_currentSound[layer]);
- ma_sound_seek_to_pcm_frame(&m_currentSound[layer], 0);
+ result = ma_sound_stop_with_fade_in_milliseconds(&m_currentSound[layer], FADE_TIME);
+ result = this->seekToCursor(layer, m_currentLayerValues[layer].cursor);
break;
case Status::PlayingLoop:
+ ma_sound_set_stop_time_in_milliseconds(&m_currentSound[layer], ~(ma_uint64)0);
ma_sound_set_looping(&m_currentSound[layer], true);
- ma_sound_start(&m_currentSound[layer]);
- break;
+ result = ma_sound_start(&m_currentSound[layer]);
+ break;
case Status::PlayingOnce:
+ case Status::PlayingFolder:
+ case Status::PlayingFolderLoop:
+ case Status::PlayingFolderRandom:
+ ma_sound_set_stop_time_in_milliseconds(&m_currentSound[layer], ~(ma_uint64)0);
ma_sound_set_looping(&m_currentSound[layer], false);
- ma_sound_start(&m_currentSound[layer]);
+ result = ma_sound_start(&m_currentSound[layer]);
+ break;
+ default:
break;
}
+ if (result == MA_SUCCESS)
+ m_currentLayerValues[layer].status = status;
+ return result;
}
-void MiniAudioEngine::setCursor(int layer, int cursor)
+ma_result MiniAudioEngine::seekToCursor(int layer, int cursor)
{
- ma_uint64 f;
+ ma_result result = MA_SUCCESS;
+ ma_uint64 end, start;
if (m_mediaLoaded[layer] == false)
- return;
- ma_sound_get_length_in_pcm_frames(&m_currentSound[layer], &f);
- f = (cursor * f) / 65025;
- ma_sound_seek_to_pcm_frame(&m_currentSound[layer], f);
+ return MA_DOES_NOT_EXIST;
+ result = ma_sound_get_length_in_pcm_frames(&m_currentSound[layer], &end);
+ if (result != MA_SUCCESS) { return result; }
+ start = (cursor * end) / 65025;
+ result = ma_sound_seek_to_pcm_frame(&m_currentSound[layer], start);
+ //if (result != MA_SUCCESS) { return result; }
+ //result = ma_data_source_set_loop_point_in_pcm_frames(&m_currentSound[layer], start, end);
+ return (result);
+}
+
+ma_result MiniAudioEngine::setCursor(int layer, int cursor)
+{
+ ma_result result = MA_SUCCESS;
+
+ m_currentLayerValues[layer].cursor = cursor;
+ result = this->seekToCursor(layer, cursor);
+ return (result);
+}
+
+Status MiniAudioEngine::getStatus(int layer)
+{
+ return m_currentLayerValues[layer].status;
+}
+
+void MiniAudioEngine::refreshValues(int layer)
+{
+ this->seekToCursor(layer, m_currentLayerValues[layer].cursor);
+ this->panChanged(layer, m_currentLayerValues[layer].pan);
+ this->volChanged(layer, m_currentLayerValues[layer].vol);
+ this->pitchChanged(layer, m_currentLayerValues[layer].pitch);
+ this->playbackChanged(layer, m_currentLayerValues[layer].status);
}
diff --git a/src/miniaudioengine.h b/src/miniaudioengine.h
index 9549602..6e1393c 100644
--- a/src/miniaudioengine.h
+++ b/src/miniaudioengine.h
@@ -5,15 +5,16 @@
#include "miniaudio.h"
#include "defines.h" // MAX_LAYERS
#include // prints messages
+#define MA_DEBUG_OUTPUT
class MiniAudioEngine
{
- friend class AudioWidget;
+ friend class libreMediaServerAudio;
public:
MiniAudioEngine();
void stopEngine();
- bool startEngine(int id);
+ bool startEngine(uint id);
static void audioDataCallback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount);
protected:
@@ -21,11 +22,15 @@ protected:
void volChanged(int layer, float vol);
void panChanged(int layer, float pan);
void pitchChanged(int layer, float pitch);
- void playbackChanged(int layer, Status status);
+ ma_result playbackChanged(int layer, Status status);
+ ma_result setCursor(int layer, int cursor);
+ ma_result printFormatInfo(int layer);
float getDuration(int layer);
float getCursor(int layer);
- void setCursor(int layer, int cursor);
- ma_result printFormatInfo(int layer);
+ Status getStatus(int layer);
+ inline float getVol(int layer) {
+ return ma_sound_get_volume(&m_currentSound[layer]); }
+ inline bool getAtEnd(int layer) { return m_currentSound[layer].atEnd; }
private:
ma_resource_manager_config resourceManagerConfig;
@@ -38,10 +43,13 @@ private:
ma_context context;
ma_sound m_currentSound[MAX_LAYERS];
ma_bool8 m_mediaLoaded[MAX_LAYERS];
+ layerData m_currentLayerValues[MAX_LAYERS];
ma_result getAllAudioDevices();
- ma_result startDevice(int id);
+ ma_result startDevice(uint id);
ma_result startContext();
+ void refreshValues(int layer);
+ ma_result seekToCursor(int layer, int cursor);
};
#endif // MINIAUDIOENGINE_H
diff --git a/src/olathread.cpp b/src/olathread.cpp
index ed0998b..6ca9325 100644
--- a/src/olathread.cpp
+++ b/src/olathread.cpp
@@ -1,11 +1,10 @@
-#include "olathread.h"
-
+#include "libremediaserver-audio.h"
olaThread::olaThread(QObject *parent, int layers) :
m_counter(0)
, m_layers(layers)
{
- Q_UNUSED(parent);
+ this->setParent(parent);
gettimeofday(&m_last_data, NULL);
for (int i=0; i < MAX_LAYERS; i++)
{
@@ -63,42 +62,65 @@ void olaThread::stop()
void olaThread::NewDmx(const ola::client::DMXMetadata &data,
const ola::DmxBuffer &buffer)
{
- bool volSent = false;
- bool entrySent = false;
-
foreach (const dmxSetting &i, m_dmxSettings) {
if(i.universe == data.universe && i.address > -1) {
+ bool volSent = false;
+ bool entrySent = false;
+ bool fileSent = false;
+ int aux;
for (int j = 0; j < LAYER_CHANNELS; j++){
int value = buffer.Get((i.address) + j);
if (m_dmx[i.layer][j] != value) {
m_dmx[i.layer][j] = value;
switch (j) {
- case VOLUME_COARSE:
- value = (value * 0x100) + buffer.Get(i.address + VOLUME_FINE);
- emit dmxOutput(i.layer,j,value);
- volSent = true;
+ case DMX_FOLDER:
+ aux = buffer.Get(i.address + DMX_FILE);
+ qobject_cast(parent())->loadMedia(i.layer, value, aux);
+ m_dmx[i.layer][DMX_FILE] = aux;
+ fileSent = true;
break;
- case ENTRY_POINT_COARSE:
- value = (value * 0x100) + buffer.Get(i.address + ENTRY_POINT_FINE);
- emit dmxOutput(i.layer,j,value);
- entrySent = true;
+ case DMX_FILE:
+ if (fileSent)
+ break;
+ aux = buffer.Get(i.address + DMX_FOLDER);
+ qobject_cast(parent())->loadMedia(i.layer, aux, value);
+ m_dmx[i.layer][DMX_FOLDER] = aux;
+ fileSent = true;
break;
case VOLUME_FINE:
- if (volSent == false)
- {
- value = (buffer.Get(i.address + VOLUME_COARSE) * 0x100) + value;
- emit dmxOutput(i.layer,j,value);
- }
+ aux = buffer.Get(i.address + VOLUME_COARSE);
+ value += (aux * 0x100);
+ qobject_cast(parent())->dmxInput(i.layer, j, value);
+ m_dmx[i.layer][VOLUME_COARSE] = aux;
+ volSent = true;
+ break;
+ case VOLUME_COARSE:
+ if (volSent)
+ break;
+ value = (value * 0x100) + buffer.Get(i.address + VOLUME_FINE);
+ qobject_cast(parent())->dmxInput(i.layer, j, value);
+ m_dmx[i.layer][VOLUME_FINE] = buffer.Get(i.address + VOLUME_FINE);
+ volSent = true;
+ break;
+ case PLAYBACK:
+ qobject_cast(parent())->dmxInput(i.layer, j, value);
break;
case ENTRY_POINT_FINE:
- if (entrySent == false)
- {
- value = (buffer.Get(i.address + ENTRY_POINT_COARSE) * 0x100) + value;
- emit dmxOutput(i.layer,j,value);
- }
+ value = (buffer.Get(i.address + ENTRY_POINT_COARSE) * 0x100) + value;
+ qobject_cast(parent())->dmxInput(i.layer, j, value);
+ m_dmx[i.layer][ENTRY_POINT_COARSE] = buffer.Get(i.address + ENTRY_POINT_COARSE);
+ entrySent = true;
+ break;
+ case ENTRY_POINT_COARSE:
+ if (entrySent)
+ break;
+ value = (value * 0x100) + buffer.Get(i.address + ENTRY_POINT_FINE);
+ qobject_cast(parent())->dmxInput(i.layer, j, value);
+ m_dmx[i.layer][ENTRY_POINT_FINE] = buffer.Get(i.address + ENTRY_POINT_FINE);
+ entrySent = true;
break;
default:
- emit dmxOutput(i.layer,j,value);
+ qobject_cast(parent())->dmxInput(i.layer, j, value);
break;
}
}
@@ -127,7 +149,7 @@ bool olaThread::CheckDataLoss() {
void olaThread::socketClosed()
{
- qWarning("ola closed connection. Try reopening it... ");
+ qWarning("ola daemon closed connection, reopening it... ");
m_clientWrapper->GetSelectServer()->Terminate();
m_client = NULL;
m_clientWrapper = NULL;
diff --git a/src/olathread.h b/src/olathread.h
index 91d78b6..ed2735a 100644
--- a/src/olathread.h
+++ b/src/olathread.h
@@ -20,10 +20,17 @@ class olaThread : public QThread
Q_OBJECT
public:
+ QList m_dmxSettings;
+ int m_dmx[MAX_LAYERS][LAYER_CHANNELS];
+ ola::client::OlaClientWrapper *m_clientWrapper;
+ ola::client::OlaClient *m_client;
+ unsigned int m_counter;
+ struct timeval m_last_data; // Last DMX frame received
+ int m_layers;
olaThread(QObject *parent = 0, int layers = 0);
virtual ~olaThread();
-
+ void run ();
/** Retorna el valor de un canal
*@param int layer the layer for we want the channel
*@param int channel the channel for the value wanted
@@ -35,14 +42,6 @@ public:
void resendDmx();
private:
- void run ();
- ola::client::OlaClientWrapper *m_clientWrapper;
- ola::client::OlaClient *m_client;
- unsigned int m_counter;
- struct timeval m_last_data; // Last DMX frame received
- int m_layers;
- int m_dmx[MAX_LAYERS][LAYER_CHANNELS];
- QList m_dmxSettings;
/**
* @brief Callback from ola. Control de errores en el registro de Universos en OLA
* typedef SingleUseCallback1 ola::client::SetCallback
@@ -53,7 +52,7 @@ private:
if (error.Success()) {
qDebug("Register Universe success");
} else {
- qWarning("Register command failed: %s", error.Error().c_str());
+ qCritical("Register command failed: %s", error.Error().c_str());
}
}
/**
@@ -67,7 +66,7 @@ private:
* This is called one for second if there is not updated in the DMX frame.
* emit only the channels that has been changed.
*/
- void NewDmx(const ola::client::DMXMetadata &dmx_meta, const ola::DmxBuffer &buffer); //
+ void NewDmx(const ola::client::DMXMetadata &dmx_meta, const ola::DmxBuffer &buffer);
/**
* @brief Sometimes the ola server closes the connection. This is a callback to handle this event an reconect to ola
*/
diff --git a/src/settings.cpp b/src/settings.cpp
index f664bb0..9d06394 100644
--- a/src/settings.cpp
+++ b/src/settings.cpp
@@ -15,6 +15,7 @@ Settings::Settings(QObject *parent) :
QObject(parent)
{
m_layersNumber = 0;
+ m_ui = false;
}
// Read the dmx settings for dmx.xml At the moment we need:
@@ -23,6 +24,7 @@ Settings::Settings(QObject *parent) :
// - The first DMX channel of each source/layer
// - The universe to bind in OLA
// - Audio device id
+// - Show the Ui or not
void Settings::readFromFile(QString file) {
QFile* xmlFile = new QFile(file);
if (!xmlFile->open(QIODevice::ReadOnly | QIODevice::Text)) {
@@ -34,53 +36,56 @@ void Settings::readFromFile(QString file) {
exit(1);
}
QXmlStreamReader* xmlReader = new QXmlStreamReader(xmlFile);
- int counter = 0;
- //Parse the XML until we reach end of it
- while(!xmlReader->atEnd() && !xmlReader->hasError() && counter < MAX_LAYERS) {
- // Read next element
+ while(!xmlReader->atEnd() && !xmlReader->hasError()) {
QXmlStreamReader::TokenType token = xmlReader->readNext();
- //If token is just StartDocument - go to next
if(token == QXmlStreamReader::StartDocument) {
continue;
}
- //If token is StartElement - read it
if(token == QXmlStreamReader::StartElement) {
- if(xmlReader->name() == "dmxSettings") {
- int version = xmlReader->attributes().value("fileVersion").toLocal8Bit().toInt();
- if(version == 1) {
- m_layersNumber = xmlReader->attributes().value("layersNumber").toLocal8Bit().toInt();
- m_pathmedia = xmlReader->attributes().value("path").toLocal8Bit();
- continue;
- }
- }
- if(xmlReader->name() == "audioDevice") {
- m_audioDeviceId = xmlReader->attributes().value("id").toLocal8Bit().toInt();
+ if(xmlReader->name() == "lmsAudio") {
+ m_ui = xmlReader->attributes().value("ui").toLocal8Bit().toInt();
+ m_layersNumber = xmlReader->attributes().value("layersNumber").toLocal8Bit().toInt();
+ m_pathmedia = xmlReader->attributes().value("path").toLocal8Bit();
continue;
}
- QString add = "layer";
- add.append(QString("%1").arg(counter));
- if((xmlReader->name() == add)) {
+ if(xmlReader->name() == "audioDevice") {
+ m_audioDeviceQty = xmlReader->attributes().value("devicesNumber").toLocal8Bit().toInt();
+ for (uint i = 0; i < m_audioDeviceQty; i++)
+ {
+ m_audioDeviceId[i] = xmlReader->attributes().value(QString("id%1").arg(i)).toLocal8Bit().toInt();
+ }
+
+ }
+ if(xmlReader->name() == "layer") {
dmxSetting temp;
temp.address = xmlReader->attributes().value("dmx").toLocal8Bit().toInt() - 1;
temp.universe = xmlReader->attributes().value("universe").toLocal8Bit().toInt();
- temp.layer = counter;
+ temp.layer = xmlReader->attributes().value("id").toLocal8Bit().toInt();
m_settings.append(temp);
if (!m_universe.contains(temp.universe)) {
m_universe.insert(temp.universe);
}
- counter++;
}
}
}
if(xmlReader->hasError()) {
QMessageBox::critical(NULL,"File xml Parse Error ", xmlReader->errorString(), QMessageBox::Ok);
qWarning("File xml Parse Error %s", xmlReader->errorString().toLatin1().constData());
- return;
+ // ToDo: manage this, open a dialog to load a new file.
}
xmlReader->clear();
xmlFile->close();
delete xmlReader;
delete xmlFile;
+ this->printSettings();
+}
+
+void Settings::printSettings() {
+ qInfo() << "Settings readed:\nShow Ui:" << m_ui << "Layers:" << m_layersNumber << "Path:" << m_pathmedia <<"Audio Device qty:" << m_audioDeviceQty;
+ for (uint i = 0; i < m_audioDeviceQty; i++)
+ qInfo() << "Audio device internal id:" << i << "system id:" << m_audioDeviceId[i];
+ for (int i = 0; i < m_layersNumber; i++)
+ qInfo() << "Layer:" << m_settings[i].layer << "Address:" << m_settings[i].address << "Universe:" << m_settings[i].universe;
}
void Settings::readFile() {
diff --git a/src/settings.h b/src/settings.h
index 8cd55bb..f27da6a 100644
--- a/src/settings.h
+++ b/src/settings.h
@@ -5,6 +5,7 @@
#include
#include
#include
+#include
#include "medialibrary.h"
#include "audiowidget.h"
@@ -15,25 +16,27 @@ class Settings : public QObject
Q_OBJECT
public:
+ Settings(QObject *parent = 0);
static Settings *getInstance();
inline QSet getUniverses() { return m_universe; }
inline QString getPathMedia() { return m_pathmedia; }
inline QList getDmxSettings() { return m_settings; }
inline int getLayersNumber() { return m_layersNumber; }
+ inline int getAudioDeviceId() { return m_audioDeviceId[0]; }
+ inline bool getShowUi() { return m_ui; }
void readFile();
- inline int getAudioDeviceId() { return m_audioDeviceId; }
+ void readFromFile(QString file);
+ void printSettings();
private:
static Settings *_instance;
QList m_settings;
QString m_pathmedia;
- uint m_audioDeviceId;
+ uint m_audioDeviceId[MAX_AUDIODEVICES];
+ uint m_audioDeviceQty;
QSet m_universe;
int m_layersNumber;
-
- explicit Settings(QObject *parent = 0);
- void readFromFile(QString file);
-
+ bool m_ui;
};
#endif // SETTINGS_H
diff --git a/src/slidergroup.cpp b/src/slidergroup.cpp
index e80a03f..9ef9680 100644
--- a/src/slidergroup.cpp
+++ b/src/slidergroup.cpp
@@ -1,45 +1,81 @@
#include "slidergroup.h"
-
-SliderGroup::SliderGroup(const QString &title, \
+#include
+SliderGroup::SliderGroup(QString name,
int min,
int max,
int decimals,
QWidget *parent)
- : QGroupBox(title, parent)
+ : QWidget(parent)
{
- this->setFlat(true);
- this->setTitle(title);
+ QVBoxLayout *layout = new QVBoxLayout;
+ layout->setAlignment(Qt::AlignHCenter);
+ layout->setContentsMargins(0, 0, 0, 0);
+ //this->setMaximumWidth(40);
slider = new QSlider(Qt::Orientation::Vertical);
slider->setFocusPolicy(Qt::StrongFocus);
slider->setTickPosition(QSlider::TicksBothSides);
slider->setTickInterval((max - min) / 11);
+ slider->setMinimumHeight(0);
slider->setSingleStep(1);
slider->setRange(min, max);
+ slider->setValue(0);
+ slider->setMinimumWidth(50);
+ slider->setToolTip(name);
+ slider->setStyleSheet("QSlider {"
+ "border: 1px solid #5a4855;"
+ "margin: 0px;"
+ "height: 200px;"
+ "width: 50px;}"
+ );
+ slider->setContentsMargins(0, 0, 0, 0);
valueBox = new QDoubleSpinBox();
valueBox->setFocusPolicy(Qt::NoFocus);
valueBox->setButtonSymbols(QAbstractSpinBox::NoButtons);
- valueBox->setMaximumWidth(45);
+ 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);
connect(slider, SIGNAL(valueChanged(int)), this, SLOT(sliderValueChanged(int)));
- QVBoxLayout *slidersLayout = new QVBoxLayout();
- slidersLayout->addWidget(valueBox);
- slidersLayout->addWidget(slider);
- setLayout(slidersLayout);
+ //connect(slider, SIGNAL(mousePressEvent(QMouseEvent)), this, SLOT(mousePressEvent(QMouseEvent *)));
+ layout->addWidget(slider);
+ layout->addWidget(valueBox);
+ this->setStyleSheet("border: 1px solid #5a4855;"
+ "width: 50px;"
+ "margin: 0px;"
+ "background-color: #383034;"
+ );
+ layout->setSpacing(0);
+ layout->setContentsMargins(0, 0, 0, 0);
+ this->setLayout(layout);
}
void SliderGroup::sliderValueChanged(int value)
{
- if (valueBox->decimals() > 1)
- value /= 100.0f;
- emit valueChanged(value);
+ valueBox->blockSignals(true);
valueBox->setValue(value);
+ valueBox->blockSignals(false);
+ emit valueChanged(value);
};
void SliderGroup::setValue(float value)
{
- if (valueBox->decimals() > 1)
- value *= 100.0f;
- slider->setValue(value);
+ slider->blockSignals(true);
+ valueBox->blockSignals(true);
+ if (int(value) != slider->value())
+ slider->setValue(value);
valueBox->setValue(value);
+ slider->blockSignals(false);
+ valueBox->blockSignals(false);
+}
+
+void SliderGroup::mousePressEvent(QMouseEvent* event) {
+ Q_UNUSED(event);
+ if (slider->isEnabled())
+ slider->setDisabled(true);
+ else
+ slider->setDisabled(false);
}
diff --git a/src/slidergroup.h b/src/slidergroup.h
index f5bae60..5bfeeb8 100644
--- a/src/slidergroup.h
+++ b/src/slidergroup.h
@@ -6,19 +6,19 @@
#include
#include
-class SliderGroup : public QGroupBox
+class SliderGroup : public QWidget
{
Q_OBJECT
public:
- SliderGroup(const QString &title,
- int min,
- int max,
- int decimals,
- QWidget *parent = nullptr);
+ SliderGroup(QString name,
+ int min,
+ int max,
+ int decimals,
+ QWidget *parent = nullptr);
signals:
- void valueChanged(float value);
+ void valueChanged(int value);
public slots:
void setValue(float value);
@@ -27,6 +27,8 @@ public slots:
private:
QSlider *slider;
QDoubleSpinBox *valueBox;
+
+ void mousePressEvent(QMouseEvent* event);
};
#endif