mirror of
https://github.com/eried/portapack-mayhem.git
synced 2024-10-01 01:26:06 -04:00
Added loop option in Replay app
Updated binary
This commit is contained in:
parent
70c7646743
commit
c9381f1418
@ -44,18 +44,15 @@ void ReplayAppView::on_file_changed(std::filesystem::path new_file_path) {
|
|||||||
|
|
||||||
file_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());
|
bbd_file.open("/" + new_file_path.string());
|
||||||
auto file_size = bbd_file.size();
|
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);
|
progressbar.set_max(file_size);
|
||||||
|
|
||||||
if (duration >= 60)
|
text_duration.set(to_string_time_ms(duration));
|
||||||
str_duration = to_string_dec_uint(duration / 60) + "m";
|
|
||||||
|
|
||||||
text_duration.set(str_duration + to_string_dec_uint(duration % 60) + "s");
|
|
||||||
|
|
||||||
button_play.focus();
|
button_play.focus();
|
||||||
}
|
}
|
||||||
@ -68,27 +65,31 @@ void ReplayAppView::focus() {
|
|||||||
button_open.focus();
|
button_open.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ReplayAppView::file_error() {
|
||||||
|
nav_.display_modal("Error", "File read error.");
|
||||||
|
}
|
||||||
|
|
||||||
bool ReplayAppView::is_active() const {
|
bool ReplayAppView::is_active() const {
|
||||||
return (bool)replay_thread;
|
return (bool)replay_thread;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReplayAppView::toggle() {
|
void ReplayAppView::toggle() {
|
||||||
if( is_active() ) {
|
if( is_active() ) {
|
||||||
stop();
|
stop(false);
|
||||||
} else {
|
} else {
|
||||||
start();
|
start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReplayAppView::start() {
|
void ReplayAppView::start() {
|
||||||
stop();
|
stop(false);
|
||||||
|
|
||||||
std::unique_ptr<stream::Reader> reader;
|
std::unique_ptr<stream::Reader> reader;
|
||||||
|
|
||||||
auto p = std::make_unique<FileReader>();
|
auto p = std::make_unique<FileReader>();
|
||||||
auto open_error = p->open(file_path);
|
auto open_error = p->open(file_path);
|
||||||
if( open_error.is_valid() ) {
|
if( open_error.is_valid() ) {
|
||||||
handle_error(open_error.value());
|
file_error();
|
||||||
} else {
|
} else {
|
||||||
reader = std::move(p);
|
reader = std::move(p);
|
||||||
}
|
}
|
||||||
@ -99,12 +100,8 @@ void ReplayAppView::start() {
|
|||||||
std::move(reader),
|
std::move(reader),
|
||||||
read_size, buffer_count,
|
read_size, buffer_count,
|
||||||
&ready_signal,
|
&ready_signal,
|
||||||
[]() {
|
[](uint32_t return_code) {
|
||||||
ReplayThreadDoneMessage message { };
|
ReplayThreadDoneMessage message { return_code };
|
||||||
EventDispatcher::send_message(message);
|
|
||||||
},
|
|
||||||
[](File::Error error) {
|
|
||||||
ReplayThreadDoneMessage message { error.code() };
|
|
||||||
EventDispatcher::send_message(message);
|
EventDispatcher::send_message(message);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -121,25 +118,27 @@ void ReplayAppView::start() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReplayAppView::stop() {
|
void ReplayAppView::stop(const bool do_loop) {
|
||||||
if( is_active() )
|
if( is_active() )
|
||||||
replay_thread.reset();
|
replay_thread.reset();
|
||||||
|
|
||||||
progressbar.set_value(0);
|
if (do_loop && check_loop.value()) {
|
||||||
|
start();
|
||||||
radio::disable();
|
} else {
|
||||||
button_play.set_bitmap(&bitmap_play);
|
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReplayAppView::handle_error(const File::Error error) {
|
void ReplayAppView::handle_replay_thread_done(const uint32_t return_code) {
|
||||||
nav_.display_modal("Error", error.what());
|
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(
|
ReplayAppView::ReplayAppView(
|
||||||
@ -150,14 +149,15 @@ ReplayAppView::ReplayAppView(
|
|||||||
|
|
||||||
add_children({
|
add_children({
|
||||||
&labels,
|
&labels,
|
||||||
&field_frequency,
|
&button_open,
|
||||||
&field_lna,
|
|
||||||
&field_rf_amp,
|
|
||||||
&button_play,
|
|
||||||
&text_filename,
|
&text_filename,
|
||||||
&text_duration,
|
&text_duration,
|
||||||
&progressbar,
|
&progressbar,
|
||||||
&button_open,
|
&field_frequency,
|
||||||
|
&field_lna,
|
||||||
|
&field_rf_amp,
|
||||||
|
&check_loop,
|
||||||
|
&button_play,
|
||||||
&waterfall,
|
&waterfall,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -45,10 +45,6 @@ public:
|
|||||||
|
|
||||||
std::string title() const override { return "Replay"; };
|
std::string title() const override { return "Replay"; };
|
||||||
|
|
||||||
void start();
|
|
||||||
void stop();
|
|
||||||
bool is_active() const;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
NavigationView& nav_;
|
NavigationView& nav_;
|
||||||
|
|
||||||
@ -67,61 +63,67 @@ private:
|
|||||||
rf::Frequency target_frequency() const;
|
rf::Frequency target_frequency() const;
|
||||||
|
|
||||||
void toggle();
|
void toggle();
|
||||||
|
void start();
|
||||||
|
void stop(const bool do_loop);
|
||||||
|
bool is_active() const;
|
||||||
void set_ready();
|
void set_ready();
|
||||||
void handle_replay_thread_done(const File::Error error);
|
void handle_replay_thread_done(const uint32_t return_code);
|
||||||
void handle_error(const File::Error error);
|
void file_error();
|
||||||
|
|
||||||
std::filesystem::path file_path { };
|
std::filesystem::path file_path { };
|
||||||
std::unique_ptr<ReplayThread> replay_thread { };
|
std::unique_ptr<ReplayThread> replay_thread { };
|
||||||
bool ready_signal { false };
|
bool ready_signal { false };
|
||||||
|
|
||||||
Labels labels {
|
Labels labels {
|
||||||
{ { 10 * 8, 0 * 8 }, "LNA: AMP:", Color::light_grey() }
|
{ { 10 * 8, 2 * 16 }, "LNA: A:", 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 }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Button button_open {
|
Button button_open {
|
||||||
{ 20 * 8, 1 * 16, 10 * 8, 2 * 16 },
|
{ 0 * 8, 0 * 16, 10 * 8, 2 * 16 },
|
||||||
"Open file"
|
"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 { };
|
spectrum::WaterfallWidget waterfall { };
|
||||||
|
|
||||||
MessageHandlerRegistration message_handler_replay_thread_error {
|
MessageHandlerRegistration message_handler_replay_thread_error {
|
||||||
Message::ID::ReplayThreadDone,
|
Message::ID::ReplayThreadDone,
|
||||||
[this](const Message* const p) {
|
[this](const Message* const p) {
|
||||||
const auto message = *reinterpret_cast<const ReplayThreadDoneMessage*>(p);
|
const auto message = *reinterpret_cast<const ReplayThreadDoneMessage*>(p);
|
||||||
this->handle_replay_thread_done(message.error);
|
this->handle_replay_thread_done(message.return_code);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -42,13 +42,11 @@ ReplayThread::ReplayThread(
|
|||||||
size_t read_size,
|
size_t read_size,
|
||||||
size_t buffer_count,
|
size_t buffer_count,
|
||||||
bool* ready_signal,
|
bool* ready_signal,
|
||||||
std::function<void()> success_callback,
|
std::function<void(uint32_t return_code)> terminate_callback
|
||||||
std::function<void(File::Error)> error_callback
|
|
||||||
) : config { read_size, buffer_count },
|
) : config { read_size, buffer_count },
|
||||||
reader { std::move(reader) },
|
reader { std::move(reader) },
|
||||||
ready_sig { ready_signal },
|
ready_sig { ready_signal },
|
||||||
success_callback { std::move(success_callback) },
|
terminate_callback { std::move(terminate_callback) }
|
||||||
error_callback { std::move(error_callback) }
|
|
||||||
{
|
{
|
||||||
// Need significant stack for FATFS
|
// Need significant stack for FATFS
|
||||||
thread = chThdCreateFromHeap(NULL, 1024, NORMALPRIO + 10, ReplayThread::static_fn, this);
|
thread = chThdCreateFromHeap(NULL, 1024, NORMALPRIO + 10, ReplayThread::static_fn, this);
|
||||||
@ -64,18 +62,14 @@ ReplayThread::~ReplayThread() {
|
|||||||
|
|
||||||
msg_t ReplayThread::static_fn(void* arg) {
|
msg_t ReplayThread::static_fn(void* arg) {
|
||||||
auto obj = static_cast<ReplayThread*>(arg);
|
auto obj = static_cast<ReplayThread*>(arg);
|
||||||
const auto error = obj->run();
|
const auto return_code = obj->run();
|
||||||
if( error.is_valid() && obj->error_callback ) {
|
if( obj->terminate_callback ) {
|
||||||
obj->error_callback(error.value());
|
obj->terminate_callback(return_code);
|
||||||
} else {
|
|
||||||
if( obj->success_callback ) {
|
|
||||||
obj->success_callback();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Optional<File::Error> ReplayThread::run() {
|
uint32_t ReplayThread::run() {
|
||||||
BasebandReplay replay { &config };
|
BasebandReplay replay { &config };
|
||||||
BufferExchange buffers { &config };
|
BufferExchange buffers { &config };
|
||||||
|
|
||||||
@ -99,7 +93,7 @@ Optional<File::Error> ReplayThread::run() {
|
|||||||
for (size_t c = 0; c < blocks; c++) {
|
for (size_t c = 0; c < blocks; c++) {
|
||||||
auto read_result = reader->read(&((uint8_t*)prefill_buffer->data())[c * 512], 512);
|
auto read_result = reader->read(&((uint8_t*)prefill_buffer->data())[c * 512], 512);
|
||||||
if( read_result.is_error() ) {
|
if( read_result.is_error() ) {
|
||||||
return read_result.error();
|
return READ_ERROR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,10 +110,10 @@ Optional<File::Error> ReplayThread::run() {
|
|||||||
|
|
||||||
auto read_result = reader->read(buffer->data(), buffer->capacity());
|
auto read_result = reader->read(buffer->data(), buffer->capacity());
|
||||||
if( read_result.is_error() ) {
|
if( read_result.is_error() ) {
|
||||||
return read_result.error();
|
return READ_ERROR;
|
||||||
} else {
|
} else {
|
||||||
if (read_result.value() == 0) {
|
if (read_result.value() == 0) {
|
||||||
return { };
|
return END_OF_FILE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,5 +122,5 @@ Optional<File::Error> ReplayThread::run() {
|
|||||||
buffers.put(buffer);
|
buffers.put(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
return { };
|
return TERMINATED;
|
||||||
}
|
}
|
||||||
|
@ -41,8 +41,7 @@ public:
|
|||||||
size_t read_size,
|
size_t read_size,
|
||||||
size_t buffer_count,
|
size_t buffer_count,
|
||||||
bool* ready_signal,
|
bool* ready_signal,
|
||||||
std::function<void()> success_callback,
|
std::function<void(uint32_t return_code)> terminate_callback
|
||||||
std::function<void(File::Error)> error_callback
|
|
||||||
);
|
);
|
||||||
~ReplayThread();
|
~ReplayThread();
|
||||||
|
|
||||||
@ -53,19 +52,24 @@ public:
|
|||||||
|
|
||||||
const ReplayConfig& state() const {
|
const ReplayConfig& state() const {
|
||||||
return config;
|
return config;
|
||||||
}
|
};
|
||||||
|
|
||||||
|
enum replaythread_return {
|
||||||
|
READ_ERROR = 0,
|
||||||
|
END_OF_FILE,
|
||||||
|
TERMINATED
|
||||||
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ReplayConfig config;
|
ReplayConfig config;
|
||||||
std::unique_ptr<stream::Reader> reader;
|
std::unique_ptr<stream::Reader> reader;
|
||||||
bool* ready_sig;
|
bool* ready_sig;
|
||||||
std::function<void()> success_callback;
|
std::function<void(uint32_t return_code)> terminate_callback;
|
||||||
std::function<void(File::Error)> error_callback;
|
|
||||||
Thread* thread { nullptr };
|
Thread* thread { nullptr };
|
||||||
|
|
||||||
static msg_t static_fn(void* arg);
|
static msg_t static_fn(void* arg);
|
||||||
|
|
||||||
Optional<File::Error> run();
|
uint32_t run();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif/*__REPLAY_THREAD_H__*/
|
#endif/*__REPLAY_THREAD_H__*/
|
||||||
|
@ -117,6 +117,23 @@ std::string to_string_short_freq(const uint64_t f) {
|
|||||||
return final_str;
|
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) {
|
static void to_string_hex_internal(char* p, const uint64_t n, const int32_t l) {
|
||||||
const uint32_t d = n & 0xf;
|
const uint32_t d = n & 0xf;
|
||||||
p[l] = (d > 9) ? (d + 55) : (d + 48);
|
p[l] = (d > 9) ? (d + 55) : (d + 48);
|
||||||
|
@ -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_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_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_datetime(const rtc::RTC& value, const TimeFormat format = YMDHMS);
|
||||||
std::string to_string_timestamp(const rtc::RTC& value);
|
std::string to_string_timestamp(const rtc::RTC& value);
|
||||||
|
@ -133,9 +133,7 @@ void SoundBoardView::play_sound(uint16_t id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SoundBoardView::show_infos(uint16_t id) {
|
void SoundBoardView::show_infos(uint16_t id) {
|
||||||
uint32_t duration = sounds[id].ms_duration;
|
text_duration.set(to_string_time_ms(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);
|
text_title.set(sounds[id].title);
|
||||||
}
|
}
|
||||||
|
@ -997,13 +997,13 @@ public:
|
|||||||
class ReplayThreadDoneMessage : public Message {
|
class ReplayThreadDoneMessage : public Message {
|
||||||
public:
|
public:
|
||||||
constexpr ReplayThreadDoneMessage(
|
constexpr ReplayThreadDoneMessage(
|
||||||
uint32_t error = 0
|
uint32_t return_code = 0
|
||||||
) : Message { ID::ReplayThreadDone },
|
) : Message { ID::ReplayThreadDone },
|
||||||
error { error }
|
return_code { return_code }
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t error;
|
uint32_t return_code;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif/*__MESSAGE_H__*/
|
#endif/*__MESSAGE_H__*/
|
||||||
|
Binary file not shown.
Loading…
Reference in New Issue
Block a user