OSDN Git Service

[VM][WIP] Apply some devices merging upstream 2018-10-05.This still not build.WIP.
[csp-qt/common_source_project-fm7.git] / source / src / vm / msm5205.cpp
1 // license:BSD-3-Clause
2 // copyright-holders:Aaron Giles
3 /*
4  *   streaming ADPCM driver
5  *   by Aaron Giles
6  *
7  *   Library to transcode from an ADPCM source to raw PCM.
8  *   Written by Buffoni Mirko in 08/06/97
9  *   References: various sources and documents.
10  *
11  *   HJB 08/31/98
12  *   modified to use an automatically selected oversampling factor
13  *   for the current sample rate
14  *
15  *   01/06/99
16  *    separate MSM5205 emulator form adpcm.c and some fix
17  *
18  *   07/29/12
19  *    added basic support for the MSM6585
20  */
21
22 #include <math.h>
23 #include "msm5205.h"
24
25 #define EVENT_TIMER     0
26
27 /*
28
29     MSM 5205 ADPCM chip:
30
31     Data is streamed from a CPU by means of a clock generated on the chip.
32
33     A reset signal is set high or low to determine whether playback (and interrupts) are occurring.
34
35   MSM6585: is an upgraded MSM5205 voice synth IC.
36    Improvements:
37     More precise internal DA converter
38     Built in low-pass filter
39     Expanded sampling frequency
40
41    Differences between MSM6585 & MSM5205:
42
43                               MSM6586          MSM5205
44     Master clock frequency    640kHz           384kHz
45     Sampling frequency        4k/8k/16k/32kHz  4k/6k/8kHz
46     ADPCM bit length          4-bit            3-bit/4-bit
47     DA converter              12-bit           10-bit
48     Low-pass filter           -40dB/oct        N/A
49     Overflow prevent circuit  Included         N/A
50
51     Timer callback at VCLK low edge on MSM5205 (at rising edge on MSM6585)
52
53    TODO:
54    - lowpass filter for MSM6585
55
56  */
57
58 void MSM5205::initialize()
59 {
60         DEVICE::initialize();
61 //      m_mod_clock = clock();
62 //      m_vclk_cb.resolve();
63
64         /* compute the difference tables */
65         compute_tables();
66
67         /* stream system initialize */
68 //      m_stream = machine().sound().stream_alloc(*this, 0, 1, clock());
69 //      m_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(msm5205_device::vclk_callback), this));
70         m_timer = -1;
71 }
72
73 //-------------------------------------------------
74 //  device_reset - device-specific reset
75 //-------------------------------------------------
76
77 void MSM5205::reset()
78 {
79         /* initialize work */
80         m_data    = 0;
81         m_vclk    = 0;
82         m_reset   = 0;
83         m_signal  = 0;
84         m_step    = 0;
85
86         /* initialize clock */
87         change_clock_w(m_mod_clock);
88
89         /* timer and bitwidth set */
90         playmode_w(m_select);
91 }
92
93
94 /*
95  * ADPCM lookup table
96  */
97
98 /* step size index shift table */
99 static const int index_shift[8] = { -1, -1, -1, -1, 2, 4, 6, 8 };
100
101 /*
102  *   Compute the difference table
103  */
104
105 void MSM5205::compute_tables()
106 {
107         /* nibble to bit map */
108         static const int nbl2bit[16][4] =
109         {
110                 { 1, 0, 0, 0}, { 1, 0, 0, 1}, { 1, 0, 1, 0}, { 1, 0, 1, 1},
111                 { 1, 1, 0, 0}, { 1, 1, 0, 1}, { 1, 1, 1, 0}, { 1, 1, 1, 1},
112                 {-1, 0, 0, 0}, {-1, 0, 0, 1}, {-1, 0, 1, 0}, {-1, 0, 1, 1},
113                 {-1, 1, 0, 0}, {-1, 1, 0, 1}, {-1, 1, 1, 0}, {-1, 1, 1, 1}
114         };
115
116         int step, nib;
117
118         /* loop over all possible steps */
119         for (step = 0; step <= 48; step++)
120         {
121                 /* compute the step value */
122                 int stepval = (int)floor (16.0 * pow (11.0 / 10.0, (double)step));
123
124                 /* loop over all nibbles and compute the difference */
125                 for (nib = 0; nib < 16; nib++)
126                 {
127                         m_diff_lookup[step*16 + nib] = nbl2bit[nib][0] *
128                                 (stepval   * nbl2bit[nib][1] +
129                                         stepval/2 * nbl2bit[nib][2] +
130                                         stepval/4 * nbl2bit[nib][3] +
131                                         stepval/8);
132                 }
133         }
134 }
135
136 /* timer callback at VCLK low edge on MSM5205 (at rising edge on MSM6585) */
137 void MSM5205::event_callback(int event_id, int err)
138 {
139         if(event_id == EVENT_TIMER)
140         {
141                 int val;
142                 int new_signal;
143
144                 /* callback user handler and latch next data */
145 //              if (!m_vclk_cb.isnull())
146 //                      m_vclk_cb(1);
147                 write_signals(&m_vclk_cb, 0xffffffff);
148
149                 /* reset check at last hiedge of VCLK */
150                 if (m_reset)
151                 {
152                         new_signal = 0;
153                         m_step = 0;
154                 }
155                 else
156                 {
157                         /* update signal */
158                         /* !! MSM5205 has internal 12bit decoding, signal width is 0 to 8191 !! */
159                         val = m_data;
160                         new_signal = m_signal + m_diff_lookup[m_step * 16 + (val & 15)];
161
162                         if (new_signal > 2047) new_signal = 2047;
163                         else if (new_signal < -2048) new_signal = -2048;
164
165                         m_step += index_shift[val & 7];
166
167                         if (m_step > 48) m_step = 48;
168                         else if (m_step < 0) m_step = 0;
169                 }
170
171                 /* update when signal changed */
172                 if( m_signal != new_signal)
173                 {
174 //                      m_stream->update();
175                         m_signal = new_signal;
176                 }
177         }
178 }
179
180
181
182 /*
183  *    Handle an update of the vclk status of a chip (1 is reset ON, 0 is reset OFF)
184  *    This function can use selector = MSM5205_SEX only
185  */
186 void MSM5205::vclk_w(int vclk)
187 {
188         if (m_prescaler != 0)
189         {
190 //              logerror("error: msm5205_vclk_w() called with chip = '%s', but VCLK selected master mode\n", this->device().tag());
191         }
192         else
193         {
194                 if (m_vclk != vclk)
195                 {
196                         m_vclk = vclk;
197                         if (!vclk)
198 //                              vclk_callback(this, 0);
199                                 event_callback(EVENT_TIMER, 0);
200                 }
201         }
202 }
203
204 /*
205  *    Handle an update of the reset status of a chip (1 is reset ON, 0 is reset OFF)
206  */
207
208 void MSM5205::reset_w(int reset)
209 {
210         touch_sound();
211         m_reset = reset;
212         set_realtime_render(this, (m_reset == 0));
213 }
214
215 /*
216  *    Handle an update of the data to the chip
217  */
218
219 void MSM5205::data_w(int data)
220 {
221         touch_sound();
222         if (m_bitwidth == 4)
223                 m_data = data & 0x0f;
224         else
225                 m_data = (data & 0x07) << 1; /* unknown */
226 }
227
228 /*
229  *    Handle a change of the selector
230  */
231
232 void MSM5205::playmode_w(int select)
233 {
234         static const int prescaler_table[2][4] =
235         {
236                 { 96, 48, 64,  0},
237                 {160, 40, 80, 20}
238         };
239         int prescaler = prescaler_table[(select >> 3) & 1][select & 3];
240         int bitwidth = (select & 4) ? 4 : 3;
241
242         if (m_prescaler != prescaler)
243         {
244                 touch_sound();
245 //              m_stream->update();
246
247                 touch_sound();
248                 m_prescaler = prescaler;
249
250                 /* timer set */
251                 if (prescaler)
252                 {
253 //                      attotime period = attotime::from_hz(m_mod_clock) * prescaler;
254 //                      m_timer->adjust(period, 0, period);
255                         double period = 1000000.0 / m_mod_clock * prescaler;
256                         if (m_timer != -1) {
257                                 cancel_event(this, m_timer);
258                         }
259                         register_event(this, EVENT_TIMER, period, true, &m_timer);
260                 }
261                 else
262                 {
263 //                      m_timer->adjust(attotime::never);
264                         if (m_timer != -1) {
265                                 cancel_event(this, m_timer);
266                                 m_timer = -1;
267                         }
268                 }
269         }
270
271         if (m_bitwidth != bitwidth)
272         {
273                 touch_sound();
274 //              m_stream->update();
275                 m_bitwidth = bitwidth;
276         }
277 }
278
279
280 void MSM5205::set_volume(int volume)
281 {
282         touch_sound();
283         volume_m = (int)(1024.0 * (max(0, min(100, volume)) / 100.0));
284 }
285
286 void MSM5205::change_clock_w(int32_t clock)
287 {
288         m_mod_clock = clock;
289
290         if (m_prescaler != 0) {
291                 touch_sound();
292                 double period = 1000000.0 / m_mod_clock * m_prescaler;
293                 if(m_timer != -1) {
294                         cancel_event(this, m_timer);
295                 }
296                 register_event(this, EVENT_TIMER, period, true, &m_timer);
297         } else {
298                 touch_sound();
299                 if(m_timer != -1) {
300                         cancel_event(this, m_timer);
301                         m_timer = -1;
302                 }
303         }
304 }
305
306
307 //-------------------------------------------------
308 //  sound_stream_update - handle a stream update
309 //-------------------------------------------------
310
311 void MSM5205::mix(int32_t* buffer, int cnt)
312 {
313         /* if this voice is active */
314         if(m_signal)
315         {
316                 int32_t val = apply_volume(m_signal * 16, volume_m);
317                 int32_t val_l = apply_volume(val, volume_l);
318                 int32_t val_r = apply_volume(val, volume_r);
319                 
320                 for(int i = 0; i < cnt; i++)
321                 {
322                         *buffer++ += val_l; // L
323                         *buffer++ += val_r; // R
324                 }
325         }
326 }
327
328 void MSM5205::set_volume(int ch, int decibel_l, int decibel_r)
329 {
330         volume_l = decibel_to_volume(decibel_l);
331         volume_r = decibel_to_volume(decibel_r);
332 }
333
334 #define STATE_VERSION   1
335
336 bool MSM5205::process_state(FILEIO* state_fio, bool loading)
337 {
338         if(!state_fio->StateCheckUint32(STATE_VERSION)) {
339                 return false;
340         }
341         if(!state_fio->StateCheckInt32(this_device_id)) {
342                 return false;
343         }
344         state_fio->StateInt32(m_mod_clock);
345         state_fio->StateInt32(m_timer);
346         state_fio->StateInt32(m_data);
347         state_fio->StateInt32(m_vclk);
348         state_fio->StateInt32(m_reset);
349         state_fio->StateInt32(m_prescaler);
350         state_fio->StateInt32(m_bitwidth);
351         state_fio->StateInt32(m_signal);
352         state_fio->StateInt32(m_step);
353         state_fio->StateInt32(m_select);
354         state_fio->StateInt32(volume_m);
355         return true;
356 }
357