Code cleanup and refactoring of voice prompts code

This commit is contained in:
Silvano Seva 2022-09-25 19:55:23 +02:00
parent c7c9c3afc5
commit 5fb11322ac
4 changed files with 319 additions and 264 deletions

View File

@ -149,7 +149,8 @@ void vp_announceBank(const uint16_t bank, const vpQueueFlags_t flags);
/**
*
*/
void vp_announceM17Info(const channel_t* channel, bool isEditing, const vpQueueFlags_t flags);
void vp_announceM17Info(const channel_t* channel, bool isEditing,
const vpQueueFlags_t flags);
/**
*
@ -195,8 +196,7 @@ void vp_announceSettingsOnOffToggle(const char* const* stringTableStringPtr,
* This is called to speak generic settings int values.
*/
void vp_announceSettingsInt(const char* const* stringTableStringPtr,
const vpQueueFlags_t flags,
int val);
const vpQueueFlags_t flags, int val);
/**
* This function is called from ui_updateFSM to speak informational screens.
@ -218,6 +218,10 @@ void vp_announceDisplayTimer();
*
*/
vpQueueFlags_t vp_getVoiceLevelQueueFlags();
/**
*
*/
void vp_playMenuBeepIfNeeded(bool firstItem);
#endif // VOICE_PROMPT_UTILS_H

View File

@ -226,7 +226,7 @@ typedef enum
vpMedium,
vpHigh
}
VoicePromptVerbosity_T;
vpVerbosity_t;
typedef enum
{
@ -258,13 +258,6 @@ typedef enum
}
vpGPSInfoFlags_t;
typedef struct
{
uint16_t freq;
uint16_t duration;
}
beep_data_t;
/**
* Initialise the voice prompt system and load vp table of contents.
@ -342,9 +335,11 @@ bool vp_sequenceNotEmpty();
* play a beep at a given frequency for a given duration.
*/
void vp_beep(uint16_t freq, uint16_t duration);
/**
* play a series of beeps at a given frequency for a given duration.
* Play a series of beeps at a given frequency for a given duration.
* Array is freq, duration, ... 0, 0 to terminate series.
*/
void vp_beepSeries(const uint16_t* beepSeries);
#endif

View File

@ -209,12 +209,13 @@ void vp_announceChannelSummary(const channel_t* channel,
{
localFlags |= vpqIncludeDescriptions;
}
if (infoFlags&vpSplashInfo)
if ((infoFlags & vpSplashInfo) != 0)
vp_queueStringTableEntry(&currentLanguage->openRTX);
// If VFO mode, announce VFO.
// channelNumber will be 0 if called from VFO mode.
if (infoFlags&vpChannelNameOrVFO)
if ((infoFlags & vpChannelNameOrVFO) != 0)
{
if (channelNumber == 0)
{
@ -226,18 +227,22 @@ void vp_announceChannelSummary(const channel_t* channel,
}
addSilenceIfNeeded(localFlags);
}
if (infoFlags&vpFrequencies)
if ((infoFlags & vpFrequencies) != 0)
vp_announceFrequencies(channel->rx_frequency, channel->tx_frequency,
localFlags);
if (infoFlags&vpRadioMode)
if ((infoFlags & vpRadioMode) != 0)
{
vp_announceRadioMode(channel->mode, localFlags);
addSilenceIfNeeded(localFlags);
}
if (infoFlags&vpModeSpecificInfo)
if ((infoFlags & vpModeSpecificInfo) != 0)
{
if (channel->mode == OPMODE_FM)
switch(channel->mode)
{
case OPMODE_FM:
{
vp_announceBandwidth(channel->bandwidth, localFlags);
addSilenceIfNeeded(localFlags);
@ -245,35 +250,43 @@ void vp_announceChannelSummary(const channel_t* channel,
if (channel->fm.rxToneEn || channel->fm.txToneEn)
{
vp_announceCTCSS(channel->fm.rxToneEn, channel->fm.rxTone,
channel->fm.txToneEn, channel->fm.txTone, localFlags);
channel->fm.txToneEn, channel->fm.txTone,
localFlags);
}
}
else if (channel->mode == OPMODE_M17)
{
break;
case OPMODE_M17:
vp_announceM17Info(channel, false, localFlags);
}
else if (channel->mode == OPMODE_DMR)
break;
case OPMODE_DMR:
{
vp_announceContactWithIndex(channel->dmr.contact_index, localFlags);
vp_announceContactWithIndex(channel->dmr.contact_index,
localFlags);
// Force announcement of the words timeslot and colorcode to avoid
// ambiguity.
vp_announceTimeslot(channel->dmr.dmr_timeslot,
(localFlags | vpqIncludeDescriptions));
vp_announceColorCode(channel->dmr.rxColorCode, channel->dmr.txColorCode,
vp_announceColorCode(channel->dmr.rxColorCode,
channel->dmr.txColorCode,
(localFlags | vpqIncludeDescriptions));
}
break;
}
addSilenceIfNeeded(localFlags);
}
if (infoFlags&vpPower)
if ((infoFlags & vpPower) != 0)
{
float power = dBmToWatt(channel->power);
vp_anouncePower(power, localFlags);
addSilenceIfNeeded(localFlags);
}
if ((infoFlags&vpBankNameOrAllChannels) &&(channelNumber > 0)) // i.e. not called from VFO.
if (((infoFlags & vpBankNameOrAllChannels) != 0) && (channelNumber > 0)) // i.e. not called from VFO.
{
vp_announceBank(bank, localFlags);
}
@ -352,6 +365,7 @@ void vp_announceCTCSS(const bool rxToneEnabled, const uint8_t rxTone,
{
if (flags & vpqIncludeDescriptions)
vp_queuePrompt(PROMPT_TONE);
vp_queueStringTableEntry(&currentLanguage->off);
playIfNeeded(flags);
return;
@ -504,7 +518,8 @@ void vp_announceBank(const uint16_t bank, const vpQueueFlags_t flags)
playIfNeeded(flags);
}
void vp_announceM17Info(const channel_t* channel, bool isEditing, const vpQueueFlags_t flags)
void vp_announceM17Info(const channel_t* channel, bool isEditing,
const vpQueueFlags_t flags)
{
clearCurrPromptIfNeeded(flags);
@ -632,19 +647,27 @@ void vp_announceGPSInfo(vpGPSInfoFlags_t gpsInfoFlags)
vp_queueString(buffer, vpAnnounceCommonSymbols);
vp_queuePrompt(PROMPT_DEGREES);
}
if (state.gps_data.tmg_true < (45+margin) || state.gps_data.tmg_true > (315-margin))
if ((state.gps_data.tmg_true < (45 + margin)) ||
(state.gps_data.tmg_true > (315 - margin)))
{
vp_queuePrompt(PROMPT_NORTH);
}
if (state.gps_data.tmg_true > (45-margin) && state.gps_data.tmg_true < (135+margin))
if ((state.gps_data.tmg_true > (45 - margin)) &&
(state.gps_data.tmg_true < (135 + margin)))
{
vp_queuePrompt(PROMPT_EAST);
}
if (state.gps_data.tmg_true > (135-margin) && state.gps_data.tmg_true < (225+margin))
if ((state.gps_data.tmg_true > (135 - margin)) &&
(state.gps_data.tmg_true < (225 + margin)))
{
vp_queuePrompt(PROMPT_SOUTH);
}
if (state.gps_data.tmg_true > (225-margin) && state.gps_data.tmg_true < (315+margin))
if ((state.gps_data.tmg_true > (225 - margin)) &&
(state.gps_data.tmg_true < (315 + margin)))
{
vp_queuePrompt(PROMPT_WEST);
}
@ -652,16 +675,16 @@ void vp_announceGPSInfo(vpGPSInfoFlags_t gpsInfoFlags)
addSilenceIfNeeded(flags);
}
if (gpsInfoFlags & vpGPSSpeed)
if ((gpsInfoFlags & vpGPSSpeed) != 0)
{
// speed/altitude:
snprintf(buffer, 16, "%4.1fkm/h", state.gps_data.speed);
vp_queuePrompt(PROMPT_SPEED);
vp_queueString(buffer,
vpAnnounceCommonSymbols|vpAnnounceLessCommonSymbols);
vp_queueString(buffer, vpAnnounceCommonSymbols |
vpAnnounceLessCommonSymbols);
}
if (gpsInfoFlags & vpGPSAltitude)
if ((gpsInfoFlags & vpGPSAltitude) != 0)
{
vp_queuePrompt(PROMPT_ALTITUDE);
@ -670,7 +693,7 @@ void vp_announceGPSInfo(vpGPSInfoFlags_t gpsInfoFlags)
addSilenceIfNeeded(flags);
}
if (gpsInfoFlags & vpGPSLatitude)
if ((gpsInfoFlags & vpGPSLatitude) != 0)
{
// lat/long
snprintf(buffer, 16, "%8.6f", state.gps_data.latitude);
@ -680,7 +703,7 @@ void vp_announceGPSInfo(vpGPSInfoFlags_t gpsInfoFlags)
vp_queuePrompt(PROMPT_NORTH);
}
if (gpsInfoFlags & vpGPSLongitude)
if ((gpsInfoFlags & vpGPSLongitude) != 0)
{
float longitude = state.gps_data.longitude;
voicePrompt_t direction = (longitude < 0) ? PROMPT_WEST : PROMPT_EAST;
@ -694,11 +717,12 @@ void vp_announceGPSInfo(vpGPSInfoFlags_t gpsInfoFlags)
addSilenceIfNeeded(flags);
}
if (gpsInfoFlags & vpGPSSatCount)
if ((gpsInfoFlags & vpGPSSatCount) != 0)
{
vp_queuePrompt(PROMPT_SATELLITES);
vp_queueInteger(state.gps_data.satellites_in_view);
}
vp_play();
}
#endif // GPS_PRESENT
@ -793,6 +817,7 @@ void vp_announceSettingsVoiceLevel(const vpQueueFlags_t flags)
vp_queuePrompt(PROMPT_VOICE_NAME);
vp_queueStringTableEntry(&currentLanguage->level);
}
vp_queueInteger(state.settings.vpLevel-vpBeep);
break;
}
@ -829,35 +854,43 @@ void vp_announceSettingsInt(const char* const* stringTableStringPtr,
void vp_announceScreen(uint8_t ui_screen)
{
const vpSummaryInfoFlags_t infoFlags = vpChannelNameOrVFO|vpFrequencies |
vpRadioMode;
const vpSummaryInfoFlags_t infoFlags = vpChannelNameOrVFO
| vpFrequencies
| vpRadioMode;
switch (ui_screen)
{
case MAIN_VFO:
vp_announceChannelSummary(&state.channel, 0, state.bank, infoFlags);
break;
case MAIN_MEM:
vp_announceChannelSummary(&state.channel, state.channel_index+1,
state.bank, infoFlags);
break;
#ifdef GPS_PRESENT
case MENU_GPS:
vp_announceGPSInfo(vpGPSAll);
break;
#endif // GPS_PRESENT
#endif
case MENU_BACKUP:
vp_announceBackupScreen();
break;
case MENU_RESTORE:
vp_announceRestoreScreen();
break;
case MENU_ABOUT:
vp_announceAboutScreen();
break;
case SETTINGS_TIMEDATE:
vp_announceSettingsTimeDate();
break;
case SETTINGS_M17:
vp_announceBuffer(&currentLanguage->callsign,
false, true, state.settings.callsign);
@ -884,8 +917,10 @@ void vp_announceBuffer(const char* const* stringTableStringPtr,
vpFlags_t flags = vpAnnounceCommonSymbols;
// add edit mode flags to adjust what is spoken.
// extra symbols not relevant when entering callsign.
if (editMode && !callsign)
flags |= vpAnnounceLessCommonSymbols | vpAnnounceSpace | vpAnnounceASCIIValueForUnknownChars;
if ((editMode == true) && (callsign == false))
flags |= vpAnnounceLessCommonSymbols
| vpAnnounceSpace
| vpAnnounceASCIIValueForUnknownChars;
vp_queueString(buffer, flags);
@ -898,8 +933,9 @@ void vp_announceDisplayTimer()
vp_flush();
if (!isPlaying)
if (isPlaying == false)
vp_queueStringTableEntry(&currentLanguage->timer);
uint8_t seconds = 0;
uint8_t minutes = 0;
@ -908,6 +944,7 @@ void vp_announceDisplayTimer()
case TIMER_OFF:
seconds = 0;
break;
case TIMER_5S:
case TIMER_10S:
case TIMER_15S:
@ -916,6 +953,7 @@ void vp_announceDisplayTimer()
case TIMER_30S:
seconds = state.settings.display_timer * 5;
break;
case TIMER_1M:
case TIMER_2M:
case TIMER_3M:
@ -923,16 +961,19 @@ void vp_announceDisplayTimer()
case TIMER_5M:
minutes = (state.settings.display_timer - (TIMER_1M - 1));
break;
case TIMER_15M:
case TIMER_30M:
case TIMER_45M:
minutes = 15 * (state.settings.display_timer - (TIMER_15M - 1));
break;
case TIMER_1H:
minutes = 60;
break;
}
if (seconds==0 && minutes==0)
if ((seconds == 0) && (minutes == 0))
{
vp_queueStringTableEntry(&currentLanguage->off);
}
@ -994,4 +1035,3 @@ void vp_playMenuBeepIfNeeded(bool firstItem)
else
vp_beep(BEEP_MENU_ITEM, SHORT_BEEP);
}

View File

@ -33,18 +33,12 @@
static const uint32_t VOICE_PROMPTS_DATA_MAGIC = 0x5056; //'VP'
static const uint32_t VOICE_PROMPTS_DATA_VERSION = 0x1000; // v1000 OpenRTX
static uint16_t currentBeepDuration=0;
// max buff size for beep series (melody).
#define beepSeriesMax 256
static beep_data_t beepSeriesBuffer[beepSeriesMax];
static uint8_t beepSeriesIndex = 0;
static bool delayBeepUntilTick=false;
const uint16_t BOOT_MELODY[] = {400, 3, 600, 3, 800, 3, 0, 0};
#define VOICE_PROMPTS_TOC_SIZE 350
#define CODEC2_HEADER_SIZE 7
#define VP_SEQUENCE_BUF_SIZE 128
#define BEEP_SEQ_BUF_SIZE 256
typedef struct
{
@ -71,6 +65,14 @@ typedef struct
}
vpSequence_t;
typedef struct
{
uint16_t freq;
uint16_t duration;
}
beepData_t;
static const userDictEntry_t userDictionary[] =
{
{"hotspot", PROMPT_CUSTOM1}, // Hotspot
@ -99,6 +101,11 @@ static uint32_t tableOfContents[VOICE_PROMPTS_TOC_SIZE];
static bool vpDataLoaded = false;
static bool voicePromptActive = false;
static beepData_t beepSeriesBuffer[BEEP_SEQ_BUF_SIZE];
static uint16_t currentBeepDuration = 0;
static uint8_t beepSeriesIndex = 0;
static bool delayBeepUntilTick = false;
#ifdef VP_USE_FILESYSTEM
static FILE *vpFile = NULL;
#else
@ -250,6 +257,61 @@ static bool GetSymbolVPIfItShouldBeAnnounced(char symbol,
(!commonSymbol && announceLessCommonSymbols));
}
/**
* \internal
* Stop an ongoing beep, if present, and clear all the beep management
* variables.
*/
static void beep_flush()
{
if (currentBeepDuration > 0)
platform_beepStop();
memset(beepSeriesBuffer, 0, sizeof(beepSeriesBuffer));
currentBeepDuration = 0;
beepSeriesIndex = 0;
}
/**
* \internal
* Function managing beep update.
*/
static bool beep_tick()
{
if (currentBeepDuration > 0)
{
if (delayBeepUntilTick)
{
platform_beepStart(beepSeriesBuffer[beepSeriesIndex].freq);
delayBeepUntilTick=false;
}
currentBeepDuration--;
if (currentBeepDuration == 0)
{
platform_beepStop();
// see if there are any more in the series to play.
if ((beepSeriesBuffer[beepSeriesIndex+1].freq != 0) &&
(beepSeriesBuffer[beepSeriesIndex+1].duration != 0))
{
beepSeriesIndex++;
currentBeepDuration = beepSeriesBuffer[beepSeriesIndex].duration;
platform_beepStart(beepSeriesBuffer[beepSeriesIndex].freq);
}
else
{
// Clear all variables for beep management
beep_flush();
}
}
return true;
}
return false;
}
void vp_init()
{
@ -316,16 +378,6 @@ void vp_terminate()
#endif
}
static void beep_flush()
{
if (currentBeepDuration > 0)
platform_beepStop();
currentBeepDuration = 0;
memset(beepSeriesBuffer, 0, sizeof(beepSeriesBuffer));
beepSeriesIndex=0;
}
void vp_stop()
{
voicePromptActive = false;
@ -439,6 +491,7 @@ void vp_queueInteger(const int value)
char buf[12] = {0}; // min: -2147483648, max: 2147483647
if (value < 0)
vp_queuePrompt(PROMPT_MINUS);
snprintf(buf, 12, "%d", value);
vp_queueString(buf, 0);
}
@ -481,36 +534,6 @@ void vp_play()
audio_enableAmp();
}
static bool beep_tick()
{
if (currentBeepDuration > 0)
{
if (delayBeepUntilTick)
{
platform_beepStart(beepSeriesBuffer[beepSeriesIndex].freq);
delayBeepUntilTick=false;
}
currentBeepDuration--;
if (currentBeepDuration==0)
{
platform_beepStop();
// see if there are any more in the series to play.
if (beepSeriesBuffer[beepSeriesIndex+1].freq && beepSeriesBuffer[beepSeriesIndex+1].duration)
{
beepSeriesIndex++;
currentBeepDuration=beepSeriesBuffer[beepSeriesIndex].duration;
platform_beepStart(beepSeriesBuffer[beepSeriesIndex].freq);
}
else
{
beep_flush();
}
}
return true;
}
return false;
}
void vp_tick()
{
if (beep_tick())
@ -573,55 +596,48 @@ bool vp_sequenceNotEmpty()
return (vpCurrentSequence.length > 0);
}
// Duration seems to be in tenths of a second.
void vp_beep(uint16_t freq, uint16_t duration)
{
if (state.settings.vpLevel < vpBeep)
return;
// Do not play a new one if one is playing.
if (currentBeepDuration)
if (currentBeepDuration != 0)
return;
// avoid extra long beeps!
if (duration > 20)
duration = 20;
currentBeepDuration=duration;
audio_enableAmp();
beepSeriesBuffer[0].freq = freq;
beepSeriesBuffer[0].duration = duration;
beepSeriesBuffer[1].freq = 0;
beepSeriesBuffer[1].duration = 0;
currentBeepDuration = duration;
beepSeriesIndex = 0;
audio_enableAmp();
platform_beepStart(freq);
// See BeepTick for termination.
}
/*
We delay the playing of the melody until the first time vp_tick is called
because there is a sleep on the splash screen which would make the first note
play extra long.
*/
void vp_beepSeries(const uint16_t* beepSeries)
{
if (state.settings.vpLevel < vpBeep)
return;
if (currentBeepDuration)
if (currentBeepDuration != 0)
return;
audio_enableAmp();
if (!beepSeries) return;
if (beepSeries == NULL)
return;
memcpy(beepSeriesBuffer, beepSeries, beepSeriesMax*sizeof(beep_data_t));
memcpy(beepSeriesBuffer, beepSeries, BEEP_SEQ_BUF_SIZE*sizeof(beepData_t));
// Always ensure that the array is terminated!
beepSeriesBuffer[beepSeriesMax-1].freq = 0;
beepSeriesBuffer[beepSeriesMax-1].duration = 0;
beepSeriesBuffer[BEEP_SEQ_BUF_SIZE-1].freq = 0;
beepSeriesBuffer[BEEP_SEQ_BUF_SIZE-1].duration = 0;
beepSeriesIndex=0;
currentBeepDuration = beepSeriesBuffer[0].duration;
beepSeriesIndex = 0;
delayBeepUntilTick = true;
}