From 375f496ff38eb1fbf8f308c161074541ac4e2213 Mon Sep 17 00:00:00 2001 From: Marek Blok <Marek.Blok@pg.edu.pl> Date: Sun, 16 May 2021 23:45:47 +0200 Subject: [PATCH] SOUND_object_t support for non-callback capture --- CHANGELOG | 5 +++ src/cpp/ALSA_support.cpp | 3 ++ src/cpp/DSP_IO.cpp | 30 +++++++++++++++-- src/cpp/WMM_support.cpp | 69 ++++++++++++++++++++++++++++++++------ src/include/ALSA_support.h | 1 + src/include/DSP_lib.h | 2 +- src/include/DSP_types.h | 49 +++++++++++++++++++++------ src/include/WMM_support.h | 1 + 8 files changed, 137 insertions(+), 23 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 15826cb..d861133 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -6,6 +6,11 @@ TODO:: LAST DONE: CHANGES: +- ver. 0.20.008 - <b>2021.05.16</b> Changed: + - Added SOUND_object_t::get_wave_in_raw_buffer for non-callback input operations + - DSP::u::AudioInput now falls back to non-callback mode if SOUND_object_t doesn't support callbacks + - Added in DSP::AudioInput::OutputExecute and DSP::AudioInput::Init support for SOUND_object_t without callbacks for audio input + - ver. 0.20.007 - <b>2021.05.16</b> Changed: - initial convertion of ALSA_object_t to class based on SOUND_object_t diff --git a/src/cpp/ALSA_support.cpp b/src/cpp/ALSA_support.cpp index 7013fd0..a2f6270 100644 --- a/src/cpp/ALSA_support.cpp +++ b/src/cpp/ALSA_support.cpp @@ -309,6 +309,9 @@ bool DSP::ALSA_object_t::start_recording(void) { assert(!"DSP::ALSA_object_t::stop_recording not implemented yet"); } +bool DSP::ALSA_object_t::get_wave_in_raw_buffer(DSP::e::SampleType &InSampleType, std::vector<char> &wave_in_raw_buffer) { + assert(!"DSP::ALSA_object_t::get_wave_in_raw_buffer not implemented yet"); +} snd_pcm_sframes_t DSP::ALSA_object_t::pcm_writei(const void *buffer, snd_pcm_uframes_t &frames) { snd_pcm_sframes_t rc = snd_pcm_writei(alsa_handle, buffer, frames); diff --git a/src/cpp/DSP_IO.cpp b/src/cpp/DSP_IO.cpp index af3da1b..7187567 100644 --- a/src/cpp/DSP_IO.cpp +++ b/src/cpp/DSP_IO.cpp @@ -4129,8 +4129,19 @@ void DSP::u::AudioInput::Init(DSP::Clock_ptr ParentClock, snd_object.select_input_device_by_number(WaveInDevNo); // use default device // DSP::input_callback_function cp = std::bind(&DSP::u::AudioInput::SOUND_object_callback, this, std::placeholders::_1, std::placeholders::_2); - DSP::input_callback_function cp = &DSP::u::AudioInput::SOUND_object_callback; - snd_object.register_input_callback_object(this, cp); // use default device + if (snd_object.is_input_callback_supported() == true) { + // use callbacks if available + DSP::input_callback_function cp = &DSP::u::AudioInput::SOUND_object_callback; + snd_object.register_input_callback_object(this, cp); // use default device +#ifdef __DEBUG__ + DSP::log << "DSP::u::AudioInput::Init" << DSP::e::LogMode::second << "using callback mode" << endl; +#endif // __DEBUG__ + } +#ifdef __DEBUG__ + else { + DSP::log << "DSP::u::AudioInput::Init" << DSP::e::LogMode::second << "using non-callback mode" << endl; + } +#endif // __DEBUG__ if (snd_object.open_PCM_device_4_input(NoOfOutputs, BitPrec, SamplingFreq, audio_inbuffer_size) > 0) { InBufferLen=NoOfOutputs*audio_inbuffer_size; for (ind = 0; ind < DSP::NoOfAudioInputBuffers; ind++) @@ -4439,6 +4450,17 @@ bool DSP::u::AudioInput::OutputExecute(OUTPUT_EXECUTE_ARGS) return true; } + // If there are free buffers check whether the sound card has any audio data already available + if (DSP_THIS->snd_object.get_input_callback_object() == NULL) { + // callbacks are not used thus audio data has to be obtained directly from snd_object + if (DSP_THIS->GetNoOfFreeBuffers() > 0) { + DSP::e::SampleType InSampleType; + std::vector<char> wave_in_raw_buffer; + if (DSP_THIS->snd_object.get_wave_in_raw_buffer(InSampleType, wave_in_raw_buffer)) { + DSP_THIS->SOUND_object_callback(InSampleType, wave_in_raw_buffer); + } + } + } // if no input buffer is ready return false (system will later return here) if (DSP_THIS->snd_object.is_device_recording() == true) @@ -5816,6 +5838,8 @@ DSP::u::AudioInput *DSP::SOUND_object_t::get_input_callback_object() { } bool DSP::SOUND_object_t::input_callback_call(const DSP::e::SampleType &InSampleType, const std::vector<char> &wave_in_raw_buffer) { + if (AudioInput_object == NULL) + return false; return (AudioInput_object->*AudioInput_callback)(InSampleType, wave_in_raw_buffer); } @@ -5846,6 +5870,8 @@ DSP::u::AudioOutput *DSP::SOUND_object_t::get_output_callback_object() { bool DSP::SOUND_object_t::output_callback_call(const DSP::e::SampleType &OutSampleType, const std::vector<char> &wave_out_raw_buffer) { // return (AudioOutput_object->*AudioOutput_callback)(); + if (AudioOutput_object == NULL) + return false; return (AudioOutput_object->*AudioOutput_callback)(OutSampleType, wave_out_raw_buffer); } diff --git a/src/cpp/WMM_support.cpp b/src/cpp/WMM_support.cpp index 75d9842..12c6536 100644 --- a/src/cpp/WMM_support.cpp +++ b/src/cpp/WMM_support.cpp @@ -166,7 +166,6 @@ long DSP::WMM_object_t::open_PCM_device_4_input(const int &no_of_channels, int n close_PCM_device_input(); } - DWORD_PTR Callback; switch (no_of_bits) { case 8: @@ -180,7 +179,20 @@ long DSP::WMM_object_t::open_PCM_device_4_input(const int &no_of_channels, int n no_of_bits=16; break; } - Callback = (DWORD_PTR)(&DSP::WMM_object_t::waveInProc); + + DWORD_PTR Callback; + DWORD_PTR dwInstance; + uint32_t fdwOpen; + if (get_input_callback_object() != NULL) { + Callback = (DWORD_PTR)(&DSP::WMM_object_t::waveInProc); + dwInstance = get_current_CallbackInstance(); + fdwOpen = CALLBACK_FUNCTION; + } + else { + Callback = (DWORD_PTR)(NULL); + dwInstance = (DWORD_PTR)(0); + fdwOpen = 0; + } //Wypeniamy struktur wfx wfx.wf.wFormatTag=WAVE_FORMAT_PCM; @@ -190,21 +202,22 @@ long DSP::WMM_object_t::open_PCM_device_4_input(const int &no_of_channels, int n wfx.wf.nAvgBytesPerSec=wfx.wf.nSamplesPerSec*(wfx.wBitsPerSample/8); wfx.wf.nBlockAlign=(uint16_t)(wfx.wf.nChannels*(wfx.wBitsPerSample/8)); + fdwOpen |= WAVE_FORMAT_DIRECT; if (waveInGetNumDevs() <= WaveInDevNo) result=waveInOpen(&hWaveIn, WAVE_MAPPER, //&DeviceID, (WAVEFORMATEX *)(&wfx), Callback, - get_current_CallbackInstance(), //CallbackInstance, - CALLBACK_FUNCTION | WAVE_FORMAT_DIRECT //| WAVE_MAPPED //CALLBACK_NULL + dwInstance, //CallbackInstance, + fdwOpen //| WAVE_MAPPED //CALLBACK_NULL ); else result=waveInOpen(&hWaveIn, WaveInDevNo, //&DeviceID, (WAVEFORMATEX *)(&wfx), Callback, - get_current_CallbackInstance(), //CallbackInstance, - CALLBACK_FUNCTION | WAVE_FORMAT_DIRECT //| WAVE_MAPPED //CALLBACK_NULL + dwInstance, //CallbackInstance, + fdwOpen //| WAVE_MAPPED //CALLBACK_NULL ); if (DSP::f::AudioCheckError(result) == false) @@ -427,10 +440,10 @@ bool DSP::WMM_object_t::start_recording(void) { DSP::log << "DSP::u::AudioInput::Execute" << DSP::e::LogMode::second << "Starting recording using two wave buffers" << endl; #endif - if (get_input_callback_object() == NULL) { - DSP::log << DSP::e::LogMode::Error << "DSP::WMM_object_t::start_recording" << DSP::e::LogMode::second << "No AudioInput object registered for callbacks" << endl; - return false; - } + // if (get_input_callback_object() == NULL) { + // DSP::log << DSP::e::LogMode::Error << "DSP::WMM_object_t::start_recording" << DSP::e::LogMode::second << "No AudioInput object registered for callbacks" << endl; + // return false; + // } result=waveInAddBuffer(hWaveIn, &(waveHeaderIn[0]), sizeof(WAVEHDR)); DSP::f::AudioCheckError(result); @@ -456,6 +469,42 @@ bool DSP::WMM_object_t::is_output_callback_supported(void) { } +bool DSP::WMM_object_t::get_wave_in_raw_buffer(DSP::e::SampleType &InSampleType_in, std::vector<char> &wave_in_raw_buffer) { + if (StopRecording) + return false; + else + { + if (waveHeaderIn[NextBufferInInd].dwFlags & WHDR_DONE) + { + result=waveInUnprepareHeader(hWaveIn, &(waveHeaderIn[NextBufferInInd]), sizeof(WAVEHDR)); + DSP::f::AudioCheckError(result); + + InSampleType_in = this->InSampleType; + std::swap(wave_in_raw_buffer, WaveInBuffers[NextBufferInInd]); + WaveInBuffers[NextBufferInInd].resize(WaveInBufferLen, 0); + waveHeaderIn[NextBufferInInd].lpData=(char *)(WaveInBuffers[NextBufferInInd].data()); + + //add put back into recording queue + result=waveInPrepareHeader(hWaveIn, &(waveHeaderIn[NextBufferInInd]), sizeof(WAVEHDR)); + DSP::f::AudioCheckError(result); + result=waveInAddBuffer(hWaveIn, &(waveHeaderIn[NextBufferInInd]), sizeof(WAVEHDR)); + DSP::f::AudioCheckError(result); + + NextBufferInInd++; NextBufferInInd %= 2; //just two buffers + } + else + { +#ifdef __DEBUG__ +#ifdef AUDIO_DEBUG_MESSAGES_ON + DSP::log << "DSP::WMM_object_t::get_wave_in_raw_buffer" << DSP::e::LogMode::second << "Wrong audio frame ready or other unexpected error" << endl; +#endif +#endif + return false; + } + } + return true; +} + long DSP::WMM_object_t::append_playback_buffer(DSP::Float_vector &float_buffer) { uint8_t *temp8; short *temp16; diff --git a/src/include/ALSA_support.h b/src/include/ALSA_support.h index a6a62a4..76183e4 100644 --- a/src/include/ALSA_support.h +++ b/src/include/ALSA_support.h @@ -57,6 +57,7 @@ namespace DSP { long append_playback_buffer(DSP::Float_vector &float_buffer); //! Starts sound capture bool start_recording(void); + bool get_wave_in_raw_buffer(DSP::e::SampleType &InSampleType, std::vector<char> &wave_in_raw_buffer); //! Returns false if callbacks are not supported of recording bool is_input_callback_supported(void); diff --git a/src/include/DSP_lib.h b/src/include/DSP_lib.h index 0abe1b4..bca9649 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 7 // !!! without zeroes before, else this will be treated as octal number +#define DSP_VER_BUILD 8 // !!! 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 750f7ea..d9b0948 100644 --- a/src/include/DSP_types.h +++ b/src/include/DSP_types.h @@ -558,6 +558,10 @@ namespace DSP { static std::vector<DSP::SOUND_object_t *> CallbackSoundObjects; static unsigned long get_free_CallbackInstance(void); + protected: + long int get_current_CallbackInstance(); + static const DSP::SOUND_object_t * get_CallbackSoundObject(const long int &instance_number); + public: virtual void log_driver_data() = 0; @@ -584,32 +588,57 @@ namespace DSP { virtual bool start_recording(void) = 0; - long int get_current_CallbackInstance(); - static const DSP::SOUND_object_t * get_CallbackSoundObject(const long int &instance_number); - // https://stackoverflow.com/questions/8865766/get-a-pointer-to-objects-member-function - /*! Returns false if callbacks are not supported of recording + /*! Returns false if callbacks are not supported for recording */ virtual bool is_input_callback_supported(void) = 0; - /*! Returns false if callbacks are not supported - * \TODO define when it will be called and what for? + /*! Registers callback for input buffer data ready. Returns false if callbacks are not supported + * If this method fails or is not used the SOUND_ocject falls back into non-callback mode. + * + * Registers as a callback function that is a member function of the AudioInput class along with the object of the AudioInput class for which it should be called. + * - bool input_callback_function(const DSP::e::SampleType &InSampleType, const std::vector<char> &wave_in_raw_buffer); * - * bool register_input_callback_object(DSP::u::AudioInput *callback_object, bool(DSP::u::AudioInput::*cp)(const DSP::e::SampleType &, const std::vector<char> &)); + * The callback function will be called by the SOUND_object when sound card's input buffer is ready. + * The callback function has to return true when it processed the input buffer or false when it cannot to process it. + * On false the SOUND_object most probably will discard the buffer. Nevertheless it can try to call the callback again for the same data. */ bool register_input_callback_object(DSP::u::AudioInput *callback_object, input_callback_function &cp); + //! Returns pointer to DSP::u::AudioInput for which callback is registered. + /*! \note If this function returns NULL, callbacks are not used even if they are supported. + */ DSP::u::AudioInput *get_input_callback_object(); + protected: + //! \note this method should be used only by descending classes bool input_callback_call(const DSP::e::SampleType &InSampleType, const std::vector<char> &wave_in_raw_buffer); + public: + //! If enough audio data are already available then fills InSampleType and wave_in_raw_buffer + /*! return true on success or false if the data are not available + * \note this method should be used only if callbacks are not active + */ + virtual bool get_wave_in_raw_buffer(DSP::e::SampleType &InSampleType, std::vector<char> &wave_in_raw_buffer) = 0; - /*! Returns false if callbacks are not supported of playback + /*! Returns false if callbacks are not supported for playback */ virtual bool is_output_callback_supported(void) = 0; /*! Returns false if callbacks are not supported - * \TODO define when it will be called and what for? + * + * Registers as a callback function that is a member function of the AudioOutput class along with the object of the AudioOutput class for which it should be called. + * - bool output_callback_function(const DSP::e::SampleType &OutSampleType, const std::vector<char> &wave_out_raw_buffer); + * + * The callback function will be called by the SOUND_object when new sound card's output buffer can processed. + * The callback function has to return true when it filled the buffer with samples or false when there is not enought data. + * On false the SOUND_object most probably will discard the buffer. Nevertheless it can try to call the callback again. + * \TODO revise the concept when the there will be a SOUND_object_t derivative that uses this approach. + * \note Needs addaptation of DSP::u::AudioOutput class. */ bool register_output_callback_object(DSP::u::AudioOutput *callback_object, output_callback_function &cp); + /*! \note If this function returns NULL, callbacks are not used are not used even if they are supported. + */ DSP::u::AudioOutput *get_output_callback_object(); + protected: bool output_callback_call(const DSP::e::SampleType &OutSampleType, const std::vector<char> &wave_out_raw_buffer); - + + public: SOUND_object_t(); virtual ~SOUND_object_t(); }; diff --git a/src/include/WMM_support.h b/src/include/WMM_support.h index 1f61d12..63af568 100644 --- a/src/include/WMM_support.h +++ b/src/include/WMM_support.h @@ -101,6 +101,7 @@ namespace DSP { //! \note values stored in float_buffer might be altered long append_playback_buffer(DSP::Float_vector &float_buffer); bool start_recording(void); + bool get_wave_in_raw_buffer(DSP::e::SampleType &InSampleType, std::vector<char> &wave_in_raw_buffer); //! object constructor \TODO check use of virtual in constructor and destructor WMM_object_t(); -- GitLab