[FL-3040] Audio support for SubGhz (#2131)
* Furi_hal_speaker: multiple resource usage * Furi_hal_speaker: fix multiple resource usage * Furi_hal_speaker: fix music_player_worker * Furi_hal_speaker: fix mutex release queue handling * SubGhz: add furi_hal_subghz_set_debug_pin * SubGhz: add sound SubGhz Read, SubGhz Read RAW * furi_hal_speaker: add __attribute__((warn_unused_result)) for furi_hal_speaker_acquire() * Furi_hal_speaker: fix review comments * SubGhz: cleanup naming and locking timings * SubGhz,FuriHal: fix speaker deinit logic and subghz speaker release sequence * FuriHal: crash on speaker acquire/release from IRQ * Furi, FuriHal: FURI_WARN_UNUSED and documentation update * Bump api symbols version: fix broken speaker Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
This commit is contained in:
@@ -28,6 +28,13 @@ typedef enum {
|
||||
SubGhzHopperStateRSSITimeOut,
|
||||
} SubGhzHopperState;
|
||||
|
||||
/** SubGhzSpeakerState state */
|
||||
typedef enum {
|
||||
SubGhzSpeakerStateDisable,
|
||||
SubGhzSpeakerStateShutdown,
|
||||
SubGhzSpeakerStateEnable,
|
||||
} SubGhzSpeakerState;
|
||||
|
||||
/** SubGhzRxKeyState state */
|
||||
typedef enum {
|
||||
SubGhzRxKeyStateIDLE,
|
||||
|
@@ -259,6 +259,7 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) {
|
||||
case SubGhzCustomEventViewReadRAWSendStop:
|
||||
subghz->state_notifications = SubGhzNotificationStateIDLE;
|
||||
if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) {
|
||||
subghz_speaker_unmute(subghz);
|
||||
subghz_tx_stop(subghz);
|
||||
subghz_sleep(subghz);
|
||||
}
|
||||
@@ -376,10 +377,12 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) {
|
||||
subghz_read_raw_add_data_rssi(subghz->subghz_read_raw, rssi, false);
|
||||
subghz_protocol_raw_save_to_file_pause(
|
||||
(SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result, true);
|
||||
subghz_speaker_mute(subghz);
|
||||
} else {
|
||||
subghz_read_raw_add_data_rssi(subghz->subghz_read_raw, rssi, true);
|
||||
subghz_protocol_raw_save_to_file_pause(
|
||||
(SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result, false);
|
||||
subghz_speaker_unmute(subghz);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -5,6 +5,7 @@ enum SubGhzSettingIndex {
|
||||
SubGhzSettingIndexFrequency,
|
||||
SubGhzSettingIndexHopping,
|
||||
SubGhzSettingIndexModulation,
|
||||
SubGhzSettingIndexSound,
|
||||
SubGhzSettingIndexLock,
|
||||
SubGhzSettingIndexRAWThesholdRSSI,
|
||||
};
|
||||
@@ -48,6 +49,16 @@ const uint32_t hopping_value[HOPPING_COUNT] = {
|
||||
SubGhzHopperStateRunnig,
|
||||
};
|
||||
|
||||
#define SPEAKER_COUNT 2
|
||||
const char* const speaker_text[SPEAKER_COUNT] = {
|
||||
"OFF",
|
||||
"ON",
|
||||
};
|
||||
const uint32_t speaker_value[SPEAKER_COUNT] = {
|
||||
SubGhzSpeakerStateShutdown,
|
||||
SubGhzSpeakerStateEnable,
|
||||
};
|
||||
|
||||
uint8_t subghz_scene_receiver_config_next_frequency(const uint32_t value, void* context) {
|
||||
furi_assert(context);
|
||||
SubGhz* subghz = context;
|
||||
@@ -167,6 +178,14 @@ static void subghz_scene_receiver_config_set_hopping_running(VariableItem* item)
|
||||
subghz->txrx->hopper_state = hopping_value[index];
|
||||
}
|
||||
|
||||
static void subghz_scene_receiver_config_set_speaker(VariableItem* item) {
|
||||
SubGhz* subghz = variable_item_get_context(item);
|
||||
uint8_t index = variable_item_get_current_value_index(item);
|
||||
|
||||
variable_item_set_current_value_text(item, speaker_text[index]);
|
||||
subghz->txrx->speaker_state = speaker_value[index];
|
||||
}
|
||||
|
||||
static void subghz_scene_receiver_config_set_raw_threshold_rssi(VariableItem* item) {
|
||||
SubGhz* subghz = variable_item_get_context(item);
|
||||
uint8_t index = variable_item_get_current_value_index(item);
|
||||
@@ -235,6 +254,16 @@ void subghz_scene_receiver_config_on_enter(void* context) {
|
||||
variable_item_set_current_value_text(
|
||||
item, subghz_setting_get_preset_name(subghz->setting, value_index));
|
||||
|
||||
item = variable_item_list_add(
|
||||
subghz->variable_item_list,
|
||||
"Sound:",
|
||||
SPEAKER_COUNT,
|
||||
subghz_scene_receiver_config_set_speaker,
|
||||
subghz);
|
||||
value_index = value_index_uint32(subghz->txrx->speaker_state, speaker_value, SPEAKER_COUNT);
|
||||
variable_item_set_current_value_index(item, value_index);
|
||||
variable_item_set_current_value_text(item, speaker_text[value_index]);
|
||||
|
||||
if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) !=
|
||||
SubGhzCustomEventManagerSet) {
|
||||
variable_item_list_add(subghz->variable_item_list, "Lock Keyboard", 1, NULL, NULL);
|
||||
|
@@ -177,6 +177,7 @@ SubGhz* subghz_alloc() {
|
||||
|
||||
subghz->txrx->txrx_state = SubGhzTxRxStateSleep;
|
||||
subghz->txrx->hopper_state = SubGhzHopperStateOFF;
|
||||
subghz->txrx->speaker_state = SubGhzSpeakerStateDisable;
|
||||
subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE;
|
||||
subghz->txrx->raw_threshold_rssi = SUBGHZ_RAW_TRESHOLD_MIN;
|
||||
subghz->txrx->history = subghz_history_alloc();
|
||||
|
@@ -86,6 +86,7 @@ uint32_t subghz_rx(SubGhz* subghz, uint32_t frequency) {
|
||||
uint32_t value = furi_hal_subghz_set_frequency_and_path(frequency);
|
||||
furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow);
|
||||
furi_hal_subghz_flush_rx();
|
||||
subghz_speaker_on(subghz);
|
||||
furi_hal_subghz_rx();
|
||||
|
||||
furi_hal_subghz_start_async_rx(subghz_worker_rx_callback, subghz->txrx->worker);
|
||||
@@ -104,6 +105,7 @@ static bool subghz_tx(SubGhz* subghz, uint32_t frequency) {
|
||||
furi_hal_subghz_set_frequency_and_path(frequency);
|
||||
furi_hal_gpio_write(&gpio_cc1101_g0, false);
|
||||
furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow);
|
||||
subghz_speaker_on(subghz);
|
||||
bool ret = furi_hal_subghz_tx();
|
||||
subghz->txrx->txrx_state = SubGhzTxRxStateTx;
|
||||
return ret;
|
||||
@@ -119,11 +121,13 @@ void subghz_idle(SubGhz* subghz) {
|
||||
void subghz_rx_end(SubGhz* subghz) {
|
||||
furi_assert(subghz);
|
||||
furi_assert(subghz->txrx->txrx_state == SubGhzTxRxStateRx);
|
||||
|
||||
if(subghz_worker_is_running(subghz->txrx->worker)) {
|
||||
subghz_worker_stop(subghz->txrx->worker);
|
||||
furi_hal_subghz_stop_async_rx();
|
||||
}
|
||||
furi_hal_subghz_idle();
|
||||
subghz_speaker_off(subghz);
|
||||
subghz->txrx->txrx_state = SubGhzTxRxStateIDLE;
|
||||
}
|
||||
|
||||
@@ -212,6 +216,7 @@ void subghz_tx_stop(SubGhz* subghz) {
|
||||
subghz, subghz->txrx->fff_data, furi_string_get_cstr(subghz->file_path));
|
||||
}
|
||||
subghz_idle(subghz);
|
||||
subghz_speaker_off(subghz);
|
||||
notification_message(subghz->notifications, &sequence_reset_red);
|
||||
}
|
||||
|
||||
@@ -585,3 +590,40 @@ void subghz_hopper_update(SubGhz* subghz) {
|
||||
subghz_rx(subghz, subghz->txrx->preset->frequency);
|
||||
}
|
||||
}
|
||||
|
||||
void subghz_speaker_on(SubGhz* subghz) {
|
||||
if(subghz->txrx->speaker_state == SubGhzSpeakerStateEnable) {
|
||||
if(furi_hal_speaker_acquire(30)) {
|
||||
furi_hal_subghz_set_async_mirror_pin(&gpio_speaker);
|
||||
} else {
|
||||
subghz->txrx->speaker_state = SubGhzSpeakerStateDisable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void subghz_speaker_off(SubGhz* subghz) {
|
||||
if(subghz->txrx->speaker_state != SubGhzSpeakerStateDisable) {
|
||||
if(furi_hal_speaker_is_mine()) {
|
||||
furi_hal_subghz_set_async_mirror_pin(NULL);
|
||||
furi_hal_speaker_release();
|
||||
if(subghz->txrx->speaker_state == SubGhzSpeakerStateShutdown)
|
||||
subghz->txrx->speaker_state = SubGhzSpeakerStateDisable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void subghz_speaker_mute(SubGhz* subghz) {
|
||||
if(subghz->txrx->speaker_state == SubGhzSpeakerStateEnable) {
|
||||
if(furi_hal_speaker_is_mine()) {
|
||||
furi_hal_subghz_set_async_mirror_pin(NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void subghz_speaker_unmute(SubGhz* subghz) {
|
||||
if(subghz->txrx->speaker_state == SubGhzSpeakerStateEnable) {
|
||||
if(furi_hal_speaker_is_mine()) {
|
||||
furi_hal_subghz_set_async_mirror_pin(&gpio_speaker);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -53,6 +53,7 @@ struct SubGhzTxRx {
|
||||
uint16_t idx_menu_chosen;
|
||||
SubGhzTxRxState txrx_state;
|
||||
SubGhzHopperState hopper_state;
|
||||
SubGhzSpeakerState speaker_state;
|
||||
uint8_t hopper_timeout;
|
||||
uint8_t hopper_idx_frequency;
|
||||
SubGhzRxKeyState rx_key_state;
|
||||
@@ -131,3 +132,7 @@ void subghz_file_name_clear(SubGhz* subghz);
|
||||
bool subghz_path_is_file(FuriString* path);
|
||||
uint32_t subghz_random_serial(void);
|
||||
void subghz_hopper_update(SubGhz* subghz);
|
||||
void subghz_speaker_on(SubGhz* subghz);
|
||||
void subghz_speaker_off(SubGhz* subghz);
|
||||
void subghz_speaker_mute(SubGhz* subghz);
|
||||
void subghz_speaker_unmute(SubGhz* subghz);
|
||||
|
@@ -47,47 +47,51 @@ static int32_t music_player_worker_thread_callback(void* context) {
|
||||
|
||||
NoteBlockArray_it_t it;
|
||||
NoteBlockArray_it(it, instance->notes);
|
||||
if(furi_hal_speaker_acquire(1000)) {
|
||||
while(instance->should_work) {
|
||||
if(NoteBlockArray_end_p(it)) {
|
||||
NoteBlockArray_it(it, instance->notes);
|
||||
furi_delay_ms(10);
|
||||
} else {
|
||||
NoteBlock* note_block = NoteBlockArray_ref(it);
|
||||
|
||||
while(instance->should_work) {
|
||||
if(NoteBlockArray_end_p(it)) {
|
||||
NoteBlockArray_it(it, instance->notes);
|
||||
furi_delay_ms(10);
|
||||
} else {
|
||||
NoteBlock* note_block = NoteBlockArray_ref(it);
|
||||
float note_from_a4 = (float)note_block->semitone - NOTE_C4_SEMITONE;
|
||||
float frequency = NOTE_C4 * powf(TWO_POW_TWELTH_ROOT, note_from_a4);
|
||||
float duration = 60.0 * furi_kernel_get_tick_frequency() * 4 / instance->bpm /
|
||||
note_block->duration;
|
||||
uint32_t dots = note_block->dots;
|
||||
while(dots > 0) {
|
||||
duration += duration / 2;
|
||||
dots--;
|
||||
}
|
||||
uint32_t next_tick = furi_get_tick() + duration;
|
||||
float volume = instance->volume;
|
||||
|
||||
float note_from_a4 = (float)note_block->semitone - NOTE_C4_SEMITONE;
|
||||
float frequency = NOTE_C4 * powf(TWO_POW_TWELTH_ROOT, note_from_a4);
|
||||
float duration =
|
||||
60.0 * furi_kernel_get_tick_frequency() * 4 / instance->bpm / note_block->duration;
|
||||
uint32_t dots = note_block->dots;
|
||||
while(dots > 0) {
|
||||
duration += duration / 2;
|
||||
dots--;
|
||||
if(instance->callback) {
|
||||
instance->callback(
|
||||
note_block->semitone,
|
||||
note_block->dots,
|
||||
note_block->duration,
|
||||
0.0,
|
||||
instance->callback_context);
|
||||
}
|
||||
|
||||
furi_hal_speaker_stop();
|
||||
furi_hal_speaker_start(frequency, volume);
|
||||
while(instance->should_work && furi_get_tick() < next_tick) {
|
||||
volume *= 0.9945679;
|
||||
furi_hal_speaker_set_volume(volume);
|
||||
furi_delay_ms(2);
|
||||
}
|
||||
NoteBlockArray_next(it);
|
||||
}
|
||||
uint32_t next_tick = furi_get_tick() + duration;
|
||||
float volume = instance->volume;
|
||||
|
||||
if(instance->callback) {
|
||||
instance->callback(
|
||||
note_block->semitone,
|
||||
note_block->dots,
|
||||
note_block->duration,
|
||||
0.0,
|
||||
instance->callback_context);
|
||||
}
|
||||
|
||||
furi_hal_speaker_stop();
|
||||
furi_hal_speaker_start(frequency, volume);
|
||||
while(instance->should_work && furi_get_tick() < next_tick) {
|
||||
volume *= 0.9945679;
|
||||
furi_hal_speaker_set_volume(volume);
|
||||
furi_delay_ms(2);
|
||||
}
|
||||
NoteBlockArray_next(it);
|
||||
}
|
||||
}
|
||||
|
||||
furi_hal_speaker_stop();
|
||||
furi_hal_speaker_stop();
|
||||
furi_hal_speaker_release();
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "Speaker system is busy with another process.");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@@ -150,11 +150,16 @@ void notification_vibro_off() {
|
||||
}
|
||||
|
||||
void notification_sound_on(float freq, float volume) {
|
||||
furi_hal_speaker_start(freq, volume);
|
||||
if(furi_hal_speaker_acquire(30)) {
|
||||
furi_hal_speaker_start(freq, volume);
|
||||
}
|
||||
}
|
||||
|
||||
void notification_sound_off() {
|
||||
furi_hal_speaker_stop();
|
||||
if(furi_hal_speaker_is_mine()) {
|
||||
furi_hal_speaker_stop();
|
||||
furi_hal_speaker_release();
|
||||
}
|
||||
}
|
||||
|
||||
// display timer
|
||||
|
Reference in New Issue
Block a user