/** * SMIoT JASON Qualilife sound recorder command line program. * * Author: Jan Schlüter <jan.schluter@lis-lab.fr> */ #include <iostream> #include <string> #include <memory> #include "recorder.h" #include "filewriter.h" #include "cleanexit.h" void print_usage(char *name) { std::cout << "SMIoT JASON Qualilife sound recorder"; #ifdef JASONREC_VERSION std::cout << " v" << JASONREC_VERSION; #endif std::cout << std::endl; std::cout << "Usage: " << name << " CHANNELS RATE FILENAME [CHUNKLEN [TOTALLEN [DEVICE]]]" << std::endl; std::cout << " CHANNELS: number of channels to record (1 to 5)" << std::endl; std::cout << " RATE: sample rate in Hz to record at (integral number)" << std::endl; std::cout << " FILENAME: output file name; should include strftime() format specifiers" << std::endl; std::cout << " if CHUNKLEN is specified. For miliseconds, use %z. Example: location/recording_%Y%m%d_%H%M%S_%z.wav" << std::endl; std::cout << " CHUNKLEN: length per output file in seconds; will start a new file whenever" << std::endl; std::cout << " this length is reached. If not given or zero, will record a single file." << std::endl; std::cout << " TOTALLEN: total recording length; will stop when this length is reached." << std::endl; std::cout << " If not given or zero, will record continuously until killed." << std::endl; std::cout << " DEVICE: which device to use in case multiple JASON cards are connected," << std::endl; std::cout << " where 0 is the first, 1 is the second card found (and so on)." << std::endl; } int record(size_t channels, size_t rate, std::string &filename, float chunklen, float totallen, size_t device) { JasonRecorder recorder = JasonRecorder(); std::cout << "Found " << recorder.get_device_count() << " JASON card(s)." << std::endl; if (recorder.get_device_count() == 0) { std::cout << "Aborting." << std::endl; return 1; } try { // prepare the device std::cout << "Selecting device number " << device << "..." << std::endl; recorder.set_device(device); std::cout << "Setting recording format to " << channels << " channels at " << rate << " Hz..." << std::endl; recorder.set_format(channels, rate); // prepare the file writer std::unique_ptr<FileWriter> filewriter; if (chunklen > 0) { // implementation note: in C++14 we would use std::make_unique<SplitWavFileWriter>(...) filewriter.reset(new SplitWavFileWriter(filename, channels, rate, chunklen * rate)); } else { filewriter.reset(new WavFileWriter(filename, channels, rate, totallen * rate)); } // 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; size_t failed_attempts = 0; std::vector<std::int16_t> samples; try { 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)) { 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); failed_attempts = 0; } else { // if we received no message or no audio data 20x in a row, abort failed_attempts += 1; if (failed_attempts >= 20) { throw std::runtime_error("Device does not send audio data."); } } } recorder.stop_recording(); std::cout << "Stopped recording." << std::endl; } catch (const std::exception& e) { recorder.stop_recording(); throw; } } catch (const std::exception& e) { std::cout << "Error: " << e.what() << std::endl; return 1; } return 0; } int main(int argc, char *argv[]) { if ((argc < 4) || (argc > 7)) { print_usage(argv[0]); return 1; } // parse command line options int num_channels; try { num_channels = std::stoi(argv[1]); } catch (const std::exception& e) { std::cout << "Error: Could not interpret " << argv[1] << " as an integer." << std::endl; return 1; } int rate; try { rate = std::stoi(argv[2]); } catch (const std::exception& e) { std::cout << "Error: Could not interpret " << argv[2] << " as an integer." << std::endl; return 1; } std::string filename = argv[3]; float chunklen; try { chunklen = (argc > 4) ? std::stof(argv[4]) : 0; } catch (const std::exception& e) { std::cout << "Error: Could not interpret " << argv[4] << " as a float." << std::endl; return 1; } float totallen; try { totallen = (argc > 5) ? std::stof(argv[5]) : 0; } catch (const std::exception& e) { std::cout << "Error: Could not interpret " << argv[5] << " as a float." << std::endl; return 1; } int device; try { device = (argc > 6) ? std::stoi(argv[6]) : 0; } catch (const std::exception& e) { std::cout << "Error: Could not interpret " << argv[6] << " as an integer." << std::endl; return 1; } // check command line options for validity if ((num_channels < 1) || (num_channels > 5)) { std::cout << "Error: CHANNELS must be in 1..5, got " << num_channels << " instead" << std::endl; return 1; } if ((rate <= 0)) { std::cout << "Error: RATE must be positive, got " << rate << " instead" << std::endl; return 1; } if ((chunklen < 0)) { std::cout << "Error: CHUNKLEN must be positive or zero, got " << chunklen << " instead" << std::endl; return 1; } if ((totallen < 0)) { std::cout << "Error: TOTALLEN must be positive or zero, got " << totallen << " instead" << std::endl; return 1; } if ((device < 0)) { std::cout << "Error: DEVICE must be nonnegative, got " << device << " instead" << std::endl; return 1; } // hand over to the recording function return record(num_channels, rate, filename, chunklen, totallen, device); }