From e8319b937e932b1ffa7e44221f1a1be75e3b1241 Mon Sep 17 00:00:00 2001 From: Marek Blok <Marek.Blok@pg.edu.pl> Date: Thu, 13 May 2021 16:51:24 +0200 Subject: [PATCH] Added a SOUND_object_t abstract class support --- CHANGELOG | 7 +- src/Main.cpp | 2 +- src/Makefile.win | 2 +- src/cpp/ALSA_support.cpp | 2 +- src/cpp/DSP_IO.cpp | 468 ++++++++++++++----------------------- src/cpp/WMM_support.cpp | 360 ++++++++++++++++++++++++++++ src/include/ALSA_support.h | 42 ++-- src/include/DSP_IO.h | 40 ++-- src/include/DSP_lib.h | 2 +- src/include/DSP_types.h | 25 ++ src/include/WMM_support.h | 83 +++++++ 11 files changed, 698 insertions(+), 335 deletions(-) create mode 100644 src/cpp/WMM_support.cpp create mode 100644 src/include/WMM_support.h diff --git a/CHANGELOG b/CHANGELOG index f00b6a5..1f233a4 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -6,7 +6,12 @@ TODO:: LAST DONE: CHANGES: - - 2021.04.21 - Added ALSA tests in main.cpp and Launch configuration for Linux + +- ver. 0.20.005 - <b>2021.04.21</b> Changed: + - Added ALSA tests in main.cpp and Launch configuration for Linux + - Added to DSP_types.h a SOUND_object_t abstract class, a wrapper for different sound cards' API + - Added WMM_object_t abstract class, a wrapper for Windows MultiMedia API + - DSP::u::AudioOutput now uses SOUND_object_t (currently just WMM_object_t) for soundcard API - ver. 0.20.004 - <b>2021.04.06</b> Changed: - Fixed makefiles for Linux - install and examples compilation works diff --git a/src/Main.cpp b/src/Main.cpp index c6b39a8..d18918a 100644 --- a/src/Main.cpp +++ b/src/Main.cpp @@ -2909,7 +2909,7 @@ polling: int test_ALSA() { ALSA_object_t ALSA_object; - ALSA_object.log_alsa_data(); + ALSA_object.log_driver_data(); unsigned int Fs = 44100; int rs = ALSA_object.open_alsa_device(SND_PCM_STREAM_PLAYBACK, 2, Fs); diff --git a/src/Makefile.win b/src/Makefile.win index 7333ccf..295ad81 100644 --- a/src/Makefile.win +++ b/src/Makefile.win @@ -32,7 +32,7 @@ LINKER_FLAGS_release = $(comflag) -s -static-libgcc -static-libstdc++ -static SOURCES_NAMES = SOURCES_NAMES += DSP_AudioMixer.cpp DSP_Fourier.cpp DSP_misc.cpp DSP_clocks.cpp DSP_modules.cpp DSP_modules2.cpp -SOURCES_NAMES += DSP_DOT.cpp DSP_modules_misc.cpp DSP_IO.cpp DSP_logstream.cpp +SOURCES_NAMES += DSP_DOT.cpp DSP_modules_misc.cpp DSP_IO.cpp DSP_logstream.cpp WMM_support.cpp SOURCES = $(addprefix $(SRC_CPP_SUBDIR)/,$(SOURCES_NAMES)) SOURCES_DBG = diff --git a/src/cpp/ALSA_support.cpp b/src/cpp/ALSA_support.cpp index 92be453..0632be7 100644 --- a/src/cpp/ALSA_support.cpp +++ b/src/cpp/ALSA_support.cpp @@ -24,7 +24,7 @@ ALSA_object_t::~ALSA_object_t() } -void ALSA_object_t::log_alsa_data() { +void ALSA_object_t::log_driver_data() { int val; DSP::log << "ALSA library version: " << SND_LIB_VERSION_STR << endl; diff --git a/src/cpp/DSP_IO.cpp b/src/cpp/DSP_IO.cpp index 3a40d1a..a356b67 100644 --- a/src/cpp/DSP_IO.cpp +++ b/src/cpp/DSP_IO.cpp @@ -3607,56 +3607,6 @@ uint32_t DSP::f::GetAudioBufferSize(const unsigned long &SamplingFreq, const DSP } #ifdef WINMMAPI - void CALLBACK DSP::u::AudioOutput::waveOutProc(HWAVEOUT hwo, UINT uMsg, - uint32_t dwInstance, uint32_t dwParam1, uint32_t dwParam2) - { - UNUSED_ARGUMENT(hwo); - UNUSED_ARGUMENT(uMsg); - UNUSED_ARGUMENT(dwInstance); - UNUSED_ARGUMENT(dwParam1); - UNUSED_ARGUMENT(dwParam2); - #ifdef __DEBUG__ - #ifdef AUDIO_DEBUG_MESSAGES_ON - // MMRESULT result; - DSP::u::AudioOutput *Current; - bool AllDone; - int ind; - string tekst; - - Current = AudioObjects[dwInstance]; - - switch (uMsg) - { - case WOM_OPEN: - DSP::log << "DSP::u::AudioOutput::waveOutProc" << DSP::e::LogMode::second - << "WOM_OPEN(" << (int)dwInstance << ")" << endl; - break; - case WOM_CLOSE: - DSP::log << "DSP::u::AudioOutput::waveOutProc" << DSP::e::LogMode::second - << "WOM_CLOSE(" << (int)dwInstance << ")" << endl; - break; - case WOM_DONE: - DSP::log << "DSP::u::AudioOutput::waveOutProc" << DSP::e::LogMode::second - << "WOM_DONE(" << (int)dwInstance << ")" << endl; - - if (Current->StopPlaying) - { - DSP::log << "DSP::u::AudioOutput::waveOutProc" << DSP::e::LogMode::second << "StopPlaying is set" << endl; - return; - } - else - { - AllDone=true; - for (ind=0; ind < DSP_NoOfAudioOutputBuffers; ind++) - AllDone &= (Current->waveHeaderOut[ind].dwFlags & WHDR_DONE); - if (AllDone) - DSP::log << "DSP::u::AudioOutput::waveOutProc" << DSP::e::LogMode::second << "All buffers had been used - nothing to play" << endl; - } - break; - } - #endif - #endif - } //! \bug allow user to select number of internal buffers void CALLBACK DSP::u::AudioInput::waveInProc_short(HWAVEIN hwi, UINT uMsg, @@ -3949,14 +3899,14 @@ void DSP::u::AudioOutput::Init(unsigned long SamplingFreq, unsigned int WaveOutDevNo) { #ifdef WINMMAPI - MMRESULT result; - DWORD_PTR Callback; - - //Rezerwacja pamięci dla formatu WAVE - // WAVEFORMATEX wfx; //to wymaga korekty - PCMWAVEFORMAT wfx; - - unsigned long ind; + // MMRESULT result; + // DWORD_PTR Callback; + // + // //Rezerwacja pamięci dla formatu WAVE + // // WAVEFORMATEX wfx; //to wymaga korekty + // PCMWAVEFORMAT wfx; + // + // unsigned long ind; #else UNUSED_ARGUMENT(WaveOutDevNo); #endif @@ -3989,10 +3939,10 @@ void DSP::u::AudioOutput::Init(unsigned long SamplingFreq, ClockGroups.AddInput2Group("input", Input(temp)); } - #ifdef WINMMAPI - //! \bug in Debug mode this callback does nothing so it would be better just not use it - Callback = (DWORD_PTR)(&DSP::u::AudioOutput::waveOutProc); - #endif +// #ifdef WINMMAPI +// //! \bug in Debug mode this callback does nothing so it would be better just not use it +// Callback = (DWORD_PTR)(&DSP::u::AudioOutput::waveOutProc); +// #endif Current_CallbackInstance=Next_CallbackInstance; Next_CallbackInstance++; AudioObjects.resize(Current_CallbackInstance+1); @@ -4000,96 +3950,28 @@ void DSP::u::AudioOutput::Init(unsigned long SamplingFreq, StopPlaying = false; - switch (BitPrec) - { - case 8: - OutSampleType=DSP::e::SampleType::ST_uchar; - break; - case 16: - OutSampleType=DSP::e::SampleType::ST_short; - break; - default: - OutSampleType=DSP::e::SampleType::ST_short; - BitPrec=16; - break; - } - audio_outbuffer_size = DSP::f::GetAudioBufferSize(SamplingFreq, DSP::e::AudioBufferType::output); - #ifdef WINMMAPI - //Wypeniamy struktur wfx - wfx.wf.wFormatTag=WAVE_FORMAT_PCM; - wfx.wf.nChannels=(uint16_t)NoOfInputs; - wfx.wf.nSamplesPerSec=(UINT)SamplingFreq; - wfx.wBitsPerSample=BitPrec; - wfx.wf.nAvgBytesPerSec=wfx.wf.nSamplesPerSec*(wfx.wBitsPerSample/8); - wfx.wf.nBlockAlign=(uint16_t)(wfx.wf.nChannels*(wfx.wBitsPerSample/8)); + snd_object.select_device_by_number(WaveOutDevNo); // use default device + snd_object.open_PCM_device_4_output(NoOfInputs, BitPrec, SamplingFreq, audio_outbuffer_size); - if (WaveOutDevNo >= (UINT)waveOutGetNumDevs()) - result=waveOutOpen(&hWaveOut, - WAVE_MAPPER, //&DeviceID, - (WAVEFORMATEX *)(&wfx), - Callback, - Current_CallbackInstance, //CallbackInstance, - CALLBACK_FUNCTION | WAVE_ALLOWSYNC | WAVE_FORMAT_DIRECT //| WAVE_MAPPED //CALLBACK_NULL - ); - else - result=waveOutOpen(&hWaveOut, - WaveOutDevNo, //&DeviceID, - (WAVEFORMATEX *)(&wfx), - Callback, - Current_CallbackInstance, //CallbackInstance, - CALLBACK_FUNCTION | WAVE_ALLOWSYNC | WAVE_FORMAT_DIRECT //| WAVE_MAPPED //CALLBACK_NULL - ); - if (DSP::f::AudioCheckError(result) == false) - { // everything is ok - waveHeaderOut.resize(DSP::NoOfAudioOutputBuffers); - WaveOutBufferLen=wfx.wf.nBlockAlign*audio_outbuffer_size; - WaveOutBuffers.resize(DSP::NoOfAudioOutputBuffers); - for (ind=0; ind< DSP::NoOfAudioOutputBuffers; ind++) - { - WaveOutBuffers[ind].clear(); - WaveOutBuffers[ind].resize(WaveOutBufferLen, 0); - } + OutBufferLen=NoOfInputs*audio_outbuffer_size; + OutBuffer.clear(); OutBuffer.resize(OutBufferLen, 0.0); + BufferIndex=0; - OutBufferLen=NoOfInputs*audio_outbuffer_size; - OutBuffer.clear(); OutBuffer.resize(OutBufferLen, 0.0); - BufferIndex=0; + // #ifdef WINMMAPI - for (ind=0; ind< DSP::NoOfAudioOutputBuffers; ind++) - { - waveHeaderOut[ind].lpData=(char *)(WaveOutBuffers[ind].data()); - waveHeaderOut[ind].dwBufferLength=OutBufferLen*(BitPrec/8); //sizeof(short); - waveHeaderOut[ind].dwFlags= 0; // WHDR_BEGINLOOP | WHDR_ENDLOOP; - waveHeaderOut[ind].dwLoops=0; - - result=waveOutPrepareHeader(hWaveOut, - &(waveHeaderOut[ind]), sizeof(WAVEHDR)); - DSP::f::AudioCheckError(result); - waveHeaderOut[ind].dwFlags= WHDR_DONE; // WHDR_BEGINLOOP | WHDR_ENDLOOP; - } - } - else - { //error while creating audio output - waveHeaderOut.clear(); - WaveOutBuffers.clear(); - WaveOutBufferLen = 0; - OutBuffer.clear(); - OutBufferLen = 0; - BufferIndex=0; - } + // #else - #else + // WaveOutBufferLen = 0; + // WaveOutBuffers.clear(); + // OutBuffer.clear(); OutBufferLen = 0; + // BufferIndex=0; - WaveOutBufferLen = 0; - WaveOutBuffers.clear(); - OutBuffer.clear(); OutBufferLen = 0; - BufferIndex=0; + // #endif // WINMMAPI - #endif // WINMMAPI - - IsPlayingNow = false; - NextBufferInd=0; + // IsPlayingNow = false; + //NextBufferInd=0; Execute_ptr = &InputExecute; } @@ -4294,50 +4176,52 @@ void DSP::u::AudioInput::Init(DSP::Clock_ptr ParentClock, DSP::u::AudioOutput::~AudioOutput() { - #ifdef WINMMAPI - MMRESULT result; - unsigned long ind; - #endif + // #ifdef WINMMAPI + // MMRESULT result; + // unsigned long ind; + // #endif if (OutBufferLen != 0) { // if device was opened successfully StopPlaying=true; - #ifdef WINMMAPI - result = waveOutReset(hWaveOut); - DSP::f::AudioCheckError(result); - for (ind=0; ind< DSP::NoOfAudioOutputBuffers; ind++) - { - result=waveOutUnprepareHeader(hWaveOut, - &(waveHeaderOut[ind]), sizeof(WAVEHDR)); - DSP::f::AudioCheckError(result); - } - - #ifdef AUDIO_DEBUG_MESSAGES_ON - DSP::log << "DSP::u::AudioOutput" << DSP::e::LogMode::second << "Closing DSP::u::AudioOutput" << endl; - #endif - result=waveOutClose(hWaveOut); - while (result==WAVERR_STILLPLAYING) - { - // #ifdef WINBASEAPI - DSP::f::Sleep(100); - // #else - // sleep(100); - // #endif - #ifdef AUDIO_DEBUG_MESSAGES_ON - DSP::log << "DSP::u::AudioOutput" << DSP::e::LogMode::second << "Closing DSP::u::AudioOutput" << endl; - #endif - result=waveOutClose(hWaveOut); - } - DSP::f::AudioCheckError(result); - #endif - - - // 2) Free buffers - WaveOutBuffers.clear(); - #ifdef WINMMAPI - waveHeaderOut.clear(); - #endif + snd_object.close_PCM_device(); + + // #ifdef WINMMAPI + // result = waveOutReset(hWaveOut); + // DSP::f::AudioCheckError(result); + // for (ind=0; ind< DSP::NoOfAudioOutputBuffers; ind++) + // { + // result=waveOutUnprepareHeader(hWaveOut, + // &(waveHeaderOut[ind]), sizeof(WAVEHDR)); + // DSP::f::AudioCheckError(result); + // } + + // #ifdef AUDIO_DEBUG_MESSAGES_ON + // DSP::log << "DSP::u::AudioOutput" << DSP::e::LogMode::second << "Closing DSP::u::AudioOutput" << endl; + // #endif + // result=waveOutClose(hWaveOut); + // while (result==WAVERR_STILLPLAYING) + // { + // // #ifdef WINBASEAPI + // DSP::f::Sleep(100); + // // #else + // // sleep(100); + // // #endif + // #ifdef AUDIO_DEBUG_MESSAGES_ON + // DSP::log << "DSP::u::AudioOutput" << DSP::e::LogMode::second << "Closing DSP::u::AudioOutput" << endl; + // #endif + // result=waveOutClose(hWaveOut); + // } + // DSP::f::AudioCheckError(result); + // #endif + + + // // 2) Free buffers + // WaveOutBuffers.clear(); + // #ifdef WINMMAPI + // waveHeaderOut.clear(); + // #endif OutBuffer.clear(); } @@ -4416,114 +4300,116 @@ DSP::u::AudioInput::~AudioInput() void DSP::u::AudioOutput::FlushBuffer(void) { - #ifdef WINMMAPI - MMRESULT result; - uint8_t *temp8; - short *temp16; - DSP::Float_ptr Sample; - short Znak; - uint32_t ind; - - // ************************************************** // - // Send buffer to the audio device - - #ifdef AUDIO_DEBUG_MESSAGES_ON - DSP::log << "DSP::u::AudioOutput" << DSP::e::LogMode::second << "Flushing output buffer" << endl; - #endif - - while (1) - { - if (waveHeaderOut[NextBufferInd].dwFlags & WHDR_DONE) - { - result=waveOutUnprepareHeader(hWaveOut, - &(waveHeaderOut[NextBufferInd]), sizeof(WAVEHDR)); - DSP::f::AudioCheckError(result); - - Sample=OutBuffer.data(); - // ************************************************** // - // Converts samples format to the one suitable for the audio device - switch (OutSampleType) - { - case DSP::e::SampleType::ST_uchar: - temp8=(uint8_t *)(WaveOutBuffers[NextBufferInd].data()); - for (ind=0; ind<OutBufferLen; ind++) - { - if (*Sample < 0) - Znak=-1; - else - Znak=1; - - *Sample*=127; - if ((*Sample)*Znak > 127) - *temp8=(unsigned char)(128+Znak*127); - else - *temp8=(unsigned char)(128+*Sample+Znak*0.5); - - Sample++; - temp8++; - } - break; - case DSP::e::SampleType::ST_short: - temp16=(short *)(WaveOutBuffers[NextBufferInd].data()); - for (ind=0; ind<OutBufferLen; ind++) - { - if (*Sample < 0) - Znak=-1; - else - Znak=1; - - *Sample*=SHRT_MAX; - if ((*Sample)*Znak > SHRT_MAX) - *temp16=(short)(Znak*SHRT_MAX); - else - *temp16=(short)(*Sample+Znak*0.5); - Sample++; - temp16++; - } - break; - default: - break; - } - - result=waveOutPrepareHeader(hWaveOut, - &(waveHeaderOut[NextBufferInd]), sizeof(WAVEHDR)); - DSP::f::AudioCheckError(result); - - if (IsPlayingNow == false) - { - if (NextBufferInd == 1) - { - for (ind=0; ind < DSP::NoOfAudioOutputBuffers-1; ind++) //one spare buffer - { - result=waveOutWrite(hWaveOut, - &(waveHeaderOut[ind]), sizeof(WAVEHDR)); - DSP::f::AudioCheckError(result); - } - IsPlayingNow = true; - } - - } - else - { - result=waveOutWrite(hWaveOut, - &(waveHeaderOut[NextBufferInd]), sizeof(WAVEHDR)); - DSP::f::AudioCheckError(result); - } - NextBufferInd++; - NextBufferInd %= DSP::NoOfAudioOutputBuffers; - - break; - } - else - { - // Sleep(10); - #ifdef AUDIO_DEBUG_MESSAGES_ON - DSP::log << "DSP::u::AudioOutput" << DSP::e::LogMode::second << "Waiting for free output buffer" << endl; - #endif - DSP::f::Sleep(0); - } - } - #endif // WINMMAPI + snd_object.append_playback_buffer(OutBuffer); + + // #ifdef WINMMAPI + // MMRESULT result; + // uint8_t *temp8; + // short *temp16; + // DSP::Float_ptr Sample; + // short Znak; + // uint32_t ind; + + // // ************************************************** // + // // Send buffer to the audio device + + // #ifdef AUDIO_DEBUG_MESSAGES_ON + // DSP::log << "DSP::u::AudioOutput" << DSP::e::LogMode::second << "Flushing output buffer" << endl; + // #endif + + // while (1) + // { + // if (waveHeaderOut[NextBufferInd].dwFlags & WHDR_DONE) + // { + // result=waveOutUnprepareHeader(hWaveOut, + // &(waveHeaderOut[NextBufferInd]), sizeof(WAVEHDR)); + // DSP::f::AudioCheckError(result); + + // Sample=OutBuffer.data(); + // // ************************************************** // + // // Converts samples format to the one suitable for the audio device + // switch (OutSampleType) + // { + // case DSP::e::SampleType::ST_uchar: + // temp8=(uint8_t *)(WaveOutBuffers[NextBufferInd].data()); + // for (ind=0; ind<OutBufferLen; ind++) + // { + // if (*Sample < 0) + // Znak=-1; + // else + // Znak=1; + + // *Sample*=127; + // if ((*Sample)*Znak > 127) + // *temp8=(unsigned char)(128+Znak*127); + // else + // *temp8=(unsigned char)(128+*Sample+Znak*0.5); + + // Sample++; + // temp8++; + // } + // break; + // case DSP::e::SampleType::ST_short: + // temp16=(short *)(WaveOutBuffers[NextBufferInd].data()); + // for (ind=0; ind<OutBufferLen; ind++) + // { + // if (*Sample < 0) + // Znak=-1; + // else + // Znak=1; + + // *Sample*=SHRT_MAX; + // if ((*Sample)*Znak > SHRT_MAX) + // *temp16=(short)(Znak*SHRT_MAX); + // else + // *temp16=(short)(*Sample+Znak*0.5); + // Sample++; + // temp16++; + // } + // break; + // default: + // break; + // } + + // result=waveOutPrepareHeader(hWaveOut, + // &(waveHeaderOut[NextBufferInd]), sizeof(WAVEHDR)); + // DSP::f::AudioCheckError(result); + + // if (IsPlayingNow == false) + // { + // if (NextBufferInd == 1) + // { + // for (ind=0; ind < DSP::NoOfAudioOutputBuffers-1; ind++) //one spare buffer + // { + // result=waveOutWrite(hWaveOut, + // &(waveHeaderOut[ind]), sizeof(WAVEHDR)); + // DSP::f::AudioCheckError(result); + // } + // IsPlayingNow = true; + // } + + // } + // else + // { + // result=waveOutWrite(hWaveOut, + // &(waveHeaderOut[NextBufferInd]), sizeof(WAVEHDR)); + // DSP::f::AudioCheckError(result); + // } + // NextBufferInd++; + // NextBufferInd %= DSP::NoOfAudioOutputBuffers; + + // break; + // } + // else + // { + // // Sleep(10); + // #ifdef AUDIO_DEBUG_MESSAGES_ON + // DSP::log << "DSP::u::AudioOutput" << DSP::e::LogMode::second << "Waiting for free output buffer" << endl; + // #endif + // DSP::f::Sleep(0); + // } + // } + // #endif // WINMMAPI } void DSP::u::AudioOutput::InputExecute(INPUT_EXECUTE_ARGS) diff --git a/src/cpp/WMM_support.cpp b/src/cpp/WMM_support.cpp new file mode 100644 index 0000000..f53a2bc --- /dev/null +++ b/src/cpp/WMM_support.cpp @@ -0,0 +1,360 @@ +/*! \file WMM_support.cpp + * WMM support code file + * + * \author Marek Blok + */ + +#include <WMM_support.h> + +#include <DSP_lib.h> // for logging + +DSP::WMM_object_t::WMM_object_t() +{ + is_device_openned = false; + WaveOutDevNo = UINT_MAX; + + NextBufferInd=0; + IsPlayingNow = false; +}; +DSP::WMM_object_t::~WMM_object_t() +{ + if (is_device_openned) { + close_PCM_device(); + } +} + +void CALLBACK DSP::WMM_object_t::waveOutProc(HWAVEOUT hwo, UINT uMsg, + uint32_t dwInstance, uint32_t dwParam1, uint32_t dwParam2) +{ + UNUSED_ARGUMENT(hwo); + UNUSED_ARGUMENT(uMsg); + UNUSED_ARGUMENT(dwInstance); + UNUSED_ARGUMENT(dwParam1); + UNUSED_ARGUMENT(dwParam2); +#ifdef __DEBUG__ +#ifdef AUDIO_DEBUG_MESSAGES_ON +// MMRESULT result; + DSP::u::AudioOutput *Current; + bool AllDone; + int ind; + string tekst; + + Current = AudioObjects[dwInstance]; + + switch (uMsg) + { + case WOM_OPEN: + DSP::log << "DSP::u::AudioOutput::waveOutProc" << DSP::e::LogMode::second + << "WOM_OPEN(" << (int)dwInstance << ")" << endl; + break; + case WOM_CLOSE: + DSP::log << "DSP::u::AudioOutput::waveOutProc" << DSP::e::LogMode::second + << "WOM_CLOSE(" << (int)dwInstance << ")" << endl; + break; + case WOM_DONE: + DSP::log << "DSP::u::AudioOutput::waveOutProc" << DSP::e::LogMode::second + << "WOM_DONE(" << (int)dwInstance << ")" << endl; + + if (Current->StopPlaying) + { + DSP::log << "DSP::u::AudioOutput::waveOutProc" << DSP::e::LogMode::second << "StopPlaying is set" << endl; + return; + } + else + { + AllDone=true; + for (ind=0; ind < DSP_NoOfAudioOutputBuffers; ind++) + AllDone &= (Current->waveHeaderOut[ind].dwFlags & WHDR_DONE); + if (AllDone) + DSP::log << "DSP::u::AudioOutput::waveOutProc" << DSP::e::LogMode::second << "All buffers had been used - nothing to play" << endl; + } + break; + } +#endif +#endif +} + + +void DSP::WMM_object_t::log_driver_data() { + /* + int val; + + DSP::log << "WMM library version: " << SND_LIB_VERSION_STR << endl; + + DSP::log << endl; + DSP::log << "PCM stream types:" << endl; + for (val = 0; val <= SND_PCM_STREAM_LAST; val++) + DSP::log << " " << snd_pcm_stream_name((snd_pcm_stream_t)val) << endl; + + DSP::log << endl; + DSP::log << "PCM access types:" << endl; + for (val = 0; val <= SND_PCM_ACCESS_LAST; val++) + DSP::log << " " << snd_pcm_access_name((snd_pcm_access_t)val) << endl; + + DSP::log << endl; + DSP::log << "PCM formats:" << endl; + for (val = 0; val <= SND_PCM_FORMAT_LAST; val++) + if (snd_pcm_format_name((snd_pcm_format_t)val) != NULL) + DSP::log << " " << snd_pcm_format_name((snd_pcm_format_t)val) << + "(" << snd_pcm_format_description((snd_pcm_format_t)val) << ")" << endl; + + DSP::log << endl; + DSP::log << "PCM subformats:" << endl; + for (val = 0; val <= SND_PCM_SUBFORMAT_LAST; val++) + DSP::log << " " << snd_pcm_subformat_name((snd_pcm_subformat_t)val) << + "(" << snd_pcm_subformat_description((snd_pcm_subformat_t)val) << ")" << endl; + + DSP::log << endl; + DSP::log << "PCM states:" << endl; + for (val = 0; val <= SND_PCM_STATE_LAST; val++) + DSP::log << " " << snd_pcm_state_name((snd_pcm_state_t)val) << endl; + */ +} + +// Callback = (DWORD_PTR)(&DSP::u::AudioOutput::waveOutProc); + +bool DSP::WMM_object_t::select_device_by_number(const unsigned int &device_number) { + WaveOutDevNo = device_number; + + return WaveOutDevNo; +} + +bool DSP::WMM_object_t::is_device_playing(void) { + return IsPlayingNow; +} + +long DSP::WMM_object_t::open_PCM_device_4_output(const int &no_of_channels, int no_of_bits, const long &sampling_rate, const long &audio_outbuffer_size) { + if (is_device_openned) + { + DSP::log << "DSP::WMM_object_t::open_PCM_device_4_output" << DSP::e::LogMode::second << "Device has been already opened: closing device before reopening" << endl; + close_PCM_device(); + } + + switch (no_of_bits) + { + case 8: + OutSampleType=DSP::e::SampleType::ST_uchar; + break; + case 16: + OutSampleType=DSP::e::SampleType::ST_short; + break; + default: + OutSampleType=DSP::e::SampleType::ST_short; + no_of_bits=16; + break; + } + + //Wypeniamy struktur wfx + wfx.wf.wFormatTag=WAVE_FORMAT_PCM; + wfx.wf.nChannels=(uint16_t)no_of_channels; + wfx.wf.nSamplesPerSec=(UINT)sampling_rate; + wfx.wBitsPerSample=(uint16_t)no_of_bits; + wfx.wf.nAvgBytesPerSec=wfx.wf.nSamplesPerSec*(wfx.wBitsPerSample/8); + wfx.wf.nBlockAlign=(uint16_t)(wfx.wf.nChannels*(wfx.wBitsPerSample/8)); + + if (WaveOutDevNo >= (UINT)waveOutGetNumDevs()) + result=waveOutOpen(&hWaveOut, + WAVE_MAPPER, //&DeviceID, + (WAVEFORMATEX *)(&wfx), + DWORD_PTR(NULL), //Callback, + 0, //Current_CallbackInstance, + WAVE_ALLOWSYNC | WAVE_FORMAT_DIRECT //| WAVE_MAPPED //CALLBACK_NULL + //CALLBACK_FUNCTION | WAVE_ALLOWSYNC | WAVE_FORMAT_DIRECT //| WAVE_MAPPED //CALLBACK_NULL + ); + else + result=waveOutOpen(&hWaveOut, + WaveOutDevNo, //&DeviceID, + (WAVEFORMATEX *)(&wfx), + DWORD_PTR(NULL), //Callback, + 0, // Current_CallbackInstance, //CallbackInstance, + WAVE_ALLOWSYNC | WAVE_FORMAT_DIRECT //| WAVE_MAPPED //CALLBACK_NULL +// CALLBACK_FUNCTION | WAVE_ALLOWSYNC | WAVE_FORMAT_DIRECT //| WAVE_MAPPED //CALLBACK_NULL + ); + + if (DSP::f::AudioCheckError(result) == false) + { // everything is ok + waveHeaderOut.resize(DSP::NoOfAudioOutputBuffers); + WaveOutBufferLen=wfx.wf.nBlockAlign*audio_outbuffer_size; + WaveOutBuffers.resize(DSP::NoOfAudioOutputBuffers); + for (unsigned int ind=0; ind< DSP::NoOfAudioOutputBuffers; ind++) + { + WaveOutBuffers[ind].clear(); + WaveOutBuffers[ind].resize(WaveOutBufferLen, 0); + } + + + for (unsigned int ind=0; ind< DSP::NoOfAudioOutputBuffers; ind++) + { + waveHeaderOut[ind].lpData=(char *)(WaveOutBuffers[ind].data()); + waveHeaderOut[ind].dwBufferLength=no_of_channels*audio_outbuffer_size*(no_of_bits/8); //sizeof(short); + waveHeaderOut[ind].dwFlags= 0; // WHDR_BEGINLOOP | WHDR_ENDLOOP; + waveHeaderOut[ind].dwLoops=0; + + result=waveOutPrepareHeader(hWaveOut, + &(waveHeaderOut[ind]), sizeof(WAVEHDR)); + DSP::f::AudioCheckError(result); + waveHeaderOut[ind].dwFlags= WHDR_DONE; // WHDR_BEGINLOOP | WHDR_ENDLOOP; + } + } + else + { //error while creating audio output + waveHeaderOut.clear(); + WaveOutBuffers.clear(); + WaveOutBufferLen = 0; + } + + return sampling_rate; +} + +long DSP::WMM_object_t::open_PCM_device_4_input(const int &no_of_channels, int no_of_bits, const long &sampling_rate, const long &audio_outbuffer_size) { + return -1; +} + + +bool DSP::WMM_object_t::close_PCM_device(void) { + result = waveOutReset(hWaveOut); + DSP::f::AudioCheckError(result); + + for (unsigned int ind=0; ind< DSP::NoOfAudioOutputBuffers; ind++) + { + result=waveOutUnprepareHeader(hWaveOut, + &(waveHeaderOut[ind]), sizeof(WAVEHDR)); + DSP::f::AudioCheckError(result); + } + + #ifdef AUDIO_DEBUG_MESSAGES_ON + DSP::log << "DSP::u::AudioOutput" << DSP::e::LogMode::second << "Closing DSP::u::AudioOutput" << endl; + #endif + result=waveOutClose(hWaveOut); + while (result==WAVERR_STILLPLAYING) + { + DSP::f::Sleep(100); + #ifdef AUDIO_DEBUG_MESSAGES_ON + DSP::log << "DSP::u::AudioOutput" << DSP::e::LogMode::second << "Closing DSP::u::AudioOutput" << endl; + #endif + result=waveOutClose(hWaveOut); + } + DSP::f::AudioCheckError(result); + + // 2) Free buffers + WaveOutBuffers.clear(); + #ifdef WINMMAPI + waveHeaderOut.clear(); + #endif + + #ifdef AUDIO_DEBUG_MESSAGES_ON + DSP::log << "WMM PCM sound closed" << endl; + #endif + + return true; // device has been closed +} + + +long DSP::WMM_object_t::append_playback_buffer(DSP::Float_vector &float_buffer) { + uint8_t *temp8; + short *temp16; + short Znak; + uint32_t ind; + + // ************************************************** // + // Send float_buffer to the audio device + + #ifdef AUDIO_DEBUG_MESSAGES_ON + DSP::log << "DSP::WMM_object_t::append_playback_buffer" << DSP::e::LogMode::second << "Flushing output buffer" << endl; + #endif + + while (1) + { + if (waveHeaderOut[NextBufferInd].dwFlags & WHDR_DONE) + { + result=waveOutUnprepareHeader(hWaveOut, + &(waveHeaderOut[NextBufferInd]), sizeof(WAVEHDR)); + DSP::f::AudioCheckError(result); + + DSP::Float_ptr Sample=float_buffer.data(); + // ************************************************** // + // Converts samples format to the one suitable for the audio device + switch (OutSampleType) + { + case DSP::e::SampleType::ST_uchar: + temp8=(uint8_t *)(WaveOutBuffers[NextBufferInd].data()); + for (ind=0; ind<float_buffer.size(); ind++) + { + if (*Sample < 0) + Znak=-1; + else + Znak=1; + + *Sample*=127; + if ((*Sample)*Znak > 127) + *temp8=(unsigned char)(128+Znak*127); + else + *temp8=(unsigned char)(128+*Sample+Znak*0.5); + + Sample++; + temp8++; + } + break; + case DSP::e::SampleType::ST_short: + temp16=(short *)(WaveOutBuffers[NextBufferInd].data()); + for (ind=0; ind<float_buffer.size(); ind++) + { + if (*Sample < 0) + Znak=-1; + else + Znak=1; + + *Sample*=SHRT_MAX; + if ((*Sample)*Znak > SHRT_MAX) + *temp16=(short)(Znak*SHRT_MAX); + else + *temp16=(short)(*Sample+Znak*0.5); + Sample++; + temp16++; + } + break; + default: + break; + } + + result=waveOutPrepareHeader(hWaveOut, + &(waveHeaderOut[NextBufferInd]), sizeof(WAVEHDR)); + DSP::f::AudioCheckError(result); + + if (IsPlayingNow == false) + { + if (NextBufferInd == 1) + { + for (ind=0; ind < DSP::NoOfAudioOutputBuffers-1; ind++) //one spare buffer + { + result=waveOutWrite(hWaveOut, + &(waveHeaderOut[ind]), sizeof(WAVEHDR)); + DSP::f::AudioCheckError(result); + } + IsPlayingNow = true; + } + + } + else + { + result=waveOutWrite(hWaveOut, + &(waveHeaderOut[NextBufferInd]), sizeof(WAVEHDR)); + DSP::f::AudioCheckError(result); + } + NextBufferInd++; + NextBufferInd %= DSP::NoOfAudioOutputBuffers; + + break; + } + else + { + // Sleep(10); + #ifdef AUDIO_DEBUG_MESSAGES_ON + DSP::log << "DSP::WMM_object_t::append_playback_buffer" << DSP::e::LogMode::second << "Waiting for free output buffer" << endl; + #endif + DSP::f::Sleep(0); + } + } + + return long(float_buffer.size()); +} \ No newline at end of file diff --git a/src/include/ALSA_support.h b/src/include/ALSA_support.h index 1dd3993..cf7084b 100644 --- a/src/include/ALSA_support.h +++ b/src/include/ALSA_support.h @@ -13,30 +13,34 @@ /* All of the ALSA library API is defined in this header */ #include <alsa/asoundlib.h> -#include <DSP_lib.h> // for logging +#include <DSP_lib.h> // for logging and types -class ALSA_object_t { -private: - snd_pcm_t *alsa_handle; - snd_pcm_hw_params_t *hw_params; +namespace DSP { + + class ALSA_object_t : public DSP::SOUND_object_t { + private: + snd_pcm_t *alsa_handle; + snd_pcm_hw_params_t *hw_params; -public: + public: - //! log basic ALSA information - void log_alsa_data(); + //! log basic ALSA information + void log_driver_data(); - //! open default PCM device and return 1 on success or negative error code - /*! stream_type = SND_PCM_STREAM_PLAYBACK or SND_PCM_STREAM_CAPTURE - */ - int open_alsa_device(snd_pcm_stream_t stream_type, int no_of_channels, unsigned int &sampling_rate); - void close_alsa_device(bool do_drain = false, bool use_log = false); + //! open default PCM device and return 1 on success or negative error code + /*! stream_type = SND_PCM_STREAM_PLAYBACK or SND_PCM_STREAM_CAPTURE + */ + int open_alsa_device(snd_pcm_stream_t stream_type, int no_of_channels, unsigned int &sampling_rate); + void close_alsa_device(bool do_drain = false, bool use_log = false); - void get_params(snd_pcm_uframes_t &frames, unsigned int &period_time); - snd_pcm_sframes_t pcm_writei(const void *buffer, snd_pcm_uframes_t &frames); + void get_params(snd_pcm_uframes_t &frames, unsigned int &period_time); + snd_pcm_sframes_t pcm_writei(const void *buffer, snd_pcm_uframes_t &frames); - //! object constructor - ALSA_object_t(); - ~ALSA_object_t(); -}; + //! object constructor + ALSA_object_t(); + ~ALSA_object_t(); + }; + +} #endif // ALSA_support_H diff --git a/src/include/DSP_IO.h b/src/include/DSP_IO.h index 74bf78f..76cb695 100644 --- a/src/include/DSP_IO.h +++ b/src/include/DSP_IO.h @@ -23,8 +23,7 @@ ////#ifdef __CYGWIN__ //#ifdef WIN32 #if defined(WIN32) || defined(WIN64) - #include <windef.h> - #include <mmsystem.h> + #include <WMM_support.h> #else #ifdef LINUX //! \TODO adapt for linux @@ -834,21 +833,22 @@ class DSP::u::AudioOutput : public DSP::Block private: DSP::T_WAVEchunk WAVEchunk; #ifdef WINMMAPI - HWAVEOUT hWaveOut; + //HWAVEOUT hWaveOut; + DSP::WMM_object_t snd_object; #endif - //! Index of the buffer which must be used next time - unsigned long NextBufferInd; - //! Type of samples in WaveOutBuffers - DSP::e::SampleType OutSampleType; - #ifdef WINMMAPI - std::vector<WAVEHDR> waveHeaderOut; - #endif - uint32_t WaveOutBufferLen; // in bytes - //! Buffers for audio samples prepared for playing - std::vector<std::vector<uint8_t>> WaveOutBuffers; - - //! size of the buffers used internally with WMM driver + // //! Index of the buffer which must be used next time + // unsigned long NextBufferInd; + // //! Type of samples in WaveOutBuffers + // DSP::e::SampleType OutSampleType; + //#ifdef WINMMAPI + // std::vector<WAVEHDR> waveHeaderOut; + //#endif + // uint32_t WaveOutBufferLen; // in bytes + // //! Buffers for audio samples prepared for playing + // std::vector<std::vector<uint8_t>> WaveOutBuffers; + + //! size of the buffers used internally with audio driver uint32_t audio_outbuffer_size; //! in samples times number of channels @@ -861,7 +861,7 @@ class DSP::u::AudioOutput : public DSP::Block /*! saturation logic should be implemented */ void FlushBuffer(void); - bool IsPlayingNow; + //bool IsPlayingNow; bool StartAudio(void); bool StopAudio(void); @@ -877,10 +877,10 @@ class DSP::u::AudioOutput : public DSP::Block //! (returns number of samples read per channel) uint32_t GetAudioSegment(void); - #ifdef WINMMAPI - static void CALLBACK waveOutProc(HWAVEOUT hwo, UINT uMsg, - uint32_t dwInstance, uint32_t dwParam1, uint32_t dwParam2); - #endif + //#ifdef WINMMAPI + // static void CALLBACK waveOutProc(HWAVEOUT hwo, UINT uMsg, + // uint32_t dwInstance, uint32_t dwParam1, uint32_t dwParam2); + //#endif void Init(unsigned long SamplingFreq, unsigned int InputsNo=1, //just one channel diff --git a/src/include/DSP_lib.h b/src/include/DSP_lib.h index e6d13c6..d89b1f4 100644 --- a/src/include/DSP_lib.h +++ b/src/include/DSP_lib.h @@ -11,7 +11,7 @@ #define DSP_VER_MAJOR 0 #define DSP_VER_MINOR 20 -#define DSP_VER_BUILD 4 // !!! without zeroes before, else this will be treated as octal number +#define DSP_VER_BUILD 5 // !!! without zeroes before, else this will be treated as octal number #define DSP_VER_YEAR 2021 #define DSP_VER DSP_VER_MAJOR.DSP_VER_MINOR.DSP_VER_BUILD diff --git a/src/include/DSP_types.h b/src/include/DSP_types.h index ce092f4..c6b5e3d 100644 --- a/src/include/DSP_types.h +++ b/src/include/DSP_types.h @@ -528,6 +528,31 @@ namespace DSP { #endif +namespace DSP { + //! Base class for classes implementing sound card support for DSP::u::AudioInput and DSP::u::AudioOutput + /*! \TODO convert WMM support in DSP::u::AudioInput and DSP::u::AudioOutput into support through WMM_object_t + */ + class SOUND_object_t { + + public: + virtual void log_driver_data() = 0; + + virtual bool select_device_by_number(const unsigned int &device_number=UINT_MAX) = 0; + + virtual long open_PCM_device_4_output(const int &no_of_channels, int no_of_bits, const long &sampling_rate, const long &audio_outbuffer_size) = 0; + virtual long open_PCM_device_4_input(const int &no_of_channels, int no_of_bits, const long &sampling_rate, const long &audio_outbuffer_size) = 0; + virtual bool close_PCM_device(void) = 0; + + //! returns true is the playback is on + virtual bool is_device_playing(void) = 0; + + //! \note values stored in float_buffer might be altered + long append_playback_buffer(DSP::Float_vector &float_buffer); + + virtual ~SOUND_object_t() {}; + }; +} + #include <DSP_DOT.h> diff --git a/src/include/WMM_support.h b/src/include/WMM_support.h new file mode 100644 index 0000000..5706e99 --- /dev/null +++ b/src/include/WMM_support.h @@ -0,0 +1,83 @@ +/*! \file WMM_support.h + * WMM support header file + * + * \author Marek Blok + */ +//--------------------------------------------------------------------------- +#ifndef WMM_support_H +#define WMM_support_H + +/* inluce WMM API */ +////#ifdef __CYGWIN__ +//#ifdef WIN32 +#if defined(WIN32) || defined(WIN64) + #include <windef.h> + #include <mmsystem.h> +#else + #error NO WIN32 +#endif + +#include <DSP_types.h> // for types + +namespace DSP { + class WMM_object_t : public DSP::SOUND_object_t { + private: + bool is_device_openned; + + HWAVEOUT hWaveOut; + + MMRESULT result; + DWORD_PTR Callback; + + //Rezerwacja pamięci dla formatu WAVE + // WAVEFORMATEX wfx; //to wymaga korekty + PCMWAVEFORMAT wfx; + + unsigned int WaveOutDevNo; // device numer used in next open operations + + std::vector<WAVEHDR> waveHeaderOut; + uint32_t WaveOutBufferLen; // in bytes + //! Buffers for audio samples prepared for playing + std::vector<std::vector<uint8_t>> WaveOutBuffers; + //! Type of samples in WaveOutBuffers + DSP::e::SampleType OutSampleType; + //! Index of the buffer which must be used next time + unsigned long NextBufferInd; + + bool IsPlayingNow; + + static void CALLBACK waveOutProc(HWAVEOUT hwo, UINT uMsg, + uint32_t dwInstance, uint32_t dwParam1, uint32_t dwParam2); + + + public: + + //! log basic WMM information + void log_driver_data(); + + // returns false if device is already opened + bool select_device_by_number(const unsigned int &device_number=UINT_MAX); + + //! opens default PCM device for playback and returns selected sampling rate on success or negative error code + long open_PCM_device_4_output(const int &no_of_channels, int no_of_bits, const long &sampling_rate, const long &audio_outbuffer_size); + //! opens default PCM device for capture and returns selected sampling rate on success or negative error code + long open_PCM_device_4_input(const int &no_of_channels, int no_of_bits, const long &sampling_rate, const long &audio_outbuffer_size); + + bool close_PCM_device(void); + + //! returns true is the playback is on + bool is_device_playing(void); + + //void get_params(snd_pcm_uframes_t &frames, unsigned int &period_time); + //snd_pcm_sframes_t pcm_writei(const void *buffer, snd_pcm_uframes_t &frames); + //! \note values stored in float_buffer might be altered + long append_playback_buffer(DSP::Float_vector &float_buffer); + + //! object constructor \TODO check use of virtual in constructor and destructor + WMM_object_t(); + ~WMM_object_t(); + }; + +} + +#endif // WMM_support_H -- GitLab