diff --git a/firmware/application/replay_app.cpp b/firmware/application/replay_app.cpp index b51a647f0..0766550f0 100644 --- a/firmware/application/replay_app.cpp +++ b/firmware/application/replay_app.cpp @@ -44,18 +44,15 @@ void ReplayAppView::on_file_changed(std::filesystem::path new_file_path) { file_path = new_file_path; - text_filename.set(new_file_path.string().substr(0, 18)); + text_filename.set(new_file_path.string().substr(0, 19)); bbd_file.open("/" + new_file_path.string()); auto file_size = bbd_file.size(); - auto duration = file_size / (2 * 2 * sampling_rate / 8); + auto duration = (file_size * 1000) / (2 * 2 * sampling_rate / 8); progressbar.set_max(file_size); - if (duration >= 60) - str_duration = to_string_dec_uint(duration / 60) + "m"; - - text_duration.set(str_duration + to_string_dec_uint(duration % 60) + "s"); + text_duration.set(to_string_time_ms(duration)); button_play.focus(); } @@ -68,27 +65,31 @@ void ReplayAppView::focus() { button_open.focus(); } +void ReplayAppView::file_error() { + nav_.display_modal("Error", "File read error."); +} + bool ReplayAppView::is_active() const { return (bool)replay_thread; } void ReplayAppView::toggle() { if( is_active() ) { - stop(); + stop(false); } else { start(); } } void ReplayAppView::start() { - stop(); + stop(false); std::unique_ptr reader; auto p = std::make_unique(); auto open_error = p->open(file_path); if( open_error.is_valid() ) { - handle_error(open_error.value()); + file_error(); } else { reader = std::move(p); } @@ -99,12 +100,8 @@ void ReplayAppView::start() { std::move(reader), read_size, buffer_count, &ready_signal, - []() { - ReplayThreadDoneMessage message { }; - EventDispatcher::send_message(message); - }, - [](File::Error error) { - ReplayThreadDoneMessage message { error.code() }; + [](uint32_t return_code) { + ReplayThreadDoneMessage message { return_code }; EventDispatcher::send_message(message); } ); @@ -121,25 +118,27 @@ void ReplayAppView::start() { }); } -void ReplayAppView::stop() { +void ReplayAppView::stop(const bool do_loop) { if( is_active() ) replay_thread.reset(); - - progressbar.set_value(0); - radio::disable(); - button_play.set_bitmap(&bitmap_play); -} - -void ReplayAppView::handle_replay_thread_done(const File::Error error) { - stop(); - if( error.code() ) { - handle_error(error); + if (do_loop && check_loop.value()) { + start(); + } else { + radio::disable(); + button_play.set_bitmap(&bitmap_play); } } -void ReplayAppView::handle_error(const File::Error error) { - nav_.display_modal("Error", error.what()); +void ReplayAppView::handle_replay_thread_done(const uint32_t return_code) { + if (return_code == ReplayThread::END_OF_FILE) { + stop(true); + } else if (return_code == ReplayThread::READ_ERROR) { + stop(false); + file_error(); + } + + progressbar.set_value(0); } ReplayAppView::ReplayAppView( @@ -150,14 +149,15 @@ ReplayAppView::ReplayAppView( add_children({ &labels, - &field_frequency, - &field_lna, - &field_rf_amp, - &button_play, + &button_open, &text_filename, &text_duration, &progressbar, - &button_open, + &field_frequency, + &field_lna, + &field_rf_amp, + &check_loop, + &button_play, &waterfall, }); diff --git a/firmware/application/replay_app.hpp b/firmware/application/replay_app.hpp index e8cc21d7e..c35956d25 100644 --- a/firmware/application/replay_app.hpp +++ b/firmware/application/replay_app.hpp @@ -45,10 +45,6 @@ public: std::string title() const override { return "Replay"; }; - void start(); - void stop(); - bool is_active() const; - private: NavigationView& nav_; @@ -67,53 +63,59 @@ private: rf::Frequency target_frequency() const; void toggle(); + void start(); + void stop(const bool do_loop); + bool is_active() const; void set_ready(); - void handle_replay_thread_done(const File::Error error); - void handle_error(const File::Error error); + void handle_replay_thread_done(const uint32_t return_code); + void file_error(); std::filesystem::path file_path { }; std::unique_ptr replay_thread { }; bool ready_signal { false }; Labels labels { - { { 10 * 8, 0 * 8 }, "LNA: AMP:", Color::light_grey() } - }; - - FrequencyField field_frequency { - { 0 * 8, 0 * 16 }, - }; - - LNAGainField field_lna { - { 14 * 8, 0 * 16 } - }; - - RFAmpField field_rf_amp { - { 21 * 8, 0 * 16 } - }; - - ImageButton button_play { - { 0 * 8, 1 * 16 + 8, 2 * 8, 1 * 16 }, - &bitmap_play, - Color::green(), - Color::black() - }; - - Text text_filename { - { 2 * 8, 1 * 16, 18 * 8, 16 }, - "-" - }; - Text text_duration { - { 2 * 8, 2 * 16, 6 * 8, 16 }, - "-" - }; - ProgressBar progressbar { - { 9 * 8, 2 * 16, 10 * 8, 16 } + { { 10 * 8, 2 * 16 }, "LNA: A:", Color::light_grey() } }; Button button_open { - { 20 * 8, 1 * 16, 10 * 8, 2 * 16 }, + { 0 * 8, 0 * 16, 10 * 8, 2 * 16 }, "Open file" }; + + Text text_filename { + { 11 * 8, 0 * 16, 19 * 8, 16 }, + "-" + }; + Text text_duration { + { 11 * 8, 1 * 16, 6 * 8, 16 }, + "-" + }; + ProgressBar progressbar { + { 18 * 8, 1 * 16, 12 * 8, 16 } + }; + + FrequencyField field_frequency { + { 0 * 8, 2 * 16 }, + }; + LNAGainField field_lna { + { 14 * 8, 2 * 16 } + }; + RFAmpField field_rf_amp { + { 19 * 8, 2 * 16 } + }; + Checkbox check_loop { + { 21 * 8, 2 * 16 }, + 4, + "Loop", + true + }; + ImageButton button_play { + { 28 * 8, 2 * 16, 2 * 8, 1 * 16 }, + &bitmap_play, + Color::green(), + Color::black() + }; spectrum::WaterfallWidget waterfall { }; @@ -121,7 +123,7 @@ private: Message::ID::ReplayThreadDone, [this](const Message* const p) { const auto message = *reinterpret_cast(p); - this->handle_replay_thread_done(message.error); + this->handle_replay_thread_done(message.return_code); } }; diff --git a/firmware/application/replay_thread.cpp b/firmware/application/replay_thread.cpp index 5d98c6b61..29f75cdd3 100644 --- a/firmware/application/replay_thread.cpp +++ b/firmware/application/replay_thread.cpp @@ -42,13 +42,11 @@ ReplayThread::ReplayThread( size_t read_size, size_t buffer_count, bool* ready_signal, - std::function success_callback, - std::function error_callback + std::function terminate_callback ) : config { read_size, buffer_count }, reader { std::move(reader) }, ready_sig { ready_signal }, - success_callback { std::move(success_callback) }, - error_callback { std::move(error_callback) } + terminate_callback { std::move(terminate_callback) } { // Need significant stack for FATFS thread = chThdCreateFromHeap(NULL, 1024, NORMALPRIO + 10, ReplayThread::static_fn, this); @@ -64,18 +62,14 @@ ReplayThread::~ReplayThread() { msg_t ReplayThread::static_fn(void* arg) { auto obj = static_cast(arg); - const auto error = obj->run(); - if( error.is_valid() && obj->error_callback ) { - obj->error_callback(error.value()); - } else { - if( obj->success_callback ) { - obj->success_callback(); - } + const auto return_code = obj->run(); + if( obj->terminate_callback ) { + obj->terminate_callback(return_code); } return 0; } -Optional ReplayThread::run() { +uint32_t ReplayThread::run() { BasebandReplay replay { &config }; BufferExchange buffers { &config }; @@ -99,7 +93,7 @@ Optional ReplayThread::run() { for (size_t c = 0; c < blocks; c++) { auto read_result = reader->read(&((uint8_t*)prefill_buffer->data())[c * 512], 512); if( read_result.is_error() ) { - return read_result.error(); + return READ_ERROR; } } @@ -116,10 +110,10 @@ Optional ReplayThread::run() { auto read_result = reader->read(buffer->data(), buffer->capacity()); if( read_result.is_error() ) { - return read_result.error(); + return READ_ERROR; } else { if (read_result.value() == 0) { - return { }; + return END_OF_FILE; } } @@ -128,5 +122,5 @@ Optional ReplayThread::run() { buffers.put(buffer); } - return { }; + return TERMINATED; } diff --git a/firmware/application/replay_thread.hpp b/firmware/application/replay_thread.hpp index f5c7bd34f..c7c509c9a 100644 --- a/firmware/application/replay_thread.hpp +++ b/firmware/application/replay_thread.hpp @@ -41,8 +41,7 @@ public: size_t read_size, size_t buffer_count, bool* ready_signal, - std::function success_callback, - std::function error_callback + std::function terminate_callback ); ~ReplayThread(); @@ -53,19 +52,24 @@ public: const ReplayConfig& state() const { return config; - } + }; + + enum replaythread_return { + READ_ERROR = 0, + END_OF_FILE, + TERMINATED + }; private: ReplayConfig config; std::unique_ptr reader; bool* ready_sig; - std::function success_callback; - std::function error_callback; + std::function terminate_callback; Thread* thread { nullptr }; static msg_t static_fn(void* arg); - Optional run(); + uint32_t run(); }; #endif/*__REPLAY_THREAD_H__*/ diff --git a/firmware/application/string_format.cpp b/firmware/application/string_format.cpp index f1213d12d..d87af2f77 100644 --- a/firmware/application/string_format.cpp +++ b/firmware/application/string_format.cpp @@ -117,6 +117,23 @@ std::string to_string_short_freq(const uint64_t f) { return final_str; } +std::string to_string_time_ms(const uint32_t ms) { + std::string final_str { "" }; + + if (ms < 1000) { + final_str = to_string_dec_uint(ms) + "ms"; + } else { + auto seconds = ms / 1000; + + if (seconds >= 60) + final_str = to_string_dec_uint(seconds / 60) + "m"; + + return final_str + to_string_dec_uint(seconds % 60) + "s"; + } + + return final_str; +} + static void to_string_hex_internal(char* p, const uint64_t n, const int32_t l) { const uint32_t d = n & 0xf; p[l] = (d > 9) ? (d + 55) : (d + 48); diff --git a/firmware/application/string_format.hpp b/firmware/application/string_format.hpp index 401831671..dd12927b9 100644 --- a/firmware/application/string_format.hpp +++ b/firmware/application/string_format.hpp @@ -45,6 +45,7 @@ std::string to_string_hex(const uint64_t n, const int32_t l = 0); std::string to_string_hex_array(uint8_t * const array, const int32_t l = 0); std::string to_string_short_freq(const uint64_t f); +std::string to_string_time_ms(const uint32_t ms); std::string to_string_datetime(const rtc::RTC& value, const TimeFormat format = YMDHMS); std::string to_string_timestamp(const rtc::RTC& value); diff --git a/firmware/application/ui_soundboard.cpp b/firmware/application/ui_soundboard.cpp index 3a540850c..9dc3f8642 100644 --- a/firmware/application/ui_soundboard.cpp +++ b/firmware/application/ui_soundboard.cpp @@ -133,9 +133,7 @@ void SoundBoardView::play_sound(uint16_t id) { } 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_duration.set(to_string_time_ms(sounds[id].ms_duration)); text_title.set(sounds[id].title); } diff --git a/firmware/common/message.hpp b/firmware/common/message.hpp index 2cb3886a3..ff9866ee8 100644 --- a/firmware/common/message.hpp +++ b/firmware/common/message.hpp @@ -997,13 +997,13 @@ public: class ReplayThreadDoneMessage : public Message { public: constexpr ReplayThreadDoneMessage( - uint32_t error = 0 + uint32_t return_code = 0 ) : Message { ID::ReplayThreadDone }, - error { error } + return_code { return_code } { } - uint32_t error; + uint32_t return_code; }; #endif/*__MESSAGE_H__*/ diff --git a/firmware/portapack-h1-havoc.bin b/firmware/portapack-h1-havoc.bin index 52ba799e8..b04efc3eb 100644 Binary files a/firmware/portapack-h1-havoc.bin and b/firmware/portapack-h1-havoc.bin differ