kopia lustrzana https://github.com/OpenRTX/OpenRTX
Implemented storage of voice prompts data inside the binary executable image. Voice prompts data is placed in .rodata section at link time
rodzic
cf842306c2
commit
d0d603901e
|
@ -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')
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
.section .note.GNU-stack,""
|
||||||
|
.section .rodata
|
||||||
|
|
||||||
|
.global _voiceprompts_start
|
||||||
|
.global _voiceprompts_end
|
||||||
|
|
||||||
|
_voiceprompts_start:
|
||||||
|
.incbin "../voiceprompts.vpc"
|
||||||
|
_voiceprompts_end:
|
|
@ -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;
|
||||||
|
|
|
@ -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();
|
||||||
|
|
Plik binarny nie jest wyświetlany.
Ładowanie…
Reference in New Issue