diff --git a/CMakeLists.txt b/CMakeLists.txt index 2e8e2ab..ad0ba03 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,7 +44,7 @@ include(cmake/fetch_content.cmake) # in code coverage computation as they are test programs themselves. set(EXTRA_COVERAGE_EXCLUSION "\'${CMAKE_CURRENT_SOURCE_DIR}/integration/*\'") -project(bofstd VERSION 5.5.3.11) +project(bofstd VERSION 5.5.4.3) if (EMSCRIPTEN) message("Force pthread detection for BofStd compilation under EMSCRIPTEN") diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index fe257c7..d584aea 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -171,7 +171,7 @@ set(LOG_FILES include/bofstd/boflogsink_spdlog.h src/boflogsink_spdlog.cpp include/bofstd/ibofloggerfactory.h - include/bofstd/basic_loggerfactory + include/bofstd/basic_loggerfactory.h include/bofstd/boflogger.h src/boflogger.cpp ) @@ -228,6 +228,8 @@ set(SYSTEM_FILES src/boftimecode.cpp include/bofstd/bofvideostandard.h src/bofvideostandard.cpp + include/bofstd/bofaudio.h + src/bofaudio.cpp include/bofstd/bofaudiostandard.h src/bofaudiostandard.cpp include/bofstd/bofdatetime.h @@ -335,6 +337,7 @@ target_include_directories(bofstd #$ + $ ) # Link diff --git a/lib/include/bofstd/bofaudio.h b/lib/include/bofstd/bofaudio.h new file mode 100644 index 0000000..a17560e --- /dev/null +++ b/lib/include/bofstd/bofaudio.h @@ -0,0 +1,402 @@ +/* + * Copyright (c) 2024-2044, EVS Broadcast Equipment S.A. All rights reserved. + * + * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + * KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + * PURPOSE. + * + * This module defines different audio interface + * + * Author: Bernard HARMEL: b.harmel@evs.com + * + * History: + * V 1.00 Apr 11 2024 BHA : Initial release + */ +#pragma once +#include +#include +#include +#include +#include + +BEGIN_BOF_NAMESPACE() +#pragma pack(1) +struct BOF_WAV_FILE_HEADER +{ + // RIFF Chunk Descriptor + char pChunkId_c[4]; // "RIFF" (4 bytes) + uint32_t ChunkSize_U32; // File size - 8 (4 bytes) + char pFormat_c[4]; // "WAVE" (4 bytes) + + // Format Chunk + char pSubchunk1Id_c[4]; // "fmt " (4 bytes) + uint32_t SubChunk1Size_U32; // Size of the fmt chunk (16 or 18 bytes for PCM) (4 bytes) + uint16_t AudioFormat_U16; // Audio format (PCM = 1) (2 bytes) + uint16_t NbChannel_U16; // Number of channels (mono = 1, stereo = 2, etc.) (2 bytes) + uint32_t SampleRate_U32; // Sample rate (e.g., 44100 Hz) (4 bytes) + uint32_t ByteRate_U32; // Byte rate (sampleRate * numChannels * bitsPerSample / 8) (4 bytes) + uint16_t BlockAlign_U16; // Block align (numChannels * bitsPerSample / 8) (2 bytes) + uint16_t BitPerSample_U16; // Bits per sample (8, 16, etc.) (2 bytes) + char pDataId_c[4]; // "data" (4 bytes) + uint32_t SubChunk2Size_U32; // Si + +}; +#pragma pack() +enum class BOF_WAVE_FORM_TYPE +{ + BOF_WAVE_FORM_TYPE_LEVEL = 0, + BOF_WAVE_FORM_TYPE_SINUS, + BOF_WAVE_FORM_TYPE_COSINUS, + BOF_WAVE_FORM_TYPE_SQUARE, + BOF_WAVE_FORM_TYPE_TRIANGLE, + BOF_WAVE_FORM_TYPE_SAW_TOOTH, + BOF_WAVE_FORM_TYPE_MAX +}; + +template +class BofRampGenerator +{ +public: + BofRampGenerator(T _FromSample, T _ToSample, T _StepSample) + { + Reset(_FromSample, _ToSample, _StepSample); + } + ~BofRampGenerator() + { + } + bool Reset() + { + return Reset(mFromSample, mToSample, mStepSample); + } + bool Reset(T _FromSample, T _ToSample, T _StepSample) + { + bool Rts_B = false; + + if (((_StepSample > static_cast(0)) && (_ToSample >= _FromSample)) || ((_StepSample < static_cast(0)) && (_ToSample <= _FromSample))) + { + Rts_B = true; + mFromSample = _FromSample; + mToSample = _ToSample; + mStepSample = _StepSample; + mCrtSample = mFromSample; + } + return Rts_B; + } + + T *Generate(uint32_t _HeaderSizeInByte_U32, uint32_t _ChunkSize_U32, uint32_t _FooterSizeInByte_U32, T *_pData) + { + T *pRts=nullptr; + uint32_t i_U32; + + if (_pData) + { + pRts = _pData; + _pData = reinterpret_cast(reinterpret_cast(_pData) + _HeaderSizeInByte_U32); + for (i_U32 = 0; i_U32 < _ChunkSize_U32; i_U32++) + { + _pData[i_U32] = mCrtSample; + mCrtSample += mStepSample; + if (mCrtSample > mToSample) + { + mCrtSample = mFromSample; + } + } + _pData += i_U32; + pRts = reinterpret_cast(reinterpret_cast(_pData) + _FooterSizeInByte_U32); + } + return pRts; + } + +private: + T mFromSample = std::numeric_limits::min(); + T mToSample = std::numeric_limits::max(); + T mStepSample = 0; + T mCrtSample = 0; +}; + +template +class BofWaveformGenerator +{ +public: + BofWaveformGenerator(BOF_WAVE_FORM_TYPE _WaveForm_E, T _Amplitude, uint32_t _Frequency_U32, uint32_t _SampleRate_U32, float _InitialPhase_f) + { + Reset(_WaveForm_E, _Amplitude, _Frequency_U32, _SampleRate_U32, _InitialPhase_f); + } + ~BofWaveformGenerator() + { + } + bool Reset(float _InitialPhase_f) + { + return Reset(mWaveForm_E, mAmplitude, mFrequency_U32, mSampleRate_U32, _InitialPhase_f); + } + bool Reset(float _InitialPhase_f, T _Amplitude) + { + return Reset(mWaveForm_E, _Amplitude, mFrequency_U32, mSampleRate_U32, _InitialPhase_f); + } + bool Reset(float _InitialPhase_f, T _Amplitude, uint32_t _Frequency_U32) + { + return Reset(mWaveForm_E, _Amplitude, _Frequency_U32, mSampleRate_U32, _InitialPhase_f); + } + bool Reset(BOF_WAVE_FORM_TYPE _WaveForm_E, T _Amplitude, uint32_t _Frequency_U32, uint32_t _SampleRate_U32, float _InitialPhase_f) + { + bool Rts_B = false; + + // Amplitude can be < 0 for level if ((_WaveForm_E < BOF_WAVE_FORM_TYPE::BOF_WAVE_FORM_TYPE_MAX) && (_Amplitude > 0.0f) && (_Frequency_U32) && (_SampleRate_U32)) + if ((_WaveForm_E < BOF_WAVE_FORM_TYPE::BOF_WAVE_FORM_TYPE_MAX) && (_Frequency_U32) && (_SampleRate_U32)) + { + Rts_B = true; + mWaveForm_E = _WaveForm_E; + mAmplitude = _Amplitude; + mFrequency_U32 = _Frequency_U32; + mSampleRate_U32 = _SampleRate_U32; + mCrtPhase_lf = fmodf(_InitialPhase_f, 2.0L * static_cast(M_PI)); // Keep phase within 0 to 2*PI range + mLastPhase_lf = mCrtPhase_lf; + } + return Rts_B; + } + float CurrentPhase() + { + return mCrtPhase_lf; + } + float LastPhase() + { + return mLastPhase_lf; + } + T *Generate(uint32_t _HeaderSizeInByte_U32, uint32_t _ChunkSize_U32, uint32_t _FooterSizeInByte_U32, T *_pDataY) + { + T *pRts = nullptr; + uint32_t i_U32; + double IncPhase_lf; // , PeriodFactor_lf; + + if ((mWaveForm_E < BOF_WAVE_FORM_TYPE::BOF_WAVE_FORM_TYPE_MAX) && (_ChunkSize_U32) && (_pDataY)) + { + pRts = _pDataY; + // After _SampleRate_f / _Frequency_f we have covered 2 M_PI, so for Fr 440 Fe 44100 we roll over angle after 100 sample->So for a chunk of 1024 byte we have 10 sinus + IncPhase_lf = (2.0L * static_cast(M_PI) * static_cast(mFrequency_U32)) / static_cast(mSampleRate_U32); + mLastPhase_lf = mCrtPhase_lf; + /* PeriodFactor_f / 10.0f->the ajust factor is 0.628 ??? + Triangle 2.0f * M_PI / 2.0f (2PI for one cycle, divided by 2 for two triangles) + Sawtooth 2.0f * M_PI * 0.25f (2PI for one cycle, compressed to 0-1 range, adjusted by 0.25) + */ + // PeriodFactor_lf = 2.0L * static_cast(M_PI) / 10.0f; // *_Frequency_f / _SampleRate_f; + _pDataY = reinterpret_cast(reinterpret_cast(_pDataY) + _HeaderSizeInByte_U32); + switch (mWaveForm_E) + { + case BOF_WAVE_FORM_TYPE::BOF_WAVE_FORM_TYPE_LEVEL: + for (i_U32 = 0; i_U32 < _ChunkSize_U32; i_U32++) + { + _pDataY[i_U32] = static_cast(mAmplitude); + } + break; + case BOF_WAVE_FORM_TYPE::BOF_WAVE_FORM_TYPE_SINUS: + for (i_U32 = 0; i_U32 < _ChunkSize_U32; i_U32++) + { + _pDataY[i_U32] = static_cast(static_cast(mAmplitude) * sin(mCrtPhase_lf)); + if constexpr (std::is_same_v) + { + // printf("i %d Ph %f Amp %f sin %f v %d %08X\n", i_U32, mPhase_f * 360.0f / M_PI, static_cast(_Amplitude), sin(mPhase_f), _pDataY[i_U32], _pDataY[i_U32]); + } + mCrtPhase_lf += IncPhase_lf; + } + break; + case BOF_WAVE_FORM_TYPE::BOF_WAVE_FORM_TYPE_COSINUS: + for (i_U32 = 0; i_U32 < _ChunkSize_U32; i_U32++) + { + _pDataY[i_U32] = static_cast(static_cast(mAmplitude) * cos(mCrtPhase_lf)); + mCrtPhase_lf += IncPhase_lf; + } + break; + case BOF_WAVE_FORM_TYPE::BOF_WAVE_FORM_TYPE_SQUARE: + for (i_U32 = 0; i_U32 < _ChunkSize_U32; i_U32++) + { + _pDataY[i_U32] = (sin(mCrtPhase_lf) >= 0) ? static_cast(mAmplitude) : static_cast(-mAmplitude); + mCrtPhase_lf += IncPhase_lf; + } + break; + case BOF_WAVE_FORM_TYPE::BOF_WAVE_FORM_TYPE_TRIANGLE: + for (i_U32 = 0; i_U32 < _ChunkSize_U32; i_U32++) + { + _pDataY[i_U32] = static_cast(static_cast(mAmplitude) * (acos(sin(mCrtPhase_lf)) / static_cast(M_PI_2) - 1.0f)); + // _pDataY[i_U32] = static_cast((static_cast(mAmplitude) * fabs(fmod(mCrtPhase_lf * PeriodFactor_lf, 4.0f) - 2.0f)) - static_cast(mAmplitude)); + mCrtPhase_lf += IncPhase_lf; + } + break; + case BOF_WAVE_FORM_TYPE::BOF_WAVE_FORM_TYPE_SAW_TOOTH: + for (i_U32 = 0; i_U32 < _ChunkSize_U32; i_U32++) + { + //_pDataY[i_U32] = static_cast((static_cast(mAmplitude) * (2.0f * fmod(mCrtPhase_lf * PeriodFactor_lf * 0.25f, 1.0f)) - static_cast(mAmplitude))); + // Calculate sawtooth value using linear function + _pDataY[i_U32] = static_cast(static_cast(mAmplitude) * (((mCrtPhase_lf / (M_PI))) - 1.0f)); + mCrtPhase_lf += IncPhase_lf; + mCrtPhase_lf = fmodf(mCrtPhase_lf, 2.0L * static_cast(M_PI)); // Keep phase within 0 to 2*PI range + } + break; + + default: + pRts = nullptr; + break; + } + + if (pRts) + { + mCrtPhase_lf = fmodf(mCrtPhase_lf, 2.0L * static_cast(M_PI)); // Keep phase within 0 to 2*PI range + _pDataY += i_U32; + pRts = reinterpret_cast(reinterpret_cast(_pDataY) + _FooterSizeInByte_U32); + } + } + return pRts; + } + +private: + BOF_WAVE_FORM_TYPE mWaveForm_E = BOF_WAVE_FORM_TYPE::BOF_WAVE_FORM_TYPE_MAX; + T mAmplitude = 0; + uint32_t mFrequency_U32 = 0; + uint32_t mSampleRate_U32 = 0; + double mCrtPhase_lf = 0.0f; + double mLastPhase_lf = 0.0f; +}; + +struct BOF_AUDIO_DEVICE_PARAM +{ + uint32_t Dummy_U32; + BOF_AUDIO_DEVICE_PARAM() + { + Reset(); + } + void Reset() + { + Dummy_U32 = 0; + } +}; +enum BOF_AUDIO_FORMAT : uint32_t +{ + BOF_AUDIO_FORMAT_UNKNOWN = 0, + BOF_AUDIO_FORMAT_U8, + BOF_AUDIO_FORMAT_S16, + BOF_AUDIO_FORMAT_S24, + BOF_AUDIO_FORMAT_S32, + BOF_AUDIO_FORMAT_F32, + BOF_AUDIO_FORMAT_MAX +}; + +struct BOF_AUDIO_DEVICE_NATIVE_FORMAT +{ + BOF_AUDIO_FORMAT Format_E; + uint32_t NbChannel_U32; /* If set to 0, all channels are supported. */ + uint32_t SampleRate_32; /* If set to 0, all sample rates are supported. */ + uint32_t Flag_U32; /* A combination of MA_DATA_FORMAT_FLAG_* flags. */ + BOF_AUDIO_DEVICE_NATIVE_FORMAT() + { + Reset(); + } + void Reset() + { + Format_E = BOF_AUDIO_FORMAT::BOF_AUDIO_FORMAT_UNKNOWN; + NbChannel_U32 = 0; + SampleRate_32 = 0; + Flag_U32 = 0; + } +}; + +struct BOF_AUDIO_DEVICE_INFO +{ + char pId_c[256]; // This is an opaque union type (cf ma_device_id) + std::string Name_S; + bool IsDefault_B; + std::vector NativeFormatCol; + BOF_AUDIO_DEVICE_INFO() + { + Reset(); + } + void Reset() + { + memset(pId_c, 0, sizeof(pId_c)); + Name_S = ""; + IsDefault_B = false; + NativeFormatCol.clear(); + } +}; +enum BOF_AUDIO_ENGINE_ID : uint32_t +{ + BOF_AUDIO_ENGINE_ID_PLAYBACK = 0, + BOF_AUDIO_ENGINE_ID_CAPTURE, + BOF_AUDIO_ENGINE_ID_MAX +}; +class BofAudioDevice; +struct BOF_AUDIO_CB_PARAM; +using BOF_AUDIO_NEED_DATA_CALLBACK = std::function; + +struct BOF_AUDIO_CB_PARAM +{ + BOF_AUDIO_NEED_DATA_CALLBACK OnNeedAudioData; + BOF_AUDIO_ENGINE_ID AudioEngineId_E; + BofAudioDevice *pAudioDevice; + uint32_t NbChannel_U32; + uint32_t SampleRate_U32; + BOF_AUDIO_FORMAT AudioFormat_E; + + //uint32_t RemainingAudioSample_U32; + //int32_t *pAudioData_S32; + BOF_AUDIO_CB_PARAM() + { + Reset(); + } + void Reset() + { + OnNeedAudioData = nullptr; + AudioEngineId_E = BOF_AUDIO_ENGINE_ID::BOF_AUDIO_ENGINE_ID_MAX; + pAudioDevice = nullptr; + NbChannel_U32 = 0; + SampleRate_U32 = 0; + AudioFormat_E = BOF_AUDIO_FORMAT::BOF_AUDIO_FORMAT_UNKNOWN; + + //RemainingAudioSample_U32 = 0; + //pAudioData_S32 = nullptr; + } +}; +struct BOF_AUDIO_ENGINE +{ + BOF_AUDIO_CB_PARAM AudioCbParam_X; + ma_device MaDevice_X; + ma_device_config MaConfig_X; + int32_t Index_S32; + bool Started_B; + std::vector AudioDeviceInfoCol; + + BOF_AUDIO_ENGINE() + { + Reset(); + } + void Reset() + { + AudioCbParam_X.Reset(); + Index_S32 = 0; + Started_B = false; + AudioDeviceInfoCol.clear(); + } +}; + +class BofAudioDevice +{ +public: + BofAudioDevice(const BOF_AUDIO_DEVICE_PARAM &_rAudioDeviceParam_X); + ~BofAudioDevice(); + uint32_t ScanForDevice(); + std::vector GetDeviceList(BOF_AUDIO_ENGINE_ID _AudioEngineId_E); + bool InitEngineDevice(BOF_AUDIO_ENGINE_ID _AudioEngineId_E, const std::string &_rName_S, uint32_t _NbChannel_U32,uint32_t _SampleRate_U32, BOF_AUDIO_FORMAT _AudioFormat_E); + + bool Start(BOF_AUDIO_ENGINE_ID _AudioEngineId_E, BOF_AUDIO_NEED_DATA_CALLBACK _OnNeedAudioData); + bool Stop(BOF_AUDIO_ENGINE_ID _AudioEngineId_E); + bool ShutdownEngineDevice(BOF_AUDIO_ENGINE_ID _AudioEngineId_E); + +//Internal +// void OnNeedAudioData(BOF_AUDIO_CB_PARAM *_pAudioCbParam_X, ma_device *_pDevice_X, void *_pOutput, const void *_pInput, ma_uint32 _FrameCount_U32); + +private: + + BOF_AUDIO_DEVICE_PARAM mAudioDeviceParam_X; + ma_context mMaContext_X; + BOF_AUDIO_ENGINE mpAudioEngine_X[BOF_AUDIO_ENGINE_ID_MAX]; +}; +END_BOF_NAMESPACE() diff --git a/lib/src/bofaudio.cpp b/lib/src/bofaudio.cpp new file mode 100644 index 0000000..3cb4a9b --- /dev/null +++ b/lib/src/bofaudio.cpp @@ -0,0 +1,226 @@ +/* + * Copyright (c) 2024-2044, EVS Broadcast Equipment S.A. All rights reserved. + * + * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + * KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + * PURPOSE. + * + * This module implements different Audio interface + * + * Author: Bernard HARMEL: b.harmel@evs.com + * + * History: + * V 1.00 Apr 11 2024 BHA : Initial release + */ +#include "bofstd/bofaudio.h" + +#define MINIAUDIO_IMPLEMENTATION +#include + +/*From doc +2.6. Emscripten +--------------- +The Emscripten build emits Web Audio JavaScript directly and should compile cleanly out of the box. +You cannot use `-std=c*` compiler flags, nor `-ansi`. + +You can enable the use of AudioWorkets by defining `MA_ENABLE_AUDIO_WORKLETS` and then compiling +with the following options: + + -sAUDIO_WORKLET=1 -sWASM_WORKERS=1 -sASYNCIFY + +An example for compiling with AudioWorklet support might look like this: + + emcc program.c -o bin/program.html -DMA_ENABLE_AUDIO_WORKLETS -sAUDIO_WORKLET=1 -sWASM_WORKERS=1 -sASYNCIFY +*/ + +BEGIN_BOF_NAMESPACE() + +void Cb_OnNeedAudioData(ma_device *_pDevice_X, void *_pOutput, const void *_pInput, ma_uint32 _FrameCount_U32) +{ + BOF_AUDIO_CB_PARAM *pAudioCbParam_X = reinterpret_cast(_pDevice_X->pUserData); + if ((pAudioCbParam_X) && (pAudioCbParam_X->pAudioDevice)) + { + pAudioCbParam_X->OnNeedAudioData(*pAudioCbParam_X, _pDevice_X, _pOutput, _pInput, _FrameCount_U32); + } +} + +BofAudioDevice::BofAudioDevice(const BOF_AUDIO_DEVICE_PARAM &_rAudioDeviceParam_X) +{ + mAudioDeviceParam_X = _rAudioDeviceParam_X; + if (ma_context_init(nullptr, 0, nullptr, &mMaContext_X) == MA_SUCCESS) + { + ScanForDevice(); + } +} +BofAudioDevice::~BofAudioDevice() +{ + ma_context_uninit(&mMaContext_X); +} + +uint32_t BofAudioDevice::ScanForDevice() +{ + uint32_t Rts_U32, i_U32, j_U32; + BOF_AUDIO_DEVICE_INFO AudioDeviceInfo_X; + ma_device_info *pPlaybackInfo_X; + ma_uint32 PlaybackCount_U32; + ma_device_info *pCaptureInfo_X; + ma_uint32 CaptureCount_U32; + BOF_AUDIO_DEVICE_NATIVE_FORMAT AudioDeviceNativeFormat_X; + + Rts_U32 = 0; + mpAudioEngine_X[BOF_AUDIO_ENGINE_ID_PLAYBACK].Index_S32 = -1; + mpAudioEngine_X[BOF_AUDIO_ENGINE_ID_PLAYBACK].AudioDeviceInfoCol.clear(); + mpAudioEngine_X[BOF_AUDIO_ENGINE_ID_CAPTURE].Index_S32 = -1; + mpAudioEngine_X[BOF_AUDIO_ENGINE_ID_CAPTURE].AudioDeviceInfoCol.clear(); + if (ma_context_get_devices(&mMaContext_X, &pPlaybackInfo_X, &PlaybackCount_U32, &pCaptureInfo_X, &CaptureCount_U32) == MA_SUCCESS) + { + Rts_U32 += (PlaybackCount_U32+ CaptureCount_U32); + // Loop over each device info and do something with it. Here we just print the name with their index. You may want + // to give the user the opportunity to choose which device they'd prefer. + for (i_U32 = 0; i_U32 < PlaybackCount_U32; i_U32++) + { + memcpy(AudioDeviceInfo_X.pId_c, pPlaybackInfo_X[i_U32].id.custom.s, 256); + AudioDeviceInfo_X.Name_S = pPlaybackInfo_X[i_U32].name; + AudioDeviceInfo_X.IsDefault_B = pPlaybackInfo_X[i_U32].isDefault; + for (j_U32 = 0; j_U32 < pPlaybackInfo_X[i_U32].nativeDataFormatCount; j_U32++) + { + AudioDeviceNativeFormat_X.Format_E = static_cast(pPlaybackInfo_X[i_U32].nativeDataFormats[j_U32].format); + AudioDeviceNativeFormat_X.NbChannel_U32 = pPlaybackInfo_X[i_U32].nativeDataFormats[j_U32].channels; + AudioDeviceNativeFormat_X.SampleRate_32 = pPlaybackInfo_X[i_U32].nativeDataFormats[j_U32].sampleRate; + AudioDeviceNativeFormat_X.Flag_U32 = pPlaybackInfo_X[i_U32].nativeDataFormats[j_U32].flags; + AudioDeviceInfo_X.NativeFormatCol.push_back(AudioDeviceNativeFormat_X); + } + mpAudioEngine_X[BOF_AUDIO_ENGINE_ID_PLAYBACK].AudioDeviceInfoCol.push_back(AudioDeviceInfo_X); + printf("out %d - %s\n", i_U32, AudioDeviceInfo_X.Name_S.c_str()); + } + + for (i_U32 = 0; i_U32 < CaptureCount_U32; i_U32++) + { + memcpy(AudioDeviceInfo_X.pId_c, pCaptureInfo_X[i_U32].id.custom.s, 256); + AudioDeviceInfo_X.Name_S = pCaptureInfo_X[i_U32].name; + AudioDeviceInfo_X.IsDefault_B = pCaptureInfo_X[i_U32].isDefault; + for (j_U32 = 0; j_U32 < pCaptureInfo_X[i_U32].nativeDataFormatCount; j_U32++) + { + AudioDeviceNativeFormat_X.Format_E = static_cast(pCaptureInfo_X[i_U32].nativeDataFormats[j_U32].format); + AudioDeviceNativeFormat_X.NbChannel_U32 = pCaptureInfo_X[i_U32].nativeDataFormats[j_U32].channels; + AudioDeviceNativeFormat_X.SampleRate_32 = pCaptureInfo_X[i_U32].nativeDataFormats[j_U32].sampleRate; + AudioDeviceNativeFormat_X.Flag_U32 = pCaptureInfo_X[i_U32].nativeDataFormats[j_U32].flags; + AudioDeviceInfo_X.NativeFormatCol.push_back(AudioDeviceNativeFormat_X); + } + mpAudioEngine_X[BOF_AUDIO_ENGINE_ID_PLAYBACK].AudioDeviceInfoCol.push_back(AudioDeviceInfo_X); + printf("out %d - %s\n", i_U32, AudioDeviceInfo_X.Name_S.c_str()); + } + } + return Rts_U32; +} + +std::vector BofAudioDevice::GetDeviceList(BOF_AUDIO_ENGINE_ID _AudioEngineId_E) +{ + std::vector Rts; + if (_AudioEngineId_E < BOF_AUDIO_ENGINE_ID_MAX) + { + Rts = mpAudioEngine_X[_AudioEngineId_E].AudioDeviceInfoCol; + } + return Rts; +} + +bool BofAudioDevice::InitEngineDevice(BOF_AUDIO_ENGINE_ID _AudioEngineId_E, const std::string &_rName_S, uint32_t _NbChannel_U32, uint32_t _SampleRate_U32, BOF_AUDIO_FORMAT _AudioFormat_E) +{ + bool Rts_B = false; + uint32_t i_U32; + + if (_AudioEngineId_E < BOF_AUDIO_ENGINE_ID_MAX) + { + if (mpAudioEngine_X[_AudioEngineId_E].Index_S32 < 0) + { + for (i_U32 = 0; i_U32 < mpAudioEngine_X[_AudioEngineId_E].AudioDeviceInfoCol.size(); i_U32++) + { + if (_rName_S == mpAudioEngine_X[_AudioEngineId_E].AudioDeviceInfoCol[i_U32].Name_S) + { + mpAudioEngine_X[_AudioEngineId_E].AudioCbParam_X.NbChannel_U32 = _NbChannel_U32; + mpAudioEngine_X[_AudioEngineId_E].AudioCbParam_X.SampleRate_U32 = _SampleRate_U32; + mpAudioEngine_X[_AudioEngineId_E].AudioCbParam_X.AudioFormat_E = _AudioFormat_E; + mpAudioEngine_X[_AudioEngineId_E].MaConfig_X = ma_device_config_init(ma_device_type_playback); + + mpAudioEngine_X[_AudioEngineId_E].MaConfig_X.playback.format = static_cast(mpAudioEngine_X[_AudioEngineId_E].AudioCbParam_X.AudioFormat_E); // ma_format_s32; + mpAudioEngine_X[_AudioEngineId_E].MaConfig_X.playback.channels = mpAudioEngine_X[_AudioEngineId_E].AudioCbParam_X.NbChannel_U32; + mpAudioEngine_X[_AudioEngineId_E].MaConfig_X.sampleRate = mpAudioEngine_X[_AudioEngineId_E].AudioCbParam_X.SampleRate_U32;// APE::AUDIO_SAMPLE_RATE; + mpAudioEngine_X[_AudioEngineId_E].MaConfig_X.dataCallback = Cb_OnNeedAudioData; // This function will be called when miniaudio needs more data. + mpAudioEngine_X[BOF_AUDIO_ENGINE_ID_PLAYBACK].AudioCbParam_X.AudioEngineId_E = _AudioEngineId_E; + mpAudioEngine_X[BOF_AUDIO_ENGINE_ID_PLAYBACK].AudioCbParam_X.pAudioDevice = this; + mpAudioEngine_X[_AudioEngineId_E].MaConfig_X.pUserData = &mpAudioEngine_X[BOF_AUDIO_ENGINE_ID_PLAYBACK].AudioCbParam_X; // Can be accessed from the device object (device.pUserData). + mpAudioEngine_X[_AudioEngineId_E].MaConfig_X.playback.pDeviceID = reinterpret_cast(mpAudioEngine_X[_AudioEngineId_E].AudioDeviceInfoCol[i_U32].pId_c); + + if (ma_device_init(nullptr, &mpAudioEngine_X[_AudioEngineId_E].MaConfig_X, &mpAudioEngine_X[_AudioEngineId_E].MaDevice_X) == MA_SUCCESS) + { + mpAudioEngine_X[_AudioEngineId_E].Index_S32 = i_U32; + Rts_B = true; + } + break; + } + } + } + } + return Rts_B; +} +bool BofAudioDevice::Start(BOF_AUDIO_ENGINE_ID _AudioEngineId_E, BOF_AUDIO_NEED_DATA_CALLBACK _OnNeedAudioData) +{ + bool Rts_B = false; + + if (_AudioEngineId_E < BOF_AUDIO_ENGINE_ID_MAX) + { + if (mpAudioEngine_X[_AudioEngineId_E].Index_S32 >= 0) + { + if (!mpAudioEngine_X[_AudioEngineId_E].Started_B) + { + mpAudioEngine_X[_AudioEngineId_E].AudioCbParam_X.OnNeedAudioData = _OnNeedAudioData; + if (ma_device_start(&mpAudioEngine_X[_AudioEngineId_E].MaDevice_X) == MA_SUCCESS) + { + mpAudioEngine_X[_AudioEngineId_E].Started_B = true; + Rts_B = true; + } + } + } + } + return Rts_B; +} +bool BofAudioDevice::Stop(BOF_AUDIO_ENGINE_ID _AudioEngineId_E) +{ + bool Rts_B = false; + + if (_AudioEngineId_E < BOF_AUDIO_ENGINE_ID_MAX) + { + if (mpAudioEngine_X[_AudioEngineId_E].Index_S32 >= 0) + { + if (mpAudioEngine_X[_AudioEngineId_E].Started_B) + { + if (ma_device_stop(&mpAudioEngine_X[_AudioEngineId_E].MaDevice_X) == MA_SUCCESS) + { + mpAudioEngine_X[_AudioEngineId_E].Started_B = false; + Rts_B = true; + } + } + } + } + return Rts_B; +} +bool BofAudioDevice::ShutdownEngineDevice(BOF_AUDIO_ENGINE_ID _AudioEngineId_E) +{ + bool Rts_B = false; + + if (_AudioEngineId_E < BOF_AUDIO_ENGINE_ID_MAX) + { + if (mpAudioEngine_X[_AudioEngineId_E].Index_S32 >= 0) + { + Stop(_AudioEngineId_E); + + ma_device_uninit(&mpAudioEngine_X[_AudioEngineId_E].MaDevice_X); + mpAudioEngine_X[_AudioEngineId_E].Index_S32 = -1; + Rts_B = true; + } + } + return Rts_B; +} + +END_BOF_NAMESPACE() \ No newline at end of file diff --git a/vcpkg.json b/vcpkg.json index 756289b..60f37c9 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -14,6 +14,9 @@ }, { "name": "linenoise-ng" + }, + { + "name": "miniaudio" } ] } \ No newline at end of file