diff --git a/firmware/application/CMakeLists.txt b/firmware/application/CMakeLists.txt index 6d0c77693..87a169487 100644 --- a/firmware/application/CMakeLists.txt +++ b/firmware/application/CMakeLists.txt @@ -165,7 +165,7 @@ set(CPPSRC ui_handwrite.cpp ui_afsksetup.cpp # ui_closecall.cpp - # ui_soundboard.cpp + ui_soundboard.cpp ui_rds.cpp ui_lcr.cpp ui_xylos.cpp diff --git a/firmware/application/Makefile b/firmware/application/Makefile index 988362ee2..e2d0e61b6 100644 --- a/firmware/application/Makefile +++ b/firmware/application/Makefile @@ -4062,6 +4062,30 @@ ui_setup.cpp.s: cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/ui_setup.cpp.s .PHONY : ui_setup.cpp.s +ui_soundboard.obj: ui_soundboard.cpp.obj +.PHONY : ui_soundboard.obj + +# target to build an object file +ui_soundboard.cpp.obj: + cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/ui_soundboard.cpp.obj +.PHONY : ui_soundboard.cpp.obj + +ui_soundboard.i: ui_soundboard.cpp.i +.PHONY : ui_soundboard.i + +# target to preprocess a source file +ui_soundboard.cpp.i: + cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/ui_soundboard.cpp.i +.PHONY : ui_soundboard.cpp.i + +ui_soundboard.s: ui_soundboard.cpp.s +.PHONY : ui_soundboard.s + +# target to generate assembly for a file +ui_soundboard.cpp.s: + cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/ui_soundboard.cpp.s +.PHONY : ui_soundboard.cpp.s + ui_spectrum.obj: ui_spectrum.cpp.obj .PHONY : ui_spectrum.obj @@ -4660,6 +4684,9 @@ help: @echo "... ui_setup.obj" @echo "... ui_setup.i" @echo "... ui_setup.s" + @echo "... ui_soundboard.obj" + @echo "... ui_soundboard.i" + @echo "... ui_soundboard.s" @echo "... ui_spectrum.obj" @echo "... ui_spectrum.i" @echo "... ui_spectrum.s" diff --git a/firmware/application/file.cpp b/firmware/application/file.cpp index f44154528..a0ebd7f3c 100644 --- a/firmware/application/file.cpp +++ b/firmware/application/file.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. + * Copyright (C) 2016 Furrtek * * This file is part of PortaPack. * @@ -177,6 +178,28 @@ std::string next_filename_stem_matching_pattern(const std::string& filename_stem return filename_stem; } +std::vector scan_root_files(const std::string& extension) { + std::vector file_list { }; + std::string fname; + FRESULT res; + DIR dir; + static FILINFO fno; + + res = f_opendir(&dir, "/"); + if (res == FR_OK) { + for (;;) { + res = f_readdir(&dir, &fno); + if (res != FR_OK || fno.fname[0] == 0) break; + fname.assign(fno.fname); + if (fname.find(extension) != std::string::npos) + file_list.push_back(fname); + } + f_closedir(&dir); + } + + return file_list; +} + namespace std { namespace filesystem { diff --git a/firmware/application/file.hpp b/firmware/application/file.hpp index f3739317f..05c7f74b2 100644 --- a/firmware/application/file.hpp +++ b/firmware/application/file.hpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. + * Copyright (C) 2016 Furrtek * * This file is part of PortaPack. * @@ -32,8 +33,10 @@ #include #include #include +#include std::string next_filename_stem_matching_pattern(const std::string& filename_stem_pattern); +std::vector scan_root_files(const std::string& extension); namespace std { namespace filesystem { diff --git a/firmware/application/main.cpp b/firmware/application/main.cpp index 23a3181dd..1654795d3 100755 --- a/firmware/application/main.cpp +++ b/firmware/application/main.cpp @@ -23,19 +23,21 @@ // Bitmaps generated with: // Gimp image > indexed colors (16), then "xxd -i *.bmp" -//BUG: No audio in about when shown second time +//BUG (fixed ?): No audio in about when shown second time +//BUG: POCSAG RX sometimes misses the first codeword after SYNC -//TODO: Make frequency set button in afsksetup accept GHz frequencies (longer label buffer) +//TODO: POCSAG 512 and 2400 (all 3 at the same time, or parameter ?) //TODO: Check AFSK transmit end, skips last bits ? +//TODO: Check jammer bandwidths //TODO: Use msgpack for settings, lists... on sd card //TODO: Frequency manager //TODO: Morse coder +//TODO: Replay //Multimon-style stuff: //TODO: AFSK receiver -//TODO: Xylos receiver +//TODO: Xylos (CCIR) receiver -//TODO: Check jammer bandwidths //TODO: Closecall wide range fix //TODO: SD card wiper //TODO: GSM channel detector diff --git a/firmware/application/pocsag_app.cpp b/firmware/application/pocsag_app.cpp index a886cab1f..7eba47774 100644 --- a/firmware/application/pocsag_app.cpp +++ b/firmware/application/pocsag_app.cpp @@ -41,18 +41,18 @@ namespace format { static std::string bitrate_str(BitRate bitrate) { switch (bitrate) { - case BitRate::FSK512: return "F512 "; - case BitRate::FSK1200: return "F1200"; - case BitRate::FSK2400: return "F2400"; - default: return "F????"; + case BitRate::FSK512: return "512 "; + case BitRate::FSK1200: return "1200"; + case BitRate::FSK2400: return "2400"; + default: return "????"; } } static std::string flag_str(PacketFlag packetflag) { switch (packetflag) { - case PacketFlag::NORMAL: return "NORMAL "; + case PacketFlag::NORMAL: return "OK"; case PacketFlag::TIMED_OUT: return "TIMED OUT"; - case PacketFlag::TOO_LONG: return "TOO LONG "; + case PacketFlag::TOO_LONG: return "TOO LONG"; default: return ""; } } @@ -208,12 +208,12 @@ void POCSAGAppView::on_packet(const POCSAGPacketMessage * message) { if (eom) { std::string console_info; - console_info = to_string_time(message->packet.timestamp()); + console_info = to_string_time(message->packet.timestamp()) + " "; if (address || function) { console_info += pocsag::format::bitrate_str( message->packet.bitrate()) + " "; console_info += pocsag::format::flag_str(message->packet.flag()) + " "; - console_info += " ADDR:" + to_string_dec_uint(address, 7) + " F:" + to_string_dec_uint(function); + console_info += "ADDR:" + to_string_dec_uint(address) + " F:" + to_string_dec_uint(function); console.writeln(console_info); diff --git a/firmware/application/ui_about.cpp b/firmware/application/ui_about.cpp index df2a5f498..e520f2904 100644 --- a/firmware/application/ui_about.cpp +++ b/firmware/application/ui_about.cpp @@ -59,9 +59,7 @@ void AboutView::on_show() { transmitter_model.set_baseband_bandwidth(1750000); transmitter_model.enable(); - baseband::set_audiotx_data( - 0 - ); + baseband::set_audiotx_data(15); //audio::headphone::set_volume(volume_t::decibel(0 - 99) + audio::headphone::volume_range().max); } @@ -132,7 +130,7 @@ void AboutView::render_video() { // Switch to next text if (anim_state == 0) { - if (credits_index == 7) + if (credits_index == 9) credits_index = 0; else credits_index++; @@ -221,7 +219,7 @@ void AboutView::render_audio() { // Render 1024 music samples for (ym_render_cnt = 0; ym_render_cnt < 1024; ym_render_cnt++) { - // Taken at 48000/960 = 50Hz + // Update registers at 48000/960 = 50Hz if (ym_sample_cnt == 0) { // "Decompress" on the fly and update YM registers for (i = 0; i < 14; i++) { @@ -338,7 +336,7 @@ void AboutView::update() { } // Slowly increase volume to avoid jumpscare - if (headphone_vol < (70<<2)) { + if (headphone_vol < (70 << 2)) { audio::headphone::set_volume(volume_t::decibel((headphone_vol/4) - 99) + audio::headphone::volume_range().max); headphone_vol++; } diff --git a/firmware/application/ui_about.hpp b/firmware/application/ui_about.hpp index 2cca9e73f..2195c4ca3 100644 --- a/firmware/application/ui_about.hpp +++ b/firmware/application/ui_about.hpp @@ -56,12 +56,12 @@ private: bool same; } ymreg_t; - uint16_t headphone_vol = 5<<2; + uint16_t headphone_vol = 5 << 2; ymreg_t ym_regs[14]; uint16_t ym_frames; uint16_t ym_frame; - uint8_t drum; + uint8_t drum = 0; uint16_t ym_osc_cnt[3]; uint32_t ym_rng = 1; uint16_t ym_noise_cnt; @@ -80,8 +80,8 @@ private: ui::Color paletteB[16]; ui::Color * framebuffer; uint32_t phase = 0; - uint8_t copperbars[5] = {0}; - uint8_t copperbuffer[72] = {0}; + uint8_t copperbars[5] = { 0 }; + uint8_t copperbuffer[72] = { 0 }; uint8_t anim_state = 0; uint8_t credits_index = 0; @@ -98,12 +98,12 @@ private: 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; - const uint8_t copperluma[16] = {8,7,6,5,4,3,2,1,1,2,3,4,5,6,7,8}; - const uint8_t coppercolor[5][3] = { {255,0,0}, - {0,255,0}, - {0,0,255}, - {255,0,255}, - {255,255,0} }; + const uint8_t copperluma[16] = { 8,7,6,5,4,3,2,1,1,2,3,4,5,6,7,8 }; + const uint8_t coppercolor[5][3] = { { 255,0,0 }, + { 0,255,0 }, + { 0,0,255 }, + { 255,0,255 }, + { 255,255,0 } }; typedef struct credits_t { char role[12]; @@ -112,10 +112,12 @@ private: } credits_t; // 0123456789A 0123456789A - const credits_t credits[8] = { {"GURUS", "J. BOONE", false}, - {"GURUS", "M. OSSMANN", true}, + const credits_t credits[10] = { {"GURUS", "J. BOONE", false}, + {"GURYS", "M. OSSMANN", true}, {"BUGS", "FURRTEK", true}, - {"RDS TABLE", "C. JACQUET", true}, + {"RDS WAVE", "C. JACQUET", true}, + {"POCSAG RX", "T. SAILER", false}, + {"POCSAG RX", "E. OENAL", true}, {"XYLOS DATA", "CLX", true}, {"GREETS TO", "SIGMOUNTE", false}, {"GREETS TO", "WINDYOONA", true}, diff --git a/firmware/application/ui_lcr.cpp b/firmware/application/ui_lcr.cpp index b632756b0..94428be2e 100644 --- a/firmware/application/ui_lcr.cpp +++ b/firmware/application/ui_lcr.cpp @@ -266,7 +266,7 @@ void LCRView::start_tx(const bool scan) { portapack::persistent_memory::afsk_mark_freq() * 437 * 5, //(0x40000 * 256) / (153600 / 25), portapack::persistent_memory::afsk_space_freq() * 437 * 5, //(0x40000 * 256) / (153600 / 25), afsk_repeats, - portapack::persistent_memory::afsk_bw() * 115, // See proc_fsk_lcr.cpp + portapack::persistent_memory::afsk_bw() * 115, // See proc_afsk.cpp afsk_format ); } diff --git a/firmware/application/ui_navigation.cpp b/firmware/application/ui_navigation.cpp index 2fc35cff3..ce4b69773 100644 --- a/firmware/application/ui_navigation.cpp +++ b/firmware/application/ui_navigation.cpp @@ -35,9 +35,9 @@ #include "ui_setup.hpp" #include "ui_debug.hpp" -//#include "ui_soundboard.hpp" // DEBUG //#include "ui_closecall.hpp" // DEBUG -#include "ui_freqman.hpp" // DEBUG +//#include "ui_freqman.hpp" // DEBUG +#include "ui_soundboard.hpp" #include "ui_encoders.hpp" #include "ui_debug.hpp" @@ -46,7 +46,7 @@ #include "ui_epar.hpp" #include "ui_lcr.hpp" #include "analog_audio_app.hpp" -#include "ui_audiotx.hpp" // DEBUG +//#include "ui_audiotx.hpp" // DEBUG //#include "ui_jammer.hpp" // DEBUG #include "analog_audio_app.hpp" @@ -246,9 +246,9 @@ SystemMenuView::SystemMenuView(NavigationView& nav) { { "Receiver RX", ui::Color::cyan(), [&nav](){ nav.push(); } }, { "Capture RX", ui::Color::cyan(), [&nav](){ nav.push(); } }, //{ "Close Call RX", ui::Color::cyan(), [&nav](){ nav.push(); } }, - { "Numbers station TX", ui::Color::purple(), [&nav](){ nav.push(); } }, //nav.push(); + //{ "Numbers station TX", ui::Color::purple(), [&nav](){ nav.push(); } }, //nav.push(); //{ "Pokemon GO Away TX", ui::Color::blue(), [&nav](){ nav.push(); } }, - //{ "Soundboard TX", ui::Color::yellow(), [&nav](){ nav.push(md5_baseband_tx, SoundBoard); } }, + { "Soundboard TX", ui::Color::yellow(), [&nav](){ nav.push(); } }, //{ "Audio TX", ui::Color::yellow(), [&nav](){ nav.push(md5_baseband_tx, AudioTX); } }, //{ "Frequency manager", ui::Color::white(), [&nav](){ nav.push(); } }, //{ "EPAR TX", ui::Color::green(), [&nav](){ nav.push(md5_baseband_tx, EPAR); } }, diff --git a/firmware/application/ui_soundboard.cpp b/firmware/application/ui_soundboard.cpp index c45b26674..ed9e03b6e 100644 --- a/firmware/application/ui_soundboard.cpp +++ b/firmware/application/ui_soundboard.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. + * Copyright (C) 2016 Furrtek * * This file is part of PortaPack. * @@ -19,6 +20,8 @@ * 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 "ch.h" @@ -35,28 +38,65 @@ #include "portapack_shared_memory.hpp" #include +#include using namespace portapack; namespace ui { + +uint16_t SoundBoardView::shitty_rand() { + uint8_t bit; -void SoundBoardView::on_show() { - /* - // Just in case - EventDispatcher::message_map().unregister_handler(Message::ID::DisplayFrameSync); + bit = ((lfsr >> 0) ^ (lfsr >> 2) ^ (lfsr >> 3) ^ (lfsr >> 5) ) & 1; - // "Vertical blank interrupt" - EventDispatcher::message_map().register_handler(Message::ID::DisplayFrameSync, - [this](const Message* const) { - pbar_test.set_value(testv/4); - testv++; - } - );*/ + return lfsr = (lfsr >> 1) | (bit << 15); } -std::string SoundBoardView::title() const { - return "Sound board"; -}; +void SoundBoardView::do_random() { + uint16_t id; + + chThdSleepMilliseconds(300); // 100ms + + id = shitty_rand() % max_sound; + + play_sound(id); + + buttons[id % 21].focus(); + page = id / 21; + + refresh_buttons(id); +} + +void SoundBoardView::prepare_audio() { + + if (cnt >= sample_duration) { + if (tx_mode == NORMAL) { + if (!check_loop.value()) { + pbar.set_value(0); + transmitter_model.disable(); + return; + } else { + file.seek(44); + cnt = 0; + } + } else { + pbar.set_value(0); + transmitter_model.disable(); + do_random(); + } + } + + pbar.set_value(cnt); + + file.read(audio_buffer, 1024); + + for (size_t n = 0; n < 1024; n++) + audio_buffer[n] -= 0x80; + + cnt += 1024; + + baseband::set_fifo_data(audio_buffer); +} void SoundBoardView::focus() { buttons[0].focus(); @@ -66,72 +106,218 @@ void SoundBoardView::on_tuning_frequency_changed(rf::Frequency f) { transmitter_model.set_tuning_frequency(f); } -void SoundBoardView::on_button(Button& button) { - text_test.set(to_string_dec_uint(button.id)); +void SoundBoardView::play_sound(uint16_t id) { + + auto error = file.open(sounds[id].filename); + if (error.is_valid()) return; + + sample_duration = sounds[id].sample_duration; + + pbar.set_max(sample_duration); + pbar.set_value(0); + + cnt = 0; + file.seek(44); // Skip header + + prepare_audio(); + + transmitter_model.set_baseband_configuration({ + .mode = 0, + .sampling_rate = 1536000, + .decimation_factor = 1, + }); + transmitter_model.set_rf_amp(true); + transmitter_model.set_lna(40); + transmitter_model.set_vga(40); + transmitter_model.set_baseband_bandwidth(1750000); + transmitter_model.enable(); + + baseband::set_audiotx_data(number_bw.value()); +} + +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"); +} + +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)); + + for (auto& button : buttons) { + n_sound = (page * 21) + n; + + button.id = n_sound; + + if (n_sound < max_sound) { + button.set_text(sounds[n_sound].shortname); + button.set_style(styles[sounds[n_sound].shortname[0] & 3]); + } else { + button.set_text("- - -"); + button.set_style(styles[0]); + } + + n++; + } + + show_infos(id); +} + +void 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); + } + } + + if (button.screen_pos().x > 120) { + if ((key == KeyEvent::Right) && (page < max_page - 1)) { + page++; + refresh_buttons(button.id); + } + } +} + +uint16_t SoundBoardView::fb_to_uint16(const std::string& fb) { + return (fb[1] << 8) + fb[0]; +} + +uint32_t SoundBoardView::fb_to_uint32(const std::string& fb) { + return (fb[3] << 24) + (fb[2] << 16) + (fb[1] << 8) + fb[0]; } SoundBoardView::SoundBoardView( NavigationView& nav ) { - size_t n; + std::vector file_list; + std::string file_name; + uint32_t size; + uint8_t c; - for (n = 0; n < 12; n++) { - sounds[n].filename = ""; - sounds[n].shortname = "Empty"; - sounds[n].min = 0; - sounds[n].sec = 0; + char file_buffer[32]; + + baseband::run_image(portapack::spi_flash::image_tag_audio_tx); + + file_list = scan_root_files(".WAV"); + + c = 0; + for (auto& file_name : file_list) { + + auto error = file.open(file_name); + + file.seek(40); + file.read(file_buffer, 4); + size = fb_to_uint32(file_buffer); + sounds[c].size = size; + + file.seek(22); + file.read(file_buffer, 2); + if (fb_to_uint16(file_buffer) > 1) { + sounds[c].stereo = true; + size /= 2; + } else + sounds[c].stereo = false; + + file.seek(24); + file.read(file_buffer, 4); + sounds[c].sample_rate = fb_to_uint32(file_buffer); + + file.seek(34); + file.read(file_buffer, 2); + if (fb_to_uint16(file_buffer) > 8) { + sounds[c].sixteenbit = true; + size /= 2; + } else + sounds[c].sixteenbit = false; + + sounds[c].ms_duration = (size * 1000) / sounds[c].sample_rate; + sounds[c].sample_duration = size; + + sounds[c].filename = file_name; + sounds[c].shortname = file_name.substr(0, file_name.find_last_of(".")); + + c++; + if (c == 100) break; } + max_sound = c; + max_page = max_sound / 21; + add_children({ { - &text_test, &field_frequency, &number_bw, - &pbar_test, - &button_load, + &text_kHz, + &text_page, + &text_duration, + &pbar, + &check_loop, + &button_random, &button_exit } }); const auto button_fn = [this](Button& button) { - this->on_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) { + (void)button; + + this->change_page(button, key); }; + size_t n = 0; for(auto& button : buttons) { add_child(&button); - button.id = n; button.on_select = button_fn; + button.on_highlight = button_focus; + button.on_dir = button_dir; button.set_parent_rect({ - static_cast((n % 3) * 70 + 15), - static_cast((n / 3) * 50 + 30), - 70, 50 + static_cast((n % 3) * 78 + 3), + static_cast((n / 3) * 28 + 26), + 78, 28 }); - button.set_text(sounds[n].shortname); n++; } + refresh_buttons(0); + + check_loop.set_value(false); + number_bw.set_value(15); field_frequency.set_value(transmitter_model.tuning_frequency()); - field_frequency.set_step(receiver_model.frequency_step()); + 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(receiver_model.tuning_frequency()); + auto new_view = nav.push(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_transmit.on_select = [](Button&){ - transmitter_model.set_baseband_configuration({ - .mode = 1, - .sampling_rate = 1536000, - .decimation_factor = 1, - }); - transmitter_model.set_rf_amp(true); - transmitter_model.enable(); - };*/ + 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(); @@ -140,6 +326,7 @@ SoundBoardView::SoundBoardView( SoundBoardView::~SoundBoardView() { transmitter_model.disable(); + baseband::shutdown(); } } diff --git a/firmware/application/ui_soundboard.hpp b/firmware/application/ui_soundboard.hpp index 398c5b0d0..324166d41 100644 --- a/firmware/application/ui_soundboard.hpp +++ b/firmware/application/ui_soundboard.hpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. + * Copyright (C) 2016 Furrtek * * This file is part of PortaPack. * @@ -19,19 +20,17 @@ * Boston, MA 02110-1301, USA. */ +#ifndef __UI_SOUNDBOARD_H__ +#define __UI_SOUNDBOARD_H__ + #include "ui.hpp" #include "ui_widget.hpp" -#include "ui_painter.hpp" -#include "ui_menu.hpp" -#include "ui_navigation.hpp" #include "ui_font_fixed_8x16.hpp" -#include "clock_manager.hpp" -#include "message.hpp" -#include "rf_path.hpp" -#include "max2837.hpp" -#include "volume.hpp" +#include "baseband_api.hpp" +#include "ui_navigation.hpp" #include "ui_receiver.hpp" -#include "transmitter_model.hpp" +#include "message.hpp" +#include "file.hpp" namespace ui { @@ -40,27 +39,81 @@ public: SoundBoardView(NavigationView& nav); ~SoundBoardView(); - std::string title() const; - void on_show() override; void focus() override; + std::string title() const override { return "Soundboard"; }; + private: + enum tx_modes { + NORMAL = 0, + RANDOM + }; + + tx_modes tx_mode = NORMAL; + struct sound { std::string filename; std::string shortname; - uint8_t min; - uint8_t sec; + bool stereo; + bool sixteenbit; + uint32_t sample_rate; + uint32_t size; + uint32_t sample_duration; + uint32_t ms_duration; }; - sound sounds[12]; + uint32_t cnt; + uint32_t sample_duration; + uint8_t page = 0; + + File file; + + uint16_t lfsr = 0x1337u; + + sound sounds[100]; + uint8_t max_sound; + uint8_t max_page; - std::array buttons; - void on_button(Button& button); + int8_t audio_buffer[1024]; + + 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 buttons; + const Style * styles[4] = { &style_a, &style_b, &style_c, &style_d }; void on_tuning_frequency_changed(rf::Frequency f); - Text text_test { - { 120, 4, 64, 16 } + void do_random(); + uint16_t shitty_rand(); + void show_infos(uint16_t id); + void change_page(Button& button, const KeyEvent key); + void refresh_buttons(uint16_t id); + void play_sound(uint16_t id); + void prepare_audio(); + uint16_t fb_to_uint16(const std::string& fb); + uint32_t fb_to_uint32(const std::string& fb); + + Text text_duration { + { 16, 236, 5 * 8, 16 } }; FrequencyField field_frequency { @@ -68,26 +121,54 @@ private: }; NumberField number_bw { - { 16 * 8, 4 }, - 5, - {1000, 50000}, - 500, + { 14 * 8, 4 }, + 2, + {1, 50}, + 1, ' ' }; - ProgressBar pbar_test { - { 45, 236, 150, 16 } + Text text_kHz { + { 16 * 8, 4, 3 * 8, 16 }, + "kHz" }; - Button button_load { - { 8, 270, 64, 32 }, - "Load" + Text text_page { + { 22 * 8, 4, 3 * 8, 16 }, + "-/-" + }; + + ProgressBar pbar { + { 72, 236, 150, 16 } + }; + + Checkbox check_loop { + { 16, 274 }, + 4, + "Loop" + }; + + Button button_random { + { 80, 270, 72, 32 }, + "Random" }; Button button_exit { - { 96, 270, 64, 32 }, + { 160, 270, 64, 32 }, "Exit" }; + + MessageHandlerRegistration message_handler_fifo_signal { + Message::ID::FIFOSignal, + [this](const Message* const p) { + const auto message = static_cast(p); + if (message->signaltype == 1) { + this->prepare_audio(); + } + } + }; }; } /* namespace ui */ + +#endif/*__UI_SOUNDBOARD_H__*/ diff --git a/firmware/baseband/proc_afsk.cpp b/firmware/baseband/proc_afsk.cpp index 055c99c48..8f0e86762 100644 --- a/firmware/baseband/proc_afsk.cpp +++ b/firmware/baseband/proc_afsk.cpp @@ -106,7 +106,8 @@ void AFSKProcessor::execute(const buffer_c8_t& buffer) { // FM // 1<<18 = 262144 - // m = (262144 * BW) / 2280000 (* 115, see ui_lcr afsk_bw setting) + // m = (262144 * a) / 2280000 + // a = 262144 / 2280000 (*1000 = 115, see ui_lcr afsk_bw setting) frq = tone_sample * afsk_bw; phase = (phase + frq); diff --git a/firmware/baseband/proc_audiotx.cpp b/firmware/baseband/proc_audiotx.cpp index a2884b146..22b5adb8e 100644 --- a/firmware/baseband/proc_audiotx.cpp +++ b/firmware/baseband/proc_audiotx.cpp @@ -38,8 +38,8 @@ void AudioTXProcessor::execute(const buffer_c8_t& buffer){ for (size_t i = 0; i= 31) { - as = 0; + if (!as) { + as = 32; audio_fifo.out(sample); //preview_audio_buffer.p[ai++] = sample << 8; @@ -50,11 +50,11 @@ void AudioTXProcessor::execute(const buffer_c8_t& buffer){ asked = true; } } else { - as++; + as--; } // FM - frq = sample * 8000; + frq = sample * bw; phase = (phase + frq); sphase = phase + (64 << 18); @@ -69,9 +69,17 @@ void AudioTXProcessor::execute(const buffer_c8_t& buffer){ } void AudioTXProcessor::on_message(const Message* const msg) { + const auto message = static_cast(msg); + switch(msg->id) { case Message::ID::AudioTXConfig: - //const auto message = static_cast(msg); + + // 1<<18 = 262144 + // m = (262144 * a) / 1536000 + // a = 262144 / 1536000 (*1000 = 171) + bw = 171 * (message->bw); + as = 0; + configured = true; break; diff --git a/firmware/baseband/proc_audiotx.hpp b/firmware/baseband/proc_audiotx.hpp index 6cb6431d8..2cad1548e 100644 --- a/firmware/baseband/proc_audiotx.hpp +++ b/firmware/baseband/proc_audiotx.hpp @@ -41,6 +41,7 @@ private: int8_t audio_fifo_data[2048]; FIFO audio_fifo = { audio_fifo_data, 11 }; // 43ms @ 48000Hz + uint32_t bw; uint8_t as = 0, ai; int8_t re, im; int8_t sample; diff --git a/firmware/baseband/proc_pocsag.cpp b/firmware/baseband/proc_pocsag.cpp index 485b967dd..bfe3cc0ee 100644 --- a/firmware/baseband/proc_pocsag.cpp +++ b/firmware/baseband/proc_pocsag.cpp @@ -94,7 +94,7 @@ void POCSAGProcessor::execute(const buffer_c8_t& buffer) { last_rx_data = rx_data; rx_state = SYNC; } else if (rx_data == POCSAG_IDLE) { - //rx_state = WAITING; + rx_state = WAITING; } } else { @@ -106,6 +106,7 @@ void POCSAGProcessor::execute(const buffer_c8_t& buffer) { if (msg_timeout < 600) { msg_timeout++; rx_bit++; + if (rx_bit >= 32) { rx_bit = 0; diff --git a/firmware/common/ui_widget.cpp b/firmware/common/ui_widget.cpp index e5506f5cf..fde500730 100644 --- a/firmware/common/ui_widget.cpp +++ b/firmware/common/ui_widget.cpp @@ -434,13 +434,13 @@ ProgressBar::ProgressBar( { } -void ProgressBar::set_max(const uint16_t max) { +void ProgressBar::set_max(const uint32_t max) { _value = 0; _max = max; set_dirty(); } -void ProgressBar::set_value(const uint16_t value) { +void ProgressBar::set_value(const uint32_t value) { if (value > _max) _value = _max; else @@ -689,6 +689,11 @@ void Button::paint(Painter& painter) { ); } +void Button::on_focus() { + if( on_highlight ) + on_highlight(*this); +} + bool Button::on_key(const KeyEvent key) { if( key == KeyEvent::Select ) { if( on_select ) { diff --git a/firmware/common/ui_widget.hpp b/firmware/common/ui_widget.hpp index b67608faa..54b4fab2d 100644 --- a/firmware/common/ui_widget.hpp +++ b/firmware/common/ui_widget.hpp @@ -220,14 +220,14 @@ class ProgressBar : public Widget { public: ProgressBar(Rect parent_rect); - void set_max(const uint16_t max); - void set_value(const uint16_t value); + void set_max(const uint32_t max); + void set_value(const uint32_t value); void paint(Painter& painter) override; private: - uint16_t _value = 0; - uint16_t _max = 100; + uint32_t _value = 0; + uint32_t _max = 100; }; class Console : public Widget { @@ -282,6 +282,7 @@ class Button : public Widget { public: std::function on_select; std::function on_dir; + std::function on_highlight; Button(Rect parent_rect, std::string text); @@ -295,6 +296,7 @@ public: void paint(Painter& painter) override; + void on_focus() override; bool on_key(const KeyEvent key) override; bool on_touch(const TouchEvent event) override; diff --git a/firmware/portapack-h1-havoc.bin b/firmware/portapack-h1-havoc.bin index 80b02e3a9..1758558ba 100644 Binary files a/firmware/portapack-h1-havoc.bin and b/firmware/portapack-h1-havoc.bin differ