Refactored voicePrompts.h, aligned function names of voice prompt API to OpenRTX coding style

md1702
Silvano Seva 2022-08-16 10:39:48 +02:00
rodzic 5840f459fa
commit df5341e103
8 zmienionych plików z 333 dodań i 307 usunięć

Wyświetl plik

@ -16,25 +16,20 @@
* You should have received a copy of the GNU General Public License *
* along with this program; if not, see <http://www.gnu.org/licenses/> *
***************************************************************************/
#ifndef voice_prompts_h_included
#define voice_prompts_h_included
#ifndef VOICEPROMPTS_H
#define VOICEPROMPTS_H
#include <datatypes.h>
#include <stdbool.h>
// Voice prompts are encoded using the codec2 file format used by ffmpeg
#define CODEC2_HEADER_SIZE 7
/*
Please note, these prompts represent spoken words or phrases which are not in
the UI string table, for example letters of the alphabet, digits, and
descriptive words not displayed in the UI.
The voice prompt data file stores these first, then after the data for these
prompts, the data for the indexed string table phrases.
*/
/* Please note! this enum must match the order of prompts defined in the
wordlist.csv file in the voicePrompts generator project.
*/
/**
* List of voice prompts for spoken words or phrases which are not in the UI
* string table. The voice prompt data file stores these first, then after the
* data for these prompts, the data for the indexed string table phrases.
*
* WARNING: this enum must match the order of prompts defined in the
* wordlist.csv file in the voicePrompts generator project.
*/
typedef enum
{
PROMPT_SILENCE, //
@ -181,19 +176,12 @@ typedef enum
PROMPT_CUSTOM9, // parrot
PROMPT_CUSTOM10, // unused
NUM_VOICE_PROMPTS,
} voicePrompt_t;
}
voicePrompt_t;
// PROMPT_VOICE_NAME is always the very last prompt after the indexed prompts
// from the strings table.
#define PROMPT_VOICE_NAME \
(NUM_VOICE_PROMPTS + (sizeof(stringsTable_t) / sizeof(char*)))
/*
These flags govern how vpQueueString operates.
For example, when editing, it is desireable to hear spaces, capitals and
extended symbols.
When just arrowing through menus, spaces, extended symbols etc should not be
announced.
*/
/**
* Flags controlling how vp_queueString operates.
*/
typedef enum
{
vpAnnounceCaps = 0x01,
@ -203,30 +191,27 @@ typedef enum
vpAnnounceLessCommonSymbols = 0x10,
vpAnnounceASCIIValueForUnknownChars = 0x20,
vpAnnouncePhoneticRendering = 0x40,
} VoicePromptFlags_T;
/*
These queuing flags determine if speech is interrupted, played
immediately, whether prompts are queued for values, etc.
They are necessary because for example if you call the announceXX functions
consecutively, it is only desireable to initially stop speech in
progress and only play after the last prompt is queued.
If however calling an announceXX function in isolation, normally any prompt in
progress should be interrupted and play should be called immediately.
At Voice level 1, changing channels in memory mode or frequencies in VFO mode
is indicated by a beep however if F1 is pressed, we will still say the current
channel name or frequency. This is accomplished by queueing but not playing a
prompt.
*/
}
VoicePromptFlags_T;
/**
* Queuing flags determining if speech is interrupted, played immediately,
* whether prompts are queued for values, etc.
*/
typedef enum
{
vpqDefault = 0,
vpqInit = 0x01, // stop any voice prompts already in progress.
vpqPlayImmediately = 0x02, // call play after queue at all levels.
vpqDefault = 0,
vpqInit = 0x01, // stop any voice prompts already in progress.
vpqPlayImmediately = 0x02, // call play after queue at all levels.
vpqPlayImmediatelyAtMediumOrHigher = 0x04,
vpqIncludeDescriptions = 0x08,
vpqAddSeparatingSilence = 0x10
} VoicePromptQueueFlags_T;
}
VoicePromptQueueFlags_T;
/**
* Voice prompt verbosity levels.
*/
typedef enum
{
vpNone = 0,
@ -234,43 +219,74 @@ typedef enum
vpLow,
vpMedium,
vpHigh
} VoicePromptVerbosity_T;
}
VoicePromptVerbosity_T;
typedef struct
{
const char* userWord;
const voicePrompt_t vp;
} userDictEntry;
/**
* Initialise the voice prompt system and load vp table of contents.
*/
void vp_init();
extern bool vpDataIsLoaded;
extern const uint32_t VOICE_PROMPTS_FLASH_HEADER_ADDRESS;
extern VoicePromptVerbosity_T vpLevel;
// Loads just the TOC from Flash and stores in RAM for fast access.
void vpCacheInit(void);
// Call before building the prompt sequence to clear prompt in progress.
void vpInit(void);
// This function appends an individual prompt item to the prompt queue.
// This can be a single letter, number, or a phrase.
void vpQueuePrompt(uint16_t prompt);
// This function appends the spelling of a complete string to the queue.
// It is used to pronounce strings for which we do not have a recorded voice
// prompt.
void vpQueueString(char* promptString, VoicePromptFlags_T flags);
// This function appends a signed integer to the queue.
void vpQueueInteger(int32_t value);
// This function appends a text string from the current language to the queue.
// e.g. currentLanguage->off
// These are recorded prompts which correspond to the strings in the strings
// table.
void vpQueueStringTableEntry(const char* const*);
/**
* Terminate the currently ongoing prompt and shutdown the voice prompt system.
*/
void vp_terminate();
void vpPlay(void); // Starts prompt playback
void vpTick(); // called to process vp data being decoded.
extern bool vpIsPlaying(void);
bool vpHasDataToPlay(void);
void vpTerminate(void);
bool vpCheckHeader(uint32_t* bufferAddress);
int vp_open(char *vp_name);
void vp_close();
/**
* Clear the currently in-progress prompt, to be called before building a new
* voice prompt sequence.
*/
void vp_clearCurrPrompt();
/**
* Append an individual prompt item to the prompt queue.
*
* @param prompt: voice prompt ID.
*/
void vp_queuePrompt(const uint16_t prompt);
/**
* Append the spelling of a complete string to the queue.
*
* @param promptString: string to be spelled.
* @param flags: control flags.
*/
void vp_queueString(char* promptString, VoicePromptFlags_T flags);
/**
* Append a signed integer to the queue.
*
* @param value: value to be appended.
*/
void vp_queueInteger(const int32_t value);
/**
* Append a text string from the current language to the queue.
*/
void vp_queueStringTableEntry(const char* const* stringTableStringPtr);
/**
* Start prompt playback.
*/
void vp_play();
/**
* Function handling vp data decoding, to be called periodically.
*/
void vp_tick();
/**
* Check if a voice prompt is being played.
*
* @return true if a voice prompt is being played.
*/
bool vp_isPlaying();
/**
* Check if the voice prompt sequence is empty.
*
* @return true if the voice prompt sequence is empty.
*/
bool vp_sequenceNotEmpty();
#endif

Wyświetl plik

@ -44,7 +44,7 @@ void openrtx_init()
gfx_init(); // Initialize display and graphics driver
kbd_init(); // Initialize keyboard driver
ui_init(); // Initialize user interface
vpCacheInit(); // Checks to see if voice prompts are loaded and initializes them
vp_init(); // Initialize voice prompts
#ifdef SCREEN_CONTRAST
display_setContrast(state.settings.contrast);
#endif

Wyświetl plik

@ -75,7 +75,7 @@ void *ui_threadFunc(void *arg)
ui_saveState(); // Save local state copy
pthread_mutex_unlock(&state_mutex); // Unlock r/w access to radio state
vpTick(); // continue playing voice prompts in progress if any.
vp_tick(); // continue playing voice prompts in progress if any.
// If synchronization needed take mutex and update RTX configuration
if(sync_rtx)

Wyświetl plik

@ -31,26 +31,26 @@
#include "interfaces/cps_io.h"
static void vpInitIfNeeded(VoicePromptQueueFlags_T flags)
static void vp_clearCurrPromptIfNeeded(VoicePromptQueueFlags_T flags)
{
if (flags & vpqInit) vpInit();
if (flags & vpqInit) vp_clearCurrPrompt();
}
static void vpPlayIfNeeded(VoicePromptQueueFlags_T flags)
static void vp_playIfNeeded(VoicePromptQueueFlags_T flags)
{
uint8_t vpLevel = state.settings.vpLevel;
if ((flags & vpqPlayImmediately) ||
((flags & vpqPlayImmediatelyAtMediumOrHigher) && (vpLevel >= vpMedium)))
vpPlay();
vp_play();
}
static void addSilenceIfNeeded(VoicePromptQueueFlags_T flags)
{
if ((flags & vpqAddSeparatingSilence) == 0) return;
vpQueuePrompt(PROMPT_SILENCE);
vpQueuePrompt(PROMPT_SILENCE);
vp_queuePrompt(PROMPT_SILENCE);
vp_queuePrompt(PROMPT_SILENCE);
}
static void removeUnnecessaryZerosFromVoicePrompts(char* str)
@ -69,32 +69,32 @@ static void removeUnnecessaryZerosFromVoicePrompts(char* str)
void announceVFO()
{
vpInit();
vp_clearCurrPrompt();
vpQueuePrompt(PROMPT_VFO);
vp_queuePrompt(PROMPT_VFO);
vpPlay();
vp_play();
}
void announceChannelName(channel_t* channel, uint16_t channelIndex,
VoicePromptQueueFlags_T flags)
{
vpInitIfNeeded(flags);
vp_clearCurrPromptIfNeeded(flags);
if (flags & vpqIncludeDescriptions)
{
vpQueuePrompt(PROMPT_CHANNEL);
vp_queuePrompt(PROMPT_CHANNEL);
}
vpQueueInteger(channelIndex);
vp_queueInteger(channelIndex);
// Only queue the name if it is not the same as the raw number.
// Otherwise the radio will say channel 1 1 for channel 1.
char numAsStr[16] = "\0";
snprintf(numAsStr, 16, "%d", channelIndex);
if (strcmp(numAsStr, channel->name) != 0)
vpQueueString(channel->name, vpAnnounceCommonSymbols);
vp_queueString(channel->name, vpAnnounceCommonSymbols);
vpPlayIfNeeded(flags);
vp_playIfNeeded(flags);
}
void vpQueueFrequency(freq_t freq)
@ -106,77 +106,77 @@ void vpQueueFrequency(freq_t freq)
snprintf(buffer, 16, "%d.%05d", mhz, khz);
removeUnnecessaryZerosFromVoicePrompts(buffer);
vpQueueString(buffer, vpAnnounceCommonSymbols);
vp_queueString(buffer, vpAnnounceCommonSymbols);
vpQueuePrompt(PROMPT_MEGAHERTZ);
vp_queuePrompt(PROMPT_MEGAHERTZ);
}
void announceFrequencies(freq_t rx, freq_t tx, VoicePromptQueueFlags_T flags)
{
vpInitIfNeeded(flags);
vp_clearCurrPromptIfNeeded(flags);
// If rx and tx frequencies differ, announce both, otherwise just one
if (rx == tx)
vpQueueFrequency(rx);
else
{
vpQueuePrompt(PROMPT_RECEIVE);
vp_queuePrompt(PROMPT_RECEIVE);
vpQueueFrequency(rx);
vpQueuePrompt(PROMPT_TRANSMIT);
vp_queuePrompt(PROMPT_TRANSMIT);
vpQueueFrequency(tx);
}
vpPlayIfNeeded(flags);
vp_playIfNeeded(flags);
}
void announceRadioMode(uint8_t mode, VoicePromptQueueFlags_T flags)
{
vpInitIfNeeded(flags);
vp_clearCurrPromptIfNeeded(flags);
if (flags & vpqIncludeDescriptions) vpQueuePrompt(PROMPT_MODE);
if (flags & vpqIncludeDescriptions) vp_queuePrompt(PROMPT_MODE);
switch (mode)
{
case OPMODE_DMR:
vpQueueStringTableEntry(&currentLanguage->dmr);
vp_queueStringTableEntry(&currentLanguage->dmr);
break;
case OPMODE_FM:
vpQueueStringTableEntry(&currentLanguage->fm);
vp_queueStringTableEntry(&currentLanguage->fm);
break;
case OPMODE_M17:
vpQueueStringTableEntry(&currentLanguage->m17);
vp_queueStringTableEntry(&currentLanguage->m17);
break;
}
vpPlayIfNeeded(flags);
vp_playIfNeeded(flags);
}
void announceBandwidth(uint8_t bandwidth, VoicePromptQueueFlags_T flags)
{
if (bandwidth > BW_25) bandwidth = BW_25; // Should probably never happen!
vpInitIfNeeded(flags);
vp_clearCurrPromptIfNeeded(flags);
if (flags & vpqIncludeDescriptions) vpQueuePrompt(PROMPT_BANDWIDTH);
if (flags & vpqIncludeDescriptions) vp_queuePrompt(PROMPT_BANDWIDTH);
char* bandwidths[] = {"12.5", "20", "25"};
vpQueueString(bandwidths[bandwidth], vpAnnounceCommonSymbols);
vpQueuePrompt(PROMPT_KILOHERTZ);
vp_queueString(bandwidths[bandwidth], vpAnnounceCommonSymbols);
vp_queuePrompt(PROMPT_KILOHERTZ);
vpPlayIfNeeded(flags);
vp_playIfNeeded(flags);
}
void anouncePower(float power, VoicePromptQueueFlags_T flags)
{
vpInitIfNeeded(flags);
vp_clearCurrPromptIfNeeded(flags);
char buffer[16] = "\0";
if (flags & vpqIncludeDescriptions) vpQueuePrompt(PROMPT_POWER);
if (flags & vpqIncludeDescriptions) vp_queuePrompt(PROMPT_POWER);
snprintf(buffer, 16, "%1.1f", power);
vpQueueString(buffer, vpAnnounceCommonSymbols);
vpQueuePrompt(PROMPT_WATTS);
vp_queueString(buffer, vpAnnounceCommonSymbols);
vp_queuePrompt(PROMPT_WATTS);
vpPlayIfNeeded(flags);
vp_playIfNeeded(flags);
}
void announceChannelSummary(channel_t* channel, uint16_t channelIndex,
@ -184,7 +184,7 @@ void announceChannelSummary(channel_t* channel, uint16_t channelIndex,
{
if (!channel) return;
vpInit();
vp_clearCurrPrompt();
VoicePromptQueueFlags_T localFlags = vpqAddSeparatingSilence;
// Force on the descriptions for level 3.
@ -192,7 +192,7 @@ void announceChannelSummary(channel_t* channel, uint16_t channelIndex,
// If VFO mode, announce VFO.
// channelIndex will be 0 if called from VFO mode.
if (channelIndex == 0)
vpQueuePrompt(PROMPT_VFO);
vp_queuePrompt(PROMPT_VFO);
else
announceChannelName(channel, channelIndex, localFlags);
announceFrequencies(channel->rx_frequency, channel->tx_frequency,
@ -233,7 +233,7 @@ void announceChannelSummary(channel_t* channel, uint16_t channelIndex,
if (channelIndex > 0) // i.e. not called from VFO.
announceBank(bank, localFlags);
vpPlay();
vp_play();
}
void AnnounceInputChar(char ch)
@ -241,72 +241,72 @@ void AnnounceInputChar(char ch)
char buf[2] = "\0";
buf[0] = ch;
vpInit();
vp_clearCurrPrompt();
uint8_t flags = vpAnnounceCaps | vpAnnounceSpace | vpAnnounceCommonSymbols |
vpAnnounceLessCommonSymbols;
vpQueueString(buf, flags);
vp_queueString(buf, flags);
vpPlay();
vp_play();
}
void announceInputReceiveOrTransmit(bool tx, VoicePromptQueueFlags_T flags)
{
vpInitIfNeeded(flags);
vp_clearCurrPromptIfNeeded(flags);
if (tx)
vpQueuePrompt(PROMPT_TRANSMIT);
vp_queuePrompt(PROMPT_TRANSMIT);
else
vpQueuePrompt(PROMPT_RECEIVE);
vp_queuePrompt(PROMPT_RECEIVE);
vpPlayIfNeeded(flags);
vp_playIfNeeded(flags);
}
void ReplayLastPrompt()
{
if (vpIsPlaying())
vpTerminate();
if (vp_isPlaying())
vp_terminate();
else
vpPlay();
vp_play();
}
void announceError(VoicePromptQueueFlags_T flags)
{
vpInitIfNeeded(flags);
vp_clearCurrPromptIfNeeded(flags);
vpQueueStringTableEntry(&currentLanguage->error);
vp_queueStringTableEntry(&currentLanguage->error);
vpPlayIfNeeded(flags);
vp_playIfNeeded(flags);
}
void announceText(char* text, VoicePromptQueueFlags_T flags)
{
if (!text || !*text) return;
vpInitIfNeeded(flags);
vp_clearCurrPromptIfNeeded(flags);
// See if we have a prompt for this string.
int offset = GetEnglishStringTableOffset(text);
if (offset != -1)
vpQueueStringTableEntry(
vp_queueStringTableEntry(
(const char* const*)(&currentLanguage->languageName + offset));
else // Just spell it out
vpQueueString(text, vpAnnounceCommonSymbols);
vp_queueString(text, vpAnnounceCommonSymbols);
vpPlayIfNeeded(flags);
vp_playIfNeeded(flags);
}
void announceCTCSS(bool rxToneEnabled, uint8_t rxTone, bool txToneEnabled,
uint8_t txTone, VoicePromptQueueFlags_T flags)
{
vpInitIfNeeded(flags);
vp_clearCurrPromptIfNeeded(flags);
if (!rxToneEnabled && !txToneEnabled)
{
vpQueuePrompt(PROMPT_TONE);
vpQueueStringTableEntry(&currentLanguage->off);
vpPlayIfNeeded(flags);
vp_queuePrompt(PROMPT_TONE);
vp_queueStringTableEntry(&currentLanguage->off);
vp_playIfNeeded(flags);
return;
}
@ -315,68 +315,68 @@ void announceCTCSS(bool rxToneEnabled, uint8_t rxTone, bool txToneEnabled,
// If the rx and tx tones are the same and both are enabled, just say Tone.
if ((rxToneEnabled && txToneEnabled) && (rxTone == txTone))
{
vpQueuePrompt(PROMPT_TONE);
vp_queuePrompt(PROMPT_TONE);
snprintf(buffer, 16, "%3.1f", ctcss_tone[rxTone] / 10.0f);
vpQueueString(buffer, vpqDefault);
vpQueuePrompt(PROMPT_HERTZ);
vpPlayIfNeeded(flags);
vp_queueString(buffer, vpqDefault);
vp_queuePrompt(PROMPT_HERTZ);
vp_playIfNeeded(flags);
return;
}
// Speak the individual rx and tx tones.
if (rxToneEnabled)
{
vpQueuePrompt(PROMPT_RECEIVE);
vpQueuePrompt(PROMPT_TONE);
vp_queuePrompt(PROMPT_RECEIVE);
vp_queuePrompt(PROMPT_TONE);
snprintf(buffer, 16, "%3.1f", ctcss_tone[rxTone] / 10.0f);
vpQueueString(buffer, vpqDefault);
vpQueuePrompt(PROMPT_HERTZ);
vp_queueString(buffer, vpqDefault);
vp_queuePrompt(PROMPT_HERTZ);
}
if (txToneEnabled)
{
vpQueuePrompt(PROMPT_TRANSMIT);
vpQueuePrompt(PROMPT_TONE);
vp_queuePrompt(PROMPT_TRANSMIT);
vp_queuePrompt(PROMPT_TONE);
snprintf(buffer, 16, "%3.1f", ctcss_tone[txTone] / 10.0f);
vpQueueString(buffer, vpqDefault);
vpQueuePrompt(PROMPT_HERTZ);
vp_queueString(buffer, vpqDefault);
vp_queuePrompt(PROMPT_HERTZ);
}
vpPlayIfNeeded(flags);
vp_playIfNeeded(flags);
}
void announceBrightness(uint8_t brightness, VoicePromptQueueFlags_T flags)
{
vpInitIfNeeded(flags);
vp_clearCurrPromptIfNeeded(flags);
if (flags & vpqIncludeDescriptions)
vpQueueStringTableEntry(&currentLanguage->brightness);
vp_queueStringTableEntry(&currentLanguage->brightness);
vpQueueInteger(brightness);
vp_queueInteger(brightness);
vpPlayIfNeeded(flags);
vp_playIfNeeded(flags);
}
void announceSquelch(uint8_t squelch, VoicePromptQueueFlags_T flags)
{
vpInitIfNeeded(flags);
vp_clearCurrPromptIfNeeded(flags);
if (flags & vpqIncludeDescriptions) vpQueuePrompt(PROMPT_SQUELCH);
if (flags & vpqIncludeDescriptions) vp_queuePrompt(PROMPT_SQUELCH);
vpQueueInteger(squelch);
vp_queueInteger(squelch);
vpPlayIfNeeded(flags);
vp_playIfNeeded(flags);
}
void announceContact(contact_t* contact, VoicePromptQueueFlags_T flags)
{
if (!contact) return;
vpInitIfNeeded(flags);
vp_clearCurrPromptIfNeeded(flags);
if (flags & vpqIncludeDescriptions) vpQueuePrompt(PROMPT_CONTACT);
if (flags & vpqIncludeDescriptions) vp_queuePrompt(PROMPT_CONTACT);
if (contact->name[0]) vpQueueString(contact->name, vpAnnounceCommonSymbols);
if (contact->name[0]) vp_queueString(contact->name, vpAnnounceCommonSymbols);
vpPlayIfNeeded(flags);
vp_playIfNeeded(flags);
}
void announceContactWithIndex(uint16_t index, VoicePromptQueueFlags_T flags)
@ -392,69 +392,69 @@ void announceContactWithIndex(uint16_t index, VoicePromptQueueFlags_T flags)
void announceTimeslot(uint8_t timeslot, VoicePromptQueueFlags_T flags)
{
vpInitIfNeeded(flags);
vp_clearCurrPromptIfNeeded(flags);
if (flags & vpqIncludeDescriptions) vpQueuePrompt(PROMPT_TIMESLOT);
if (flags & vpqIncludeDescriptions) vp_queuePrompt(PROMPT_TIMESLOT);
vpQueueInteger(timeslot);
vp_queueInteger(timeslot);
vpPlayIfNeeded(flags);
vp_playIfNeeded(flags);
}
void announceColorCode(uint8_t rxColorCode, uint8_t txColorCode,
VoicePromptQueueFlags_T flags)
{
vpInitIfNeeded(flags);
vp_clearCurrPromptIfNeeded(flags);
if (flags & vpqIncludeDescriptions) vpQueuePrompt(PROMPT_COLORCODE);
if (flags & vpqIncludeDescriptions) vp_queuePrompt(PROMPT_COLORCODE);
if (rxColorCode == txColorCode)
{
vpQueueInteger(rxColorCode);
vp_queueInteger(rxColorCode);
}
else
{
vpQueuePrompt(PROMPT_RECEIVE);
vpQueueInteger(rxColorCode);
vpQueuePrompt(PROMPT_TRANSMIT);
vpQueueInteger(txColorCode);
vp_queuePrompt(PROMPT_RECEIVE);
vp_queueInteger(rxColorCode);
vp_queuePrompt(PROMPT_TRANSMIT);
vp_queueInteger(txColorCode);
}
vpPlayIfNeeded(flags);
vp_playIfNeeded(flags);
}
void announceBank(uint16_t bank, VoicePromptQueueFlags_T flags)
{
vpInitIfNeeded(flags);
vp_clearCurrPromptIfNeeded(flags);
if (flags & vpqIncludeDescriptions)
vpQueueStringTableEntry(&currentLanguage->banks);
vp_queueStringTableEntry(&currentLanguage->banks);
if (state.bank_enabled)
{
bankHdr_t bank_hdr = {0};
cps_readBankHeader(&bank_hdr, bank);
vpQueueString(bank_hdr.name, vpAnnounceCommonSymbols);
vp_queueString(bank_hdr.name, vpAnnounceCommonSymbols);
}
else
vpQueueStringTableEntry(&currentLanguage->allChannels);
vp_queueStringTableEntry(&currentLanguage->allChannels);
vpPlayIfNeeded(flags);
vp_playIfNeeded(flags);
}
void announceM17Info(channel_t* channel, VoicePromptQueueFlags_T flags)
{
if (!channel) return;
vpInitIfNeeded(flags);
vp_clearCurrPromptIfNeeded(flags);
if (state.m17_data.dst_addr[0])
{
if (flags & vpqIncludeDescriptions) vpQueuePrompt(PROMPT_DEST_ID);
vpQueueString(state.m17_data.dst_addr, vpAnnounceCommonSymbols);
if (flags & vpqIncludeDescriptions) vp_queuePrompt(PROMPT_DEST_ID);
vp_queueString(state.m17_data.dst_addr, vpAnnounceCommonSymbols);
}
else if (channel->m17.contact_index)
announceContactWithIndex(channel->m17.contact_index, flags);
vpPlayIfNeeded(flags);
vp_playIfNeeded(flags);
}
#ifdef GPS_PRESENT
@ -462,33 +462,33 @@ void announceGPSInfo()
{
if (!state.settings.gps_enabled) return;
vpInit();
vp_clearCurrPrompt();
VoicePromptQueueFlags_T flags =
vpqIncludeDescriptions | vpqAddSeparatingSilence;
vpQueueStringTableEntry(&currentLanguage->gps);
vp_queueStringTableEntry(&currentLanguage->gps);
switch (state.gps_data.fix_quality)
{
case 0:
vpQueueStringTableEntry(&currentLanguage->noFix);
vp_queueStringTableEntry(&currentLanguage->noFix);
break;
case 1:
vpQueueString("SPS", vpAnnounceCommonSymbols);
vp_queueString("SPS", vpAnnounceCommonSymbols);
break;
case 2:
vpQueueString("DGPS", vpAnnounceCommonSymbols);
vp_queueString("DGPS", vpAnnounceCommonSymbols);
break;
case 3:
vpQueueString("PPS", vpAnnounceCommonSymbols);
vp_queueString("PPS", vpAnnounceCommonSymbols);
break;
case 6:
vpQueueStringTableEntry(&currentLanguage->fixLost);
vp_queueStringTableEntry(&currentLanguage->fixLost);
break;
default:
vpQueueStringTableEntry(&currentLanguage->error);
vp_queueStringTableEntry(&currentLanguage->error);
vpPlay();
vp_play();
return;
}
@ -497,113 +497,113 @@ void announceGPSInfo()
switch (state.gps_data.fix_type)
{
case 2:
vpQueueString("2D", vpAnnounceCommonSymbols);
vp_queueString("2D", vpAnnounceCommonSymbols);
break;
case 3:
vpQueueString("3D", vpAnnounceCommonSymbols);
vp_queueString("3D", vpAnnounceCommonSymbols);
break;
}
addSilenceIfNeeded(flags);
// lat/long
char buffer[16] = "\0";
vpQueuePrompt(PROMPT_LATITUDE);
vp_queuePrompt(PROMPT_LATITUDE);
snprintf(buffer, 16, "%8.6f", state.gps_data.latitude);
vpQueueString(buffer, vpAnnounceCommonSymbols);
vpQueuePrompt(PROMPT_NORTH);
vp_queueString(buffer, vpAnnounceCommonSymbols);
vp_queuePrompt(PROMPT_NORTH);
float longitude = state.gps_data.longitude;
voicePrompt_t direction = (longitude < 0) ? PROMPT_WEST : PROMPT_EAST;
longitude = (longitude < 0) ? -longitude : longitude;
snprintf(buffer, 16, "%8.6f", longitude);
vpQueuePrompt(PROMPT_LONGITUDE);
vpQueueString(buffer, vpAnnounceCommonSymbols);
vpQueuePrompt(direction);
vp_queuePrompt(PROMPT_LONGITUDE);
vp_queueString(buffer, vpAnnounceCommonSymbols);
vp_queuePrompt(direction);
addSilenceIfNeeded(flags);
// speed/altitude:
vpQueuePrompt(PROMPT_SPEED);
vp_queuePrompt(PROMPT_SPEED);
snprintf(buffer, 16, "%4.1fkm/h", state.gps_data.speed);
vpQueueString(buffer, vpAnnounceCommonSymbols);
vpQueuePrompt(PROMPT_ALTITUDE);
vp_queueString(buffer, vpAnnounceCommonSymbols);
vp_queuePrompt(PROMPT_ALTITUDE);
snprintf(buffer, 16, "%4.1fm", state.gps_data.altitude);
vpQueueString(buffer, vpAnnounceCommonSymbols);
vp_queueString(buffer, vpAnnounceCommonSymbols);
addSilenceIfNeeded(flags);
vpQueuePrompt(PROMPT_COMPASS);
vp_queuePrompt(PROMPT_COMPASS);
snprintf(buffer, 16, "%3.1f", state.gps_data.tmg_true);
vpQueueString(buffer, vpAnnounceCommonSymbols);
vpQueuePrompt(PROMPT_DEGREES);
vp_queueString(buffer, vpAnnounceCommonSymbols);
vp_queuePrompt(PROMPT_DEGREES);
addSilenceIfNeeded(flags);
vpQueuePrompt(PROMPT_SATELLITES);
vpQueueInteger(__builtin_popcount(state.gps_data.active_sats));
vp_queuePrompt(PROMPT_SATELLITES);
vp_queueInteger(__builtin_popcount(state.gps_data.active_sats));
vpPlay();
vp_play();
}
#endif // GPS_PRESENT
void announceAboutScreen()
{
vpInit();
vp_clearCurrPrompt();
vpQueueStringTableEntry(&currentLanguage->openRTX);
vp_queueStringTableEntry(&currentLanguage->openRTX);
vpQueueStringTableEntry(&currentLanguage->Niccolo);
vpQueueStringTableEntry(&currentLanguage->Silvano);
vpQueueStringTableEntry(&currentLanguage->Federico);
vpQueueStringTableEntry(&currentLanguage->Fred);
vpQueueStringTableEntry(&currentLanguage->Joseph);
vp_queueStringTableEntry(&currentLanguage->Niccolo);
vp_queueStringTableEntry(&currentLanguage->Silvano);
vp_queueStringTableEntry(&currentLanguage->Federico);
vp_queueStringTableEntry(&currentLanguage->Fred);
vp_queueStringTableEntry(&currentLanguage->Joseph);
vpPlay();
vp_play();
}
void announceBackupScreen()
{
vpInit();
vp_clearCurrPrompt();
vpQueueStringTableEntry(&currentLanguage->flashBackup);
vp_queueStringTableEntry(&currentLanguage->flashBackup);
vpQueueStringTableEntry(&currentLanguage->connectToRTXTool);
vpQueueStringTableEntry(&currentLanguage->toBackupFlashAnd);
vpQueueStringTableEntry(&currentLanguage->pressPTTToStart);
vpQueuePrompt(PROMPT_VP_UNAVAILABLE);
vp_queueStringTableEntry(&currentLanguage->connectToRTXTool);
vp_queueStringTableEntry(&currentLanguage->toBackupFlashAnd);
vp_queueStringTableEntry(&currentLanguage->pressPTTToStart);
vp_queuePrompt(PROMPT_VP_UNAVAILABLE);
vpPlay();
vp_play();
}
void announceRestoreScreen()
{
vpInit();
vp_clearCurrPrompt();
vpQueueStringTableEntry(&currentLanguage->flashRestore);
vp_queueStringTableEntry(&currentLanguage->flashRestore);
vpQueueStringTableEntry(&currentLanguage->connectToRTXTool);
vpQueueStringTableEntry(&currentLanguage->toRestoreFlashAnd);
vpQueueStringTableEntry(&currentLanguage->pressPTTToStart);
vpQueuePrompt(PROMPT_VP_UNAVAILABLE);
vp_queueStringTableEntry(&currentLanguage->connectToRTXTool);
vp_queueStringTableEntry(&currentLanguage->toRestoreFlashAnd);
vp_queueStringTableEntry(&currentLanguage->pressPTTToStart);
vp_queuePrompt(PROMPT_VP_UNAVAILABLE);
vpPlay();
vp_play();
}
#ifdef RTC_PRESENT
void announceSettingsTimeDate()
{
vpInit();
vp_clearCurrPrompt();
vpQueueStringTableEntry(&currentLanguage->timeAndDate);
vp_queueStringTableEntry(&currentLanguage->timeAndDate);
datetime_t local_time = utcToLocalTime(state.time, state.settings.utc_timezone);
char buffer[16] = "\0";
snprintf(buffer, 16, "%02d/%02d/%02d", local_time.date, local_time.month,
local_time.year);
vpQueueString(buffer,
vp_queueString(buffer,
(vpAnnounceCommonSymbols | vpAnnounceLessCommonSymbols));
snprintf(buffer, 16, "%02d:%02d:%02d", local_time.hour, local_time.minute,
local_time.second);
vpQueueString(buffer,
vp_queueString(buffer,
(vpAnnounceCommonSymbols | vpAnnounceLessCommonSymbols));
vpPlay();
vp_play();
}
#endif // RTC_PRESENT
@ -647,7 +647,7 @@ VoicePromptQueueFlags_T GetQueueFlagsForVoiceLevel()
// Play immediately with descriptions unless speech is in progress.
case vpHigh:
flags |= vpqPlayImmediately;
if (!vpIsPlaying()) flags |= vpqIncludeDescriptions;
if (!vp_isPlaying()) flags |= vpqIncludeDescriptions;
break;
}

Wyświetl plik

@ -40,8 +40,16 @@ const uint32_t VOICE_PROMPTS_DATA_VERSION = 0x1000; // v1000 OpenRTX
// The length is the length in bytes of the data.
static void GetCodec2Data(int offset, int length);
#define CODEC2_HEADER_SIZE 7
static FILE *voice_prompt_file = NULL;
typedef struct
{
const char* userWord;
const voicePrompt_t vp;
} userDictEntry;
typedef struct
{
uint32_t magic;
@ -50,7 +58,7 @@ typedef struct
// offset into voice prompt vpc file where actual codec2 data starts.
static uint32_t vpDataOffset = 0;
// Each codec2 frame is 8 bytes.
// 256 x 8 bytes
// 256 x 8 bytes
#define Codec2DataBufferSize 2048
bool vpDataIsLoaded = false;
@ -70,7 +78,7 @@ typedef struct
uint16_t buffer[VOICE_PROMPTS_SEQUENCE_BUFFER_SIZE];
int pos; // index into above buffer.
int length; // number of entries in above buffer.
int codec2DataIndex; // index into current codec2 data
int codec2DataIndex; // index into current codec2 data
//(buffer content sent in lots of 8 byte frames.)
int codec2DataLength; // length of codec2 data for current prompt.
} vpSequence_t;
@ -106,17 +114,25 @@ void vp_close()
fclose(voice_prompt_file);
}
void vpCacheInit(void)
bool vpCheckHeader(uint32_t* bufferAddress)
{
voicePromptsDataHeader_t* header = (voicePromptsDataHeader_t*)bufferAddress;
return ((header->magic == VOICE_PROMPTS_DATA_MAGIC) &&
(header->version == VOICE_PROMPTS_DATA_VERSION));
}
void vp_init(void)
{
voicePromptsDataHeader_t header;
vpDataOffset=0;
if (!voice_prompt_file)
vp_open(NULL);
if (!voice_prompt_file)
return;
fseek(voice_prompt_file, 0L, SEEK_SET);
fread((void*)&header, sizeof(header), 1, voice_prompt_file);
@ -141,26 +157,20 @@ void vpCacheInit(void)
codec_init();
}
bool vpCheckHeader(uint32_t* bufferAddress)
{
voicePromptsDataHeader_t* header = (voicePromptsDataHeader_t*)bufferAddress;
return ((header->magic == VOICE_PROMPTS_DATA_MAGIC) &&
(header->version == VOICE_PROMPTS_DATA_VERSION));
}
static void GetCodec2Data(int offset, int length)
{
if (!voice_prompt_file || (vpDataOffset < (sizeof(voicePromptsDataHeader_t) + sizeof(tableOfContents))))
return;
if ((offset < 0) || (length > Codec2DataBufferSize))
return;
// Skip codec2 header
fseek(voice_prompt_file, vpDataOffset+offset+CODEC2_HEADER_SIZE, SEEK_SET);
fread((void*)&Codec2Data, length, 1, voice_prompt_file);
// zero buffer from length to the next multiple of 8 to avoid garbage
// zero buffer from length to the next multiple of 8 to avoid garbage
// being played back, since codec2 frames are pushed in lots of 8 bytes.
if ((length % 8) != 0)
{
@ -169,7 +179,7 @@ static void GetCodec2Data(int offset, int length)
}
}
void vpTerminate(void)
void vp_terminate(void)
{
if (voicePromptIsActive)
{
@ -182,7 +192,7 @@ void vpTerminate(void)
}
}
void vpInit(void)
void vp_clearCurrPrompt(void)
{
vpCurrentSequence.length = 0;
vpCurrentSequence.pos = 0;
@ -190,13 +200,13 @@ void vpInit(void)
vpCurrentSequence.codec2DataLength = 0;
}
void vpQueuePrompt(uint16_t prompt)
void vp_queuePrompt(const uint16_t prompt)
{
if (state.settings.vpLevel < vpLow) return;
if (voicePromptIsActive)
{
vpInit();
vp_clearCurrPrompt();
}
if (vpCurrentSequence.length < VOICE_PROMPTS_SEQUENCE_BUFFER_SIZE)
{
@ -254,13 +264,13 @@ static bool GetSymbolVPIfItShouldBeAnnounced(char symbol,
}
// This function spells out a string letter by letter.
void vpQueueString(char* promptString, VoicePromptFlags_T flags)
void vp_queueString(char* promptString, VoicePromptFlags_T flags)
{
if (state.settings.vpLevel < vpLow) return;
if (voicePromptIsActive)
{
vpInit();
vp_clearCurrPrompt();
}
if (state.settings.vpPhoneticSpell) flags |= vpAnnouncePhoneticRendering;
@ -270,70 +280,70 @@ void vpQueueString(char* promptString, VoicePromptFlags_T flags)
voicePrompt_t vp = UserDictLookup(promptString, &advanceBy);
if (vp)
{
vpQueuePrompt(vp);
vp_queuePrompt(vp);
promptString += advanceBy;
continue;
}
else if ((*promptString >= '0') && (*promptString <= '9'))
{
vpQueuePrompt(*promptString - '0' + PROMPT_0);
vp_queuePrompt(*promptString - '0' + PROMPT_0);
}
else if ((*promptString >= 'A') && (*promptString <= 'Z'))
{
if (flags & vpAnnounceCaps) vpQueuePrompt(PROMPT_CAP);
if (flags & vpAnnounceCaps) vp_queuePrompt(PROMPT_CAP);
if (flags & vpAnnouncePhoneticRendering)
vpQueuePrompt((*promptString - 'A') + PROMPT_A_PHONETIC);
vp_queuePrompt((*promptString - 'A') + PROMPT_A_PHONETIC);
else
vpQueuePrompt(*promptString - 'A' + PROMPT_A);
vp_queuePrompt(*promptString - 'A' + PROMPT_A);
}
else if ((*promptString >= 'a') && (*promptString <= 'z'))
{
if (flags & vpAnnouncePhoneticRendering)
vpQueuePrompt((*promptString - 'a') + PROMPT_A_PHONETIC);
vp_queuePrompt((*promptString - 'a') + PROMPT_A_PHONETIC);
else
vpQueuePrompt(*promptString - 'a' + PROMPT_A);
vp_queuePrompt(*promptString - 'a' + PROMPT_A);
}
else if ((*promptString == ' ') && (flags & vpAnnounceSpace))
{
vpQueuePrompt(PROMPT_SPACE);
vp_queuePrompt(PROMPT_SPACE);
}
else if (GetSymbolVPIfItShouldBeAnnounced(*promptString, flags, &vp))
{
if (vp != PROMPT_SILENCE)
vpQueuePrompt(vp);
vp_queuePrompt(vp);
else // announce ASCII
{
int32_t val = *promptString;
vpQueuePrompt(PROMPT_CHARACTER); // just the word "code" as we
vp_queuePrompt(PROMPT_CHARACTER); // just the word "code" as we
// don't have character.
vpQueueInteger(val);
vp_queueInteger(val);
}
}
else
{
// otherwise just add silence
vpQueuePrompt(PROMPT_SILENCE);
vp_queuePrompt(PROMPT_SILENCE);
}
promptString++;
}
if (flags & vpqAddSeparatingSilence) vpQueuePrompt(PROMPT_SILENCE);
if (flags & vpqAddSeparatingSilence) vp_queuePrompt(PROMPT_SILENCE);
}
void vpQueueInteger(int32_t value)
void vp_queueInteger(const int32_t value)
{
if (state.settings.vpLevel < vpLow) return;
char buf[12] = {0}; // min: -2147483648, max: 2147483647
snprintf(buf, 12, "%d", value);
vpQueueString(buf, 0);
vp_queueString(buf, 0);
}
// This function looks up a voice prompt corresponding to a string table entry.
// These are stored in the voice data after the voice prompts with no
// corresponding string table entry, hence the offset calculation:
// NUM_VOICE_PROMPTS + (stringTableStringPtr - currentLanguage->languageName)
void vpQueueStringTableEntry(const char* const* stringTableStringPtr)
void vp_queueStringTableEntry(const char* const* stringTableStringPtr)
{
if (state.settings.vpLevel < vpLow) return;
@ -341,31 +351,31 @@ void vpQueueStringTableEntry(const char* const* stringTableStringPtr)
{
return;
}
vpQueuePrompt(NUM_VOICE_PROMPTS + 1 +
vp_queuePrompt(NUM_VOICE_PROMPTS + 1 +
(stringTableStringPtr - &currentLanguage->languageName)
/ sizeof(const char *));
}
void vpPlay(void)
void vp_play(void)
{
if (state.settings.vpLevel < vpLow) return;
if (voicePromptIsActive) return;
if (vpCurrentSequence.length <= 0) return;
voicePromptIsActive = true; // Start the playback
codec_startDecode(SINK_SPK);
audio_enableAmp();
}
// Call this from the main timer thread to continue voice prompt playback.
void vpTick()
void vp_tick()
{
if (!voicePromptIsActive) return;
while (vpCurrentSequence.pos < vpCurrentSequence.length)
{// get the codec2 data for the current prompt if needed.
if (vpCurrentSequence.codec2DataLength == 0)
@ -374,16 +384,16 @@ void vpTick()
vpCurrentSequence.codec2DataLength =
tableOfContents[promptNumber + 1] - tableOfContents[promptNumber];
GetCodec2Data(tableOfContents[promptNumber], vpCurrentSequence.codec2DataLength);
vpCurrentSequence.codec2DataIndex = 0;
}
// push the codec2 data in lots of 8 byte frames.
while (vpCurrentSequence.codec2DataIndex < vpCurrentSequence.codec2DataLength)
{
if (!codec_pushFrame(Codec2Data+vpCurrentSequence.codec2DataIndex, false))
return; // wait until there is room, perhaps next vpTick call.
return; // wait until there is room, perhaps next vp_tick call.
vpCurrentSequence.codec2DataIndex += 8;
}
@ -396,12 +406,12 @@ void vpTick()
voicePromptIsActive=false;
}
inline bool vpIsPlaying(void)
bool vp_isPlaying(void)
{
return voicePromptIsActive;
}
bool vpHasDataToPlay(void)
bool vp_sequenceNotEmpty(void)
{
return (vpCurrentSequence.length > 0);
}

Wyświetl plik

@ -616,7 +616,7 @@ int _ui_fsm_loadChannel(int16_t channel_index, bool *sync_rtx) {
void _ui_fsm_confirmVFOInput(bool *sync_rtx)
{
vpInit();
vp_clearCurrPrompt();
// Switch to TX input
if(ui_state.input_set == SET_RX)
{
@ -653,7 +653,7 @@ void _ui_fsm_confirmVFOInput(bool *sync_rtx)
announceError(vpqInit);
state.ui_screen = MAIN_VFO;
}
vpPlay();
vp_play();
}
void _ui_fsm_insertVFONumber(kbd_msg_t msg, bool *sync_rtx)
@ -661,14 +661,14 @@ void _ui_fsm_insertVFONumber(kbd_msg_t msg, bool *sync_rtx)
// Advance input position
ui_state.input_position += 1;
// clear any prompts in progress.
vpInit();
vp_clearCurrPrompt();
// Save pressed number to calculate frequency and show in GUI
ui_state.input_number = input_getPressedNumber(msg);
// queue the digit just pressed.
vpQueueInteger(ui_state.input_number);
vp_queueInteger(ui_state.input_number);
// queue point if user has entered three digits.
if (ui_state.input_position==3)
vpQueuePrompt(PROMPT_POINT);
vp_queuePrompt(PROMPT_POINT);
if(ui_state.input_set == SET_RX)
{
@ -681,7 +681,7 @@ void _ui_fsm_insertVFONumber(kbd_msg_t msg, bool *sync_rtx)
{// queue the rx freq just completed.
vpQueueFrequency(ui_state.new_rx_frequency);
/// now queue tx as user has changed fields.
vpQueuePrompt(PROMPT_TRANSMIT);
vp_queuePrompt(PROMPT_TRANSMIT);
// Switch to TX input
ui_state.input_set = SET_TX;
// Reset input position
@ -712,7 +712,7 @@ void _ui_fsm_insertVFONumber(kbd_msg_t msg, bool *sync_rtx)
state.ui_screen = MAIN_VFO;
}
}
vpPlay();
vp_play();
}
void _ui_changeBrightness(int variation)

Wyświetl plik

@ -99,9 +99,9 @@ static void announceMenuItemIfNeeded(char* name, char* value)
// See if we are already in the middle of speaking a menu item.
// e.g. when changing a value with left or right, we don't want to repeat the
// prompt if arrowing rapidly.
bool voicePromptWasPlaying=vpIsPlaying();
bool voicePromptWasPlaying=vp_isPlaying();
// Stop any prompt in progress and clear the buffer.
vpInit();
vp_clearCurrPrompt();
// If no value is supplied, or, no prompt is in progress, announce the name.
if (!voicePromptWasPlaying || !value || !*value)
announceText(name, vpqDefault);
@ -109,7 +109,7 @@ static void announceMenuItemIfNeeded(char* name, char* value)
if (value && *value)
announceText(value, vpqDefault);
vpPlay();
vp_play();
}
void _ui_drawMenuList(uint8_t selected, int (*getCurrentEntry)(char *buf, uint8_t max_len, uint8_t index))

Wyświetl plik

@ -34,12 +34,12 @@ int main()
state.settings.vpLevel = 3;
VoicePromptQueueFlags_T flags = GetQueueFlagsForVoiceLevel();
vpCacheInit();
vpInit();
vpQueueStringTableEntry(&currentLanguage->allChannels);
vpPlay();
vp_init();
vp_clearCurrPrompt();
vp_queueStringTableEntry(&currentLanguage->allChannels);
vp_play();
while(true)
{
vpTick();
vp_tick();
}
}