1 /******************************************************************************
2 
3      TMS5110 interface
4 
5      slightly modified from 5220intf by Jarek Burczynski
6 
7      Written for MAME by Frank Palazzolo
8      With help from Neill Corlett
9      Additional tweaking by Aaron Giles
10 
11 ******************************************************************************/
12 
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <math.h>
16 
17 #include "driver.h"
18 #include "tms5110.h"
19 
20 
21 #define MAX_SAMPLE_CHUNK	10000
22 
23 #define FRAC_BITS			14
24 #define FRAC_ONE			(1 << FRAC_BITS)
25 #define FRAC_MASK			(FRAC_ONE - 1)
26 
27 
28 /* the state of the streamed output */
29 static const struct TMS5110interface *intf;
30 static INT16 last_sample, curr_sample;
31 static UINT32 source_step;
32 static UINT32 source_pos;
33 static int stream;
34 
35 
36 /* static function prototypes */
37 static void tms5110_update(int ch, INT16 *buffer, int length);
38 
39 
40 
41 /******************************************************************************
42 
43      tms5110_sh_start -- allocate buffers and reset the 5110
44 
45 ******************************************************************************/
46 
tms5110_sh_start(const struct MachineSound * msound)47 int tms5110_sh_start(const struct MachineSound *msound)
48 {
49     intf = msound->sound_interface;
50 
51     if (intf->M0_callback==NULL)
52     {
53 	logerror("\n file: 5110intf.c, tms5110_sh_start(), line 53:\n  Missing _mandatory_ 'M0_callback' function pointer in the TMS5110 interface\n  This function is used by TMS5110 to call for a single bits\n  needed to generate the speech\n  Aborting startup...\n");
54 	return 1;
55     }
56     tms5110_set_M0_callback( intf->M0_callback );
57 
58     /* reset the 5110 */
59     tms5110_reset();
60 
61     /* set the initial frequency */
62     stream = -1;
63     tms5110_set_frequency(intf->baseclock);
64     source_pos = 0;
65     last_sample = curr_sample = 0;
66 
67 	/* initialize a stream */
68 	stream = stream_init("TMS5110", intf->mixing_level, Machine->sample_rate, 0, tms5110_update);
69 	if (stream == -1)
70 		return 1;
71 
72     /* request a sound channel */
73     return 0;
74 }
75 
76 
77 
78 /******************************************************************************
79 
80      tms5110_sh_stop -- free buffers
81 
82 ******************************************************************************/
83 
tms5110_sh_stop(void)84 void tms5110_sh_stop(void)
85 {
86 }
87 
88 
89 
90 /******************************************************************************
91 
92      tms5110_sh_update -- update the sound chip
93 
94 ******************************************************************************/
95 
tms5110_sh_update(void)96 void tms5110_sh_update(void)
97 {
98 }
99 
100 
101 
102 /******************************************************************************
103 
104      tms5110_CTL_w -- write Control Command to the sound chip
105 commands like Speech, Reset, etc., are loaded into the chip via the CTL pins
106 
107 ******************************************************************************/
108 
WRITE_HANDLER(tms5110_CTL_w)109 WRITE_HANDLER( tms5110_CTL_w )
110 {
111     /* bring up to date first */
112     stream_update(stream, 0);
113     tms5110_CTL_set(data);
114 }
115 
116 /******************************************************************************
117 
118      tms5110_PDC_w -- write to PDC pin on the sound chip
119 
120 ******************************************************************************/
121 
WRITE_HANDLER(tms5110_PDC_w)122 WRITE_HANDLER( tms5110_PDC_w )
123 {
124     /* bring up to date first */
125     stream_update(stream, 0);
126     tms5110_PDC_set(data);
127 }
128 
129 
130 
131 /******************************************************************************
132 
133      tms5110_status_r -- read status from the sound chip
134 
135 ******************************************************************************/
136 
READ_HANDLER(tms5110_status_r)137 READ_HANDLER( tms5110_status_r )
138 {
139     /* bring up to date first */
140     stream_update(stream, 0);
141     return tms5110_status_read();
142 }
143 
144 
145 
146 /******************************************************************************
147 
148      tms5110_ready_r -- return the not ready status from the sound chip
149 
150 ******************************************************************************/
151 
tms5110_ready_r(void)152 int tms5110_ready_r(void)
153 {
154     /* bring up to date first */
155     stream_update(stream, 0);
156     return tms5110_ready_read();
157 }
158 
159 
160 
161 /******************************************************************************
162 
163      tms5110_update -- update the sound chip so that it is in sync with CPU execution
164 
165 ******************************************************************************/
166 
tms5110_update(int ch,INT16 * buffer,int length)167 static void tms5110_update(int ch, INT16 *buffer, int length)
168 {
169 	INT16 sample_data[MAX_SAMPLE_CHUNK], *curr_data = sample_data;
170 	INT16 prev = last_sample, curr = curr_sample;
171 	UINT32 final_pos;
172 	UINT32 new_samples;
173 
174 	/* finish off the current sample */
175 	if (source_pos > 0)
176 	{
177 		/* interpolate */
178 		while (length > 0 && source_pos < FRAC_ONE)
179 		{
180 			*buffer++ = (((INT32)prev * (FRAC_ONE - source_pos)) + ((INT32)curr * source_pos)) >> FRAC_BITS;
181 			source_pos += source_step;
182 			length--;
183 		}
184 
185 		/* if we're over, continue; otherwise, we're done */
186 		if (source_pos >= FRAC_ONE)
187 			source_pos -= FRAC_ONE;
188 		else
189 		{
190 			tms5110_process(sample_data, 0);
191 			return;
192 		}
193 	}
194 
195 	/* compute how many new samples we need */
196 	final_pos = source_pos + length * source_step;
197 	new_samples = (final_pos + FRAC_ONE - 1) >> FRAC_BITS;
198 	if (new_samples > MAX_SAMPLE_CHUNK)
199 		new_samples = MAX_SAMPLE_CHUNK;
200 
201 	/* generate them into our buffer */
202 	tms5110_process(sample_data, new_samples);
203 	prev = curr;
204 	curr = *curr_data++;
205 
206 	/* then sample-rate convert with linear interpolation */
207 	while (length > 0)
208 	{
209 		/* interpolate */
210 		while (length > 0 && source_pos < FRAC_ONE)
211 		{
212 			*buffer++ = (((INT32)prev * (FRAC_ONE - source_pos)) + ((INT32)curr * source_pos)) >> FRAC_BITS;
213 			source_pos += source_step;
214 			length--;
215 		}
216 
217 		/* if we're over, grab the next samples */
218 		if (source_pos >= FRAC_ONE)
219 		{
220 			source_pos -= FRAC_ONE;
221 			prev = curr;
222 			curr = *curr_data++;
223 		}
224 	}
225 
226 	/* remember the last samples */
227 	last_sample = prev;
228 	curr_sample = curr;
229 }
230 
231 
232 
233 /******************************************************************************
234 
235      tms5110_set_frequency -- adjusts the playback frequency
236 
237 ******************************************************************************/
238 
tms5110_set_frequency(int frequency)239 void tms5110_set_frequency(int frequency)
240 {
241 	/* skip if output frequency is zero */
242 	if (!Machine->sample_rate)
243 		return;
244 
245 	/* update the stream and compute a new step size */
246 	if (stream != -1)
247 		stream_update(stream, 0);
248 	source_step = (UINT32)((double)(frequency / 80) * (double)FRAC_ONE / (double)Machine->sample_rate);
249 }
250