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;
-}