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