From 6ac911feb774e75186ce1a09db4feae4a980376e Mon Sep 17 00:00:00 2001 From: furrtek Date: Sat, 11 Mar 2017 00:59:04 +0000 Subject: [PATCH] CTCSS bugfix, reading of title in wav files Added wav file title (INFO chunk) display in soundboard UI Added CTCSS frequency next to PL code Increased CTCSS tone amplitude Added Family Radio Service channels file FRS.TXT --- firmware/application/io_wave.cpp | 61 +++++++++++++++++++++----- firmware/application/io_wave.hpp | 4 +- firmware/application/ui_soundboard.cpp | 41 +++++++++++------ firmware/application/ui_soundboard.hpp | 40 ++++++++++------- firmware/baseband/proc_audiotx.cpp | 2 +- sdcard/FREQMAN/FRS.TXT | 14 ++++++ 6 files changed, 119 insertions(+), 43 deletions(-) create mode 100644 sdcard/FREQMAN/FRS.TXT diff --git a/firmware/application/io_wave.cpp b/firmware/application/io_wave.cpp index d6c9a1b1..37830ec0 100644 --- a/firmware/application/io_wave.cpp +++ b/firmware/application/io_wave.cpp @@ -22,31 +22,66 @@ #include "io_wave.hpp" -int WAVFileReader::open(const std::filesystem::path& path) { +bool WAVFileReader::open(const std::filesystem::path& path) { + size_t i = 0; + char ch; + const uint8_t tag_INAM[4] = { 'I', 'N', 'A', 'M' }; + char title_buffer[32]; + uint32_t riff_size, data_end, title_size; + size_t search_limit = 0; + + // Already open ? if (path.string() == last_path.string()) { rewind(); - return 1; // Already open + return true; } auto error = file.open(path); if (!error.is_valid()) { - file.read((void*)&header, sizeof(header)); - - // TODO: Check validity here - - last_path = path; - data_start = header.fmt.cksize + 28; // Skip "data" and cksize + file.read((void*)&header, sizeof(header)); // Read header (RIFF and WAVE) + riff_size = header.cksize + 8; + data_start = header.fmt.cksize + 28; data_size_ = header.data.cksize; + data_end = data_start + data_size_ + 1; + + // Look for INAM (title) tag + if (data_end < riff_size) { + file.seek(data_end); + while(file.read((void*)&ch, 1).is_ok()) { + if (ch == tag_INAM[i++]) { + if (i == 4) { + // Tag found, copy title + file.read((void*)&title_size, sizeof(uint32_t)); + if (title_size > 32) title_size = 32; + file.read((void*)&title_buffer, title_size); + title_string = title_buffer; + break; + } + } else { + if (ch == tag_INAM[0]) + i = 1; + else + i = 0; + } + if (search_limit == 256) + break; + else + search_limit++; + } + } + sample_rate_ = header.fmt.nSamplesPerSec; bytes_per_sample = header.fmt.wBitsPerSample / 8; rewind(); - return 1; + last_path = path; + + return true; } else { - return 0; + return false; } } @@ -54,6 +89,10 @@ void WAVFileReader::rewind() { file.seek(data_start); } +std::string WAVFileReader::title() { + return title_string; +} + uint32_t WAVFileReader::ms_duration() { return ((data_size_ * 1000) / sample_rate_) / bytes_per_sample; } @@ -97,7 +136,7 @@ Optional WAVFileWriter::create( } Optional WAVFileWriter::update_header() { - header_t header { sampling_rate, bytes_written }; + header_t header { sampling_rate, (uint32_t)bytes_written }; const auto seek_0_result = file.seek(0); if( seek_0_result.is_error() ) { return seek_0_result.error(); diff --git a/firmware/application/io_wave.hpp b/firmware/application/io_wave.hpp index fab94937..4898fb46 100644 --- a/firmware/application/io_wave.hpp +++ b/firmware/application/io_wave.hpp @@ -90,7 +90,7 @@ public: virtual ~WAVFileReader() = default; - int open(const std::filesystem::path& path); + bool open(const std::filesystem::path& path); void rewind(); uint32_t ms_duration(); //int seek_mss(const uint16_t minutes, const uint8_t seconds, const uint32_t samples); @@ -98,6 +98,7 @@ public: uint32_t sample_rate(); uint32_t data_size(); uint16_t bits_per_sample(); + std::string title(); private: struct fmt_pcm_t { @@ -130,6 +131,7 @@ private: uint32_t bytes_per_sample { }; uint32_t data_size_ { 0 }; uint32_t sample_rate_ { }; + std::string title_string { }; std::filesystem::path last_path { }; }; diff --git a/firmware/application/ui_soundboard.cpp b/firmware/application/ui_soundboard.cpp index b39cdea8..66b571d0 100644 --- a/firmware/application/ui_soundboard.cpp +++ b/firmware/application/ui_soundboard.cpp @@ -47,8 +47,8 @@ void SoundBoardView::do_random() { play_sound(id); - buttons[id % 21].focus(); - page = id / 21; + buttons[id % 18].focus(); + page = id / 18; refresh_buttons(id); } @@ -126,9 +126,10 @@ void SoundBoardView::play_sound(uint16_t id) { ctcss_index = options_ctcss.selected_index(); - if (ctcss_index) + if (ctcss_index) { ctcss_enabled = true; - else + ctcss_index--; + } else ctcss_enabled = false; divider = (1536000 / sounds[id].sample_rate) - 1; @@ -145,15 +146,17 @@ void SoundBoardView::show_infos(uint16_t id) { uint32_t duration = sounds[id].ms_duration; text_duration.set(to_string_dec_uint(duration / 1000) + "." + to_string_dec_uint((duration / 100) % 10) + "s"); + + text_title.set(sounds[id].title); } void SoundBoardView::refresh_buttons(uint16_t id) { size_t n = 0, n_sound; - text_page.set(to_string_dec_uint(page + 1) + "/" + to_string_dec_uint(max_page)); + text_page.set("Page " + to_string_dec_uint(page + 1) + "/" + to_string_dec_uint(max_page)); for (auto& button : buttons) { - n_sound = (page * 21) + n; + n_sound = (page * 18) + n; button.id = n_sound; @@ -197,7 +200,9 @@ SoundBoardView::SoundBoardView( using options_t = std::vector; options_t ctcss_options; std::vector file_list; + std::string title, f_string; uint8_t c; + uint32_t f; reader = std::make_unique(); @@ -216,8 +221,13 @@ SoundBoardView::SoundBoardView( sounds[c].sixteenbit = false;*/ sounds[c].ms_duration = reader->ms_duration(); sounds[c].path = u"wav/" + path.native(); + title = reader->title().substr(0, 20); + if (title != "") + sounds[c].title = title; + else + sounds[c].title = "-"; c++; - if (c == 105) break; // Limit to 105 files (5 pages) + if (c == 108) break; // Limit to 108 files (6 pages) } } } @@ -225,13 +235,14 @@ SoundBoardView::SoundBoardView( baseband::run_image(portapack::spi_flash::image_tag_audio_tx); max_sound = c; - max_page = max_sound / 21; // 3 * 7 = 21 buttons per page + max_page = (max_sound + 18 - 1) / 18; // 3 * 6 = 18 buttons per page add_children({ &field_frequency, &number_bw, &text_kHz, &options_ctcss, + &text_title, &text_page, &text_duration, &pbar, @@ -240,10 +251,14 @@ SoundBoardView::SoundBoardView( &button_exit }); + // Populate CTCSS list ctcss_options.emplace_back(std::make_pair("None", 0)); - - for (c = 0; c < CTCSS_TONES_NB; c++) - ctcss_options.emplace_back(std::make_pair(ctcss_tones[c].PL_code, c)); + for (c = 0; c < CTCSS_TONES_NB; c++) { + f = (uint32_t)(ctcss_tones[c].frequency * 10); + f_string = ctcss_tones[c].PL_code; + f_string += " " + to_string_dec_uint(f / 10) + "." + to_string_dec_uint(f % 10); + ctcss_options.emplace_back(f_string, c); + } options_ctcss.set_options(ctcss_options); @@ -271,8 +286,8 @@ SoundBoardView::SoundBoardView( button.on_dir = button_dir; button.set_parent_rect({ static_cast((n % 3) * 78 + 3), - static_cast((n / 3) * 28 + 26), - 78, 28 + static_cast((n / 3) * 30 + 24), + 78, 30 }); n++; } diff --git a/firmware/application/ui_soundboard.hpp b/firmware/application/ui_soundboard.hpp index 0838cefe..37bfbc32 100644 --- a/firmware/application/ui_soundboard.hpp +++ b/firmware/application/ui_soundboard.hpp @@ -67,6 +67,7 @@ private: uint32_t size = 0; uint32_t sample_duration = 0; uint32_t ms_duration = 0; + std::string title { }; }; uint32_t sample_counter { 0 }; @@ -77,7 +78,7 @@ private: std::unique_ptr reader { }; - sound sounds[105]; + sound sounds[108]; // 6 pages * 18 buttons uint32_t max_sound { }; uint8_t max_page { }; @@ -104,7 +105,7 @@ private: .foreground = { 153, 102, 255 } }; - std::array buttons { }; + std::array buttons { }; const Style * styles[4] = { &style_a, &style_b, &style_c, &style_d }; void on_tuning_frequency_changed(rf::Frequency f); @@ -117,16 +118,12 @@ private: void prepare_audio(); void on_ctcss_changed(uint32_t v); - Text text_duration { - { 16, 236, 5 * 8, 16 } - }; - FrequencyField field_frequency { - { 1 * 8, 4 }, + { 0, 4 }, }; NumberField number_bw { - { 11 * 8, 4 }, + { 10 * 8, 4 }, 3, {1, 150}, 1, @@ -134,23 +131,32 @@ private: }; Text text_kHz { - { 14 * 8, 4, 3 * 8, 16 }, - "kHz" + { 13 * 8, 4, 8 * 8, 16 }, + "k CTCSS:" }; OptionsField options_ctcss { - { 18 * 8, 4 }, - 6, + { 21 * 8, 4 }, + 8, { } }; + Text text_title { + { 1 * 8, 26 * 8, 20 * 8, 16 }, + "-" + }; + Text text_page { - { 25 * 8, 4, 3 * 8, 16 }, - "-/-" + { 22 * 8 - 4, 26 * 8, 8 * 8, 16 }, + "Page -/-" + }; + + Text text_duration { + { 1 * 8, 30 * 8, 5 * 8, 16 } }; ProgressBar pbar { - { 72, 236, 150, 16 } + { 9 * 8, 30 * 8, 19 * 8, 16 } }; Checkbox check_loop { @@ -160,12 +166,12 @@ private: }; Button button_random { - { 80, 270, 72, 32 }, + { 10 * 8, 34 * 8, 9 * 8, 32 }, "Random" }; Button button_exit { - { 160, 270, 64, 32 }, + { 21 * 8, 34 * 8, 8 * 8, 32 }, "Exit" }; diff --git a/firmware/baseband/proc_audiotx.cpp b/firmware/baseband/proc_audiotx.cpp index 9230f9a0..a4b1d202 100644 --- a/firmware/baseband/proc_audiotx.cpp +++ b/firmware/baseband/proc_audiotx.cpp @@ -55,7 +55,7 @@ void AudioTXProcessor::execute(const buffer_c8_t& buffer){ if (ctcss_enabled) { ctcss_sample = sine_table_i8[(ctcss_phase & 0xFF000000U) >> 24]; - sample_mixed = ((sample * 217) + (ctcss_sample * 38)) / 256; // ~15% + sample_mixed = ((sample * 205) + (ctcss_sample * 50)) / 256; // ~20% ctcss_phase += ctcss_phase_inc; } else { sample_mixed = sample; diff --git a/sdcard/FREQMAN/FRS.TXT b/sdcard/FREQMAN/FRS.TXT new file mode 100644 index 00000000..61dda978 --- /dev/null +++ b/sdcard/FREQMAN/FRS.TXT @@ -0,0 +1,14 @@ +f=462562500,d=FRS CH1 +f=462587500,d=FRS CH2 +f=462612500,d=FRS CH3 +f=462637500,d=FRS CH4 +f=462662500,d=FRS CH5 +f=462687500,d=FRS CH6 +f=462712500,d=FRS CH7 +f=467562500,d=FRS CH8 +f=467587500,d=FRS CH9 +f=467612500,d=FRS CH10 +f=467637500,d=FRS CH11 +f=467662500,d=FRS CH12 +f=467687500,d=FRS CH13 +f=467712500,d=FRS CH14