src/mess/audio/mea8000.c
| r3147 | r3148 | |
| 18 | 18 | All parameters, including filter parameters, are smoothly interpolated |
| 19 | 19 | for the duration of a frame (8ms, 16ms, 32ms, or 64 ms). |
| 20 | 20 | |
| 21 | | IMPORTANT NOTE: |
| 22 | | Emulation is not satisfactory and needs some work! |
| 23 | | Control logics & quantisation tables seem OK. |
| 24 | | However, the voice is terrible, you can barely recognise the words. |
| 25 | | It may be a problem with the interpolation or the filtering logics. |
| 26 | | |
| 27 | 21 | TODO: |
| 28 | | - improve accuracy and sound quality!!! |
| 29 | 22 | - REQ output pin |
| 30 | 23 | - optimize mea8000_compute_sample |
| 31 | 24 | - should we accept new frames in slow-stop mode ? |
| r3147 | r3148 | |
| 247 | 240 | } |
| 248 | 241 | |
| 249 | 242 | |
| 243 | #ifndef FLOAT_MODE /* UINT16 version */ |
| 250 | 244 | |
| 251 | | /* linear interpolation */ |
| 252 | | static int mea8000_interp_i( UINT16 org, UINT16 dst ) |
| 253 | | { |
| 254 | | return org + (((dst-org) * mea8000.framepos) >> mea8000.framelog); |
| 255 | | } |
| 256 | 245 | |
| 257 | 246 | |
| 258 | | #ifdef UNUSED_FUNCTION |
| 259 | 247 | /* linear interpolation */ |
| 260 | | static double mea8000_interp_f( double org, double dst ) |
| 248 | static int mea8000_interp( UINT16 org, UINT16 dst ) |
| 261 | 249 | { |
| 262 | | return org + ((dst-org) * mea8000.framepos) / mea8000.framelength; |
| 250 | return org + (((dst-org) * mea8000.framepos) >> mea8000.framelog); |
| 263 | 251 | } |
| 264 | | #endif |
| 265 | 252 | |
| 266 | 253 | |
| 267 | | #ifndef FLOAT_MODE /* UINT16 version */ |
| 268 | 254 | |
| 269 | | |
| 270 | | |
| 271 | 255 | /* apply second order digital filter, sampling at F0 */ |
| 272 | 256 | static int mea8000_filter_step( int i, int input ) |
| 273 | 257 | { |
| 274 | 258 | /* frequency */ |
| 275 | | int fm = mea8000_interp_i(mea8000.f[i].last_fm, mea8000.f[i].fm); |
| 259 | int fm = mea8000_interp(mea8000.f[i].last_fm, mea8000.f[i].fm); |
| 276 | 260 | /* bandwidth */ |
| 277 | | int bw = mea8000_interp_i(mea8000.f[i].last_bw, mea8000.f[i].bw); |
| 261 | int bw = mea8000_interp(mea8000.f[i].last_bw, mea8000.f[i].bw); |
| 278 | 262 | /* filter coefficients */ |
| 279 | 263 | int b = (cos_table[fm] * exp_table[bw]) / QUANT; |
| 280 | 264 | int c = exp2_table[bw]; |
| r3147 | r3148 | |
| 286 | 270 | } |
| 287 | 271 | |
| 288 | 272 | |
| 289 | | |
| 290 | 273 | /* random waveform, in [-QUANT,QUANT] */ |
| 291 | 274 | static int mea8000_noise_gen( void ) |
| 292 | 275 | { |
| r3147 | r3148 | |
| 299 | 282 | /* sawtooth waveform at F0, in [-QUANT,QUANT] */ |
| 300 | 283 | static int mea8000_freq_gen( void ) |
| 301 | 284 | { |
| 302 | | int pitch = mea8000_interp_i(mea8000.last_pitch, mea8000.pitch); |
| 285 | int pitch = mea8000_interp(mea8000.last_pitch, mea8000.pitch); |
| 303 | 286 | mea8000.phi = (mea8000.phi + pitch) % F0; |
| 304 | 287 | return ((mea8000.phi % F0) * QUANT * 2) / F0 - QUANT; |
| 305 | 288 | } |
| r3147 | r3148 | |
| 310 | 293 | static int mea8000_compute_sample( void ) |
| 311 | 294 | { |
| 312 | 295 | int i; |
| 313 | | int in, out; |
| 314 | | int ampl = mea8000_interp_i(mea8000.last_ampl, mea8000.ampl); |
| 296 | int out; |
| 297 | int ampl = mea8000_interp(mea8000.last_ampl, mea8000.ampl); |
| 315 | 298 | |
| 316 | 299 | if (mea8000.noise) |
| 317 | | in = mea8000_noise_gen(); |
| 300 | out = mea8000_noise_gen(); |
| 318 | 301 | else |
| 319 | | in = mea8000_freq_gen(); |
| 320 | | in = (in * ampl) / 1024; |
| 302 | out = mea8000_freq_gen(); |
| 321 | 303 | |
| 304 | out *= ampl / 32; |
| 305 | |
| 322 | 306 | for (i=0; i<4; i++) |
| 323 | | in = mea8000_filter_step(i, in); |
| 324 | | out = in; |
| 307 | { |
| 308 | out = mea8000_filter_step(i, out); |
| 309 | } |
| 310 | |
| 311 | if ( out > 32767 ) |
| 312 | out = 32767; |
| 313 | if ( out < -32767) |
| 314 | out = -32767; |
| 325 | 315 | return out; |
| 326 | 316 | } |
| 327 | 317 | |
| r3147 | r3148 | |
| 331 | 321 | |
| 332 | 322 | |
| 333 | 323 | |
| 324 | /* linear interpolation */ |
| 325 | static double mea8000_interp( double org, double dst ) |
| 326 | { |
| 327 | return org + ((dst-org) * mea8000.framepos) / mea8000.framelength; |
| 328 | } |
| 329 | |
| 330 | |
| 331 | |
| 332 | /* apply second order digital filter, sampling at F0 */ |
| 334 | 333 | static double mea8000_filter_step( int i, double input ) |
| 335 | 334 | { |
| 336 | | double fm = mea8000_interp_f(mea8000.f[i].last_fm, mea8000.f[i].fm); |
| 337 | | double bw = mea8000_interp_f(mea8000.f[i].last_bw, mea8000.f[i].bw); |
| 335 | double fm = mea8000_interp(mea8000.f[i].last_fm, mea8000.f[i].fm); |
| 336 | double bw = mea8000_interp(mea8000.f[i].last_bw, mea8000.f[i].bw); |
| 338 | 337 | double b = 2.*cos(2.*M_PI*fm/F0); |
| 339 | 338 | double c = -exp(-M_PI*bw/F0); |
| 340 | 339 | double next_output = |
| r3147 | r3148 | |
| 359 | 358 | /* sawtooth waveform at F0, in [-1,1] */ |
| 360 | 359 | static double mea8000_freq_gen( void ) |
| 361 | 360 | { |
| 362 | | int pitch = mea8000_interp_i(mea8000.last_pitch, mea8000.pitch); |
| 361 | int pitch = mea8000_interp(mea8000.last_pitch, mea8000.pitch); |
| 363 | 362 | mea8000.phi += pitch; |
| 364 | 363 | return (double) (mea8000.phi % F0) / (F0/2.) - 1.; |
| 365 | 364 | } |
| 366 | 365 | |
| 367 | 366 | |
| 368 | | |
| 367 | /* sample in [-32767,32767], at F0 */ |
| 369 | 368 | static int mea8000_compute_sample( void ) |
| 370 | 369 | { |
| 371 | 370 | int i; |
| 372 | | double in, out; |
| 373 | | double ampl = mea8000_interp_f(mea8000.last_ampl, mea8000.ampl); |
| 371 | double out; |
| 372 | double ampl = mea8000_interp(8.*mea8000.last_ampl, 8.*mea8000.ampl); |
| 374 | 373 | |
| 375 | 374 | if (mea8000.noise) |
| 376 | | in = mea8000_noise_gen(); |
| 375 | out = mea8000_noise_gen(); |
| 377 | 376 | else |
| 378 | | in = mea8000_freq_gen(); |
| 379 | | in *= ampl / 1000.; |
| 377 | out = mea8000_freq_gen(); |
| 380 | 378 | |
| 379 | out *= ampl; |
| 380 | |
| 381 | 381 | for (i=0; i<4; i++) |
| 382 | 382 | { |
| 383 | | in = mea8000_filter_step(i, in); |
| 384 | | out += in; |
| 383 | out = mea8000_filter_step(i, out); |
| 385 | 384 | } |
| 385 | |
| 386 | if ( out > 32767 ) |
| 387 | out = 32767; |
| 388 | if ( out < -32767) |
| 389 | out = -32767; |
| 386 | 390 | return out; |
| 387 | 391 | } |
| 388 | 392 | |
| 389 | 393 | |
| 390 | | |
| 391 | 394 | #endif |
| 392 | 395 | |
| 393 | 396 | |
| r3147 | r3148 | |
| 462 | 465 | /* enter stop mode */ |
| 463 | 466 | timer_reset( mea8000.timer, attotime_never ); |
| 464 | 467 | mea8000.state = MEA8000_STOPPED; |
| 465 | | dac_signed_data_16_w(mea8000.channel, 0); |
| 468 | dac_signed_data_16_w(mea8000.channel, 0x8000); |
| 466 | 469 | } |
| 467 | 470 | |
| 468 | 471 | |
| r3147 | r3148 | |
| 477 | 480 | /* sample is really computed only every 8-th time */ |
| 478 | 481 | mea8000.lastsample = mea8000.sample; |
| 479 | 482 | mea8000.sample = mea8000_compute_sample(); |
| 480 | | dac_signed_data_16_w(mea8000.channel, mea8000.lastsample); |
| 483 | dac_signed_data_16_w(mea8000.channel, 0x8000+mea8000.lastsample); |
| 481 | 484 | } |
| 482 | 485 | else |
| 483 | 486 | { |
| r3147 | r3148 | |
| 485 | 488 | int sample = |
| 486 | 489 | mea8000.lastsample + |
| 487 | 490 | ((pos*(mea8000.sample-mea8000.lastsample)) / SUPERSAMPLING); |
| 488 | | dac_signed_data_16_w(mea8000.channel, sample); |
| 491 | dac_signed_data_16_w(mea8000.channel, 0x8000+sample); |
| 489 | 492 | } |
| 490 | 493 | |
| 491 | 494 | mea8000.framepos++; |
| 492 | | if (mea8000.framepos == mea8000.framelength) |
| 495 | if (mea8000.framepos >= mea8000.framelength) |
| 493 | 496 | { |
| 494 | 497 | mea8000_shift_frame(); |
| 495 | 498 | /* end of frame */ |