From fc274179ad4cb6613fac27eb8bd07c95cd5bf020 Mon Sep 17 00:00:00 2001 From: snt Date: Sat, 18 May 2024 22:52:22 +0200 Subject: [PATCH] =?UTF-8?q?funcionando=20en=20dos=20dispositivos=20mediant?= =?UTF-8?q?e=20ring=20buffer,=20pero=20no=20puedo=20mandar=20a=20dos=20dis?= =?UTF-8?q?positivos,=20si=20lo=20pongo=20con=20ma=5Fsplitter=20reproduce?= =?UTF-8?q?=20m=C3=A1s=20r=C3=A1pido=20y=20con=20glitches=20mandar=20difer?= =?UTF-8?q?entes?= 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();