Implemented storage of voice prompts data inside the binary executable image. Voice prompts data is placed in .rodata section at link time

md1702
Silvano Seva 2022-08-18 08:34:01 +02:00
rodzic cf842306c2
commit d0d603901e
5 zmienionych plików z 106 dodań i 43 usunięć

Wyświetl plik

@ -53,6 +53,7 @@ openrtx_src = ['openrtx/src/core/state.c',
'openrtx/src/core/memory_profiling.cpp', 'openrtx/src/core/memory_profiling.cpp',
'openrtx/src/core/voicePrompts.c', 'openrtx/src/core/voicePrompts.c',
'openrtx/src/core/voicePromptUtils.c', 'openrtx/src/core/voicePromptUtils.c',
'openrtx/src/core/voicePromptData.S',
'openrtx/src/ui/ui.c', 'openrtx/src/ui/ui.c',
'openrtx/src/ui/ui_strings.c', 'openrtx/src/ui/ui_strings.c',
'openrtx/src/ui/ui_main.c', 'openrtx/src/ui/ui_main.c',
@ -275,8 +276,9 @@ linux_src = src + linux_platform_src
# MDx family display emulation # MDx family display emulation
linux_def = def + {'SCREEN_WIDTH': '160', 'SCREEN_HEIGHT': '128', 'PIX_FMT_RGB565': ''} linux_def = def + {'SCREEN_WIDTH': '160', 'SCREEN_HEIGHT': '128', 'PIX_FMT_RGB565': ''}
linux_inc = inc + ['platform/targets/linux', linux_def += {'VP_USE_FILESYSTEM':''}
'platform/targets/linux/emulator'] linux_inc = inc + ['platform/targets/linux',
'platform/targets/linux/emulator']
if not meson.is_cross_build() if not meson.is_cross_build()
sdl_dep = dependency('SDL2') sdl_dep = dependency('SDL2')

Wyświetl plik

@ -0,0 +1,9 @@
.section .note.GNU-stack,""
.section .rodata
.global _voiceprompts_start
.global _voiceprompts_end
_voiceprompts_start:
.incbin "../voiceprompts.vpc"
_voiceprompts_end:

Wyświetl plik

@ -35,8 +35,6 @@ static const uint32_t VOICE_PROMPTS_DATA_VERSION = 0x1000; // v1000 OpenRTX
#define VOICE_PROMPTS_TOC_SIZE 350 #define VOICE_PROMPTS_TOC_SIZE 350
#define CODEC2_HEADER_SIZE 7 #define CODEC2_HEADER_SIZE 7
#define VP_SEQUENCE_BUF_SIZE 128 #define VP_SEQUENCE_BUF_SIZE 128
#define CODEC2_DATA_BUF_SIZE 2048
typedef struct typedef struct
{ {
@ -57,12 +55,12 @@ typedef struct
uint16_t buffer[VP_SEQUENCE_BUF_SIZE]; // Buffer of individual prompt indices. uint16_t buffer[VP_SEQUENCE_BUF_SIZE]; // Buffer of individual prompt indices.
uint16_t pos; // Index into above buffer. uint16_t pos; // Index into above buffer.
uint16_t length; // Number of entries in above buffer. uint16_t length; // Number of entries in above buffer.
uint32_t c2DataStart;
uint32_t c2DataIndex; // Index into current codec2 data uint32_t c2DataIndex; // Index into current codec2 data
uint32_t c2DataLength; // Length of codec2 data for current prompt. uint32_t c2DataLength; // Length of codec2 data for current prompt.
} }
vpSequence_t; vpSequence_t;
static const userDictEntry_t userDictionary[] = static const userDictEntry_t userDictionary[] =
{ {
{"hotspot", PROMPT_CUSTOM1}, // Hotspot {"hotspot", PROMPT_CUSTOM1}, // Hotspot
@ -82,16 +80,57 @@ static vpSequence_t vpCurrentSequence =
{ {
.pos = 0, .pos = 0,
.length = 0, .length = 0,
.c2DataStart = 0,
.c2DataIndex = 0, .c2DataIndex = 0,
.c2DataLength = 0 .c2DataLength = 0
}; };
static uint8_t codec2Data[CODEC2_DATA_BUF_SIZE];
static uint32_t tableOfContents[VOICE_PROMPTS_TOC_SIZE]; static uint32_t tableOfContents[VOICE_PROMPTS_TOC_SIZE];
static uint32_t vpDataOffset = 0; static bool vpDataLoaded = false;
static bool vpDataLoaded = false; static bool voicePromptActive = false;
static bool voicePromptActive = false;
static FILE *vpFile = NULL; #ifdef VP_USE_FILESYSTEM
static FILE *vpFile = NULL;
#else
extern unsigned char _vpdata_start asm("_voiceprompts_start");
extern unsigned char _vpdata_end asm("_voiceprompts_end");
unsigned char *vpData = &_vpdata_start;
#endif
/**
* \internal
* Load voice prompts header.
*
* @param header: pointer toa vpHeader_t data structure.
*/
static void loadVpHeader(vpHeader_t *header)
{
#ifdef VP_USE_FILESYSTEM
fseek(vpFile, 0L, SEEK_SET);
fread(header, sizeof(vpHeader_t), 1, vpFile);
#else
memcpy(header, vpData, sizeof(vpHeader_t));
#endif
}
/**
* \internal
* Load voice prompts' table of contents.
*/
static void loadVpToC()
{
#ifdef VP_USE_FILESYSTEM
fread(&tableOfContents, sizeof(tableOfContents), 1, vpFile);
size_t vpDataOffset = ftell(vpFile);
if(vpDataOffset == (sizeof(vpHeader_t) + sizeof(tableOfContents)))
vpDataLoaded = true;
#else
uint8_t *tocPtr = vpData + sizeof(vpHeader_t);
memcpy(&tableOfContents, tocPtr, sizeof(tableOfContents));
vpDataLoaded = true;
#endif
}
/** /**
* \internal * \internal
@ -100,27 +139,35 @@ static FILE *vpFile = NULL;
* @param offset: offset relative to the start of the voice prompt data. * @param offset: offset relative to the start of the voice prompt data.
* @param length: data length in bytes. * @param length: data length in bytes.
*/ */
static void loadCodec2Data(const int offset, const int length) static void fetchCodec2Data(uint8_t *data, const size_t offset)
{ {
const uint32_t minOffset = sizeof(vpHeader_t) + sizeof(tableOfContents); if (vpDataLoaded == false)
if ((vpFile == NULL) || (vpDataOffset < minOffset))
return; return;
if ((offset < 0) || (length > CODEC2_DATA_BUF_SIZE)) #ifdef VP_USE_FILESYSTEM
if (vpFile == NULL)
return; return;
// Skip codec2 header size_t start = sizeof(vpHeader_t)
fseek(vpFile, vpDataOffset + offset + CODEC2_HEADER_SIZE, SEEK_SET); + sizeof(tableOfContents)
fread((void*)&codec2Data, length, 1, vpFile); + CODEC2_HEADER_SIZE;
// zero buffer from length to the next multiple of 8 to avoid garbage fseek(vpFile, start + offset, SEEK_SET);
// being played back, since codec2 frames are pushed in lots of 8 bytes. fread(data, 8, 1, vpFile);
if ((length % 8) != 0) #else
uint8_t *dataPtr = vpData
+ sizeof(vpHeader_t)
+ sizeof(tableOfContents)
+ CODEC2_HEADER_SIZE;
if((dataPtr + 8) >= &_vpdata_end)
{ {
int bytesToZero = length % 8; memset(data, 0x00, 8);
memset(codec2Data + length, 0, bytesToZero); return;
} }
memcpy(data, dataPtr + offset, 8);
#endif
} }
/** /**
@ -144,7 +191,7 @@ static inline bool checkVpHeader(const vpHeader_t* header)
* @param advanceBy: final offset with respect of dictionary beginning. * @param advanceBy: final offset with respect of dictionary beginning.
* @return index of user dictionary's voice prompt. * @return index of user dictionary's voice prompt.
*/ */
static uint16_t UserDictLookup(const char* ptr, int* advanceBy) static uint16_t userDictLookup(const char* ptr, int* advanceBy)
{ {
if ((ptr == NULL) || (*ptr == '\0')) if ((ptr == NULL) || (*ptr == '\0'))
return 0; return 0;
@ -196,27 +243,24 @@ static bool GetSymbolVPIfItShouldBeAnnounced(char symbol,
void vp_init() void vp_init()
{ {
vpDataOffset = 0; #ifdef VP_USE_FILESYSTEM
if(vpFile == NULL) if(vpFile == NULL)
vpFile = fopen("voiceprompts.vpc", "r"); vpFile = fopen("voiceprompts.vpc", "r");
if(vpFile == NULL) if(vpFile == NULL)
return; return;
#else
if(&_vpdata_start == &_vpdata_end)
return;
#endif
// Read header // Read header
vpHeader_t header; vpHeader_t header;
fseek(vpFile, 0L, SEEK_SET); loadVpHeader(&header);
fread((void*)&header, sizeof(header), 1, vpFile);
if(checkVpHeader(&header) == true) if(checkVpHeader(&header) == true)
{ {
// Read in the TOC. loadVpToC();
fread((void*)&tableOfContents, sizeof(tableOfContents), 1, vpFile);
vpDataOffset = ftell(vpFile);
if(vpDataOffset == (sizeof(vpHeader_t) + sizeof(tableOfContents)))
vpDataLoaded = true;
} }
if (vpDataLoaded) if (vpDataLoaded)
@ -233,7 +277,7 @@ void vp_init()
state.settings.vpLevel = vpBeep; state.settings.vpLevel = vpBeep;
} }
// TODO: Move this somewhere else for compatibility with M17 // Initialize codec2 module
codec_init(); codec_init();
} }
@ -248,11 +292,16 @@ void vp_terminate()
voicePromptActive = false; voicePromptActive = false;
} }
codec_terminate();
#ifdef VP_USE_FILESYSTEM
fclose(vpFile); fclose(vpFile);
#endif
} }
void vp_clearCurrPrompt() void vp_clearCurrPrompt()
{ {
voicePromptActive = false;
vpCurrentSequence.length = 0; vpCurrentSequence.length = 0;
vpCurrentSequence.pos = 0; vpCurrentSequence.pos = 0;
vpCurrentSequence.c2DataIndex = 0; vpCurrentSequence.c2DataIndex = 0;
@ -288,7 +337,7 @@ void vp_queueString(const char* string, vpFlags_t flags)
while (*string != '\0') while (*string != '\0')
{ {
int advanceBy = 0; int advanceBy = 0;
voicePrompt_t vp = UserDictLookup(string, &advanceBy); voicePrompt_t vp = userDictLookup(string, &advanceBy);
if (vp != 0) if (vp != 0)
{ {
@ -397,7 +446,7 @@ void vp_play()
void vp_tick() void vp_tick()
{ {
if (!voicePromptActive) if (voicePromptActive == false)
return; return;
while(vpCurrentSequence.pos < vpCurrentSequence.length) while(vpCurrentSequence.pos < vpCurrentSequence.length)
@ -408,19 +457,21 @@ void vp_tick()
// obtain the data for the prompt. // obtain the data for the prompt.
int promptNumber = vpCurrentSequence.buffer[vpCurrentSequence.pos]; int promptNumber = vpCurrentSequence.buffer[vpCurrentSequence.pos];
vpCurrentSequence.c2DataIndex = 0;
vpCurrentSequence.c2DataStart = tableOfContents[promptNumber];
vpCurrentSequence.c2DataLength = tableOfContents[promptNumber + 1] vpCurrentSequence.c2DataLength = tableOfContents[promptNumber + 1]
- tableOfContents[promptNumber]; - tableOfContents[promptNumber];
loadCodec2Data(tableOfContents[promptNumber],
vpCurrentSequence.c2DataLength);
vpCurrentSequence.c2DataIndex = 0;
} }
while (vpCurrentSequence.c2DataIndex < vpCurrentSequence.c2DataLength) while (vpCurrentSequence.c2DataIndex < vpCurrentSequence.c2DataLength)
{ {
// push the codec2 data in lots of 8 byte frames. // push the codec2 data in lots of 8 byte frames.
if (!codec_pushFrame(codec2Data+vpCurrentSequence.c2DataIndex, false)) uint8_t c2Frame[8] = {0};
fetchCodec2Data(c2Frame, vpCurrentSequence.c2DataStart +
vpCurrentSequence.c2DataIndex);
if (!codec_pushFrame(c2Frame, false))
return; return;
vpCurrentSequence.c2DataIndex += 8; vpCurrentSequence.c2DataIndex += 8;

Wyświetl plik

@ -55,6 +55,7 @@ void OpMode_M17::disable()
startTx = false; startTx = false;
platform_ledOff(GREEN); platform_ledOff(GREEN);
platform_ledOff(RED); platform_ledOff(RED);
codec_stop();
codec_terminate(); codec_terminate();
audio_disableAmp(); audio_disableAmp();
audio_disableMic(); audio_disableMic();

BIN
voiceprompts.vpc 100644

Plik binarny nie jest wyświetlany.