├── src ├── render.h ├── reciter.h ├── esp8266sam_debug.h ├── sam.h ├── SamData.h ├── esp8266sam_debug.c ├── ESP8266SAM.h ├── ESP8266SAM.cpp ├── SamTabs.h ├── reciter.c ├── RenderTabs.h ├── ReciterTabs.h ├── render.c └── sam.c ├── library.properties ├── examples ├── Speak │ └── Speak.ino ├── SpeakNoDac │ └── SpeakNoDac.ino └── remoteSAM │ └── remoteSAM.ino ├── tools ├── README.md └── makever.py ├── library.json └── README.md /src/render.h: -------------------------------------------------------------------------------- 1 | #ifndef RENDER_H 2 | #define RENDER_H 3 | 4 | void Render(); 5 | void SetMouthThroat(unsigned char mouth, unsigned char throat); 6 | 7 | #endif 8 | -------------------------------------------------------------------------------- /src/reciter.h: -------------------------------------------------------------------------------- 1 | #ifndef RECITER_C 2 | #define RECITER_C 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | //int TextToPhonemes(char *input, char *output); 9 | 10 | int TextToPhonemes(char *input); 11 | 12 | #ifdef __cplusplus 13 | } 14 | #endif 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=ESP8266SAM 2 | version=1.1.0 3 | author=Earle F. Philhower, III 4 | maintainer=Earle F. Philhower, III 5 | sentence=Speech synthesis on the ESP8266, ESP32, and RP2040 6 | paragraph=Speech synthesis program SAM ported to ESP8266/ESP32/RP2040 and ESP8266Audio 7 | category=Signal Input/Output 8 | url=https://github.com/earlephilhower/ESP8266SAM 9 | architectures=esp8266,esp32,rp2040 10 | -------------------------------------------------------------------------------- /examples/Speak/Speak.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | AudioOutputI2S *out = NULL; 6 | 7 | void setup() 8 | { 9 | out = new AudioOutputI2S(); 10 | out->begin(); 11 | } 12 | 13 | void loop() 14 | { 15 | ESP8266SAM *sam = new ESP8266SAM; 16 | sam->Say(out, "Can you hear me now?"); 17 | delay(500); 18 | sam->Say(out, "I can't hear you!"); 19 | delete sam; 20 | } 21 | -------------------------------------------------------------------------------- /examples/SpeakNoDac/SpeakNoDac.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "AudioOutputI2SNoDAC.h" 4 | 5 | AudioOutputI2SNoDAC *out = NULL; 6 | 7 | void setup() 8 | { 9 | out = new AudioOutputI2SNoDAC(); 10 | out->begin(); 11 | } 12 | 13 | void loop() 14 | { 15 | ESP8266SAM *sam = new ESP8266SAM; 16 | sam->Say(out, "Can you hear me now?"); 17 | delay(500); 18 | sam->Say(out, "I can't hear you!"); 19 | delete sam; 20 | } 21 | -------------------------------------------------------------------------------- /tools/README.md: -------------------------------------------------------------------------------- 1 | Publishing New Releases 2 | ======================= 3 | 4 | First, update the version number throughout the repo and push the change: 5 | 6 | ./tools/makever.py --version X.Y.Z 7 | git commit -a -m "Update version" 8 | git push 9 | 10 | Then tag it 11 | 12 | git tag X.Y.Z 13 | git push origin X.Y.Z 14 | 15 | Then on the GH web interface it will generate a draft release from that tag 16 | that can be released if proper through the GUI. 17 | -------------------------------------------------------------------------------- /src/esp8266sam_debug.h: -------------------------------------------------------------------------------- 1 | #ifndef ESP8266SAM_DEBUG_H 2 | #define ESP8266SAM_DEBUG_H 3 | 4 | void PrintPhonemes(unsigned char *phonemeindex, unsigned char *phonemeLength, unsigned char *stress); 5 | void PrintOutput( 6 | unsigned char *flag, 7 | unsigned char *f1, 8 | unsigned char *f2, 9 | unsigned char *f3, 10 | unsigned char *a1, 11 | unsigned char *a2, 12 | unsigned char *a3, 13 | unsigned char *p); 14 | 15 | void PrintRule(int offset); 16 | 17 | #define DEBUG_ESP8266SAM_LIB 0 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /src/sam.h: -------------------------------------------------------------------------------- 1 | #ifndef SAM_H 2 | #define SAM_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | void SetInput(char *_input); 9 | void SetSpeed(unsigned char _speed); 10 | void SetPitch(unsigned char _pitch); 11 | void SetMouth(unsigned char _mouth); 12 | void SetThroat(unsigned char _throat); 13 | void EnableSingmode(int x); 14 | 15 | int SAMMain( void (*cb)(void *, unsigned char), void *cbdata ); 16 | 17 | int GetBufferLength(); 18 | 19 | int SAMPrepare(); 20 | 21 | 22 | 23 | #ifdef __cplusplus 24 | } 25 | #endif 26 | 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ESP8266SAM", 3 | "description": "Speech synthesizer SAM ported to ESP8266/ESP32/RP2040", 4 | "keywords": "ESP8266, ESP32, RP2040, Speecn Synthesis, SAM", 5 | "authors": [ 6 | { 7 | "name": "Earle F. Philhower, III", 8 | "email": "earlephilhower@yahoo.com", 9 | "url": "https://github.com/earlephilhower/ESP8266SAM", 10 | "maintainer": true 11 | } 12 | ], 13 | "repository": { 14 | "type": "git", 15 | "url": "https://github.com/earlephilhower/ESP8266SAM" 16 | }, 17 | "version": "1.1.0", 18 | "homepage": "https://github.com/earlephilhower/ESP8266SAM", 19 | "frameworks": "Arduino", 20 | "examples": [ 21 | "examples/*/*.ino" 22 | ] 23 | } -------------------------------------------------------------------------------- /src/SamData.h: -------------------------------------------------------------------------------- 1 | /* 2 | * dataspace.h 3 | * 4 | * Created on: Feb 24, 2019 5 | * Author: chris.l 6 | */ 7 | 8 | #ifndef SAMDATA_H_ 9 | #define SAMDATA_H_ 10 | 11 | #ifdef __cplusplus 12 | extern "C" { 13 | #endif 14 | 15 | #define SAMDATA 16 | 17 | typedef struct s_samdata 18 | { 19 | struct render 20 | { 21 | unsigned char pitches[256]; // tab43008 22 | unsigned char frequency1[256]; 23 | unsigned char frequency2[256]; 24 | unsigned char frequency3[256]; 25 | unsigned char amplitude1[256]; 26 | unsigned char amplitude2[256]; 27 | unsigned char amplitude3[256]; 28 | unsigned char sampledConsonantFlag[256]; // tab44800 29 | } render; 30 | struct reciter { 31 | unsigned char inputtemp[256]; 32 | } reciter; 33 | struct sam 34 | { 35 | char input[256]; //tab39445 36 | unsigned char stress[256]; //numbers from 0 to 8 37 | unsigned char phonemeLength[256]; //tab40160 38 | unsigned char phonemeindex[256]; 39 | unsigned char phonemeIndexOutput[60]; //tab47296 40 | unsigned char stressOutput[60]; //tab47365 41 | unsigned char phonemeLengthOutput[60]; //tab47416 42 | } sam; 43 | } SamData; 44 | 45 | extern SamData* samdata; 46 | 47 | #ifdef __cplusplus 48 | } 49 | #endif 50 | 51 | 52 | 53 | #endif /* SAMDATA_H_ */ 54 | -------------------------------------------------------------------------------- /tools/makever.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import sys 3 | import struct 4 | import subprocess 5 | import re 6 | import os 7 | import os.path 8 | import argparse 9 | import time 10 | import shutil 11 | import json 12 | 13 | def main(): 14 | parser = argparse.ArgumentParser(description='Version updater') 15 | parser.add_argument('-v', '--version', action='store', required=True, help='Version in X.Y.Z form') 16 | args = parser.parse_args() 17 | 18 | major, minor, sub = args.version.split(".") 19 | # Silly way to check for integer x.y.z 20 | major = int(major) 21 | minor = int(minor) 22 | sub = int(sub) 23 | 24 | # library.properties 25 | with open("library.properties", "r") as fin: 26 | with open("library.properties.new", "w") as fout: 27 | for l in fin: 28 | if l.startswith("version="): 29 | l = "version=" + str(args.version) + "\n" 30 | fout.write(l); 31 | shutil.move("library.properties.new", "library.properties") 32 | 33 | # package.json 34 | with open("library.json", "r") as fin: 35 | library = json.load(fin); 36 | library["version"] = str(args.version); 37 | with open("library.json.new", "w") as fout: 38 | json.dump(library, fout, indent = 4); 39 | shutil.move("library.json.new", "library.json") 40 | 41 | main() 42 | -------------------------------------------------------------------------------- /src/esp8266sam_debug.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | extern unsigned char signInputTable1[]; 4 | extern unsigned char signInputTable2[]; 5 | 6 | void PrintPhonemes(unsigned char *phonemeindex, unsigned char *phonemeLength, unsigned char *stress) 7 | { 8 | int i = 0; 9 | printf("===========================================\n"); 10 | 11 | printf("Internal Phoneme presentation:\n\n"); 12 | printf(" idx phoneme length stress\n"); 13 | printf("------------------------------\n"); 14 | 15 | while((phonemeindex[i] != 255) && (i < 255)) 16 | { 17 | if (phonemeindex[i] < 81) 18 | { 19 | printf(" %3i %c%c %3i %i\n", 20 | phonemeindex[i], 21 | signInputTable1[phonemeindex[i]], 22 | signInputTable2[phonemeindex[i]], 23 | phonemeLength[i], 24 | stress[i] 25 | ); 26 | } else 27 | { 28 | printf(" %3i ?? %3i %i\n", phonemeindex[i], phonemeLength[i], stress[i]); 29 | } 30 | i++; 31 | } 32 | printf("===========================================\n"); 33 | printf("\n"); 34 | } 35 | 36 | void PrintOutput( 37 | unsigned char *flag, 38 | unsigned char *f1, 39 | unsigned char *f2, 40 | unsigned char *f3, 41 | unsigned char *a1, 42 | unsigned char *a2, 43 | unsigned char *a3, 44 | unsigned char *p) 45 | { 46 | printf("===========================================\n"); 47 | printf("Final data for speech output:\n\n"); 48 | int i = 0; 49 | printf(" flags ampl1 freq1 ampl2 freq2 ampl3 freq3 pitch\n"); 50 | printf("------------------------------------------------\n"); 51 | while(i < 255) 52 | { 53 | printf("%5i %5i %5i %5i %5i %5i %5i %5i\n", flag[i], a1[i], f1[i], a2[i], f2[i], a3[i], f3[i], p[i]); 54 | i++; 55 | } 56 | printf("===========================================\n"); 57 | 58 | } 59 | 60 | extern unsigned char GetRuleByte(unsigned short mem62, unsigned char Y); 61 | 62 | void PrintRule(int offset) 63 | { 64 | int i = 1; 65 | unsigned char A = 0; 66 | printf("Applying rule: "); 67 | do 68 | { 69 | A = GetRuleByte(offset, i); 70 | if ((A&127) == '=') printf(" -> "); else printf("%c", A&127); 71 | i++; 72 | } while ((A&128)==0); 73 | printf("\n"); 74 | } 75 | -------------------------------------------------------------------------------- /src/ESP8266SAM.h: -------------------------------------------------------------------------------- 1 | /* 2 | ESP8266SAM 3 | Port of SAM to the ESP8266 4 | 5 | Copyright (C) 2017 Earle F. Philhower, III 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | You should have received a copy of the GNU General Public License 15 | along with this program. If not, see . 16 | */ 17 | 18 | #ifndef _ESP8266SAM_H 19 | #define _ESP8266SAM_H 20 | 21 | #include 22 | #include 23 | 24 | class ESP8266SAM { 25 | 26 | public: 27 | ESP8266SAM() 28 | { 29 | singmode = false; 30 | phonetic = false; 31 | pitch = 0; 32 | mouth = 0; 33 | throat = 0; 34 | speed = 0; 35 | output = NULL; 36 | }; 37 | 38 | ~ESP8266SAM() 39 | { 40 | } 41 | 42 | enum SAMVoice { VOICE_SAM, VOICE_ELF, VOICE_ROBOT, VOICE_STUFFY, VOICE_OLDLADY, VOICE_ET }; 43 | void SetVoice(enum SAMVoice voice); 44 | 45 | void SetSingMode(bool val) { singmode = val; } 46 | void SetPhonetic(bool val) { phonetic = val; } 47 | void SetPitch(uint8_t val) { pitch = val; } 48 | void SetMouth(uint8_t val) { mouth = val; } 49 | void SetThroat(uint8_t val) { throat = val; } 50 | void SetSpeed(uint8_t val) { speed = val; } 51 | 52 | bool Say(AudioOutput *out, const char *str); 53 | bool Say_P(AudioOutput *out, const char *str) { 54 | char ram[256]; 55 | strncpy_P(ram, str, 256); 56 | ram[255] = 0; 57 | return Say(out, ram); 58 | }; 59 | 60 | private: 61 | static void OutputByteCallback(void *cbdata, unsigned char b); 62 | void OutputByte(unsigned char b); 63 | bool singmode; 64 | bool phonetic; 65 | int pitch; 66 | int speed; 67 | int mouth; 68 | int throat; 69 | AudioOutput *output; 70 | }; 71 | 72 | #endif 73 | 74 | -------------------------------------------------------------------------------- /src/ESP8266SAM.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ESP8266SAM 3 | Port of SAM to the ESP8266 4 | 5 | Copyright (C) 2017 Earle F. Philhower, III 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | You should have received a copy of the GNU General Public License 15 | along with this program. If not, see . 16 | */ 17 | 18 | 19 | #include 20 | #include 21 | 22 | #include "reciter.h" 23 | #include "sam.h" 24 | #include "SamData.h" 25 | 26 | SamData* samdata; 27 | 28 | // Thunk from C to C++ with a this-> pointer 29 | void ESP8266SAM::OutputByteCallback(void *cbdata, unsigned char b) 30 | { 31 | ESP8266SAM *sam = static_cast(cbdata); 32 | sam->OutputByte(b); 33 | } 34 | 35 | void ESP8266SAM::OutputByte(unsigned char b) 36 | { 37 | // Upsample from unsigned 8 bits to signed 16 bits 38 | int16_t sample[2]; 39 | sample[0] = b; 40 | sample[0] = (((int16_t)(sample[0] & 0xff)) - 128) << 8; 41 | sample[1] = sample[0]; 42 | while (!output->ConsumeSample(sample)) yield(); 43 | } 44 | 45 | bool ESP8266SAM::Say(AudioOutput *out, const char *str) 46 | { 47 | if (!str || strlen(str)>254) return false; // Only can speak up to 1 page worth of data... 48 | samdata = new SamData; 49 | if (samdata == nullptr) 50 | { 51 | // allocation failed! 52 | return false; 53 | } 54 | 55 | // These are fixed by the synthesis routines 56 | out->SetRate(22050); 57 | out->SetChannels(1); 58 | out->begin(); 59 | 60 | // SAM settings 61 | EnableSingmode(singmode); 62 | if (speed) ::SetSpeed(speed); 63 | if (pitch) ::SetPitch(pitch); 64 | if (mouth) ::SetMouth(mouth); 65 | if (throat) ::SetThroat(throat); 66 | 67 | // Input massaging 68 | char input[256]; 69 | for (int i=0; str[i]; i++) 70 | input[i] = toupper((int)str[i]); 71 | input[strlen(str)] = 0; 72 | 73 | // To phonemes 74 | if (phonetic) { 75 | strncat(input, "\x9b", 255); 76 | } else { 77 | strncat(input, "[", 255); 78 | if (!TextToPhonemes(input)) return false; // ERROR 79 | } 80 | 81 | // Say it! 82 | output = out; 83 | SetInput(input); 84 | SAMMain(OutputByteCallback, (void*)this); 85 | delete samdata; 86 | return true; 87 | } 88 | 89 | void ESP8266SAM::SetVoice(enum SAMVoice voice) 90 | { 91 | switch (voice) { 92 | case VOICE_ELF: SetSpeed(72); SetPitch(64); SetThroat(110); SetMouth(160); break; 93 | case VOICE_ROBOT: SetSpeed(92); SetPitch(60); SetThroat(190); SetMouth(190); break; 94 | case VOICE_STUFFY: SetSpeed(82); SetPitch(72); SetThroat(110); SetMouth(105); break; 95 | case VOICE_OLDLADY: SetSpeed(82); SetPitch(32); SetThroat(145); SetMouth(145); break; 96 | case VOICE_ET: SetSpeed(100); SetPitch(64); SetThroat(150); SetMouth(200); break; 97 | default: 98 | case VOICE_SAM: SetSpeed(72); SetPitch(64); SetThroat(128); SetMouth(128); break; 99 | } 100 | } 101 | 102 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ESP8266SAM 2 | Speech synthesis for ESP8266 and ESP-32 using ported SAM code 3 | 4 | This is a port, wrapper, and update of the reverse-engineered speech synthesizer Software Automatic Mouth (SAM). Utilize it with the [ESP8266Audio library](https://github.com/earlephilhower/ESP8266Audio/) to have your ESP speak via a DAC or a direct-drive speaker. No web services are required, everything from text parsing to speech generation is done directly on the ESP. 5 | 6 | [Original sources](https://github.com/s-macke/SAM) 7 | 8 | This version has been reworked to generate full 8-bit speech formants as well as proper time-series waveforms. 9 | 10 | ## ESP32 and Raspberry Pi Pico RP2040 and Pico 2 RP2350 Note 11 | 12 | Consider using [BackgroundAudio](https://github.com/earlephilhower/BackgroundAudio) instead which uses `espeak-ng` to produce higher quality speech, in the background while your sketch does other things. (Unfortunately, Espeak-NG needs more memory then the ESP8266 has available.) 13 | 14 | ## Usage 15 | You will need: 16 | 1. The ESP8266Audio library installed in your Arduino/libraries folder ( git clone https://github.com/earlephilhower/ESP8266Audio ) 17 | 2. An audio output device compatible with ESP8266Audio. There are two described in its readme, and either the DAC or 1-transistor amplifier will work fine. 18 | 19 | Then in your code instantiate an AudioOutput object, ->begin() it, and then simply create the SAM object and call Say(). 20 | 21 | ## About SAM 22 | SAM is a very basic speech synthesizer originally developed in 1979 and ported to the Commodore 64. It was designed to run on very low memory 8-bit processors, and as such is a great for the ESP8266 with it's limited RAM. 23 | 24 | The quality of speech isn't phenomenal. To paraphrase Samuel Johnson, "It is not done well; but you are surprised to find it done at all." The same could be said of the decompiled, disassembled, C-translated source code. 25 | 26 | Output is fixed at 22050Hz due to some hardcoded delays to help match C64 internal timing parameters. 27 | 28 | The voice is formant generated and can be modified by setting things such as speed, pitch, mouth and throat configuration, and even sending in phonetic codes instead of English text. See @s-macke's repository for more information. 29 | 30 | ## Cool uses of SAM 31 | Jan Derogee has used ESP8266SAM to build a complete VIC-20 compatible speech synthesis cartridge that works with BASIC applications. It also has a great background on early speech synthesis and a neat construction and demo video. Check it out at https://janderogee.com/projects/SerialSpeechSynthesisSAM/SerialSpeechSynthesisSAM.htm 32 | 33 | Ken McMullan has used an ESP8266 to create a type of MODEM allowing the control of a set of Internet of Things (IoT) devices around his home from a Ham Radio transmitter. ESP8266SAM is employed to give feedback. For example, it is possible to query the temperature around the house, or in the garden, or switch on/off various lights, and teh response comes from the inimitable Speak and Spell voice! Take a look at: https://github.com/KDMcMullan/HaMQTT . 34 | 35 | ## License 36 | While the ESP8266 wrapper is my own, the SAM software is a reverse-engineered version of a software published more than 34 years ago by "Don't ask Software". 37 | 38 | The company no longer exists. Any attempt to contact the original authors failed. Hence S.A.M. can be best described as Abandonware (http://en.wikipedia.org/wiki/Abandonware) 39 | 40 | As long this is the case I cannot put my code under any specific open source software license. However the software might be used under the "Fair Use" act (https://en.wikipedia.org/wiki/FAIR_USE_Act) in the USA. 41 | -------------------------------------------------------------------------------- /examples/remoteSAM/remoteSAM.ino: -------------------------------------------------------------------------------- 1 | // In a webbrowser go to http://sam.local/say/{message} to make it speak 2 | // ex: http://sam.local/say/hello world 3 | 4 | #include 5 | 6 | #if !defined(ESP8266) 7 | void setup() { 8 | Serial.begin(115200); 9 | Serial.println("This example is only for the ESP8266"); 10 | } 11 | 12 | void loop() { 13 | } 14 | #else 15 | 16 | #include 17 | #include "AudioOutputI2SNoDAC.h" 18 | 19 | #include 20 | #include 21 | #include //Library for SSDP (Show ESP in Network on Windows) 22 | #include //Library for WebServer 23 | #include 24 | #include 25 | 26 | AudioOutputI2SNoDAC *out = NULL; 27 | 28 | ESP8266WebServer server(80); //Web Server on port 80 29 | const char* NAME = "SAM"; 30 | const char *ssid = ""; 31 | const char *pass = ""; 32 | 33 | void setup() 34 | { 35 | Serial.begin(115200); 36 | out = new AudioOutputI2SNoDAC(); 37 | out->begin(); 38 | 39 | WiFi.mode(WIFI_STA); 40 | WiFi.begin(ssid, pass); 41 | 42 | while (WiFi.status() != WL_CONNECTED) { 43 | delay(500); 44 | Serial.print("."); 45 | } 46 | Serial.println(""); 47 | 48 | Serial.println("WiFi connected"); 49 | Serial.println("IP address: "); 50 | Serial.println(WiFi.localIP()); 51 | 52 | MDNS.begin(NAME); 53 | MDNS.addService("http", "tcp", 80); 54 | NBNS.begin(NAME); 55 | 56 | server.on(UriBraces("/say/{}"), []() { 57 | String message_encoded = server.pathArg(0); 58 | String message_decoded = urldecode(message_encoded); 59 | const char* message = message_decoded.c_str(); 60 | 61 | Serial.println(message_encoded); 62 | Serial.println(message_decoded); 63 | Serial.println(message); 64 | 65 | ESP8266SAM *sam = new ESP8266SAM; 66 | sam->Say(out, message); 67 | delete sam; 68 | server.send(200, "text/plain", "OK"); 69 | }); 70 | 71 | server.on("/description.xml", HTTP_GET, []() { 72 | SSDP.schema(server.client()); 73 | }); 74 | server.begin(); 75 | ssdp(); 76 | } 77 | 78 | void ssdp() { 79 | //Simple Service Discovery Protocol : Display ESP in Windows Network Tab 80 | SSDP.setSchemaURL("description.xml"); 81 | SSDP.setHTTPPort(80); 82 | SSDP.setName(NAME); 83 | SSDP.setDeviceType("upnp: rootdevice"); 84 | SSDP.setSerialNumber("000000000001"); 85 | SSDP.setURL("/say/connected"); 86 | SSDP.setModelName("ESP8266SAM"); 87 | SSDP.setModelNumber("0000000000001"); 88 | SSDP.setModelURL("https://github.com/earlephilhower/ESP8266SAM"); 89 | SSDP.setManufacturer("earlephilhower"); 90 | SSDP.setManufacturerURL("https://github.com/earlephilhower/"); 91 | SSDP.begin(); 92 | } 93 | 94 | void loop() { 95 | server.handleClient(); 96 | } 97 | 98 | char* string2char(String command) { 99 | if (command.length() != 0) { 100 | char *p = const_cast(command.c_str()); 101 | return p; 102 | } else { 103 | return ""; 104 | } 105 | } 106 | 107 | unsigned char h2int(char c) { 108 | if (c >= '0' && c <= '9') { 109 | return ((unsigned char)c - '0'); 110 | } 111 | if (c >= 'a' && c <= 'f') { 112 | return ((unsigned char)c - 'a' + 10); 113 | } 114 | if (c >= 'A' && c <= 'F') { 115 | return ((unsigned char)c - 'A' + 10); 116 | } 117 | return (0); 118 | } 119 | 120 | String urldecode(String str) 121 | { 122 | 123 | String encodedString = ""; 124 | char c; 125 | char code0; 126 | char code1; 127 | for (int i = 0; i < str.length(); i++) { 128 | c = str.charAt(i); 129 | if (c == '+') { 130 | encodedString += ' '; 131 | } else if (c == '%') { 132 | i++; 133 | code0 = str.charAt(i); 134 | i++; 135 | code1 = str.charAt(i); 136 | c = (h2int(code0) << 4) | h2int(code1); 137 | encodedString += c; 138 | } else { 139 | 140 | encodedString += c; 141 | } 142 | 143 | yield(); 144 | } 145 | 146 | return encodedString; 147 | } 148 | 149 | #endif 150 | -------------------------------------------------------------------------------- /src/SamTabs.h: -------------------------------------------------------------------------------- 1 | #ifndef SAMTABS_H 2 | #define SAMTABS_H 3 | 4 | #include 5 | #include "esp8266sam_debug.h" 6 | #if DEBUG_ESP8266SAM_LIB 7 | #define PROGMEM 8 | #endif 9 | 10 | //tab40672 11 | const unsigned char stressInputTable[] PROGMEM = 12 | { 13 | '*', '1', '2', '3', '4', '5', '6', '7', '8' 14 | }; 15 | 16 | //tab40682 17 | const unsigned char signInputTable1[] PROGMEM = 18 | { 19 | ' ', '.', '?', ',', '-', 'I', 'I', 'E', 20 | 'A', 'A', 'A', 'A', 'U', 'A', 'I', 'E', 21 | 'U', 'O', 'R', 'L', 'W', 'Y', 'W', 'R', 22 | 'L', 'W', 'Y', 'M', 'N', 'N', 'D', 'Q', 23 | 'S', 'S', 'F', 'T', '/', '/', 'Z', 'Z', 24 | 'V', 'D', 'C', '*', 'J', '*', '*', '*', 25 | 'E', 'A', 'O', 'A', 'O', 'U', 'B', '*', 26 | '*', 'D', '*', '*', 'G', '*', '*', 'G', 27 | '*', '*', 'P', '*', '*', 'T', '*', '*', 28 | 'K', '*', '*', 'K', '*', '*', 'U', 'U', 29 | 'U' 30 | }; 31 | 32 | //tab40763 33 | const unsigned char signInputTable2[] PROGMEM = 34 | { 35 | '*', '*', '*', '*', '*', 'Y', 'H', 'H', 36 | 'E', 'A', 'H', 'O', 'H', 'X', 'X', 'R', 37 | 'X', 'H', 'X', 'X', 'X', 'X', 'H', '*', 38 | '*', '*', '*', '*', '*', 'X', 'X', '*', 39 | '*', 'H', '*', 'H', 'H', 'X', '*', 'H', 40 | '*', 'H', 'H', '*', '*', '*', '*', '*', 41 | 'Y', 'Y', 'Y', 'W', 'W', 'W', '*', '*', 42 | '*', '*', '*', '*', '*', '*', '*', 'X', 43 | '*', '*', '*', '*', '*', '*', '*', '*', 44 | '*', '*', '*', 'X', '*', '*', 'L', 'M', 45 | 'N' 46 | }; 47 | 48 | //loc_9F8C 49 | const unsigned char flags[]={ 50 | 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0xA4 , 0xA4 , 0xA4 , 51 | 0xA4 , 0xA4 , 0xA4 , 0x84 , 0x84 , 0xA4 , 0xA4 , 0x84 , 52 | 0x84 , 0x84 , 0x84 , 0x84 , 0x84 , 0x84 , 0x44 , 0x44 , 53 | 0x44 , 0x44 , 0x44 , 0x4C , 0x4C , 0x4C , 0x48 , 0x4C , 54 | 0x40 , 0x40 , 0x40 , 0x40 , 0x40 , 0x40 , 0x44 , 0x44 , 55 | 0x44 , 0x44 , 0x48 , 0x40 , 0x4C , 0x44 , 0x00 , 0x00 , 56 | 0xB4 , 0xB4 , 0xB4 , 0x94 , 0x94 , 0x94 , 0x4E , 0x4E , 57 | 0x4E , 0x4E , 0x4E , 0x4E , 0x4E , 0x4E , 0x4E , 0x4E , 58 | 0x4E , 0x4E , 0x4B , 0x4B , 0x4B , 0x4B , 0x4B , 0x4B , 59 | 0x4B , 0x4B , 0x4B , 0x4B , 0x4B , 0x4B , 0x80 , 0xC1 , 60 | 0xC1 61 | 62 | 63 | }; 64 | 65 | //??? flags overlap flags2 66 | //loc_9FDA 67 | const unsigned char flags2[] = 68 | { 69 | 0x80 , 0xC1 , 0xC1 , 0xC1 , 0xC1 , 0x00 , 0x00 , 0x00 , 70 | 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 71 | 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x10 , 72 | 0x10 , 0x10 , 0x10 , 0x08 , 0x0C , 0x08 , 0x04 , 0x40 , 73 | 0x24 , 0x20 , 0x20 , 0x24 , 0x00 , 0x00 , 0x24 , 0x20 , 74 | 0x20 , 0x24 , 0x20 , 0x20 , 0x00 , 0x20 , 0x00 , 0x00 , 75 | 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 76 | 0x00 , 0x04 , 0x04 , 0x04 , 0x00 , 0x00 , 0x00 , 0x00 , 77 | 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x04 , 0x04 , 0x04 , 78 | 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 79 | }; 80 | 81 | 82 | 83 | //tab45616??? 84 | const unsigned char phonemeStressedLengthTable[] PROGMEM = 85 | { 86 | 0x00 , 0x12 , 0x12 , 0x12 , 8 ,0xB , 9 ,0xB , 87 | 0xE ,0xF ,0xB , 0x10 ,0xC , 6 , 6 ,0xE , 88 | 0xC ,0xE ,0xC ,0xB , 8 , 8 ,0xB ,0xA , 89 | 9 , 8 , 8 , 8 , 8 , 8 , 3 , 5 , 90 | 2 , 2 , 2 , 2 , 2 , 2 , 6 , 6 , 91 | 8 , 6 , 6 , 2 , 9 , 4 , 2 , 1 , 92 | 0xE ,0xF ,0xF ,0xF ,0xE ,0xE , 8 , 2 , 93 | 2 , 7 , 2 , 1 , 7 , 2 , 2 , 7 , 94 | 2 , 2 , 8 , 2 , 2 , 6 , 2 , 2 , 95 | 7 , 2 , 4 , 7 , 1 , 4 , 5 , 5 96 | }; 97 | 98 | //tab45536??? 99 | const unsigned char phonemeLengthTable[] PROGMEM = 100 | { 101 | 0 , 0x12 , 0x12 , 0x12 , 8 , 8 , 8 , 8 , 102 | 8 ,0xB , 6 ,0xC ,0xA , 5 , 5 ,0xB , 103 | 0xA ,0xA ,0xA , 9 , 8 , 7 , 9 , 7 , 104 | 6 , 8 , 6 , 7 , 7 , 7 , 2 , 5 , 105 | 2 , 2 , 2 , 2 , 2 , 2 , 6 , 6 , 106 | 7 , 6 , 6 , 2 , 8 , 3 , 1 , 0x1E , 107 | 0xD ,0xC ,0xC ,0xC ,0xE , 9 , 6 , 1 , 108 | 2 , 5 , 1 , 1 , 6 , 1 , 2 , 6 , 109 | 1 , 2 , 8 , 2 , 2 , 4 , 2 , 2 , 110 | 6 , 1 , 4 , 6 , 1 , 4 , 0xC7 , 0xFF 111 | }; 112 | 113 | 114 | 115 | /* 116 | 117 | Ind | phoneme | flags | 118 | -----|---------|----------| 119 | 0 | * | 00000000 | 120 | 1 | .* | 00000000 | 121 | 2 | ?* | 00000000 | 122 | 3 | ,* | 00000000 | 123 | 4 | -* | 00000000 | 124 | 125 | VOWELS 126 | 5 | IY | 10100100 | 127 | 6 | IH | 10100100 | 128 | 7 | EH | 10100100 | 129 | 8 | AE | 10100100 | 130 | 9 | AA | 10100100 | 131 | 10 | AH | 10100100 | 132 | 11 | AO | 10000100 | 133 | 17 | OH | 10000100 | 134 | 12 | UH | 10000100 | 135 | 16 | UX | 10000100 | 136 | 15 | ER | 10000100 | 137 | 13 | AX | 10100100 | 138 | 14 | IX | 10100100 | 139 | 140 | DIPHTONGS 141 | 48 | EY | 10110100 | 142 | 49 | AY | 10110100 | 143 | 50 | OY | 10110100 | 144 | 51 | AW | 10010100 | 145 | 52 | OW | 10010100 | 146 | 53 | UW | 10010100 | 147 | 148 | 149 | 21 | YX | 10000100 | 150 | 20 | WX | 10000100 | 151 | 18 | RX | 10000100 | 152 | 19 | LX | 10000100 | 153 | 37 | /X | 01000000 | 154 | 30 | DX | 01001000 | 155 | 156 | 157 | 22 | WH | 01000100 | 158 | 159 | 160 | VOICED CONSONANTS 161 | 23 | R* | 01000100 | 162 | 24 | L* | 01000100 | 163 | 25 | W* | 01000100 | 164 | 26 | Y* | 01000100 | 165 | 27 | M* | 01001100 | 166 | 28 | N* | 01001100 | 167 | 29 | NX | 01001100 | 168 | 54 | B* | 01001110 | 169 | 57 | D* | 01001110 | 170 | 60 | G* | 01001110 | 171 | 44 | J* | 01001100 | 172 | 38 | Z* | 01000100 | 173 | 39 | ZH | 01000100 | 174 | 40 | V* | 01000100 | 175 | 41 | DH | 01000100 | 176 | 177 | unvoiced CONSONANTS 178 | 32 | S* | 01000000 | 179 | 33 | SH | 01000000 | 180 | 34 | F* | 01000000 | 181 | 35 | TH | 01000000 | 182 | 66 | P* | 01001011 | 183 | 69 | T* | 01001011 | 184 | 72 | K* | 01001011 | 185 | 42 | CH | 01001000 | 186 | 36 | /H | 01000000 | 187 | 188 | 43 | ** | 01000000 | 189 | 45 | ** | 01000100 | 190 | 46 | ** | 00000000 | 191 | 47 | ** | 00000000 | 192 | 193 | 194 | 55 | ** | 01001110 | 195 | 56 | ** | 01001110 | 196 | 58 | ** | 01001110 | 197 | 59 | ** | 01001110 | 198 | 61 | ** | 01001110 | 199 | 62 | ** | 01001110 | 200 | 63 | GX | 01001110 | 201 | 64 | ** | 01001110 | 202 | 65 | ** | 01001110 | 203 | 67 | ** | 01001011 | 204 | 68 | ** | 01001011 | 205 | 70 | ** | 01001011 | 206 | 71 | ** | 01001011 | 207 | 73 | ** | 01001011 | 208 | 74 | ** | 01001011 | 209 | 75 | KX | 01001011 | 210 | 76 | ** | 01001011 | 211 | 77 | ** | 01001011 | 212 | 213 | 214 | SPECIAL 215 | 78 | UL | 10000000 | 216 | 79 | UM | 11000001 | 217 | 80 | UN | 11000001 | 218 | 31 | Q* | 01001100 | 219 | 220 | */ 221 | 222 | #endif 223 | -------------------------------------------------------------------------------- /src/reciter.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "reciter.h" 4 | #include "ReciterTabs.h" 5 | #include "esp8266sam_debug.h" 6 | #include "SamData.h" 7 | 8 | unsigned char A, X, Y; 9 | //extern int debug; 10 | 11 | #define inputtemp (samdata->reciter.inputtemp) 12 | 13 | void Code37055(unsigned char mem59) 14 | { 15 | X = mem59; 16 | X--; 17 | A = inputtemp[X]; 18 | Y = A; 19 | A = pgm_read_byte(tab36376+Y); //tab36376[Y]; 20 | return; 21 | } 22 | 23 | void Code37066(unsigned char mem58) 24 | { 25 | X = mem58; 26 | X++; 27 | A = inputtemp[X]; 28 | Y = A; 29 | A = pgm_read_byte(tab36376+Y); //tab36376[Y]; 30 | } 31 | 32 | unsigned char GetRuleByte(unsigned short mem62, unsigned char Y) 33 | { 34 | unsigned int address = mem62; 35 | 36 | if (mem62 >= 37541) 37 | { 38 | address -= 37541; 39 | return pgm_read_byte(rules2+address+Y); //rules2[address+Y]; 40 | } 41 | address -= 32000; 42 | return pgm_read_byte(rules+address+Y); //rules[address+Y]; 43 | } 44 | 45 | int TextToPhonemes(char *input) // Code36484 46 | { 47 | //unsigned char *tab39445 = &mem[39445]; //input and output 48 | //unsigned char mem29; 49 | unsigned char mem56; //output position for phonemes 50 | unsigned char mem57; 51 | unsigned char mem58; 52 | unsigned char mem59; 53 | unsigned char mem60; 54 | unsigned char mem61; 55 | unsigned short mem62; // memory position of current rule 56 | 57 | unsigned char mem64; // position of '=' or current character 58 | unsigned char mem65; // position of ')' 59 | unsigned char mem66; // position of '(' 60 | unsigned char mem36653; 61 | 62 | inputtemp[0] = 32; 63 | 64 | // secure copy of input 65 | // because input will be overwritten by phonemes 66 | X = 1; 67 | Y = 0; 68 | do 69 | { 70 | //pos36499: 71 | A = input[Y] & 127; 72 | if ( A >= 112) A = A & 95; 73 | else if ( A >= 96) A = A & 79; 74 | 75 | inputtemp[X] = A; 76 | X++; 77 | Y++; 78 | } while (Y != 255); 79 | 80 | 81 | X = 255; 82 | inputtemp[X] = 27; 83 | mem61 = 255; 84 | 85 | 86 | pos36550: 87 | A = 255; 88 | mem56 = 255; 89 | 90 | 91 | pos36554: 92 | while(1) 93 | { 94 | mem61++; 95 | X = mem61; 96 | A = inputtemp[X]; 97 | mem64 = A; 98 | if (A == '[') 99 | { 100 | mem56++; 101 | X = mem56; 102 | A = 155; 103 | input[X] = 155; 104 | //goto pos36542; 105 | // Code39771(); //Code39777(); 106 | return 1; 107 | } 108 | 109 | //pos36579: 110 | if (A != '.') break; 111 | X++; 112 | Y = inputtemp[X]; 113 | A = pgm_read_byte(tab36376+Y)/*tab36376[Y]*/ & 1; 114 | if(A != 0) break; 115 | mem56++; 116 | X = mem56; 117 | A = '.'; 118 | input[X] = '.'; 119 | } //while 120 | 121 | 122 | //pos36607: 123 | A = mem64; 124 | Y = A; 125 | A = pgm_read_byte(tab36376+A); //tab36376[A]; 126 | mem57 = A; 127 | if((A&2) != 0) 128 | { 129 | mem62 = 37541; 130 | goto pos36700; 131 | } 132 | 133 | //pos36630: 134 | A = mem57; 135 | if(A != 0) goto pos36677; 136 | A = 32; 137 | inputtemp[X] = ' '; 138 | mem56++; 139 | X = mem56; 140 | if (X > 120) goto pos36654; 141 | input[X] = A; 142 | goto pos36554; 143 | 144 | // ----- 145 | 146 | //36653 is unknown. Contains position 147 | 148 | pos36654: 149 | input[X] = 155; 150 | A = mem61; 151 | mem36653 = A; 152 | // mem29 = A; // not used 153 | // Code36538(); das ist eigentlich 154 | return 1; 155 | //Code39771(); 156 | //go on if there is more input ??? 157 | mem61 = mem36653; 158 | goto pos36550; 159 | 160 | pos36677: 161 | A = mem57 & 128; 162 | if(A == 0) 163 | { 164 | //36683: BRK 165 | return 0; 166 | } 167 | 168 | // go to the right rules for this character. 169 | X = mem64 - 'A'; 170 | mem62 = pgm_read_byte(&tab37489[X]) | (pgm_read_byte(&tab37515[X])<<8); 171 | 172 | // ------------------------------------- 173 | // go to next rule 174 | // ------------------------------------- 175 | 176 | pos36700: 177 | 178 | // find next rule 179 | Y = 0; 180 | do 181 | { 182 | mem62 += 1; 183 | A = GetRuleByte(mem62, Y); 184 | } while ((A & 128) == 0); 185 | Y++; 186 | 187 | //pos36720: 188 | // find '(' 189 | while(1) 190 | { 191 | A = GetRuleByte(mem62, Y); 192 | if (A == '(') break; 193 | Y++; 194 | } 195 | mem66 = Y; 196 | 197 | //pos36732: 198 | // find ')' 199 | do 200 | { 201 | Y++; 202 | A = GetRuleByte(mem62, Y); 203 | } while(A != ')'); 204 | mem65 = Y; 205 | 206 | //pos36741: 207 | // find '=' 208 | do 209 | { 210 | Y++; 211 | A = GetRuleByte(mem62, Y); 212 | A = A & 127; 213 | } while (A != '='); 214 | mem64 = Y; 215 | 216 | X = mem61; 217 | mem60 = X; 218 | 219 | // compare the string within the bracket 220 | Y = mem66; 221 | Y++; 222 | //pos36759: 223 | while(1) 224 | { 225 | mem57 = inputtemp[X]; 226 | A = GetRuleByte(mem62, Y); 227 | if (A != mem57) goto pos36700; 228 | Y++; 229 | if(Y == mem65) break; 230 | X++; 231 | mem60 = X; 232 | } 233 | 234 | // the string in the bracket is correct 235 | 236 | //pos36787: 237 | A = mem61; 238 | mem59 = mem61; 239 | 240 | pos36791: 241 | while(1) 242 | { 243 | mem66--; 244 | Y = mem66; 245 | A = GetRuleByte(mem62, Y); 246 | mem57 = A; 247 | //36800: BPL 36805 248 | if ((A & 128) != 0) goto pos37180; 249 | X = A & 127; 250 | A = pgm_read_byte(tab36376+X)/*tab36376[X]*/ & 128; 251 | if (A == 0) break; 252 | X = mem59-1; 253 | A = inputtemp[X]; 254 | if (A != mem57) goto pos36700; 255 | mem59 = X; 256 | } 257 | 258 | //pos36833: 259 | A = mem57; 260 | if (A == ' ') goto pos36895; 261 | if (A == '#') goto pos36910; 262 | if (A == '.') goto pos36920; 263 | if (A == '&') goto pos36935; 264 | if (A == '@') goto pos36967; 265 | if (A == '^') goto pos37004; 266 | if (A == '+') goto pos37019; 267 | if (A == ':') goto pos37040; 268 | // Code42041(); //Error 269 | //36894: BRK 270 | return 0; 271 | 272 | // -------------- 273 | 274 | pos36895: 275 | Code37055(mem59); 276 | A = A & 128; 277 | if(A != 0) goto pos36700; 278 | pos36905: 279 | mem59 = X; 280 | goto pos36791; 281 | 282 | // -------------- 283 | 284 | pos36910: 285 | Code37055(mem59); 286 | A = A & 64; 287 | if(A != 0) goto pos36905; 288 | goto pos36700; 289 | 290 | // -------------- 291 | 292 | 293 | pos36920: 294 | Code37055(mem59); 295 | A = A & 8; 296 | if(A == 0) goto pos36700; 297 | pos36930: 298 | mem59 = X; 299 | goto pos36791; 300 | 301 | // -------------- 302 | 303 | pos36935: 304 | Code37055(mem59); 305 | A = A & 16; 306 | if(A != 0) goto pos36930; 307 | A = inputtemp[X]; 308 | if (A != 72) goto pos36700; 309 | X--; 310 | A = inputtemp[X]; 311 | if ((A == 67) || (A == 83)) goto pos36930; 312 | goto pos36700; 313 | 314 | // -------------- 315 | 316 | pos36967: 317 | Code37055(mem59); 318 | A = A & 4; 319 | if(A != 0) goto pos36930; 320 | A = inputtemp[X]; 321 | if (A != 72) goto pos36700; 322 | if ((A != 84) && (A != 67) && (A != 83)) goto pos36700; 323 | mem59 = X; 324 | goto pos36791; 325 | 326 | // -------------- 327 | 328 | 329 | pos37004: 330 | Code37055(mem59); 331 | A = A & 32; 332 | if(A == 0) goto pos36700; 333 | 334 | pos37014: 335 | mem59 = X; 336 | goto pos36791; 337 | 338 | // -------------- 339 | 340 | pos37019: 341 | X = mem59; 342 | X--; 343 | A = inputtemp[X]; 344 | if ((A == 'E') || (A == 'I') || (A == 'Y')) goto pos37014; 345 | goto pos36700; 346 | // -------------- 347 | 348 | pos37040: 349 | Code37055(mem59); 350 | A = A & 32; 351 | if(A == 0) goto pos36791; 352 | mem59 = X; 353 | goto pos37040; 354 | 355 | //--------------------------------------- 356 | 357 | 358 | pos37077: 359 | X = mem58+1; 360 | A = inputtemp[X]; 361 | if (A != 'E') goto pos37157; 362 | X++; 363 | Y = inputtemp[X]; 364 | X--; 365 | A = pgm_read_byte(tab36376+Y)/*tab36376[Y]*/ & 128; 366 | if(A == 0) goto pos37108; 367 | X++; 368 | A = inputtemp[X]; 369 | if (A != 'R') goto pos37113; 370 | pos37108: 371 | mem58 = X; 372 | goto pos37184; 373 | pos37113: 374 | if ((A == 83) || (A == 68)) goto pos37108; // 'S' 'D' 375 | if (A != 76) goto pos37135; // 'L' 376 | X++; 377 | A = inputtemp[X]; 378 | if (A != 89) goto pos36700; 379 | goto pos37108; 380 | 381 | pos37135: 382 | if (A != 70) goto pos36700; 383 | X++; 384 | A = inputtemp[X]; 385 | if (A != 85) goto pos36700; 386 | X++; 387 | A = inputtemp[X]; 388 | if (A == 76) goto pos37108; 389 | goto pos36700; 390 | 391 | pos37157: 392 | if (A != 73) goto pos36700; 393 | X++; 394 | A = inputtemp[X]; 395 | if (A != 78) goto pos36700; 396 | X++; 397 | A = inputtemp[X]; 398 | if (A == 71) goto pos37108; 399 | //pos37177: 400 | goto pos36700; 401 | 402 | // ----------------------------------------- 403 | 404 | pos37180: 405 | 406 | A = mem60; 407 | mem58 = A; 408 | 409 | pos37184: 410 | Y = mem65 + 1; 411 | 412 | //37187: CPY 64 413 | // if(? != 0) goto pos37194; 414 | if(Y == mem64) goto pos37455; 415 | mem65 = Y; 416 | //37196: LDA (62),y 417 | A = GetRuleByte(mem62, Y); 418 | mem57 = A; 419 | X = A; 420 | A = pgm_read_byte(tab36376+X)/*tab36376[X]*/ & 128; 421 | if(A == 0) goto pos37226; 422 | X = mem58+1; 423 | A = inputtemp[X]; 424 | if (A != mem57) goto pos36700; 425 | mem58 = X; 426 | goto pos37184; 427 | pos37226: 428 | A = mem57; 429 | if (A == 32) goto pos37295; // ' ' 430 | if (A == 35) goto pos37310; // '#' 431 | if (A == 46) goto pos37320; // '.' 432 | if (A == 38) goto pos37335; // '&' 433 | if (A == 64) goto pos37367; // '' 434 | if (A == 94) goto pos37404; // '' 435 | if (A == 43) goto pos37419; // '+' 436 | if (A == 58) goto pos37440; // ':' 437 | if (A == 37) goto pos37077; // '%' 438 | //pos37291: 439 | // Code42041(); //Error 440 | //37294: BRK 441 | return 0; 442 | 443 | // -------------- 444 | pos37295: 445 | Code37066(mem58); 446 | A = A & 128; 447 | if(A != 0) goto pos36700; 448 | pos37305: 449 | mem58 = X; 450 | goto pos37184; 451 | 452 | // -------------- 453 | 454 | pos37310: 455 | Code37066(mem58); 456 | A = A & 64; 457 | if(A != 0) goto pos37305; 458 | goto pos36700; 459 | 460 | // -------------- 461 | 462 | 463 | pos37320: 464 | Code37066(mem58); 465 | A = A & 8; 466 | if(A == 0) goto pos36700; 467 | 468 | pos37330: 469 | mem58 = X; 470 | goto pos37184; 471 | 472 | // -------------- 473 | 474 | pos37335: 475 | Code37066(mem58); 476 | A = A & 16; 477 | if(A != 0) goto pos37330; 478 | A = inputtemp[X]; 479 | if (A != 72) goto pos36700; 480 | X++; 481 | A = inputtemp[X]; 482 | if ((A == 67) || (A == 83)) goto pos37330; 483 | goto pos36700; 484 | 485 | // -------------- 486 | 487 | 488 | pos37367: 489 | Code37066(mem58); 490 | A = A & 4; 491 | if(A != 0) goto pos37330; 492 | A = inputtemp[X]; 493 | if (A != 72) goto pos36700; 494 | if ((A != 84) && (A != 67) && (A != 83)) goto pos36700; 495 | mem58 = X; 496 | goto pos37184; 497 | 498 | // -------------- 499 | 500 | pos37404: 501 | Code37066(mem58); 502 | A = A & 32; 503 | if(A == 0) goto pos36700; 504 | pos37414: 505 | mem58 = X; 506 | goto pos37184; 507 | 508 | // -------------- 509 | 510 | pos37419: 511 | X = mem58; 512 | X++; 513 | A = inputtemp[X]; 514 | if ((A == 69) || (A == 73) || (A == 89)) goto pos37414; 515 | goto pos36700; 516 | 517 | // ---------------------- 518 | 519 | pos37440: 520 | 521 | Code37066(mem58); 522 | A = A & 32; 523 | if(A == 0) goto pos37184; 524 | mem58 = X; 525 | goto pos37440; 526 | pos37455: 527 | Y = mem64; 528 | mem61 = mem60; 529 | 530 | if (DEBUG_ESP8266SAM_LIB) 531 | PrintRule(mem62); 532 | 533 | pos37461: 534 | //37461: LDA (62),y 535 | A = GetRuleByte(mem62, Y); 536 | mem57 = A; 537 | A = A & 127; 538 | if (A != '=') 539 | { 540 | mem56++; 541 | X = mem56; 542 | input[X] = A; 543 | } 544 | 545 | //37478: BIT 57 546 | //37480: BPL 37485 //not negative flag 547 | if ((mem57 & 128) == 0) goto pos37485; //??? 548 | goto pos36554; 549 | pos37485: 550 | Y++; 551 | goto pos37461; 552 | } 553 | -------------------------------------------------------------------------------- /src/RenderTabs.h: -------------------------------------------------------------------------------- 1 | #ifndef RENDERTABS_H 2 | #define RENDERTABS_H 3 | 4 | #include 5 | #include "esp8266sam_debug.h" 6 | #if DEBUG_ESP8266SAM_LIB 7 | #define PROGMEM 8 | #endif 9 | 10 | const unsigned char tab48426[5] PROGMEM = { 0x18, 0x1A, 0x17, 0x17, 0x17 }; 11 | 12 | const unsigned char tab47492[] PROGMEM = 13 | { 14 | 0 , 0 , 0xE0 , 0xE6 , 0xEC , 0xF3 , 0xF9 , 0 , 15 | 6 , 0xC , 6 16 | }; 17 | 18 | 19 | const unsigned char amplitudeRescale[] PROGMEM = 20 | { 21 | 0 , 1 , 2 , 2 , 2 , 3 , 3 , 4 , 22 | 4 , 5 , 6 , 8 , 9 ,0xB ,0xD ,0xF, 0 //17 elements? 23 | }; 24 | 25 | // Used to decide which phoneme's blend lengths. The candidate with the lower score is selected. 26 | // tab45856 27 | const unsigned char blendRank[] PROGMEM = 28 | { 29 | 0 , 0x1F , 0x1F , 0x1F , 0x1F , 2 , 2 , 2 , 30 | 2 , 2 , 2 , 2 , 2 , 2 , 5 , 5 , 31 | 2 ,0xA , 2 , 8 , 5 , 5 ,0xB ,0xA , 32 | 9 , 8 , 8 , 0xA0 , 8 , 8 , 0x17 , 0x1F , 33 | 0x12 , 0x12 , 0x12 , 0x12 , 0x1E , 0x1E , 0x14 , 0x14 , 34 | 0x14 , 0x14 , 0x17 , 0x17 , 0x1A , 0x1A , 0x1D , 0x1D , 35 | 2 , 2 , 2 , 2 , 2 , 2 , 0x1A , 0x1D , 36 | 0x1B , 0x1A , 0x1D , 0x1B , 0x1A , 0x1D , 0x1B , 0x1A , 37 | 0x1D , 0x1B , 0x17 , 0x1D , 0x17 , 0x17 , 0x1D , 0x17 , 38 | 0x17 , 0x1D , 0x17 , 0x17 , 0x1D , 0x17 , 0x17 , 0x17 39 | }; 40 | 41 | 42 | // Number of frames at the end of a phoneme devoted to interpolating to next phoneme's final value 43 | //tab45696 44 | const unsigned char outBlendLength[] PROGMEM = 45 | { 46 | 0 , 2 , 2 , 2 , 2 , 4 , 4 , 4 , 47 | 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 48 | 4 , 4 , 3 , 2 , 4 , 4 , 2 , 2 , 49 | 2 , 2 , 2 , 1 , 1 , 1 , 1 , 1 , 50 | 1 , 1 , 1 , 1 , 1 , 1 , 2 , 2 , 51 | 2 , 1 , 0 , 1 , 0 , 1 , 0 , 5 , 52 | 5 , 5 , 5 , 5 , 4 , 4 , 2 , 0 , 53 | 1 , 2 , 0 , 1 , 2 , 0 , 1 , 2 , 54 | 0 , 1 , 2 , 0 , 2 , 2 , 0 , 1 , 55 | 3 , 0 , 2 , 3 , 0 , 2 , 0xA0 , 0xA0 56 | }; 57 | 58 | 59 | // Number of frames at beginning of a phoneme devoted to interpolating to phoneme's final value 60 | // tab45776 61 | const unsigned char inBlendLength[] PROGMEM = 62 | { 63 | 0 , 2 , 2 , 2 , 2 , 4 , 4 , 4 , 64 | 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 65 | 4 , 4 , 3 , 3 , 4 , 4 , 3 , 3 , 66 | 3 , 3 , 3 , 1 , 2 , 3 , 2 , 1 , 67 | 3 , 3 , 3 , 3 , 1 , 1 , 3 , 3 , 68 | 3 , 2 , 2 , 3 , 2 , 3 , 0 , 0 , 69 | 5 , 5 , 5 , 5 , 4 , 4 , 2 , 0 , 70 | 2 , 2 , 0 , 3 , 2 , 0 , 4 , 2 , 71 | 0 , 3 , 2 , 0 , 2 , 2 , 0 , 2 , 72 | 3 , 0 , 3 , 3 , 0 , 3 , 0xB0 , 0xA0 73 | }; 74 | 75 | 76 | // Looks like it's used as bit flags 77 | // High bits masked by 248 (11111000) 78 | // 79 | // 32: S* 241 11110001 80 | // 33: SH 226 11100010 81 | // 34: F* 211 11010011 82 | // 35: TH 187 10111011 83 | // 36: /H 124 01111100 84 | // 37: /X 149 10010101 85 | // 38: Z* 1 00000001 86 | // 39: ZH 2 00000010 87 | // 40: V* 3 00000011 88 | // 41: DH 3 00000011 89 | // 43: ** 114 01110010 90 | // 45: ** 2 00000010 91 | // 67: ** 27 00011011 92 | // 70: ** 25 00011001 93 | // tab45936 94 | const unsigned char sampledConsonantFlags[] PROGMEM = 95 | { 96 | 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 97 | 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 98 | 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 99 | 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 100 | 0xF1 , 0xE2 , 0xD3 , 0xBB , 0x7C , 0x95 , 1 , 2 , 101 | 3 , 3 , 0 , 0x72 , 0 , 2 , 0 , 0 , 102 | 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 103 | 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 104 | 0 , 0 , 0 , 0x1B , 0 , 0 , 0x19 , 0 , 105 | 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 106 | }; 107 | 108 | 109 | //tab45056 110 | unsigned char freq1data[]= 111 | { 112 | 0x00 ,0x13 ,0x13 ,0x13 ,0x13 , 0xA , 0xE ,0x12 113 | , 0x18 ,0x1A ,0x16 ,0x14 ,0x10 ,0x14 , 0xE ,0x12 114 | , 0xE ,0x12 ,0x12 ,0x10 , 0xC , 0xE , 0xA ,0x12 115 | , 0xE ,0xA , 8 , 6 , 6 , 6 , 6 ,0x11 116 | , 6 , 6 , 6 , 6 ,0xE , 0x10 , 9 ,0xA 117 | , 8 ,0xA , 6 , 6 , 6 , 5 , 6 , 0 118 | , 0x12 , 0x1A , 0x14 , 0x1A , 0x12 ,0xC , 6 , 6 119 | , 6 , 6 , 6 , 6 , 6 , 6 , 6 , 6 120 | , 6 , 6 , 6 , 6 , 6 , 6 , 6 , 6 121 | , 6 ,0xA ,0xA , 6 , 6 , 6 , 0x2C , 0x13 122 | }; 123 | 124 | //tab451356 125 | unsigned char freq2data[]= 126 | { 127 | 0x00 , 0x43 , 0x43 , 0x43 , 0x43 , 0x54 , 0x48 , 0x42 , 128 | 0x3E , 0x28 , 0x2C , 0x1E , 0x24 , 0x2C , 0x48 , 0x30 , 129 | 0x24 , 0x1E , 0x32 , 0x24 , 0x1C , 0x44 , 0x18 , 0x32 , 130 | 0x1E , 0x18 , 0x52 , 0x2E , 0x36 , 0x56 , 0x36 , 0x43 , 131 | 0x49 , 0x4F , 0x1A , 0x42 , 0x49 , 0x25 , 0x33 , 0x42 , 132 | 0x28 , 0x2F , 0x4F , 0x4F , 0x42 , 0x4F , 0x6E , 0x00 , 133 | 0x48 , 0x26 , 0x1E , 0x2A , 0x1E , 0x22 , 0x1A , 0x1A , 134 | 0x1A , 0x42 , 0x42 , 0x42 , 0x6E , 0x6E , 0x6E , 0x54 , 135 | 0x54 , 0x54 , 0x1A , 0x1A , 0x1A , 0x42 , 0x42 , 0x42 , 136 | 0x6D , 0x56 , 0x6D , 0x54 , 0x54 , 0x54 , 0x7F , 0x7F 137 | }; 138 | 139 | //tab45216 140 | unsigned char freq3data[]= 141 | { 142 | 0x00 , 0x5B , 0x5B , 0x5B , 0x5B , 0x6E , 0x5D , 0x5B , 143 | 0x58 , 0x59 , 0x57 , 0x58 , 0x52 , 0x59 , 0x5D , 0x3E , 144 | 0x52 , 0x58 , 0x3E , 0x6E , 0x50 , 0x5D , 0x5A , 0x3C , 145 | 0x6E , 0x5A , 0x6E , 0x51 , 0x79 , 0x65 , 0x79 , 0x5B , 146 | 0x63 , 0x6A , 0x51 , 0x79 , 0x5D , 0x52 , 0x5D , 0x67 , 147 | 0x4C , 0x5D , 0x65 , 0x65 , 0x79 , 0x65 , 0x79 , 0x00 , 148 | 0x5A , 0x58 , 0x58 , 0x58 , 0x58 , 0x52 , 0x51 , 0x51 , 149 | 0x51 , 0x79 , 0x79 , 0x79 , 0x70 , 0x6E , 0x6E , 0x5E , 150 | 0x5E , 0x5E , 0x51 , 0x51 , 0x51 , 0x79 , 0x79 , 0x79 , 151 | 0x65 , 0x65 , 0x70 , 0x5E , 0x5E , 0x5E , 0x08 , 0x01 152 | }; 153 | 154 | const unsigned char ampl1data[] PROGMEM = 155 | { 156 | 0 , 0 , 0 , 0 , 0 ,0xD ,0xD ,0xE , 157 | 0xF ,0xF ,0xF ,0xF ,0xF ,0xC ,0xD ,0xC , 158 | 0xF ,0xF ,0xD ,0xD ,0xD ,0xE ,0xD ,0xC , 159 | 0xD ,0xD ,0xD ,0xC , 9 , 9 , 0 , 0 , 160 | 0 , 0 , 0 , 0 , 0 , 0 ,0xB ,0xB , 161 | 0xB ,0xB , 0 , 0 , 1 ,0xB , 0 , 2 , 162 | 0xE ,0xF ,0xF ,0xF ,0xF ,0xD , 2 , 4 , 163 | 0 , 2 , 4 , 0 , 1 , 4 , 0 , 1 , 164 | 4 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 165 | 0 ,0xC , 0 , 0 , 0 , 0 ,0xF ,0xF 166 | }; 167 | 168 | const unsigned char ampl2data[] PROGMEM = 169 | { 170 | 0 , 0 , 0 , 0 , 0 ,0xA ,0xB ,0xD , 171 | 0xE ,0xD ,0xC ,0xC ,0xB , 9 ,0xB ,0xB , 172 | 0xC ,0xC ,0xC , 8 , 8 ,0xC , 8 ,0xA , 173 | 8 , 8 ,0xA , 3 , 9 , 6 , 0 , 0 , 174 | 0 , 0 , 0 , 0 , 0 , 0 , 3 , 5 , 175 | 3 , 4 , 0 , 0 , 0 , 5 ,0xA , 2 , 176 | 0xE ,0xD ,0xC ,0xD ,0xC , 8 , 0 , 1 , 177 | 0 , 0 , 1 , 0 , 0 , 1 , 0 , 0 , 178 | 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 179 | 0 ,0xA , 0 , 0 ,0xA , 0 , 0 , 0 180 | }; 181 | 182 | const unsigned char ampl3data[] PROGMEM = 183 | { 184 | 0 , 0 , 0 , 0 , 0 , 8 , 7 , 8 , 185 | 8 , 1 , 1 , 0 , 1 , 0 , 7 , 5 , 186 | 1 , 0 , 6 , 1 , 0 , 7 , 0 , 5 , 187 | 1 , 0 , 8 , 0 , 0 , 3 , 0 , 0 , 188 | 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 189 | 0 , 0 , 0 , 0 , 0 , 1 ,0xE , 1 , 190 | 9 , 1 , 0 , 1 , 0 , 0 , 0 , 0 , 191 | 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 192 | 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 193 | 0 , 7 , 0 , 0 , 5 , 0 , 0x13 , 0x10 194 | }; 195 | 196 | 197 | 198 | //tab42240 199 | const signed char sinus[256] PROGMEM = {0,3,6,9,12,16,19,22,25,28,31,34,37,40,43,46,49,51,54,57,60,63,65,68,71,73,76,78,81,83,85,88,90,92,94,96,98,100,102,104,106,107,109,111,112,113,115,116,117,118,120,121,122,122,123,124,125,125,126,126,126,127,127,127,127,127,127,127,126,126,126,125,125,124,123,122,122,121,120,118,117,116,115,113,112,111,109,107,106,104,102,100,98,96,94,92,90,88,85,83,81,78,76,73,71,68,65,63,60,57,54,51,49,46,43,40,37,34,31,28,25,22,19,16,12,9,6,3,0,-3,-6,-9,-12,-16,-19,-22,-25,-28,-31,-34,-37,-40,-43,-46,-49,-51,-54,-57,-60,-63,-65,-68,-71,-73,-76,-78,-81,-83,-85,-88,-90,-92,-94,-96,-98,-100,-102,-104,-106,-107,-109,-111,-112,-113,-115,-116,-117,-118,-120,-121,-122,-122,-123,-124,-125,-125,-126,-126,-126,-127,-127,-127,-127,-127,-127,-127,-126,-126,-126,-125,-125,-124,-123,-122,-122,-121,-120,-118,-117,-116,-115,-113,-112,-111,-109,-107,-106,-104,-102,-100,-98,-96,-94,-92,-90,-88,-85,-83,-81,-78,-76,-73,-71,-68,-65,-63,-60,-57,-54,-51,-49,-46,-43,-40,-37,-34,-31,-28,-25,-22,-19,-16,-12,-9,-6,-3}; 200 | 201 | //tab42496 202 | const unsigned char rectangle[] PROGMEM = 203 | { 204 | 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 205 | 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 206 | 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 207 | 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 208 | 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 209 | 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 210 | 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 211 | 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 212 | 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 213 | 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 214 | 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 215 | 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 216 | 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 217 | 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 218 | 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 219 | 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 220 | 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 221 | 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 222 | 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 223 | 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 224 | 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 225 | 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 226 | 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 227 | 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 228 | 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 229 | 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 230 | 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 231 | 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 232 | 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 233 | 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 234 | 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 235 | 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 , 0x70 236 | }; 237 | 238 | 239 | 240 | //random data ? 241 | const unsigned char sampleTable[0x500] PROGMEM = 242 | { 243 | //00 244 | 245 | 0x38 , 0x84 , 0x6B , 0x19 , 0xC6 , 0x63 , 0x18 , 0x86 246 | , 0x73 , 0x98 , 0xC6 , 0xB1 , 0x1C , 0xCA , 0x31 , 0x8C 247 | , 0xC7 , 0x31 , 0x88 , 0xC2 , 0x30 , 0x98 , 0x46 , 0x31 248 | , 0x18 , 0xC6 , 0x35 ,0xC , 0xCA , 0x31 ,0xC , 0xC6 249 | //20 250 | , 0x21 , 0x10 , 0x24 , 0x69 , 0x12 , 0xC2 , 0x31 , 0x14 251 | , 0xC4 , 0x71 , 8 , 0x4A , 0x22 , 0x49 , 0xAB , 0x6A 252 | , 0xA8 , 0xAC , 0x49 , 0x51 , 0x32 , 0xD5 , 0x52 , 0x88 253 | , 0x93 , 0x6C , 0x94 , 0x22 , 0x15 , 0x54 , 0xD2 , 0x25 254 | //40 255 | , 0x96 , 0xD4 , 0x50 , 0xA5 , 0x46 , 0x21 , 8 , 0x85 256 | , 0x6B , 0x18 , 0xC4 , 0x63 , 0x10 , 0xCE , 0x6B , 0x18 257 | , 0x8C , 0x71 , 0x19 , 0x8C , 0x63 , 0x35 ,0xC , 0xC6 258 | , 0x33 , 0x99 , 0xCC , 0x6C , 0xB5 , 0x4E , 0xA2 , 0x99 259 | //60 260 | , 0x46 , 0x21 , 0x28 , 0x82 , 0x95 , 0x2E , 0xE3 , 0x30 261 | , 0x9C , 0xC5 , 0x30 , 0x9C , 0xA2 , 0xB1 , 0x9C , 0x67 262 | , 0x31 , 0x88 , 0x66 , 0x59 , 0x2C , 0x53 , 0x18 , 0x84 263 | , 0x67 , 0x50 , 0xCA , 0xE3 ,0xA , 0xAC , 0xAB , 0x30 264 | //80 265 | , 0xAC , 0x62 , 0x30 , 0x8C , 0x63 , 0x10 , 0x94 , 0x62 266 | , 0xB1 , 0x8C , 0x82 , 0x28 , 0x96 , 0x33 , 0x98 , 0xD6 267 | , 0xB5 , 0x4C , 0x62 , 0x29 , 0xA5 , 0x4A , 0xB5 , 0x9C 268 | , 0xC6 , 0x31 , 0x14 , 0xD6 , 0x38 , 0x9C , 0x4B , 0xB4 269 | //A0 270 | , 0x86 , 0x65 , 0x18 , 0xAE , 0x67 , 0x1C , 0xA6 , 0x63 271 | , 0x19 , 0x96 , 0x23 , 0x19 , 0x84 , 0x13 , 8 , 0xA6 272 | , 0x52 , 0xAC , 0xCA , 0x22 , 0x89 , 0x6E , 0xAB , 0x19 273 | , 0x8C , 0x62 , 0x34 , 0xC4 , 0x62 , 0x19 , 0x86 , 0x63 274 | //C0 275 | , 0x18 , 0xC4 , 0x23 , 0x58 , 0xD6 , 0xA3 , 0x50 , 0x42 276 | , 0x54 , 0x4A , 0xAD , 0x4A , 0x25 , 0x11 , 0x6B , 0x64 277 | , 0x89 , 0x4A , 0x63 , 0x39 , 0x8A , 0x23 , 0x31 , 0x2A 278 | , 0xEA , 0xA2 , 0xA9 , 0x44 , 0xC5 , 0x12 , 0xCD , 0x42 279 | //E0 280 | , 0x34 , 0x8C , 0x62 , 0x18 , 0x8C , 0x63 , 0x11 , 0x48 281 | , 0x66 , 0x31 , 0x9D , 0x44 , 0x33 , 0x1D , 0x46 , 0x31 282 | , 0x9C , 0xC6 , 0xB1 ,0xC , 0xCD , 0x32 , 0x88 , 0xC4 283 | , 0x73 , 0x18 , 0x86 , 0x73 , 8 , 0xD6 , 0x63 , 0x58 284 | //100 285 | , 7 , 0x81 , 0xE0 , 0xF0 , 0x3C , 7 , 0x87 , 0x90 286 | , 0x3C , 0x7C ,0xF , 0xC7 , 0xC0 , 0xC0 , 0xF0 , 0x7C 287 | , 0x1E , 7 , 0x80 , 0x80 , 0 , 0x1C , 0x78 , 0x70 288 | , 0xF1 , 0xC7 , 0x1F , 0xC0 ,0xC , 0xFE , 0x1C , 0x1F 289 | //120 290 | , 0x1F ,0xE ,0xA , 0x7A , 0xC0 , 0x71 , 0xF2 , 0x83 291 | , 0x8F , 3 ,0xF ,0xF ,0xC , 0 , 0x79 , 0xF8 292 | , 0x61 , 0xE0 , 0x43 ,0xF , 0x83 , 0xE7 , 0x18 , 0xF9 293 | , 0xC1 , 0x13 , 0xDA , 0xE9 , 0x63 , 0x8F ,0xF , 0x83 294 | //140 295 | , 0x83 , 0x87 , 0xC3 , 0x1F , 0x3C , 0x70 , 0xF0 , 0xE1 296 | , 0xE1 , 0xE3 , 0x87 , 0xB8 , 0x71 ,0xE , 0x20 , 0xE3 297 | , 0x8D , 0x48 , 0x78 , 0x1C , 0x93 , 0x87 , 0x30 , 0xE1 298 | , 0xC1 , 0xC1 , 0xE4 , 0x78 , 0x21 , 0x83 , 0x83 , 0xC3 299 | //160 300 | , 0x87 , 6 , 0x39 , 0xE5 , 0xC3 , 0x87 , 7 ,0xE 301 | , 0x1C , 0x1C , 0x70 , 0xF4 , 0x71 , 0x9C , 0x60 , 0x36 302 | , 0x32 , 0xC3 , 0x1E , 0x3C , 0xF3 , 0x8F ,0xE , 0x3C 303 | , 0x70 , 0xE3 , 0xC7 , 0x8F ,0xF ,0xF ,0xE , 0x3C 304 | //180 305 | , 0x78 , 0xF0 , 0xE3 , 0x87 , 6 , 0xF0 , 0xE3 , 7 306 | , 0xC1 , 0x99 , 0x87 ,0xF , 0x18 , 0x78 , 0x70 , 0x70 307 | , 0xFC , 0xF3 , 0x10 , 0xB1 , 0x8C , 0x8C , 0x31 , 0x7C 308 | , 0x70 , 0xE1 , 0x86 , 0x3C , 0x64 , 0x6C , 0xB0 , 0xE1 309 | //1A0 310 | , 0xE3 ,0xF , 0x23 , 0x8F ,0xF , 0x1E , 0x3E , 0x38 311 | , 0x3C , 0x38 , 0x7B , 0x8F , 7 ,0xE , 0x3C , 0xF4 312 | , 0x17 , 0x1E , 0x3C , 0x78 , 0xF2 , 0x9E , 0x72 , 0x49 313 | , 0xE3 , 0x25 , 0x36 , 0x38 , 0x58 , 0x39 , 0xE2 , 0xDE 314 | //1C0 315 | , 0x3C , 0x78 , 0x78 , 0xE1 , 0xC7 , 0x61 , 0xE1 , 0xE1 316 | , 0xB0 , 0xF0 , 0xF0 , 0xC3 , 0xC7 ,0xE , 0x38 , 0xC0 317 | , 0xF0 , 0xCE , 0x73 , 0x73 , 0x18 , 0x34 , 0xB0 , 0xE1 318 | , 0xC7 , 0x8E , 0x1C , 0x3C , 0xF8 , 0x38 , 0xF0 , 0xE1 319 | //1E0 320 | , 0xC1 , 0x8B , 0x86 , 0x8F , 0x1C , 0x78 , 0x70 , 0xF0 321 | , 0x78 , 0xAC , 0xB1 , 0x8F , 0x39 , 0x31 , 0xDB , 0x38 322 | , 0x61 , 0xC3 ,0xE ,0xE , 0x38 , 0x78 , 0x73 , 0x17 323 | , 0x1E , 0x39 , 0x1E , 0x38 , 0x64 , 0xE1 , 0xF1 , 0xC1 324 | //200 325 | , 0x4E ,0xF , 0x40 , 0xA2 , 2 , 0xC5 , 0x8F , 0x81 326 | , 0xA1 , 0xFC , 0x12 , 8 , 0x64 , 0xE0 , 0x3C , 0x22 327 | , 0xE0 , 0x45 , 7 , 0x8E ,0xC , 0x32 , 0x90 , 0xF0 328 | , 0x1F , 0x20 , 0x49 , 0xE0 , 0xF8 ,0xC , 0x60 , 0xF0 329 | //220 330 | , 0x17 , 0x1A , 0x41 , 0xAA , 0xA4 , 0xD0 , 0x8D , 0x12 331 | , 0x82 , 0x1E , 0x1E , 3 , 0xF8 , 0x3E , 3 ,0xC 332 | , 0x73 , 0x80 , 0x70 , 0x44 , 0x26 , 3 , 0x24 , 0xE1 333 | , 0x3E , 4 , 0x4E , 4 , 0x1C , 0xC1 , 9 , 0xCC 334 | //240 335 | , 0x9E , 0x90 , 0x21 , 7 , 0x90 , 0x43 , 0x64 , 0xC0 336 | , 0xF , 0xC6 , 0x90 , 0x9C , 0xC1 , 0x5B , 3 , 0xE2 337 | , 0x1D , 0x81 , 0xE0 , 0x5E , 0x1D , 3 , 0x84 , 0xB8 338 | , 0x2C ,0xF , 0x80 , 0xB1 , 0x83 , 0xE0 , 0x30 , 0x41 339 | //260 340 | , 0x1E , 0x43 , 0x89 , 0x83 , 0x50 , 0xFC , 0x24 , 0x2E 341 | , 0x13 , 0x83 , 0xF1 , 0x7C , 0x4C , 0x2C , 0xC9 ,0xD 342 | , 0x83 , 0xB0 , 0xB5 , 0x82 , 0xE4 , 0xE8 , 6 , 0x9C 343 | , 7 , 0xA0 , 0x99 , 0x1D , 7 , 0x3E , 0x82 , 0x8F 344 | //280 345 | , 0x70 , 0x30 , 0x74 , 0x40 , 0xCA , 0x10 , 0xE4 , 0xE8 346 | , 0xF , 0x92 , 0x14 , 0x3F , 6 , 0xF8 , 0x84 , 0x88 347 | , 0x43 , 0x81 ,0xA , 0x34 , 0x39 , 0x41 , 0xC6 , 0xE3 348 | , 0x1C , 0x47 , 3 , 0xB0 , 0xB8 , 0x13 ,0xA , 0xC2 349 | //2A0 350 | , 0x64 , 0xF8 , 0x18 , 0xF9 , 0x60 , 0xB3 , 0xC0 , 0x65 351 | , 0x20 , 0x60 , 0xA6 , 0x8C , 0xC3 , 0x81 , 0x20 , 0x30 352 | , 0x26 , 0x1E , 0x1C , 0x38 , 0xD3 , 1 , 0xB0 , 0x26 353 | , 0x40 , 0xF4 ,0xB , 0xC3 , 0x42 , 0x1F , 0x85 , 0x32 354 | //2C0 355 | , 0x26 , 0x60 , 0x40 , 0xC9 , 0xCB , 1 , 0xEC , 0x11 356 | , 0x28 , 0x40 , 0xFA , 4 , 0x34 , 0xE0 , 0x70 , 0x4C 357 | , 0x8C , 0x1D , 7 , 0x69 , 3 , 0x16 , 0xC8 , 4 358 | , 0x23 , 0xE8 , 0xC6 , 0x9A ,0xB , 0x1A , 3 , 0xE0 359 | //2E0 360 | , 0x76 , 6 , 5 , 0xCF , 0x1E , 0xBC , 0x58 , 0x31 361 | , 0x71 , 0x66 , 0 , 0xF8 , 0x3F , 4 , 0xFC ,0xC 362 | , 0x74 , 0x27 , 0x8A , 0x80 , 0x71 , 0xC2 , 0x3A , 0x26 363 | , 6 , 0xC0 , 0x1F , 5 ,0xF , 0x98 , 0x40 , 0xAE 364 | //300 365 | , 1 , 0x7F , 0xC0 , 7 , 0xFF , 0 ,0xE , 0xFE 366 | , 0 , 3 , 0xDF , 0x80 , 3 , 0xEF , 0x80 , 0x1B 367 | , 0xF1 , 0xC2 , 0 , 0xE7 , 0xE0 , 0x18 , 0xFC , 0xE0 368 | , 0x21 , 0xFC , 0x80 , 0x3C , 0xFC , 0x40 ,0xE , 0x7E 369 | //320 370 | , 0 , 0x3F , 0x3E , 0 ,0xF , 0xFE , 0 , 0x1F 371 | , 0xFF , 0 , 0x3E , 0xF0 , 7 , 0xFC , 0 , 0x7E 372 | , 0x10 , 0x3F , 0xFF , 0 , 0x3F , 0x38 ,0xE , 0x7C 373 | , 1 , 0x87 ,0xC , 0xFC , 0xC7 , 0 , 0x3E , 4 374 | //340 375 | , 0xF , 0x3E , 0x1F ,0xF ,0xF , 0x1F ,0xF , 2 376 | , 0x83 , 0x87 , 0xCF , 3 , 0x87 ,0xF , 0x3F , 0xC0 377 | , 7 , 0x9E , 0x60 , 0x3F , 0xC0 , 3 , 0xFE , 0 378 | , 0x3F , 0xE0 , 0x77 , 0xE1 , 0xC0 , 0xFE , 0xE0 , 0xC3 379 | //360 380 | , 0xE0 , 1 , 0xDF , 0xF8 , 3 , 7 , 0 , 0x7E 381 | , 0x70 , 0 , 0x7C , 0x38 , 0x18 , 0xFE ,0xC , 0x1E 382 | , 0x78 , 0x1C , 0x7C , 0x3E ,0xE , 0x1F , 0x1E , 0x1E 383 | , 0x3E , 0 , 0x7F , 0x83 , 7 , 0xDB , 0x87 , 0x83 384 | //380 385 | , 7 , 0xC7 , 7 , 0x10 , 0x71 , 0xFF , 0 , 0x3F 386 | , 0xE2 , 1 , 0xE0 , 0xC1 , 0xC3 , 0xE1 , 0 , 0x7F 387 | , 0xC0 , 5 , 0xF0 , 0x20 , 0xF8 , 0xF0 , 0x70 , 0xFE 388 | , 0x78 , 0x79 , 0xF8 , 2 , 0x3F ,0xC , 0x8F , 3 389 | //3a0 390 | , 0xF , 0x9F , 0xE0 , 0xC1 , 0xC7 , 0x87 , 3 , 0xC3 391 | , 0xC3 , 0xB0 , 0xE1 , 0xE1 , 0xC1 , 0xE3 , 0xE0 , 0x71 392 | , 0xF0 , 0 , 0xFC , 0x70 , 0x7C ,0xC , 0x3E , 0x38 393 | , 0xE , 0x1C , 0x70 , 0xC3 , 0xC7 , 3 , 0x81 , 0xC1 394 | //3c0 395 | , 0xC7 , 0xE7 , 0 ,0xF , 0xC7 , 0x87 , 0x19 , 9 396 | , 0xEF , 0xC4 , 0x33 , 0xE0 , 0xC1 , 0xFC , 0xF8 , 0x70 397 | , 0xF0 , 0x78 , 0xF8 , 0xF0 , 0x61 , 0xC7 , 0 , 0x1F 398 | , 0xF8 , 1 , 0x7C , 0xF8 , 0xF0 , 0x78 , 0x70 , 0x3C 399 | //3e0 400 | , 0x7C , 0xCE ,0xE , 0x21 , 0x83 , 0xCF , 8 , 7 401 | , 0x8F , 8 , 0xC1 , 0x87 , 0x8F , 0x80 , 0xC7 , 0xE3 402 | , 0 , 7 , 0xF8 , 0xE0 , 0xEF , 0 , 0x39 , 0xF7 403 | , 0x80 ,0xE , 0xF8 , 0xE1 , 0xE3 , 0xF8 , 0x21 , 0x9F 404 | //400 405 | , 0xC0 , 0xFF , 3 , 0xF8 , 7 , 0xC0 , 0x1F , 0xF8 406 | , 0xC4 , 4 , 0xFC , 0xC4 , 0xC1 , 0xBC , 0x87 , 0xF0 407 | , 0xF , 0xC0 , 0x7F , 5 , 0xE0 , 0x25 , 0xEC , 0xC0 408 | , 0x3E , 0x84 , 0x47 , 0xF0 , 0x8E , 3 , 0xF8 , 3 409 | //420 410 | , 0xFB , 0xC0 , 0x19 , 0xF8 , 7 , 0x9C ,0xC , 0x17 411 | , 0xF8 , 7 , 0xE0 , 0x1F , 0xA1 , 0xFC ,0xF , 0xFC 412 | , 1 , 0xF0 , 0x3F , 0 , 0xFE , 3 , 0xF0 , 0x1F 413 | , 0 , 0xFD , 0 , 0xFF , 0x88 ,0xD , 0xF9 , 1 414 | //440 415 | , 0xFF , 0 , 0x70 , 7 , 0xC0 , 0x3E , 0x42 , 0xF3 416 | , 0xD , 0xC4 , 0x7F , 0x80 , 0xFC , 7 , 0xF0 , 0x5E 417 | , 0xC0 , 0x3F , 0 , 0x78 , 0x3F , 0x81 , 0xFF , 1 418 | , 0xF8 , 1 , 0xC3 , 0xE8 ,0xC , 0xE4 , 0x64 , 0x8F 419 | ////460 420 | , 0xE4 ,0xF , 0xF0 , 7 , 0xF0 , 0xC2 , 0x1F , 0 421 | , 0x7F , 0xC0 , 0x6F , 0x80 , 0x7E , 3 , 0xF8 , 7 422 | , 0xF0 , 0x3F , 0xC0 , 0x78 ,0xF , 0x82 , 7 , 0xFE 423 | , 0x22 , 0x77 , 0x70 , 2 , 0x76 , 3 , 0xFE , 0 424 | //480 425 | , 0xFE , 0x67 , 0 , 0x7C , 0xC7 , 0xF1 , 0x8E , 0xC6 426 | , 0x3B , 0xE0 , 0x3F , 0x84 , 0xF3 , 0x19 , 0xD8 , 3 427 | , 0x99 , 0xFC , 9 , 0xB8 ,0xF , 0xF8 , 0 , 0x9D 428 | , 0x24 , 0x61 , 0xF9 ,0xD , 0 , 0xFD , 3 , 0xF0 429 | //4a0 430 | , 0x1F , 0x90 , 0x3F , 1 , 0xF8 , 0x1F , 0xD0 ,0xF 431 | , 0xF8 , 0x37 , 1 , 0xF8 , 7 , 0xF0 ,0xF , 0xC0 432 | , 0x3F , 0 , 0xFE , 3 , 0xF8 ,0xF , 0xC0 , 0x3F 433 | , 0 , 0xFA , 3 , 0xF0 ,0xF , 0x80 , 0xFF , 1 434 | //4c0 435 | , 0xB8 , 7 , 0xF0 , 1 , 0xFC , 1 , 0xBC , 0x80 436 | , 0x13 , 0x1E , 0 , 0x7F , 0xE1 , 0x40 , 0x7F , 0xA0 437 | , 0x7F , 0xB0 , 0 , 0x3F , 0xC0 , 0x1F , 0xC0 , 0x38 438 | , 0xF , 0xF0 , 0x1F , 0x80 , 0xFF , 1 , 0xFC , 3 439 | //4e0 440 | , 0xF1 , 0x7E , 1 , 0xFE , 1 , 0xF0 , 0xFF , 0 441 | , 0x7F , 0xC0 , 0x1D , 7 , 0xF0 ,0xF , 0xC0 , 0x7E 442 | , 6 , 0xE0 , 7 , 0xE0 ,0xF , 0xF8 , 6 , 0xC1 443 | , 0xFE , 1 , 0xFC , 3 , 0xE0 ,0xF , 0 , 0xFC 444 | }; 445 | 446 | 447 | 448 | #endif 449 | -------------------------------------------------------------------------------- /src/ReciterTabs.h: -------------------------------------------------------------------------------- 1 | #ifndef RECITERTABS_H 2 | #define RECITERTABS_H 3 | 4 | #include 5 | #include "esp8266sam_debug.h" 6 | #if DEBUG_ESP8266SAM_LIB 7 | #define PROGMEM 8 | #endif 9 | 10 | //some flags 11 | const unsigned char tab36376[] PROGMEM = 12 | { 13 | 0, 0, 0, 0, 0, 0, 0, 0, // 0-7 14 | 0, 0, 0, 0, 0, 0, 0, 0, // 8-15 15 | 0, 0, 0, 0, 0, 0, 0, 0, 16 | 0, 0, 0, 0, 0, 0, 0, 0, 17 | 0, 2, 2, 2, 2, 2, 2, 130, // ' ', '!' 18 | 0, 0, 2, 2, 2, 2, 2, 2, 19 | 3, 3, 3, 3, 3, 3, 3, 3, 20 | 3, 3, 2, 2, 2, 2, 2, 2, 21 | 2, 192, 168, 176, 172, 192, 160, 184, // '@', 'A' 22 | 160, 192, 188, 160, 172, 168, 172, 192, 23 | 160, 160, 172, 180, 164, 192, 168, 168, 24 | 176, 192, 188, 0, 0, 0, 2, 0, // 'X', 'Y', 'Z', '[', 25 | 32, 32, 155, 32, 192, 185, 32, 205, 26 | 163, 76, 138, 142 27 | }; 28 | 29 | const char rules[] PROGMEM = 30 | { 31 | ']','A'|0x80, 32 | ' ','(','A','.',')', '=','E','H','4','Y','.',' '|0x80, 33 | '(','A',')',' ', '=','A','H'|0x80, 34 | ' ','(','A','R','E',')',' ', '=','A','A','R'|0x80, 35 | ' ','(','A','R',')','O', '=','A','X','R'|0x80, 36 | '(','A','R',')','#', '=','E','H','4','R'|0x80, 37 | ' ','^','(','A','S',')','#', '=','E','Y','4','S'|0x80, 38 | '(','A',')','W','A', '=','A','X'|0x80, 39 | '(','A','W',')', '=','A','O','5'|0x80, 40 | ' ',':','(','A','N','Y',')', '=','E','H','4','N','I','Y'|0x80, 41 | '(','A',')','^','+','#', '=','E','Y','5'|0x80, 42 | '#',':','(','A','L','L','Y',')', '=','U','L','I','Y'|0x80, 43 | ' ','(','A','L',')','#', '=','U','L'|0x80, 44 | '(','A','G','A','I','N',')', '=','A','X','G','E','H','4','N'|0x80, 45 | '#',':','(','A','G',')','E', '=','I','H','J'|0x80, 46 | '(','A',')','^','%', '=','E','Y'|0x80, 47 | '(','A',')','^','+',':','#', '=','A','E'|0x80, 48 | ' ',':','(','A',')','^','+',' ', '=','E','Y','4'|0x80, 49 | ' ','(','A','R','R',')', '=','A','X','R'|0x80, 50 | '(','A','R','R',')', '=','A','E','4','R'|0x80, 51 | ' ','^','(','A','R',')',' ', '=','A','A','5','R'|0x80, 52 | '(','A','R',')', '=','A','A','5','R'|0x80, 53 | '(','A','I','R',')', '=','E','H','4','R'|0x80, 54 | '(','A','I',')', '=','E','Y','4'|0x80, 55 | '(','A','Y',')', '=','E','Y','5'|0x80, 56 | '(','A','U',')', '=','A','O','4'|0x80, 57 | '#',':','(','A','L',')',' ', '=','U','L'|0x80, 58 | '#',':','(','A','L','S',')',' ', '=','U','L','Z'|0x80, 59 | '(','A','L','K',')', '=','A','O','4','K'|0x80, 60 | '(','A','L',')','^', '=','A','O','L'|0x80, 61 | ' ',':','(','A','B','L','E',')', '=','E','Y','4','B','U','L'|0x80, 62 | '(','A','B','L','E',')', '=','A','X','B','U','L'|0x80, 63 | '(','A',')','V','O', '=','E','Y','4'|0x80, 64 | '(','A','N','G',')','+', '=','E','Y','4','N','J'|0x80, 65 | '(','A','T','A','R','I',')', '=','A','H','T','A','A','4','R','I','Y'|0x80, 66 | '(','A',')','T','O','M', '=','A','E'|0x80, 67 | '(','A',')','T','T','I', '=','A','E'|0x80, 68 | ' ','(','A','T',')',' ', '=','A','E','T'|0x80, 69 | ' ','(','A',')','T', '=','A','H'|0x80, 70 | '(','A',')', '=','A','E'|0x80, 71 | 72 | ']','B'|0x80, 73 | ' ','(','B',')',' ', '=','B','I','Y','4'|0x80, 74 | ' ','(','B','E',')','^','#', '=','B','I','H'|0x80, 75 | '(','B','E','I','N','G',')', '=','B','I','Y','4','I','H','N','X'|0x80, 76 | ' ','(','B','O','T','H',')',' ', '=','B','O','W','4','T','H'|0x80, 77 | ' ','(','B','U','S',')','#', '=','B','I','H','4','Z'|0x80, 78 | '(','B','R','E','A','K',')', '=','B','R','E','Y','5','K'|0x80, 79 | '(','B','U','I','L',')', '=','B','I','H','4','L'|0x80, 80 | '(','B',')', '=','B'|0x80, 81 | 82 | ']','C'|0x80, 83 | ' ','(','C',')',' ', '=','S','I','Y','4'|0x80, 84 | ' ','(','C','H',')','^', '=','K'|0x80, 85 | '^','E','(','C','H',')', '=','K'|0x80, 86 | '(','C','H','A',')','R','#', '=','K','E','H','5'|0x80, 87 | '(','C','H',')', '=','C','H'|0x80, 88 | ' ','S','(','C','I',')','#', '=','S','A','Y','4'|0x80, 89 | '(','C','I',')','A', '=','S','H'|0x80, 90 | '(','C','I',')','O', '=','S','H'|0x80, 91 | '(','C','I',')','E','N', '=','S','H'|0x80, 92 | '(','C','I','T','Y',')', '=','S','I','H','T','I','Y'|0x80, 93 | '(','C',')','+', '=','S'|0x80, 94 | '(','C','K',')', '=','K'|0x80, 95 | '(','C','O','M','M','O','D','O','R','E',')','=','K','A','A','4','M','A','H','D','O','H','R'|0x80, 96 | '(','C','O','M',')', '=','K','A','H','M'|0x80, 97 | '(','C','U','I','T',')', '=','K','I','H','T'|0x80, 98 | '(','C','R','E','A',')', '=','K','R','I','Y','E','Y'|0x80, 99 | '(','C',')', '=','K'|0x80, 100 | 101 | ']','D'|0x80, 102 | ' ','(','D',')',' ', '=','D','I','Y','4'|0x80, 103 | ' ','(','D','R','.',')',' ', '=','D','A','A','4','K','T','E','R'|0x80, 104 | '#',':','(','D','E','D',')',' ', '=','D','I','H','D'|0x80, 105 | '.','E','(','D',')',' ', '=','D'|0x80, 106 | '#',':','^','E','(','D',')',' ', '=','T'|0x80, 107 | ' ','(','D','E',')','^','#', '=','D','I','H'|0x80, 108 | ' ','(','D','O',')',' ', '=','D','U','W'|0x80, 109 | ' ','(','D','O','E','S',')', '=','D','A','H','Z'|0x80, 110 | '(','D','O','N','E',')',' ', '=','D','A','H','5','N'|0x80, 111 | '(','D','O','I','N','G',')', '=','D','U','W','4','I','H','N','X'|0x80, 112 | ' ','(','D','O','W',')', '=','D','A','W'|0x80, 113 | '#','(','D','U',')','A', '=','J','U','W'|0x80, 114 | '#','(','D','U',')','^','#', '=','J','A','X'|0x80, 115 | '(','D',')', '=','D'|0x80, 116 | 117 | ']','E'|0x80, 118 | ' ','(','E',')',' ', '=','I','Y','I','Y','4'|0x80, 119 | '#',':','(','E',')',' ','='|0x80, 120 | '\'',':','^','(','E',')',' ','='|0x80, 121 | ' ',':','(','E',')',' ', '=','I','Y'|0x80, 122 | '#','(','E','D',')',' ', '=','D'|0x80, 123 | '#',':','(','E',')','D',' ','='|0x80, 124 | '(','E','V',')','E','R', '=','E','H','4','V'|0x80, 125 | '(','E',')','^','%', '=','I','Y','4'|0x80, 126 | '(','E','R','I',')','#', '=','I','Y','4','R','I','Y'|0x80, 127 | '(','E','R','I',')', '=','E','H','4','R','I','H'|0x80, 128 | '#',':','(','E','R',')','#', '=','E','R'|0x80, 129 | '(','E','R','R','O','R',')', '=','E','H','4','R','O','H','R'|0x80, 130 | '(','E','R','A','S','E',')', '=','I','H','R','E','Y','5','S'|0x80, 131 | '(','E','R',')','#', '=','E','H','R'|0x80, 132 | '(','E','R',')', '=','E','R'|0x80, 133 | ' ','(','E','V','E','N',')', '=','I','Y','V','E','H','N'|0x80, 134 | '#',':','(','E',')','W','='|0x80, 135 | '@','(','E','W',')', '=','U','W'|0x80, 136 | '(','E','W',')', '=','Y','U','W'|0x80, 137 | '(','E',')','O', '=','I','Y'|0x80, 138 | '#',':','&','(','E','S',')',' ', '=','I','H','Z'|0x80, 139 | '#',':','(','E',')','S',' ','='|0x80, 140 | '#',':','(','E','L','Y',')',' ', '=','L','I','Y'|0x80, 141 | '#',':','(','E','M','E','N','T',')', '=','M','E','H','N','T'|0x80, 142 | '(','E','F','U','L',')', '=','F','U','H','L'|0x80, 143 | '(','E','E',')', '=','I','Y','4'|0x80, 144 | '(','E','A','R','N',')', '=','E','R','5','N'|0x80, 145 | ' ','(','E','A','R',')','^', '=','E','R','5'|0x80, 146 | '(','E','A','D',')', '=','E','H','D'|0x80, 147 | '#',':','(','E','A',')',' ', '=','I','Y','A','X'|0x80, 148 | '(','E','A',')','S','U', '=','E','H','5'|0x80, 149 | '(','E','A',')', '=','I','Y','5'|0x80, 150 | '(','E','I','G','H',')', '=','E','Y','4'|0x80, 151 | '(','E','I',')', '=','I','Y','4'|0x80, 152 | ' ','(','E','Y','E',')', '=','A','Y','4'|0x80, 153 | '(','E','Y',')', '=','I','Y'|0x80, 154 | '(','E','U',')', '=','Y','U','W','5'|0x80, 155 | '(','E','Q','U','A','L',')', '=','I','Y','4','K','W','U','L'|0x80, 156 | '(','E',')', '=','E','H'|0x80, 157 | 158 | ']','F'|0x80, 159 | ' ','(','F',')',' ', '=','E','H','4','F'|0x80, 160 | '(','F','U','L',')', '=','F','U','H','L'|0x80, 161 | '(','F','R','I','E','N','D',')', '=','F','R','E','H','5','N','D'|0x80, 162 | '(','F','A','T','H','E','R',')', '=','F','A','A','4','D','H','E','R'|0x80, 163 | '(','F',')','F','='|0x80, 164 | '(','F',')', '=','F'|0x80, 165 | 166 | ']','G'|0x80, 167 | ' ','(','G',')',' ', '=','J','I','Y','4'|0x80, 168 | '(','G','I','V',')', '=','G','I','H','5','V'|0x80, 169 | ' ','(','G',')','I','^', '=','G'|0x80, 170 | '(','G','E',')','T', '=','G','E','H','5'|0x80, 171 | 'S','U','(','G','G','E','S',')', '=','G','J','E','H','4','S'|0x80, 172 | '(','G','G',')', '=','G'|0x80, 173 | ' ','B','#','(','G',')', '=','G'|0x80, 174 | '(','G',')','+', '=','J'|0x80, 175 | '(','G','R','E','A','T',')', '=','G','R','E','Y','4','T'|0x80, 176 | '(','G','O','N',')','E', '=','G','A','O','5','N'|0x80, 177 | '#','(','G','H',')','='|0x80, 178 | ' ','(','G','N',')', '=','N'|0x80, 179 | '(','G',')', '=','G'|0x80, 180 | 181 | ']','H'|0x80, 182 | ' ','(','H',')',' ', '=','E','Y','4','C','H'|0x80, 183 | ' ','(','H','A','V',')', '=','/','H','A','E','6','V'|0x80, 184 | ' ','(','H','E','R','E',')', '=','/','H','I','Y','R'|0x80, 185 | ' ','(','H','O','U','R',')', '=','A','W','5','E','R'|0x80, 186 | '(','H','O','W',')', '=','/','H','A','W'|0x80, 187 | '(','H',')','#', '=','/','H'|0x80, 188 | '(','H',')','='|0x80, 189 | 190 | ']','I'|0x80, 191 | ' ','(','I','N',')', '=','I','H','N'|0x80, 192 | ' ','(','I',')',' ', '=','A','Y','4'|0x80, 193 | '(','I',')',' ', '=','A','Y'|0x80, 194 | '(','I','N',')','D', '=','A','Y','5','N'|0x80, 195 | 'S','E','M','(','I',')', '=','I','Y'|0x80, 196 | ' ','A','N','T','(','I',')', '=','A','Y'|0x80, 197 | '(','I','E','R',')', '=','I','Y','E','R'|0x80, 198 | '#',':','R','(','I','E','D',')',' ', '=','I','Y','D'|0x80, 199 | '(','I','E','D',')',' ', '=','A','Y','5','D'|0x80, 200 | '(','I','E','N',')', '=','I','Y','E','H','N'|0x80, 201 | '(','I','E',')','T', '=','A','Y','4','E','H'|0x80, 202 | '(','I','\'',')', '=','A','Y','5'|0x80, 203 | ' ',':','(','I',')','^','%', '=','A','Y','5'|0x80, 204 | ' ',':','(','I','E',')',' ', '=','A','Y','4'|0x80, 205 | '(','I',')','%', '=','I','Y'|0x80, 206 | '(','I','E',')', '=','I','Y','4'|0x80, 207 | ' ','(','I','D','E','A',')', '=','A','Y','D','I','Y','5','A','H'|0x80, 208 | '(','I',')','^','+',':','#', '=','I','H'|0x80, 209 | '(','I','R',')','#', '=','A','Y','R'|0x80, 210 | '(','I','Z',')','%', '=','A','Y','Z'|0x80, 211 | '(','I','S',')','%', '=','A','Y','Z'|0x80, 212 | 'I','^','(','I',')','^','#', '=','I','H'|0x80, 213 | '+','^','(','I',')','^','+', '=','A','Y'|0x80, 214 | '#',':','^','(','I',')','^','+', '=','I','H'|0x80, 215 | '(','I',')','^','+', '=','A','Y'|0x80, 216 | '(','I','R',')', '=','E','R'|0x80, 217 | '(','I','G','H',')', '=','A','Y','4'|0x80, 218 | '(','I','L','D',')', '=','A','Y','5','L','D'|0x80, 219 | ' ','(','I','G','N',')', '=','I','H','G','N'|0x80, 220 | '(','I','G','N',')',' ', '=','A','Y','4','N'|0x80, 221 | '(','I','G','N',')','^', '=','A','Y','4','N'|0x80, 222 | '(','I','G','N',')','%', '=','A','Y','4','N'|0x80, 223 | '(','I','C','R','O',')', '=','A','Y','4','K','R','O','H'|0x80, 224 | '(','I','Q','U','E',')', '=','I','Y','4','K'|0x80, 225 | '(','I',')', '=','I','H'|0x80, 226 | 227 | ']','J'|0x80, 228 | ' ','(','J',')',' ', '=','J','E','Y','4'|0x80, 229 | '(','J',')', '=','J'|0x80, 230 | 231 | ']','K'|0x80, 232 | ' ','(','K',')',' ', '=','K','E','Y','4'|0x80, 233 | ' ','(','K',')','N','='|0x80, 234 | '(','K',')', '=','K'|0x80, 235 | 236 | ']','L'|0x80, 237 | ' ','(','L',')',' ', '=','E','H','4','L'|0x80, 238 | '(','L','O',')','C','#', '=','L','O','W'|0x80, 239 | 'L','(','L',')','='|0x80, 240 | '#',':','^','(','L',')','%', '=','U','L'|0x80, 241 | '(','L','E','A','D',')', '=','L','I','Y','D'|0x80, 242 | ' ','(','L','A','U','G','H',')', '=','L','A','E','4','F'|0x80, 243 | '(','L',')', '=','L'|0x80, 244 | 245 | ']','M'|0x80, 246 | ' ','(','M',')',' ', '=','E','H','4','M'|0x80, 247 | ' ','(','M','R','.',')',' ', '=','M','I','H','4','S','T','E','R'|0x80, 248 | ' ','(','M','S','.',')', '=','M','I','H','5','Z'|0x80, 249 | ' ','(','M','R','S','.',')',' ', '=','M','I','H','4','S','I','X','Z'|0x80, 250 | '(','M','O','V',')', '=','M','U','W','4','V'|0x80, 251 | '(','M','A','C','H','I','N',')', '=','M','A','H','S','H','I','Y','5','N'|0x80, 252 | 'M','(','M',')','='|0x80, 253 | '(','M',')', '=','M'|0x80, 254 | 255 | ']','N'|0x80, 256 | ' ','(','N',')',' ', '=','E','H','4','N'|0x80, 257 | 'E','(','N','G',')','+', '=','N','J'|0x80, 258 | '(','N','G',')','R', '=','N','X','G'|0x80, 259 | '(','N','G',')','#', '=','N','X','G'|0x80, 260 | '(','N','G','L',')','%', '=','N','X','G','U','L'|0x80, 261 | '(','N','G',')', '=','N','X'|0x80, 262 | '(','N','K',')', '=','N','X','K'|0x80, 263 | ' ','(','N','O','W',')',' ', '=','N','A','W','4'|0x80, 264 | 'N','(','N',')','='|0x80, 265 | '(','N','O','N',')','E', '=','N','A','H','4','N'|0x80, 266 | '(','N',')', '=','N'|0x80, 267 | 268 | ']','O'|0x80, 269 | ' ','(','O',')',' ', '=','O','H','4','W'|0x80, 270 | '(','O','F',')',' ', '=','A','H','V'|0x80, 271 | ' ','(','O','H',')',' ', '=','O','W','5'|0x80, 272 | '(','O','R','O','U','G','H',')', '=','E','R','4','O','W'|0x80, 273 | '#',':','(','O','R',')',' ', '=','E','R'|0x80, 274 | '#',':','(','O','R','S',')',' ', '=','E','R','Z'|0x80, 275 | '(','O','R',')', '=','A','O','R'|0x80, 276 | ' ','(','O','N','E',')', '=','W','A','H','N'|0x80, 277 | '#','(','O','N','E',')',' ', '=','W','A','H','N'|0x80, 278 | '(','O','W',')', '=','O','W'|0x80, 279 | ' ','(','O','V','E','R',')', '=','O','W','5','V','E','R'|0x80, 280 | 'P','R','(','O',')','V', '=','U','W','4'|0x80, 281 | '(','O','V',')', '=','A','H','4','V'|0x80, 282 | '(','O',')','^','%', '=','O','W','5'|0x80, 283 | '(','O',')','^','E','N', '=','O','W'|0x80, 284 | '(','O',')','^','I','#', '=','O','W','5'|0x80, 285 | '(','O','L',')','D', '=','O','W','4','L'|0x80, 286 | '(','O','U','G','H','T',')', '=','A','O','5','T'|0x80, 287 | '(','O','U','G','H',')', '=','A','H','5','F'|0x80, 288 | ' ','(','O','U',')', '=','A','W'|0x80, 289 | 'H','(','O','U',')','S','#', '=','A','W','4'|0x80, 290 | '(','O','U','S',')', '=','A','X','S'|0x80, 291 | '(','O','U','R',')', '=','O','H','R'|0x80, 292 | '(','O','U','L','D',')', '=','U','H','5','D'|0x80, 293 | '(','O','U',')','^','L', '=','A','H','5'|0x80, 294 | '(','O','U','P',')', '=','U','W','5','P'|0x80, 295 | '(','O','U',')', '=','A','W'|0x80, 296 | '(','O','Y',')', '=','O','Y'|0x80, 297 | '(','O','I','N','G',')', '=','O','W','4','I','H','N','X'|0x80, 298 | '(','O','I',')', '=','O','Y','5'|0x80, 299 | '(','O','O','R',')', '=','O','H','5','R'|0x80, 300 | '(','O','O','K',')', '=','U','H','5','K'|0x80, 301 | 'F','(','O','O','D',')', '=','U','W','5','D'|0x80, 302 | 'L','(','O','O','D',')', '=','A','H','5','D'|0x80, 303 | 'M','(','O','O','D',')', '=','U','W','5','D'|0x80, 304 | '(','O','O','D',')', '=','U','H','5','D'|0x80, 305 | 'F','(','O','O','T',')', '=','U','H','5','T'|0x80, 306 | '(','O','O',')', '=','U','W','5'|0x80, 307 | '(','O','\'',')', '=','O','H'|0x80, 308 | '(','O',')','E', '=','O','W'|0x80, 309 | '(','O',')',' ', '=','O','W'|0x80, 310 | '(','O','A',')', '=','O','W','4'|0x80, 311 | ' ','(','O','N','L','Y',')', '=','O','W','4','N','L','I','Y'|0x80, 312 | ' ','(','O','N','C','E',')', '=','W','A','H','4','N','S'|0x80, 313 | '(','O','N','\'','T',')', '=','O','W','4','N','T'|0x80, 314 | 'C','(','O',')','N', '=','A','A'|0x80, 315 | '(','O',')','N','G', '=','A','O'|0x80, 316 | ' ',':','^','(','O',')','N', '=','A','H'|0x80, 317 | 'I','(','O','N',')', '=','U','N'|0x80, 318 | '#',':','(','O','N',')', '=','U','N'|0x80, 319 | '#','^','(','O','N',')', '=','U','N'|0x80, 320 | '(','O',')','S','T', '=','O','W'|0x80, 321 | '(','O','F',')','^', '=','A','O','4','F'|0x80, 322 | '(','O','T','H','E','R',')', '=','A','H','5','D','H','E','R'|0x80, 323 | 'R','(','O',')','B', '=','R','A','A'|0x80, 324 | '^','R','(','O',')',':','#', '=','O','W','5'|0x80, 325 | '(','O','S','S',')',' ', '=','A','O','5','S'|0x80, 326 | '#',':','^','(','O','M',')', '=','A','H','M'|0x80, 327 | '(','O',')', '=','A','A'|0x80, 328 | 329 | ']','P'|0x80, 330 | ' ','(','P',')',' ', '=','P','I','Y','4'|0x80, 331 | '(','P','H',')', '=','F'|0x80, 332 | '(','P','E','O','P','L',')', '=','P','I','Y','5','P','U','L'|0x80, 333 | '(','P','O','W',')', '=','P','A','W','4'|0x80, 334 | '(','P','U','T',')',' ', '=','P','U','H','T'|0x80, 335 | '(','P',')','P','='|0x80, 336 | '(','P',')','S','='|0x80, 337 | '(','P',')','N','='|0x80, 338 | '(','P','R','O','F','.',')', '=','P','R','O','H','F','E','H','4','S','E','R'|0x80, 339 | '(','P',')', '=','P'|0x80, 340 | 341 | ']','Q'|0x80, 342 | ' ','(','Q',')',' ', '=','K','Y','U','W','4'|0x80, 343 | '(','Q','U','A','R',')', '=','K','W','O','H','5','R'|0x80, 344 | '(','Q','U',')', '=','K','W'|0x80, 345 | '(','Q',')', '=','K'|0x80, 346 | ']','R'|0x80, 347 | ' ','(','R',')',' ', '=','A','A','5','R'|0x80, 348 | ' ','(','R','E',')','^','#', '=','R','I','Y'|0x80, 349 | '(','R',')','R','='|0x80, 350 | '(','R',')', '=','R'|0x80, 351 | 352 | ']','S'|0x80, 353 | ' ','(','S',')',' ', '=','E','H','4','S'|0x80, 354 | '(','S','H',')', '=','S','H'|0x80, 355 | '#','(','S','I','O','N',')', '=','Z','H','U','N'|0x80, 356 | '(','S','O','M','E',')', '=','S','A','H','M'|0x80, 357 | '#','(','S','U','R',')','#', '=','Z','H','E','R'|0x80, 358 | '(','S','U','R',')','#', '=','S','H','E','R'|0x80, 359 | '#','(','S','U',')','#', '=','Z','H','U','W'|0x80, 360 | '#','(','S','S','U',')','#', '=','S','H','U','W'|0x80, 361 | '#','(','S','E','D',')', '=','Z','D'|0x80, 362 | '#','(','S',')','#', '=','Z'|0x80, 363 | '(','S','A','I','D',')', '=','S','E','H','D'|0x80, 364 | '^','(','S','I','O','N',')', '=','S','H','U','N'|0x80, 365 | '(','S',')','S','='|0x80, 366 | '.','(','S',')',' ', '=','Z'|0x80, 367 | '#',':','.','E','(','S',')',' ', '=','Z'|0x80, 368 | '#',':','^','#','(','S',')',' ', '=','S'|0x80, 369 | 'U','(','S',')',' ', '=','S'|0x80, 370 | ' ',':','#','(','S',')',' ', '=','Z'|0x80, 371 | '#','#','(','S',')',' ', '=','Z'|0x80, 372 | ' ','(','S','C','H',')', '=','S','K'|0x80, 373 | '(','S',')','C','+','='|0x80, 374 | '#','(','S','M',')', '=','Z','U','M'|0x80, 375 | '#','(','S','N',')','\'', '=','Z','U','M'|0x80, 376 | '(','S','T','L','E',')', '=','S','U','L'|0x80, 377 | '(','S',')', '=','S'|0x80, 378 | 379 | ']','T'|0x80, 380 | ' ','(','T',')',' ', '=','T','I','Y','4'|0x80, 381 | ' ','(','T','H','E',')',' ','#', '=','D','H','I','Y'|0x80, 382 | ' ','(','T','H','E',')',' ', '=','D','H','A','X'|0x80, 383 | '(','T','O',')',' ', '=','T','U','X'|0x80, 384 | ' ','(','T','H','A','T',')', '=','D','H','A','E','T'|0x80, 385 | ' ','(','T','H','I','S',')',' ', '=','D','H','I','H','S'|0x80, 386 | ' ','(','T','H','E','Y',')', '=','D','H','E','Y'|0x80, 387 | ' ','(','T','H','E','R','E',')', '=','D','H','E','H','R'|0x80, 388 | '(','T','H','E','R',')', '=','D','H','E','R'|0x80, 389 | '(','T','H','E','I','R',')', '=','D','H','E','H','R'|0x80, 390 | ' ','(','T','H','A','N',')',' ', '=','D','H','A','E','N'|0x80, 391 | ' ','(','T','H','E','M',')',' ', '=','D','H','A','E','N'|0x80, 392 | '(','T','H','E','S','E',')',' ', '=','D','H','I','Y','Z'|0x80, 393 | ' ','(','T','H','E','N',')', '=','D','H','E','H','N'|0x80, 394 | '(','T','H','R','O','U','G','H',')', '=','T','H','R','U','W','4'|0x80, 395 | '(','T','H','O','S','E',')', '=','D','H','O','H','Z'|0x80, 396 | '(','T','H','O','U','G','H',')',' ', '=','D','H','O','W'|0x80, 397 | '(','T','O','D','A','Y',')', '=','T','U','X','D','E','Y'|0x80, 398 | '(','T','O','M','O',')','R','R','O','W','=','T','U','M','A','A','5'|0x80, 399 | '(','T','O',')','T','A','L', '=','T','O','W','5'|0x80, 400 | ' ','(','T','H','U','S',')', '=','D','H','A','H','4','S'|0x80, 401 | '(','T','H',')', '=','T','H'|0x80, 402 | '#',':','(','T','E','D',')', '=','T','I','X','D'|0x80, 403 | 'S','(','T','I',')','#','N', '=','C','H'|0x80, 404 | '(','T','I',')','O', '=','S','H'|0x80, 405 | '(','T','I',')','A', '=','S','H'|0x80, 406 | '(','T','I','E','N',')', '=','S','H','U','N'|0x80, 407 | '(','T','U','R',')','#', '=','C','H','E','R'|0x80, 408 | '(','T','U',')','A', '=','C','H','U','W'|0x80, 409 | ' ','(','T','W','O',')', '=','T','U','W'|0x80, 410 | '&','(','T',')','E','N',' ','='|0x80, 411 | '(','T',')', '=','T'|0x80, 412 | 413 | ']','U'|0x80, 414 | ' ','(','U',')',' ', '=','Y','U','W','4'|0x80, 415 | ' ','(','U','N',')','I', '=','Y','U','W','N'|0x80, 416 | ' ','(','U','N',')', '=','A','H','N'|0x80, 417 | ' ','(','U','P','O','N',')', '=','A','X','P','A','O','N'|0x80, 418 | '@','(','U','R',')','#', '=','U','H','4','R'|0x80, 419 | '(','U','R',')','#', '=','Y','U','H','4','R'|0x80, 420 | '(','U','R',')', '=','E','R'|0x80, 421 | '(','U',')','^',' ', '=','A','H'|0x80, 422 | '(','U',')','^','^', '=','A','H','5'|0x80, 423 | '(','U','Y',')', '=','A','Y','5'|0x80, 424 | ' ','G','(','U',')','#','='|0x80, 425 | 'G','(','U',')','%','='|0x80, 426 | 'G','(','U',')','#', '=','W'|0x80, 427 | '#','N','(','U',')', '=','Y','U','W'|0x80, 428 | '@','(','U',')', '=','U','W'|0x80, 429 | '(','U',')', '=','Y','U','W'|0x80, 430 | 431 | ']','V'|0x80, 432 | ' ','(','V',')',' ', '=','V','I','Y','4'|0x80, 433 | '(','V','I','E','W',')', '=','V','Y','U','W','5'|0x80, 434 | '(','V',')', '=','V'|0x80, 435 | 436 | ']','W'|0x80, 437 | ' ','(','W',')',' ', '=','D','A','H','4','B','U','L','Y','U','W'|0x80, 438 | ' ','(','W','E','R','E',')', '=','W','E','R'|0x80, 439 | '(','W','A',')','S','H', '=','W','A','A'|0x80, 440 | '(','W','A',')','S','T', '=','W','E','Y'|0x80, 441 | '(','W','A',')','S', '=','W','A','H'|0x80, 442 | '(','W','A',')','T', '=','W','A','A'|0x80, 443 | '(','W','H','E','R','E',')', '=','W','H','E','H','R'|0x80, 444 | '(','W','H','A','T',')', '=','W','H','A','H','T'|0x80, 445 | '(','W','H','O','L',')', '=','/','H','O','W','L'|0x80, 446 | '(','W','H','O',')', '=','/','H','U','W'|0x80, 447 | '(','W','H',')', '=','W','H'|0x80, 448 | '(','W','A','R',')','#', '=','W','E','H','R'|0x80, 449 | '(','W','A','R',')', '=','W','A','O','R'|0x80, 450 | '(','W','O','R',')','^', '=','W','E','R'|0x80, 451 | '(','W','R',')', '=','R'|0x80, 452 | '(','W','O','M',')','A', '=','W','U','H','M'|0x80, 453 | '(','W','O','M',')','E', '=','W','I','H','M'|0x80, 454 | '(','W','E','A',')','R', '=','W','E','H'|0x80, 455 | '(','W','A','N','T',')', '=','W','A','A','5','N','T'|0x80, 456 | 'A','N','S','(','W','E','R',')', '=','E','R'|0x80, 457 | '(','W',')', '=','W'|0x80, 458 | 459 | ']','X'|0x80, 460 | ' ','(','X',')',' ', '=','E','H','4','K','R'|0x80, 461 | ' ','(','X',')', '=','Z'|0x80, 462 | '(','X',')', '=','K','S'|0x80, 463 | 464 | ']','Y'|0x80, 465 | ' ','(','Y',')',' ', '=','W','A','Y','4'|0x80, 466 | '(','Y','O','U','N','G',')', '=','Y','A','H','N','X'|0x80, 467 | ' ','(','Y','O','U','R',')', '=','Y','O','H','R'|0x80, 468 | ' ','(','Y','O','U',')', '=','Y','U','W'|0x80, 469 | ' ','(','Y','E','S',')', '=','Y','E','H','S'|0x80, 470 | ' ','(','Y',')', '=','Y'|0x80, 471 | 'F','(','Y',')', '=','A','Y'|0x80, 472 | 'P','S','(','Y','C','H',')', '=','A','Y','K'|0x80, 473 | '#',':','^','(','Y',')', '=','I','Y'|0x80, 474 | '#',':','^','(','Y',')','I', '=','I','Y'|0x80, 475 | ' ',':','(','Y',')',' ', '=','A','Y'|0x80, 476 | ' ',':','(','Y',')','#', '=','A','Y'|0x80, 477 | ' ',':','(','Y',')','^','+',':','#', '=','I','H'|0x80, 478 | ' ',':','(','Y',')','^','#', '=','A','Y'|0x80, 479 | '(','Y',')', '=','I','H'|0x80, 480 | 481 | ']','Z'|0x80, 482 | ' ','(','Z',')',' ', '=','Z','I','Y','4'|0x80, 483 | '(','Z',')', '=','Z'|0x80, 484 | 'j'|0x80 485 | }; 486 | 487 | const char rules2[] PROGMEM = 488 | { 489 | '(','A',')', '='|0x80, 490 | '(','!',')', '=','.'|0x80, 491 | '(','"',')',' ', '=','-','A','H','5','N','K','W','O','W','T','-'|0x80, 492 | '(','"',')', '=','K','W','O','W','4','T','-'|0x80, 493 | '(','#',')', '=',' ','N','A','H','4','M','B','E','R'|0x80, 494 | '(','$',')', '=',' ','D','A','A','4','L','E','R'|0x80, 495 | '(','%',')', '=',' ','P','E','R','S','E','H','4','N','T'|0x80, 496 | '(','&',')', '=',' ','A','E','N','D'|0x80, 497 | '(','\'',')', '='|0x80, 498 | '(','*',')', '=',' ','A','E','4','S','T','E','R','I','H','S','K'|0x80, 499 | '(','+',')', '=',' ','P','L','A','H','4','S'|0x80, 500 | '(',',',')', '=',','|0x80, 501 | ' ','(','-',')',' ', '=','-'|0x80, 502 | '(','-',')', '='|0x80, 503 | '(','.',')', '=',' ','P','O','Y','N','T'|0x80, 504 | '(','/',')', '=',' ','S','L','A','E','4','S','H'|0x80, 505 | '(','0',')', '=',' ','Z','I','Y','4','R','O','W'|0x80, 506 | ' ','(','1','S','T',')', '=','F','E','R','4','S','T'|0x80, 507 | ' ','(','1','0','T','H',')', '=','T','E','H','4','N','T','H'|0x80, 508 | '(','1',')', '=',' ','W','A','H','4','N'|0x80, 509 | ' ','(','2','N','D',')', '=','S','E','H','4','K','U','N','D'|0x80, 510 | '(','2',')', '=',' ','T','U','W','4'|0x80, 511 | ' ','(','3','R','D',')', '=','T','H','E','R','4','D'|0x80, 512 | '(','3',')', '=',' ','T','H','R','I','Y','4'|0x80, 513 | '(','4',')', '=',' ','F','O','H','4','R'|0x80, 514 | ' ','(','5','T','H',')', '=','F','I','H','4','F','T','H'|0x80, 515 | '(','5',')', '=',' ','F','A','Y','4','V'|0x80, 516 | ' ','(','6','4',')',' ', '=','S','I','H','4','K','S','T','I','Y',' ','F','O','H','R'|0x80, 517 | '(','6',')', '=',' ','S','I','H','4','K','S'|0x80, 518 | '(','7',')', '=',' ','S','E','H','4','V','U','N'|0x80, 519 | ' ','(','8','T','H',')', '=','E','Y','4','T','H'|0x80, 520 | '(','8',')', '=',' ','E','Y','4','T'|0x80, 521 | '(','9',')', '=',' ','N','A','Y','4','N'|0x80, 522 | '(',':',')', '=','.'|0x80, 523 | '(',';',')', '=','.'|0x80, 524 | '(','<',')', '=',' ','L','E','H','4','S',' ','D','H','A','E','N'|0x80, 525 | '(','=',')', '=',' ','I','Y','4','K','W','U','L','Z'|0x80, 526 | '(','>',')', '=',' ','G','R','E','Y','4','T','E','R',' ','D','H','A','E','N'|0x80, 527 | '(','?',')', '=','?'|0x80, 528 | '(','@',')', '=',' ','A','E','6','T'|0x80, 529 | '(','^',')', '=',' ','K','A','E','4','R','I','X','T'|0x80, 530 | ']','A'|0x80 531 | }; 532 | 533 | 534 | //26 items. From 'A' to 'Z' 535 | // positions for mem62 and mem63 for each character 536 | const unsigned char tab37489[] PROGMEM = 537 | { 538 | 0, 149, 247, 162, 57, 197, 6, 126, 539 | 199, 38, 55, 78, 145, 241, 85, 161, 540 | 254, 36, 69, 45, 167, 54, 83, 46, 541 | 71, 218 542 | }; 543 | 544 | const unsigned char tab37515[] PROGMEM = 545 | { 546 | 125, 126, 126, 127, 128, 129, 130, 130, 547 | 130, 132, 132, 132, 132, 132, 133, 135, 548 | 135, 136, 136, 137, 138, 139, 139, 140, 549 | 140, 140 550 | }; 551 | 552 | #endif 553 | -------------------------------------------------------------------------------- /src/render.c: -------------------------------------------------------------------------------- 1 | //#include 2 | #include 3 | #include // abs() 4 | 5 | #include "render.h" 6 | #include "RenderTabs.h" 7 | 8 | #include "esp8266sam_debug.h" 9 | #include 10 | #include "SamData.h" 11 | 12 | unsigned char wait1 = 7; 13 | unsigned char wait2 = 6; 14 | 15 | extern unsigned char A, X, Y; 16 | extern unsigned char mem44; 17 | extern unsigned char mem47; 18 | extern unsigned char mem49; 19 | extern unsigned char mem39; 20 | extern unsigned char mem50; 21 | extern unsigned char mem51; 22 | extern unsigned char mem53; 23 | extern unsigned char mem56; 24 | 25 | extern unsigned char speed; 26 | extern unsigned char pitch; 27 | extern int singmode; 28 | 29 | #define phonemeIndexOutput (samdata->sam.phonemeIndexOutput) 30 | #define stressOutput (samdata->sam.stressOutput) 31 | #define phonemeLengthOutput (samdata->sam.phonemeLengthOutput) 32 | #define pitches (samdata->render.pitches) 33 | #define frequency1 (samdata->render.frequency1) 34 | #define frequency2 (samdata->render.frequency2) 35 | #define frequency3 (samdata->render.frequency3) 36 | #define amplitude1 (samdata->render.amplitude1) 37 | #define amplitude2 (samdata->render.amplitude2) 38 | #define amplitude3 (samdata->render.amplitude3) 39 | #define sampledConsonantFlag (samdata->render.sampledConsonantFlag) 40 | 41 | void AddInflection(unsigned char mem48, unsigned char phase1); 42 | unsigned char trans(unsigned char mem39212, unsigned char mem39213); 43 | 44 | 45 | // contains the final soundbuffer 46 | extern int bufferpos; 47 | //extern char *buffer; 48 | 49 | #ifndef ESP8266 50 | static void yield() { /* NOOP */ } 51 | #endif 52 | 53 | //timetable for more accurate c64 simulation 54 | const unsigned char timetable[5][5] PROGMEM = 55 | { 56 | {162, 167, 167, 127, 128}, 57 | {226, 60, 60, 0, 0}, 58 | {225, 60, 59, 0, 0}, 59 | {200, 0, 0, 54, 55}, 60 | {199, 0, 0, 54, 54} 61 | }; 62 | 63 | extern void (*outcb)(void *, unsigned char); 64 | extern void *outcbdata; 65 | static unsigned char oldtimetableindex = 0; 66 | static unsigned char lastAry[5]; 67 | void Output8BitAry(int index, unsigned char ary[5]) 68 | { 69 | int newbufferpos = bufferpos + pgm_read_byte(&timetable[oldtimetableindex][index]); 70 | int bp0 = bufferpos / 50; 71 | int bp1 = newbufferpos / 50; 72 | int k=0; 73 | for (int i=bp0; i X 173 | // unvoiced 1 bit -> 5 174 | // 175 | // voiced 0 bit -> 6 176 | // voiced 1 bit -> 24 177 | // 178 | // Where X is a value from the table: 179 | // 180 | // { 0x18, 0x1A, 0x17, 0x17, 0x17 }; 181 | // 182 | // The index into this table is determined by masking off the lower 183 | // 3 bits from the SampledPhonemesTable: 184 | // 185 | // index = (SampledPhonemesTable[i] & 7) - 1; 186 | // 187 | // For voices samples, samples are interleaved between voiced output. 188 | 189 | 190 | // Code48227() 191 | void RenderSample(unsigned char *mem66) 192 | { 193 | int tempA; 194 | // current phoneme's index 195 | mem49 = Y; 196 | 197 | // mask low three bits and subtract 1 get value to 198 | // convert 0 bits on unvoiced samples. 199 | A = mem39&7; 200 | X = A-1; 201 | 202 | // store the result 203 | mem56 = X; 204 | 205 | // determine which offset to use from table { 0x18, 0x1A, 0x17, 0x17, 0x17 } 206 | // T, S, Z 0 0x18 207 | // CH, J, SH, ZH 1 0x1A 208 | // P, F*, V, TH, DH 2 0x17 209 | // /H 3 0x17 210 | // /X 4 0x17 211 | 212 | // get value from the table 213 | mem53 = pgm_read_byte(tab48426+X); //tab48426[X]; 214 | mem47 = X; //46016+mem[56]*256 215 | 216 | // voiced sample? 217 | A = mem39 & 248; 218 | if(A == 0) 219 | { 220 | // voiced phoneme: Z*, ZH, V*, DH 221 | Y = mem49; 222 | A = pitches[mem49] >> 4; 223 | 224 | // jump to voiced portion 225 | goto pos48315; 226 | } 227 | 228 | Y = A ^ 255; 229 | pos48274: 230 | 231 | // step through the 8 bits in the sample 232 | mem56 = 8; 233 | 234 | // get the next sample from the table 235 | // mem47*256 = offset to start of samples 236 | A = pgm_read_byte(sampleTable + mem47*256+Y); // sampleTable[mem47*256+Y]; 237 | pos48280: 238 | 239 | // left shift to get the high bit 240 | tempA = A; 241 | A = A << 1; 242 | //48281: BCC 48290 243 | 244 | // bit not set? 245 | if ((tempA & 128) == 0) 246 | { 247 | // convert the bit to value from table 248 | X = mem53; 249 | //mem[54296] = X; 250 | // output the byte 251 | Output8Bit(1, (X&0xf)*16); 252 | // if X != 0, exit loop 253 | if(X != 0) goto pos48296; 254 | } 255 | 256 | // output a 5 for the on bit 257 | Output8Bit(2, 5*16); 258 | 259 | //48295: NOP 260 | pos48296: 261 | 262 | X = 0; 263 | 264 | // decrement counter 265 | mem56--; 266 | 267 | // if not done, jump to top of loop 268 | if (mem56 != 0) goto pos48280; 269 | 270 | // increment position 271 | Y++; 272 | if (Y != 0) goto pos48274; 273 | 274 | // restore values and return 275 | mem44 = 1; 276 | Y = mem49; 277 | return; 278 | 279 | 280 | unsigned char phase1; 281 | 282 | pos48315: 283 | // handle voiced samples here 284 | 285 | // number of samples? 286 | phase1 = A ^ 255; 287 | 288 | Y = *mem66; 289 | do 290 | { 291 | //pos48321: 292 | 293 | // shift through all 8 bits 294 | mem56 = 8; 295 | //A = Read(mem47, Y); 296 | 297 | // fetch value from table 298 | A = pgm_read_byte(sampleTable + mem47*256+Y); //sampleTable[mem47*256+Y]; 299 | 300 | // loop 8 times 301 | //pos48327: 302 | do 303 | { 304 | //48327: ASL A 305 | //48328: BCC 48337 306 | 307 | // left shift and check high bit 308 | tempA = A; 309 | A = A << 1; 310 | if ((tempA & 128) != 0) 311 | { 312 | // if bit set, output 26 313 | X = 26; 314 | Output8Bit(3, (X&0xf)*16); 315 | } else 316 | { 317 | //timetable 4 318 | // bit is not set, output a 6 319 | X=6; 320 | Output8Bit(4, (X&0xf)*16); 321 | } 322 | 323 | mem56--; 324 | } while(mem56 != 0); 325 | 326 | // move ahead in the table 327 | Y++; 328 | 329 | // continue until counter done 330 | phase1++; 331 | 332 | } while (phase1 != 0); 333 | // if (phase1 != 0) goto pos48321; 334 | 335 | // restore values and return 336 | A = 1; 337 | mem44 = 1; 338 | *mem66 = Y; 339 | Y = mem49; 340 | return; 341 | } 342 | 343 | 344 | 345 | // RENDER THE PHONEMES IN THE LIST 346 | // 347 | // The phoneme list is converted into sound through the steps: 348 | // 349 | // 1. Copy each phoneme number of times into the frames list, 350 | // where each frame represents 10 milliseconds of sound. 351 | // 352 | // 2. Determine the transitions lengths between phonemes, and linearly 353 | // interpolate the values across the frames. 354 | // 355 | // 3. Offset the pitches by the fundamental frequency. 356 | // 357 | // 4. Render the each frame. 358 | 359 | 360 | 361 | //void Code47574() 362 | void Render() 363 | { 364 | unsigned char phase1 = 0; //mem43 365 | unsigned char phase2 = 0; 366 | unsigned char phase3 = 0; 367 | unsigned char mem66 = 0; 368 | unsigned char mem38 = 0; 369 | unsigned char mem40 = 0; 370 | unsigned char speedcounter = 0; //mem45 371 | unsigned char mem48 = 0; 372 | int i; 373 | if (phonemeIndexOutput[0] == 255) return; //exit if no data 374 | 375 | A = 0; 376 | X = 0; 377 | mem44 = 0; 378 | 379 | 380 | // CREATE FRAMES 381 | // 382 | // The length parameter in the list corresponds to the number of frames 383 | // to expand the phoneme to. Each frame represents 10 milliseconds of time. 384 | // So a phoneme with a length of 7 = 7 frames = 70 milliseconds duration. 385 | // 386 | // The parameters are copied from the phoneme to the frame verbatim. 387 | 388 | 389 | // pos47587: 390 | do 391 | { 392 | // get the index 393 | Y = mem44; 394 | // get the phoneme at the index 395 | A = phonemeIndexOutput[mem44]; 396 | mem56 = A; 397 | 398 | // if terminal phoneme, exit the loop 399 | if (A == 255) break; 400 | 401 | // period phoneme *. 402 | if (A == 1) 403 | { 404 | // add rising inflection 405 | A = 1; 406 | mem48 = 1; 407 | //goto pos48376; 408 | AddInflection(mem48, phase1); 409 | } 410 | /* 411 | if (A == 2) goto pos48372; 412 | */ 413 | 414 | // question mark phoneme? 415 | if (A == 2) 416 | { 417 | // create falling inflection 418 | mem48 = 255; 419 | AddInflection(mem48, phase1); 420 | } 421 | // pos47615: 422 | 423 | // get the stress amount (more stress = higher pitch) 424 | phase1 = pgm_read_byte(tab47492 + stressOutput[Y] + 1); // tab47492[stressOutput[Y] + 1]; 425 | 426 | // get number of frames to write 427 | phase2 = phonemeLengthOutput[Y]; 428 | Y = mem56; 429 | 430 | // copy from the source to the frames list 431 | do 432 | { 433 | frequency1[X] = freq1data[Y]; // F1 frequency 434 | frequency2[X] = freq2data[Y]; // F2 frequency 435 | frequency3[X] = freq3data[Y]; // F3 frequency 436 | amplitude1[X] = pgm_read_byte(&l1data[Y]); // F1 amplitude 437 | amplitude2[X] = pgm_read_byte(&l2data[Y]); // F2 amplitude 438 | amplitude3[X] = pgm_read_byte(&l3data[Y]); // F3 amplitude 439 | sampledConsonantFlag[X] = pgm_read_byte(&sampledConsonantFlags[Y]); // phoneme data for sampled consonants 440 | pitches[X] = pitch + phase1; // pitch 441 | X++; 442 | phase2--; 443 | } while(phase2 != 0); 444 | mem44++; 445 | } while(mem44 != 0); 446 | yield(); 447 | if (DEBUG_ESP8266SAM_LIB) 448 | { 449 | PrintOutput(sampledConsonantFlag, frequency1, frequency2, frequency3, amplitude1, amplitude2, amplitude3, pitches); 450 | } 451 | // ------------------- 452 | //pos47694: 453 | 454 | // CREATE TRANSITIONS 455 | // 456 | // Linear transitions are now created to smoothly connect the 457 | // end of one sustained portion of a phoneme to the following 458 | // phoneme. 459 | // 460 | // To do this, three tables are used: 461 | // 462 | // Table Purpose 463 | // ========= ================================================== 464 | // blendRank Determines which phoneme's blend values are used. 465 | // 466 | // blendOut The number of frames at the end of the phoneme that 467 | // will be used to transition to the following phoneme. 468 | // 469 | // blendIn The number of frames of the following phoneme that 470 | // will be used to transition into that phoneme. 471 | // 472 | // In creating a transition between two phonemes, the phoneme 473 | // with the HIGHEST rank is used. Phonemes are ranked on how much 474 | // their identity is based on their transitions. For example, 475 | // vowels are and diphthongs are identified by their sustained portion, 476 | // rather than the transitions, so they are given low values. In contrast, 477 | // stop consonants (P, B, T, K) and glides (Y, L) are almost entirely 478 | // defined by their transitions, and are given high rank values. 479 | // 480 | // Here are the rankings used by SAM: 481 | // 482 | // Rank Type Phonemes 483 | // 2 All vowels IY, IH, etc. 484 | // 5 Diphthong endings YX, WX, ER 485 | // 8 Terminal liquid consonants LX, WX, YX, N, NX 486 | // 9 Liquid consonants L, RX, W 487 | // 10 Glide R, OH 488 | // 11 Glide WH 489 | // 18 Voiceless fricatives S, SH, F, TH 490 | // 20 Voiced fricatives Z, ZH, V, DH 491 | // 23 Plosives, stop consonants P, T, K, KX, DX, CH 492 | // 26 Stop consonants J, GX, B, D, G 493 | // 27-29 Stop consonants (internal) ** 494 | // 30 Unvoiced consonants /H, /X and Q* 495 | // 160 Nasal M 496 | // 497 | // To determine how many frames to use, the two phonemes are 498 | // compared using the blendRank[] table. The phoneme with the 499 | // higher rank is selected. In case of a tie, a blend of each is used: 500 | // 501 | // if blendRank[phoneme1] == blendRank[phomneme2] 502 | // // use lengths from each phoneme 503 | // outBlendFrames = outBlend[phoneme1] 504 | // inBlendFrames = outBlend[phoneme2] 505 | // else if blendRank[phoneme1] > blendRank[phoneme2] 506 | // // use lengths from first phoneme 507 | // outBlendFrames = outBlendLength[phoneme1] 508 | // inBlendFrames = inBlendLength[phoneme1] 509 | // else 510 | // // use lengths from the second phoneme 511 | // // note that in and out are SWAPPED! 512 | // outBlendFrames = inBlendLength[phoneme2] 513 | // inBlendFrames = outBlendLength[phoneme2] 514 | // 515 | // Blend lengths can't be less than zero. 516 | // 517 | // Transitions are assumed to be symetrical, so if the transition 518 | // values for the second phoneme are used, the inBlendLength and 519 | // outBlendLength values are SWAPPED. 520 | // 521 | // For most of the parameters, SAM interpolates over the range of the last 522 | // outBlendFrames-1 and the first inBlendFrames. 523 | // 524 | // The exception to this is the Pitch[] parameter, which is interpolates the 525 | // pitch from the CENTER of the current phoneme to the CENTER of the next 526 | // phoneme. 527 | // 528 | // Here are two examples. First, For example, consider the word "SUN" (S AH N) 529 | // 530 | // Phoneme Duration BlendWeight OutBlendFrames InBlendFrames 531 | // S 2 18 1 3 532 | // AH 8 2 4 4 533 | // N 7 8 1 2 534 | // 535 | // The formant transitions for the output frames are calculated as follows: 536 | // 537 | // flags ampl1 freq1 ampl2 freq2 ampl3 freq3 pitch 538 | // ------------------------------------------------ 539 | // S 540 | // 241 0 6 0 73 0 99 61 Use S (weight 18) for transition instead of AH (weight 2) 541 | // 241 0 6 0 73 0 99 61 <-- (OutBlendFrames-1) = (1-1) = 0 frames 542 | // AH 543 | // 0 2 10 2 66 0 96 59 * <-- InBlendFrames = 3 frames 544 | // 0 4 14 3 59 0 93 57 * 545 | // 0 8 18 5 52 0 90 55 * 546 | // 0 15 22 9 44 1 87 53 547 | // 0 15 22 9 44 1 87 53 548 | // 0 15 22 9 44 1 87 53 Use N (weight 8) for transition instead of AH (weight 2). 549 | // 0 15 22 9 44 1 87 53 Since N is second phoneme, reverse the IN and OUT values. 550 | // 0 11 17 8 47 1 98 56 * <-- (InBlendFrames-1) = (2-1) = 1 frames 551 | // N 552 | // 0 8 12 6 50 1 109 58 * <-- OutBlendFrames = 1 553 | // 0 5 6 5 54 0 121 61 554 | // 0 5 6 5 54 0 121 61 555 | // 0 5 6 5 54 0 121 61 556 | // 0 5 6 5 54 0 121 61 557 | // 0 5 6 5 54 0 121 61 558 | // 0 5 6 5 54 0 121 61 559 | // 560 | // Now, consider the reverse "NUS" (N AH S): 561 | // 562 | // flags ampl1 freq1 ampl2 freq2 ampl3 freq3 pitch 563 | // ------------------------------------------------ 564 | // N 565 | // 0 5 6 5 54 0 121 61 566 | // 0 5 6 5 54 0 121 61 567 | // 0 5 6 5 54 0 121 61 568 | // 0 5 6 5 54 0 121 61 569 | // 0 5 6 5 54 0 121 61 570 | // 0 5 6 5 54 0 121 61 Use N (weight 8) for transition instead of AH (weight 2) 571 | // 0 5 6 5 54 0 121 61 <-- (OutBlendFrames-1) = (1-1) = 0 frames 572 | // AH 573 | // 0 8 11 6 51 0 110 59 * <-- InBlendFrames = 2 574 | // 0 11 16 8 48 0 99 56 * 575 | // 0 15 22 9 44 1 87 53 Use S (weight 18) for transition instead of AH (weight 2) 576 | // 0 15 22 9 44 1 87 53 Since S is second phoneme, reverse the IN and OUT values. 577 | // 0 9 18 5 51 1 90 55 * <-- (InBlendFrames-1) = (3-1) = 2 578 | // 0 4 14 3 58 1 93 57 * 579 | // S 580 | // 241 2 10 2 65 1 96 59 * <-- OutBlendFrames = 1 581 | // 241 0 6 0 73 0 99 61 582 | 583 | A = 0; 584 | mem44 = 0; 585 | mem49 = 0; // mem49 starts at as 0 586 | X = 0; 587 | while(1) //while No. 1 588 | { 589 | 590 | // get the current and following phoneme 591 | Y = phonemeIndexOutput[X]; 592 | A = phonemeIndexOutput[X+1]; 593 | X++; 594 | 595 | // exit loop at end token 596 | if (A == 255) break;//goto pos47970; 597 | 598 | 599 | // get the ranking of each phoneme 600 | X = A; 601 | mem56 = pgm_read_byte(blendRank+A); //blendRank[A]; 602 | A = pgm_read_byte(blendRank+Y); //blendRank[Y]; 603 | 604 | // compare the rank - lower rank value is stronger 605 | if (A == mem56) 606 | { 607 | // same rank, so use out blend lengths from each phoneme 608 | phase1 = pgm_read_byte(outBlendLength+Y);//outBlendLength[Y]; 609 | phase2 = pgm_read_byte(outBlendLength+X);//outBlendLength[X]; 610 | } else 611 | if (A < mem56) 612 | { 613 | // first phoneme is stronger, so us it's blend lengths 614 | phase1 = pgm_read_byte(inBlendLength+X);//inBlendLength[X]; 615 | phase2 = pgm_read_byte(outBlendLength+X);//outBlendLength[X]; 616 | } else 617 | { 618 | // second phoneme is stronger, so use it's blend lengths 619 | // note the out/in are swapped 620 | phase1 = pgm_read_byte(outBlendLength+Y);//outBlendLength[Y]; 621 | phase2 = pgm_read_byte(inBlendLength+Y);//inBlendLength[Y]; 622 | } 623 | 624 | Y = mem44; 625 | A = mem49 + phonemeLengthOutput[mem44]; // A is mem49 + length 626 | mem49 = A; // mem49 now holds length + position 627 | A = A + phase2; //Maybe Problem because of carry flag 628 | 629 | //47776: ADC 42 630 | speedcounter = A; 631 | mem47 = 168; 632 | phase3 = mem49 - phase1; // what is mem49 633 | A = phase1 + phase2; // total transition? 634 | mem38 = A; 635 | 636 | X = A; 637 | X -= 2; 638 | if ((X & 128) == 0) 639 | do //while No. 2 640 | { 641 | //pos47810: 642 | 643 | // mem47 is used to index the tables: 644 | // 168 pitches[] 645 | // 169 frequency1 646 | // 170 frequency2 647 | // 171 frequency3 648 | // 172 amplitude1 649 | // 173 amplitude2 650 | // 174 amplitude3 651 | 652 | mem40 = mem38; 653 | 654 | if (mem47 == 168) // pitch 655 | { 656 | 657 | // unlike the other values, the pitches[] interpolates from 658 | // the middle of the current phoneme to the middle of the 659 | // next phoneme 660 | 661 | unsigned char mem36, mem37; 662 | // half the width of the current phoneme 663 | mem36 = phonemeLengthOutput[mem44] >> 1; 664 | // half the width of the next phoneme 665 | mem37 = phonemeLengthOutput[mem44+1] >> 1; 666 | // sum the values 667 | mem40 = mem36 + mem37; // length of both halves 668 | mem37 += mem49; // center of next phoneme 669 | mem36 = mem49 - mem36; // center index of current phoneme 670 | A = Read(mem47, mem37); // value at center of next phoneme - end interpolation value 671 | //A = mem[address]; 672 | 673 | Y = mem36; // start index of interpolation 674 | mem53 = A - Read(mem47, mem36); // value to center of current phoneme 675 | } else 676 | { 677 | // value to interpolate to 678 | A = Read(mem47, speedcounter); 679 | // position to start interpolation from 680 | Y = phase3; 681 | // value to interpolate from 682 | mem53 = A - Read(mem47, phase3); 683 | } 684 | 685 | //Code47503(mem40); 686 | // ML : Code47503 is division with remainder, and mem50 gets the sign 687 | 688 | // calculate change per frame 689 | signed char m53 = (signed char)mem53; 690 | mem50 = mem53 & 128; 691 | unsigned char m53abs = abs(m53); 692 | mem51 = m53abs % mem40; //abs((char)m53) % mem40; 693 | mem53 = (unsigned char)((signed char)(m53) / mem40); 694 | // interpolation range 695 | X = mem40; // number of frames to interpolate over 696 | Y = phase3; // starting frame 697 | 698 | 699 | // linearly interpolate values 700 | 701 | mem56 = 0; 702 | //47907: CLC 703 | //pos47908: 704 | while(1) //while No. 3 705 | { 706 | A = Read(mem47, Y) + mem53; //carry alway cleared 707 | 708 | mem48 = A; 709 | Y++; 710 | X--; 711 | if(X == 0) break; 712 | 713 | mem56 += mem51; 714 | if (mem56 >= mem40) //??? 715 | { 716 | mem56 -= mem40; //carry? is set 717 | //if ((mem56 & 128)==0) 718 | if ((mem50 & 128)==0) 719 | { 720 | //47935: BIT 50 721 | //47937: BMI 47943 722 | if(mem48 != 0) mem48++; 723 | } else mem48--; 724 | } 725 | //pos47945: 726 | Write(mem47, Y, mem48); 727 | } //while No. 3 728 | 729 | //pos47952: 730 | mem47++; 731 | //if (mem47 != 175) goto pos47810; 732 | } while (mem47 != 175); //while No. 2 733 | //pos47963: 734 | mem44++; 735 | X = mem44; 736 | } //while No. 1 737 | yield(); 738 | //goto pos47701; 739 | //pos47970: 740 | 741 | // add the length of this phoneme 742 | mem48 = mem49 + phonemeLengthOutput[mem44]; 743 | 744 | 745 | // ASSIGN PITCH CONTOUR 746 | // 747 | // This subtracts the F1 frequency from the pitch to create a 748 | // pitch contour. Without this, the output would be at a single 749 | // pitch level (monotone). 750 | 751 | 752 | // don't adjust pitch if in sing mode 753 | if (!singmode) 754 | { 755 | // iterate through the buffer 756 | for(i=0; i<256; i++) { 757 | // subtract half the frequency of the formant 1. 758 | // this adds variety to the voice 759 | pitches[i] -= (frequency1[i] >> 1); 760 | } 761 | } 762 | 763 | phase1 = 0; 764 | phase2 = 0; 765 | phase3 = 0; 766 | mem49 = 0; 767 | speedcounter = 72; //sam standard speed 768 | 769 | // RESCALE AMPLITUDE 770 | // 771 | // Rescale volume from a linear scale to decibels. 772 | // 773 | 774 | //amplitude rescaling 775 | for(i=255; i>=0; i--) 776 | { 777 | amplitude1[i] = pgm_read_byte(amplitudeRescale + amplitude1[i]);//amplitudeRescale[amplitude1[i]]; 778 | amplitude2[i] = pgm_read_byte(amplitudeRescale + amplitude2[i]);//amplitudeRescale[amplitude2[i]]; 779 | amplitude3[i] = pgm_read_byte(amplitudeRescale + amplitude3[i]);//amplitudeRescale[amplitude3[i]]; 780 | } 781 | 782 | Y = 0; 783 | A = pitches[0]; 784 | mem44 = A; 785 | X = A; 786 | mem38 = A - (A>>2); // 3/4*A ??? 787 | yield(); 788 | if (DEBUG_ESP8266SAM_LIB) 789 | { 790 | PrintOutput(sampledConsonantFlag, frequency1, frequency2, frequency3, amplitude1, amplitude2, amplitude3, pitches); 791 | } 792 | 793 | // PROCESS THE FRAMES 794 | // 795 | // In traditional vocal synthesis, the glottal pulse drives filters, which 796 | // are attenuated to the frequencies of the formants. 797 | // 798 | // SAM generates these formants directly with sin and rectangular waves. 799 | // To simulate them being driven by the glottal pulse, the waveforms are 800 | // reset at the beginning of each glottal pulse. 801 | 802 | //finally the loop for sound output 803 | //pos48078: 804 | while(1) 805 | { 806 | // get the sampled information on the phoneme 807 | A = sampledConsonantFlag[Y]; 808 | mem39 = A; 809 | 810 | // unvoiced sampled phoneme? 811 | A = A & 248; 812 | if(A != 0) 813 | { 814 | // render the sample for the phoneme 815 | RenderSample(&mem66); 816 | 817 | // skip ahead two in the phoneme buffer 818 | Y += 2; 819 | mem48 -= 2; 820 | } else 821 | { 822 | // simulate the glottal pulse and formants 823 | unsigned char ary[5]; 824 | unsigned int p1 = phase1 * 256; // Fixed point integers because we need to divide later on 825 | unsigned int p2 = phase2 * 256; 826 | unsigned int p3 = phase3 * 256; 827 | 828 | for (int k=0; k<5; k++) { 829 | signed char sp1 = (signed char)pgm_read_byte(&sinus[0xff & (p1>>8)]); 830 | signed char sp2 = (signed char)pgm_read_byte(&sinus[0xff & (p2>>8)]); 831 | signed char rp3 = (signed char)pgm_read_byte(&rectangle[0xff & (p3>>8)]); 832 | signed int sin1 = sp1 * ((unsigned char)amplitude1[Y] & 0x0f); 833 | signed int sin2 = sp2 * ((unsigned char)amplitude2[Y] & 0x0f); 834 | signed int rect = rp3 * ((unsigned char)amplitude3[Y] & 0x0f); 835 | signed int mux = sin1 + sin2 + rect; 836 | mux /= 32; 837 | mux += 128; // Go from signed to unsigned amplitude 838 | ary[k] = mux; 839 | p1 += ((int)frequency1[Y]) * 256 / 4; // Compromise, this becomes a shift and works well 840 | p2 += ((int)frequency2[Y]) * 256 / 4; 841 | p3 += ((int)frequency3[Y]) * 256 / 4; 842 | } 843 | // output the accumulated value 844 | Output8BitAry(0, ary); 845 | speedcounter--; 846 | if (speedcounter != 0) goto pos48155; 847 | Y++; //go to next amplitude 848 | 849 | // decrement the frame count 850 | mem48--; 851 | } 852 | 853 | // if the frame count is zero, exit the loop 854 | if(mem48 == 0) return; 855 | speedcounter = speed; 856 | pos48155: 857 | 858 | // decrement the remaining length of the glottal pulse 859 | mem44--; 860 | 861 | // finished with a glottal pulse? 862 | if(mem44 == 0) 863 | { 864 | pos48159: 865 | // fetch the next glottal pulse length 866 | A = pitches[Y]; 867 | mem44 = A; 868 | A = A - (A>>2); 869 | mem38 = A; 870 | 871 | // reset the formant wave generators to keep them in 872 | // sync with the glottal pulse 873 | phase1 = 0; 874 | phase2 = 0; 875 | phase3 = 0; 876 | continue; 877 | } 878 | 879 | // decrement the count 880 | mem38--; 881 | 882 | // is the count non-zero and the sampled flag is zero? 883 | if((mem38 != 0) || (mem39 == 0)) 884 | { 885 | // reset the phase of the formants to match the pulse 886 | phase1 += frequency1[Y]; 887 | phase2 += frequency2[Y]; 888 | phase3 += frequency3[Y]; 889 | continue; 890 | } 891 | 892 | // voiced sampled phonemes interleave the sample with the 893 | // glottal pulse. The sample flag is non-zero, so render 894 | // the sample for the phoneme. 895 | RenderSample(&mem66); 896 | goto pos48159; 897 | } //while 898 | 899 | } 900 | 901 | 902 | // Create a rising or falling inflection 30 frames prior to 903 | // index X. A rising inflection is used for questions, and 904 | // a falling inflection is used for statements. 905 | 906 | void AddInflection(unsigned char mem48, unsigned char phase1) 907 | { 908 | //pos48372: 909 | // mem48 = 255; 910 | //pos48376: 911 | 912 | // store the location of the punctuation 913 | mem49 = X; 914 | A = X; 915 | int Atemp = A; 916 | 917 | // backup 30 frames 918 | A = A - 30; 919 | // if index is before buffer, point to start of buffer 920 | if (Atemp <= 30) A=0; 921 | X = A; 922 | 923 | // FIXME: Explain this fix better, it's not obvious 924 | // ML : A =, fixes a problem with invalid pitch with '.' 925 | while( (A=pitches[X]) == 127) X++; 926 | 927 | 928 | pos48398: 929 | //48398: CLC 930 | //48399: ADC 48 931 | 932 | // add the inflection direction 933 | A += mem48; 934 | phase1 = A; 935 | 936 | // set the inflection 937 | pitches[X] = A; 938 | pos48406: 939 | 940 | // increment the position 941 | X++; 942 | 943 | // exit if the punctuation has been reached 944 | if (X == mem49) return; //goto pos47615; 945 | if (pitches[X] == 255) goto pos48406; 946 | A = phase1; 947 | goto pos48398; 948 | } 949 | 950 | /* 951 | SAM's voice can be altered by changing the frequencies of the 952 | mouth formant (F1) and the throat formant (F2). Only the voiced 953 | phonemes (5-29 and 48-53) are altered. 954 | */ 955 | // mouth formants (F1) 5..29 956 | const unsigned char mouthFormants5_29[30] PROGMEM = { 957 | 0, 0, 0, 0, 0, 10, 958 | 14, 19, 24, 27, 23, 21, 16, 20, 14, 18, 14, 18, 18, 959 | 16, 13, 15, 11, 18, 14, 11, 9, 6, 6, 6}; 960 | 961 | // throat formants (F2) 5..29 962 | const unsigned char throatFormants5_29[30] PROGMEM = { 963 | 255, 255, 964 | 255, 255, 255, 84, 73, 67, 63, 40, 44, 31, 37, 45, 73, 49, 965 | 36, 30, 51, 37, 29, 69, 24, 50, 30, 24, 83, 46, 54, 86}; 966 | 967 | // there must be no zeros in this 2 tables 968 | // formant 1 frequencies (mouth) 48..53 969 | const unsigned char mouthFormants48_53[6] PROGMEM = {19, 27, 21, 27, 18, 13}; 970 | 971 | // formant 2 frequencies (throat) 48..53 972 | const unsigned char throatFormants48_53[6] PROGMEM = {72, 39, 31, 43, 30, 34}; 973 | 974 | void SetMouthThroat(unsigned char mouth, unsigned char throat) 975 | { 976 | unsigned char initialFrequency; 977 | unsigned char newFrequency = 0; 978 | //unsigned char mouth; //mem38880 979 | //unsigned char throat; //mem38881 980 | 981 | unsigned char pos = 5; //mem39216 982 | //pos38942: 983 | // recalculate formant frequencies 5..29 for the mouth (F1) and throat (F2) 984 | while(pos != 30) 985 | { 986 | // recalculate mouth frequency 987 | initialFrequency = pgm_read_byte(&mouthFormants5_29[pos]); 988 | if (initialFrequency != 0) newFrequency = trans(mouth, initialFrequency); 989 | freq1data[pos] = newFrequency; 990 | 991 | // recalculate throat frequency 992 | initialFrequency = pgm_read_byte(&throatFormants5_29[pos]); 993 | if(initialFrequency != 0) newFrequency = trans(throat, initialFrequency); 994 | freq2data[pos] = newFrequency; 995 | pos++; 996 | } 997 | 998 | //pos39059: 999 | // recalculate formant frequencies 48..53 1000 | pos = 48; 1001 | Y = 0; 1002 | while(pos != 54) 1003 | { 1004 | // recalculate F1 (mouth formant) 1005 | initialFrequency = pgm_read_byte(&mouthFormants48_53[Y]); 1006 | newFrequency = trans(mouth, initialFrequency); 1007 | freq1data[pos] = newFrequency; 1008 | 1009 | // recalculate F2 (throat formant) 1010 | initialFrequency = pgm_read_byte(&throatFormants48_53[Y]); 1011 | newFrequency = trans(throat, initialFrequency); 1012 | freq2data[pos] = newFrequency; 1013 | Y++; 1014 | pos++; 1015 | } 1016 | } 1017 | 1018 | 1019 | //return = (mem39212*mem39213) >> 1 1020 | unsigned char trans(unsigned char mem39212, unsigned char mem39213) 1021 | { 1022 | //pos39008: 1023 | unsigned char carry; 1024 | int temp; 1025 | unsigned char mem39214, mem39215; 1026 | A = 0; 1027 | mem39215 = 0; 1028 | mem39214 = 0; 1029 | X = 8; 1030 | do 1031 | { 1032 | carry = mem39212 & 1; 1033 | mem39212 = mem39212 >> 1; 1034 | if (carry != 0) 1035 | { 1036 | /* 1037 | 39018: LSR 39212 1038 | 39021: BCC 39033 1039 | */ 1040 | carry = 0; 1041 | A = mem39215; 1042 | temp = (int)A + (int)mem39213; 1043 | A = A + mem39213; 1044 | if (temp > 255) carry = 1; 1045 | mem39215 = A; 1046 | } 1047 | temp = mem39215 & 1; 1048 | mem39215 = (mem39215 >> 1) | (carry?128:0); 1049 | carry = temp; 1050 | //39033: ROR 39215 1051 | X--; 1052 | } while (X != 0); 1053 | temp = mem39214 & 128; 1054 | mem39214 = (mem39214 << 1) | (carry?1:0); 1055 | carry = temp; 1056 | temp = mem39215 & 128; 1057 | mem39215 = (mem39215 << 1) | (carry?1:0); 1058 | carry = temp; 1059 | 1060 | return mem39215; 1061 | } 1062 | -------------------------------------------------------------------------------- /src/sam.c: -------------------------------------------------------------------------------- 1 | #include // define printf() 2 | #include // strlen() 3 | //#include 4 | #include // define NULL 5 | #include "esp8266sam_debug.h" 6 | #include "sam.h" 7 | #include "render.h" 8 | #include "SamTabs.h" 9 | #include "SamData.h" 10 | 11 | //standard sam sound 12 | unsigned char speed = 72; 13 | unsigned char pitch = 64; 14 | static unsigned char mouth = 128; 15 | static unsigned char throat = 128; 16 | int singmode = 0; 17 | 18 | unsigned char mem39; 19 | unsigned char mem44; 20 | unsigned char mem47; 21 | unsigned char mem49; 22 | unsigned char mem50; 23 | unsigned char mem51; 24 | unsigned char mem53; 25 | unsigned char mem56; 26 | 27 | unsigned char mem59=0; 28 | 29 | static unsigned char A, X, Y; 30 | 31 | #define input (samdata->sam.input) 32 | #define stress (samdata->sam.stress) 33 | #define phonemeLength (samdata->sam.phonemeLength) 34 | #define phonemeindex (samdata->sam.phonemeindex) 35 | #define phonemeIndexOutput (samdata->sam.phonemeIndexOutput) 36 | #define stressOutput (samdata->sam.stressOutput) 37 | #define phonemeLengthOutput (samdata->sam.phonemeLengthOutput) 38 | 39 | 40 | 41 | // contains the final soundbuffer 42 | int bufferpos=0; 43 | //char *buffer = NULL; 44 | 45 | 46 | void SetInput(char *_input) 47 | { 48 | int i, l; 49 | l = strlen(_input); 50 | if (l > 254) l = 254; 51 | for(i=0; i 80) 158 | { 159 | phonemeindex[X] = 255; 160 | break; // error: delete all behind it 161 | } 162 | X++; 163 | } while (X != 0); 164 | 165 | //pos39848: 166 | InsertBreath(); 167 | 168 | //mem[40158] = 255; 169 | if (DEBUG_ESP8266SAM_LIB) 170 | { 171 | PrintPhonemes(phonemeindex, phonemeLength, stress); 172 | } 173 | 174 | PrepareOutput(); 175 | 176 | return 1; 177 | } 178 | 179 | int SAMPrepare() 180 | { 181 | Init(); 182 | phonemeindex[255] = 32; //to prevent buffer overflow 183 | 184 | if (!Parser1()) return 0; 185 | Parser2(); 186 | CopyStress(); 187 | SetPhonemeLength(); 188 | AdjustLengths(); 189 | Code41240(); 190 | do 191 | { 192 | A = phonemeindex[X]; 193 | if (A > 80) 194 | { 195 | phonemeindex[X] = 255; 196 | break; // error: delete all behind it 197 | } 198 | X++; 199 | } while (X != 0); 200 | 201 | InsertBreath(); 202 | return 1; 203 | } 204 | 205 | 206 | 207 | //void Code48547() 208 | void PrepareOutput() 209 | { 210 | A = 0; 211 | X = 0; 212 | Y = 0; 213 | 214 | //pos48551: 215 | while(1) 216 | { 217 | A = phonemeindex[X]; 218 | if (A == 255) 219 | { 220 | A = 255; 221 | phonemeIndexOutput[Y] = 255; 222 | Render(); 223 | return; 224 | } 225 | if (A == 254) 226 | { 227 | X++; 228 | int temp = X; 229 | //mem[48546] = X; 230 | phonemeIndexOutput[Y] = 255; 231 | Render(); 232 | //X = mem[48546]; 233 | X=temp; 234 | Y = 0; 235 | continue; 236 | } 237 | 238 | if (A == 0) 239 | { 240 | X++; 241 | continue; 242 | } 243 | 244 | phonemeIndexOutput[Y] = A; 245 | phonemeLengthOutput[Y] = phonemeLength[X]; 246 | stressOutput[Y] = stress[X]; 247 | X++; 248 | Y++; 249 | } 250 | } 251 | 252 | //void Code48431() 253 | void InsertBreath() 254 | { 255 | unsigned char mem54; 256 | unsigned char mem55; 257 | unsigned char index; //variable Y 258 | mem54 = 255; 259 | X++; 260 | mem55 = 0; 261 | unsigned char mem66 = 0; 262 | while(1) 263 | { 264 | //pos48440: 265 | X = mem66; 266 | index = phonemeindex[X]; 267 | if (index == 255) return; 268 | mem55 += phonemeLength[X]; 269 | 270 | if (mem55 < 232) 271 | { 272 | if (index != 254) // ML : Prevents an index out of bounds problem 273 | { 274 | A = flags2[index]&1; 275 | if(A != 0) 276 | { 277 | X++; 278 | mem55 = 0; 279 | Insert(X, 254, mem59, 0); 280 | mem66++; 281 | mem66++; 282 | continue; 283 | } 284 | } 285 | if (index == 0) mem54 = X; 286 | mem66++; 287 | continue; 288 | } 289 | X = mem54; 290 | phonemeindex[X] = 31; // 'Q*' glottal stop 291 | phonemeLength[X] = 4; 292 | stress[X] = 0; 293 | X++; 294 | mem55 = 0; 295 | Insert(X, 254, mem59, 0); 296 | X++; 297 | mem66 = X; 298 | } 299 | 300 | } 301 | 302 | // Iterates through the phoneme buffer, copying the stress value from 303 | // the following phoneme under the following circumstance: 304 | 305 | // 1. The current phoneme is voiced, excluding plosives and fricatives 306 | // 2. The following phoneme is voiced, excluding plosives and fricatives, and 307 | // 3. The following phoneme is stressed 308 | // 309 | // In those cases, the stress value+1 from the following phoneme is copied. 310 | // 311 | // For example, the word LOITER is represented as LOY5TER, with as stress 312 | // of 5 on the diphtong OY. This routine will copy the stress value of 6 (5+1) 313 | // to the L that precedes it. 314 | 315 | 316 | //void Code41883() 317 | void CopyStress() 318 | { 319 | // loop thought all the phonemes to be output 320 | unsigned char pos=0; //mem66 321 | while(1) 322 | { 323 | // get the phomene 324 | Y = phonemeindex[pos]; 325 | 326 | // exit at end of buffer 327 | if (Y == 255) return; 328 | 329 | // if CONSONANT_FLAG set, skip - only vowels get stress 330 | if ((flags[Y] & 64) == 0) {pos++; continue;} 331 | // get the next phoneme 332 | Y = phonemeindex[pos+1]; 333 | if (Y == 255) //prevent buffer overflow 334 | { 335 | pos++; continue; 336 | } else 337 | // if the following phoneme is a vowel, skip 338 | if ((flags[Y] & 128) == 0) {pos++; continue;} 339 | 340 | // get the stress value at the next position 341 | Y = stress[pos+1]; 342 | 343 | // if next phoneme is not stressed, skip 344 | if (Y == 0) {pos++; continue;} 345 | 346 | // if next phoneme is not a VOWEL OR ER, skip 347 | if ((Y & 128) != 0) {pos++; continue;} 348 | 349 | // copy stress from prior phoneme to this one 350 | stress[pos] = Y+1; 351 | 352 | // advance pointer 353 | pos++; 354 | } 355 | 356 | } 357 | 358 | 359 | //void Code41014() 360 | void Insert(unsigned char position/*var57*/, unsigned char mem60, unsigned char mem59, unsigned char mem58) 361 | { 362 | int i; 363 | for(i=253; i >= position; i--) // ML : always keep last safe-guarding 255 364 | { 365 | phonemeindex[i+1] = phonemeindex[i]; 366 | phonemeLength[i+1] = phonemeLength[i]; 367 | stress[i+1] = stress[i]; 368 | } 369 | 370 | phonemeindex[position] = mem60; 371 | phonemeLength[position] = mem59; 372 | stress[position] = mem58; 373 | return; 374 | } 375 | 376 | // The input[] buffer contains a string of phonemes and stress markers along 377 | // the lines of: 378 | // 379 | // DHAX KAET IHZ AH5GLIY. <0x9B> 380 | // 381 | // The byte 0x9B marks the end of the buffer. Some phonemes are 2 bytes 382 | // long, such as "DH" and "AX". Others are 1 byte long, such as "T" and "Z". 383 | // There are also stress markers, such as "5" and ".". 384 | // 385 | // The first character of the phonemes are stored in the table signInputTable1[]. 386 | // The second character of the phonemes are stored in the table signInputTable2[]. 387 | // The stress characters are arranged in low to high stress order in stressInputTable[]. 388 | // 389 | // The following process is used to parse the input[] buffer: 390 | // 391 | // Repeat until the <0x9B> character is reached: 392 | // 393 | // First, a search is made for a 2 character match for phonemes that do not 394 | // end with the '*' (wildcard) character. On a match, the index of the phoneme 395 | // is added to phonemeIndex[] and the buffer position is advanced 2 bytes. 396 | // 397 | // If this fails, a search is made for a 1 character match against all 398 | // phoneme names ending with a '*' (wildcard). If this succeeds, the 399 | // phoneme is added to phonemeIndex[] and the buffer position is advanced 400 | // 1 byte. 401 | // 402 | // If this fails, search for a 1 character match in the stressInputTable[]. 403 | // If this succeeds, the stress value is placed in the last stress[] table 404 | // at the same index of the last added phoneme, and the buffer position is 405 | // advanced by 1 byte. 406 | // 407 | // If this fails, return a 0. 408 | // 409 | // On success: 410 | // 411 | // 1. phonemeIndex[] will contain the index of all the phonemes. 412 | // 2. The last index in phonemeIndex[] will be 255. 413 | // 3. stress[] will contain the stress value for each phoneme 414 | 415 | // input[] holds the string of phonemes, each two bytes wide 416 | // signInputTable1[] holds the first character of each phoneme 417 | // signInputTable2[] holds te second character of each phoneme 418 | // phonemeIndex[] holds the indexes of the phonemes after parsing input[] 419 | // 420 | // The parser scans through the input[], finding the names of the phonemes 421 | // by searching signInputTable1[] and signInputTable2[]. On a match, it 422 | // copies the index of the phoneme into the phonemeIndexTable[]. 423 | // 424 | // The character <0x9B> marks the end of text in input[]. When it is reached, 425 | // the index 255 is placed at the end of the phonemeIndexTable[], and the 426 | // function returns with a 1 indicating success. 427 | int Parser1() 428 | { 429 | int i; 430 | unsigned char sign1; 431 | unsigned char sign2; 432 | unsigned char position = 0; 433 | X = 0; 434 | A = 0; 435 | Y = 0; 436 | 437 | // CLEAR THE STRESS TABLE 438 | for(i=0; i<256; i++) 439 | stress[i] = 0; 440 | 441 | // THIS CODE MATCHES THE PHONEME LETTERS TO THE TABLE 442 | // pos41078: 443 | while(1) 444 | { 445 | // GET THE FIRST CHARACTER FROM THE PHONEME BUFFER 446 | sign1 = input[X]; 447 | // TEST FOR 155 (�) END OF LINE MARKER 448 | if (sign1 == 155) 449 | { 450 | // MARK ENDPOINT AND RETURN 451 | phonemeindex[position] = 255; //mark endpoint 452 | // REACHED END OF PHONEMES, SO EXIT 453 | return 1; //all ok 454 | } 455 | 456 | // GET THE NEXT CHARACTER FROM THE BUFFER 457 | X++; 458 | sign2 = input[X]; 459 | 460 | // NOW sign1 = FIRST CHARACTER OF PHONEME, AND sign2 = SECOND CHARACTER OF PHONEME 461 | 462 | // TRY TO MATCH PHONEMES ON TWO TWO-CHARACTER NAME 463 | // IGNORE PHONEMES IN TABLE ENDING WITH WILDCARDS 464 | 465 | // SET INDEX TO 0 466 | Y = 0; 467 | pos41095: 468 | 469 | // GET FIRST CHARACTER AT POSITION Y IN signInputTable 470 | // --> should change name to PhonemeNameTable1 471 | A = pgm_read_byte(signInputTable1+Y);//signInputTable1[Y]; 472 | 473 | // FIRST CHARACTER MATCHES? 474 | if (A == sign1) 475 | { 476 | // GET THE CHARACTER FROM THE PhonemeSecondLetterTable 477 | A = pgm_read_byte(signInputTable2+Y);//signInputTable2[Y]; 478 | // NOT A SPECIAL AND MATCHES SECOND CHARACTER? 479 | if ((A != '*') && (A == sign2)) 480 | { 481 | // STORE THE INDEX OF THE PHONEME INTO THE phomeneIndexTable 482 | phonemeindex[position] = Y; 483 | 484 | // ADVANCE THE POINTER TO THE phonemeIndexTable 485 | position++; 486 | // ADVANCE THE POINTER TO THE phonemeInputBuffer 487 | X++; 488 | 489 | // CONTINUE PARSING 490 | continue; 491 | } 492 | } 493 | 494 | // NO MATCH, TRY TO MATCH ON FIRST CHARACTER TO WILDCARD NAMES (ENDING WITH '*') 495 | 496 | // ADVANCE TO THE NEXT POSITION 497 | Y++; 498 | // IF NOT END OF TABLE, CONTINUE 499 | if (Y != 81) goto pos41095; 500 | 501 | // REACHED END OF TABLE WITHOUT AN EXACT (2 CHARACTER) MATCH. 502 | // THIS TIME, SEARCH FOR A 1 CHARACTER MATCH AGAINST THE WILDCARDS 503 | 504 | // RESET THE INDEX TO POINT TO THE START OF THE PHONEME NAME TABLE 505 | Y = 0; 506 | pos41134: 507 | // DOES THE PHONEME IN THE TABLE END WITH '*'? 508 | if (pgm_read_byte(signInputTable2+Y)/*signInputTable2[Y]*/ == '*') 509 | { 510 | // DOES THE FIRST CHARACTER MATCH THE FIRST LETTER OF THE PHONEME 511 | if (pgm_read_byte(signInputTable1+Y)/*]signInputTable1[Y]*/ == sign1) 512 | { 513 | // SAVE THE POSITION AND MOVE AHEAD 514 | phonemeindex[position] = Y; 515 | 516 | // ADVANCE THE POINTER 517 | position++; 518 | 519 | // CONTINUE THROUGH THE LOOP 520 | continue; 521 | } 522 | } 523 | Y++; 524 | if (Y != 81) goto pos41134; //81 is size of PHONEME NAME table 525 | 526 | // FAILED TO MATCH WITH A WILDCARD. ASSUME THIS IS A STRESS 527 | // CHARACTER. SEARCH THROUGH THE STRESS TABLE 528 | 529 | // SET INDEX TO POSITION 8 (END OF STRESS TABLE) 530 | Y = 8; 531 | 532 | // WALK BACK THROUGH TABLE LOOKING FOR A MATCH 533 | while( (sign1 != pgm_read_byte(stressInputTable+Y)/*stressInputTable[Y]*/) && (Y>0)) 534 | { 535 | // DECREMENT INDEX 536 | Y--; 537 | } 538 | 539 | // REACHED THE END OF THE SEARCH WITHOUT BREAKING OUT OF LOOP? 540 | if (Y == 0) 541 | { 542 | //mem[39444] = X; 543 | //41181: JSR 42043 //Error 544 | // FAILED TO MATCH ANYTHING, RETURN 0 ON FAILURE 545 | return 0; 546 | } 547 | // SET THE STRESS FOR THE PRIOR PHONEME 548 | stress[position-1] = Y; 549 | } //while 550 | } 551 | 552 | 553 | 554 | 555 | //change phonemelength depedendent on stress 556 | //void Code41203() 557 | void SetPhonemeLength() 558 | { 559 | unsigned char A; 560 | int position = 0; 561 | while(phonemeindex[position] != 255 ) 562 | { 563 | A = stress[position]; 564 | //41218: BMI 41229 565 | if ((A == 0) || ((A&128) != 0)) 566 | { 567 | phonemeLength[position] = pgm_read_byte(&phonemeLengthTable[phonemeindex[position]]); 568 | } else 569 | { 570 | phonemeLength[position] = pgm_read_byte(&phonemeStressedLengthTable[phonemeindex[position]]); 571 | } 572 | position++; 573 | } 574 | } 575 | 576 | 577 | void Code41240() 578 | { 579 | unsigned char pos=0; 580 | 581 | while(phonemeindex[pos] != 255) 582 | { 583 | unsigned char index; //register AC 584 | X = pos; 585 | index = phonemeindex[pos]; 586 | if ((flags[index]&2) == 0) 587 | { 588 | pos++; 589 | continue; 590 | } else 591 | if ((flags[index]&1) == 0) 592 | { 593 | Insert(pos+1, index+1, pgm_read_byte(&phonemeLengthTable[index+1]), stress[pos]); 594 | Insert(pos+2, index+2, pgm_read_byte(&phonemeLengthTable[index+2]), stress[pos]); 595 | pos += 3; 596 | continue; 597 | } 598 | 599 | do 600 | { 601 | X++; 602 | A = phonemeindex[X]; 603 | } while(A==0); 604 | 605 | if (A != 255) 606 | { 607 | if ((flags[A] & 8) != 0) {pos++; continue;} 608 | if ((A == 36) || (A == 37)) {pos++; continue;} // '/H' '/X' 609 | } 610 | 611 | Insert(pos+1, index+1, pgm_read_byte(&phonemeLengthTable[index+1]), stress[pos]); 612 | Insert(pos+2, index+2, pgm_read_byte(&phonemeLengthTable[index+2]), stress[pos]); 613 | pos += 3; 614 | }; 615 | 616 | } 617 | 618 | // Rewrites the phonemes using the following rules: 619 | // 620 | // -> WX 621 | // -> YX 622 | // UL -> AX L 623 | // UM -> AX M 624 | // -> Q 625 | // T R -> CH R 626 | // D R -> J R 627 | // R -> RX 628 | // L -> LX 629 | // G S -> G Z 630 | // K -> KX 631 | // G -> GX 632 | // S P -> S B 633 | // S T -> S D 634 | // S K -> S G 635 | // S KX -> S GX 636 | // UW -> UX 637 | // CH -> CH CH' (CH requires two phonemes to represent it) 638 | // J -> J J' (J requires two phonemes to represent it) 639 | // T -> DX 640 | // D -> DX 641 | 642 | 643 | //void Code41397() 644 | void Parser2() 645 | { 646 | if (DEBUG_ESP8266SAM_LIB) printf("Parser2\n"); 647 | unsigned char pos = 0; //mem66; 648 | unsigned char mem58 = 0; 649 | 650 | 651 | // Loop through phonemes 652 | while(1) 653 | { 654 | // SET X TO THE CURRENT POSITION 655 | X = pos; 656 | // GET THE PHONEME AT THE CURRENT POSITION 657 | A = phonemeindex[pos]; 658 | 659 | // DEBUG: Print phoneme and index 660 | if (DEBUG_ESP8266SAM_LIB && A != 255) printf("%d: %c%c\n", X, signInputTable1[A], signInputTable2[A]); 661 | 662 | // Is phoneme pause? 663 | if (A == 0) 664 | { 665 | // Move ahead to the 666 | pos++; 667 | continue; 668 | } 669 | 670 | // If end of phonemes flag reached, exit routine 671 | if (A == 255) return; 672 | 673 | // Copy the current phoneme index to Y 674 | Y = A; 675 | 676 | // RULE: 677 | // -> WX 678 | // -> YX 679 | // Example: OIL, COW 680 | 681 | 682 | // Check for DIPHTONG 683 | if ((flags[A] & 16) == 0) goto pos41457; 684 | 685 | // Not a diphthong. Get the stress 686 | mem58 = stress[pos]; 687 | 688 | // End in IY sound? 689 | A = flags[Y] & 32; 690 | 691 | // If ends with IY, use YX, else use WX 692 | if (A == 0) A = 20; else A = 21; // 'WX' = 20 'YX' = 21 693 | //pos41443: 694 | // Insert at WX or YX following, copying the stress 695 | 696 | if (DEBUG_ESP8266SAM_LIB) if (A==20) printf("RULE: insert WX following diphtong NOT ending in IY sound\n"); 697 | if (DEBUG_ESP8266SAM_LIB) if (A==21) printf("RULE: insert YX following diphtong ending in IY sound\n"); 698 | Insert(pos+1, A, mem59, mem58); 699 | X = pos; 700 | // Jump to ??? 701 | goto pos41749; 702 | 703 | 704 | 705 | pos41457: 706 | 707 | // RULE: 708 | // UL -> AX L 709 | // Example: MEDDLE 710 | 711 | // Get phoneme 712 | A = phonemeindex[X]; 713 | // Skip this rule if phoneme is not UL 714 | if (A != 78) goto pos41487; // 'UL' 715 | A = 24; // 'L' //change 'UL' to 'AX L' 716 | 717 | if (DEBUG_ESP8266SAM_LIB) printf("RULE: UL -> AX L\n"); 718 | 719 | pos41466: 720 | // Get current phoneme stress 721 | mem58 = stress[X]; 722 | 723 | // Change UL to AX 724 | phonemeindex[X] = 13; // 'AX' 725 | // Perform insert. Note code below may jump up here with different values 726 | Insert(X+1, A, mem59, mem58); 727 | pos++; 728 | // Move to next phoneme 729 | continue; 730 | 731 | pos41487: 732 | 733 | // RULE: 734 | // UM -> AX M 735 | // Example: ASTRONOMY 736 | 737 | // Skip rule if phoneme != UM 738 | if (A != 79) goto pos41495; // 'UM' 739 | // Jump up to branch - replaces current phoneme with AX and continues 740 | A = 27; // 'M' //change 'UM' to 'AX M' 741 | if (DEBUG_ESP8266SAM_LIB) printf("RULE: UM -> AX M\n"); 742 | goto pos41466; 743 | pos41495: 744 | 745 | // RULE: 746 | // UN -> AX N 747 | // Example: FUNCTION 748 | 749 | 750 | // Skip rule if phoneme != UN 751 | if (A != 80) goto pos41503; // 'UN' 752 | 753 | // Jump up to branch - replaces current phoneme with AX and continues 754 | A = 28; // 'N' //change UN to 'AX N' 755 | if (DEBUG_ESP8266SAM_LIB) printf("RULE: UN -> AX N\n"); 756 | goto pos41466; 757 | pos41503: 758 | 759 | // RULE: 760 | // -> Q 761 | // EXAMPLE: AWAY EIGHT 762 | 763 | Y = A; 764 | // VOWEL set? 765 | A = flags[A] & 128; 766 | 767 | // Skip if not a vowel 768 | if (A != 0) 769 | { 770 | // Get the stress 771 | A = stress[X]; 772 | 773 | // If stressed... 774 | if (A != 0) 775 | { 776 | // Get the following phoneme 777 | X++; 778 | A = phonemeindex[X]; 779 | // If following phoneme is a pause 780 | 781 | if (A == 0) 782 | { 783 | // Get the phoneme following pause 784 | X++; 785 | Y = phonemeindex[X]; 786 | 787 | // Check for end of buffer flag 788 | if (Y == 255) //buffer overflow 789 | // ??? Not sure about these flags 790 | A = 65&128; 791 | else 792 | // And VOWEL flag to current phoneme's flags 793 | A = flags[Y] & 128; 794 | 795 | // If following phonemes is not a pause 796 | if (A != 0) 797 | { 798 | // If the following phoneme is not stressed 799 | A = stress[X]; 800 | if (A != 0) 801 | { 802 | // Insert a glottal stop and move forward 803 | if (DEBUG_ESP8266SAM_LIB) printf("RULE: Insert glottal stop between two stressed vowels with space between them\n"); 804 | // 31 = 'Q' 805 | Insert(X, 31, mem59, 0); 806 | pos++; 807 | continue; 808 | } 809 | } 810 | } 811 | } 812 | } 813 | 814 | 815 | // RULES FOR PHONEMES BEFORE R 816 | // T R -> CH R 817 | // Example: TRACK 818 | 819 | 820 | // Get current position and phoneme 821 | X = pos; 822 | A = phonemeindex[pos]; 823 | if (A != 23) goto pos41611; // 'R' 824 | 825 | // Look at prior phoneme 826 | X--; 827 | A = phonemeindex[pos-1]; 828 | //pos41567: 829 | if (A == 69) // 'T' 830 | { 831 | // Change T to CH 832 | if (DEBUG_ESP8266SAM_LIB) printf("RULE: T R -> CH R\n"); 833 | phonemeindex[pos-1] = 42; 834 | goto pos41779; 835 | } 836 | 837 | 838 | // RULES FOR PHONEMES BEFORE R 839 | // D R -> J R 840 | // Example: DRY 841 | 842 | // Prior phonemes D? 843 | if (A == 57) // 'D' 844 | { 845 | // Change D to J 846 | phonemeindex[pos-1] = 44; 847 | if (DEBUG_ESP8266SAM_LIB) printf("RULE: D R -> J R\n"); 848 | goto pos41788; 849 | } 850 | 851 | // RULES FOR PHONEMES BEFORE R 852 | // R -> RX 853 | // Example: ART 854 | 855 | 856 | // If vowel flag is set change R to RX 857 | A = flags[A] & 128; 858 | if (DEBUG_ESP8266SAM_LIB) printf("RULE: R -> RX\n"); 859 | if (A != 0) phonemeindex[pos] = 18; // 'RX' 860 | 861 | // continue to next phoneme 862 | pos++; 863 | continue; 864 | 865 | pos41611: 866 | 867 | // RULE: 868 | // L -> LX 869 | // Example: ALL 870 | 871 | // Is phoneme L? 872 | if (A == 24) // 'L' 873 | { 874 | // If prior phoneme does not have VOWEL flag set, move to next phoneme 875 | if ((flags[phonemeindex[pos-1]] & 128) == 0) {pos++; continue;} 876 | // Prior phoneme has VOWEL flag set, so change L to LX and move to next phoneme 877 | if (DEBUG_ESP8266SAM_LIB) printf("RULE: L -> LX\n"); 878 | phonemeindex[X] = 19; // 'LX' 879 | pos++; 880 | continue; 881 | } 882 | 883 | // RULE: 884 | // G S -> G Z 885 | // 886 | // Can't get to fire - 887 | // 1. The G -> GX rule intervenes 888 | // 2. Reciter already replaces GS -> GZ 889 | 890 | // Is current phoneme S? 891 | if (A == 32) // 'S' 892 | { 893 | // If prior phoneme is not G, move to next phoneme 894 | if (phonemeindex[pos-1] != 60) {pos++; continue;} 895 | // Replace S with Z and move on 896 | if (DEBUG_ESP8266SAM_LIB) printf("RULE: G S -> G Z\n"); 897 | phonemeindex[pos] = 38; // 'Z' 898 | pos++; 899 | continue; 900 | } 901 | 902 | // RULE: 903 | // K -> KX 904 | // Example: COW 905 | 906 | // Is current phoneme K? 907 | if (A == 72) // 'K' 908 | { 909 | // Get next phoneme 910 | Y = phonemeindex[pos+1]; 911 | // If at end, replace current phoneme with KX 912 | if (Y == 255) phonemeindex[pos] = 75; // ML : prevents an index out of bounds problem 913 | else 914 | { 915 | // VOWELS AND DIPHTONGS ENDING WITH IY SOUND flag set? 916 | A = flags[Y] & 32; 917 | if (DEBUG_ESP8266SAM_LIB) if (A==0) printf("RULE: K -> KX \n"); 918 | // Replace with KX 919 | if (A == 0) phonemeindex[pos] = 75; // 'KX' 920 | } 921 | } 922 | else 923 | 924 | // RULE: 925 | // G -> GX 926 | // Example: GO 927 | 928 | 929 | // Is character a G? 930 | if (A == 60) // 'G' 931 | { 932 | // Get the following character 933 | unsigned char index = phonemeindex[pos+1]; 934 | 935 | // At end of buffer? 936 | if (index == 255) //prevent buffer overflow 937 | { 938 | pos++; continue; 939 | } 940 | else 941 | // If diphtong ending with YX, move continue processing next phoneme 942 | if ((flags[index] & 32) != 0) {pos++; continue;} 943 | // replace G with GX and continue processing next phoneme 944 | if (DEBUG_ESP8266SAM_LIB) printf("RULE: G -> GX \n"); 945 | phonemeindex[pos] = 63; // 'GX' 946 | pos++; 947 | continue; 948 | } 949 | 950 | // RULE: 951 | // S P -> S B 952 | // S T -> S D 953 | // S K -> S G 954 | // S KX -> S GX 955 | // Examples: SPY, STY, SKY, SCOWL 956 | 957 | Y = phonemeindex[pos]; 958 | //pos41719: 959 | // Replace with softer version? 960 | A = flags[Y] & 1; 961 | if (A == 0) goto pos41749; 962 | A = phonemeindex[pos-1]; 963 | if (A != 32) // 'S' 964 | { 965 | A = Y; 966 | goto pos41812; 967 | } 968 | // Replace with softer version 969 | if (DEBUG_ESP8266SAM_LIB) printf("RULE: S* %c%c -> S* %c%c\n", signInputTable1[Y], signInputTable2[Y],signInputTable1[Y-12], signInputTable2[Y-12]); 970 | phonemeindex[pos] = Y-12; 971 | pos++; 972 | continue; 973 | 974 | 975 | pos41749: 976 | 977 | // RULE: 978 | // UW -> UX 979 | // 980 | // Example: NEW, DEW, SUE, ZOO, THOO, TOO 981 | 982 | // UW -> UX 983 | 984 | A = phonemeindex[X]; 985 | if (A == 53) // 'UW' 986 | { 987 | // ALVEOLAR flag set? 988 | Y = phonemeindex[X-1]; 989 | A = flags2[Y] & 4; 990 | // If not set, continue processing next phoneme 991 | if (A == 0) {pos++; continue;} 992 | if (DEBUG_ESP8266SAM_LIB) printf("RULE: UW -> UX\n"); 993 | phonemeindex[X] = 16; 994 | pos++; 995 | continue; 996 | } 997 | pos41779: 998 | 999 | // RULE: 1000 | // CH -> CH CH' (CH requires two phonemes to represent it) 1001 | // Example: CHEW 1002 | 1003 | if (A == 42) // 'CH' 1004 | { 1005 | // pos41783: 1006 | if (DEBUG_ESP8266SAM_LIB) printf("CH -> CH CH+1\n"); 1007 | Insert(X+1, A+1, mem59, stress[X]); 1008 | pos++; 1009 | continue; 1010 | } 1011 | 1012 | pos41788: 1013 | 1014 | // RULE: 1015 | // J -> J J' (J requires two phonemes to represent it) 1016 | // Example: JAY 1017 | 1018 | 1019 | if (A == 44) // 'J' 1020 | { 1021 | if (DEBUG_ESP8266SAM_LIB) printf("J -> J J+1\n"); 1022 | Insert(X+1, A+1, mem59, stress[X]); 1023 | pos++; 1024 | continue; 1025 | } 1026 | 1027 | // Jump here to continue 1028 | pos41812: 1029 | 1030 | // RULE: Soften T following vowel 1031 | // NOTE: This rule fails for cases such as "ODD" 1032 | // T -> DX 1033 | // D -> DX 1034 | // Example: PARTY, TARDY 1035 | 1036 | 1037 | // Past this point, only process if phoneme is T or D 1038 | 1039 | if (A != 69) // 'T' 1040 | if (A != 57) {pos++; continue;} // 'D' 1041 | //pos41825: 1042 | 1043 | 1044 | // If prior phoneme is not a vowel, continue processing phonemes 1045 | if ((flags[phonemeindex[X-1]] & 128) == 0) {pos++; continue;} 1046 | 1047 | // Get next phoneme 1048 | X++; 1049 | A = phonemeindex[X]; 1050 | //pos41841 1051 | // Is the next phoneme a pause? 1052 | if (A != 0) 1053 | { 1054 | // If next phoneme is not a pause, continue processing phonemes 1055 | if ((flags[A] & 128) == 0) {pos++; continue;} 1056 | // If next phoneme is stressed, continue processing phonemes 1057 | // FIXME: How does a pause get stressed? 1058 | if (stress[X] != 0) {pos++; continue;} 1059 | //pos41856: 1060 | // Set phonemes to DX 1061 | if (DEBUG_ESP8266SAM_LIB) printf("RULE: Soften T or D following vowel or ER and preceding a pause -> DX\n"); 1062 | phonemeindex[pos] = 30; // 'DX' 1063 | } else 1064 | { 1065 | A = phonemeindex[X+1]; 1066 | if (A == 255) //prevent buffer overflow 1067 | A = 65 & 128; 1068 | else 1069 | // Is next phoneme a vowel or ER? 1070 | A = flags[A] & 128; 1071 | if (DEBUG_ESP8266SAM_LIB) if (A != 0) printf("RULE: Soften T or D following vowel or ER and preceding a pause -> DX\n"); 1072 | if (A != 0) phonemeindex[pos] = 30; // 'DX' 1073 | } 1074 | 1075 | pos++; 1076 | 1077 | } // while 1078 | } 1079 | 1080 | 1081 | // Applies various rules that adjust the lengths of phonemes 1082 | // 1083 | // Lengthen or between and by 1.5 1084 | // - decrease length by 1 1085 | // - decrease vowel by 1/8th 1086 | // - increase vowel by 1/2 + 1 1087 | // - set nasal = 5, consonant = 6 1088 | // {optional silence} - shorten both to 1/2 + 1 1089 | // - decrease by 2 1090 | 1091 | 1092 | //void Code48619() 1093 | void AdjustLengths() 1094 | { 1095 | 1096 | // LENGTHEN VOWELS PRECEDING PUNCTUATION 1097 | // 1098 | // Search for punctuation. If found, back up to the first vowel, then 1099 | // process all phonemes between there and up to (but not including) the punctuation. 1100 | // If any phoneme is found that is a either a fricative or voiced, the duration is 1101 | // increased by (length * 1.5) + 1 1102 | 1103 | // loop index 1104 | X = 0; 1105 | unsigned char index; 1106 | 1107 | // iterate through the phoneme list 1108 | unsigned char loopIndex=0; 1109 | while(1) 1110 | { 1111 | // get a phoneme 1112 | index = phonemeindex[X]; 1113 | 1114 | // exit loop if end on buffer token 1115 | if (index == 255) break; 1116 | 1117 | // not punctuation? 1118 | if((flags2[index] & 1) == 0) 1119 | { 1120 | // skip 1121 | X++; 1122 | continue; 1123 | } 1124 | 1125 | // hold index 1126 | loopIndex = X; 1127 | 1128 | // Loop backwards from this point 1129 | pos48644: 1130 | 1131 | // back up one phoneme 1132 | X--; 1133 | 1134 | // stop once the beginning is reached 1135 | if(X == 0) break; 1136 | 1137 | // get the preceding phoneme 1138 | index = phonemeindex[X]; 1139 | 1140 | if (index != 255) //inserted to prevent access overrun 1141 | if((flags[index] & 128) == 0) goto pos48644; // if not a vowel, continue looping 1142 | 1143 | //pos48657: 1144 | do 1145 | { 1146 | // test for vowel 1147 | index = phonemeindex[X]; 1148 | 1149 | if (index != 255)//inserted to prevent access overrun 1150 | // test for fricative/unvoiced or not voiced 1151 | if(((flags2[index] & 32) == 0) || ((flags[index] & 4) != 0)) //nochmal �berpr�fen 1152 | { 1153 | //A = flags[Y] & 4; 1154 | //if(A == 0) goto pos48688; 1155 | 1156 | // get the phoneme length 1157 | A = phonemeLength[X]; 1158 | 1159 | // change phoneme length to (length * 1.5) + 1 1160 | A = (A >> 1) + A + 1; 1161 | if (DEBUG_ESP8266SAM_LIB) printf("RULE: Lengthen or between and by 1.5\n"); 1162 | if (DEBUG_ESP8266SAM_LIB) printf("PRE\n"); 1163 | if (DEBUG_ESP8266SAM_LIB) printf("phoneme %d (%c%c) length %d\n", X, signInputTable1[phonemeindex[X]], signInputTable2[phonemeindex[X]], phonemeLength[X]); 1164 | 1165 | phonemeLength[X] = A; 1166 | 1167 | if (DEBUG_ESP8266SAM_LIB) printf("POST\n"); 1168 | if (DEBUG_ESP8266SAM_LIB) printf("phoneme %d (%c%c) length %d\n", X, signInputTable1[phonemeindex[X]], signInputTable2[phonemeindex[X]], phonemeLength[X]); 1169 | 1170 | } 1171 | // keep moving forward 1172 | X++; 1173 | } while (X != loopIndex); 1174 | // if (X != loopIndex) goto pos48657; 1175 | X++; 1176 | } // while 1177 | 1178 | // Similar to the above routine, but shorten vowels under some circumstances 1179 | 1180 | // Loop throught all phonemes 1181 | loopIndex = 0; 1182 | //pos48697 1183 | 1184 | while(1) 1185 | { 1186 | // get a phoneme 1187 | X = loopIndex; 1188 | index = phonemeindex[X]; 1189 | 1190 | // exit routine at end token 1191 | if (index == 255) return; 1192 | 1193 | // vowel? 1194 | A = flags[index] & 128; 1195 | if (A != 0) 1196 | { 1197 | // get next phoneme 1198 | X++; 1199 | index = phonemeindex[X]; 1200 | 1201 | // get flags 1202 | if (index == 255) 1203 | mem56 = 65; // use if end marker 1204 | else 1205 | mem56 = flags[index]; 1206 | 1207 | // not a consonant 1208 | if ((flags[index] & 64) == 0) 1209 | { 1210 | // RX or LX? 1211 | if ((index == 18) || (index == 19)) // 'RX' & 'LX' 1212 | { 1213 | // get the next phoneme 1214 | X++; 1215 | index = phonemeindex[X]; 1216 | 1217 | // next phoneme a consonant? 1218 | if ((flags[index] & 64) != 0) { 1219 | // RULE: RX | LX 1220 | 1221 | 1222 | if (DEBUG_ESP8266SAM_LIB) printf("RULE: - decrease length by 1\n"); 1223 | if (DEBUG_ESP8266SAM_LIB) printf("PRE\n"); 1224 | if (DEBUG_ESP8266SAM_LIB) printf("phoneme %d (%c%c) length %d\n", loopIndex, signInputTable1[phonemeindex[loopIndex]], signInputTable2[phonemeindex[loopIndex]], phonemeLength[loopIndex]); 1225 | 1226 | // decrease length of vowel by 1 frame 1227 | phonemeLength[loopIndex]--; 1228 | 1229 | if (DEBUG_ESP8266SAM_LIB) printf("POST\n"); 1230 | if (DEBUG_ESP8266SAM_LIB) printf("phoneme %d (%c%c) length %d\n", loopIndex, signInputTable1[phonemeindex[loopIndex]], signInputTable2[phonemeindex[loopIndex]], phonemeLength[loopIndex]); 1231 | 1232 | } 1233 | // move ahead 1234 | loopIndex++; 1235 | continue; 1236 | } 1237 | // move ahead 1238 | loopIndex++; 1239 | continue; 1240 | } 1241 | 1242 | 1243 | // Got here if not 1244 | 1245 | // not voiced 1246 | if ((mem56 & 4) == 0) 1247 | { 1248 | 1249 | // Unvoiced 1250 | // *, .*, ?*, ,*, -*, DX, S*, SH, F*, TH, /H, /X, CH, P*, T*, K*, KX 1251 | 1252 | // not an unvoiced plosive? 1253 | if((mem56 & 1) == 0) { 1254 | // move ahead 1255 | loopIndex++; 1256 | continue; 1257 | } 1258 | 1259 | // P*, T*, K*, KX 1260 | 1261 | 1262 | // RULE: 1263 | // 1264 | 1265 | // move back 1266 | X--; 1267 | 1268 | if (DEBUG_ESP8266SAM_LIB) printf("RULE: - decrease vowel by 1/8th\n"); 1269 | if (DEBUG_ESP8266SAM_LIB) printf("PRE\n"); 1270 | if (DEBUG_ESP8266SAM_LIB) printf("phoneme %d (%c%c) length %d\n", X, signInputTable1[phonemeindex[X]], signInputTable2[phonemeindex[X]], phonemeLength[X]); 1271 | 1272 | // decrease length by 1/8th 1273 | mem56 = phonemeLength[X] >> 3; 1274 | phonemeLength[X] -= mem56; 1275 | 1276 | if (DEBUG_ESP8266SAM_LIB) printf("POST\n"); 1277 | if (DEBUG_ESP8266SAM_LIB) printf("phoneme %d (%c%c) length %d\n", X, signInputTable1[phonemeindex[X]], signInputTable2[phonemeindex[X]], phonemeLength[X]); 1278 | 1279 | // move ahead 1280 | loopIndex++; 1281 | continue; 1282 | } 1283 | 1284 | // RULE: 1285 | // 1286 | 1287 | if (DEBUG_ESP8266SAM_LIB) printf("RULE: - increase vowel by 1/2 + 1\n"); 1288 | if (DEBUG_ESP8266SAM_LIB) printf("PRE\n"); 1289 | if (DEBUG_ESP8266SAM_LIB) printf("phoneme %d (%c%c) length %d\n", X-1, signInputTable1[phonemeindex[X-1]], signInputTable2[phonemeindex[X-1]], phonemeLength[X-1]); 1290 | 1291 | // decrease length 1292 | A = phonemeLength[X-1]; 1293 | phonemeLength[X-1] = (A >> 2) + A + 1; // 5/4*A + 1 1294 | 1295 | if (DEBUG_ESP8266SAM_LIB) printf("POST\n"); 1296 | if (DEBUG_ESP8266SAM_LIB) printf("phoneme %d (%c%c) length %d\n", X-1, signInputTable1[phonemeindex[X-1]], signInputTable2[phonemeindex[X-1]], phonemeLength[X-1]); 1297 | 1298 | // move ahead 1299 | loopIndex++; 1300 | continue; 1301 | 1302 | } 1303 | 1304 | 1305 | // WH, R*, L*, W*, Y*, M*, N*, NX, Q*, Z*, ZH, V*, DH, J*, B*, D*, G*, GX 1306 | 1307 | //pos48821: 1308 | 1309 | // RULE: 1310 | // Set punctuation length to 6 1311 | // Set stop consonant length to 5 1312 | 1313 | // nasal? 1314 | if((flags2[index] & 8) != 0) 1315 | { 1316 | 1317 | // M*, N*, NX, 1318 | 1319 | // get the next phoneme 1320 | X++; 1321 | index = phonemeindex[X]; 1322 | 1323 | // end of buffer? 1324 | if (index == 255) 1325 | A = 65&2; //prevent buffer overflow 1326 | else 1327 | A = flags[index] & 2; // check for stop consonant 1328 | 1329 | 1330 | // is next phoneme a stop consonant? 1331 | if (A != 0) 1332 | 1333 | // B*, D*, G*, GX, P*, T*, K*, KX 1334 | 1335 | { 1336 | if (DEBUG_ESP8266SAM_LIB) printf("RULE: - set nasal = 5, consonant = 6\n"); 1337 | if (DEBUG_ESP8266SAM_LIB) printf("POST\n"); 1338 | if (DEBUG_ESP8266SAM_LIB) printf("phoneme %d (%c%c) length %d\n", X, signInputTable1[phonemeindex[X]], signInputTable2[phonemeindex[X]], phonemeLength[X]); 1339 | if (DEBUG_ESP8266SAM_LIB) printf("phoneme %d (%c%c) length %d\n", X-1, signInputTable1[phonemeindex[X-1]], signInputTable2[phonemeindex[X-1]], phonemeLength[X-1]); 1340 | 1341 | // set stop consonant length to 6 1342 | phonemeLength[X] = 6; 1343 | 1344 | // set nasal length to 5 1345 | phonemeLength[X-1] = 5; 1346 | 1347 | if (DEBUG_ESP8266SAM_LIB) printf("POST\n"); 1348 | if (DEBUG_ESP8266SAM_LIB) printf("phoneme %d (%c%c) length %d\n", X, signInputTable1[phonemeindex[X]], signInputTable2[phonemeindex[X]], phonemeLength[X]); 1349 | if (DEBUG_ESP8266SAM_LIB) printf("phoneme %d (%c%c) length %d\n", X-1, signInputTable1[phonemeindex[X-1]], signInputTable2[phonemeindex[X-1]], phonemeLength[X-1]); 1350 | 1351 | } 1352 | // move to next phoneme 1353 | loopIndex++; 1354 | continue; 1355 | } 1356 | 1357 | 1358 | // WH, R*, L*, W*, Y*, Q*, Z*, ZH, V*, DH, J*, B*, D*, G*, GX 1359 | 1360 | // RULE: {optional silence} 1361 | // Shorten both to (length/2 + 1) 1362 | 1363 | // (voiced) stop consonant? 1364 | if((flags[index] & 2) != 0) 1365 | { 1366 | // B*, D*, G*, GX 1367 | 1368 | // move past silence 1369 | do 1370 | { 1371 | // move ahead 1372 | X++; 1373 | index = phonemeindex[X]; 1374 | } while(index == 0); 1375 | 1376 | 1377 | // check for end of buffer 1378 | if (index == 255) //buffer overflow 1379 | { 1380 | // ignore, overflow code 1381 | if ((65 & 2) == 0) {loopIndex++; continue;} 1382 | } else if ((flags[index] & 2) == 0) { 1383 | // if another stop consonant, move ahead 1384 | loopIndex++; 1385 | continue; 1386 | } 1387 | 1388 | // RULE: {optional silence} 1389 | if (DEBUG_ESP8266SAM_LIB) printf("RULE: {optional silence} - shorten both to 1/2 + 1\n"); 1390 | if (DEBUG_ESP8266SAM_LIB) printf("PRE\n"); 1391 | if (DEBUG_ESP8266SAM_LIB) printf("phoneme %d (%c%c) length %d\n", X, signInputTable1[phonemeindex[X]], signInputTable2[phonemeindex[X]], phonemeLength[X]); 1392 | if (DEBUG_ESP8266SAM_LIB) printf("phoneme %d (%c%c) length %d\n", X-1, signInputTable1[phonemeindex[X-1]], signInputTable2[phonemeindex[X-1]], phonemeLength[X-1]); 1393 | // X gets overwritten, so hold prior X value for debug statement 1394 | int debugX = X; 1395 | // shorten the prior phoneme length to (length/2 + 1) 1396 | phonemeLength[X] = (phonemeLength[X] >> 1) + 1; 1397 | X = loopIndex; 1398 | 1399 | // also shorten this phoneme length to (length/2 +1) 1400 | phonemeLength[loopIndex] = (phonemeLength[loopIndex] >> 1) + 1; 1401 | 1402 | if (DEBUG_ESP8266SAM_LIB) printf("POST\n"); 1403 | if (DEBUG_ESP8266SAM_LIB) printf("phoneme %d (%c%c) length %d\n", debugX, signInputTable1[phonemeindex[debugX]], signInputTable2[phonemeindex[debugX]], phonemeLength[debugX]); 1404 | if (DEBUG_ESP8266SAM_LIB) printf("phoneme %d (%c%c) length %d\n", debugX-1, signInputTable1[phonemeindex[debugX-1]], signInputTable2[phonemeindex[debugX-1]], phonemeLength[debugX-1]); 1405 | 1406 | 1407 | // move ahead 1408 | loopIndex++; 1409 | continue; 1410 | } 1411 | 1412 | 1413 | // WH, R*, L*, W*, Y*, Q*, Z*, ZH, V*, DH, J*, **, 1414 | 1415 | // RULE: 1416 | // Decrease by 2 1417 | 1418 | // liquic consonant? 1419 | if ((flags2[index] & 16) != 0) 1420 | { 1421 | // R*, L*, W*, Y* 1422 | 1423 | // get the prior phoneme 1424 | index = phonemeindex[X-1]; 1425 | 1426 | // prior phoneme a stop consonant> 1427 | if((flags[index] & 2) != 0) 1428 | // Rule: 1429 | 1430 | if (DEBUG_ESP8266SAM_LIB) printf("RULE: - decrease by 2\n"); 1431 | if (DEBUG_ESP8266SAM_LIB) printf("PRE\n"); 1432 | if (DEBUG_ESP8266SAM_LIB) printf("phoneme %d (%c%c) length %d\n", X, signInputTable1[phonemeindex[X]], signInputTable2[phonemeindex[X]], phonemeLength[X]); 1433 | 1434 | // decrease the phoneme length by 2 frames (20 ms) 1435 | phonemeLength[X] -= 2; 1436 | 1437 | if (DEBUG_ESP8266SAM_LIB) printf("POST\n"); 1438 | if (DEBUG_ESP8266SAM_LIB) printf("phoneme %d (%c%c) length %d\n", X, signInputTable1[phonemeindex[X]], signInputTable2[phonemeindex[X]], phonemeLength[X]); 1439 | } 1440 | 1441 | // move to next phoneme 1442 | loopIndex++; 1443 | continue; 1444 | } 1445 | // goto pos48701; 1446 | } 1447 | 1448 | // ------------------------------------------------------------------------- 1449 | // ML : Code47503 is division with remainder, and mem50 gets the sign 1450 | void Code47503(unsigned char mem52) 1451 | { 1452 | 1453 | Y = 0; 1454 | if ((mem53 & 128) != 0) 1455 | { 1456 | mem53 = -mem53; 1457 | Y = 128; 1458 | } 1459 | mem50 = Y; 1460 | A = 0; 1461 | for(X=8; X > 0; X--) 1462 | { 1463 | int temp = mem53; 1464 | mem53 = mem53 << 1; 1465 | A = A << 1; 1466 | if (temp >= 128) A++; 1467 | if (A >= mem52) 1468 | { 1469 | A = A - mem52; 1470 | mem53++; 1471 | } 1472 | } 1473 | 1474 | mem51 = A; 1475 | if ((mem50 & 128) != 0) mem53 = -mem53; 1476 | 1477 | } 1478 | --------------------------------------------------------------------------------