1 /**************************************************************************
2 
3 	WOW/Votrax SC-01 Emulator
4 
5  	Mike@Dissfulfils.co.uk
6 
7 		Modified to match phonemes to words
8 
9 		Ajudd@quantime.co.uk
10 
11 **************************************************************************
12 
13 wow_sh_start  - Start emulation, load samples from Votrax subdirectory
14 wow_sh_w      - Write data to votrax port
15 wow_sh_status - Return busy status (-1 = busy)
16 wow_port_2_r  - Returns status of voice port
17 wow_sh_ update- Null
18 
19 If you need to alter the base frequency (i.e. Qbert) then just alter
20 the variable wowBaseFrequency, this is defaulted to 8000
21 
22 **************************************************************************/
23 
24 #include "driver.h"
25 #include "cpu/z80/z80.h"
26 
27 
28 
29 int	wowBaseFrequency;		/* Some games (Qbert) change this */
30 int 	wowBaseVolume;
31 int 	wowChannel = 0;
32 struct  GameSamples *wowSamples;
33 
34 /****************************************************************************
35  * 64 Phonemes - currently 1 sample per phoneme, will be combined sometime!
36  ****************************************************************************/
37 
38 static const char *PhonemeTable[65] =
39 {
40  "EH3","EH2","EH1","PA0","DT" ,"A1" ,"A2" ,"ZH",
41  "AH2","I3" ,"I2" ,"I1" ,"M"  ,"N"  ,"B"  ,"V",
42  "CH" ,"SH" ,"Z"  ,"AW1","NG" ,"AH1","OO1","OO",
43  "L"  ,"K"  ,"J"  ,"H"  ,"G"  ,"F"  ,"D"  ,"S",
44  "A"  ,"AY" ,"Y1" ,"UH3","AH" ,"P"  ,"O"  ,"I",
45  "U"  ,"Y"  ,"T"  ,"R"  ,"E"  ,"W"  ,"AE" ,"AE1",
46  "AW2","UH2","UH1","UH" ,"O2" ,"O1" ,"IU" ,"U1",
47  "THV","TH" ,"ER" ,"EH" ,"E1" ,"AW" ,"PA1","STOP",
48  0
49 };
50 
51 /* Missing samples : ready.wav from.wav one.wav bite.wav youl.wav explode.wav if.wav myself.wav back.wav
52    cant.wav do.wav wait.wav worlings.wav very.wav babies.wav breath.wav fire.wav beat.wav rest.wav
53    then.wav never.wav worlock.wav escape.wav door.wav try.wav any.wav harder.wav only.wav meet.wav with.wav
54    doom.wav pop.wav
55    Problems with YOU and YOU'LL and YOU'DD */
56 
57 static const char *wowWordTable[] =
58 {
59 "AH1I3Y1", "UH1GA1EH1N", "AHAH2", "AE1EH3M", "AE1EH3ND",
60 "anew.wav", "AH1NUHTHER", "AE1NY", "anyone.wav", "appear.wav", "AH1UH3R", "UHR", "BABYY1S", "BAE1EH3K",
61 "BE1T", "become.wav", "BEHST", "BEH1TER", "BUH3AH2YT", "bones.wav", "BRE1YTH", "but.wav", "can.wav", "KAE1EH3NT",
62 "chance.wav", "CHEHST", "KO1O2I3Y1N", "dance.wav", "DE1STRO1UH3I3AY",
63 "DE1VEH1LUH3PT", "DIUU", "DONT", "DUUM", "DOO1R", "draw.wav", "DUHNJEH1N", "DUHNJEH1NZ",
64 "each.wav", "eaten.wav", "EHSPA0KA2I3Y1P", "EHKPA0SPLOU1D", "fear.wav", "FAH1I3YND", "FAH1I3Y1ND", "FAH1EH3AYR", "FOR", "FRUHMM",
65 "garwor.wav", "GEHT", "GEH1T", "GEHEH3T", "GEHTING", "good.wav", "HAH1HAH1HAH1HAH1", "HAH1RDER",
66 "hasnt.wav", "have.wav", "HEH1I3VE1WA1I3Y1TS", "HAI1Y1", "HOP",
67 "HUHNGRY", "HUHNGGRY", "HERRY", "AH1EH3I3Y", "AH1UH3I3Y", "IF", "I1F", "AH1I3YM", "AH1EH3I3YL", "AH1I3Y1L", "IN1",
68 "INSERT", "invisibl.wav", "IT", "lie.wav", "MAE1EH3DJI1KUH1L",
69 "MAE1EH3DJI1KUH1L", "MEE1", "MEE1T", "months.wav",
70 "MAH1EH3I3Y", "MAH2AH2EH3I3Y", "MAH1I1Y", "MAH1I3Y1", "MAH1I3Y", "MAH1I3YSEHLF", "near.wav", "NEH1VER",
71 "NAH1UH3U1", "UHV", "AWF", "WUHN", "O1NLY", "UHVEHN", "PA1", "PEHTS", "PAH1WERFUH1L", "PAH1P",
72 "radar.wav", "REHDY",
73 "REHST", "say.wav", "SAH1I3AYEHNS", "SE1Y", "PA0", "start.wav", "THVAYAY", "THVUH", "THVUH1", "THUH1", "THVEH1N",
74 "THVRU", "thurwor.wav", "time.wav", "TU1", "TUU1", "TIUU1", "TREH1ZHERT", "TRAH1EH3I3Y", "VEHEH3RY", "WA2AYYT",
75 "WOO1R", "WORYER", "watch.wav", "WE1Y", "WEHLKUHM",
76 "WERR", "WAH1EH3I3L", "WIL", "WITH", "WIZERD", "wont.wav",
77 "WO1O2R", "WO1ERLD", "WORLINGS", "WORLUHK",
78 "YI3U", "Y1IUU", "YIUUI", "Y1IUU1U1", "YI3U1", "Y1IUUL", "YIUU1L", "Y1IUUD", "YO2O2R",0
79 };
80 
81 #define num_samples (sizeof(wowWordTable)/sizeof(char *))
82 
83 
84 static const char *wow_sample_names[] =
85 {
86 	"*wow",
87 	"a.wav", "again.wav", "ahh.wav", "am.wav", "and.wav",
88 	"anew.wav", "another.wav", "any.wav", "anyone.wav", "appear.wav", "are.wav", "are.wav", "babies.wav", "back.wav",
89 	"beat.wav", "become.wav", "best.wav", "better.wav", "bite.wav", "bones.wav", "breath.wav", "but.wav", "can.wav", "cant.wav",
90 	"chance.wav", "chest.wav", "coin.wav", "dance.wav", "destroy.wav",
91 	"develop.wav", "do.wav", "dont.wav", "doom.wav", "door.wav", "draw.wav", "dungeon.wav", "dungeons.wav",
92 	"each.wav", "eaten.wav", "escape.wav", "explode.wav", "fear.wav", "find.wav", "find.wav", "fire.wav", "for.wav", "from.wav",
93 	"garwor.wav", "get.wav", "get.wav", "get.wav", "getting.wav", "good.wav", "hahahaha.wav", "harder.wav",
94 	"hasnt.wav", "have.wav", "heavyw.wav", "hey.wav", "hope.wav",
95 	"hungry.wav", "hungry.wav", "hurry.wav", "i.wav", "i.wav", "if.wav", "if.wav", "im.wav", "i1.wav", "ill.wav", "in.wav",
96 	"insert.wav", "invisibl.wav", "it.wav", "lie.wav", "magic.wav",
97 	"magical.wav", "me.wav", "meet.wav", "months.wav",
98 	"my.wav", "my.wav", "my.wav", "my.wav", "my.wav", "myself.wav", "near.wav", "never.wav",
99 	"now.wav", "of.wav", "off.wav", "one.wav", "only.wav", "oven.wav", "pause.wav", "pets.wav", "powerful.wav", "pop.wav",
100 	"radar.wav", "ready.wav",
101 	"rest.wav", "say.wav", "science.wav", "see.wav", "spause.wav", "start.wav", "the.wav", "the.wav", "the.wav", "the.wav", "then.wav",
102 	"through.wav", "thurwor.wav", "time.wav", "to.wav", "to.wav", "to.wav", "treasure.wav", "try.wav", "very.wav", "wait.wav",
103 	"war.wav", "warrior.wav", "watch.wav", "we.wav", "welcome.wav",
104 	"were.wav", "while.wav", "will.wav", "with.wav", "wizard.wav", "wont.wav",
105 	"wor.wav", "world.wav", "worlings.wav", "worlock.wav",
106 	"you.wav", "you.wav", "you.wav", "you.wav", "you.wav", "youl.wav", "youl.wav", "youd.wav", "your.wav",0
107 };
108 
109 
110 /* Total word to join the phonemes together - Global to make it easier to use */
111 /* Note the definitions for these are global and defined in src/sndhrdw/gorf.c
112    (not great I know, but it will have to do for the moment ;) ) */
113 
114 extern char totalword[256], *totalword_ptr;
115 extern char oldword[256];
116 extern int plural;
117 
wow_sh_start(const struct MachineSound * msound)118 int wow_sh_start(const struct MachineSound *msound)
119 {
120 	Machine->samples = readsamples(wow_sample_names,Machine->gamedrv->name);
121 
122 	wowBaseFrequency = 11025;
123 	wowBaseVolume = 230;
124 	wowChannel = 0;
125 	return 0;
126 }
127 
READ_HANDLER(wow_speech_r)128 READ_HANDLER( wow_speech_r )
129 {
130 	int Phoneme,Intonation;
131 	int i = 0;
132 
133 	int data;
134 
135 	totalword_ptr = totalword;
136 
137 	data = cpu_get_reg(Z80_BC) >> 8;
138 
139 	Phoneme = data & 0x3F;
140 	Intonation = data >> 6;
141 
142 //	logerror("Data : %d Speech : %s at intonation %d\n",Phoneme, PhonemeTable[Phoneme],Intonation);
143 
144 	if(Phoneme==63) {
145    		sample_stop(wowChannel);
146 //				logerror("Clearing sample %s\n",totalword);
147 				totalword[0] = 0;				   /* Clear the total word stack */
148 				return data;
149 	}
150 	if (strcmp(PhonemeTable[Phoneme], "PA0")==0)						   /* We know PA0 is never part of a word */
151 				totalword[0] = 0;				   /* Clear the total word stack */
152 
153 /* Phoneme to word translation */
154 
155 	if (strlen(totalword) == 0) {
156 	   strcpy(totalword,PhonemeTable[Phoneme]);	                   /* Copy over the first phoneme */
157 	   if (plural != 0) {
158 //		  logerror("found a possible plural at %d\n",plural-1);
159 		  if (!strcmp("S",totalword)) {		   /* Plural check */
160 			 sample_start(wowChannel, num_samples-2, 0);	   /* play the sample at position of word */
161 			 sample_set_freq(wowChannel, wowBaseFrequency);    /* play at correct rate */
162 			 totalword[0] = 0;				   /* Clear the total word stack */
163 			 oldword[0] = 0;				   /* Clear the total word stack */
164 			 return data;
165 		  } else {
166 			 plural=0;
167 		  }
168 	   }
169 	} else
170 	   strcat(totalword,PhonemeTable[Phoneme]);	                   /* Copy over the first phoneme */
171 
172 //	logerror("Total word = %s\n",totalword);
173 
174 	for (i=0; wowWordTable[i]; i++) {
175 	   if (!strcmp(wowWordTable[i],totalword)) {		   /* Scan the word (sample) table for the complete word */
176 	  /* WOW has Dungeon */
177 		  if ((!strcmp("GDTO1RFYA2N",totalword)) || (!strcmp("RO1U1BAH1T",totalword)) || (!strcmp("KO1UH3I3E1N",totalword))) {		   /* May be plural */
178 			 plural=i+1;
179 			 strcpy(oldword,totalword);
180 //	     logerror("Storing sample position %d and copying string %s\n",plural,oldword);
181 		  } else {
182 			 plural=0;
183 		  }
184 		  sample_start(wowChannel, i, 0);	                   /* play the sample at position of word */
185 		  sample_set_freq(wowChannel, wowBaseFrequency);         /* play at correct rate */
186 //		  logerror("Playing sample %d\n",i);
187 		  totalword[0] = 0;				   /* Clear the total word stack */
188 		  return data;
189 	   }
190 	}
191 
192 	/* Note : We should really also use volume in this as well as frequency */
193 	return data;				                   /* Return nicely */
194 }
195 
wow_status_r(void)196 int wow_status_r(void)
197 {
198 //	logerror("asked for samples status %d\n",wowChannel);
199 	return !sample_playing(wowChannel);
200 }
201 
202 /* Read from port 2 (0x12) returns speech status as 0x80 */
203 
READ_HANDLER(wow_port_2_r)204 READ_HANDLER( wow_port_2_r )
205 {
206 	int Ans;
207 
208 	Ans = (input_port_2_r(0) & 0x7F);
209 	if (wow_status_r() != 0) Ans += 128;
210 	return Ans;
211 }
212 
wow_sh_update(void)213 void wow_sh_update(void)
214 {
215 }
216