From 103a33820ef8c407f6b02519c1e286378d2bd0e3 Mon Sep 17 00:00:00 2001 From: snt Date: Fri, 10 May 2024 20:03:14 +0200 Subject: [PATCH 01/25] 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 02/25] 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 03/25] 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 04/25] 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 05/25] 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 06/25] =?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 07/25] =?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 08/25] =?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 09/25] =?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 10/25] 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 11/25] =?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 12/25] 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 13/25] 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 14/25] =?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 15/25] =?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 16/25] =?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 17/25] =?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 18/25] =?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 19/25] =?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 20/25] 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 21/25] =?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 22/25] =?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 23/25] =?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 24/25] 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 25/25] 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.