From cd0105c9f9d66173dd8f6d61e5eec3327990b1fb Mon Sep 17 00:00:00 2001 From: snt Date: Wed, 8 May 2024 18:56:16 +0200 Subject: [PATCH] =?UTF-8?q?quitado=20peque=C3=B1o=20glitch=20cuando=20entr?= =?UTF-8?q?y=20point=20no=20es=20cero.=20stop=20con=20fade=20out=20para=20?= =?UTF-8?q?evitar=20click.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/roadmap.txt | 6 +- src/audiolayerwidget.cpp | 10 ++-- src/audiowidget.cpp | 11 +++- src/defines.h | 2 +- src/libremediaserver-audio.cpp | 24 ++++++-- src/miniaudioengine.cpp | 105 ++++++++++++++++++--------------- src/miniaudioengine.h | 14 +++-- src/settings.cpp | 4 +- 8 files changed, 105 insertions(+), 71 deletions(-) diff --git a/docs/roadmap.txt b/docs/roadmap.txt index 153c596..94081f4 100644 --- a/docs/roadmap.txt +++ b/docs/roadmap.txt @@ -5,7 +5,6 @@ https://git.criptomart.net/libremediaserver ******************************************************************************* Libre Media Server Roadmap -(en continuo crecimiento...) v 0.2.x - skin, UI/UX @@ -45,9 +44,12 @@ v 0.2.1 - Ui/Ux: Dar la opción clickeando en el widget de tiempo de poner una cuenta atrás en vez de hacia delante. - Logs, verbosity, timestamp. - New play mode without pitch control, it saves resources. MA_SOUND_FLAG_NO_PITCH -- Vumeter or indicator about audio output in layer and master, add to sliderGroup. - SettingsDialog. - Load/save conf file. - ¿Exit Point? is it needed? - Hardening: check return errors, try/catch exceptions, i'm too happy.... - Tests: errors on wrong conf file. + +v0.2.0: +- BUGFIX: crash at startup and no dmx signal. +- Vumeter or indicator about audio output in layer and master, add to sliderGroup. diff --git a/src/audiolayerwidget.cpp b/src/audiolayerwidget.cpp index 4a07451..e8d22bd 100644 --- a/src/audiolayerwidget.cpp +++ b/src/audiolayerwidget.cpp @@ -10,8 +10,8 @@ AudioLayerWidget::AudioLayerWidget(QWidget *parent, int layer): QVBoxLayout *playback = new QVBoxLayout; m_folderValue = new ClickableLabel; - //m_folderValue->setMaximumWidth(160); - m_folderValue->setAlignment(Qt::AlignHCenter); + m_folderValue->setMaximumWidth(160); + m_folderValue->setAlignment(Qt::AlignLeft); m_folderValue->setStyleSheet( "color: white;" "background-color: black;" @@ -20,8 +20,8 @@ AudioLayerWidget::AudioLayerWidget(QWidget *parent, int layer): m_fileValue = new ClickableLabel; connect(m_fileValue, SIGNAL(clicked()), this, SLOT(openMediaDialog())); connect(m_folderValue, SIGNAL(clicked()), this, SLOT(openMediaDialog())); - //m_fileValue->setMaximumWidth(160); - m_fileValue->setAlignment(Qt::AlignHCenter); + m_fileValue->setMaximumWidth(160); + m_fileValue->setAlignment(Qt::AlignLeft); m_fileValue->setStyleSheet( "color: white;" "background-color: black;" @@ -33,7 +33,6 @@ AudioLayerWidget::AudioLayerWidget(QWidget *parent, int layer): m_suspendResumeButton = new QPushButton(this); m_suspendResumeButton->setText(StatusStr[Status::Iddle]); - //m_suspendResumeButton->setMaximumWidth(180); connect(m_suspendResumeButton, SIGNAL(clicked()), SLOT(toggleSuspendResume())); layout->addWidget(m_suspendResumeButton); @@ -42,7 +41,6 @@ AudioLayerWidget::AudioLayerWidget(QWidget *parent, int layer): m_progress->setRange(0, 0); m_progress->setValue(0); m_progress->setFormat("%v / %m"); - //m_progress->setMaximumWidth(180); layout->addWidget(m_progress); m_progressTime = new QTimeEdit; diff --git a/src/audiowidget.cpp b/src/audiowidget.cpp index ca2633a..8bd077c 100644 --- a/src/audiowidget.cpp +++ b/src/audiowidget.cpp @@ -11,13 +11,20 @@ AudioWidget::AudioWidget(QWidget *parent) : connect(alw, SIGNAL(uiSliderChanged(int, Slider, int)), this, SIGNAL(uiSliderChanged(int, Slider, int))); connect(alw, SIGNAL(uiPlaybackChanged(int, Status)), this, SIGNAL(uiPlaybackChanged(int, Status))); connect(alw, SIGNAL(uiLoadMedia(int, QString)), this, SIGNAL(uiLoadMedia(int, QString))); + m_layerUpdate[i].status = Status::Iddle; + m_layerUpdate[i].duration = 0; + m_layerUpdate[i].media = ""; + m_layerUpdate[i].vol = 0; + m_layerUpdate[i].pan = 128; + m_layerUpdate[i].pitch = 128; + m_layerUpdate[i].cursor = 0; } m_layout->setSpacing(0); m_layout->setContentsMargins(1, 1, 1, 1); setLayout(m_layout); m_refreshUi = new QTimer(this); connect(m_refreshUi, SIGNAL(timeout()), this, SLOT(refreshUi())); - m_refreshUi->start(UI_REFRESH_TIME * 2); + m_refreshUi->start(UI_REFRESH_TIME * 1.5); } void AudioWidget::mediaLoaded(int layer, QString file, float duration) @@ -51,7 +58,7 @@ void AudioWidget::playbackChanged(int layer, Status status) void AudioWidget::cursorChanged(int layer, float cursor) { - m_layerUpdate[layer].cursor = floor(cursor * 1000) / 1000; + m_layerUpdate[layer].cursor = cursor; m_layerUpdate[layer].updated = true; } diff --git a/src/defines.h b/src/defines.h index 6552d19..a9c92b8 100644 --- a/src/defines.h +++ b/src/defines.h @@ -7,7 +7,7 @@ #define DEFAULT_FILE "lms-audio.xlm" #define MAX_LAYERS 4 #define MAX_AUDIODEVICES 8 -#define UI_REFRESH_TIME 77 +#define UI_REFRESH_TIME 66 #define FADE_TIME 25 // DMX Frame time, 40 fps, avoid clicks struct dmxSetting { diff --git a/src/libremediaserver-audio.cpp b/src/libremediaserver-audio.cpp index ab12820..2ea3266 100644 --- a/src/libremediaserver-audio.cpp +++ b/src/libremediaserver-audio.cpp @@ -108,7 +108,7 @@ void libreMediaServerAudio::dmxInput(int layer, int channel, int value) #ifndef NOGUI if (m_ui) { m_lmsUi->m_aw->playbackChanged(layer, s); - m_lmsUi->m_aw->cursorChanged(layer, m_mae.getCursor(layer)); + //m_lmsUi->m_aw->cursorChanged(layer, m_mae.getCursor(layer)); } #endif } @@ -170,16 +170,28 @@ void libreMediaServerAudio::uiSliderChanged(int layer, Slider s, int value) void libreMediaServerAudio::uiPlaybackChanged(int layer, Status s) { - m_mae.playbackChanged(layer, s); - m_currentStatus[layer] = s; + ma_result result; + + result = m_mae.playbackChanged(layer, s); + if (result == MA_SUCCESS) { + m_currentStatus[layer] = s; + } else { + qWarning() << "ui playback change error" << result << "status" << s << "layer" << layer; + } } void libreMediaServerAudio::uiLoadMedia(int layer, QString mediaFile) { + ma_result result; + if (strcmp(mediaFile.toLatin1().constData(), m_currentMedia[layer].toLatin1().constData()) == 0) return; - m_mae.loadMedia(layer, mediaFile.toLatin1().data()); - m_currentMedia[layer] = mediaFile; - m_lmsUi->m_aw->mediaLoaded(layer, mediaFile, m_mae.getDuration(layer)); + result = m_mae.loadMedia(layer, mediaFile.toLatin1().data()); + if (result == MA_SUCCESS) { + m_currentMedia[layer] = mediaFile; + m_lmsUi->m_aw->mediaLoaded(layer, mediaFile, m_mae.getDuration(layer)); + } else { + qWarning() << "ui load media error" << result << "file" << mediaFile << "layer" << layer; + } } #endif diff --git a/src/miniaudioengine.cpp b/src/miniaudioengine.cpp index fa58464..d1040b9 100644 --- a/src/miniaudioengine.cpp +++ b/src/miniaudioengine.cpp @@ -1,5 +1,5 @@ #include "miniaudioengine.h" - +#include //enum macro MiniAudioEngine::MiniAudioEngine() { for (int i =0; i < MAX_LAYERS; i++) { @@ -27,25 +27,26 @@ void MiniAudioEngine::stopEngine() ma_resource_manager_uninit(&resourceManager); } -bool MiniAudioEngine::startEngine(int n) +bool MiniAudioEngine::startEngine(uint n) { ma_result result; result = this->startContext(); - if (result != MA_SUCCESS) { - return result; - } - this->getAllAudioDevices(); + if (result != MA_SUCCESS) return result; + result = this->getAllAudioDevices(); + if (result != MA_SUCCESS) return result; result = this->startDevice(n); return result; } -ma_result MiniAudioEngine::startDevice(int id) +ma_result MiniAudioEngine::startDevice(uint id) { ma_result result; ma_device_config deviceConfig; ma_engine_config engineConfig; + if (id >= playbackDeviceCount) + id = playbackDeviceCount - 1; deviceConfig = ma_device_config_init(ma_device_type_playback); deviceConfig.playback.pDeviceID = &pPlaybackDeviceInfos[id].id; deviceConfig.playback.format = resourceManager.config.decodedFormat; @@ -73,7 +74,7 @@ ma_result MiniAudioEngine::startDevice(int id) return result; } iChosenDevice = id; - qInfo("Initialized audio device %d: %s", id, pPlaybackDeviceInfos[id].name); + qInfo("Initialized audio device %d : %s", id, pPlaybackDeviceInfos[id].name); return result; } @@ -85,7 +86,7 @@ ma_result MiniAudioEngine::startContext() resourceManagerConfig.decodedFormat = ma_format_f32; /* ma_format_f32 should almost always be used as that's what the engine (and most everything else) uses for mixing. */ resourceManagerConfig.decodedChannels = 0; resourceManagerConfig.decodedSampleRate = ma_standard_sample_rate_48000; - resourceManagerConfig.jobThreadCount = 1; + resourceManagerConfig.jobThreadCount = 4; result = ma_resource_manager_init(&resourceManagerConfig, &resourceManager); if (result != MA_SUCCESS) { qCritical("Failed to initialize audio resource manager."); @@ -109,7 +110,7 @@ ma_result MiniAudioEngine::getAllAudioDevices() ma_context_uninit(&context); return result; } - printf("Audio devices detected in system:\n"); + printf("Audio devices available:\n"); for (ma_uint32 iAvailableDevice = 0; iAvailableDevice < playbackDeviceCount; iAvailableDevice += 1) { qInfo("%d: : %s", iAvailableDevice, pPlaybackDeviceInfos[iAvailableDevice].name); } @@ -127,10 +128,8 @@ ma_result MiniAudioEngine::loadMedia(int layer, char *file) } result = ma_sound_init_from_file(&engine, file, \ MA_SOUND_FLAG_NO_SPATIALIZATION \ - /*| MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE \ - | MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC \ - | MA_SOUND_FLAG_NO_PITCH \ - | MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM \*/ + | MA_SOUND_FLAG_DECODE \ + /*| MA_SOUND_FLAG_NO_PITCH \*/ , NULL, NULL, &m_currentSound[layer]); if (result != MA_SUCCESS) qWarning("Failed to load file %s", file); @@ -157,7 +156,7 @@ float MiniAudioEngine::getDuration(int layer) } result = ma_sound_get_data_format(&m_currentSound[layer], NULL, NULL, &sampleRate, NULL, 0); if (result != MA_SUCCESS) { - return result; + return MA_ERROR; } ret = 1000.0f * (lengthInPCMFrames / float(sampleRate)); return ret; @@ -173,8 +172,8 @@ float MiniAudioEngine::getCursor(int layer) result = ma_sound_get_cursor_in_seconds(&m_currentSound[layer], &ret); if (result != MA_SUCCESS) { - qWarning("Can not get cursor %i", layer); - ret = -1; + qWarning("%i can not get cursor error %i", layer, result); + ret = MA_ERROR; } return ret; } @@ -188,11 +187,10 @@ ma_result MiniAudioEngine::printFormatInfo(int layer) if (m_mediaLoaded[layer] == false) return MA_DOES_NOT_EXIST; ma_result result = ma_sound_get_data_format(&m_currentSound[layer], &format, &channels, &sampleRate, NULL, 0); - if (result != MA_SUCCESS) { - qWarning("Failed to get data format %i\n", layer); - return MA_INVALID_DATA; - } - qInfo() << "Layer:" << layer << m_currentLayerValues[layer].media << "samples/sec:" << sampleRate << "format:" << format << "channels:" << channels; + if (result != MA_SUCCESS) + qWarning("%i failed to get data format %i\n", layer, result); + else + qInfo() << "Layer:" << layer << m_currentLayerValues[layer].media << "samples/sec:" << sampleRate << "format:" << format << "channels:" << channels; return result; } @@ -220,55 +218,70 @@ void MiniAudioEngine::panChanged(int layer, float value) void MiniAudioEngine::pitchChanged(int layer, float value) { - float result; + float pitch; if (m_mediaLoaded[layer] == false) return; - result = value / 128.0; - ma_sound_group_set_pitch(&m_currentSound[layer], result); + pitch = value / 128.0; + ma_sound_group_set_pitch(&m_currentSound[layer], pitch); m_currentLayerValues[layer].pitch = value; } -void MiniAudioEngine::playbackChanged(int layer, Status status) +ma_result MiniAudioEngine::playbackChanged(int layer, Status status) { + ma_result result = MA_SUCCESS; + if (m_mediaLoaded[layer] == false) - return; + return MA_DOES_NOT_EXIST; switch (status) { case Status::Paused: - ma_sound_stop(&m_currentSound[layer]); + result = ma_sound_stop_with_fade_in_milliseconds(&m_currentSound[layer], FADE_TIME); break; case Status::Stopped: - ma_sound_stop(&m_currentSound[layer]); - this->setCursor(layer, m_currentLayerValues[layer].cursor); + result = ma_sound_stop_with_fade_in_milliseconds(&m_currentSound[layer], FADE_TIME); + result = this->seekToCursor(layer, m_currentLayerValues[layer].cursor); break; case Status::PlayingLoop: + ma_sound_set_stop_time_in_milliseconds(&m_currentSound[layer], ~(ma_uint64)0); ma_sound_set_looping(&m_currentSound[layer], true); - ma_sound_start(&m_currentSound[layer]); - break; + result = ma_sound_start(&m_currentSound[layer]); + break; case Status::PlayingOnce: + ma_sound_set_stop_time_in_milliseconds(&m_currentSound[layer], ~(ma_uint64)0); ma_sound_set_looping(&m_currentSound[layer], false); - ma_sound_start(&m_currentSound[layer]); + result = ma_sound_start(&m_currentSound[layer]); break; default: break; } - m_currentLayerValues[layer].status = status; + if (result == MA_SUCCESS) + m_currentLayerValues[layer].status = status; + return result; } -void MiniAudioEngine::setCursor(int layer, int cursor) +ma_result MiniAudioEngine::seekToCursor(int layer, int cursor) { - ma_uint64 end; + ma_result result = MA_SUCCESS; + ma_uint64 end, start; + + if (m_mediaLoaded[layer] == false) + return MA_DOES_NOT_EXIST; + result = ma_sound_get_length_in_pcm_frames(&m_currentSound[layer], &end); + if (result != MA_SUCCESS) { return result; } + start = (cursor * end) / 65025; + result = ma_sound_seek_to_pcm_frame(&m_currentSound[layer], start); + //if (result != MA_SUCCESS) { return result; } + //result = ma_data_source_set_loop_point_in_pcm_frames(&m_currentSound[layer], start, end); + return (result); +} + +ma_result MiniAudioEngine::setCursor(int layer, int cursor) +{ + ma_result result = MA_SUCCESS; m_currentLayerValues[layer].cursor = cursor; - if (m_mediaLoaded[layer] == false) - return; - ma_sound_get_length_in_pcm_frames(&m_currentSound[layer], &end); - ma_uint64 start = (cursor * end) / 65025; - ma_sound_seek_to_pcm_frame(&m_currentSound[layer], start); - ma_result result = ma_data_source_set_loop_point_in_pcm_frames(&m_currentSound[layer], start, end); - if (result != MA_SUCCESS) { - return; // Failed to set the loop point. - } + result = this->seekToCursor(layer, cursor); + return (result); } Status MiniAudioEngine::getStatus(int layer) @@ -278,9 +291,9 @@ Status MiniAudioEngine::getStatus(int layer) void MiniAudioEngine::refreshValues(int layer) { + this->seekToCursor(layer, m_currentLayerValues[layer].cursor); this->panChanged(layer, m_currentLayerValues[layer].pan); this->volChanged(layer, m_currentLayerValues[layer].vol); this->pitchChanged(layer, m_currentLayerValues[layer].pitch); this->playbackChanged(layer, m_currentLayerValues[layer].status); - this->setCursor(layer, m_currentLayerValues[layer].cursor); } diff --git a/src/miniaudioengine.h b/src/miniaudioengine.h index 2c3ec5a..6d0ea19 100644 --- a/src/miniaudioengine.h +++ b/src/miniaudioengine.h @@ -13,7 +13,7 @@ class MiniAudioEngine public: MiniAudioEngine(); void stopEngine(); - bool startEngine(int id); + bool startEngine(uint id); static void audioDataCallback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount); protected: @@ -21,13 +21,14 @@ protected: void volChanged(int layer, float vol); void panChanged(int layer, float pan); void pitchChanged(int layer, float pitch); - void playbackChanged(int layer, Status status); + ma_result playbackChanged(int layer, Status status); + ma_result setCursor(int layer, int cursor); + ma_result printFormatInfo(int layer); float getDuration(int layer); float getCursor(int layer); - void setCursor(int layer, int cursor); - ma_result printFormatInfo(int layer); Status getStatus(int layer); - inline float getVol(int layer) { return ma_sound_get_volume(&m_currentSound[layer]); } + inline float getVol(int layer) { + return ma_sound_get_volume(&m_currentSound[layer]); } private: ma_resource_manager_config resourceManagerConfig; @@ -43,9 +44,10 @@ private: layerData m_currentLayerValues[MAX_LAYERS]; ma_result getAllAudioDevices(); - ma_result startDevice(int id); + ma_result startDevice(uint id); ma_result startContext(); void refreshValues(int layer); + ma_result seekToCursor(int layer, int cursor); }; #endif // MINIAUDIOENGINE_H diff --git a/src/settings.cpp b/src/settings.cpp index 9e00914..9d06394 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -81,9 +81,9 @@ void Settings::readFromFile(QString file) { } void Settings::printSettings() { - qInfo() << "Settings read;\nShow Ui:" << m_ui << "Layers:" << m_layersNumber << "Path:" << m_pathmedia <<"Audio Device qty:" << m_audioDeviceQty; + qInfo() << "Settings readed:\nShow Ui:" << m_ui << "Layers:" << m_layersNumber << "Path:" << m_pathmedia <<"Audio Device qty:" << m_audioDeviceQty; for (uint i = 0; i < m_audioDeviceQty; i++) - qInfo() << "Audio device id:" << m_audioDeviceId[i]; + qInfo() << "Audio device internal id:" << i << "system id:" << m_audioDeviceId[i]; for (int i = 0; i < m_layersNumber; i++) qInfo() << "Layer:" << m_settings[i].layer << "Address:" << m_settings[i].address << "Universe:" << m_settings[i].universe; }