diff --git a/CMakeLists.txt b/CMakeLists.txt index 8d14cad842dc23e00c67e6371d530047b4f3882f..3eaad61d151a225bb5e87ab90cdbdb0933cb6415 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ if(POLICY CMP0063) endif() project(jasonrec) -set(JASONREC_VERSION "1.1") +set(JASONREC_VERSION "1.2") add_definitions(-DJASONREC_VERSION="${JASONREC_VERSION}") find_path(LIBUSB_INCLUDE_DIR diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0f3186179766a0caf5f9ed0b0eafa09f39a69035..3a9ed88ff9ef8b948129bd0d5613341499e14236 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,6 +1,7 @@ add_executable(jasonrec recorder.cpp filewriter.cpp + cleanexit.cpp main.cpp) # compile as C++11 set_property(TARGET jasonrec @@ -17,4 +18,3 @@ target_link_libraries(jasonrec ${LIBUSB_LIBRARY}) install(TARGETS jasonrec DESTINATION bin) - diff --git a/src/cleanexit.cpp b/src/cleanexit.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bd977c8327f21ed2dfce4625bb2d89edffabcce8 --- /dev/null +++ b/src/cleanexit.cpp @@ -0,0 +1,35 @@ +/** + * Simple signal handler for clean termination of an application. + * + * Author: Jan Schlüter <jan.schluter@lis-lab.fr> + */ + +#include <atomic> +#include <csignal> +#ifdef __unix__ +#include <unistd.h> +#endif + +std::atomic<bool> _exit_requested(false); + +void caught_signal(int) +{ + _exit_requested.store(true); +} + +void allow_clean_exit() { +#ifdef __unix__ + struct sigaction sa = {0}; + sa.sa_handler = caught_signal; + sigfillset(&sa.sa_mask); + sigaction(SIGINT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); +#else + signal(SIGINT, caught_signal); + signal(SIGTERM, caught_signal); +#endif +} + +bool exit_requested() { + return _exit_requested.load(); +} diff --git a/src/cleanexit.h b/src/cleanexit.h new file mode 100644 index 0000000000000000000000000000000000000000..24a88dd473897e30a69d9f3010128315095de43b --- /dev/null +++ b/src/cleanexit.h @@ -0,0 +1,21 @@ +/** + * Simple signal handler for clean termination of an application. + * + * Author: Jan Schlüter <jan.schluter@lis-lab.fr> + */ + +#ifndef CLEANEXIT_H +#define CLEANEXIT_H + +/** + * Set up signal handlers to make exit_requested() work. + */ +void allow_clean_exit(); + +/** + * Check if the application was requested to terminate. + * \returns whether the application was requested to terminate. + */ +bool exit_requested(); + +#endif diff --git a/src/main.cpp b/src/main.cpp index 8c503216b0d79ba8a9cf9ccb2d393d6828473fe8..20c7756354ce4c891e86a9dd6b089bf6d0bcbfd8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -8,6 +8,7 @@ #include <memory> #include "recorder.h" #include "filewriter.h" +#include "cleanexit.h" void print_usage(char *name) { std::cout << "SMIoT JASON Qualilife sound recorder"; @@ -52,6 +53,7 @@ int record(size_t channels, size_t rate, std::string &filename, float chunklen, } // start the recording loop std::cout << "Starting to record..." << std::endl; + allow_clean_exit(); size_t total_samples_wanted = totallen * rate; size_t total_samples_read = 0; std::vector<std::int16_t> samples; @@ -59,14 +61,20 @@ int record(size_t channels, size_t rate, std::string &filename, float chunklen, recorder.start_recording(); // we will record until we have enough (or forever, if totallen == 0) while ((total_samples_wanted == 0) || (total_samples_read < total_samples_wanted)) { - recorder.get_samples(samples); - total_samples_read += samples.size() / channels; - // if we have too much now, crop the last packet - if ((total_samples_wanted > 0) && (total_samples_read > total_samples_wanted)) { - samples.resize(samples.size() - (total_samples_read - total_samples_wanted) * channels); + if (exit_requested()) { + std::cout << "Termination requested." << std::endl; + break; + } + recorder.get_samples(samples, false, 500); + if (samples.size() > 0) { + total_samples_read += samples.size() / channels; + // if we have too much now, crop the last packet + if ((total_samples_wanted > 0) && (total_samples_read > total_samples_wanted)) { + samples.resize(samples.size() - (total_samples_read - total_samples_wanted) * channels); + } + // pass it on to the file writer + filewriter->write(samples); } - // pass it on to the file writer - filewriter->write(samples); } recorder.stop_recording(); std::cout << "Stopped recording." << std::endl; diff --git a/src/recorder.cpp b/src/recorder.cpp index 8c5e33e8609d4c82275ab1021897965abce3347f..6c73ea7f283223ea3072312d30b1d91a4c886cd3 100644 --- a/src/recorder.cpp +++ b/src/recorder.cpp @@ -158,13 +158,13 @@ size_t JasonRecorder::receive_message(std::uint8_t *buffer, size_t length, size_ return received; } -void JasonRecorder::get_samples(std::vector<std::int16_t> &samples, bool planar) { +void JasonRecorder::get_samples(std::vector<std::int16_t> &samples, bool planar, size_t max_wait) { if (!num_channels || !sample_rate) { throw std::logic_error("must call set_format() first"); } std::array<std::uint8_t, 4 + MAX_PAYLOAD> buffer; while (true) { - size_t received = receive_message(buffer.data(), buffer.size()); + size_t received = receive_message(buffer.data(), buffer.size(), max_wait); if (received) { // we could read the payload length, but it is wrong for sample data //size_t length = buffer[1] << 8 + buffer[2]; @@ -194,6 +194,11 @@ void JasonRecorder::get_samples(std::vector<std::int16_t> &samples, bool planar) break; } } + else if (max_wait > 0) { + // we timed out, we do not want to wait again + samples.resize(0); + break; + } } } diff --git a/src/recorder.h b/src/recorder.h index 0c92ed1d5af86e1edd98f00ade553c0ece077e07..3bfb7dc5c859f0a8f58e4e639936bc556399ffdd 100644 --- a/src/recorder.h +++ b/src/recorder.h @@ -95,8 +95,12 @@ public: * \param[in] planar If true, return the samples in planar form, i.e., one * channel after the other, the format sent by the device. If false (the * default), interleave the channels as done in PCM WAVE files. + * \param[in] max_wait Maximum time in milliseconds to block waiting for a + * packet from the device. If the time elapses before receiving any packets, + * or if a different type of packet was received, returns with an empty + * samples vector. Set to zero to wait indefinitely for a sample packet. */ - void get_samples(std::vector<std::int16_t> &samples, bool planar=false); + void get_samples(std::vector<std::int16_t> &samples, bool planar=false, size_t max_wait=0); /** Converts samples from planar to interleaved form. * \param[in] input A pointer to the planar samples to read. * \param[out] output A pointer to write the interleaved samples to.