Antigona Release #1

Merged
snt merged 49 commits from filters into main 2024-05-26 12:42:53 +00:00
12 changed files with 165 additions and 23 deletions
Showing only changes of commit 53bcb38455 - Show all commits

View file

@ -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.

View file

@ -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

View file

@ -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);
}

View file

@ -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;

View file

@ -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;
}

View file

@ -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;

View file

@ -75,6 +75,7 @@ struct layerData {
int device;
int bus1Vol;
int bus2Vol;
float level;
};
#endif // __cplusplus
#endif // DEFINES_H

View file

@ -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;

View file

@ -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);
}

View file

@ -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

View file

@ -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);

View file

@ -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;