From c1bee8516d2614d686f646265775f85ebe9c529c Mon Sep 17 00:00:00 2001
From: ferrari <maxence.ferrari@gmail.com>
Date: Mon, 27 Sep 2021 16:46:43 +0200
Subject: [PATCH] Upgrade to HighBlue

---
 src/filewriter.cpp |  75 ++++++++++++++++-----
 src/filewriter.h   |  25 +++++--
 src/main.cpp       | 161 ++++++++++++++++++++++++++-------------------
 src/recorder.cpp   | 137 +++++++++++++++++++++-----------------
 src/recorder.h     |  67 ++++++++++---------
 5 files changed, 286 insertions(+), 179 deletions(-)

diff --git a/src/filewriter.cpp b/src/filewriter.cpp
index a86db05..dbb2ed1 100644
--- a/src/filewriter.cpp
+++ b/src/filewriter.cpp
@@ -2,6 +2,7 @@
  * Wave file writing, with or without splitting by length.
  *
  * Author: Jan Schlüter <jan.schluter@lis-lab.fr>
+ * Author: Maxence Ferrari <maxence.ferrari@lis-lab.fr>
  */
 
 #include "filewriter.h"
@@ -12,8 +13,8 @@
 #include <sstream>
 #include <iomanip>
 
-FileWriter::FileWriter(std::string &filename_template, size_t num_channels, size_t sample_rate) :
-        filename_template(filename_template), num_channels(num_channels), sample_rate(sample_rate) {
+FileWriter::FileWriter(std::string &filename_template, size_t num_channels, size_t sample_rate, size_t depth) :
+        filename_template(filename_template), num_channels(num_channels), sample_rate(sample_rate), depth(depth) {
     // nothing
 }
 
@@ -50,6 +51,10 @@ std::string FileWriter::generate_filename() {
     return std::string(buffer.begin(), buffer.begin() + length);
 }
 
+void FileWriter::write(std::vector<uint8_t> &samples) {
+    write(samples.data(), samples.size());
+}
+
 void FileWriter::write(std::vector<int16_t> &samples) {
     write(samples.data(), samples.size());
 }
@@ -70,13 +75,14 @@ uint32_t read_little_endian(uint8_t (&source)[4]) {
     return (source[0] + source[1] << 8 + source[2] << 16 + source[3] << 24);
 }
 
-WavFileWriter::WavFileWriter(std::string &filename_template, size_t num_channels, size_t sample_rate) :
-        WavFileWriter(filename_template, num_channels, sample_rate, 0) {
+WavFileWriter::WavFileWriter(std::string &filename_template, size_t num_channels, size_t sample_rate, size_t depth) :
+        WavFileWriter(filename_template, num_channels, sample_rate, depth, 0) {
     // nothing
 }
 
-WavFileWriter::WavFileWriter(std::string &filename_template, size_t num_channels, size_t sample_rate, size_t expected_num_samples) :
-        FileWriter(filename_template, num_channels, sample_rate),
+WavFileWriter::WavFileWriter(std::string &filename_template, size_t num_channels, size_t sample_rate, size_t depth,
+                             size_t expected_num_samples) :
+        FileWriter(filename_template, num_channels, sample_rate, depth),
         outfile(generate_filename(), std::ios::out | std::ios::binary | std::ios::trunc),
         samples_written(0) {
     // check if we could open the file
@@ -86,10 +92,11 @@ WavFileWriter::WavFileWriter(std::string &filename_template, size_t num_channels
     // write header
     store_little_endian(header.fmt_channels, num_channels);
     store_little_endian(header.fmt_sample_rate, sample_rate);
-    store_little_endian(header.fmt_byte_rate, num_channels * sample_rate * 2);
-    store_little_endian(header.fmt_frame_size, num_channels * 2);
+    store_little_endian(header.fmt_bits_per_sample, 1<<(2+depth));
+    store_little_endian(header.fmt_byte_rate, num_channels * sample_rate * depth);
+    store_little_endian(header.fmt_frame_size, num_channels * depth);
     if (expected_num_samples) {
-        size_t expected_data_size = expected_num_samples * num_channels * 2;
+        size_t expected_data_size = expected_num_samples * num_channels * depth;
         store_little_endian(header.data_size, expected_data_size);
         store_little_endian(header.chunk_size, expected_data_size + 36);
         // TODO: on linux, we could use fallocate to reserve the final size
@@ -101,7 +108,7 @@ WavFileWriter::WavFileWriter(std::string &filename_template, size_t num_channels
 
 WavFileWriter::~WavFileWriter() {
     // finalize header, if needed
-    size_t data_size = samples_written * num_channels * 2;
+    size_t data_size = samples_written * num_channels * depth;
     if (data_size != read_little_endian(header.data_size)) {
         store_little_endian(header.data_size, data_size);
         store_little_endian(header.chunk_size, data_size + 36);
@@ -110,16 +117,22 @@ WavFileWriter::~WavFileWriter() {
     }
 }
 
+void WavFileWriter::write(uint8_t *samples, size_t num_samples) {
+    outfile.write((char*) samples, num_samples * sizeof(*samples));
+    samples_written += num_samples /(num_channels * depth);
+}
+
 void WavFileWriter::write(int16_t *samples, size_t num_samples) {
     outfile.write((char*) samples, num_samples * sizeof(*samples));
-    samples_written += num_samples / num_channels;
+    samples_written += num_samples / num_channels ;
 }
 
 
-SplitWavFileWriter::SplitWavFileWriter(std::string &filename_template, size_t num_channels, size_t sample_rate, size_t samples_per_file) :
-        FileWriter(filename_template, num_channels, sample_rate),
+SplitWavFileWriter::SplitWavFileWriter(std::string &filename_template, size_t num_channels, size_t sample_rate,
+                                       size_t depth, size_t samples_per_file) :
+        FileWriter(filename_template, num_channels, sample_rate, depth),
         samples_per_file(samples_per_file),
-        current_file(NULL),
+        current_file(nullptr),
         current_file_samples_written(0) {
     // nothing
 }
@@ -130,12 +143,40 @@ SplitWavFileWriter::~SplitWavFileWriter() {
     }
 }
 
+void SplitWavFileWriter::write(uint8_t *samples, size_t num_samples) {
+    size_t available = num_samples /(num_channels * depth);
+    // start as many new files as required to write out the samples
+    while (current_file_samples_written + available >= samples_per_file) {
+        if (!current_file) {
+            current_file = new WavFileWriter(filename_template, num_channels, sample_rate, depth, samples_per_file);
+        }
+        // write out as much as fits into the current file and move the pointer
+        size_t missing = samples_per_file - current_file_samples_written;
+        current_file->write(samples, missing * num_channels * depth);
+        samples += missing * num_channels * depth;
+        available -= missing;
+        // start a new file
+        delete current_file;
+        current_file = nullptr;
+        current_file_samples_written = 0;
+    }
+    // if there are samples left, write them to the current file
+    if (available) {
+        if (!current_file) {
+            current_file = new WavFileWriter(filename_template, num_channels, sample_rate, depth, samples_per_file);
+        }
+        current_file->write(samples, available * num_channels * depth);
+        current_file_samples_written += available;
+    }
+}
+
+
 void SplitWavFileWriter::write(int16_t *samples, size_t num_samples) {
     size_t available = num_samples / num_channels;
     // start as many new files as required to write out the samples
     while (current_file_samples_written + available >= samples_per_file) {
         if (!current_file) {
-            current_file = new WavFileWriter(filename_template, num_channels, sample_rate, samples_per_file);
+            current_file = new WavFileWriter(filename_template, num_channels, sample_rate, 0, samples_per_file);
         }
         // write out as much as fits into the current file and move the pointer
         size_t missing = samples_per_file - current_file_samples_written;
@@ -144,13 +185,13 @@ void SplitWavFileWriter::write(int16_t *samples, size_t num_samples) {
         available -= missing;
         // start a new file
         delete current_file;
-        current_file = NULL;
+        current_file = nullptr;
         current_file_samples_written = 0;
     }
     // if there are samples left, write them to the current file
     if (available) {
         if (!current_file) {
-            current_file = new WavFileWriter(filename_template, num_channels, sample_rate, samples_per_file);
+            current_file = new WavFileWriter(filename_template, num_channels, sample_rate, 0, samples_per_file);
         }
         current_file->write(samples, available * num_channels);
         current_file_samples_written += available;
diff --git a/src/filewriter.h b/src/filewriter.h
index 76f6509..b502e7f 100644
--- a/src/filewriter.h
+++ b/src/filewriter.h
@@ -2,6 +2,7 @@
  * Wave file writing, with or without splitting by length.
  *
  * Author: Jan Schlüter <jan.schluter@lis-lab.fr>
+ * Author: Maxence Ferrari <maxence.ferrari@lis-lab.fr>
  */
 
 #ifndef FILEWRITER_H
@@ -23,12 +24,17 @@ protected:
     // sample format options
     size_t num_channels;
     size_t sample_rate;
+    size_t depth;
 public:
     /** Abstract constructor to be used by subclasses.
      */
-    FileWriter(std::string &filename_template, size_t num_channels, size_t sample_rate);
+    FileWriter(std::string &filename_template, size_t num_channels, size_t sample_rate, size_t depth);
     virtual ~FileWriter();
 
+    /** Writes out the given vector of 8-bit samples.
+     * \param[in] samples The samples to write, with interleaved channels.
+     */
+    void write(std::vector<uint8_t> &samples);
     /** Writes out the given vector of 16-bit samples.
      * \param[in] samples The samples to write, with interleaved channels.
      */
@@ -39,6 +45,7 @@ public:
      * \param[in] num_samples The number of samples to write, adding up the
      * counts per channel (must be divisible by the number of channels).
      */
+    virtual void write(uint8_t *samples, size_t num_samples) = 0;
     virtual void write(int16_t *samples, size_t num_samples) = 0;
 };
 
@@ -81,8 +88,10 @@ public:
      * written will contain.
      * \param[in] sample_rate The number of samples per second (per channel) the
      * sample data to be written will contain.
+     * \param[in] depth The number of bytes per samples the
+     * sample data to be written will contain.
      */
-    WavFileWriter(std::string &filename_template, size_t num_channels, size_t sample_rate);
+    WavFileWriter(std::string &filename_template, size_t num_channels, size_t sample_rate, size_t depth);
     /** Instantiates a wave file writer.
      * \param[in] filename_template The name of the file to write to. Will be
      * created or opened immediately, truncating any existing content. May
@@ -92,13 +101,17 @@ public:
      * written will contain.
      * \param[in] sample_rate The number of samples per second (per channel) the
      * sample data to be written will contain.
+     * \param[in] depth The number of bytes per samples the
+     * sample data to be written will contain.
      * \param[in] expected_num_samples The expected total number of samples (per
      * channel) that will be written. The file header will be written
      * accordingly and not rewritten on closing the file if the number matches.
      */
-    WavFileWriter(std::string &filename_template, size_t num_channels, size_t sample_rate, size_t expected_num_samples);
+    WavFileWriter(std::string &filename_template, size_t num_channels, size_t sample_rate, size_t depth,
+                  size_t expected_num_samples);
     ~WavFileWriter();
 
+    void write(uint8_t *samples, size_t num_samples);
     void write(int16_t *samples, size_t num_samples);
 };
 
@@ -122,12 +135,16 @@ public:
      * written will contain.
      * \param[in] sample_rate The number of samples per second (per channel) the
      * sample data to be written will contain.
+     * \param[in] depth The number of bytes per samples the
+     * sample data to be written will contain.
      * \param[in] samples_per_file The target number of samples (per channel)
      * that will be written to a file before starting the next one.
      */
-    SplitWavFileWriter(std::string &filename_template, size_t num_channels, size_t sample_rate, size_t samples_per_file);
+    SplitWavFileWriter(std::string &filename_template, size_t num_channels, size_t sample_rate,
+                       size_t depth, size_t samples_per_file);
     ~SplitWavFileWriter();
 
+    void write(uint8_t *samples, size_t num_samples);
     void write(int16_t *samples, size_t num_samples);
 };
 
diff --git a/src/main.cpp b/src/main.cpp
index 6f3ef59..d99fb45 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -2,10 +2,12 @@
  * SMIoT JASON Qualilife sound recorder command line program.
  *
  * Author: Jan Schlüter <jan.schluter@lis-lab.fr>
+ * Author: Maxence Ferrari <maxence.ferrari@lis-lab.fr>
  */
 #include <iostream>
 #include <string>
 #include <memory>
+#include <cstring>
 #include "recorder.h"
 #include "filewriter.h"
 #include "cleanexit.h"
@@ -16,40 +18,47 @@ void print_usage(char *name) {
     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 << "Usage: " << name << " channels rate filename [--help, -h] [--chunk_len, -c CHUNK_LEN] "
+                                   << "[--total_len, -t TOTAL_LEN] [--device, -d DEVICE] [--bit_depth, -b BIT_DEPTH] "
+                                   << "[--filter, -f FILTER] [--verbose, -v]" << std::endl;
+    std::cout << "Positional arguments:" << std::endl;
+    std::cout << "  CHANNELS:\tnumber of channels to record (1 to 5)" << std::endl;
+    std::cout << "  RATE:\tsample rate in Hz to record at (integral number)" << std::endl;
+    std::cout << "  FILENAME:\toutput file name; should include strftime() format specifiers" << std::endl;
+    std::cout << "    if CHUNK_LEN is specified. For miliseconds, use %z. Example: location/recording_%Y%m%d_%H%M%S_%z.wav" << std::endl;
+    std::cout << "Optional arguments:" << std::endl;
+    std::cout << "-h, --help\t\tshow this help message and exit" << std::endl;
+
+    std::cout << "  --bit_depth, -b\tBIT_DEPTH:\tSize of each samples in bits. Must be a multiple of 8. (Default: 16)" << std::endl;
+    std::cout << "  --filter, -f\tFILTER:\tNumber of the filter to use. Must be between 0 and 2. (Default: 0)" << std::endl;
+    std::cout << "  --chunk_len, -c\tCHUNK_LEN:\tlength 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 << "  --total_len, -t\tTOTAL_LEN:\tTotal 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 << "  --device, -d\tDEVICE:\tWhich 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;
+    std::cout << "  --verbose, -v\t\tEnable the printing of status message " << std::endl;
 }
 
-int record(size_t channels, size_t rate, std::string &filename, float chunklen, float totallen, size_t device) {
-    JasonRecorder recorder = JasonRecorder();
+int record(size_t channels, size_t rate, size_t depth, size_t filter,  std::string &filename, float chunklen, float totallen, size_t device, bool verbose) {
+    JasonRecorder recorder = JasonRecorder(verbose);
     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;
+        return 2;
     }
     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));
+            filewriter.reset(new SplitWavFileWriter(filename, channels, rate, depth, chunklen * rate));
         }
         else {
-            filewriter.reset(new WavFileWriter(filename, channels, rate, totallen * rate));
+            filewriter.reset(new WavFileWriter(filename, channels, rate, depth, totallen * rate));
         }
         // start the recording loop
         std::cout << "Starting to record..." << std::endl;
@@ -57,9 +66,10 @@ int record(size_t channels, size_t rate, std::string &filename, float chunklen,
         size_t total_samples_wanted = totallen * rate;
         size_t total_samples_read = 0;
         size_t failed_attempts = 0;
-        std::vector<std::int16_t> samples;
+        std::vector<std::uint8_t> samples;
         try {
-            recorder.start_recording();
+            std::cout << "Setting recording format to " << channels << " channels at " << rate << " Hz " << (1<<(2+depth)) << " bits" << std::endl;
+            recorder.start_recording(channels, rate, depth, filter);
             // 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()) {
@@ -95,82 +105,97 @@ int record(size_t channels, size_t rate, std::string &filename, float chunklen,
     }
     catch (const std::exception& e) {
         std::cout << "Error: " << e.what() << std::endl;
-        return 1;
+        return 2;
     }
     return 0;
 }
 
 int main(int argc, char *argv[]) {
-    if ((argc < 4) || (argc > 7)) {
+    if (argc < 4) {
         print_usage(argv[0]);
-        return 1;
+        return 2;
     }
 
     // parse command line options
     int num_channels;
     try {
         num_channels = std::stoi(argv[1]);
+        if ((num_channels < 1) || (num_channels > MAX_CHANNELS)) {
+            std::cout << "Error: CHANNELS must be in 1.." << MAX_CHANNELS << ", got " << num_channels << " instead" << std::endl;
+            return 2;}
     }
     catch (const std::exception& e) {
         std::cout << "Error: Could not interpret " << argv[1] << " as an integer." << std::endl;
-        return 1;
+        return 2;
     }
     int rate;
     try {
         rate = std::stoi(argv[2]);
+        if (rate!= 32000 && rate!=64000 && rate!=128000 && rate!=256000 && rate!=512000) {
+            std::cout << "Error: RATE must be a power 2 times 32kHz, got " << rate << " instead" << std::endl;
+            return 2;
+        }
     }
     catch (const std::exception& e) {
         std::cout << "Error: Could not interpret " << argv[2] << " as an integer." << std::endl;
-        return 1;
+        return 2;
     }
     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;
-    }
+    int i=4;
 
+    int device(0), bit_depth(16), filter(0);
+    float chunklen(0), totallen(0);
+    bool verbose(false);
+    while (i < argc){
+        try{
+            if (strcmp(argv[i], "--chunk_len") == 0 || strcmp(argv[i], "-c") == 0) {
+                chunklen = atof(argv[++i]);
+                if ((chunklen < 0)) {
+                    std::cout << "Error: CHUNKLEN must be positive or zero, got " << chunklen << " instead" << std::endl;
+                    return 2;
+                }}
+            else if (strcmp(argv[i], "--device") == 0 || strcmp(argv[i], "-d") == 0) {
+                device = atoi(argv[++i]);
+                if ((device < 0)) {
+                    std::cout << "Error: DEVICE must be nonnegative, got " << device << " instead" << std::endl;
+                    return 2;
+                }}
+            else if (strcmp(argv[i], "--total_len") == 0 || strcmp(argv[i], "-t") == 0) {
+                totallen = atof(argv[++i]);
+                if ((totallen < 0)) {
+                    std::cout << "Error: TOTALLEN must be positive or zero, got " << totallen << " instead" << std::endl;
+                    return 2;
+                }}
+            else if (strcmp(argv[i], "--bit_depth") == 0 || strcmp(argv[i], "-b") == 0) {
+                bit_depth = atoi(argv[++i]);
+                if (not(bit_depth % 8)) {
+                    std::cout << "Error: DEPTH must be a multiple of 8, got " << bit_depth << " instead" << std::endl;
+                    return 2;
+                }}
+            else if (strcmp(argv[i], "--filter") == 0 || strcmp(argv[i], "-f") == 0) {
+                filter = atoi(argv[++i]);
+                if (filter < 0 || filter > 2 ) {
+                    std::cout << "Error: filter must be between 0 and 2, got " << filter << " instead" << std::endl;
+                    return 2;
+                }}
+            else if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0){
+                print_usage(argv[0]);
+                return 1;
+            }
+            else if (strcmp(argv[i], "--verbose") == 0 || strcmp(argv[i], "-v") == 0){
+                verbose = true;
+            }
+            else {
+                std::cout << "Unrecognized argument " << argv[i] << std::endl;
+                return 2;
+            }}
+        catch (const std::exception& e) {
+            std::cout << "Error: Could not interpret " << argv[i] << " ( "  << argv[i-1] << " ) " " as a number." << std::endl;
+            return 2;
+        }
+        i++;
+    }
     // hand over to the recording function
-    return record(num_channels, rate, filename, chunklen, totallen, device);
+    return record(num_channels, rate, bit_depth/8, filter,  filename, chunklen, totallen, device, verbose);
 }
diff --git a/src/recorder.cpp b/src/recorder.cpp
index 6c73ea7..fc49272 100644
--- a/src/recorder.cpp
+++ b/src/recorder.cpp
@@ -2,15 +2,17 @@
  * SMIoT JASON Qualilife sound recording class.
  *
  * Author: Jan Schlüter <jan.schluter@lis-lab.fr>
+ * Author: Maxence Ferrari <maxence.ferrari@lis-lab.fr>
  */
 
 #include "recorder.h"
 #include <stdexcept>
+#include <iostream>
 #include <vector>
 #include <array>
 #include <algorithm>
 
-JasonRecorder::JasonRecorder() {
+JasonRecorder::JasonRecorder(bool verbose) : verbose(verbose) {
     // create libusb context
     if (libusb_init(&ctx) < 0) {
         throw std::runtime_error("libusb initialization failed");
@@ -76,28 +78,32 @@ void JasonRecorder::set_device(size_t number) {
     }
 }
 
-void JasonRecorder::send_message(std::uint8_t cmd) {
+void JasonRecorder::send_message(std::uint16_t cmd) {
     send_message(cmd, NULL, 0);
 }
 
-void JasonRecorder::send_message(std::uint8_t cmd, std::vector<std::uint8_t> &payload) {
+void JasonRecorder::send_message(std::uint16_t cmd, std::vector<std::uint8_t> &payload) {
     send_message(cmd, payload.data(), payload.size());
 }
 
-void JasonRecorder::send_message(std::uint8_t cmd, std::uint8_t *payload, size_t length) {
+void JasonRecorder::send_message(std::uint16_t cmd, std::uint8_t *payload, size_t length) {
     if (!handle) {
         throw std::logic_error("must call set_device() first");
     }
     // message format: 0xfe + payload size (2 byte) + command (1 byte) + payload
     std::vector<std::uint8_t> data;
-    data.reserve(4 + length);
-    data.push_back(0xfe);
+    data.reserve(6 + length);
+    data.push_back(FRAME_START);
+    data.push_back((std::uint8_t) ((cmd >> 8) & 0xFF));
+    data.push_back((std::uint8_t) (cmd & 0xFF));
     data.push_back((std::uint8_t) ((length >> 8) & 0xFF));
     data.push_back((std::uint8_t) (length & 0xFF));
-    data.push_back(cmd);
     if (length) {
         data.insert(data.end(), payload, payload + length);
     }
+    // compute the checksum
+    data.push_back(FRAME_START);
+    for (int i=1; i < 5+length; data[5 + length] ^= data[i++]);
     // send message, allow a maximum of 10 seconds for it to go through
     int sent;
     if (libusb_bulk_transfer(handle, ENDPOINT_SEND, data.data(), data.size(), &sent, 10000) < 0) {
@@ -108,91 +114,101 @@ void JasonRecorder::send_message(std::uint8_t cmd, std::uint8_t *payload, size_t
     };
 }
 
-void JasonRecorder::set_format(size_t num_channels, size_t sample_rate) {
-    // set channels
-    std::vector<std::uint8_t> payload1 = {(std::uint8_t) num_channels};
-    send_message(CMD_SET_CHANNELS, payload1);
+void JasonRecorder::start_recording(std::uint8_t num_channels,size_t  sample_rate, std::uint8_t depth, std::uint8_t num_filter) {
+    std::vector<std::uint8_t> payload1 = {
+            START,
+            (std::uint8_t) ((sample_rate >> 24) & 0xFF),
+            (std::uint8_t) ((sample_rate >> 16) & 0xFF),
+            (std::uint8_t) ((sample_rate >> 8) & 0xFF),
+            (std::uint8_t) (sample_rate & 0xFF),
+            num_channels,
+            (std::uint8_t) ((1 << (2 + depth))),
+            num_filter};
+    send_message(START_ID, payload1);
     this->num_channels = num_channels;
-    // set sample rate (needs to be sent in Big-endian order, MSB first)
-    std::vector<std::uint8_t> payload2 = {0x00,
-        (std::uint8_t) ((sample_rate >> 24) & 0xFF),
-        (std::uint8_t) ((sample_rate >> 16) & 0xFF),
-        (std::uint8_t) ((sample_rate >> 8) & 0xFF),
-        (std::uint8_t) (sample_rate & 0xFF)
-    };
-    send_message(CMD_SET_SAMPLE_RATE, payload2);
     this->sample_rate = sample_rate;
-    // set resolution to 16 bits
-    std::vector<std::uint8_t> payload3 = {16};
-    send_message(CMD_SET_RESOLUTION, payload3);
-}
-
-void JasonRecorder::start_recording() {
-    if (!num_channels || !sample_rate) {
-        throw std::logic_error("must call set_format() first");
-    }
-    send_message(CMD_START_RECORDING);
+    this->depth = depth;
+    this->num_filter = num_filter;
     recording = true;
 }
 
+
 void JasonRecorder::stop_recording() {
-    send_message(CMD_STOP_RECORDING);
+    std::vector<std::uint8_t> payload1 = {
+            STOP,
+            (std::uint8_t) ((this->sample_rate >> 24) & 0xFF),
+            (std::uint8_t) ((this->sample_rate >> 16) & 0xFF),
+            (std::uint8_t) ((this->sample_rate >> 8) & 0xFF),
+            (std::uint8_t) (this->sample_rate & 0xFF),
+            this->num_channels,
+            (std::uint8_t) ((1 << (2 + this->depth))),
+            this->num_filter};
+    send_message(START_ID, payload1);
     recording = false;
 }
 
-size_t JasonRecorder::receive_message(std::uint8_t *buffer, size_t length, size_t max_wait) {
+size_t JasonRecorder::receive_message(uint8_t *buffer, size_t max_wait) {
     if (!handle) {
         throw std::logic_error("must call set_device() first");
     }
     int received;
-    int status = libusb_bulk_transfer(handle, ENDPOINT_RECEIVE, buffer, length, &received, max_wait);
+    int status = libusb_bulk_transfer(handle, ENDPOINT_RECEIVE, buffer, MAX_MSG_LENGTH, &received, max_wait);
     if (status == LIBUSB_ERROR_OVERFLOW) {
         throw std::runtime_error("buffer too small to receive message from device");
     }
     else if ((status < 0) && (status != LIBUSB_ERROR_TIMEOUT)) {
         throw std::runtime_error("could not receive message from device");
     }
-    if (received && (buffer[0] != 0xfe)) {
-        throw std::runtime_error("received invalid message header from device");
-    }
     return received;
 }
 
-void JasonRecorder::get_samples(std::vector<std::int16_t> &samples, bool planar, size_t max_wait) {
+void JasonRecorder::get_samples(std::vector<std::uint8_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;
+    std::array<std::uint8_t, MAX_MSG_LENGTH> buffer{};
     while (true) {
-        size_t received = receive_message(buffer.data(), buffer.size(), max_wait);
+        size_t received = receive_message(buffer.data(), 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];
-            if (buffer[3] == MSG_SAMPLES) {
-                // the beginning of the payload should have 0x00, 0x01, timestamp (4 bytes)
-                if ((buffer[4] != 0x00) || (buffer[5] != 0x01)) {
-                    throw std::runtime_error("received invalid sample data from device");
-                }
+            if (buffer[0] != FRAME_START); // invalid message
+            else if ((((std::uint16_t) buffer[1] << 8 )|(buffer[2])) == DATA_ID) {
                 // find the beginning and length of the samples in the buffer
-                std::int16_t *received_samples = (std::int16_t*) &buffer[10];
-                size_t num_samples = (received - 10) / sizeof(std::int16_t);
-                num_samples = (num_samples / num_channels) * num_channels;
+                size_t num_samples = (received - 6) /this->depth;
+                num_samples = (num_samples / num_channels) * num_channels * this->depth;
                 // copy data to provided vector
                 if (planar || (num_channels == 1)) {
                     // copy out directly
                     samples.resize(0);
                     samples.reserve(num_samples);
-                    samples.insert(samples.end(), received_samples, received_samples + num_samples);
+                    samples.insert(samples.end(), &buffer[6], &buffer[6] + num_samples);
                 }
                 else {
                     // convert from blocked channels to interleaved channels
                     samples.resize(num_samples);
-                    JasonRecorder::interleave_channels(received_samples,
-                            samples.data(), num_samples / num_channels,
-                            this->num_channels);
+                    JasonRecorder::interleave_channels(&buffer[6],
+                                                       samples.data(), num_samples,
+                                                       this->num_channels, this->depth);
                 }
                 break;
             }
+            else if (this->verbose && (((std::uint16_t) buffer[1] << 8 )|(buffer[2])) == STATUS_ID) {
+                samples.resize(0);
+                std::uint8_t cks=FRAME_START; //buffer[0] == FRAME_START already check
+                for (int i=1; i <  31; cks ^= buffer[i++]);
+                std::cout << " Sr: " << (  ((size_t) buffer[5] << 24) | ((size_t) buffer[6] << 16)
+                                         | ((size_t) buffer[7] <<  8) | ((size_t)  buffer[8]))
+                          << " #Ch: " << (size_t) buffer[9] << " D: " << (size_t) buffer[10] <<  " Time: "
+                          << 2000 + buffer[11] <<'-'<< (size_t) buffer[12] <<'-'<< (size_t) buffer[13] <<' '
+                          << (size_t) buffer[14] <<':'<< (size_t) buffer[15] <<':'<< (size_t) buffer[16]
+                          << " UUID: "  << std::hex << (size_t) buffer[17] << (size_t) buffer[18] << (size_t) buffer[19] << (size_t) buffer[20]
+                          << (size_t) buffer[21] << (size_t) buffer[22] << (size_t) buffer[23] << (size_t) buffer[24] << std::dec
+                          << " Rec: " << (buffer[25] !=0)
+                          << " SPI: " << (size_t) buffer[26] << (size_t) buffer[27] << (size_t) buffer[28] << (size_t) buffer[29]
+                          << " CKS: " << (cks == 0?"True":"False") << std::endl;
+                break;
+            }
         }
         else if (max_wait > 0) {
             // we timed out, we do not want to wait again
@@ -202,23 +218,24 @@ void JasonRecorder::get_samples(std::vector<std::int16_t> &samples, bool planar,
     }
 }
 
-void JasonRecorder::interleave_channels(std::int16_t *input, std::int16_t *output, size_t num_samples_per_channel, size_t num_channels) {
+void JasonRecorder::interleave_channels(std::uint8_t *input, std::uint8_t *output, size_t num_bytes,
+                                        size_t num_channels, size_t depth) {
     // the input comes in num_channels blocks of num_samples_per_channel little-endian 16-bit samples each
     // we write these to the output in a round-robin manner, interleaving the channels
     // we use a pattern that accesses the output strictly sequentially, so it can be used to write to a mem-mapped file
     if ((num_channels < 1) || (num_channels > MAX_CHANNELS)) {
-        throw std::out_of_range("num_channels must be in [1, 5]");
+        throw std::out_of_range("num_channels must be in [1, 6]");
     }
     // prepare one input pointer per channel
-    // hoping it will end up in registers (everything is known at compile time)
-    std::int16_t *inputs[MAX_CHANNELS];
-    for (size_t c = 0; c < MAX_CHANNELS; c++) {
-        inputs[c] = input + c * num_samples_per_channel;
+    std::uint8_t *inputs[num_channels];
+    for (size_t c = 0; c < num_channels; c++) {
+        inputs[c] = input + c * (num_bytes/num_channels);
     }
     // iterate over the samples, copying in interleaved fashion
-    while (num_samples_per_channel--) {
-        for (size_t c = 0; c < num_channels; c++) {
-            *(output++) = *(inputs[c]++);
-        }
+    size_t c = 0;
+    for (size_t b=0; b < num_bytes;) {
+        *(output++) = *(inputs[c]++);
+        if (++b % depth == 0)
+            c = (c + 1) % num_channels;
     }
 }
diff --git a/src/recorder.h b/src/recorder.h
index 3bfb7dc..b29f3d1 100644
--- a/src/recorder.h
+++ b/src/recorder.h
@@ -2,6 +2,7 @@
  * SMIoT JASON Qualilife sound recording class.
  *
  * Author: Jan Schlüter <jan.schluter@lis-lab.fr>
+ * Author: Maxence Ferrari <maxence.ferrari@lis-lab.fr>
  */
 
 #ifndef RECORDER_H
@@ -11,49 +12,49 @@
 #include <cstdint>
 #include <vector>
 
+
+#define MAX_MSG_LENGTH  65536
+#define MAX_CHANNELS  6
+
 /** Class for retrieving sample data from a JASON Qualilife sound card.
  */
 class JasonRecorder {
-    const std::int16_t VENDOR_ID = 0x04d8;
+    const std::int16_t VENDOR_ID  = 0x04D8;
     const std::int16_t PRODUCT_ID = 0x0053;
-    const std::uint8_t ENDPOINT_SEND = 0x01;
+    const std::uint8_t ENDPOINT_SEND    = 0x01;
     const std::uint8_t ENDPOINT_RECEIVE = 0x81;
+
+    // device control messages
+    const std::uint8_t FRAME_START = 0xFE;
+    const std::uint16_t START_ID     = 0x0C01;
+    const std::uint16_t SET_CLOCK_ID = 0x0C06;
+    const std::uint16_t DATA_ID      = 0x0B01;
+    const std::uint16_t STATUS_ID    = 0x0B02;
+
+    const std::uint8_t START = 1;
+    const std::uint8_t STOP = 0;
 private:
     // libusb handles
     struct libusb_context *ctx;
     std::vector<struct libusb_device*> devices;
     struct libusb_device_handle *handle = NULL;
-
-    // device control messages
-    static const std::uint8_t CMD_SET_CHANNELS = 0x19;
-    static const std::uint8_t CMD_SET_SAMPLE_RATE = 0x20;
-    static const std::uint8_t CMD_SET_RESOLUTION = 0x21;
-    static const std::uint8_t CMD_START_RECORDING = 0x22;
-    static const std::uint8_t CMD_STOP_RECORDING = 0x23;
-    static const std::uint8_t CMD_RESET_DEVICE = 0x99;
     /** Sends a message to the device, without payload.
      * \param[in] cmd The command identifier.
      */
-    void send_message(std::uint8_t cmd);
+    void send_message(std::uint16_t cmd);
     /** Sends a message with payload to the device.
      * \param[in] cmd The command identifier.
      * \param[in] payload The payload data to include.
      */
-    void send_message(std::uint8_t cmd, std::vector<std::uint8_t> &payload);
+    void send_message(std::uint16_t cmd, std::vector<std::uint8_t> &payload);
     /** Sends a message with payload to the device.
      * \param[in] cmd The command identifier.
      * \param[in] payload Pointer to the payload data to include.
      * \param[in] length The size of the payload data in bytes.
      */
-    void send_message(std::uint8_t cmd, std::uint8_t *payload, size_t length);
+    void send_message(std::uint16_t cmd, std::uint8_t *payload, size_t length);
 
     // device messages sent back
-    static const size_t MAX_PAYLOAD = 131082;
-    static const size_t MAX_CHANNELS = 5;
-    static const std::uint8_t MSG_SAMPLES = 0x17;
-    static const std::uint8_t MSG_PING = 0x32;
-    static const std::uint8_t MSG_PING_CHARGED = 0x33;
-    static const std::uint8_t MSG_DEBUG_INFO = 0x34;
     /** Reads a message from the device. Waits for a message if necessary.
      * \param[out] buffer Pointer to memory to write the message to.
      * \param[out] length Length of the buffer.
@@ -61,14 +62,17 @@ private:
      * indefinitely. If no message could be read within this time, returns zero.
      * \returns the number of bytes received and written to the buffer.
      */
-    size_t receive_message(std::uint8_t *buffer, size_t length, size_t max_wait=0);
+    size_t receive_message(uint8_t *buffer, size_t max_wait = 0);
 
     // device state, as far as known
-    size_t num_channels = 0;
+    std::uint8_t num_channels = 0;
+    std::uint8_t depth = 0;
+    std::uint8_t num_filter = 0;
     size_t sample_rate = 0;
     bool recording = false;
+    bool verbose = false;
 public:
-    JasonRecorder();
+    JasonRecorder(bool verbose);
     ~JasonRecorder();
 
     /** Searches for JASON Qualilife sound cards attached via USB.
@@ -79,17 +83,18 @@ public:
      * \param[in] number The device, must be smaller than get_device_count().
      */
     void set_device(size_t number);
-    /** Sets the recording format. Requires set_device() to be called before.
+    /** Starts recording from the device chosen with set_device(). Requires set_device() to be called before.
      * \param[in] num_channels The number of channels, between 1 and 5.
      * \param[in] sample_rate The number of samples per second (per channel).
+     * \param[in] depth The number of bytes of each sample.
+     * \param[in] num_filter The filter number (between 0 and 2).
      */
-    void set_format(size_t num_channels, size_t sample_rate);
-    /** Starts recording from the device chosen with set_device(). */
-    void start_recording();
+    void start_recording(std::uint8_t num_channels, size_t sample_rate, std::uint8_t depth, std::uint8_t num_filter);
     /** Stops recording from the device chosen with set_device(). */
     void stop_recording();
-    /** Fetches a packet of samples from the device chosen with set_device().
+    /** Fetches a messages from the device chosen with set_device().
      * Requires the format to have been set before using set_format().
+     * If the messsages contains samples, they will be put in samples.
      * \param[out] samples A vector the samples will be written to, replacing
      * existing content if any.
      * \param[in] planar If true, return the samples in planar form, i.e., one
@@ -100,15 +105,17 @@ public:
      * 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, size_t max_wait=0);
+    void get_samples(std::vector<std::uint8_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.
-     * \param[in] num_samples_per_channel The number of samples (per channel)
+     * \param[in] num_bytes The total number of bytes
      * to convert.
      * \param[in] num_channels The number of channels.
+     * \param[in] depth The number of bytes per sample.
      */
-    static void interleave_channels(std::int16_t *input, std::int16_t *output, size_t num_samples_per_channel, size_t num_channels);
+    static void interleave_channels(std::uint8_t *input, std::uint8_t *output, size_t num_bytes,
+                                    size_t num_channels, size_t depth);
 };
 
 #endif  // RECORDER_H
-- 
GitLab