mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2025-08-15 03:47:42 +00:00
Added LGE app, nothing to see here
Update button in signal gen now works for shape change
This commit is contained in:
199
firmware/application/apps/lge_app.cpp
Normal file
199
firmware/application/apps/lge_app.cpp
Normal file
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2019 Furrtek
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* 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 2, 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; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "lge_app.hpp"
|
||||
|
||||
#include "baseband_api.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
|
||||
#include "crc.hpp"
|
||||
#include "string_format.hpp"
|
||||
|
||||
#include <cstring>
|
||||
#include <stdio.h>
|
||||
|
||||
using namespace portapack;
|
||||
|
||||
namespace ui {
|
||||
|
||||
void LGEView::focus() {
|
||||
tx_view.focus();
|
||||
}
|
||||
|
||||
LGEView::~LGEView() {
|
||||
transmitter_model.disable();
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
void LGEView::generate_frame() {
|
||||
CRC<16> crc { 0x1021, 0x90BE };
|
||||
std::vector<uint8_t> frame { };
|
||||
uint8_t payload[9] = { 0x06, 0x0D, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00 };
|
||||
uint8_t out = 0;
|
||||
uint32_t c;
|
||||
|
||||
payload[6] = field_zone.value(); // Zone
|
||||
|
||||
// Preamble
|
||||
// Really is 0xAA but the RFM69 skips the very last bit (bug ?)
|
||||
// so the whole preamble should be shifted right 1 bit to simulate that
|
||||
for (c = 0; c < 5; c++)
|
||||
frame.push_back(0x55);
|
||||
|
||||
frame.push_back(0x2D); // Sync word
|
||||
frame.push_back(0xD4);
|
||||
|
||||
crc.process_bytes(payload, 9 - 2);
|
||||
|
||||
payload[7] = crc.checksum() >> 8;
|
||||
payload[8] = crc.checksum() & 0xFF;
|
||||
|
||||
// Manchester-encode payload
|
||||
for (c = 0; c < 9; c++) {
|
||||
uint8_t byte = payload[c];
|
||||
for (uint32_t b = 0; b < 8; b++) {
|
||||
out <<= 2;
|
||||
|
||||
if (byte & 0x80)
|
||||
out |= 0b10;
|
||||
else
|
||||
out |= 0b01;
|
||||
|
||||
if ((b & 3) == 3)
|
||||
frame.push_back(out);
|
||||
|
||||
byte <<= 1;
|
||||
}
|
||||
}
|
||||
|
||||
frame_size = frame.size();
|
||||
|
||||
/*std::string debug_str { "" };
|
||||
|
||||
for (c = 0; c < 10; c++)
|
||||
debug_str += (to_string_hex(frame[c], 2) + " ");
|
||||
|
||||
text_messagea.set(debug_str);
|
||||
|
||||
debug_str = "";
|
||||
for (c = 15; c < frame_size; c++)
|
||||
debug_str += (to_string_hex(frame[c], 2) + " ");
|
||||
|
||||
text_messageb.set(debug_str);*/
|
||||
|
||||
// Copy for baseband
|
||||
memcpy(shared_memory.bb_data.data, frame.data(), frame_size);
|
||||
}
|
||||
|
||||
void LGEView::start_tx() {
|
||||
if (tx_mode == ALL) {
|
||||
transmitter_model.set_tuning_frequency(channels[channel_index]);
|
||||
tx_view.on_show(); // Refresh tuning frequency display
|
||||
tx_view.set_dirty();
|
||||
}
|
||||
transmitter_model.set_sampling_rate(2280000);
|
||||
transmitter_model.set_rf_amp(true);
|
||||
transmitter_model.set_baseband_bandwidth(1750000);
|
||||
transmitter_model.enable();
|
||||
|
||||
chThdSleep(100);
|
||||
|
||||
baseband::set_fsk_data(frame_size * 8, 2280000 / 9600, 4000, 256);
|
||||
}
|
||||
|
||||
void LGEView::stop_tx() {
|
||||
tx_mode = IDLE;
|
||||
transmitter_model.disable();
|
||||
tx_view.set_transmitting(false);
|
||||
}
|
||||
|
||||
void LGEView::on_tx_progress(const uint32_t progress, const bool done) {
|
||||
(void)progress;
|
||||
|
||||
if (!done) return;
|
||||
|
||||
transmitter_model.disable();
|
||||
|
||||
if (repeats < 2) {
|
||||
chThdSleep(100);
|
||||
repeats++;
|
||||
start_tx();
|
||||
} else {
|
||||
if (tx_mode == ALL) {
|
||||
if (channel_index < 2) {
|
||||
channel_index++;
|
||||
repeats = 0;
|
||||
start_tx();
|
||||
} else {
|
||||
stop_tx();
|
||||
}
|
||||
} else {
|
||||
stop_tx();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LGEView::LGEView(NavigationView& nav) {
|
||||
baseband::run_image(portapack::spi_flash::image_tag_fsktx);
|
||||
|
||||
add_children({
|
||||
&labels,
|
||||
&field_zone,
|
||||
&checkbox_channels,
|
||||
&text_messagea,
|
||||
&text_messageb,
|
||||
&tx_view
|
||||
});
|
||||
|
||||
field_zone.set_value(1);
|
||||
checkbox_channels.set_value(true);
|
||||
|
||||
generate_frame();
|
||||
|
||||
field_zone.on_change = [this](int32_t) {
|
||||
generate_frame();
|
||||
};
|
||||
|
||||
tx_view.on_edit_frequency = [this, &nav]() {
|
||||
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.tuning_frequency());
|
||||
new_view->on_changed = [this](rf::Frequency f) {
|
||||
receiver_model.set_tuning_frequency(f);
|
||||
};
|
||||
};
|
||||
|
||||
tx_view.on_start = [this]() {
|
||||
if (tx_mode == IDLE) {
|
||||
generate_frame();
|
||||
repeats = 0;
|
||||
channel_index = 0;
|
||||
tx_mode = checkbox_channels.value() ? ALL : SINGLE;
|
||||
tx_view.set_transmitting(true);
|
||||
start_tx();
|
||||
}
|
||||
};
|
||||
|
||||
tx_view.on_stop = [this]() {
|
||||
stop_tx();
|
||||
};
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
107
firmware/application/apps/lge_app.hpp
Normal file
107
firmware/application/apps/lge_app.hpp
Normal file
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2019 Furrtek
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* 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 2, 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; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "ui_widget.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "ui_transmitter.hpp"
|
||||
#include "ui_font_fixed_8x16.hpp"
|
||||
|
||||
#include "message.hpp"
|
||||
#include "transmitter_model.hpp"
|
||||
#include "portapack.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
class LGEView : public View {
|
||||
public:
|
||||
LGEView(NavigationView& nav);
|
||||
~LGEView();
|
||||
|
||||
void focus() override;
|
||||
|
||||
std::string title() const override { return "LGE tool"; };
|
||||
|
||||
private:
|
||||
enum tx_modes {
|
||||
IDLE = 0,
|
||||
SINGLE,
|
||||
ALL
|
||||
};
|
||||
|
||||
tx_modes tx_mode = IDLE;
|
||||
|
||||
uint32_t frame_size { 0 };
|
||||
uint32_t repeats { 0 };
|
||||
uint32_t channel_index { 0 };
|
||||
|
||||
rf::Frequency channels[3] = { 868067000, 868183000, 868295000 };
|
||||
|
||||
void start_tx();
|
||||
void stop_tx();
|
||||
void generate_frame();
|
||||
void on_tx_progress(const uint32_t progress, const bool done);
|
||||
|
||||
Labels labels {
|
||||
{ { 7 * 8, 4 * 8 }, "NO FUN ALLOWED !", Color::red() },
|
||||
{ { 11 * 8, 8 * 8 }, "Zone:", Color::light_grey() }
|
||||
};
|
||||
|
||||
NumberField field_zone {
|
||||
{ 16 * 8, 8 * 8 },
|
||||
1,
|
||||
{ 1, 6 },
|
||||
16,
|
||||
'0'
|
||||
};
|
||||
|
||||
Checkbox checkbox_channels {
|
||||
{ 7 * 8, 14 * 8 },
|
||||
12,
|
||||
"All channels"
|
||||
};
|
||||
|
||||
Text text_messagea {
|
||||
{ 0 * 8, 10 * 16, 30 * 8, 16 },
|
||||
""
|
||||
};
|
||||
Text text_messageb {
|
||||
{ 0 * 8, 11 * 16, 30 * 8, 16 },
|
||||
""
|
||||
};
|
||||
|
||||
TransmitterView tx_view {
|
||||
16 * 16,
|
||||
10000,
|
||||
12
|
||||
};
|
||||
|
||||
MessageHandlerRegistration message_handler_tx_progress {
|
||||
Message::ID::TXProgress,
|
||||
[this](const Message* const p) {
|
||||
const auto message = *reinterpret_cast<const TXProgressMessage*>(p);
|
||||
this->on_tx_progress(message.progress, message.done);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
@@ -42,23 +42,28 @@ SigGenView::~SigGenView() {
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
void SigGenView::update_config() {
|
||||
baseband::set_siggen_config(transmitter_model.channel_bandwidth(), options_shape.selected_index_value(), field_stop.value());
|
||||
}
|
||||
|
||||
void SigGenView::update_tone() {
|
||||
baseband::set_siggen_tone(symfield_tone.value_dec_u32());
|
||||
}
|
||||
|
||||
void SigGenView::start_tx() {
|
||||
transmitter_model.set_sampling_rate(1536000);
|
||||
transmitter_model.set_rf_amp(true);
|
||||
transmitter_model.set_baseband_bandwidth(1750000);
|
||||
transmitter_model.enable();
|
||||
|
||||
baseband::set_siggen_tone(symfield_tone.value_dec_u32());
|
||||
update_tone();
|
||||
|
||||
auto duration = field_stop.value();
|
||||
/*auto duration = field_stop.value();
|
||||
if (!checkbox_auto.value())
|
||||
duration = 0;
|
||||
baseband::set_siggen_config(transmitter_model.channel_bandwidth(), options_shape.selected_index_value(), duration);
|
||||
duration = 0;*/
|
||||
update_config();
|
||||
}
|
||||
|
||||
void SigGenView::update_tone() {
|
||||
baseband::set_siggen_tone(symfield_tone.value_dec_u32());
|
||||
}
|
||||
|
||||
void SigGenView::on_tx_progress(const uint32_t progress, const bool done) {
|
||||
(void) progress;
|
||||
@@ -87,6 +92,8 @@ SigGenView::SigGenView(
|
||||
|
||||
options_shape.on_change = [this](size_t, OptionsField::value_t v) {
|
||||
text_shape.set(shape_strings[v]);
|
||||
if (auto_update)
|
||||
update_config();
|
||||
};
|
||||
options_shape.set_selected_index(0);
|
||||
text_shape.set(shape_strings[0]);
|
||||
@@ -99,6 +106,7 @@ SigGenView::SigGenView(
|
||||
|
||||
button_update.on_select = [this](Button&) {
|
||||
update_tone();
|
||||
update_config();
|
||||
};
|
||||
|
||||
checkbox_auto.on_select = [this](Checkbox&, bool v) {
|
||||
|
@@ -44,6 +44,7 @@ public:
|
||||
|
||||
private:
|
||||
void start_tx();
|
||||
void update_config();
|
||||
void update_tone();
|
||||
void on_tx_progress(const uint32_t progress, const bool done);
|
||||
|
||||
|
@@ -1,315 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* 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 2, 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; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
// To prepare samples: for f in ./*.wav; do sox "$f" -r 48000 -c 1 -b8 --norm "conv/$f"; done
|
||||
|
||||
#include "ui_soundboard.hpp"
|
||||
#include "lfsr_random.hpp"
|
||||
#include "string_format.hpp"
|
||||
#include "tonesets.hpp"
|
||||
|
||||
using namespace tonekey;
|
||||
using namespace portapack;
|
||||
|
||||
namespace ui {
|
||||
|
||||
// TODO: Use Sharebrained's PRNG
|
||||
void SoundBoardView::do_random() {
|
||||
uint32_t id;
|
||||
|
||||
chThdSleepMilliseconds(500);
|
||||
|
||||
lfsr_v = lfsr_iterate(lfsr_v);
|
||||
id = lfsr_v % max_sound;
|
||||
|
||||
play_sound(id);
|
||||
|
||||
buttons[id % 15].focus();
|
||||
page = id / 15;
|
||||
|
||||
refresh_buttons(id);
|
||||
}
|
||||
|
||||
bool SoundBoardView::is_active() const {
|
||||
return (bool)replay_thread;
|
||||
}
|
||||
|
||||
void SoundBoardView::stop(const bool do_loop) {
|
||||
if( is_active() )
|
||||
replay_thread.reset();
|
||||
|
||||
if (do_loop && check_loop.value()) {
|
||||
play_sound(playing_id);
|
||||
} else {
|
||||
radio::disable();
|
||||
//button_play.set_bitmap(&bitmap_play);
|
||||
}
|
||||
ready_signal = false;
|
||||
}
|
||||
|
||||
void SoundBoardView::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);
|
||||
}
|
||||
|
||||
void SoundBoardView::set_ready() {
|
||||
ready_signal = true;
|
||||
}
|
||||
|
||||
void SoundBoardView::focus() {
|
||||
buttons[0].focus();
|
||||
|
||||
if (!max_sound)
|
||||
nav_.display_modal("No files", "No files in /WAV/ directory", ABORT, nullptr);
|
||||
}
|
||||
|
||||
void SoundBoardView::on_tuning_frequency_changed(rf::Frequency f) {
|
||||
transmitter_model.set_tuning_frequency(f);
|
||||
}
|
||||
|
||||
void SoundBoardView::file_error() {
|
||||
nav_.display_modal("Error", "File read error.");
|
||||
}
|
||||
|
||||
void SoundBoardView::play_sound(uint16_t id) {
|
||||
uint32_t sample_rate = 0;
|
||||
auto reader = std::make_unique<WAVFileReader>();
|
||||
uint32_t tone_key_index = options_tone_key.selected_index();
|
||||
|
||||
stop(false);
|
||||
|
||||
if (!reader->open(sounds[id].path)) {
|
||||
file_error();
|
||||
return;
|
||||
}
|
||||
|
||||
playing_id = id;
|
||||
|
||||
progressbar.set_max(reader->sample_count());
|
||||
sample_rate = reader->sample_rate() * 32;
|
||||
|
||||
if( reader ) {
|
||||
//button_play.set_bitmap(&bitmap_stop);
|
||||
baseband::set_sample_rate(sample_rate);
|
||||
|
||||
replay_thread = std::make_unique<ReplayThread>(
|
||||
std::move(reader),
|
||||
read_size, buffer_count,
|
||||
&ready_signal,
|
||||
[](uint32_t return_code) {
|
||||
ReplayThreadDoneMessage message { return_code };
|
||||
EventDispatcher::send_message(message);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
baseband::set_audiotx_config(
|
||||
0, // Divider is unused
|
||||
number_bw.value() * 1000,
|
||||
0, // Gain is unused
|
||||
TONES_F2D(tone_key_frequency(tone_key_index), sample_rate)
|
||||
);
|
||||
|
||||
radio::enable({
|
||||
receiver_model.tuning_frequency(),
|
||||
sample_rate,
|
||||
1750000,
|
||||
rf::Direction::Transmit,
|
||||
receiver_model.rf_amp(),
|
||||
static_cast<int8_t>(receiver_model.lna()),
|
||||
static_cast<int8_t>(receiver_model.vga())
|
||||
});
|
||||
}
|
||||
|
||||
void SoundBoardView::show_infos(uint16_t id) {
|
||||
text_duration.set(to_string_time_ms(sounds[id].ms_duration));
|
||||
|
||||
text_title.set(sounds[id].title);
|
||||
}
|
||||
|
||||
void SoundBoardView::refresh_buttons(uint16_t id) {
|
||||
size_t n = 0, n_sound;
|
||||
|
||||
text_page.set("Page " + to_string_dec_uint(page + 1) + "/" + to_string_dec_uint(max_page));
|
||||
|
||||
for (auto& button : buttons) {
|
||||
n_sound = (page * 15) + n;
|
||||
|
||||
button.id = n_sound;
|
||||
|
||||
if (n_sound < max_sound) {
|
||||
button.set_text(sounds[n_sound].path.stem().string().substr(0, 8));
|
||||
button.set_style(styles[sounds[n_sound].path.stem().string()[0] & 3]);
|
||||
} else {
|
||||
button.set_text("- - -");
|
||||
button.set_style(styles[0]);
|
||||
}
|
||||
|
||||
n++;
|
||||
}
|
||||
|
||||
show_infos(id);
|
||||
}
|
||||
|
||||
bool SoundBoardView::change_page(Button& button, const KeyEvent key) {
|
||||
// Stupid way to find out if the button is on the sides
|
||||
if (button.screen_pos().x() < 32) {
|
||||
if ((key == KeyEvent::Left) && (page > 0)) {
|
||||
page--;
|
||||
refresh_buttons(button.id);
|
||||
return true;
|
||||
}
|
||||
} else if (button.screen_pos().x() > 120) {
|
||||
if ((key == KeyEvent::Right) && (page < max_page - 1)) {
|
||||
page++;
|
||||
refresh_buttons(button.id);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void SoundBoardView::on_tx_progress(const uint32_t progress) {
|
||||
progressbar.set_value(progress);
|
||||
}
|
||||
|
||||
SoundBoardView::SoundBoardView(
|
||||
NavigationView& nav
|
||||
) : nav_ (nav)
|
||||
{
|
||||
auto reader = std::make_unique<WAVFileReader>();
|
||||
uint8_t c = 0;
|
||||
|
||||
for(const auto& entry : std::filesystem::directory_iterator(u"WAV", u"*.WAV")) {
|
||||
if( std::filesystem::is_regular_file(entry.status()) ) {
|
||||
if (reader->open(u"WAV/" + entry.path().native())) {
|
||||
if ((reader->channels() == 1) && (reader->bits_per_sample() == 8)) {
|
||||
sounds[c].ms_duration = reader->ms_duration();
|
||||
sounds[c].path = u"WAV/" + entry.path().native();
|
||||
std::string title = reader->title().substr(0, 20);
|
||||
if (title != "")
|
||||
sounds[c].title = title;
|
||||
else
|
||||
sounds[c].title = "-";
|
||||
c++;
|
||||
if (c == 60) break; // Limit to 60 files (4 pages)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
baseband::run_image(portapack::spi_flash::image_tag_audio_tx);
|
||||
|
||||
max_sound = c;
|
||||
max_page = (max_sound + 15 - 1) / 15; // 3 * 5 = 15 buttons per page
|
||||
|
||||
add_children({
|
||||
&labels,
|
||||
&field_frequency,
|
||||
&number_bw,
|
||||
&options_tone_key,
|
||||
&text_title,
|
||||
&text_page,
|
||||
&text_duration,
|
||||
&progressbar,
|
||||
&check_loop,
|
||||
&button_random,
|
||||
&button_exit
|
||||
});
|
||||
|
||||
tone_keys_populate(options_tone_key);
|
||||
options_tone_key.set_selected_index(0);
|
||||
|
||||
const auto button_fn = [this](Button& button) {
|
||||
tx_mode = NORMAL;
|
||||
this->play_sound(button.id);
|
||||
};
|
||||
|
||||
const auto button_focus = [this](Button& button) {
|
||||
this->show_infos(button.id);
|
||||
};
|
||||
|
||||
const auto button_dir = [this](Button& button, const KeyEvent key) {
|
||||
return change_page(button, key);
|
||||
};
|
||||
|
||||
// Generate buttons
|
||||
size_t n = 0;
|
||||
for(auto& button : buttons) {
|
||||
add_child(&button);
|
||||
button.on_select = button_fn;
|
||||
button.on_highlight = button_focus;
|
||||
button.on_dir = button_dir;
|
||||
button.set_parent_rect({
|
||||
static_cast<Coord>((n % 3) * 78 + 3),
|
||||
static_cast<Coord>((n / 3) * 38 + 24),
|
||||
78, 38
|
||||
});
|
||||
n++;
|
||||
}
|
||||
refresh_buttons(0);
|
||||
|
||||
check_loop.set_value(false);
|
||||
number_bw.set_value(12);
|
||||
|
||||
field_frequency.set_value(transmitter_model.tuning_frequency());
|
||||
field_frequency.set_step(10000);
|
||||
field_frequency.on_change = [this](rf::Frequency f) {
|
||||
this->on_tuning_frequency_changed(f);
|
||||
};
|
||||
field_frequency.on_edit = [this, &nav]() {
|
||||
// TODO: Provide separate modal method/scheme?
|
||||
auto new_view = nav.push<FrequencyKeypadView>(transmitter_model.tuning_frequency());
|
||||
new_view->on_changed = [this](rf::Frequency f) {
|
||||
this->on_tuning_frequency_changed(f);
|
||||
this->field_frequency.set_value(f);
|
||||
};
|
||||
};
|
||||
|
||||
button_random.on_select = [this](Button&){
|
||||
if (tx_mode == NORMAL) {
|
||||
tx_mode = RANDOM;
|
||||
button_random.set_text("STOP");
|
||||
do_random();
|
||||
} else {
|
||||
tx_mode = NORMAL;
|
||||
button_random.set_text("Random");
|
||||
}
|
||||
};
|
||||
|
||||
button_exit.on_select = [&nav](Button&){
|
||||
nav.pop();
|
||||
};
|
||||
}
|
||||
|
||||
SoundBoardView::~SoundBoardView() {
|
||||
transmitter_model.disable();
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
}
|
@@ -1,207 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* 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 2, 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; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __UI_SOUNDBOARD_H__
|
||||
#define __UI_SOUNDBOARD_H__
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "ui_widget.hpp"
|
||||
#include "ui_font_fixed_8x16.hpp"
|
||||
#include "replay_thread.hpp"
|
||||
#include "baseband_api.hpp"
|
||||
#include "ui_receiver.hpp"
|
||||
#include "io_wave.hpp"
|
||||
#include "tone_key.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
class SoundBoardView : public View {
|
||||
public:
|
||||
SoundBoardView(NavigationView& nav);
|
||||
~SoundBoardView();
|
||||
|
||||
SoundBoardView(const SoundBoardView&) = delete;
|
||||
SoundBoardView(SoundBoardView&&) = delete;
|
||||
SoundBoardView& operator=(const SoundBoardView&) = delete;
|
||||
SoundBoardView& operator=(SoundBoardView&&) = delete;
|
||||
|
||||
void focus() override;
|
||||
|
||||
std::string title() const override { return "Soundboard"; };
|
||||
|
||||
private:
|
||||
NavigationView& nav_;
|
||||
|
||||
enum tx_modes {
|
||||
NORMAL = 0,
|
||||
RANDOM
|
||||
};
|
||||
|
||||
tx_modes tx_mode = NORMAL;
|
||||
|
||||
struct sound {
|
||||
std::filesystem::path path { };
|
||||
uint32_t ms_duration = 0;
|
||||
std::string title { };
|
||||
};
|
||||
|
||||
uint32_t sample_counter { 0 };
|
||||
uint32_t sample_duration { 0 };
|
||||
uint8_t page = 0;
|
||||
|
||||
uint32_t lfsr_v = 0x13377331;
|
||||
|
||||
sound sounds[60]; // 5 pages * 12 buttons
|
||||
uint32_t max_sound { };
|
||||
uint8_t max_page { };
|
||||
uint32_t playing_id { };
|
||||
|
||||
const size_t read_size { 2048 }; // Less ?
|
||||
const size_t buffer_count { 3 };
|
||||
std::unique_ptr<ReplayThread> replay_thread { };
|
||||
bool ready_signal { false };
|
||||
|
||||
Style style_a {
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::black(),
|
||||
.foreground = { 255, 51, 153 }
|
||||
};
|
||||
Style style_b {
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::black(),
|
||||
.foreground = { 153, 204, 0 }
|
||||
};
|
||||
Style style_c {
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::black(),
|
||||
.foreground = { 51, 204, 204 }
|
||||
};
|
||||
Style style_d {
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::black(),
|
||||
.foreground = { 153, 102, 255 }
|
||||
};
|
||||
|
||||
std::array<Button, 15> buttons { };
|
||||
const Style * styles[4] = { &style_a, &style_b, &style_c, &style_d };
|
||||
|
||||
void on_tuning_frequency_changed(rf::Frequency f);
|
||||
|
||||
void do_random();
|
||||
void show_infos(uint16_t id);
|
||||
bool change_page(Button& button, const KeyEvent key);
|
||||
void refresh_buttons(uint16_t id);
|
||||
void play_sound(uint16_t id);
|
||||
void on_ctcss_changed(uint32_t v);
|
||||
void stop(const bool do_loop);
|
||||
bool is_active() const;
|
||||
void set_ready();
|
||||
void handle_replay_thread_done(const uint32_t return_code);
|
||||
void file_error();
|
||||
void on_tx_progress(const uint32_t progress);
|
||||
|
||||
Labels labels {
|
||||
{ { 10 * 8, 4 }, "BW: kHz", Color::light_grey() }
|
||||
};
|
||||
|
||||
FrequencyField field_frequency {
|
||||
{ 0, 4 },
|
||||
};
|
||||
|
||||
NumberField number_bw {
|
||||
{ 13 * 8, 4 },
|
||||
3,
|
||||
{ 1, 150 },
|
||||
1,
|
||||
' '
|
||||
};
|
||||
|
||||
OptionsField options_tone_key {
|
||||
{ 21 * 8, 4 },
|
||||
8,
|
||||
{ }
|
||||
};
|
||||
|
||||
Text text_title {
|
||||
{ 1 * 8, 28 * 8, 20 * 8, 16 },
|
||||
"-"
|
||||
};
|
||||
|
||||
Text text_page {
|
||||
{ 22 * 8 - 4, 28 * 8, 8 * 8, 16 },
|
||||
"Page -/-"
|
||||
};
|
||||
|
||||
Text text_duration {
|
||||
{ 1 * 8, 31 * 8, 5 * 8, 16 }
|
||||
};
|
||||
|
||||
ProgressBar progressbar {
|
||||
{ 9 * 8, 31 * 8, 20 * 8, 16 }
|
||||
};
|
||||
|
||||
Checkbox check_loop {
|
||||
{ 8, 274 },
|
||||
4,
|
||||
"Loop"
|
||||
};
|
||||
|
||||
Button button_random {
|
||||
{ 10 * 8, 34 * 8, 9 * 8, 32 },
|
||||
"Random"
|
||||
};
|
||||
|
||||
Button button_exit {
|
||||
{ 21 * 8, 34 * 8, 8 * 8, 32 },
|
||||
"Exit"
|
||||
};
|
||||
|
||||
MessageHandlerRegistration message_handler_replay_thread_error {
|
||||
Message::ID::ReplayThreadDone,
|
||||
[this](const Message* const p) {
|
||||
const auto message = *reinterpret_cast<const ReplayThreadDoneMessage*>(p);
|
||||
this->handle_replay_thread_done(message.return_code);
|
||||
}
|
||||
};
|
||||
|
||||
MessageHandlerRegistration message_handler_fifo_signal {
|
||||
Message::ID::RequestSignal,
|
||||
[this](const Message* const p) {
|
||||
const auto message = static_cast<const RequestSignalMessage*>(p);
|
||||
if (message->signal == RequestSignalMessage::Signal::FillRequest) {
|
||||
this->set_ready();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
MessageHandlerRegistration message_handler_tx_progress {
|
||||
Message::ID::TXProgress,
|
||||
[this](const Message* const p) {
|
||||
const auto message = *reinterpret_cast<const TXProgressMessage*>(p);
|
||||
this->on_tx_progress(message.progress);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
#endif/*__UI_SOUNDBOARD_H__*/
|
Reference in New Issue
Block a user