diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..648dfcc --- /dev/null +++ b/.gitignore @@ -0,0 +1,35 @@ +build/ +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app +sdkconfig +.vscode/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..b986ebb --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,6 @@ +# The following lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(hello_world) \ No newline at end of file diff --git a/components/ESPIDF-SAM/CMakeLists.txt b/components/ESPIDF-SAM/CMakeLists.txt new file mode 100644 index 0000000..3c55f79 --- /dev/null +++ b/components/ESPIDF-SAM/CMakeLists.txt @@ -0,0 +1,15 @@ +set(PROJECT_SOURCE_DIR src) + +set(include_dirs + ${PROJECT_SOURCE_DIR} +) + +file(GLOB SOURCE_FILES + "${PROJECT_SOURCE_DIR}/*.c" + "${PROJECT_SOURCE_DIR}/*.cpp" + ) + +idf_component_register(SRCS "${SOURCE_FILES}" + INCLUDE_DIRS "${include_dirs}") + +component_compile_options(-Wno-error=format= -Wno-format) \ No newline at end of file diff --git a/components/ESPIDF-SAM/component.mk b/components/ESPIDF-SAM/component.mk new file mode 100644 index 0000000..df64658 --- /dev/null +++ b/components/ESPIDF-SAM/component.mk @@ -0,0 +1,7 @@ +COMPONENT_SRCDIRS := . +COMPONENT_ADD_INCLUDEDIRS := . + +# If your component requires any specific libraries, you can add them here +# COMPONENT_REQUIRES := + +include $(IDF_PATH)/make/component.mk \ No newline at end of file diff --git a/src/ESP8266SAM.cpp b/components/ESPIDF-SAM/src/ESP8266SAM.cpp similarity index 88% rename from src/ESP8266SAM.cpp rename to components/ESPIDF-SAM/src/ESP8266SAM.cpp index d2b2b49..8fe77e4 100644 --- a/src/ESP8266SAM.cpp +++ b/components/ESPIDF-SAM/src/ESP8266SAM.cpp @@ -1,7 +1,7 @@ /* ESP8266SAM Port of SAM to the ESP8266 - + Copyright (C) 2017 Earle F. Philhower, III This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -15,14 +15,21 @@ along with this program. If not, see . */ +#include +#include +#include +#include -#include #include #include "reciter.h" #include "sam.h" #include "SamData.h" +#ifndef ESP8266 +static void yield() { /* NOOP */ } +#endif + SamData* samdata; // Thunk from C to C++ with a this-> pointer @@ -35,14 +42,16 @@ void ESP8266SAM::OutputByteCallback(void *cbdata, unsigned char b) void ESP8266SAM::OutputByte(unsigned char b) { // Xvert unsigned 8 to signed 16... - int16_t s16 = b;// s16 -= 128; //s16 *= 128; + int16_t s16 = b; + s16 -= 128; + s16 *= 128; int16_t sample[2]; sample[0] = s16; sample[1] = s16; - while (!output->ConsumeSample(sample)) yield(); + while (!output_cb((void*)(0), sample)) yield(); } - -bool ESP8266SAM::Say(AudioOutput *out, const char *str) + +bool ESP8266SAM::Say(const char *str) { if (!str || strlen(str)>254) return false; // Only can speak up to 1 page worth of data... samdata = new SamData; @@ -51,12 +60,6 @@ bool ESP8266SAM::Say(AudioOutput *out, const char *str) // allocation failed! return false; } - - // These are fixed by the synthesis routines - out->SetRate(22050); - out->SetBitsPerSample(8); - out->SetChannels(1); - out->begin(); // SAM settings EnableSingmode(singmode); @@ -80,7 +83,6 @@ bool ESP8266SAM::Say(AudioOutput *out, const char *str) } // Say it! - output = out; SetInput(input); SAMMain(OutputByteCallback, (void*)this); delete samdata; diff --git a/src/ESP8266SAM.h b/components/ESPIDF-SAM/src/ESP8266SAM.h similarity index 83% rename from src/ESP8266SAM.h rename to components/ESPIDF-SAM/src/ESP8266SAM.h index 526f41b..274a12f 100644 --- a/src/ESP8266SAM.h +++ b/components/ESPIDF-SAM/src/ESP8266SAM.h @@ -1,7 +1,7 @@ /* ESP8266SAM Port of SAM to the ESP8266 - + Copyright (C) 2017 Earle F. Philhower, III This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -18,13 +18,10 @@ #ifndef _ESP8266SAM_H #define _ESP8266SAM_H -#include -#include - class ESP8266SAM { public: - ESP8266SAM() + ESP8266SAM(bool output_cb(void *cbdata, int16_t* b)) : output_cb(output_cb) { singmode = false; phonetic = false; @@ -32,9 +29,8 @@ class ESP8266SAM { mouth = 0; throat = 0; speed = 0; - output = NULL; }; - + ~ESP8266SAM() { } @@ -49,13 +45,8 @@ class ESP8266SAM { void SetThroat(uint8_t val) { throat = val; } void SetSpeed(uint8_t val) { speed = val; } - bool Say(AudioOutput *out, const char *str); - bool Say_P(AudioOutput *out, const char *str) { - char ram[256]; - strncpy_P(ram, str, 256); - ram[255] = 0; - return Say(out, ram); - }; + bool Say(const char *str); + bool(*output_cb)(void *cbdata, int16_t* b); private: static void OutputByteCallback(void *cbdata, unsigned char b); @@ -66,7 +57,6 @@ class ESP8266SAM { int speed; int mouth; int throat; - AudioOutput *output; }; #endif diff --git a/src/ReciterTabs.h b/components/ESPIDF-SAM/src/ReciterTabs.h similarity index 99% rename from src/ReciterTabs.h rename to components/ESPIDF-SAM/src/ReciterTabs.h index 3f152cc..73dcd9e 100644 --- a/src/ReciterTabs.h +++ b/components/ESPIDF-SAM/src/ReciterTabs.h @@ -1,14 +1,14 @@ #ifndef RECITERTABS_H #define RECITERTABS_H -#include +// #include #include "esp8266sam_debug.h" #if DEBUG_ESP8266SAM_LIB #define PROGMEM #endif //some flags -const unsigned char tab36376[] PROGMEM = +const unsigned char tab36376[] = { 0, 0, 0, 0, 0, 0, 0, 0, // 0-7 0, 0, 0, 0, 0, 0, 0, 0, // 8-15 @@ -26,7 +26,7 @@ const unsigned char tab36376[] PROGMEM = 163, 76, 138, 142 }; -const char rules[] PROGMEM = +const char rules[] = { ']','A'|0x80, ' ','(','A','.',')', '=','E','H','4','Y','.',' '|0x80, @@ -484,7 +484,7 @@ const char rules[] PROGMEM = 'j'|0x80 }; -const char rules2[] PROGMEM = +const char rules2[] = { '(','A',')', '='|0x80, '(','!',')', '=','.'|0x80, @@ -533,7 +533,7 @@ const char rules2[] PROGMEM = //26 items. From 'A' to 'Z' // positions for mem62 and mem63 for each character -const unsigned char tab37489[] PROGMEM = +const unsigned char tab37489[] = { 0, 149, 247, 162, 57, 197, 6, 126, 199, 38, 55, 78, 145, 241, 85, 161, @@ -541,7 +541,7 @@ const unsigned char tab37489[] PROGMEM = 71, 218 }; -const unsigned char tab37515[] PROGMEM = +const unsigned char tab37515[] = { 125, 126, 126, 127, 128, 129, 130, 130, 130, 132, 132, 132, 132, 132, 133, 135, diff --git a/src/RenderTabs.h b/components/ESPIDF-SAM/src/RenderTabs.h similarity index 90% rename from src/RenderTabs.h rename to components/ESPIDF-SAM/src/RenderTabs.h index c492fb9..88e550d 100644 --- a/src/RenderTabs.h +++ b/components/ESPIDF-SAM/src/RenderTabs.h @@ -1,22 +1,21 @@ #ifndef RENDERTABS_H #define RENDERTABS_H -#include #include "esp8266sam_debug.h" #if DEBUG_ESP8266SAM_LIB #define PROGMEM #endif -const unsigned char tab48426[5] PROGMEM = { 0x18, 0x1A, 0x17, 0x17, 0x17 }; +const unsigned char tab48426[5] = { 0x18, 0x1A, 0x17, 0x17, 0x17 }; -const unsigned char tab47492[] PROGMEM = +const unsigned char tab47492[] = { 0 , 0 , 0xE0 , 0xE6 , 0xEC , 0xF3 , 0xF9 , 0 , 6 , 0xC , 6 }; -const unsigned char amplitudeRescale[] PROGMEM = +const unsigned char amplitudeRescale[] = { 0 , 1 , 2 , 2 , 2 , 3 , 3 , 4 , 4 , 5 , 6 , 8 , 9 ,0xB ,0xD ,0xF, 0 //17 elements? @@ -24,7 +23,7 @@ const unsigned char amplitudeRescale[] PROGMEM = // Used to decide which phoneme's blend lengths. The candidate with the lower score is selected. // tab45856 -const unsigned char blendRank[] PROGMEM = +const unsigned char blendRank[] = { 0 , 0x1F , 0x1F , 0x1F , 0x1F , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 5 , 5 , @@ -41,7 +40,7 @@ const unsigned char blendRank[] PROGMEM = // Number of frames at the end of a phoneme devoted to interpolating to next phoneme's final value //tab45696 -const unsigned char outBlendLength[] PROGMEM = +const unsigned char outBlendLength[] = { 0 , 2 , 2 , 2 , 2 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , @@ -58,7 +57,7 @@ const unsigned char outBlendLength[] PROGMEM = // Number of frames at beginning of a phoneme devoted to interpolating to phoneme's final value // tab45776 -const unsigned char inBlendLength[] PROGMEM = +const unsigned char inBlendLength[] = { 0 , 2 , 2 , 2 , 2 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , @@ -91,7 +90,7 @@ const unsigned char inBlendLength[] PROGMEM = // 67: ** 27 00011011 // 70: ** 25 00011001 // tab45936 -const unsigned char sampledConsonantFlags[] PROGMEM = +const unsigned char sampledConsonantFlags[] = { 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , @@ -151,7 +150,7 @@ unsigned char freq3data[]= 0x65 , 0x65 , 0x70 , 0x5E , 0x5E , 0x5E , 0x08 , 0x01 }; -const unsigned char ampl1data[] PROGMEM = +const unsigned char ampl1data[] = { 0 , 0 , 0 , 0 , 0 ,0xD ,0xD ,0xE , 0xF ,0xF ,0xF ,0xF ,0xF ,0xC ,0xD ,0xC , @@ -165,7 +164,7 @@ const unsigned char ampl1data[] PROGMEM = 0 ,0xC , 0 , 0 , 0 , 0 ,0xF ,0xF }; -const unsigned char ampl2data[] PROGMEM = +const unsigned char ampl2data[] = { 0 , 0 , 0 , 0 , 0 ,0xA ,0xB ,0xD , 0xE ,0xD ,0xC ,0xC ,0xB , 9 ,0xB ,0xB , @@ -179,7 +178,7 @@ const unsigned char ampl2data[] PROGMEM = 0 ,0xA , 0 , 0 ,0xA , 0 , 0 , 0 }; -const unsigned char ampl3data[] PROGMEM = +const unsigned char ampl3data[] = { 0 , 0 , 0 , 0 , 0 , 8 , 7 , 8 , 8 , 1 , 1 , 0 , 1 , 0 , 7 , 5 , @@ -196,10 +195,10 @@ const unsigned char ampl3data[] PROGMEM = //tab42240 -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}; +const signed char sinus[256] = {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}; //tab42496 -const unsigned char rectangle[] PROGMEM = +const unsigned char rectangle[] = { 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , @@ -238,7 +237,7 @@ const unsigned char rectangle[] PROGMEM = //random data ? -const unsigned char sampleTable[0x500] PROGMEM = +const unsigned char sampleTable[0x500] = { //00 diff --git a/src/SamData.h b/components/ESPIDF-SAM/src/SamData.h similarity index 100% rename from src/SamData.h rename to components/ESPIDF-SAM/src/SamData.h diff --git a/src/SamTabs.h b/components/ESPIDF-SAM/src/SamTabs.h similarity index 95% rename from src/SamTabs.h rename to components/ESPIDF-SAM/src/SamTabs.h index 933f95b..b223d2a 100644 --- a/src/SamTabs.h +++ b/components/ESPIDF-SAM/src/SamTabs.h @@ -1,20 +1,19 @@ #ifndef SAMTABS_H #define SAMTABS_H -#include #include "esp8266sam_debug.h" #if DEBUG_ESP8266SAM_LIB #define PROGMEM #endif //tab40672 -const unsigned char stressInputTable[] PROGMEM = +const unsigned char stressInputTable[] = { '*', '1', '2', '3', '4', '5', '6', '7', '8' }; //tab40682 -const unsigned char signInputTable1[] PROGMEM = +const unsigned char signInputTable1[] = { ' ', '.', '?', ',', '-', 'I', 'I', 'E', 'A', 'A', 'A', 'A', 'U', 'A', 'I', 'E', @@ -30,7 +29,7 @@ const unsigned char signInputTable1[] PROGMEM = }; //tab40763 -const unsigned char signInputTable2[] PROGMEM = +const unsigned char signInputTable2[] = { '*', '*', '*', '*', '*', 'Y', 'H', 'H', 'E', 'A', 'H', 'O', 'H', 'X', 'X', 'R', @@ -81,7 +80,7 @@ const unsigned char flags2[] = //tab45616??? -const unsigned char phonemeStressedLengthTable[] PROGMEM = +const unsigned char phonemeStressedLengthTable[] = { 0x00 , 0x12 , 0x12 , 0x12 , 8 ,0xB , 9 ,0xB , 0xE ,0xF ,0xB , 0x10 ,0xC , 6 , 6 ,0xE , @@ -96,7 +95,7 @@ const unsigned char phonemeStressedLengthTable[] PROGMEM = }; //tab45536??? -const unsigned char phonemeLengthTable[] PROGMEM = +const unsigned char phonemeLengthTable[] = { 0 , 0x12 , 0x12 , 0x12 , 8 , 8 , 8 , 8 , 8 ,0xB , 6 ,0xC ,0xA , 5 , 5 ,0xB , diff --git a/src/esp8266sam_debug.c b/components/ESPIDF-SAM/src/esp8266sam_debug.c similarity index 100% rename from src/esp8266sam_debug.c rename to components/ESPIDF-SAM/src/esp8266sam_debug.c diff --git a/src/esp8266sam_debug.h b/components/ESPIDF-SAM/src/esp8266sam_debug.h similarity index 100% rename from src/esp8266sam_debug.h rename to components/ESPIDF-SAM/src/esp8266sam_debug.h diff --git a/components/ESPIDF-SAM/src/reciter.c b/components/ESPIDF-SAM/src/reciter.c new file mode 100644 index 0000000..a59546a --- /dev/null +++ b/components/ESPIDF-SAM/src/reciter.c @@ -0,0 +1,552 @@ +#include +#include +#include "reciter.h" +#include "ReciterTabs.h" +#include "esp8266sam_debug.h" +#include "SamData.h" + +unsigned char A, X, Y; +//extern int debug; + +#define inputtemp (samdata->reciter.inputtemp) + +void Code37055(unsigned char mem59) +{ + X = mem59; + X--; + A = inputtemp[X]; + Y = A; + A = tab36376[Y]; + return; +} + +void Code37066(unsigned char mem58) +{ + X = mem58; + X++; + A = inputtemp[X]; + Y = A; + A = tab36376[Y]; +} + +unsigned char GetRuleByte(unsigned short mem62, unsigned char Y) +{ + unsigned int address = mem62; + + if (mem62 >= 37541) + { + address -= 37541; + return rules2[address+Y]; + } + address -= 32000; + return rules[address+Y]; +} + +int TextToPhonemes(char *input) // Code36484 +{ + //unsigned char *tab39445 = &mem[39445]; //input and output + //unsigned char mem29; + unsigned char mem56; //output position for phonemes + unsigned char mem57; + unsigned char mem58; + unsigned char mem59; + unsigned char mem60; + unsigned char mem61; + unsigned short mem62; // memory position of current rule + + unsigned char mem64; // position of '=' or current character + unsigned char mem65; // position of ')' + unsigned char mem66; // position of '(' + unsigned char mem36653; + + inputtemp[0] = 32; + + // secure copy of input + // because input will be overwritten by phonemes + X = 1; + Y = 0; + do + { + //pos36499: + A = input[Y] & 127; + if ( A >= 112) A = A & 95; + else if ( A >= 96) A = A & 79; + + inputtemp[X] = A; + X++; + Y++; + } while (Y != 255); + + + X = 255; + inputtemp[X] = 27; + mem61 = 255; + + +pos36550: + A = 255; + mem56 = 255; + + +pos36554: + while(1) + { + mem61++; + X = mem61; + A = inputtemp[X]; + mem64 = A; + if (A == '[') + { + mem56++; + X = mem56; + A = 155; + input[X] = 155; + //goto pos36542; + // Code39771(); //Code39777(); + return 1; + } + + //pos36579: + if (A != '.') break; + X++; + Y = inputtemp[X]; + A = tab36376[Y] & 1; + if(A != 0) break; + mem56++; + X = mem56; + A = '.'; + input[X] = '.'; + } //while + + + //pos36607: + A = mem64; + Y = A; + A = tab36376[A]; + mem57 = A; + if((A&2) != 0) + { + mem62 = 37541; + goto pos36700; + } + + //pos36630: + A = mem57; + if(A != 0) goto pos36677; + A = 32; + inputtemp[X] = ' '; + mem56++; + X = mem56; + if (X > 120) goto pos36654; + input[X] = A; + goto pos36554; + + // ----- + + //36653 is unknown. Contains position + +pos36654: + input[X] = 155; + A = mem61; + mem36653 = A; + // mem29 = A; // not used + // Code36538(); das ist eigentlich + return 1; + //Code39771(); + //go on if there is more input ??? + mem61 = mem36653; + goto pos36550; + +pos36677: + A = mem57 & 128; + if(A == 0) + { + //36683: BRK + return 0; + } + + // go to the right rules for this character. + X = mem64 - 'A'; + mem62 = tab37489[X] | (tab37515[X]<<8); + + // ------------------------------------- + // go to next rule + // ------------------------------------- + +pos36700: + + // find next rule + Y = 0; + do + { + mem62 += 1; + A = GetRuleByte(mem62, Y); + } while ((A & 128) == 0); + Y++; + + //pos36720: + // find '(' + while(1) + { + A = GetRuleByte(mem62, Y); + if (A == '(') break; + Y++; + } + mem66 = Y; + + //pos36732: + // find ')' + do + { + Y++; + A = GetRuleByte(mem62, Y); + } while(A != ')'); + mem65 = Y; + + //pos36741: + // find '=' + do + { + Y++; + A = GetRuleByte(mem62, Y); + A = A & 127; + } while (A != '='); + mem64 = Y; + + X = mem61; + mem60 = X; + + // compare the string within the bracket + Y = mem66; + Y++; + //pos36759: + while(1) + { + mem57 = inputtemp[X]; + A = GetRuleByte(mem62, Y); + if (A != mem57) goto pos36700; + Y++; + if(Y == mem65) break; + X++; + mem60 = X; + } + +// the string in the bracket is correct + +//pos36787: + A = mem61; + mem59 = mem61; + +pos36791: + while(1) + { + mem66--; + Y = mem66; + A = GetRuleByte(mem62, Y); + mem57 = A; + //36800: BPL 36805 + if ((A & 128) != 0) goto pos37180; + X = A & 127; + A = tab36376[X] & 128; + if (A == 0) break; + X = mem59-1; + A = inputtemp[X]; + if (A != mem57) goto pos36700; + mem59 = X; + } + +//pos36833: + A = mem57; + if (A == ' ') goto pos36895; + if (A == '#') goto pos36910; + if (A == '.') goto pos36920; + if (A == '&') goto pos36935; + if (A == '@') goto pos36967; + if (A == '^') goto pos37004; + if (A == '+') goto pos37019; + if (A == ':') goto pos37040; + // Code42041(); //Error + //36894: BRK + return 0; + + // -------------- + +pos36895: + Code37055(mem59); + A = A & 128; + if(A != 0) goto pos36700; +pos36905: + mem59 = X; + goto pos36791; + + // -------------- + +pos36910: + Code37055(mem59); + A = A & 64; + if(A != 0) goto pos36905; + goto pos36700; + + // -------------- + + +pos36920: + Code37055(mem59); + A = A & 8; + if(A == 0) goto pos36700; +pos36930: + mem59 = X; + goto pos36791; + + // -------------- + +pos36935: + Code37055(mem59); + A = A & 16; + if(A != 0) goto pos36930; + A = inputtemp[X]; + if (A != 72) goto pos36700; + X--; + A = inputtemp[X]; + if ((A == 67) || (A == 83)) goto pos36930; + goto pos36700; + + // -------------- + +pos36967: + Code37055(mem59); + A = A & 4; + if(A != 0) goto pos36930; + A = inputtemp[X]; + if (A != 72) goto pos36700; + if ((A != 84) && (A != 67) && (A != 83)) goto pos36700; + mem59 = X; + goto pos36791; + + // -------------- + + +pos37004: + Code37055(mem59); + A = A & 32; + if(A == 0) goto pos36700; + +pos37014: + mem59 = X; + goto pos36791; + + // -------------- + +pos37019: + X = mem59; + X--; + A = inputtemp[X]; + if ((A == 'E') || (A == 'I') || (A == 'Y')) goto pos37014; + goto pos36700; + // -------------- + +pos37040: + Code37055(mem59); + A = A & 32; + if(A == 0) goto pos36791; + mem59 = X; + goto pos37040; + +//--------------------------------------- + + +pos37077: + X = mem58+1; + A = inputtemp[X]; + if (A != 'E') goto pos37157; + X++; + Y = inputtemp[X]; + X--; + A = tab36376[Y] & 128; + if(A == 0) goto pos37108; + X++; + A = inputtemp[X]; + if (A != 'R') goto pos37113; +pos37108: + mem58 = X; + goto pos37184; +pos37113: + if ((A == 83) || (A == 68)) goto pos37108; // 'S' 'D' + if (A != 76) goto pos37135; // 'L' + X++; + A = inputtemp[X]; + if (A != 89) goto pos36700; + goto pos37108; + +pos37135: + if (A != 70) goto pos36700; + X++; + A = inputtemp[X]; + if (A != 85) goto pos36700; + X++; + A = inputtemp[X]; + if (A == 76) goto pos37108; + goto pos36700; + +pos37157: + if (A != 73) goto pos36700; + X++; + A = inputtemp[X]; + if (A != 78) goto pos36700; + X++; + A = inputtemp[X]; + if (A == 71) goto pos37108; + //pos37177: + goto pos36700; + + // ----------------------------------------- + +pos37180: + + A = mem60; + mem58 = A; + +pos37184: + Y = mem65 + 1; + + //37187: CPY 64 + // if(? != 0) goto pos37194; + if(Y == mem64) goto pos37455; + mem65 = Y; + //37196: LDA (62),y + A = GetRuleByte(mem62, Y); + mem57 = A; + X = A; + A = tab36376[X] & 128; + if(A == 0) goto pos37226; + X = mem58+1; + A = inputtemp[X]; + if (A != mem57) goto pos36700; + mem58 = X; + goto pos37184; +pos37226: + A = mem57; + if (A == 32) goto pos37295; // ' ' + if (A == 35) goto pos37310; // '#' + if (A == 46) goto pos37320; // '.' + if (A == 38) goto pos37335; // '&' + if (A == 64) goto pos37367; // '' + if (A == 94) goto pos37404; // '' + if (A == 43) goto pos37419; // '+' + if (A == 58) goto pos37440; // ':' + if (A == 37) goto pos37077; // '%' + //pos37291: + // Code42041(); //Error + //37294: BRK + return 0; + + // -------------- +pos37295: + Code37066(mem58); + A = A & 128; + if(A != 0) goto pos36700; +pos37305: + mem58 = X; + goto pos37184; + + // -------------- + +pos37310: + Code37066(mem58); + A = A & 64; + if(A != 0) goto pos37305; + goto pos36700; + + // -------------- + + +pos37320: + Code37066(mem58); + A = A & 8; + if(A == 0) goto pos36700; + +pos37330: + mem58 = X; + goto pos37184; + + // -------------- + +pos37335: + Code37066(mem58); + A = A & 16; + if(A != 0) goto pos37330; + A = inputtemp[X]; + if (A != 72) goto pos36700; + X++; + A = inputtemp[X]; + if ((A == 67) || (A == 83)) goto pos37330; + goto pos36700; + + // -------------- + + +pos37367: + Code37066(mem58); + A = A & 4; + if(A != 0) goto pos37330; + A = inputtemp[X]; + if (A != 72) goto pos36700; + if ((A != 84) && (A != 67) && (A != 83)) goto pos36700; + mem58 = X; + goto pos37184; + + // -------------- + +pos37404: + Code37066(mem58); + A = A & 32; + if(A == 0) goto pos36700; +pos37414: + mem58 = X; + goto pos37184; + + // -------------- + +pos37419: + X = mem58; + X++; + A = inputtemp[X]; + if ((A == 69) || (A == 73) || (A == 89)) goto pos37414; + goto pos36700; + +// ---------------------- + +pos37440: + + Code37066(mem58); + A = A & 32; + if(A == 0) goto pos37184; + mem58 = X; + goto pos37440; +pos37455: + Y = mem64; + mem61 = mem60; + + if (DEBUG_ESP8266SAM_LIB) + PrintRule(mem62); + +pos37461: + //37461: LDA (62),y + A = GetRuleByte(mem62, Y); + mem57 = A; + A = A & 127; + if (A != '=') + { + mem56++; + X = mem56; + input[X] = A; + } + + //37478: BIT 57 + //37480: BPL 37485 //not negative flag + if ((mem57 & 128) == 0) goto pos37485; //??? + goto pos36554; +pos37485: + Y++; + goto pos37461; +} diff --git a/src/reciter.h b/components/ESPIDF-SAM/src/reciter.h similarity index 100% rename from src/reciter.h rename to components/ESPIDF-SAM/src/reciter.h diff --git a/components/ESPIDF-SAM/src/render.c b/components/ESPIDF-SAM/src/render.c new file mode 100644 index 0000000..a607a4b --- /dev/null +++ b/components/ESPIDF-SAM/src/render.c @@ -0,0 +1,1056 @@ +//#include +#include +#include // abs() +#include + +#include "render.h" +#include "RenderTabs.h" + +#include "esp8266sam_debug.h" +#include "SamData.h" + +unsigned char wait1 = 7; +unsigned char wait2 = 6; + +extern unsigned char A, X, Y; +extern unsigned char mem44; +extern unsigned char mem47; +extern unsigned char mem49; +extern unsigned char mem39; +extern unsigned char mem50; +extern unsigned char mem51; +extern unsigned char mem53; +extern unsigned char mem56; + +extern unsigned char speed; +extern unsigned char pitch; +extern int singmode; + +#define phonemeIndexOutput (samdata->sam.phonemeIndexOutput) +#define stressOutput (samdata->sam.stressOutput) +#define phonemeLengthOutput (samdata->sam.phonemeLengthOutput) +#define pitches (samdata->render.pitches) +#define frequency1 (samdata->render.frequency1) +#define frequency2 (samdata->render.frequency2) +#define frequency3 (samdata->render.frequency3) +#define amplitude1 (samdata->render.amplitude1) +#define amplitude2 (samdata->render.amplitude2) +#define amplitude3 (samdata->render.amplitude3) +#define sampledConsonantFlag (samdata->render.sampledConsonantFlag) + +void AddInflection(unsigned char mem48, unsigned char phase1); +unsigned char trans(unsigned char mem39212, unsigned char mem39213); + + +// contains the final soundbuffer +extern int bufferpos; +//extern char *buffer; + +#ifndef ESP8266 +static void yield() { /* NOOP */ } +#endif + +//timetable for more accurate c64 simulation +int timetable[5][5] = +{ + {162, 167, 167, 127, 128}, + {226, 60, 60, 0, 0}, + {225, 60, 59, 0, 0}, + {200, 0, 0, 54, 55}, + {199, 0, 0, 54, 54} +}; + +extern void (*outcb)(void *, unsigned char); +extern void *outcbdata; +static unsigned char oldtimetableindex = 0; +static unsigned char lastAry[5]; +void Output8BitAry(int index, unsigned char ary[5]) +{ + int newbufferpos = bufferpos + timetable[oldtimetableindex][index]; + int bp0 = bufferpos / 50; + int bp1 = newbufferpos / 50; + int k=0; + for (int i=bp0; i X +// unvoiced 1 bit -> 5 +// +// voiced 0 bit -> 6 +// voiced 1 bit -> 24 +// +// Where X is a value from the table: +// +// { 0x18, 0x1A, 0x17, 0x17, 0x17 }; +// +// The index into this table is determined by masking off the lower +// 3 bits from the SampledPhonemesTable: +// +// index = (SampledPhonemesTable[i] & 7) - 1; +// +// For voices samples, samples are interleaved between voiced output. + + +// Code48227() +void RenderSample(unsigned char *mem66) +{ + int tempA; + // current phoneme's index + mem49 = Y; + + // mask low three bits and subtract 1 get value to + // convert 0 bits on unvoiced samples. + A = mem39&7; + X = A-1; + + // store the result + mem56 = X; + + // determine which offset to use from table { 0x18, 0x1A, 0x17, 0x17, 0x17 } + // T, S, Z 0 0x18 + // CH, J, SH, ZH 1 0x1A + // P, F*, V, TH, DH 2 0x17 + // /H 3 0x17 + // /X 4 0x17 + + // get value from the table + mem53 = tab48426[X]; + mem47 = X; //46016+mem[56]*256 + + // voiced sample? + A = mem39 & 248; + if(A == 0) + { + // voiced phoneme: Z*, ZH, V*, DH + Y = mem49; + A = pitches[mem49] >> 4; + + // jump to voiced portion + goto pos48315; + } + + Y = A ^ 255; +pos48274: + + // step through the 8 bits in the sample + mem56 = 8; + + // get the next sample from the table + // mem47*256 = offset to start of samples + A = sampleTable[mem47*256+Y]; +pos48280: + + // left shift to get the high bit + tempA = A; + A = A << 1; + //48281: BCC 48290 + + // bit not set? + if ((tempA & 128) == 0) + { + // convert the bit to value from table + X = mem53; + //mem[54296] = X; + // output the byte + Output8Bit(1, (X&0x0f) * 16); + // if X != 0, exit loop + if(X != 0) goto pos48296; + } + + // output a 5 for the on bit + Output8Bit(2, 5 * 16); + + //48295: NOP +pos48296: + + X = 0; + + // decrement counter + mem56--; + + // if not done, jump to top of loop + if (mem56 != 0) goto pos48280; + + // increment position + Y++; + if (Y != 0) goto pos48274; + + // restore values and return + mem44 = 1; + Y = mem49; + return; + + + unsigned char phase1; + +pos48315: +// handle voiced samples here + + // number of samples? + phase1 = A ^ 255; + + Y = *mem66; + do + { + //pos48321: + + // shift through all 8 bits + mem56 = 8; + //A = Read(mem47, Y); + + // fetch value from table + A = sampleTable[mem47*256+Y]; + + // loop 8 times + //pos48327: + do + { + //48327: ASL A + //48328: BCC 48337 + + // left shift and check high bit + tempA = A; + A = A << 1; + if ((tempA & 128) != 0) + { + // if bit set, output 26 + X = 26; + Output8Bit(3, (X&0xf)*16); + } else + { + //timetable 4 + // bit is not set, output a 6 + X=6; + Output8Bit(4, (X&0xf)*16); + } + + mem56--; + } while(mem56 != 0); + + // move ahead in the table + Y++; + + // continue until counter done + phase1++; + + } while (phase1 != 0); + // if (phase1 != 0) goto pos48321; + + // restore values and return + A = 1; + mem44 = 1; + *mem66 = Y; + Y = mem49; + return; +} + + + +// RENDER THE PHONEMES IN THE LIST +// +// The phoneme list is converted into sound through the steps: +// +// 1. Copy each phoneme number of times into the frames list, +// where each frame represents 10 milliseconds of sound. +// +// 2. Determine the transitions lengths between phonemes, and linearly +// interpolate the values across the frames. +// +// 3. Offset the pitches by the fundamental frequency. +// +// 4. Render the each frame. + + + +//void Code47574() +void Render() +{ + unsigned char phase1 = 0; //mem43 + unsigned char phase2=0; + unsigned char phase3=0; + unsigned char mem66=0; + unsigned char mem38=0; + unsigned char mem40=0; + unsigned char speedcounter=0; //mem45 + unsigned char mem48=0; + int i; + if (phonemeIndexOutput[0] == 255) return; //exit if no data + + A = 0; + X = 0; + mem44 = 0; + + +// CREATE FRAMES +// +// The length parameter in the list corresponds to the number of frames +// to expand the phoneme to. Each frame represents 10 milliseconds of time. +// So a phoneme with a length of 7 = 7 frames = 70 milliseconds duration. +// +// The parameters are copied from the phoneme to the frame verbatim. + + +// pos47587: +do +{ + // get the index + Y = mem44; + // get the phoneme at the index + A = phonemeIndexOutput[mem44]; + mem56 = A; + + // if terminal phoneme, exit the loop + if (A == 255) break; + + // period phoneme *. + if (A == 1) + { + // add rising inflection + A = 1; + mem48 = 1; + //goto pos48376; + AddInflection(mem48, phase1); + } + /* + if (A == 2) goto pos48372; + */ + + // question mark phoneme? + if (A == 2) + { + // create falling inflection + mem48 = 255; + AddInflection(mem48, phase1); + } + // pos47615: + + // get the stress amount (more stress = higher pitch) + phase1 = tab47492[stressOutput[Y] + 1]; + + // get number of frames to write + phase2 = phonemeLengthOutput[Y]; + Y = mem56; + + // copy from the source to the frames list + do + { + frequency1[X] = freq1data[Y]; // F1 frequency + frequency2[X] = freq2data[Y]; // F2 frequency + frequency3[X] = freq3data[Y]; // F3 frequency + amplitude1[X] = ampl1data[Y]; // F1 amplitude + amplitude2[X] = ampl2data[Y]; // F2 amplitude + amplitude3[X] = ampl3data[Y]; // F3 amplitude + sampledConsonantFlag[X] = sampledConsonantFlags[Y]; // phoneme data for sampled consonants + pitches[X] = pitch + phase1; // pitch + X++; + phase2--; + } while(phase2 != 0); + mem44++; +} while(mem44 != 0); +// ------------------- +//pos47694: + +// CREATE TRANSITIONS +// +// Linear transitions are now created to smoothly connect the +// end of one sustained portion of a phoneme to the following +// phoneme. +// +// To do this, three tables are used: +// +// Table Purpose +// ========= ================================================== +// blendRank Determines which phoneme's blend values are used. +// +// blendOut The number of frames at the end of the phoneme that +// will be used to transition to the following phoneme. +// +// blendIn The number of frames of the following phoneme that +// will be used to transition into that phoneme. +// +// In creating a transition between two phonemes, the phoneme +// with the HIGHEST rank is used. Phonemes are ranked on how much +// their identity is based on their transitions. For example, +// vowels are and diphthongs are identified by their sustained portion, +// rather than the transitions, so they are given low values. In contrast, +// stop consonants (P, B, T, K) and glides (Y, L) are almost entirely +// defined by their transitions, and are given high rank values. +// +// Here are the rankings used by SAM: +// +// Rank Type Phonemes +// 2 All vowels IY, IH, etc. +// 5 Diphthong endings YX, WX, ER +// 8 Terminal liquid consonants LX, WX, YX, N, NX +// 9 Liquid consonants L, RX, W +// 10 Glide R, OH +// 11 Glide WH +// 18 Voiceless fricatives S, SH, F, TH +// 20 Voiced fricatives Z, ZH, V, DH +// 23 Plosives, stop consonants P, T, K, KX, DX, CH +// 26 Stop consonants J, GX, B, D, G +// 27-29 Stop consonants (internal) ** +// 30 Unvoiced consonants /H, /X and Q* +// 160 Nasal M +// +// To determine how many frames to use, the two phonemes are +// compared using the blendRank[] table. The phoneme with the +// higher rank is selected. In case of a tie, a blend of each is used: +// +// if blendRank[phoneme1] == blendRank[phomneme2] +// // use lengths from each phoneme +// outBlendFrames = outBlend[phoneme1] +// inBlendFrames = outBlend[phoneme2] +// else if blendRank[phoneme1] > blendRank[phoneme2] +// // use lengths from first phoneme +// outBlendFrames = outBlendLength[phoneme1] +// inBlendFrames = inBlendLength[phoneme1] +// else +// // use lengths from the second phoneme +// // note that in and out are SWAPPED! +// outBlendFrames = inBlendLength[phoneme2] +// inBlendFrames = outBlendLength[phoneme2] +// +// Blend lengths can't be less than zero. +// +// Transitions are assumed to be symetrical, so if the transition +// values for the second phoneme are used, the inBlendLength and +// outBlendLength values are SWAPPED. +// +// For most of the parameters, SAM interpolates over the range of the last +// outBlendFrames-1 and the first inBlendFrames. +// +// The exception to this is the Pitch[] parameter, which is interpolates the +// pitch from the CENTER of the current phoneme to the CENTER of the next +// phoneme. +// +// Here are two examples. First, For example, consider the word "SUN" (S AH N) +// +// Phoneme Duration BlendWeight OutBlendFrames InBlendFrames +// S 2 18 1 3 +// AH 8 2 4 4 +// N 7 8 1 2 +// +// The formant transitions for the output frames are calculated as follows: +// +// flags ampl1 freq1 ampl2 freq2 ampl3 freq3 pitch +// ------------------------------------------------ +// S +// 241 0 6 0 73 0 99 61 Use S (weight 18) for transition instead of AH (weight 2) +// 241 0 6 0 73 0 99 61 <-- (OutBlendFrames-1) = (1-1) = 0 frames +// AH +// 0 2 10 2 66 0 96 59 * <-- InBlendFrames = 3 frames +// 0 4 14 3 59 0 93 57 * +// 0 8 18 5 52 0 90 55 * +// 0 15 22 9 44 1 87 53 +// 0 15 22 9 44 1 87 53 +// 0 15 22 9 44 1 87 53 Use N (weight 8) for transition instead of AH (weight 2). +// 0 15 22 9 44 1 87 53 Since N is second phoneme, reverse the IN and OUT values. +// 0 11 17 8 47 1 98 56 * <-- (InBlendFrames-1) = (2-1) = 1 frames +// N +// 0 8 12 6 50 1 109 58 * <-- OutBlendFrames = 1 +// 0 5 6 5 54 0 121 61 +// 0 5 6 5 54 0 121 61 +// 0 5 6 5 54 0 121 61 +// 0 5 6 5 54 0 121 61 +// 0 5 6 5 54 0 121 61 +// 0 5 6 5 54 0 121 61 +// +// Now, consider the reverse "NUS" (N AH S): +// +// flags ampl1 freq1 ampl2 freq2 ampl3 freq3 pitch +// ------------------------------------------------ +// N +// 0 5 6 5 54 0 121 61 +// 0 5 6 5 54 0 121 61 +// 0 5 6 5 54 0 121 61 +// 0 5 6 5 54 0 121 61 +// 0 5 6 5 54 0 121 61 +// 0 5 6 5 54 0 121 61 Use N (weight 8) for transition instead of AH (weight 2) +// 0 5 6 5 54 0 121 61 <-- (OutBlendFrames-1) = (1-1) = 0 frames +// AH +// 0 8 11 6 51 0 110 59 * <-- InBlendFrames = 2 +// 0 11 16 8 48 0 99 56 * +// 0 15 22 9 44 1 87 53 Use S (weight 18) for transition instead of AH (weight 2) +// 0 15 22 9 44 1 87 53 Since S is second phoneme, reverse the IN and OUT values. +// 0 9 18 5 51 1 90 55 * <-- (InBlendFrames-1) = (3-1) = 2 +// 0 4 14 3 58 1 93 57 * +// S +// 241 2 10 2 65 1 96 59 * <-- OutBlendFrames = 1 +// 241 0 6 0 73 0 99 61 + + A = 0; + mem44 = 0; + mem49 = 0; // mem49 starts at as 0 + X = 0; + while(1) //while No. 1 + { + + // get the current and following phoneme + Y = phonemeIndexOutput[X]; + A = phonemeIndexOutput[X+1]; + X++; + + // exit loop at end token + if (A == 255) break;//goto pos47970; + + + // get the ranking of each phoneme + X = A; + mem56 = blendRank[A]; + A = blendRank[Y]; + + // compare the rank - lower rank value is stronger + if (A == mem56) + { + // same rank, so use out blend lengths from each phoneme + phase1 = outBlendLength[Y]; + phase2 = outBlendLength[X]; + } else + if (A < mem56) + { + // first phoneme is stronger, so us it's blend lengths + phase1 = inBlendLength[X]; + phase2 = outBlendLength[X]; + } else + { + // second phoneme is stronger, so use it's blend lengths + // note the out/in are swapped + phase1 = outBlendLength[Y]; + phase2 = inBlendLength[Y]; + } + + Y = mem44; + A = mem49 + phonemeLengthOutput[mem44]; // A is mem49 + length + mem49 = A; // mem49 now holds length + position + A = A + phase2; //Maybe Problem because of carry flag + + //47776: ADC 42 + speedcounter = A; + mem47 = 168; + phase3 = mem49 - phase1; // what is mem49 + A = phase1 + phase2; // total transition? + mem38 = A; + + X = A; + X -= 2; + if ((X & 128) == 0) + do //while No. 2 + { + //pos47810: + + // mem47 is used to index the tables: + // 168 pitches[] + // 169 frequency1 + // 170 frequency2 + // 171 frequency3 + // 172 amplitude1 + // 173 amplitude2 + // 174 amplitude3 + + mem40 = mem38; + + if (mem47 == 168) // pitch + { + + // unlike the other values, the pitches[] interpolates from + // the middle of the current phoneme to the middle of the + // next phoneme + + unsigned char mem36, mem37; + // half the width of the current phoneme + mem36 = phonemeLengthOutput[mem44] >> 1; + // half the width of the next phoneme + mem37 = phonemeLengthOutput[mem44+1] >> 1; + // sum the values + mem40 = mem36 + mem37; // length of both halves + mem37 += mem49; // center of next phoneme + mem36 = mem49 - mem36; // center index of current phoneme + A = Read(mem47, mem37); // value at center of next phoneme - end interpolation value + //A = mem[address]; + + Y = mem36; // start index of interpolation + mem53 = A - Read(mem47, mem36); // value to center of current phoneme + } else + { + // value to interpolate to + A = Read(mem47, speedcounter); + // position to start interpolation from + Y = phase3; + // value to interpolate from + mem53 = A - Read(mem47, phase3); + } + + //Code47503(mem40); + // ML : Code47503 is division with remainder, and mem50 gets the sign + + // calculate change per frame + signed char m53 = (signed char)mem53; + mem50 = mem53 & 128; + unsigned char m53abs = abs(m53); + mem51 = m53abs % mem40; //abs((char)m53) % mem40; + mem53 = (unsigned char)((signed char)(m53) / mem40); + + // interpolation range + X = mem40; // number of frames to interpolate over + Y = phase3; // starting frame + + + // linearly interpolate values + + mem56 = 0; + //47907: CLC + //pos47908: + while(1) //while No. 3 + { + A = Read(mem47, Y) + mem53; //carry alway cleared + + mem48 = A; + Y++; + X--; + if(X == 0) break; + + mem56 += mem51; + if (mem56 >= mem40) //??? + { + mem56 -= mem40; //carry? is set + //if ((mem56 & 128)==0) + if ((mem50 & 128)==0) + { + //47935: BIT 50 + //47937: BMI 47943 + if(mem48 != 0) mem48++; + } else mem48--; + } + //pos47945: + Write(mem47, Y, mem48); + } //while No. 3 + + //pos47952: + mem47++; + //if (mem47 != 175) goto pos47810; + } while (mem47 != 175); //while No. 2 + //pos47963: + mem44++; + X = mem44; + } //while No. 1 + + //goto pos47701; + //pos47970: + + // add the length of this phoneme + mem48 = mem49 + phonemeLengthOutput[mem44]; + + +// ASSIGN PITCH CONTOUR +// +// This subtracts the F1 frequency from the pitch to create a +// pitch contour. Without this, the output would be at a single +// pitch level (monotone). + + + // don't adjust pitch if in sing mode + if (!singmode) + { + // iterate through the buffer + for(i=0; i<256; i++) { + // subtract half the frequency of the formant 1. + // this adds variety to the voice + pitches[i] -= (frequency1[i] >> 1); + } + } + + phase1 = 0; + phase2 = 0; + phase3 = 0; + mem49 = 0; + speedcounter = 72; //sam standard speed + +// RESCALE AMPLITUDE +// +// Rescale volume from a linear scale to decibels. +// + + //amplitude rescaling + for(i=255; i>=0; i--) + { + amplitude1[i] = amplitudeRescale[amplitude1[i]]; + amplitude2[i] = amplitudeRescale[amplitude2[i]]; + amplitude3[i] = amplitudeRescale[amplitude3[i]]; + } + + Y = 0; + A = pitches[0]; + mem44 = A; + X = A; + mem38 = A - (A>>2); // 3/4*A ??? + +if (DEBUG_ESP8266SAM_LIB) +{ + PrintOutput(sampledConsonantFlag, frequency1, frequency2, frequency3, amplitude1, amplitude2, amplitude3, pitches); +} + +// PROCESS THE FRAMES +// +// In traditional vocal synthesis, the glottal pulse drives filters, which +// are attenuated to the frequencies of the formants. +// +// SAM generates these formants directly with sin and rectangular waves. +// To simulate them being driven by the glottal pulse, the waveforms are +// reset at the beginning of each glottal pulse. + + //finally the loop for sound output + //pos48078: + while(1) + { + // get the sampled information on the phoneme + A = sampledConsonantFlag[Y]; + mem39 = A; + + // unvoiced sampled phoneme? + A = A & 248; + if(A != 0) + { + // render the sample for the phoneme + RenderSample(&mem66); + + // skip ahead two in the phoneme buffer + Y += 2; + mem48 -= 2; + } else + { + // simulate the glottal pulse and formants + unsigned char ary[5]; + unsigned int p1 = phase1 * 256; // Fixed point integers because we need to divide later on + unsigned int p2 = phase2 * 256; + unsigned int p3 = phase3 * 256; + int k; + for (k=0; k<5; k++) { + signed char sp1 = (signed char)sinus[0xff & (p1>>8)]; + signed char sp2 = (signed char)sinus[0xff & (p2>>8)]; + signed char rp3 = (signed char)rectangle[0xff & (p3>>8)]; + signed int sin1 = sp1 * ((unsigned char)amplitude1[Y] & 0x0f); + signed int sin2 = sp2 * ((unsigned char)amplitude2[Y] & 0x0f); + signed int rect = rp3 * ((unsigned char)amplitude3[Y] & 0x0f); + signed int mux = sin1 + sin2 + rect; + mux /= 32; + mux += 128; // Go from signed to unsigned amplitude + ary[k] = mux; + p1 += frequency1[Y] * 256 / 4; // Compromise, this becomes a shift and works well + p2 += frequency2[Y] * 256 / 4; + p3 += frequency3[Y] * 256 / 4; + } + // output the accumulated value + Output8BitAry(0, ary); + speedcounter--; + if (speedcounter != 0) goto pos48155; + Y++; //go to next amplitude + + // decrement the frame count + mem48--; + } + + // if the frame count is zero, exit the loop + if(mem48 == 0) return; + speedcounter = speed; +pos48155: + + // decrement the remaining length of the glottal pulse + mem44--; + + // finished with a glottal pulse? + if(mem44 == 0) + { +pos48159: + // fetch the next glottal pulse length + A = pitches[Y]; + mem44 = A; + A = A - (A>>2); + mem38 = A; + + // reset the formant wave generators to keep them in + // sync with the glottal pulse + phase1 = 0; + phase2 = 0; + phase3 = 0; + continue; + } + + // decrement the count + mem38--; + + // is the count non-zero and the sampled flag is zero? + if((mem38 != 0) || (mem39 == 0)) + { + // reset the phase of the formants to match the pulse + phase1 += frequency1[Y]; + phase2 += frequency2[Y]; + phase3 += frequency3[Y]; + continue; + } + + // voiced sampled phonemes interleave the sample with the + // glottal pulse. The sample flag is non-zero, so render + // the sample for the phoneme. + RenderSample(&mem66); + goto pos48159; + } //while + +} + + +// Create a rising or falling inflection 30 frames prior to +// index X. A rising inflection is used for questions, and +// a falling inflection is used for statements. + +void AddInflection(unsigned char mem48, unsigned char phase1) +{ + //pos48372: + // mem48 = 255; +//pos48376: + + // store the location of the punctuation + mem49 = X; + A = X; + int Atemp = A; + + // backup 30 frames + A = A - 30; + // if index is before buffer, point to start of buffer + if (Atemp <= 30) A=0; + X = A; + + // FIXME: Explain this fix better, it's not obvious + // ML : A =, fixes a problem with invalid pitch with '.' + while( (A=pitches[X]) == 127) X++; + + +pos48398: + //48398: CLC + //48399: ADC 48 + + // add the inflection direction + A += mem48; + phase1 = A; + + // set the inflection + pitches[X] = A; +pos48406: + + // increment the position + X++; + + // exit if the punctuation has been reached + if (X == mem49) return; //goto pos47615; + if (pitches[X] == 255) goto pos48406; + A = phase1; + goto pos48398; +} + +/* + SAM's voice can be altered by changing the frequencies of the + mouth formant (F1) and the throat formant (F2). Only the voiced + phonemes (5-29 and 48-53) are altered. +*/ +void SetMouthThroat(unsigned char mouth, unsigned char throat) +{ + unsigned char initialFrequency; + unsigned char newFrequency = 0; + //unsigned char mouth; //mem38880 + //unsigned char throat; //mem38881 + + // mouth formants (F1) 5..29 + unsigned char mouthFormants5_29[30] = { + 0, 0, 0, 0, 0, 10, + 14, 19, 24, 27, 23, 21, 16, 20, 14, 18, 14, 18, 18, + 16, 13, 15, 11, 18, 14, 11, 9, 6, 6, 6}; + + // throat formants (F2) 5..29 + unsigned char throatFormants5_29[30] = { + 255, 255, + 255, 255, 255, 84, 73, 67, 63, 40, 44, 31, 37, 45, 73, 49, + 36, 30, 51, 37, 29, 69, 24, 50, 30, 24, 83, 46, 54, 86}; + + // there must be no zeros in this 2 tables + // formant 1 frequencies (mouth) 48..53 + unsigned char mouthFormants48_53[6] = {19, 27, 21, 27, 18, 13}; + + // formant 2 frequencies (throat) 48..53 + unsigned char throatFormants48_53[6] = {72, 39, 31, 43, 30, 34}; + + unsigned char pos = 5; //mem39216 +//pos38942: + // recalculate formant frequencies 5..29 for the mouth (F1) and throat (F2) + while(pos != 30) + { + // recalculate mouth frequency + initialFrequency = mouthFormants5_29[pos]; + if (initialFrequency != 0) newFrequency = trans(mouth, initialFrequency); + freq1data[pos] = newFrequency; + + // recalculate throat frequency + initialFrequency = throatFormants5_29[pos]; + if(initialFrequency != 0) newFrequency = trans(throat, initialFrequency); + freq2data[pos] = newFrequency; + pos++; + } + +//pos39059: + // recalculate formant frequencies 48..53 + pos = 48; + Y = 0; + while(pos != 54) + { + // recalculate F1 (mouth formant) + initialFrequency = mouthFormants48_53[Y]; + newFrequency = trans(mouth, initialFrequency); + freq1data[pos] = newFrequency; + + // recalculate F2 (throat formant) + initialFrequency = throatFormants48_53[Y]; + newFrequency = trans(throat, initialFrequency); + freq2data[pos] = newFrequency; + Y++; + pos++; + } +} + + +//return = (mem39212*mem39213) >> 1 +unsigned char trans(unsigned char mem39212, unsigned char mem39213) +{ + //pos39008: + unsigned char carry; + int temp; + unsigned char mem39214, mem39215; + A = 0; + mem39215 = 0; + mem39214 = 0; + X = 8; + do + { + carry = mem39212 & 1; + mem39212 = mem39212 >> 1; + if (carry != 0) + { + /* + 39018: LSR 39212 + 39021: BCC 39033 + */ + carry = 0; + A = mem39215; + temp = (int)A + (int)mem39213; + A = A + mem39213; + if (temp > 255) carry = 1; + mem39215 = A; + } + temp = mem39215 & 1; + mem39215 = (mem39215 >> 1) | (carry?128:0); + carry = temp; + //39033: ROR 39215 + X--; + } while (X != 0); + temp = mem39214 & 128; + mem39214 = (mem39214 << 1) | (carry?1:0); + carry = temp; + temp = mem39215 & 128; + mem39215 = (mem39215 << 1) | (carry?1:0); + carry = temp; + + return mem39215; +} diff --git a/src/render.h b/components/ESPIDF-SAM/src/render.h similarity index 100% rename from src/render.h rename to components/ESPIDF-SAM/src/render.h diff --git a/src/sam.c b/components/ESPIDF-SAM/src/sam.c similarity index 97% rename from src/sam.c rename to components/ESPIDF-SAM/src/sam.c index c20d8dd..fea0db4 100644 --- a/src/sam.c +++ b/components/ESPIDF-SAM/src/sam.c @@ -468,13 +468,13 @@ int Parser1() // GET FIRST CHARACTER AT POSITION Y IN signInputTable // --> should change name to PhonemeNameTable1 - A = pgm_read_byte(signInputTable1+Y);//signInputTable1[Y]; + A = signInputTable1[Y];//signInputTable1[Y]; // FIRST CHARACTER MATCHES? if (A == sign1) { // GET THE CHARACTER FROM THE PhonemeSecondLetterTable - A = pgm_read_byte(signInputTable2+Y);//signInputTable2[Y]; + A = signInputTable2[Y];//signInputTable2[Y]; // NOT A SPECIAL AND MATCHES SECOND CHARACTER? if ((A != '*') && (A == sign2)) { @@ -505,10 +505,10 @@ int Parser1() Y = 0; pos41134: // DOES THE PHONEME IN THE TABLE END WITH '*'? - if (pgm_read_byte(signInputTable2+Y)/*signInputTable2[Y]*/ == '*') + if (signInputTable2[Y]/*signInputTable2[Y]*/ == '*') { // DOES THE FIRST CHARACTER MATCH THE FIRST LETTER OF THE PHONEME - if (pgm_read_byte(signInputTable1+Y)/*]signInputTable1[Y]*/ == sign1) + if (signInputTable1[Y]/*]signInputTable1[Y]*/ == sign1) { // SAVE THE POSITION AND MOVE AHEAD phonemeindex[position] = Y; @@ -530,7 +530,7 @@ int Parser1() Y = 8; // WALK BACK THROUGH TABLE LOOKING FOR A MATCH - while( (sign1 != pgm_read_byte(stressInputTable+Y)/*stressInputTable[Y]*/) && (Y>0)) + while( (sign1 != stressInputTable[Y]/*stressInputTable[Y]*/) && (Y>0)) { // DECREMENT INDEX Y--; @@ -564,10 +564,10 @@ void SetPhonemeLength() //41218: BMI 41229 if ((A == 0) || ((A&128) != 0)) { - phonemeLength[position] = pgm_read_byte(&phonemeLengthTable[phonemeindex[position]]); + phonemeLength[position] = phonemeLengthTable[phonemeindex[position]]; } else { - phonemeLength[position] = pgm_read_byte(&phonemeStressedLengthTable[phonemeindex[position]]); + phonemeLength[position] = phonemeStressedLengthTable[phonemeindex[position]]; } position++; } @@ -590,8 +590,8 @@ void Code41240() } else if ((flags[index]&1) == 0) { - Insert(pos+1, index+1, pgm_read_byte(&phonemeLengthTable[index+1]), stress[pos]); - Insert(pos+2, index+2, pgm_read_byte(&phonemeLengthTable[index+2]), stress[pos]); + Insert(pos+1, index+1, phonemeLengthTable[index+1], stress[pos]); + Insert(pos+2, index+2, phonemeLengthTable[index+2], stress[pos]); pos += 3; continue; } @@ -608,8 +608,8 @@ void Code41240() if ((A == 36) || (A == 37)) {pos++; continue;} // '/H' '/X' } - Insert(pos+1, index+1, pgm_read_byte(&phonemeLengthTable[index+1]), stress[pos]); - Insert(pos+2, index+2, pgm_read_byte(&phonemeLengthTable[index+2]), stress[pos]); + Insert(pos+1, index+1, phonemeLengthTable[index+1], stress[pos]); + Insert(pos+2, index+2, phonemeLengthTable[index+2], stress[pos]); pos += 3; }; diff --git a/src/sam.h b/components/ESPIDF-SAM/src/sam.h similarity index 100% rename from src/sam.h rename to components/ESPIDF-SAM/src/sam.h diff --git a/examples/Speak/Speak.ino b/examples/Speak/Speak.ino deleted file mode 100644 index 3f595ee..0000000 --- a/examples/Speak/Speak.ino +++ /dev/null @@ -1,20 +0,0 @@ -#include -#include -#include - -AudioOutputI2S *out = NULL; - -void setup() -{ - out = new AudioOutputI2S(); - out->begin(); -} - -void loop() -{ - ESP8266SAM *sam = new ESP8266SAM; - sam->Say(out, "Can you hear me now?"); - delay(500); - sam->Say(out, "I can't hear you!"); - delete sam; -} diff --git a/examples/SpeakNoDac/SpeakNoDac.ino b/examples/SpeakNoDac/SpeakNoDac.ino deleted file mode 100644 index 6951fe8..0000000 --- a/examples/SpeakNoDac/SpeakNoDac.ino +++ /dev/null @@ -1,20 +0,0 @@ -#include -#include -#include "AudioOutputI2SNoDAC.h" - -AudioOutputI2SNoDAC *out = NULL; - -void setup() -{ - out = new AudioOutputI2SNoDAC(); - out->begin(); -} - -void loop() -{ - ESP8266SAM *sam = new ESP8266SAM; - sam->Say(out, "Can you hear me now?"); - delay(500); - sam->Say(out, "I can't hear you!"); - delete sam; -} diff --git a/examples/remoteSAM/remoteSAM.ino b/examples/remoteSAM/remoteSAM.ino deleted file mode 100644 index c84272a..0000000 --- a/examples/remoteSAM/remoteSAM.ino +++ /dev/null @@ -1,149 +0,0 @@ -// In a webbrowser go to http://sam.local/say/{message} to make it speak -// ex: http://sam.local/say/hello world - -#include - -#if !defined(ESP8266) -void setup() { - Serial.begin(115200); - Serial.println("This example is only for the ESP8266"); -} - -void loop() { -} -#else - -#include -#include "AudioOutputI2SNoDAC.h" - -#include -#include -#include //Library for SSDP (Show ESP in Network on Windows) -#include //Library for WebServer -#include -#include - -AudioOutputI2SNoDAC *out = NULL; - -ESP8266WebServer server(80); //Web Server on port 80 -const char* NAME = "SAM"; -const char *ssid = ""; -const char *pass = ""; - -void setup() -{ - Serial.begin(115200); - out = new AudioOutputI2SNoDAC(); - out->begin(); - - WiFi.mode(WIFI_STA); - WiFi.begin(ssid, pass); - - while (WiFi.status() != WL_CONNECTED) { - delay(500); - Serial.print("."); - } - Serial.println(""); - - Serial.println("WiFi connected"); - Serial.println("IP address: "); - Serial.println(WiFi.localIP()); - - MDNS.begin(NAME); - MDNS.addService("http", "tcp", 80); - NBNS.begin(NAME); - - server.on(UriBraces("/say/{}"), []() { - String message_encoded = server.pathArg(0); - String message_decoded = urldecode(message_encoded); - const char* message = message_decoded.c_str(); - - Serial.println(message_encoded); - Serial.println(message_decoded); - Serial.println(message); - - ESP8266SAM *sam = new ESP8266SAM; - sam->Say(out, message); - delete sam; - server.send(200, "text/plain", "OK"); - }); - - server.on("/description.xml", HTTP_GET, []() { - SSDP.schema(server.client()); - }); - server.begin(); - ssdp(); -} - -void ssdp() { - //Simple Service Discovery Protocol : Display ESP in Windows Network Tab - SSDP.setSchemaURL("description.xml"); - SSDP.setHTTPPort(80); - SSDP.setName(NAME); - SSDP.setDeviceType("upnp: rootdevice"); - SSDP.setSerialNumber("000000000001"); - SSDP.setURL("/say/connected"); - SSDP.setModelName("ESP8266SAM"); - SSDP.setModelNumber("0000000000001"); - SSDP.setModelURL("https://github.com/earlephilhower/ESP8266SAM"); - SSDP.setManufacturer("earlephilhower"); - SSDP.setManufacturerURL("https://github.com/earlephilhower/"); - SSDP.begin(); -} - -void loop() { - server.handleClient(); -} - -char* string2char(String command) { - if (command.length() != 0) { - char *p = const_cast(command.c_str()); - return p; - } else { - return ""; - } -} - -unsigned char h2int(char c) { - if (c >= '0' && c <= '9') { - return ((unsigned char)c - '0'); - } - if (c >= 'a' && c <= 'f') { - return ((unsigned char)c - 'a' + 10); - } - if (c >= 'A' && c <= 'F') { - return ((unsigned char)c - 'A' + 10); - } - return (0); -} - -String urldecode(String str) -{ - - String encodedString = ""; - char c; - char code0; - char code1; - for (int i = 0; i < str.length(); i++) { - c = str.charAt(i); - if (c == '+') { - encodedString += ' '; - } else if (c == '%') { - i++; - code0 = str.charAt(i); - i++; - code1 = str.charAt(i); - c = (h2int(code0) << 4) | h2int(code1); - encodedString += c; - } else { - - encodedString += c; - } - - yield(); - } - - return encodedString; -} - -#endif diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt new file mode 100644 index 0000000..13744e7 --- /dev/null +++ b/main/CMakeLists.txt @@ -0,0 +1,4 @@ +idf_component_register(SRCS "main.cpp" + INCLUDE_DIRS "${include_dirs}") + +component_compile_options(-Wno-error=format= -Wno-format) \ No newline at end of file diff --git a/main/main.cpp b/main/main.cpp new file mode 100644 index 0000000..0e0754e --- /dev/null +++ b/main/main.cpp @@ -0,0 +1,47 @@ +/* Hello World Example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#include +#include "sdkconfig.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_system.h" +#include "esp_spi_flash.h" +#include + +extern "C" { + #include "sound.h" +} + +bool output_audio(void *cbdata, int16_t* b) { + size_t bytes_written; + i2s_write((i2s_port_t)0, b, 2, &bytes_written, portMAX_DELAY); + return true; +} + +extern "C" void app_main() +{ + + size_t bytes_written; + int i2s_read_len = EXAMPLE_I2S_READ_LEN; + uint8_t* i2s_write_buff = (uint8_t*) calloc(i2s_read_len, sizeof(char)); + + example_i2s_init(); + example_set_file_play_mode(); + + ESP8266SAM *sam = new ESP8266SAM(output_audio); + sam->SetSpeed(120); + sam->SetPitch(100); + sam->SetThroat(100); + sam->SetMouth(200); + sam->Say("I am Dalek, you are the Doctor, you must be exterminated!"); + printf("Done\n"); + vTaskDelay(500 / portTICK_RATE_MS); + sam->Say("EXTERMINATE!"); + delete sam; +} diff --git a/main/sound.h b/main/sound.h new file mode 100644 index 0000000..acb7f19 --- /dev/null +++ b/main/sound.h @@ -0,0 +1,97 @@ +#include "driver/i2s.h" +#include "driver/adc.h" +#include "esp_adc_cal.h" +#include "esp_rom_sys.h" + +//i2s number +#define EXAMPLE_I2S_NUM (i2s_port_t)(0) +//i2s sample rate +#define EXAMPLE_I2S_SAMPLE_RATE (22050) +//i2s data bits +#define EXAMPLE_I2S_SAMPLE_BITS (16) +//enable display buffer for debug +#define EXAMPLE_I2S_BUF_DEBUG (0) +//I2S read buffer length +#define EXAMPLE_I2S_READ_LEN (16 * 1024) +//I2S data format +#define EXAMPLE_I2S_FORMAT (I2S_CHANNEL_FMT_RIGHT_LEFT) +//I2S channel number +#define EXAMPLE_I2S_CHANNEL_NUM ((EXAMPLE_I2S_FORMAT < I2S_CHANNEL_FMT_ONLY_RIGHT) ? (2) : (1)) +//I2S built-in ADC unit +#define I2S_ADC_UNIT ADC_UNIT_1 +//I2S built-in ADC channel +#define I2S_ADC_CHANNEL ADC1_CHANNEL_0 + +/** + * @brief I2S ADC/DAC mode init. + */ +void example_i2s_init(void) +{ + i2s_config_t i2s_config = { + .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN | I2S_MODE_ADC_BUILT_IN), + .sample_rate = EXAMPLE_I2S_SAMPLE_RATE, + .bits_per_sample = (i2s_bits_per_sample_t)EXAMPLE_I2S_SAMPLE_BITS, + .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, + .communication_format = I2S_COMM_FORMAT_STAND_MSB, + .intr_alloc_flags = 0, + .dma_buf_count = 6, + .dma_buf_len = 256, + .use_apll = 1, + }; + //install and start i2s driver + i2s_driver_install((i2s_port_t)0, &i2s_config, 0, NULL); + //init DAC pad + i2s_set_dac_mode(I2S_DAC_CHANNEL_BOTH_EN); + //init ADC pad + i2s_set_adc_mode(I2S_ADC_UNIT, I2S_ADC_CHANNEL); +} + +/** + * @brief Set i2s clock for example audio file + */ +void example_set_file_play_mode(void) +{ + i2s_set_clk(EXAMPLE_I2S_NUM, EXAMPLE_I2S_SAMPLE_RATE, EXAMPLE_I2S_SAMPLE_BITS, (i2s_channel_t)1); +} + +/** + * @brief debug buffer data + */ +void example_disp_buf(uint8_t* buf, int length) +{ +#if EXAMPLE_I2S_BUF_DEBUG + printf("======\n"); + for (int i = 0; i < length; i++) { + printf("%02x ", buf[i]); + if ((i + 1) % 8 == 0) { + printf("\n"); + } + } + printf("======\n"); +#endif +} + +/** + * @brief Scale data to 16bit/32bit for I2S DMA output. + * DAC can only output 8bit data value. + * I2S DMA will still send 16 bit or 32bit data, the highest 8bit contains DAC data. + */ +int example_i2s_dac_data_scale(uint8_t* d_buff, uint8_t* s_buff, uint32_t len) +{ + uint32_t j = 0; +#if (EXAMPLE_I2S_SAMPLE_BITS == 16) + for (int i = 0; i < len; i++) { + d_buff[j++] = 0; + d_buff[j++] = s_buff[i]; + } + return (len * 2); +#else + for (int i = 0; i < len; i++) { + d_buff[j++] = 0; + d_buff[j++] = 0; + d_buff[j++] = 0; + d_buff[j++] = s_buff[i]; + } + return (len * 4); +#endif +} \ No newline at end of file diff --git a/src/reciter.c b/src/reciter.c deleted file mode 100644 index f949ca5..0000000 --- a/src/reciter.c +++ /dev/null @@ -1,552 +0,0 @@ -#include -#include -#include "reciter.h" -#include "ReciterTabs.h" -#include "esp8266sam_debug.h" -#include "SamData.h" - -unsigned char A, X, Y; -//extern int debug; - -#define inputtemp (samdata->reciter.inputtemp) - -void Code37055(unsigned char mem59) -{ - X = mem59; - X--; - A = inputtemp[X]; - Y = A; - A = pgm_read_byte(tab36376+Y); //tab36376[Y]; - return; -} - -void Code37066(unsigned char mem58) -{ - X = mem58; - X++; - A = inputtemp[X]; - Y = A; - A = pgm_read_byte(tab36376+Y); //tab36376[Y]; -} - -unsigned char GetRuleByte(unsigned short mem62, unsigned char Y) -{ - unsigned int address = mem62; - - if (mem62 >= 37541) - { - address -= 37541; - return pgm_read_byte(rules2+address+Y); //rules2[address+Y]; - } - address -= 32000; - return pgm_read_byte(rules+address+Y); //rules[address+Y]; -} - -int TextToPhonemes(char *input) // Code36484 -{ - //unsigned char *tab39445 = &mem[39445]; //input and output - //unsigned char mem29; - unsigned char mem56; //output position for phonemes - unsigned char mem57; - unsigned char mem58; - unsigned char mem59; - unsigned char mem60; - unsigned char mem61; - unsigned short mem62; // memory position of current rule - - unsigned char mem64; // position of '=' or current character - unsigned char mem65; // position of ')' - unsigned char mem66; // position of '(' - unsigned char mem36653; - - inputtemp[0] = 32; - - // secure copy of input - // because input will be overwritten by phonemes - X = 1; - Y = 0; - do - { - //pos36499: - A = input[Y] & 127; - if ( A >= 112) A = A & 95; - else if ( A >= 96) A = A & 79; - - inputtemp[X] = A; - X++; - Y++; - } while (Y != 255); - - - X = 255; - inputtemp[X] = 27; - mem61 = 255; - - -pos36550: - A = 255; - mem56 = 255; - - -pos36554: - while(1) - { - mem61++; - X = mem61; - A = inputtemp[X]; - mem64 = A; - if (A == '[') - { - mem56++; - X = mem56; - A = 155; - input[X] = 155; - //goto pos36542; - // Code39771(); //Code39777(); - return 1; - } - - //pos36579: - if (A != '.') break; - X++; - Y = inputtemp[X]; - A = pgm_read_byte(tab36376+Y)/*tab36376[Y]*/ & 1; - if(A != 0) break; - mem56++; - X = mem56; - A = '.'; - input[X] = '.'; - } //while - - - //pos36607: - A = mem64; - Y = A; - A = pgm_read_byte(tab36376+A); //tab36376[A]; - mem57 = A; - if((A&2) != 0) - { - mem62 = 37541; - goto pos36700; - } - - //pos36630: - A = mem57; - if(A != 0) goto pos36677; - A = 32; - inputtemp[X] = ' '; - mem56++; - X = mem56; - if (X > 120) goto pos36654; - input[X] = A; - goto pos36554; - - // ----- - - //36653 is unknown. Contains position - -pos36654: - input[X] = 155; - A = mem61; - mem36653 = A; - // mem29 = A; // not used - // Code36538(); das ist eigentlich - return 1; - //Code39771(); - //go on if there is more input ??? - mem61 = mem36653; - goto pos36550; - -pos36677: - A = mem57 & 128; - if(A == 0) - { - //36683: BRK - return 0; - } - - // go to the right rules for this character. - X = mem64 - 'A'; - mem62 = pgm_read_byte(&tab37489[X]) | (pgm_read_byte(&tab37515[X])<<8); - - // ------------------------------------- - // go to next rule - // ------------------------------------- - -pos36700: - - // find next rule - Y = 0; - do - { - mem62 += 1; - A = GetRuleByte(mem62, Y); - } while ((A & 128) == 0); - Y++; - - //pos36720: - // find '(' - while(1) - { - A = GetRuleByte(mem62, Y); - if (A == '(') break; - Y++; - } - mem66 = Y; - - //pos36732: - // find ')' - do - { - Y++; - A = GetRuleByte(mem62, Y); - } while(A != ')'); - mem65 = Y; - - //pos36741: - // find '=' - do - { - Y++; - A = GetRuleByte(mem62, Y); - A = A & 127; - } while (A != '='); - mem64 = Y; - - X = mem61; - mem60 = X; - - // compare the string within the bracket - Y = mem66; - Y++; - //pos36759: - while(1) - { - mem57 = inputtemp[X]; - A = GetRuleByte(mem62, Y); - if (A != mem57) goto pos36700; - Y++; - if(Y == mem65) break; - X++; - mem60 = X; - } - -// the string in the bracket is correct - -//pos36787: - A = mem61; - mem59 = mem61; - -pos36791: - while(1) - { - mem66--; - Y = mem66; - A = GetRuleByte(mem62, Y); - mem57 = A; - //36800: BPL 36805 - if ((A & 128) != 0) goto pos37180; - X = A & 127; - A = pgm_read_byte(tab36376+X)/*tab36376[X]*/ & 128; - if (A == 0) break; - X = mem59-1; - A = inputtemp[X]; - if (A != mem57) goto pos36700; - mem59 = X; - } - -//pos36833: - A = mem57; - if (A == ' ') goto pos36895; - if (A == '#') goto pos36910; - if (A == '.') goto pos36920; - if (A == '&') goto pos36935; - if (A == '@') goto pos36967; - if (A == '^') goto pos37004; - if (A == '+') goto pos37019; - if (A == ':') goto pos37040; - // Code42041(); //Error - //36894: BRK - return 0; - - // -------------- - -pos36895: - Code37055(mem59); - A = A & 128; - if(A != 0) goto pos36700; -pos36905: - mem59 = X; - goto pos36791; - - // -------------- - -pos36910: - Code37055(mem59); - A = A & 64; - if(A != 0) goto pos36905; - goto pos36700; - - // -------------- - - -pos36920: - Code37055(mem59); - A = A & 8; - if(A == 0) goto pos36700; -pos36930: - mem59 = X; - goto pos36791; - - // -------------- - -pos36935: - Code37055(mem59); - A = A & 16; - if(A != 0) goto pos36930; - A = inputtemp[X]; - if (A != 72) goto pos36700; - X--; - A = inputtemp[X]; - if ((A == 67) || (A == 83)) goto pos36930; - goto pos36700; - - // -------------- - -pos36967: - Code37055(mem59); - A = A & 4; - if(A != 0) goto pos36930; - A = inputtemp[X]; - if (A != 72) goto pos36700; - if ((A != 84) && (A != 67) && (A != 83)) goto pos36700; - mem59 = X; - goto pos36791; - - // -------------- - - -pos37004: - Code37055(mem59); - A = A & 32; - if(A == 0) goto pos36700; - -pos37014: - mem59 = X; - goto pos36791; - - // -------------- - -pos37019: - X = mem59; - X--; - A = inputtemp[X]; - if ((A == 'E') || (A == 'I') || (A == 'Y')) goto pos37014; - goto pos36700; - // -------------- - -pos37040: - Code37055(mem59); - A = A & 32; - if(A == 0) goto pos36791; - mem59 = X; - goto pos37040; - -//--------------------------------------- - - -pos37077: - X = mem58+1; - A = inputtemp[X]; - if (A != 'E') goto pos37157; - X++; - Y = inputtemp[X]; - X--; - A = pgm_read_byte(tab36376+Y)/*tab36376[Y]*/ & 128; - if(A == 0) goto pos37108; - X++; - A = inputtemp[X]; - if (A != 'R') goto pos37113; -pos37108: - mem58 = X; - goto pos37184; -pos37113: - if ((A == 83) || (A == 68)) goto pos37108; // 'S' 'D' - if (A != 76) goto pos37135; // 'L' - X++; - A = inputtemp[X]; - if (A != 89) goto pos36700; - goto pos37108; - -pos37135: - if (A != 70) goto pos36700; - X++; - A = inputtemp[X]; - if (A != 85) goto pos36700; - X++; - A = inputtemp[X]; - if (A == 76) goto pos37108; - goto pos36700; - -pos37157: - if (A != 73) goto pos36700; - X++; - A = inputtemp[X]; - if (A != 78) goto pos36700; - X++; - A = inputtemp[X]; - if (A == 71) goto pos37108; - //pos37177: - goto pos36700; - - // ----------------------------------------- - -pos37180: - - A = mem60; - mem58 = A; - -pos37184: - Y = mem65 + 1; - - //37187: CPY 64 - // if(? != 0) goto pos37194; - if(Y == mem64) goto pos37455; - mem65 = Y; - //37196: LDA (62),y - A = GetRuleByte(mem62, Y); - mem57 = A; - X = A; - A = pgm_read_byte(tab36376+X)/*tab36376[X]*/ & 128; - if(A == 0) goto pos37226; - X = mem58+1; - A = inputtemp[X]; - if (A != mem57) goto pos36700; - mem58 = X; - goto pos37184; -pos37226: - A = mem57; - if (A == 32) goto pos37295; // ' ' - if (A == 35) goto pos37310; // '#' - if (A == 46) goto pos37320; // '.' - if (A == 38) goto pos37335; // '&' - if (A == 64) goto pos37367; // '' - if (A == 94) goto pos37404; // '' - if (A == 43) goto pos37419; // '+' - if (A == 58) goto pos37440; // ':' - if (A == 37) goto pos37077; // '%' - //pos37291: - // Code42041(); //Error - //37294: BRK - return 0; - - // -------------- -pos37295: - Code37066(mem58); - A = A & 128; - if(A != 0) goto pos36700; -pos37305: - mem58 = X; - goto pos37184; - - // -------------- - -pos37310: - Code37066(mem58); - A = A & 64; - if(A != 0) goto pos37305; - goto pos36700; - - // -------------- - - -pos37320: - Code37066(mem58); - A = A & 8; - if(A == 0) goto pos36700; - -pos37330: - mem58 = X; - goto pos37184; - - // -------------- - -pos37335: - Code37066(mem58); - A = A & 16; - if(A != 0) goto pos37330; - A = inputtemp[X]; - if (A != 72) goto pos36700; - X++; - A = inputtemp[X]; - if ((A == 67) || (A == 83)) goto pos37330; - goto pos36700; - - // -------------- - - -pos37367: - Code37066(mem58); - A = A & 4; - if(A != 0) goto pos37330; - A = inputtemp[X]; - if (A != 72) goto pos36700; - if ((A != 84) && (A != 67) && (A != 83)) goto pos36700; - mem58 = X; - goto pos37184; - - // -------------- - -pos37404: - Code37066(mem58); - A = A & 32; - if(A == 0) goto pos36700; -pos37414: - mem58 = X; - goto pos37184; - - // -------------- - -pos37419: - X = mem58; - X++; - A = inputtemp[X]; - if ((A == 69) || (A == 73) || (A == 89)) goto pos37414; - goto pos36700; - -// ---------------------- - -pos37440: - - Code37066(mem58); - A = A & 32; - if(A == 0) goto pos37184; - mem58 = X; - goto pos37440; -pos37455: - Y = mem64; - mem61 = mem60; - - if (DEBUG_ESP8266SAM_LIB) - PrintRule(mem62); - -pos37461: - //37461: LDA (62),y - A = GetRuleByte(mem62, Y); - mem57 = A; - A = A & 127; - if (A != '=') - { - mem56++; - X = mem56; - input[X] = A; - } - - //37478: BIT 57 - //37480: BPL 37485 //not negative flag - if ((mem57 & 128) == 0) goto pos37485; //??? - goto pos36554; -pos37485: - Y++; - goto pos37461; -} diff --git a/src/render.c b/src/render.c deleted file mode 100644 index 4cbabb1..0000000 --- a/src/render.c +++ /dev/null @@ -1,1061 +0,0 @@ -//#include -#include -#include // abs() - -#include "render.h" -#include "RenderTabs.h" - -#include "esp8266sam_debug.h" -#include -#include "SamData.h" - -unsigned char wait1 = 7; -unsigned char wait2 = 6; - -extern unsigned char A, X, Y; -extern unsigned char mem44; -extern unsigned char mem47; -extern unsigned char mem49; -extern unsigned char mem39; -extern unsigned char mem50; -extern unsigned char mem51; -extern unsigned char mem53; -extern unsigned char mem56; - -extern unsigned char speed; -extern unsigned char pitch; -extern int singmode; - -#define phonemeIndexOutput (samdata->sam.phonemeIndexOutput) -#define stressOutput (samdata->sam.stressOutput) -#define phonemeLengthOutput (samdata->sam.phonemeLengthOutput) -#define pitches (samdata->render.pitches) -#define frequency1 (samdata->render.frequency1) -#define frequency2 (samdata->render.frequency2) -#define frequency3 (samdata->render.frequency3) -#define amplitude1 (samdata->render.amplitude1) -#define amplitude2 (samdata->render.amplitude2) -#define amplitude3 (samdata->render.amplitude3) -#define sampledConsonantFlag (samdata->render.sampledConsonantFlag) - -void AddInflection(unsigned char mem48, unsigned char phase1); -unsigned char trans(unsigned char mem39212, unsigned char mem39213); - - -// contains the final soundbuffer -extern int bufferpos; -//extern char *buffer; - -#ifndef ESP8266 -static void yield() { /* NOOP */ } -#endif - -//timetable for more accurate c64 simulation -const unsigned char timetable[5][5] PROGMEM = -{ - {162, 167, 167, 127, 128}, - {226, 60, 60, 0, 0}, - {225, 60, 59, 0, 0}, - {200, 0, 0, 54, 55}, - {199, 0, 0, 54, 54} -}; - -extern void (*outcb)(void *, unsigned char); -extern void *outcbdata; -static unsigned char oldtimetableindex = 0; -static unsigned char lastAry[5]; -void Output8BitAry(int index, unsigned char ary[5]) -{ - int newbufferpos = bufferpos + pgm_read_byte(&timetable[oldtimetableindex][index]); - int bp0 = bufferpos / 50; - int bp1 = newbufferpos / 50; - int k=0; - for (int i=bp0; i X -// unvoiced 1 bit -> 5 -// -// voiced 0 bit -> 6 -// voiced 1 bit -> 24 -// -// Where X is a value from the table: -// -// { 0x18, 0x1A, 0x17, 0x17, 0x17 }; -// -// The index into this table is determined by masking off the lower -// 3 bits from the SampledPhonemesTable: -// -// index = (SampledPhonemesTable[i] & 7) - 1; -// -// For voices samples, samples are interleaved between voiced output. - - -// Code48227() -void RenderSample(unsigned char *mem66) -{ - int tempA; - // current phoneme's index - mem49 = Y; - - // mask low three bits and subtract 1 get value to - // convert 0 bits on unvoiced samples. - A = mem39&7; - X = A-1; - - // store the result - mem56 = X; - - // determine which offset to use from table { 0x18, 0x1A, 0x17, 0x17, 0x17 } - // T, S, Z 0 0x18 - // CH, J, SH, ZH 1 0x1A - // P, F*, V, TH, DH 2 0x17 - // /H 3 0x17 - // /X 4 0x17 - - // get value from the table - mem53 = pgm_read_byte(tab48426+X); //tab48426[X]; - mem47 = X; //46016+mem[56]*256 - - // voiced sample? - A = mem39 & 248; - if(A == 0) - { - // voiced phoneme: Z*, ZH, V*, DH - Y = mem49; - A = pitches[mem49] >> 4; - - // jump to voiced portion - goto pos48315; - } - - Y = A ^ 255; -pos48274: - - // step through the 8 bits in the sample - mem56 = 8; - - // get the next sample from the table - // mem47*256 = offset to start of samples - A = pgm_read_byte(sampleTable + mem47*256+Y); // sampleTable[mem47*256+Y]; -pos48280: - - // left shift to get the high bit - tempA = A; - A = A << 1; - //48281: BCC 48290 - - // bit not set? - if ((tempA & 128) == 0) - { - // convert the bit to value from table - X = mem53; - //mem[54296] = X; - // output the byte - Output8Bit(1, (X&0xf)*16); - // if X != 0, exit loop - if(X != 0) goto pos48296; - } - - // output a 5 for the on bit - Output8Bit(2, 5*16); - - //48295: NOP -pos48296: - - X = 0; - - // decrement counter - mem56--; - - // if not done, jump to top of loop - if (mem56 != 0) goto pos48280; - - // increment position - Y++; - if (Y != 0) goto pos48274; - - // restore values and return - mem44 = 1; - Y = mem49; - return; - - - unsigned char phase1; - -pos48315: -// handle voiced samples here - - // number of samples? - phase1 = A ^ 255; - - Y = *mem66; - do - { - //pos48321: - - // shift through all 8 bits - mem56 = 8; - //A = Read(mem47, Y); - - // fetch value from table - A = pgm_read_byte(sampleTable + mem47*256+Y); //sampleTable[mem47*256+Y]; - - // loop 8 times - //pos48327: - do - { - //48327: ASL A - //48328: BCC 48337 - - // left shift and check high bit - tempA = A; - A = A << 1; - if ((tempA & 128) != 0) - { - // if bit set, output 26 - X = 26; - Output8Bit(3, (X&0xf)*16); - } else - { - //timetable 4 - // bit is not set, output a 6 - X=6; - Output8Bit(4, (X&0xf)*16); - } - - mem56--; - } while(mem56 != 0); - - // move ahead in the table - Y++; - - // continue until counter done - phase1++; - - } while (phase1 != 0); - // if (phase1 != 0) goto pos48321; - - // restore values and return - A = 1; - mem44 = 1; - *mem66 = Y; - Y = mem49; - return; -} - - - -// RENDER THE PHONEMES IN THE LIST -// -// The phoneme list is converted into sound through the steps: -// -// 1. Copy each phoneme number of times into the frames list, -// where each frame represents 10 milliseconds of sound. -// -// 2. Determine the transitions lengths between phonemes, and linearly -// interpolate the values across the frames. -// -// 3. Offset the pitches by the fundamental frequency. -// -// 4. Render the each frame. - - - -//void Code47574() -void Render() -{ - unsigned char phase1 = 0; //mem43 - unsigned char phase2 = 0; - unsigned char phase3 = 0; - unsigned char mem66 = 0; - unsigned char mem38 = 0; - unsigned char mem40 = 0; - unsigned char speedcounter = 0; //mem45 - unsigned char mem48 = 0; - int i; - if (phonemeIndexOutput[0] == 255) return; //exit if no data - - A = 0; - X = 0; - mem44 = 0; - - -// CREATE FRAMES -// -// The length parameter in the list corresponds to the number of frames -// to expand the phoneme to. Each frame represents 10 milliseconds of time. -// So a phoneme with a length of 7 = 7 frames = 70 milliseconds duration. -// -// The parameters are copied from the phoneme to the frame verbatim. - - -// pos47587: -do -{ - // get the index - Y = mem44; - // get the phoneme at the index - A = phonemeIndexOutput[mem44]; - mem56 = A; - - // if terminal phoneme, exit the loop - if (A == 255) break; - - // period phoneme *. - if (A == 1) - { - // add rising inflection - A = 1; - mem48 = 1; - //goto pos48376; - AddInflection(mem48, phase1); - } - /* - if (A == 2) goto pos48372; - */ - - // question mark phoneme? - if (A == 2) - { - // create falling inflection - mem48 = 255; - AddInflection(mem48, phase1); - } - // pos47615: - - // get the stress amount (more stress = higher pitch) - phase1 = pgm_read_byte(tab47492 + stressOutput[Y] + 1); // tab47492[stressOutput[Y] + 1]; - - // get number of frames to write - phase2 = phonemeLengthOutput[Y]; - Y = mem56; - - // copy from the source to the frames list - do - { - frequency1[X] = freq1data[Y]; // F1 frequency - frequency2[X] = freq2data[Y]; // F2 frequency - frequency3[X] = freq3data[Y]; // F3 frequency - amplitude1[X] = pgm_read_byte(&l1data[Y]); // F1 amplitude - amplitude2[X] = pgm_read_byte(&l2data[Y]); // F2 amplitude - amplitude3[X] = pgm_read_byte(&l3data[Y]); // F3 amplitude - sampledConsonantFlag[X] = pgm_read_byte(&sampledConsonantFlags[Y]); // phoneme data for sampled consonants - pitches[X] = pitch + phase1; // pitch - X++; - phase2--; - } while(phase2 != 0); - mem44++; -} while(mem44 != 0); -yield(); -if (DEBUG_ESP8266SAM_LIB) -{ - PrintOutput(sampledConsonantFlag, frequency1, frequency2, frequency3, amplitude1, amplitude2, amplitude3, pitches); -} -// ------------------- -//pos47694: - -// CREATE TRANSITIONS -// -// Linear transitions are now created to smoothly connect the -// end of one sustained portion of a phoneme to the following -// phoneme. -// -// To do this, three tables are used: -// -// Table Purpose -// ========= ================================================== -// blendRank Determines which phoneme's blend values are used. -// -// blendOut The number of frames at the end of the phoneme that -// will be used to transition to the following phoneme. -// -// blendIn The number of frames of the following phoneme that -// will be used to transition into that phoneme. -// -// In creating a transition between two phonemes, the phoneme -// with the HIGHEST rank is used. Phonemes are ranked on how much -// their identity is based on their transitions. For example, -// vowels are and diphthongs are identified by their sustained portion, -// rather than the transitions, so they are given low values. In contrast, -// stop consonants (P, B, T, K) and glides (Y, L) are almost entirely -// defined by their transitions, and are given high rank values. -// -// Here are the rankings used by SAM: -// -// Rank Type Phonemes -// 2 All vowels IY, IH, etc. -// 5 Diphthong endings YX, WX, ER -// 8 Terminal liquid consonants LX, WX, YX, N, NX -// 9 Liquid consonants L, RX, W -// 10 Glide R, OH -// 11 Glide WH -// 18 Voiceless fricatives S, SH, F, TH -// 20 Voiced fricatives Z, ZH, V, DH -// 23 Plosives, stop consonants P, T, K, KX, DX, CH -// 26 Stop consonants J, GX, B, D, G -// 27-29 Stop consonants (internal) ** -// 30 Unvoiced consonants /H, /X and Q* -// 160 Nasal M -// -// To determine how many frames to use, the two phonemes are -// compared using the blendRank[] table. The phoneme with the -// higher rank is selected. In case of a tie, a blend of each is used: -// -// if blendRank[phoneme1] == blendRank[phomneme2] -// // use lengths from each phoneme -// outBlendFrames = outBlend[phoneme1] -// inBlendFrames = outBlend[phoneme2] -// else if blendRank[phoneme1] > blendRank[phoneme2] -// // use lengths from first phoneme -// outBlendFrames = outBlendLength[phoneme1] -// inBlendFrames = inBlendLength[phoneme1] -// else -// // use lengths from the second phoneme -// // note that in and out are SWAPPED! -// outBlendFrames = inBlendLength[phoneme2] -// inBlendFrames = outBlendLength[phoneme2] -// -// Blend lengths can't be less than zero. -// -// Transitions are assumed to be symetrical, so if the transition -// values for the second phoneme are used, the inBlendLength and -// outBlendLength values are SWAPPED. -// -// For most of the parameters, SAM interpolates over the range of the last -// outBlendFrames-1 and the first inBlendFrames. -// -// The exception to this is the Pitch[] parameter, which is interpolates the -// pitch from the CENTER of the current phoneme to the CENTER of the next -// phoneme. -// -// Here are two examples. First, For example, consider the word "SUN" (S AH N) -// -// Phoneme Duration BlendWeight OutBlendFrames InBlendFrames -// S 2 18 1 3 -// AH 8 2 4 4 -// N 7 8 1 2 -// -// The formant transitions for the output frames are calculated as follows: -// -// flags ampl1 freq1 ampl2 freq2 ampl3 freq3 pitch -// ------------------------------------------------ -// S -// 241 0 6 0 73 0 99 61 Use S (weight 18) for transition instead of AH (weight 2) -// 241 0 6 0 73 0 99 61 <-- (OutBlendFrames-1) = (1-1) = 0 frames -// AH -// 0 2 10 2 66 0 96 59 * <-- InBlendFrames = 3 frames -// 0 4 14 3 59 0 93 57 * -// 0 8 18 5 52 0 90 55 * -// 0 15 22 9 44 1 87 53 -// 0 15 22 9 44 1 87 53 -// 0 15 22 9 44 1 87 53 Use N (weight 8) for transition instead of AH (weight 2). -// 0 15 22 9 44 1 87 53 Since N is second phoneme, reverse the IN and OUT values. -// 0 11 17 8 47 1 98 56 * <-- (InBlendFrames-1) = (2-1) = 1 frames -// N -// 0 8 12 6 50 1 109 58 * <-- OutBlendFrames = 1 -// 0 5 6 5 54 0 121 61 -// 0 5 6 5 54 0 121 61 -// 0 5 6 5 54 0 121 61 -// 0 5 6 5 54 0 121 61 -// 0 5 6 5 54 0 121 61 -// 0 5 6 5 54 0 121 61 -// -// Now, consider the reverse "NUS" (N AH S): -// -// flags ampl1 freq1 ampl2 freq2 ampl3 freq3 pitch -// ------------------------------------------------ -// N -// 0 5 6 5 54 0 121 61 -// 0 5 6 5 54 0 121 61 -// 0 5 6 5 54 0 121 61 -// 0 5 6 5 54 0 121 61 -// 0 5 6 5 54 0 121 61 -// 0 5 6 5 54 0 121 61 Use N (weight 8) for transition instead of AH (weight 2) -// 0 5 6 5 54 0 121 61 <-- (OutBlendFrames-1) = (1-1) = 0 frames -// AH -// 0 8 11 6 51 0 110 59 * <-- InBlendFrames = 2 -// 0 11 16 8 48 0 99 56 * -// 0 15 22 9 44 1 87 53 Use S (weight 18) for transition instead of AH (weight 2) -// 0 15 22 9 44 1 87 53 Since S is second phoneme, reverse the IN and OUT values. -// 0 9 18 5 51 1 90 55 * <-- (InBlendFrames-1) = (3-1) = 2 -// 0 4 14 3 58 1 93 57 * -// S -// 241 2 10 2 65 1 96 59 * <-- OutBlendFrames = 1 -// 241 0 6 0 73 0 99 61 - - A = 0; - mem44 = 0; - mem49 = 0; // mem49 starts at as 0 - X = 0; - while(1) //while No. 1 - { - - // get the current and following phoneme - Y = phonemeIndexOutput[X]; - A = phonemeIndexOutput[X+1]; - X++; - - // exit loop at end token - if (A == 255) break;//goto pos47970; - - - // get the ranking of each phoneme - X = A; - mem56 = pgm_read_byte(blendRank+A); //blendRank[A]; - A = pgm_read_byte(blendRank+Y); //blendRank[Y]; - - // compare the rank - lower rank value is stronger - if (A == mem56) - { - // same rank, so use out blend lengths from each phoneme - phase1 = pgm_read_byte(outBlendLength+Y);//outBlendLength[Y]; - phase2 = pgm_read_byte(outBlendLength+X);//outBlendLength[X]; - } else - if (A < mem56) - { - // first phoneme is stronger, so us it's blend lengths - phase1 = pgm_read_byte(inBlendLength+X);//inBlendLength[X]; - phase2 = pgm_read_byte(outBlendLength+X);//outBlendLength[X]; - } else - { - // second phoneme is stronger, so use it's blend lengths - // note the out/in are swapped - phase1 = pgm_read_byte(outBlendLength+Y);//outBlendLength[Y]; - phase2 = pgm_read_byte(inBlendLength+Y);//inBlendLength[Y]; - } - - Y = mem44; - A = mem49 + phonemeLengthOutput[mem44]; // A is mem49 + length - mem49 = A; // mem49 now holds length + position - A = A + phase2; //Maybe Problem because of carry flag - - //47776: ADC 42 - speedcounter = A; - mem47 = 168; - phase3 = mem49 - phase1; // what is mem49 - A = phase1 + phase2; // total transition? - mem38 = A; - - X = A; - X -= 2; - if ((X & 128) == 0) - do //while No. 2 - { - //pos47810: - - // mem47 is used to index the tables: - // 168 pitches[] - // 169 frequency1 - // 170 frequency2 - // 171 frequency3 - // 172 amplitude1 - // 173 amplitude2 - // 174 amplitude3 - - mem40 = mem38; - - if (mem47 == 168) // pitch - { - - // unlike the other values, the pitches[] interpolates from - // the middle of the current phoneme to the middle of the - // next phoneme - - unsigned char mem36, mem37; - // half the width of the current phoneme - mem36 = phonemeLengthOutput[mem44] >> 1; - // half the width of the next phoneme - mem37 = phonemeLengthOutput[mem44+1] >> 1; - // sum the values - mem40 = mem36 + mem37; // length of both halves - mem37 += mem49; // center of next phoneme - mem36 = mem49 - mem36; // center index of current phoneme - A = Read(mem47, mem37); // value at center of next phoneme - end interpolation value - //A = mem[address]; - - Y = mem36; // start index of interpolation - mem53 = A - Read(mem47, mem36); // value to center of current phoneme - } else - { - // value to interpolate to - A = Read(mem47, speedcounter); - // position to start interpolation from - Y = phase3; - // value to interpolate from - mem53 = A - Read(mem47, phase3); - } - - //Code47503(mem40); - // ML : Code47503 is division with remainder, and mem50 gets the sign - - // calculate change per frame - signed char m53 = (signed char)mem53; - mem50 = mem53 & 128; - unsigned char m53abs = abs(m53); - mem51 = m53abs % mem40; //abs((char)m53) % mem40; - mem53 = (unsigned char)((signed char)(m53) / mem40); - // interpolation range - X = mem40; // number of frames to interpolate over - Y = phase3; // starting frame - - - // linearly interpolate values - - mem56 = 0; - //47907: CLC - //pos47908: - while(1) //while No. 3 - { - A = Read(mem47, Y) + mem53; //carry alway cleared - - mem48 = A; - Y++; - X--; - if(X == 0) break; - - mem56 += mem51; - if (mem56 >= mem40) //??? - { - mem56 -= mem40; //carry? is set - //if ((mem56 & 128)==0) - if ((mem50 & 128)==0) - { - //47935: BIT 50 - //47937: BMI 47943 - if(mem48 != 0) mem48++; - } else mem48--; - } - //pos47945: - Write(mem47, Y, mem48); - } //while No. 3 - - //pos47952: - mem47++; - //if (mem47 != 175) goto pos47810; - } while (mem47 != 175); //while No. 2 - //pos47963: - mem44++; - X = mem44; - } //while No. 1 - yield(); - //goto pos47701; - //pos47970: - - // add the length of this phoneme - mem48 = mem49 + phonemeLengthOutput[mem44]; - - -// ASSIGN PITCH CONTOUR -// -// This subtracts the F1 frequency from the pitch to create a -// pitch contour. Without this, the output would be at a single -// pitch level (monotone). - - - // don't adjust pitch if in sing mode - if (!singmode) - { - // iterate through the buffer - for(i=0; i<256; i++) { - // subtract half the frequency of the formant 1. - // this adds variety to the voice - pitches[i] -= (frequency1[i] >> 1); - } - } - - phase1 = 0; - phase2 = 0; - phase3 = 0; - mem49 = 0; - speedcounter = 72; //sam standard speed - -// RESCALE AMPLITUDE -// -// Rescale volume from a linear scale to decibels. -// - - //amplitude rescaling - for(i=255; i>=0; i--) - { - amplitude1[i] = pgm_read_byte(amplitudeRescale + amplitude1[i]);//amplitudeRescale[amplitude1[i]]; - amplitude2[i] = pgm_read_byte(amplitudeRescale + amplitude2[i]);//amplitudeRescale[amplitude2[i]]; - amplitude3[i] = pgm_read_byte(amplitudeRescale + amplitude3[i]);//amplitudeRescale[amplitude3[i]]; - } - - Y = 0; - A = pitches[0]; - mem44 = A; - X = A; - mem38 = A - (A>>2); // 3/4*A ??? -yield(); -if (DEBUG_ESP8266SAM_LIB) -{ - PrintOutput(sampledConsonantFlag, frequency1, frequency2, frequency3, amplitude1, amplitude2, amplitude3, pitches); -} - -// PROCESS THE FRAMES -// -// In traditional vocal synthesis, the glottal pulse drives filters, which -// are attenuated to the frequencies of the formants. -// -// SAM generates these formants directly with sin and rectangular waves. -// To simulate them being driven by the glottal pulse, the waveforms are -// reset at the beginning of each glottal pulse. - - //finally the loop for sound output - //pos48078: - while(1) - { - // get the sampled information on the phoneme - A = sampledConsonantFlag[Y]; - mem39 = A; - - // unvoiced sampled phoneme? - A = A & 248; - if(A != 0) - { - // render the sample for the phoneme - RenderSample(&mem66); - - // skip ahead two in the phoneme buffer - Y += 2; - mem48 -= 2; - } else - { - // simulate the glottal pulse and formants - unsigned char ary[5]; - unsigned int p1 = phase1 * 256; // Fixed point integers because we need to divide later on - unsigned int p2 = phase2 * 256; - unsigned int p3 = phase3 * 256; - - for (int k=0; k<5; k++) { - signed char sp1 = (signed char)pgm_read_byte(&sinus[0xff & (p1>>8)]); - signed char sp2 = (signed char)pgm_read_byte(&sinus[0xff & (p2>>8)]); - signed char rp3 = (signed char)pgm_read_byte(&rectangle[0xff & (p3>>8)]); - signed int sin1 = sp1 * ((unsigned char)amplitude1[Y] & 0x0f); - signed int sin2 = sp2 * ((unsigned char)amplitude2[Y] & 0x0f); - signed int rect = rp3 * ((unsigned char)amplitude3[Y] & 0x0f); - signed int mux = sin1 + sin2 + rect; - mux /= 32; - mux += 128; // Go from signed to unsigned amplitude - ary[k] = mux; - p1 += ((int)frequency1[Y]) * 256 / 4; // Compromise, this becomes a shift and works well - p2 += ((int)frequency2[Y]) * 256 / 4; - p3 += ((int)frequency3[Y]) * 256 / 4; - } - // output the accumulated value - Output8BitAry(0, ary); - speedcounter--; - if (speedcounter != 0) goto pos48155; - Y++; //go to next amplitude - - // decrement the frame count - mem48--; - } - - // if the frame count is zero, exit the loop - if(mem48 == 0) return; - speedcounter = speed; -pos48155: - - // decrement the remaining length of the glottal pulse - mem44--; - - // finished with a glottal pulse? - if(mem44 == 0) - { -pos48159: - // fetch the next glottal pulse length - A = pitches[Y]; - mem44 = A; - A = A - (A>>2); - mem38 = A; - - // reset the formant wave generators to keep them in - // sync with the glottal pulse - phase1 = 0; - phase2 = 0; - phase3 = 0; - continue; - } - - // decrement the count - mem38--; - - // is the count non-zero and the sampled flag is zero? - if((mem38 != 0) || (mem39 == 0)) - { - // reset the phase of the formants to match the pulse - phase1 += frequency1[Y]; - phase2 += frequency2[Y]; - phase3 += frequency3[Y]; - continue; - } - - // voiced sampled phonemes interleave the sample with the - // glottal pulse. The sample flag is non-zero, so render - // the sample for the phoneme. - RenderSample(&mem66); - goto pos48159; - } //while - -} - - -// Create a rising or falling inflection 30 frames prior to -// index X. A rising inflection is used for questions, and -// a falling inflection is used for statements. - -void AddInflection(unsigned char mem48, unsigned char phase1) -{ - //pos48372: - // mem48 = 255; -//pos48376: - - // store the location of the punctuation - mem49 = X; - A = X; - int Atemp = A; - - // backup 30 frames - A = A - 30; - // if index is before buffer, point to start of buffer - if (Atemp <= 30) A=0; - X = A; - - // FIXME: Explain this fix better, it's not obvious - // ML : A =, fixes a problem with invalid pitch with '.' - while( (A=pitches[X]) == 127) X++; - - -pos48398: - //48398: CLC - //48399: ADC 48 - - // add the inflection direction - A += mem48; - phase1 = A; - - // set the inflection - pitches[X] = A; -pos48406: - - // increment the position - X++; - - // exit if the punctuation has been reached - if (X == mem49) return; //goto pos47615; - if (pitches[X] == 255) goto pos48406; - A = phase1; - goto pos48398; -} - -/* - SAM's voice can be altered by changing the frequencies of the - mouth formant (F1) and the throat formant (F2). Only the voiced - phonemes (5-29 and 48-53) are altered. -*/ -// mouth formants (F1) 5..29 -const unsigned char mouthFormants5_29[30] PROGMEM = { - 0, 0, 0, 0, 0, 10, - 14, 19, 24, 27, 23, 21, 16, 20, 14, 18, 14, 18, 18, - 16, 13, 15, 11, 18, 14, 11, 9, 6, 6, 6}; - -// throat formants (F2) 5..29 -const unsigned char throatFormants5_29[30] PROGMEM = { - 255, 255, - 255, 255, 255, 84, 73, 67, 63, 40, 44, 31, 37, 45, 73, 49, - 36, 30, 51, 37, 29, 69, 24, 50, 30, 24, 83, 46, 54, 86}; - - // there must be no zeros in this 2 tables - // formant 1 frequencies (mouth) 48..53 -const unsigned char mouthFormants48_53[6] PROGMEM = {19, 27, 21, 27, 18, 13}; - - // formant 2 frequencies (throat) 48..53 -const unsigned char throatFormants48_53[6] PROGMEM = {72, 39, 31, 43, 30, 34}; - -void SetMouthThroat(unsigned char mouth, unsigned char throat) -{ - unsigned char initialFrequency; - unsigned char newFrequency = 0; - //unsigned char mouth; //mem38880 - //unsigned char throat; //mem38881 - - unsigned char pos = 5; //mem39216 -//pos38942: - // recalculate formant frequencies 5..29 for the mouth (F1) and throat (F2) - while(pos != 30) - { - // recalculate mouth frequency - initialFrequency = pgm_read_byte(&mouthFormants5_29[pos]); - if (initialFrequency != 0) newFrequency = trans(mouth, initialFrequency); - freq1data[pos] = newFrequency; - - // recalculate throat frequency - initialFrequency = pgm_read_byte(&throatFormants5_29[pos]); - if(initialFrequency != 0) newFrequency = trans(throat, initialFrequency); - freq2data[pos] = newFrequency; - pos++; - } - -//pos39059: - // recalculate formant frequencies 48..53 - pos = 48; - Y = 0; - while(pos != 54) - { - // recalculate F1 (mouth formant) - initialFrequency = pgm_read_byte(&mouthFormants48_53[Y]); - newFrequency = trans(mouth, initialFrequency); - freq1data[pos] = newFrequency; - - // recalculate F2 (throat formant) - initialFrequency = pgm_read_byte(&throatFormants48_53[Y]); - newFrequency = trans(throat, initialFrequency); - freq2data[pos] = newFrequency; - Y++; - pos++; - } -} - - -//return = (mem39212*mem39213) >> 1 -unsigned char trans(unsigned char mem39212, unsigned char mem39213) -{ - //pos39008: - unsigned char carry; - int temp; - unsigned char mem39214, mem39215; - A = 0; - mem39215 = 0; - mem39214 = 0; - X = 8; - do - { - carry = mem39212 & 1; - mem39212 = mem39212 >> 1; - if (carry != 0) - { - /* - 39018: LSR 39212 - 39021: BCC 39033 - */ - carry = 0; - A = mem39215; - temp = (int)A + (int)mem39213; - A = A + mem39213; - if (temp > 255) carry = 1; - mem39215 = A; - } - temp = mem39215 & 1; - mem39215 = (mem39215 >> 1) | (carry?128:0); - carry = temp; - //39033: ROR 39215 - X--; - } while (X != 0); - temp = mem39214 & 128; - mem39214 = (mem39214 << 1) | (carry?1:0); - carry = temp; - temp = mem39215 & 128; - mem39215 = (mem39215 << 1) | (carry?1:0); - carry = temp; - - return mem39215; -}