Antigona Release #1
8 changed files with 390 additions and 90 deletions
|
@ -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 \
|
||||
|
|
|
@ -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 <lms@criptomart.net>"
|
||||
#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 <lms@criptomart.net>"
|
||||
#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 <QString>
|
||||
struct layerData {
|
||||
|
@ -81,4 +72,5 @@ struct layerData {
|
|||
unsigned int universe;
|
||||
int device;
|
||||
};
|
||||
#endif // __cplusplus
|
||||
#endif // DEFINES_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 "++--++--++";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
175
src/ma_writer_node.c
Normal file
175
src/ma_writer_node.c
Normal file
|
@ -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);
|
||||
}
|
52
src/ma_writer_node.h
Normal file
52
src/ma_writer_node.h
Normal file
|
@ -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 */
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 <bits/stdc++.h>
|
||||
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();
|
||||
|
|
Loading…
Add table
Reference in a new issue