Compare commits

...

10 Commits

Author SHA1 Message Date
jLynx eb22af84ff Update version.txt 2024-12-20 20:05:01 +13:00
jLynx 6dc25813e2 Update past_version.txt 2024-12-20 20:04:46 +13:00
Bernd Herzog 4ee00c0031 Enable exFAT feature (#2437)
* enabled exFAT in ffconf
* added exFAT to sd card check
* solved todo
2024-12-20 07:55:39 +01:00
sommermorgentraum 55525ed4ad fix wrong cast in morse app (#2436)
* fix wrong cast

* sorry, forgot to use enum
2024-12-20 07:53:15 +01:00
Erwin Ried 84dcfe29a7 Update README.md link to h4m was broken again 2024-12-18 23:44:35 +01:00
Erwin Ried 1ebe956557 h4m photo was broken 2024-12-18 23:35:42 +01:00
gullradriel ae656f07bf Usb fix (#2433)
* revert custom compiler definition since we are now having correct hackrf m0 implementation

* update submodule

---------

Co-authored-by: gullradriel <gullradriel@no-mail.com>
2024-12-18 21:55:53 +01:00
sommermorgentraum d5c894b322 fix namingspace for an app, remove too old images and links (#2432) 2024-12-18 11:20:53 +01:00
sommermorgentraum 9cea76a9f3 add metronome app (merge tuner firstly to satisfy conflict) (#2431)
* _

* _
2024-12-18 09:25:02 +00:00
sommermorgentraum ead9449609 add tuner app (#2429)
* _

* icon
2024-12-18 10:06:25 +01:00
22 changed files with 926 additions and 42 deletions
+1 -1
View File
@@ -1 +1 @@
v2.0.1
v2.0.2
+1 -1
View File
@@ -1 +1 @@
v2.0.2
v2.1.0
+2 -4
View File
@@ -9,9 +9,7 @@
This is a fork of the [Havoc](https://github.com/furrtek/portapack-havoc/) firmware, which itself was a fork of the [PortaPack](https://github.com/sharebrained/portapack-hackrf) firmware, an add-on for the [HackRF](http://greatscottgadgets.com/hackrf/). A fork is a derivate, in this case one that has extra features and fixes when compared to the older versions.
[<img src="https://raw.githubusercontent.com/wiki/portapack-mayhem/mayhem-firmware/img/hw_overview_h2_front.png" height="400">](https://github.com/portapack-mayhem/mayhem-firmware/wiki/Hardware-overview) [<img src="https://raw.githubusercontent.com/wiki/portapack-mayhem/mayhem-firmware/img/hw_overview_h2_inside.png" height="400">](https://github.com/portapack-mayhem/mayhem-firmware/wiki/Hardware-overview#portapack-internals)
*[PortaPack H2+HackRF+battery](https://www.ebay.com/itm/116382397447) (clone) with a custom [3d printed case](https://github.com/portapack-mayhem/mayhem-firmware/wiki/3d-printed-enclosure)*
[<img src="https://github.com/user-attachments/assets/dea337ab-fb64-4a2a-b419-69afd272e815" height="400">](https://github.com/portapack-mayhem/mayhem-firmware/wiki/PortaPack-Versions#new-h4m-mayhem-edition) [<img src="https://camo.githubusercontent.com/5c1f1da0688240ac7b2ccca0c8dbfd1d73f2540741ad8b1828ba4d5ea68af248/68747470733a2f2f6769746875622d70726f64756374696f6e2d757365722d61737365742d3632313064662e73332e616d617a6f6e6177732e636f6d2f343339333937392f3239353533323731382d38653562363631632d663934362d346365652d386232642d3061363135663737313566342e706e67" height="400">](https://github.com/portapack-mayhem/mayhem-firmware/wiki/PortaPack-Versions#h2m-mayhem-edition)
# What is this?
@@ -28,7 +26,7 @@ This repository expands upon the previous work by many people and aims to consta
## What to buy?
<!---not direct to h4m but to opensourcesdrlab https://share.hackrf.app/TUOLYI--->
:heavy_check_mark: ![Static Badge](https://img.shields.io/badge/NEW-yellow) The fabulous [PortaPack H4M Mayhem](https://share.hackrf.app/0STXCA), featuring numerous improvements and accessories. Sold by our friends at [OpenSourceSDRLab](https://share.hackrf.app/99SAMT). Join their giveaways on discord (check the badge on top).
:heavy_check_mark: ![Static Badge](https://img.shields.io/badge/NEW-yellow) The fabulous H4M [complete](https://share.hackrf.app/TUOLYI) or [upgrade](https://share.hackrf.app/FPLM1H), featuring numerous improvements and accessories. Sold by our friends at [OpenSourceSDRLab](https://share.hackrf.app/99SAMT). Join their giveaways on discord (check the badge on top).
:heavy_check_mark: A recommended one is this [PortaPack H2](https://www.ebay.com/itm/116382397447), that includes everything you need with the plastic case "inspired" on [this](https://github.com/portapack-mayhem/mayhem-firmware/wiki/3d-printed-enclosure).
+2 -16
View File
@@ -102,21 +102,6 @@ include(external/external.cmake)
# Define linker script file here
set(LDSCRIPT ${PORTLD}/LPC43xx_M0.ld)
# Special case for these two files:
# Originally these are compiled for M4 support, but in Mayhem we use them with M0
# As a result they are generating a lot of noise warnings
# Since they are not really using the defines stated in the warnings
# Since the only used one (NVIC_USB0_IRQ) is having the same value in both M0 and M4 mode
# => We are faking M4 mode on them
set(NO_WARNINGS_USBFILES
${HACKRF_PATH}/firmware/common/usb.c
${HACKRF_PATH}/firmware/common/usb_queue.c
)
# -DLPC43XX_M4 kills the 'warning: "NVIC_XXXX..." redefined'
# -D__ARM_ARCH_7M__ kills the 'warning: implicit declaration of function __ldrex && __sdrex'
set_source_files_properties(${NO_WARNINGS_USBFILES} PROPERTIES COMPILE_FLAGS "-DLPC43XX_M4 -D__ARM_ARCH_7M__")
# C sources that can be compiled in ARM or THUMB mode depending on the global
# setting.
set(CSRC
@@ -133,7 +118,8 @@ set(CSRC
usb_serial_endpoints.c
usb_serial_device_to_host.c
i2c_device_to_host.c
${NO_WARNINGS_USBFILES}
${HACKRF_PATH}/firmware/common/usb.c
${HACKRF_PATH}/firmware/common/usb_queue.c
${HACKRF_PATH}/firmware/hackrf_usb/usb_device.c
${HACKRF_PATH}/firmware/common/usb_request.c
${HACKRF_PATH}/firmware/common/usb_standard_request.c
+2 -2
View File
@@ -26,7 +26,7 @@
using namespace portapack;
namespace ui {
namespace ui::external_app::audio_test {
AudioTestView::AudioTestView(NavigationView& nav)
: nav_{nav} {
@@ -103,4 +103,4 @@ void AudioTestView::update_audio_beep() {
baseband::request_beep_stop();
}
} /* namespace ui */
} /* namespace ui::external_app::audio_test */
+2 -2
View File
@@ -25,7 +25,7 @@
#include "ui_navigation.hpp"
#include "ui_receiver.hpp"
namespace ui {
namespace ui::external_app::audio_test {
class AudioTestView : public View {
public:
@@ -96,6 +96,6 @@ class AudioTestView : public View {
Theme::getInstance()->bg_dark->background};
};
} /* namespace ui */
} /* namespace ui::external_app::audio_test */
#endif /*__UI_AUDIO_TEST_H__*/
+11 -2
View File
@@ -134,11 +134,18 @@ set(EXTCPPSRC
#mcu_temperature
external/mcu_temperature/main.cpp
external/mcu_temperature/mcu_temperature.cpp
#fmradio
external/fmradio/main.cpp
external/fmradio/ui_fmradio.cpp
#tuner
external/tuner/main.cpp
external/tuner/ui_tuner.cpp
#metronome
external/metronome/main.cpp
external/metronome/ui_metronome.cpp
)
set(EXTAPPLIST
@@ -172,7 +179,9 @@ set(EXTAPPLIST
ook_editor
shoppingcart_lock
flippertx
remote
remote
mcu_temperature
fmradio
tuner
metronome
)
+15 -2
View File
@@ -55,7 +55,9 @@ MEMORY
ram_external_app_ook_editor(rwx) : org = 0xADCE0000, len = 32k
ram_external_app_remote(rwx) : org = 0xADCF0000, len = 32k
ram_external_app_mcu_temperature(rwx) : org = 0xADD00000, len = 32k
ram_external_app_fmradio(rwx) : org = 0xADE00000, len = 32k
ram_external_app_fmradio(rwx) : org = 0xADD10000, len = 32k
ram_external_app_tuner(rwx) : org = 0xADD20000, len = 32k
ram_external_app_metronome(rwx) : org = 0xADD30000, len = 32k
}
SECTIONS
@@ -252,11 +254,22 @@ SECTIONS
KEEP(*(.external_app.app_mcu_temperature.application_information));
*(*ui*external_app*mcu_temperature*);
} > ram_external_app_mcu_temperature
.external_app_fmradio : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_fmradio.application_information));
*(*ui*external_app*fmradio*);
} > ram_external_app_fmradio
.external_app_tuner : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_tuner.application_information));
*(*ui*external_app*tuner*);
} > ram_external_app_tuner
.external_app_metronome : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_metronome.application_information));
*(*ui*external_app*metronome*);
} > ram_external_app_metronome
}
+84
View File
@@ -0,0 +1,84 @@
/*
* Copyright (C) 2024 Bernd
*
* 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_metronome.hpp"
#include "ui_navigation.hpp"
#include "external_app.hpp"
namespace ui::external_app::metronome {
void initialize_app(ui::NavigationView& nav) {
nav.push<MetronomeView>();
}
} // namespace ui::external_app::metronome
extern "C" {
__attribute__((section(".external_app.app_metronome.application_information"), used)) application_information_t _application_information_metronome = {
/*.memory_location = */ (uint8_t*)0x00000000,
/*.externalAppEntry = */ ui::external_app::metronome::initialize_app,
/*.header_version = */ CURRENT_HEADER_VERSION,
/*.app_version = */ VERSION_MD5,
/*.app_name = */ "Metronome",
/*.bitmap_data = */ {
0x00,
0x00,
0xC0,
0x43,
0x20,
0x66,
0xA0,
0x34,
0x20,
0x18,
0xB0,
0x0C,
0x10,
0x04,
0x10,
0x06,
0x10,
0x0B,
0x98,
0x19,
0x08,
0x10,
0xF8,
0x1F,
0xF8,
0x1F,
0xF8,
0x1F,
0xF0,
0x0F,
0x00,
0x00,
},
/*.icon_color = */ ui::Color::cyan().v,
/*.menu_location = */ app_location_t::UTILITIES,
/*.desired_menu_position = */ -1,
/*.m4_app_tag = portapack::spi_flash::image_tag_none */ {'P', 'A', 'B', 'P'},
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
};
}
+177
View File
@@ -0,0 +1,177 @@
/*
* copyleft 2024 sommermorgentraum
*
* 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_metronome.hpp"
#include "baseband_api.hpp"
#include "audio.hpp"
#include "portapack.hpp"
using namespace portapack;
namespace ui::external_app::metronome {
MetronomeView::MetronomeView(NavigationView& nav)
: nav_{nav} {
baseband::run_prepared_image(portapack::memory::map::m4_code.base()); // proc_audio_beep baseband is external too
add_children({
&labels,
&field_volume,
&button_play_stop,
&field_rythm_unaccent_time,
&field_rythm_accent_time,
&field_accent_beep_tune,
&field_unaccent_beep_tune,
&field_beep_flash_duration,
&field_bpm,
&progressbar,
});
field_bpm.set_value(120);
field_rythm_accent_time.set_value(4);
field_rythm_unaccent_time.set_value(4);
field_accent_beep_tune.set_value(880);
field_unaccent_beep_tune.set_value(440);
field_beep_flash_duration.set_value(100);
button_play_stop.on_select = [this]() {
if (playing_) {
stop_play();
} else {
play();
}
};
field_volume.set_value(0); // seems that a change is required to force update, so setting to 0 first
field_volume.set_value(99);
audio::set_rate(audio::Rate::Hz_48000);
audio::output::start();
}
MetronomeView::~MetronomeView() {
should_exit = true;
if (thread) {
chThdWait(thread);
thread = nullptr;
}
receiver_model.disable();
baseband::shutdown();
audio::output::stop();
}
void MetronomeView::focus() {
field_bpm.focus();
}
void MetronomeView::stop_play() {
if (playing_) {
playing_ = false;
button_play_stop.set_bitmap(&bitmap_icon_replay);
baseband::request_beep_stop();
progressbar.set_value(0);
progressbar.set_style(Theme::getInstance()->fg_light);
}
}
void MetronomeView::play() {
if (!playing_) {
playing_ = true;
current_beat_ = 0;
button_play_stop.set_bitmap(&bitmap_icon_sleep);
if (!thread) {
thread = chThdCreateFromHeap(NULL, 1024, NORMALPRIO + 10, MetronomeView::static_fn, this);
}
}
}
void MetronomeView::beep_accent_beat() {
baseband::request_audio_beep(field_accent_beep_tune.value(), 48000, field_beep_flash_duration.value());
}
void MetronomeView::beep_unaccent_beat() {
baseband::request_audio_beep(field_unaccent_beep_tune.value(), 48000, field_beep_flash_duration.value());
}
// TODO: draw the beat
// void MetronomeView::paint(Painter& painter) {
// View::paint(painter);
// painter.fill_rectangle(
// {visual_x, visual_y, visual_width, visual_height},
// Theme::getInstance()->bg_darkest->background);
// if (playing_) {
// const bool is_accent_beat = (current_beat_ % field_rythm_accent_time.value()) == 0;
// const Color beat_color = is_accent_beat ?
// Theme::getInstance()->fg_red->foreground :
// Theme::getInstance()->fg_green->foreground;
// painter.fill_rectangle(
// {visual_x + visual_width/4,
// visual_y + visual_height/4,
// visual_width/2,
// visual_height/2},
// beat_color);
// }
// }
msg_t MetronomeView::static_fn(void* arg) {
auto obj = static_cast<MetronomeView*>(arg);
obj->run();
return 0;
}
void MetronomeView::run() {
while (!should_exit) {
if (!playing_) {
chThdSleepMilliseconds(100);
continue;
}
uint32_t base_interval = (60 * 1000) / field_bpm.value(); // quarter note as 1 beat
uint32_t beats_per_measure = field_rythm_unaccent_time.value(); // how many beates per bar
progressbar.set_max(beats_per_measure);
uint32_t beat_unit = field_rythm_accent_time.value(); // which note type (quarter, eighth, etc.) as 1 beat
uint32_t actual_interval = (base_interval * 4) / beat_unit; // e.g. when beat_unit==8 it's 1/2 of base_interval AKA eighths notes
uint32_t beat_in_measure = current_beat_ % beats_per_measure; // current beat in this bar (need to decide accent or unaccent)
progressbar.set_value(beat_in_measure + 1);
// accent beat is the first beat of this bar
if (beat_in_measure == 0) {
beep_accent_beat();
progressbar.set_style(Theme::getInstance()->fg_red);
} else {
beep_unaccent_beat();
progressbar.set_style(Theme::getInstance()->fg_green);
}
current_beat_++;
chThdSleepMilliseconds(actual_interval);
}
}
} // namespace ui::external_app::metronome
+126
View File
@@ -0,0 +1,126 @@
/*
* copyleft 2024 sommermorgentraum
*
* 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_METRONOME_H__
#define __UI_METRONOME_H__
#include "ui_navigation.hpp"
#include "ui_receiver.hpp"
#include "audio.hpp"
#include "ch.h"
namespace ui::external_app::metronome {
class MetronomeView : public View {
public:
MetronomeView(NavigationView& nav);
~MetronomeView();
MetronomeView(const MetronomeView& other) = delete;
MetronomeView& operator=(const MetronomeView& other) = delete;
void focus() override;
std::string title() const override { return "Metronome"; };
private:
NavigationView& nav_;
void beep_accent_beat(); // e.g. 3 of 3/4 beat
void beep_unaccent_beat(); // e.g. 4 of 3/4 beat
void stop_play();
void play();
// void paint(Painter& painter) override;
Thread* thread{nullptr};
bool should_exit{false};
static msg_t static_fn(void* arg);
void run();
bool playing_{false};
uint32_t current_beat_{0};
Labels labels{
{{0 * 8, 1 * 16}, "BPM:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 2 * 16}, "Accent Beep Tune:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 3 * 16}, "Unaccent Beep Tune:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 4 * 16}, "Rhythm:", Theme::getInstance()->fg_light->foreground},
{{(sizeof("Rhythm:") + 1) * 8 + 4 * 8, 4 * 16}, "/", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 5 * 16}, "Beep Flash Duration:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 6 * 16}, "Volume:", Theme::getInstance()->fg_light->foreground}};
NumberField field_bpm{
{(sizeof("BPM:") + 1) * 8, 1 * 16},
4,
{1, 1000},
1,
' '};
NumberField field_rythm_unaccent_time{// e.g. 3 in 3/4 beat
{(sizeof("Rhythm:") + 1) * 8, 4 * 16},
2,
{1, 99},
1,
' '};
NumberField field_rythm_accent_time{// e.g. 4 in 3/4 beat
{(sizeof("Rhythm:") + 1) * 8 + 5 * 8, 4 * 16},
2,
{1, 99},
1,
' '};
NumberField field_beep_flash_duration{
{(sizeof("Beep Flash Duration:") + 1) * 8, 5 * 16},
3,
{10, 999},
1,
' '};
NumberField field_accent_beep_tune{
{(sizeof("Accent Beep Tune:") + 1) * 8, 2 * 16},
5,
{380, 24000},
20,
' '};
NumberField field_unaccent_beep_tune{
{(sizeof("Unaccent Beep Tune:") + 1) * 8, 3 * 16},
5,
{380, 24000},
20,
' '};
AudioVolumeField field_volume{
{(sizeof("Volume:") + 1) * 8, 6 * 16}};
NewButton button_play_stop{
{0 * 16, 16 * 16, screen_width, screen_height - 16 * 16},
{},
&bitmap_icon_replay,
Theme::getInstance()->fg_red->foreground};
ProgressBar progressbar{
{0 * 16, 8 * 16, screen_width, screen_height - 14 * 16}};
};
} // namespace ui::external_app::metronome
#endif /*__UI_METRONOME_H__*/
+2 -3
View File
@@ -232,9 +232,8 @@ MorseView::MorseView(
set_foxhunt(foxhunt_code);
};
options_modulation.on_change = [this](size_t i, int32_t value) {
(void)i; // avoid unused warning
mode_cw = (bool)value;
options_modulation.on_change = [this](size_t, OptionsField::value_t v) {
mode_cw = (bool)v;
};
options_loop.on_change = [this](size_t i, uint32_t value) {
+7 -2
View File
@@ -41,6 +41,11 @@ using namespace morse;
namespace ui::external_app::morse_tx {
enum Modulation {
FM = 0,
CW = 1
};
class MorseView : public View {
public:
MorseView(NavigationView& nav);
@@ -137,8 +142,8 @@ class MorseView : public View {
OptionsField options_modulation{
{15 * 8, 10 * 8},
2,
{{"CW", true},
{"FM", false}}};
{{"CW", Modulation::CW},
{"FM", Modulation::FM}}};
OptionsField options_loop{
{9 * 8, 12 * 8},
+83
View File
@@ -0,0 +1,83 @@
/*
* Copyright (C) 2024 Bernd
*
* 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_tuner.hpp"
#include "ui_navigation.hpp"
#include "external_app.hpp"
namespace ui::external_app::tuner {
void initialize_app(ui::NavigationView& nav) {
nav.push<TunerView>();
}
} // namespace ui::external_app::tuner
extern "C" {
__attribute__((section(".external_app.app_tuner.application_information"), used)) application_information_t _application_information_tuner = {
/*.memory_location = */ (uint8_t*)0x00000000,
/*.externalAppEntry = */ ui::external_app::tuner::initialize_app,
/*.header_version = */ CURRENT_HEADER_VERSION,
/*.app_version = */ VERSION_MD5,
/*.app_name = */ "Tuner",
/*.bitmap_data = */ {
0x00,
0x00,
0x00,
0x00,
0x22,
0x44,
0x21,
0x84,
0x2D,
0xB4,
0x25,
0xA4,
0x25,
0xA4,
0x2D,
0xB4,
0x61,
0x86,
0xC2,
0x43,
0x80,
0x01,
0x80,
0x01,
0x80,
0x01,
0x80,
0x01,
0x00,
0x00,
0x00,
0x00,
},
/*.icon_color = */ ui::Color::cyan().v,
/*.menu_location = */ app_location_t::UTILITIES,
/*.desired_menu_position = */ -1,
/*.m4_app_tag = portapack::spi_flash::image_tag_none */ {'P', 'A', 'B', 'P'},
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
};
}
+256
View File
@@ -0,0 +1,256 @@
/*
* copyleft 2024 sommermorgentraum
*
* 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_tuner.hpp"
#include "baseband_api.hpp"
#include "audio.hpp"
#include "portapack.hpp"
using namespace portapack;
namespace ui::external_app::tuner {
TunerView::TunerView(NavigationView& nav)
: nav_{nav} {
baseband::run_prepared_image(portapack::memory::map::m4_code.base()); // proc_audio_beep baseband is external too
add_children({
&labels,
&field_volume,
&options_instrument,
&options_note,
&button_play_stop,
&text_note_frequency,
&text_note_octave_shift,
});
audio::set_rate(audio::Rate::Hz_24000);
options_instrument.on_change = [this](size_t, int32_t value) {
const Instrument* selected_instrument = nullptr;
switch (value) {
case 0:
selected_instrument = &guitar;
break;
case 1:
selected_instrument = &violin;
break;
case 2:
selected_instrument = &pitch_fork;
break;
}
if (selected_instrument) {
update_notes_for_instrument(*selected_instrument);
}
update_current_note();
};
options_note.on_change = [this](size_t, int32_t index) {
const Instrument* current_instrument = nullptr;
switch (options_instrument.selected_index_value()) {
case 0:
current_instrument = &guitar;
break;
case 1:
current_instrument = &violin;
break;
case 2:
current_instrument = &pitch_fork;
break;
}
if (current_instrument) {
auto it = current_instrument->notes.begin();
std::advance(it, index);
if (it != current_instrument->notes.end()) {
current_note_frequency = it->second.frequency;
current_note_sample_rate = it->second.sample_rate;
current_note_octave_shift = it->second.octave_shift;
}
}
update_current_note();
};
button_play_stop.on_select = [this]() {
if (current_note_playing) {
stop_play();
} else {
play_change_note();
}
};
options_instrument.set_selected_index(0);
update_notes_for_instrument(guitar);
update_current_note();
field_volume.set_value(0); // seems that a change is required to force update, so setting to 0 first
field_volume.set_value(99);
audio::set_rate(audio::Rate::Hz_24000);
audio::output::start();
}
TunerView::~TunerView() {
receiver_model.disable();
baseband::shutdown();
audio::output::stop();
}
void TunerView::focus() {
options_instrument.focus();
}
void TunerView::update_notes_for_instrument(const Instrument& instrument) {
std::vector<OptionsField::option_t> note_options;
size_t index = 0;
for (const auto& note_pair : instrument.notes) {
note_options.emplace_back(OptionsField::option_t{
note_pair.first,
index++});
}
options_note.set_options(note_options);
if (!note_options.empty()) {
options_note.set_selected_index(0);
}
}
void TunerView::update_current_note() {
const Instrument* current_instrument = nullptr;
switch (options_instrument.selected_index_value()) {
case 0:
current_instrument = &guitar;
break;
case 1:
current_instrument = &violin;
break;
case 2:
current_instrument = &pitch_fork;
break;
}
if (current_instrument) {
std::string note_name = options_note.selected_index_name();
// map
auto note_it = current_instrument->notes.find(note_name);
if (note_it != current_instrument->notes.end()) {
current_note_frequency = note_it->second.frequency;
current_note_sample_rate = note_it->second.sample_rate;
current_note_octave_shift = note_it->second.octave_shift;
text_note_frequency.set(std::to_string(current_note_frequency));
text_note_octave_shift.set(std::to_string(current_note_octave_shift));
set_dirty();
if (current_note_playing) {
play_change_note();
}
}
}
}
void TunerView::stop_play() {
baseband::request_beep_stop();
current_note_playing = false;
button_play_stop.set_bitmap(&bitmap_icon_replay);
}
void TunerView::play_change_note() {
if (current_note_playing) {
baseband::request_beep_stop();
audio::set_rate(current_note_sample_rate);
baseband::request_audio_beep(current_note_frequency, protected_sample_rate(current_note_sample_rate), 0);
} else {
audio::set_rate(current_note_sample_rate);
baseband::request_audio_beep(current_note_frequency, protected_sample_rate(current_note_sample_rate), 0);
}
current_note_playing = true;
button_play_stop.set_bitmap(&bitmap_icon_sleep);
set_dirty();
}
uint32_t TunerView::protected_sample_rate(audio::Rate r) {
switch (r) {
case audio::Rate::Hz_12000:
return 12000;
case audio::Rate::Hz_24000:
return 24000;
case audio::Rate::Hz_48000:
return 48000;
default:
return 24000;
}
}
void TunerView::paint(Painter& painter) {
View::paint(painter);
painter.fill_rectangle(
{screen_width / 4, 8 * 16, screen_width / 2, 6 * 16},
Theme::getInstance()->bg_darkest->background);
if (!current_note_playing) return;
painter.fill_rectangle(
{screen_width / 4, 10 * 16, 2, 2 * 16},
Theme::getInstance()->fg_light->foreground);
painter.fill_rectangle(
{screen_width / 4, 12 * 16, screen_width / 4 * 2 + 2, 2},
Theme::getInstance()->fg_light->foreground);
painter.fill_rectangle(
{(screen_width / 4) * 3, 10 * 16, 2, 2 * 16},
Theme::getInstance()->fg_light->foreground);
painter.draw_string(
{screen_width / 4 - 2 * 8, 8 * 16},
(current_note_octave_shift == 0) ? *Theme::getInstance()->bg_darkest : *Theme::getInstance()->fg_red,
std::to_string(current_note_frequency));
int16_t real_frequency = current_note_frequency;
if (current_note_octave_shift > 0) {
for (int i = 0; i < current_note_octave_shift; i++) {
real_frequency *= 2;
}
} else if (current_note_octave_shift < 0) {
for (int i = 0; i > current_note_octave_shift; i--) {
real_frequency /= 2;
}
}
painter.draw_string({(screen_width / 4) * 3 - 2 * 8, 8 * 16},
(current_note_octave_shift == 0) ? *Theme::getInstance()->bg_darkest : *Theme::getInstance()->fg_red,
std::to_string(real_frequency));
painter.draw_string({screen_width / 2 - 3 * 16, 13 * 16},
(current_note_octave_shift == 0) ? *Theme::getInstance()->bg_darkest : *Theme::getInstance()->fg_red,
std::to_string(current_note_octave_shift) + " * 8ev");
}
} // namespace ui::external_app::tuner
+145
View File
@@ -0,0 +1,145 @@
/*
* copyleft 2024 sommermorgentraum
*
* 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_TUNER_H__
#define __UI_TUNER_H__
#include "ui_navigation.hpp"
#include "ui_receiver.hpp"
#include "audio.hpp"
namespace ui::external_app::tuner {
struct Instrument {
std::string name;
struct NoteInfo {
uint16_t frequency;
audio::Rate sample_rate; // tune samplerate to allow for freqs
int8_t octave_shift;
// PP hardware can't handle some extremely low/high frequencies, this indicates how much the freq that were played is shifted up/down,
// for example, if shift is -2 and note is A3, it means the real string should play A5 but PP's speaker plays A3
};
std::map<std::string, NoteInfo> notes; // this is for fast looking : O(log(n)) , usage `auto note = guitar.notes["A4"];`
};
class TunerView : public View {
public:
TunerView(NavigationView& nav);
~TunerView();
TunerView(const TunerView& other) = delete;
TunerView& operator=(const TunerView& other) = delete;
void focus() override;
void update_audio_beep();
std::string title() const override { return "Tuner"; };
private:
NavigationView& nav_;
bool beep{false};
void update_notes_for_instrument(const Instrument& instrument);
void play_change_note();
void stop_play();
void update_current_note();
uint32_t protected_sample_rate(audio::Rate r);
void paint(Painter& painter) override;
uint16_t current_note_frequency{440};
audio::Rate current_note_sample_rate{audio::Rate::Hz_12000};
int8_t current_note_octave_shift{0};
bool current_note_playing{false};
Labels labels{
{{0 * 8, 1 * 16}, "Instrument:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 2 * 16}, "Note:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 3 * 16}, "Note Frequency:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 4 * 16}, "Note Octave Shift:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 5 * 16}, "Volume:", Theme::getInstance()->fg_light->foreground}};
Text text_note_frequency{
{(sizeof("Note Frequency:") + 1) * 8, 3 * 16, screen_width - (sizeof("Note Frequency:") + 1) * 8, 16},
"",
};
Text text_note_octave_shift{
{(sizeof("Note Octave Shift:") + 1) * 8, 4 * 16, screen_width - (sizeof("Note Octave Shift:") + 1) * 8, 16},
"",
};
AudioVolumeField field_volume{
{(sizeof("Volume:") + 1) * 8, 5 * 16}};
// TODO: load list runtime
OptionsField options_instrument{
{(sizeof("Instrument:") + 1) * 8, 1 * 16},
screen_width - (sizeof("Instrument:") + 1) * 8,
{{"Guitar", 0},
{"Violin", 1},
{"Pitch Fork", 2}}};
OptionsField options_note{
{(sizeof("Note:") + 1) * 8, 2 * 16},
screen_width - (sizeof("Note:") + 1) * 8,
{}};
NewButton button_play_stop{
{0 * 8, 16 * 16, screen_width, screen_height - 16 * 16},
{},
&bitmap_icon_replay,
Theme::getInstance()->fg_red->foreground};
// TODO: load from file DB
const Instrument guitar = {
.name = "Folk Guitar",
.notes = {
{"E2", {165, audio::Rate::Hz_12000, -1}}, // actual: E2, PP speaker: E3
{"A2", {110, audio::Rate::Hz_12000, 0}},
{"D3", {147, audio::Rate::Hz_12000, 0}},
{"G3", {196, audio::Rate::Hz_24000, 0}},
{"B3", {247, audio::Rate::Hz_24000, 0}},
{"E4", {330, audio::Rate::Hz_24000, 0}}}};
const Instrument violin = {
.name = "Violin 440 Standard, 12ET",
.notes = {
{"G3", {196, audio::Rate::Hz_12000, 0}},
{"D4", {294, audio::Rate::Hz_24000, 0}},
{"A4", {440, audio::Rate::Hz_48000, 0}},
{"E5", {659, audio::Rate::Hz_48000, 0}}}};
const Instrument pitch_fork = {// freq copied from flipper app
.name = "Pitch Fork",
.notes = {
{"12ET A4", {440, audio::Rate::Hz_48000, 0}},
{"Sarti's A4", {436, audio::Rate::Hz_48000, 0}},
{"1858 A4", {435, audio::Rate::Hz_48000, 0}},
{"Verdi's A4", {432, audio::Rate::Hz_48000, 0}}}};
std::map<std::string, Instrument> instruments{
{"Guitar", guitar},
{"Violin", violin},
{"Pitch Fork", pitch_fork}};
};
} // namespace ui::external_app::tuner
#endif /*__UI_TUNER_H__*/
+2 -2
View File
@@ -798,7 +798,7 @@ void add_external_items(NavigationView& nav, app_location_t location, BtnGridVie
bool verify_sdcard_format() {
FATFS* fs = &sd_card::fs;
return (fs->fs_type == FS_FAT32) || !(sd_card::status() == sd_card::Status::Mounted);
return (fs->fs_type == FS_FAT32 || fs->fs_type == FS_EXFAT) || !(sd_card::status() == sd_card::Status::Mounted);
/* ^ to satisfy those users that not use an sd*/
}
@@ -875,7 +875,7 @@ SystemMenuView::SystemMenuView(NavigationView& nav)
void SystemMenuView::on_populate() {
if (!verify_sdcard_format()) {
add_item({"SDCard Error", Theme::getInstance()->error_dark->foreground, nullptr, [this]() {
nav_.display_modal("Error", "SD Card is not FAT32,\nformat to FAT32 on PC");
nav_.display_modal("Error", "SD Card is not exFAT/FAT32,\nformat to exFAT or FAT32 on PC");
}});
}
add_apps(nav_, *this, HOME);
+1 -1
View File
@@ -473,7 +473,7 @@ std::string SDCardDebugView::fetch_sdcard_format() {
case FS_FAT32:
resault = "FAT32";
break;
case FS_EXFAT: // TODO: a bug from filesystem can't detect exfat, issue not from here
case FS_EXFAT:
resault = "ExFAT";
break;
default:
+4 -1
View File
@@ -271,6 +271,7 @@ macro(DeclareTargets chunk_tag name)
project("baseband_${name}")
include(${RULESPATH}/rules.cmake)
set_source_files_properties(${MODE_CPPSRC} PROPERTIES COMPILE_FLAGS "${MODE_FLAGS}")
add_executable(${PROJECT_NAME}.elf $<TARGET_OBJECTS:baseband_shared> ${MODE_CPPSRC} ${HALSRC} ${PLATFORMSRC})
set_target_properties(${PROJECT_NAME}.elf PROPERTIES LINK_DEPENDS ${LDSCRIPT})
add_definitions(${DEFS})
@@ -313,6 +314,7 @@ macro(DeclareTargets chunk_tag name)
endmacro()
set(add_to_firmware TRUE)
set(MODE_FLAGS "-O3")
### ADS-B RX
@@ -498,6 +500,7 @@ set(MODE_CPPSRC
)
DeclareTargets(PWTH weather)
set(MODE_FLAGS "-Os")
### Flash Utility
@@ -559,7 +562,7 @@ DeclareTargets(PUSB sd_over_usb)
### Place external app and disabled images below so they don't get added to the firmware
set(add_to_firmware FALSE)
set(MODE_FLAGS "-O3")
### ACARS RX
+2 -2
View File
@@ -90,7 +90,7 @@
/ 950 - Traditional Chinese (DBCS)
*/
#define _USE_LFN 2
#define _USE_LFN 3
#define _MAX_LFN 255
/* The _USE_LFN switches the support of long file name (LFN).
/
@@ -189,7 +189,7 @@
/ Instead of private sector buffer eliminated from the file object, common sector
/ buffer in the file system object (FATFS) is used for the file data transfer. */
#define _FS_EXFAT 0
#define _FS_EXFAT 1
/* This option switches support of exFAT file system. (0:Disable or 1:Enable)
/ When enable exFAT, also LFN needs to be enabled. (_USE_LFN >= 1)
/ Note that enabling exFAT discards ANSI C (C89) compatibility. */
Binary file not shown.

After

Width:  |  Height:  |  Size: 886 B

+1 -1
Submodule hackrf updated: 94dfae3f46...65bf67f9b5