From 4870d0c148eb49ffeb5482ae55d9cc4c9cc89786 Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Mon, 3 Jul 2017 17:20:35 +0200 Subject: Measure audio levels, add ensemble database WIP --- Makefile.am | 1 + src/dabplussnoop.cpp | 27 ++++++++++--- src/dabplussnoop.hpp | 27 ++++++------- src/ensembledatabase.hpp | 68 +++++++++++++++++++++++++++++++++ src/etianalyse.cpp | 48 ++++++++++++------------ src/etianalyse.hpp | 36 +++++++++++------- src/etisnoop.cpp | 28 ++++++-------- src/faad_decoder.cpp | 98 +++++++++++++++++++++++++++++++++++++++++------- src/faad_decoder.hpp | 19 +++++++++- src/figs.hpp | 4 ++ src/utils.cpp | 8 ++++ src/utils.hpp | 3 ++ 12 files changed, 278 insertions(+), 89 deletions(-) create mode 100644 src/ensembledatabase.hpp diff --git a/Makefile.am b/Makefile.am index 696575c..f66e7e5 100644 --- a/Makefile.am +++ b/Makefile.am @@ -16,6 +16,7 @@ etisnoop_SOURCES = src/dabplussnoop.cpp src/dabplussnoop.hpp \ src/etianalyse.cpp src/etianalyse.hpp \ src/etisnoop.cpp \ src/faad_decoder.cpp src/faad_decoder.hpp \ + src/ensembledatabase.hpp \ src/fig0_0.cpp \ src/fig0_10.cpp \ src/fig0_11.cpp \ diff --git a/src/dabplussnoop.cpp b/src/dabplussnoop.cpp index f0badaf..228016d 100644 --- a/src/dabplussnoop.cpp +++ b/src/dabplussnoop.cpp @@ -63,6 +63,11 @@ void DabPlusSnoop::push(uint8_t* streamdata, size_t streamsize) } } +audio_statistics_t DabPlusSnoop::get_audio_statistics(void) const +{ + return m_faad_decoder.get_audio_statistics(); +} + // Idea and some code taken from Xpadxpert bool DabPlusSnoop::seek_valid_firecode() { @@ -305,9 +310,14 @@ bool DabPlusSnoop::analyse_au(vector >& aus) return m_faad_decoder.decode(aus); } -DabPlusSnoop::~DabPlusSnoop() +StreamSnoop::StreamSnoop(StreamSnoop&& other) { - m_faad_decoder.close(); + dps = move(other.dps); + m_index = other.m_index; + m_raw_data_stream_fd = other.m_raw_data_stream_fd; + other.m_raw_data_stream_fd = nullptr; + m_dump_to_file = other.m_dump_to_file; + other.m_dump_to_file = false; } StreamSnoop::~StreamSnoop() @@ -324,18 +334,25 @@ void StreamSnoop::push(uint8_t* streamdata, size_t streamsize) } // First dump to subchannel file (superframe+parity word) - if (m_raw_data_stream_fd == NULL) { + if (m_dump_to_file and m_raw_data_stream_fd == nullptr) { stringstream dump_filename; dump_filename << "stream-" << m_index << ".msc"; m_raw_data_stream_fd = fopen(dump_filename.str().c_str(), "w"); - if (m_raw_data_stream_fd == NULL) { + if (m_raw_data_stream_fd == nullptr) { perror("File open failed"); } } - fwrite(streamdata, streamsize, 1, m_raw_data_stream_fd); + if (m_raw_data_stream_fd) { + fwrite(streamdata, streamsize, 1, m_raw_data_stream_fd); + } dps.push(streamdata, streamsize); } + +audio_statistics_t StreamSnoop::get_audio_statistics(void) const +{ + return dps.get_audio_statistics(); +} diff --git a/src/dabplussnoop.hpp b/src/dabplussnoop.hpp index 8ba35d5..6322c9b 100644 --- a/src/dabplussnoop.hpp +++ b/src/dabplussnoop.hpp @@ -78,21 +78,12 @@ he_aac_super_frame(subchannel_index) #include #include "faad_decoder.hpp" -#ifndef __DABPLUSSNOOP_H_ -#define __DABPLUSSNOOP_H_ +#pragma once // DabPlusSnoop is responsible for decoding DAB+ audio class DabPlusSnoop { public: - DabPlusSnoop() : - m_index(0), - m_subchannel_index(0), - m_data(0) {} - ~DabPlusSnoop(); - DabPlusSnoop(const DabPlusSnoop& other) = delete; - DabPlusSnoop& operator=(const DabPlusSnoop& other) = delete; - void set_subchannel_index(unsigned subchannel_index) { m_subchannel_index = subchannel_index; @@ -105,6 +96,8 @@ class DabPlusSnoop void push(uint8_t* streamdata, size_t streamsize); + audio_statistics_t get_audio_statistics(void) const; + private: /* Data needed for FAAD */ FaadDecoder m_faad_decoder; @@ -132,11 +125,14 @@ class DabPlusSnoop class StreamSnoop { public: - StreamSnoop() : + StreamSnoop(bool dump_to_file) : dps(), m_index(-1), - m_raw_data_stream_fd(NULL) {} + m_raw_data_stream_fd(nullptr), + m_dump_to_file(dump_to_file) {} ~StreamSnoop(); + StreamSnoop(StreamSnoop&& other); + StreamSnoop(const StreamSnoop& other) = delete; StreamSnoop& operator=(const StreamSnoop& other) = delete; @@ -153,13 +149,12 @@ class StreamSnoop void push(uint8_t* streamdata, size_t streamsize); + audio_statistics_t get_audio_statistics(void) const; + private: DabPlusSnoop dps; int m_index; - FILE* m_raw_data_stream_fd; + bool m_dump_to_file; }; - -#endif - diff --git a/src/ensembledatabase.hpp b/src/ensembledatabase.hpp new file mode 100644 index 0000000..ebe7a66 --- /dev/null +++ b/src/ensembledatabase.hpp @@ -0,0 +1,68 @@ +/* + Copyright (C) 2014 CSP Innovazione nelle ICT s.c.a r.l. (http://www.csp.it/) + Copyright (C) 2017 Matthias P. Braendli (http://www.opendigitalradio.org) + Copyright (C) 2015 Data Path + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + Ensemble Database, gather data from the ensemble for the + statistics. + + Authors: + Sergio Sagliocco + Matthias P. Braendli + / | |- ')|) |-|_ _ (|,_ .| _ ,_ \ + Data Path \(|(||_(|/_| (||_||(a)_||||(|||.(_()|||/ + +*/ + +#pragma once + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include + +struct service_t { + uint32_t id; + std::string label; + std::string shortlabel; + // TODO PTy language announcement +}; + +struct subchannel_t { + uint8_t address; + uint8_t num_cu; + // TODO type bitrate protection +}; + +struct component_t { + uint32_t service_id; + uint8_t subchId; + /* TODO + uint8_t type; + uint8_t SCIdS; + + uaType for audio + */ +}; + +struct ensemble_t { + std::list services; + std::list subchannels; + std::list components; +}; + diff --git a/src/etianalyse.cpp b/src/etianalyse.cpp index 9687755..9799fb5 100644 --- a/src/etianalyse.cpp +++ b/src/etianalyse.cpp @@ -16,9 +16,6 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . - etisnoop.cpp - Parse ETI NI G.703 file - Authors: Sergio Sagliocco Matthias P. Braendli @@ -47,13 +44,6 @@ using namespace std; // Signal handler flag std::atomic quit(false); -struct FIG0_13_shortAppInfo -{ - uint16_t SId; - uint8_t No:4; - uint8_t SCIdS:4; -} PACKED; - bool eti_analyse_config_t::is_fig_to_be_printed(int type, int extension) const { @@ -94,7 +84,7 @@ static void print_fig_result(const fig_result_t& fig_result, const display_setti } -int eti_analyse(eti_analyse_config_t &config) +void ETI_Analyser::eti_analyse() { uint8_t p[ETINIPACKETSIZE]; string desc; @@ -129,8 +119,6 @@ int eti_analyse(eti_analyse_config_t &config) printf("?\n"); } - WatermarkDecoder wm_decoder; - if (config.analyse_fig_rates) { rate_display_header(config.analyse_fig_rates_per_second); } @@ -346,9 +334,15 @@ int eti_analyse(eti_analyse_config_t &config) sprintf(sdesc, "%d => %d kbit/s", stl[i], stl[i]*8/3); printbuf("STL - Sub-channel Stream Length", 3, NULL, 0, sdesc); + if (config.statistics and config.streams_to_decode.count(i) == 0) { + config.streams_to_decode.emplace(std::piecewise_construct, + std::make_tuple(i), + std::make_tuple(false)); // do not dump to file + } + if (config.streams_to_decode.count(i) > 0) { - config.streams_to_decode[i].set_subchannel_index(stl[i]/3); - config.streams_to_decode[i].set_index(i); + config.streams_to_decode.at(i).set_subchannel_index(stl[i]/3); + config.streams_to_decode.at(i).set_index(i); } } @@ -409,7 +403,7 @@ int eti_analyse(eti_analyse_config_t &config) figlen = fig[0] & 0x1F; sprintf(sdesc, "FIG %d [%d bytes]", figtype, figlen); printbuf(sdesc, 3, fig+1, figlen); - decodeFIG(config, figs, wm_decoder, fig+1, figlen, figtype, 4); + decodeFIG(config, figs, fig+1, figlen, figtype, 4); fig += figlen + 1; figcount += figlen + 1; if (figcount >= 29) @@ -458,7 +452,7 @@ int eti_analyse(eti_analyse_config_t &config) } if (config.streams_to_decode.count(i) > 0) { - config.streams_to_decode[i].push(streamdata, stl[i]*8); + config.streams_to_decode.at(i).push(streamdata, stl[i]*8); } } @@ -502,6 +496,18 @@ int eti_analyse(eti_analyse_config_t &config) rate_display_analysis(false, config.analyse_fig_rates_per_second); } + if (config.statistics) { + for (const auto& snoop : config.streams_to_decode) { + printf("Statistics for %d:\n", snoop.first); + + const auto& stat = snoop.second.get_audio_statistics(); + printf(" Avg L: %d dB\n", absolute_to_dB(stat.average_level_left)); + printf(" Avg R: %d dB\n", absolute_to_dB(stat.average_level_right)); + printf(" Peak L: %d dB\n", absolute_to_dB(stat.peak_level_left)); + printf(" Peak R: %d dB\n", absolute_to_dB(stat.peak_level_right)); + } + } + if (quit.load()) running = false; } @@ -515,15 +521,11 @@ int eti_analyse(eti_analyse_config_t &config) } figs_cleardb(); - - - return 0; } -void decodeFIG( +void ETI_Analyser::decodeFIG( const eti_analyse_config_t &config, FIGalyser &figs, - WatermarkDecoder &wm_decoder, uint8_t* f, uint8_t figlen, uint16_t figtype, @@ -534,7 +536,7 @@ void decodeFIG( switch (figtype) { case 0: { - fig0_common_t fig0(f, figlen, wm_decoder); + fig0_common_t fig0(f, figlen, ensemble, wm_decoder); const display_settings_t disp(config.is_fig_to_be_printed(figtype, fig0.ext()), indent); diff --git a/src/etianalyse.hpp b/src/etianalyse.hpp index 40a19fe..7d02071 100644 --- a/src/etianalyse.hpp +++ b/src/etianalyse.hpp @@ -16,9 +16,6 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . - etisnoop.cpp - Parse ETI NI G.703 file - Authors: Sergio Sagliocco Matthias P. Braendli @@ -43,6 +40,7 @@ #include "watermarkdecoder.hpp" #include "repetitionrate.hpp" #include "figalyser.hpp" +#include "ensembledatabase.hpp" extern std::atomic quit; @@ -54,7 +52,8 @@ struct eti_analyse_config_t { analyse_fic_carousel(false), analyse_fig_rates(false), analyse_fig_rates_per_second(false), - decode_watermark(false) {} + decode_watermark(false), + statistics(false) {} FILE* etifd; bool ignore_error; @@ -64,19 +63,30 @@ struct eti_analyse_config_t { bool analyse_fig_rates; bool analyse_fig_rates_per_second; bool decode_watermark; + bool statistics; bool is_fig_to_be_printed(int type, int extension) const; }; +class ETI_Analyser { + public: + ETI_Analyser(eti_analyse_config_t &config) : + config(config) {} + + void eti_analyse(void); -int eti_analyse(eti_analyse_config_t &config); + private: + void decodeFIG( + const eti_analyse_config_t &config, + FIGalyser &figs, + uint8_t* f, + uint8_t figlen, + uint16_t figtype, + int indent); -void decodeFIG( - const eti_analyse_config_t &config, - FIGalyser &figs, - WatermarkDecoder &wm_decoder, - uint8_t* f, - uint8_t figlen, - uint16_t figtype, - int indent); + eti_analyse_config_t &config; + + ensemble_t ensemble; + WatermarkDecoder wm_decoder; +}; diff --git a/src/etisnoop.cpp b/src/etisnoop.cpp index 194dfc0..4c0645f 100644 --- a/src/etisnoop.cpp +++ b/src/etisnoop.cpp @@ -57,18 +57,6 @@ using namespace std; -// Function prototypes -void decodeFIG( - const eti_analyse_config_t &config, - FIGalyser &figs, - WatermarkDecoder &wm_decoder, - uint8_t* figdata, - uint8_t figlen, - uint16_t figtype, - int indent); - -int eti_analyse(eti_analyse_config_t &config); - const char *get_programme_type_str(size_t int_table_Id, size_t pty); int sprintfMJD(char *dst, int mjd); @@ -85,6 +73,7 @@ const struct option longopts[] = { {"help", no_argument, 0, 'h'}, {"ignore-error", no_argument, 0, 'e'}, {"input-file", no_argument, 0, 'i'}, + {"statistics", no_argument, 0, 's'}, {"verbose", no_argument, 0, 'v'}, {"decode-stream", required_argument, 0, 'd'}, {"filter-fig", required_argument, 0, 'F'}, @@ -100,10 +89,11 @@ void usage(void) "\n" " http://www.opendigitalradio.org\n" "\n" - "Usage: etisnoop [-v] [-f] [-w] [-i filename] [-d stream_index]\n" + "Usage: etisnoop [options] [-i filename]\n" "\n" " -v increase verbosity (can be given more than once)\n" " -d N decode subchannel N into .msc file and if DAB+, decode to .wav file\n" + " -s statistic mode: decode all subchannels and measure audio level\n" " -f analyse FIC carousel\n" " -r analyse FIG rates in FIGs per second\n" " -R analyse FIG rates in frames per FIG\n" @@ -135,12 +125,14 @@ int main(int argc, char *argv[]) eti_analyse_config_t config; while(ch != -1) { - ch = getopt_long(argc, argv, "d:efF:hrRvwi:", longopts, &index); + ch = getopt_long(argc, argv, "d:efF:hrRsvwi:", longopts, &index); switch (ch) { case 'd': { int subchix = atoi(optarg); - config.streams_to_decode.emplace(std::piecewise_construct, std::make_tuple(subchix), std::make_tuple()); + config.streams_to_decode.emplace(std::piecewise_construct, + std::make_tuple(subchix), + std::make_tuple(true)); // dump to file } break; case 'e': @@ -180,6 +172,9 @@ int main(int argc, char *argv[]) config.analyse_fig_rates = true; config.analyse_fig_rates_per_second = false; break; + case 's': + config.statistics = true; + break; case 'v': set_verbosity(get_verbosity() + 1); break; @@ -208,7 +203,8 @@ int main(int argc, char *argv[]) } config.etifd = etifd; - eti_analyse(config); + ETI_Analyser eti_analyser(config); + eti_analyser.eti_analyse(); fclose(etifd); } diff --git a/src/faad_decoder.cpp b/src/faad_decoder.cpp index 24c951a..ced9921 100644 --- a/src/faad_decoder.cpp +++ b/src/faad_decoder.cpp @@ -43,6 +43,34 @@ FaadDecoder::FaadDecoder() : { } +FaadDecoder& FaadDecoder::operator=(FaadDecoder&& other) +{ + m_data_len = other.m_data_len; + m_fd = other.m_fd; + other.m_fd = nullptr; + m_initialised = other.m_initialised; + other.m_initialised = false; + + return *this; +} + +FaadDecoder::FaadDecoder(FaadDecoder&& other) +{ + m_data_len = other.m_data_len; + m_fd = other.m_fd; + other.m_fd = nullptr; + m_initialised = other.m_initialised; + other.m_initialised = false; +} + +FaadDecoder::~FaadDecoder() +{ + if (m_fd) { + wavfile_close(m_fd); + } +} + + void FaadDecoder::open(string filename, bool ps_flag, bool aac_channel_mode, bool dac_rate, bool sbr_flag, int mpeg_surround_config) { @@ -125,27 +153,71 @@ bool FaadDecoder::decode(vector > aus) return false; } - if (m_fd == nullptr) { + if (m_fd == nullptr and not m_filename.empty()) { stringstream ss; ss << m_filename << ".wav"; m_fd = wavfile_open(ss.str().c_str(), m_sample_rate); } if (samples) { + if (m_channels != 1 and m_channels != 2) { + printf("Cannot handle %d channels\n", m_channels); + } + + m_stats.peak_level_left = 0; + m_stats.peak_level_right = 0; + if (m_channels == 1) { - int16_t *buffer = (int16_t *)alloca (2 * samples); - size_t i; - for (i = 0; i < samples; i ++) { - buffer [2 * i] = ((int16_t *)outBuffer) [i]; - buffer [2 * i + 1] = buffer [2 * i]; + int32_t sum_ampl = 0; + for (size_t i = 0; i < samples; i ++) { + const int16_t ampl = abs(outBuffer[i]); + if (m_stats.peak_level_left < ampl) { + m_stats.peak_level_left = ampl; + } + + sum_ampl += ampl; } - wavfile_write(m_fd, buffer, 2*samples); + m_stats.average_level_left = sum_ampl / samples; } else if (m_channels == 2) { - wavfile_write(m_fd, outBuffer, samples); + assert((samples % 2) == 0); + + int32_t sum_left = 0; + int32_t sum_right = 0; + + for (size_t i = 0; i < samples; i += 2) { + const int16_t ampl_left = abs(outBuffer[i]); + if (m_stats.peak_level_left < ampl_left) { + m_stats.peak_level_left = ampl_left; + } + + sum_left += ampl_left; + + const int16_t ampl_right = abs(outBuffer[i+1]); + if (m_stats.peak_level_right < ampl_right) { + m_stats.peak_level_right = ampl_right; + } + + sum_right += ampl_right; + } + + m_stats.average_level_left = sum_left / samples; + m_stats.average_level_right = sum_right / samples; } - else { - printf("Cannot handle %d channels\n", m_channels); + + if (m_fd) { + if (m_channels == 1) { + vector buffer(2*samples); + for (size_t i = 0; i < samples; i ++) { + buffer [2 * i] = ((int16_t *)outBuffer) [i]; + buffer [2 * i + 1] = buffer [2 * i]; + } + + wavfile_write(m_fd, buffer.data(), 2*samples); + } + else if (m_channels == 2) { + wavfile_write(m_fd, outBuffer, samples); + } } } @@ -153,11 +225,9 @@ bool FaadDecoder::decode(vector > aus) return true; } -void FaadDecoder::close() +audio_statistics_t FaadDecoder::get_audio_statistics(void) const { - if (m_fd) { - wavfile_close(m_fd); - } + return m_stats; } int FaadDecoder::get_aac_channel_configuration() diff --git a/src/faad_decoder.hpp b/src/faad_decoder.hpp index 470f905..e154619 100644 --- a/src/faad_decoder.hpp +++ b/src/faad_decoder.hpp @@ -61,24 +61,39 @@ class FaadHandle NeAACDecHandle decoder; }; +struct audio_statistics_t { + int16_t average_level_left; + int16_t average_level_right; + + int16_t peak_level_left; + int16_t peak_level_right; +}; + class FaadDecoder { public: FaadDecoder(); + ~FaadDecoder(); + FaadDecoder& operator=(FaadDecoder&& other); + FaadDecoder(FaadDecoder&& other); + FaadDecoder(const FaadDecoder&) = delete; + FaadDecoder& operator=(const FaadDecoder&) = delete; void open(std::string filename, bool ps_flag, bool aac_channel_mode, bool dac_rate, bool sbr_flag, int mpeg_surround_config); - void close(void); - bool decode(std::vector > aus); bool is_initialised(void) { return m_initialised; } + audio_statistics_t get_audio_statistics(void) const; + private: int get_aac_channel_configuration(); size_t m_data_len; + audio_statistics_t m_stats; + std::string m_filename; FILE* m_fd; diff --git a/src/figs.hpp b/src/figs.hpp index 403970d..8decea3 100644 --- a/src/figs.hpp +++ b/src/figs.hpp @@ -33,6 +33,7 @@ #include "utils.hpp" #include "tables.hpp" #include "watermarkdecoder.hpp" +#include "ensembledatabase.hpp" struct fig_result_t { struct msg_info_t { @@ -58,13 +59,16 @@ struct fig0_common_t { fig0_common_t( uint8_t* fig_data, uint16_t fig_len, + ensemble_t &ens, WatermarkDecoder &wm_dec) : f(fig_data), figlen(fig_len), + ensemble(ens), wm_decoder(wm_dec) { } uint8_t* f; uint16_t figlen; + ensemble_t& ensemble; WatermarkDecoder &wm_decoder; uint16_t cn(void) { return (f[0] & 0x80) >> 7; } diff --git a/src/utils.cpp b/src/utils.cpp index 3e741c9..d0c1273 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -29,6 +29,8 @@ #include "utils.hpp" #include +#include +#include #include using namespace std; @@ -229,3 +231,9 @@ std::string pnum_to_str(uint16_t Programme_Number) } } +int absolute_to_dB(int16_t value) +{ + const int16_t int16_max = std::numeric_limits::max(); + return value ? round(20*log10((double)value / int16_max)) : -90; +} + diff --git a/src/utils.hpp b/src/utils.hpp index 890bd50..3ff9f13 100644 --- a/src/utils.hpp +++ b/src/utils.hpp @@ -82,3 +82,6 @@ int sprintfMJD(char *dst, int mjd); // as the RDS "Programme Item Number (PIN)" feature (EN 62106). std::string pnum_to_str(uint16_t Programme_Number); +// Convert and absolute 16-bit int value to dB. If value is zero, returns +// -90dB +int absolute_to_dB(int16_t value); -- cgit v1.2.3