📄 core.c
字号:
#include "global.h"
// Random number seed
uint8_t seed0;
uint8_t seed1;
uint8_t seed2;
char phonemes[128];
char modifier[128]; // must be same size as 'phonemes'
char g_text[64];
uint8_t default_pitch = 7;
#ifdef _AVR_
// Define routines to log to the UART
void loggerP(const char * s){
rprintfProgStr(s);
}
void logger(const char * s){
rprintfStr((char *)s);
}
void loggerc(char c){
rprintfChar(c);
}
void logger_uint8(uint8_t n){
rprintfNum(10, 3, 0, ' ', n);
}
void loggerCRLF(void){
loggerP(PSTR("\n"));
}
#else
FILE * log_dest = stderr;
void setLogFile(FILE * file){
log_dest = file;
}
void logger(const char* s){
fprintf(log_dest,"%s",s);
}
void loggerc(char c){
fprintf(log_dest,"%c",c);
}
void logger_uint8(uint8_t n){
fprintf(log_dest,"%03d",n);
}
void loggerCRLF(void){
fprintf(log_dest,"\n");
}
#endif
// Lookup user specified pitch changes
static const uint8_t PROGMEM PitchesP[] = { 1, 2, 4, 6, 8, 10, 13, 16 };
/**
*
* Find the single character 'token' in 'vocab'
* and append its phonemes to dest[x]
*
* Return new 'x'
*/
int copyToken(char token,char * dest, int x, const VOCAB* vocab){
int ph;
const char* src;
for(ph = 0; ph < numVocab; ph++){
const char *txt = (const char *)pgm_read_word(&vocab[ph].txt);
if(pgm_read_byte(&txt[0]) == token && pgm_read_byte(&txt[1])==0){
src = (const char *)pgm_read_word(&vocab[ph].phoneme);
while(pgm_read_byte(src)!=0){
dest[x++] = pgm_read_byte(src);
src++;
}
break;
}
}
return x;
}
uint8_t whitespace(char c){
return (c==0 || c==' ' || c==',' || c=='.' || c=='?' || c=='\''
|| c=='!' || c==':' || c=='/') ? 1 : 0;
}
/**
* Enter:
* src => English text in upper case
* vocab => VOCAB array
* dest => address to return result
* return 1 if ok, or 0 if error
*/
int textToPhonemes(const char * src, const VOCAB* vocab, char * dest){
int outIndex = 0;// Current offset into dest
int inIndex = -1; // Starts at -1 so that a leading space is assumed
while(inIndex==-1 || src[inIndex]!= 0){ // until end of text
int maxMatch=0; // Max chars matched on input text
int numOut=0; // Number of characters copied to output stream for the best match
int ph;
boolean endsInWhiteSpace=FALSE;
int maxWildcardPos = 0;
// Get next phoneme, P2
for(ph = 0; ph < numVocab; ph++){
int y,x;
char wildcard=0; // modifier
int wildcardInPos=0;
boolean hasWhiteSpace=FALSE;
const char* text = (const char*)pgm_read_word(&vocab[ph].txt);
const char* phon = (const char*)pgm_read_word(&vocab[ph].phoneme);
for(y=0;;y++){
char nextVocabChar = pgm_read_byte(&text[y]);
char nextCharIn = (y + inIndex==-1) ? ' ' : src[y + inIndex];
if(nextCharIn>='a' && nextCharIn<='z'){
nextCharIn = nextCharIn - 'a' + 'A';
}
if(nextVocabChar=='#' && nextCharIn >= 'A' && nextCharIn <= 'Z'){
wildcard = nextCharIn; // The character equivalent to the '#'
wildcardInPos=y;
continue;
}
if(nextVocabChar=='_'){
// try to match against a white space
hasWhiteSpace=TRUE;
if(whitespace(nextCharIn)){
continue;
}
y--;
break;
}
// check for end of either string
if(nextVocabChar==0 || nextCharIn==0){
break;
}
if(nextVocabChar != nextCharIn){
break;
}
}
// See if its the longest complete match so far
if(y<=maxMatch || pgm_read_byte(&text[y])!=0){
continue;
}
// This is the longest complete match
maxMatch = y;
maxWildcardPos = 0;
x = outIndex; // offset into phoneme return data
// Copy the matching phrase changing any '#' to the phoneme for the wildcard
for(y=0;;y++){
char c = pgm_read_byte(&phon[y]);
if(c==0)
break;
if(c=='#'){
if(pgm_read_byte(&phon[y+1])==0){
// replacement ends in wildcard
maxWildcardPos = wildcardInPos;
}else{
x = copyToken(wildcard,dest,x, vocab); // Copy the phonemes for the wildcard character
}
}else{
dest[x++] = c;
}
}
dest[x]=0;
endsInWhiteSpace = hasWhiteSpace;
// 14
numOut = x - outIndex; // The number of bytes added
}// check next phoneme
// 15 - end of vocab table
//16
if(endsInWhiteSpace==TRUE){
maxMatch--;
}
//17
if(maxMatch==0){
loggerP(PSTR("Mistake in SAY, no token for "));
logger(&src[inIndex]);
loggerCRLF();
return 0;
}
//20
outIndex += numOut;
if(outIndex > 256-16){
loggerP(PSTR("Mistake in SAY, text too long\n"));
return 0;
}
//21
inIndex += (maxWildcardPos>0) ? maxWildcardPos : maxMatch;
}
return 1;
}
/**
*
* Convert phonemes to data string
* Enter: textp = phonemes string
* Return: phonemes = string of sound data
* modifier = 2 bytes per sound data
*
*/
int phonemesToData(const char* textp, const PHONEME* phoneme){
int phonemeOut = 0; // offset into the phonemes array
int modifierOut = 0; // offset into the modifiers array
unsigned int L81=0; // attenuate
unsigned int L80=16;
while(*textp != 0){
// P20: Get next phoneme
boolean anyMatch=FALSE;
int longestMatch=0;
int ph;
int numOut=0; // The number of bytes copied to the output for the longest match
// Get next phoneme, P2
for(ph = 0; ph<numPhoneme; ph++){
int numChars;
// Locate start of next phoneme
const char* ph_text = (const char*)pgm_read_word(&phoneme[ph].txt);
// Set 'numChars' to the number of characters
// that we match against this phoneme
for(numChars=0;textp[numChars]!=0 ;numChars++){
// get next input character and make lower case
char nextChar = textp[numChars];
if(nextChar>='A' && nextChar<='Z'){
nextChar = nextChar - 'A' + 'a';
}
if(nextChar!=pgm_read_byte(&ph_text[numChars])){
break;
}
}
// if not the longest match so far then ignore
if(numChars <= longestMatch) continue;
if(pgm_read_byte(&ph_text[numChars])!=0){
// partial phoneme match
continue;
}
// P7: we have matched the whole phoneme
longestMatch = numChars;
// Copy phoneme data to 'phonemes'
{
const char* ph_ph = (const char*)pgm_read_word(&phoneme[ph].phoneme);
for(numOut=0; pgm_read_byte(&ph_ph[numOut])!= 0; numOut++){
phonemes[phonemeOut+numOut] = pgm_read_byte(&ph_ph[numOut]);
}
}
L81 = pgm_read_byte(&phoneme[ph].attenuate)+'0';
anyMatch=TRUE; // phoneme match found
modifier[modifierOut]=-1;
modifier[modifierOut+1]=0;
// Get char from text after the phoneme and test if it is a numeric
if(textp[longestMatch]>='0' && textp[longestMatch]<='9'){
// Pitch change requested
modifier[modifierOut] = pgm_read_byte(&PitchesP[textp[longestMatch]-'1'] );
modifier[modifierOut+1] = L81;
longestMatch++;
}
// P10
if(L81!='0' && L81 != L80 && modifier[modifierOut]>=0){
modifier[modifierOut - 2] = modifier[modifierOut];
modifier[modifierOut - 1] = '0';
continue;
}
// P11
if( (textp[longestMatch-1] | 0x20) == 0x20){
// end of input string or a space
modifier[modifierOut] = (modifierOut==0) ? 16 : modifier[modifierOut-2];
}
} // next phoneme
// p13
L80 = L81;
if(longestMatch==0 && anyMatch==FALSE){
loggerP(PSTR("Mistake in speech at "));
logger(textp);
loggerCRLF();
return 0;
}
// Move over the bytes we have copied to the output
phonemeOut += numOut;
if(phonemeOut > sizeof(phonemes)-16){
loggerP(PSTR("Line too long\n"));
return 0;
}
// P16
// Copy the modifier setting to each sound data element for this phoneme
if(numOut > 2){
int count;
for(count=0; count != numOut; count+=2){
modifier[modifierOut + count + 2] = modifier[modifierOut + count];
modifier[modifierOut + count + 3] = 0;
}
}
modifierOut += numOut;
//p21
textp += longestMatch;
}
phonemes[phonemeOut++]='z';
phonemes[phonemeOut++]='z';
phonemes[phonemeOut++]='z';
phonemes[phonemeOut++]='z';
while(phonemeOut < sizeof(phonemes)){
phonemes[phonemeOut++]=0;
}
while(modifierOut < sizeof(modifier)){
modifier[modifierOut++]=-1;
modifier[modifierOut++]=0;
}
return 1;
}
/*
* A delay loop that doesn't change with different optimisation settings
*/
void loop(uint8_t delay){
#ifdef _AVR_
__asm__ volatile (
"1: dec %0" "\n\t"
"brne 1b"
: "=r" (delay)
: "0" (delay)
);
#endif
}
void pause(uint8_t delay){
uint8_t r;
for(r=TIME_FACTOR; r>0; r--){
loop(delay);
}
}
void delay(uint8_t d){
while(d!=0){
pause(0); // 256
pause(0); // 256
d--;
}
}
/*
Generate a random number
*/
uint8_t random(void){
uint8_t tmp = (seed0 & 0x48) + 0x38;
seed0<<=1;
if(seed1 & 0x80){
seed0++;
}
seed1<<=1;
if(seed2 & 0x80){
seed1++;
}
seed2<<=1;
if(tmp & 0x40){
seed2++;
}
return seed0;
}
void soundOff(void){
#ifdef _AVR_
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -