diff --git a/docs/LibreMediaServer_Audio.hed b/docs/LibreMediaServer_Audio.hed index fda3e2d..5da3a28 100644 --- a/docs/LibreMediaServer_Audio.hed +++ b/docs/LibreMediaServer_Audio.hed @@ -1,79 +1,76 @@ ް׆ˌĠ򝤫鿰맫͓Ԕ ҊÅЁ䍡 ӽ -Ꭸʆ -ɑ -ɬ -򉇁 -Ԑɉ - -ٿդϨ҃ -񀠧ڔ -ڑŌ -򻴧 - - - - - - - - -쏎ބ -힇ݳ - -Ԓlj - -Ŷ§ -ÐŽÊ -𡭟Ē - -܊ -񢰽 -зм -񓎍 -ҭ -ܻۏ -ڑן -ޏ - -Ƴ - - - -Ǖ - -𓎍Ԏ - - -Ξ -ֆ - -ց - -ᱯ -݈ - -؈ - -馹 -۴ -𓎍 - - - - - -𓎑 - - - -Ɨ - - - -Ӌ -쏎 - - - +䉭ėĈ +ϛ +Ӧ +ڑ +ė + +ݍ +Ȫ +웣傪Ⓨ֚ +ކՂ +߸“ +Ŷ +ѝ +ωƀ + + + + +ŕ +ʷí +̙ +򴹤 +ԥѼ܅ +𗹣َ +̚ +Რ + +Ņ +⽬ +̫ +喤쏎ʁǏ + +߬ػс +썎ޛ + +쏎 + + + +Ճ + +尨 +ގ + +ލ +ƕ + +ŗ + + + + + +ˤ + + + + + +쏐 + + + +쏎 +󩼿 + + + +ÛƘ + + + + diff --git a/docs/changelog.txt b/docs/changelog.txt index 5c7e0b6..48b0ddf 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -4,45 +4,39 @@ Libre Media Server Audio - An Open source Media Server for arts and performing. https://git.criptomart.net/libremediaserver ******************************************************************************* -Libre Media Server ChangeLog +Lbre Media Server ChangeLog -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. +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. + Select sound device output. -+ Pan. -+ Show faders values. New SliderGroup class. -+ Entry Point 16 bits. ++ pan. ++ Show faders values. ++ play offset. + Refactor AudioMasterWidget to AudioDMXReceptionWidget. -+ 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. ++ 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. -v 0.1.3 Leúcade (19/04/2024) +v 0.1.3 Unreleased (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 4274267..cc498ca 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 0771257..8ff57d7 100644 --- a/docs/roadmap.txt +++ b/docs/roadmap.txt @@ -5,6 +5,7 @@ https://git.criptomart.net/libremediaserver ******************************************************************************* Libre Media Server Roadmap +(en continuo crecimiento...) v 0.2.x - skin, UI/UX @@ -19,8 +20,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, ...) by master and layer. -- CIPT/MSex, send icons play/pause/stop. +- audio processing (eq, rev, compresor, ...) por master y capa. +- CIPT/MSex, send icons play-pause-stop. - Rasp build. - Octopus Sound Card support (6 outputs - 8 inputs). @@ -40,15 +41,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. -- 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. +- 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. - Logs, verbosity, timestamp. -- New play mode without pitch control, it saves resources. MA_SOUND_FLAG_NO_PITCH +- 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. - SettingsDialog. - Load/save conf file. -- ¿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. +- ¿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.... diff --git a/libremediaserver-audio.pro b/libremediaserver-audio.pro index 41d6ecd..a33e231 100644 --- a/libremediaserver-audio.pro +++ b/libremediaserver-audio.pro @@ -2,10 +2,7 @@ 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 \ @@ -17,9 +14,7 @@ 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 \ @@ -29,10 +24,10 @@ SOURCES += src/main.cpp \ src/audiowidget.cpp \ src/settings.cpp \ src/slidergroup.cpp -FORMS += src/libremediaserver-audio-gui.ui -CCFLAG += -msse2 -mavx2 #-fsanitize=address -g3 -O0 +FORMS += src/libremediaserver-audio.ui +CCFLAG += -msse2 -mavx2 #-fsanitize=address -g -O0 QMAKE_CXXFLAGS += $$(CXXFLAG) -#QMAKE_CXXFLAGS += -fsanitize=address -g3 -O0 +#QMAKE_CXXFLAGS += -fsanitize=address -g -O0 QMAKE_CFLAGS += $$(CCFLAG) QMAKE_LFLAGS += $$(LDFLAG) LIBS += -lola -lolacommon -ldl -lpthread -lm diff --git a/src/audiolayerwidget.cpp b/src/audiolayerwidget.cpp index e8d22bd..99e1996 100644 --- a/src/audiolayerwidget.cpp +++ b/src/audiolayerwidget.cpp @@ -1,89 +1,64 @@ #include "audiolayerwidget.h" -#include -AudioLayerWidget::AudioLayerWidget(QWidget *parent, int layer): - QWidget(parent) + +AudioLayerWidget::AudioLayerWidget(QWidget *parent, QString name, int layer): + QGroupBox(parent) , m_layer(layer) , m_suspendResumeButton(0) { + this->setTitle(name); QVBoxLayout *layout = new QVBoxLayout; - 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::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); - + QHBoxLayout *progressTime = new QHBoxLayout; m_progressTime = new QTimeEdit; - m_progressTime->setToolTip("Current Time"); - m_progressTime->setObjectName("Current Time"); - m_progressTime->setDisplayFormat("mm:ss:zz"); + m_progressTime->text(); + m_progressTime->setDisplayFormat("h:mm:ss:zzz"); m_progressTime->setReadOnly(true); m_progressTime->setButtonSymbols(QAbstractSpinBox::NoButtons); - m_progressTime->setMinimumWidth(80); - m_progressTime->setMaximumWidth(80); + m_progressTime->setMaximumWidth(90); m_progressTime->setFocusPolicy(Qt::NoFocus); - m_progressTime->setAlignment(Qt::AlignHCenter); - m_progressTime->setContentsMargins(0,0,0,0); + progressTime->addWidget(m_progressTime); m_totalTimeValue = new QTimeEdit; - m_totalTimeValue->setObjectName("Track Length"); - m_totalTimeValue->setToolTip("Track Length"); - m_totalTimeValue->setDisplayFormat("mm:ss:zzz"); + m_totalTimeValue->setDisplayFormat("h:mm:ss:zzz"); m_totalTimeValue->setReadOnly(true); m_totalTimeValue->setButtonSymbols(QAbstractSpinBox::NoButtons); - m_totalTimeValue->setMinimumWidth(80); + m_totalTimeValue->setMaximumWidth(90); 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); + 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(int)), this, SLOT(volumeChanged(int))); + 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(int)), this, SLOT(panChanged(int))); + connect(m_pan, SIGNAL(valueChanged(float)), this, SLOT(panChanged(float))); 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))); + connect(m_pitch, SIGNAL(valueChanged(float)), this, SLOT(pitchChanged(float))); layout->addLayout(volumeBox); - layout->setAlignment(Qt::AlignHCenter); - layout->setSpacing(0); - layout->setContentsMargins(2, 2, 2, 2); + + m_suspendResumeButton = new QPushButton(this); + m_suspendResumeButton->setText(StatusStr[Status::Stopped]); + connect(m_suspendResumeButton, SIGNAL(clicked()), SLOT(toggleSuspendResume())); + layout->addWidget(m_suspendResumeButton); + this->setLayout(layout); } @@ -93,17 +68,17 @@ AudioLayerWidget::~AudioLayerWidget() } // From UI. -void AudioLayerWidget::volumeChanged(int value) +void AudioLayerWidget::volumeChanged(float value) { emit(uiSliderChanged(m_layer, Slider::Volume, value)); } -void AudioLayerWidget::panChanged(int value) +void AudioLayerWidget::panChanged(float value) { emit(uiSliderChanged(m_layer, Slider::Pan, value)); } -void AudioLayerWidget::pitchChanged(int value) +void AudioLayerWidget::pitchChanged(float value) { emit(uiSliderChanged(m_layer, Slider::Pitch, value)); } @@ -118,28 +93,12 @@ void AudioLayerWidget::toggleSuspendResume() break; case Status::Paused: case Status::Stopped: - this->setPlaybackStatus(Status::PlayingLoop); - emit uiPlaybackChanged(m_layer, Status::PlayingLoop); - case Status::Iddle: + this->setPlaybackStatus(Status::PlayingOnce); + emit uiPlaybackChanged(m_layer, Status::PlayingOnce); 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) { @@ -162,7 +121,7 @@ void AudioLayerWidget::setPitch(int pitch) m_pitch->blockSignals(false); } -void AudioLayerWidget::setMediaFile(QString file) +void AudioLayerWidget::fileLoaded(QString file) { QStringList list = file.split("/"); int size = list.size(); @@ -172,36 +131,41 @@ void AudioLayerWidget::setMediaFile(QString file) } } -void AudioLayerWidget::setPlaybackStatus(Status s) +void AudioLayerWidget::setPlaybackStatus(Status status) { - Status status = static_cast(s); - m_suspendResumeButton->blockSignals(true); m_status = status; + if (status == Status::Stopped) + m_progressTime->setTime(QTime::fromMSecsSinceStartOfDay(0)); + m_statusValue->blockSignals(true); + m_suspendResumeButton->blockSignals(true); + m_statusValue->setText(StatusStr[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::setDuration(float dur) +void AudioLayerWidget::durationChanged(float 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)); + dur *= 1000; + m_progressSlider->setMaximum(dur); m_totalTimeValue->setTime(QTime::fromMSecsSinceStartOfDay(dur)); - m_progress->blockSignals(false); - m_progressTime->blockSignals(false); - m_totalTimeValue->blockSignals(false); } -void AudioLayerWidget::setCurrentTime(float progress) +void AudioLayerWidget::refreshUi(float progress) { progress *= 1000; - m_progress->blockSignals(true); - m_progressTime->blockSignals(true); - m_progress->setValue(progress); + m_progressSlider->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 9380b81..53401fb 100644 --- a/src/audiolayerwidget.h +++ b/src/audiolayerwidget.h @@ -3,57 +3,53 @@ #include #include -#include -#include +#include #include "defines.h" #include "slidergroup.h" -#include "clickablelabel.h" -#include "settings.h" -class AudioLayerWidget : public QWidget +class AudioLayerWidget : public QGroupBox { Q_OBJECT public: - explicit AudioLayerWidget(QWidget *parent = 0, int layer = 0); + explicit AudioLayerWidget(QWidget *parent = 0, QString name = "Layer", 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; - ClickableLabel *m_fileValue; - ClickableLabel * m_folderValue; + QLabel * m_statusValue; + QLabel *m_fileValue; + QLabel * 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(int vol); - void panChanged(int pan); - void pitchChanged(int pitch); + void volumeChanged(float vol); + void panChanged(float pan); + void pitchChanged(float pitch); + void fileLoaded(QString file); + void durationChanged(float dur); + void refreshUi(float progress); 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 8bd077c..4b35a5c 100644 --- a/src/audiowidget.cpp +++ b/src/audiowidget.cpp @@ -1,100 +1,121 @@ #include "audiowidget.h" -#include -AudioWidget::AudioWidget(QWidget *parent) : - QWidget(parent) - , m_layout(new QHBoxLayout()) + +AudioWidget::AudioWidget() : + m_layout(new QHBoxLayout()) + , m_refreshUi(new QTimer(this)) { for (int i= 0; i < Settings::getInstance()->getLayersNumber(); i++ ) { - AudioLayerWidget *alw = new AudioLayerWidget(this, i); + AudioLayerWidget *alw = new AudioLayerWidget(this, tr("Layer %1").arg(i + 1), i); m_layout->insertWidget(i, alw); - 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; + connect(alw, SIGNAL(uiSliderChanged(int, Slider, int)), this, SLOT(uiSliderAction(int, Slider, int))); + connect(alw, SIGNAL(uiPlaybackChanged(int, Status)), this, SLOT(uiChangePlaybackStatus(int, Status))); } - 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 * 1.5); + m_refreshUi->start(UI_REFRESH_TIME); } -void AudioWidget::mediaLoaded(int layer, QString file, float duration) +bool AudioWidget::startEngine(int id) { - m_layerUpdate[layer].media = file; - m_layerUpdate[layer].duration = duration; - m_layerUpdate[layer].updated = true; + 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); } void AudioWidget::volChanged(int layer, float vol) { - m_layerUpdate[layer].vol = vol; - m_layerUpdate[layer].updated = true; + m_mae.volChanged(layer, vol); + QLayoutItem * const item = m_layout->itemAt(layer); + dynamic_cast(item->widget())->setVol(vol); } void AudioWidget::panChanged(int layer, int pan) { - m_layerUpdate[layer].pan = pan; - m_layerUpdate[layer].updated = true; + m_mae.panChanged(layer, pan); + QLayoutItem * const item = m_layout->itemAt(layer); + dynamic_cast(item->widget())->setPan(pan); } void AudioWidget::pitchChanged(int layer, int pitch) { - - m_layerUpdate[layer].pitch = pitch; - m_layerUpdate[layer].updated = true; + m_mae.pitchChanged(layer, pitch); + QLayoutItem * const item = m_layout->itemAt(layer); + dynamic_cast(item->widget())->setPitch(pitch); } void AudioWidget::playbackChanged(int layer, Status status) { - m_layerUpdate[layer].status = status; - m_layerUpdate[layer].updated = true; + m_mae.playbackChanged(layer, status); + QLayoutItem * const item = m_layout->itemAt(layer); + dynamic_cast(item->widget())->setPlaybackStatus(status); } -void AudioWidget::cursorChanged(int layer, float cursor) +void AudioWidget::entryPointChanged(int layer, int cursor) { - m_layerUpdate[layer].cursor = cursor; - m_layerUpdate[layer].updated = true; + m_mae.setCursor(layer, cursor); + QLayoutItem * const item = m_layout->itemAt(layer); + AudioLayerWidget *aw = dynamic_cast(item->widget()); + aw->refreshUi(m_mae.getCursor(layer)); } -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::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::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 dd9b1f1..254e59e 100644 --- a/src/audiowidget.h +++ b/src/audiowidget.h @@ -5,6 +5,7 @@ #include "audiolayerwidget.h" #include "settings.h" +#include "miniaudioengine.h" #include "defines.h" // MAX_LAYERS class AudioWidget : public QWidget @@ -12,27 +13,29 @@ class AudioWidget : public QWidget Q_OBJECT public: - AudioWidget(QWidget *parent = nullptr); - -private: - QHBoxLayout *m_layout; - layerData m_layerUpdate[MAX_LAYERS]; - QTimer *m_refreshUi; - -public slots: + 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 cursorChanged(int layer, float cursor); - void mediaLoaded(int layer, QString media, float duration); void playbackChanged(int layer, Status status); + void entryPointChanged(int layer, int cursor); + +private: + MiniAudioEngine m_mae; + QString m_currentMedia[MAX_LAYERS]; + QHBoxLayout *m_layout; + QTimer *m_refreshUi; + +public slots: + void uiSliderAction(int layer, Slider s, int value); + void uiChangePlaybackStatus(int layer, Status s); 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 deleted file mode 100644 index 623493d..0000000 --- a/src/clickablelabel.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#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 deleted file mode 100644 index e75aae7..0000000 --- a/src/clickablelabel.h +++ /dev/null @@ -1,22 +0,0 @@ -#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 ff299c1..68675c2 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 "LibreMediaServerAudio 0.2.0 Antigona Release" +#define VERSION "Kike Substitutor - No AI required - v0.2.0" #define COPYRIGHT "(C) 2014-2024 Santi Noreña " -#define LICENSE "GPL 3 Licensed. See LICENSE.txt." +#define LICENSE "GPL 3 Licensed. See LICENSE.txt.\nSound guys are not allowed to use this software." #define DEFAULT_FILE "lms-audio.xlm" -#define MAX_LAYERS 4 -#define MAX_AUDIODEVICES 8 -#define UI_REFRESH_TIME 66 -#define FADE_TIME 25 // DMX Frame time, 40 fps, avoid clicks +#define MAX_LAYERS 16 +#define UI_REFRESH_TIME 200 +// struct where save the DMX settings for each layer struct dmxSetting { int address; unsigned int universe; @@ -22,10 +22,6 @@ enum Status Paused, PlayingOnce, PlayingLoop, - Iddle, - PlayingFolder, - PlayingFolderLoop, - PlayingFolderRandom }; static const char* StatusStr[] = @@ -33,11 +29,7 @@ static const char* StatusStr[] = "Stop", "Pause", "Playing One", - "Playing One Loop", - "Iddle", - "Playing Folder", - "Playing Folder Loop", - "Playing Folder Random", + "Playing Loop", 0x0 }; @@ -48,18 +40,5 @@ 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 82f9468..1e42df7 100644 --- a/src/dmxPersonality.h +++ b/src/dmxPersonality.h @@ -1,15 +1,39 @@ #ifndef DMXPERSONALITY_H #define DMXPERSONALITY_H -#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 +/** 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 #endif // DMXPERSONALITY_H diff --git a/src/dmxwidget.cpp b/src/dmxwidget.cpp index c5b4150..60d4206 100644 --- a/src/dmxwidget.cpp +++ b/src/dmxwidget.cpp @@ -9,8 +9,6 @@ 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 deleted file mode 100644 index b769679..0000000 --- a/src/libremediaserver-audio-gui.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/* - - 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 deleted file mode 100644 index 50b4d8b..0000000 --- a/src/libremediaserver-audio-gui.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - 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 deleted file mode 100644 index c0f11ad..0000000 --- a/src/libremediaserver-audio-gui.ui +++ /dev/null @@ -1,55 +0,0 @@ - - - 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 - - - - - - 0 - 0 - 500 - 21 - - - - - File - - - - - - - - OLA Setup - - - - - - diff --git a/src/libremediaserver-audio.cpp b/src/libremediaserver-audio.cpp index a9868fd..0d595ec 100644 --- a/src/libremediaserver-audio.cpp +++ b/src/libremediaserver-audio.cpp @@ -21,239 +21,106 @@ #include "libremediaserver-audio.h" -libreMediaServerAudio::libreMediaServerAudio() +libreMediaServerAudio::libreMediaServerAudio(QStringList args, QWidget *parent) + : QMainWindow(parent) { - m_settings = Settings::getInstance(); - m_settings->readFile(); - m_ui = m_settings->getShowUi(); + Q_UNUSED(args); + qDebug() << VERSION; + qDebug() << COPYRIGHT; + qDebug() << LICENSE; + ui.setupUi(this); + this->setWindowTitle(VERSION); + Settings *set = Settings::getInstance(); + set->readFile(); m_mediaLibrary = new MediaLibrary; m_mediaLibrary->initMediaLibrary(); - 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()); + 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()); 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(); - m_mae.startEngine(m_settings->getAudioDeviceId()); - qDebug("Core init Complete. Start reading DMX."); + connect(ui.actionLaunch_OLA_Setup, SIGNAL(triggered()), this, SLOT(olasetup())); + m_aw->startEngine(); + qDebug("Init Complete."); m_ola->blockSignals(false); -#ifdef NOGUI m_ola->start(QThread::TimeCriticalPriority ); -#endif } libreMediaServerAudio::~libreMediaServerAudio() { m_ola->stop(); - m_mae.stopEngine(); + m_aw->stopEngine(); } -void libreMediaServerAudio::loadMedia(int layer, int folder, int file) +void libreMediaServerAudio::olasetup() { - 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(); - } + QWebView *view = new QWebView(); + view->load(QUrl("http://localhost:9090/ola.html")); + view->show(); } void libreMediaServerAudio::dmxInput(int layer, int channel, int value) { - if (layer >= MAX_LAYERS || channel >= LAYER_CHANNELS) + if (layer > LAYER_CHANNELS) return; QString mediaFile = NULL; int aux; - 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) { + 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; aux = value / 25; - 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)); + 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; } -#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); + default: 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 d83446a..9cd154b 100644 --- a/src/libremediaserver-audio.h +++ b/src/libremediaserver-audio.h @@ -20,51 +20,35 @@ #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" -#ifndef NOGUI -#include "libremediaserver-audio-gui.h" -#endif +#include "ui_libremediaserver-audio.h" -class libreMediaServerAudio : public QObject +class libreMediaServerAudio : public QMainWindow { Q_OBJECT public: - libreMediaServerAudio(); + libreMediaServerAudio (QStringList args, QWidget *parent = 0); virtual ~libreMediaServerAudio(); - 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 + Ui::LibreMediaServerAudio ui; 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 refreshUi(); - void uiSliderChanged(int layer, Slider s, int value); - void uiPlaybackChanged(int layer, Status s); - void uiLoadMedia(int layer, QString s); - -#endif + void olasetup(); + void dmxInput(int layer, int channel, int value); }; #endif // LIBREMEDIASERVERAUDIO_H diff --git a/src/libremediaserver-audio.ui b/src/libremediaserver-audio.ui new file mode 100644 index 0000000..6ade744 --- /dev/null +++ b/src/libremediaserver-audio.ui @@ -0,0 +1,81 @@ + + + Santi Noreña belfegor@gmail.com + LibreMediaServerAudio + + + + 0 + 0 + 114 + 218 + + + + LibreMediaServer + + + + + + 0 + 0 + 114 + 22 + + + + + File + + + + + + + + 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 79b7a91..bfadafe 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -18,28 +18,34 @@ along with this program. If not, see . */ -#include "main.h" +#include "libremediaserver-audio.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); - libreMediaServerAudio lms; -#ifndef NOGUI - if (hasUi(argc, argv) || lms.getShowUi()) + QStringList args = app.arguments(); + if (args.size() > 1) { - libreMediaServerAudioUi *lmsUi = new libreMediaServerAudioUi(); - lms.setUi(lmsUi); - lmsUi->show(); + 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; + } } -#endif + libreMediaServerAudio libreMediaServerAudio(args); + libreMediaServerAudio.show(); return app.exec(); } diff --git a/src/main.h b/src/main.h deleted file mode 100644 index 1a96ff6..0000000 --- a/src/main.h +++ /dev/null @@ -1,20 +0,0 @@ -#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 ea92bca..5a0b55b 100644 --- a/src/medialibrary.h +++ b/src/medialibrary.h @@ -27,11 +27,14 @@ 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 9204636..6d5490c 100644 --- a/src/miniaudioengine.cpp +++ b/src/miniaudioengine.cpp @@ -1,15 +1,8 @@ #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) @@ -27,26 +20,25 @@ void MiniAudioEngine::stopEngine() ma_resource_manager_uninit(&resourceManager); } -bool MiniAudioEngine::startEngine(uint n) +bool MiniAudioEngine::startEngine(int n) { ma_result result; result = this->startContext(); - if (result != MA_SUCCESS) return result; - result = this->getAllAudioDevices(); - if (result != MA_SUCCESS) return result; + if (result != MA_SUCCESS) { + return result; + } + this->getAllAudioDevices(); result = this->startDevice(n); return result; } -ma_result MiniAudioEngine::startDevice(uint id) +ma_result MiniAudioEngine::startDevice(int 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; @@ -74,7 +66,7 @@ ma_result MiniAudioEngine::startDevice(uint 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; } @@ -85,8 +77,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 = ma_standard_sample_rate_48000; - resourceManagerConfig.jobThreadCount = 4; + resourceManagerConfig.decodedSampleRate = 0; + resourceManagerConfig.jobThreadCount = 0; result = ma_resource_manager_init(&resourceManagerConfig, &resourceManager); if (result != MA_SUCCESS) { qCritical("Failed to initialize audio resource manager."); @@ -110,7 +102,7 @@ ma_result MiniAudioEngine::getAllAudioDevices() ma_context_uninit(&context); return result; } - printf("Audio devices available:\n"); + printf("Audio devices detected in system:\n"); for (ma_uint32 iAvailableDevice = 0; iAvailableDevice < playbackDeviceCount; iAvailableDevice += 1) { qInfo("%d: : %s", iAvailableDevice, pPlaybackDeviceInfos[iAvailableDevice].name); } @@ -123,20 +115,21 @@ 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_SOUND_FLAG_DECODE \ - /*| MA_SOUND_FLAG_NO_PITCH \*/ + /*| MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE \ + | MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC \ + | MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM \*/ , NULL, NULL, &m_currentSound[layer]); if (result != MA_SUCCESS) qWarning("Failed to load file %s", file); else { m_mediaLoaded[layer] = true; - this->refreshValues(layer); - m_currentLayerValues[layer].media = file; + this->volChanged(layer, 0); } return result; } @@ -144,21 +137,16 @@ ma_result MiniAudioEngine::loadMedia(int layer, char *file) float MiniAudioEngine::getDuration(int layer) { ma_result result; - ma_uint64 lengthInPCMFrames; - ma_uint32 sampleRate; - float ret; + float ret = 0; if (m_mediaLoaded[layer] == false) return MA_DOES_NOT_EXIST; - result = ma_sound_get_length_in_pcm_frames(&m_currentSound[layer], &lengthInPCMFrames); - if (result != MA_SUCCESS) { - return result; + 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_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; } @@ -172,8 +160,8 @@ float MiniAudioEngine::getCursor(int layer) result = ma_sound_get_cursor_in_seconds(&m_currentSound[layer], &ret); if (result != MA_SUCCESS) { - qWarning("%i can not get cursor error %i", layer, result); - ret = MA_ERROR; + qWarning("Can not get cursor %i", layer); + ret = 0; } return ret; } @@ -187,10 +175,11 @@ 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("%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; + 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); return result; } @@ -199,10 +188,7 @@ void MiniAudioEngine::volChanged(int layer, float vol) { if (m_mediaLoaded[layer] == false) return; - 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; + ma_sound_group_set_volume(&m_currentSound[layer], vol); } void MiniAudioEngine::panChanged(int layer, float value) @@ -213,90 +199,48 @@ 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 pitch; + float result; if (m_mediaLoaded[layer] == false) return; - pitch = value / 128.0; - ma_sound_group_set_pitch(&m_currentSound[layer], pitch); - m_currentLayerValues[layer].pitch = value; + result = value / 128.0; + ma_sound_group_set_pitch(&m_currentSound[layer], result); } -ma_result MiniAudioEngine::playbackChanged(int layer, Status status) +void MiniAudioEngine::playbackChanged(int layer, Status status) { - ma_result result = MA_SUCCESS; - if (m_mediaLoaded[layer] == false) - return MA_DOES_NOT_EXIST; + return; switch (status) { case Status::Paused: - result = ma_sound_stop_with_fade_in_milliseconds(&m_currentSound[layer], FADE_TIME); + ma_sound_stop(&m_currentSound[layer]); break; case Status::Stopped: - result = ma_sound_stop_with_fade_in_milliseconds(&m_currentSound[layer], FADE_TIME); - result = this->seekToCursor(layer, m_currentLayerValues[layer].cursor); + ma_sound_stop(&m_currentSound[layer]); + ma_sound_seek_to_pcm_frame(&m_currentSound[layer], 0); 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); - result = ma_sound_start(&m_currentSound[layer]); - break; + 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); - result = ma_sound_start(&m_currentSound[layer]); - break; - default: + ma_sound_start(&m_currentSound[layer]); break; } - if (result == MA_SUCCESS) - m_currentLayerValues[layer].status = status; - return result; } -ma_result MiniAudioEngine::seekToCursor(int layer, int cursor) +void MiniAudioEngine::setCursor(int layer, int cursor) { - ma_result result = MA_SUCCESS; - ma_uint64 end, start; + ma_uint64 f; if (m_mediaLoaded[layer] == false) - 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); + 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); } diff --git a/src/miniaudioengine.h b/src/miniaudioengine.h index 6e1393c..9549602 100644 --- a/src/miniaudioengine.h +++ b/src/miniaudioengine.h @@ -5,16 +5,15 @@ #include "miniaudio.h" #include "defines.h" // MAX_LAYERS #include // prints messages -#define MA_DEBUG_OUTPUT class MiniAudioEngine { - friend class libreMediaServerAudio; + friend class AudioWidget; public: MiniAudioEngine(); void stopEngine(); - bool startEngine(uint id); + bool startEngine(int id); static void audioDataCallback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount); protected: @@ -22,15 +21,11 @@ protected: void volChanged(int layer, float vol); void panChanged(int layer, float pan); void pitchChanged(int layer, float pitch); - ma_result playbackChanged(int layer, Status status); - ma_result setCursor(int layer, int cursor); - ma_result printFormatInfo(int layer); + void playbackChanged(int layer, Status status); float getDuration(int layer); float getCursor(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; } + void setCursor(int layer, int cursor); + ma_result printFormatInfo(int layer); private: ma_resource_manager_config resourceManagerConfig; @@ -43,13 +38,10 @@ 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(uint id); + ma_result startDevice(int 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 6ca9325..ed0998b 100644 --- a/src/olathread.cpp +++ b/src/olathread.cpp @@ -1,10 +1,11 @@ -#include "libremediaserver-audio.h" +#include "olathread.h" + olaThread::olaThread(QObject *parent, int layers) : m_counter(0) , m_layers(layers) { - this->setParent(parent); + Q_UNUSED(parent); gettimeofday(&m_last_data, NULL); for (int i=0; i < MAX_LAYERS; i++) { @@ -62,65 +63,42 @@ 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 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 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: - 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); + emit dmxOutput(i.layer,j,value); volSent = true; break; - case PLAYBACK: - qobject_cast(parent())->dmxInput(i.layer, j, value); - break; - case ENTRY_POINT_FINE: - 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); + emit dmxOutput(i.layer,j,value); entrySent = true; break; + case VOLUME_FINE: + if (volSent == false) + { + value = (buffer.Get(i.address + VOLUME_COARSE) * 0x100) + value; + emit dmxOutput(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); + } + break; default: - qobject_cast(parent())->dmxInput(i.layer, j, value); + emit dmxOutput(i.layer,j,value); break; } } @@ -149,7 +127,7 @@ bool olaThread::CheckDataLoss() { void olaThread::socketClosed() { - qWarning("ola daemon closed connection, reopening it... "); + qWarning("ola closed connection. Try reopening it... "); m_clientWrapper->GetSelectServer()->Terminate(); m_client = NULL; m_clientWrapper = NULL; diff --git a/src/olathread.h b/src/olathread.h index ed2735a..91d78b6 100644 --- a/src/olathread.h +++ b/src/olathread.h @@ -20,17 +20,10 @@ 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 @@ -42,6 +35,14 @@ 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 @@ -52,7 +53,7 @@ private: if (error.Success()) { qDebug("Register Universe success"); } else { - qCritical("Register command failed: %s", error.Error().c_str()); + qWarning("Register command failed: %s", error.Error().c_str()); } } /** @@ -66,7 +67,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 9d06394..f664bb0 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -15,7 +15,6 @@ Settings::Settings(QObject *parent) : QObject(parent) { m_layersNumber = 0; - m_ui = false; } // Read the dmx settings for dmx.xml At the moment we need: @@ -24,7 +23,6 @@ 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)) { @@ -36,56 +34,53 @@ void Settings::readFromFile(QString file) { exit(1); } QXmlStreamReader* xmlReader = new QXmlStreamReader(xmlFile); - while(!xmlReader->atEnd() && !xmlReader->hasError()) { + int counter = 0; + //Parse the XML until we reach end of it + while(!xmlReader->atEnd() && !xmlReader->hasError() && counter < MAX_LAYERS) { + // Read next element 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() == "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; + 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_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(); - } - + m_audioDeviceId = xmlReader->attributes().value("id").toLocal8Bit().toInt(); + continue; } - if(xmlReader->name() == "layer") { + QString add = "layer"; + add.append(QString("%1").arg(counter)); + if((xmlReader->name() == add)) { dmxSetting temp; temp.address = xmlReader->attributes().value("dmx").toLocal8Bit().toInt() - 1; temp.universe = xmlReader->attributes().value("universe").toLocal8Bit().toInt(); - temp.layer = xmlReader->attributes().value("id").toLocal8Bit().toInt(); + temp.layer = counter; 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()); - // ToDo: manage this, open a dialog to load a new file. + return; } 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 f27da6a..8cd55bb 100644 --- a/src/settings.h +++ b/src/settings.h @@ -5,7 +5,6 @@ #include #include #include -#include #include "medialibrary.h" #include "audiowidget.h" @@ -16,27 +15,25 @@ 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(); - void readFromFile(QString file); - void printSettings(); + inline int getAudioDeviceId() { return m_audioDeviceId; } private: static Settings *_instance; QList m_settings; QString m_pathmedia; - uint m_audioDeviceId[MAX_AUDIODEVICES]; - uint m_audioDeviceQty; + uint m_audioDeviceId; QSet m_universe; int m_layersNumber; - bool m_ui; + + explicit Settings(QObject *parent = 0); + void readFromFile(QString file); + }; #endif // SETTINGS_H diff --git a/src/slidergroup.cpp b/src/slidergroup.cpp index 9ef9680..e80a03f 100644 --- a/src/slidergroup.cpp +++ b/src/slidergroup.cpp @@ -1,81 +1,45 @@ #include "slidergroup.h" -#include -SliderGroup::SliderGroup(QString name, + +SliderGroup::SliderGroup(const QString &title, \ int min, int max, int decimals, QWidget *parent) - : QWidget(parent) + : QGroupBox(title, parent) { - QVBoxLayout *layout = new QVBoxLayout; - layout->setAlignment(Qt::AlignHCenter); - layout->setContentsMargins(0, 0, 0, 0); - //this->setMaximumWidth(40); + this->setFlat(true); + this->setTitle(title); 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->setMinimumWidth(50); + valueBox->setMaximumWidth(45); 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))); - //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); + QVBoxLayout *slidersLayout = new QVBoxLayout(); + slidersLayout->addWidget(valueBox); + slidersLayout->addWidget(slider); + setLayout(slidersLayout); } void SliderGroup::sliderValueChanged(int value) { - valueBox->blockSignals(true); - valueBox->setValue(value); - valueBox->blockSignals(false); + if (valueBox->decimals() > 1) + value /= 100.0f; emit valueChanged(value); + valueBox->setValue(value); }; void SliderGroup::setValue(float value) { - slider->blockSignals(true); - valueBox->blockSignals(true); - if (int(value) != slider->value()) - slider->setValue(value); + if (valueBox->decimals() > 1) + value *= 100.0f; + 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 5bfeeb8..f5bae60 100644 --- a/src/slidergroup.h +++ b/src/slidergroup.h @@ -6,19 +6,19 @@ #include #include -class SliderGroup : public QWidget +class SliderGroup : public QGroupBox { Q_OBJECT public: - SliderGroup(QString name, - int min, - int max, - int decimals, - QWidget *parent = nullptr); + SliderGroup(const QString &title, + int min, + int max, + int decimals, + QWidget *parent = nullptr); signals: - void valueChanged(int value); + void valueChanged(float value); public slots: void setValue(float value); @@ -27,8 +27,6 @@ public slots: private: QSlider *slider; QDoubleSpinBox *valueBox; - - void mousePressEvent(QMouseEvent* event); }; #endif