
What is SAM?
============

Sam is a very small Text-To-Speech (TTS) program written in C, that runs on most popular platforms.
It is an adaption to C of the speech software SAM (Software Automatic Mouth) for the Commodore C64 published 
in the year 1982 by Don't Ask Software. It includes a Text-To-Phoneme converter called reciter and a Phoneme-To-Speech routine for the 
final output. It is so small that it will work also on embedded computers. On my computer it takes
less than 39KB of disk space and is a fully stand alone program.
For immediate output it uses the SDL-library, otherwise it can save .wav files. 

Compile
=======

Simply type "make" in your command prompt.
If you want to compile without SDL delete the "-DUSESDL" statement from the file "Makefile".

It should compile on every UNIX-like operating system. For Windows you need cygwin.


Usage
=====

type

	./sam I am Sam

for the first output.

If you have disabled SDL try

	./sam -wav i_am_sam.wav I am Sam

to get a wav file.

you can try other options like
	-pitch number
	-speed number
	-throat number
	-mouth number

Some typical values written in the original manual are:

	DESCRIPTION          SPEED     PITCH     THROAT    MOUTH
	Elf                   72        64        110       160
	Little Robot          92        60        190       190
	Stuffy Guy            82        72        110       105
	Little Old Lady       82        32        145       145
	Extra-Terrestrial    100        64        150       200
	SAM                   72        64        128       128


It can even sing
look at
	sing.bat
for a small example.

For phoneme output look in the table in Appendix 1.


For additional features take a look at the original manual found at
	http://www.retrobits.net/atari/sam.shtml


Adaption To C
=============

This program was converted semi-automatic into C by converting each assembler opcode.
e. g. 

	lda 56		=>	A = mem[56];
	jmp 38018  	=>	goto pos38018;
	inc 38		=>	mem[38]++;
	.			.
	.			.

Then it was manually rewritten to remove most of the 
jumps and register variables in the code and rename the variables to proper names. 
Most of the description below is a result of this rewriting process.

Unfortunately its still a not very good readable. But you should see where I started :)



Short description
=================

First of all I will limit myself here to a very coarse description. 
There are very many exceptions defined in the source code that I will not explain. 
Also a lot of code is unknown for me e. g. Code47503. 
For a complete understanding of the code I need more time and especially more eyes have a look on the code. 

Reciter
-------

It changes the english text to phonemes by a ruleset shown in Appendix 3.

The rule
	" ANT(I)",	"AY",
means that if he find an "I" with previous letters " ANT", exchange the I by the phoneme "AY".

There are some special signs in this rules like
	#
	&
	@
	^
	+
	:
	%
which can mean e. g. that there must be a vocal or a consonant or something else. 

With the -debug option you will get the resulting phonemes for this translation.


Output
------

Here is the full tree of subroutine calls:

Code39771
	Parser1
	Parser2
		Insert
	Code41883
	SetPhonemeLength
	Code48619
	Code41240
		Insert
	Code48431
		Insert
		
	Code48547
		Code47574
			Special1
			Code47503
			Code48227


Code39771 is the entry routine and calls all further routines. 
Parser1 transforms the phoneme input and transforms it to three tables
	phonemeindex[]
	stress[]
	phonemelength[] (zero at this moment)
	
This tables are now changed: 

Parser2 exchanges some phonemes by others and inserts new. 
Code41883 adds 1 to the stress under some circumstances
SetPhonemeLength sets phoneme lengths. 
Code48619 changes the phoneme lengths
Code41240 adds some additional phonemes
Code48431 has some extra rules


Appendix 2 shows all possible phonemes and some flag fields.  
The final content of these tables can be seen with the -debug command.


These tables is now partly copied into the small tables:
	phonemeindexOutput[]
	stressOutput[]
	phonemelengthOutput[]
for output.

Final Output
------------

Except of some special phonemes the output is build by a linear combination:
	
	A =   A1 * sin ( f1 * t ) +
	      A2 * sin ( f2 * t ) +
	      A3 * rect( f3 * t )

where rect is a rectangular function with the same periodicity like sin. 
It seems really strange, but this is really enough for most types of phonemes. 

Therefore the above phonemes are converted with some tables to 
	tab43008[]
	frequency1[]  =  f1
	frequency2[]  =  f2
	frequency3[]  =  f3
	amplitude1[]  =  A1
	amplitude2[]  =  A2
	amplitude3[]  =  A3
	
Above formula is calculated in one very good omptimized routine.
It only consist of 26 commands:

48087: 	LDX 43		; get phase	
	CLC		
	LDA 42240,x	; load sine value (high 4 bits)
	ORA TabAmpl1,y	; get amplitude (in low 4 bits)
	TAX		
	LDA 42752,x	; multiplication table
	STA 56		; store 

	LDX 42		; get phase
	LDA 42240,x	; load sine value (high 4 bits)
	ORA TabAmpl2,y	; get amplitude (in low 4 bits)
	TAX		
	LDA 42752,x	; multiplication table
	ADC Var56	; add with previous values
	STA 56		; and store

	LDX 41		; get phase
	LDA 42496,x	; load rect value (high 4 bits)
	ORA TabAmpl3,y	; get amplitude (in low 4 bits)
	TAX		
	LDA 42752,x	; multiplication table
	ADC 56		; add with previous values

	ADC #136		
	LSR A		; get highest 4 bits
	LSR A		
	LSR A		
	LSR A		
	STA 54296	;SID   main output command


The rest is handled in a special way. At the moment I cannot figure out in which way. 
But it seems that it uses some noise (e. g. for "s") using a table with random values. 


Contact
=======

If you have questions don' t hesitate to ask me.
If you discovered some new knowledge about the code please mail me.

Sebastian Macke
Email: sebastian@macke.de



APPENDIX 1: PHONETIC ALPHABET FOR SAM
-------------------------------------

     VOWELS                            VOICED CONSONANTS
IY           f(ee)t                    R        red
IH           p(i)n                     L        allow
EH           beg                       W        away
AE           Sam                       W        whale
AA           pot                       Y        you
AH           b(u)dget                  M        Sam
AO           t(al)k                    N        man
OH           cone                      NX       so(ng)
UH           book                      B        bad
UX           l(oo)t                    D        dog
ER           bird                      G        again
AX           gall(o)n                  J        judge
IX           dig(i)t                   Z        zoo
                                       ZH       plea(s)ure
   DIPHTHONGS                          V        seven
EY           m(a)de                    DH       (th)en
AY           h(igh)
OY           boy
AW           h(ow)                     UNVOICED CONSONANTS
OW           slow                      S         Sam
UW           crew                      Sh        fish
                                       F         fish
                                       TH        thin
 SPECIAL PHONEMES                      P         poke
UL           sett(le) (=AXL)           T         talk
UM           astron(omy) (=AXM)        K         cake
UN           functi(on) (=AXN)         CH        speech
Q            kitt-en (glottal stop)    /H        a(h)ead


APPENDIX 2: Phonemes And flag Field
-----------------------------------


Ind  | phoneme |  flags   |
-----|---------|----------|
0    |   *     | 00000000 |
1    |  .*     | 00000000 |
2    |  ?*     | 00000000 |
3    |  ,*     | 00000000 |
4    |  -*     | 00000000 |

VOWELS
5    |  IY     | 10100100 |
6    |  IH     | 10100100 |
7    |  EH     | 10100100 |
8    |  AE     | 10100100 |
9    |  AA     | 10100100 |
10   |  AH     | 10100100 |
11   |  AO     | 10000100 |
17   |  OH     | 10000100 |
12   |  UH     | 10000100 |
16   |  UX     | 10000100 |
15   |  ER     | 10000100 |
13   |  AX     | 10100100 |
14   |  IX     | 10100100 |

DIPTHONGS
48   |  EY     | 10110100 |
49   |  AY     | 10110100 |
50   |  OY     | 10110100 |
51   |  AW     | 10010100 |
52   |  OW     | 10010100 |
53   |  UW     | 10010100 |


21   |  YX     | 10000100 |
20   |  WX     | 10000100 |
18   |  RX     | 10000100 |
19   |  LX     | 10000100 |
37   |  /X     | 01000000 |
30   |  DX     | 01001000 |


22   |  WH     | 01000100 |


VOICED CONSONANTS
23   |  R*     | 01000100 |
24   |  L*     | 01000100 |
25   |  W*     | 01000100 |
26   |  Y*     | 01000100 |
27   |  M*     | 01001100 |
28   |  N*     | 01001100 |
29   |  NX     | 01001100 |
54   |  B*     | 01001110 |
57   |  D*     | 01001110 |
60   |  G*     | 01001110 |
44   |  J*     | 01001100 |
38   |  Z*     | 01000100 |
39   |  ZH     | 01000100 |
40   |  V*     | 01000100 |
41   |  DH     | 01000100 |

unvoiced CONSONANTS
32   |  S*     | 01000000 |
33   |  SH     | 01000000 |
34   |  F*     | 01000000 |
35   |  TH     | 01000000 |
66   |  P*     | 01001011 |
69   |  T*     | 01001011 |
72   |  K*     | 01001011 |
42   |  CH     | 01001000 |
36   |  /H     | 01000000 |

43   |  **     | 01000000 |
45   |  **     | 01000100 |
46   |  **     | 00000000 |
47   |  **     | 00000000 |


55   |  **     | 01001110 |
56   |  **     | 01001110 |
58   |  **     | 01001110 |
59   |  **     | 01001110 |
61   |  **     | 01001110 |
62   |  **     | 01001110 |
63   |  GX     | 01001110 |
64   |  **     | 01001110 |
65   |  **     | 01001110 |
67   |  **     | 01001011 |
68   |  **     | 01001011 |
70   |  **     | 01001011 |
71   |  **     | 01001011 |
73   |  **     | 01001011 |
74   |  **     | 01001011 |
75   |  KX     | 01001011 |
76   |  **     | 01001011 |
77   |  **     | 01001011 |


SPECIAL
78   |  UL     | 10000000 |
79   |  UM     | 11000001 |
80   |  UN     | 11000001 |
31   |  Q*     | 01001100 |





APPENDIX 3: Text To Phoneme Table
---------------------------------

" (A.)",	"EH4Y. ",
"(A) ",		"AH",
" (ARE) ",	"AAR",
" (AR)O",	"AXR",
"(AR)#",	"EH4R",
" ^(AS)#",	"EY4S",
"(A)WA",	"AX",
"(AW)",		"AO5",
" :(ANY)",	"EH4NIY",
"(A)^+#",	"EY5",
"#:(ALLY)",	"ULIY",
" (AL)#",	"UL",
"(AGAIN)",	"AXGEH4N",
"#:(AG)E",	"IHJ",
"(A)^%",	"EY",
"(A)^+:#",	"AE",
" :(A)^+ ",	"EY4",
" (ARR)",	"AXR",
"(ARR)",	"AE4R",
" ^(AR) ",	"AA5R",
"(AR)",		"AA5R",
"(AIR)",	"EH4R",
"(AI)",		"EY4",
"(AY)",		"EY5",
"(AU)",		"AO4",
"#:(AL) ",	"UL",
"#:(ALS) ",	"ULZ",
"(ALK)",	"AO4K",
"(AL)^",	"AOL",
" :(ABLE)",	"EY4BUL",
"(ABLE)",	"AXBUL",
"(A)VO",	"EY4",
"(ANG)+",	"EY4NJ",
"(ATARI)",	"AHTAA4RIY",
"(A)TOM",	"AE",
"(A)TTI",	"AE",
" (AT) ",	"AET",
" (A)T",	"AH",
"(A)",		"AE",
" (B) ",	"BIY4",
" (BE)^#",	"BIH",
"(BEING)",	"BIY4IHNX",
" (BOTH) ",	"BOW4TH",
" (BUS)#",	"BIH4Z",
"(BREAK)",	"BREY5K",
"(BUIL)",	"BIH4L",
"(B)",		"B",
" (C) ",	"SIY4",
" (CH)^",	"K",
"^E(CH)",	"K",
"(CHA)R#",	"KEH5",
"(CH)",		"CH",
" S(CI)#",	"SAY4",
"(CI)A",	"SH",
"(CI)O",	"SH",
"(CI)EN",	"SH",
"(CITY)",	"SIHTIY",
"(C)+",		"S",
"(CK)",		"K",
"(COMMODORE)",	"KAA4MAHDOHR",
"(COM)",	"KAHM",
"(CUIT)",	"KIHT",
"(CREA)",	"KRIYEY",
"(C)",		"K",
" (D) ",	"DIY4",
" (DR.) ",	"DAA4KTER",
"#:(DED) ",	"DIHD",
".E(D) ",	"D",
"#:^E(D) ",	"T",
" (DE)^#",	"DIH",
" (DO) ",	"DUW",
" (DOES)",	"DAHZ",
"(DONE) ",	"DAH5N",
"(DOING)",	"DUW4IHNX",
" (DOW)",	"DAW",
"#(DU)A",	"JUW",
"#(DU)^#",	"JAX",
"(D)",		"D",

" (E) ",	"IYIY4",
"#:(E) ",	"",
"':^(E) ",	"",
" :(E) ",	"IY",
"#(ED) ",	"D",
"#:(E)D ",	"",
"(EV)ER",	"EH4V",
"(E)^%",	"IY4",
"(ERI)#",	"IY4RIY",
"(ERI)",	"EH4RIH",
"#:(ER)#",	"ER",
"(ERROR)",	"EH4ROHR",
"(ERASE)",	"IHREY5S",
"(ER)#",	"EHR",
"(ER)",		"ER",
" (EVEN)",	"IYVEHN",
"#:(E)W",	"",
"@(EW)",	"UW",
"(EW)",		"YUW",
"(E)O",		"IY",
"#:&(ES) ",	"IHZ",
"#:(E)S ",	"",
"#:(ELY) ",	"LIY",
"#:(EMENT)",	"MEHNT",
"(EFUL)",	"FUHL",
"(EE)",		"IY4",
"(EARN)",	"ER5N",
" (EAR)^",	"ER5",
"(EAD)",	"EHD",
"#:(EA) ",	"IYAX",
"(EA)SU",	"EH5",
"(EA)",		"IY5",
"(EIGH)",	"EY4",
"(EI)",		"IY4",
" (EYE)",	"AY4",
"(EY)",		"IY",
"(EU)",		"YUW5",
"(EQUAL)",	"IY4KWUL",
"(E)",		"EH",

" (F) ",	"EH4F",
"(FUL)",	"FUHL",
"(FRIEND)",	"FREH5ND",
"(FATHER)",	"FAA4DHER",
"(F)F",		"",
"(F)",		"F",

" (G) ",	"JIY4",
"(GIV)",	"GIH5V",
" (G)I^",	"G",
"(GE)T",	"GEH5",
"SU(GGES)",	"GJEH4S",
"(GG)",		"G",
" B#(G)",	"G",
"(G)+",		"J",
"(GREAT)",	"GREY4T",
"(GON)E",	"GAO5N",
"#(GH)",	"",
" (GN)",	"N",
"(G)",		"G",

" (H) ",	"EY4CH",
" (HAV)",	"/HAE6V",
" (HERE)",	"/HIYR",
" (HOUR)",	"AW5ER",
"(HOW)",	"/HAW",
"(H)#",		"/H",
"(H)",		"",

" (IN)",	"IHN",
" (I) ",	"AY4",
"(I) ",		"AY",
"(IN)D",	"AY5N",
"SEM(I)",	"IY",
" ANT(I)",	"AY",
"(IER)",	"IYER",
"#:R(IED) ",	"IYD",
"(IED) ",	"AY5D",
"(IEN)",	"IYEHN",
"(IE)T",	"AY4EH",
"(I')",		"AY5",
" :(I)^%",	"AY5",
" :(IE) ",	"AY4",
"(I)%",		"IY",
"(IE)",		"IY4",
" (IDEA)",	"AYDIY5AH",
"(I)^+:#",	"IH",
"(IR)#",	"AYR",
"(IZ)%",	"AYZ",
"(IS)%",	"AYZ",
"I^(I)^#",	"IH",
"+^(I)^+",	"AY",
"#:^(I)^+",	"IH",
"(I)^+",	"AY",
"(IR)",		"ER",
"(IGH)",	"AY4",
"(ILD)",	"AY5LD",
" (IGN)",	"IHGN",
"(IGN) ",	"AY4N",
"(IGN)^",	"AY4N",
"(IGN)%",	"AY4N",
"(ICRO)",	"AY4KROH",
"(IQUE)",	"IY4K",
"(I)",		"IH",

" (J) ",	"JEY4",
"(J)",		"J",

" (K) ",	"KEY4",
" (K)N",	"",
"(K)",		"K",

" (L) ",	"EH4L",
"(LO)C#",	"LOW",
"L(L)",		"",
"#:^(L)%",	"UL",
"(LEAD)",	"LIYD",
" (LAUGH)",	"LAE4F",
"(L)",		"L",

" (M) ",	"EH4M",
" (MR.) ",	"MIH4STER",
" (MS.)",	"MIH5Z",
" (MRS.) ",	"MIH4SIXZ",
"(MOV)",	"MUW4V",
"(MACHIN)",	"MAHSHIY5N",
"M(M)",		"",
"(M)",		"M",

" (N) ",	"EH4N",
"E(NG)+",	"NJ",
"(NG)R",	"NXG",
"(NG)#",	"NXG",
"(NGL)%",	"NXGUL",
"(NG)",		"NX",
"(NK)",		"NXK",
" (NOW) ",	"NAW4",
"N(N)",		"",
"(NON)E",	"NAH4N",
"(N)",		"N",

" (O) ",	"OH4W",
"(OF) ",	"AHV",
" (OH) ",	"OW5",
"(OROUGH)",	"ER4OW",
"#:(OR) ",	"ER",
"#:(ORS) ",	"ERZ",
"(OR)",		"AOR",
" (ONE)",	"WAHN",
"#(ONE) ",	"WAHN",
"(OW)",		"OW",
" (OVER)",	"OW5VER",
"PR(O)V",	"UW4",
"(OV)",		"AH4V",
"(O)^%",	"OW5",
"(O)^EN",	"OW",
"(O)^I#",	"OW5",
"(OL)D",	"OW4L",
"(OUGHT)",	"AO5T",
"(OUGH)",	"AH5F",
" (OU)",	"AW",
"H(OU)S#",	"AW4",
"(OUS)",	"AXS",
"(OUR)",	"OHR",
"(OULD)",	"UH5D",
"(OU)^L",	"AH5",
"(OUP)",	"UW5P",
"(OU)",		"AW",
"(OY)",		"OY",
"(OING)",	"OW4IHNX",
"(OI)",		"OY5",
"(OOR)",	"OH5R",
"(OOK)",	"UH5K",
"F(OOD)",	"UW5D",
"L(OOD)",	"AH5D",
"M(OOD)",	"UW5D",
"(OOD)",	"UH5D",
"F(OOT)",	"UH5T",
"(OO)",		"UW5",
"(O')",		"OH",
"(O)E",		"OW",
"(O) ",		"OW",
"(OA)",		"OW4",
" (ONLY)",	"OW4NLIY",
" (ONCE)",	"WAH4NS",
"(ON'T)",	"OW4NT",
"C(O)N",	"AA",
"(O)NG",	"AO",
" :^(O)N",	"AH",
"I(ON)",	"UN",
"#:(ON)",	"UN",
"#^(ON)",	"UN",
"(O)ST",	"OW",
"(OF)^",	"AO4F",
"(OTHER)",	"AH5DHER",
"R(O)B",	"RAA",
"^R(O):#",	"OW5",
"(OSS) ",	"AO5S",
"#:^(OM)",	"AHM",
"(O)",		"AA",

" (P) ",	"PIY4",
"(PH)",		"F",
"(PEOPL)",	"PIY5PUL",
"(POW)",	"PAW4",
"(PUT) ",	"PUHT",
"(P)P",		"",
"(P)S",		"",
"(P)N",		"",
"(PROF.)",	"PROHFEH4SER",
"(P)",		"P",

" (Q) ",	"KYUW4",
"(QUAR)",	"KWOH5R",
"(QU)",		"KW",
"(Q)",		"K",

" (R) ",	"AA5R",
" (RE)^#",	"RIY",
"(R)R",		"",
"(R)",		"R",

" (S) ",	"EH4S",
"(SH)",		"SH",
"#(SION)",	"ZHUN",
"(SOME)",	"SAHM",
"#(SUR)#",	"ZHER",
"(SUR)#",	"SHER",
"#(SU)#",	"ZHUW",
"#(SSU)#",	"SHUW",
"#(SED)",	"ZD",
"#(S)#",	"Z",
"(SAID)",	"SEHD",
"^(SION)",	"SHUN",
"(S)S", 	"",
".(S) ",	"Z",
"#:.E(S) ",	"Z",
"#:^#(S) ",	"S",
"U(S) ",	"S",
" :#(S) ",	"Z",
"##(S) ",	"Z",
" (SCH)",	"SK",
"(S)C+",	"",
"#(SM)",	"ZUM",
"#(SN)'",	"ZUM",
"(STLE)",	"SUL",
"(S)",		"S",

" (T) ",	"TIY4",
" (THE) #",	"DHIY",
" (THE) ",	"DHAX",
"(TO) ",	"TUX",
" (THAT)",	"DHAET",
" (THIS) ",	"DHIHS",
" (THEY)",	"DHEY",
" (THERE)",	"DHEHR",
"(THER)",	"DHER",
"(THEIR)",	"DHEHR",
" (THAN) ",	"DHAEN",
" (THEM) ",	"DHAEN",
"(THESE) ",	"DHIYZ",
" (THEN)",	"DHEHN",
"(THROUGH)",	"THRUW4",
"(THOSE)",	"DHOHZ",
"(THOUGH) ",	"DHOW",
"(TODAY)",	"TUXDEY",
"(TOMO)RROW",	"TUMAA5",
"(TO)TAL",	"TOW5",
" (THUS)",	"DHAH4S",
"(TH)",		"TH",
"#:(TED)",	"TIXD",
"S(TI)#N",	"CH",
"(TI)O",	"SH",
"(TI)A",	"SH",
"(TIEN)",	"SHUN",
"(TUR)#",	"CHER",
"(TU)A",	"CHUW",
" (TWO)",	"TUW",
"&(T)EN ",	"",
"(T)",		"T",

" (U) ",	"YUW4",
" (UN)I",	"YUWN",
" (UN)",	"AHN",
" (UPON)",	"AXPAON",
"@(UR)#",	"UH4R",
"(UR)#",	"YUH4R",
"(UR)",		"ER",
"(U)^ ",	"AH",
"(U)^^",	"AH5",
"(UY)",		"AY5",
" G(U)#",	"",
"G(U)%",	"",
"G(U)#",	"W",
"#N(U)",	"YUW",
"@(U)",		"UW",
"(U)",		"YUW",

" (V) ",	"VIY4",
"(VIEW)",	"VYUW5",
"(V)",		"V",

" (W) ",	"DAH4BULYUW",
" (WERE)",	"WER",
"(WA)SH",	"WAA",
"(WA)ST",	"WEY",
"(WA)S",	"WAH",
"(WA)T",	"WAA",
"(WHERE)",	"WHEHR",
"(WHAT)",	"WHAHT",
"(WHOL)",	"/HOWL",
"(WHO)",	"/HUW",
"(WH)",		"WH",
"(WAR)#",	"WEHR",
"(WAR)",	"WAOR",
"(WOR)^",	"WER",
"(WR)",		"R",
"(WOM)A",	"WUHM",
"(WOM)E",	"WIHM",
"(WEA)R",	"WEH",
"(WANT)",	"WAA5NT",
"ANS(WER)",	"ER",
"(W)",		"W",

" (X) ",	"EH4KR",
" (X)",		"Z",
"(X)",		"KS",

" (Y) ",	"WAY4",
"(YOUNG)",	"YAHNX",
" (YOUR)",	"YOHR",
" (YOU)",	"YUW",
" (YES)",	"YEHS",
" (Y)",		"Y",
"F(Y)",		"AY",
"PS(YCH)",	"AYK",
"#:^(Y)",	"IY",
"#:^(Y)I",	"IY",
" :(Y) ",	"AY",
" :(Y)#",	"AY",
" :(Y)^+:#","IH",
" :(Y)^#",	"AY",
"(Y)",		"IH",

" (Z) ",	"ZIY4",
"(Z)",		"Z",
" ", 	" "

