Adding voice prompts skeleton.

md1702
vk7js 2022-05-04 21:57:46 +10:00 zatwierdzone przez Silvano Seva
rodzic 7e660f2fe8
commit d5568b8ab3
3 zmienionych plików z 491 dodań i 1 usunięć

Wyświetl plik

@ -0,0 +1,172 @@
/***************************************************************************
* Copyright (C) 2022 by Federico Amedeo Izzo IU2NUO, *
* Niccolò Izzo IU2KIN, *
* Silvano Seva IU2KWO *
* Joseph Stephen VK7JS *
* 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 3 of the License, 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; if not, see <http://www.gnu.org/licenses/> *
***************************************************************************/
#ifndef voice_prompts_h_included
#define voice_prompts_h_included
/*
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.
*/
typedef enum
{
PROMPT_SILENCE, //
PROMPT_POINT, // POINT
PROMPT_0, // 0
PROMPT_1, // 1
PROMPT_2, // 2
PROMPT_3, // 3
PROMPT_4, // 4
PROMPT_5, // 5
PROMPT_6, // 6
PROMPT_7, // 7
PROMPT_8, // 8
PROMPT_9, // 9
PROMPT_A, // A
PROMPT_B, // B
PROMPT_C, // C
PROMPT_D, // D
PROMPT_E, // E
PROMPT_F, // F
PROMPT_G, // G
PROMPT_H, // H
PROMPT_I, // I
PROMPT_J, // J
PROMPT_K, // K
PROMPT_L, // L
PROMPT_M, // M
PROMPT_N, // N
PROMPT_O, // O
PROMPT_P, // P
PROMPT_Q, // Q
PROMPT_R, // R
PROMPT_S, // S
PROMPT_T, // T
PROMPT_U, // U
PROMPT_V, // V
PROMPT_W, // W
PROMPT_X, // X
PROMPT_Y, // Y
PROMPT_Z, // Zed
PROMPT_A_PHONETIC, // alpha
PROMPT_B_PHONETIC, // bravo
PROMPT_C_PHONETIC, // charlie
PROMPT_D_PHONETIC, // delta
PROMPT_E_PHONETIC, // echo
PROMPT_F_PHONETIC, // foxtrot
PROMPT_G_PHONETIC, // golf
PROMPT_H_PHONETIC, // hotel
PROMPT_I_PHONETIC, // india
PROMPT_J_PHONETIC, // juliet
PROMPT_K_PHONETIC, // kilo
PROMPT_L_PHONETIC, // lema
PROMPT_M_PHONETIC, // mike
PROMPT_N_PHONETIC, // november
PROMPT_O_PHONETIC, // oscar
PROMPT_P_PHONETIC, // papa
PROMPT_Q_PHONETIC, // quebec
PROMPT_R_PHONETIC, // romeo
PROMPT_S_PHONETIC, // siera
PROMPT_T_PHONETIC, // tango
PROMPT_U_PHONETIC, // uniform
PROMPT_V_PHONETIC, // victor
PROMPT_W_PHONETIC, // whisky
PROMPT_X_PHONETIC, // exray
PROMPT_Y_PHONETIC, // yankie
PROMPT_Z_PHONETIC, // zulu
PROMPT_CAP, // cap
PROMPT_HERTZ, // hertz
PROMPT_KILOHERTZ, // Kilohertz
PROMPT_MEGAHERTZ, // Megahertz
PROMPT_VFO, // V F O
PROMPT_MILLISECONDS, // Milliseconds
PROMPT_SECONDS, // Seconds
PROMPT_MINUTES, // Minutes
PROMPT_VOLTS, // Volts
PROMPT_MILLIWATTS, // Milliwatts
PROMPT_WATT, // Wattt
PROMPT_WATTS, // Watts
PROMPT_PERCENT, // Percent
PROMPT_RECEIVE, // Receive
PROMPT_TRANSMIT, // Transmit
PROMPT_MODE, // Mode
PROMPT_DMR, // D M R
PROMPT_FM, // F M
PROMPT_M17, // M seventeen
PROMPT_PLUS, // Plus
PROMPT_MINUS, // Minus
PROMPT_STAR, // Star
PROMPT_HASH, // Hash
PROMPT_SPACE, // space
PROMPT_EXCLAIM, // exclaim
PROMPT_COMMA, // comma
PROMPT_AT, // at
PROMPT_COLON, // colon
PROMPT_QUESTION, // question
PROMPT_LEFT_PAREN, // left paren
PROMPT_RIGHT_PAREN, // right paren
PROMPT_TILDE, // tilde
PROMPT_SLASH, // slash
PROMPT_LEFT_BRACKET, // left bracket
PROMPT_RIGHT_BRACKET, // right bracket
PROMPT_LESS, // less
PROMPT_GREATER, // greater
PROMPT_EQUALS, // equals
PROMPT_DOLLAR, // dollar
PROMPT_APOSTROPHE, // apostrophe
PROMPT_GRAVE, // grave
PROMPT_AMPERSAND, // and
PROMPT_BAR, // bar
PROMPT_UNDERLINE, // underline
PROMPT_CARET, // caret
PROMPT_LEFT_BRACE, // left brace
NUM_VOICE_PROMPTS,
__MAKE_ENUM_16BITS = INT16_MAX
} voicePrompt_t;
#define PROMPT_VOICE_NAME (NUM_VOICE_PROMPTS + (sizeof(stringsTable_t)/sizeof(char*)))
typedef enum
{
vpAnnounceCaps=0x01,
vpAnnounceCustomPrompts=0x02,
vpAnnounceSpaceAndSymbols=0x04,
vpAnnouncePhoneticRendering=0x08,
} VoicePromptFlags_T;
extern bool voicePromptDataIsLoaded;
extern const uint32_t VOICE_PROMPTS_FLASH_HEADER_ADDRESS;
// Loads just the TOC from Flash and stores in RAM for fast access.
void vpCacheInit(void);
// event driven to play a voice prompt in progress.
void vpTick(void);
void vpInit(void);// Call before building the prompt sequence
void vpAppendPrompt(uint16_t prompt);// Append an individual prompt item. This can be a single letter number or a phrase
void vpQueueString(char *promptString, VoicePromptFlags_T flags);
void vpQueueInteger(int32_t value); // Append a signed integer
void vpQueueStringTableEntry(const char * const *);//Append a text string from the current language e.g. currentLanguage->off
void vpPlay(void);// Starts prompt playback
extern bool vpIsPlaying(void);
bool vpHasDataToPlay(void);
void vpTerminate(void);
bool vpCheckHeader(uint32_t *bufferAddress);
#endif

Wyświetl plik

@ -0,0 +1,318 @@
/***************************************************************************
* Copyright (C) 2022 by Federico Amedeo Izzo IU2NUO, *
* Niccolò Izzo IU2KIN, *
* Silvano Seva IU2KWO *
* Joseph Stephen VK7JS *
* Roger Clark VK3KYY *
* 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 3 of the License, 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; if not, see <http://www.gnu.org/licenses/> *
***************************************************************************/
#include <ctype.h>
#include "core/voicePrompts.h"
#include "ui/UIStrings.h"
const uint32_t VOICE_PROMPTS_DATA_MAGIC = 0x5056;//'VP'
const uint32_t VOICE_PROMPTS_DATA_VERSION = 0x1000; // v1000 OpenRTX
#define VOICE_PROMPTS_TOC_SIZE 350
static void getM17Data(int offset,int length);
typedef struct
{
uint32_t magic;
uint32_t version;
} voicePromptsDataHeader_t;
const uint32_t VOICE_PROMPTS_FLASH_HEADER_ADDRESS = 0x8F400; // todo figure this out for OpenRTX
static uint32_t vpFlashDataAddress;// = VOICE_PROMPTS_FLASH_HEADER_ADDRESS + sizeof(voicePromptsDataHeader_t) + sizeof(uint32_t)*VOICE_PROMPTS_TOC_SIZE ;
// TODO figure out M17 frame equivalent.
// 76 x 27 byte ambe frames
#define M17_DATA_BUFFER_SIZE 2052
bool voicePromptDataIsLoaded = false;
static bool voicePromptIsActive = false;
static int promptDataPosition = -1;
static int currentPromptLength = -1;
#define PROMPT_TAIL 30
static int promptTail = 0;
static uint8_t M17Data[M17_DATA_BUFFER_SIZE];
#define VOICE_PROMPTS_SEQUENCE_BUFFER_SIZE 128
typedef struct
{
uint16_t Buffer[VOICE_PROMPTS_SEQUENCE_BUFFER_SIZE];
int Pos;
int Length;
} vpSequence_t;
static vpSequence_t vpCurrentSequence =
{
.Pos = 0,
.Length = 0
};
uint32_t tableOfContents[VOICE_PROMPTS_TOC_SIZE];
void vpCacheInit(void)
{
voicePromptsDataHeader_t header;
SPI_Flash_read(VOICE_PROMPTS_FLASH_HEADER_ADDRESS,(uint8_t *)&header,sizeof(voicePromptsDataHeader_t));
if (vpCheckHeader((uint32_t *)&header))
{
voicePromptDataIsLoaded = SPI_Flash_read(VOICE_PROMPTS_FLASH_HEADER_ADDRESS + sizeof(voicePromptsDataHeader_t), (uint8_t *)&tableOfContents, sizeof(uint32_t) * VOICE_PROMPTS_TOC_SIZE);
vpFlashDataAddress = VOICE_PROMPTS_FLASH_HEADER_ADDRESS + sizeof(voicePromptsDataHeader_t) + sizeof(uint32_t)*VOICE_PROMPTS_TOC_SIZE ;
}
}
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 getM17Data(int offset,int length)
{
if (length <= M17_DATA_BUFFER_SIZE)
{
SPI_Flash_read(vpFlashDataAddress + offset, (uint8_t *)&M17Data, length);
}
}
void vpTick(void)
{
if (voicePromptIsActive)
{
if (promptDataPosition < currentPromptLength)
{
//if (wavbuffer_count <= (WAV_BUFFER_COUNT / 2))
{
// codecDecode((uint8_t *)&M17Data[promptDataPosition], 3);
promptDataPosition += 27;
}
//soundTickRXBuffer();
}
else
{
if ( vpCurrentSequence.Pos < (vpCurrentSequence.Length - 1))
{
vpCurrentSequence.Pos++;
promptDataPosition = 0;
int promptNumber = vpCurrentSequence.Buffer[vpCurrentSequence.Pos];
currentPromptLength = tableOfContents[promptNumber + 1] - tableOfContents[promptNumber];
getM17Data(tableOfContents[promptNumber], currentPromptLength);
}
else
{
// wait for wave buffer to empty when prompt has finished playing
// if (wavbuffer_count == 0)
{
vpTerminate();
}
}
}
}
else
{
if (promptTail > 0)
{
promptTail--;
if ((promptTail == 0) && trxCarrierDetected() && (trxGetMode() == RADIO_MODE_ANALOG))
{
//GPIO_PinWrite(GPIO_RX_audio_mux, Pin_RX_audio_mux, 1); // Set the audio path to AT1846 -> audio amp.
}
}
}
}
void vpTerminate(void)
{
if (voicePromptIsActive)
{
//disableAudioAmp(AUDIO_AMP_MODE_PROMPT);
vpCurrentSequence.Pos = 0;
//soundTerminateSound();
//soundInit();
promptTail = PROMPT_TAIL;
voicePromptIsActive = false;
}
}
void vpInit(void)
{
if (voicePromptIsActive)
{
vpTerminate();
}
vpCurrentSequence.Length = 0;
vpCurrentSequence.Pos = 0;
}
void vpQueuePrompt(uint16_t prompt)
{
if (voicePromptIsActive)
{
vpInit();
}
if (vpCurrentSequence.Length < VOICE_PROMPTS_SEQUENCE_BUFFER_SIZE)
{
vpCurrentSequence.Buffer[vpCurrentSequence.Length] = prompt;
vpCurrentSequence.Length++;
}
}
// This function spells out a string letter by letter.
void vpQueueString(char *promptString, VoicePromptFlags_T flags)
{
const char indexedSymbols[] = "!,@:?()~/[]<>=$'`&|_^{}"; // handles most of them in indexed order, must match order of vps.
if (voicePromptIsActive)
{
vpInit();
}
while (*promptString != 0)
{
if ((*promptString >= '0') && (*promptString <= '9'))
{
vpQueuePrompt(*promptString - '0' + PROMPT_0);
}
else if ((*promptString >= 'A') && (*promptString <= 'Z'))
{
if (flags&vpAnnounceCaps)
vpQueuePrompt(PROMPT_CAP);
if (flags&vpAnnouncePhoneticRendering)
vpQueuePrompt((*promptString - 'A') + PROMPT_A_PHONETIC);
else
vpQueuePrompt(*promptString - 'A' + PROMPT_A);
}
else if ((*promptString >= 'a') && (*promptString <= 'z'))
{
if (flags&vpAnnouncePhoneticRendering)
vpQueuePrompt((*promptString - 'a') + PROMPT_A_PHONETIC);
else
vpQueuePrompt(*promptString - 'a' + PROMPT_A);
}
else if (*promptString == '.')
{
vpQueuePrompt(PROMPT_POINT);
}
else if (*promptString == '+')
{
vpQueuePrompt(PROMPT_PLUS);
}
else if (*promptString == '-')
{
vpQueuePrompt(PROMPT_MINUS);
}
else if (*promptString == '%')
{
vpQueuePrompt(PROMPT_PERCENT);
}
else if (*promptString == '*')
{
vpQueuePrompt(PROMPT_STAR);
}
else if (*promptString == '#')
{
vpQueuePrompt(PROMPT_HASH);
}
else if (flags&(vpAnnounceSpaceAndSymbols))
{
if (*promptString==' ')
vpQueuePrompt(PROMPT_SPACE);
else
{
char* ptr=strchr(indexedSymbols, *promptString);
if (ptr)
{
vpQueuePrompt(PROMPT_EXCLAIM+(ptr-indexedSymbols));
}
else
{
int32_t val = *promptString;
vpQueueLanguageString(&currentLanguage->dtmf_code); // just the word "code" as we don't have character.
vpQueueInteger(val);
}
}
}
else
// otherwise just add silence
vpQueuePrompt(PROMPT_SILENCE);
promptString++;
}
}
void vpQueueInteger(int32_t value)
{
char buf[12] = {0}; // min: -2147483648, max: 2147483647
itoa(value, buf, 10);
vpQueueString(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)
{
if (stringTableStringPtr == NULL)
{
return;
}
vpQueuePrompt(NUM_VOICE_PROMPTS + (stringTableStringPtr - currentLanguage->languageName));
}
void vpPlay(void)
{
if ((voicePromptIsActive == false) && (vpCurrentSequence.Length > 0))
{
voicePromptIsActive = true;// Start the playback
int promptNumber = vpCurrentSequence.Buffer[0];
vpCurrentSequence.Pos = 0;
currentPromptLength = tableOfContents[promptNumber + 1] - tableOfContents[promptNumber];
getM17Data(tableOfContents[promptNumber], currentPromptLength);
// GPIO_PinWrite(GPIO_RX_audio_mux, Pin_RX_audio_mux, 0);// set the audio mux HR-C6000 -> audio amp
//enableAudioAmp(AUDIO_AMP_MODE_PROMPT);
//codecInit(true);
promptDataPosition = 0;
}
}
inline bool vpIsPlaying(void)
{
return (voicePromptIsActive || (promptTail > 0));
}
bool vpHasDataToPlay(void)
{
return (vpCurrentSequence.Length > 0);
}

Wyświetl plik

@ -28,7 +28,7 @@
#include <interfaces/platform.h>
#include <interfaces/delays.h>
#include <memory_profiling.h>
#include <ui/uiStrings.h>
#include <ui/UIStrings.h>
/* UI main screen helper functions, their implementation is in "ui_main.c" */
extern void _ui_drawMainBottom();