From 1fccbf64fdd0ea55b3f7c66661223ec4a2160fc4 Mon Sep 17 00:00:00 2001 From: snt Date: Sat, 4 May 2024 01:43:44 +0200 Subject: [PATCH 01/38] =?UTF-8?q?refactorizada=20la=20GUI,=20ola=20y=20aud?= =?UTF-8?q?ioengine=20se=20ejecutan=20fuera=20de=20widgets.=20Opci=C3=B3n?= =?UTF-8?q?=20para=20ejecutar=20sin=20mostrar=20la=20GUI.=20Los=20controle?= =?UTF-8?q?s=20de=20la=20interfaz=20no=20son=20reactivos,=20no=20est=C3=A1?= =?UTF-8?q?n=20conectados=20a=20las=20=C3=B3rdenes=20de=20audio.=20cambio?= =?UTF-8?q?=20en=20la=20personalidad=20dmx=20para=20procesar=20los=20canal?= =?UTF-8?q?es=20en=20mejor=20orden.=20evita=20mandar=20dos=20veces=20los?= =?UTF-8?q?=20canales=20dobles=20por=20cada=20dmx=20frame,=20incluyendo=20?= =?UTF-8?q?file/folder.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- libremediaserver-audio.pro | 5 +- src/audiolayerwidget.cpp | 101 +++++------ src/audiolayerwidget.h | 8 +- src/audiowidget.cpp | 73 ++------ src/audiowidget.h | 21 +-- src/defines.h | 5 +- src/dmxPersonality.h | 43 ++--- src/libremediaserver-audio-gui.cpp | 48 ++++++ src/libremediaserver-audio-gui.h | 48 ++++++ ...audio.ui => libremediaserver-audio-gui.ui} | 0 src/libremediaserver-audio.cpp | 161 ++++++++++-------- src/libremediaserver-audio.h | 32 ++-- src/main.cpp | 39 ++--- src/main.h | 20 +++ src/miniaudioengine.cpp | 36 +++- src/miniaudioengine.h | 3 +- src/olathread.cpp | 59 ++++--- src/olathread.h | 2 +- src/slidergroup.cpp | 24 ++- src/slidergroup.h | 11 +- 20 files changed, 416 insertions(+), 323 deletions(-) create mode 100644 src/libremediaserver-audio-gui.cpp create mode 100644 src/libremediaserver-audio-gui.h rename src/{libremediaserver-audio.ui => libremediaserver-audio-gui.ui} (100%) create mode 100644 src/main.h diff --git a/libremediaserver-audio.pro b/libremediaserver-audio.pro index a33e231..154d1fe 100644 --- a/libremediaserver-audio.pro +++ b/libremediaserver-audio.pro @@ -3,6 +3,8 @@ TARGET = libremediaserver-audio QT += webkitwidgets widgets HEADERS += src/libremediaserver-audio.h \ src/dmxwidget.h \ + src/libremediaserver-audio-gui.h \ + src/main.h \ src/miniaudio.h \ src/medialibrary.h \ src/miniaudioengine.h \ @@ -15,6 +17,7 @@ HEADERS += src/libremediaserver-audio.h \ src/slidergroup.h SOURCES += src/main.cpp \ src/dmxwidget.cpp \ + src/libremediaserver-audio-gui.cpp \ src/miniaudio.c \ src/libremediaserver-audio.cpp \ src/medialibrary.cpp \ @@ -24,7 +27,7 @@ SOURCES += src/main.cpp \ src/audiowidget.cpp \ src/settings.cpp \ src/slidergroup.cpp -FORMS += src/libremediaserver-audio.ui +FORMS += src/libremediaserver-audio-gui.ui CCFLAG += -msse2 -mavx2 #-fsanitize=address -g -O0 QMAKE_CXXFLAGS += $$(CXXFLAG) #QMAKE_CXXFLAGS += -fsanitize=address -g -O0 diff --git a/src/audiolayerwidget.cpp b/src/audiolayerwidget.cpp index 99e1996..ffb8341 100644 --- a/src/audiolayerwidget.cpp +++ b/src/audiolayerwidget.cpp @@ -1,64 +1,67 @@ #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_suspendResumeButton = new QPushButton(this); + m_suspendResumeButton->setText(StatusStr[Status::Iddle]); + m_suspendResumeButton->setMaximumWidth(200); + //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"); + m_progress->setMaximumWidth(200); + layout->addWidget(m_progress); + m_progressTime = new QTimeEdit; - m_progressTime->text(); + m_progressTime->setToolTip("Current Time"); + m_progressTime->setObjectName("Current Time"); m_progressTime->setDisplayFormat("h:mm:ss:zzz"); m_progressTime->setReadOnly(true); m_progressTime->setButtonSymbols(QAbstractSpinBox::NoButtons); - m_progressTime->setMaximumWidth(90); + m_progressTime->setMaximumWidth(88); m_progressTime->setFocusPolicy(Qt::NoFocus); - progressTime->addWidget(m_progressTime); m_totalTimeValue = new QTimeEdit; + m_totalTimeValue->setObjectName("Track Length"); + m_totalTimeValue->setToolTip("Track Length"); m_totalTimeValue->setDisplayFormat("h:mm:ss:zzz"); m_totalTimeValue->setReadOnly(true); m_totalTimeValue->setButtonSymbols(QAbstractSpinBox::NoButtons); - m_totalTimeValue->setMaximumWidth(90); + m_totalTimeValue->setMaximumWidth(88); 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); + QHBoxLayout *status = new QHBoxLayout; + status->addWidget(m_progressTime); + status->addWidget(m_totalTimeValue); + layout->addLayout(status); + QVBoxLayout *playback = new QVBoxLayout; m_folderValue = new QLabel; m_folderValue->setMaximumWidth(200); - status->addWidget(m_folderValue, 1, 0); + playback->addWidget(m_folderValue); m_fileValue = new QLabel; m_fileValue->setMaximumWidth(200); - status->addWidget(m_fileValue, 2, 0); - layout->addLayout(status); + playback->addWidget(m_fileValue); + layout->addLayout(playback); QHBoxLayout *volumeBox = new QHBoxLayout; - m_volume = new SliderGroup("Vol", 0 , 100, 2, NULL); + m_volume = new SliderGroup(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); + m_pan = new SliderGroup(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); + m_pitch = new SliderGroup(0 , 255, 0, NULL); volumeBox->addWidget(m_pitch); connect(m_pitch, SIGNAL(valueChanged(float)), this, SLOT(pitchChanged(float))); layout->addLayout(volumeBox); - - 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); } @@ -95,6 +98,7 @@ void AudioLayerWidget::toggleSuspendResume() case Status::Stopped: this->setPlaybackStatus(Status::PlayingOnce); emit uiPlaybackChanged(m_layer, Status::PlayingOnce); + case Status::Iddle: break; } } @@ -129,43 +133,40 @@ void AudioLayerWidget::fileLoaded(QString file) m_folderValue->setText(list.at(size - 2)); m_fileValue->setText(list.at(size - 1)); } + this->setPlaybackStatus(Status::Stopped); } void AudioLayerWidget::setPlaybackStatus(Status status) { m_status = status; if (status == Status::Stopped) - m_progressTime->setTime(QTime::fromMSecsSinceStartOfDay(0)); - m_statusValue->blockSignals(true); + m_progress->setValue(0); 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::durationChanged(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) { 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..df5853a 100644 --- a/src/audiolayerwidget.h +++ b/src/audiolayerwidget.h @@ -4,16 +4,17 @@ #include #include #include +#include #include "defines.h" #include "slidergroup.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(); @@ -27,15 +28,14 @@ private: Status m_status; int m_layer; QPushButton *m_suspendResumeButton; - 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; public slots: void toggleSuspendResume(); diff --git a/src/audiowidget.cpp b/src/audiowidget.cpp index 4b35a5c..69dc60d 100644 --- a/src/audiowidget.cpp +++ b/src/audiowidget.cpp @@ -3,119 +3,66 @@ 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, 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))); } setLayout(m_layout); - connect(m_refreshUi, SIGNAL(timeout()), this, SLOT(refreshUi())); - m_refreshUi->start(UI_REFRESH_TIME); } -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); + dynamic_cast(item->widget())->durationChanged(duration); } 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); } 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); } 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); } 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); } -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)); -} - -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)); - } - } + AudioLayerWidget *alw = dynamic_cast(item->widget()); + alw->refreshUi(cursor); } void AudioWidget::uiSliderAction(int layer, Slider s, int value) { switch (s){ case Slider::Volume: - m_mae.volChanged(layer, value); + emit uiVolChanged(layer, value); break; case Slider::Pan: - m_mae.panChanged(layer, value); + emit uiPanChanged(layer, value); break; case Slider::Pitch: - m_mae.pitchChanged(layer, value); + emit uiPitchChanged(layer, value); break; } } void AudioWidget::uiChangePlaybackStatus(int layer, Status s) { - m_mae.playbackChanged(layer, s); + emit uiPlaybackChanged(layer, s); } - diff --git a/src/audiowidget.h b/src/audiowidget.h index 254e59e..3314127 100644 --- a/src/audiowidget.h +++ b/src/audiowidget.h @@ -14,28 +14,25 @@ class AudioWidget : public QWidget public: AudioWidget(); - bool startEngine(); - bool startEngine(int id); - void stopEngine(); - void mediaLoaded(int layer, QString media ); + void mediaLoaded(int layer, QString media, float duration); 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); - -private: - MiniAudioEngine m_mae; - QString m_currentMedia[MAX_LAYERS]; + void cursorChanged(int layer, float cursor); 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 uiMediaLoaded(int layer, QString media ); + void uiVolChanged(int layer, float vol); + void uiPanChanged(int layer, int pan); + void uiPitchChanged(int layer, int pitch); + void uiPlaybackChanged(int layer, Status status); + void uiEntryPointChanged(int layer, int cursor); }; #endif // AUDIOWIDGET_H diff --git a/src/defines.h b/src/defines.h index 68675c2..dee7850 100644 --- a/src/defines.h +++ b/src/defines.h @@ -1,8 +1,7 @@ #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 DEFAULT_FILE "lms-audio.xlm" @@ -22,6 +21,7 @@ enum Status Paused, PlayingOnce, PlayingLoop, + Iddle }; static const char* StatusStr[] = @@ -30,6 +30,7 @@ static const char* StatusStr[] = "Pause", "Playing One", "Playing Loop", + "Iddle", 0x0 }; diff --git a/src/dmxPersonality.h b/src/dmxPersonality.h index 1e42df7..c56829e 100644 --- a/src/dmxPersonality.h +++ b/src/dmxPersonality.h @@ -1,39 +1,16 @@ #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 -*/ +#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 -// 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 LAYER_CHANNELS 9 #endif // DMXPERSONALITY_H diff --git a/src/libremediaserver-audio-gui.cpp b/src/libremediaserver-audio-gui.cpp new file mode 100644 index 0000000..a300229 --- /dev/null +++ b/src/libremediaserver-audio-gui.cpp @@ -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 . +*/ + +#include "libremediaserver-audio-gui.h" + + +libreMediaServerAudioUi::libreMediaServerAudioUi(QWidget *parent) + : QMainWindow(parent) +{ + ui.setupUi(this); + this->setWindowTitle(VERSION); + 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); + connect(ui.actionLaunch_OLA_Setup, SIGNAL(triggered()), this, SLOT(olasetup())); +} + +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.ui b/src/libremediaserver-audio-gui.ui similarity index 100% rename from src/libremediaserver-audio.ui rename to src/libremediaserver-audio-gui.ui diff --git a/src/libremediaserver-audio.cpp b/src/libremediaserver-audio.cpp index 0d595ec..d049c44 100644 --- a/src/libremediaserver-audio.cpp +++ b/src/libremediaserver-audio.cpp @@ -21,50 +21,35 @@ #include "libremediaserver-audio.h" -libreMediaServerAudio::libreMediaServerAudio(QStringList args, QWidget *parent) - : QMainWindow(parent) +libreMediaServerAudio::libreMediaServerAudio(bool gui) { - Q_UNUSED(args); - qDebug() << VERSION; - qDebug() << COPYRIGHT; - qDebug() << LICENSE; - ui.setupUi(this); - this->setWindowTitle(VERSION); + m_ui = gui; Settings *set = Settings::getInstance(); set->readFile(); 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()); 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(); + m_mae.startEngine(set->getAudioDeviceId()); qDebug("Init Complete."); m_ola->blockSignals(false); m_ola->start(QThread::TimeCriticalPriority ); +#ifndef NOGUI + if (m_ui) { + m_refreshUi = new QTimer(this); + connect(m_refreshUi, SIGNAL(timeout()), this, SLOT(refreshUi())); + m_refreshUi->start(UI_REFRESH_TIME); + } +#endif } libreMediaServerAudio::~libreMediaServerAudio() { m_ola->stop(); - m_aw->stopEngine(); -} - -void libreMediaServerAudio::olasetup() -{ - QWebView *view = new QWebView(); - view->load(QUrl("http://localhost:9090/ola.html")); - view->show(); + m_mae.stopEngine(); } void libreMediaServerAudio::dmxInput(int layer, int channel, int value) @@ -73,54 +58,82 @@ void libreMediaServerAudio::dmxInput(int layer, int channel, int value) 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; - 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; + if (channel == DMX_FOLDER || channel == DMX_FILE){ + int folder = (value >> 8) & 0x000000FF; + int file = value & 0x000000FF; + 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()); +#ifndef NOGUI + if (m_ui) + m_lmsUi->m_aw->mediaLoaded(layer, mediaFile, m_mae.getDuration(layer)); +#endif + m_currentMedia[layer] = mediaFile; + } + } else if (channel == VOLUME_COARSE || channel == VOLUME_FINE) { + m_mae.volChanged(layer, (value / 65025.0f)); +#ifndef NOGUI + if (m_ui) + m_lmsUi->m_aw->volChanged(layer, (value / 650.25f)); +#endif + } else if (channel == PAN) { + m_mae.panChanged(layer, value); +#ifndef NOGUI + if (m_ui) + m_lmsUi->m_aw->panChanged(layer, value); +#endif + } else if (channel == PITCH) { + m_mae.pitchChanged(layer, value); +#ifndef NOGUI + if (m_ui) + m_lmsUi->m_aw->pitchChanged(layer, value); +#endif + } else if (channel == ENTRY_POINT_COARSE || channel == ENTRY_POINT_FINE) { + m_mae.setCursor(layer, value); +#ifndef NOGUI + if (m_ui) + m_lmsUi->m_aw->cursorChanged(layer, m_mae.getCursor(layer)); +#endif + + } else if (channel == PLAYBACK && value > 0) { + Status s = m_mae.getStatus(layer); + aux = value / 25; + if (s != aux) { + 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; + m_mae.playbackChanged(layer, s); +#ifndef NOGUI + if (m_ui) + m_lmsUi->m_aw->playbackChanged(layer, s); +#endif } - default: - break; } } + +#ifndef NOGUI +void libreMediaServerAudio::refreshUi() { + if (!m_ui) + return; + for (int i= 0; i < Settings::getInstance()->getLayersNumber(); i++ ) { + Status s = m_mae.getStatus(i); + if (s == Status::PlayingOnce || s == Status::PlayingLoop) { + m_lmsUi->m_aw->cursorChanged(i, m_mae.getCursor(i)); + } + } +} + +void libreMediaServerAudio::setUi(libreMediaServerAudioUi *lmsUi) +{ + m_lmsUi = lmsUi; + connect(m_ola, SIGNAL(universeReceived(int)), m_lmsUi->m_dmxWidget, SLOT(updateWatchDMX(int))); +}; + +#endif diff --git a/src/libremediaserver-audio.h b/src/libremediaserver-audio.h index 9cd154b..923a42b 100644 --- a/src/libremediaserver-audio.h +++ b/src/libremediaserver-audio.h @@ -20,35 +20,41 @@ #ifndef LIBREMEDIASERVERAUDIO_H #define LIBREMEDIASERVERAUDIO_H -#include -#include - -#include "audiowidget.h" #include "medialibrary.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(bool gui = false); virtual ~libreMediaServerAudio(); - Ui::LibreMediaServerAudio ui; + olaThread *m_ola; +#ifndef NOGUI + void setUi(libreMediaServerAudioUi *lmsUi); +#endif private: - AudioWidget *m_aw; - dmxWidget *m_dmxWidget; - olaThread *m_ola; MediaLibrary *m_mediaLibrary; + MiniAudioEngine m_mae; + QString m_currentMedia[MAX_LAYERS]; +#ifndef NOGUI + bool m_ui; + QTimer *m_refreshUi; + libreMediaServerAudioUi *m_lmsUi; +#endif private slots: - void olasetup(); void dmxInput(int layer, int channel, int value); +#ifndef NOGUI + void refreshUi(); +#endif }; #endif // LIBREMEDIASERVERAUDIO_H diff --git a/src/main.cpp b/src/main.cpp index bfadafe..99643b0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -18,34 +18,29 @@ 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(hasUi(argc, argv)); +#ifndef NOGUI + if (hasUi(argc, argv)) { - 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/miniaudioengine.cpp b/src/miniaudioengine.cpp index 6d5490c..7ec94ba 100644 --- a/src/miniaudioengine.cpp +++ b/src/miniaudioengine.cpp @@ -115,7 +115,6 @@ 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; } @@ -137,16 +136,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 result; + } + ret = 1000.0f * (lengthInPCMFrames / float(sampleRate)); return ret; } @@ -161,7 +165,7 @@ float MiniAudioEngine::getCursor(int layer) if (result != MA_SUCCESS) { qWarning("Can not get cursor %i", layer); - ret = 0; + ret = -1; } return ret; } @@ -231,6 +235,8 @@ void MiniAudioEngine::playbackChanged(int layer, Status status) ma_sound_set_looping(&m_currentSound[layer], false); ma_sound_start(&m_currentSound[layer]); break; + default: + break; } } @@ -244,3 +250,17 @@ void MiniAudioEngine::setCursor(int layer, int cursor) f = (cursor * f) / 65025; ma_sound_seek_to_pcm_frame(&m_currentSound[layer], f); } + +Status MiniAudioEngine::getStatus(int layer) +{ + if (m_mediaLoaded[layer] == ma_bool8(false)) + return Status::Iddle; + if (ma_sound_is_playing(&m_currentSound[layer])) { + if (ma_sound_is_looping(&m_currentSound[layer])) + return Status::PlayingLoop; + return Status::PlayingOnce; + } + if (this->getDuration(layer) > 0) + return Status::Paused; + return Status::Stopped; +} diff --git a/src/miniaudioengine.h b/src/miniaudioengine.h index 9549602..2bf224b 100644 --- a/src/miniaudioengine.h +++ b/src/miniaudioengine.h @@ -8,7 +8,7 @@ class MiniAudioEngine { - friend class AudioWidget; + friend class libreMediaServerAudio; public: MiniAudioEngine(); @@ -26,6 +26,7 @@ protected: float getCursor(int layer); void setCursor(int layer, int cursor); ma_result printFormatInfo(int layer); + Status getStatus(int layer); private: ma_resource_manager_config resourceManagerConfig; diff --git a/src/olathread.cpp b/src/olathread.cpp index ed0998b..471775c 100644 --- a/src/olathread.cpp +++ b/src/olathread.cpp @@ -63,39 +63,58 @@ 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; 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); + case DMX_FOLDER: + value *= 0x100; + value += buffer.Get(i.address + DMX_FILE); emit dmxOutput(i.layer,j,value); - volSent = true; + m_dmx[i.layer][DMX_FILE] = buffer.Get(i.address + DMX_FILE); + fileSent = true; break; - case ENTRY_POINT_COARSE: - value = (value * 0x100) + buffer.Get(i.address + ENTRY_POINT_FINE); + case DMX_FILE: + if (fileSent) + break; + value += buffer.Get(i.address + DMX_FOLDER) * 0x100; emit dmxOutput(i.layer,j,value); - entrySent = true; + m_dmx[i.layer][DMX_FOLDER] = buffer.Get(i.address + DMX_FOLDER); + fileSent = true; break; case VOLUME_FINE: - if (volSent == false) - { - value = (buffer.Get(i.address + VOLUME_COARSE) * 0x100) + value; - emit dmxOutput(i.layer,j,value); - } + value = (buffer.Get(i.address + VOLUME_COARSE) * 0x100) + value; + emit dmxOutput(i.layer,j,value); + m_dmx[i.layer][VOLUME_COARSE] = buffer.Get(i.address + VOLUME_COARSE); + volSent = true; + break; + case VOLUME_COARSE: + if (volSent) + break; + value = (value * 0x100) + buffer.Get(i.address + VOLUME_FINE); + emit dmxOutput(i.layer,j,value); + m_dmx[i.layer][VOLUME_FINE] = buffer.Get(i.address + VOLUME_FINE); + volSent = true; 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; + emit dmxOutput(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); + emit dmxOutput(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); @@ -127,7 +146,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..dcae35b 100644 --- a/src/olathread.h +++ b/src/olathread.h @@ -53,7 +53,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()); } } /** diff --git a/src/slidergroup.cpp b/src/slidergroup.cpp index e80a03f..2941bc4 100644 --- a/src/slidergroup.cpp +++ b/src/slidergroup.cpp @@ -1,45 +1,43 @@ #include "slidergroup.h" -SliderGroup::SliderGroup(const QString &title, \ - int min, +SliderGroup::SliderGroup(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); + this->setMaximumWidth(65); slider = new QSlider(Qt::Orientation::Vertical); slider->setFocusPolicy(Qt::StrongFocus); slider->setTickPosition(QSlider::TicksBothSides); slider->setTickInterval((max - min) / 11); slider->setSingleStep(1); slider->setRange(min, max); + slider->setMaximumWidth(65); valueBox = new QDoubleSpinBox(); valueBox->setFocusPolicy(Qt::NoFocus); valueBox->setButtonSymbols(QAbstractSpinBox::NoButtons); - valueBox->setMaximumWidth(45); + valueBox->setMaximumWidth(65); valueBox->setRange(min, max); valueBox->setDecimals(decimals); connect(slider, SIGNAL(valueChanged(int)), this, SLOT(sliderValueChanged(int))); - QVBoxLayout *slidersLayout = new QVBoxLayout(); - slidersLayout->addWidget(valueBox); - slidersLayout->addWidget(slider); - setLayout(slidersLayout); + layout->addWidget(slider); + layout->addWidget(valueBox); + this->setLayout(layout); } void SliderGroup::sliderValueChanged(int value) { + valueBox->setValue(value); if (valueBox->decimals() > 1) value /= 100.0f; emit valueChanged(value); - valueBox->setValue(value); }; void SliderGroup::setValue(float value) { - if (valueBox->decimals() > 1) - value *= 100.0f; slider->setValue(value); valueBox->setValue(value); } diff --git a/src/slidergroup.h b/src/slidergroup.h index f5bae60..8972d56 100644 --- a/src/slidergroup.h +++ b/src/slidergroup.h @@ -6,16 +6,15 @@ #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(int min, + int max, + int decimals, + QWidget *parent = nullptr); signals: void valueChanged(float value); From 88704cd7269cb6bfe5e377dff44e63fdc0e9b6ec Mon Sep 17 00:00:00 2001 From: snt Date: Sat, 4 May 2024 15:05:45 +0200 Subject: [PATCH 02/38] better ui, change colors and font --- docs/changelog.txt | 52 ++++++++++++++----------- docs/roadmap.txt | 18 ++++----- src/audiolayerwidget.cpp | 50 ++++++++++++++++-------- src/audiowidget.cpp | 2 + src/dmxwidget.cpp | 2 + src/libremediaserver-audio-gui.cpp | 8 ++++ src/libremediaserver-audio-gui.ui | 62 +++++++++--------------------- src/slidergroup.cpp | 29 ++++++++++++-- src/slidergroup.h | 3 +- 9 files changed, 129 insertions(+), 97 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 48b0ddf..22a7e1f 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -4,39 +4,47 @@ 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 (24/04/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); -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/roadmap.txt b/docs/roadmap.txt index 8ff57d7..6b12098 100644 --- a/docs/roadmap.txt +++ b/docs/roadmap.txt @@ -20,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, ...) 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 +41,13 @@ 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, load media files from ui. +- 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 +- New play 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. -- ¿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, i'm too happy.... +- Tests: errors on wrong conf file. diff --git a/src/audiolayerwidget.cpp b/src/audiolayerwidget.cpp index ffb8341..23dcb9a 100644 --- a/src/audiolayerwidget.cpp +++ b/src/audiolayerwidget.cpp @@ -8,9 +8,31 @@ AudioLayerWidget::AudioLayerWidget(QWidget *parent, int layer): { QVBoxLayout *layout = new QVBoxLayout; + QVBoxLayout *playback = new QVBoxLayout; + m_folderValue = new QLabel; + //m_folderValue->setMaximumWidth(160); + m_folderValue->setAlignment(Qt::AlignHCenter); + m_folderValue->setStyleSheet( + "color: white;" + "background-color: black;" + ); + playback->addWidget(m_folderValue); + m_fileValue = new QLabel; + //m_fileValue->setMaximumWidth(160); + m_fileValue->setAlignment(Qt::AlignHCenter); + 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]); - m_suspendResumeButton->setMaximumWidth(200); + //m_suspendResumeButton->setMaximumWidth(180); //connect(m_suspendResumeButton, SIGNAL(clicked()), SLOT(toggleSuspendResume())); layout->addWidget(m_suspendResumeButton); @@ -19,7 +41,7 @@ AudioLayerWidget::AudioLayerWidget(QWidget *parent, int layer): m_progress->setRange(0, 0); m_progress->setValue(0); m_progress->setFormat("%v / %m"); - m_progress->setMaximumWidth(200); + //m_progress->setMaximumWidth(180); layout->addWidget(m_progress); m_progressTime = new QTimeEdit; @@ -28,7 +50,7 @@ AudioLayerWidget::AudioLayerWidget(QWidget *parent, int layer): m_progressTime->setDisplayFormat("h:mm:ss:zzz"); m_progressTime->setReadOnly(true); m_progressTime->setButtonSymbols(QAbstractSpinBox::NoButtons); - m_progressTime->setMaximumWidth(88); + //m_progressTime->setMaximumWidth(80); m_progressTime->setFocusPolicy(Qt::NoFocus); m_totalTimeValue = new QTimeEdit; m_totalTimeValue->setObjectName("Track Length"); @@ -36,32 +58,28 @@ AudioLayerWidget::AudioLayerWidget(QWidget *parent, int layer): m_totalTimeValue->setDisplayFormat("h:mm:ss:zzz"); m_totalTimeValue->setReadOnly(true); m_totalTimeValue->setButtonSymbols(QAbstractSpinBox::NoButtons); - m_totalTimeValue->setMaximumWidth(88); + //m_totalTimeValue->setMaximumWidth(80); m_totalTimeValue->setFocusPolicy(Qt::NoFocus); QHBoxLayout *status = new QHBoxLayout; status->addWidget(m_progressTime); status->addWidget(m_totalTimeValue); layout->addLayout(status); - QVBoxLayout *playback = new QVBoxLayout; - m_folderValue = new QLabel; - m_folderValue->setMaximumWidth(200); - playback->addWidget(m_folderValue); - m_fileValue = new QLabel; - m_fileValue->setMaximumWidth(200); - playback->addWidget(m_fileValue); - layout->addLayout(playback); - QHBoxLayout *volumeBox = new QHBoxLayout; - m_volume = new SliderGroup(0 , 100, 2, NULL); + 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(0 , 255, 0, NULL); + 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(0 , 255, 0, NULL); + 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(float)), this, SLOT(pitchChanged(float))); layout->addLayout(volumeBox); + layout->setAlignment(Qt::AlignHCenter); + layout->setSpacing(0); + layout->setContentsMargins(2, 2, 2, 2); this->setLayout(layout); } diff --git a/src/audiowidget.cpp b/src/audiowidget.cpp index 69dc60d..316e43a 100644 --- a/src/audiowidget.cpp +++ b/src/audiowidget.cpp @@ -10,6 +10,8 @@ AudioWidget::AudioWidget() : 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(0, 0, 0, 0); setLayout(m_layout); } 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 index a300229..5c366ac 100644 --- a/src/libremediaserver-audio-gui.cpp +++ b/src/libremediaserver-audio-gui.cpp @@ -32,8 +32,16 @@ libreMediaServerAudioUi::libreMediaServerAudioUi(QWidget *parent) 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: gray;" + "selection-color: blue;" + "selection-background-color: green" + ); } libreMediaServerAudioUi::~libreMediaServerAudioUi() diff --git a/src/libremediaserver-audio-gui.ui b/src/libremediaserver-audio-gui.ui index 6ade744..65c83c2 100644 --- a/src/libremediaserver-audio-gui.ui +++ b/src/libremediaserver-audio-gui.ui @@ -1,19 +1,31 @@ - Santi Noreña belfegor@gmail.com + Santi Noreña lms@criptomart.net LibreMediaServerAudio - + 0 0 - 114 - 218 + 800 + 800 + + + Unifont + 12 + 75 + true + + LibreMediaServer + + + ../../../../criptomart/artwork/logo_v2_criptomart.net.png../../../../criptomart/artwork/logo_v2_criptomart.net.png + @@ -21,7 +33,7 @@ 0 0 114 - 22 + 21 @@ -32,47 +44,9 @@ - - - Exit - - - - - Open Configuration... - - - - - Save Configuration... - - - - - Settings... - - - - - false - - - Init - - - - - IP Address - - - - - Make Thumbs - - - OLA Setup... + OLA Setup diff --git a/src/slidergroup.cpp b/src/slidergroup.cpp index 2941bc4..4bef674 100644 --- a/src/slidergroup.cpp +++ b/src/slidergroup.cpp @@ -1,6 +1,7 @@ #include "slidergroup.h" -SliderGroup::SliderGroup(int min, +SliderGroup::SliderGroup(QString name, + int min, int max, int decimals, QWidget *parent) @@ -8,23 +9,43 @@ SliderGroup::SliderGroup(int min, { QVBoxLayout *layout = new QVBoxLayout; layout->setAlignment(Qt::AlignHCenter); - this->setMaximumWidth(65); + 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(100); slider->setSingleStep(1); slider->setRange(min, max); - slider->setMaximumWidth(65); + //slider->setMaximumWidth(40); + slider->setToolTip(name); + slider->setStyleSheet("QSlider {" + "border: 1px solid #999999;" + "margin: 0px;" + "height: 200px;" + "width: 40px;}" + ); + slider->setContentsMargins(0, 0, 0, 0); valueBox = new QDoubleSpinBox(); valueBox->setFocusPolicy(Qt::NoFocus); valueBox->setButtonSymbols(QAbstractSpinBox::NoButtons); - valueBox->setMaximumWidth(65); + //valueBox->setMaximumWidth(40); valueBox->setRange(min, max); 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))); layout->addWidget(slider); layout->addWidget(valueBox); + this->setStyleSheet("border: 1px solid #999999;" + "width: 40px;" + "margin: 0px;" + ); + layout->setSpacing(0); + layout->setContentsMargins(0, 0, 0, 0); this->setLayout(layout); } diff --git a/src/slidergroup.h b/src/slidergroup.h index 8972d56..1d8cc8c 100644 --- a/src/slidergroup.h +++ b/src/slidergroup.h @@ -11,7 +11,8 @@ class SliderGroup : public QWidget Q_OBJECT public: - SliderGroup(int min, + SliderGroup(QString name, + int min, int max, int decimals, QWidget *parent = nullptr); From 5a7a82736febc489a258d9c53c342f9452781714 Mon Sep 17 00:00:00 2001 From: snt Date: Sat, 4 May 2024 16:45:05 +0200 Subject: [PATCH 03/38] working UI controls --- src/audiolayerwidget.cpp | 25 +++++++++++++------------ src/audiolayerwidget.h | 6 +++--- src/audiowidget.cpp | 29 ++++++++++------------------- src/audiowidget.h | 16 ++++++---------- src/libremediaserver-audio-gui.cpp | 2 +- src/libremediaserver-audio.cpp | 22 ++++++++++++++++++++++ src/libremediaserver-audio.h | 2 ++ src/medialibrary.h | 6 ------ src/slidergroup.cpp | 2 -- src/slidergroup.h | 2 +- 10 files changed, 58 insertions(+), 54 deletions(-) diff --git a/src/audiolayerwidget.cpp b/src/audiolayerwidget.cpp index 23dcb9a..2dd1d94 100644 --- a/src/audiolayerwidget.cpp +++ b/src/audiolayerwidget.cpp @@ -24,7 +24,6 @@ AudioLayerWidget::AudioLayerWidget(QWidget *parent, int layer): "color: white;" "background-color: black;" ); - playback->addWidget(m_fileValue); playback->setSpacing(0); playback->setContentsMargins(0, 0, 0, 0); @@ -33,7 +32,7 @@ AudioLayerWidget::AudioLayerWidget(QWidget *parent, int layer): m_suspendResumeButton = new QPushButton(this); m_suspendResumeButton->setText(StatusStr[Status::Iddle]); //m_suspendResumeButton->setMaximumWidth(180); - //connect(m_suspendResumeButton, SIGNAL(clicked()), SLOT(toggleSuspendResume())); + connect(m_suspendResumeButton, SIGNAL(clicked()), SLOT(toggleSuspendResume())); layout->addWidget(m_suspendResumeButton); m_progress = new QProgressBar(this); @@ -47,19 +46,21 @@ AudioLayerWidget::AudioLayerWidget(QWidget *parent, int layer): m_progressTime = new QTimeEdit; m_progressTime->setToolTip("Current Time"); m_progressTime->setObjectName("Current Time"); - m_progressTime->setDisplayFormat("h:mm:ss:zzz"); + m_progressTime->setDisplayFormat("mm:ss:zz"); m_progressTime->setReadOnly(true); m_progressTime->setButtonSymbols(QAbstractSpinBox::NoButtons); - //m_progressTime->setMaximumWidth(80); + m_progressTime->setMaximumWidth(75); m_progressTime->setFocusPolicy(Qt::NoFocus); + m_progressTime->setAlignment(Qt::AlignHCenter); m_totalTimeValue = new QTimeEdit; m_totalTimeValue->setObjectName("Track Length"); m_totalTimeValue->setToolTip("Track Length"); - m_totalTimeValue->setDisplayFormat("h:mm:ss:zzz"); + m_totalTimeValue->setDisplayFormat("mm:ss:zz"); m_totalTimeValue->setReadOnly(true); m_totalTimeValue->setButtonSymbols(QAbstractSpinBox::NoButtons); - //m_totalTimeValue->setMaximumWidth(80); + m_totalTimeValue->setMaximumWidth(75); m_totalTimeValue->setFocusPolicy(Qt::NoFocus); + m_totalTimeValue->setAlignment(Qt::AlignHCenter); QHBoxLayout *status = new QHBoxLayout; status->addWidget(m_progressTime); status->addWidget(m_totalTimeValue); @@ -67,15 +68,15 @@ AudioLayerWidget::AudioLayerWidget(QWidget *parent, int layer): 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))); + 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(float)), this, SLOT(panChanged(float))); + 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(float)), this, SLOT(pitchChanged(float))); + connect(m_pitch, SIGNAL(valueChanged(int)), this, SLOT(pitchChanged(int))); layout->addLayout(volumeBox); layout->setAlignment(Qt::AlignHCenter); layout->setSpacing(0); @@ -89,17 +90,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)); } diff --git a/src/audiolayerwidget.h b/src/audiolayerwidget.h index df5853a..4f89d17 100644 --- a/src/audiolayerwidget.h +++ b/src/audiolayerwidget.h @@ -39,9 +39,9 @@ private: public slots: void toggleSuspendResume(); - void volumeChanged(float vol); - void panChanged(float pan); - void pitchChanged(float pitch); + void volumeChanged(int vol); + void panChanged(int pan); + void pitchChanged(int pitch); void fileLoaded(QString file); void durationChanged(float dur); void refreshUi(float progress); diff --git a/src/audiowidget.cpp b/src/audiowidget.cpp index 316e43a..58ca7fd 100644 --- a/src/audiowidget.cpp +++ b/src/audiowidget.cpp @@ -1,17 +1,18 @@ #include "audiowidget.h" -AudioWidget::AudioWidget() : - m_layout(new QHBoxLayout()) +AudioWidget::AudioWidget(QWidget *parent) : + QWidget(parent) + , m_layout(new QHBoxLayout()) { for (int i= 0; i < Settings::getInstance()->getLayersNumber(); 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))); } m_layout->setSpacing(0); - m_layout->setContentsMargins(0, 0, 0, 0); + m_layout->setContentsMargins(1, 1, 1, 1); setLayout(m_layout); } @@ -49,22 +50,12 @@ void AudioWidget::cursorChanged(int layer, float cursor) AudioLayerWidget *alw = dynamic_cast(item->widget()); alw->refreshUi(cursor); } - -void AudioWidget::uiSliderAction(int layer, Slider s, int value) +/* +void AudioWidget::uiSliderAction(int layer, Slider s, float value) { - switch (s){ - case Slider::Volume: - emit uiVolChanged(layer, value); - break; - case Slider::Pan: - emit uiPanChanged(layer, value); - break; - case Slider::Pitch: - emit uiPitchChanged(layer, value); - break; - } + emit uiSliderChanged(layer, s, value); } void AudioWidget::uiChangePlaybackStatus(int layer, Status s) { emit uiPlaybackChanged(layer, s); -} +}*/ diff --git a/src/audiowidget.h b/src/audiowidget.h index 3314127..ff3adea 100644 --- a/src/audiowidget.h +++ b/src/audiowidget.h @@ -13,7 +13,7 @@ class AudioWidget : public QWidget Q_OBJECT public: - AudioWidget(); + AudioWidget(QWidget *parent = nullptr); void mediaLoaded(int layer, QString media, float duration); void volChanged(int layer, float vol); void panChanged(int layer, int pan); @@ -21,18 +21,14 @@ public: void playbackChanged(int layer, Status status); void cursorChanged(int layer, float cursor); QHBoxLayout *m_layout; - +/* public slots: - void uiSliderAction(int layer, Slider s, int value); + void uiSliderAction(int layer, Slider s, float value); void uiChangePlaybackStatus(int layer, Status s); - +*/ signals: - void uiMediaLoaded(int layer, QString media ); - void uiVolChanged(int layer, float vol); - void uiPanChanged(int layer, int pan); - void uiPitchChanged(int layer, int pitch); - void uiPlaybackChanged(int layer, Status status); - void uiEntryPointChanged(int layer, int cursor); + void uiPlaybackChanged(int layer, Status s); + void uiSliderChanged(int layer, Slider s, int vol); }; #endif // AUDIOWIDGET_H diff --git a/src/libremediaserver-audio-gui.cpp b/src/libremediaserver-audio-gui.cpp index 5c366ac..2d92918 100644 --- a/src/libremediaserver-audio-gui.cpp +++ b/src/libremediaserver-audio-gui.cpp @@ -26,7 +26,7 @@ libreMediaServerAudioUi::libreMediaServerAudioUi(QWidget *parent) { ui.setupUi(this); this->setWindowTitle(VERSION); - m_aw = new AudioWidget; + m_aw = new AudioWidget(this); setCentralWidget(m_aw); m_dmxWidget = new dmxWidget(this); QDockWidget *topWidget = new QDockWidget(tr("Master"), this); diff --git a/src/libremediaserver-audio.cpp b/src/libremediaserver-audio.cpp index d049c44..deb1a7d 100644 --- a/src/libremediaserver-audio.cpp +++ b/src/libremediaserver-audio.cpp @@ -134,6 +134,28 @@ void libreMediaServerAudio::setUi(libreMediaServerAudioUi *lmsUi) { m_lmsUi = lmsUi; 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))); }; +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) +{ + m_mae.playbackChanged(layer, s); +} + #endif diff --git a/src/libremediaserver-audio.h b/src/libremediaserver-audio.h index 923a42b..9c8f152 100644 --- a/src/libremediaserver-audio.h +++ b/src/libremediaserver-audio.h @@ -54,6 +54,8 @@ private slots: void dmxInput(int layer, int channel, int value); #ifndef NOGUI void refreshUi(); + void uiSliderChanged(int layer, Slider s, int value); + void uiPlaybackChanged(int layer, Status s); #endif }; diff --git a/src/medialibrary.h b/src/medialibrary.h index 5a0b55b..8c2eb07 100644 --- a/src/medialibrary.h +++ b/src/medialibrary.h @@ -27,12 +27,6 @@ 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(); diff --git a/src/slidergroup.cpp b/src/slidergroup.cpp index 4bef674..e58588e 100644 --- a/src/slidergroup.cpp +++ b/src/slidergroup.cpp @@ -52,8 +52,6 @@ SliderGroup::SliderGroup(QString name, void SliderGroup::sliderValueChanged(int value) { valueBox->setValue(value); - if (valueBox->decimals() > 1) - value /= 100.0f; emit valueChanged(value); }; diff --git a/src/slidergroup.h b/src/slidergroup.h index 1d8cc8c..f597a5b 100644 --- a/src/slidergroup.h +++ b/src/slidergroup.h @@ -18,7 +18,7 @@ public: QWidget *parent = nullptr); signals: - void valueChanged(float value); + void valueChanged(int value); public slots: void setValue(float value); From 389966782d739776fe570fdf48a5da1dcb69e6ca Mon Sep 17 00:00:00 2001 From: snt Date: Sat, 4 May 2024 19:35:12 +0200 Subject: [PATCH 04/38] =?UTF-8?q?open=20file=20dialog=20clicking=20on=20fi?= =?UTF-8?q?le/folder=20labels.=20se=20atasca=20y=20se=20pone=20a=20100%,?= =?UTF-8?q?=20los=20faders=20no=20refrescan=20como=20debieran,=20parece=20?= =?UTF-8?q?que=20se=20saturan=20las=20se=C3=B1ales.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- libremediaserver-audio.pro | 2 ++ src/audiolayerwidget.cpp | 29 +++++++++++++++++++++++++---- src/audiolayerwidget.h | 13 +++++++++---- src/audiowidget.cpp | 10 +--------- src/audiowidget.h | 7 ++----- src/clickablelabel.cpp | 13 +++++++++++++ src/clickablelabel.h | 22 ++++++++++++++++++++++ src/defines.h | 6 +++--- src/libremediaserver-audio.cpp | 6 ++++++ src/libremediaserver-audio.h | 1 + src/miniaudioengine.cpp | 2 ++ 11 files changed, 86 insertions(+), 25 deletions(-) create mode 100644 src/clickablelabel.cpp create mode 100644 src/clickablelabel.h diff --git a/libremediaserver-audio.pro b/libremediaserver-audio.pro index 154d1fe..42ef0d1 100644 --- a/libremediaserver-audio.pro +++ b/libremediaserver-audio.pro @@ -2,6 +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 \ @@ -16,6 +17,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 \ diff --git a/src/audiolayerwidget.cpp b/src/audiolayerwidget.cpp index 2dd1d94..ee112dd 100644 --- a/src/audiolayerwidget.cpp +++ b/src/audiolayerwidget.cpp @@ -9,7 +9,7 @@ AudioLayerWidget::AudioLayerWidget(QWidget *parent, int layer): QVBoxLayout *layout = new QVBoxLayout; QVBoxLayout *playback = new QVBoxLayout; - m_folderValue = new QLabel; + m_folderValue = new ClickableLabel; //m_folderValue->setMaximumWidth(160); m_folderValue->setAlignment(Qt::AlignHCenter); m_folderValue->setStyleSheet( @@ -17,7 +17,9 @@ AudioLayerWidget::AudioLayerWidget(QWidget *parent, int layer): "background-color: black;" ); playback->addWidget(m_folderValue); - m_fileValue = new QLabel; + 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::AlignHCenter); m_fileValue->setStyleSheet( @@ -52,6 +54,7 @@ AudioLayerWidget::AudioLayerWidget(QWidget *parent, int layer): m_progressTime->setMaximumWidth(75); 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"); @@ -61,6 +64,7 @@ AudioLayerWidget::AudioLayerWidget(QWidget *parent, int layer): m_totalTimeValue->setMaximumWidth(75); 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); @@ -115,13 +119,28 @@ void AudioLayerWidget::toggleSuspendResume() break; case Status::Paused: case Status::Stopped: - this->setPlaybackStatus(Status::PlayingOnce); + //this->setPlaybackStatus(Status::PlayingOnce); emit uiPlaybackChanged(m_layer, Status::PlayingOnce); 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->fileLoaded(fileNames.at(0)); +} + // from DMX signals void AudioLayerWidget::setVol(float vol) { @@ -157,10 +176,12 @@ void AudioLayerWidget::fileLoaded(QString file) void AudioLayerWidget::setPlaybackStatus(Status status) { + if (StatusStr[status] == m_suspendResumeButton->text()) + return; + m_suspendResumeButton->blockSignals(true); m_status = status; if (status == Status::Stopped) m_progress->setValue(0); - m_suspendResumeButton->blockSignals(true); m_suspendResumeButton->setText(StatusStr[status]); m_suspendResumeButton->blockSignals(false); } diff --git a/src/audiolayerwidget.h b/src/audiolayerwidget.h index 4f89d17..c0c6b3b 100644 --- a/src/audiolayerwidget.h +++ b/src/audiolayerwidget.h @@ -3,11 +3,13 @@ #include #include -#include +#include #include #include "defines.h" #include "slidergroup.h" +#include "clickablelabel.h" +#include "settings.h" class AudioLayerWidget : public QWidget { @@ -28,8 +30,8 @@ private: Status m_status; int m_layer; QPushButton *m_suspendResumeButton; - QLabel *m_fileValue; - QLabel * m_folderValue; + ClickableLabel *m_fileValue; + ClickableLabel * m_folderValue; SliderGroup *m_volume; SliderGroup *m_pan; SliderGroup *m_pitch; @@ -46,10 +48,13 @@ public slots: void durationChanged(float dur); void refreshUi(float progress); +private slots: + void openMediaDialog(); + 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 58ca7fd..47d7ce5 100644 --- a/src/audiowidget.cpp +++ b/src/audiowidget.cpp @@ -10,6 +10,7 @@ AudioWidget::AudioWidget(QWidget *parent) : 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_layout->setSpacing(0); m_layout->setContentsMargins(1, 1, 1, 1); @@ -50,12 +51,3 @@ void AudioWidget::cursorChanged(int layer, float cursor) AudioLayerWidget *alw = dynamic_cast(item->widget()); alw->refreshUi(cursor); } -/* -void AudioWidget::uiSliderAction(int layer, Slider s, float value) -{ - emit uiSliderChanged(layer, s, value); -} - -void AudioWidget::uiChangePlaybackStatus(int layer, Status s) { - emit uiPlaybackChanged(layer, s); -}*/ diff --git a/src/audiowidget.h b/src/audiowidget.h index ff3adea..a293a0a 100644 --- a/src/audiowidget.h +++ b/src/audiowidget.h @@ -21,14 +21,11 @@ public: void playbackChanged(int layer, Status status); void cursorChanged(int layer, float cursor); QHBoxLayout *m_layout; -/* -public slots: - void uiSliderAction(int layer, Slider s, float value); - void uiChangePlaybackStatus(int layer, Status s); -*/ + 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..333219b --- /dev/null +++ b/src/clickablelabel.cpp @@ -0,0 +1,13 @@ +#include "clickablelabel.h" + +ClickableLabel::ClickableLabel(QWidget *parent, Qt::WindowFlags f) + : QLabel{parent} +{ + +} + +ClickableLabel::~ClickableLabel() {} + +void ClickableLabel::mousePressEvent(QMouseEvent* 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 dee7850..633b06c 100644 --- a/src/defines.h +++ b/src/defines.h @@ -3,10 +3,10 @@ #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 UI_REFRESH_TIME 1000 // struct where save the DMX settings for each layer struct dmxSetting { diff --git a/src/libremediaserver-audio.cpp b/src/libremediaserver-audio.cpp index deb1a7d..b59a8f1 100644 --- a/src/libremediaserver-audio.cpp +++ b/src/libremediaserver-audio.cpp @@ -126,6 +126,7 @@ void libreMediaServerAudio::refreshUi() { Status s = m_mae.getStatus(i); if (s == Status::PlayingOnce || s == Status::PlayingLoop) { m_lmsUi->m_aw->cursorChanged(i, m_mae.getCursor(i)); + //m_lmsUi->m_aw->playbackChanged(i, s); } } } @@ -136,6 +137,7 @@ void libreMediaServerAudio::setUi(libreMediaServerAudioUi *lmsUi) 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))); }; void libreMediaServerAudio::uiSliderChanged(int layer, Slider s, int value) @@ -158,4 +160,8 @@ void libreMediaServerAudio::uiPlaybackChanged(int layer, Status s) m_mae.playbackChanged(layer, s); } +void libreMediaServerAudio::uiLoadMedia(int layer, QString s) +{ + m_mae.loadMedia(layer, s.toLatin1().data()); +} #endif diff --git a/src/libremediaserver-audio.h b/src/libremediaserver-audio.h index 9c8f152..6b80f97 100644 --- a/src/libremediaserver-audio.h +++ b/src/libremediaserver-audio.h @@ -56,6 +56,7 @@ 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 }; diff --git a/src/miniaudioengine.cpp b/src/miniaudioengine.cpp index 7ec94ba..1f2c0b4 100644 --- a/src/miniaudioengine.cpp +++ b/src/miniaudioengine.cpp @@ -122,6 +122,7 @@ ma_result MiniAudioEngine::loadMedia(int layer, char *file) MA_SOUND_FLAG_NO_SPATIALIZATION \ /*| MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE \ | MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC \ + | MA_SOUND_FLAG_NO_PITCH \ | MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM \*/ , NULL, NULL, &m_currentSound[layer]); if (result != MA_SUCCESS) @@ -249,6 +250,7 @@ void MiniAudioEngine::setCursor(int layer, int cursor) 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); + // ToDo: change the loop entry point too } Status MiniAudioEngine::getStatus(int layer) From ef653553d9801b495f0d4ab73c05188517718518 Mon Sep 17 00:00:00 2001 From: snt Date: Sat, 4 May 2024 19:38:43 +0200 Subject: [PATCH 05/38] update magicq head qith new personality --- docs/LibreMediaServer_Audio.hed | 146 ++++++++++++++++---------------- 1 file changed, 73 insertions(+), 73 deletions(-) diff --git a/docs/LibreMediaServer_Audio.hed b/docs/LibreMediaServer_Audio.hed index 5da3a28..58d0fd5 100644 --- a/docs/LibreMediaServer_Audio.hed +++ b/docs/LibreMediaServer_Audio.hed @@ -1,76 +1,76 @@ ް׆ˌĠ򝤫鿰맫͓Ԕ ҊÅЁ䍡 ӽ -䉭ėĈ -ϛ -Ӧ -ڑ -ė - -ݍ -Ȫ -웣傪Ⓨ֚ -ކՂ -߸“ -Ŷ -ѝ -ωƀ - - - - -ŕ -ʷí -̙ -򴹤 -ԥѼ܅ -𗹣َ -̚ -Რ - -Ņ -⽬ -̫ -喤쏎ʁǏ - -߬ػс -썎ޛ - -쏎 - - - -Ճ - -尨 -ގ - -ލ -ƕ - -ŗ - - - - - -ˤ - - - - - -쏐 - - - -쏎 -󩼿 - - - -ÛƘ - - - - +Ꭸʆ +ɑ +ɬ +򉇁 +Ԑɉ + +ٿդϨ҃ +񀠧ڔ +ڑŌ +򻴧 + + + + + + + + +쏎ބ +힇ݳ + +Ԓlj + +έ +倬쏎 + +޽ڊ + +؛ +ŕ +ðݑ +ȕ +˛ +̽ɧ + +𓎍 + + +ً + +ʐ +ï +뷪 + + +܋ +Ò +뻡 +ǒ + +𓎍 +ʟ + +㍛ + + + + + + +𓎑 + + + +ҟ + + + +ف + + + + From 246a8a2f98a201883d51177c5fb89998282b5976 Mon Sep 17 00:00:00 2001 From: snt Date: Sun, 5 May 2024 14:39:21 +0200 Subject: [PATCH 06/38] =?UTF-8?q?restringe=20las=20se=C3=B1ales=20de=20los?= =?UTF-8?q?=20sliders,=20en=20vez=20de=20actualizar=20en=20directo,=20s?= =?UTF-8?q?=C3=B3lo=20actualiza=20con=20el=20timer=20refreshGui.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/roadmap.txt | 2 +- libremediaserver-audio.pro | 4 +-- src/clickablelabel.cpp | 3 +- src/defines.h | 2 +- src/libremediaserver-audio.cpp | 60 +++++++++++++++++++--------------- src/libremediaserver-audio.h | 2 ++ src/miniaudioengine.cpp | 7 ++-- src/slidergroup.cpp | 7 +++- 8 files changed, 53 insertions(+), 34 deletions(-) diff --git a/docs/roadmap.txt b/docs/roadmap.txt index 6b12098..894c32c 100644 --- a/docs/roadmap.txt +++ b/docs/roadmap.txt @@ -49,5 +49,5 @@ v 0.2.1 - SettingsDialog. - Load/save conf file. - ¿Exit Point? is it needed? -- Hardening: check return errors, i'm too happy.... +- Hardening: check return errors, catch execptions, i'm too happy.... - Tests: errors on wrong conf file. diff --git a/libremediaserver-audio.pro b/libremediaserver-audio.pro index 42ef0d1..41d6ecd 100644 --- a/libremediaserver-audio.pro +++ b/libremediaserver-audio.pro @@ -30,9 +30,9 @@ SOURCES += src/main.cpp \ src/settings.cpp \ src/slidergroup.cpp FORMS += src/libremediaserver-audio-gui.ui -CCFLAG += -msse2 -mavx2 #-fsanitize=address -g -O0 +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/clickablelabel.cpp b/src/clickablelabel.cpp index 333219b..623493d 100644 --- a/src/clickablelabel.cpp +++ b/src/clickablelabel.cpp @@ -3,11 +3,12 @@ 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/defines.h b/src/defines.h index 633b06c..a4eeea5 100644 --- a/src/defines.h +++ b/src/defines.h @@ -6,7 +6,7 @@ #define LICENSE "GPL 3 Licensed. See LICENSE.txt." #define DEFAULT_FILE "lms-audio.xlm" #define MAX_LAYERS 4 -#define UI_REFRESH_TIME 1000 +#define UI_REFRESH_TIME 200 // struct where save the DMX settings for each layer struct dmxSetting { diff --git a/src/libremediaserver-audio.cpp b/src/libremediaserver-audio.cpp index b59a8f1..d146e10 100644 --- a/src/libremediaserver-audio.cpp +++ b/src/libremediaserver-audio.cpp @@ -28,6 +28,12 @@ libreMediaServerAudio::libreMediaServerAudio(bool gui) set->readFile(); m_mediaLibrary = new MediaLibrary; m_mediaLibrary->initMediaLibrary(); + for (int i = 0; i < MAX_LAYERS; i++) { + m_currentMedia[i] = Status::Iddle; + m_updateUi[i][0] = -1; + m_updateUi[i][1] = -1; + m_updateUi[i][2] = -1; + } m_ola = new olaThread(this, set->getLayersNumber()); Q_CHECK_PTR(m_ola); m_ola->blockSignals(true); @@ -54,7 +60,7 @@ libreMediaServerAudio::~libreMediaServerAudio() 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; @@ -66,40 +72,32 @@ void libreMediaServerAudio::dmxInput(int layer, int channel, int value) return; if (QFile::exists(mediaFile)){ m_mae.loadMedia(layer, mediaFile.toLatin1().data()); -#ifndef NOGUI + m_currentMedia[layer] = mediaFile; +#ifndef NOGIO if (m_ui) m_lmsUi->m_aw->mediaLoaded(layer, mediaFile, m_mae.getDuration(layer)); #endif - m_currentMedia[layer] = mediaFile; } + return; } else if (channel == VOLUME_COARSE || channel == VOLUME_FINE) { - m_mae.volChanged(layer, (value / 65025.0f)); -#ifndef NOGUI - if (m_ui) - m_lmsUi->m_aw->volChanged(layer, (value / 650.25f)); -#endif + float tmp = value / 65025.0f; + m_mae.volChanged(layer, tmp); + m_updateUi[layer][0] = tmp * 100; } else if (channel == PAN) { m_mae.panChanged(layer, value); -#ifndef NOGUI - if (m_ui) - m_lmsUi->m_aw->panChanged(layer, value); -#endif + m_updateUi[layer][1] = value; } else if (channel == PITCH) { m_mae.pitchChanged(layer, value); -#ifndef NOGUI - if (m_ui) - m_lmsUi->m_aw->pitchChanged(layer, value); -#endif + m_updateUi[layer][2] = value; } else if (channel == ENTRY_POINT_COARSE || channel == ENTRY_POINT_FINE) { m_mae.setCursor(layer, value); #ifndef NOGUI - if (m_ui) - m_lmsUi->m_aw->cursorChanged(layer, m_mae.getCursor(layer)); + m_lmsUi->m_aw->cursorChanged(layer, m_mae.getCursor(layer)); #endif - + return; } else if (channel == PLAYBACK && value > 0) { - Status s = m_mae.getStatus(layer); aux = value / 25; + Status s = m_currentStatus[layer]; if (s != aux) { if (aux == 0) s = Status::PlayingOnce; @@ -110,9 +108,9 @@ void libreMediaServerAudio::dmxInput(int layer, int channel, int value) else if (aux == 3) s = Status::PlayingLoop; m_mae.playbackChanged(layer, s); + m_currentStatus[layer] = s; #ifndef NOGUI - if (m_ui) - m_lmsUi->m_aw->playbackChanged(layer, s); + if (m_ui) m_lmsUi->m_aw->playbackChanged(layer, s); #endif } } @@ -120,13 +118,23 @@ void libreMediaServerAudio::dmxInput(int layer, int channel, int value) #ifndef NOGUI void libreMediaServerAudio::refreshUi() { - if (!m_ui) - return; + if (!m_ui) return; for (int i= 0; i < Settings::getInstance()->getLayersNumber(); i++ ) { - Status s = m_mae.getStatus(i); + Status s = m_currentStatus[i]; if (s == Status::PlayingOnce || s == Status::PlayingLoop) { m_lmsUi->m_aw->cursorChanged(i, m_mae.getCursor(i)); - //m_lmsUi->m_aw->playbackChanged(i, s); + } + 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; } } } diff --git a/src/libremediaserver-audio.h b/src/libremediaserver-audio.h index 6b80f97..9782d88 100644 --- a/src/libremediaserver-audio.h +++ b/src/libremediaserver-audio.h @@ -44,10 +44,12 @@ private: MediaLibrary *m_mediaLibrary; MiniAudioEngine m_mae; QString m_currentMedia[MAX_LAYERS]; + Status m_currentStatus[MAX_LAYERS]; #ifndef NOGUI bool m_ui; QTimer *m_refreshUi; libreMediaServerAudioUi *m_lmsUi; + int m_updateUi[MAX_LAYERS][3]; #endif private slots: diff --git a/src/miniaudioengine.cpp b/src/miniaudioengine.cpp index 1f2c0b4..8dc38da 100644 --- a/src/miniaudioengine.cpp +++ b/src/miniaudioengine.cpp @@ -2,7 +2,9 @@ MiniAudioEngine::MiniAudioEngine() { - + for (int i =0; i < MAX_LAYERS; i++) { + m_mediaLoaded[i] = false; + } } void MiniAudioEngine::audioDataCallback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) @@ -194,6 +196,7 @@ void MiniAudioEngine::volChanged(int layer, float vol) if (m_mediaLoaded[layer] == false) return; ma_sound_group_set_volume(&m_currentSound[layer], vol); + qDebug() << "vol: " << vol; } void MiniAudioEngine::panChanged(int layer, float value) @@ -255,7 +258,7 @@ void MiniAudioEngine::setCursor(int layer, int cursor) Status MiniAudioEngine::getStatus(int layer) { - if (m_mediaLoaded[layer] == ma_bool8(false)) + if (m_mediaLoaded[layer] == false) return Status::Iddle; if (ma_sound_is_playing(&m_currentSound[layer])) { if (ma_sound_is_looping(&m_currentSound[layer])) diff --git a/src/slidergroup.cpp b/src/slidergroup.cpp index e58588e..6120093 100644 --- a/src/slidergroup.cpp +++ b/src/slidergroup.cpp @@ -57,6 +57,11 @@ void SliderGroup::sliderValueChanged(int value) void SliderGroup::setValue(float value) { - 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); } From d34b972a549a315b40977766e8f000af375caa4c Mon Sep 17 00:00:00 2001 From: snt Date: Sun, 5 May 2024 17:36:20 +0200 Subject: [PATCH 07/38] mejor rendimiento --- src/audiolayerwidget.cpp | 14 +++++------ src/audiolayerwidget.h | 2 +- src/audiowidget.cpp | 2 +- src/defines.h | 2 +- src/libremediaserver-audio-gui.ui | 8 +++--- src/libremediaserver-audio.cpp | 42 ++++++++++++++----------------- src/libremediaserver-audio.h | 2 +- src/main.cpp | 1 - src/miniaudioengine.cpp | 3 ++- src/miniaudioengine.h | 1 + src/slidergroup.cpp | 18 +++++++------ 11 files changed, 47 insertions(+), 48 deletions(-) diff --git a/src/audiolayerwidget.cpp b/src/audiolayerwidget.cpp index ee112dd..563eeb9 100644 --- a/src/audiolayerwidget.cpp +++ b/src/audiolayerwidget.cpp @@ -51,7 +51,7 @@ AudioLayerWidget::AudioLayerWidget(QWidget *parent, int layer): m_progressTime->setDisplayFormat("mm:ss:zz"); m_progressTime->setReadOnly(true); m_progressTime->setButtonSymbols(QAbstractSpinBox::NoButtons); - m_progressTime->setMaximumWidth(75); + m_progressTime->setMinimumWidth(80); m_progressTime->setFocusPolicy(Qt::NoFocus); m_progressTime->setAlignment(Qt::AlignHCenter); m_progressTime->setContentsMargins(0,0,0,0); @@ -61,7 +61,7 @@ AudioLayerWidget::AudioLayerWidget(QWidget *parent, int layer): m_totalTimeValue->setDisplayFormat("mm:ss:zz"); m_totalTimeValue->setReadOnly(true); m_totalTimeValue->setButtonSymbols(QAbstractSpinBox::NoButtons); - m_totalTimeValue->setMaximumWidth(75); + m_totalTimeValue->setMinimumWidth(80); m_totalTimeValue->setFocusPolicy(Qt::NoFocus); m_totalTimeValue->setAlignment(Qt::AlignHCenter); m_totalTimeValue->setContentsMargins(0,0,0,0); @@ -119,8 +119,8 @@ 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; } @@ -176,12 +176,12 @@ void AudioLayerWidget::fileLoaded(QString file) void AudioLayerWidget::setPlaybackStatus(Status status) { - if (StatusStr[status] == m_suspendResumeButton->text()) + if (!strcmp(StatusStr[status], m_suspendResumeButton->text().toLatin1().constData())) return; m_suspendResumeButton->blockSignals(true); m_status = status; if (status == Status::Stopped) - m_progress->setValue(0); + refreshCurrentTime(0); m_suspendResumeButton->setText(StatusStr[status]); m_suspendResumeButton->blockSignals(false); } @@ -200,7 +200,7 @@ void AudioLayerWidget::durationChanged(float dur) m_totalTimeValue->blockSignals(false); } -void AudioLayerWidget::refreshUi(float progress) +void AudioLayerWidget::refreshCurrentTime(float progress) { progress *= 1000; m_progress->blockSignals(true); diff --git a/src/audiolayerwidget.h b/src/audiolayerwidget.h index c0c6b3b..1c0ee83 100644 --- a/src/audiolayerwidget.h +++ b/src/audiolayerwidget.h @@ -46,7 +46,7 @@ public slots: void pitchChanged(int pitch); void fileLoaded(QString file); void durationChanged(float dur); - void refreshUi(float progress); + void refreshCurrentTime(float progress); private slots: void openMediaDialog(); diff --git a/src/audiowidget.cpp b/src/audiowidget.cpp index 47d7ce5..3d17c2d 100644 --- a/src/audiowidget.cpp +++ b/src/audiowidget.cpp @@ -49,5 +49,5 @@ void AudioWidget::cursorChanged(int layer, float cursor) { QLayoutItem * const item = m_layout->itemAt(layer); AudioLayerWidget *alw = dynamic_cast(item->widget()); - alw->refreshUi(cursor); + alw->refreshCurrentTime(cursor); } diff --git a/src/defines.h b/src/defines.h index a4eeea5..f8a2312 100644 --- a/src/defines.h +++ b/src/defines.h @@ -6,7 +6,7 @@ #define LICENSE "GPL 3 Licensed. See LICENSE.txt." #define DEFAULT_FILE "lms-audio.xlm" #define MAX_LAYERS 4 -#define UI_REFRESH_TIME 200 +#define UI_REFRESH_TIME 93 // struct where save the DMX settings for each layer struct dmxSetting { diff --git a/src/libremediaserver-audio-gui.ui b/src/libremediaserver-audio-gui.ui index 65c83c2..c0f11ad 100644 --- a/src/libremediaserver-audio-gui.ui +++ b/src/libremediaserver-audio-gui.ui @@ -2,13 +2,13 @@ Santi Noreña lms@criptomart.net LibreMediaServerAudio - + 0 0 - 800 - 800 + 500 + 400 @@ -32,7 +32,7 @@ 0 0 - 114 + 500 21 diff --git a/src/libremediaserver-audio.cpp b/src/libremediaserver-audio.cpp index d146e10..5eb65aa 100644 --- a/src/libremediaserver-audio.cpp +++ b/src/libremediaserver-audio.cpp @@ -40,16 +40,9 @@ libreMediaServerAudio::libreMediaServerAudio(bool gui) connect(m_ola, SIGNAL(dmxOutput(int, int, int)), this, SLOT(dmxInput(int, int, int))); m_ola->registerUniverse(); m_mae.startEngine(set->getAudioDeviceId()); - qDebug("Init Complete."); + qDebug("Core init Complete. Start reading DMX."); m_ola->blockSignals(false); m_ola->start(QThread::TimeCriticalPriority ); -#ifndef NOGUI - if (m_ui) { - m_refreshUi = new QTimer(this); - connect(m_refreshUi, SIGNAL(timeout()), this, SLOT(refreshUi())); - m_refreshUi->start(UI_REFRESH_TIME); - } -#endif } libreMediaServerAudio::~libreMediaServerAudio() @@ -73,7 +66,7 @@ void libreMediaServerAudio::dmxInput(int layer, int channel, int value) if (QFile::exists(mediaFile)){ m_mae.loadMedia(layer, mediaFile.toLatin1().data()); m_currentMedia[layer] = mediaFile; -#ifndef NOGIO +#ifndef NOGUI if (m_ui) m_lmsUi->m_aw->mediaLoaded(layer, mediaFile, m_mae.getDuration(layer)); #endif @@ -82,7 +75,7 @@ void libreMediaServerAudio::dmxInput(int layer, int channel, int value) } else if (channel == VOLUME_COARSE || channel == VOLUME_FINE) { float tmp = value / 65025.0f; m_mae.volChanged(layer, tmp); - m_updateUi[layer][0] = tmp * 100; + m_updateUi[layer][0] = tmp * 100.0f; } else if (channel == PAN) { m_mae.panChanged(layer, value); m_updateUi[layer][1] = value; @@ -98,21 +91,19 @@ void libreMediaServerAudio::dmxInput(int layer, int channel, int value) } else if (channel == PLAYBACK && value > 0) { aux = value / 25; Status s = m_currentStatus[layer]; - if (s != aux) { - 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; - m_mae.playbackChanged(layer, s); - m_currentStatus[layer] = s; + 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; + m_mae.playbackChanged(layer, s); + m_currentStatus[layer] = s; #ifndef NOGUI - if (m_ui) m_lmsUi->m_aw->playbackChanged(layer, s); + if (m_ui) m_lmsUi->m_aw->playbackChanged(layer, s); #endif - } } } @@ -136,6 +127,7 @@ void libreMediaServerAudio::refreshUi() { m_lmsUi->m_aw->pitchChanged(i, m_updateUi[i][2]); m_updateUi[i][2] = -1; } + //m_lmsUi->m_aw->playbackChanged(i, m_currentStatus[i]); } } @@ -146,6 +138,9 @@ void libreMediaServerAudio::setUi(libreMediaServerAudioUi *lmsUi) 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); }; void libreMediaServerAudio::uiSliderChanged(int layer, Slider s, int value) @@ -166,6 +161,7 @@ void libreMediaServerAudio::uiSliderChanged(int layer, Slider s, int value) void libreMediaServerAudio::uiPlaybackChanged(int layer, Status s) { m_mae.playbackChanged(layer, s); + m_currentStatus[layer] = s; } void libreMediaServerAudio::uiLoadMedia(int layer, QString s) diff --git a/src/libremediaserver-audio.h b/src/libremediaserver-audio.h index 9782d88..5e956ac 100644 --- a/src/libremediaserver-audio.h +++ b/src/libremediaserver-audio.h @@ -49,7 +49,7 @@ private: bool m_ui; QTimer *m_refreshUi; libreMediaServerAudioUi *m_lmsUi; - int m_updateUi[MAX_LAYERS][3]; + float m_updateUi[MAX_LAYERS][3]; #endif private slots: diff --git a/src/main.cpp b/src/main.cpp index 99643b0..eea3c29 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -32,7 +32,6 @@ bool hasUi(int &argc, char *argv[]) int main(int argc, char *argv[]) { QApplication app(argc, argv); - libreMediaServerAudio lms(hasUi(argc, argv)); #ifndef NOGUI if (hasUi(argc, argv)) diff --git a/src/miniaudioengine.cpp b/src/miniaudioengine.cpp index 8dc38da..59345e9 100644 --- a/src/miniaudioengine.cpp +++ b/src/miniaudioengine.cpp @@ -195,8 +195,9 @@ void MiniAudioEngine::volChanged(int layer, float vol) { if (m_mediaLoaded[layer] == false) return; + if (vol > 1) + vol = 1; ma_sound_group_set_volume(&m_currentSound[layer], vol); - qDebug() << "vol: " << vol; } void MiniAudioEngine::panChanged(int layer, float value) diff --git a/src/miniaudioengine.h b/src/miniaudioengine.h index 2bf224b..169d33b 100644 --- a/src/miniaudioengine.h +++ b/src/miniaudioengine.h @@ -27,6 +27,7 @@ protected: 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]); } private: ma_resource_manager_config resourceManagerConfig; diff --git a/src/slidergroup.cpp b/src/slidergroup.cpp index 6120093..a488560 100644 --- a/src/slidergroup.cpp +++ b/src/slidergroup.cpp @@ -1,5 +1,5 @@ #include "slidergroup.h" - +#include SliderGroup::SliderGroup(QString name, int min, int max, @@ -15,22 +15,22 @@ SliderGroup::SliderGroup(QString name, slider->setFocusPolicy(Qt::StrongFocus); slider->setTickPosition(QSlider::TicksBothSides); slider->setTickInterval((max - min) / 11); - slider->setMinimumHeight(100); + slider->setMinimumHeight(0); slider->setSingleStep(1); slider->setRange(min, max); - //slider->setMaximumWidth(40); + slider->setMinimumWidth(50); slider->setToolTip(name); slider->setStyleSheet("QSlider {" - "border: 1px solid #999999;" + "border: 2px solid #685060;" "margin: 0px;" "height: 200px;" - "width: 40px;}" + "width: 50px;}" ); slider->setContentsMargins(0, 0, 0, 0); valueBox = new QDoubleSpinBox(); valueBox->setFocusPolicy(Qt::NoFocus); valueBox->setButtonSymbols(QAbstractSpinBox::NoButtons); - //valueBox->setMaximumWidth(40); + valueBox->setMinimumWidth(50); valueBox->setRange(min, max); valueBox->setDecimals(decimals); valueBox->setObjectName(name); @@ -40,8 +40,8 @@ SliderGroup::SliderGroup(QString name, connect(slider, SIGNAL(valueChanged(int)), this, SLOT(sliderValueChanged(int))); layout->addWidget(slider); layout->addWidget(valueBox); - this->setStyleSheet("border: 1px solid #999999;" - "width: 40px;" + this->setStyleSheet("border: 2px solid #685060;" + "width: 50px;" "margin: 0px;" ); layout->setSpacing(0); @@ -51,7 +51,9 @@ SliderGroup::SliderGroup(QString name, void SliderGroup::sliderValueChanged(int value) { + valueBox->blockSignals(true); valueBox->setValue(value); + valueBox->blockSignals(false); emit valueChanged(value); }; From 5915d4898e9f85eef11a1a4cf183b44f0251d790 Mon Sep 17 00:00:00 2001 From: snt Date: Sun, 5 May 2024 21:44:21 +0200 Subject: [PATCH 08/38] =?UTF-8?q?cambida=20se=C3=B1al=20dmx=20por=20llamad?= =?UTF-8?q?a=20directa=20al=20m=C3=A9todo,=20reduce=20un=2020%=20de=20cpu?= =?UTF-8?q?=20en=20mi=20ordenador.=20Restringida=20actualizaci=C3=B3n=20de?= =?UTF-8?q?=20entry=20point=20como=20volumen,=20sigue=20cascando=20la=20b?= =?UTF-8?q?=C3=BAsqueda=20en=20mp3,=20wav=20va=20fino.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/audiolayerwidget.cpp | 2 +- src/libremediaserver-audio.cpp | 24 +++++++++++++----------- src/libremediaserver-audio.h | 9 +++++---- src/miniaudioengine.cpp | 7 ++++++- src/olathread.cpp | 21 +++++++++++---------- src/olathread.h | 19 +++++++++---------- 6 files changed, 45 insertions(+), 37 deletions(-) diff --git a/src/audiolayerwidget.cpp b/src/audiolayerwidget.cpp index 563eeb9..02da882 100644 --- a/src/audiolayerwidget.cpp +++ b/src/audiolayerwidget.cpp @@ -58,7 +58,7 @@ AudioLayerWidget::AudioLayerWidget(QWidget *parent, int layer): m_totalTimeValue = new QTimeEdit; m_totalTimeValue->setObjectName("Track Length"); m_totalTimeValue->setToolTip("Track Length"); - m_totalTimeValue->setDisplayFormat("mm:ss:zz"); + m_totalTimeValue->setDisplayFormat("mm:ss:zzz"); m_totalTimeValue->setReadOnly(true); m_totalTimeValue->setButtonSymbols(QAbstractSpinBox::NoButtons); m_totalTimeValue->setMinimumWidth(80); diff --git a/src/libremediaserver-audio.cpp b/src/libremediaserver-audio.cpp index 5eb65aa..589c190 100644 --- a/src/libremediaserver-audio.cpp +++ b/src/libremediaserver-audio.cpp @@ -29,10 +29,14 @@ libreMediaServerAudio::libreMediaServerAudio(bool gui) m_mediaLibrary = new MediaLibrary; m_mediaLibrary->initMediaLibrary(); for (int i = 0; i < MAX_LAYERS; i++) { - m_currentMedia[i] = Status::Iddle; + 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, set->getLayersNumber()); Q_CHECK_PTR(m_ola); @@ -42,7 +46,9 @@ libreMediaServerAudio::libreMediaServerAudio(bool gui) m_mae.startEngine(set->getAudioDeviceId()); qDebug("Core init Complete. Start reading DMX."); m_ola->blockSignals(false); +#ifdef NOGUI m_ola->start(QThread::TimeCriticalPriority ); +#endif } libreMediaServerAudio::~libreMediaServerAudio() @@ -71,7 +77,6 @@ void libreMediaServerAudio::dmxInput(int layer, int channel, int value) m_lmsUi->m_aw->mediaLoaded(layer, mediaFile, m_mae.getDuration(layer)); #endif } - return; } else if (channel == VOLUME_COARSE || channel == VOLUME_FINE) { float tmp = value / 65025.0f; m_mae.volChanged(layer, tmp); @@ -84,10 +89,7 @@ void libreMediaServerAudio::dmxInput(int layer, int channel, int value) m_updateUi[layer][2] = value; } else if (channel == ENTRY_POINT_COARSE || channel == ENTRY_POINT_FINE) { m_mae.setCursor(layer, value); -#ifndef NOGUI - m_lmsUi->m_aw->cursorChanged(layer, m_mae.getCursor(layer)); -#endif - return; + m_updateUi[layer][3] = value; } else if (channel == PLAYBACK && value > 0) { aux = value / 25; Status s = m_currentStatus[layer]; @@ -111,10 +113,6 @@ void libreMediaServerAudio::dmxInput(int layer, int channel, int value) void libreMediaServerAudio::refreshUi() { if (!m_ui) return; for (int i= 0; i < Settings::getInstance()->getLayersNumber(); i++ ) { - Status s = m_currentStatus[i]; - if (s == Status::PlayingOnce || s == Status::PlayingLoop) { - m_lmsUi->m_aw->cursorChanged(i, m_mae.getCursor(i)); - } if (m_updateUi[i][0] >= 0) { m_lmsUi->m_aw->volChanged(i, m_updateUi[i][0]); m_updateUi[i][0] = -1; @@ -127,7 +125,10 @@ void libreMediaServerAudio::refreshUi() { m_lmsUi->m_aw->pitchChanged(i, m_updateUi[i][2]); m_updateUi[i][2] = -1; } - //m_lmsUi->m_aw->playbackChanged(i, m_currentStatus[i]); + if (m_updateUi[i][3] >= 0) { + m_lmsUi->m_aw->cursorChanged(i, m_mae.getCursor(i)); + m_updateUi[i][3] = -1; + } } } @@ -141,6 +142,7 @@ void libreMediaServerAudio::setUi(libreMediaServerAudioUi *lmsUi) m_refreshUi = new QTimer(this); connect(m_refreshUi, SIGNAL(timeout()), this, SLOT(refreshUi())); m_refreshUi->start(UI_REFRESH_TIME); + m_ola->start(QThread::TimeCriticalPriority ); }; void libreMediaServerAudio::uiSliderChanged(int layer, Slider s, int value) diff --git a/src/libremediaserver-audio.h b/src/libremediaserver-audio.h index 5e956ac..665f3e2 100644 --- a/src/libremediaserver-audio.h +++ b/src/libremediaserver-audio.h @@ -35,25 +35,26 @@ class libreMediaServerAudio : public QObject public: libreMediaServerAudio(bool gui = false); virtual ~libreMediaServerAudio(); - olaThread *m_ola; + void dmxInput(int layer, int channel, int value); #ifndef NOGUI void setUi(libreMediaServerAudioUi *lmsUi); #endif - + //static void NewDmx(const ola::client::DMXMetadata &data, const ola::DmxBuffer &buffer); private: + olaThread *m_ola; MediaLibrary *m_mediaLibrary; MiniAudioEngine m_mae; QString m_currentMedia[MAX_LAYERS]; Status m_currentStatus[MAX_LAYERS]; + static QList m_dmxSettings; #ifndef NOGUI bool m_ui; QTimer *m_refreshUi; libreMediaServerAudioUi *m_lmsUi; - float m_updateUi[MAX_LAYERS][3]; + float m_updateUi[MAX_LAYERS][4]; #endif private slots: - void dmxInput(int layer, int channel, int value); #ifndef NOGUI void refreshUi(); void uiSliderChanged(int layer, Slider s, int value); diff --git a/src/miniaudioengine.cpp b/src/miniaudioengine.cpp index 59345e9..f65744d 100644 --- a/src/miniaudioengine.cpp +++ b/src/miniaudioengine.cpp @@ -114,9 +114,11 @@ ma_result MiniAudioEngine::getAllAudioDevices() ma_result MiniAudioEngine::loadMedia(int layer, char *file) { ma_result result; + float vol = -1; if (m_mediaLoaded[layer] == true) { + vol = ma_sound_get_volume(&m_currentSound[layer]); ma_sound_uninit(&m_currentSound[layer]); m_mediaLoaded[layer] = false; } @@ -131,7 +133,10 @@ ma_result MiniAudioEngine::loadMedia(int layer, char *file) qWarning("Failed to load file %s", file); else { m_mediaLoaded[layer] = true; - this->volChanged(layer, 0); + if (vol == -1) + this->volChanged(layer, 0); + else + this->volChanged(layer, vol); } return result; } diff --git a/src/olathread.cpp b/src/olathread.cpp index 471775c..409cf79 100644 --- a/src/olathread.cpp +++ b/src/olathread.cpp @@ -1,11 +1,11 @@ -#include "olathread.h" - +#include "libremediaserver-audio.h" +//#include "olathread.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++) { @@ -36,6 +36,7 @@ void olaThread::init() } m_client = m_clientWrapper->GetClient(); m_client->SetDMXCallback(ola::NewCallback(this, &olaThread::NewDmx)); + //m_client->SetDMXCallback(ola::NewCallback((libreMediaServerAudio *)this->parent(), libreMediaServerAudio::NewDmx)); m_clientWrapper->GetSelectServer()->RegisterRepeatingTimeout(4000, ola::NewCallback(this, &olaThread::CheckDataLoss)); m_client->SetCloseHandler(ola::NewSingleCallback(this, &olaThread::socketClosed)); m_dmxSettings = Settings::getInstance()->getDmxSettings(); @@ -76,7 +77,7 @@ void olaThread::NewDmx(const ola::client::DMXMetadata &data, case DMX_FOLDER: value *= 0x100; value += buffer.Get(i.address + DMX_FILE); - emit dmxOutput(i.layer,j,value); + qobject_cast(parent())->dmxInput(i.layer, j, value); m_dmx[i.layer][DMX_FILE] = buffer.Get(i.address + DMX_FILE); fileSent = true; break; @@ -84,13 +85,13 @@ void olaThread::NewDmx(const ola::client::DMXMetadata &data, if (fileSent) break; value += buffer.Get(i.address + DMX_FOLDER) * 0x100; - emit dmxOutput(i.layer,j,value); + qobject_cast(parent())->dmxInput(i.layer, j, value); m_dmx[i.layer][DMX_FOLDER] = buffer.Get(i.address + DMX_FOLDER); fileSent = true; break; case VOLUME_FINE: value = (buffer.Get(i.address + VOLUME_COARSE) * 0x100) + value; - emit dmxOutput(i.layer,j,value); + qobject_cast(parent())->dmxInput(i.layer, j, value); m_dmx[i.layer][VOLUME_COARSE] = buffer.Get(i.address + VOLUME_COARSE); volSent = true; break; @@ -98,13 +99,13 @@ void olaThread::NewDmx(const ola::client::DMXMetadata &data, if (volSent) break; value = (value * 0x100) + buffer.Get(i.address + VOLUME_FINE); - emit dmxOutput(i.layer,j,value); + qobject_cast(parent())->dmxInput(i.layer, j, value); m_dmx[i.layer][VOLUME_FINE] = buffer.Get(i.address + VOLUME_FINE); volSent = true; break; case ENTRY_POINT_FINE: value = (buffer.Get(i.address + ENTRY_POINT_COARSE) * 0x100) + value; - emit dmxOutput(i.layer,j,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; @@ -112,12 +113,12 @@ void olaThread::NewDmx(const ola::client::DMXMetadata &data, if (entrySent) break; value = (value * 0x100) + buffer.Get(i.address + ENTRY_POINT_FINE); - emit dmxOutput(i.layer,j,value); + 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; } } diff --git a/src/olathread.h b/src/olathread.h index dcae35b..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 @@ -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 */ From 7a9c0cd0ac3d1fd32a47e1779ce053f810b01baf Mon Sep 17 00:00:00 2001 From: snt Date: Tue, 7 May 2024 20:23:09 +0200 Subject: [PATCH 09/38] =?UTF-8?q?libreMediaServer=20no=20refresca=20direct?= =?UTF-8?q?amente=20la=20ui,=20solo=20actualiza=20valores=20en=20=20audiow?= =?UTF-8?q?idget.=20la=20ui=20se=20actualiza=20con=20un=20timer=20en=20aud?= =?UTF-8?q?iowidget.=20Quitadas=20se=C3=B1ales=20en=20todo,=20mejora=20ren?= =?UTF-8?q?dimiento.=20fade=20en=20volumen=20basado=20en=20la=20trama=20dm?= =?UTF-8?q?x=20(25=20ms)=20para=20evitar=20clicks.=20refresca=20los=20valo?= =?UTF-8?q?res=20de=20la=20capa=20cuando=20carga=20un=20media.=20Ui=20Ok.?= =?UTF-8?q?=20nuevo=20formato=20de=20archivo=20de=20configuraci=C3=B3n=20x?= =?UTF-8?q?ml.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/changelog.txt | 2 + docs/lms-audio.xlm | 14 +++--- docs/roadmap.txt | 8 +++- src/audiolayerwidget.cpp | 18 ++++---- src/audiolayerwidget.h | 25 +++++------ src/audiowidget.cpp | 70 +++++++++++++++++++++++------- src/audiowidget.h | 15 +++++-- src/defines.h | 17 ++++++-- src/dmxPersonality.h | 1 - src/libremediaserver-audio-gui.cpp | 2 +- src/libremediaserver-audio.cpp | 67 ++++++++++++++++------------ src/libremediaserver-audio.h | 15 ++++--- src/main.cpp | 4 +- src/miniaudioengine.cpp | 65 +++++++++++++++------------ src/miniaudioengine.h | 2 + src/olathread.cpp | 24 +++++----- src/settings.cpp | 49 +++++++++++---------- src/settings.h | 15 ++++--- src/slidergroup.cpp | 16 ++++++- src/slidergroup.h | 2 + 20 files changed, 271 insertions(+), 160 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 22a7e1f..a69017b 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -27,6 +27,8 @@ v 0.2.0 Antígona (24/04/2024) + 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). v 0.1.3 Leúcade (19/04/2024) 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 894c32c..bd98089 100644 --- a/docs/roadmap.txt +++ b/docs/roadmap.txt @@ -45,9 +45,13 @@ v 0.2.1 - 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. - New play mode without pitch control, it saves resources. MA_SOUND_FLAG_NO_PITCH -- Vumeter or indicator about audio output in layer and master. +- Vumeter or indicator about audio output in layer and master, add to sliderGroup. +- QSlider can not accept floats and it can no manage high frequency updates. - SettingsDialog. - Load/save conf file. - ¿Exit Point? is it needed? -- Hardening: check return errors, catch execptions, i'm too happy.... +- Hardening: check return errors, try/catch exceptions, i'm too happy.... - Tests: errors on wrong conf file. +- BUGFIX: there are some small clicks when changing volume and play/stop/pause. vol 24 bits? microfades in engine? setVol 0 before changing playback state? +- refactorize dmxInput: loadMedia, changePlayBack, +- BUGFIX: in nogui mode we need more info print at terminal (load media, change playback status). diff --git a/src/audiolayerwidget.cpp b/src/audiolayerwidget.cpp index 02da882..a22b2cc 100644 --- a/src/audiolayerwidget.cpp +++ b/src/audiolayerwidget.cpp @@ -52,6 +52,7 @@ AudioLayerWidget::AudioLayerWidget(QWidget *parent, int layer): 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); @@ -138,7 +139,7 @@ void AudioLayerWidget::openMediaDialog() QStringList fileNames; fileNames = dialog.selectedFiles(); emit uiLoadMedia(m_layer, fileNames.at(0)); - this->fileLoaded(fileNames.at(0)); + this->setMediaFile(fileNames.at(0)); } // from DMX signals @@ -163,7 +164,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(); @@ -174,19 +175,18 @@ void AudioLayerWidget::fileLoaded(QString file) this->setPlaybackStatus(Status::Stopped); } -void AudioLayerWidget::setPlaybackStatus(Status status) +void AudioLayerWidget::setPlaybackStatus(Status s) { - if (!strcmp(StatusStr[status], m_suspendResumeButton->text().toLatin1().constData())) - return; + Status status = static_cast(s); m_suspendResumeButton->blockSignals(true); m_status = status; - if (status == Status::Stopped) - refreshCurrentTime(0); + //if (status == Status::Stopped) + // refreshCurrentTime(0); m_suspendResumeButton->setText(StatusStr[status]); m_suspendResumeButton->blockSignals(false); } -void AudioLayerWidget::durationChanged(float dur) +void AudioLayerWidget::setDuration(float dur) { m_progress->blockSignals(true); m_progressTime->blockSignals(true); @@ -200,7 +200,7 @@ void AudioLayerWidget::durationChanged(float dur) m_totalTimeValue->blockSignals(false); } -void AudioLayerWidget::refreshCurrentTime(float progress) +void AudioLayerWidget::setCurrentTime(float progress) { progress *= 1000; m_progress->blockSignals(true); diff --git a/src/audiolayerwidget.h b/src/audiolayerwidget.h index 1c0ee83..9380b81 100644 --- a/src/audiolayerwidget.h +++ b/src/audiolayerwidget.h @@ -18,13 +18,6 @@ class AudioLayerWidget : public QWidget public: 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; @@ -39,17 +32,23 @@ private: 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 fileLoaded(QString file); - void durationChanged(float dur); - void refreshCurrentTime(float progress); - -private slots: - void openMediaDialog(); signals: void uiPlaybackChanged(int layer, Status s); diff --git a/src/audiowidget.cpp b/src/audiowidget.cpp index 3d17c2d..ca2633a 100644 --- a/src/audiowidget.cpp +++ b/src/audiowidget.cpp @@ -1,5 +1,5 @@ #include "audiowidget.h" - +#include AudioWidget::AudioWidget(QWidget *parent) : QWidget(parent) @@ -15,39 +15,79 @@ AudioWidget::AudioWidget(QWidget *parent) : 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 * 2); } void AudioWidget::mediaLoaded(int layer, QString file, float duration) { - QLayoutItem * const item = m_layout->itemAt(layer); - dynamic_cast(item->widget())->fileLoaded(file); - dynamic_cast(item->widget())->durationChanged(duration); + m_layerUpdate[layer].media = file; + m_layerUpdate[layer].duration = duration; + m_layerUpdate[layer].updated = true; } void AudioWidget::volChanged(int layer, float 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) { - 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) { - 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) { - 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::cursorChanged(int layer, float cursor) { - QLayoutItem * const item = m_layout->itemAt(layer); - AudioLayerWidget *alw = dynamic_cast(item->widget()); - alw->refreshCurrentTime(cursor); + m_layerUpdate[layer].cursor = floor(cursor * 1000) / 1000; + m_layerUpdate[layer].updated = true; +} + +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; + } + } } diff --git a/src/audiowidget.h b/src/audiowidget.h index a293a0a..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 @@ -14,14 +13,22 @@ class AudioWidget : public QWidget public: AudioWidget(QWidget *parent = nullptr); - void mediaLoaded(int layer, QString media, float duration); + +private: + QHBoxLayout *m_layout; + layerData m_layerUpdate[MAX_LAYERS]; + QTimer *m_refreshUi; + +public slots: 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 cursorChanged(int layer, float cursor); - QHBoxLayout *m_layout; + 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); diff --git a/src/defines.h b/src/defines.h index f8a2312..6552d19 100644 --- a/src/defines.h +++ b/src/defines.h @@ -6,9 +6,10 @@ #define LICENSE "GPL 3 Licensed. See LICENSE.txt." #define DEFAULT_FILE "lms-audio.xlm" #define MAX_LAYERS 4 -#define UI_REFRESH_TIME 93 +#define MAX_AUDIODEVICES 8 +#define UI_REFRESH_TIME 77 +#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; @@ -41,5 +42,15 @@ enum Slider Pitch, }; +#include +struct layerData { + QString media; + Status status; + bool updated; + float vol; + float cursor; + int pan; + int pitch; + float duration; +}; #endif // DEFINES_H - diff --git a/src/dmxPersonality.h b/src/dmxPersonality.h index c56829e..82f9468 100644 --- a/src/dmxPersonality.h +++ b/src/dmxPersonality.h @@ -10,7 +10,6 @@ #define ENTRY_POINT_COARSE 5 #define ENTRY_POINT_FINE 4 #define PITCH 7 - #define LAYER_CHANNELS 9 #endif // DMXPERSONALITY_H diff --git a/src/libremediaserver-audio-gui.cpp b/src/libremediaserver-audio-gui.cpp index 2d92918..b769679 100644 --- a/src/libremediaserver-audio-gui.cpp +++ b/src/libremediaserver-audio-gui.cpp @@ -38,7 +38,7 @@ libreMediaServerAudioUi::libreMediaServerAudioUi(QWidget *parent) this->setContentsMargins(5, 5, 5, 5); this->setStyleSheet( "color: white;" - "background-color: gray;" + "background-color: #4f4048;" "selection-color: blue;" "selection-background-color: green" ); diff --git a/src/libremediaserver-audio.cpp b/src/libremediaserver-audio.cpp index 589c190..cb18dc2 100644 --- a/src/libremediaserver-audio.cpp +++ b/src/libremediaserver-audio.cpp @@ -21,11 +21,11 @@ #include "libremediaserver-audio.h" -libreMediaServerAudio::libreMediaServerAudio(bool gui) +libreMediaServerAudio::libreMediaServerAudio() { - m_ui = gui; - 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(); for (int i = 0; i < MAX_LAYERS; i++) { @@ -38,12 +38,11 @@ libreMediaServerAudio::libreMediaServerAudio(bool gui) m_updateUi[i][3] = -1; #endif } - m_ola = new olaThread(this, set->getLayersNumber()); + m_ola = new olaThread(this, m_settings->getLayersNumber()); Q_CHECK_PTR(m_ola); m_ola->blockSignals(true); - connect(m_ola, SIGNAL(dmxOutput(int, int, int)), this, SLOT(dmxInput(int, int, int))); m_ola->registerUniverse(); - m_mae.startEngine(set->getAudioDeviceId()); + m_mae.startEngine(m_settings->getAudioDeviceId()); qDebug("Core init Complete. Start reading DMX."); m_ola->blockSignals(false); #ifdef NOGUI @@ -57,27 +56,29 @@ libreMediaServerAudio::~libreMediaServerAudio() m_mae.stopEngine(); } +void libreMediaServerAudio::loadMedia(int layer, int folder, int file) +{ + 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); + } +} + void libreMediaServerAudio::dmxInput(int layer, int channel, int value) { if (layer >= MAX_LAYERS || channel >= LAYER_CHANNELS) return; QString mediaFile = NULL; int aux; - if (channel == DMX_FOLDER || channel == DMX_FILE){ - int folder = (value >> 8) & 0x000000FF; - int file = value & 0x000000FF; - 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 - } - } else if (channel == VOLUME_COARSE || channel == VOLUME_FINE) { + if (channel == VOLUME_COARSE || channel == VOLUME_FINE) { float tmp = value / 65025.0f; m_mae.volChanged(layer, tmp); m_updateUi[layer][0] = tmp * 100.0f; @@ -104,15 +105,17 @@ void libreMediaServerAudio::dmxInput(int layer, int channel, int value) m_mae.playbackChanged(layer, s); m_currentStatus[layer] = s; #ifndef NOGUI - if (m_ui) m_lmsUi->m_aw->playbackChanged(layer, s); + if (m_ui) { + m_lmsUi->m_aw->playbackChanged(layer, s); + m_lmsUi->m_aw->cursorChanged(layer, m_mae.getCursor(layer)); + } #endif } } - #ifndef NOGUI void libreMediaServerAudio::refreshUi() { if (!m_ui) return; - for (int i= 0; i < Settings::getInstance()->getLayersNumber(); i++ ) { + 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; @@ -125,7 +128,9 @@ void libreMediaServerAudio::refreshUi() { m_lmsUi->m_aw->pitchChanged(i, m_updateUi[i][2]); m_updateUi[i][2] = -1; } - if (m_updateUi[i][3] >= 0) { + if (m_updateUi[i][3] >= 0 \ + || m_currentStatus[i] == Status::PlayingOnce\ + || m_currentStatus[i] == Status::PlayingLoop) { m_lmsUi->m_aw->cursorChanged(i, m_mae.getCursor(i)); m_updateUi[i][3] = -1; } @@ -135,6 +140,7 @@ void libreMediaServerAudio::refreshUi() { 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))); @@ -145,6 +151,7 @@ void libreMediaServerAudio::setUi(libreMediaServerAudioUi *lmsUi) m_ola->start(QThread::TimeCriticalPriority ); }; +// From Ui widgets void libreMediaServerAudio::uiSliderChanged(int layer, Slider s, int value) { switch (s){ @@ -166,8 +173,12 @@ void libreMediaServerAudio::uiPlaybackChanged(int layer, Status s) m_currentStatus[layer] = s; } -void libreMediaServerAudio::uiLoadMedia(int layer, QString s) +void libreMediaServerAudio::uiLoadMedia(int layer, QString mediaFile) { - m_mae.loadMedia(layer, s.toLatin1().data()); + if (strcmp(mediaFile.toLatin1().constData(), m_currentMedia[layer].toLatin1().constData()) == 0) + return; + m_mae.loadMedia(layer, mediaFile.toLatin1().data()); + m_currentMedia[layer] = mediaFile; + m_lmsUi->m_aw->mediaLoaded(layer, mediaFile, m_mae.getDuration(layer)); } #endif diff --git a/src/libremediaserver-audio.h b/src/libremediaserver-audio.h index 665f3e2..1af37df 100644 --- a/src/libremediaserver-audio.h +++ b/src/libremediaserver-audio.h @@ -21,6 +21,7 @@ #define LIBREMEDIASERVERAUDIO_H #include "medialibrary.h" +#include "miniaudioengine.h" #include "olathread.h" #include "settings.h" #include "defines.h" @@ -33,33 +34,35 @@ class libreMediaServerAudio : public QObject Q_OBJECT public: - libreMediaServerAudio(bool gui = false); + libreMediaServerAudio(); 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 - //static void NewDmx(const ola::client::DMXMetadata &data, const ola::DmxBuffer &buffer); + private: olaThread *m_ola; MediaLibrary *m_mediaLibrary; MiniAudioEngine m_mae; + Settings *m_settings; QString m_currentMedia[MAX_LAYERS]; Status m_currentStatus[MAX_LAYERS]; - static QList m_dmxSettings; -#ifndef NOGUI + QList m_dmxSettings; bool m_ui; +#ifndef NOGUI QTimer *m_refreshUi; libreMediaServerAudioUi *m_lmsUi; float m_updateUi[MAX_LAYERS][4]; -#endif private slots: -#ifndef NOGUI void refreshUi(); void uiSliderChanged(int layer, Slider s, int value); void uiPlaybackChanged(int layer, Status s); void uiLoadMedia(int layer, QString s); + #endif }; diff --git a/src/main.cpp b/src/main.cpp index eea3c29..79b7a91 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -32,9 +32,9 @@ bool hasUi(int &argc, char *argv[]) int main(int argc, char *argv[]) { QApplication app(argc, argv); - libreMediaServerAudio lms(hasUi(argc, argv)); + libreMediaServerAudio lms; #ifndef NOGUI - if (hasUi(argc, argv)) + if (hasUi(argc, argv) || lms.getShowUi()) { libreMediaServerAudioUi *lmsUi = new libreMediaServerAudioUi(); lms.setUi(lmsUi); diff --git a/src/miniaudioengine.cpp b/src/miniaudioengine.cpp index f65744d..c2d9a6d 100644 --- a/src/miniaudioengine.cpp +++ b/src/miniaudioengine.cpp @@ -4,6 +4,11 @@ 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; } } @@ -79,8 +84,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 = 1; result = ma_resource_manager_init(&resourceManagerConfig, &resourceManager); if (result != MA_SUCCESS) { qCritical("Failed to initialize audio resource manager."); @@ -114,11 +119,9 @@ ma_result MiniAudioEngine::getAllAudioDevices() ma_result MiniAudioEngine::loadMedia(int layer, char *file) { ma_result result; - float vol = -1; if (m_mediaLoaded[layer] == true) { - vol = ma_sound_get_volume(&m_currentSound[layer]); ma_sound_uninit(&m_currentSound[layer]); m_mediaLoaded[layer] = false; } @@ -133,10 +136,8 @@ ma_result MiniAudioEngine::loadMedia(int layer, char *file) qWarning("Failed to load file %s", file); else { m_mediaLoaded[layer] = true; - if (vol == -1) - this->volChanged(layer, 0); - else - this->volChanged(layer, vol); + this->refreshValues(layer); + m_currentLayerValues[layer].media = file; } return result; } @@ -191,7 +192,7 @@ ma_result MiniAudioEngine::printFormatInfo(int layer) qWarning("Failed to get data format %i\n", layer); return MA_INVALID_DATA; } - qInfo("samples/sec: %u format: %u channels: %u", sampleRate, format, channels); + qInfo() << "name:" << m_currentLayerValues[layer].media << "samples/sec:" << sampleRate << "format:" << format << "channels:" << channels; return result; } @@ -200,9 +201,10 @@ void MiniAudioEngine::volChanged(int layer, float vol) { if (m_mediaLoaded[layer] == false) return; - if (vol > 1) - vol = 1; - 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) @@ -213,6 +215,7 @@ 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) @@ -223,6 +226,7 @@ void MiniAudioEngine::pitchChanged(int layer, float value) return; result = value / 128.0; ma_sound_group_set_pitch(&m_currentSound[layer], result); + m_currentLayerValues[layer].pitch = value; } void MiniAudioEngine::playbackChanged(int layer, Status status) @@ -235,7 +239,7 @@ void MiniAudioEngine::playbackChanged(int layer, Status status) break; case Status::Stopped: ma_sound_stop(&m_currentSound[layer]); - ma_sound_seek_to_pcm_frame(&m_currentSound[layer], 0); + this->setCursor(layer, m_currentLayerValues[layer].cursor); break; case Status::PlayingLoop: ma_sound_set_looping(&m_currentSound[layer], true); @@ -248,30 +252,35 @@ void MiniAudioEngine::playbackChanged(int layer, Status status) default: break; } + m_currentLayerValues[layer].status = status; } void MiniAudioEngine::setCursor(int layer, int cursor) { - ma_uint64 f; + ma_uint64 end; + m_currentLayerValues[layer].cursor = cursor; 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); - // ToDo: change the loop entry point too + ma_sound_get_length_in_pcm_frames(&m_currentSound[layer], &end); + ma_uint64 start = (cursor * end) / 65025; + ma_sound_seek_to_pcm_frame(&m_currentSound[layer], start); + ma_result result = ma_data_source_set_loop_point_in_pcm_frames(&m_currentSound[layer], start, end); + if (result != MA_SUCCESS) { + return; // Failed to set the loop point. + } } Status MiniAudioEngine::getStatus(int layer) { - if (m_mediaLoaded[layer] == false) - return Status::Iddle; - if (ma_sound_is_playing(&m_currentSound[layer])) { - if (ma_sound_is_looping(&m_currentSound[layer])) - return Status::PlayingLoop; - return Status::PlayingOnce; - } - if (this->getDuration(layer) > 0) - return Status::Paused; - return Status::Stopped; + return m_currentLayerValues[layer].status; +} + +void MiniAudioEngine::refreshValues(int layer) +{ + 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); + this->setCursor(layer, m_currentLayerValues[layer].cursor); } diff --git a/src/miniaudioengine.h b/src/miniaudioengine.h index 169d33b..2c3ec5a 100644 --- a/src/miniaudioengine.h +++ b/src/miniaudioengine.h @@ -40,10 +40,12 @@ 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 startContext(); + void refreshValues(int layer); }; #endif // MINIAUDIOENGINE_H diff --git a/src/olathread.cpp b/src/olathread.cpp index 409cf79..6ca9325 100644 --- a/src/olathread.cpp +++ b/src/olathread.cpp @@ -1,5 +1,4 @@ #include "libremediaserver-audio.h" -//#include "olathread.h" olaThread::olaThread(QObject *parent, int layers) : m_counter(0) @@ -36,7 +35,6 @@ void olaThread::init() } m_client = m_clientWrapper->GetClient(); m_client->SetDMXCallback(ola::NewCallback(this, &olaThread::NewDmx)); - //m_client->SetDMXCallback(ola::NewCallback((libreMediaServerAudio *)this->parent(), libreMediaServerAudio::NewDmx)); m_clientWrapper->GetSelectServer()->RegisterRepeatingTimeout(4000, ola::NewCallback(this, &olaThread::CheckDataLoss)); m_client->SetCloseHandler(ola::NewSingleCallback(this, &olaThread::socketClosed)); m_dmxSettings = Settings::getInstance()->getDmxSettings(); @@ -69,30 +67,31 @@ void olaThread::NewDmx(const ola::client::DMXMetadata &data, 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: - value *= 0x100; - value += buffer.Get(i.address + DMX_FILE); - qobject_cast(parent())->dmxInput(i.layer, j, value); - m_dmx[i.layer][DMX_FILE] = buffer.Get(i.address + DMX_FILE); + 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; - value += buffer.Get(i.address + DMX_FOLDER) * 0x100; - qobject_cast(parent())->dmxInput(i.layer, j, value); - m_dmx[i.layer][DMX_FOLDER] = buffer.Get(i.address + DMX_FOLDER); + 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: - value = (buffer.Get(i.address + VOLUME_COARSE) * 0x100) + 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] = buffer.Get(i.address + VOLUME_COARSE); + m_dmx[i.layer][VOLUME_COARSE] = aux; volSent = true; break; case VOLUME_COARSE: @@ -103,6 +102,9 @@ void olaThread::NewDmx(const ola::client::DMXMetadata &data, 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: value = (buffer.Get(i.address + ENTRY_POINT_COARSE) * 0x100) + value; qobject_cast(parent())->dmxInput(i.layer, j, value); diff --git a/src/settings.cpp b/src/settings.cpp index f664bb0..9e00914 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 read;\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 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 a488560..9ef9680 100644 --- a/src/slidergroup.cpp +++ b/src/slidergroup.cpp @@ -18,10 +18,11 @@ SliderGroup::SliderGroup(QString name, slider->setMinimumHeight(0); slider->setSingleStep(1); slider->setRange(min, max); + slider->setValue(0); slider->setMinimumWidth(50); slider->setToolTip(name); slider->setStyleSheet("QSlider {" - "border: 2px solid #685060;" + "border: 1px solid #5a4855;" "margin: 0px;" "height: 200px;" "width: 50px;}" @@ -32,17 +33,20 @@ SliderGroup::SliderGroup(QString name, valueBox->setButtonSymbols(QAbstractSpinBox::NoButtons); valueBox->setMinimumWidth(50); valueBox->setRange(min, max); + valueBox->setValue(0); valueBox->setDecimals(decimals); valueBox->setObjectName(name); valueBox->setToolTip(name); valueBox->setAlignment(Qt::AlignHCenter); valueBox->setContentsMargins(0, 0, 0, 0); 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: 2px solid #685060;" + this->setStyleSheet("border: 1px solid #5a4855;" "width: 50px;" "margin: 0px;" + "background-color: #383034;" ); layout->setSpacing(0); layout->setContentsMargins(0, 0, 0, 0); @@ -67,3 +71,11 @@ void SliderGroup::setValue(float 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 f597a5b..5bfeeb8 100644 --- a/src/slidergroup.h +++ b/src/slidergroup.h @@ -27,6 +27,8 @@ public slots: private: QSlider *slider; QDoubleSpinBox *valueBox; + + void mousePressEvent(QMouseEvent* event); }; #endif From 0979c3608e3f1d961453fb4f128f2c1c9573d450 Mon Sep 17 00:00:00 2001 From: snt Date: Tue, 7 May 2024 20:57:13 +0200 Subject: [PATCH 10/38] puliendo --- docs/changelog.txt | 8 ++------ docs/roadmap.txt | 6 +----- src/audiolayerwidget.cpp | 3 --- src/libremediaserver-audio.cpp | 1 + src/miniaudioengine.cpp | 2 +- 5 files changed, 5 insertions(+), 15 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index a69017b..5c7e0b6 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -6,12 +6,7 @@ https://git.criptomart.net/libremediaserver Libre Media Server ChangeLog - - - - - -v 0.2.0 Antígona (24/04/2024) +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. @@ -29,6 +24,7 @@ v 0.2.0 Antígona (24/04/2024) + 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 Leúcade (19/04/2024) diff --git a/docs/roadmap.txt b/docs/roadmap.txt index bd98089..153c596 100644 --- a/docs/roadmap.txt +++ b/docs/roadmap.txt @@ -41,17 +41,13 @@ 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, load media files from ui. +- 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. - New play mode without pitch control, it saves resources. MA_SOUND_FLAG_NO_PITCH - Vumeter or indicator about audio output in layer and master, add to sliderGroup. -- QSlider can not accept floats and it can no manage high frequency updates. - 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. -- BUGFIX: there are some small clicks when changing volume and play/stop/pause. vol 24 bits? microfades in engine? setVol 0 before changing playback state? -- refactorize dmxInput: loadMedia, changePlayBack, -- BUGFIX: in nogui mode we need more info print at terminal (load media, change playback status). diff --git a/src/audiolayerwidget.cpp b/src/audiolayerwidget.cpp index a22b2cc..4a07451 100644 --- a/src/audiolayerwidget.cpp +++ b/src/audiolayerwidget.cpp @@ -172,7 +172,6 @@ void AudioLayerWidget::setMediaFile(QString file) m_folderValue->setText(list.at(size - 2)); m_fileValue->setText(list.at(size - 1)); } - this->setPlaybackStatus(Status::Stopped); } void AudioLayerWidget::setPlaybackStatus(Status s) @@ -180,8 +179,6 @@ void AudioLayerWidget::setPlaybackStatus(Status s) Status status = static_cast(s); m_suspendResumeButton->blockSignals(true); m_status = status; - //if (status == Status::Stopped) - // refreshCurrentTime(0); m_suspendResumeButton->setText(StatusStr[status]); m_suspendResumeButton->blockSignals(false); } diff --git a/src/libremediaserver-audio.cpp b/src/libremediaserver-audio.cpp index cb18dc2..ab12820 100644 --- a/src/libremediaserver-audio.cpp +++ b/src/libremediaserver-audio.cpp @@ -104,6 +104,7 @@ void libreMediaServerAudio::dmxInput(int layer, int channel, int value) s = Status::PlayingLoop; 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); diff --git a/src/miniaudioengine.cpp b/src/miniaudioengine.cpp index c2d9a6d..fa58464 100644 --- a/src/miniaudioengine.cpp +++ b/src/miniaudioengine.cpp @@ -192,7 +192,7 @@ ma_result MiniAudioEngine::printFormatInfo(int layer) qWarning("Failed to get data format %i\n", layer); return MA_INVALID_DATA; } - qInfo() << "name:" << m_currentLayerValues[layer].media << "samples/sec:" << sampleRate << "format:" << format << "channels:" << channels; + qInfo() << "Layer:" << layer << m_currentLayerValues[layer].media << "samples/sec:" << sampleRate << "format:" << format << "channels:" << channels; return result; } From cd0105c9f9d66173dd8f6d61e5eec3327990b1fb Mon Sep 17 00:00:00 2001 From: snt Date: Wed, 8 May 2024 18:56:16 +0200 Subject: [PATCH 11/38] =?UTF-8?q?quitado=20peque=C3=B1o=20glitch=20cuando?= =?UTF-8?q?=20entry=20point=20no=20es=20cero.=20stop=20con=20fade=20out=20?= =?UTF-8?q?para=20evitar=20click.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/roadmap.txt | 6 +- src/audiolayerwidget.cpp | 10 ++-- src/audiowidget.cpp | 11 +++- src/defines.h | 2 +- src/libremediaserver-audio.cpp | 24 ++++++-- src/miniaudioengine.cpp | 105 ++++++++++++++++++--------------- src/miniaudioengine.h | 14 +++-- src/settings.cpp | 4 +- 8 files changed, 105 insertions(+), 71 deletions(-) diff --git a/docs/roadmap.txt b/docs/roadmap.txt index 153c596..94081f4 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 @@ -45,9 +44,12 @@ v 0.2.1 - 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. - New play mode without pitch control, it saves resources. MA_SOUND_FLAG_NO_PITCH -- Vumeter or indicator about audio output in layer and master, add to sliderGroup. - 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: +- BUGFIX: crash at startup and no dmx signal. +- Vumeter or indicator about audio output in layer and master, add to sliderGroup. diff --git a/src/audiolayerwidget.cpp b/src/audiolayerwidget.cpp index 4a07451..e8d22bd 100644 --- a/src/audiolayerwidget.cpp +++ b/src/audiolayerwidget.cpp @@ -10,8 +10,8 @@ AudioLayerWidget::AudioLayerWidget(QWidget *parent, int layer): QVBoxLayout *playback = new QVBoxLayout; m_folderValue = new ClickableLabel; - //m_folderValue->setMaximumWidth(160); - m_folderValue->setAlignment(Qt::AlignHCenter); + m_folderValue->setMaximumWidth(160); + m_folderValue->setAlignment(Qt::AlignLeft); m_folderValue->setStyleSheet( "color: white;" "background-color: black;" @@ -20,8 +20,8 @@ AudioLayerWidget::AudioLayerWidget(QWidget *parent, int layer): 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::AlignHCenter); + m_fileValue->setMaximumWidth(160); + m_fileValue->setAlignment(Qt::AlignLeft); m_fileValue->setStyleSheet( "color: white;" "background-color: black;" @@ -33,7 +33,6 @@ AudioLayerWidget::AudioLayerWidget(QWidget *parent, int layer): m_suspendResumeButton = new QPushButton(this); m_suspendResumeButton->setText(StatusStr[Status::Iddle]); - //m_suspendResumeButton->setMaximumWidth(180); connect(m_suspendResumeButton, SIGNAL(clicked()), SLOT(toggleSuspendResume())); layout->addWidget(m_suspendResumeButton); @@ -42,7 +41,6 @@ AudioLayerWidget::AudioLayerWidget(QWidget *parent, int layer): m_progress->setRange(0, 0); m_progress->setValue(0); m_progress->setFormat("%v / %m"); - //m_progress->setMaximumWidth(180); layout->addWidget(m_progress); m_progressTime = new QTimeEdit; diff --git a/src/audiowidget.cpp b/src/audiowidget.cpp index ca2633a..8bd077c 100644 --- a/src/audiowidget.cpp +++ b/src/audiowidget.cpp @@ -11,13 +11,20 @@ AudioWidget::AudioWidget(QWidget *parent) : 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 * 2); + m_refreshUi->start(UI_REFRESH_TIME * 1.5); } void AudioWidget::mediaLoaded(int layer, QString file, float duration) @@ -51,7 +58,7 @@ void AudioWidget::playbackChanged(int layer, Status status) void AudioWidget::cursorChanged(int layer, float cursor) { - m_layerUpdate[layer].cursor = floor(cursor * 1000) / 1000; + m_layerUpdate[layer].cursor = cursor; m_layerUpdate[layer].updated = true; } diff --git a/src/defines.h b/src/defines.h index 6552d19..a9c92b8 100644 --- a/src/defines.h +++ b/src/defines.h @@ -7,7 +7,7 @@ #define DEFAULT_FILE "lms-audio.xlm" #define MAX_LAYERS 4 #define MAX_AUDIODEVICES 8 -#define UI_REFRESH_TIME 77 +#define UI_REFRESH_TIME 66 #define FADE_TIME 25 // DMX Frame time, 40 fps, avoid clicks struct dmxSetting { diff --git a/src/libremediaserver-audio.cpp b/src/libremediaserver-audio.cpp index ab12820..2ea3266 100644 --- a/src/libremediaserver-audio.cpp +++ b/src/libremediaserver-audio.cpp @@ -108,7 +108,7 @@ void libreMediaServerAudio::dmxInput(int layer, int channel, int value) #ifndef NOGUI if (m_ui) { m_lmsUi->m_aw->playbackChanged(layer, s); - m_lmsUi->m_aw->cursorChanged(layer, m_mae.getCursor(layer)); + //m_lmsUi->m_aw->cursorChanged(layer, m_mae.getCursor(layer)); } #endif } @@ -170,16 +170,28 @@ void libreMediaServerAudio::uiSliderChanged(int layer, Slider s, int value) void libreMediaServerAudio::uiPlaybackChanged(int layer, Status s) { - m_mae.playbackChanged(layer, s); - m_currentStatus[layer] = 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; - m_mae.loadMedia(layer, mediaFile.toLatin1().data()); - m_currentMedia[layer] = mediaFile; - m_lmsUi->m_aw->mediaLoaded(layer, mediaFile, m_mae.getDuration(layer)); + 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/miniaudioengine.cpp b/src/miniaudioengine.cpp index fa58464..d1040b9 100644 --- a/src/miniaudioengine.cpp +++ b/src/miniaudioengine.cpp @@ -1,5 +1,5 @@ #include "miniaudioengine.h" - +#include //enum macro MiniAudioEngine::MiniAudioEngine() { for (int i =0; i < MAX_LAYERS; i++) { @@ -27,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; @@ -73,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; } @@ -85,7 +86,7 @@ ma_result MiniAudioEngine::startContext() 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 = 1; + resourceManagerConfig.jobThreadCount = 4; result = ma_resource_manager_init(&resourceManagerConfig, &resourceManager); if (result != MA_SUCCESS) { qCritical("Failed to initialize audio resource manager."); @@ -109,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); } @@ -127,10 +128,8 @@ ma_result MiniAudioEngine::loadMedia(int layer, char *file) } 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_SOUND_FLAG_NO_PITCH \ - | 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); @@ -157,7 +156,7 @@ float MiniAudioEngine::getDuration(int layer) } result = ma_sound_get_data_format(&m_currentSound[layer], NULL, NULL, &sampleRate, NULL, 0); if (result != MA_SUCCESS) { - return result; + return MA_ERROR; } ret = 1000.0f * (lengthInPCMFrames / float(sampleRate)); return ret; @@ -173,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 = -1; + qWarning("%i can not get cursor error %i", layer, result); + ret = MA_ERROR; } return ret; } @@ -188,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() << "Layer:" << layer << m_currentLayerValues[layer].media << "samples/sec:" << sampleRate << "format:" << format << "channels:" << 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; } @@ -220,55 +218,70 @@ void MiniAudioEngine::panChanged(int layer, float 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]); - this->setCursor(layer, m_currentLayerValues[layer].cursor); + 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: + 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; } - m_currentLayerValues[layer].status = status; + 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 end; + ma_result result = MA_SUCCESS; + ma_uint64 end, start; + + 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; - if (m_mediaLoaded[layer] == false) - return; - ma_sound_get_length_in_pcm_frames(&m_currentSound[layer], &end); - ma_uint64 start = (cursor * end) / 65025; - ma_sound_seek_to_pcm_frame(&m_currentSound[layer], start); - ma_result result = ma_data_source_set_loop_point_in_pcm_frames(&m_currentSound[layer], start, end); - if (result != MA_SUCCESS) { - return; // Failed to set the loop point. - } + result = this->seekToCursor(layer, cursor); + return (result); } Status MiniAudioEngine::getStatus(int layer) @@ -278,9 +291,9 @@ Status MiniAudioEngine::getStatus(int layer) 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); - this->setCursor(layer, m_currentLayerValues[layer].cursor); } diff --git a/src/miniaudioengine.h b/src/miniaudioengine.h index 2c3ec5a..6d0ea19 100644 --- a/src/miniaudioengine.h +++ b/src/miniaudioengine.h @@ -13,7 +13,7 @@ class MiniAudioEngine 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,13 +21,14 @@ 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 float getVol(int layer) { + return ma_sound_get_volume(&m_currentSound[layer]); } private: ma_resource_manager_config resourceManagerConfig; @@ -43,9 +44,10 @@ private: 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/settings.cpp b/src/settings.cpp index 9e00914..9d06394 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -81,9 +81,9 @@ void Settings::readFromFile(QString file) { } void Settings::printSettings() { - qInfo() << "Settings read;\nShow Ui:" << m_ui << "Layers:" << m_layersNumber << "Path:" << m_pathmedia <<"Audio Device qty:" << m_audioDeviceQty; + qInfo() << "Settings readed:\nShow Ui:" << m_ui << "Layers:" << m_layersNumber << "Path:" << m_pathmedia <<"Audio Device qty:" << m_audioDeviceQty; for (uint i = 0; i < m_audioDeviceQty; i++) - qInfo() << "Audio device id:" << m_audioDeviceId[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; } From f67ad9b1e1c2b2a98b2f7c6abf5d925a1f35a48b Mon Sep 17 00:00:00 2001 From: snt Date: Thu, 9 May 2024 14:07:24 +0200 Subject: [PATCH 12/38] =?UTF-8?q?nuevos=20modos=20de=20reproducci=C3=B3n:?= =?UTF-8?q?=20Play=20Folder,=20Play=20Folder=20Loop,=20Play=20Folder=20Ran?= =?UTF-8?q?dom.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/roadmap.txt | 1 - src/defines.h | 13 +++++-- src/libremediaserver-audio.cpp | 64 +++++++++++++++++++++++++++++++++- src/libremediaserver-audio.h | 1 + src/medialibrary.h | 3 ++ src/miniaudioengine.cpp | 3 ++ src/miniaudioengine.h | 2 ++ 7 files changed, 83 insertions(+), 4 deletions(-) diff --git a/docs/roadmap.txt b/docs/roadmap.txt index 94081f4..0771257 100644 --- a/docs/roadmap.txt +++ b/docs/roadmap.txt @@ -51,5 +51,4 @@ v 0.2.1 - Tests: errors on wrong conf file. v0.2.0: -- BUGFIX: crash at startup and no dmx signal. - Vumeter or indicator about audio output in layer and master, add to sliderGroup. diff --git a/src/defines.h b/src/defines.h index a9c92b8..ff299c1 100644 --- a/src/defines.h +++ b/src/defines.h @@ -22,7 +22,10 @@ enum Status Paused, PlayingOnce, PlayingLoop, - Iddle + Iddle, + PlayingFolder, + PlayingFolderLoop, + PlayingFolderRandom }; static const char* StatusStr[] = @@ -30,8 +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 }; @@ -52,5 +58,8 @@ struct layerData { int pan; int pitch; float duration; + int address; + unsigned int universe; + int device; }; #endif // DEFINES_H diff --git a/src/libremediaserver-audio.cpp b/src/libremediaserver-audio.cpp index 2ea3266..a9868fd 100644 --- a/src/libremediaserver-audio.cpp +++ b/src/libremediaserver-audio.cpp @@ -70,6 +70,14 @@ void libreMediaServerAudio::loadMedia(int layer, int folder, int file) #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) @@ -102,6 +110,12 @@ void libreMediaServerAudio::dmxInput(int layer, int channel, int value) 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]; @@ -131,10 +145,58 @@ void libreMediaServerAudio::refreshUi() { } if (m_updateUi[i][3] >= 0 \ || m_currentStatus[i] == Status::PlayingOnce\ - || m_currentStatus[i] == Status::PlayingLoop) { + || 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); + } + } } } diff --git a/src/libremediaserver-audio.h b/src/libremediaserver-audio.h index 1af37df..d83446a 100644 --- a/src/libremediaserver-audio.h +++ b/src/libremediaserver-audio.h @@ -52,6 +52,7 @@ private: Status m_currentStatus[MAX_LAYERS]; QList m_dmxSettings; bool m_ui; + QList m_played; #ifndef NOGUI QTimer *m_refreshUi; libreMediaServerAudioUi *m_lmsUi; diff --git a/src/medialibrary.h b/src/medialibrary.h index 8c2eb07..ea92bca 100644 --- a/src/medialibrary.h +++ b/src/medialibrary.h @@ -29,6 +29,9 @@ public: MediaLibrary(QObject *parent = 0); 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 d1040b9..9204636 100644 --- a/src/miniaudioengine.cpp +++ b/src/miniaudioengine.cpp @@ -247,6 +247,9 @@ ma_result MiniAudioEngine::playbackChanged(int layer, Status status) 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); result = ma_sound_start(&m_currentSound[layer]); diff --git a/src/miniaudioengine.h b/src/miniaudioengine.h index 6d0ea19..6e1393c 100644 --- a/src/miniaudioengine.h +++ b/src/miniaudioengine.h @@ -5,6 +5,7 @@ #include "miniaudio.h" #include "defines.h" // MAX_LAYERS #include // prints messages +#define MA_DEBUG_OUTPUT class MiniAudioEngine { @@ -29,6 +30,7 @@ protected: 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; From 7631e54d51aa2dc2bcb8c6640047cec8272219bc Mon Sep 17 00:00:00 2001 From: snt Date: Thu, 9 May 2024 14:10:46 +0200 Subject: [PATCH 13/38] =?UTF-8?q?nuevos=20modos=20reproducci=C3=B3n=20en?= =?UTF-8?q?=20personalidad?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/LibreMediaServer_Audio.hed | 105 ++++++++++++++++---------------- 1 file changed, 54 insertions(+), 51 deletions(-) diff --git a/docs/LibreMediaServer_Audio.hed b/docs/LibreMediaServer_Audio.hed index 58d0fd5..fda3e2d 100644 --- a/docs/LibreMediaServer_Audio.hed +++ b/docs/LibreMediaServer_Audio.hed @@ -2,7 +2,7 @@ ҊÅЁ䍡 ӽ Ꭸʆ -ɑ +ɑ ɬ 򉇁 Ԑɉ @@ -24,53 +24,56 @@ Ԓlj -έ -倬쏎 - -޽ڊ - -؛ -ŕ -ðݑ -ȕ -˛ -̽ɧ - -𓎍 - - -ً - -ʐ -ï -뷪 - - -܋ -Ò -뻡 -ǒ - -𓎍 -ʟ - -㍛ - - - - - - -𓎑 - - - -ҟ - - - -ف - - - - +Ŷ§ +ÐŽÊ +𡭟Ē + +܊ +񢰽 +зм +񓎍 +ҭ +ܻۏ +ڑן +ޏ + +Ƴ + + + +Ǖ + +𓎍Ԏ + + +Ξ +ֆ + +ց + +ᱯ +݈ + +؈ + +馹 +۴ +𓎍 + + + + + +𓎑 + + + +Ɨ + + + +Ӌ +쏎 + + + From 103a33820ef8c407f6b02519c1e286378d2bd0e3 Mon Sep 17 00:00:00 2001 From: snt Date: Fri, 10 May 2024 20:03:14 +0200 Subject: [PATCH 14/38] pan y pitchs faders horizontales, funciona varias instancias con multidispositivo y patcheable en jack. --- docs/changelog.txt | 10 ++- docs/roadmap.txt | 3 - src/audiolayerwidget.cpp | 33 +++++----- src/audiolayerwidget.h | 1 + src/audiowidget.cpp | 5 +- src/audiowidget.h | 1 + src/defines.h | 12 ++-- src/libremediaserver-audio.cpp | 5 +- src/miniaudioengine.cpp | 29 +++------ src/miniaudioengine.h | 6 +- src/slidergroup.cpp | 108 ++++++++++++++++++--------------- src/slidergroup.h | 80 +++++++++++++++++++++++- 12 files changed, 188 insertions(+), 105 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 5c7e0b6..c324e44 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -13,8 +13,8 @@ v 0.2.0 Antígona (26/05/2024) + Pan. + Show faders values. New SliderGroup class. + Entry Point 16 bits. -+ Refactor AudioMasterWidget to AudioDMXReceptionWidget. -+ Read mp3, flac, wav (mp3 has given some errors seeking cursor...). ++ Refactor AudioMasterWidget to dmxWidget. ++ Read mp3, flac, wav (mp3 has given some errors seeking cursors...). Audio Engine is working at 48Khz and 32 bits pcm float, if media files are encoding at same configuration, it saves a resampling operation later live. + 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. @@ -23,8 +23,12 @@ v 0.2.0 Antígona (26/05/2024) + 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). ++ 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). Signals are used only from Ui to libreMediaServer to notify user interactions and Qtimers. + Load media files from ui clicking in the media labels. ++ New Play Modes: + - Play all medias found in one folder. + - Play all medias in one folder consecutevily. + - Play all medias in one folder randomly. v 0.1.3 Leúcade (19/04/2024) diff --git a/docs/roadmap.txt b/docs/roadmap.txt index 0771257..9385090 100644 --- a/docs/roadmap.txt +++ b/docs/roadmap.txt @@ -26,9 +26,6 @@ v 0.2.2 v 0.2.1 - Multi devices output. -- Play Mode: - - Play all medias found in folder consecutevily or random, with loop. - - Play all medias, consecutevily and random, with loop. - mute/panic on layer. - Master Bus Layer: - each layer will have one "Gain" prefader that acts in source, "Vol" in v 1.3. diff --git a/src/audiolayerwidget.cpp b/src/audiolayerwidget.cpp index e8d22bd..2655938 100644 --- a/src/audiolayerwidget.cpp +++ b/src/audiolayerwidget.cpp @@ -46,11 +46,11 @@ AudioLayerWidget::AudioLayerWidget(QWidget *parent, int layer): m_progressTime = new QTimeEdit; m_progressTime->setToolTip("Current Time"); m_progressTime->setObjectName("Current Time"); - m_progressTime->setDisplayFormat("mm:ss:zz"); + m_progressTime->setDisplayFormat("mm:ss:zzz"); m_progressTime->setReadOnly(true); m_progressTime->setButtonSymbols(QAbstractSpinBox::NoButtons); m_progressTime->setMinimumWidth(80); - m_progressTime->setMaximumWidth(80); + //m_progressTime->setMaximumWidth(80); m_progressTime->setFocusPolicy(Qt::NoFocus); m_progressTime->setAlignment(Qt::AlignHCenter); m_progressTime->setContentsMargins(0,0,0,0); @@ -68,22 +68,22 @@ AudioLayerWidget::AudioLayerWidget(QWidget *parent, int layer): 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))); + QVBoxLayout *volumeBox = new QVBoxLayout; + m_pitch = new SliderGroup("Pitch", 0 , 255, 0, NULL); + volumeBox->addWidget(m_pitch); + connect(m_pitch, SIGNAL(valueChanged(int)), this, SLOT(pitchChanged(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); + m_volume = new SliderGroup("Vol", 0 , 100, 2, NULL); + volumeBox->addWidget(m_volume); + connect(m_volume, SIGNAL(valueChanged(int)), this, SLOT(volumeChanged(int))); 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); + layout->setContentsMargins(1, 1, 1, 1); this->setLayout(layout); } @@ -113,13 +113,17 @@ void AudioLayerWidget::toggleSuspendResume() switch (m_status) { case Status::PlayingLoop: case Status::PlayingOnce: + case Status::PlayingFolder: + case Status::PlayingFolderLoop: + case Status::PlayingFolderRandom: + m_oldStatus = m_status; this->setPlaybackStatus(Status::Paused); emit uiPlaybackChanged(m_layer, Status::Paused); break; case Status::Paused: case Status::Stopped: - this->setPlaybackStatus(Status::PlayingLoop); - emit uiPlaybackChanged(m_layer, Status::PlayingLoop); + this->setPlaybackStatus(m_oldStatus); + emit uiPlaybackChanged(m_layer, m_oldStatus); case Status::Iddle: break; } @@ -174,10 +178,9 @@ void AudioLayerWidget::setMediaFile(QString file) void AudioLayerWidget::setPlaybackStatus(Status s) { - Status status = static_cast(s); m_suspendResumeButton->blockSignals(true); - m_status = status; - m_suspendResumeButton->setText(StatusStr[status]); + m_status = s; + m_suspendResumeButton->setText(StatusStr[s]); m_suspendResumeButton->blockSignals(false); } diff --git a/src/audiolayerwidget.h b/src/audiolayerwidget.h index 9380b81..cae18d4 100644 --- a/src/audiolayerwidget.h +++ b/src/audiolayerwidget.h @@ -21,6 +21,7 @@ public: private: Status m_status; + Status m_oldStatus; int m_layer; QPushButton *m_suspendResumeButton; ClickableLabel *m_fileValue; diff --git a/src/audiowidget.cpp b/src/audiowidget.cpp index 8bd077c..e163da3 100644 --- a/src/audiowidget.cpp +++ b/src/audiowidget.cpp @@ -5,7 +5,8 @@ AudioWidget::AudioWidget(QWidget *parent) : QWidget(parent) , m_layout(new QHBoxLayout()) { - for (int i= 0; i < Settings::getInstance()->getLayersNumber(); i++ ) { + m_layers = Settings::getInstance()->getLayersNumber(); + for (uint i= 0; i < m_layers; i++ ) { AudioLayerWidget *alw = new AudioLayerWidget(this, i); m_layout->insertWidget(i, alw); connect(alw, SIGNAL(uiSliderChanged(int, Slider, int)), this, SIGNAL(uiSliderChanged(int, Slider, int))); @@ -64,7 +65,7 @@ void AudioWidget::cursorChanged(int layer, float cursor) void AudioWidget::refreshUi() { - for (int i = 0; i < MAX_LAYERS; i++) + for (uint i = 0; i < m_layers; i++) { if (m_layerUpdate[i].updated) { QLayoutItem * const item = m_layout->itemAt(i); diff --git a/src/audiowidget.h b/src/audiowidget.h index dd9b1f1..c314656 100644 --- a/src/audiowidget.h +++ b/src/audiowidget.h @@ -18,6 +18,7 @@ private: QHBoxLayout *m_layout; layerData m_layerUpdate[MAX_LAYERS]; QTimer *m_refreshUi; + uint m_layers; public slots: void volChanged(int layer, float vol); diff --git a/src/defines.h b/src/defines.h index ff299c1..2355059 100644 --- a/src/defines.h +++ b/src/defines.h @@ -7,7 +7,7 @@ #define DEFAULT_FILE "lms-audio.xlm" #define MAX_LAYERS 4 #define MAX_AUDIODEVICES 8 -#define UI_REFRESH_TIME 66 +#define UI_REFRESH_TIME 100 #define FADE_TIME 25 // DMX Frame time, 40 fps, avoid clicks struct dmxSetting { @@ -32,12 +32,12 @@ static const char* StatusStr[] = { "Stop", "Pause", - "Playing One", - "Playing One Loop", + "Play One", + "Play One Loop", "Iddle", - "Playing Folder", - "Playing Folder Loop", - "Playing Folder Random", + "Play Folder", + "Play Folder Loop", + "Play Folder Rand", 0x0 }; diff --git a/src/libremediaserver-audio.cpp b/src/libremediaserver-audio.cpp index a9868fd..8f16ebc 100644 --- a/src/libremediaserver-audio.cpp +++ b/src/libremediaserver-audio.cpp @@ -122,7 +122,8 @@ void libreMediaServerAudio::dmxInput(int layer, int channel, int value) #ifndef NOGUI if (m_ui) { m_lmsUi->m_aw->playbackChanged(layer, s); - //m_lmsUi->m_aw->cursorChanged(layer, m_mae.getCursor(layer)); + m_played.clear(); + m_played.append(m_ola->getValue(layer, DMX_FILE)); } #endif } @@ -153,8 +154,6 @@ void libreMediaServerAudio::refreshUi() { 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; } diff --git a/src/miniaudioengine.cpp b/src/miniaudioengine.cpp index 9204636..264871b 100644 --- a/src/miniaudioengine.cpp +++ b/src/miniaudioengine.cpp @@ -85,7 +85,7 @@ 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.decodedSampleRate = ma_standard_sample_rate_44100; resourceManagerConfig.jobThreadCount = 4; result = ma_resource_manager_init(&resourceManagerConfig, &resourceManager); if (result != MA_SUCCESS) { @@ -129,6 +129,7 @@ ma_result MiniAudioEngine::loadMedia(int layer, char *file) result = ma_sound_init_from_file(&engine, file, \ MA_SOUND_FLAG_NO_SPATIALIZATION \ | MA_SOUND_FLAG_DECODE \ + | MA_SOUND_FLAG_STREAM \ /*| MA_SOUND_FLAG_NO_PITCH \*/ , NULL, NULL, &m_currentSound[layer]); if (result != MA_SUCCESS) @@ -144,22 +145,15 @@ ma_result MiniAudioEngine::loadMedia(int layer, char *file) float MiniAudioEngine::getDuration(int layer) { ma_result result; - ma_uint64 lengthInPCMFrames; - ma_uint32 sampleRate; float ret; if (m_mediaLoaded[layer] == false) return MA_DOES_NOT_EXIST; - result = ma_sound_get_length_in_pcm_frames(&m_currentSound[layer], &lengthInPCMFrames); + result = ma_sound_get_length_in_seconds(&m_currentSound[layer], &ret); 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; + return (ret * 1000); } float MiniAudioEngine::getCursor(int layer) @@ -233,27 +227,25 @@ ma_result MiniAudioEngine::playbackChanged(int layer, Status status) if (m_mediaLoaded[layer] == false) return MA_DOES_NOT_EXIST; + bool loop = false; switch (status) { case Status::Paused: result = ma_sound_stop_with_fade_in_milliseconds(&m_currentSound[layer], FADE_TIME); break; case Status::Stopped: - result = ma_sound_stop_with_fade_in_milliseconds(&m_currentSound[layer], FADE_TIME); + 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); - result = ma_sound_start(&m_currentSound[layer]); - break; + loop = true; 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_set_looping(&m_currentSound[layer], loop); result = ma_sound_start(&m_currentSound[layer]); - break; + //this->volChanged(layer, m_currentLayerValues[layer].vol); // glitch when seek to cursor, how flush the audio buffer? default: break; } @@ -273,8 +265,7 @@ ma_result MiniAudioEngine::seekToCursor(int layer, int cursor) 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); + //result = ma_data_source_set_loop_point_in_pcm_frames(&m_currentSound[layer], start, end); // this do nothing here, it must be done after set_looping or start? return (result); } diff --git a/src/miniaudioengine.h b/src/miniaudioengine.h index 6e1393c..103126e 100644 --- a/src/miniaudioengine.h +++ b/src/miniaudioengine.h @@ -1,11 +1,15 @@ #ifndef MINIAUDIOENGINE_H #define MINIAUDIOENGINE_H +#define MA_ENABLE_ONLY_SPECIFIC_BACKENDS 1 +#define MA_ENABLE_JACK 1 +#define MA_NO_GENERATION 1 +#define MA_DEBUG_OUTPUT 1 +#define MA_DISABLE_PULSEAUDIO #define MINIAUDIO_IMPLEMENTATION #include "miniaudio.h" #include "defines.h" // MAX_LAYERS #include // prints messages -#define MA_DEBUG_OUTPUT class MiniAudioEngine { diff --git a/src/slidergroup.cpp b/src/slidergroup.cpp index 9ef9680..4395a8d 100644 --- a/src/slidergroup.cpp +++ b/src/slidergroup.cpp @@ -1,5 +1,17 @@ #include "slidergroup.h" #include +#include + +DoubleSpinBoxClickable::DoubleSpinBoxClickable(QWidget *parent) + : QDoubleSpinBox{parent} {} + +DoubleSpinBoxClickable::~DoubleSpinBoxClickable() {} + +SliderClickDisable::SliderClickDisable(QWidget *parent) + : QSlider{parent} {} + +SliderClickDisable::~SliderClickDisable() {} + SliderGroup::SliderGroup(QString name, int min, int max, @@ -7,42 +19,46 @@ SliderGroup::SliderGroup(QString name, QWidget *parent) : QWidget(parent) { - QVBoxLayout *layout = new QVBoxLayout; + QBoxLayout *layout; + if (decimals) { + layout = new QVBoxLayout; + slider.setOrientation(Qt::Vertical); + } + else { + layout = new QHBoxLayout; + slider.setOrientation(Qt::Horizontal); + slider.setMinimumHeight(10); + } 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 {" + 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;}" + "margin: 0px;}" ); - slider->setContentsMargins(0, 0, 0, 0); - valueBox = new QDoubleSpinBox(); - valueBox->setFocusPolicy(Qt::NoFocus); - valueBox->setButtonSymbols(QAbstractSpinBox::NoButtons); - valueBox->setMinimumWidth(50); - valueBox->setRange(min, max); - valueBox->setValue(0); - valueBox->setDecimals(decimals); - valueBox->setObjectName(name); - valueBox->setToolTip(name); - valueBox->setAlignment(Qt::AlignHCenter); - valueBox->setContentsMargins(0, 0, 0, 0); - connect(slider, SIGNAL(valueChanged(int)), this, SLOT(sliderValueChanged(int))); - //connect(slider, SIGNAL(mousePressEvent(QMouseEvent)), this, SLOT(mousePressEvent(QMouseEvent *))); - layout->addWidget(slider); - layout->addWidget(valueBox); + slider.setContentsMargins(0, 0, 0, 0); + valueBox.setFocusPolicy(Qt::NoFocus); + valueBox.setButtonSymbols(QAbstractSpinBox::NoButtons); + valueBox.setMinimumWidth(50); + valueBox.setRange(min, max); + valueBox.setValue(0); + valueBox.setDecimals(decimals); + valueBox.setObjectName(name); + valueBox.setToolTip(name); + valueBox.setAlignment(Qt::AlignHCenter); + valueBox.setContentsMargins(0, 0, 0, 0); + connect(&slider, SIGNAL(valueChanged(int)), this, SLOT(sliderValueChanged(int))); + connect(&valueBox, SIGNAL(enableSlider()), this, SLOT(enableSlider())); + layout->addWidget(&slider); + layout->addWidget(&valueBox); this->setStyleSheet("border: 1px solid #5a4855;" "width: 50px;" "margin: 0px;" @@ -55,27 +71,19 @@ SliderGroup::SliderGroup(QString name, void SliderGroup::sliderValueChanged(int value) { - valueBox->blockSignals(true); - valueBox->setValue(value); - valueBox->blockSignals(false); + valueBox.blockSignals(true); + valueBox.setValue(value); + valueBox.blockSignals(false); emit valueChanged(value); }; void SliderGroup::setValue(float 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); + slider.blockSignals(true); + valueBox.blockSignals(true); + if (int(value) != slider.value()) + slider.setValue(value); + valueBox.setValue(value); + slider.blockSignals(false); + valueBox.blockSignals(false); } diff --git a/src/slidergroup.h b/src/slidergroup.h index 5bfeeb8..b6940b1 100644 --- a/src/slidergroup.h +++ b/src/slidergroup.h @@ -5,6 +5,78 @@ #include #include #include +#include +#include +#include +/* +//slider->installEventFilter(new QSliderAnalyser); +class QSliderAnalyser + : public QObject +{ + public: + QSliderAnalyser() + { + } + + virtual ~QSliderAnalyser() + { + } + + protected: + bool eventFilter(QObject* object, QEvent* event) override + { + if (event->type() == QEvent::MouseButtonPress) { + qDebug() << event->type() << object->objectName(); + } + return QObject::eventFilter(object, event); + } +};*/ + +class DoubleSpinBoxClickable: public QDoubleSpinBox +{ + Q_OBJECT + +public: + DoubleSpinBoxClickable(QWidget *parent = 0); + ~DoubleSpinBoxClickable(); + +signals: + void enableSlider(); + +protected: + void mousePressEvent ( QMouseEvent * event ) + { + if (event->button() == Qt::LeftButton) { + qDebug() << "enabling slider"; + emit(enableSlider()); + } + event->accept(); + } +}; + +class SliderClickDisable + : public QSlider +{ + Q_OBJECT + +public: + explicit SliderClickDisable(QWidget *parent = Q_NULLPTR); + ~SliderClickDisable(); + +protected: + void mousePressEvent ( QMouseEvent * event ) + { + if (event->button() == Qt::RightButton) + { + if (this->isEnabled()) { + qDebug() << "disabling slider"; + this->setDisabled(true); + } + event->accept(); + } + QSlider::mousePressEvent(event); + } +}; class SliderGroup : public QWidget { @@ -25,10 +97,12 @@ public slots: void sliderValueChanged(int value); private: - QSlider *slider; - QDoubleSpinBox *valueBox; + SliderClickDisable slider; + DoubleSpinBoxClickable valueBox; + +private slots: + void enableSlider() { slider.setEnabled(true); } - void mousePressEvent(QMouseEvent* event); }; #endif From 8c69da5f9d4886e96805e4a8e70f74fd06e762e4 Mon Sep 17 00:00:00 2001 From: snt Date: Fri, 10 May 2024 21:04:26 +0200 Subject: [PATCH 15/38] funcionando multidispositivo, cada capa se patchea a un dispositivo de audio. --- docs/lms-audio.xlm | 10 ++++----- src/defines.h | 1 + src/libremediaserver-audio.cpp | 10 ++++++--- src/miniaudioengine.cpp | 37 ++++++++++++++++------------------ src/miniaudioengine.h | 10 ++++----- src/settings.cpp | 4 +--- src/settings.h | 3 ++- 7 files changed, 38 insertions(+), 37 deletions(-) diff --git a/docs/lms-audio.xlm b/docs/lms-audio.xlm index 4274267..b2ea7ed 100644 --- a/docs/lms-audio.xlm +++ b/docs/lms-audio.xlm @@ -1,8 +1,8 @@ - - - - - + + + + + diff --git a/src/defines.h b/src/defines.h index 2355059..a885485 100644 --- a/src/defines.h +++ b/src/defines.h @@ -14,6 +14,7 @@ struct dmxSetting { int address; unsigned int universe; int layer; + int audioDevice; }; enum Status diff --git a/src/libremediaserver-audio.cpp b/src/libremediaserver-audio.cpp index 8f16ebc..10cef51 100644 --- a/src/libremediaserver-audio.cpp +++ b/src/libremediaserver-audio.cpp @@ -26,6 +26,7 @@ libreMediaServerAudio::libreMediaServerAudio() m_settings = Settings::getInstance(); m_settings->readFile(); m_ui = m_settings->getShowUi(); + m_dmxSettings = m_settings->getDmxSettings(); m_mediaLibrary = new MediaLibrary; m_mediaLibrary->initMediaLibrary(); for (int i = 0; i < MAX_LAYERS; i++) { @@ -42,7 +43,10 @@ libreMediaServerAudio::libreMediaServerAudio() Q_CHECK_PTR(m_ola); m_ola->blockSignals(true); m_ola->registerUniverse(); - m_mae.startEngine(m_settings->getAudioDeviceId()); + m_mae.startEngine(); + uint *audioDevList = m_settings->getAudioDeviceId(); + for (uint i = 0; i < m_settings->getAudioDeviceQty(); i++) + m_mae.startDevice(audioDevList[i], i); qDebug("Core init Complete. Start reading DMX."); m_ola->blockSignals(false); #ifdef NOGUI @@ -62,7 +66,7 @@ void libreMediaServerAudio::loadMedia(int layer, int folder, int 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_mae.loadMedia(layer, mediaFile.toLatin1().data(), m_dmxSettings.at(layer).audioDevice); m_currentMedia[layer] = mediaFile; #ifndef NOGUI if (m_ui) @@ -247,7 +251,7 @@ void libreMediaServerAudio::uiLoadMedia(int layer, QString mediaFile) if (strcmp(mediaFile.toLatin1().constData(), m_currentMedia[layer].toLatin1().constData()) == 0) return; - result = m_mae.loadMedia(layer, mediaFile.toLatin1().data()); + result = m_mae.loadMedia(layer, mediaFile.toLatin1().data(), m_dmxSettings[layer].audioDevice); if (result == MA_SUCCESS) { m_currentMedia[layer] = mediaFile; m_lmsUi->m_aw->mediaLoaded(layer, mediaFile, m_mae.getDuration(layer)); diff --git a/src/miniaudioengine.cpp b/src/miniaudioengine.cpp index 264871b..6051105 100644 --- a/src/miniaudioengine.cpp +++ b/src/miniaudioengine.cpp @@ -21,60 +21,57 @@ void MiniAudioEngine::audioDataCallback(ma_device* pDevice, void* pOutput, const void MiniAudioEngine::stopEngine() { - ma_engine_uninit(&engine); - ma_device_uninit(&device); + ma_engine_uninit(&engine[0]); + ma_device_uninit(&device[0]); ma_context_uninit(&context); ma_resource_manager_uninit(&resourceManager); } -bool MiniAudioEngine::startEngine(uint n) +bool MiniAudioEngine::startEngine() { ma_result result; result = this->startContext(); 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(uint id) +ma_result MiniAudioEngine::startDevice(uint systemId, uint internalId) { ma_result result; ma_device_config deviceConfig; ma_engine_config engineConfig; - if (id >= playbackDeviceCount) - id = playbackDeviceCount - 1; + if (systemId >= playbackDeviceCount) + systemId = playbackDeviceCount - 1; deviceConfig = ma_device_config_init(ma_device_type_playback); - deviceConfig.playback.pDeviceID = &pPlaybackDeviceInfos[id].id; + deviceConfig.playback.pDeviceID = &pPlaybackDeviceInfos[systemId].id; deviceConfig.playback.format = resourceManager.config.decodedFormat; deviceConfig.playback.channels = 0; deviceConfig.sampleRate = resourceManager.config.decodedSampleRate; deviceConfig.dataCallback = audioDataCallback; - deviceConfig.pUserData = &engine; - result = ma_device_init(&context, &deviceConfig, &device); + deviceConfig.pUserData = &engine[internalId]; + result = ma_device_init(&context, &deviceConfig, &device[internalId]); if (result != MA_SUCCESS) { - qCritical("Failed to initialize audio device %s.", pPlaybackDeviceInfos[id].name); + qCritical("Failed to initialize audio device %s.", pPlaybackDeviceInfos[systemId].name); return result; } engineConfig = ma_engine_config_init(); - engineConfig.pDevice = &device; + engineConfig.pDevice = &device[internalId]; engineConfig.pResourceManager = &resourceManager; engineConfig.noAutoStart = MA_TRUE; - result = ma_engine_init(NULL, &engine); + result = ma_engine_init(NULL, &engine[internalId]); if (result != MA_SUCCESS) { qCritical("Failed to initialize audio engine."); return result; } - result = ma_engine_start(&engine); + result = ma_engine_start(&engine[internalId]); if (result != MA_SUCCESS) { - qCritical("Failed to start audio engine %i.", id); + qCritical("Failed to start audio engine %i.", systemId); return result; } - iChosenDevice = id; - qInfo("Initialized audio device %d : %s", id, pPlaybackDeviceInfos[id].name); + qInfo("Initialized audio device internal: %ui system: %d %s", internalId, systemId, pPlaybackDeviceInfos[systemId].name); return result; } @@ -117,7 +114,7 @@ ma_result MiniAudioEngine::getAllAudioDevices() return result; } -ma_result MiniAudioEngine::loadMedia(int layer, char *file) +ma_result MiniAudioEngine::loadMedia(int layer, char *file, uint audioDevice) { ma_result result; @@ -126,7 +123,7 @@ ma_result MiniAudioEngine::loadMedia(int layer, char *file) ma_sound_uninit(&m_currentSound[layer]); m_mediaLoaded[layer] = false; } - result = ma_sound_init_from_file(&engine, file, \ + result = ma_sound_init_from_file(&engine[audioDevice], file, \ MA_SOUND_FLAG_NO_SPATIALIZATION \ | MA_SOUND_FLAG_DECODE \ | MA_SOUND_FLAG_STREAM \ diff --git a/src/miniaudioengine.h b/src/miniaudioengine.h index 103126e..b91d718 100644 --- a/src/miniaudioengine.h +++ b/src/miniaudioengine.h @@ -18,11 +18,12 @@ class MiniAudioEngine public: MiniAudioEngine(); void stopEngine(); - bool startEngine(uint id); + bool startEngine(); + ma_result startDevice(uint id, uint internalId); static void audioDataCallback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount); protected: - ma_result loadMedia(int layer, char *media ); + ma_result loadMedia(int layer, char *media, uint audioDevice); void volChanged(int layer, float vol); void panChanged(int layer, float pan); void pitchChanged(int layer, float pitch); @@ -42,15 +43,14 @@ private: ma_device_info* pPlaybackDeviceInfos; ma_uint32 playbackDeviceCount; ma_uint32 iChosenDevice; - ma_engine engine; - ma_device device; + ma_engine engine[MAX_AUDIODEVICES]; + ma_device device[MAX_AUDIODEVICES]; 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 startContext(); void refreshValues(int layer); ma_result seekToCursor(int layer, int cursor); diff --git a/src/settings.cpp b/src/settings.cpp index 9d06394..6527bcb 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -51,16 +51,14 @@ void Settings::readFromFile(QString file) { 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 = xmlReader->attributes().value("id").toLocal8Bit().toInt(); + temp.audioDevice = xmlReader->attributes().value("audioDevice").toLocal8Bit().toInt(); m_settings.append(temp); if (!m_universe.contains(temp.universe)) { m_universe.insert(temp.universe); diff --git a/src/settings.h b/src/settings.h index f27da6a..6e51d14 100644 --- a/src/settings.h +++ b/src/settings.h @@ -22,7 +22,8 @@ public: 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 uint *getAudioDeviceId() { return m_audioDeviceId; } + inline uint getAudioDeviceQty() { return m_audioDeviceQty; } inline bool getShowUi() { return m_ui; } void readFile(); void readFromFile(QString file); From 7cd4c8fbd84d917806c4eecf6c3220b5ac4106e2 Mon Sep 17 00:00:00 2001 From: snt Date: Sun, 12 May 2024 23:23:26 +0200 Subject: [PATCH 16/38] prototipo de filtro funcionando, muy sucio. --- docs/changelog.txt | 13 +- docs/roadmap.txt | 2 +- src/libremediaserver-audio.cpp | 16 +- src/libremediaserver-audio.h | 4 + src/miniaudioengine.cpp | 286 ++++++++++++++++++++++++--------- src/miniaudioengine.h | 53 ++++-- 6 files changed, 276 insertions(+), 98 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index c324e44..ef76b53 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -29,24 +29,21 @@ v 0.2.0 Antígona (26/05/2024) - Play all medias found in one folder. - Play all medias in one folder consecutevily. - Play all medias in one folder randomly. ++ Multi audio devices output. v 0.1.3 Leúcade (19/04/2024) - + Ubuntu 22.04 jammy. + Qt 5.15.3. + 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. ++ 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. - ++ Qt4 diff --git a/docs/roadmap.txt b/docs/roadmap.txt index 9385090..154fd78 100644 --- a/docs/roadmap.txt +++ b/docs/roadmap.txt @@ -25,7 +25,6 @@ v 0.2.2 - Octopus Sound Card support (6 outputs - 8 inputs). v 0.2.1 -- Multi devices output. - mute/panic on layer. - Master Bus Layer: - each layer will have one "Gain" prefader that acts in source, "Vol" in v 1.3. @@ -46,6 +45,7 @@ v 0.2.1 - ¿Exit Point? is it needed? - Hardening: check return errors, try/catch exceptions, i'm too happy.... - Tests: errors on wrong conf file. +- Ui/Ux: seek cursor playback v0.2.0: - Vumeter or indicator about audio output in layer and master, add to sliderGroup. diff --git a/src/libremediaserver-audio.cpp b/src/libremediaserver-audio.cpp index 10cef51..39c700d 100644 --- a/src/libremediaserver-audio.cpp +++ b/src/libremediaserver-audio.cpp @@ -43,11 +43,16 @@ libreMediaServerAudio::libreMediaServerAudio() Q_CHECK_PTR(m_ola); m_ola->blockSignals(true); m_ola->registerUniverse(); - m_mae.startEngine(); + if (!m_mae.startEngine()) { + cout << "Can not start Audio Engine!" << endl; + this->~libreMediaServerAudio(); + } uint *audioDevList = m_settings->getAudioDeviceId(); - for (uint i = 0; i < m_settings->getAudioDeviceQty(); i++) - m_mae.startDevice(audioDevList[i], i); - qDebug("Core init Complete. Start reading DMX."); + if (!m_mae.startDevice(audioDevList, m_settings->getAudioDeviceQty())) { + cout << "Can not start Audio Device!" << audioDevList << endl; + this->~libreMediaServerAudio(); + } + cout << "Core init Complete. Start reading DMX." << endl; m_ola->blockSignals(false); #ifdef NOGUI m_ola->start(QThread::TimeCriticalPriority ); @@ -58,6 +63,9 @@ libreMediaServerAudio::~libreMediaServerAudio() { m_ola->stop(); m_mae.stopEngine(); + sleep(1); + cout << "bye!" << endl; + exit(0); } void libreMediaServerAudio::loadMedia(int layer, int folder, int file) diff --git a/src/libremediaserver-audio.h b/src/libremediaserver-audio.h index d83446a..aed7e45 100644 --- a/src/libremediaserver-audio.h +++ b/src/libremediaserver-audio.h @@ -20,6 +20,10 @@ #ifndef LIBREMEDIASERVERAUDIO_H #define LIBREMEDIASERVERAUDIO_H + +#include +using namespace std; + #include "medialibrary.h" #include "miniaudioengine.h" #include "olathread.h" diff --git a/src/miniaudioengine.cpp b/src/miniaudioengine.cpp index 6051105..a0445c9 100644 --- a/src/miniaudioengine.cpp +++ b/src/miniaudioengine.cpp @@ -1,5 +1,15 @@ #include "miniaudioengine.h" -#include //enum macro +#define LPF_BIAS 0.9f /* Higher values means more bias towards the low pass filter (the low pass filter will be more audible). Lower values means more bias towards the echo. Must be between 0 and 1. */ +#define LPF_CUTOFF_FACTOR 80 /* High values = more filter. */ +#define LPF_ORDER 8 + +//static filterBank m_filterBank[MAX_LAYERS]; +//static ma_node_graph m_nodeGraph[MAX_LAYERS]; +//static soundNode m_soundNode[MAX_LAYERS]; + +static ma_lpf_node g_lpfNode[MAX_AUDIODEVICES]; +static ma_engine m_engine[MAX_AUDIODEVICES]; + MiniAudioEngine::MiniAudioEngine() { for (int i =0; i < MAX_LAYERS; i++) { @@ -12,19 +22,37 @@ MiniAudioEngine::MiniAudioEngine() } } -void MiniAudioEngine::audioDataCallback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) +void MiniAudioEngine::audioDataCallback1(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) { (void)pInput; - //Do master audio processing before sending to device. - ma_engine_read_pcm_frames((ma_engine*)pDevice->pUserData, pOutput, frameCount, NULL); + (void)pDevice; + ma_result result; + result = ma_engine_read_pcm_frames((ma_engine*)pDevice->pUserData, pOutput, frameCount, NULL); + if (result != MA_SUCCESS) { + cout << "1"; + } +} + +void MiniAudioEngine::audioDataCallback2(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) +{ + (void)pInput; + (void)pDevice; + ma_result result; + + result = ma_engine_read_pcm_frames((ma_engine*)pDevice->pUserData, pOutput, frameCount, NULL); + if (result != MA_SUCCESS) { + cout << "2"; + } } void MiniAudioEngine::stopEngine() { - ma_engine_uninit(&engine[0]); - ma_device_uninit(&device[0]); - ma_context_uninit(&context); - ma_resource_manager_uninit(&resourceManager); + for (uint i = 0; i < m_devicesSelected; i++) { + ma_engine_uninit(&m_engine[i]); + ma_device_uninit(&m_device[i]); + } + ma_context_uninit(&m_context); + ma_resource_manager_uninit(&m_resourceManager); } bool MiniAudioEngine::startEngine() @@ -32,66 +60,144 @@ bool MiniAudioEngine::startEngine() ma_result result; result = this->startContext(); - if (result != MA_SUCCESS) return result; + if (result != MA_SUCCESS) return false; result = this->getAllAudioDevices(); - return result; + if (result != MA_SUCCESS) return false; + return true; } -ma_result MiniAudioEngine::startDevice(uint systemId, uint internalId) -{ +ma_result MiniAudioEngine::setNodeGraph(int id) { ma_result result; + + //ma_node_graph_config nodeGraphConfig = ma_node_graph_config_init(CHANNELS); + //result = ma_node_graph_init(&nodeGraphConfig, NULL, &m_nodeGraph[id]); + //if (result != MA_SUCCESS) { + // cout << "ERROR " << result << ": Failed to initialize node graph."; + // return MA_ERROR; + //} +/* + ma_loshelf2_config losConfig = ma_loshelf2_config_init(FORMAT, CHANNELS, SAMPLE_RATE, 1, 0.5, 83.3); // double gainDB, double shelfSlope, double frequency) + result = ma_loshelf2_init(&losConfig, NULL, &m_filterBank[id].loShelfNode); + if (result != MA_SUCCESS) { + cout << "Error " << result << ": Can not init loShelf node." << endl; + return result; + } + ma_engine m; + ma_node_graph *ng = (ma_node_graph *)(&m_engine[id]); + ma_node *node = ma_node_graph_get_endpoint(ng); + node = ma_engine_get_endpoint(&m_engine[id]); + ma_node_base nodeBase = ng->endpoint; + result = ma_node_attach_output_bus(&m_filterBank[id].loShelfNode, 0, &nodeBase, 0); + //result = ma_node_attach_output_bus(&m_filterBank[id].loShelfNode, 0, ma_engine_get_endpoint(engine), 0); + if (result != MA_SUCCESS) { + cout << "Error " << result << ": Can not attach filter to graph endpoint." << endl; + return result; + }*/ +/* + ma_splitter_node_config splitterConfig = ma_splitter_node_config_init(ma_engine_get_channels(&engine[0])); + result = ma_splitter_node_init(ng, &splitterConfig, NULL, &m_filterBank[layer].outputNode); + if (result != MA_SUCCESS) { + cout << "Can not init splitter node." << endl; + return result; + } + result = ma_node_attach_output_bus(&m_filterBank[layer].loShelfNode, 0, &m_filterBank[layer].outputNode, 0); + if (result != MA_SUCCESS) { + cout << "Can not attach loShelf to output." << endl; + return result; + } + result = ma_node_attach_output_bus(&m_filterBank[layer].outputNode, 0, ma_node_graph_get_endpoint(ng), 0); + if (result != MA_SUCCESS) { + cout << "Can not attach splitter to graph endpoint." << endl; + return result; + } + }*/ + /* Low Pass Filter. */ + ma_lpf_node_config lpfNodeConfig = ma_lpf_node_config_init(CHANNELS, SAMPLE_RATE, SAMPLE_RATE / LPF_CUTOFF_FACTOR, LPF_ORDER); + ma_node_graph *ng = ma_engine_get_node_graph(&m_engine[id]); + result = ma_lpf_node_init(ng, &lpfNodeConfig, NULL, &g_lpfNode[id]); + if (result != MA_SUCCESS) { + cout << "ERROR " << result << ": Failed to initialize low pass filter node." << endl; + return result; + } + ma_node *endpoint = ma_engine_get_endpoint(&m_engine[id]); + result = ma_node_attach_output_bus(&g_lpfNode[id], 0, endpoint, 0); + if (result != MA_SUCCESS) { + cout << "ERROR " << result << ": Failed to attach low pass filter node." << endl; + return result; + } + result = ma_node_set_output_bus_volume(&g_lpfNode[id], 0, LPF_BIAS); + if (result != MA_SUCCESS) { + cout << "ERROR " << result << ": Failed to set volume low pass filter node." << endl; + return result; + } + ma_node_set_state(&g_lpfNode[id], ma_node_state::ma_node_state_started); + return (result); +} + +bool MiniAudioEngine::startDevice(uint *systemId, uint nb) +{ + ma_result result = MA_SUCCESS; ma_device_config deviceConfig; ma_engine_config engineConfig; - if (systemId >= playbackDeviceCount) - systemId = playbackDeviceCount - 1; - deviceConfig = ma_device_config_init(ma_device_type_playback); - deviceConfig.playback.pDeviceID = &pPlaybackDeviceInfos[systemId].id; - deviceConfig.playback.format = resourceManager.config.decodedFormat; - deviceConfig.playback.channels = 0; - deviceConfig.sampleRate = resourceManager.config.decodedSampleRate; - deviceConfig.dataCallback = audioDataCallback; - deviceConfig.pUserData = &engine[internalId]; - result = ma_device_init(&context, &deviceConfig, &device[internalId]); - if (result != MA_SUCCESS) { - qCritical("Failed to initialize audio device %s.", pPlaybackDeviceInfos[systemId].name); - return result; + m_devicesSelected = nb; + for (uint internalId = 0; internalId < nb; internalId++) { + deviceConfig = ma_device_config_init(ma_device_type_playback); + deviceConfig.playback.pDeviceID = &m_pPlaybackDeviceInfos[systemId[internalId]].id; + deviceConfig.playback.format = m_resourceManager.config.decodedFormat; + deviceConfig.playback.channels = 0; + deviceConfig.sampleRate = m_resourceManager.config.decodedSampleRate; + if (internalId == 0) + deviceConfig.dataCallback = audioDataCallback1; + else + deviceConfig.dataCallback = audioDataCallback2; + deviceConfig.pUserData = &m_engine[internalId]; + result = ma_device_init(&m_context, &deviceConfig, &m_device[internalId]); + if (result != MA_SUCCESS) { + cout << "Error " << result << ": Failed to initialize audio device " << m_pPlaybackDeviceInfos[*systemId].name << endl; + return false; + } + engineConfig = ma_engine_config_init(); + engineConfig.pDevice = &m_device[internalId]; + engineConfig.pResourceManager = &m_resourceManager; + engineConfig.noAutoStart = MA_TRUE; + result = ma_engine_init(&engineConfig, &m_engine[internalId]); + if (result != MA_SUCCESS) { + cout << "Error " << result << ": Failed to initialize audio engine" << endl; + return false; + } + result = this->setNodeGraph(internalId); + if (result != MA_SUCCESS) { + cout << "Error " << result << ": Failed to set node graph " << systemId[internalId] << endl; + return false; + } + result = ma_engine_start(&m_engine[internalId]); + if (result != MA_SUCCESS) { + cout << "Error " << result << ": Failed to start audio engine" << systemId[internalId] << endl; + return false; + } + cout << "Initialized Audio Device. internalId: " << internalId << " systemId: " << systemId[internalId] << " " << m_pPlaybackDeviceInfos[systemId[internalId]].name << endl; } - engineConfig = ma_engine_config_init(); - engineConfig.pDevice = &device[internalId]; - engineConfig.pResourceManager = &resourceManager; - engineConfig.noAutoStart = MA_TRUE; - result = ma_engine_init(NULL, &engine[internalId]); - if (result != MA_SUCCESS) { - qCritical("Failed to initialize audio engine."); - return result; - } - result = ma_engine_start(&engine[internalId]); - if (result != MA_SUCCESS) { - qCritical("Failed to start audio engine %i.", systemId); - return result; - } - qInfo("Initialized audio device internal: %ui system: %d %s", internalId, systemId, pPlaybackDeviceInfos[systemId].name); - return result; + return true; } ma_result MiniAudioEngine::startContext() { ma_result result; - 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_44100; - resourceManagerConfig.jobThreadCount = 4; - result = ma_resource_manager_init(&resourceManagerConfig, &resourceManager); + m_resourceManagerConfig = ma_resource_manager_config_init(); + m_resourceManagerConfig.decodedFormat = FORMAT; + m_resourceManagerConfig.decodedChannels = CHANNELS; + m_resourceManagerConfig.decodedSampleRate = SAMPLE_RATE; + m_resourceManagerConfig.jobThreadCount = 4; + result = ma_resource_manager_init(&m_resourceManagerConfig, &m_resourceManager); if (result != MA_SUCCESS) { - qCritical("Failed to initialize audio resource manager."); + cout << "Error " << result << ": Failed to initialize audio resource manager." << endl; return result; } - result = ma_context_init(NULL, 0, NULL, &context); + result = ma_context_init(NULL, 0, NULL, &m_context); if (result != MA_SUCCESS) { - qCritical("Failed to initialize audio context."); + cout << "Error " << result << ": Failed to initialize audio context." << endl; } return result; } @@ -101,15 +207,15 @@ ma_result MiniAudioEngine::getAllAudioDevices() { ma_result result; - result = ma_context_get_devices(&context, &pPlaybackDeviceInfos, &playbackDeviceCount, NULL, NULL); + result = ma_context_get_devices(&m_context, &m_pPlaybackDeviceInfos, &m_playbackDeviceCount, NULL, NULL); if (result != MA_SUCCESS) { - qWarning("Failed to enumerate playback devices.\n"); - ma_context_uninit(&context); + cout << "Error" << result << ": Failed to enumerate playback devices." << endl; + ma_context_uninit(&m_context); return result; } - printf("Audio devices available:\n"); - for (ma_uint32 iAvailableDevice = 0; iAvailableDevice < playbackDeviceCount; iAvailableDevice += 1) { - qInfo("%d: : %s", iAvailableDevice, pPlaybackDeviceInfos[iAvailableDevice].name); + cout << "Audio devices available:" << endl; + for (ma_uint32 iAvailableDevice = 0; iAvailableDevice < m_playbackDeviceCount; iAvailableDevice += 1) { + cout << iAvailableDevice << " : " << m_pPlaybackDeviceInfos[iAvailableDevice].name << endl; } return result; } @@ -123,19 +229,49 @@ ma_result MiniAudioEngine::loadMedia(int layer, char *file, uint audioDevice) ma_sound_uninit(&m_currentSound[layer]); m_mediaLoaded[layer] = false; } - result = ma_sound_init_from_file(&engine[audioDevice], file, \ +/* + ma_sound_config soundConfig = ma_sound_config_init(); + soundConfig = ma_sound_config_init(); + soundConfig.pFilePath = file; + soundConfig.pInitialAttachment = &m_filterBank[layer].loShelfNode; + soundConfig.initialAttachmentInputBusIndex = 0; + soundConfig.channelsIn = 0; + soundConfig.channelsOut = CHANNELS; + //soundConfig.monoExpansionMode; + soundConfig.flags = MA_SOUND_FLAG_NO_SPATIALIZATION; + // | MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT; + //| MA_SOUND_FLAG_STREAM | MA_SOUND_FLAG_NO_PITCH + soundConfig.volumeSmoothTimeInPCMFrames = 480; + //soundConfig.initialSeekPointInPCMFrames; + //soundConfig.rangeBegInPCMFrames; + //soundConfig.rangeEndInPCMFrames; + //soundConfig.loopPointBegInPCMFrames; + //soundConfig.loopPointEndInPCMFrames; + //soundConfig.isLooping; + //soundConfig.endCallback; + //soundConfig.pEndCallbackUserData; + + result = ma_sound_init_ex(&m_engine[audioDevice], &soundConfig, &m_currentSound[layer]); + if (result != MA_SUCCESS) { + cout << "Error" << result << ": Failed to load file " << file << endl; + return result; + }*/ + result = ma_sound_init_from_file(&m_engine[audioDevice], file, \ MA_SOUND_FLAG_NO_SPATIALIZATION \ - | MA_SOUND_FLAG_DECODE \ - | MA_SOUND_FLAG_STREAM \ - /*| MA_SOUND_FLAG_NO_PITCH \*/ + // | MA_SOUND_FLAG_DECODE // | MA_SOUND_FLAG_STREAM // | MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT , 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; + if (result != MA_SUCCESS) { + cout << "Error " << result << ": Failed to load file " << file << endl; + return result; } + result = ma_node_attach_output_bus(&m_currentSound[layer], 0, &g_lpfNode[audioDevice], 0); + if (result != MA_SUCCESS) { + cout << "Error " << result << ": Failed to attach output bus " << audioDevice << endl; + //return result; + } + m_mediaLoaded[layer] = true; + this->refreshValues(layer); + m_currentLayerValues[layer].media = file; return result; } @@ -163,7 +299,7 @@ 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); + cout << "Error" << result << ": Can not get cursor " << layer << endl; ret = MA_ERROR; } return ret; @@ -177,11 +313,17 @@ 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; + ma_result result = ma_sound_get_data_format(&m_currentSound[layer], \ + &format, &channels, &sampleRate, NULL, 0); + if (result != MA_SUCCESS) { + cout << "Error" << result << ": Failed to get data format " << layer; + cout << endl; + } else { + cout << "Layer: " << layer; + cout << m_currentLayerValues[layer].media.toLatin1().data(); + cout << " samples/sec:" << sampleRate << " format:" << format; + cout << " channels:" << channels << endl; + } return result; } diff --git a/src/miniaudioengine.h b/src/miniaudioengine.h index b91d718..4f24ce7 100644 --- a/src/miniaudioengine.h +++ b/src/miniaudioengine.h @@ -9,7 +9,32 @@ #define MINIAUDIO_IMPLEMENTATION #include "miniaudio.h" #include "defines.h" // MAX_LAYERS -#include // prints messages +#include +using namespace std; + +/* Data Format */ +#define FORMAT ma_format_f32 /* Must always be f32. */ +#define CHANNELS 2 +#define SAMPLE_RATE 48000 + +typedef struct +{ + ma_node_base node; + ma_loshelf2 loShelfNode; + ma_peak2 midLowNode; + ma_peak2 midHighNode; + ma_hishelf2 hiShelfNode; + ma_splitter_node outputNode; +} filterBank; + +typedef struct +{ + ma_node_base input; + ma_data_source_node node; + ma_decoder decoder; + filterBank filters; +} soundNode; + class MiniAudioEngine { @@ -19,8 +44,13 @@ public: MiniAudioEngine(); void stopEngine(); bool startEngine(); - ma_result startDevice(uint id, uint internalId); - static void audioDataCallback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount); + bool startDevice(uint *id, uint nb); + static void audioDataCallback1(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount); + static void audioDataCallback2(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount); + + ma_device m_device[MAX_AUDIODEVICES]; + ma_sound m_currentSound[MAX_LAYERS]; + ma_bool8 m_mediaLoaded[MAX_LAYERS]; protected: ma_result loadMedia(int layer, char *media, uint audioDevice); @@ -38,22 +68,19 @@ protected: inline bool getAtEnd(int layer) { return m_currentSound[layer].atEnd; } private: - ma_resource_manager_config resourceManagerConfig; - ma_resource_manager resourceManager; - ma_device_info* pPlaybackDeviceInfos; - ma_uint32 playbackDeviceCount; - ma_uint32 iChosenDevice; - ma_engine engine[MAX_AUDIODEVICES]; - ma_device device[MAX_AUDIODEVICES]; - ma_context context; - ma_sound m_currentSound[MAX_LAYERS]; - ma_bool8 m_mediaLoaded[MAX_LAYERS]; + ma_resource_manager_config m_resourceManagerConfig; + ma_resource_manager m_resourceManager; + ma_context m_context; + ma_device_info* m_pPlaybackDeviceInfos; + ma_uint32 m_playbackDeviceCount; + ma_uint32 m_devicesSelected; layerData m_currentLayerValues[MAX_LAYERS]; ma_result getAllAudioDevices(); ma_result startContext(); void refreshValues(int layer); ma_result seekToCursor(int layer, int cursor); + ma_result setNodeGraph(int id); }; #endif // MINIAUDIOENGINE_H From f87c908d300f93ec646a269d712d7fbfb38473f4 Mon Sep 17 00:00:00 2001 From: snt Date: Mon, 13 May 2024 02:22:34 +0200 Subject: [PATCH 17/38] wip filtros --- src/miniaudioengine.cpp | 139 ++++++++++------------------------------ src/miniaudioengine.h | 31 +++------ 2 files changed, 43 insertions(+), 127 deletions(-) diff --git a/src/miniaudioengine.cpp b/src/miniaudioengine.cpp index a0445c9..6e9c1c9 100644 --- a/src/miniaudioengine.cpp +++ b/src/miniaudioengine.cpp @@ -1,15 +1,9 @@ #include "miniaudioengine.h" -#define LPF_BIAS 0.9f /* Higher values means more bias towards the low pass filter (the low pass filter will be more audible). Lower values means more bias towards the echo. Must be between 0 and 1. */ -#define LPF_CUTOFF_FACTOR 80 /* High values = more filter. */ +#define LPF_BIAS 0.9f +#define LPF_CUTOFF_FACTOR 80 +#define HPF_CUTOFF_FACTOR 1 #define LPF_ORDER 8 -//static filterBank m_filterBank[MAX_LAYERS]; -//static ma_node_graph m_nodeGraph[MAX_LAYERS]; -//static soundNode m_soundNode[MAX_LAYERS]; - -static ma_lpf_node g_lpfNode[MAX_AUDIODEVICES]; -static ma_engine m_engine[MAX_AUDIODEVICES]; - MiniAudioEngine::MiniAudioEngine() { for (int i =0; i < MAX_LAYERS; i++) { @@ -22,7 +16,7 @@ MiniAudioEngine::MiniAudioEngine() } } -void MiniAudioEngine::audioDataCallback1(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) +void MiniAudioEngine::audioDataCallback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) { (void)pInput; (void)pDevice; @@ -33,18 +27,6 @@ void MiniAudioEngine::audioDataCallback1(ma_device* pDevice, void* pOutput, cons } } -void MiniAudioEngine::audioDataCallback2(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) -{ - (void)pInput; - (void)pDevice; - ma_result result; - - result = ma_engine_read_pcm_frames((ma_engine*)pDevice->pUserData, pOutput, frameCount, NULL); - if (result != MA_SUCCESS) { - cout << "2"; - } -} - void MiniAudioEngine::stopEngine() { for (uint i = 0; i < m_devicesSelected; i++) { @@ -69,68 +51,38 @@ bool MiniAudioEngine::startEngine() ma_result MiniAudioEngine::setNodeGraph(int id) { ma_result result; - //ma_node_graph_config nodeGraphConfig = ma_node_graph_config_init(CHANNELS); - //result = ma_node_graph_init(&nodeGraphConfig, NULL, &m_nodeGraph[id]); - //if (result != MA_SUCCESS) { - // cout << "ERROR " << result << ": Failed to initialize node graph."; - // return MA_ERROR; - //} -/* - ma_loshelf2_config losConfig = ma_loshelf2_config_init(FORMAT, CHANNELS, SAMPLE_RATE, 1, 0.5, 83.3); // double gainDB, double shelfSlope, double frequency) - result = ma_loshelf2_init(&losConfig, NULL, &m_filterBank[id].loShelfNode); - if (result != MA_SUCCESS) { - cout << "Error " << result << ": Can not init loShelf node." << endl; - return result; - } - ma_engine m; - ma_node_graph *ng = (ma_node_graph *)(&m_engine[id]); - ma_node *node = ma_node_graph_get_endpoint(ng); - node = ma_engine_get_endpoint(&m_engine[id]); - ma_node_base nodeBase = ng->endpoint; - result = ma_node_attach_output_bus(&m_filterBank[id].loShelfNode, 0, &nodeBase, 0); - //result = ma_node_attach_output_bus(&m_filterBank[id].loShelfNode, 0, ma_engine_get_endpoint(engine), 0); - if (result != MA_SUCCESS) { - cout << "Error " << result << ": Can not attach filter to graph endpoint." << endl; - return result; - }*/ -/* - ma_splitter_node_config splitterConfig = ma_splitter_node_config_init(ma_engine_get_channels(&engine[0])); - result = ma_splitter_node_init(ng, &splitterConfig, NULL, &m_filterBank[layer].outputNode); - if (result != MA_SUCCESS) { - cout << "Can not init splitter node." << endl; - return result; - } - result = ma_node_attach_output_bus(&m_filterBank[layer].loShelfNode, 0, &m_filterBank[layer].outputNode, 0); - if (result != MA_SUCCESS) { - cout << "Can not attach loShelf to output." << endl; - return result; - } - result = ma_node_attach_output_bus(&m_filterBank[layer].outputNode, 0, ma_node_graph_get_endpoint(ng), 0); - if (result != MA_SUCCESS) { - cout << "Can not attach splitter to graph endpoint." << endl; - return result; - } - }*/ - /* Low Pass Filter. */ ma_lpf_node_config lpfNodeConfig = ma_lpf_node_config_init(CHANNELS, SAMPLE_RATE, SAMPLE_RATE / LPF_CUTOFF_FACTOR, LPF_ORDER); ma_node_graph *ng = ma_engine_get_node_graph(&m_engine[id]); - result = ma_lpf_node_init(ng, &lpfNodeConfig, NULL, &g_lpfNode[id]); + result = ma_lpf_node_init(ng, &lpfNodeConfig, NULL, &m_filterBank[id].lpfNode); if (result != MA_SUCCESS) { cout << "ERROR " << result << ": Failed to initialize low pass filter node." << endl; return result; } ma_node *endpoint = ma_engine_get_endpoint(&m_engine[id]); - result = ma_node_attach_output_bus(&g_lpfNode[id], 0, endpoint, 0); + // ToDo: ampliar dimensión a m_filterBank con las capas + ma_hpf_node_config hpfNodeConfig = ma_hpf_node_config_init(CHANNELS, SAMPLE_RATE, SAMPLE_RATE / HPF_CUTOFF_FACTOR, LPF_ORDER); + result = ma_hpf_node_init(ng, &hpfNodeConfig, NULL, &m_filterBank[id].hpfNode); + if (result != MA_SUCCESS) { + cout << "ERROR " << result << ": Failed to initialize high pass filter node." << endl; + return result; + } + result = ma_node_attach_output_bus(&m_filterBank[id].lpfNode, 0, &m_filterBank[id].hpfNode, 0); if (result != MA_SUCCESS) { cout << "ERROR " << result << ": Failed to attach low pass filter node." << endl; return result; } - result = ma_node_set_output_bus_volume(&g_lpfNode[id], 0, LPF_BIAS); + // ToDo: add peak filters + result = ma_node_attach_output_bus(&m_filterBank[id].hpfNode, 0, endpoint, 0); if (result != MA_SUCCESS) { - cout << "ERROR " << result << ": Failed to set volume low pass filter node." << endl; + cout << "ERROR " << result << ": Failed to attach low pass filter node." << endl; return result; } - ma_node_set_state(&g_lpfNode[id], ma_node_state::ma_node_state_started); +/* + result = ma_node_set_state(&m_filterBank[id].lpfNode, ma_node_state::ma_node_state_started); + if (result != MA_SUCCESS) { + cout << "ERROR " << result << ": Failed to set state to filter node." << endl; + return result; + }*/ return (result); } @@ -147,10 +99,7 @@ bool MiniAudioEngine::startDevice(uint *systemId, uint nb) deviceConfig.playback.format = m_resourceManager.config.decodedFormat; deviceConfig.playback.channels = 0; deviceConfig.sampleRate = m_resourceManager.config.decodedSampleRate; - if (internalId == 0) - deviceConfig.dataCallback = audioDataCallback1; - else - deviceConfig.dataCallback = audioDataCallback2; + deviceConfig.dataCallback = audioDataCallback; deviceConfig.pUserData = &m_engine[internalId]; result = ma_device_init(&m_context, &deviceConfig, &m_device[internalId]); if (result != MA_SUCCESS) { @@ -209,7 +158,7 @@ ma_result MiniAudioEngine::getAllAudioDevices() result = ma_context_get_devices(&m_context, &m_pPlaybackDeviceInfos, &m_playbackDeviceCount, NULL, NULL); if (result != MA_SUCCESS) { - cout << "Error" << result << ": Failed to enumerate playback devices." << endl; + cout << "Error " << result << ": Failed to enumerate playback devices." << endl; ma_context_uninit(&m_context); return result; } @@ -224,51 +173,29 @@ ma_result MiniAudioEngine::loadMedia(int layer, char *file, uint audioDevice) { ma_result result; + // ToDo: ver si s puede attach dos dispositivos a la vez. si no: + // enchufar a un splitter al sonido y attach cada uno de los lados. + // iniciar un sonido por cada capa if (m_mediaLoaded[layer] == true) { ma_sound_uninit(&m_currentSound[layer]); m_mediaLoaded[layer] = false; } -/* - ma_sound_config soundConfig = ma_sound_config_init(); - soundConfig = ma_sound_config_init(); - soundConfig.pFilePath = file; - soundConfig.pInitialAttachment = &m_filterBank[layer].loShelfNode; - soundConfig.initialAttachmentInputBusIndex = 0; - soundConfig.channelsIn = 0; - soundConfig.channelsOut = CHANNELS; - //soundConfig.monoExpansionMode; - soundConfig.flags = MA_SOUND_FLAG_NO_SPATIALIZATION; - // | MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT; - //| MA_SOUND_FLAG_STREAM | MA_SOUND_FLAG_NO_PITCH - soundConfig.volumeSmoothTimeInPCMFrames = 480; - //soundConfig.initialSeekPointInPCMFrames; - //soundConfig.rangeBegInPCMFrames; - //soundConfig.rangeEndInPCMFrames; - //soundConfig.loopPointBegInPCMFrames; - //soundConfig.loopPointEndInPCMFrames; - //soundConfig.isLooping; - //soundConfig.endCallback; - //soundConfig.pEndCallbackUserData; - - result = ma_sound_init_ex(&m_engine[audioDevice], &soundConfig, &m_currentSound[layer]); - if (result != MA_SUCCESS) { - cout << "Error" << result << ": Failed to load file " << file << endl; - return result; - }*/ result = ma_sound_init_from_file(&m_engine[audioDevice], file, \ MA_SOUND_FLAG_NO_SPATIALIZATION \ - // | MA_SOUND_FLAG_DECODE // | MA_SOUND_FLAG_STREAM // | MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT , NULL, NULL, &m_currentSound[layer]); if (result != MA_SUCCESS) { cout << "Error " << result << ": Failed to load file " << file << endl; return result; } - result = ma_node_attach_output_bus(&m_currentSound[layer], 0, &g_lpfNode[audioDevice], 0); + result = ma_node_attach_output_bus(&m_currentSound[layer], 0, &m_filterBank[audioDevice].lpfNode, 0); if (result != MA_SUCCESS) { cout << "Error " << result << ": Failed to attach output bus " << audioDevice << endl; //return result; } + // ToDo: ampliar dimensión a m_filterBank con las capas + // attach las capas en los dispositivos + // master cada capa? al principio o final del filtro? m_mediaLoaded[layer] = true; this->refreshValues(layer); m_currentLayerValues[layer].media = file; @@ -316,10 +243,10 @@ ma_result MiniAudioEngine::printFormatInfo(int layer) ma_result result = ma_sound_get_data_format(&m_currentSound[layer], \ &format, &channels, &sampleRate, NULL, 0); if (result != MA_SUCCESS) { - cout << "Error" << result << ": Failed to get data format " << layer; + cout << "Error " << result << ": Failed to get data format " << layer; cout << endl; } else { - cout << "Layer: " << layer; + cout << "Layer:" << layer << " "; cout << m_currentLayerValues[layer].media.toLatin1().data(); cout << " samples/sec:" << sampleRate << " format:" << format; cout << " channels:" << channels << endl; diff --git a/src/miniaudioengine.h b/src/miniaudioengine.h index 4f24ce7..fec6c07 100644 --- a/src/miniaudioengine.h +++ b/src/miniaudioengine.h @@ -19,23 +19,12 @@ using namespace std; typedef struct { - ma_node_base node; - ma_loshelf2 loShelfNode; - ma_peak2 midLowNode; - ma_peak2 midHighNode; - ma_hishelf2 hiShelfNode; - ma_splitter_node outputNode; + ma_hpf_node hpfNode; + ma_lpf_node lpfNode; + ma_peak_node midLowNode; + ma_peak_node midHighNode; } filterBank; -typedef struct -{ - ma_node_base input; - ma_data_source_node node; - ma_decoder decoder; - filterBank filters; -} soundNode; - - class MiniAudioEngine { friend class libreMediaServerAudio; @@ -45,12 +34,7 @@ public: void stopEngine(); bool startEngine(); bool startDevice(uint *id, uint nb); - static void audioDataCallback1(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount); - static void audioDataCallback2(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount); - - ma_device m_device[MAX_AUDIODEVICES]; - ma_sound m_currentSound[MAX_LAYERS]; - ma_bool8 m_mediaLoaded[MAX_LAYERS]; + static void audioDataCallback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount); protected: ma_result loadMedia(int layer, char *media, uint audioDevice); @@ -74,7 +58,12 @@ private: ma_device_info* m_pPlaybackDeviceInfos; ma_uint32 m_playbackDeviceCount; ma_uint32 m_devicesSelected; + ma_device m_device[MAX_AUDIODEVICES]; + ma_sound m_currentSound[MAX_LAYERS]; + ma_bool8 m_mediaLoaded[MAX_LAYERS]; layerData m_currentLayerValues[MAX_LAYERS]; + filterBank m_filterBank[MAX_LAYERS]; + ma_engine m_engine[MAX_AUDIODEVICES]; ma_result getAllAudioDevices(); ma_result startContext(); From 352513328704e4b36e4ab060c9740aa7a18a8648 Mon Sep 17 00:00:00 2001 From: snt Date: Tue, 14 May 2024 20:49:28 +0200 Subject: [PATCH 18/38] filtros funcionando --- src/dmxPersonality.h | 15 ++- src/libremediaserver-audio.cpp | 27 +++-- src/libremediaserver-audio.h | 1 + src/miniaudioengine.cpp | 188 ++++++++++++++++++++++++++------- src/miniaudioengine.h | 21 ++-- 5 files changed, 195 insertions(+), 57 deletions(-) diff --git a/src/dmxPersonality.h b/src/dmxPersonality.h index 82f9468..a9977d8 100644 --- a/src/dmxPersonality.h +++ b/src/dmxPersonality.h @@ -10,6 +10,19 @@ #define ENTRY_POINT_COARSE 5 #define ENTRY_POINT_FINE 4 #define PITCH 7 -#define LAYER_CHANNELS 9 +#define VOL1 9 +#define VOL2 10 +#define LOW_FREQ 11 +#define LOW_Q 12 +#define MIDLOW_FREQ 13 +#define MIDLOW_Q 14 +#define MIDLOW_GAIN 15 +#define MIDHIGH_FREQ 16 +#define MIDHIGH_Q 17 +#define MIDHIGH_GAIN 18 +#define HIGH_FREQ 19 +#define HIGH_Q 20 + +#define LAYER_CHANNELS 21 #endif // DMXPERSONALITY_H diff --git a/src/libremediaserver-audio.cpp b/src/libremediaserver-audio.cpp index 39c700d..d516e6c 100644 --- a/src/libremediaserver-audio.cpp +++ b/src/libremediaserver-audio.cpp @@ -26,10 +26,11 @@ libreMediaServerAudio::libreMediaServerAudio() m_settings = Settings::getInstance(); m_settings->readFile(); m_ui = m_settings->getShowUi(); + m_layersQty = m_settings->getLayersNumber(); m_dmxSettings = m_settings->getDmxSettings(); m_mediaLibrary = new MediaLibrary; m_mediaLibrary->initMediaLibrary(); - for (int i = 0; i < MAX_LAYERS; i++) { + for (uint i = 0; i < m_layersQty; i++) { m_currentMedia[i] = ""; m_currentStatus[i] = Status::Iddle; #ifdef NOGUI @@ -39,24 +40,24 @@ libreMediaServerAudio::libreMediaServerAudio() m_updateUi[i][3] = -1; #endif } - m_ola = new olaThread(this, m_settings->getLayersNumber()); - Q_CHECK_PTR(m_ola); - m_ola->blockSignals(true); - m_ola->registerUniverse(); - if (!m_mae.startEngine()) { + if (!m_mae.startEngine(m_layersQty)) { cout << "Can not start Audio Engine!" << endl; - this->~libreMediaServerAudio(); + exit(-1); } uint *audioDevList = m_settings->getAudioDeviceId(); if (!m_mae.startDevice(audioDevList, m_settings->getAudioDeviceQty())) { cout << "Can not start Audio Device!" << audioDevList << endl; - this->~libreMediaServerAudio(); + exit(-1); } - cout << "Core init Complete. Start reading DMX." << endl; - m_ola->blockSignals(false); + m_ola = new olaThread(this, m_layersQty); + Q_CHECK_PTR(m_ola); + m_ola->blockSignals(true); + m_ola->registerUniverse(); #ifdef NOGUI m_ola->start(QThread::TimeCriticalPriority ); #endif + m_ola->blockSignals(false); + cout << "Core init Complete." << endl; } libreMediaServerAudio::~libreMediaServerAudio() @@ -74,7 +75,8 @@ void libreMediaServerAudio::loadMedia(int layer, int folder, int 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_dmxSettings.at(layer).audioDevice); + m_mae.loadMedia(layer, mediaFile.toLatin1().data(),\ + m_dmxSettings.at(layer).audioDevice); m_currentMedia[layer] = mediaFile; #ifndef NOGUI if (m_ui) @@ -138,6 +140,9 @@ void libreMediaServerAudio::dmxInput(int layer, int channel, int value) m_played.append(m_ola->getValue(layer, DMX_FILE)); } #endif + } else if (channel >= LOW_FREQ) { + m_mae.filterParamChanged(layer, m_dmxSettings.at(layer).audioDevice, channel, value); + } } #ifndef NOGUI diff --git a/src/libremediaserver-audio.h b/src/libremediaserver-audio.h index aed7e45..898d408 100644 --- a/src/libremediaserver-audio.h +++ b/src/libremediaserver-audio.h @@ -57,6 +57,7 @@ private: QList m_dmxSettings; bool m_ui; QList m_played; + uint m_layersQty; #ifndef NOGUI QTimer *m_refreshUi; libreMediaServerAudioUi *m_lmsUi; diff --git a/src/miniaudioengine.cpp b/src/miniaudioengine.cpp index 6e9c1c9..01e274a 100644 --- a/src/miniaudioengine.cpp +++ b/src/miniaudioengine.cpp @@ -1,35 +1,35 @@ #include "miniaudioengine.h" -#define LPF_BIAS 0.9f -#define LPF_CUTOFF_FACTOR 80 -#define HPF_CUTOFF_FACTOR 1 -#define LPF_ORDER 8 +#include "dmxPersonality.h" -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; - } -} +#define LPF_BIAS 0.9f +#define FILTER_ORDER 2 +#define LPF_CUTOFF_FACTOR 2 +#define HPF_CUTOFF_FACTOR 20 + +MiniAudioEngine::MiniAudioEngine() {} void MiniAudioEngine::audioDataCallback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) { (void)pInput; - (void)pDevice; ma_result result; result = ma_engine_read_pcm_frames((ma_engine*)pDevice->pUserData, pOutput, frameCount, NULL); if (result != MA_SUCCESS) { - cout << "1"; + cout << "Error " << result << ": error audio callback."; } } void MiniAudioEngine::stopEngine() { + for (uint i = 0; i < m_layersQty; i++) { + ma_sound_uninit(&m_currentSound[i]); + } for (uint i = 0; i < m_devicesSelected; i++) { + for (uint j = 0; j < m_layersQty; j++) { + ma_hpf_node_uninit(&m_filterBank[i][j].hpf, NULL); + ma_lpf_node_uninit(&m_filterBank[i][j].lpf, NULL); + ma_peak_node_uninit(&m_filterBank[i][j].mLow, NULL); + ma_peak_node_uninit(&m_filterBank[i][j].mHigh, NULL); + } ma_engine_uninit(&m_engine[i]); ma_device_uninit(&m_device[i]); } @@ -37,10 +37,19 @@ void MiniAudioEngine::stopEngine() ma_resource_manager_uninit(&m_resourceManager); } -bool MiniAudioEngine::startEngine() +bool MiniAudioEngine::startEngine(uint layers) { ma_result result; + m_layersQty = layers; + for (uint i =0; i < m_layersQty; 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; + } result = this->startContext(); if (result != MA_SUCCESS) return false; result = this->getAllAudioDevices(); @@ -48,41 +57,74 @@ bool MiniAudioEngine::startEngine() return true; } -ma_result MiniAudioEngine::setNodeGraph(int id) { +// lpf -> hpf -> mLow -> mHigh -> engine +ma_result MiniAudioEngine::createFilterBank(int id, uint layer) +{ ma_result result; - ma_lpf_node_config lpfNodeConfig = ma_lpf_node_config_init(CHANNELS, SAMPLE_RATE, SAMPLE_RATE / LPF_CUTOFF_FACTOR, LPF_ORDER); ma_node_graph *ng = ma_engine_get_node_graph(&m_engine[id]); - result = ma_lpf_node_init(ng, &lpfNodeConfig, NULL, &m_filterBank[id].lpfNode); + ma_node *endpoint = ma_engine_get_endpoint(&m_engine[id]); + filterBank *fb = &m_filterBank[id][layer]; + + ma_lpf_node_config lpfConfig = ma_lpf_node_config_init(CHANNELS, SAMPLE_RATE, SAMPLE_RATE / LPF_CUTOFF_FACTOR, FILTER_ORDER); + fb->lpfConfig = ma_lpf_config_init(FORMAT, CHANNELS, SAMPLE_RATE, SAMPLE_RATE / LPF_CUTOFF_FACTOR, FILTER_ORDER); + result = ma_lpf_node_init(ng, &lpfConfig, NULL, &fb->lpf); if (result != MA_SUCCESS) { cout << "ERROR " << result << ": Failed to initialize low pass filter node." << endl; return result; } - ma_node *endpoint = ma_engine_get_endpoint(&m_engine[id]); - // ToDo: ampliar dimensión a m_filterBank con las capas - ma_hpf_node_config hpfNodeConfig = ma_hpf_node_config_init(CHANNELS, SAMPLE_RATE, SAMPLE_RATE / HPF_CUTOFF_FACTOR, LPF_ORDER); - result = ma_hpf_node_init(ng, &hpfNodeConfig, NULL, &m_filterBank[id].hpfNode); + ma_hpf_node_config hpfConfig = ma_hpf_node_config_init(CHANNELS, SAMPLE_RATE, HPF_CUTOFF_FACTOR, FILTER_ORDER); + fb->hpfConfig = ma_hpf_config_init(FORMAT, CHANNELS, SAMPLE_RATE, HPF_CUTOFF_FACTOR, FILTER_ORDER); + result = ma_hpf_node_init(ng, &hpfConfig, NULL, &fb->hpf); if (result != MA_SUCCESS) { cout << "ERROR " << result << ": Failed to initialize high pass filter node." << endl; return result; } - result = ma_node_attach_output_bus(&m_filterBank[id].lpfNode, 0, &m_filterBank[id].hpfNode, 0); + ma_peak_node_config mLowConfig = ma_peak_node_config_init(CHANNELS, SAMPLE_RATE, 1.0, 5.0, 300); // double gainDB, double q, double frequency); + fb->mLowConfig = ma_peak2_config_init(FORMAT, CHANNELS, SAMPLE_RATE, 100.0, 50.0, 1000); + result = ma_peak_node_init(ng, &mLowConfig, NULL, &fb->mLow); + if (result != MA_SUCCESS) { + cout << "ERROR " << result << ": Failed to initialize peak low filter node." << endl; + return result; + } + ma_peak_node_config mHighConfig = ma_peak_node_config_init(CHANNELS, SAMPLE_RATE, 0.0, 0.0, 1000); // double gainDB, double q, double frequency); + fb->mHighConfig = ma_peak2_config_init(FORMAT, CHANNELS, SAMPLE_RATE, 0.0, 0.0, 220); + result = ma_peak_node_init(ng, &mHighConfig, NULL, &fb->mHigh); + if (result != MA_SUCCESS) { + cout << "ERROR " << result << ": Failed to initialize peak high filter node." << endl; + return result; + } + result = ma_node_attach_output_bus(&fb->lpf, 0, &fb->hpf, 0); if (result != MA_SUCCESS) { cout << "ERROR " << result << ": Failed to attach low pass filter node." << endl; return result; } - // ToDo: add peak filters - result = ma_node_attach_output_bus(&m_filterBank[id].hpfNode, 0, endpoint, 0); + result = ma_node_attach_output_bus(&fb->hpf, 0, &fb->mLow, 0); if (result != MA_SUCCESS) { - cout << "ERROR " << result << ": Failed to attach low pass filter node." << endl; + cout << "ERROR " << result << ": Failed to attach Mid Low pass filter node." << endl; return result; } -/* - result = ma_node_set_state(&m_filterBank[id].lpfNode, ma_node_state::ma_node_state_started); + result = ma_node_attach_output_bus(&fb->mLow, 0, &fb->mHigh, 0); if (result != MA_SUCCESS) { - cout << "ERROR " << result << ": Failed to set state to filter node." << endl; + cout << "ERROR " << result << ": Failed to attach high peaks filter node." << endl; return result; - }*/ + } + result = ma_node_attach_output_bus(&fb->mHigh, 0, endpoint, 0); + if (result != MA_SUCCESS) { + cout << "ERROR " << result << ": Failed to attach high peaks filter node." << endl; + return result; + } + return result; +} + +ma_result MiniAudioEngine::setNodeGraph(int id) { + ma_result result = MA_SUCCESS; + uint i = 0; + + while (result == MA_SUCCESS && i < m_layersQty) { + result = this->createFilterBank(id, i); + i++; + } return (result); } @@ -174,8 +216,11 @@ ma_result MiniAudioEngine::loadMedia(int layer, char *file, uint audioDevice) ma_result result; // ToDo: ver si s puede attach dos dispositivos a la vez. si no: - // enchufar a un splitter al sonido y attach cada uno de los lados. - // iniciar un sonido por cada capa + // - enchufar a un splitter al sonido y attach cada uno de los lados. + // - iniciar un sonido por cada capa, copiar la capa en otro dispositivo + // - splitter al final de filterBank, esas señales se mezclan en un nodo mudo + // y se escribe la mezcla en un buffer. Mezclar el buffer en disco con el + // del otro device en el audio callback . if (m_mediaLoaded[layer] == true) { ma_sound_uninit(&m_currentSound[layer]); @@ -188,14 +233,11 @@ ma_result MiniAudioEngine::loadMedia(int layer, char *file, uint audioDevice) cout << "Error " << result << ": Failed to load file " << file << endl; return result; } - result = ma_node_attach_output_bus(&m_currentSound[layer], 0, &m_filterBank[audioDevice].lpfNode, 0); + result = ma_node_attach_output_bus(&m_currentSound[layer], 0, &m_filterBank[audioDevice][layer].lpf, 0); if (result != MA_SUCCESS) { cout << "Error " << result << ": Failed to attach output bus " << audioDevice << endl; //return result; } - // ToDo: ampliar dimensión a m_filterBank con las capas - // attach las capas en los dispositivos - // master cada capa? al principio o final del filtro? m_mediaLoaded[layer] = true; this->refreshValues(layer); m_currentLayerValues[layer].media = file; @@ -357,3 +399,71 @@ void MiniAudioEngine::refreshValues(int layer) this->pitchChanged(layer, m_currentLayerValues[layer].pitch); this->playbackChanged(layer, m_currentLayerValues[layer].status); } + +ma_result MiniAudioEngine::filterParamChanged(int layer, int audioDevice, int channel, int value) +{ + ma_result result = MA_SUCCESS; + + filterBank *fb = &m_filterBank[audioDevice][layer]; + + if (channel == LOW_FREQ) { + fb->hpfConfig.cutoffFrequency = (value * 2) + 20; + result = ma_hpf_node_reinit(&fb->hpfConfig, &fb->hpf); + if (result != MA_SUCCESS) { + cout << "ERROR " << result << ": Failed to set frecuency Low filter node." << endl; + return result; + } + } else if (channel == HIGH_FREQ) { + fb->lpfConfig.cutoffFrequency = 22000 - (value * 80); + result = ma_lpf_node_reinit(&fb->lpfConfig, &fb->lpf); + if (result != MA_SUCCESS) { + cout << "ERROR " << result << ": Failed to set frecuency High filter node." << endl; + return result; + } + } else if (channel == MIDLOW_FREQ) { + fb->mLowConfig.frequency = 60 + (value * 4); + result = ma_peak_node_reinit(&fb->mLowConfig, &fb->mLow); + if (result != MA_SUCCESS) { + cout << "ERROR " << result << ": Failed to set frecuency Mid Low pass filter node." << endl; + return result; + } + cout << "frec " << fb->mLowConfig.frequency << endl; + } else if (channel == MIDLOW_Q) { + fb->mLowConfig.q = (double)( value / 32.0f) + 0.50; + result = ma_peak_node_reinit(&fb->mLowConfig, &fb->mLow); + if (result != MA_SUCCESS) { + cout << "ERROR " << result << ": Failed to set Q Mid Low filter node." << endl; + return result; + } + cout << "Q " << fb->mLowConfig.q << endl; + } else if (channel == MIDLOW_GAIN) { + fb->mLowConfig.gainDB = (double)(value / 8.0f) - 16.0f; + result = ma_peak_node_reinit(&fb->mLowConfig, &fb->mLow); + if (result != MA_SUCCESS) { + cout << "ERROR " << result << ": Failed to set gain Mid Low filter node." << endl; + return result; + } + } else if (channel == MIDHIGH_FREQ) { + fb->mHighConfig.frequency = 400 + (value * 32); + result = ma_peak_node_reinit(&fb->mHighConfig, &fb->mHigh); + if (result != MA_SUCCESS) { + cout << "ERROR " << result << ": Failed to set frecuency Mid High filter node." << endl; + return result; + } + } else if (channel == MIDHIGH_Q) { + fb->mHighConfig.q = (double)( value / 32.0f) + 0.50; + result = ma_peak_node_reinit(&fb->mHighConfig, &fb->mHigh); + if (result != MA_SUCCESS) { + cout << "ERROR " << result << ": Failed to set Q Mid High filter node." << endl; + return result; + } + } else if (channel == MIDHIGH_GAIN) { + fb->mHighConfig.gainDB = (double)(value / 8.0f) - 16.0f; + result = ma_peak_node_reinit(&fb->mHighConfig, &fb->mHigh); + if (result != MA_SUCCESS) { + cout << "ERROR " << result << ": Failed to set gain Mid High filter node." << endl; + return result; + } + } + return (result); +} diff --git a/src/miniaudioengine.h b/src/miniaudioengine.h index fec6c07..d5d798b 100644 --- a/src/miniaudioengine.h +++ b/src/miniaudioengine.h @@ -19,12 +19,18 @@ using namespace std; typedef struct { - ma_hpf_node hpfNode; - ma_lpf_node lpfNode; - ma_peak_node midLowNode; - ma_peak_node midHighNode; + ma_hpf_node hpf; + ma_hpf_config hpfConfig; + ma_lpf_node lpf; + ma_lpf_config lpfConfig; + ma_peak_node mLow; + ma_peak_config mLowConfig; + ma_peak_node mHigh; + ma_notch_node notch; + ma_peak_config mHighConfig; } filterBank; + class MiniAudioEngine { friend class libreMediaServerAudio; @@ -32,7 +38,7 @@ class MiniAudioEngine public: MiniAudioEngine(); void stopEngine(); - bool startEngine(); + bool startEngine(uint layersQty); bool startDevice(uint *id, uint nb); static void audioDataCallback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount); @@ -50,6 +56,7 @@ protected: inline float getVol(int layer) { return ma_sound_get_volume(&m_currentSound[layer]); } inline bool getAtEnd(int layer) { return m_currentSound[layer].atEnd; } + ma_result filterParamChanged(int layer, int audioDevice, int channel, int value); private: ma_resource_manager_config m_resourceManagerConfig; @@ -62,14 +69,16 @@ private: ma_sound m_currentSound[MAX_LAYERS]; ma_bool8 m_mediaLoaded[MAX_LAYERS]; layerData m_currentLayerValues[MAX_LAYERS]; - filterBank m_filterBank[MAX_LAYERS]; + filterBank m_filterBank[MAX_AUDIODEVICES][MAX_LAYERS]; ma_engine m_engine[MAX_AUDIODEVICES]; + uint m_layersQty; ma_result getAllAudioDevices(); ma_result startContext(); void refreshValues(int layer); ma_result seekToCursor(int layer, int cursor); ma_result setNodeGraph(int id); + ma_result createFilterBank(int id, uint layer); }; #endif // MINIAUDIOENGINE_H From 3244ea2abcee1cb65af7051e29091dd4b804bc92 Mon Sep 17 00:00:00 2001 From: snt Date: Wed, 15 May 2024 00:16:49 +0200 Subject: [PATCH 19/38] =?UTF-8?q?cambio=20la=20configuraci=C3=B3n=20del=20?= =?UTF-8?q?banco=20de=20filtros=20a=20la=20de=20SSL.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/dmxPersonality.h | 22 ++--- src/libremediaserver-audio.cpp | 3 +- src/miniaudioengine.cpp | 158 ++++++++++++++++++++++----------- src/miniaudioengine.h | 14 +-- 4 files changed, 128 insertions(+), 69 deletions(-) diff --git a/src/dmxPersonality.h b/src/dmxPersonality.h index a9977d8..b083ae6 100644 --- a/src/dmxPersonality.h +++ b/src/dmxPersonality.h @@ -1,19 +1,19 @@ #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 VOLUME_COARSE 3 #define ENTRY_POINT_FINE 4 +#define ENTRY_POINT_COARSE 5 +#define PAN 6 #define PITCH 7 -#define VOL1 9 -#define VOL2 10 -#define LOW_FREQ 11 -#define LOW_Q 12 +#define PLAYBACK 8 +#define HP_FREQ 9 +#define LOW_FREQ 10 +#define LOW_Q 11 +#define LOW_GAIN 12 #define MIDLOW_FREQ 13 #define MIDLOW_Q 14 #define MIDLOW_GAIN 15 @@ -22,7 +22,9 @@ #define MIDHIGH_GAIN 18 #define HIGH_FREQ 19 #define HIGH_Q 20 - -#define LAYER_CHANNELS 21 +#define HIGH_GAIN 21 +#define SEND1 22 +#define SEND2 23 +#define LAYER_CHANNELS 24 #endif // DMXPERSONALITY_H diff --git a/src/libremediaserver-audio.cpp b/src/libremediaserver-audio.cpp index d516e6c..07ab9a8 100644 --- a/src/libremediaserver-audio.cpp +++ b/src/libremediaserver-audio.cpp @@ -140,9 +140,8 @@ void libreMediaServerAudio::dmxInput(int layer, int channel, int value) m_played.append(m_ola->getValue(layer, DMX_FILE)); } #endif - } else if (channel >= LOW_FREQ) { + } else if (channel >= HP_FREQ && channel <= HIGH_GAIN) { m_mae.filterParamChanged(layer, m_dmxSettings.at(layer).audioDevice, channel, value); - } } #ifndef NOGUI diff --git a/src/miniaudioengine.cpp b/src/miniaudioengine.cpp index 01e274a..9dbd3c8 100644 --- a/src/miniaudioengine.cpp +++ b/src/miniaudioengine.cpp @@ -1,10 +1,8 @@ #include "miniaudioengine.h" #include "dmxPersonality.h" -#define LPF_BIAS 0.9f -#define FILTER_ORDER 2 -#define LPF_CUTOFF_FACTOR 2 -#define HPF_CUTOFF_FACTOR 20 +#define BIAS 0.99f +#define FILTER_ORDER 3 MiniAudioEngine::MiniAudioEngine() {} @@ -26,9 +24,11 @@ void MiniAudioEngine::stopEngine() for (uint i = 0; i < m_devicesSelected; i++) { for (uint j = 0; j < m_layersQty; j++) { ma_hpf_node_uninit(&m_filterBank[i][j].hpf, NULL); - ma_lpf_node_uninit(&m_filterBank[i][j].lpf, NULL); + ma_loshelf_node_uninit(&m_filterBank[i][j].loshelf, NULL); ma_peak_node_uninit(&m_filterBank[i][j].mLow, NULL); ma_peak_node_uninit(&m_filterBank[i][j].mHigh, NULL); + ma_hishelf_node_uninit(&m_filterBank[i][j].hishelf, NULL); + ma_splitter_node_uninit(&m_filterBank[i][j].output, NULL); } ma_engine_uninit(&m_engine[i]); ma_device_uninit(&m_device[i]); @@ -57,7 +57,6 @@ bool MiniAudioEngine::startEngine(uint layers) return true; } -// lpf -> hpf -> mLow -> mHigh -> engine ma_result MiniAudioEngine::createFilterBank(int id, uint layer) { ma_result result; @@ -66,52 +65,76 @@ ma_result MiniAudioEngine::createFilterBank(int id, uint layer) ma_node *endpoint = ma_engine_get_endpoint(&m_engine[id]); filterBank *fb = &m_filterBank[id][layer]; - ma_lpf_node_config lpfConfig = ma_lpf_node_config_init(CHANNELS, SAMPLE_RATE, SAMPLE_RATE / LPF_CUTOFF_FACTOR, FILTER_ORDER); - fb->lpfConfig = ma_lpf_config_init(FORMAT, CHANNELS, SAMPLE_RATE, SAMPLE_RATE / LPF_CUTOFF_FACTOR, FILTER_ORDER); - result = ma_lpf_node_init(ng, &lpfConfig, NULL, &fb->lpf); - if (result != MA_SUCCESS) { - cout << "ERROR " << result << ": Failed to initialize low pass filter node." << endl; - return result; - } - ma_hpf_node_config hpfConfig = ma_hpf_node_config_init(CHANNELS, SAMPLE_RATE, HPF_CUTOFF_FACTOR, FILTER_ORDER); - fb->hpfConfig = ma_hpf_config_init(FORMAT, CHANNELS, SAMPLE_RATE, HPF_CUTOFF_FACTOR, FILTER_ORDER); - result = ma_hpf_node_init(ng, &hpfConfig, NULL, &fb->hpf); + fb->hpfConfig = ma_hpf_node_config_init(CHANNELS, SAMPLE_RATE, 16, FILTER_ORDER); + result = ma_hpf_node_init(ng, &fb->hpfConfig, NULL, &fb->hpf); if (result != MA_SUCCESS) { cout << "ERROR " << result << ": Failed to initialize high pass filter node." << endl; return result; } - ma_peak_node_config mLowConfig = ma_peak_node_config_init(CHANNELS, SAMPLE_RATE, 1.0, 5.0, 300); // double gainDB, double q, double frequency); - fb->mLowConfig = ma_peak2_config_init(FORMAT, CHANNELS, SAMPLE_RATE, 100.0, 50.0, 1000); - result = ma_peak_node_init(ng, &mLowConfig, NULL, &fb->mLow); + + fb->loshelfConfig = ma_loshelf_node_config_init(CHANNELS, SAMPLE_RATE, 0.0f, 1.0f, 30); + result = ma_loshelf_node_init(ng, &fb->loshelfConfig, NULL, &fb->loshelf); + if (result != MA_SUCCESS) { + cout << "ERROR " << result << ": Failed to initialize low pass filter node." << endl; + return result; + } + + fb->mLowConfig = ma_peak_node_config_init(CHANNELS, SAMPLE_RATE, 0.0, 4.0, 200); // double gainDB, double q, double frequency); + result = ma_peak_node_init(ng, &fb->mLowConfig, NULL, &fb->mLow); if (result != MA_SUCCESS) { cout << "ERROR " << result << ": Failed to initialize peak low filter node." << endl; return result; } - ma_peak_node_config mHighConfig = ma_peak_node_config_init(CHANNELS, SAMPLE_RATE, 0.0, 0.0, 1000); // double gainDB, double q, double frequency); - fb->mHighConfig = ma_peak2_config_init(FORMAT, CHANNELS, SAMPLE_RATE, 0.0, 0.0, 220); - result = ma_peak_node_init(ng, &mHighConfig, NULL, &fb->mHigh); + + fb->mHighConfig = ma_peak_node_config_init(CHANNELS, SAMPLE_RATE, 0.0, 0.0, 600); // double gainDB, double q, double frequency); + result = ma_peak_node_init(ng, &fb->mHighConfig, NULL, &fb->mHigh); if (result != MA_SUCCESS) { cout << "ERROR " << result << ": Failed to initialize peak high filter node." << endl; return result; } - result = ma_node_attach_output_bus(&fb->lpf, 0, &fb->hpf, 0); + + fb->hishelfConfig = ma_hishelf_node_config_init(CHANNELS, SAMPLE_RATE, 0.0f, 1.0f, 20000); + result = ma_hishelf_node_init(ng, &fb->hishelfConfig, NULL, &fb->hishelf); if (result != MA_SUCCESS) { - cout << "ERROR " << result << ": Failed to attach low pass filter node." << endl; + cout << "ERROR " << result << ": Failed to initialize hi shelf filter node." << endl; return result; } - result = ma_node_attach_output_bus(&fb->hpf, 0, &fb->mLow, 0); + + ma_splitter_node_config splitterConfig = ma_splitter_node_config_init(CHANNELS); + result = ma_splitter_node_init(ng, &splitterConfig, NULL, &fb->output); if (result != MA_SUCCESS) { - cout << "ERROR " << result << ": Failed to attach Mid Low pass filter node." << endl; + cout << "ERROR " << result << ": Failed to initialize output node." << endl; + return result; + } + + result = ma_node_attach_output_bus(&fb->hpf, 0, &fb->loshelf, 0); + if (result != MA_SUCCESS) { + cout << "ERROR " << result << ": Failed to attach high pass pass filter node." << endl; + return result; + } + result = ma_node_attach_output_bus(&fb->loshelf, 0, &fb->mLow, 0); + if (result != MA_SUCCESS) { + cout << "ERROR " << result << ": Failed to attach low shelf filter node." << endl; return result; } result = ma_node_attach_output_bus(&fb->mLow, 0, &fb->mHigh, 0); + if (result != MA_SUCCESS) { + cout << "ERROR " << result << ": Failed to attach low peaks filter node." << endl; + return result; + } + result = ma_node_attach_output_bus(&fb->mHigh, 0, &fb->hishelf, 0); if (result != MA_SUCCESS) { cout << "ERROR " << result << ": Failed to attach high peaks filter node." << endl; return result; } - result = ma_node_attach_output_bus(&fb->mHigh, 0, endpoint, 0); + result = ma_node_attach_output_bus(&fb->hishelf, 0, &fb->output, 0); if (result != MA_SUCCESS) { - cout << "ERROR " << result << ": Failed to attach high peaks filter node." << endl; + cout << "ERROR " << result << ": Failed to attach high shelf filter node." << endl; + return result; + } + result = ma_node_attach_output_bus(&fb->output, 0, endpoint, 0); + if (result != MA_SUCCESS) { + cout << "ERROR " << result << ": Failed to attach output node to engine." << endl; return result; } return result; @@ -233,7 +256,7 @@ ma_result MiniAudioEngine::loadMedia(int layer, char *file, uint audioDevice) cout << "Error " << result << ": Failed to load file " << file << endl; return result; } - result = ma_node_attach_output_bus(&m_currentSound[layer], 0, &m_filterBank[audioDevice][layer].lpf, 0); + result = ma_node_attach_output_bus(&m_currentSound[layer], 0, &m_filterBank[audioDevice][layer].hpf, 0); if (result != MA_SUCCESS) { cout << "Error " << result << ": Failed to attach output bus " << audioDevice << endl; //return result; @@ -406,64 +429,97 @@ ma_result MiniAudioEngine::filterParamChanged(int layer, int audioDevice, int ch filterBank *fb = &m_filterBank[audioDevice][layer]; - if (channel == LOW_FREQ) { - fb->hpfConfig.cutoffFrequency = (value * 2) + 20; - result = ma_hpf_node_reinit(&fb->hpfConfig, &fb->hpf); + if (channel == HP_FREQ) { + fb->hpfConfig.hpf.cutoffFrequency = double((value * 1.31) + 16.0f); // 16 - 350 + result = ma_hpf_node_reinit(&fb->hpfConfig.hpf, &fb->hpf); if (result != MA_SUCCESS) { - cout << "ERROR " << result << ": Failed to set frecuency Low filter node." << endl; + cout << "ERROR " << result << ": Failed to set frecuency high pass filter node." << endl; return result; } - } else if (channel == HIGH_FREQ) { - fb->lpfConfig.cutoffFrequency = 22000 - (value * 80); - result = ma_lpf_node_reinit(&fb->lpfConfig, &fb->lpf); + } else if (channel == LOW_FREQ) { + fb->loshelfConfig.loshelf.frequency = 30 + (value * 1.647); // 30 - 450 + result = ma_loshelf_node_reinit(&fb->loshelfConfig.loshelf, &fb->loshelf); if (result != MA_SUCCESS) { - cout << "ERROR " << result << ": Failed to set frecuency High filter node." << endl; + cout << "ERROR " << result << ": Failed to set frecuency low shelf filter node." << endl; + return result; + } + } else if (channel == LOW_Q) { + fb->loshelfConfig.loshelf.shelfSlope = (double)(value / 32.0f) + 0.1f; // 0.1 - 8 + result = ma_loshelf_node_reinit(&fb->loshelfConfig.loshelf, &fb->loshelf); + if (result != MA_SUCCESS) { + cout << "ERROR " << result << ": Failed set Q low shelf filter node." << endl; + return result; + } + } else if (channel == LOW_GAIN) { + fb->loshelfConfig.loshelf.gainDB = (double)(value / 21.25f) - 6.023528412f; + result = ma_loshelf_node_reinit(&fb->loshelfConfig.loshelf, &fb->loshelf); + if (result != MA_SUCCESS) { + cout << "ERROR " << result << ": Failed set gain low shelf filter node." << endl; return result; } } else if (channel == MIDLOW_FREQ) { - fb->mLowConfig.frequency = 60 + (value * 4); - result = ma_peak_node_reinit(&fb->mLowConfig, &fb->mLow); + fb->mLowConfig.peak.frequency = 200 + (value * 9.019607843); // 200 - 450 + result = ma_peak_node_reinit(&fb->mLowConfig.peak, &fb->mLow); if (result != MA_SUCCESS) { cout << "ERROR " << result << ": Failed to set frecuency Mid Low pass filter node." << endl; return result; } - cout << "frec " << fb->mLowConfig.frequency << endl; } else if (channel == MIDLOW_Q) { - fb->mLowConfig.q = (double)( value / 32.0f) + 0.50; - result = ma_peak_node_reinit(&fb->mLowConfig, &fb->mLow); + fb->mLowConfig.peak.q = (double)( value / 64.0f) + 0.10; // 0.1 - 4 + result = ma_peak_node_reinit(&fb->mLowConfig.peak, &fb->mLow); if (result != MA_SUCCESS) { cout << "ERROR " << result << ": Failed to set Q Mid Low filter node." << endl; return result; } - cout << "Q " << fb->mLowConfig.q << endl; } else if (channel == MIDLOW_GAIN) { - fb->mLowConfig.gainDB = (double)(value / 8.0f) - 16.0f; - result = ma_peak_node_reinit(&fb->mLowConfig, &fb->mLow); + fb->mLowConfig.peak.gainDB = (double)(value / 7.0833333333333f) - 18.0f; + result = ma_peak_node_reinit(&fb->mLowConfig.peak, &fb->mLow); if (result != MA_SUCCESS) { cout << "ERROR " << result << ": Failed to set gain Mid Low filter node." << endl; return result; } } else if (channel == MIDHIGH_FREQ) { - fb->mHighConfig.frequency = 400 + (value * 32); - result = ma_peak_node_reinit(&fb->mHighConfig, &fb->mHigh); + fb->mHighConfig.peak.frequency = 600 + (value * 25.09803922); // 600 - 7000 + result = ma_peak_node_reinit(&fb->mHighConfig.peak, &fb->mHigh); if (result != MA_SUCCESS) { cout << "ERROR " << result << ": Failed to set frecuency Mid High filter node." << endl; return result; } } else if (channel == MIDHIGH_Q) { - fb->mHighConfig.q = (double)( value / 32.0f) + 0.50; - result = ma_peak_node_reinit(&fb->mHighConfig, &fb->mHigh); + fb->mHighConfig.peak.q = (double)( value / 64.0f) + 0.10; // 0.1 - 4 + result = ma_peak_node_reinit(&fb->mHighConfig.peak, &fb->mHigh); if (result != MA_SUCCESS) { cout << "ERROR " << result << ": Failed to set Q Mid High filter node." << endl; return result; } } else if (channel == MIDHIGH_GAIN) { - fb->mHighConfig.gainDB = (double)(value / 8.0f) - 16.0f; - result = ma_peak_node_reinit(&fb->mHighConfig, &fb->mHigh); + fb->mHighConfig.peak.gainDB = (double)(value / 7.0833333333333f) - 18.0f; + result = ma_peak_node_reinit(&fb->mHighConfig.peak, &fb->mHigh); if (result != MA_SUCCESS) { cout << "ERROR " << result << ": Failed to set gain Mid High filter node." << endl; return result; } + } else if (channel == HIGH_FREQ) { + fb->hishelfConfig.hishelf.frequency = 1500 + (value * 56.8627451); // 1500 - 16000 + result = ma_hishelf_node_reinit(&fb->hishelfConfig.hishelf, &fb->hishelf); + if (result != MA_SUCCESS) { + cout << "ERROR " << result << ": Failed to frecuency high shelf filter node." << endl; + return result; + } + } else if (channel == HIGH_Q) { + fb->hishelfConfig.hishelf.shelfSlope = (double)( value / 32.0f) + 0.1f; + result = ma_hishelf_node_reinit(&fb->hishelfConfig.hishelf, &fb->hishelf); + if (result != MA_SUCCESS) { + cout << "ERROR " << result << ": Failed set Q high shelf filter node." << endl; + return result; + } + } else if (channel == HIGH_GAIN) { + fb->hishelfConfig.hishelf.gainDB = (double)(value / 21.25) - 6.023528412f; + result = ma_hishelf_node_reinit(&fb->hishelfConfig.hishelf, &fb->hishelf); + if (result != MA_SUCCESS) { + cout << "ERROR " << result << ": Failed set gain high shelf filter node." << endl; + return result; + } } return (result); } diff --git a/src/miniaudioengine.h b/src/miniaudioengine.h index d5d798b..332c1bf 100644 --- a/src/miniaudioengine.h +++ b/src/miniaudioengine.h @@ -20,14 +20,16 @@ using namespace std; typedef struct { ma_hpf_node hpf; - ma_hpf_config hpfConfig; - ma_lpf_node lpf; - ma_lpf_config lpfConfig; + ma_hpf_node_config hpfConfig; + ma_loshelf_node loshelf; + ma_loshelf_node_config loshelfConfig; ma_peak_node mLow; - ma_peak_config mLowConfig; + ma_peak_node_config mLowConfig; ma_peak_node mHigh; - ma_notch_node notch; - ma_peak_config mHighConfig; + ma_peak_node_config mHighConfig; + ma_hishelf_node hishelf; + ma_hishelf_node_config hishelfConfig; + ma_splitter_node output; } filterBank; From 7bc339dfe334265b87060de00aa296eca249ef97 Mon Sep 17 00:00:00 2001 From: snt Date: Wed, 15 May 2024 01:25:44 +0200 Subject: [PATCH 20/38] =?UTF-8?q?a=C3=B1adidos=20filtros?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/LibreMediaServer_Audio.hed | 168 +++++++++++++++++--------------- 1 file changed, 91 insertions(+), 77 deletions(-) diff --git a/docs/LibreMediaServer_Audio.hed b/docs/LibreMediaServer_Audio.hed index fda3e2d..1598cab 100644 --- a/docs/LibreMediaServer_Audio.hed +++ b/docs/LibreMediaServer_Audio.hed @@ -1,79 +1,93 @@ ް׆ˌĠ򝤫鿰맫͓Ԕ -ҊÅЁ䍡 +ҊÅЁ䍡 ӽ -Ꭸʆ -ɑ -ɬ -򉇁 -Ԑɉ - -ٿդϨ҃ -񀠧ڔ -ڑŌ -򻴧 - - - - - - - - -쏎ބ -힇ݳ - -Ԓlj - -Ŷ§ -ÐŽÊ -𡭟Ē - -܊ -񢰽 -зм -񓎍 -ҭ -ܻۏ -ڑן -ޏ - -Ƴ - - - -Ǖ - -𓎍Ԏ - - -Ξ -ֆ - -ց - -ᱯ -݈ - -؈ - -馹 -۴ -𓎍 - - - - - -𓎑 - - - -Ɨ - - - -Ӌ -쏎 - - - +͌وГ +䩴؍ + + +ǁ + +ήƵ +ꂨ⑯ɞɅ +ˆ +㨥 +פ + +Θב +˻ + +ūî +䈍 +п +¥߸ڋ +Ĩ© +勌㊈ +͹ +ˢ݈ +ɢƢ +񍒍Й +˟ + +ǔ +檵 +Ӡ̧׀ +ꗪㄮϞѝ +ᯠ쫶 +ϼȡ嵶 +ᄨڍ֟ + +ڱ +񔸼񣏎 +ڔ + + +֐ + +̽ɤ +甯؞τ + + + + + + +𣇒 +Ԇ + +䷩ +ߍ + + + + + + + + + + +܂ +쏎 +מ + + +Ӽ + + + + + + + + + + +͚ʝǓ + + + +ݽ + + + + From 8f321b9d6949cf111792c95242bbf0aca7fd0c47 Mon Sep 17 00:00:00 2001 From: snt Date: Wed, 15 May 2024 02:21:30 +0200 Subject: [PATCH 21/38] =?UTF-8?q?fix=20sigsev=20cuando=20se=20pincha=20en?= =?UTF-8?q?=20el=20bot=C3=B3n=20de=20play/pause?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/audiolayerwidget.cpp | 5 +++-- src/audiolayerwidget.h | 1 + src/defines.h | 20 ++++++++++++++++++-- src/libremediaserver-audio.cpp | 4 ++-- 4 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/audiolayerwidget.cpp b/src/audiolayerwidget.cpp index 2655938..bd9d371 100644 --- a/src/audiolayerwidget.cpp +++ b/src/audiolayerwidget.cpp @@ -3,6 +3,7 @@ AudioLayerWidget::AudioLayerWidget(QWidget *parent, int layer): QWidget(parent) + , m_oldStatus(Status::PlayingLoop) , m_layer(layer) , m_suspendResumeButton(0) { @@ -32,7 +33,7 @@ AudioLayerWidget::AudioLayerWidget(QWidget *parent, int layer): layout->addLayout(playback); m_suspendResumeButton = new QPushButton(this); - m_suspendResumeButton->setText(StatusStr[Status::Iddle]); + m_suspendResumeButton->setText(statusToString(Status::Iddle)); connect(m_suspendResumeButton, SIGNAL(clicked()), SLOT(toggleSuspendResume())); layout->addWidget(m_suspendResumeButton); @@ -180,7 +181,7 @@ void AudioLayerWidget::setPlaybackStatus(Status s) { m_suspendResumeButton->blockSignals(true); m_status = s; - m_suspendResumeButton->setText(StatusStr[s]); + m_suspendResumeButton->setText(statusToString(s)); m_suspendResumeButton->blockSignals(false); } diff --git a/src/audiolayerwidget.h b/src/audiolayerwidget.h index cae18d4..51c1203 100644 --- a/src/audiolayerwidget.h +++ b/src/audiolayerwidget.h @@ -11,6 +11,7 @@ #include "clickablelabel.h" #include "settings.h" + class AudioLayerWidget : public QWidget { Q_OBJECT diff --git a/src/defines.h b/src/defines.h index a885485..b4560a8 100644 --- a/src/defines.h +++ b/src/defines.h @@ -17,7 +17,7 @@ struct dmxSetting { int audioDevice; }; -enum Status +enum class Status { Stopped, Paused, @@ -29,6 +29,22 @@ enum Status PlayingFolderRandom }; +constexpr const char* statusToString(Status e) noexcept +{ + switch (e) + { + case Status::Stopped: return "Stop"; + case Status::Paused: return "Paused"; + case Status::PlayingOnce: return "Play 1"; + case Status::PlayingLoop: return "Play Loop"; + case Status::Iddle: return "Iddle"; + case Status::PlayingFolder: return "Play Folder"; + case Status::PlayingFolderLoop: return "Play Folder Loop"; + case Status::PlayingFolderRandom: return "Playing Folder Random"; + default: return "--++--"; + } +} +/* static const char* StatusStr[] = { "Stop", @@ -40,7 +56,7 @@ static const char* StatusStr[] = "Play Folder Loop", "Play Folder Rand", 0x0 -}; +};*/ enum Slider { diff --git a/src/libremediaserver-audio.cpp b/src/libremediaserver-audio.cpp index 07ab9a8..9366935 100644 --- a/src/libremediaserver-audio.cpp +++ b/src/libremediaserver-audio.cpp @@ -132,7 +132,7 @@ void libreMediaServerAudio::dmxInput(int layer, int channel, int value) s = Status::PlayingFolderRandom; m_mae.playbackChanged(layer, s); m_currentStatus[layer] = s; - qInfo() << "Layer" << layer << StatusStr[s]; + qInfo() << "Layer" << layer << statusToString(s); #ifndef NOGUI if (m_ui) { m_lmsUi->m_aw->playbackChanged(layer, s); @@ -253,7 +253,7 @@ void libreMediaServerAudio::uiPlaybackChanged(int layer, Status s) if (result == MA_SUCCESS) { m_currentStatus[layer] = s; } else { - qWarning() << "ui playback change error" << result << "status" << s << "layer" << layer; + qWarning() << "ui playback change error " << result << " status " << statusToString(s) << "layer" << layer; } } From 86567b8beff47daec58372570773f107b6a4c7ea Mon Sep 17 00:00:00 2001 From: snt Date: Wed, 15 May 2024 17:50:21 +0200 Subject: [PATCH 22/38] =?UTF-8?q?indicadores=20filtros=20Ui=20funcionando,?= =?UTF-8?q?=20sin=20interacci=C3=B3n=20de=20usuario.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- libremediaserver-audio.pro | 6 +++ src/audiolayerwidget.cpp | 23 +++++++--- src/audiolayerwidget.h | 24 +++++----- src/audiowidget.cpp | 18 +++++++- src/audiowidget.h | 3 ++ src/clickabledoublespinbox.cpp | 21 +++++++++ src/clickabledoublespinbox.h | 25 ++++++++++ src/clickableslider.cpp | 3 ++ src/clickableslider.h | 30 ++++++++++++ src/defines.h | 1 + src/dmxPersonality.h | 20 ++++++++ src/filterbankwidget.cpp | 82 +++++++++++++++++++++++++++++++++ src/filterbankwidget.h | 22 +++++++++ src/libremediaserver-audio.cpp | 7 +++ src/slidergroup.cpp | 15 ++---- src/slidergroup.h | 83 +++------------------------------- 16 files changed, 275 insertions(+), 108 deletions(-) create mode 100644 src/clickabledoublespinbox.cpp create mode 100644 src/clickabledoublespinbox.h create mode 100644 src/clickableslider.cpp create mode 100644 src/clickableslider.h create mode 100644 src/filterbankwidget.cpp create mode 100644 src/filterbankwidget.h diff --git a/libremediaserver-audio.pro b/libremediaserver-audio.pro index 41d6ecd..0371808 100644 --- a/libremediaserver-audio.pro +++ b/libremediaserver-audio.pro @@ -2,8 +2,11 @@ TEMPLATE = app TARGET = libremediaserver-audio QT += webkitwidgets widgets HEADERS += src/libremediaserver-audio.h \ + src/clickabledoublespinbox.h \ src/clickablelabel.h \ + src/clickableslider.h \ src/dmxwidget.h \ + src/filterbankwidget.h \ src/libremediaserver-audio-gui.h \ src/main.h \ src/miniaudio.h \ @@ -17,8 +20,11 @@ HEADERS += src/libremediaserver-audio.h \ src/settings.h \ src/slidergroup.h SOURCES += src/main.cpp \ + src/clickabledoublespinbox.cpp \ src/clickablelabel.cpp \ + src/clickableslider.cpp \ src/dmxwidget.cpp \ + src/filterbankwidget.cpp \ src/libremediaserver-audio-gui.cpp \ src/miniaudio.c \ src/libremediaserver-audio.cpp \ diff --git a/src/audiolayerwidget.cpp b/src/audiolayerwidget.cpp index bd9d371..b2f2782 100644 --- a/src/audiolayerwidget.cpp +++ b/src/audiolayerwidget.cpp @@ -1,5 +1,7 @@ #include "audiolayerwidget.h" -#include + +#include +#include AudioLayerWidget::AudioLayerWidget(QWidget *parent, int layer): QWidget(parent) @@ -21,7 +23,7 @@ AudioLayerWidget::AudioLayerWidget(QWidget *parent, int layer): 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->setMaximumWidth(300); m_fileValue->setAlignment(Qt::AlignLeft); m_fileValue->setStyleSheet( "color: white;" @@ -69,6 +71,10 @@ AudioLayerWidget::AudioLayerWidget(QWidget *parent, int layer): status->addWidget(m_progressTime); status->addWidget(m_totalTimeValue); layout->addLayout(status); + + m_filterBank = new FilterBankWidget(this); + layout->addWidget(m_filterBank); + QVBoxLayout *volumeBox = new QVBoxLayout; m_pitch = new SliderGroup("Pitch", 0 , 255, 0, NULL); volumeBox->addWidget(m_pitch); @@ -88,10 +94,7 @@ AudioLayerWidget::AudioLayerWidget(QWidget *parent, int layer): this->setLayout(layout); } -AudioLayerWidget::~AudioLayerWidget() -{ - -} +AudioLayerWidget::~AudioLayerWidget() {} // From UI. void AudioLayerWidget::volumeChanged(int value) @@ -209,3 +212,11 @@ void AudioLayerWidget::setCurrentTime(float progress) m_progress->blockSignals(false); m_progressTime->blockSignals(false); } + +void AudioLayerWidget::setFilterParam(int channel, int value) +{ + m_filterBank->blockSignals(true); + m_filterBank->setValue(channel, value); + m_filterBank->blockSignals(false); + +} diff --git a/src/audiolayerwidget.h b/src/audiolayerwidget.h index 51c1203..32bd7c7 100644 --- a/src/audiolayerwidget.h +++ b/src/audiolayerwidget.h @@ -3,14 +3,13 @@ #include #include -#include #include #include "defines.h" #include "slidergroup.h" #include "clickablelabel.h" #include "settings.h" - +#include "filterbankwidget.h" class AudioLayerWidget : public QWidget { @@ -20,6 +19,16 @@ public: explicit AudioLayerWidget(QWidget *parent = 0, int layer = 0); ~AudioLayerWidget(); + // From OLA -> LibreMediaServer -> AudioWidget + 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); + void setFilterParam(int channel, int value); + private: Status m_status; Status m_oldStatus; @@ -33,16 +42,9 @@ private: QTimeEdit *m_progressTime; QTimeEdit *m_totalTimeValue; QProgressBar *m_progress; + FilterBankWidget *m_filterBank; -// 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); +//public slots: // From Ui private slots: diff --git a/src/audiowidget.cpp b/src/audiowidget.cpp index e163da3..2f3b634 100644 --- a/src/audiowidget.cpp +++ b/src/audiowidget.cpp @@ -1,5 +1,6 @@ #include "audiowidget.h" -#include + +//#include AudioWidget::AudioWidget(QWidget *parent) : QWidget(parent) @@ -19,6 +20,8 @@ AudioWidget::AudioWidget(QWidget *parent) : m_layerUpdate[i].pan = 128; m_layerUpdate[i].pitch = 128; m_layerUpdate[i].cursor = 0; + for (int j = 0; j < FILTER_CHANNELS; j++) + m_filtersUpdate[i][j] = -1; } m_layout->setSpacing(0); m_layout->setContentsMargins(1, 1, 1, 1); @@ -68,7 +71,7 @@ void AudioWidget::refreshUi() for (uint i = 0; i < m_layers; i++) { if (m_layerUpdate[i].updated) { - QLayoutItem * const item = m_layout->itemAt(i); + QLayoutItem *item = m_layout->itemAt(i); AudioLayerWidget *alw = dynamic_cast(item->widget()); if (m_layerUpdate[i].vol > -1) { alw->setVol(m_layerUpdate[i].vol); @@ -95,7 +98,18 @@ void AudioWidget::refreshUi() alw->setDuration(m_layerUpdate[i].duration); m_layerUpdate[i].duration = -1; } + for (int j = 0; j < FILTER_CHANNELS; j++) { + if (m_filtersUpdate[i][j] > -1) + alw->setFilterParam(j, m_filtersUpdate[i][j]); + m_filtersUpdate[i][j] = -1; + } m_layerUpdate[i].updated = false; } } } + +void AudioWidget::filterParamChanged(int layer, int channel, int value) +{ + m_filtersUpdate[layer][channel - 9] = value; + m_layerUpdate[layer].updated = true; +} diff --git a/src/audiowidget.h b/src/audiowidget.h index c314656..6849f5e 100644 --- a/src/audiowidget.h +++ b/src/audiowidget.h @@ -2,6 +2,7 @@ #define AUDIOWIDGET_H #include +#include #include "audiolayerwidget.h" #include "settings.h" @@ -13,12 +14,14 @@ class AudioWidget : public QWidget public: AudioWidget(QWidget *parent = nullptr); + void filterParamChanged(int layer, int channel, int value); private: QHBoxLayout *m_layout; layerData m_layerUpdate[MAX_LAYERS]; QTimer *m_refreshUi; uint m_layers; + int m_filtersUpdate[MAX_LAYERS][FILTER_CHANNELS]; public slots: void volChanged(int layer, float vol); diff --git a/src/clickabledoublespinbox.cpp b/src/clickabledoublespinbox.cpp new file mode 100644 index 0000000..b2b9c3a --- /dev/null +++ b/src/clickabledoublespinbox.cpp @@ -0,0 +1,21 @@ +#include "clickabledoublespinbox.h" + +#include + +ClickableDoubleSpinBox::ClickableDoubleSpinBox(QWidget *parent) + : QDoubleSpinBox(parent) +{ + setFocusPolicy(Qt::NoFocus); + setButtonSymbols(QAbstractSpinBox::NoButtons); + setValue(-1); + setDecimals(1); + setAlignment(Qt::AlignHCenter); + setContentsMargins(0, 0, 0, 0); + setMaximumWidth(50); + this->setStyleSheet("border: 0px solid #5a4855;" + "width: 50px;" + "margin: 0px;" + "background-color: #383034;" + ); +} + diff --git a/src/clickabledoublespinbox.h b/src/clickabledoublespinbox.h new file mode 100644 index 0000000..81bcb76 --- /dev/null +++ b/src/clickabledoublespinbox.h @@ -0,0 +1,25 @@ +#ifndef CLICKABLEDOUBLESPINBOX_H +#define CLICKABLEDOUBLESPINBOX_H + +#include +#include +#include +#include + +class ClickableDoubleSpinBox : public QDoubleSpinBox +{ + Q_OBJECT +public: + explicit ClickableDoubleSpinBox(QWidget *parent = nullptr); +protected: + void mousePressEvent ( QMouseEvent * event ) { + if (event->button() == Qt::LeftButton) { + emit click(); + } + event->accept(); + } +signals: + void click(); +}; + +#endif // CLICKABLEDOUBLESPINBOX_H diff --git a/src/clickableslider.cpp b/src/clickableslider.cpp new file mode 100644 index 0000000..2c34478 --- /dev/null +++ b/src/clickableslider.cpp @@ -0,0 +1,3 @@ +#include "clickableslider.h" + +ClickableSlider::ClickableSlider(QWidget *parent) : QSlider{parent} {} diff --git a/src/clickableslider.h b/src/clickableslider.h new file mode 100644 index 0000000..af34c91 --- /dev/null +++ b/src/clickableslider.h @@ -0,0 +1,30 @@ +#ifndef CLICKABLESLIDER_H +#define CLICKABLESLIDER_H + +#include +#include +#include +#include + +class ClickableSlider : public QSlider +{ + Q_OBJECT +public: + ClickableSlider(QWidget *parent = nullptr); + +protected: + void mousePressEvent ( QMouseEvent * event ) + { + if (event->button() == Qt::RightButton) + { + if (this->isEnabled()) { + qDebug() << "disabling slider"; + this->setDisabled(true); + } + event->accept(); + } + QSlider::mousePressEvent(event); + } +}; + +#endif // CLICKABLESLIDER_H diff --git a/src/defines.h b/src/defines.h index b4560a8..68c72f5 100644 --- a/src/defines.h +++ b/src/defines.h @@ -9,6 +9,7 @@ #define MAX_AUDIODEVICES 8 #define UI_REFRESH_TIME 100 #define FADE_TIME 25 // DMX Frame time, 40 fps, avoid clicks +#define FILTER_CHANNELS 13 // number of dmx channels dedicated to filters by layer struct dmxSetting { int address; diff --git a/src/dmxPersonality.h b/src/dmxPersonality.h index b083ae6..9f965be 100644 --- a/src/dmxPersonality.h +++ b/src/dmxPersonality.h @@ -27,4 +27,24 @@ #define SEND2 23 #define LAYER_CHANNELS 24 +constexpr const char* dmxChannelToString(int e) noexcept +{ + switch (e) { + case HP_FREQ: return "High Pass Cutoff Frec"; + case LOW_FREQ: return "Low Cutoff Frec"; + case LOW_Q: return "Low Slope"; + case LOW_GAIN: return "Low Gain"; + case MIDLOW_FREQ: return "Mid Low Frec"; + case MIDLOW_Q: return "Mid Low Q"; + case MIDLOW_GAIN: return "Mid Low Gain"; + case MIDHIGH_FREQ: return "Mid High Frec"; + case MIDHIGH_Q: return "Mid High Q"; + case MIDHIGH_GAIN: return "Mid High Gain"; + case HIGH_FREQ: return "High Cutoff Frec"; + case HIGH_Q: return "High Slope"; + case HIGH_GAIN: return "High Gain"; + default: return "++--++--++"; + } +} + #endif // DMXPERSONALITY_H diff --git a/src/filterbankwidget.cpp b/src/filterbankwidget.cpp new file mode 100644 index 0000000..276b7f2 --- /dev/null +++ b/src/filterbankwidget.cpp @@ -0,0 +1,82 @@ +#include "filterbankwidget.h" + +#include +#include "dmxPersonality.h" + +FilterBankWidget::FilterBankWidget(QWidget *parent) + : QWidget{parent} +{ + QHBoxLayout *layout = new QHBoxLayout; + layout->setAlignment(Qt::AlignHCenter); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + this->setStyleSheet("border: 1px solid #5a4855;" + "margin: 0px;" + "background-color: #383034;" + ); + for (int i = 0; i < 13; i++) { + fb[i] = new ClickableDoubleSpinBox; + const char *name = dmxChannelToString(i + 9); + fb[i]->setObjectName(name); + fb[i]->setToolTip(name); + } + fb[0]->setRange(0, 500); + layout->insertWidget(0, fb[0]); + for (int i = 1; i < 13;) { + QVBoxLayout *filterLayout= new QVBoxLayout; + for (int j = i; j < i + 3; j++) { + if ((j - 1) % 3 == 0) + fb[j]->setRange(0, 24000); + else if ((i - 1) % 3 == 1) { + fb[j]->setRange(0, 10); + } else { + fb[j]->setRange(-50, 50); + fb[j]->setMinimum(-50); + fb[j]->setValue(-8); + } + filterLayout->insertWidget(j, fb[j]); + } + filterLayout->setSpacing(0); + filterLayout->setAlignment(Qt::AlignHCenter); + filterLayout->setContentsMargins(0, 0, 0, 0); + layout->addLayout(filterLayout); + i += 3; + } + setLayout(layout); +} + +void FilterBankWidget::setValue(int filter, int value) +{ + double result = 0; + int channel = filter + 9; + + if (channel == HP_FREQ) { + result = double((value * 1.31) + 16.0f); // 16 - 350 + } else if (channel == LOW_FREQ) { + result = 30 + (value * 1.647); // 30 - 450 + } else if (channel == LOW_Q) { + result = (double)(value / 32.0f) + 0.1f; // 0.1 - 8 + } else if (channel == LOW_GAIN) { + result = (double)(value / 21.25f) - 6.023528412f; + } else if (channel == MIDLOW_FREQ) { + result = 200 + (value * 9.019607843); // 200 - 450 + } else if (channel == MIDLOW_Q) { + result = (double)( value / 64.0f) + 0.10; // 0.1 - 4 + } else if (channel == MIDLOW_GAIN) { + result = (double)(value / 7.0833333333333f) - 18.0f; + } else if (channel == MIDHIGH_FREQ) { + result = 600 + (value * 25.09803922); // 600 - 7000 + } else if (channel == MIDHIGH_Q) { + result = (double)( value / 64.0f) + 0.10; // 0.1 - 4 + } else if (channel == MIDHIGH_GAIN) { + result = (double)(value / 7.0833333333333f) - 18.0f; + } else if (channel == HIGH_FREQ) { + result = 1500 + (value * 56.8627451); // 1500 - 16000 + } else if (channel == HIGH_Q) { + result = (double)( value / 32.0f) + 0.1f; + } else if (channel == HIGH_GAIN) { + result = (double)(value / 21.25) - 6.023528412f; + } + fb[filter]->setValue(result); + qDebug() << "filter " << filter << " " << result; +} diff --git a/src/filterbankwidget.h b/src/filterbankwidget.h new file mode 100644 index 0000000..07f2e9b --- /dev/null +++ b/src/filterbankwidget.h @@ -0,0 +1,22 @@ +#ifndef FILTERBANKWIDGET_H +#define FILTERBANKWIDGET_H + +#include +#include +#include "clickabledoublespinbox.h" + + +class FilterBankWidget : public QWidget +{ + Q_OBJECT +public: + explicit FilterBankWidget(QWidget *parent = nullptr); + ClickableDoubleSpinBox *fb[13]; + void setValue(int filter, int value); + + +signals: + +}; + +#endif // FILTERBANKWIDGET_H diff --git a/src/libremediaserver-audio.cpp b/src/libremediaserver-audio.cpp index 9366935..0c42e86 100644 --- a/src/libremediaserver-audio.cpp +++ b/src/libremediaserver-audio.cpp @@ -142,6 +142,13 @@ void libreMediaServerAudio::dmxInput(int layer, int channel, int value) #endif } else if (channel >= HP_FREQ && channel <= HIGH_GAIN) { m_mae.filterParamChanged(layer, m_dmxSettings.at(layer).audioDevice, channel, value); +#ifndef NOGUI + if (m_ui) { + m_lmsUi->m_aw->filterParamChanged(layer, channel, value); + m_played.clear(); + m_played.append(m_ola->getValue(layer, DMX_FILE)); + } +#endif } } #ifndef NOGUI diff --git a/src/slidergroup.cpp b/src/slidergroup.cpp index 4395a8d..c8e267d 100644 --- a/src/slidergroup.cpp +++ b/src/slidergroup.cpp @@ -1,16 +1,7 @@ #include "slidergroup.h" -#include -#include -DoubleSpinBoxClickable::DoubleSpinBoxClickable(QWidget *parent) - : QDoubleSpinBox{parent} {} - -DoubleSpinBoxClickable::~DoubleSpinBoxClickable() {} - -SliderClickDisable::SliderClickDisable(QWidget *parent) - : QSlider{parent} {} - -SliderClickDisable::~SliderClickDisable() {} +#include +#include SliderGroup::SliderGroup(QString name, int min, @@ -56,7 +47,7 @@ SliderGroup::SliderGroup(QString name, valueBox.setAlignment(Qt::AlignHCenter); valueBox.setContentsMargins(0, 0, 0, 0); connect(&slider, SIGNAL(valueChanged(int)), this, SLOT(sliderValueChanged(int))); - connect(&valueBox, SIGNAL(enableSlider()), this, SLOT(enableSlider())); + connect(&valueBox, SIGNAL(click()), this, SLOT(enableSlider())); layout->addWidget(&slider); layout->addWidget(&valueBox); this->setStyleSheet("border: 1px solid #5a4855;" diff --git a/src/slidergroup.h b/src/slidergroup.h index b6940b1..39843bc 100644 --- a/src/slidergroup.h +++ b/src/slidergroup.h @@ -1,87 +1,16 @@ #ifndef SLIDERGROUP_H #define SLIDERGROUP_H -#include -#include -#include -#include +#include +#include #include -#include -#include -/* -//slider->installEventFilter(new QSliderAnalyser); -class QSliderAnalyser - : public QObject -{ - public: - QSliderAnalyser() - { - } - virtual ~QSliderAnalyser() - { - } - - protected: - bool eventFilter(QObject* object, QEvent* event) override - { - if (event->type() == QEvent::MouseButtonPress) { - qDebug() << event->type() << object->objectName(); - } - return QObject::eventFilter(object, event); - } -};*/ - -class DoubleSpinBoxClickable: public QDoubleSpinBox -{ - Q_OBJECT - -public: - DoubleSpinBoxClickable(QWidget *parent = 0); - ~DoubleSpinBoxClickable(); - -signals: - void enableSlider(); - -protected: - void mousePressEvent ( QMouseEvent * event ) - { - if (event->button() == Qt::LeftButton) { - qDebug() << "enabling slider"; - emit(enableSlider()); - } - event->accept(); - } -}; - -class SliderClickDisable - : public QSlider -{ - Q_OBJECT - -public: - explicit SliderClickDisable(QWidget *parent = Q_NULLPTR); - ~SliderClickDisable(); - -protected: - void mousePressEvent ( QMouseEvent * event ) - { - if (event->button() == Qt::RightButton) - { - if (this->isEnabled()) { - qDebug() << "disabling slider"; - this->setDisabled(true); - } - event->accept(); - } - QSlider::mousePressEvent(event); - } -}; +#include "clickabledoublespinbox.h" +#include "clickableslider.h" class SliderGroup : public QWidget { Q_OBJECT - public: SliderGroup(QString name, int min, @@ -97,8 +26,8 @@ public slots: void sliderValueChanged(int value); private: - SliderClickDisable slider; - DoubleSpinBoxClickable valueBox; + ClickableSlider slider; + ClickableDoubleSpinBox valueBox; private slots: void enableSlider() { slider.setEnabled(true); } From 14ac167dfbb869a1527a185c3e6c5eef6954d6c9 Mon Sep 17 00:00:00 2001 From: snt Date: Wed, 15 May 2024 18:30:15 +0200 Subject: [PATCH 23/38] cleaning --- src/audiolayerwidget.cpp | 3 +-- src/filterbankwidget.cpp | 7 +++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/audiolayerwidget.cpp b/src/audiolayerwidget.cpp index b2f2782..3c81f6c 100644 --- a/src/audiolayerwidget.cpp +++ b/src/audiolayerwidget.cpp @@ -13,7 +13,7 @@ AudioLayerWidget::AudioLayerWidget(QWidget *parent, int layer): QVBoxLayout *playback = new QVBoxLayout; m_folderValue = new ClickableLabel; - m_folderValue->setMaximumWidth(160); + m_folderValue->setMaximumWidth(300); m_folderValue->setAlignment(Qt::AlignLeft); m_folderValue->setStyleSheet( "color: white;" @@ -53,7 +53,6 @@ AudioLayerWidget::AudioLayerWidget(QWidget *parent, int layer): 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); diff --git a/src/filterbankwidget.cpp b/src/filterbankwidget.cpp index 276b7f2..c07453a 100644 --- a/src/filterbankwidget.cpp +++ b/src/filterbankwidget.cpp @@ -8,10 +8,12 @@ FilterBankWidget::FilterBankWidget(QWidget *parent) { QHBoxLayout *layout = new QHBoxLayout; layout->setAlignment(Qt::AlignHCenter); - layout->setContentsMargins(0, 0, 0, 0); + layout->setContentsMargins(0, 2, 0, 2); layout->setSpacing(0); this->setStyleSheet("border: 1px solid #5a4855;" "margin: 0px;" + "margin-top: 2px;" + "margin-bottom: 2px;" "background-color: #383034;" ); for (int i = 0; i < 13; i++) { @@ -31,8 +33,6 @@ FilterBankWidget::FilterBankWidget(QWidget *parent) fb[j]->setRange(0, 10); } else { fb[j]->setRange(-50, 50); - fb[j]->setMinimum(-50); - fb[j]->setValue(-8); } filterLayout->insertWidget(j, fb[j]); } @@ -78,5 +78,4 @@ void FilterBankWidget::setValue(int filter, int value) result = (double)(value / 21.25) - 6.023528412f; } fb[filter]->setValue(result); - qDebug() << "filter " << filter << " " << result; } From d94f874259cd4a18a6f3e8294f6ff512bd22daf4 Mon Sep 17 00:00:00 2001 From: snt Date: Wed, 15 May 2024 19:28:30 +0200 Subject: [PATCH 24/38] =?UTF-8?q?resuelve=20glitch=20cuando=20no=20empieza?= =?UTF-8?q?=20en=20cero,=20parece=20que=20se=20queda=20en=20alguna=20cach?= =?UTF-8?q?=C3=A9=20intermedia=20unos=20frames=20guardados=20que=20no=20s?= =?UTF-8?q?=C3=A9=20flushear...?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/defines.h | 2 +- src/miniaudioengine.cpp | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/defines.h b/src/defines.h index 68c72f5..23d3322 100644 --- a/src/defines.h +++ b/src/defines.h @@ -1,7 +1,7 @@ #ifndef DEFINES_H #define DEFINES_H -#define VERSION "LibreMediaServerAudio 0.2.0 Antigona Release" +#define VERSION "LibreMediaServerAudio v0.2.0 Antigona" #define COPYRIGHT "(C) 2014-2024 Santi Noreña " #define LICENSE "GPL 3 Licensed. See LICENSE.txt." #define DEFAULT_FILE "lms-audio.xlm" diff --git a/src/miniaudioengine.cpp b/src/miniaudioengine.cpp index 9dbd3c8..6bdeb2d 100644 --- a/src/miniaudioengine.cpp +++ b/src/miniaudioengine.cpp @@ -376,7 +376,10 @@ ma_result MiniAudioEngine::playbackChanged(int layer, Status status) ma_sound_set_stop_time_in_milliseconds(&m_currentSound[layer], ~(ma_uint64)0); ma_sound_set_looping(&m_currentSound[layer], loop); result = ma_sound_start(&m_currentSound[layer]); - //this->volChanged(layer, m_currentLayerValues[layer].vol); // glitch when seek to cursor, how flush the audio buffer? + if (m_currentLayerValues[layer].cursor != 0) { + usleep(1000 * 50); // Avoid small glitch at start, how to flush the cached buffers in audio pipe line? + } + this->volChanged(layer, m_currentLayerValues[layer].vol); default: break; } From 27c7969df38cfbc8c46c15fb30c2bff7a9d1b6c7 Mon Sep 17 00:00:00 2001 From: snt Date: Wed, 15 May 2024 20:29:24 +0200 Subject: [PATCH 25/38] true bypass en filterBank --- src/audiolayerwidget.cpp | 7 +++++++ src/audiolayerwidget.h | 1 + src/defines.h | 1 + src/filterbankwidget.cpp | 15 ++++++++++++++- src/filterbankwidget.h | 6 +++++- src/libremediaserver-audio.cpp | 2 ++ src/miniaudioengine.cpp | 35 ++++++++++++++++++++++++++++++++-- src/miniaudioengine.h | 2 ++ 8 files changed, 65 insertions(+), 4 deletions(-) diff --git a/src/audiolayerwidget.cpp b/src/audiolayerwidget.cpp index 3c81f6c..c3b1f86 100644 --- a/src/audiolayerwidget.cpp +++ b/src/audiolayerwidget.cpp @@ -72,6 +72,7 @@ AudioLayerWidget::AudioLayerWidget(QWidget *parent, int layer): layout->addLayout(status); m_filterBank = new FilterBankWidget(this); + connect(m_filterBank, SIGNAL(setBypass(bool)), this, SLOT(setBypass(bool))); layout->addWidget(m_filterBank); QVBoxLayout *volumeBox = new QVBoxLayout; @@ -111,6 +112,12 @@ void AudioLayerWidget::pitchChanged(int value) emit(uiSliderChanged(m_layer, Slider::Pitch, value)); } +void AudioLayerWidget::setBypass(bool value) +{ + emit(uiSliderChanged(m_layer, Slider::Bypass, value)); +} + + void AudioLayerWidget::toggleSuspendResume() { switch (m_status) { diff --git a/src/audiolayerwidget.h b/src/audiolayerwidget.h index 32bd7c7..f3ba1e4 100644 --- a/src/audiolayerwidget.h +++ b/src/audiolayerwidget.h @@ -53,6 +53,7 @@ private slots: void volumeChanged(int vol); void panChanged(int pan); void pitchChanged(int pitch); + void setBypass(bool value); signals: void uiPlaybackChanged(int layer, Status s); diff --git a/src/defines.h b/src/defines.h index 23d3322..0c78cd9 100644 --- a/src/defines.h +++ b/src/defines.h @@ -64,6 +64,7 @@ enum Slider Volume, Pan, Pitch, + Bypass }; #include diff --git a/src/filterbankwidget.cpp b/src/filterbankwidget.cpp index c07453a..e3e9790 100644 --- a/src/filterbankwidget.cpp +++ b/src/filterbankwidget.cpp @@ -22,8 +22,13 @@ FilterBankWidget::FilterBankWidget(QWidget *parent) fb[i]->setObjectName(name); fb[i]->setToolTip(name); } + QVBoxLayout *master = new QVBoxLayout; fb[0]->setRange(0, 500); - layout->insertWidget(0, fb[0]); + m_bypass = new QCheckBox; + connect(m_bypass, SIGNAL(stateChanged(int)), this, SLOT(bypassChanged(int))); + master->addWidget(m_bypass); + master->addWidget(fb[0]); + layout->addLayout(master); for (int i = 1; i < 13;) { QVBoxLayout *filterLayout= new QVBoxLayout; for (int j = i; j < i + 3; j++) { @@ -79,3 +84,11 @@ void FilterBankWidget::setValue(int filter, int value) } fb[filter]->setValue(result); } + +void FilterBankWidget::bypassChanged(int value) +{ + if (value == 0) + emit setBypass(false); + else + emit setBypass(true); +} diff --git a/src/filterbankwidget.h b/src/filterbankwidget.h index 07f2e9b..bb36ef2 100644 --- a/src/filterbankwidget.h +++ b/src/filterbankwidget.h @@ -3,6 +3,7 @@ #include #include +#include #include "clickabledoublespinbox.h" @@ -12,11 +13,14 @@ class FilterBankWidget : public QWidget public: explicit FilterBankWidget(QWidget *parent = nullptr); ClickableDoubleSpinBox *fb[13]; + QCheckBox *m_bypass; void setValue(int filter, int value); +private slots: + void bypassChanged(int value); signals: - + void setBypass(bool value); }; #endif // FILTERBANKWIDGET_H diff --git a/src/libremediaserver-audio.cpp b/src/libremediaserver-audio.cpp index 0c42e86..2916007 100644 --- a/src/libremediaserver-audio.cpp +++ b/src/libremediaserver-audio.cpp @@ -249,6 +249,8 @@ void libreMediaServerAudio::uiSliderChanged(int layer, Slider s, int value) case Slider::Pitch: m_mae.pitchChanged(layer, value); break; + case Slider::Bypass: + m_mae.setBypass(m_dmxSettings.at(layer).audioDevice, layer, value); } } diff --git a/src/miniaudioengine.cpp b/src/miniaudioengine.cpp index 6bdeb2d..adce7ac 100644 --- a/src/miniaudioengine.cpp +++ b/src/miniaudioengine.cpp @@ -65,6 +65,13 @@ ma_result MiniAudioEngine::createFilterBank(int id, uint layer) ma_node *endpoint = ma_engine_get_endpoint(&m_engine[id]); filterBank *fb = &m_filterBank[id][layer]; + ma_splitter_node_config splitterConfig = ma_splitter_node_config_init(CHANNELS); + result = ma_splitter_node_init(ng, &splitterConfig, NULL, &fb->input); + if (result != MA_SUCCESS) { + cout << "ERROR " << result << ": Failed to initialize input node." << endl; + return result; + } + fb->hpfConfig = ma_hpf_node_config_init(CHANNELS, SAMPLE_RATE, 16, FILTER_ORDER); result = ma_hpf_node_init(ng, &fb->hpfConfig, NULL, &fb->hpf); if (result != MA_SUCCESS) { @@ -100,13 +107,23 @@ ma_result MiniAudioEngine::createFilterBank(int id, uint layer) return result; } - ma_splitter_node_config splitterConfig = ma_splitter_node_config_init(CHANNELS); result = ma_splitter_node_init(ng, &splitterConfig, NULL, &fb->output); if (result != MA_SUCCESS) { cout << "ERROR " << result << ": Failed to initialize output node." << endl; return result; } + result = ma_node_attach_output_bus(&fb->input, 0, &fb->hpf, 0); + if (result != MA_SUCCESS) { + cout << "ERROR " << result << ": Failed to attach input node." << endl; + return result; + } + result = ma_node_attach_output_bus(&fb->input, 1, &fb->output, 0); + if (result != MA_SUCCESS) { + cout << "ERROR " << result << ": Failed to attach high pass pass filter node." << endl; + return result; + } + ma_node_set_output_bus_volume(&fb->input, 1, 0.0f); result = ma_node_attach_output_bus(&fb->hpf, 0, &fb->loshelf, 0); if (result != MA_SUCCESS) { cout << "ERROR " << result << ": Failed to attach high pass pass filter node." << endl; @@ -256,7 +273,7 @@ ma_result MiniAudioEngine::loadMedia(int layer, char *file, uint audioDevice) cout << "Error " << result << ": Failed to load file " << file << endl; return result; } - result = ma_node_attach_output_bus(&m_currentSound[layer], 0, &m_filterBank[audioDevice][layer].hpf, 0); + result = ma_node_attach_output_bus(&m_currentSound[layer], 0, &m_filterBank[audioDevice][layer].input, 0); if (result != MA_SUCCESS) { cout << "Error " << result << ": Failed to attach output bus " << audioDevice << endl; //return result; @@ -526,3 +543,17 @@ ma_result MiniAudioEngine::filterParamChanged(int layer, int audioDevice, int ch } return (result); } + +bool MiniAudioEngine::setBypass(int audioDevice, int layer, bool bypass) +{ + filterBank *fb = &m_filterBank[audioDevice][layer]; + + if (bypass) { + ma_node_set_output_bus_volume(&fb->input, 1, 1.0f); + ma_node_set_output_bus_volume(&fb->input, 0, 0.0f); + } else { + ma_node_set_output_bus_volume(&fb->input, 1, 0.0f); + ma_node_set_output_bus_volume(&fb->input, 0, 1.0f); + } + +} diff --git a/src/miniaudioengine.h b/src/miniaudioengine.h index 332c1bf..b55311b 100644 --- a/src/miniaudioengine.h +++ b/src/miniaudioengine.h @@ -19,6 +19,7 @@ using namespace std; typedef struct { + ma_splitter_node input; ma_hpf_node hpf; ma_hpf_node_config hpfConfig; ma_loshelf_node loshelf; @@ -43,6 +44,7 @@ public: bool startEngine(uint layersQty); bool startDevice(uint *id, uint nb); static void audioDataCallback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount); + bool setBypass(int audioDevice, int layer, bool bypass); protected: ma_result loadMedia(int layer, char *media, uint audioDevice); From 7aced09a0218a6421fa000384e33ae80eb76151c Mon Sep 17 00:00:00 2001 From: snt Date: Wed, 15 May 2024 22:36:46 +0200 Subject: [PATCH 26/38] style bypass checkbox --- src/filterbankwidget.cpp | 23 +++++++++++++++++++---- src/libremediaserver-audio-gui.cpp | 2 +- src/miniaudioengine.cpp | 9 +++++++++ 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/filterbankwidget.cpp b/src/filterbankwidget.cpp index e3e9790..aafa6fb 100644 --- a/src/filterbankwidget.cpp +++ b/src/filterbankwidget.cpp @@ -10,10 +10,10 @@ FilterBankWidget::FilterBankWidget(QWidget *parent) layout->setAlignment(Qt::AlignHCenter); layout->setContentsMargins(0, 2, 0, 2); layout->setSpacing(0); - this->setStyleSheet("border: 1px solid #5a4855;" + this->setStyleSheet("border: 2px solid #5a4855;" "margin: 0px;" - "margin-top: 2px;" - "margin-bottom: 2px;" + "margin-top: 3px;" + "margin-bottom: 3px;" "background-color: #383034;" ); for (int i = 0; i < 13; i++) { @@ -25,8 +25,23 @@ FilterBankWidget::FilterBankWidget(QWidget *parent) QVBoxLayout *master = new QVBoxLayout; fb[0]->setRange(0, 500); m_bypass = new QCheckBox; - connect(m_bypass, SIGNAL(stateChanged(int)), this, SLOT(bypassChanged(int))); master->addWidget(m_bypass); + m_bypass->setText("Bypass"); + m_bypass->setStyleSheet("QCheckBox { border: 2px solid #2a0825;" + "text-align: left top;" + "margin: 0px;" + "background-color: #885074;" + "font-size: 7px;}" + "QCheckBox::indicator { subcontrol-position: right top;" + "width: 30px;" + "height: 30px;" + "background-color: gray;" + "border-radius: 15px;" + "border-style: solid;" + "border-width: 1px;" + "border-color: white white black black;" + ); + connect(m_bypass, SIGNAL(stateChanged(int)), this, SLOT(bypassChanged(int))); master->addWidget(fb[0]); layout->addLayout(master); for (int i = 1; i < 13;) { diff --git a/src/libremediaserver-audio-gui.cpp b/src/libremediaserver-audio-gui.cpp index b769679..22f673c 100644 --- a/src/libremediaserver-audio-gui.cpp +++ b/src/libremediaserver-audio-gui.cpp @@ -35,7 +35,7 @@ libreMediaServerAudioUi::libreMediaServerAudioUi(QWidget *parent) 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->setContentsMargins(3, 3, 3, 3); this->setStyleSheet( "color: white;" "background-color: #4f4048;" diff --git a/src/miniaudioengine.cpp b/src/miniaudioengine.cpp index adce7ac..888163b 100644 --- a/src/miniaudioengine.cpp +++ b/src/miniaudioengine.cpp @@ -165,6 +165,15 @@ ma_result MiniAudioEngine::setNodeGraph(int id) { result = this->createFilterBank(id, i); i++; } + //ma_node_graph *ng = ma_engine_get_node_graph(&m_engine[id]); + //ma_node *endpoint = ma_engine_get_endpoint(&m_engine[id]); + for (uint i = 0; i < m_layersQty; i++) { + result = ma_node_attach_output_bus(&m_filterBank[0][i].output, 0, &m_filterBank[1][i].output, 0); + if (result != MA_SUCCESS) { + cout << "ERROR " << result << ": Failed to attach aux send." << endl; + //return result; + } + } return (result); } From fc274179ad4cb6613fac27eb8bd07c95cd5bf020 Mon Sep 17 00:00:00 2001 From: snt Date: Sat, 18 May 2024 22:52:22 +0200 Subject: [PATCH 27/38] =?UTF-8?q?funcionando=20en=20dos=20dispositivos=20m?= =?UTF-8?q?ediante=20ring=20buffer,=20pero=20no=20puedo=20mandar=20a=20dos?= =?UTF-8?q?=20dispositivos,=20si=20lo=20pongo=20con=20ma=5Fsplitter=20repr?= =?UTF-8?q?oduce=20m=C3=A1s=20r=C3=A1pido=20y=20con=20glitches=20mandar=20?= =?UTF-8?q?diferentes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- libremediaserver-audio.pro | 7 +- src/defines.h | 54 +++++------- src/dmxPersonality.h | 8 +- src/filterbankwidget.cpp | 12 +-- src/ma_writer_node.c | 175 +++++++++++++++++++++++++++++++++++++ src/ma_writer_node.h | 52 +++++++++++ src/miniaudioengine.cpp | 142 +++++++++++++++++++++++------- src/miniaudioengine.h | 30 +++++-- 8 files changed, 390 insertions(+), 90 deletions(-) create mode 100644 src/ma_writer_node.c create mode 100644 src/ma_writer_node.h diff --git a/libremediaserver-audio.pro b/libremediaserver-audio.pro index 0371808..391f2c9 100644 --- a/libremediaserver-audio.pro +++ b/libremediaserver-audio.pro @@ -8,9 +8,11 @@ HEADERS += src/libremediaserver-audio.h \ src/dmxwidget.h \ src/filterbankwidget.h \ src/libremediaserver-audio-gui.h \ + src/ma_writer_node.h \ src/main.h \ - src/miniaudio.h \ src/medialibrary.h \ + src/miniaudio.h \ + src/ma_writer_node.h \ src/miniaudioengine.h \ src/olathread.h \ src/audiolayerwidget.h \ @@ -26,10 +28,11 @@ SOURCES += src/main.cpp \ src/dmxwidget.cpp \ src/filterbankwidget.cpp \ src/libremediaserver-audio-gui.cpp \ - src/miniaudio.c \ src/libremediaserver-audio.cpp \ src/medialibrary.cpp \ + src/miniaudio.c \ src/miniaudioengine.cpp \ + src/ma_writer_node.c \ src/olathread.cpp \ src/audiolayerwidget.cpp \ src/audiowidget.cpp \ diff --git a/src/defines.h b/src/defines.h index 0c78cd9..5f73abe 100644 --- a/src/defines.h +++ b/src/defines.h @@ -1,15 +1,18 @@ #ifndef DEFINES_H #define DEFINES_H -#define VERSION "LibreMediaServerAudio v0.2.0 Antigona" -#define COPYRIGHT "(C) 2014-2024 Santi Noreña " -#define LICENSE "GPL 3 Licensed. See LICENSE.txt." -#define DEFAULT_FILE "lms-audio.xlm" -#define MAX_LAYERS 4 -#define MAX_AUDIODEVICES 8 -#define UI_REFRESH_TIME 100 -#define FADE_TIME 25 // DMX Frame time, 40 fps, avoid clicks -#define FILTER_CHANNELS 13 // number of dmx channels dedicated to filters by layer +#define VERSION "LibreMediaServerAudio v0.2.0 Antigona" +#define COPYRIGHT "(C) 2014-2024 Santi Noreña " +#define LICENSE "GPL3 Licensed. See LICENSE.txt." +#define DEFAULT_FILE "lms-audio.xlm" +#define MAX_LAYERS 4 +#define MAX_AUDIODEVICES 8 +#define FORMAT ma_format_f32 /* Must always be f32. */ +#define CHANNELS 2 +#define SAMPLE_RATE 48000 +#define UI_REFRESH_TIME 100 +#define FADE_TIME 25 // DMX Frame time, 40 fps, avoid clicks +#define FILTER_CHANNELS 13 // number of dmx channels dedicated to filters by layer struct dmxSetting { int address; @@ -18,7 +21,7 @@ struct dmxSetting { int audioDevice; }; -enum class Status +enum Status { Stopped, Paused, @@ -30,6 +33,15 @@ enum class Status PlayingFolderRandom }; +enum Slider +{ + Volume, + Pan, + Pitch, + Bypass +}; + +#ifdef __cplusplus constexpr const char* statusToString(Status e) noexcept { switch (e) @@ -45,27 +57,6 @@ constexpr const char* statusToString(Status e) noexcept default: return "--++--"; } } -/* -static const char* StatusStr[] = -{ - "Stop", - "Pause", - "Play One", - "Play One Loop", - "Iddle", - "Play Folder", - "Play Folder Loop", - "Play Folder Rand", - 0x0 -};*/ - -enum Slider -{ - Volume, - Pan, - Pitch, - Bypass -}; #include struct layerData { @@ -81,4 +72,5 @@ struct layerData { unsigned int universe; int device; }; +#endif // __cplusplus #endif // DEFINES_H diff --git a/src/dmxPersonality.h b/src/dmxPersonality.h index 9f965be..1da964a 100644 --- a/src/dmxPersonality.h +++ b/src/dmxPersonality.h @@ -23,9 +23,10 @@ #define HIGH_FREQ 19 #define HIGH_Q 20 #define HIGH_GAIN 21 -#define SEND1 22 -#define SEND2 23 -#define LAYER_CHANNELS 24 +#define FILTER_BANK_GAIN 22 // not implemented yet +#define SEND1 23 +#define SEND2 24 +#define LAYER_CHANNELS 25 constexpr const char* dmxChannelToString(int e) noexcept { @@ -43,6 +44,7 @@ constexpr const char* dmxChannelToString(int e) noexcept case HIGH_FREQ: return "High Cutoff Frec"; case HIGH_Q: return "High Slope"; case HIGH_GAIN: return "High Gain"; + case FILTER_BANK_GAIN: return "Post Filters Gain"; default: return "++--++--++"; } } diff --git a/src/filterbankwidget.cpp b/src/filterbankwidget.cpp index aafa6fb..c9b107d 100644 --- a/src/filterbankwidget.cpp +++ b/src/filterbankwidget.cpp @@ -28,19 +28,9 @@ FilterBankWidget::FilterBankWidget(QWidget *parent) master->addWidget(m_bypass); m_bypass->setText("Bypass"); m_bypass->setStyleSheet("QCheckBox { border: 2px solid #2a0825;" - "text-align: left top;" "margin: 0px;" "background-color: #885074;" - "font-size: 7px;}" - "QCheckBox::indicator { subcontrol-position: right top;" - "width: 30px;" - "height: 30px;" - "background-color: gray;" - "border-radius: 15px;" - "border-style: solid;" - "border-width: 1px;" - "border-color: white white black black;" - ); + "font-size: 7px;}"); connect(m_bypass, SIGNAL(stateChanged(int)), this, SLOT(bypassChanged(int))); master->addWidget(fb[0]); layout->addLayout(master); diff --git a/src/ma_writer_node.c b/src/ma_writer_node.c new file mode 100644 index 0000000..e05d38d --- /dev/null +++ b/src/ma_writer_node.c @@ -0,0 +1,175 @@ +#include "ma_writer_node.h" +#include "miniaudio.c" +MA_API ma_writer_node_config ma_writer_node_config_init(ma_uint32 channels, ma_uint32 bufferSizeInFrames, ma_pcm_rb *rb) +{ + ma_writer_node_config config; + + MA_ZERO_OBJECT(&config); + config.nodeConfig = ma_node_config_init(); + config.channels = channels; + config.bufferSizeInFrames = bufferSizeInFrames; + config.pBuffer = rb; + + return config; +} + +static void ma_writer_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) +{ + ma_writer_node* pWriteNode = (ma_writer_node*)pNode; + + MA_ASSERT(pWriteNode != NULL); + MA_ASSERT(ma_node_get_input_bus_count(&pWriteNode->baseNode) == 1); + + if (*pFrameCountIn > 0) { + void *pWriteBuffer = NULL; + ma_pcm_rb_acquire_write(pWriteNode->pBuffer, pFrameCountIn, &pWriteBuffer); + if (pWriteBuffer != NULL) { + ma_copy_pcm_frames(pWriteBuffer, ppFramesIn[0], *pFrameCountIn, ma_format_f32, pWriteNode->channels); + ma_pcm_rb_commit_write(pWriteNode->pBuffer, *pFrameCountIn); + } + //ma_silence_pcm_frames(ppFramesOut[0], *pFrameCountOut, ma_format_f32, pWriteNode->channels); + } + //*pFrameCountOut = 0; + ma_copy_pcm_frames(ppFramesOut[0], ppFramesIn[0], *pFrameCountOut, ma_format_f32, pWriteNode->channels); +} + +static ma_node_vtable g_ma_writer_node_vtable = +{ + ma_writer_node_process_pcm_frames, + NULL, + 1, + 1, + 0 +// MA_NODE_FLAG_CONTINUOUS_PROCESSING +// MA_NODE_FLAG_SILENT_OUTPUT +}; + +MA_API ma_result ma_writer_node_init(ma_node_graph* pNodeGraph, const ma_writer_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_writer_node* pWriteNode) +{ + ma_result result; + ma_node_config baseConfig; + + if (pWriteNode == NULL || pConfig == NULL || pConfig->pBuffer == NULL \ + || (pConfig->channels > MA_MAX_NODE_BUS_COUNT) ) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pWriteNode); + + baseConfig = pConfig->nodeConfig; + baseConfig.vtable = &g_ma_writer_node_vtable; + baseConfig.pInputChannels = &pConfig->channels; + baseConfig.pOutputChannels = &pConfig->channels; + + result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pWriteNode->baseNode); + if (result != MA_SUCCESS) { + return result; + } + pWriteNode->bufferSizeInFrames = pConfig->bufferSizeInFrames; + pWriteNode->pBuffer = pConfig->pBuffer; + pWriteNode->channels = pConfig->channels; + return MA_SUCCESS; +} + +MA_API void ma_writer_node_uninit(ma_writer_node* pWriteNode, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_node_uninit(&pWriteNode->baseNode, pAllocationCallbacks); +} + +/* + * Data Source Ring Buffer + */ + + +ma_result ma_data_source_rb_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + ma_data_source_rb* ds = (ma_data_source_rb*)pDataSource; + + ma_uint32 pcmFramesAvailableInRB = 0; + ma_uint32 pcmFramesProcessed = 0; + // lo mismo que en el callback, va el doble de rápido y con glitches. + while (pcmFramesProcessed < frameCount) { + pcmFramesAvailableInRB = ma_pcm_rb_available_read(ds->rb); + if (pcmFramesAvailableInRB == 0) { + break; + } + ma_uint32 framesToRead = frameCount - pcmFramesProcessed; + if (framesToRead > pcmFramesAvailableInRB) { + framesToRead = pcmFramesAvailableInRB; + } + void* pReadBuffer = NULL; + ma_pcm_rb_acquire_read(ds->rb, &framesToRead, &pReadBuffer); + if (pReadBuffer != NULL) { + ma_copy_pcm_frames(pFramesOut, pReadBuffer, framesToRead, ma_format_f32, 2); + ma_pcm_rb_commit_read(ds->rb, framesToRead); + pcmFramesProcessed += framesToRead; + } + else { + break; + } + } + *pFramesRead += pcmFramesProcessed; + return MA_SUCCESS; +} + +ma_result ma_data_source_rb_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) +{ + (void)pDataSource; + (void)frameIndex; + return MA_NOT_IMPLEMENTED; +} + +ma_result ma_data_source_rb_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +{ + (void)pDataSource; + *pFormat = ma_format_f32; + *pChannels = 2; + *pSampleRate = ma_standard_sample_rate_48000; + return MA_SUCCESS; +} + +ma_result ma_data_source_rb_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) +{ + (void)pDataSource; + *pCursor = 0; + return MA_NOT_IMPLEMENTED; +} + +ma_result ma_data_source_rb_get_length(ma_data_source* pDataSource, ma_uint64* pLength) +{ + (void)pDataSource; + *pLength = 0; + return MA_NOT_IMPLEMENTED; +} + +ma_data_source_vtable g_ma_data_source_rb_vtable = +{ + ma_data_source_rb_read, + ma_data_source_rb_seek, + ma_data_source_rb_get_data_format, + ma_data_source_rb_get_cursor, + ma_data_source_rb_get_length +}; + +ma_result ma_data_source_rb_init(ma_data_source_rb* pMyDataSource, ma_pcm_rb *ringBuffer) +{ + ma_result result; + ma_data_source_config baseConfig; + + baseConfig = ma_data_source_config_init(); + baseConfig.vtable = &g_ma_data_source_rb_vtable; + + result = ma_data_source_init(&baseConfig, &pMyDataSource->base); + if (result != MA_SUCCESS) { + return result; + } + + pMyDataSource->rb = ringBuffer; + + return MA_SUCCESS; +} + +void ma_data_source_rb_uninit(ma_data_source_rb* pMyDataSource) +{ + ma_data_source_uninit(&pMyDataSource->base); +} diff --git a/src/ma_writer_node.h b/src/ma_writer_node.h new file mode 100644 index 0000000..1b5d259 --- /dev/null +++ b/src/ma_writer_node.h @@ -0,0 +1,52 @@ +/* Include ma_writer_node.h after miniaudio.h */ +#ifndef ma_writer_node_h +#define ma_writer_node_h + +#ifdef __cplusplus +extern "C" { +#endif + +#include "miniaudio.h" + +typedef struct +{ + ma_node_config nodeConfig; + ma_uint32 channels; + ma_uint32 bufferSizeInFrames; + ma_pcm_rb *pBuffer; +} ma_writer_node_config; + +MA_API ma_writer_node_config ma_writer_node_config_init(ma_uint32 channels, ma_uint32 bufferSizeInFrames, ma_pcm_rb *rb); + +typedef struct +{ + ma_node_base baseNode; + ma_uint32 bufferSizeInFrames; + ma_pcm_rb *pBuffer; + ma_uint32 channels; +} ma_writer_node; + +MA_API ma_result ma_writer_node_init(ma_node_graph* pNodeGraph, const ma_writer_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_writer_node* pWriteNode); +MA_API void ma_writer_node_uninit(ma_writer_node* pWriteNode, const ma_allocation_callbacks* pAllocationCallbacks); +/** + * data source ring buffer + */ + +typedef struct +{ + ma_data_source_base base; + ma_pcm_rb *rb; +} ma_data_source_rb; + +ma_result ma_data_source_rb_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); +ma_result ma_data_source_rb_seek(ma_data_source* pDataSource, ma_uint64 frameIndex); +ma_result ma_data_source_rb_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); +ma_result ma_data_source_rb_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor); +ma_result ma_data_source_rb_get_length(ma_data_source* pDataSource, ma_uint64* pLength); +ma_result ma_data_source_rb_init(ma_data_source_rb* pMyDataSource, ma_pcm_rb *ringBuffer); +void ma_data_source_rb_uninit(ma_data_source_rb* pMyDataSource); + +#ifdef __cplusplus +} +#endif +#endif /* ma_writer_node_h */ diff --git a/src/miniaudioengine.cpp b/src/miniaudioengine.cpp index 888163b..914708d 100644 --- a/src/miniaudioengine.cpp +++ b/src/miniaudioengine.cpp @@ -4,16 +4,48 @@ #define BIAS 0.99f #define FILTER_ORDER 3 +static ma_pcm_rb aux1Buffer; +static ma_data_source_node g_dataSupplyNode; +static ma_data_source_rb g_dataSourceRB; + MiniAudioEngine::MiniAudioEngine() {} void MiniAudioEngine::audioDataCallback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) { - (void)pInput; ma_result result; + result = ma_engine_read_pcm_frames((ma_engine*)pDevice->pUserData, pOutput, frameCount, NULL); if (result != MA_SUCCESS) { cout << "Error " << result << ": error audio callback."; } + (void)pInput; +} + +void MiniAudioEngine::audioDataCallback1(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) +{ + (void)pDevice; + ma_result result; + + ma_uint32 pcmFramesAvailableInRB = 0; + ma_uint32 pcmFramesProcessed = 0; + while (pcmFramesProcessed < frameCount) { + pcmFramesAvailableInRB = ma_pcm_rb_available_read(&aux1Buffer); + if (pcmFramesAvailableInRB == 0) { + break; + } + ma_uint32 framesToRead = frameCount - pcmFramesProcessed; + if (framesToRead > pcmFramesAvailableInRB) { + framesToRead = pcmFramesAvailableInRB; + } + void* pReadBuffer = NULL; + ma_pcm_rb_acquire_read(&aux1Buffer, &framesToRead, &pReadBuffer); + if (pReadBuffer != NULL) { + ma_copy_pcm_frames(pOutput, pReadBuffer, framesToRead, FORMAT, CHANNELS); + ma_pcm_rb_commit_read(&aux1Buffer, framesToRead); + pcmFramesProcessed += framesToRead; + }/* else { break; }*/ + } + (void)pInput; } void MiniAudioEngine::stopEngine() @@ -23,6 +55,7 @@ void MiniAudioEngine::stopEngine() } for (uint i = 0; i < m_devicesSelected; i++) { for (uint j = 0; j < m_layersQty; j++) { + ma_node_uninit(&m_filterBank[i][j].input, NULL); ma_hpf_node_uninit(&m_filterBank[i][j].hpf, NULL); ma_loshelf_node_uninit(&m_filterBank[i][j].loshelf, NULL); ma_peak_node_uninit(&m_filterBank[i][j].mLow, NULL); @@ -30,6 +63,8 @@ void MiniAudioEngine::stopEngine() ma_hishelf_node_uninit(&m_filterBank[i][j].hishelf, NULL); ma_splitter_node_uninit(&m_filterBank[i][j].output, NULL); } + //ma_writer_node_uninit(&m_sendToAux[0], NULL); + //ma_pcm_rb_uninit(&aux1Buffer); ma_engine_uninit(&m_engine[i]); ma_device_uninit(&m_device[i]); } @@ -60,56 +95,55 @@ bool MiniAudioEngine::startEngine(uint layers) ma_result MiniAudioEngine::createFilterBank(int id, uint layer) { ma_result result; - ma_node_graph *ng = ma_engine_get_node_graph(&m_engine[id]); ma_node *endpoint = ma_engine_get_endpoint(&m_engine[id]); - filterBank *fb = &m_filterBank[id][layer]; + filterBank *fb = &m_filterBank[id][layer]; ma_splitter_node_config splitterConfig = ma_splitter_node_config_init(CHANNELS); result = ma_splitter_node_init(ng, &splitterConfig, NULL, &fb->input); if (result != MA_SUCCESS) { - cout << "ERROR " << result << ": Failed to initialize input node." << endl; + cout << "ERROR " << result << ": Failed to init input node." << endl; return result; } fb->hpfConfig = ma_hpf_node_config_init(CHANNELS, SAMPLE_RATE, 16, FILTER_ORDER); result = ma_hpf_node_init(ng, &fb->hpfConfig, NULL, &fb->hpf); if (result != MA_SUCCESS) { - cout << "ERROR " << result << ": Failed to initialize high pass filter node." << endl; + cout << "ERROR " << result << ": Failed to init high pass filter node." << endl; return result; } fb->loshelfConfig = ma_loshelf_node_config_init(CHANNELS, SAMPLE_RATE, 0.0f, 1.0f, 30); result = ma_loshelf_node_init(ng, &fb->loshelfConfig, NULL, &fb->loshelf); if (result != MA_SUCCESS) { - cout << "ERROR " << result << ": Failed to initialize low pass filter node." << endl; + cout << "ERROR " << result << ": Failed to init low pass filter node." << endl; return result; } fb->mLowConfig = ma_peak_node_config_init(CHANNELS, SAMPLE_RATE, 0.0, 4.0, 200); // double gainDB, double q, double frequency); result = ma_peak_node_init(ng, &fb->mLowConfig, NULL, &fb->mLow); if (result != MA_SUCCESS) { - cout << "ERROR " << result << ": Failed to initialize peak low filter node." << endl; + cout << "ERROR " << result << ": Failed to init peak low filter node." << endl; return result; } fb->mHighConfig = ma_peak_node_config_init(CHANNELS, SAMPLE_RATE, 0.0, 0.0, 600); // double gainDB, double q, double frequency); result = ma_peak_node_init(ng, &fb->mHighConfig, NULL, &fb->mHigh); if (result != MA_SUCCESS) { - cout << "ERROR " << result << ": Failed to initialize peak high filter node." << endl; + cout << "ERROR " << result << ": Failed to init peak high filter node." << endl; return result; } fb->hishelfConfig = ma_hishelf_node_config_init(CHANNELS, SAMPLE_RATE, 0.0f, 1.0f, 20000); result = ma_hishelf_node_init(ng, &fb->hishelfConfig, NULL, &fb->hishelf); if (result != MA_SUCCESS) { - cout << "ERROR " << result << ": Failed to initialize hi shelf filter node." << endl; + cout << "ERROR " << result << ": Failed to init hi shelf filter node." << endl; return result; } result = ma_splitter_node_init(ng, &splitterConfig, NULL, &fb->output); if (result != MA_SUCCESS) { - cout << "ERROR " << result << ": Failed to initialize output node." << endl; + cout << "ERROR " << result << ": Failed to init output node." << endl; return result; } @@ -120,7 +154,7 @@ ma_result MiniAudioEngine::createFilterBank(int id, uint layer) } result = ma_node_attach_output_bus(&fb->input, 1, &fb->output, 0); if (result != MA_SUCCESS) { - cout << "ERROR " << result << ": Failed to attach high pass pass filter node." << endl; + cout << "ERROR " << result << ": Failed to attach bypass connection." << endl; return result; } ma_node_set_output_bus_volume(&fb->input, 1, 0.0f); @@ -149,10 +183,17 @@ ma_result MiniAudioEngine::createFilterBank(int id, uint layer) cout << "ERROR " << result << ": Failed to attach high shelf filter node." << endl; return result; } - result = ma_node_attach_output_bus(&fb->output, 0, endpoint, 0); - if (result != MA_SUCCESS) { - cout << "ERROR " << result << ": Failed to attach output node to engine." << endl; - return result; + if (id == 0) { + //result = ma_node_attach_output_bus(&fb->output, 1, endpoint, 0); + if (result != MA_SUCCESS) { + cout << "ERROR " << result << ": Failed to attach output node to engine." << endl; + return result; + } + result = ma_node_attach_output_bus(&fb->output, 0, &m_sendToAux[id], 0); + if (result != MA_SUCCESS) { + cout << "ERROR " << result << ": Failed to attach output node to aux send 1." << endl; + return result; + } } return result; } @@ -161,19 +202,31 @@ ma_result MiniAudioEngine::setNodeGraph(int id) { ma_result result = MA_SUCCESS; uint i = 0; + if (id == 0) { + ma_node_graph *ng = ma_engine_get_node_graph(&m_engine[id]); + size_t sizeInFrames = SAMPLE_RATE / 10; // ma_get_bytes_per_frame(FORMAT, CHANNELS); + result = ma_pcm_rb_init(FORMAT, CHANNELS, sizeInFrames, NULL, NULL, &aux1Buffer); + if (result != MA_SUCCESS) { + printf("Failed to initialize ring buffer.\n"); + return result; + } + ma_silence_pcm_frames(aux1Buffer.rb.pBuffer, sizeInFrames, FORMAT, CHANNELS); + ma_writer_node_config writerConfig = ma_writer_node_config_init(CHANNELS, SAMPLE_RATE * 5, &aux1Buffer); + result = ma_writer_node_init(ng, &writerConfig, NULL, &m_sendToAux[id]); + if (result != MA_SUCCESS) { + cout << "ERROR " << result << ": Failed to init writer node." << endl; + return result; + } + result = ma_node_attach_output_bus(&m_sendToAux[id], 0, ma_engine_get_endpoint(&m_engine[id]), 0); // Pull API + if (result != MA_SUCCESS) { + cout << "ERROR " << result << ": Failed to attach writer node." << endl; + return result; + } + } while (result == MA_SUCCESS && i < m_layersQty) { result = this->createFilterBank(id, i); i++; } - //ma_node_graph *ng = ma_engine_get_node_graph(&m_engine[id]); - //ma_node *endpoint = ma_engine_get_endpoint(&m_engine[id]); - for (uint i = 0; i < m_layersQty; i++) { - result = ma_node_attach_output_bus(&m_filterBank[0][i].output, 0, &m_filterBank[1][i].output, 0); - if (result != MA_SUCCESS) { - cout << "ERROR " << result << ": Failed to attach aux send." << endl; - //return result; - } - } return (result); } @@ -185,12 +238,19 @@ bool MiniAudioEngine::startDevice(uint *systemId, uint nb) m_devicesSelected = nb; for (uint internalId = 0; internalId < nb; internalId++) { - deviceConfig = ma_device_config_init(ma_device_type_playback); + deviceConfig = ma_device_config_init(ma_device_type_duplex); + deviceConfig.capture.pDeviceID = &m_pPlaybackDeviceInfos[systemId[internalId]].id; + deviceConfig.capture.format = m_resourceManager.config.decodedFormat; + deviceConfig.capture.channels = CHANNELS; + deviceConfig.capture.shareMode = ma_share_mode_shared; deviceConfig.playback.pDeviceID = &m_pPlaybackDeviceInfos[systemId[internalId]].id; deviceConfig.playback.format = m_resourceManager.config.decodedFormat; - deviceConfig.playback.channels = 0; + deviceConfig.playback.channels = CHANNELS; deviceConfig.sampleRate = m_resourceManager.config.decodedSampleRate; - deviceConfig.dataCallback = audioDataCallback; + if (internalId == 0) + deviceConfig.dataCallback = audioDataCallback; + else if (internalId == 1) + deviceConfig.dataCallback = audioDataCallback1; deviceConfig.pUserData = &m_engine[internalId]; result = ma_device_init(&m_context, &deviceConfig, &m_device[internalId]); if (result != MA_SUCCESS) { @@ -200,6 +260,7 @@ bool MiniAudioEngine::startDevice(uint *systemId, uint nb) engineConfig = ma_engine_config_init(); engineConfig.pDevice = &m_device[internalId]; engineConfig.pResourceManager = &m_resourceManager; + engineConfig.gainSmoothTimeInMilliseconds = SAMPLE_RATE / 100; engineConfig.noAutoStart = MA_TRUE; result = ma_engine_init(&engineConfig, &m_engine[internalId]); if (result != MA_SUCCESS) { @@ -218,6 +279,23 @@ bool MiniAudioEngine::startDevice(uint *systemId, uint nb) } cout << "Initialized Audio Device. internalId: " << internalId << " systemId: " << systemId[internalId] << " " << m_pPlaybackDeviceInfos[systemId[internalId]].name << endl; } + //result = ma_data_source_rb_init(&g_dataSourceRB, &aux1Buffer); + if (result != MA_SUCCESS) { + cout << "Error " << result << ": Failed to init data source ring buffer" << endl; + return false; + } + ma_data_source_node_config dataSupplyNodeConfig = ma_data_source_node_config_init(&g_dataSourceRB); + //result = ma_data_source_node_init(ma_engine_get_node_graph(&m_engine[1]), &dataSupplyNodeConfig, NULL, &g_dataSupplyNode); + if (result != MA_SUCCESS) { + cout << "Error " << result << ": Failed to init data source node" << endl; + return false; + } + //result = ma_node_attach_output_bus(&g_dataSupplyNode, 0, ma_engine_get_endpoint(&m_engine[1]), 0); + if (result != MA_SUCCESS) { + cout << "Error " << result << ": Failed to attach data source rb node" << endl; + return false; + } + cout << "data source node state " << ma_node_get_state(&g_dataSupplyNode.base) << endl; return true; } @@ -264,12 +342,8 @@ ma_result MiniAudioEngine::loadMedia(int layer, char *file, uint audioDevice) { ma_result result; - // ToDo: ver si s puede attach dos dispositivos a la vez. si no: - // - enchufar a un splitter al sonido y attach cada uno de los lados. // - iniciar un sonido por cada capa, copiar la capa en otro dispositivo - // - splitter al final de filterBank, esas señales se mezclan en un nodo mudo - // y se escribe la mezcla en un buffer. Mezclar el buffer en disco con el - // del otro device en el audio callback . + // - writer node y source con el buffer escrito en el otro device if (m_mediaLoaded[layer] == true) { ma_sound_uninit(&m_currentSound[layer]); @@ -284,7 +358,7 @@ ma_result MiniAudioEngine::loadMedia(int layer, char *file, uint audioDevice) } result = ma_node_attach_output_bus(&m_currentSound[layer], 0, &m_filterBank[audioDevice][layer].input, 0); if (result != MA_SUCCESS) { - cout << "Error " << result << ": Failed to attach output bus " << audioDevice << endl; + cout << "Error " << result << ": Failed to attach sound output bus " << audioDevice << endl; //return result; } m_mediaLoaded[layer] = true; @@ -352,7 +426,7 @@ void MiniAudioEngine::volChanged(int layer, float vol) return; if (vol >= 1) vol = 0.99f; - ma_sound_group_set_fade_in_milliseconds(&m_currentSound[layer], -1, vol, FADE_TIME); + ma_sound_group_set_fade_in_milliseconds(&m_currentSound[layer], -1, pow(vol, 3), FADE_TIME); m_currentLayerValues[layer].vol = vol; } diff --git a/src/miniaudioengine.h b/src/miniaudioengine.h index b55311b..d908c70 100644 --- a/src/miniaudioengine.h +++ b/src/miniaudioengine.h @@ -1,21 +1,19 @@ #ifndef MINIAUDIOENGINE_H #define MINIAUDIOENGINE_H -#define MA_ENABLE_ONLY_SPECIFIC_BACKENDS 1 -#define MA_ENABLE_JACK 1 -#define MA_NO_GENERATION 1 -#define MA_DEBUG_OUTPUT 1 +#define MA_ENABLE_ONLY_SPECIFIC_BACKENDS +#define MA_ENABLE_JACK +#define MA_NO_GENERATION +#define MA_DEBUG_OUTPUT #define MA_DISABLE_PULSEAUDIO +#define MA_DEBUG_OUTPUT #define MINIAUDIO_IMPLEMENTATION #include "miniaudio.h" -#include "defines.h" // MAX_LAYERS +#include "ma_writer_node.h" #include using namespace std; +#include "defines.h" -/* Data Format */ -#define FORMAT ma_format_f32 /* Must always be f32. */ -#define CHANNELS 2 -#define SAMPLE_RATE 48000 typedef struct { @@ -31,12 +29,23 @@ typedef struct ma_hishelf_node hishelf; ma_hishelf_node_config hishelfConfig; ma_splitter_node output; + ma_writer_node send1; + ma_data_source_node return1; + ma_audio_buffer_ref supplyReturn1; } filterBank; +typedef struct +{ + ma_engine m_engine[MAX_AUDIODEVICES]; + filterBank m_filterBank[MAX_AUDIODEVICES][MAX_LAYERS]; + ma_writer_node m_sendAux1[MAX_LAYERS]; + ma_pcm_rb *aux1Buffer; +} audioObjects; class MiniAudioEngine { friend class libreMediaServerAudio; + static ma_pcm_rb *rb; public: MiniAudioEngine(); @@ -44,6 +53,7 @@ public: bool startEngine(uint layersQty); bool startDevice(uint *id, uint nb); static void audioDataCallback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount); + static void audioDataCallback1(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount); bool setBypass(int audioDevice, int layer, bool bypass); protected: @@ -76,6 +86,8 @@ private: filterBank m_filterBank[MAX_AUDIODEVICES][MAX_LAYERS]; ma_engine m_engine[MAX_AUDIODEVICES]; uint m_layersQty; + ma_writer_node m_sendToAux[MAX_LAYERS]; + audioObjects m_audioObjects; ma_result getAllAudioDevices(); ma_result startContext(); From a935d4e619144c338bd5ec10f73c82a656776dbc Mon Sep 17 00:00:00 2001 From: snt Date: Sun, 19 May 2024 01:36:23 +0200 Subject: [PATCH 28/38] =?UTF-8?q?multi=20dispositivo=20con=20env=C3=ADo=20?= =?UTF-8?q?independientes=20por=20capa.=20est=C3=A1=20sucio=20con=20trozos?= =?UTF-8?q?=20sn=20usar=20y=20statics,=20pero=20funciona.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/LibreMediaServer_Audio.hed | 190 ++++++++++++++++---------------- src/libremediaserver-audio.cpp | 2 +- src/ma_writer_node.c | 17 +-- src/miniaudioengine.cpp | 24 +++- src/miniaudioengine.h | 1 + 5 files changed, 128 insertions(+), 106 deletions(-) diff --git a/docs/LibreMediaServer_Audio.hed b/docs/LibreMediaServer_Audio.hed index 1598cab..90b0781 100644 --- a/docs/LibreMediaServer_Audio.hed +++ b/docs/LibreMediaServer_Audio.hed @@ -1,93 +1,99 @@ ް׆ˌĠ򝤫鿰맫͓Ԕ -ҊÅЁ䍡 -ӽ -͌وГ -䩴؍ - - -ǁ - -ήƵ -ꂨ⑯ɞɅ -ˆ -㨥 -פ - -Θב -˻ - -ūî -䈍 -п -¥߸ڋ -Ĩ© -勌㊈ -͹ -ˢ݈ -ɢƢ -񍒍Й -˟ - -ǔ -檵 -Ӡ̧׀ -ꗪㄮϞѝ -ᯠ쫶 -ϼȡ嵶 -ᄨڍ֟ - -ڱ -񔸼񣏎 -ڔ - - -֐ - -̽ɤ -甯؞τ - - - - - - -𣇒 -Ԇ - -䷩ -ߍ - - - - - - - - - - -܂ -쏎 -מ - - -Ӽ - - - - - - - - - - -͚ʝǓ - - - -ݽ - - - - +ҊÅЁͽ +Щܰ +Χ +쏎՚ +ڥ +з +򗹡 +֐ +ᬡ䨳͘ +۽Ӣ + +ԙ + +ا࠭ +Ϡɮ +򃡺쏎 +Ē + +ջо +󕐙ޗ + +Ѵد߯ +ںԻϩ +񖝋ޕ +㓜 +Ѹڱ +ܯԕ + +ĉ +ⷶ +šҔ +㰽 +С׃ +苩ܑ +ٿ뺻 +ڧӴ̧Ӈ +똫䅭̝ԛʂ +껺 +۾ + +Κ + + +Dž +ꬡ˞“ +̾ +虤刬Ⓨ̀ +㨳 + + + +҂ + + +鱤 + +ً + +ۄ +í + + + + + + + + +𓎍 +̀ + +Řϒ +݁׺ + +րɂÈ +񺥤 +ଛ + +ܕ + + + + + +쏒 + + +稳 + + + + + +ٕ + + + + diff --git a/src/libremediaserver-audio.cpp b/src/libremediaserver-audio.cpp index 2916007..76f711d 100644 --- a/src/libremediaserver-audio.cpp +++ b/src/libremediaserver-audio.cpp @@ -140,7 +140,7 @@ void libreMediaServerAudio::dmxInput(int layer, int channel, int value) m_played.append(m_ola->getValue(layer, DMX_FILE)); } #endif - } else if (channel >= HP_FREQ && channel <= HIGH_GAIN) { + } else if (channel >= HP_FREQ) { m_mae.filterParamChanged(layer, m_dmxSettings.at(layer).audioDevice, channel, value); #ifndef NOGUI if (m_ui) { diff --git a/src/ma_writer_node.c b/src/ma_writer_node.c index e05d38d..f58881d 100644 --- a/src/ma_writer_node.c +++ b/src/ma_writer_node.c @@ -18,16 +18,15 @@ static void ma_writer_node_process_pcm_frames(ma_node* pNode, const float** ppFr ma_writer_node* pWriteNode = (ma_writer_node*)pNode; MA_ASSERT(pWriteNode != NULL); - MA_ASSERT(ma_node_get_input_bus_count(&pWriteNode->baseNode) == 1); + MA_ASSERT(ma_node_get_input_bus_count(&pWriteNode->baseNode) == 2); if (*pFrameCountIn > 0) { void *pWriteBuffer = NULL; ma_pcm_rb_acquire_write(pWriteNode->pBuffer, pFrameCountIn, &pWriteBuffer); if (pWriteBuffer != NULL) { - ma_copy_pcm_frames(pWriteBuffer, ppFramesIn[0], *pFrameCountIn, ma_format_f32, pWriteNode->channels); + ma_copy_pcm_frames(pWriteBuffer, ppFramesIn[1], *pFrameCountIn, ma_format_f32, pWriteNode->channels); ma_pcm_rb_commit_write(pWriteNode->pBuffer, *pFrameCountIn); } - //ma_silence_pcm_frames(ppFramesOut[0], *pFrameCountOut, ma_format_f32, pWriteNode->channels); } //*pFrameCountOut = 0; ma_copy_pcm_frames(ppFramesOut[0], ppFramesIn[0], *pFrameCountOut, ma_format_f32, pWriteNode->channels); @@ -37,7 +36,7 @@ static ma_node_vtable g_ma_writer_node_vtable = { ma_writer_node_process_pcm_frames, NULL, - 1, + 2, 1, 0 // MA_NODE_FLAG_CONTINUOUS_PROCESSING @@ -48,6 +47,8 @@ MA_API ma_result ma_writer_node_init(ma_node_graph* pNodeGraph, const ma_writer_ { ma_result result; ma_node_config baseConfig; + ma_uint32 inputChannels[2]; // Equal in size to the number of input channels specified in the vtable. + ma_uint32 outputChannels[1]; if (pWriteNode == NULL || pConfig == NULL || pConfig->pBuffer == NULL \ || (pConfig->channels > MA_MAX_NODE_BUS_COUNT) ) { @@ -55,11 +56,13 @@ MA_API ma_result ma_writer_node_init(ma_node_graph* pNodeGraph, const ma_writer_ } MA_ZERO_OBJECT(pWriteNode); - + inputChannels[0] = pConfig->channels; + inputChannels[1] = pConfig->channels; + outputChannels[0] = pConfig->channels; baseConfig = pConfig->nodeConfig; baseConfig.vtable = &g_ma_writer_node_vtable; - baseConfig.pInputChannels = &pConfig->channels; - baseConfig.pOutputChannels = &pConfig->channels; + baseConfig.pInputChannels = inputChannels; + baseConfig.pOutputChannels = outputChannels; result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pWriteNode->baseNode); if (result != MA_SUCCESS) { diff --git a/src/miniaudioengine.cpp b/src/miniaudioengine.cpp index 914708d..33c7840 100644 --- a/src/miniaudioengine.cpp +++ b/src/miniaudioengine.cpp @@ -183,13 +183,13 @@ ma_result MiniAudioEngine::createFilterBank(int id, uint layer) cout << "ERROR " << result << ": Failed to attach high shelf filter node." << endl; return result; } - if (id == 0) { - //result = ma_node_attach_output_bus(&fb->output, 1, endpoint, 0); +if (id == 0) { + result = ma_node_attach_output_bus(&fb->output, 0, &m_sendToAux[id], 0); if (result != MA_SUCCESS) { cout << "ERROR " << result << ": Failed to attach output node to engine." << endl; return result; } - result = ma_node_attach_output_bus(&fb->output, 0, &m_sendToAux[id], 0); + result = ma_node_attach_output_bus(&fb->output, 1, &m_sendToAux[id], 1); if (result != MA_SUCCESS) { cout << "ERROR " << result << ": Failed to attach output node to aux send 1." << endl; return result; @@ -204,7 +204,7 @@ ma_result MiniAudioEngine::setNodeGraph(int id) { if (id == 0) { ma_node_graph *ng = ma_engine_get_node_graph(&m_engine[id]); - size_t sizeInFrames = SAMPLE_RATE / 10; // ma_get_bytes_per_frame(FORMAT, CHANNELS); + size_t sizeInFrames = SAMPLE_RATE; // ma_get_bytes_per_frame(FORMAT, CHANNELS); result = ma_pcm_rb_init(FORMAT, CHANNELS, sizeInFrames, NULL, NULL, &aux1Buffer); if (result != MA_SUCCESS) { printf("Failed to initialize ring buffer.\n"); @@ -623,8 +623,20 @@ ma_result MiniAudioEngine::filterParamChanged(int layer, int audioDevice, int ch cout << "ERROR " << result << ": Failed set gain high shelf filter node." << endl; return result; } - } - return (result); + } else if (channel == SEND1) { + ma_node_set_output_bus_volume(&fb->output, 0, pow((value / 255.0f), 2)); + if (result != MA_SUCCESS) { + cout << "ERROR " << result << ": Failed set Send 1 Volume." << endl; + return result; + } + } else if (channel == SEND2) { + ma_node_set_output_bus_volume(&fb->output, 1, pow((value / 255.0f), 2)); + if (result != MA_SUCCESS) { + cout << "ERROR " << result << ": Failed set Send 2 Volume." << endl; + } + return result; + } + return (result); } bool MiniAudioEngine::setBypass(int audioDevice, int layer, bool bypass) diff --git a/src/miniaudioengine.h b/src/miniaudioengine.h index d908c70..69147d3 100644 --- a/src/miniaudioengine.h +++ b/src/miniaudioengine.h @@ -7,6 +7,7 @@ #define MA_DEBUG_OUTPUT #define MA_DISABLE_PULSEAUDIO #define MA_DEBUG_OUTPUT +#define MA_LOG_LEVEL_DEBUG DEBUG #define MINIAUDIO_IMPLEMENTATION #include "miniaudio.h" #include "ma_writer_node.h" From 5d56921aeb3a6cfce9ba07b68b36658d95e86571 Mon Sep 17 00:00:00 2001 From: snt Date: Mon, 20 May 2024 19:00:05 +0200 Subject: [PATCH 29/38] =?UTF-8?q?refactorizado=20todo=20a=20una=20struct.?= =?UTF-8?q?=20Maximo=20de=20dispositivos=20MAX=5FAUDIODEVICES,=20sin=20tes?= =?UTF-8?q?tear=20mas=20de=20dos.=20Los=20devices=20auxiliares=20leen=20de?= =?UTF-8?q?=20data=20source=20rb=20en=20vez=20de=20en=20el=20callback.=20l?= =?UTF-8?q?a=20idea=20del=20nodegraph=20funcionando=20en=20una=20engine=20?= =?UTF-8?q?dummy=20no=20ha=20funcionado,=20pero=20puede=20que=20fuera=20po?= =?UTF-8?q?r=20la=20refactorizaci=C3=B3n=20y=20la=20introducci=C3=B3n=20de?= =?UTF-8?q?=20las=20data=20sources=20rb.=20Ahora=20que=20est=C3=A1=20todo?= =?UTF-8?q?=20m=C3=A1s=20ordenado=20se=20puede=20volver=20a=20intentar.=20?= =?UTF-8?q?Pero=20tampoco=20merece=20mucho=20la=20pena,=20la=20principal?= =?UTF-8?q?=20diferencia=20era=20el=20master=20bus,=20pero=20se=20puede=20?= =?UTF-8?q?atacar=20la=20salida=20de=20auxNode[0]=20a=20una=20capa=20de=20?= =?UTF-8?q?master=20en=20vez=20de=20al=20endpoint=20directamente.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/libremediaserver-audio.cpp | 7 +- src/ma_writer_node.c | 1 - src/miniaudioengine.cpp | 415 ++++++++++++++++----------------- src/miniaudioengine.h | 61 +++-- 4 files changed, 234 insertions(+), 250 deletions(-) diff --git a/src/libremediaserver-audio.cpp b/src/libremediaserver-audio.cpp index 76f711d..72398ee 100644 --- a/src/libremediaserver-audio.cpp +++ b/src/libremediaserver-audio.cpp @@ -40,15 +40,10 @@ libreMediaServerAudio::libreMediaServerAudio() m_updateUi[i][3] = -1; #endif } - if (!m_mae.startEngine(m_layersQty)) { + if (!m_mae.startEngine(m_layersQty, m_settings->getAudioDeviceId(), m_settings->getAudioDeviceQty())) { cout << "Can not start Audio Engine!" << endl; exit(-1); } - uint *audioDevList = m_settings->getAudioDeviceId(); - if (!m_mae.startDevice(audioDevList, m_settings->getAudioDeviceQty())) { - cout << "Can not start Audio Device!" << audioDevList << endl; - exit(-1); - } m_ola = new olaThread(this, m_layersQty); Q_CHECK_PTR(m_ola); m_ola->blockSignals(true); diff --git a/src/ma_writer_node.c b/src/ma_writer_node.c index f58881d..82e057c 100644 --- a/src/ma_writer_node.c +++ b/src/ma_writer_node.c @@ -28,7 +28,6 @@ static void ma_writer_node_process_pcm_frames(ma_node* pNode, const float** ppFr ma_pcm_rb_commit_write(pWriteNode->pBuffer, *pFrameCountIn); } } - //*pFrameCountOut = 0; ma_copy_pcm_frames(ppFramesOut[0], ppFramesIn[0], *pFrameCountOut, ma_format_f32, pWriteNode->channels); } diff --git a/src/miniaudioengine.cpp b/src/miniaudioengine.cpp index 33c7840..9b2f64a 100644 --- a/src/miniaudioengine.cpp +++ b/src/miniaudioengine.cpp @@ -4,10 +4,6 @@ #define BIAS 0.99f #define FILTER_ORDER 3 -static ma_pcm_rb aux1Buffer; -static ma_data_source_node g_dataSupplyNode; -static ma_data_source_rb g_dataSourceRB; - MiniAudioEngine::MiniAudioEngine() {} void MiniAudioEngine::audioDataCallback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) @@ -21,83 +17,78 @@ void MiniAudioEngine::audioDataCallback(ma_device* pDevice, void* pOutput, const (void)pInput; } -void MiniAudioEngine::audioDataCallback1(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) -{ - (void)pDevice; - ma_result result; - - ma_uint32 pcmFramesAvailableInRB = 0; - ma_uint32 pcmFramesProcessed = 0; - while (pcmFramesProcessed < frameCount) { - pcmFramesAvailableInRB = ma_pcm_rb_available_read(&aux1Buffer); - if (pcmFramesAvailableInRB == 0) { - break; - } - ma_uint32 framesToRead = frameCount - pcmFramesProcessed; - if (framesToRead > pcmFramesAvailableInRB) { - framesToRead = pcmFramesAvailableInRB; - } - void* pReadBuffer = NULL; - ma_pcm_rb_acquire_read(&aux1Buffer, &framesToRead, &pReadBuffer); - if (pReadBuffer != NULL) { - ma_copy_pcm_frames(pOutput, pReadBuffer, framesToRead, FORMAT, CHANNELS); - ma_pcm_rb_commit_read(&aux1Buffer, framesToRead); - pcmFramesProcessed += framesToRead; - }/* else { break; }*/ - } - (void)pInput; -} - void MiniAudioEngine::stopEngine() { - for (uint i = 0; i < m_layersQty; i++) { - ma_sound_uninit(&m_currentSound[i]); + for (uint i = 0; i < m_mae.layersQty; i++) { + if (m_mae.mediaLoaded[i]) + ma_sound_uninit(&m_mae.sounds[i]); } - for (uint i = 0; i < m_devicesSelected; i++) { - for (uint j = 0; j < m_layersQty; j++) { - ma_node_uninit(&m_filterBank[i][j].input, NULL); - ma_hpf_node_uninit(&m_filterBank[i][j].hpf, NULL); - ma_loshelf_node_uninit(&m_filterBank[i][j].loshelf, NULL); - ma_peak_node_uninit(&m_filterBank[i][j].mLow, NULL); - ma_peak_node_uninit(&m_filterBank[i][j].mHigh, NULL); - ma_hishelf_node_uninit(&m_filterBank[i][j].hishelf, NULL); - ma_splitter_node_uninit(&m_filterBank[i][j].output, NULL); + for (uint i = 0; i < m_mae.layersQty; i++) { + ma_node_uninit(&m_mae.filters[i].input, NULL); + ma_hpf_node_uninit(&m_mae.filters[i].hpf, NULL); + ma_loshelf_node_uninit(&m_mae.filters[i].loshelf, NULL); + ma_peak_node_uninit(&m_mae.filters[i].mLow, NULL); + ma_peak_node_uninit(&m_mae.filters[i].mHigh, NULL); + ma_hishelf_node_uninit(&m_mae.filters[i].hishelf, NULL); + ma_splitter_node_uninit(&m_mae.filters[i].output, NULL); + } + for (uint i = 0; i < m_mae.audioDevicesQty; i++) { + if (i > 0) { + ma_writer_node_uninit(&m_mae.sendAuxNode[i], NULL); + ma_pcm_rb_uninit(&m_mae.auxBuffers[i]); } - //ma_writer_node_uninit(&m_sendToAux[0], NULL); - //ma_pcm_rb_uninit(&aux1Buffer); - ma_engine_uninit(&m_engine[i]); - ma_device_uninit(&m_device[i]); + ma_engine_uninit(&m_mae.engines[i]); + ma_device_uninit(&m_mae.devices[i]); } - ma_context_uninit(&m_context); - ma_resource_manager_uninit(&m_resourceManager); + ma_context_uninit(&m_mae.context); + ma_resource_manager_uninit(&m_mae.resourceManager); } -bool MiniAudioEngine::startEngine(uint layers) +bool MiniAudioEngine::startEngine(uint layers, uint* audioDevicesId, uint audioDevicesQty) { ma_result result; - m_layersQty = layers; - for (uint i =0; i < m_layersQty; 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; + m_mae.layersQty = layers; + m_mae.audioDevicesId = audioDevicesId; + m_mae.audioDevicesQty = audioDevicesQty; + for (uint i =0; i < m_mae.layersQty; i++) { + m_mae.mediaLoaded[i] = false; + m_mae.currentStatus[i].status = Status::Iddle; + m_mae.currentStatus[i].pan = 128; + m_mae.currentStatus[i].pitch = 128; + m_mae.currentStatus[i].vol = 0; + m_mae.currentStatus[i].cursor = 0; } result = this->startContext(); if (result != MA_SUCCESS) return false; result = this->getAllAudioDevices(); if (result != MA_SUCCESS) return false; + result = this->startDevices(); + if (result != MA_SUCCESS) { + cout << "Error " << result << ": Failed start audio devices." << endl; + return false; + } + result = this->setNodeGraph(); + if (result != MA_SUCCESS) { + cout << "Error " << result << ": Failed to set node graph." << endl; + return false; + } + for (uint i = 0; i < m_mae.audioDevicesQty; i++) { + result = ma_engine_start(&m_mae.engines[i]); + if (result != MA_SUCCESS) { + cout << "Error " << result << ": Failed to start audio device" << m_mae.audioDevicesId[i] << endl; + return false; + } + } return true; } -ma_result MiniAudioEngine::createFilterBank(int id, uint layer) +ma_result MiniAudioEngine::createFilterBank(uint layer) { ma_result result; - ma_node_graph *ng = ma_engine_get_node_graph(&m_engine[id]); - ma_node *endpoint = ma_engine_get_endpoint(&m_engine[id]); - filterBank *fb = &m_filterBank[id][layer]; + ma_node_graph *ng = ma_engine_get_node_graph(&m_mae.engines[0]); + ma_node *endpoint = ma_engine_get_endpoint(&m_mae.engines[0]); + filterBank *fb = &m_mae.filters[layer]; ma_splitter_node_config splitterConfig = ma_splitter_node_config_init(CHANNELS); result = ma_splitter_node_init(ng, &splitterConfig, NULL, &fb->input); @@ -140,13 +131,12 @@ ma_result MiniAudioEngine::createFilterBank(int id, uint layer) cout << "ERROR " << result << ": Failed to init hi shelf filter node." << endl; return result; } - + splitterConfig.outputBusCount = m_mae.audioDevicesQty; result = ma_splitter_node_init(ng, &splitterConfig, NULL, &fb->output); if (result != MA_SUCCESS) { cout << "ERROR " << result << ": Failed to init output node." << endl; return result; } - result = ma_node_attach_output_bus(&fb->input, 0, &fb->hpf, 0); if (result != MA_SUCCESS) { cout << "ERROR " << result << ": Failed to attach input node." << endl; @@ -183,137 +173,143 @@ ma_result MiniAudioEngine::createFilterBank(int id, uint layer) cout << "ERROR " << result << ": Failed to attach high shelf filter node." << endl; return result; } -if (id == 0) { - result = ma_node_attach_output_bus(&fb->output, 0, &m_sendToAux[id], 0); + if (m_mae.audioDevicesQty == 1) { + result = ma_node_attach_output_bus(&fb->output, 0, endpoint, 0); if (result != MA_SUCCESS) { - cout << "ERROR " << result << ": Failed to attach output node to engine." << endl; + cout << "ERROR " << result << ": Failed to attach output to endpoint." << endl; return result; } - result = ma_node_attach_output_bus(&fb->output, 1, &m_sendToAux[id], 1); + } else { + result = ma_node_attach_output_bus(&fb->output, 0, &m_mae.sendAuxNode[1], 0); if (result != MA_SUCCESS) { cout << "ERROR " << result << ": Failed to attach output node to aux send 1." << endl; return result; } + result = ma_node_attach_output_bus(&fb->output, 1, &m_mae.sendAuxNode[1], 1); + if (result != MA_SUCCESS) { + cout << "ERROR " << result << ": Failed to attach output node to aux send 1." << endl; + return result; + } + for (uint i = 2; i < m_mae.audioDevicesQty; i++) { + result = ma_node_attach_output_bus(&fb->output, i, &m_mae.sendAuxNode[i], 1); + if (result != MA_SUCCESS) { + cout << "ERROR " << result << ": Failed to attach output node to aux send 1." << endl; + return result; + } + } } return result; } -ma_result MiniAudioEngine::setNodeGraph(int id) { +ma_result MiniAudioEngine::setNodeGraph() { ma_result result = MA_SUCCESS; - uint i = 0; - if (id == 0) { - ma_node_graph *ng = ma_engine_get_node_graph(&m_engine[id]); - size_t sizeInFrames = SAMPLE_RATE; // ma_get_bytes_per_frame(FORMAT, CHANNELS); - result = ma_pcm_rb_init(FORMAT, CHANNELS, sizeInFrames, NULL, NULL, &aux1Buffer); - if (result != MA_SUCCESS) { - printf("Failed to initialize ring buffer.\n"); - return result; - } - ma_silence_pcm_frames(aux1Buffer.rb.pBuffer, sizeInFrames, FORMAT, CHANNELS); - ma_writer_node_config writerConfig = ma_writer_node_config_init(CHANNELS, SAMPLE_RATE * 5, &aux1Buffer); - result = ma_writer_node_init(ng, &writerConfig, NULL, &m_sendToAux[id]); - if (result != MA_SUCCESS) { - cout << "ERROR " << result << ": Failed to init writer node." << endl; - return result; - } - result = ma_node_attach_output_bus(&m_sendToAux[id], 0, ma_engine_get_endpoint(&m_engine[id]), 0); // Pull API - if (result != MA_SUCCESS) { - cout << "ERROR " << result << ": Failed to attach writer node." << endl; - return result; - } + ma_node_graph *ng = ma_engine_get_node_graph(&m_mae.engines[0]); + for (uint i = 1; i < m_mae.audioDevicesQty; i++) { + size_t sizeInFrames = SAMPLE_RATE; + result = ma_pcm_rb_init(FORMAT, CHANNELS, sizeInFrames, NULL, NULL, &m_mae.auxBuffers[i]); + if (result != MA_SUCCESS) { + printf("Failed to initialize ring buffer.\n"); + return result; + } + ma_silence_pcm_frames(m_mae.auxBuffers[i].rb.pBuffer, sizeInFrames, FORMAT, CHANNELS); + ma_writer_node_config writerConfig = ma_writer_node_config_init(CHANNELS, SAMPLE_RATE * 5, &m_mae.auxBuffers[i]); + result = ma_writer_node_init(ng, &writerConfig, NULL, &m_mae.sendAuxNode[i]); + if (result != MA_SUCCESS) { + cout << "ERROR " << result << ": Failed to init writer node." << endl; + return result; + } + // esto va a dar problemas al sumar en el envío 0 una vez por cad envío extra. + // writer_node puede ser silencioso + // así ya estamos en el caso de disparar varios bang por cada envío en el mismo nodegraph + // es mejor que writer node tenga varias entradas, una por cada envío + // que se dispara con un único engine y proporciona un único stream de audio de vuelta a ese engine + // en vez de un puntero hay que pasarle un array de rb + result = ma_node_attach_output_bus(&m_mae.sendAuxNode[i], 0, ma_engine_get_endpoint(&m_mae.engines[0]), 0); + if (result != MA_SUCCESS) { + cout << "ERROR " << result << ": Failed to attach writer node." << endl; + return result; + } + result = ma_data_source_rb_init(&m_mae.dataSourceRB[i], &m_mae.auxBuffers[i]); + if (result != MA_SUCCESS) { + cout << "Error " << result << ": Failed to init data source ring buffer" << endl; + return result; + } + ma_data_source_node_config dataSupplyNodeConfig = ma_data_source_node_config_init(&m_mae.dataSourceRB[i]); + result = ma_data_source_node_init(ma_engine_get_node_graph(&m_mae.engines[i]), &dataSupplyNodeConfig, NULL, &m_mae.dataSupplyNode[i]); + if (result != MA_SUCCESS) { + cout << "Error " << result << ": Failed to init data source node" << endl; + return result; + } + result = ma_node_attach_output_bus(&m_mae.dataSupplyNode[i], 0, ma_engine_get_endpoint(&m_mae.engines[i]), 0); + if (result != MA_SUCCESS) { + cout << "Error " << result << ": Failed to attach data source rb node" << endl; + return result; + } } - while (result == MA_SUCCESS && i < m_layersQty) { - result = this->createFilterBank(id, i); - i++; + for (uint i = 0; i < m_mae.layersQty; i++) { + result = this->createFilterBank(i); + if (result != MA_SUCCESS) { + cout << "ERROR " << result << ": Failed creating filter bank." << endl; + } } return (result); } -bool MiniAudioEngine::startDevice(uint *systemId, uint nb) +ma_result MiniAudioEngine::startDevices() { ma_result result = MA_SUCCESS; ma_device_config deviceConfig; ma_engine_config engineConfig; - m_devicesSelected = nb; - for (uint internalId = 0; internalId < nb; internalId++) { - deviceConfig = ma_device_config_init(ma_device_type_duplex); - deviceConfig.capture.pDeviceID = &m_pPlaybackDeviceInfos[systemId[internalId]].id; - deviceConfig.capture.format = m_resourceManager.config.decodedFormat; - deviceConfig.capture.channels = CHANNELS; - deviceConfig.capture.shareMode = ma_share_mode_shared; - deviceConfig.playback.pDeviceID = &m_pPlaybackDeviceInfos[systemId[internalId]].id; - deviceConfig.playback.format = m_resourceManager.config.decodedFormat; - deviceConfig.playback.channels = CHANNELS; - deviceConfig.sampleRate = m_resourceManager.config.decodedSampleRate; - if (internalId == 0) - deviceConfig.dataCallback = audioDataCallback; - else if (internalId == 1) - deviceConfig.dataCallback = audioDataCallback1; - deviceConfig.pUserData = &m_engine[internalId]; - result = ma_device_init(&m_context, &deviceConfig, &m_device[internalId]); + deviceConfig = ma_device_config_init(ma_device_type_duplex); + deviceConfig.capture.format = m_mae.resourceManager.config.decodedFormat; + deviceConfig.capture.channels = CHANNELS; + deviceConfig.playback.channels = CHANNELS; + deviceConfig.capture.shareMode = ma_share_mode_shared; + deviceConfig.playback.format = m_mae.resourceManager.config.decodedFormat; + deviceConfig.sampleRate = m_mae.resourceManager.config.decodedSampleRate; + deviceConfig.dataCallback = audioDataCallback; + engineConfig = ma_engine_config_init(); + engineConfig.pResourceManager = &m_mae.resourceManager; + engineConfig.gainSmoothTimeInMilliseconds = SAMPLE_RATE / 25; + engineConfig.noAutoStart = MA_TRUE; + + for (uint internalId = 0; internalId < m_mae.audioDevicesQty; internalId++) { + deviceConfig.capture.pDeviceID = &m_mae.pPlaybackDeviceInfos[m_mae.audioDevicesId[internalId]].id; + deviceConfig.playback.pDeviceID = &m_mae.pPlaybackDeviceInfos[m_mae.audioDevicesId[internalId]].id; + deviceConfig.pUserData = &m_mae.engines[internalId]; + result = ma_device_init(&m_mae.context, &deviceConfig, &m_mae.devices[internalId]); if (result != MA_SUCCESS) { - cout << "Error " << result << ": Failed to initialize audio device " << m_pPlaybackDeviceInfos[*systemId].name << endl; - return false; + cout << "Error " << result << ": Failed to initialize audio device " << m_mae.pPlaybackDeviceInfos[m_mae.audioDevicesId[internalId]].name << endl; + return result; } - engineConfig = ma_engine_config_init(); - engineConfig.pDevice = &m_device[internalId]; - engineConfig.pResourceManager = &m_resourceManager; - engineConfig.gainSmoothTimeInMilliseconds = SAMPLE_RATE / 100; - engineConfig.noAutoStart = MA_TRUE; - result = ma_engine_init(&engineConfig, &m_engine[internalId]); + engineConfig.pDevice = &m_mae.devices[internalId]; + result = ma_engine_init(&engineConfig, &m_mae.engines[internalId]); if (result != MA_SUCCESS) { cout << "Error " << result << ": Failed to initialize audio engine" << endl; - return false; + return result; } - result = this->setNodeGraph(internalId); - if (result != MA_SUCCESS) { - cout << "Error " << result << ": Failed to set node graph " << systemId[internalId] << endl; - return false; - } - result = ma_engine_start(&m_engine[internalId]); - if (result != MA_SUCCESS) { - cout << "Error " << result << ": Failed to start audio engine" << systemId[internalId] << endl; - return false; - } - cout << "Initialized Audio Device. internalId: " << internalId << " systemId: " << systemId[internalId] << " " << m_pPlaybackDeviceInfos[systemId[internalId]].name << endl; + cout << "Initialized Audio Device. internalId: " << internalId << " systemId: " << m_mae.audioDevicesId[internalId] << " " << m_mae.pPlaybackDeviceInfos[m_mae.audioDevicesId[internalId]].name << endl; } - //result = ma_data_source_rb_init(&g_dataSourceRB, &aux1Buffer); - if (result != MA_SUCCESS) { - cout << "Error " << result << ": Failed to init data source ring buffer" << endl; - return false; - } - ma_data_source_node_config dataSupplyNodeConfig = ma_data_source_node_config_init(&g_dataSourceRB); - //result = ma_data_source_node_init(ma_engine_get_node_graph(&m_engine[1]), &dataSupplyNodeConfig, NULL, &g_dataSupplyNode); - if (result != MA_SUCCESS) { - cout << "Error " << result << ": Failed to init data source node" << endl; - return false; - } - //result = ma_node_attach_output_bus(&g_dataSupplyNode, 0, ma_engine_get_endpoint(&m_engine[1]), 0); - if (result != MA_SUCCESS) { - cout << "Error " << result << ": Failed to attach data source rb node" << endl; - return false; - } - cout << "data source node state " << ma_node_get_state(&g_dataSupplyNode.base) << endl; - return true; + return result; } ma_result MiniAudioEngine::startContext() { ma_result result; - m_resourceManagerConfig = ma_resource_manager_config_init(); - m_resourceManagerConfig.decodedFormat = FORMAT; - m_resourceManagerConfig.decodedChannels = CHANNELS; - m_resourceManagerConfig.decodedSampleRate = SAMPLE_RATE; - m_resourceManagerConfig.jobThreadCount = 4; - result = ma_resource_manager_init(&m_resourceManagerConfig, &m_resourceManager); + ma_resource_manager_config resourceManagerConfig = ma_resource_manager_config_init(); + resourceManagerConfig.decodedFormat = FORMAT; + resourceManagerConfig.decodedChannels = CHANNELS; + resourceManagerConfig.decodedSampleRate = SAMPLE_RATE; + resourceManagerConfig.jobThreadCount = MAX_LAYERS; + result = ma_resource_manager_init(&resourceManagerConfig, &m_mae.resourceManager); if (result != MA_SUCCESS) { cout << "Error " << result << ": Failed to initialize audio resource manager." << endl; return result; } - result = ma_context_init(NULL, 0, NULL, &m_context); + result = ma_context_init(NULL, 0, NULL, &m_mae.context); if (result != MA_SUCCESS) { cout << "Error " << result << ": Failed to initialize audio context." << endl; } @@ -325,15 +321,15 @@ ma_result MiniAudioEngine::getAllAudioDevices() { ma_result result; - result = ma_context_get_devices(&m_context, &m_pPlaybackDeviceInfos, &m_playbackDeviceCount, NULL, NULL); + result = ma_context_get_devices(&m_mae.context, &m_mae.pPlaybackDeviceInfos, &m_mae.playbackDeviceCount, NULL, NULL); if (result != MA_SUCCESS) { cout << "Error " << result << ": Failed to enumerate playback devices." << endl; - ma_context_uninit(&m_context); + ma_context_uninit(&m_mae.context); return result; } cout << "Audio devices available:" << endl; - for (ma_uint32 iAvailableDevice = 0; iAvailableDevice < m_playbackDeviceCount; iAvailableDevice += 1) { - cout << iAvailableDevice << " : " << m_pPlaybackDeviceInfos[iAvailableDevice].name << endl; + for (ma_uint32 iAvailableDevice = 0; iAvailableDevice < m_mae.playbackDeviceCount; iAvailableDevice += 1) { + cout << iAvailableDevice << " : " << m_mae.pPlaybackDeviceInfos[iAvailableDevice].name << endl; } return result; } @@ -342,28 +338,26 @@ ma_result MiniAudioEngine::loadMedia(int layer, char *file, uint audioDevice) { ma_result result; - // - iniciar un sonido por cada capa, copiar la capa en otro dispositivo - // - writer node y source con el buffer escrito en el otro device - if (m_mediaLoaded[layer] == true) + if (m_mae.mediaLoaded[layer] == true) { - ma_sound_uninit(&m_currentSound[layer]); - m_mediaLoaded[layer] = false; + ma_sound_uninit(&m_mae.sounds[layer]); + m_mae.mediaLoaded[layer] = false; } - result = ma_sound_init_from_file(&m_engine[audioDevice], file, \ + result = ma_sound_init_from_file(&m_mae.engines[0], file, \ MA_SOUND_FLAG_NO_SPATIALIZATION \ - , NULL, NULL, &m_currentSound[layer]); + , NULL, NULL, &m_mae.sounds[layer]); if (result != MA_SUCCESS) { cout << "Error " << result << ": Failed to load file " << file << endl; return result; } - result = ma_node_attach_output_bus(&m_currentSound[layer], 0, &m_filterBank[audioDevice][layer].input, 0); + result = ma_node_attach_output_bus(&m_mae.sounds[layer], 0, &m_mae.filters[layer].input, 0); if (result != MA_SUCCESS) { cout << "Error " << result << ": Failed to attach sound output bus " << audioDevice << endl; - //return result; + return result; } - m_mediaLoaded[layer] = true; + m_mae.mediaLoaded[layer] = true; this->refreshValues(layer); - m_currentLayerValues[layer].media = file; + m_mae.currentStatus[layer].media = file; return result; } @@ -372,9 +366,9 @@ float MiniAudioEngine::getDuration(int layer) ma_result result; float ret; - if (m_mediaLoaded[layer] == false) + if (m_mae.mediaLoaded[layer] == false) return MA_DOES_NOT_EXIST; - result = ma_sound_get_length_in_seconds(&m_currentSound[layer], &ret); + result = ma_sound_get_length_in_seconds(&m_mae.sounds[layer], &ret); if (result != MA_SUCCESS) { return result; } @@ -386,9 +380,9 @@ float MiniAudioEngine::getCursor(int layer) ma_result result; float ret = 0; - if (m_mediaLoaded[layer] == false) + if (m_mae.mediaLoaded[layer] == false) return MA_DOES_NOT_EXIST; - result = ma_sound_get_cursor_in_seconds(&m_currentSound[layer], &ret); + result = ma_sound_get_cursor_in_seconds(&m_mae.sounds[layer], &ret); if (result != MA_SUCCESS) { cout << "Error" << result << ": Can not get cursor " << layer << endl; @@ -403,16 +397,16 @@ ma_result MiniAudioEngine::printFormatInfo(int layer) ma_uint32 channels; ma_uint32 sampleRate; - if (m_mediaLoaded[layer] == false) + if (m_mae.mediaLoaded[layer] == false) return MA_DOES_NOT_EXIST; - ma_result result = ma_sound_get_data_format(&m_currentSound[layer], \ + ma_result result = ma_sound_get_data_format(&m_mae.sounds[layer], \ &format, &channels, &sampleRate, NULL, 0); if (result != MA_SUCCESS) { cout << "Error " << result << ": Failed to get data format " << layer; cout << endl; } else { cout << "Layer:" << layer << " "; - cout << m_currentLayerValues[layer].media.toLatin1().data(); + cout << m_mae.currentStatus[layer].media.toLatin1().data(); cout << " samples/sec:" << sampleRate << " format:" << format; cout << " channels:" << channels << endl; } @@ -422,50 +416,50 @@ ma_result MiniAudioEngine::printFormatInfo(int layer) // Expects between 0 and 1 vol value void MiniAudioEngine::volChanged(int layer, float vol) { - if (m_mediaLoaded[layer] == false) + if (m_mae.mediaLoaded[layer] == false) return; if (vol >= 1) vol = 0.99f; - ma_sound_group_set_fade_in_milliseconds(&m_currentSound[layer], -1, pow(vol, 3), FADE_TIME); - m_currentLayerValues[layer].vol = vol; + ma_sound_group_set_fade_in_milliseconds(&m_mae.sounds[layer], -1, pow(vol, 3), FADE_TIME); + m_mae.currentStatus[layer].vol = vol; } void MiniAudioEngine::panChanged(int layer, float value) { float result; - if (m_mediaLoaded[layer] == false) + if (m_mae.mediaLoaded[layer] == false) return; result = (value / 128.0) - 1.0; - ma_sound_group_set_pan(&m_currentSound[layer], result); - m_currentLayerValues[layer].pan = value; + ma_sound_group_set_pan(&m_mae.sounds[layer], result); + m_mae.currentStatus[layer].pan = value; } void MiniAudioEngine::pitchChanged(int layer, float value) { float pitch; - if (m_mediaLoaded[layer] == false) + if (m_mae.mediaLoaded[layer] == false) return; pitch = value / 128.0; - ma_sound_group_set_pitch(&m_currentSound[layer], pitch); - m_currentLayerValues[layer].pitch = value; + ma_sound_group_set_pitch(&m_mae.sounds[layer], pitch); + m_mae.currentStatus[layer].pitch = value; } ma_result MiniAudioEngine::playbackChanged(int layer, Status status) { ma_result result = MA_SUCCESS; - if (m_mediaLoaded[layer] == false) + if (m_mae.mediaLoaded[layer] == false) return MA_DOES_NOT_EXIST; bool loop = false; switch (status) { case Status::Paused: - result = ma_sound_stop_with_fade_in_milliseconds(&m_currentSound[layer], FADE_TIME); + result = ma_sound_stop_with_fade_in_milliseconds(&m_mae.sounds[layer], FADE_TIME); break; case Status::Stopped: - ma_sound_stop_with_fade_in_milliseconds(&m_currentSound[layer], FADE_TIME); - result = this->seekToCursor(layer, m_currentLayerValues[layer].cursor); + ma_sound_stop_with_fade_in_milliseconds(&m_mae.sounds[layer], FADE_TIME); + result = this->seekToCursor(layer, m_mae.currentStatus[layer].cursor); break; case Status::PlayingLoop: loop = true; @@ -473,18 +467,18 @@ ma_result MiniAudioEngine::playbackChanged(int layer, Status status) 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], loop); - result = ma_sound_start(&m_currentSound[layer]); - if (m_currentLayerValues[layer].cursor != 0) { + ma_sound_set_stop_time_in_milliseconds(&m_mae.sounds[layer], ~(ma_uint64)0); + ma_sound_set_looping(&m_mae.sounds[layer], loop); + result = ma_sound_start(&m_mae.sounds[layer]); + if (m_mae.currentStatus[layer].cursor != 0) { usleep(1000 * 50); // Avoid small glitch at start, how to flush the cached buffers in audio pipe line? } - this->volChanged(layer, m_currentLayerValues[layer].vol); + this->volChanged(layer, m_mae.currentStatus[layer].vol); default: break; } if (result == MA_SUCCESS) - m_currentLayerValues[layer].status = status; + m_mae.currentStatus[layer].status = status; return result; } @@ -493,13 +487,13 @@ ma_result MiniAudioEngine::seekToCursor(int layer, int cursor) ma_result result = MA_SUCCESS; ma_uint64 end, start; - if (m_mediaLoaded[layer] == false) + if (m_mae.mediaLoaded[layer] == false) return MA_DOES_NOT_EXIST; - result = ma_sound_get_length_in_pcm_frames(&m_currentSound[layer], &end); + result = ma_sound_get_length_in_pcm_frames(&m_mae.sounds[layer], &end); if (result != MA_SUCCESS) { return result; } start = (cursor * end) / 65025; - result = ma_sound_seek_to_pcm_frame(&m_currentSound[layer], start); - //result = ma_data_source_set_loop_point_in_pcm_frames(&m_currentSound[layer], start, end); // this do nothing here, it must be done after set_looping or start? + result = ma_sound_seek_to_pcm_frame(&m_mae.sounds[layer], start); + //result = ma_data_source_set_loop_point_in_pcm_frames(&m_mae.sounds[layer], start, end); // this do nothing here, it must be done after set_looping or start? return (result); } @@ -507,30 +501,30 @@ ma_result MiniAudioEngine::setCursor(int layer, int cursor) { ma_result result = MA_SUCCESS; - m_currentLayerValues[layer].cursor = cursor; + m_mae.currentStatus[layer].cursor = cursor; result = this->seekToCursor(layer, cursor); return (result); } Status MiniAudioEngine::getStatus(int layer) { - return m_currentLayerValues[layer].status; + return m_mae.currentStatus[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); + this->seekToCursor(layer, m_mae.currentStatus[layer].cursor); + this->panChanged(layer, m_mae.currentStatus[layer].pan); + this->volChanged(layer, m_mae.currentStatus[layer].vol); + this->pitchChanged(layer, m_mae.currentStatus[layer].pitch); + this->playbackChanged(layer, m_mae.currentStatus[layer].status); } ma_result MiniAudioEngine::filterParamChanged(int layer, int audioDevice, int channel, int value) { + (void)audioDevice; ma_result result = MA_SUCCESS; - - filterBank *fb = &m_filterBank[audioDevice][layer]; + filterBank *fb = &m_mae.filters[layer]; if (channel == HP_FREQ) { fb->hpfConfig.hpf.cutoffFrequency = double((value * 1.31) + 16.0f); // 16 - 350 @@ -641,7 +635,8 @@ ma_result MiniAudioEngine::filterParamChanged(int layer, int audioDevice, int ch bool MiniAudioEngine::setBypass(int audioDevice, int layer, bool bypass) { - filterBank *fb = &m_filterBank[audioDevice][layer]; + (void)audioDevice; + filterBank *fb = &m_mae.filters[layer]; if (bypass) { ma_node_set_output_bus_volume(&fb->input, 1, 1.0f); diff --git a/src/miniaudioengine.h b/src/miniaudioengine.h index 69147d3..308706b 100644 --- a/src/miniaudioengine.h +++ b/src/miniaudioengine.h @@ -15,7 +15,6 @@ using namespace std; #include "defines.h" - typedef struct { ma_splitter_node input; @@ -30,32 +29,40 @@ typedef struct ma_hishelf_node hishelf; ma_hishelf_node_config hishelfConfig; ma_splitter_node output; - ma_writer_node send1; - ma_data_source_node return1; - ma_audio_buffer_ref supplyReturn1; } filterBank; typedef struct { - ma_engine m_engine[MAX_AUDIODEVICES]; - filterBank m_filterBank[MAX_AUDIODEVICES][MAX_LAYERS]; - ma_writer_node m_sendAux1[MAX_LAYERS]; - ma_pcm_rb *aux1Buffer; -} audioObjects; + ma_engine engines[MAX_AUDIODEVICES]; + ma_device devices[MAX_AUDIODEVICES]; + filterBank filters[MAX_LAYERS]; + ma_writer_node sendAuxNode[MAX_AUDIODEVICES]; + ma_pcm_rb auxBuffers[MAX_AUDIODEVICES]; + ma_node_graph ng; + layerData currentStatus[MAX_LAYERS]; + ma_sound sounds[MAX_LAYERS]; + ma_resource_manager resourceManager; + ma_context context; + ma_device_info* pPlaybackDeviceInfos; + ma_uint32 playbackDeviceCount; + ma_uint32 devicesSelected; + ma_bool8 mediaLoaded[MAX_LAYERS]; + uint layersQty; + uint *audioDevicesId; + uint audioDevicesQty; + ma_data_source_node dataSupplyNode[MAX_AUDIODEVICES]; + ma_data_source_rb dataSourceRB[MAX_AUDIODEVICES]; +} MAE; class MiniAudioEngine { friend class libreMediaServerAudio; - static ma_pcm_rb *rb; public: MiniAudioEngine(); void stopEngine(); - bool startEngine(uint layersQty); - bool startDevice(uint *id, uint nb); + bool startEngine(uint layersQty, uint* audioDevicesID, uint audioDevicesQty); static void audioDataCallback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount); - static void audioDataCallback1(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount); - bool setBypass(int audioDevice, int layer, bool bypass); protected: ma_result loadMedia(int layer, char *media, uint audioDevice); @@ -69,33 +76,21 @@ protected: 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; } + return ma_sound_get_volume(&m_mae.sounds[layer]); } + inline bool getAtEnd(int layer) { return m_mae.sounds[layer].atEnd; } ma_result filterParamChanged(int layer, int audioDevice, int channel, int value); + bool setBypass(int audioDevice, int layer, bool bypass); private: - ma_resource_manager_config m_resourceManagerConfig; - ma_resource_manager m_resourceManager; - ma_context m_context; - ma_device_info* m_pPlaybackDeviceInfos; - ma_uint32 m_playbackDeviceCount; - ma_uint32 m_devicesSelected; - ma_device m_device[MAX_AUDIODEVICES]; - ma_sound m_currentSound[MAX_LAYERS]; - ma_bool8 m_mediaLoaded[MAX_LAYERS]; - layerData m_currentLayerValues[MAX_LAYERS]; - filterBank m_filterBank[MAX_AUDIODEVICES][MAX_LAYERS]; - ma_engine m_engine[MAX_AUDIODEVICES]; - uint m_layersQty; - ma_writer_node m_sendToAux[MAX_LAYERS]; - audioObjects m_audioObjects; + MAE m_mae; + ma_result startDevices(); ma_result getAllAudioDevices(); ma_result startContext(); void refreshValues(int layer); ma_result seekToCursor(int layer, int cursor); - ma_result setNodeGraph(int id); - ma_result createFilterBank(int id, uint layer); + ma_result setNodeGraph(); + ma_result createFilterBank(uint layer); }; #endif // MINIAUDIOENGINE_H From f0f6e595fb0797e7eaa365b486a8ed64fbcc4674 Mon Sep 17 00:00:00 2001 From: snt Date: Mon, 20 May 2024 20:08:26 +0200 Subject: [PATCH 30/38] =?UTF-8?q?cambiada=20curva=20de=20volume=20de=20c?= =?UTF-8?q?=C3=BAbia=20a=20cuadr=C3=A1tica,=20en=20pruebas=20a=20ver=20si?= =?UTF-8?q?=20es=20suficiente.=20valuebox=20de=20volumen=20en=20decibelios?= =?UTF-8?q?.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/audiolayerwidget.cpp | 2 +- src/miniaudioengine.cpp | 2 +- src/slidergroup.cpp | 14 ++++++++++---- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/audiolayerwidget.cpp b/src/audiolayerwidget.cpp index c3b1f86..3528f50 100644 --- a/src/audiolayerwidget.cpp +++ b/src/audiolayerwidget.cpp @@ -82,7 +82,7 @@ AudioLayerWidget::AudioLayerWidget(QWidget *parent, int layer): m_pan = new SliderGroup("Pan", 0 , 255, 0, NULL); volumeBox->addWidget(m_pan); connect(m_pan, SIGNAL(valueChanged(int)), this, SLOT(panChanged(int))); - m_volume = new SliderGroup("Vol", 0 , 100, 2, NULL); + m_volume = new SliderGroup("Vol", 0, 100, 2, NULL); volumeBox->addWidget(m_volume); connect(m_volume, SIGNAL(valueChanged(int)), this, SLOT(volumeChanged(int))); volumeBox->setSpacing(0); diff --git a/src/miniaudioengine.cpp b/src/miniaudioengine.cpp index 9b2f64a..0599fcc 100644 --- a/src/miniaudioengine.cpp +++ b/src/miniaudioengine.cpp @@ -420,7 +420,7 @@ void MiniAudioEngine::volChanged(int layer, float vol) return; if (vol >= 1) vol = 0.99f; - ma_sound_group_set_fade_in_milliseconds(&m_mae.sounds[layer], -1, pow(vol, 3), FADE_TIME); + ma_sound_group_set_fade_in_milliseconds(&m_mae.sounds[layer], -1, pow(vol, 2), FADE_TIME); m_mae.currentStatus[layer].vol = vol; } diff --git a/src/slidergroup.cpp b/src/slidergroup.cpp index c8e267d..55bfcf7 100644 --- a/src/slidergroup.cpp +++ b/src/slidergroup.cpp @@ -1,5 +1,5 @@ #include "slidergroup.h" - +#include #include #include @@ -39,7 +39,10 @@ SliderGroup::SliderGroup(QString name, valueBox.setFocusPolicy(Qt::NoFocus); valueBox.setButtonSymbols(QAbstractSpinBox::NoButtons); valueBox.setMinimumWidth(50); - valueBox.setRange(min, max); + if (decimals) + valueBox.setRange(-100, 100); + else + valueBox.setRange(min, max); valueBox.setValue(0); valueBox.setDecimals(decimals); valueBox.setObjectName(name); @@ -51,7 +54,7 @@ SliderGroup::SliderGroup(QString name, layout->addWidget(&slider); layout->addWidget(&valueBox); this->setStyleSheet("border: 1px solid #5a4855;" - "width: 50px;" + "width: 60px;" "margin: 0px;" "background-color: #383034;" ); @@ -74,7 +77,10 @@ void SliderGroup::setValue(float value) valueBox.blockSignals(true); if (int(value) != slider.value()) slider.setValue(value); - valueBox.setValue(value); + if (valueBox.decimals()) + valueBox.setValue(value - 100.0f); + else + valueBox.setValue(value); slider.blockSignals(false); valueBox.blockSignals(false); } From 200dcf86d45e1d760368ebb9b772975da35cea19 Mon Sep 17 00:00:00 2001 From: snt Date: Tue, 21 May 2024 18:17:55 +0200 Subject: [PATCH 31/38] =?UTF-8?q?volumen=20y=20env=C3=ADos=20en=20logar?= =?UTF-8?q?=C3=ADtmico=20aut=C3=A9ntico,=20entre=20-85dB=20y=200.=20SLider?= =?UTF-8?q?s=20en=20UI=20para=20los=20env=C3=ADos=20(s=C3=B3lo=202,=20est?= =?UTF-8?q?=C3=A1ticos,=20hay=20que=20meter=20un=20bucle=20con=20el=20n?= =?UTF-8?q?=C3=BAmero=20de=20env=C3=ADos).?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/roadmap.txt | 4 +++ src/audiolayerwidget.cpp | 58 +++++++++++++++++++++++----------- src/audiolayerwidget.h | 4 +++ src/audiowidget.cpp | 9 +++--- src/defines.h | 10 ++++-- src/dmxPersonality.h | 2 +- src/filterbankwidget.cpp | 3 +- src/libremediaserver-audio.cpp | 14 +++++--- src/miniaudioengine.cpp | 27 ++++++++++++---- src/miniaudioengine.h | 2 +- src/slidergroup.cpp | 22 +++++++++---- 11 files changed, 108 insertions(+), 47 deletions(-) diff --git a/docs/roadmap.txt b/docs/roadmap.txt index 154fd78..9e5ec0c 100644 --- a/docs/roadmap.txt +++ b/docs/roadmap.txt @@ -46,6 +46,10 @@ v 0.2.1 - Hardening: check return errors, try/catch exceptions, i'm too happy.... - Tests: errors on wrong conf file. - Ui/Ux: seek cursor playback +- ampliar writer para recibir un número n de entradas y escribirlas cada una en un buffer v0.2.0: - Vumeter or indicator about audio output in layer and master, add to sliderGroup. + --> en master se puede hacer con jack, solo en capas. + --> en las capas, hace falta otro nodo atacado en los buses de input que analice la entrada. +- mostrad información de envíos y dispositivos en ui diff --git a/src/audiolayerwidget.cpp b/src/audiolayerwidget.cpp index 3528f50..f37b67d 100644 --- a/src/audiolayerwidget.cpp +++ b/src/audiolayerwidget.cpp @@ -1,4 +1,5 @@ #include "audiolayerwidget.h" +#include "dmxPersonality.h" #include #include @@ -11,7 +12,6 @@ AudioLayerWidget::AudioLayerWidget(QWidget *parent, int layer): { QVBoxLayout *layout = new QVBoxLayout; - QVBoxLayout *playback = new QVBoxLayout; m_folderValue = new ClickableLabel; m_folderValue->setMaximumWidth(300); m_folderValue->setAlignment(Qt::AlignLeft); @@ -19,7 +19,7 @@ AudioLayerWidget::AudioLayerWidget(QWidget *parent, int layer): "color: white;" "background-color: black;" ); - playback->addWidget(m_folderValue); + layout->addWidget(m_folderValue); m_fileValue = new ClickableLabel; connect(m_fileValue, SIGNAL(clicked()), this, SLOT(openMediaDialog())); connect(m_folderValue, SIGNAL(clicked()), this, SLOT(openMediaDialog())); @@ -29,10 +29,7 @@ AudioLayerWidget::AudioLayerWidget(QWidget *parent, int layer): "color: white;" "background-color: black;" ); - playback->addWidget(m_fileValue); - playback->setSpacing(0); - playback->setContentsMargins(0, 0, 0, 0); - layout->addLayout(playback); + layout->addWidget(m_fileValue); m_suspendResumeButton = new QPushButton(this); m_suspendResumeButton->setText(statusToString(Status::Iddle)); @@ -74,17 +71,23 @@ AudioLayerWidget::AudioLayerWidget(QWidget *parent, int layer): m_filterBank = new FilterBankWidget(this); connect(m_filterBank, SIGNAL(setBypass(bool)), this, SLOT(setBypass(bool))); layout->addWidget(m_filterBank); - - QVBoxLayout *volumeBox = new QVBoxLayout; m_pitch = new SliderGroup("Pitch", 0 , 255, 0, NULL); - volumeBox->addWidget(m_pitch); + layout->addWidget(m_pitch); connect(m_pitch, SIGNAL(valueChanged(int)), this, SLOT(pitchChanged(int))); m_pan = new SliderGroup("Pan", 0 , 255, 0, NULL); - volumeBox->addWidget(m_pan); + layout->addWidget(m_pan); connect(m_pan, SIGNAL(valueChanged(int)), this, SLOT(panChanged(int))); - m_volume = new SliderGroup("Vol", 0, 100, 2, NULL); + + QHBoxLayout *volumeBox = new QHBoxLayout; + m_volume = new SliderGroup("Vol", 0, 65535, 2, NULL); volumeBox->addWidget(m_volume); connect(m_volume, SIGNAL(valueChanged(int)), this, SLOT(volumeChanged(int))); + m_bus1 = new SliderGroup("Bus 1", 0, 65535, 2, NULL); + volumeBox->addWidget(m_bus1); + connect(m_bus1, SIGNAL(valueChanged(int)), this, SLOT(bus1VolumeChanged(int))); + m_bus2 = new SliderGroup("Bus 2", 0, 65535, 2, NULL); + volumeBox->addWidget(m_bus2); + connect(m_bus2, SIGNAL(valueChanged(int)), this, SLOT(bus2VolumeChanged(int))); volumeBox->setSpacing(0); volumeBox->setContentsMargins(0, 0, 0, 0); layout->addLayout(volumeBox); @@ -102,6 +105,16 @@ void AudioLayerWidget::volumeChanged(int value) emit(uiSliderChanged(m_layer, Slider::Volume, value)); } +void AudioLayerWidget::bus1VolumeChanged(int value) +{ + emit(uiSliderChanged(m_layer, Slider::Bus1, value)); +} + +void AudioLayerWidget::bus2VolumeChanged(int value) +{ + emit(uiSliderChanged(m_layer, Slider::Bus2, value)); +} + void AudioLayerWidget::panChanged(int value) { emit(uiSliderChanged(m_layer, Slider::Pan, value)); @@ -157,9 +170,9 @@ void AudioLayerWidget::openMediaDialog() // from DMX signals void AudioLayerWidget::setVol(float vol) { - m_volume->blockSignals(true); - m_volume->setValue(vol); - m_volume->blockSignals(false); + m_volume->blockSignals(true); + m_volume->setValue(vol); + m_volume->blockSignals(false); } void AudioLayerWidget::setPan(int pan) @@ -221,8 +234,17 @@ void AudioLayerWidget::setCurrentTime(float progress) void AudioLayerWidget::setFilterParam(int channel, int value) { - m_filterBank->blockSignals(true); - m_filterBank->setValue(channel, value); - m_filterBank->blockSignals(false); - + if (channel <= FILTER_BANK_GAIN - FILTER_CHANNELS){ + m_filterBank->blockSignals(true); + m_filterBank->setValue(channel, value); + m_filterBank->blockSignals(false); + } else if (channel == SEND1 - HP_FREQ) { + m_bus1->blockSignals(false); + m_bus1->setValue((value * 256) + 255); + m_bus1->blockSignals(true); + } else if (channel == SEND2 - HP_FREQ) { + m_bus2->blockSignals(false); + m_bus2->setValue(value * 256 + 255); + m_bus2->blockSignals(true); + } } diff --git a/src/audiolayerwidget.h b/src/audiolayerwidget.h index f3ba1e4..cb446a9 100644 --- a/src/audiolayerwidget.h +++ b/src/audiolayerwidget.h @@ -39,6 +39,8 @@ private: SliderGroup *m_volume; SliderGroup *m_pan; SliderGroup *m_pitch; + SliderGroup *m_bus1; + SliderGroup *m_bus2; QTimeEdit *m_progressTime; QTimeEdit *m_totalTimeValue; QProgressBar *m_progress; @@ -51,6 +53,8 @@ private slots: void openMediaDialog(); void toggleSuspendResume(); void volumeChanged(int vol); + void bus1VolumeChanged(int vol); + void bus2VolumeChanged(int vol); void panChanged(int pan); void pitchChanged(int pitch); void setBypass(bool value); diff --git a/src/audiowidget.cpp b/src/audiowidget.cpp index 2f3b634..da7c33f 100644 --- a/src/audiowidget.cpp +++ b/src/audiowidget.cpp @@ -1,6 +1,5 @@ #include "audiowidget.h" - -//#include +#include "dmxPersonality.h" AudioWidget::AudioWidget(QWidget *parent) : QWidget(parent) @@ -23,8 +22,8 @@ AudioWidget::AudioWidget(QWidget *parent) : for (int j = 0; j < FILTER_CHANNELS; j++) m_filtersUpdate[i][j] = -1; } - m_layout->setSpacing(0); - m_layout->setContentsMargins(1, 1, 1, 1); + m_layout->setSpacing(2); + m_layout->setContentsMargins(2, 2, 2, 2); setLayout(m_layout); m_refreshUi = new QTimer(this); connect(m_refreshUi, SIGNAL(timeout()), this, SLOT(refreshUi())); @@ -110,6 +109,6 @@ void AudioWidget::refreshUi() void AudioWidget::filterParamChanged(int layer, int channel, int value) { - m_filtersUpdate[layer][channel - 9] = value; + m_filtersUpdate[layer][channel - HP_FREQ] = value; m_layerUpdate[layer].updated = true; } diff --git a/src/defines.h b/src/defines.h index 5f73abe..8d81a59 100644 --- a/src/defines.h +++ b/src/defines.h @@ -10,9 +10,9 @@ #define FORMAT ma_format_f32 /* Must always be f32. */ #define CHANNELS 2 #define SAMPLE_RATE 48000 -#define UI_REFRESH_TIME 100 +#define UI_REFRESH_TIME 97 #define FADE_TIME 25 // DMX Frame time, 40 fps, avoid clicks -#define FILTER_CHANNELS 13 // number of dmx channels dedicated to filters by layer +#define FILTER_CHANNELS 16 // number of dmx channels dedicated to filters by layer struct dmxSetting { int address; @@ -38,7 +38,9 @@ enum Slider Volume, Pan, Pitch, - Bypass + Bypass, + Bus1, + Bus2 }; #ifdef __cplusplus @@ -71,6 +73,8 @@ struct layerData { int address; unsigned int universe; int device; + int bus1Vol; + int bus2Vol; }; #endif // __cplusplus #endif // DEFINES_H diff --git a/src/dmxPersonality.h b/src/dmxPersonality.h index 1da964a..d08671a 100644 --- a/src/dmxPersonality.h +++ b/src/dmxPersonality.h @@ -23,7 +23,7 @@ #define HIGH_FREQ 19 #define HIGH_Q 20 #define HIGH_GAIN 21 -#define FILTER_BANK_GAIN 22 // not implemented yet +#define FILTER_BANK_GAIN 22 #define SEND1 23 #define SEND2 24 #define LAYER_CHANNELS 25 diff --git a/src/filterbankwidget.cpp b/src/filterbankwidget.cpp index c9b107d..4742af2 100644 --- a/src/filterbankwidget.cpp +++ b/src/filterbankwidget.cpp @@ -86,7 +86,8 @@ void FilterBankWidget::setValue(int filter, int value) result = (double)( value / 32.0f) + 0.1f; } else if (channel == HIGH_GAIN) { result = (double)(value / 21.25) - 6.023528412f; - } + } else + result = (double)value; fb[filter]->setValue(result); } diff --git a/src/libremediaserver-audio.cpp b/src/libremediaserver-audio.cpp index 72398ee..07e3b0e 100644 --- a/src/libremediaserver-audio.cpp +++ b/src/libremediaserver-audio.cpp @@ -96,9 +96,8 @@ void libreMediaServerAudio::dmxInput(int layer, int channel, int value) 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; + m_mae.volChanged(layer, value); + m_updateUi[layer][0] = value; } else if (channel == PAN) { m_mae.panChanged(layer, value); m_updateUi[layer][1] = value; @@ -236,7 +235,7 @@ void libreMediaServerAudio::uiSliderChanged(int layer, Slider s, int value) { switch (s){ case Slider::Volume: - m_mae.volChanged(layer, float((value / 100.0f))); + m_mae.volChanged(layer, value); break; case Slider::Pan: m_mae.panChanged(layer, value); @@ -246,6 +245,13 @@ void libreMediaServerAudio::uiSliderChanged(int layer, Slider s, int value) break; case Slider::Bypass: m_mae.setBypass(m_dmxSettings.at(layer).audioDevice, layer, value); + break; + case Slider::Bus1: + m_mae.filterParamChanged(layer, m_dmxSettings.at(layer).audioDevice, SEND1, value / 255); + break; + case Slider::Bus2: + m_mae.filterParamChanged(layer, m_dmxSettings.at(layer).audioDevice, SEND2, value / 255); + break; } } diff --git a/src/miniaudioengine.cpp b/src/miniaudioengine.cpp index 0599fcc..328e738 100644 --- a/src/miniaudioengine.cpp +++ b/src/miniaudioengine.cpp @@ -413,14 +413,17 @@ ma_result MiniAudioEngine::printFormatInfo(int layer) return result; } -// Expects between 0 and 1 vol value -void MiniAudioEngine::volChanged(int layer, float vol) +// Expects between 0 and 65535 vol value +void MiniAudioEngine::volChanged(int layer, int vol) { if (m_mae.mediaLoaded[layer] == false) return; - if (vol >= 1) - vol = 0.99f; - ma_sound_group_set_fade_in_milliseconds(&m_mae.sounds[layer], -1, pow(vol, 2), FADE_TIME); + float db = ((float)vol / 771.0f) - 85.0f; + if (db <= -85.0f) { + db = 0; + } else + db = ma_volume_db_to_linear(db); + ma_sound_group_set_fade_in_milliseconds(&m_mae.sounds[layer], -1, db, FADE_TIME); m_mae.currentStatus[layer].vol = vol; } @@ -618,13 +621,23 @@ ma_result MiniAudioEngine::filterParamChanged(int layer, int audioDevice, int ch return result; } } else if (channel == SEND1) { - ma_node_set_output_bus_volume(&fb->output, 0, pow((value / 255.0f), 2)); + float db = ((float)value / 3.0f) - 85.0f; + if (db <= -85.0f) { + db = 0; + } else + db = ma_volume_db_to_linear(db); + ma_node_set_output_bus_volume(&fb->output, 0, db); if (result != MA_SUCCESS) { cout << "ERROR " << result << ": Failed set Send 1 Volume." << endl; return result; } } else if (channel == SEND2) { - ma_node_set_output_bus_volume(&fb->output, 1, pow((value / 255.0f), 2)); + float db = ((float)value / 3.0f) - 85.0f; + if (db <= -85.0f) { + db = 0; + } else + db = ma_volume_db_to_linear(db); + ma_node_set_output_bus_volume(&fb->output, 1, db); if (result != MA_SUCCESS) { cout << "ERROR " << result << ": Failed set Send 2 Volume." << endl; } diff --git a/src/miniaudioengine.h b/src/miniaudioengine.h index 308706b..870dc42 100644 --- a/src/miniaudioengine.h +++ b/src/miniaudioengine.h @@ -66,7 +66,7 @@ public: protected: ma_result loadMedia(int layer, char *media, uint audioDevice); - void volChanged(int layer, float vol); + void volChanged(int layer, int vol); void panChanged(int layer, float pan); void pitchChanged(int layer, float pitch); ma_result playbackChanged(int layer, Status status); diff --git a/src/slidergroup.cpp b/src/slidergroup.cpp index 55bfcf7..9eedb70 100644 --- a/src/slidergroup.cpp +++ b/src/slidergroup.cpp @@ -39,9 +39,10 @@ SliderGroup::SliderGroup(QString name, valueBox.setFocusPolicy(Qt::NoFocus); valueBox.setButtonSymbols(QAbstractSpinBox::NoButtons); valueBox.setMinimumWidth(50); - if (decimals) - valueBox.setRange(-100, 100); - else + if (decimals) { + valueBox.setRange(-84.0f, 0.0f); + valueBox.setSpecialValueText("-inf"); + } else valueBox.setRange(min, max); valueBox.setValue(0); valueBox.setDecimals(decimals); @@ -66,20 +67,27 @@ SliderGroup::SliderGroup(QString name, void SliderGroup::sliderValueChanged(int value) { valueBox.blockSignals(true); - valueBox.setValue(value); + float db = ((float)value / 771.0f) - 85.0f; + if (db <= -84.5f) { + valueBox.setSpecialValueText("-inf"); + } else + valueBox.setValue(db); valueBox.blockSignals(false); emit valueChanged(value); }; void SliderGroup::setValue(float value) { + float db; + slider.blockSignals(true); valueBox.blockSignals(true); if (int(value) != slider.value()) slider.setValue(value); - if (valueBox.decimals()) - valueBox.setValue(value - 100.0f); - else + if (valueBox.decimals()) { + db = (float)(value / 771.0f) - 85.0f; + valueBox.setValue(db); + } else valueBox.setValue(value); slider.blockSignals(false); valueBox.blockSignals(false); From 53bcb38455cb9aae86219910cd8b4b7505ebf3b2 Mon Sep 17 00:00:00 2001 From: snt Date: Wed, 22 May 2024 20:52:13 +0200 Subject: [PATCH 32/38] =?UTF-8?q?vumeter=20funcionando,=20hay=20que=20comp?= =?UTF-8?q?arar=20la=20salida=20con=20un=20vumeter=20que=20sepa=20que=20fu?= =?UTF-8?q?nciona=20bien=20y=20definir=20los=20par=C3=A1metros=20de=20vent?= =?UTF-8?q?anas,=20picos=20y=20dem=C3=A1s.=20Se=20insertan=20en=20la=20cad?= =?UTF-8?q?ena=20de=20audio=20porque=20no=20veo=20la=20forma=20de=20hacerl?= =?UTF-8?q?o=20en=20paralelo=20https://github.com/mackron/miniaudio/issues?= =?UTF-8?q?/850?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/changelog.txt | 1 + docs/roadmap.txt | 3 -- src/audiolayerwidget.cpp | 13 ++++++ src/audiolayerwidget.h | 2 + src/audiowidget.cpp | 21 ++++++--- src/audiowidget.h | 1 + src/defines.h | 1 + src/libremediaserver-audio.cpp | 1 + src/ma_writer_node.c | 79 ++++++++++++++++++++++++++++++++-- src/ma_writer_node.h | 31 +++++++++++++ src/miniaudioengine.cpp | 25 +++++++---- src/miniaudioengine.h | 10 +++-- 12 files changed, 165 insertions(+), 23 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index ef76b53..9a2f61b 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -30,6 +30,7 @@ v 0.2.0 Antígona (26/05/2024) - Play all medias in one folder consecutevily. - Play all medias in one folder randomly. + Multi audio devices output. ++ Vumeter for each layer v 0.1.3 Leúcade (19/04/2024) + Ubuntu 22.04 jammy. diff --git a/docs/roadmap.txt b/docs/roadmap.txt index 9e5ec0c..bc06163 100644 --- a/docs/roadmap.txt +++ b/docs/roadmap.txt @@ -49,7 +49,4 @@ v 0.2.1 - ampliar writer para recibir un número n de entradas y escribirlas cada una en un buffer v0.2.0: -- Vumeter or indicator about audio output in layer and master, add to sliderGroup. - --> en master se puede hacer con jack, solo en capas. - --> en las capas, hace falta otro nodo atacado en los buses de input que analice la entrada. - mostrad información de envíos y dispositivos en ui diff --git a/src/audiolayerwidget.cpp b/src/audiolayerwidget.cpp index f37b67d..646cb96 100644 --- a/src/audiolayerwidget.cpp +++ b/src/audiolayerwidget.cpp @@ -91,6 +91,9 @@ AudioLayerWidget::AudioLayerWidget(QWidget *parent, int layer): volumeBox->setSpacing(0); volumeBox->setContentsMargins(0, 0, 0, 0); layout->addLayout(volumeBox); + m_level = new QLabel(); + m_level->setText("0"); + layout->addWidget(m_level); layout->setAlignment(Qt::AlignHCenter); layout->setSpacing(0); layout->setContentsMargins(1, 1, 1, 1); @@ -248,3 +251,13 @@ void AudioLayerWidget::setFilterParam(int channel, int value) m_bus2->blockSignals(true); } } + +void AudioLayerWidget::setLevel(float db) +{ + m_level->blockSignals(true); + if (db > -200) + m_level->setText(QString::number(db)); + else + m_level->setText("-inf"); + m_level->blockSignals(false); +} diff --git a/src/audiolayerwidget.h b/src/audiolayerwidget.h index cb446a9..0f9517c 100644 --- a/src/audiolayerwidget.h +++ b/src/audiolayerwidget.h @@ -28,6 +28,7 @@ public: void setPan(int pan); void setPitch(int pitch); void setFilterParam(int channel, int value); + void setLevel(float db); private: Status m_status; @@ -36,6 +37,7 @@ private: QPushButton *m_suspendResumeButton; ClickableLabel *m_fileValue; ClickableLabel * m_folderValue; + QLabel *m_level; SliderGroup *m_volume; SliderGroup *m_pan; SliderGroup *m_pitch; diff --git a/src/audiowidget.cpp b/src/audiowidget.cpp index da7c33f..98b3a32 100644 --- a/src/audiowidget.cpp +++ b/src/audiowidget.cpp @@ -13,12 +13,13 @@ AudioWidget::AudioWidget(QWidget *parent) : 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].duration = -1; 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_layerUpdate[i].vol = -1; + m_layerUpdate[i].pan = -1; + m_layerUpdate[i].pitch = -1; + m_layerUpdate[i].cursor = -1; + m_layerUpdate[i].level = 100; for (int j = 0; j < FILTER_CHANNELS; j++) m_filtersUpdate[i][j] = -1; } @@ -102,6 +103,10 @@ void AudioWidget::refreshUi() alw->setFilterParam(j, m_filtersUpdate[i][j]); m_filtersUpdate[i][j] = -1; } + if (m_layerUpdate[i].level < 100) { + alw->setLevel(m_layerUpdate[i].level); + m_layerUpdate[i].level = 100; + } m_layerUpdate[i].updated = false; } } @@ -112,3 +117,9 @@ void AudioWidget::filterParamChanged(int layer, int channel, int value) m_filtersUpdate[layer][channel - HP_FREQ] = value; m_layerUpdate[layer].updated = true; } + +void AudioWidget::levelChanged(int layer, float db) +{ + m_layerUpdate[layer].level = db; + m_layerUpdate[layer].updated = true; +} diff --git a/src/audiowidget.h b/src/audiowidget.h index 6849f5e..ddb5767 100644 --- a/src/audiowidget.h +++ b/src/audiowidget.h @@ -15,6 +15,7 @@ class AudioWidget : public QWidget public: AudioWidget(QWidget *parent = nullptr); void filterParamChanged(int layer, int channel, int value); + void levelChanged(int layer, float db); private: QHBoxLayout *m_layout; diff --git a/src/defines.h b/src/defines.h index 8d81a59..6751fd0 100644 --- a/src/defines.h +++ b/src/defines.h @@ -75,6 +75,7 @@ struct layerData { int device; int bus1Vol; int bus2Vol; + float level; }; #endif // __cplusplus #endif // DEFINES_H diff --git a/src/libremediaserver-audio.cpp b/src/libremediaserver-audio.cpp index 07e3b0e..56b016a 100644 --- a/src/libremediaserver-audio.cpp +++ b/src/libremediaserver-audio.cpp @@ -170,6 +170,7 @@ void libreMediaServerAudio::refreshUi() { m_lmsUi->m_aw->cursorChanged(i, m_mae.getCursor(i)); m_updateUi[i][3] = -1; } + m_lmsUi->m_aw->levelChanged(i, m_mae.getLevel(i)); if (m_mae.getAtEnd(i)) { if (m_currentStatus[i] == Status::PlayingOnce) { m_currentStatus[i] = Status::Stopped; diff --git a/src/ma_writer_node.c b/src/ma_writer_node.c index 82e057c..2845649 100644 --- a/src/ma_writer_node.c +++ b/src/ma_writer_node.c @@ -46,7 +46,7 @@ MA_API ma_result ma_writer_node_init(ma_node_graph* pNodeGraph, const ma_writer_ { ma_result result; ma_node_config baseConfig; - ma_uint32 inputChannels[2]; // Equal in size to the number of input channels specified in the vtable. + ma_uint32 inputChannels[2]; ma_uint32 outputChannels[1]; if (pWriteNode == NULL || pConfig == NULL || pConfig->pBuffer == NULL \ @@ -82,14 +82,12 @@ MA_API void ma_writer_node_uninit(ma_writer_node* pWriteNode, const ma_allocatio * Data Source Ring Buffer */ - ma_result ma_data_source_rb_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) { ma_data_source_rb* ds = (ma_data_source_rb*)pDataSource; ma_uint32 pcmFramesAvailableInRB = 0; ma_uint32 pcmFramesProcessed = 0; - // lo mismo que en el callback, va el doble de rápido y con glitches. while (pcmFramesProcessed < frameCount) { pcmFramesAvailableInRB = ma_pcm_rb_available_read(ds->rb); if (pcmFramesAvailableInRB == 0) { @@ -175,3 +173,78 @@ void ma_data_source_rb_uninit(ma_data_source_rb* pMyDataSource) { ma_data_source_uninit(&pMyDataSource->base); } + +/* + * vumeter + */ + +MA_API ma_vumeter_node_config ma_vumeter_node_config_init(ma_uint32 channels, ma_uint32 sampleRate) +{ + ma_vumeter_node_config config; + + MA_ZERO_OBJECT(&config); + config.nodeConfig = ma_node_config_init(); + config.channels = channels; + config.sampleRate = sampleRate; + + return config; +} + +static void ma_vumeter_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) +{ + ma_vumeter_node* pVumeterNode = (ma_vumeter_node*)pNode; + + MA_ASSERT(pVumeterNode != NULL); + MA_ASSERT(ma_node_get_input_bus_count(&pVumeterNode->baseNode) == 1); + + for (uint i = 0; i < *pFrameCountIn; i++) { + float input = fabsf(ppFramesIn[0][i]); + pVumeterNode->level += pVumeterNode->alpha * (input - pVumeterNode->level); + } + ma_copy_pcm_frames(ppFramesOut[0], ppFramesIn[0], *pFrameCountOut, ma_format_f32, pVumeterNode->channels); +} + +static ma_node_vtable g_ma_vumeter_node_vtable = +{ + ma_vumeter_node_process_pcm_frames, + NULL, + 1, + 1, + 0 +}; + +MA_API ma_result ma_vumeter_node_init(ma_node_graph* pNodeGraph, const ma_vumeter_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_vumeter_node* pVumeterNode) +{ + ma_result result; + ma_node_config baseConfig; + ma_uint32 inputChannels[1]; + ma_uint32 outputChannels[1]; + + if (pVumeterNode == NULL || pConfig == NULL \ + || (pConfig->channels > MA_MAX_NODE_BUS_COUNT) ) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pVumeterNode); + inputChannels[0] = pConfig->channels; + outputChannels[0] = pConfig->channels; + baseConfig = pConfig->nodeConfig; + baseConfig.vtable = &g_ma_vumeter_node_vtable; + baseConfig.pInputChannels = inputChannels; + baseConfig.pOutputChannels = outputChannels; + + result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pVumeterNode->baseNode); + if (result != MA_SUCCESS) { return result; + } + pVumeterNode->sampleRate = pConfig->sampleRate; + pVumeterNode->channels = pConfig->channels; + pVumeterNode->level = 0; + pVumeterNode->TC = 10000.0f; + pVumeterNode->alpha = 1.0 - expf( (-2.0 * M_PI) / (pVumeterNode->TC * pConfig->sampleRate)); + return MA_SUCCESS; +} + +MA_API void ma_vumeter_node_uninit(ma_vumeter_node* pVumeterNode, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_node_uninit(&pVumeterNode->baseNode, pAllocationCallbacks); +} diff --git a/src/ma_writer_node.h b/src/ma_writer_node.h index 1b5d259..ac592c7 100644 --- a/src/ma_writer_node.h +++ b/src/ma_writer_node.h @@ -8,6 +8,10 @@ extern "C" { #include "miniaudio.h" +/* + * writer + */ + typedef struct { ma_node_config nodeConfig; @@ -46,6 +50,33 @@ ma_result ma_data_source_rb_get_length(ma_data_source* pDataSource, ma_uint64* p ma_result ma_data_source_rb_init(ma_data_source_rb* pMyDataSource, ma_pcm_rb *ringBuffer); void ma_data_source_rb_uninit(ma_data_source_rb* pMyDataSource); + +/* + * VU meter + */ +typedef struct +{ + ma_node_config nodeConfig; + ma_uint32 channels; + ma_uint32 sampleRate; +} ma_vumeter_node_config; + +MA_API ma_vumeter_node_config ma_vumeter_node_config_init(ma_uint32 channels, ma_uint32 sampleRate); + +typedef struct +{ + ma_node_base baseNode; + ma_uint32 channels; + ma_uint32 sampleRate; + float level; + float TC; + float alpha; +} ma_vumeter_node; + +MA_API ma_result ma_vumeter_node_init(ma_node_graph* pNodeGraph, const ma_vumeter_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_vumeter_node* pVumeterNode); +MA_API void ma_vumeter_node_uninit(ma_vumeter_node* pVumeterNode, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API inline float ma_vumeter_node_get_level(ma_vumeter_node* pVumeterNode) { return 5 * pVumeterNode->level; }; + #ifdef __cplusplus } #endif diff --git a/src/miniaudioengine.cpp b/src/miniaudioengine.cpp index 328e738..3ea5c1b 100644 --- a/src/miniaudioengine.cpp +++ b/src/miniaudioengine.cpp @@ -24,7 +24,7 @@ void MiniAudioEngine::stopEngine() ma_sound_uninit(&m_mae.sounds[i]); } for (uint i = 0; i < m_mae.layersQty; i++) { - ma_node_uninit(&m_mae.filters[i].input, NULL); + ma_splitter_node_uninit(&m_mae.filters[i].input, NULL); ma_hpf_node_uninit(&m_mae.filters[i].hpf, NULL); ma_loshelf_node_uninit(&m_mae.filters[i].loshelf, NULL); ma_peak_node_uninit(&m_mae.filters[i].mLow, NULL); @@ -91,6 +91,7 @@ ma_result MiniAudioEngine::createFilterBank(uint layer) filterBank *fb = &m_mae.filters[layer]; ma_splitter_node_config splitterConfig = ma_splitter_node_config_init(CHANNELS); + splitterConfig.outputBusCount= 3; result = ma_splitter_node_init(ng, &splitterConfig, NULL, &fb->input); if (result != MA_SUCCESS) { cout << "ERROR " << result << ": Failed to init input node." << endl; @@ -131,6 +132,12 @@ ma_result MiniAudioEngine::createFilterBank(uint layer) cout << "ERROR " << result << ": Failed to init hi shelf filter node." << endl; return result; } + ma_vumeter_node_config vuc = ma_vumeter_node_config_init(CHANNELS, FORMAT); + ma_vumeter_node_init(ng, &vuc, NULL, &fb->vumeter); + if (result != MA_SUCCESS) { + cout << "ERROR " << result << ": Failed to init vumeter node." << endl; + return result; + } splitterConfig.outputBusCount = m_mae.audioDevicesQty; result = ma_splitter_node_init(ng, &splitterConfig, NULL, &fb->output); if (result != MA_SUCCESS) { @@ -142,7 +149,7 @@ ma_result MiniAudioEngine::createFilterBank(uint layer) cout << "ERROR " << result << ": Failed to attach input node." << endl; return result; } - result = ma_node_attach_output_bus(&fb->input, 1, &fb->output, 0); + result = ma_node_attach_output_bus(&fb->input, 1, &fb->vumeter, 0); if (result != MA_SUCCESS) { cout << "ERROR " << result << ": Failed to attach bypass connection." << endl; return result; @@ -168,7 +175,12 @@ ma_result MiniAudioEngine::createFilterBank(uint layer) cout << "ERROR " << result << ": Failed to attach high peaks filter node." << endl; return result; } - result = ma_node_attach_output_bus(&fb->hishelf, 0, &fb->output, 0); + result = ma_node_attach_output_bus(&fb->hishelf, 0, &fb->vumeter, 0); + if (result != MA_SUCCESS) { + cout << "ERROR " << result << ": Failed to attach high shelf filter node." << endl; + return result; + } + result = ma_node_attach_output_bus(&fb->vumeter, 0, &fb->output, 0); if (result != MA_SUCCESS) { cout << "ERROR " << result << ": Failed to attach high shelf filter node." << endl; return result; @@ -219,12 +231,6 @@ ma_result MiniAudioEngine::setNodeGraph() { cout << "ERROR " << result << ": Failed to init writer node." << endl; return result; } - // esto va a dar problemas al sumar en el envío 0 una vez por cad envío extra. - // writer_node puede ser silencioso - // así ya estamos en el caso de disparar varios bang por cada envío en el mismo nodegraph - // es mejor que writer node tenga varias entradas, una por cada envío - // que se dispara con un único engine y proporciona un único stream de audio de vuelta a ese engine - // en vez de un puntero hay que pasarle un array de rb result = ma_node_attach_output_bus(&m_mae.sendAuxNode[i], 0, ma_engine_get_endpoint(&m_mae.engines[0]), 0); if (result != MA_SUCCESS) { cout << "ERROR " << result << ": Failed to attach writer node." << endl; @@ -495,6 +501,7 @@ ma_result MiniAudioEngine::seekToCursor(int layer, int cursor) result = ma_sound_get_length_in_pcm_frames(&m_mae.sounds[layer], &end); if (result != MA_SUCCESS) { return result; } start = (cursor * end) / 65025; + Status oldStatus = m_mae.currentStatus[layer].status; result = ma_sound_seek_to_pcm_frame(&m_mae.sounds[layer], start); //result = ma_data_source_set_loop_point_in_pcm_frames(&m_mae.sounds[layer], start, end); // this do nothing here, it must be done after set_looping or start? return (result); diff --git a/src/miniaudioengine.h b/src/miniaudioengine.h index 870dc42..89095e6 100644 --- a/src/miniaudioengine.h +++ b/src/miniaudioengine.h @@ -5,8 +5,6 @@ #define MA_ENABLE_JACK #define MA_NO_GENERATION #define MA_DEBUG_OUTPUT -#define MA_DISABLE_PULSEAUDIO -#define MA_DEBUG_OUTPUT #define MA_LOG_LEVEL_DEBUG DEBUG #define MINIAUDIO_IMPLEMENTATION #include "miniaudio.h" @@ -28,6 +26,7 @@ typedef struct ma_peak_node_config mHighConfig; ma_hishelf_node hishelf; ma_hishelf_node_config hishelfConfig; + ma_vumeter_node vumeter; ma_splitter_node output; } filterBank; @@ -76,10 +75,15 @@ protected: float getCursor(int layer); Status getStatus(int layer); inline float getVol(int layer) { - return ma_sound_get_volume(&m_mae.sounds[layer]); } + return ma_sound_get_volume(&m_mae.sounds[layer]); + }; inline bool getAtEnd(int layer) { return m_mae.sounds[layer].atEnd; } ma_result filterParamChanged(int layer, int audioDevice, int channel, int value); bool setBypass(int audioDevice, int layer, bool bypass); + inline float getLevel(int layer) { + float level = ma_vumeter_node_get_level(&m_mae.filters[layer].vumeter); + return ma_volume_linear_to_db(level); + }; private: MAE m_mae; From db86987b6ade7d15ee2bafc8cb42caa0be730c62 Mon Sep 17 00:00:00 2001 From: snt Date: Thu, 23 May 2024 15:53:44 +0200 Subject: [PATCH 33/38] muestra nombre de los dispositivos en la UI --- src/audiolayerwidget.cpp | 51 +++++++++++++++++++++++++++++++--- src/audiolayerwidget.h | 7 +++-- src/audiowidget.cpp | 9 ++++++ src/audiowidget.h | 1 + src/libremediaserver-audio.cpp | 4 +++ src/miniaudioengine.cpp | 6 ++++ src/miniaudioengine.h | 8 ++++-- 7 files changed, 76 insertions(+), 10 deletions(-) diff --git a/src/audiolayerwidget.cpp b/src/audiolayerwidget.cpp index 646cb96..7fc467b 100644 --- a/src/audiolayerwidget.cpp +++ b/src/audiolayerwidget.cpp @@ -91,9 +91,39 @@ AudioLayerWidget::AudioLayerWidget(QWidget *parent, int layer): volumeBox->setSpacing(0); volumeBox->setContentsMargins(0, 0, 0, 0); layout->addLayout(volumeBox); - m_level = new QLabel(); - m_level->setText("0"); - layout->addWidget(m_level); + QHBoxLayout *labelsBox = new QHBoxLayout; + m_level = new QLabel("-inf"); + m_level->setStyleSheet("border: 1px solid #5a4855;" + "margin: 0px;" + "background-color: #180014;" + "color: white;" + "width:70px;" + "text-align: center;" + ); + m_level->setMinimumWidth(70); + m_level->setAlignment(Qt::AlignHCenter); + labelsBox->addWidget(m_level); + m_bus1Label = new QLabel("dummy"); + m_bus1Label->setMinimumWidth(80); + m_bus1Label->setStyleSheet("border: 1px solid #5a4855;" + "margin: 0px;" + "background-color: #383034;" + "width:70px;" + ); + m_bus1Label->setAlignment(Qt::AlignHCenter); + labelsBox->addWidget(m_bus1Label); + m_bus2Label = new QLabel("dummy"); + m_bus2Label->setMinimumWidth(80); + m_bus2Label->setStyleSheet("border: 1px solid #5a4855;" + "margin: 0px;" + "background-color: #383034;" + "width: 70px;" + ); + m_bus2Label->setAlignment(Qt::AlignHCenter); + labelsBox->addWidget(m_bus2Label); + labelsBox->setSpacing(0); + labelsBox->setContentsMargins(0, 0, 0, 0); + layout->addLayout(labelsBox); layout->setAlignment(Qt::AlignHCenter); layout->setSpacing(0); layout->setContentsMargins(1, 1, 1, 1); @@ -255,9 +285,22 @@ void AudioLayerWidget::setFilterParam(int channel, int value) void AudioLayerWidget::setLevel(float db) { m_level->blockSignals(true); - if (db > -200) + if (db > -150) m_level->setText(QString::number(db)); else m_level->setText("-inf"); m_level->blockSignals(false); } + +void AudioLayerWidget::setBusName(uint bus, char *name) +{ + if (bus == 0) { + m_bus1Label->blockSignals(true); + m_bus1Label->setText(name); + m_bus1Label->blockSignals(false); + } else if (bus == 1) { + m_bus2Label->blockSignals(true); + m_bus2Label->setText(name); + m_bus2Label->blockSignals(false); + } +} diff --git a/src/audiolayerwidget.h b/src/audiolayerwidget.h index 0f9517c..bc7010f 100644 --- a/src/audiolayerwidget.h +++ b/src/audiolayerwidget.h @@ -29,6 +29,7 @@ public: void setPitch(int pitch); void setFilterParam(int channel, int value); void setLevel(float db); + void setBusName(uint bus, char *name); private: Status m_status; @@ -37,7 +38,6 @@ private: QPushButton *m_suspendResumeButton; ClickableLabel *m_fileValue; ClickableLabel * m_folderValue; - QLabel *m_level; SliderGroup *m_volume; SliderGroup *m_pan; SliderGroup *m_pitch; @@ -47,8 +47,9 @@ private: QTimeEdit *m_totalTimeValue; QProgressBar *m_progress; FilterBankWidget *m_filterBank; - -//public slots: + QLabel *m_level; + QLabel *m_bus1Label; + QLabel *m_bus2Label; // From Ui private slots: diff --git a/src/audiowidget.cpp b/src/audiowidget.cpp index 98b3a32..8d43127 100644 --- a/src/audiowidget.cpp +++ b/src/audiowidget.cpp @@ -123,3 +123,12 @@ void AudioWidget::levelChanged(int layer, float db) m_layerUpdate[layer].level = db; m_layerUpdate[layer].updated = true; } + +void AudioWidget::busNameChanged(uint bus, char* name) +{ + for (uint i = 0; i < m_layers; i++) { + QLayoutItem *item = m_layout->itemAt(i); + AudioLayerWidget *alw = dynamic_cast(item->widget()); + alw->setBusName(bus, name); + } +} diff --git a/src/audiowidget.h b/src/audiowidget.h index ddb5767..238ead2 100644 --- a/src/audiowidget.h +++ b/src/audiowidget.h @@ -16,6 +16,7 @@ public: AudioWidget(QWidget *parent = nullptr); void filterParamChanged(int layer, int channel, int value); void levelChanged(int layer, float db); + void busNameChanged(uint bus, char *name); private: QHBoxLayout *m_layout; diff --git a/src/libremediaserver-audio.cpp b/src/libremediaserver-audio.cpp index 56b016a..177eec2 100644 --- a/src/libremediaserver-audio.cpp +++ b/src/libremediaserver-audio.cpp @@ -229,6 +229,10 @@ void libreMediaServerAudio::setUi(libreMediaServerAudioUi *lmsUi) connect(m_refreshUi, SIGNAL(timeout()), this, SLOT(refreshUi())); m_refreshUi->start(UI_REFRESH_TIME); m_ola->start(QThread::TimeCriticalPriority ); + for (uint i = 0; i < m_settings->getAudioDeviceQty(); i++) { + char *name = m_mae.getDeviceName(i); + m_lmsUi->m_aw->busNameChanged(i, name); + } }; // From Ui widgets diff --git a/src/miniaudioengine.cpp b/src/miniaudioengine.cpp index 3ea5c1b..292251e 100644 --- a/src/miniaudioengine.cpp +++ b/src/miniaudioengine.cpp @@ -340,6 +340,12 @@ ma_result MiniAudioEngine::getAllAudioDevices() return result; } +char* MiniAudioEngine::getDeviceName(uint id) +{ + return m_mae.pPlaybackDeviceInfos[m_mae.audioDevicesId[id]].name; + +} + ma_result MiniAudioEngine::loadMedia(int layer, char *file, uint audioDevice) { ma_result result; diff --git a/src/miniaudioengine.h b/src/miniaudioengine.h index 89095e6..b97a9e9 100644 --- a/src/miniaudioengine.h +++ b/src/miniaudioengine.h @@ -43,6 +43,7 @@ typedef struct ma_resource_manager resourceManager; ma_context context; ma_device_info* pPlaybackDeviceInfos; + ma_device_info pSelectedPlaybackDeviceInfos[MAX_AUDIODEVICES]; ma_uint32 playbackDeviceCount; ma_uint32 devicesSelected; ma_bool8 mediaLoaded[MAX_LAYERS]; @@ -58,12 +59,12 @@ class MiniAudioEngine friend class libreMediaServerAudio; public: - MiniAudioEngine(); - void stopEngine(); - bool startEngine(uint layersQty, uint* audioDevicesID, uint audioDevicesQty); static void audioDataCallback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount); protected: + MiniAudioEngine(); + void stopEngine(); + bool startEngine(uint layersQty, uint* audioDevicesID, uint audioDevicesQty); ma_result loadMedia(int layer, char *media, uint audioDevice); void volChanged(int layer, int vol); void panChanged(int layer, float pan); @@ -84,6 +85,7 @@ protected: float level = ma_vumeter_node_get_level(&m_mae.filters[layer].vumeter); return ma_volume_linear_to_db(level); }; + char* getDeviceName(uint id); private: MAE m_mae; From 0d29dda4c1e2ea5f4562df282c4ddfa8d6251fb2 Mon Sep 17 00:00:00 2001 From: snt Date: Fri, 24 May 2024 01:49:21 +0200 Subject: [PATCH 34/38] =?UTF-8?q?quita=20el=20glitch=20al=20resproducir=20?= =?UTF-8?q?despu=C3=A9s=20de=20cargar=20un=20fichero.=20varias=20optimizac?= =?UTF-8?q?iones=20y=20comprobaciones?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/changelog.txt | 7 ++--- docs/roadmap.txt | 38 ++++++++++---------------- src/audiolayerwidget.cpp | 11 ++++---- src/libremediaserver-audio.cpp | 11 ++++---- src/ma_writer_node.c | 10 +++---- src/ma_writer_node.h | 4 ++- src/miniaudioengine.cpp | 50 ++++++++++++++++++---------------- src/miniaudioengine.h | 6 ++-- 8 files changed, 66 insertions(+), 71 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 9a2f61b..5cd2ca3 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -1,10 +1,10 @@ ******************************************************************************* -Libre Media Server Audio - An Open source Media Server for arts and performing. +LibreMediaServer Audio - An open source media server for arts and performing. (c) Criptomart - Santiago Noreña 2012-2024 https://git.criptomart.net/libremediaserver ******************************************************************************* -Libre Media Server ChangeLog +LibreMediaServer 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. @@ -20,9 +20,7 @@ v 0.2.0 Antígona (26/05/2024) + 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). Signals are used only from Ui to libreMediaServer to notify user interactions and Qtimers. + Load media files from ui clicking in the media labels. + New Play Modes: @@ -31,6 +29,7 @@ v 0.2.0 Antígona (26/05/2024) - Play all medias in one folder randomly. + Multi audio devices output. + Vumeter for each layer ++ Show device name on Ui and ouput bus slider. v 0.1.3 Leúcade (19/04/2024) + Ubuntu 22.04 jammy. diff --git a/docs/roadmap.txt b/docs/roadmap.txt index bc06163..bc786a9 100644 --- a/docs/roadmap.txt +++ b/docs/roadmap.txt @@ -6,38 +6,32 @@ https://git.criptomart.net/libremediaserver Libre Media Server Roadmap -v 0.2.x -- skin, UI/UX +v 0.3.0 +- Ui/Ux: skin, style. +- 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. +- Ui/Ux: seek cursor playback - live input. -- insertar/bypass/eliminar audio procesadores sin reiniciar por capa y master. (compresor, equs). -- FX en capas master para que se puedan usar como envíos de auxiliar. -- Enroutado de masters en otros masters (retorno de efectos). - -v 0.2.2 -- Use sACN directly. +- remove ola and use sACN directly. + la instalación de OLA es mediante compilación, el repo de paquetes no está actualizado, nada user-friendly. + 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. +- CIPT/MSex. - Rasp build. - Octopus Sound Card support (6 outputs - 8 inputs). - -v 0.2.1 -- mute/panic on layer. - Master Bus Layer: - - each layer will have one "Gain" prefader that acts in source, "Vol" in v 1.3. - - each layer will have one volume dmx channel for each bus layer. One aux "Send" prefader. - mute/panic. - fader + value - - pan. - - magicq .hed + - pan + - magicq personality .hed - 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. + - compresor/limiter. +- Layer: + - audio procesadores (compresor, reveb, delay). + - mute/panic. +- Rose noise and sine generator. - Logs, verbosity, timestamp. - New play mode without pitch control, it saves resources. MA_SOUND_FLAG_NO_PITCH - SettingsDialog. @@ -45,8 +39,4 @@ v 0.2.1 - ¿Exit Point? is it needed? - Hardening: check return errors, try/catch exceptions, i'm too happy.... - Tests: errors on wrong conf file. -- Ui/Ux: seek cursor playback - ampliar writer para recibir un número n de entradas y escribirlas cada una en un buffer - -v0.2.0: -- mostrad información de envíos y dispositivos en ui diff --git a/src/audiolayerwidget.cpp b/src/audiolayerwidget.cpp index 7fc467b..b1eb8b7 100644 --- a/src/audiolayerwidget.cpp +++ b/src/audiolayerwidget.cpp @@ -198,6 +198,7 @@ void AudioLayerWidget::openMediaDialog() fileNames = dialog.selectedFiles(); emit uiLoadMedia(m_layer, fileNames.at(0)); this->setMediaFile(fileNames.at(0)); + this->setPlaybackStatus(Status::Stopped); } // from DMX signals @@ -267,18 +268,18 @@ void AudioLayerWidget::setCurrentTime(float progress) void AudioLayerWidget::setFilterParam(int channel, int value) { - if (channel <= FILTER_BANK_GAIN - FILTER_CHANNELS){ + if (channel <= FILTER_BANK_GAIN - HP_FREQ){ m_filterBank->blockSignals(true); m_filterBank->setValue(channel, value); m_filterBank->blockSignals(false); } else if (channel == SEND1 - HP_FREQ) { - m_bus1->blockSignals(false); - m_bus1->setValue((value * 256) + 255); m_bus1->blockSignals(true); + m_bus1->setValue((value * 256) + 255); + m_bus1->blockSignals(false); } else if (channel == SEND2 - HP_FREQ) { - m_bus2->blockSignals(false); - m_bus2->setValue(value * 256 + 255); m_bus2->blockSignals(true); + m_bus2->setValue(value * 256 + 255); + m_bus2->blockSignals(false); } } diff --git a/src/libremediaserver-audio.cpp b/src/libremediaserver-audio.cpp index 177eec2..dd6cedd 100644 --- a/src/libremediaserver-audio.cpp +++ b/src/libremediaserver-audio.cpp @@ -70,8 +70,7 @@ void libreMediaServerAudio::loadMedia(int layer, int folder, int 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_dmxSettings.at(layer).audioDevice); + m_mae.loadMedia(layer, mediaFile.toLatin1().data()); m_currentMedia[layer] = mediaFile; #ifndef NOGUI if (m_ui) @@ -135,7 +134,7 @@ void libreMediaServerAudio::dmxInput(int layer, int channel, int value) } #endif } else if (channel >= HP_FREQ) { - m_mae.filterParamChanged(layer, m_dmxSettings.at(layer).audioDevice, channel, value); + m_mae.filterParamChanged(layer, channel, value); #ifndef NOGUI if (m_ui) { m_lmsUi->m_aw->filterParamChanged(layer, channel, value); @@ -252,10 +251,10 @@ void libreMediaServerAudio::uiSliderChanged(int layer, Slider s, int value) m_mae.setBypass(m_dmxSettings.at(layer).audioDevice, layer, value); break; case Slider::Bus1: - m_mae.filterParamChanged(layer, m_dmxSettings.at(layer).audioDevice, SEND1, value / 255); + m_mae.filterParamChanged(layer, SEND1, value / 255.0f); break; case Slider::Bus2: - m_mae.filterParamChanged(layer, m_dmxSettings.at(layer).audioDevice, SEND2, value / 255); + m_mae.filterParamChanged(layer, SEND2, value / 255.0f); break; } } @@ -278,7 +277,7 @@ void libreMediaServerAudio::uiLoadMedia(int layer, QString mediaFile) if (strcmp(mediaFile.toLatin1().constData(), m_currentMedia[layer].toLatin1().constData()) == 0) return; - result = m_mae.loadMedia(layer, mediaFile.toLatin1().data(), m_dmxSettings[layer].audioDevice); + 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)); diff --git a/src/ma_writer_node.c b/src/ma_writer_node.c index 2845649..5b20f66 100644 --- a/src/ma_writer_node.c +++ b/src/ma_writer_node.c @@ -38,8 +38,6 @@ static ma_node_vtable g_ma_writer_node_vtable = 2, 1, 0 -// MA_NODE_FLAG_CONTINUOUS_PROCESSING -// MA_NODE_FLAG_SILENT_OUTPUT }; MA_API ma_result ma_writer_node_init(ma_node_graph* pNodeGraph, const ma_writer_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_writer_node* pWriteNode) @@ -178,7 +176,7 @@ void ma_data_source_rb_uninit(ma_data_source_rb* pMyDataSource) * vumeter */ -MA_API ma_vumeter_node_config ma_vumeter_node_config_init(ma_uint32 channels, ma_uint32 sampleRate) +MA_API ma_vumeter_node_config ma_vumeter_node_config_init(ma_uint32 channels, ma_uint32 format, ma_uint32 sampleRate) { ma_vumeter_node_config config; @@ -186,6 +184,7 @@ MA_API ma_vumeter_node_config ma_vumeter_node_config_init(ma_uint32 channels, ma config.nodeConfig = ma_node_config_init(); config.channels = channels; config.sampleRate = sampleRate; + config.format = format; return config; } @@ -201,7 +200,7 @@ static void ma_vumeter_node_process_pcm_frames(ma_node* pNode, const float** ppF float input = fabsf(ppFramesIn[0][i]); pVumeterNode->level += pVumeterNode->alpha * (input - pVumeterNode->level); } - ma_copy_pcm_frames(ppFramesOut[0], ppFramesIn[0], *pFrameCountOut, ma_format_f32, pVumeterNode->channels); + ma_copy_pcm_frames(ppFramesOut[0], ppFramesIn[0], *pFrameCountOut, pVumeterNode->format, pVumeterNode->channels); } static ma_node_vtable g_ma_vumeter_node_vtable = @@ -238,8 +237,9 @@ MA_API ma_result ma_vumeter_node_init(ma_node_graph* pNodeGraph, const ma_vumete } pVumeterNode->sampleRate = pConfig->sampleRate; pVumeterNode->channels = pConfig->channels; + pVumeterNode->format = pConfig->format; pVumeterNode->level = 0; - pVumeterNode->TC = 10000.0f; + pVumeterNode->TC = 0.250f; pVumeterNode->alpha = 1.0 - expf( (-2.0 * M_PI) / (pVumeterNode->TC * pConfig->sampleRate)); return MA_SUCCESS; } diff --git a/src/ma_writer_node.h b/src/ma_writer_node.h index ac592c7..3e03df5 100644 --- a/src/ma_writer_node.h +++ b/src/ma_writer_node.h @@ -59,15 +59,17 @@ typedef struct ma_node_config nodeConfig; ma_uint32 channels; ma_uint32 sampleRate; + ma_uint32 format; } ma_vumeter_node_config; -MA_API ma_vumeter_node_config ma_vumeter_node_config_init(ma_uint32 channels, ma_uint32 sampleRate); +MA_API ma_vumeter_node_config ma_vumeter_node_config_init(ma_uint32 channels, ma_uint32 format, ma_uint32 sampleRate); typedef struct { ma_node_base baseNode; ma_uint32 channels; ma_uint32 sampleRate; + ma_uint32 format; float level; float TC; float alpha; diff --git a/src/miniaudioengine.cpp b/src/miniaudioengine.cpp index 292251e..1d0d5b3 100644 --- a/src/miniaudioengine.cpp +++ b/src/miniaudioengine.cpp @@ -132,7 +132,7 @@ ma_result MiniAudioEngine::createFilterBank(uint layer) cout << "ERROR " << result << ": Failed to init hi shelf filter node." << endl; return result; } - ma_vumeter_node_config vuc = ma_vumeter_node_config_init(CHANNELS, FORMAT); + ma_vumeter_node_config vuc = ma_vumeter_node_config_init(CHANNELS, FORMAT, SAMPLE_RATE); ma_vumeter_node_init(ng, &vuc, NULL, &fb->vumeter); if (result != MA_SUCCESS) { cout << "ERROR " << result << ": Failed to init vumeter node." << endl; @@ -278,7 +278,7 @@ ma_result MiniAudioEngine::startDevices() deviceConfig.dataCallback = audioDataCallback; engineConfig = ma_engine_config_init(); engineConfig.pResourceManager = &m_mae.resourceManager; - engineConfig.gainSmoothTimeInMilliseconds = SAMPLE_RATE / 25; + engineConfig.defaultVolumeSmoothTimeInPCMFrames = SAMPLE_RATE / 20; engineConfig.noAutoStart = MA_TRUE; for (uint internalId = 0; internalId < m_mae.audioDevicesQty; internalId++) { @@ -346,25 +346,28 @@ char* MiniAudioEngine::getDeviceName(uint id) } -ma_result MiniAudioEngine::loadMedia(int layer, char *file, uint audioDevice) +ma_result MiniAudioEngine::loadMedia(int layer, char *file) { ma_result result; if (m_mae.mediaLoaded[layer] == true) { + ma_sound_set_volume(&m_mae.sounds[layer], 0.0f); + ma_sound_stop(&m_mae.sounds[layer]); ma_sound_uninit(&m_mae.sounds[layer]); m_mae.mediaLoaded[layer] = false; } - result = ma_sound_init_from_file(&m_mae.engines[0], file, \ - MA_SOUND_FLAG_NO_SPATIALIZATION \ - , NULL, NULL, &m_mae.sounds[layer]); + ma_sound_config soundConfig = ma_sound_config_init(); + soundConfig = ma_sound_config_init(); + soundConfig.pFilePath = file; + soundConfig.pInitialAttachment = &m_mae.filters[layer].input; + soundConfig.initialAttachmentInputBusIndex = 0; + soundConfig.channelsIn = 0; + soundConfig.channelsOut = CHANNELS; + soundConfig.flags = MA_SOUND_FLAG_NO_SPATIALIZATION | MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT | MA_SOUND_FLAG_STREAM; //| MA_SOUND_FLAG_NO_PITCH + result = ma_sound_init_ex(&m_mae.engines[0], &soundConfig, &m_mae.sounds[layer]); if (result != MA_SUCCESS) { - cout << "Error " << result << ": Failed to load file " << file << endl; - return result; - } - result = ma_node_attach_output_bus(&m_mae.sounds[layer], 0, &m_mae.filters[layer].input, 0); - if (result != MA_SUCCESS) { - cout << "Error " << result << ": Failed to attach sound output bus " << audioDevice << endl; + cout << "Error" << result << ": Failed to load file " << file << endl; return result; } m_mae.mediaLoaded[layer] = true; @@ -435,7 +438,7 @@ void MiniAudioEngine::volChanged(int layer, int vol) db = 0; } else db = ma_volume_db_to_linear(db); - ma_sound_group_set_fade_in_milliseconds(&m_mae.sounds[layer], -1, db, FADE_TIME); + ma_sound_set_fade_in_milliseconds(&m_mae.sounds[layer], -1, db, FADE_TIME); m_mae.currentStatus[layer].vol = vol; } @@ -485,12 +488,13 @@ ma_result MiniAudioEngine::playbackChanged(int layer, Status status) ma_sound_set_stop_time_in_milliseconds(&m_mae.sounds[layer], ~(ma_uint64)0); ma_sound_set_looping(&m_mae.sounds[layer], loop); result = ma_sound_start(&m_mae.sounds[layer]); - if (m_mae.currentStatus[layer].cursor != 0) { - usleep(1000 * 50); // Avoid small glitch at start, how to flush the cached buffers in audio pipe line? - } - this->volChanged(layer, m_mae.currentStatus[layer].vol); - default: - break; + //this->volChanged(layer, m_mae.currentStatus[layer].vol); + float db = (m_mae.currentStatus[layer].vol / 771.0f) - 85.0f; + if (db <= -85.0f) { + db = 0; + } else + db = ma_volume_db_to_linear(db); + ma_sound_set_fade_in_milliseconds(&m_mae.sounds[layer], -1, db, 0); } if (result == MA_SUCCESS) m_mae.currentStatus[layer].status = status; @@ -506,7 +510,7 @@ ma_result MiniAudioEngine::seekToCursor(int layer, int cursor) return MA_DOES_NOT_EXIST; result = ma_sound_get_length_in_pcm_frames(&m_mae.sounds[layer], &end); if (result != MA_SUCCESS) { return result; } - start = (cursor * end) / 65025; + start = (cursor * end) / 65535; Status oldStatus = m_mae.currentStatus[layer].status; result = ma_sound_seek_to_pcm_frame(&m_mae.sounds[layer], start); //result = ma_data_source_set_loop_point_in_pcm_frames(&m_mae.sounds[layer], start, end); // this do nothing here, it must be done after set_looping or start? @@ -531,14 +535,14 @@ void MiniAudioEngine::refreshValues(int layer) { this->seekToCursor(layer, m_mae.currentStatus[layer].cursor); this->panChanged(layer, m_mae.currentStatus[layer].pan); - this->volChanged(layer, m_mae.currentStatus[layer].vol); this->pitchChanged(layer, m_mae.currentStatus[layer].pitch); + ma_sound_group_set_fade_in_milliseconds(&m_mae.sounds[layer], -1, 0, 0.0f); this->playbackChanged(layer, m_mae.currentStatus[layer].status); + this->volChanged(layer, m_mae.currentStatus[layer].vol); } -ma_result MiniAudioEngine::filterParamChanged(int layer, int audioDevice, int channel, int value) +ma_result MiniAudioEngine::filterParamChanged(int layer, int channel, int value) { - (void)audioDevice; ma_result result = MA_SUCCESS; filterBank *fb = &m_mae.filters[layer]; diff --git a/src/miniaudioengine.h b/src/miniaudioengine.h index b97a9e9..38107d8 100644 --- a/src/miniaudioengine.h +++ b/src/miniaudioengine.h @@ -65,7 +65,7 @@ protected: MiniAudioEngine(); void stopEngine(); bool startEngine(uint layersQty, uint* audioDevicesID, uint audioDevicesQty); - ma_result loadMedia(int layer, char *media, uint audioDevice); + ma_result loadMedia(int layer, char *media); void volChanged(int layer, int vol); void panChanged(int layer, float pan); void pitchChanged(int layer, float pitch); @@ -79,11 +79,11 @@ protected: return ma_sound_get_volume(&m_mae.sounds[layer]); }; inline bool getAtEnd(int layer) { return m_mae.sounds[layer].atEnd; } - ma_result filterParamChanged(int layer, int audioDevice, int channel, int value); + ma_result filterParamChanged(int layer, int channel, int value); bool setBypass(int audioDevice, int layer, bool bypass); inline float getLevel(int layer) { float level = ma_vumeter_node_get_level(&m_mae.filters[layer].vumeter); - return ma_volume_linear_to_db(level); + return ma_volume_linear_to_db(level) - 4.0f; }; char* getDeviceName(uint id); From 6a22534686e84215e24aaa24ba6eea54dae349ef Mon Sep 17 00:00:00 2001 From: snt Date: Fri, 24 May 2024 19:48:45 +0200 Subject: [PATCH 35/38] =?UTF-8?q?solucionado=20el=20glich=20de=20verdad...?= =?UTF-8?q?=20los=20loop=20points=20y=20range=20points=20no=20funcionan=20?= =?UTF-8?q?al=20final=20de=20l=20loop,=20es=20igual=20que=20seektoPCMFrame?= =?UTF-8?q?=20en=20su=20estado=20actual.=20cambios=20cosm=C3=A9ticos?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/roadmap.txt | 2 + libremediaserver-audio.pro | 3 +- src/audiolayerwidget.cpp | 30 +++++++------- src/defines.h | 4 +- src/libremediaserver-audio.cpp | 6 ++- src/miniaudioengine.cpp | 74 +++++++++++++++++++++++++++------- src/miniaudioengine.h | 3 ++ 7 files changed, 87 insertions(+), 35 deletions(-) diff --git a/docs/roadmap.txt b/docs/roadmap.txt index bc786a9..9e4f017 100644 --- a/docs/roadmap.txt +++ b/docs/roadmap.txt @@ -40,3 +40,5 @@ v 0.3.0 - Hardening: check return errors, try/catch exceptions, i'm too happy.... - Tests: errors on wrong conf file. - ampliar writer para recibir un número n de entradas y escribirlas cada una en un buffer +- aislar miniaudio del callback dmx tal como hemos hecho con la Ui, al menos las operaciones lentas como cargar medios. +- en load media usar un fence para actualizar mediaLoaded. diff --git a/libremediaserver-audio.pro b/libremediaserver-audio.pro index 391f2c9..67c50c5 100644 --- a/libremediaserver-audio.pro +++ b/libremediaserver-audio.pro @@ -39,9 +39,8 @@ SOURCES += src/main.cpp \ src/settings.cpp \ src/slidergroup.cpp FORMS += src/libremediaserver-audio-gui.ui -CCFLAG += -msse2 -mavx2 #-fsanitize=address -g3 -O0 +CCFLAG += -msse2 -mavx2 QMAKE_CXXFLAGS += $$(CXXFLAG) -#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 b1eb8b7..42c4838 100644 --- a/src/audiolayerwidget.cpp +++ b/src/audiolayerwidget.cpp @@ -93,33 +93,35 @@ AudioLayerWidget::AudioLayerWidget(QWidget *parent, int layer): layout->addLayout(volumeBox); QHBoxLayout *labelsBox = new QHBoxLayout; m_level = new QLabel("-inf"); - m_level->setStyleSheet("border: 1px solid #5a4855;" + m_level->setStyleSheet("border: 1px solid #CFB0C9;" "margin: 0px;" - "background-color: #180014;" + "margin-right: 5px;" + "margin-left: 5px;" + "background-color: black;" "color: white;" - "width:70px;" + "width:80px;" "text-align: center;" ); - m_level->setMinimumWidth(70); + m_level->setMinimumWidth(80); + m_level->setMaximumWidth(80); + m_level->setMinimumHeight(20); m_level->setAlignment(Qt::AlignHCenter); labelsBox->addWidget(m_level); m_bus1Label = new QLabel("dummy"); m_bus1Label->setMinimumWidth(80); - m_bus1Label->setStyleSheet("border: 1px solid #5a4855;" + m_bus1Label->setStyleSheet("border: 1px solid #CFB0C9;" "margin: 0px;" - "background-color: #383034;" - "width:70px;" - ); - m_bus1Label->setAlignment(Qt::AlignHCenter); + "background-color: black;" + "color: white;" + "font-size: 11px;"); labelsBox->addWidget(m_bus1Label); m_bus2Label = new QLabel("dummy"); m_bus2Label->setMinimumWidth(80); - m_bus2Label->setStyleSheet("border: 1px solid #5a4855;" + m_bus2Label->setStyleSheet("border: 1px solid #CFB0C9;" "margin: 0px;" - "background-color: #383034;" - "width: 70px;" - ); - m_bus2Label->setAlignment(Qt::AlignHCenter); + "background-color: black;" + "color: white;" + "font-size: 11px;"); labelsBox->addWidget(m_bus2Label); labelsBox->setSpacing(0); labelsBox->setContentsMargins(0, 0, 0, 0); diff --git a/src/defines.h b/src/defines.h index 6751fd0..e19ecf7 100644 --- a/src/defines.h +++ b/src/defines.h @@ -10,8 +10,8 @@ #define FORMAT ma_format_f32 /* Must always be f32. */ #define CHANNELS 2 #define SAMPLE_RATE 48000 -#define UI_REFRESH_TIME 97 -#define FADE_TIME 25 // DMX Frame time, 40 fps, avoid clicks +#define UI_REFRESH_TIME 123 +#define FADE_TIME 20 #define FILTER_CHANNELS 16 // number of dmx channels dedicated to filters by layer struct dmxSetting { diff --git a/src/libremediaserver-audio.cpp b/src/libremediaserver-audio.cpp index dd6cedd..c9d2dd3 100644 --- a/src/libremediaserver-audio.cpp +++ b/src/libremediaserver-audio.cpp @@ -129,6 +129,7 @@ void libreMediaServerAudio::dmxInput(int layer, int channel, int value) #ifndef NOGUI if (m_ui) { m_lmsUi->m_aw->playbackChanged(layer, s); + m_updateUi[layer][3] = 1; m_played.clear(); m_played.append(m_ola->getValue(layer, DMX_FILE)); } @@ -173,6 +174,7 @@ void libreMediaServerAudio::refreshUi() { if (m_mae.getAtEnd(i)) { if (m_currentStatus[i] == Status::PlayingOnce) { m_currentStatus[i] = Status::Stopped; + m_lmsUi->m_aw->playbackChanged(i, Status::Stopped); } if (m_currentStatus[i] == Status::PlayingFolder) { uint last = m_played.last(); @@ -187,7 +189,7 @@ void libreMediaServerAudio::refreshUi() { m_lmsUi->m_aw->playbackChanged(i, Status::Stopped); } } - if (m_currentStatus[i] == Status::PlayingFolderLoop) { + else if (m_currentStatus[i] == Status::PlayingFolderLoop) { uint last = m_played.last(); int folder = m_ola->getValue(i, DMX_FOLDER); last++; @@ -199,7 +201,7 @@ void libreMediaServerAudio::refreshUi() { m_mae.playbackChanged(i, Status::PlayingFolder); } } - if (m_currentStatus[i] == Status::PlayingFolderRandom) { + else 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)) diff --git a/src/miniaudioengine.cpp b/src/miniaudioengine.cpp index 1d0d5b3..d2ebc17 100644 --- a/src/miniaudioengine.cpp +++ b/src/miniaudioengine.cpp @@ -278,7 +278,7 @@ ma_result MiniAudioEngine::startDevices() deviceConfig.dataCallback = audioDataCallback; engineConfig = ma_engine_config_init(); engineConfig.pResourceManager = &m_mae.resourceManager; - engineConfig.defaultVolumeSmoothTimeInPCMFrames = SAMPLE_RATE / 20; + //engineConfig.defaultVolumeSmoothTimeInPCMFrames = SAMPLE_RATE / 20; engineConfig.noAutoStart = MA_TRUE; for (uint internalId = 0; internalId < m_mae.audioDevicesQty; internalId++) { @@ -482,38 +482,85 @@ ma_result MiniAudioEngine::playbackChanged(int layer, Status status) case Status::PlayingLoop: loop = true; case Status::PlayingOnce: + ma_sound_set_looping(&m_mae.sounds[layer], loop); + if (m_mae.currentStatus[layer].cursor > 0) { + this->setRangePoint(layer, m_mae.currentStatus[layer].cursor); + this->setLoopPoint(layer, m_mae.currentStatus[layer].cursor); + } case Status::PlayingFolder: case Status::PlayingFolderLoop: case Status::PlayingFolderRandom: ma_sound_set_stop_time_in_milliseconds(&m_mae.sounds[layer], ~(ma_uint64)0); - ma_sound_set_looping(&m_mae.sounds[layer], loop); - result = ma_sound_start(&m_mae.sounds[layer]); - //this->volChanged(layer, m_mae.currentStatus[layer].vol); float db = (m_mae.currentStatus[layer].vol / 771.0f) - 85.0f; if (db <= -85.0f) { db = 0; } else db = ma_volume_db_to_linear(db); - ma_sound_set_fade_in_milliseconds(&m_mae.sounds[layer], -1, db, 0); + result = ma_sound_start(&m_mae.sounds[layer]); + ma_sound_group_set_volume(&m_mae.sounds[layer], 0.0f); + ma_sound_set_fade_in_milliseconds(&m_mae.sounds[layer], 0, db, FADE_TIME); + ma_sound_group_set_volume(&m_mae.sounds[layer], 1.0f); } if (result == MA_SUCCESS) m_mae.currentStatus[layer].status = status; return result; } -ma_result MiniAudioEngine::seekToCursor(int layer, int cursor) +ma_result MiniAudioEngine::setRangePoint(int layer, int cursor) { ma_result result = MA_SUCCESS; - ma_uint64 end, start; + ma_uint64 end = 0, start; if (m_mae.mediaLoaded[layer] == false) return MA_DOES_NOT_EXIST; - result = ma_sound_get_length_in_pcm_frames(&m_mae.sounds[layer], &end); - if (result != MA_SUCCESS) { return result; } - start = (cursor * end) / 65535; - Status oldStatus = m_mae.currentStatus[layer].status; + if (cursor == 0) + start = 0; + else { + result = ma_sound_get_length_in_pcm_frames(&m_mae.sounds[layer], &end); + if (result != MA_SUCCESS) { return result; } + start = (cursor * end) / 65535; + } + result = ma_data_source_set_range_in_pcm_frames(&m_mae.sounds[layer].pDataSource, start, end); + if (result != MA_SUCCESS) + cout << "ERROR " << result << " :set range point" << endl; + return (result); +} + +ma_result MiniAudioEngine::setLoopPoint(int layer, int cursor) +{ + ma_result result = MA_SUCCESS; + ma_uint64 end = 0, start; + + if (m_mae.mediaLoaded[layer] == false) + return MA_DOES_NOT_EXIST; + if (cursor == 0) + start = 0; + else { + result = ma_sound_get_length_in_pcm_frames(&m_mae.sounds[layer], &end); + if (result != MA_SUCCESS) { return result; } + start = (cursor * end) / 65535; + } + result = ma_data_source_set_loop_point_in_pcm_frames(&m_mae.sounds[layer].pDataSource, start + 1, end - 1); + if (result != MA_SUCCESS) + cout << "ERROR " << result << " :set loop point" << endl; + return (result); +} + +ma_result MiniAudioEngine::seekToCursor(int layer, int cursor) +{ + ma_result result = MA_SUCCESS; + ma_uint64 end = 0, start; + + if (m_mae.mediaLoaded[layer] == false) + return MA_DOES_NOT_EXIST; + if (cursor == 0) + start = 0; + else { + result = ma_sound_get_length_in_pcm_frames(&m_mae.sounds[layer], &end); + if (result != MA_SUCCESS) { return result; } + start = (cursor * end) / 65535; + } result = ma_sound_seek_to_pcm_frame(&m_mae.sounds[layer], start); - //result = ma_data_source_set_loop_point_in_pcm_frames(&m_mae.sounds[layer], start, end); // this do nothing here, it must be done after set_looping or start? return (result); } @@ -533,12 +580,9 @@ Status MiniAudioEngine::getStatus(int layer) void MiniAudioEngine::refreshValues(int layer) { - this->seekToCursor(layer, m_mae.currentStatus[layer].cursor); this->panChanged(layer, m_mae.currentStatus[layer].pan); this->pitchChanged(layer, m_mae.currentStatus[layer].pitch); - ma_sound_group_set_fade_in_milliseconds(&m_mae.sounds[layer], -1, 0, 0.0f); this->playbackChanged(layer, m_mae.currentStatus[layer].status); - this->volChanged(layer, m_mae.currentStatus[layer].vol); } ma_result MiniAudioEngine::filterParamChanged(int layer, int channel, int value) diff --git a/src/miniaudioengine.h b/src/miniaudioengine.h index 38107d8..21deff3 100644 --- a/src/miniaudioengine.h +++ b/src/miniaudioengine.h @@ -3,6 +3,7 @@ #define MA_ENABLE_ONLY_SPECIFIC_BACKENDS #define MA_ENABLE_JACK +#define MA_DISABLE_PULSE #define MA_NO_GENERATION #define MA_DEBUG_OUTPUT #define MA_LOG_LEVEL_DEBUG DEBUG @@ -97,6 +98,8 @@ private: ma_result seekToCursor(int layer, int cursor); ma_result setNodeGraph(); ma_result createFilterBank(uint layer); + ma_result setLoopPoint(int layer, int cursor); + ma_result setRangePoint(int layer, int cursor); }; #endif // MINIAUDIOENGINE_H From 029bcdc47ea8dbd24bb9ef4301e83f69ae4ddd70 Mon Sep 17 00:00:00 2001 From: snt Date: Sat, 25 May 2024 17:35:53 +0200 Subject: [PATCH 36/38] =?UTF-8?q?cambios=20cosm=C3=A9ticos.=20protege=20me?= =?UTF-8?q?jor=20el=20cambio=20de=20medios.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/audiolayerwidget.cpp | 83 ++++++++++++++++++++++-------- src/audiowidget.cpp | 5 +- src/clickabledoublespinbox.cpp | 8 +-- src/defines.h | 4 +- src/filterbankwidget.cpp | 21 ++++---- src/libremediaserver-audio-gui.cpp | 5 +- src/libremediaserver-audio-gui.ui | 4 +- src/miniaudioengine.cpp | 69 +++++++++++++------------ 8 files changed, 123 insertions(+), 76 deletions(-) diff --git a/src/audiolayerwidget.cpp b/src/audiolayerwidget.cpp index 42c4838..0e9527b 100644 --- a/src/audiolayerwidget.cpp +++ b/src/audiolayerwidget.cpp @@ -13,21 +13,33 @@ AudioLayerWidget::AudioLayerWidget(QWidget *parent, int layer): QVBoxLayout *layout = new QVBoxLayout; m_folderValue = new ClickableLabel; - m_folderValue->setMaximumWidth(300); m_folderValue->setAlignment(Qt::AlignLeft); + m_folderValue->setFocusPolicy(Qt::NoFocus); + m_folderValue->setMinimumWidth(MIN_WIDTH); + m_folderValue->setMaximumWidth(MAX_WIDTH); + m_folderValue->setContentsMargins(3,1,3,1); + m_folderValue->setText("Click to open media file (mp3, wav, flac)"); m_folderValue->setStyleSheet( + "margin: 0px;" "color: white;" "background-color: black;" + "font-size: 12px;" ); layout->addWidget(m_folderValue); m_fileValue = new ClickableLabel; + m_fileValue->setAlignment(Qt::AlignLeft); + m_fileValue->setFocusPolicy(Qt::NoFocus); + m_fileValue->setMinimumWidth(MIN_WIDTH); + m_fileValue->setMaximumWidth(MAX_WIDTH); + m_fileValue->setContentsMargins(3,1,3,1); + m_fileValue->setText("++ empty ++"); connect(m_fileValue, SIGNAL(clicked()), this, SLOT(openMediaDialog())); connect(m_folderValue, SIGNAL(clicked()), this, SLOT(openMediaDialog())); - m_fileValue->setMaximumWidth(300); - m_fileValue->setAlignment(Qt::AlignLeft); m_fileValue->setStyleSheet( + "margin: 0px;" "color: white;" "background-color: black;" + "font-size: 14px;" ); layout->addWidget(m_fileValue); @@ -38,9 +50,20 @@ AudioLayerWidget::AudioLayerWidget(QWidget *parent, int layer): m_progress = new QProgressBar(this); m_progress->setOrientation(Qt::Horizontal); + m_progress->setFocusPolicy(Qt::NoFocus); + m_progress->setAlignment(Qt::AlignHCenter); + m_progress->setContentsMargins(0, 1, 0, 1); + m_progress->setMinimumWidth(MIN_WIDTH); + m_progress->setMaximumWidth(MAX_WIDTH); + m_progress->setMaximumHeight(15); m_progress->setRange(0, 0); m_progress->setValue(0); m_progress->setFormat("%v / %m"); + m_progress->setStyleSheet( + "margin: 0px;" + "font-size: 13px;" + "background-color: black;" + ); layout->addWidget(m_progress); m_progressTime = new QTimeEdit; @@ -49,26 +72,30 @@ AudioLayerWidget::AudioLayerWidget(QWidget *parent, int layer): m_progressTime->setDisplayFormat("mm:ss:zzz"); m_progressTime->setReadOnly(true); m_progressTime->setButtonSymbols(QAbstractSpinBox::NoButtons); - m_progressTime->setMinimumWidth(80); m_progressTime->setFocusPolicy(Qt::NoFocus); m_progressTime->setAlignment(Qt::AlignHCenter); m_progressTime->setContentsMargins(0,0,0,0); + m_progressTime->setMinimumWidth(MIN_WIDTH); + m_progressTime->setMaximumWidth(MAX_WIDTH); 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); + m_totalTimeValue->setMinimumWidth(MIN_WIDTH); + m_totalTimeValue->setMaximumWidth(MAX_WIDTH); QHBoxLayout *status = new QHBoxLayout; status->addWidget(m_progressTime); status->addWidget(m_totalTimeValue); layout->addLayout(status); m_filterBank = new FilterBankWidget(this); + m_filterBank->setMaximumWidth(MAX_WIDTH); + m_filterBank->setMinimumWidth(MIN_WIDTH); connect(m_filterBank, SIGNAL(setBypass(bool)), this, SLOT(setBypass(bool))); layout->addWidget(m_filterBank); m_pitch = new SliderGroup("Pitch", 0 , 255, 0, NULL); @@ -95,40 +122,45 @@ AudioLayerWidget::AudioLayerWidget(QWidget *parent, int layer): m_level = new QLabel("-inf"); m_level->setStyleSheet("border: 1px solid #CFB0C9;" "margin: 0px;" - "margin-right: 5px;" - "margin-left: 5px;" "background-color: black;" "color: white;" - "width:80px;" "text-align: center;" - ); - m_level->setMinimumWidth(80); - m_level->setMaximumWidth(80); - m_level->setMinimumHeight(20); + "font-size: 15px;"); + m_level->setMinimumWidth(MIN_WIDTH / 2); + m_level->setMaximumWidth(MAX_WIDTH / 3); + m_level->setMinimumHeight(25); m_level->setAlignment(Qt::AlignHCenter); + m_level->setContentsMargins(0, 4, 0, 4); labelsBox->addWidget(m_level); m_bus1Label = new QLabel("dummy"); - m_bus1Label->setMinimumWidth(80); + m_bus1Label->setAlignment(Qt::AlignHCenter); + m_bus1Label->setContentsMargins(0, 6, 0, 6); + m_bus1Label->setMinimumHeight(25); + m_bus1Label->setMinimumWidth(MIN_WIDTH / 4); + m_bus1Label->setMaximumWidth(MAX_WIDTH / 3); m_bus1Label->setStyleSheet("border: 1px solid #CFB0C9;" "margin: 0px;" "background-color: black;" "color: white;" - "font-size: 11px;"); + "font-size: 10px;"); labelsBox->addWidget(m_bus1Label); m_bus2Label = new QLabel("dummy"); - m_bus2Label->setMinimumWidth(80); + m_bus2Label->setAlignment(Qt::AlignHCenter); + m_bus2Label->setMinimumWidth(MIN_WIDTH / 4); + m_bus2Label->setMaximumWidth(MAX_WIDTH / 3); + m_bus2Label->setContentsMargins(0, 6, 0, 6); + m_bus2Label->setMinimumHeight(25); m_bus2Label->setStyleSheet("border: 1px solid #CFB0C9;" "margin: 0px;" "background-color: black;" "color: white;" - "font-size: 11px;"); + "font-size: 10px;"); labelsBox->addWidget(m_bus2Label); labelsBox->setSpacing(0); - labelsBox->setContentsMargins(0, 0, 0, 0); + layout->setContentsMargins(0, 0, 0, 0); layout->addLayout(labelsBox); layout->setAlignment(Qt::AlignHCenter); layout->setSpacing(0); - layout->setContentsMargins(1, 1, 1, 1); this->setLayout(layout); } @@ -230,8 +262,12 @@ void AudioLayerWidget::setMediaFile(QString file) QStringList list = file.split("/"); int size = list.size(); if (size >= 2) { - m_folderValue->setText(list.at(size - 2)); - m_fileValue->setText(list.at(size - 1)); + QString tmp = list.at(size - 2); + tmp.truncate(60); + m_folderValue->setText(tmp); + tmp = list.at(size - 1); + tmp.truncate(40); + m_fileValue->setText(tmp); } } @@ -288,8 +324,9 @@ void AudioLayerWidget::setFilterParam(int channel, int value) void AudioLayerWidget::setLevel(float db) { m_level->blockSignals(true); - if (db > -150) - m_level->setText(QString::number(db)); + if (db > -140) + + m_level->setText(QString::number(db, 'f', 2)); else m_level->setText("-inf"); m_level->blockSignals(false); @@ -297,6 +334,8 @@ void AudioLayerWidget::setLevel(float db) void AudioLayerWidget::setBusName(uint bus, char *name) { + if (name && strlen(name) > 17) + name[16] = '\0'; if (bus == 0) { m_bus1Label->blockSignals(true); m_bus1Label->setText(name); diff --git a/src/audiowidget.cpp b/src/audiowidget.cpp index 8d43127..ec96a9e 100644 --- a/src/audiowidget.cpp +++ b/src/audiowidget.cpp @@ -23,8 +23,9 @@ AudioWidget::AudioWidget(QWidget *parent) : for (int j = 0; j < FILTER_CHANNELS; j++) m_filtersUpdate[i][j] = -1; } - m_layout->setSpacing(2); - m_layout->setContentsMargins(2, 2, 2, 2); + m_layout->setSpacing(0); + m_layout->setContentsMargins(0, 0, 0, 0); + this->setStyleSheet("margin: 2px;"); setLayout(m_layout); m_refreshUi = new QTimer(this); connect(m_refreshUi, SIGNAL(timeout()), this, SLOT(refreshUi())); diff --git a/src/clickabledoublespinbox.cpp b/src/clickabledoublespinbox.cpp index b2b9c3a..76feb8b 100644 --- a/src/clickabledoublespinbox.cpp +++ b/src/clickabledoublespinbox.cpp @@ -11,11 +11,7 @@ ClickableDoubleSpinBox::ClickableDoubleSpinBox(QWidget *parent) setDecimals(1); setAlignment(Qt::AlignHCenter); setContentsMargins(0, 0, 0, 0); - setMaximumWidth(50); - this->setStyleSheet("border: 0px solid #5a4855;" - "width: 50px;" - "margin: 0px;" - "background-color: #383034;" - ); + setMaximumWidth(66); + setMinimumWidth(25); } diff --git a/src/defines.h b/src/defines.h index e19ecf7..e9d4bf2 100644 --- a/src/defines.h +++ b/src/defines.h @@ -11,8 +11,10 @@ #define CHANNELS 2 #define SAMPLE_RATE 48000 #define UI_REFRESH_TIME 123 -#define FADE_TIME 20 +#define FADE_TIME 25 #define FILTER_CHANNELS 16 // number of dmx channels dedicated to filters by layer +#define MAX_WIDTH 500 +#define MIN_WIDTH 50 struct dmxSetting { int address; diff --git a/src/filterbankwidget.cpp b/src/filterbankwidget.cpp index 4742af2..098e591 100644 --- a/src/filterbankwidget.cpp +++ b/src/filterbankwidget.cpp @@ -2,20 +2,22 @@ #include #include "dmxPersonality.h" +#include "defines.h" + +#define BORDER "#CFB0C9;" +#define BACK "#281024;" FilterBankWidget::FilterBankWidget(QWidget *parent) : QWidget{parent} { QHBoxLayout *layout = new QHBoxLayout; layout->setAlignment(Qt::AlignHCenter); - layout->setContentsMargins(0, 2, 0, 2); layout->setSpacing(0); - this->setStyleSheet("border: 2px solid #5a4855;" + layout->setContentsMargins(0, 0, 0, 0); + this->setStyleSheet("border: 1px solid #CFB0C9;" "margin: 0px;" - "margin-top: 3px;" - "margin-bottom: 3px;" - "background-color: #383034;" - ); + "background-color: #080402;" + "font-size: 13px;"); for (int i = 0; i < 13; i++) { fb[i] = new ClickableDoubleSpinBox; const char *name = dmxChannelToString(i + 9); @@ -27,10 +29,11 @@ FilterBankWidget::FilterBankWidget(QWidget *parent) m_bypass = new QCheckBox; master->addWidget(m_bypass); m_bypass->setText("Bypass"); - m_bypass->setStyleSheet("QCheckBox { border: 2px solid #2a0825;" + m_bypass->setMinimumWidth(MIN_WIDTH / 4); + m_bypass->setStyleSheet("QCheckBox { border: 1px solid #CFB0C9;" "margin: 0px;" - "background-color: #885074;" - "font-size: 7px;}"); + "background-color: #c82840;" + "font-size: 8px;}"); connect(m_bypass, SIGNAL(stateChanged(int)), this, SLOT(bypassChanged(int))); master->addWidget(fb[0]); layout->addLayout(master); diff --git a/src/libremediaserver-audio-gui.cpp b/src/libremediaserver-audio-gui.cpp index 22f673c..62b4544 100644 --- a/src/libremediaserver-audio-gui.cpp +++ b/src/libremediaserver-audio-gui.cpp @@ -35,10 +35,11 @@ libreMediaServerAudioUi::libreMediaServerAudioUi(QWidget *parent) topWidget->setContentsMargins(0, 0, 0, 0); addDockWidget(Qt::TopDockWidgetArea, topWidget); connect(ui.actionLaunch_OLA_Setup, SIGNAL(triggered()), this, SLOT(olasetup())); - this->setContentsMargins(3, 3, 3, 3); + this->setContentsMargins(0, 0, 0, 0); this->setStyleSheet( + "margin: 0px;" "color: white;" - "background-color: #4f4048;" + "background-color: #3f3038;" "selection-color: blue;" "selection-background-color: green" ); diff --git a/src/libremediaserver-audio-gui.ui b/src/libremediaserver-audio-gui.ui index c0f11ad..ad4c061 100644 --- a/src/libremediaserver-audio-gui.ui +++ b/src/libremediaserver-audio-gui.ui @@ -7,7 +7,7 @@ 0 0 - 500 + 400 400 @@ -32,7 +32,7 @@ 0 0 - 500 + 400 21 diff --git a/src/miniaudioengine.cpp b/src/miniaudioengine.cpp index d2ebc17..edc5f78 100644 --- a/src/miniaudioengine.cpp +++ b/src/miniaudioengine.cpp @@ -1,7 +1,7 @@ #include "miniaudioengine.h" #include "dmxPersonality.h" -#define BIAS 0.99f +#define BIAS 1.0f #define FILTER_ORDER 3 MiniAudioEngine::MiniAudioEngine() {} @@ -52,12 +52,13 @@ bool MiniAudioEngine::startEngine(uint layers, uint* audioDevicesId, uint audioD m_mae.audioDevicesId = audioDevicesId; m_mae.audioDevicesQty = audioDevicesQty; for (uint i =0; i < m_mae.layersQty; i++) { - m_mae.mediaLoaded[i] = false; + m_mae.mediaLoaded[i] = MA_FALSE; m_mae.currentStatus[i].status = Status::Iddle; m_mae.currentStatus[i].pan = 128; m_mae.currentStatus[i].pitch = 128; - m_mae.currentStatus[i].vol = 0; + m_mae.currentStatus[i].vol = 0.0f; m_mae.currentStatus[i].cursor = 0; + m_mae.currentStatus[i].updated = false; } result = this->startContext(); if (result != MA_SUCCESS) return false; @@ -278,7 +279,7 @@ ma_result MiniAudioEngine::startDevices() deviceConfig.dataCallback = audioDataCallback; engineConfig = ma_engine_config_init(); engineConfig.pResourceManager = &m_mae.resourceManager; - //engineConfig.defaultVolumeSmoothTimeInPCMFrames = SAMPLE_RATE / 20; + engineConfig.defaultVolumeSmoothTimeInPCMFrames = SAMPLE_RATE / 500; engineConfig.noAutoStart = MA_TRUE; for (uint internalId = 0; internalId < m_mae.audioDevicesQty; internalId++) { @@ -350,12 +351,12 @@ ma_result MiniAudioEngine::loadMedia(int layer, char *file) { ma_result result; - if (m_mae.mediaLoaded[layer] == true) + if (m_mae.mediaLoaded[layer] == MA_TRUE) { + m_mae.mediaLoaded[layer] = MA_FALSE; ma_sound_set_volume(&m_mae.sounds[layer], 0.0f); ma_sound_stop(&m_mae.sounds[layer]); ma_sound_uninit(&m_mae.sounds[layer]); - m_mae.mediaLoaded[layer] = false; } ma_sound_config soundConfig = ma_sound_config_init(); soundConfig = ma_sound_config_init(); @@ -370,9 +371,10 @@ ma_result MiniAudioEngine::loadMedia(int layer, char *file) cout << "Error" << result << ": Failed to load file " << file << endl; return result; } - m_mae.mediaLoaded[layer] = true; - this->refreshValues(layer); m_mae.currentStatus[layer].media = file; + m_mae.currentStatus[layer].updated = true; + m_mae.mediaLoaded[layer] = MA_TRUE; + this->refreshValues(layer); return result; } @@ -431,7 +433,8 @@ ma_result MiniAudioEngine::printFormatInfo(int layer) // Expects between 0 and 65535 vol value void MiniAudioEngine::volChanged(int layer, int vol) { - if (m_mae.mediaLoaded[layer] == false) + m_mae.currentStatus[layer].vol = vol; + if (m_mae.mediaLoaded[layer] == MA_FALSE && m_mae.currentStatus[layer].updated) return; float db = ((float)vol / 771.0f) - 85.0f; if (db <= -85.0f) { @@ -439,70 +442,72 @@ void MiniAudioEngine::volChanged(int layer, int vol) } else db = ma_volume_db_to_linear(db); ma_sound_set_fade_in_milliseconds(&m_mae.sounds[layer], -1, db, FADE_TIME); - m_mae.currentStatus[layer].vol = vol; } void MiniAudioEngine::panChanged(int layer, float value) { float result; + m_mae.currentStatus[layer].pan = value; if (m_mae.mediaLoaded[layer] == false) return; result = (value / 128.0) - 1.0; ma_sound_group_set_pan(&m_mae.sounds[layer], result); - m_mae.currentStatus[layer].pan = value; } void MiniAudioEngine::pitchChanged(int layer, float value) { float pitch; + m_mae.currentStatus[layer].pitch = value; if (m_mae.mediaLoaded[layer] == false) return; pitch = value / 128.0; ma_sound_group_set_pitch(&m_mae.sounds[layer], pitch); - m_mae.currentStatus[layer].pitch = value; } ma_result MiniAudioEngine::playbackChanged(int layer, Status status) { ma_result result = MA_SUCCESS; - - if (m_mae.mediaLoaded[layer] == false) - return MA_DOES_NOT_EXIST; + float db = 0; bool loop = false; + + m_mae.currentStatus[layer].status = status; + if (m_mae.mediaLoaded[layer] == MA_FALSE) + return MA_DOES_NOT_EXIST; + m_mae.currentStatus[layer].updated = false; switch (status) { case Status::Paused: result = ma_sound_stop_with_fade_in_milliseconds(&m_mae.sounds[layer], FADE_TIME); break; case Status::Stopped: - ma_sound_stop_with_fade_in_milliseconds(&m_mae.sounds[layer], FADE_TIME); + ma_sound_stop_with_fade_in_milliseconds(&m_mae.sounds[layer], 0.0f); result = this->seekToCursor(layer, m_mae.currentStatus[layer].cursor); break; case Status::PlayingLoop: loop = true; - case Status::PlayingOnce: - ma_sound_set_looping(&m_mae.sounds[layer], loop); - if (m_mae.currentStatus[layer].cursor > 0) { - this->setRangePoint(layer, m_mae.currentStatus[layer].cursor); - this->setLoopPoint(layer, m_mae.currentStatus[layer].cursor); + if (m_mae.currentStatus[layer].cursor > 0) { + result = this->seekToCursor(layer, m_mae.currentStatus[layer].cursor); } + case Status::PlayingOnce: case Status::PlayingFolder: case Status::PlayingFolderLoop: case Status::PlayingFolderRandom: + ma_sound_set_looping(&m_mae.sounds[layer], loop); + if (ma_sound_is_playing(&m_mae.sounds[layer])) break; ma_sound_set_stop_time_in_milliseconds(&m_mae.sounds[layer], ~(ma_uint64)0); - float db = (m_mae.currentStatus[layer].vol / 771.0f) - 85.0f; - if (db <= -85.0f) { - db = 0; - } else - db = ma_volume_db_to_linear(db); + db = (m_mae.currentStatus[layer].vol / 771.0f) - 85.0f; + if (db <= -85.0f) db = 0; + else db = ma_volume_db_to_linear(db); result = ma_sound_start(&m_mae.sounds[layer]); - ma_sound_group_set_volume(&m_mae.sounds[layer], 0.0f); - ma_sound_set_fade_in_milliseconds(&m_mae.sounds[layer], 0, db, FADE_TIME); - ma_sound_group_set_volume(&m_mae.sounds[layer], 1.0f); + ma_sound_set_fade_in_milliseconds(&m_mae.sounds[layer], 0.000001f, 0.000000f, FADE_TIME); + if (m_mae.currentStatus[layer].cursor > 0) + usleep(FADE_TIME * 1500); // avoid glitch when load when seeking + ma_sound_set_fade_in_milliseconds(&m_mae.sounds[layer], 0, db, FADE_TIME * 2); + default: + break; } - if (result == MA_SUCCESS) - m_mae.currentStatus[layer].status = status; + m_mae.currentStatus[layer].updated = true; return result; } @@ -719,5 +724,5 @@ bool MiniAudioEngine::setBypass(int audioDevice, int layer, bool bypass) ma_node_set_output_bus_volume(&fb->input, 1, 0.0f); ma_node_set_output_bus_volume(&fb->input, 0, 1.0f); } - + return true; } From d0f565e1c0b9b8b46265d1785fe2b8377191bb82 Mon Sep 17 00:00:00 2001 From: snt Date: Sun, 26 May 2024 14:40:15 +0200 Subject: [PATCH 37/38] cosmetic, fix pan and pitch spinbox, style slider. --- src/audiolayerwidget.cpp | 16 +++++++++++++-- src/slidergroup.cpp | 43 +++++++++++++++++++++++++++++----------- 2 files changed, 45 insertions(+), 14 deletions(-) diff --git a/src/audiolayerwidget.cpp b/src/audiolayerwidget.cpp index 0e9527b..9b6ecf7 100644 --- a/src/audiolayerwidget.cpp +++ b/src/audiolayerwidget.cpp @@ -61,7 +61,7 @@ AudioLayerWidget::AudioLayerWidget(QWidget *parent, int layer): m_progress->setFormat("%v / %m"); m_progress->setStyleSheet( "margin: 0px;" - "font-size: 13px;" + "font-size: 10px;" "background-color: black;" ); layout->addWidget(m_progress); @@ -77,6 +77,12 @@ AudioLayerWidget::AudioLayerWidget(QWidget *parent, int layer): m_progressTime->setContentsMargins(0,0,0,0); m_progressTime->setMinimumWidth(MIN_WIDTH); m_progressTime->setMaximumWidth(MAX_WIDTH); + m_progressTime->setStyleSheet( + "margin: 0px;" + "color: white;" + "background-color: black;" + "font-size: 16px;" + ); m_totalTimeValue = new QTimeEdit; m_totalTimeValue->setObjectName("Track Length"); m_totalTimeValue->setToolTip("Track Length"); @@ -88,6 +94,12 @@ AudioLayerWidget::AudioLayerWidget(QWidget *parent, int layer): m_totalTimeValue->setContentsMargins(0,0,0,0); m_totalTimeValue->setMinimumWidth(MIN_WIDTH); m_totalTimeValue->setMaximumWidth(MAX_WIDTH); + m_totalTimeValue->setStyleSheet( + "margin: 0px;" + "color: white;" + "background-color: black;" + "font-size: 16px;" + ); QHBoxLayout *status = new QHBoxLayout; status->addWidget(m_progressTime); status->addWidget(m_totalTimeValue); @@ -160,7 +172,7 @@ AudioLayerWidget::AudioLayerWidget(QWidget *parent, int layer): layout->setContentsMargins(0, 0, 0, 0); layout->addLayout(labelsBox); layout->setAlignment(Qt::AlignHCenter); - layout->setSpacing(0); + layout->setSpacing(1); this->setLayout(layout); } diff --git a/src/slidergroup.cpp b/src/slidergroup.cpp index 9eedb70..481d554 100644 --- a/src/slidergroup.cpp +++ b/src/slidergroup.cpp @@ -18,7 +18,8 @@ SliderGroup::SliderGroup(QString name, else { layout = new QHBoxLayout; slider.setOrientation(Qt::Horizontal); - slider.setMinimumHeight(10); + slider.setMaximumHeight(15); + valueBox.setMaximumHeight(15); } layout->setAlignment(Qt::AlignHCenter); layout->setContentsMargins(0, 0, 0, 0); @@ -32,9 +33,24 @@ SliderGroup::SliderGroup(QString name, slider.setMinimumWidth(50); slider.setToolTip(name); slider.setStyleSheet("QSlider {" - "border: 1px solid #5a4855;" + "border: 1px solid #aa8895;" + "background: #20182d;" "margin: 0px;}" - ); + "QSlider::groove:vertical {" + "border: 1px solid #999999;" + "width: 25px;" + "margin: -4px;}" + "QSlider::handle:vertical {" + "background: white;" + "border: 1px solid #5c5c5c;" + "width: 29px;" + "height: 7px;" + "margin: -2px;" + "border-radius: 2px;}" + "Qslider::tickmarks:vertical {background: white;" + "color: white;}" + "QSlider::add-page:vertical {background: blue;}" + "QSlider::sub-page:vertical {background: #20182d;}"); slider.setContentsMargins(0, 0, 0, 0); valueBox.setFocusPolicy(Qt::NoFocus); valueBox.setButtonSymbols(QAbstractSpinBox::NoButtons); @@ -54,10 +70,9 @@ SliderGroup::SliderGroup(QString name, connect(&valueBox, SIGNAL(click()), this, SLOT(enableSlider())); layout->addWidget(&slider); layout->addWidget(&valueBox); - this->setStyleSheet("border: 1px solid #5a4855;" - "width: 60px;" - "margin: 0px;" - "background-color: #383034;" + this->setStyleSheet("border: 1px solid #aa8895;" + "background-color: black;" + "margin: 1px;" ); layout->setSpacing(0); layout->setContentsMargins(0, 0, 0, 0); @@ -67,11 +82,15 @@ SliderGroup::SliderGroup(QString name, void SliderGroup::sliderValueChanged(int value) { valueBox.blockSignals(true); - float db = ((float)value / 771.0f) - 85.0f; - if (db <= -84.5f) { - valueBox.setSpecialValueText("-inf"); - } else - valueBox.setValue(db); + if (valueBox.decimals()) { + float db = ((float)value / 771.0f) - 85.0f; + if (db <= -84.5f) { + valueBox.setSpecialValueText("-inf"); + } else + valueBox.setValue(db); + } else { + valueBox.setValue(value); + } valueBox.blockSignals(false); emit valueChanged(value); }; From 33d9cd699ef65f6e578b875083cf70e500b1d485 Mon Sep 17 00:00:00 2001 From: snt Date: Sun, 26 May 2024 15:08:41 +0200 Subject: [PATCH 38/38] v0.2.0 antigona --- docs/changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index 5cd2ca3..ac07acf 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -30,6 +30,7 @@ v 0.2.0 Antígona (26/05/2024) + Multi audio devices output. + Vumeter for each layer + Show device name on Ui and ouput bus slider. ++ 84dae057dbf7c0 v 0.1.3 Leúcade (19/04/2024) + Ubuntu 22.04 jammy.