diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index 952f41e384b6ee77df99f8a03f8042824cb64afb..7ec2fc7f6c9d200b77751e445160aee544b438e6 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -4,7 +4,7 @@ { "name": "Windows64-Release", "includePath": [ - "${workspaceFolder}/src", + "${workspaceFolder}/src/", "${workspaceFolder}/src/include", "${workspaceFolder}/src/include/rls" ], @@ -20,7 +20,7 @@ { "name": "Windows32-Release", "includePath": [ - "${workspaceFolder}/src", + "${workspaceFolder}/src/", "${workspaceFolder}/src/include", "${workspaceFolder}/src/include/rls" ], @@ -36,7 +36,7 @@ { "name": "Windows32-Debug", "includePath": [ - "${workspaceFolder}/src", + "${workspaceFolder}/src/", "${workspaceFolder}/src/include", "${workspaceFolder}/src/include/dbg" ], @@ -53,7 +53,7 @@ { "name": "Windows64-Debug", "includePath": [ - "${workspaceFolder}/src", + "${workspaceFolder}/src/", "${workspaceFolder}/src/include", "${workspaceFolder}/src/include/dbg" ], @@ -108,7 +108,8 @@ "defines": [ "LINUX", "__DEBUG__=1", - "INCLUDE_DSPE_EXAMPLES" + "INCLUDE_DSPE_EXAMPLES", + "AUDIO_DEBUG_MESSAGES_ON" ], "compilerPath": "/usr/bin/gcc", "cStandard": "c11", diff --git a/.vscode/settings.json b/.vscode/settings.json index 2b08d7f5660ec1f557bcdbe7237f90be84df997c..420a4e7c887428510cdcd56b06a700272562db9d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -49,6 +49,10 @@ "streambuf": "cpp", "thread": "cpp", "typeinfo": "cpp", - "iterator": "cpp" - } + "iterator": "cpp", + "bit": "cpp", + "numeric": "cpp", + "random": "cpp" + }, + "C_Cpp.errorSquiggles": "Disabled" } \ No newline at end of file diff --git a/CHANGELOG b/CHANGELOG index cadb94adbd80910013d4d785f58488dbc9cbfa47..7d8878a218bec9c6b109b8dcabbb7680dccea74a 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,4 +1,5 @@ -TODO:: +TODO: + - test socket support on Linux (test examples as there are have been noticed some problems with bind) ? DSP::Clock_ptr => migrate to std::shared_ptr !!! Add TELEsound and PiAPS_sound projects to GitLab and start addaptation to new library version diff --git a/examples/DSPE_examples.h b/examples/DSPE_examples.h index 5bd1f3c5e2806f862dd60ed578223c15d34de1ec..bbaa0df1b52d6a90b66a56fb94fd35cb18e91e78 100644 --- a/examples/DSPE_examples.h +++ b/examples/DSPE_examples.h @@ -9,7 +9,7 @@ #define EXAMPLES_DSPE_EXAMPLES_H_ int test_hello(void); -int test_sound_input(void); +int test_sound_input(bool use_audio_output = true); #endif /* EXAMPLES_DSPE_EXAMPLES_H_ */ diff --git a/examples/sound_input.cpp b/examples/sound_input.cpp index e15d363e1afee5af3f9895d1ae5731608e7b50b1..da1364f234ba12f3b3ab5ae11461f2767f8d751e 100644 --- a/examples/sound_input.cpp +++ b/examples/sound_input.cpp @@ -7,11 +7,14 @@ #ifndef INCLUDE_DSPE_EXAMPLES int main(void) +{ + bool use_audio_output = true; #else #include "DSPE_examples.h" -int test_sound_input(void) -#endif // INCLUDE_DSPE_EXAMPLES +int test_sound_input(bool use_audio_output) { +#endif // INCLUDE_DSPE_EXAMPLES + DSP::Clock_ptr MasterClock, AudioInClock; string tekst; int temp; @@ -25,17 +28,26 @@ int test_sound_input(void) MasterClock=DSP::Clock::CreateMasterClock(); +#ifndef INCLUDE_DSPE_EXAMPLES DSP::u::WaveInput WaveIn(MasterClock, "DSPElib.wav", "."); +#else + DSP::u::WaveInput WaveIn(MasterClock, "DSPElib.wav", "../examples"); +#endif // INCLUDE_DSPE_EXAMPLES + Fp = WaveIn.GetSamplingRate(); - DSP::u::AudioOutput AudioOut(Fp); + std::shared_ptr<DSP::Block> AudioOut = nullptr; + if (use_audio_output == true) + AudioOut.reset(new DSP::u::AudioOutput(Fp)); + else + AudioOut.reset(new DSP::u::Vacuum(1U)); - WaveIn.Output("out") >> AudioOut.Input("in"); + WaveIn.Output("out") >> AudioOut->Input("in"); Fp2 = 8000; long Fp_gcd = DSP::f::gcd(Fp, Fp2); AudioInClock=DSP::Clock::GetClock(MasterClock, Fp2 / Fp_gcd, Fp / Fp_gcd); - DSP::u::AudioInput AudioIn(AudioInClock, 8000, 1); + DSP::u::AudioInput AudioIn(AudioInClock, Fp2, 1); DSP::u::FileOutput WaveOut("captured_sample.wav",DSP::e::SampleType::ST_short, 1, DSP::e::FileType::FT_wav, Fp2); AudioIn.Output("out") >> WaveOut.Input("in"); @@ -51,8 +63,9 @@ int test_sound_input(void) DSP::log << "MAIN" << DSP::e::LogMode::second << temp << endl; temp++; } - while (WaveIn.GetBytesRead() != 0); + while ((temp <= 8) || (WaveIn.GetBytesRead() != 0)); + AudioOut.reset(); DSP::Clock::FreeClocks(); DSP::log << DSP::e::LogMode::Error << "MAIN" << DSP::e::LogMode::second << "end" << endl; diff --git a/src/Main.cpp b/src/Main.cpp index 339600de3ad8e36a397ff80d3adddb0642e9f802..333f2403aec0ea66342d53119a60e1c013dfc132 100644 --- a/src/Main.cpp +++ b/src/Main.cpp @@ -2772,7 +2772,7 @@ int test_ZPSTC_cw_3() } //ALSA_object.close_alsa_device(true); //snd_pcm_close(alsa_handle); - ALSA_object.close_PCM_device_output(); + ALSA_object.close_PCM_device_output(true); //free(buffer); return 0; @@ -2917,18 +2917,29 @@ polling: ALSA_object.log_driver_data(); unsigned int Fs = 44100; - int rs = ALSA_object.open_PCM_device_4_output(2, 16, Fs); - if (rs == 1) { - ALSA_object.close_PCM_device_output(); - } - - vector<int16_t> signal = test_record(); + //vector<int16_t> signal = test_record(); DSP::Float_vector float_signal; - for (auto ind=0; ind < signal.size(); ind++) { - float_signal[ind] = signal[ind]; + float_signal.resize(2*Fs); + // for (auto ind=0; ind < float_signal.size(); ind++) { + // float_signal[ind] = sin(6000.0 / Fs * ind); + // } + + const int no_of_channels = 2; + for (auto ind=0; ind < float_signal.size()/no_of_channels; ind++) { + //float_signal[no_of_channels*ind] = sin(6000.0 / Fs * ind); // M.B. moĹźna usĹyszeÄ nieciÄ gĹoĹÄ fazy na ĹÄ czeniach poszczegĂłlnych segmentĂłw + float_signal[no_of_channels*ind] = sin(2*DSP::M_PIf * 1000.0 / Fs * ind); // M.B. brak nieciÄ gĹoĹÄ fazy na ĹÄ czeniach poszczegĂłlnych segmentĂłw + float_signal[no_of_channels*ind+1] = 0; } - test_playback(float_signal); + + int rs = ALSA_object.open_PCM_device_4_output(no_of_channels, 16, Fs, float_signal.size()/no_of_channels); + + //test_playback(float_signal); + ALSA_object.append_playback_buffer(float_signal); // M.B. tutaj tylko rpzekazujemy dane do ALSA_object, ale jeszcze nie trafiajÄ do karty dĹşwiÄkowej + ALSA_object.append_playback_buffer(float_signal); // M.B. dopiero to wywoĹanie spowoduje faktyczne przesĹanie danych do karty dĹşwiÄkowej + ALSA_object.append_playback_buffer(float_signal); + ALSA_object.append_playback_buffer(float_signal); + ALSA_object.close_PCM_device_output(true); return 0; } @@ -2943,7 +2954,6 @@ int main(int argc, char*argv[]) #ifdef ALSA_support_H DSP::log << "test ALSA" << endl; test_ALSA(); - return 0; #endif // ALSA_support_H DSP::log << "test DSP::log" << endl; @@ -2961,13 +2971,16 @@ int main(int argc, char*argv[]) DSP::log << DSP::e::LogMode::pause << "Finished SolveMatrix test" << endl; #ifdef INCLUDE_DSPE_EXAMPLES - DSP::log << "Starting test_sound_input" << endl; - test_sound_input(); - DSP::log << DSP::e::LogMode::Error << "Finished test_sound_input" << endl; - + DSP::log << "Starting test_hello" << endl; test_hello(); DSP::log << DSP::e::LogMode::Error << "Finished test_hello" << endl; + + DSP::log << "Starting test_sound_input" << endl; + bool use_audio_output = false; + test_sound_input(use_audio_output); + DSP::log << DSP::e::LogMode::Error << "Finished test_sound_input" << endl; + #endif // INCLUDE_DSPE_EXAMPLES DSP::log << "Starting SymbolMapper test" << endl; diff --git a/src/Makefile.linux b/src/Makefile.linux index 2443d6c0b481b9c986ba6bb69521a7aefec7fcbc..920c8cd93fdce865ea42fba54825420acbe4987c 100644 --- a/src/Makefile.linux +++ b/src/Makefile.linux @@ -24,7 +24,7 @@ INCLUDES_RLS := -I./src/include -I./src/include/rls LIBS := -lasound # \TODO is DEVCPP ok or is it unnecessary -DFLAGS = -DLINUX -DDEVCPP -DINCLUDE_DSPE_EXAMPLES +DFLAGS = -DLINUX -DDEVCPP -DINCLUDE_DSPE_EXAMPLES # -DAUDIO_DEBUG_MESSAGES_ON CFLAGS_release = $(comflag) -std=c++0x -O3 -Wall -c -fmessage-length=0 -fno-strict-aliasing CFLAGS_debug = $(comflag) -std=c++0x -O0 -g3 -Wall -c -fmessage-length=0 -W -Wshadow -Wconversion -fstrict-aliasing -fmax-errors=5 # -U__STRICT_ANSI__ jest potrzebne do kompilacji debug_new.cpp, jeďż˝eli pomin�� ten plik to moďż˝na rďż˝wnieďż˝ wyrzuciďż˝ tďż˝ opcjďż˝ diff --git a/src/cpp/ALSA_support.cpp b/src/cpp/ALSA_support.cpp index a54f695494429f2828b67f3fde1cf02cbfc255cb..1b3d7e7ee001e47e50ab1e1b96ed9f0625144e6b 100644 --- a/src/cpp/ALSA_support.cpp +++ b/src/cpp/ALSA_support.cpp @@ -1,11 +1,12 @@ /*! \file ALSA_support.cpp * ALSA support code file * - * \author Marek Blok + * \authors Damian KaraĹ, Marek Blok */ #include <ALSA_support.h> - +#include <vector> +#include <cmath> #include <DSP_lib.h> // for logging // ============================================================= // @@ -16,339 +17,1052 @@ DSP::ALSA_object_t::ALSA_object_t() { alsa_handle = NULL; hw_params = NULL; -}; + + std::string endianness; + endianness = system("lscpu | grep \"Byte Order\" | egrep -o 'Little Endian|Big Endian'"); + + if (endianness == "Big Endian") + IsLittleEndian = false; + else + IsLittleEndian = true; + + IsHigherQualityMode = false; + + non_blocking_mode = true; + + IsDeviceInputOpen = false; + IsDeviceOutputOpen = false; + + IsPlayingNow = false; + StopPlayback = false; + + StopRecording = false; + IsRecordingNow = false; + + OutDevNo = 1; + InDevNo = 1; + + NextBufferOutInd = 0; + + /* 44100 bits/second sampling rate (CD quality) */ + sampling_rate_alsa = 44100; + no_of_channels_alsa = 2; + no_of_bytes_in_channel = 2; + + audio_inbuffer_size_in_frames = 8000; + audio_outbuffer_size_in_frames = 8000; + size_b = 32; +} + DSP::ALSA_object_t::~ALSA_object_t() { - if (hw_params != NULL){ + if (hw_params != NULL) + { snd_pcm_hw_params_free(hw_params); hw_params = NULL; } + + buffers_8bit.clear(); + buffers_16bit.clear(); + buffers_32bit.clear(); + buffers_32bit_f.clear(); + buffers_64bit.clear(); + + capture_buffer.clear(); + + pcm_buffer.clear(); + pcm_buffer_size_in_frames.clear(); + + if (IsDeviceInputOpen) + close_PCM_device_input(); + if (IsDeviceOutputOpen) + close_PCM_device_output(true); } -unsigned int DSP::ALSA_object_t::select_input_device_by_number(const unsigned int &device_number) { - assert(!"ALSA_object_t::select_input_device_by_number not yet implemented"); +unsigned int DSP::ALSA_object_t::select_input_device_by_number(const unsigned int &device_number) +{ + InDevNo = device_number; + + return InDevNo; } -unsigned int DSP::ALSA_object_t::select_output_device_by_number(const unsigned int &device_number) { - assert(!"ALSA_object_t::select_output_device_by_number not yet implemented"); +unsigned int DSP::ALSA_object_t::select_output_device_by_number(const unsigned int &device_number) +{ + OutDevNo = device_number; + + return OutDevNo; } -bool DSP::ALSA_object_t::is_output_callback_supported(void) { - assert(!"ALSA_object_t::is_output_callback_supported not yet implemented"); +bool DSP::ALSA_object_t::is_input_callback_supported(void) +{ return false; } -bool DSP::ALSA_object_t::is_input_callback_supported(void) { - assert(!"ALSA_object_t::is_input_callback_supported not yet implemented"); +bool DSP::ALSA_object_t::is_output_callback_supported(void) +{ return false; } -void DSP::ALSA_object_t::log_driver_data() { - int val; - - DSP::log << "ALSA 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; +void DSP::ALSA_object_t::log_driver_data() +{ + #ifdef AUDIO_DEBUG_MESSAGES_ON + + //! For logging + int val; + + DSP::log << "ALSA 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; + #endif // AUDIO_DEBUG_MESSAGES_ON } +int DSP::ALSA_object_t::open_alsa_device(snd_pcm_stream_t stream_type) +{ + //! Errors controller in API ALSA functions + int rc; + //! Errors controller in set_snd_pcm_format() function + int errc; -/* - -This example opens the default PCM device, sets -some parameters, and then displays the value -of most of the hardware parameters. It does not -perform any sound playback or recording. - -returns actually selected sampling_rate + //! Local configuration space handler + snd_pcm_hw_params_t *params; -*/ -int DSP::ALSA_object_t::open_alsa_device(snd_pcm_stream_t stream_type, int no_of_channels, unsigned int &sampling_rate) { - int rc; - snd_pcm_t *handle; + //! For logging unsigned int val, val2; + //! Auxiliary variable in API ALSA functions int dir; - snd_pcm_uframes_t frames; if (alsa_handle != NULL) close_alsa_device(); - // ==================================================== // - DSP::log << "Opening ALSA device" << endl; - - { + #ifdef AUDIO_DEBUG_MESSAGES_ON + DSP::log << "Opening ALSA device" << endl; + //! \TODO Test mode: Open mode (see SND_PCM_NONBLOCK, SND_PCM_ASYNC) - /* Open PCM device for playback. */ - rc = snd_pcm_open(&handle, "default", - stream_type, SND_PCM_NONBLOCK); - if (rc < 0) { - DSP::log << "unable to open pcm device: " << snd_strerror(rc) << endl; - //exit(1); - return -1; - } - alsa_handle = handle; - } + + if (stream_type == SND_PCM_STREAM_PLAYBACK) + DSP::log << "Opening PCM device for playback." << endl; + + else + DSP::log << "Opening PCM device for recording (capture)." << endl; + + #endif // AUDIO_DEBUG_MESSAGES_ON + rc = snd_pcm_open(&alsa_handle, "default", stream_type, SND_PCM_NONBLOCK); + + if (rc < 0) { - snd_pcm_hw_params_t *params; + DSP::log << "Unable to open pcm device: " << snd_strerror(rc) << endl; + return -1; + } - /* Allocate a hardware parameters object. */ - snd_pcm_hw_params_alloca(¶ms); + /*! Allocate a hardware parameters object. */ + snd_pcm_hw_params_alloca(¶ms); - /* Fill it in with default values. */ - snd_pcm_hw_params_any(alsa_handle, params); + /*! Fill it in with default values. */ + snd_pcm_hw_params_any(alsa_handle, params); - /* Set the desired hardware parameters. */ + // ============================================================= // + // Set the desired hardware parameters. + // ============================================================= // - /* Interleaved mode */ - snd_pcm_hw_params_set_access(handle, params, - SND_PCM_ACCESS_RW_INTERLEAVED); + /*! Interleaved mode */ + snd_pcm_hw_params_set_access(alsa_handle, params, SND_PCM_ACCESS_RW_INTERLEAVED); - //! \TODO Format selection based on input parameters - /* Signed 16-bit little-endian format */ - snd_pcm_hw_params_set_format(alsa_handle, params, SND_PCM_FORMAT_S16_LE); + #ifdef AUDIO_DEBUG_MESSAGES_ON + DSP::log << "Setting the SND PCM FORMAT." << endl; + DSP::log << "Something less than 0 means an error occurance." << endl; + #endif // AUDIO_DEBUG_MESSAGES_ON + + errc = DSP::ALSA_object_t::set_snd_pcm_format(params); - /* Two channels (stereo) */ - snd_pcm_hw_params_set_channels(alsa_handle, params, no_of_channels); + if (errc < 0) + return -4; - /* 44100 bits/second sampling rate (CD quality) */ - snd_pcm_hw_params_set_rate_near(alsa_handle, params, &sampling_rate, &dir); + snd_pcm_hw_params_set_channels(alsa_handle, params, no_of_channels_alsa); - //snd_pcm_hw_params_set_buffer_size(); + snd_pcm_hw_params_set_rate_near(alsa_handle, params, &sampling_rate_alsa, &dir); - /* Write the parameters to the driver */ - rc = snd_pcm_hw_params(alsa_handle, params); - if (rc < 0) { - DSP::log << "unable to set hw parameters: " << snd_strerror(rc) << endl; - //snd_pcm_hw_params_free(params); - close_alsa_device(); - //exit(1); - return -2; + if (stream_type == SND_PCM_STREAM_PLAYBACK) + { + if (audio_outbuffer_size_in_frames <= 0) + audio_outbuffer_size_in_frames = 8000; + + size_b = audio_outbuffer_size_in_frames * no_of_channels_alsa * no_of_bytes_in_channel; + + rc = snd_pcm_hw_params_set_buffer_size(alsa_handle, params, DSP::NoOfAudioOutputBuffers*audio_outbuffer_size_in_frames); + + snd_pcm_uframes_t requested_audio_outbuffer_size_in_frames = audio_outbuffer_size_in_frames; + + if (rc < 0) + { + DSP::log << "Unable to set a buffer size with error code: " << rc << endl; + return -5; } - // make a copy of hardware parameters - snd_pcm_hw_params_malloc(&hw_params); - snd_pcm_hw_params_copy(hw_params, params); + + /*! Set period size to desired number of frames. */ + snd_pcm_hw_params_set_period_size_near(alsa_handle, params, &audio_outbuffer_size_in_frames, &dir); + + #ifdef AUDIO_DEBUG_MESSAGES_ON + if (audio_outbuffer_size_in_frames != requested_audio_outbuffer_size_in_frames) + { + DSP::log << "Current frames value should be equal: " << requested_audio_outbuffer_size_in_frames << endl; + DSP::log << "Frames is not equal to tmp_frames! Frames: " << audio_outbuffer_size_in_frames << endl; + } + + else + DSP::log << "Frames has been set correctly." << endl; + #endif // AUDIO_DEBUG_MESSAGES_ON } - /* Display information about the PCM interface */ + else // stream_type == SND_PCM_STREAM_CAPTURE + { + if (audio_inbuffer_size_in_frames <= 0) + audio_inbuffer_size_in_frames = 8000; + + size_b = audio_inbuffer_size_in_frames * no_of_channels_alsa * no_of_bytes_in_channel; + + rc = snd_pcm_hw_params_set_buffer_size(alsa_handle, params, size_b); + + snd_pcm_uframes_t requested_audio_inbuffer_size_in_frames = audio_inbuffer_size_in_frames; + + if (rc < 0) + { + DSP::log << "Unable to set a buffer size with error code: " << rc << endl; + return -5; + } - DSP::log << "PCM handle name = '" << snd_pcm_name(alsa_handle) << "'" << endl; + /*! Set period size to desired number of frames. */ + snd_pcm_hw_params_set_period_size_near(alsa_handle, params, &audio_inbuffer_size_in_frames, &dir); - DSP::log << "PCM state = " << snd_pcm_state_name(snd_pcm_state(alsa_handle)) << endl; + #ifdef AUDIO_DEBUG_MESSAGES_ON + if (audio_inbuffer_size_in_frames != requested_audio_inbuffer_size_in_frames) + { + DSP::log << "Current frames value should be equal: " << requested_audio_inbuffer_size_in_frames << endl; + DSP::log << "Frames is not equal to tmp_frames! Frames: " << audio_inbuffer_size_in_frames << endl; + } - snd_pcm_hw_params_get_access(hw_params, (snd_pcm_access_t *) &val); - DSP::log << "access type = " << snd_pcm_access_name((snd_pcm_access_t)val) << endl; + else + DSP::log << "Frames has been set correctly." << endl; + #endif // AUDIO_DEBUG_MESSAGES_ON + } - snd_pcm_format_t format; - snd_pcm_hw_params_get_format(hw_params, &format); - DSP::log << "format = '" << snd_pcm_format_name(format) << "' " - << "(" << snd_pcm_format_description(format) << ")" << endl; + /* Write the parameters to the driver */ + rc = snd_pcm_hw_params(alsa_handle, params); - snd_pcm_hw_params_get_subformat(hw_params, (snd_pcm_subformat_t *)&val); - DSP::log << "subformat = '" << snd_pcm_subformat_name((snd_pcm_subformat_t)val) << "' " - << "(" << snd_pcm_subformat_description((snd_pcm_subformat_t)val) << ")" << endl; + if (rc < 0) + { + DSP::log << "Unable to set hw parameters: " << snd_strerror(rc) << endl; + DSP::log << "Closing ALSA device" << endl; - snd_pcm_hw_params_get_channels(hw_params, &val); - DSP::log << "channels = " << val << endl; + close_alsa_device(); + return -2; + } + /*! Make a copy of hardware parameters. */ + snd_pcm_hw_params_malloc(&hw_params); + snd_pcm_hw_params_copy(hw_params, params); + + // ============================================================= // + // Display information about the PCM interface. + // ============================================================= // + + #ifdef AUDIO_DEBUG_MESSAGES_ON + DSP::log << "PCM handle name = '" << snd_pcm_name(alsa_handle) << "'" << endl; - snd_pcm_hw_params_get_rate(hw_params, &val, &dir); - DSP::log << "rate = " << val << " bps" << endl; + DSP::log << "PCM state = " << snd_pcm_state_name(snd_pcm_state(alsa_handle)) << endl; - snd_pcm_hw_params_get_period_time(hw_params, &val, &dir); - DSP::log << "period time = " << val << " us" << endl; + snd_pcm_hw_params_get_access(hw_params, (snd_pcm_access_t *) &val); + DSP::log << "access type = " << snd_pcm_access_name((snd_pcm_access_t)val) << endl; - snd_pcm_hw_params_get_period_size(hw_params, &frames, &dir); - DSP::log << "period size = " << (int) frames << " frames" << endl; + snd_pcm_format_t format; - snd_pcm_hw_params_get_buffer_time(hw_params, &val, &dir); - DSP::log << "buffer time = " << val << " us" << endl; + snd_pcm_hw_params_get_format(hw_params, &format); + DSP::log << "format = '" << snd_pcm_format_name(format) << "' " + << "(" << snd_pcm_format_description(format) << ")" << endl; - snd_pcm_hw_params_get_buffer_size(hw_params, (snd_pcm_uframes_t *) &val); - DSP::log << "buffer size = " << val << " frames" << endl; + snd_pcm_hw_params_get_subformat(hw_params, (snd_pcm_subformat_t *)&val); + DSP::log << "subformat = '" << snd_pcm_subformat_name((snd_pcm_subformat_t)val) << "' " + << "(" << snd_pcm_subformat_description((snd_pcm_subformat_t)val) << ")" << endl; + + snd_pcm_hw_params_get_channels(hw_params, &val); + DSP::log << "channels = " << val << endl; + + snd_pcm_hw_params_get_rate(hw_params, &val, &dir); + DSP::log << "rate = " << val << " bps" << endl; + + snd_pcm_hw_params_get_period_time(hw_params, &val, &dir); + DSP::log << "period time = " << val << " us" << endl; - snd_pcm_hw_params_get_periods(hw_params, &val, &dir); - DSP::log << "periods per buffer = " << val << " frames" << endl; + snd_pcm_hw_params_get_period_size(hw_params, (snd_pcm_uframes_t *)&val, &dir); + DSP::log << "period size = " << val << " frames" << endl; - snd_pcm_hw_params_get_rate_numden(hw_params, &val, &val2); - DSP::log << "exact rate = " << val << "/" << val2 << " bps" << endl; + snd_pcm_hw_params_get_buffer_time(hw_params, &val, &dir); + DSP::log << "buffer time = " << val << " us" << endl; + snd_pcm_hw_params_get_buffer_size(hw_params, (snd_pcm_uframes_t *) &val); + DSP::log << "buffer size = " << val << " frames" << endl; - val = snd_pcm_hw_params_get_sbits(hw_params); - DSP::log << "significant bits = " << val << endl; + snd_pcm_hw_params_get_periods(hw_params, &val, &dir); + DSP::log << "periods per buffer = " << val << " frames" << endl; - snd_pcm_hw_params_get_tick_time(hw_params, &val, &dir); // deprecated !!! - DSP::log << "tick time = " << val << " us" << endl; + snd_pcm_hw_params_get_rate_numden(hw_params, &val, &val2); + DSP::log << "exact rate = " << val << "/" << val2 << " bps" << endl; - val = snd_pcm_hw_params_is_batch(hw_params); - DSP::log << "is batch = " << val << endl; + val = snd_pcm_hw_params_get_sbits(hw_params); + DSP::log << "significant bits = " << val << endl; - val = snd_pcm_hw_params_is_block_transfer(hw_params); - DSP::log << "is block transfer = " << val << endl; + snd_pcm_hw_params_get_tick_time(hw_params, &val, &dir); // deprecated !!! + DSP::log << "tick time = " << val << " us" << endl; - val = snd_pcm_hw_params_is_double(hw_params); - DSP::log << "is double = " << val << endl; + val = snd_pcm_hw_params_is_batch(hw_params); + DSP::log << "is batch = " << val << endl; - val = snd_pcm_hw_params_is_half_duplex(hw_params); - DSP::log << "is half duplex = " << val << endl; + val = snd_pcm_hw_params_is_block_transfer(hw_params); + DSP::log << "is block transfer = " << val << endl; - val = snd_pcm_hw_params_is_joint_duplex(hw_params); - DSP::log <<"is joint duplex = " << val << endl; + val = snd_pcm_hw_params_is_double(hw_params); + DSP::log << "is double = " << val << endl; - val = snd_pcm_hw_params_can_overrange(hw_params); - DSP::log << "can overrange = " << val << endl; + val = snd_pcm_hw_params_is_half_duplex(hw_params); + DSP::log << "is half duplex = " << val << endl; - val = snd_pcm_hw_params_can_mmap_sample_resolution(hw_params); - DSP::log << "can mmap = " << val << endl; + val = snd_pcm_hw_params_is_joint_duplex(hw_params); + DSP::log <<"is joint duplex = " << val << endl; - val = snd_pcm_hw_params_can_pause(hw_params); - DSP::log << "can pause = " << val << endl; + val = snd_pcm_hw_params_can_overrange(hw_params); + DSP::log << "can overrange = " << val << endl; - val = snd_pcm_hw_params_can_resume(hw_params); - DSP::log << "can resume = " << val << endl; + val = snd_pcm_hw_params_can_mmap_sample_resolution(hw_params); + DSP::log << "can mmap = " << val << endl; - val = snd_pcm_hw_params_can_sync_start(hw_params); - DSP::log << "can sync start = " << val << endl; + val = snd_pcm_hw_params_can_pause(hw_params); + DSP::log << "can pause = " << val << endl; - //snd_pcm_hw_params_free(params); -// There are two ways of allocating such structures: -//1) Use snd_xxx_malloc() and snd_xxx_free() to allocate memory from the -//heap, or -//2) use snd_xxx_alloca() to allocate memory from the stack. -// -//The snd_xxx_alloca() functions behave just like alloca(): their memory -//is automatically freed when the function returns. + val = snd_pcm_hw_params_can_resume(hw_params); + DSP::log << "can resume = " << val << endl; - return 1; -} + val = snd_pcm_hw_params_can_sync_start(hw_params); + DSP::log << "can sync start = " << val << endl; + #endif // AUDIO_DEBUG_MESSAGES_ON -void DSP::ALSA_object_t::get_period_size(snd_pcm_uframes_t &frames, unsigned int &period_time) { - int dir; + // snd_pcm_hw_params_free(params); + // There are two ways of allocating such structures: + // 1) Use snd_xxx_malloc() and snd_xxx_free() to allocate memory from the + // heap, or + // 2) use snd_xxx_alloca() to allocate memory from the stack. + + // The snd_xxx_alloca() functions behave just like alloca(): their memory + // is automatically freed when the function returns. - // snd_pcm_hw_params_current() // Retreive current PCM hardware configuration chosen with snd_pcm_hw_params. - //snd_pcm_hw_params_current(alsa_handle, hw_params); + // snd_pcm_hw_params_current() // Retreive current PCM hardware configuration chosen with snd_pcm_hw_params. + // snd_pcm_hw_params_current(alsa_handle, hw_params); // https://www.alsa-project.org/wiki/FramesPeriods // frame - size of sample in byts // A period is the number of frames in between each hardware interrupt - /* Use a buffer large enough to hold one period */ - snd_pcm_hw_params_get_period_size(hw_params, &frames, &dir); + /*! Use a buffer large enough to hold one period */ + + // Can be useful: + // snd_pcm_hw_params_get_period_size(hw_params, &frames, &dir); + + if (stream_type == SND_PCM_STREAM_PLAYBACK) + { + pcm_buffer.resize(DSP::NoOfAudioOutputBuffers); + pcm_buffer_size_in_frames.resize(DSP::NoOfAudioOutputBuffers); + + switch (no_of_bytes_in_channel) + { + case 1: + buffers_8bit.resize(DSP::NoOfAudioOutputBuffers); + + for(unsigned int ind = 0; ind < DSP::NoOfAudioOutputBuffers; ind++) + { + buffers_8bit[ind].resize(size_b / no_of_bytes_in_channel); + pcm_buffer[ind] = (uint8_t *)(buffers_8bit[ind].data()); + pcm_buffer_size_in_frames[ind] = (snd_pcm_sframes_t) buffers_8bit[ind].size() / no_of_channels_alsa; + } + break; + + case 2: + buffers_16bit.resize(DSP::NoOfAudioOutputBuffers); + + for(unsigned int ind = 0; ind < DSP::NoOfAudioOutputBuffers; ind++) + { + buffers_16bit[ind].resize(size_b / no_of_bytes_in_channel); + pcm_buffer[ind] = (uint8_t *)(buffers_16bit[ind].data()); + pcm_buffer_size_in_frames[ind] = (snd_pcm_sframes_t) buffers_16bit[ind].size() / no_of_channels_alsa; + } + break; + + case 3: + case 4: + if (IsHigherQualityMode) + { + buffers_32bit.resize(DSP::NoOfAudioOutputBuffers); + + for(unsigned int ind = 0; ind < DSP::NoOfAudioOutputBuffers; ind++) + { + buffers_32bit[ind].resize(size_b / no_of_bytes_in_channel); + pcm_buffer[ind] = (uint8_t *)(buffers_32bit[ind].data()); + pcm_buffer_size_in_frames[ind] = (snd_pcm_sframes_t) buffers_32bit[ind].size() / no_of_channels_alsa; + } + } + + else //! native mode + { + buffers_32bit_f.resize(DSP::NoOfAudioOutputBuffers); + + for(unsigned int ind = 0; ind < DSP::NoOfAudioOutputBuffers; ind++) + { + buffers_32bit_f[ind].resize(size_b / no_of_bytes_in_channel); + pcm_buffer[ind] = (uint8_t *)(buffers_32bit_f[ind].data()); + pcm_buffer_size_in_frames[ind] = (snd_pcm_sframes_t) buffers_32bit_f[ind].size() / no_of_channels_alsa; + } + } + break; + + case 8: + buffers_64bit.resize(DSP::NoOfAudioOutputBuffers); + + for(unsigned int ind = 0; ind < DSP::NoOfAudioOutputBuffers; ind++) + { + buffers_64bit[ind].resize(size_b / no_of_bytes_in_channel); + pcm_buffer[ind] = (uint8_t *)(buffers_64bit[ind].data()); + pcm_buffer_size_in_frames[ind] = (snd_pcm_sframes_t) buffers_64bit[ind].size() / no_of_channels_alsa; + } + break; + + default: + DSP::log << "Unsupported no of bytes in channel" << endl; + return -6; + break; + } + } + else // (stream_type == SND_PCM_STREAM_CAPTURE) + { + pcm_buffer.resize(1); + pcm_buffer_size_in_frames.resize(1); + + capture_buffer.resize(size_b); + + for(unsigned int ind = 0; ind < pcm_buffer.size(); ind++) + { + pcm_buffer[ind] = (uint8_t *)(capture_buffer.data()); + pcm_buffer_size_in_frames[ind] = (snd_pcm_sframes_t) capture_buffer.size() / no_of_bytes_in_channel / no_of_channels_alsa; + } + + switch (no_of_bytes_in_channel) + { + case 1: + InSampleTypeALSA = DSP::e::SampleType::ST_uchar; + break; + + case 2: + InSampleTypeALSA = DSP::e::SampleType::ST_short; + break; + + case 3: + InSampleTypeALSA = DSP::e::SampleType::ST_int; + break; + + case 4: + if (IsHigherQualityMode) + InSampleTypeALSA = DSP::e::SampleType::ST_int; + else + InSampleTypeALSA = DSP::e::SampleType::ST_float; + break; + + case 8: + InSampleTypeALSA = DSP::e::SampleType::ST_double; + break; + + default: + DSP::log << "Unsupported no of bytes in channel" << endl; + return -9; + break; + } + } + + if (non_blocking_mode == false) + { + rc = snd_pcm_nonblock(alsa_handle, 0); + + if (rc < 0) + { + DSP::log << "Unable to set blocking mode" << endl; + return -7; + } + } + + else + { + rc = snd_pcm_nonblock(alsa_handle, 1); + + if (rc < 0) + { + DSP::log << "Unable to set non blocking mode" << endl; + return -8; + } + } - /* We want to loop for 5 seconds */ - snd_pcm_hw_params_get_period_time(hw_params, &period_time, &dir); + return 1; } -long DSP::ALSA_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) { - assert(!"DSP::ALSA_object_t::open_PCM_device_4_output not implemented yet"); +int DSP::ALSA_object_t::set_snd_pcm_format(snd_pcm_hw_params_t *params) +{ + int errc; + + switch (no_of_bytes_in_channel) + { + case 1: + /*! Signed 8-bit format */ + errc = snd_pcm_hw_params_set_format(alsa_handle, params, SND_PCM_FORMAT_U8); + + #ifdef AUDIO_DEBUG_MESSAGES_ON + DSP::log << "Format set with error code: " << errc << endl; + #endif // AUDIO_DEBUG_MESSAGES_ON + break; + + case 2: + if (IsLittleEndian == false) + { + /*! Signed 16-bit big-endian format */ + errc = snd_pcm_hw_params_set_format(alsa_handle, params, SND_PCM_FORMAT_S16_BE); + } + + else + { + /*! Signed 16-bit little-endian format */ + errc = snd_pcm_hw_params_set_format(alsa_handle, params, SND_PCM_FORMAT_S16_LE); + } + + #ifdef AUDIO_DEBUG_MESSAGES_ON + DSP::log << "Format set with error code: " << errc << endl; + #endif // AUDIO_DEBUG_MESSAGES_ON + break; + + case 3: // 32-bits buffer can be used + if (IsLittleEndian == false) + { + /*! Signed 24-bit big-endian low three bytes in 32-bit word format */ + errc = snd_pcm_hw_params_set_format(alsa_handle, params, SND_PCM_FORMAT_S32_BE); + } + + else + { + /*! Signed 24-bit little-endian low three bytes in 32-bit word format */ + errc = snd_pcm_hw_params_set_format(alsa_handle, params, SND_PCM_FORMAT_S32_LE); + } + + IsHigherQualityMode = true; + + #ifdef AUDIO_DEBUG_MESSAGES_ON + DSP::log << "Format set with error code: " << errc << std::endl; + #endif // AUDIO_DEBUG_MESSAGES_ON + break; + + case 4: + if (IsLittleEndian == false) + { + /*! Float Little Endian, Range -1.0 to 1.0 */ + errc = snd_pcm_hw_params_set_format(alsa_handle, params, SND_PCM_FORMAT_FLOAT_BE); + + IsHigherQualityMode = false; // native + + if (errc < 0) + { + /*! Signed 32-bit little-endian format */ + errc = snd_pcm_hw_params_set_format(alsa_handle, params, SND_PCM_FORMAT_S32_BE); + + IsHigherQualityMode = true; + } + } + + else + { + /*! Float Little Endian, Range -1.0 to 1.0 */ + errc = snd_pcm_hw_params_set_format(alsa_handle, params, SND_PCM_FORMAT_FLOAT_LE); + + IsHigherQualityMode = false; // native + + if(errc < 0) + { + /*! Signed 32-bit little-endian format */ + errc = snd_pcm_hw_params_set_format(alsa_handle, params, SND_PCM_FORMAT_S32_LE); + + IsHigherQualityMode = true; + } + } + + #ifdef AUDIO_DEBUG_MESSAGES_ON + DSP::log << "Format set with error code: " << errc << endl; + #endif // AUDIO_DEBUG_MESSAGES_ON + break; + + case 8: + if (IsLittleEndian == false) + { + /*! Float Big Endian, Range -1.0 to 1.0 */ + errc = snd_pcm_hw_params_set_format(alsa_handle, params, SND_PCM_FORMAT_FLOAT64_BE); + } + + else + { + /*! Float Little Endian, Range -1.0 to 1.0 */ + errc = snd_pcm_hw_params_set_format(alsa_handle, params, SND_PCM_FORMAT_FLOAT64_LE); + } + + #ifdef AUDIO_DEBUG_MESSAGES_ON + DSP::log << "Format set with error code: " << errc << endl; + #endif // AUDIO_DEBUG_MESSAGES_ON + break; + + default: + DSP::log << "Unsupported no of bytes in channel" << endl; + return -1; + break; + } + + return errc; } -long DSP::ALSA_object_t::open_PCM_device_4_input(const int &no_of_channels, int no_of_bits, const long &sampling_rate, const long &audio_inbuffer_size) { - assert(!"DSP::ALSA_object_t::open_PCM_device_4_input not implemented yet"); +long DSP::ALSA_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) +{ + int rc; + + no_of_channels_alsa = (unsigned int) no_of_channels; + no_of_bytes_in_channel = (unsigned int) no_of_bits / 8; + sampling_rate_alsa = (unsigned int) sampling_rate; + audio_outbuffer_size_in_frames = (snd_pcm_uframes_t) audio_outbuffer_size; + + rc = open_alsa_device(SND_PCM_STREAM_PLAYBACK); + + if(rc > 0) + { + IsDeviceOutputOpen = true; + return (long) sampling_rate_alsa; + } + + else + { + DSP::log << "Device has not been opened. Error code: " << rc << endl; + return -1; + } } -long DSP::ALSA_object_t::append_playback_buffer(DSP::Float_vector &float_buffer) { - assert(!"DSP::ALSA_object_t::append_playback_buffer not implemented yet"); +long DSP::ALSA_object_t::open_PCM_device_4_input(const int &no_of_channels, int no_of_bits, const long &sampling_rate, const long &audio_inbuffer_size) +{ + int rc; + + no_of_channels_alsa = (unsigned int) no_of_channels; + no_of_bytes_in_channel = (unsigned int) no_of_bits / 8; + sampling_rate_alsa = (unsigned int) sampling_rate; + audio_inbuffer_size_in_frames = (snd_pcm_uframes_t) audio_inbuffer_size; + + rc = open_alsa_device(SND_PCM_STREAM_CAPTURE); + + if(rc > 0) + { + IsDeviceInputOpen = true; + return (long) sampling_rate_alsa; + } + else + { + DSP::log << "Device has not been opened. Error code: " << rc << endl; + return -1; + } } -bool DSP::ALSA_object_t::close_PCM_device_input(void) { - assert(!"DSP::ALSA_object_t::close_PCM_device_input not implemented yet"); +long DSP::ALSA_object_t::append_playback_buffer(DSP::Float_vector &float_buffer) +{ + snd_pcm_sframes_t rc = -1; + + while (1) + { + if (rc < 0) + { + // Regulation of sample values in case of exceeding the range [-1, 1] + for (unsigned int m = 0; m < float_buffer.size(); m++) + { + if (float_buffer[m] < -1) + float_buffer[m] = -1; + + else if (float_buffer[m] > 1) + float_buffer[m] = 1; + } + + // Converts samples format to the one suitable for the audio device + switch (no_of_bytes_in_channel) + { + case 1: + for (unsigned int n = 0; n < size_b / no_of_bytes_in_channel / no_of_channels_alsa; n++) + { + buffers_8bit[NextBufferOutInd][no_of_channels_alsa * n] = uint8_t(128 + 127 * float_buffer[no_of_channels_alsa * n]); + if (no_of_channels_alsa == 2) + buffers_8bit[NextBufferOutInd][no_of_channels_alsa * n + 1] = uint8_t(128 + 127 * float_buffer[no_of_channels_alsa * n + 1]); + } + break; + + case 2: + for (unsigned int n = 0; n < size_b / no_of_bytes_in_channel / no_of_channels_alsa; n++) + { + buffers_16bit[NextBufferOutInd][no_of_channels_alsa * n] = int16_t(INT16_MAX * float_buffer[no_of_channels_alsa * n]); + if (no_of_channels_alsa == 2) + buffers_16bit[NextBufferOutInd][no_of_channels_alsa * n + 1] = int16_t(INT16_MAX * float_buffer[no_of_channels_alsa * n + 1]); + } + break; + + case 3: + for (unsigned int n = 0; n < size_b / no_of_bytes_in_channel / no_of_channels_alsa; n++) + { + buffers_32bit[NextBufferOutInd][no_of_channels_alsa * n] = int32_t(INT32_MAX * float_buffer[no_of_channels_alsa * n]); + if (no_of_channels_alsa == 2) + buffers_32bit[NextBufferOutInd][no_of_channels_alsa * n + 1] = int32_t(INT32_MAX * float_buffer[no_of_channels_alsa * n + 1]); + } + break; + + case 4: + for (unsigned int n = 0; n < size_b / no_of_bytes_in_channel / no_of_channels_alsa; n++) + { + if (IsHigherQualityMode) + { + buffers_32bit[NextBufferOutInd][no_of_channels_alsa * n] = int32_t(INT32_MAX * float_buffer[no_of_channels_alsa * n]); + if (no_of_channels_alsa == 2) + buffers_32bit[NextBufferOutInd][no_of_channels_alsa * n + 1] = int32_t(INT32_MAX * float_buffer[no_of_channels_alsa * n + 1]); + + } + + else + { + buffers_32bit_f[NextBufferOutInd][no_of_channels_alsa * n] = float_buffer[no_of_channels_alsa * n]; + if (no_of_channels_alsa == 2) + buffers_32bit_f[NextBufferOutInd][no_of_channels_alsa * n + 1] = float_buffer[no_of_channels_alsa * n + 1]; + + } + } + break; + + case 8: + for (unsigned int n = 0; n < size_b / no_of_bytes_in_channel / no_of_channels_alsa; n++) + { + buffers_64bit[NextBufferOutInd][no_of_channels_alsa * n] = float_buffer[no_of_channels_alsa * n]; + if (no_of_channels_alsa == 2) + buffers_64bit[NextBufferOutInd][no_of_channels_alsa * n + 1] = float_buffer[no_of_channels_alsa * n + 1]; + + } + break; + + default: + DSP::log << "Unsupported no of bytes in channel" << endl; + break; + + // converting samples ends + } + + if (IsPlayingNow == false) + { + if (NextBufferOutInd == DSP::NoOfAudioOutputBuffers - 2) //all but one spare buffer are filled up + { + snd_pcm_prepare(alsa_handle); // solves underrun problem after filling ALSA buffer + + // send all data from buffers to soundcard to start playback + for (unsigned int ind = 0; ind < DSP::NoOfAudioOutputBuffers - 1; ind++) //one spare buffer + { + rc = DSP::ALSA_object_t::pcm_writei(pcm_buffer[ind], pcm_buffer_size_in_frames[ind]); + } + + IsPlayingNow = true; + + #ifdef AUDIO_DEBUG_MESSAGES_ON + DSP::log << "IsPlayingNow set to true" << endl; + #endif // AUDIO_DEBUG_MESSAGES_ON + } + } + + else + rc = DSP::ALSA_object_t::pcm_writei(pcm_buffer[NextBufferOutInd], pcm_buffer_size_in_frames[NextBufferOutInd]); + + NextBufferOutInd++; + NextBufferOutInd %= DSP::NoOfAudioOutputBuffers; + + break; + } + + else + { + #ifdef AUDIO_DEBUG_MESSAGES_ON + DSP::log << "DSP::ALSA_object_t::append_playback_buffer error code is positive. Nothing to play." << endl; + #endif // AUDIO_DEBUG_MESSAGES_ON + + DSP::f::Sleep(0); + IsPlayingNow = false; + } + // end of the main loop + } + + return pcm_buffer_size_in_frames[NextBufferOutInd]; +} + +bool DSP::ALSA_object_t::close_PCM_device_input(void) +{ close_alsa_device(true); return true; } -bool DSP::ALSA_object_t::close_PCM_device_output(bool do_drain) { - assert(!"DSP::ALSA_object_t::close_PCM_device_output not implemented yet"); +bool DSP::ALSA_object_t::close_PCM_device_output(const bool &do_drain) +{ + stop_playback(); // just to be sure that all prepared buffershave been sent to sound card + close_alsa_device(do_drain); + return true; } -bool DSP::ALSA_object_t::is_device_playing(void) { - assert(!"DSP::ALSA_object_t::is_device_playing not implemented yet"); - return false; +bool DSP::ALSA_object_t::is_device_playing(void) +{ + return IsPlayingNow; } -bool DSP::ALSA_object_t::is_device_recording(void) { - assert(!"DSP::ALSA_object_t::is_device_recording not implemented yet"); - return false; +bool DSP::ALSA_object_t::is_device_recording(void) +{ + return IsRecordingNow; } -bool DSP::ALSA_object_t::stop_playback(void) { - assert(!"DSP::ALSA_object_t::stop_playback not implemented yet"); +bool DSP::ALSA_object_t::stop_playback(void) +{ + StopPlayback = true; + + snd_pcm_sframes_t rc; + snd_pcm_sframes_t buffer_size_in_frames; + + // if there are still buffers that haven't been yet sent to sound card then do it now + if (IsPlayingNow == false) + { + if (NextBufferOutInd > 0) // anything is in the buffer + { + // processing was too short so the playback haven't started yet + // send data stored in buffers to the sound card before closing the device + snd_pcm_prepare(alsa_handle); + + // send all data from buffers to soundcard to start playback + for (unsigned int ind = 0; ind < NextBufferOutInd; ind++) //one spare buffer + { + buffer_size_in_frames = pcm_buffer_size_in_frames[ind]; + rc = DSP::ALSA_object_t::pcm_writei(pcm_buffer[ind], buffer_size_in_frames); + } + if (rc > 0) + IsPlayingNow = true; + } + } + + return true; } -bool DSP::ALSA_object_t::stop_recording(void) { - assert(!"DSP::ALSA_object_t::stop_recording not implemented yet"); +bool DSP::ALSA_object_t::stop_recording(void) +{ + StopRecording = true; + return true; } -bool DSP::ALSA_object_t::start_recording(void) { - assert(!"DSP::ALSA_object_t::stop_recording not implemented yet"); +bool DSP::ALSA_object_t::start_recording(void) +{ + IsRecordingNow = true; + return true; } -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"); +bool DSP::ALSA_object_t::get_wave_in_raw_buffer(DSP::e::SampleType &InSampleType, std::vector<char> &wave_in_raw_buffer) +{ + InSampleType = this->InSampleTypeALSA; + + snd_pcm_sframes_t rc; + + for (unsigned int ind = 0; ind < pcm_buffer.size(); ind++) + { + while (pcm_buffer_size_in_frames[ind]) + { + rc = snd_pcm_readi(alsa_handle, pcm_buffer[ind], pcm_buffer_size_in_frames[ind]); + + switch (-rc) + { + case EPIPE: + + #ifdef AUDIO_DEBUG_MESSAGES_ON + // EPIPE means underrun + DSP::log << "Underrun occurred" << endl; + #endif // AUDIO_DEBUG_MESSAGES_ON + + snd_pcm_prepare(alsa_handle); + break; + + case EAGAIN: + + #ifdef AUDIO_DEBUG_MESSAGES_ON + DSP::log << "EAGAIN occurred. Waiting for a free buffer." << endl; + #endif // AUDIO_DEBUG_MESSAGES_ON + + DSP::f::Sleep(0); + break; + + default: + if (rc > 0) + { + pcm_buffer_size_in_frames[ind] -= rc; + pcm_buffer[ind] += rc * no_of_channels_alsa * no_of_bytes_in_channel; + + #ifdef AUDIO_DEBUG_MESSAGES_ON + DSP::log << "Short read. Current rc = " << rc << "." << endl; + #endif // AUDIO_DEBUG_MESSAGES_ON + + if (pcm_buffer_size_in_frames[ind] == 0) + { + wave_in_raw_buffer.resize(capture_buffer.size()); + std::swap(wave_in_raw_buffer, capture_buffer); + + #ifdef AUDIO_DEBUG_MESSAGES_ON + DSP::log << "Inbuffer is full." << endl; + #endif // AUDIO_DEBUG_MESSAGES_ON + + pcm_buffer[ind] = (uint8_t *)(capture_buffer.data()); + pcm_buffer_size_in_frames[ind] = (snd_pcm_sframes_t) capture_buffer.size() / no_of_bytes_in_channel / no_of_channels_alsa; + + return true; + } + } + + else + { + #ifdef AUDIO_DEBUG_MESSAGES_ON + // EPIPE means underrun + DSP::log << "Unsupported error." << endl; + DSP::log << "Error from readi: " << snd_strerror(rc) << endl; + #endif // AUDIO_DEBUG_MESSAGES_ON + + pcm_buffer_size_in_frames[ind] = 0; + return false; + } + break; + } + } + } + return false; } -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); - if (rc == -EPIPE) { - /* EPIPE means underrun */ - fprintf(stderr, "underrun occurred\n"); - snd_pcm_prepare(alsa_handle); - } else if (rc < 0) { - fprintf(stderr, - "error from writei: %s\n", - snd_strerror(int(rc))); - } else if (rc != (int)frames) { - fprintf(stderr, - "short write, write %d frames\n", int(rc)); +snd_pcm_sframes_t DSP::ALSA_object_t::pcm_writei(const void *buffer, const snd_pcm_uframes_t &frames) +{ + snd_pcm_sframes_t rc; + snd_pcm_sframes_t buffer_size_in_frames = frames; + uint8_t* current_buffer = (uint8_t*) buffer; + + while (buffer_size_in_frames > 0) + { + rc = snd_pcm_writei(alsa_handle, current_buffer, buffer_size_in_frames); + + switch (-rc) + { + case EAGAIN: + #ifdef AUDIO_DEBUG_MESSAGES_ON + DSP::log << "EAGAIN occurred. Waiting for a free buffer." << endl; + #endif // AUDIO_DEBUG_MESSAGES_ON + //! \TODO M.B. In the future snd_pcm_status_get_avail / snd_pcm_avail_update could be used to select sleep time + DSP::f::Sleep(0); + break; + + case EPIPE: + #ifdef AUDIO_DEBUG_MESSAGES_ON + // EPIPE means underrun + DSP::log << "Underrun occurred" << endl; + #endif // AUDIO_DEBUG_MESSAGES_ON + + snd_pcm_prepare(alsa_handle); + //! \TODO snd_pcm_sw_params_set_start_threshold() : implement this solution (consider setting threshold to 1 so the sound playback will start without delay) + break; + + default: + if (rc > 0) + { + buffer_size_in_frames -= rc; + current_buffer += rc * no_of_channels_alsa * no_of_bytes_in_channel; + + #ifdef AUDIO_DEBUG_MESSAGES_ON + DSP::log << "Short write. Current rc = " << rc << "." << endl; + #endif // AUDIO_DEBUG_MESSAGES_ON + } + + else + { + #ifdef AUDIO_DEBUG_MESSAGES_ON + // EPIPE means underrun + DSP::log << "Unsupported error." << endl; + DSP::log << "Error from writei: " << snd_strerror(rc) << endl; + #endif // AUDIO_DEBUG_MESSAGES_ON + + buffer_size_in_frames = 0; + } + break; + } } + + #ifdef AUDIO_DEBUG_MESSAGES_ON + DSP::log << "The end of the playback" << endl; + #endif // AUDIO_DEBUG_MESSAGES_ON + return rc; } -void DSP::ALSA_object_t::close_alsa_device(bool do_drain, bool use_log) { - if (alsa_handle != NULL) { - if (do_drain == true) { +void DSP::ALSA_object_t::close_alsa_device(bool do_drain, bool use_log) +{ + if (alsa_handle != NULL) + { + if (do_drain == true) + { snd_pcm_drain(alsa_handle); } + + #ifdef AUDIO_DEBUG_MESSAGES_ON + DSP::log << "Closing the PCM device." << endl; + #endif // AUDIO_DEBUG_MESSAGES_ON + snd_pcm_close(alsa_handle); alsa_handle = NULL; - if (hw_params != NULL){ + if (hw_params != NULL) + { snd_pcm_hw_params_free(hw_params); hw_params = NULL; } } - if (use_log == true) { - DSP::log << "ALSA PCM sound closed" << endl; - } -} - - - + if (use_log == true) + { + DSP::log << "ALSA PCM sound closed." << endl; + } + IsDeviceInputOpen = false; + IsDeviceOutputOpen = false; +} diff --git a/src/cpp/DSP_IO.cpp b/src/cpp/DSP_IO.cpp index 504ae84cd0d49e7245e6436c9f3afa2939d1334e..f59f4583e4645b19dc95c07bbcbec42728873245 100644 --- a/src/cpp/DSP_IO.cpp +++ b/src/cpp/DSP_IO.cpp @@ -3995,6 +3995,28 @@ bool DSP::u::AudioInput::SOUND_object_callback(const DSP::e::SampleType &InSampl DSP::Float_ptr Sample; unsigned int ind; +#ifdef __DEBUG__ + switch (InSampleType) { + case DSP::e::SampleType::ST_short: { + if (InBufferLen != wave_in_raw_buffer.size() / sizeof(short)) { + DSP::log << "DSP::u::AudioInput::SOUND_object_callback" << DSP::e::LogMode::second << "incorrect wave_in_raw_buffer size: " << + wave_in_raw_buffer.size() << " instead of " << InBufferLen * sizeof(short) << endl; + } + } + break; + case DSP::e::SampleType::ST_uchar: { + if (InBufferLen != wave_in_raw_buffer.size() / sizeof(uint8_t)) { + DSP::log << "DSP::u::AudioInput::SOUND_object_callback" << DSP::e::LogMode::second << "incorrect wave_in_raw_buffer size: " << + wave_in_raw_buffer.size() << " instead of " << InBufferLen * sizeof(uint8_t) << endl; + } + } + break; + default: + DSP::log << "DSP::u::AudioInput::SOUND_object_callback" << DSP::e::LogMode::second << "Unsupported Current->InSampleType" << endl; + break; + } +#endif // __DEBUG__ + // reading sound card input buffer Sample=InBuffers[EmptyBufferIndex].data(); // ************************************************** // @@ -4028,7 +4050,7 @@ bool DSP::u::AudioInput::SOUND_object_callback(const DSP::e::SampleType &InSampl default: #ifdef __DEBUG__ - DSP::log << "DSP::u::AudioInput::waveInProc_short" << DSP::e::LogMode::second << "Unsupported Current->InSampleType" << endl; + DSP::log << "DSP::u::AudioInput::SOUND_object_callback" << DSP::e::LogMode::second << "Unsupported Current->InSampleType" << endl; #endif break; } diff --git a/src/cpp/WMM_support.cpp b/src/cpp/WMM_support.cpp index cba5f89939bd8173a829c01c5037d815301b26a3..44042e5b953d4ee6a1b301d757602056286ecbd1 100644 --- a/src/cpp/WMM_support.cpp +++ b/src/cpp/WMM_support.cpp @@ -618,7 +618,7 @@ long DSP::WMM_object_t::append_playback_buffer(DSP::Float_vector &float_buffer) if (NextBufferOutInd == DSP::NoOfAudioOutputBuffers - 2) //all but one spare buffer are filled up { // send all data from buffers to soundcard to start playback for (ind=0; ind < DSP::NoOfAudioOutputBuffers-1; ind++) //one spare buffer - { + { // send all data from buffers to soundcard to start playback result=waveOutWrite(hWaveOut, &(waveHeaderOut[ind]), sizeof(WAVEHDR)); DSP::f::AudioCheckError(result); diff --git a/src/include/ALSA_support.h b/src/include/ALSA_support.h index 5d413b71ab2a9da391fb64790c6e74c1ec2d099b..2c5c734bfdf75b6525583cfecf41755e1af15799 100644 --- a/src/include/ALSA_support.h +++ b/src/include/ALSA_support.h @@ -1,7 +1,7 @@ /*! \file ALSA_support.h * ALSA support header file * - * \author Marek Blok + * \authors Damian KaraĹ, Marek Blok */ //--------------------------------------------------------------------------- #ifndef ALSA_support_H @@ -12,51 +12,143 @@ /* All of the ALSA library API is defined in this header */ #include <alsa/asoundlib.h> - +#include <vector> +#include <cmath> #include <DSP_types.h> // for types namespace DSP { class ALSA_object_t : public DSP::SOUND_object_t { private: + //! PCM device handler snd_pcm_t *alsa_handle; + //! copy of set configuration space handler snd_pcm_hw_params_t *hw_params; - //! open default PCM device and return 1 on success or negative error code - /*! stream_type = SND_PCM_STREAM_PLAYBACK or SND_PCM_STREAM_CAPTURE + //! stores pointers to audio buffers + std::vector<uint8_t *> pcm_buffer; + //! stores size of audio buffers + std::vector<snd_pcm_sframes_t> pcm_buffer_size_in_frames; + + //! output device number used in next open operations + unsigned int OutDevNo; + //! input device number used in next open operations + unsigned int InDevNo; + + //! keeping track of which outbuffer is currently being filled + unsigned int NextBufferOutInd; + + //! sampling rate + unsigned int sampling_rate_alsa; + //! number of channels + unsigned int no_of_channels_alsa; + //! number of bytes in channel + unsigned int no_of_bytes_in_channel; + + /*! + It is better to use STD containers - they are more convenient, + and they mean fewer problems with memory leaks. */ - int open_alsa_device(snd_pcm_stream_t stream_type, int no_of_channels, unsigned int &sampling_rate); + //! outbuffers depending on samples type + std::vector<std::vector<uint8_t>> buffers_8bit; + std::vector<std::vector<int16_t>> buffers_16bit; + std::vector<std::vector<int32_t>> buffers_32bit; + std::vector<std::vector<float>> buffers_32bit_f; + std::vector<std::vector<double>> buffers_64bit; + + //! inbuffer + std::vector<char> capture_buffer; + + //! samples have integer values instead of float ones + bool IsHigherQualityMode; + + //! CPU architecture - endianness + bool IsLittleEndian; + + //! Is the PCM device opened for recording? + bool IsDeviceInputOpen; + //! Is the PCM device opened for playback? + bool IsDeviceOutputOpen; + + //! Has playback already started? + bool IsPlayingNow; + //! Is playback stopped? + bool StopPlayback; + + //! Has recording already started? + bool StopRecording; + //! Has recording already started? + bool IsRecordingNow; + + //! audio inbuffer size in samples + snd_pcm_uframes_t audio_inbuffer_size_in_frames; + //! audio outbuffer size in samples + snd_pcm_uframes_t audio_outbuffer_size_in_frames; + + //! type of samples in WaveInBuffers + DSP::e::SampleType InSampleTypeALSA; + + //! We always use the non-blocking mode in DSPElib + bool non_blocking_mode; + + //! buffer capacity + int size_b; + + /*! Open default PCM device and return 1 on success or negative error code + stream_type = SND_PCM_STREAM_PLAYBACK or SND_PCM_STREAM_CAPTURE + the rest of the parameters are set in the class + */ + int open_alsa_device(snd_pcm_stream_t stream_type); + //! Close opened PCM device void close_alsa_device(bool do_drain = false, bool use_log = false); - void get_period_size(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_period_size(snd_pcm_uframes_t &frames, unsigned int &period_time); + + //! playback + snd_pcm_sframes_t pcm_writei(const void *buffer, const snd_pcm_uframes_t &frames); // M.B. this will be more transparent + + //! Set SND PCM format depending on no of bytes in channel and CPU endianness + int set_snd_pcm_format(snd_pcm_hw_params_t *params); public: - //! log basic ALSA information + //! Log basic ALSA information void log_driver_data(); + //! Select the desired device from the interface for recording unsigned int select_input_device_by_number(const unsigned int &device_number=UINT_MAX); + //! Select the desired device from the interface for playback unsigned int select_output_device_by_number(const unsigned int &device_number=UINT_MAX); + //! Open PCM device for playback long open_PCM_device_4_output(const int &no_of_channels, int no_of_bits, const long &sampling_rate, const long &audio_outbuffer_size = -1); + //! Open PCM device for recording long open_PCM_device_4_input(const int &no_of_channels, int no_of_bits, const long &sampling_rate, const long &audio_inbuffer_size = -1); + + //! Closes PCM device from recording bool close_PCM_device_input(void); + //! Closes PCM device from playback bool close_PCM_device_output(const bool &do_drain); - //! returns true is the playback is on + //! Returns true is the playback is on bool is_device_playing(void); - //! initializes playback stopping + + //! Initializes playback stopping bool stop_playback(void); - //! returns true is the sound capture is on + + //! Returns true is the sound capture is on bool is_device_recording(void); - //! returns true is the sound capture is on + + //! Returns true is the sound capture is on bool stop_recording(void); //! \note values stored in float_buffer might be altered long append_playback_buffer(DSP::Float_vector &float_buffer); + //! Starts sound capture bool start_recording(void); + + //! For sound capture 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 @@ -65,8 +157,9 @@ namespace DSP { //! Returns false if callbacks are not supported of playback bool is_output_callback_supported(void); - //! object constructor + //! ALSA_obcject_t class constructor ALSA_object_t(); + //! ALSA_obcject_t class destructor ~ALSA_object_t(); }; diff --git a/src/include/DSP_types.h b/src/include/DSP_types.h index a434110ba8b0bc60ff8c81a97b42b9df3002e8d4..5f2cb5e2ef4b7fb2ae75a27d02fc0fae46b964bd 100644 --- a/src/include/DSP_types.h +++ b/src/include/DSP_types.h @@ -569,17 +569,11 @@ namespace DSP { virtual unsigned int select_output_device_by_number(const unsigned int &device_number=UINT_MAX) = 0; //! audio_outbuffer_size is in samples (note that, for example, sample for 16bit stereo is represented by 4bytes) - /*! On success returns actual sampling rate of the opened device - * On fail returen -1; - */ 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 = -1) = 0; //! audio_inbuffer_size is in samples (note that, for example, sample for 16bit stereo is represented by 4bytes) - /*! On success returns actual sampling rate of the opened device. - * On fail returen -1; - */ virtual long open_PCM_device_4_input(const int &no_of_channels, int no_of_bits, const long &sampling_rate, const long &audio_inbuffer_size = -1) = 0; virtual bool close_PCM_device_input(void) = 0; - //! close sound card output + //! close sound card output /*! if do_drain == true wait until sound card stops playing */ virtual bool close_PCM_device_output(const bool &do_drain) = 0; @@ -596,10 +590,10 @@ namespace DSP { virtual bool stop_recording(void) = 0; /*! Appends data to audio buffers and sends data to sound card is buffer is full. - * The class implementation should provide DSP::NoOfAudioOutputBuffers buffers with space for audio_outbuffer_size sample each. + * The class implementation should provide at least DSP::NoOfAudioOutputBuffers buffers with space for audio_outbuffer_size sample each. * When sound is playing then if the function call results in filling the buffer (or buffers) * all full buffers should be sent to the sound card. - * When sound has not stated playing yet then full buffers should be sent to the sound card only when + * When sound has not stated playing yest then full buffers sould be sent to the sound card only when * DSP::NoOfAudioOutputBuffers-1 buffers are filled. This prevents sound staterring and leaves one spare buffer for new samples. * * \note values stored in float_buffer might be altered