trunk/src/emu/sound/es8712.c
| r21175 | r21176 | |
| 18 | 18 | #define MAX_SAMPLE_CHUNK 10000 |
| 19 | 19 | |
| 20 | 20 | |
| 21 | | /* struct describing a playing ADPCM chip */ |
| 22 | | struct es8712_state |
| 21 | /* step size index shift table */ |
| 22 | static const int index_shift[8] = { -1, -1, -1, -1, 2, 4, 6, 8 }; |
| 23 | |
| 24 | /* lookup table for the precomputed difference */ |
| 25 | static int diff_lookup[49*16]; |
| 26 | |
| 27 | |
| 28 | // device type definition |
| 29 | const device_type ES8712 = &device_creator<es8712_device>; |
| 30 | |
| 31 | |
| 32 | //************************************************************************** |
| 33 | // LIVE DEVICE |
| 34 | //************************************************************************** |
| 35 | |
| 36 | //------------------------------------------------- |
| 37 | // es8712_device - constructor |
| 38 | //------------------------------------------------- |
| 39 | |
| 40 | es8712_device::es8712_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) |
| 41 | : device_t(mconfig, ES8712, "ES8712", tag, owner, clock), |
| 42 | device_sound_interface(mconfig, *this), |
| 43 | m_playing(0), |
| 44 | m_base_offset(0), |
| 45 | m_sample(0), |
| 46 | m_count(0), |
| 47 | m_signal(0), |
| 48 | m_step(0), |
| 49 | m_start(0), |
| 50 | m_end(0), |
| 51 | m_repeat(0), |
| 52 | m_bank_offset(0), |
| 53 | m_region_base(NULL), |
| 54 | m_stream(NULL) |
| 23 | 55 | { |
| 24 | | UINT8 playing; /* 1 if we're actively playing */ |
| 56 | } |
| 25 | 57 | |
| 26 | | UINT32 base_offset; /* pointer to the base memory location */ |
| 27 | | UINT32 sample; /* current sample number */ |
| 28 | | UINT32 count; /* total samples to play */ |
| 29 | 58 | |
| 30 | | UINT32 signal; /* current ADPCM signal */ |
| 31 | | UINT32 step; /* current ADPCM step */ |
| 59 | //------------------------------------------------- |
| 60 | // device_start - start emulation of an ES8712 chip |
| 61 | //------------------------------------------------- |
| 32 | 62 | |
| 33 | | UINT32 start; /* starting address for the next loop */ |
| 34 | | UINT32 end; /* ending address for the next loop */ |
| 35 | | UINT8 repeat; /* Repeat current sample when 1 */ |
| 63 | void es8712_device::device_start() |
| 64 | { |
| 65 | compute_tables(); |
| 36 | 66 | |
| 37 | | INT32 bank_offset; |
| 38 | | UINT8 *region_base; /* pointer to the base of the region */ |
| 39 | | sound_stream *stream; /* which stream are we playing on? */ |
| 40 | | }; |
| 67 | m_start = 0; |
| 68 | m_end = 0; |
| 69 | m_repeat = 0; |
| 41 | 70 | |
| 42 | | /* step size index shift table */ |
| 43 | | static const int index_shift[8] = { -1, -1, -1, -1, 2, 4, 6, 8 }; |
| 71 | m_bank_offset = 0; |
| 72 | m_region_base = *region(); |
| 44 | 73 | |
| 45 | | /* lookup table for the precomputed difference */ |
| 46 | | static int diff_lookup[49*16]; |
| 74 | /* generate the name and create the stream */ |
| 75 | m_stream = stream_alloc(0, 1, clock()); |
| 47 | 76 | |
| 77 | /* initialize the rest of the structure */ |
| 78 | m_signal = -2; |
| 48 | 79 | |
| 49 | | INLINE es8712_state *get_safe_token(device_t *device) |
| 80 | es8712_state_save_register(); |
| 81 | } |
| 82 | |
| 83 | |
| 84 | //------------------------------------------------- |
| 85 | // device_reset - stop emulation of an ES8712-compatible chip |
| 86 | //------------------------------------------------- |
| 87 | |
| 88 | void es8712_device::device_reset() |
| 50 | 89 | { |
| 51 | | assert(device != NULL); |
| 52 | | assert(device->type() == ES8712); |
| 53 | | return (es8712_state *)downcast<es8712_device *>(device)->token(); |
| 90 | if (m_playing) |
| 91 | { |
| 92 | /* update the stream, then turn it off */ |
| 93 | m_stream->update(); |
| 94 | m_playing = 0; |
| 95 | m_repeat = 0; |
| 96 | } |
| 54 | 97 | } |
| 55 | 98 | |
| 56 | 99 | |
| 57 | | /********************************************************************************************** |
| 100 | //------------------------------------------------- |
| 101 | // sound_stream_update - update the sound chip so that it is in sync with CPU execution |
| 102 | //------------------------------------------------- |
| 58 | 103 | |
| 59 | | compute_tables -- compute the difference tables |
| 104 | void es8712_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) |
| 105 | { |
| 106 | stream_sample_t *buffer = outputs[0]; |
| 60 | 107 | |
| 61 | | ***********************************************************************************************/ |
| 108 | /* generate them into our buffer */ |
| 109 | generate_adpcm(buffer, samples); |
| 110 | } |
| 62 | 111 | |
| 63 | | static void compute_tables(void) |
| 112 | |
| 113 | //------------------------------------------------- |
| 114 | // compute_tables -- compute the difference tables |
| 115 | //------------------------------------------------- |
| 116 | |
| 117 | void es8712_device::compute_tables() |
| 64 | 118 | { |
| 65 | 119 | /* nibble to bit map */ |
| 66 | 120 | static const int nbl2bit[16][4] = |
| r21175 | r21176 | |
| 92 | 146 | } |
| 93 | 147 | |
| 94 | 148 | |
| 149 | //------------------------------------------------- |
| 150 | // generate_adpcm -- general ADPCM decoding routine |
| 151 | //------------------------------------------------- |
| 95 | 152 | |
| 96 | | /********************************************************************************************** |
| 97 | | |
| 98 | | generate_adpcm -- general ADPCM decoding routine |
| 99 | | |
| 100 | | ***********************************************************************************************/ |
| 101 | | |
| 102 | | static void generate_adpcm(es8712_state *chip, stream_sample_t *buffer, int samples) |
| 153 | void es8712_device::generate_adpcm(stream_sample_t *buffer, int samples) |
| 103 | 154 | { |
| 104 | 155 | /* if this chip is active */ |
| 105 | | if (chip->playing) |
| 156 | if (m_playing) |
| 106 | 157 | { |
| 107 | | UINT8 *base = chip->region_base + chip->bank_offset + chip->base_offset; |
| 108 | | int sample = chip->sample; |
| 109 | | int signal = chip->signal; |
| 110 | | int count = chip->count; |
| 111 | | int step = chip->step; |
| 158 | UINT8 *base = m_region_base + m_bank_offset + m_base_offset; |
| 159 | int sample = m_sample; |
| 160 | int signal = m_signal; |
| 161 | int count = m_count; |
| 162 | int step = m_step; |
| 112 | 163 | int val; |
| 113 | 164 | |
| 114 | 165 | /* loop while we still have samples to generate */ |
| r21175 | r21176 | |
| 138 | 189 | /* next! */ |
| 139 | 190 | if (++sample >= count) |
| 140 | 191 | { |
| 141 | | if (chip->repeat) |
| 192 | if (m_repeat) |
| 142 | 193 | { |
| 143 | 194 | sample = 0; |
| 144 | 195 | signal = -2; |
| r21175 | r21176 | |
| 146 | 197 | } |
| 147 | 198 | else |
| 148 | 199 | { |
| 149 | | chip->playing = 0; |
| 200 | m_playing = 0; |
| 150 | 201 | break; |
| 151 | 202 | } |
| 152 | 203 | } |
| 153 | 204 | } |
| 154 | 205 | |
| 155 | 206 | /* update the parameters */ |
| 156 | | chip->sample = sample; |
| 157 | | chip->signal = signal; |
| 158 | | chip->step = step; |
| 207 | m_sample = sample; |
| 208 | m_signal = signal; |
| 209 | m_step = step; |
| 159 | 210 | } |
| 160 | 211 | |
| 161 | 212 | /* fill the rest with silence */ |
| r21175 | r21176 | |
| 164 | 215 | } |
| 165 | 216 | |
| 166 | 217 | |
| 167 | | /********************************************************************************************** |
| 168 | 218 | |
| 169 | | es8712_update -- update the sound chip so that it is in sync with CPU execution |
| 219 | //------------------------------------------------- |
| 220 | // state save support for MAME |
| 221 | //------------------------------------------------- |
| 170 | 222 | |
| 171 | | ***********************************************************************************************/ |
| 172 | | |
| 173 | | static STREAM_UPDATE( es8712_update ) |
| 223 | void es8712_device::es8712_state_save_register() |
| 174 | 224 | { |
| 175 | | stream_sample_t *buffer = outputs[0]; |
| 176 | | es8712_state *chip = (es8712_state *)param; |
| 225 | save_item(NAME(m_bank_offset)); |
| 177 | 226 | |
| 178 | | /* generate them into our buffer */ |
| 179 | | generate_adpcm(chip, buffer, samples); |
| 180 | | } |
| 227 | save_item(NAME(m_playing)); |
| 228 | save_item(NAME(m_sample)); |
| 229 | save_item(NAME(m_count)); |
| 230 | save_item(NAME(m_signal)); |
| 231 | save_item(NAME(m_step)); |
| 181 | 232 | |
| 233 | save_item(NAME(m_base_offset)); |
| 182 | 234 | |
| 183 | | |
| 184 | | /********************************************************************************************** |
| 185 | | |
| 186 | | state save support for MAME |
| 187 | | |
| 188 | | ***********************************************************************************************/ |
| 189 | | |
| 190 | | static void es8712_state_save_register(es8712_state *chip, device_t *device) |
| 191 | | { |
| 192 | | device->save_item(NAME(chip->bank_offset)); |
| 193 | | |
| 194 | | device->save_item(NAME(chip->playing)); |
| 195 | | device->save_item(NAME(chip->sample)); |
| 196 | | device->save_item(NAME(chip->count)); |
| 197 | | device->save_item(NAME(chip->signal)); |
| 198 | | device->save_item(NAME(chip->step)); |
| 199 | | |
| 200 | | device->save_item(NAME(chip->base_offset)); |
| 201 | | |
| 202 | | device->save_item(NAME(chip->start)); |
| 203 | | device->save_item(NAME(chip->end)); |
| 204 | | device->save_item(NAME(chip->repeat)); |
| 235 | save_item(NAME(m_start)); |
| 236 | save_item(NAME(m_end)); |
| 237 | save_item(NAME(m_repeat)); |
| 205 | 238 | } |
| 206 | 239 | |
| 207 | 240 | |
| 208 | 241 | |
| 209 | | /********************************************************************************************** |
| 210 | 242 | |
| 211 | | DEVICE_START( es8712 ) -- start emulation of an ES8712 chip |
| 243 | //------------------------------------------------- |
| 244 | // es8712_set_bank_base -- set the base of the bank on a given chip |
| 245 | //------------------------------------------------- |
| 212 | 246 | |
| 213 | | ***********************************************************************************************/ |
| 214 | | |
| 215 | | static DEVICE_START( es8712 ) |
| 247 | void es8712_device::set_bank_base(int base) |
| 216 | 248 | { |
| 217 | | es8712_state *chip = get_safe_token(device); |
| 218 | | |
| 219 | | compute_tables(); |
| 220 | | |
| 221 | | chip->start = 0; |
| 222 | | chip->end = 0; |
| 223 | | chip->repeat = 0; |
| 224 | | |
| 225 | | chip->bank_offset = 0; |
| 226 | | chip->region_base = *device->region(); |
| 227 | | |
| 228 | | /* generate the name and create the stream */ |
| 229 | | chip->stream = device->machine().sound().stream_alloc(*device, 0, 1, device->clock(), chip, es8712_update); |
| 230 | | |
| 231 | | /* initialize the rest of the structure */ |
| 232 | | chip->signal = -2; |
| 233 | | |
| 234 | | es8712_state_save_register(chip, device); |
| 249 | m_stream->update(); |
| 250 | m_bank_offset = base; |
| 235 | 251 | } |
| 236 | 252 | |
| 237 | 253 | |
| 254 | //------------------------------------------------- |
| 255 | // es8712_set_frequency -- dynamically adjusts the frequency of a given ADPCM chip |
| 256 | //------------------------------------------------- |
| 238 | 257 | |
| 239 | | /************************************************************************************* |
| 240 | | |
| 241 | | DEVICE_RESET( es8712 ) -- stop emulation of an ES8712-compatible chip |
| 242 | | |
| 243 | | **************************************************************************************/ |
| 244 | | |
| 245 | | static DEVICE_RESET( es8712 ) |
| 258 | void es8712_device::set_frequency(int frequency) |
| 246 | 259 | { |
| 247 | | es8712_state *chip = get_safe_token(device); |
| 248 | | |
| 249 | | if (chip->playing) |
| 250 | | { |
| 251 | | /* update the stream, then turn it off */ |
| 252 | | chip->stream->update(); |
| 253 | | chip->playing = 0; |
| 254 | | chip->repeat = 0; |
| 255 | | } |
| 256 | | } |
| 257 | | |
| 258 | | |
| 259 | | /**************************************************************************** |
| 260 | | |
| 261 | | es8712_set_bank_base -- set the base of the bank on a given chip |
| 262 | | |
| 263 | | *****************************************************************************/ |
| 264 | | |
| 265 | | void es8712_set_bank_base(device_t *device, int base) |
| 266 | | { |
| 267 | | es8712_state *chip = get_safe_token(device); |
| 268 | | chip->stream->update(); |
| 269 | | chip->bank_offset = base; |
| 270 | | } |
| 271 | | |
| 272 | | |
| 273 | | /**************************************************************************** |
| 274 | | |
| 275 | | es8712_set_frequency -- dynamically adjusts the frequency of a given ADPCM chip |
| 276 | | |
| 277 | | *****************************************************************************/ |
| 278 | | |
| 279 | | void es8712_set_frequency(device_t *device, int frequency) |
| 280 | | { |
| 281 | | es8712_state *chip = get_safe_token(device); |
| 282 | | |
| 283 | 260 | /* update the stream and set the new base */ |
| 284 | | chip->stream->update(); |
| 285 | | chip->stream->set_sample_rate(frequency); |
| 261 | m_stream->update(); |
| 262 | m_stream->set_sample_rate(frequency); |
| 286 | 263 | } |
| 287 | 264 | |
| 288 | 265 | |
| 266 | //------------------------------------------------- |
| 267 | // play -- Begin playing the addressed sample |
| 268 | //------------------------------------------------- |
| 289 | 269 | |
| 290 | | /********************************************************************************************** |
| 291 | | |
| 292 | | es8712_play -- Begin playing the addressed sample |
| 293 | | |
| 294 | | ***********************************************************************************************/ |
| 295 | | |
| 296 | | void es8712_play(device_t *device) |
| 270 | void es8712_device::play() |
| 297 | 271 | { |
| 298 | | es8712_state *chip = get_safe_token(device); |
| 299 | | |
| 300 | | |
| 301 | | if (chip->start < chip->end) |
| 272 | if (m_start < m_end) |
| 302 | 273 | { |
| 303 | | if (!chip->playing) |
| 274 | if (!m_playing) |
| 304 | 275 | { |
| 305 | | chip->playing = 1; |
| 306 | | chip->base_offset = chip->start; |
| 307 | | chip->sample = 0; |
| 308 | | chip->count = 2 * (chip->end - chip->start + 1); |
| 309 | | chip->repeat = 0;//1; |
| 276 | m_playing = 1; |
| 277 | m_base_offset = m_start; |
| 278 | m_sample = 0; |
| 279 | m_count = 2 * (m_end - m_start + 1); |
| 280 | m_repeat = 0;//1; |
| 310 | 281 | |
| 311 | 282 | /* also reset the ADPCM parameters */ |
| 312 | | chip->signal = -2; |
| 313 | | chip->step = 0; |
| 283 | m_signal = -2; |
| 284 | m_step = 0; |
| 314 | 285 | } |
| 315 | 286 | } |
| 316 | 287 | /* invalid samples go here */ |
| 317 | 288 | else |
| 318 | 289 | { |
| 319 | | logerror("ES871295:'%s' requested to play invalid sample range %06x-%06x\n",device->tag(),chip->start,chip->end); |
| 290 | logerror("ES871295:'%s' requested to play invalid sample range %06x-%06x\n", tag(), m_start, m_end); |
| 320 | 291 | |
| 321 | | if (chip->playing) |
| 292 | if (m_playing) |
| 322 | 293 | { |
| 323 | 294 | /* update the stream */ |
| 324 | | chip->stream->update(); |
| 325 | | chip->playing = 0; |
| 295 | m_stream->update(); |
| 296 | m_playing = 0; |
| 326 | 297 | } |
| 327 | 298 | } |
| 328 | 299 | } |
| r21175 | r21176 | |
| 353 | 324 | * |
| 354 | 325 | ***********************************************************************************************/ |
| 355 | 326 | |
| 356 | | WRITE8_DEVICE_HANDLER( es8712_w ) |
| 327 | WRITE8_MEMBER( es8712_device::es8712_w ) |
| 357 | 328 | { |
| 358 | | es8712_state *chip = get_safe_token(device); |
| 359 | 329 | switch (offset) |
| 360 | 330 | { |
| 361 | | case 00: chip->start &= 0x000fff00; |
| 362 | | chip->start |= ((data & 0xff) << 0); break; |
| 363 | | case 01: chip->start &= 0x000f00ff; |
| 364 | | chip->start |= ((data & 0xff) << 8); break; |
| 365 | | case 02: chip->start &= 0x0000ffff; |
| 366 | | chip->start |= ((data & 0x0f) << 16); break; |
| 367 | | case 03: chip->end &= 0x000fff00; |
| 368 | | chip->end |= ((data & 0xff) << 0); break; |
| 369 | | case 04: chip->end &= 0x000f00ff; |
| 370 | | chip->end |= ((data & 0xff) << 8); break; |
| 371 | | case 05: chip->end &= 0x0000ffff; |
| 372 | | chip->end |= ((data & 0x0f) << 16); break; |
| 331 | case 00: m_start &= 0x000fff00; |
| 332 | m_start |= ((data & 0xff) << 0); break; |
| 333 | case 01: m_start &= 0x000f00ff; |
| 334 | m_start |= ((data & 0xff) << 8); break; |
| 335 | case 02: m_start &= 0x0000ffff; |
| 336 | m_start |= ((data & 0x0f) << 16); break; |
| 337 | case 03: m_end &= 0x000fff00; |
| 338 | m_end |= ((data & 0xff) << 0); break; |
| 339 | case 04: m_end &= 0x000f00ff; |
| 340 | m_end |= ((data & 0xff) << 8); break; |
| 341 | case 05: m_end &= 0x0000ffff; |
| 342 | m_end |= ((data & 0x0f) << 16); break; |
| 373 | 343 | case 06: |
| 374 | | es8712_play(device); |
| 375 | | break; |
| 344 | play(); break; |
| 376 | 345 | default: break; |
| 377 | 346 | } |
| 378 | | chip->start &= 0xfffff; chip->end &= 0xfffff; |
| 347 | m_start &= 0xfffff; m_end &= 0xfffff; |
| 379 | 348 | } |
| 380 | | |
| 381 | | const device_type ES8712 = &device_creator<es8712_device>; |
| 382 | | |
| 383 | | es8712_device::es8712_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) |
| 384 | | : device_t(mconfig, ES8712, "ES8712", tag, owner, clock), |
| 385 | | device_sound_interface(mconfig, *this) |
| 386 | | { |
| 387 | | m_token = global_alloc_clear(es8712_state); |
| 388 | | } |
| 389 | | |
| 390 | | //------------------------------------------------- |
| 391 | | // device_config_complete - perform any |
| 392 | | // operations now that the configuration is |
| 393 | | // complete |
| 394 | | //------------------------------------------------- |
| 395 | | |
| 396 | | void es8712_device::device_config_complete() |
| 397 | | { |
| 398 | | } |
| 399 | | |
| 400 | | //------------------------------------------------- |
| 401 | | // device_start - device-specific startup |
| 402 | | //------------------------------------------------- |
| 403 | | |
| 404 | | void es8712_device::device_start() |
| 405 | | { |
| 406 | | DEVICE_START_NAME( es8712 )(this); |
| 407 | | } |
| 408 | | |
| 409 | | //------------------------------------------------- |
| 410 | | // device_reset - device-specific reset |
| 411 | | //------------------------------------------------- |
| 412 | | |
| 413 | | void es8712_device::device_reset() |
| 414 | | { |
| 415 | | DEVICE_RESET_NAME( es8712 )(this); |
| 416 | | } |
| 417 | | |
| 418 | | //------------------------------------------------- |
| 419 | | // sound_stream_update - handle a stream update |
| 420 | | //------------------------------------------------- |
| 421 | | |
| 422 | | void es8712_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) |
| 423 | | { |
| 424 | | // should never get here |
| 425 | | fatalerror("sound_stream_update called; not applicable to legacy sound devices\n"); |
| 426 | | } |
trunk/src/emu/sound/k051649.c
| r21175 | r21176 | |
| 29 | 29 | #define DEF_GAIN 8 |
| 30 | 30 | |
| 31 | 31 | |
| 32 | | /* this structure defines the parameters for a channel */ |
| 33 | | struct k051649_sound_channel |
| 34 | | { |
| 35 | | unsigned long counter; |
| 36 | | int frequency; |
| 37 | | int volume; |
| 38 | | int key; |
| 39 | | signed char waveram[32]; |
| 40 | | }; |
| 32 | // device type definition |
| 33 | const device_type K051649 = &device_creator<k051649_device>; |
| 41 | 34 | |
| 42 | | struct k051649_state |
| 43 | | { |
| 44 | | k051649_sound_channel channel_list[5]; |
| 45 | 35 | |
| 46 | | /* global sound parameters */ |
| 47 | | sound_stream * stream; |
| 48 | | int mclock,rate; |
| 36 | //************************************************************************** |
| 37 | // LIVE DEVICE |
| 38 | //************************************************************************** |
| 49 | 39 | |
| 50 | | /* mixer tables and internal buffers */ |
| 51 | | INT16 *mixer_table; |
| 52 | | INT16 *mixer_lookup; |
| 53 | | short *mixer_buffer; |
| 40 | //------------------------------------------------- |
| 41 | // k051649_device - constructor |
| 42 | //------------------------------------------------- |
| 54 | 43 | |
| 55 | | /* chip registers */ |
| 56 | | UINT8 test; |
| 57 | | }; |
| 58 | | |
| 59 | | INLINE k051649_state *get_safe_token(device_t *device) |
| 44 | k051649_device::k051649_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) |
| 45 | : device_t(mconfig, K051649, "K051649", tag, owner, clock), |
| 46 | device_sound_interface(mconfig, *this), |
| 47 | m_stream(NULL), |
| 48 | m_mclock(0), |
| 49 | m_rate(0), |
| 50 | m_mixer_table(NULL), |
| 51 | m_mixer_lookup(NULL), |
| 52 | m_mixer_buffer(NULL), |
| 53 | m_test(0) |
| 60 | 54 | { |
| 61 | | assert(device != NULL); |
| 62 | | assert(device->type() == K051649); |
| 63 | | return (k051649_state *)downcast<k051649_device *>(device)->token(); |
| 64 | 55 | } |
| 65 | 56 | |
| 66 | | /* build a table to divide by the number of voices */ |
| 67 | | static void make_mixer_table(running_machine &machine, k051649_state *info, int voices) |
| 57 | |
| 58 | //------------------------------------------------- |
| 59 | // device_start - device-specific startup |
| 60 | //------------------------------------------------- |
| 61 | |
| 62 | void k051649_device::device_start() |
| 68 | 63 | { |
| 69 | | int i; |
| 64 | // get stream channels |
| 65 | m_rate = clock()/16; |
| 66 | m_stream = stream_alloc(0, 1, m_rate); |
| 67 | m_mclock = clock(); |
| 70 | 68 | |
| 71 | | /* allocate memory */ |
| 72 | | info->mixer_table = auto_alloc_array(machine, INT16, 512 * voices); |
| 69 | // allocate a buffer to mix into - 1 second's worth should be more than enough |
| 70 | m_mixer_buffer = auto_alloc_array(machine(), short, 2 * m_rate); |
| 73 | 71 | |
| 74 | | /* find the middle of the table */ |
| 75 | | info->mixer_lookup = info->mixer_table + (256 * voices); |
| 72 | // build the mixer table |
| 73 | make_mixer_table(5); |
| 74 | } |
| 76 | 75 | |
| 77 | | /* fill in the table - 16 bit case */ |
| 78 | | for (i = 0; i < (voices * 256); i++) |
| 79 | | { |
| 80 | | int val = i * DEF_GAIN * 16 / voices; |
| 81 | | if (val > 32767) val = 32767; |
| 82 | | info->mixer_lookup[ i] = val; |
| 83 | | info->mixer_lookup[-i] = -val; |
| 76 | |
| 77 | //------------------------------------------------- |
| 78 | // device_reset - device-specific reset |
| 79 | //------------------------------------------------- |
| 80 | |
| 81 | void k051649_device::device_reset() |
| 82 | { |
| 83 | k051649_sound_channel *voice = m_channel_list; |
| 84 | int i; |
| 85 | |
| 86 | // reset all the voices |
| 87 | for (i = 0; i < 5; i++) |
| 88 | { |
| 89 | voice[i].frequency = 0; |
| 90 | voice[i].volume = 0xf; |
| 91 | voice[i].counter = 0; |
| 92 | voice[i].key = 0; |
| 84 | 93 | } |
| 94 | |
| 95 | // other parameters |
| 96 | m_test = 0; |
| 85 | 97 | } |
| 86 | 98 | |
| 87 | 99 | |
| 88 | | /* generate sound to the mix buffer */ |
| 89 | | static STREAM_UPDATE( k051649_update ) |
| 100 | //------------------------------------------------- |
| 101 | // sound_stream_update - handle a stream update |
| 102 | //------------------------------------------------- |
| 103 | |
| 104 | void k051649_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) |
| 90 | 105 | { |
| 91 | | k051649_state *info = (k051649_state *)param; |
| 92 | | k051649_sound_channel *voice=info->channel_list; |
| 106 | k051649_sound_channel *voice=m_channel_list; |
| 93 | 107 | stream_sample_t *buffer = outputs[0]; |
| 94 | 108 | short *mix; |
| 95 | 109 | int i,j; |
| 96 | 110 | |
| 97 | | /* zap the contents of the mixer buffer */ |
| 98 | | memset(info->mixer_buffer, 0, samples * sizeof(short)); |
| 111 | // zap the contents of the mixer buffer |
| 112 | memset(m_mixer_buffer, 0, samples * sizeof(short)); |
| 99 | 113 | |
| 100 | | for (j = 0; j < 5; j++) { |
| 101 | | /* channel is halted for freq < 9 */ |
| 114 | for (j = 0; j < 5; j++) |
| 115 | { |
| 116 | // channel is halted for freq < 9 |
| 102 | 117 | if (voice[j].frequency > 8) |
| 103 | 118 | { |
| 104 | 119 | const signed char *w = voice[j].waveram; |
| 105 | 120 | int v=voice[j].volume * voice[j].key; |
| 106 | 121 | int c=voice[j].counter; |
| 107 | | int step = ((INT64)info->mclock * (1 << FREQ_BITS)) / (float)((voice[j].frequency + 1) * 16 * (info->rate / 32)) + 0.5; |
| 122 | int step = ((INT64)m_mclock * (1 << FREQ_BITS)) / (float)((voice[j].frequency + 1) * 16 * (m_rate / 32)) + 0.5; |
| 108 | 123 | |
| 109 | | mix = info->mixer_buffer; |
| 124 | mix = m_mixer_buffer; |
| 110 | 125 | |
| 111 | | /* add our contribution */ |
| 126 | // add our contribution |
| 112 | 127 | for (i = 0; i < samples; i++) |
| 113 | 128 | { |
| 114 | 129 | int offs; |
| r21175 | r21176 | |
| 118 | 133 | *mix++ += (w[offs] * v)>>3; |
| 119 | 134 | } |
| 120 | 135 | |
| 121 | | /* update the counter for this voice */ |
| 136 | // update the counter for this voice |
| 122 | 137 | voice[j].counter = c; |
| 123 | 138 | } |
| 124 | 139 | } |
| 125 | 140 | |
| 126 | | /* mix it down */ |
| 127 | | mix = info->mixer_buffer; |
| 141 | // mix it down |
| 142 | mix = m_mixer_buffer; |
| 128 | 143 | for (i = 0; i < samples; i++) |
| 129 | | *buffer++ = info->mixer_lookup[*mix++]; |
| 144 | *buffer++ = m_mixer_lookup[*mix++]; |
| 130 | 145 | } |
| 131 | 146 | |
| 132 | | static DEVICE_START( k051649 ) |
| 133 | | { |
| 134 | | k051649_state *info = get_safe_token(device); |
| 135 | 147 | |
| 136 | | /* get stream channels */ |
| 137 | | info->rate = device->clock()/16; |
| 138 | | info->stream = device->machine().sound().stream_alloc(*device, 0, 1, info->rate, info, k051649_update); |
| 139 | | info->mclock = device->clock(); |
| 140 | | |
| 141 | | /* allocate a buffer to mix into - 1 second's worth should be more than enough */ |
| 142 | | info->mixer_buffer = auto_alloc_array(device->machine(), short, 2 * info->rate); |
| 143 | | |
| 144 | | /* build the mixer table */ |
| 145 | | make_mixer_table(device->machine(), info, 5); |
| 146 | | } |
| 147 | | |
| 148 | | static DEVICE_RESET( k051649 ) |
| 149 | | { |
| 150 | | k051649_state *info = get_safe_token(device); |
| 151 | | k051649_sound_channel *voice = info->channel_list; |
| 152 | | int i; |
| 153 | | |
| 154 | | /* reset all the voices */ |
| 155 | | for (i = 0; i < 5; i++) { |
| 156 | | voice[i].frequency = 0; |
| 157 | | voice[i].volume = 0xf; |
| 158 | | voice[i].counter = 0; |
| 159 | | voice[i].key = 0; |
| 160 | | } |
| 161 | | |
| 162 | | /* other parameters */ |
| 163 | | info->test = 0; |
| 164 | | } |
| 165 | | |
| 166 | 148 | /********************************************************************************/ |
| 167 | 149 | |
| 168 | | WRITE8_DEVICE_HANDLER( k051649_waveform_w ) |
| 169 | | { |
| 170 | | k051649_state *info = get_safe_token(device); |
| 171 | 150 | |
| 172 | | /* waveram is read-only? */ |
| 173 | | if (info->test & 0x40 || (info->test & 0x80 && offset >= 0x60)) |
| 151 | WRITE8_MEMBER( k051649_device::k051649_waveform_w ) |
| 152 | { |
| 153 | // waveram is read-only? |
| 154 | if (m_test & 0x40 || (m_test & 0x80 && offset >= 0x60)) |
| 174 | 155 | return; |
| 175 | 156 | |
| 176 | | info->stream->update(); |
| 157 | m_stream->update(); |
| 177 | 158 | |
| 178 | 159 | if (offset >= 0x60) |
| 179 | 160 | { |
| 180 | | /* channel 5 shares waveram with channel 4 */ |
| 181 | | info->channel_list[3].waveram[offset&0x1f]=data; |
| 182 | | info->channel_list[4].waveram[offset&0x1f]=data; |
| 161 | // channel 5 shares waveram with channel 4 |
| 162 | m_channel_list[3].waveram[offset&0x1f]=data; |
| 163 | m_channel_list[4].waveram[offset&0x1f]=data; |
| 183 | 164 | } |
| 184 | 165 | else |
| 185 | | info->channel_list[offset>>5].waveram[offset&0x1f]=data; |
| 166 | m_channel_list[offset>>5].waveram[offset&0x1f]=data; |
| 186 | 167 | } |
| 187 | 168 | |
| 188 | | READ8_DEVICE_HANDLER ( k051649_waveform_r ) |
| 189 | | { |
| 190 | | k051649_state *info = get_safe_token(device); |
| 191 | 169 | |
| 192 | | /* test-register bits 6/7 expose the internal counter */ |
| 193 | | if (info->test & 0xc0) |
| 170 | READ8_MEMBER ( k051649_device::k051649_waveform_r ) |
| 171 | { |
| 172 | // test-register bits 6/7 expose the internal counter |
| 173 | if (m_test & 0xc0) |
| 194 | 174 | { |
| 195 | | info->stream->update(); |
| 175 | m_stream->update(); |
| 196 | 176 | |
| 197 | 177 | if (offset >= 0x60) |
| 198 | | offset += (info->channel_list[3 + (info->test >> 6 & 1)].counter >> FREQ_BITS); |
| 199 | | else if (info->test & 0x40) |
| 200 | | offset += (info->channel_list[offset>>5].counter >> FREQ_BITS); |
| 178 | offset += (m_channel_list[3 + (m_test >> 6 & 1)].counter >> FREQ_BITS); |
| 179 | else if (m_test & 0x40) |
| 180 | offset += (m_channel_list[offset>>5].counter >> FREQ_BITS); |
| 201 | 181 | } |
| 202 | | return info->channel_list[offset>>5].waveram[offset&0x1f]; |
| 182 | return m_channel_list[offset>>5].waveram[offset&0x1f]; |
| 203 | 183 | } |
| 204 | 184 | |
| 205 | | WRITE8_DEVICE_HANDLER( k052539_waveform_w ) |
| 206 | | { |
| 207 | | k051649_state *info = get_safe_token(device); |
| 208 | 185 | |
| 209 | | /* waveram is read-only? */ |
| 210 | | if (info->test & 0x40) |
| 186 | WRITE8_MEMBER( k051649_device::k052539_waveform_w ) |
| 187 | { |
| 188 | // waveram is read-only? |
| 189 | if (m_test & 0x40) |
| 211 | 190 | return; |
| 212 | 191 | |
| 213 | | info->stream->update(); |
| 214 | | info->channel_list[offset>>5].waveram[offset&0x1f]=data; |
| 192 | m_stream->update(); |
| 193 | m_channel_list[offset>>5].waveram[offset&0x1f]=data; |
| 215 | 194 | } |
| 216 | 195 | |
| 217 | | READ8_DEVICE_HANDLER ( k052539_waveform_r ) |
| 218 | | { |
| 219 | | k051649_state *info = get_safe_token(device); |
| 220 | 196 | |
| 221 | | /* test-register bit 6 exposes the internal counter */ |
| 222 | | if (info->test & 0x40) |
| 197 | READ8_MEMBER ( k051649_device::k052539_waveform_r ) |
| 198 | { |
| 199 | // test-register bit 6 exposes the internal counter |
| 200 | if (m_test & 0x40) |
| 223 | 201 | { |
| 224 | | info->stream->update(); |
| 225 | | offset += (info->channel_list[offset>>5].counter >> FREQ_BITS); |
| 202 | m_stream->update(); |
| 203 | offset += (m_channel_list[offset>>5].counter >> FREQ_BITS); |
| 226 | 204 | } |
| 227 | | return info->channel_list[offset>>5].waveram[offset&0x1f]; |
| 205 | return m_channel_list[offset>>5].waveram[offset&0x1f]; |
| 228 | 206 | } |
| 229 | 207 | |
| 230 | | WRITE8_DEVICE_HANDLER( k051649_volume_w ) |
| 208 | |
| 209 | WRITE8_MEMBER( k051649_device::k051649_volume_w ) |
| 231 | 210 | { |
| 232 | | k051649_state *info = get_safe_token(device); |
| 233 | | info->stream->update(); |
| 234 | | info->channel_list[offset&0x7].volume=data&0xf; |
| 211 | m_stream->update(); |
| 212 | m_channel_list[offset&0x7].volume=data&0xf; |
| 235 | 213 | } |
| 236 | 214 | |
| 237 | | WRITE8_DEVICE_HANDLER( k051649_frequency_w ) |
| 215 | |
| 216 | WRITE8_MEMBER( k051649_device::k051649_frequency_w ) |
| 238 | 217 | { |
| 239 | | k051649_state *info = get_safe_token(device); |
| 240 | 218 | int freq_hi = offset & 1; |
| 241 | 219 | offset >>= 1; |
| 242 | 220 | |
| 243 | | info->stream->update(); |
| 221 | m_stream->update(); |
| 244 | 222 | |
| 245 | | /* test-register bit 5 resets the internal counter */ |
| 246 | | if (info->test & 0x20) |
| 247 | | info->channel_list[offset].counter = ~0; |
| 248 | | else if (info->channel_list[offset].frequency < 9) |
| 249 | | info->channel_list[offset].counter |= ((1 << FREQ_BITS) - 1); |
| 223 | // test-register bit 5 resets the internal counter |
| 224 | if (m_test & 0x20) |
| 225 | m_channel_list[offset].counter = ~0; |
| 226 | else if (m_channel_list[offset].frequency < 9) |
| 227 | m_channel_list[offset].counter |= ((1 << FREQ_BITS) - 1); |
| 250 | 228 | |
| 251 | | /* update frequency */ |
| 229 | // update frequency |
| 252 | 230 | if (freq_hi) |
| 253 | | info->channel_list[offset].frequency = (info->channel_list[offset].frequency & 0x0ff) | (data << 8 & 0xf00); |
| 231 | m_channel_list[offset].frequency = (m_channel_list[offset].frequency & 0x0ff) | (data << 8 & 0xf00); |
| 254 | 232 | else |
| 255 | | info->channel_list[offset].frequency = (info->channel_list[offset].frequency & 0xf00) | data; |
| 233 | m_channel_list[offset].frequency = (m_channel_list[offset].frequency & 0xf00) | data; |
| 256 | 234 | } |
| 257 | 235 | |
| 258 | | WRITE8_DEVICE_HANDLER( k051649_keyonoff_w ) |
| 236 | |
| 237 | WRITE8_MEMBER( k051649_device::k051649_keyonoff_w ) |
| 259 | 238 | { |
| 260 | 239 | int i; |
| 261 | | k051649_state *info = get_safe_token(device); |
| 262 | | info->stream->update(); |
| 240 | m_stream->update(); |
| 263 | 241 | |
| 264 | | for (i = 0; i < 5; i++) { |
| 265 | | info->channel_list[i].key=data&1; |
| 242 | for (i = 0; i < 5; i++) |
| 243 | { |
| 244 | m_channel_list[i].key=data&1; |
| 266 | 245 | data >>= 1; |
| 267 | 246 | } |
| 268 | 247 | } |
| 269 | 248 | |
| 270 | | WRITE8_DEVICE_HANDLER( k051649_test_w ) |
| 271 | | { |
| 272 | | k051649_state *info = get_safe_token(device); |
| 273 | | info->test = data; |
| 274 | | } |
| 275 | 249 | |
| 276 | | READ8_DEVICE_HANDLER ( k051649_test_r ) |
| 250 | WRITE8_MEMBER( k051649_device::k051649_test_w ) |
| 277 | 251 | { |
| 278 | | /* reading the test register sets it to $ff! */ |
| 279 | | k051649_test_w(device, space, offset, 0xff); |
| 280 | | return 0xff; |
| 252 | m_test = data; |
| 281 | 253 | } |
| 282 | 254 | |
| 283 | | const device_type K051649 = &device_creator<k051649_device>; |
| 284 | 255 | |
| 285 | | k051649_device::k051649_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) |
| 286 | | : device_t(mconfig, K051649, "K051649", tag, owner, clock), |
| 287 | | device_sound_interface(mconfig, *this) |
| 256 | READ8_MEMBER ( k051649_device::k051649_test_r ) |
| 288 | 257 | { |
| 289 | | m_token = global_alloc_clear(k051649_state); |
| 258 | // reading the test register sets it to $ff! |
| 259 | k051649_test_w(space, offset, 0xff); |
| 260 | return 0xff; |
| 290 | 261 | } |
| 291 | 262 | |
| 292 | | //------------------------------------------------- |
| 293 | | // device_config_complete - perform any |
| 294 | | // operations now that the configuration is |
| 295 | | // complete |
| 296 | | //------------------------------------------------- |
| 297 | 263 | |
| 298 | | void k051649_device::device_config_complete() |
| 299 | | { |
| 300 | | } |
| 301 | | |
| 302 | 264 | //------------------------------------------------- |
| 303 | | // device_start - device-specific startup |
| 265 | // build a table to divide by the number of voices |
| 304 | 266 | //------------------------------------------------- |
| 305 | 267 | |
| 306 | | void k051649_device::device_start() |
| 268 | void k051649_device::make_mixer_table(int voices) |
| 307 | 269 | { |
| 308 | | DEVICE_START_NAME( k051649 )(this); |
| 309 | | } |
| 270 | int i; |
| 310 | 271 | |
| 311 | | //------------------------------------------------- |
| 312 | | // device_reset - device-specific reset |
| 313 | | //------------------------------------------------- |
| 272 | // allocate memory |
| 273 | m_mixer_table = auto_alloc_array(machine(), INT16, 512 * voices); |
| 314 | 274 | |
| 315 | | void k051649_device::device_reset() |
| 316 | | { |
| 317 | | DEVICE_RESET_NAME( k051649 )(this); |
| 275 | // find the middle of the table |
| 276 | m_mixer_lookup = m_mixer_table + (256 * voices); |
| 277 | |
| 278 | // fill in the table - 16 bit case |
| 279 | for (i = 0; i < (voices * 256); i++) |
| 280 | { |
| 281 | int val = i * DEF_GAIN * 16 / voices; |
| 282 | if (val > 32767) val = 32767; |
| 283 | m_mixer_lookup[ i] = val; |
| 284 | m_mixer_lookup[-i] = -val; |
| 285 | } |
| 318 | 286 | } |
| 319 | 287 | |
| 320 | | //------------------------------------------------- |
| 321 | | // sound_stream_update - handle a stream update |
| 322 | | //------------------------------------------------- |
| 323 | 288 | |
| 324 | | void k051649_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) |
| 325 | | { |
| 326 | | // should never get here |
| 327 | | fatalerror("sound_stream_update called; not applicable to legacy sound devices\n"); |
| 328 | | } |
trunk/src/emu/sound/tms3615.c
| r21175 | r21176 | |
| 4 | 4 | #define VMIN 0x0000 |
| 5 | 5 | #define VMAX 0x7fff |
| 6 | 6 | |
| 7 | | #define TONES 13 |
| 7 | static const int divisor[TMS3615_TONES] = { 478, 451, 426, 402, 379, 358, 338, 319, 301, 284, 268, 253, 239 }; |
| 8 | 8 | |
| 9 | | static const int divisor[TONES] = { 478, 451, 426, 402, 379, 358, 338, 319, 301, 284, 268, 253, 239 }; |
| 10 | 9 | |
| 11 | | struct tms_state { |
| 12 | | sound_stream *channel; /* returned by stream_create() */ |
| 13 | | int samplerate; /* output sample rate */ |
| 14 | | int basefreq; /* chip's base frequency */ |
| 15 | | int counter8[TONES]; /* tone frequency counter for 8' */ |
| 16 | | int counter16[TONES]; /* tone frequency counter for 16'*/ |
| 17 | | int output8; /* output signal bits for 8' */ |
| 18 | | int output16; /* output signal bits for 16' */ |
| 19 | | int enable; /* mask which tones to play */ |
| 20 | | }; |
| 10 | // device type definition |
| 11 | const device_type TMS3615 = &device_creator<tms3615_device>; |
| 21 | 12 | |
| 22 | | INLINE tms_state *get_safe_token(device_t *device) |
| 13 | |
| 14 | //************************************************************************** |
| 15 | // LIVE DEVICE |
| 16 | //************************************************************************** |
| 17 | |
| 18 | //------------------------------------------------- |
| 19 | // qsound_device - constructor |
| 20 | //------------------------------------------------- |
| 21 | |
| 22 | tms3615_device::tms3615_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) |
| 23 | : device_t(mconfig, TMS3615, "TMS3615", tag, owner, clock), |
| 24 | device_sound_interface(mconfig, *this), |
| 25 | m_channel(0), |
| 26 | m_samplerate(0), |
| 27 | m_basefreq(0), |
| 28 | m_output8(0), |
| 29 | m_output16(0), |
| 30 | m_enable(0) |
| 23 | 31 | { |
| 24 | | assert(device != NULL); |
| 25 | | assert(device->type() == TMS3615); |
| 26 | | return (tms_state *)downcast<tms3615_device *>(device)->token(); |
| 32 | memset(m_counter8, 0, TMS3615_TONES); |
| 33 | memset(m_counter16, 0, TMS3615_TONES); |
| 27 | 34 | } |
| 28 | 35 | |
| 29 | 36 | |
| 30 | | static STREAM_UPDATE( tms3615_sound_update ) |
| 37 | //------------------------------------------------- |
| 38 | // device_start - device-specific startup |
| 39 | //------------------------------------------------- |
| 40 | |
| 41 | void tms3615_device::device_start() |
| 31 | 42 | { |
| 32 | | tms_state *tms = (tms_state *)param; |
| 33 | | int samplerate = tms->samplerate; |
| 43 | m_channel = stream_alloc(0, 2, clock()/8); |
| 44 | m_samplerate = clock()/8; |
| 45 | m_basefreq = clock(); |
| 46 | } |
| 47 | |
| 48 | |
| 49 | //------------------------------------------------- |
| 50 | // sound_stream_update - handle a stream update |
| 51 | //------------------------------------------------- |
| 52 | |
| 53 | void tms3615_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) |
| 54 | { |
| 55 | int samplerate = m_samplerate; |
| 34 | 56 | stream_sample_t *buffer8 = outputs[TMS3615_FOOTAGE_8]; |
| 35 | 57 | stream_sample_t *buffer16 = outputs[TMS3615_FOOTAGE_16]; |
| 36 | 58 | |
| r21175 | r21176 | |
| 38 | 60 | { |
| 39 | 61 | int sum8 = 0, sum16 = 0, tone = 0; |
| 40 | 62 | |
| 41 | | for (tone = 0; tone < TONES; tone++) |
| 63 | for (tone = 0; tone < TMS3615_TONES; tone++) |
| 42 | 64 | { |
| 43 | 65 | // 8' |
| 44 | 66 | |
| 45 | | tms->counter8[tone] -= (tms->basefreq / divisor[tone]); |
| 67 | m_counter8[tone] -= (m_basefreq / divisor[tone]); |
| 46 | 68 | |
| 47 | | while( tms->counter8[tone] <= 0 ) |
| 69 | while( m_counter8[tone] <= 0 ) |
| 48 | 70 | { |
| 49 | | tms->counter8[tone] += samplerate; |
| 50 | | tms->output8 ^= 1 << tone; |
| 71 | m_counter8[tone] += samplerate; |
| 72 | m_output8 ^= 1 << tone; |
| 51 | 73 | } |
| 52 | 74 | |
| 53 | | if (tms->output8 & tms->enable & (1 << tone)) |
| 75 | if (m_output8 & m_enable & (1 << tone)) |
| 54 | 76 | { |
| 55 | 77 | sum8 += VMAX; |
| 56 | 78 | } |
| 57 | 79 | |
| 58 | 80 | // 16' |
| 59 | 81 | |
| 60 | | tms->counter16[tone] -= (tms->basefreq / divisor[tone] / 2); |
| 82 | m_counter16[tone] -= (m_basefreq / divisor[tone] / 2); |
| 61 | 83 | |
| 62 | | while( tms->counter16[tone] <= 0 ) |
| 84 | while( m_counter16[tone] <= 0 ) |
| 63 | 85 | { |
| 64 | | tms->counter16[tone] += samplerate; |
| 65 | | tms->output16 ^= 1 << tone; |
| 86 | m_counter16[tone] += samplerate; |
| 87 | m_output16 ^= 1 << tone; |
| 66 | 88 | } |
| 67 | 89 | |
| 68 | | if (tms->output16 & tms->enable & (1 << tone)) |
| 90 | if (m_output16 & m_enable & (1 << tone)) |
| 69 | 91 | { |
| 70 | 92 | sum16 += VMAX; |
| 71 | 93 | } |
| 72 | 94 | } |
| 73 | 95 | |
| 74 | | *buffer8++ = sum8 / TONES; |
| 75 | | *buffer16++ = sum16 / TONES; |
| 96 | *buffer8++ = sum8 / TMS3615_TONES; |
| 97 | *buffer16++ = sum16 / TMS3615_TONES; |
| 76 | 98 | } |
| 77 | 99 | |
| 78 | | tms->enable = 0; |
| 100 | m_enable = 0; |
| 79 | 101 | } |
| 80 | 102 | |
| 81 | | void tms3615_enable_w(device_t *device, int enable) |
| 82 | | { |
| 83 | | tms_state *tms = get_safe_token(device); |
| 84 | | tms->enable = enable; |
| 85 | | } |
| 86 | 103 | |
| 87 | | static DEVICE_START( tms3615 ) |
| 104 | void tms3615_device::enable_w(int enable) |
| 88 | 105 | { |
| 89 | | tms_state *tms = get_safe_token(device); |
| 90 | | |
| 91 | | tms->channel = device->machine().sound().stream_alloc(*device, 0, 2, device->clock()/8, tms, tms3615_sound_update); |
| 92 | | tms->samplerate = device->clock()/8; |
| 93 | | tms->basefreq = device->clock(); |
| 106 | m_enable = enable; |
| 94 | 107 | } |
| 95 | 108 | |
| 96 | | const device_type TMS3615 = &device_creator<tms3615_device>; |
| 97 | 109 | |
| 98 | | tms3615_device::tms3615_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) |
| 99 | | : device_t(mconfig, TMS3615, "TMS3615", tag, owner, clock), |
| 100 | | device_sound_interface(mconfig, *this) |
| 101 | | { |
| 102 | | m_token = global_alloc_clear(tms_state); |
| 103 | | } |
| 104 | | |
| 105 | | //------------------------------------------------- |
| 106 | | // device_config_complete - perform any |
| 107 | | // operations now that the configuration is |
| 108 | | // complete |
| 109 | | //------------------------------------------------- |
| 110 | | |
| 111 | | void tms3615_device::device_config_complete() |
| 112 | | { |
| 113 | | } |
| 114 | | |
| 115 | | //------------------------------------------------- |
| 116 | | // device_start - device-specific startup |
| 117 | | //------------------------------------------------- |
| 118 | | |
| 119 | | void tms3615_device::device_start() |
| 120 | | { |
| 121 | | DEVICE_START_NAME( tms3615 )(this); |
| 122 | | } |
| 123 | | |
| 124 | | //------------------------------------------------- |
| 125 | | // sound_stream_update - handle a stream update |
| 126 | | //------------------------------------------------- |
| 127 | | |
| 128 | | void tms3615_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) |
| 129 | | { |
| 130 | | // should never get here |
| 131 | | fatalerror("sound_stream_update called; not applicable to legacy sound devices\n"); |
| 132 | | } |
trunk/src/emu/sound/rf5c400.c
| r21175 | r21176 | |
| 18 | 18 | #include "emu.h" |
| 19 | 19 | #include "rf5c400.h" |
| 20 | 20 | |
| 21 | | struct rf5c400_channel |
| 22 | | { |
| 23 | | UINT16 startH; |
| 24 | | UINT16 startL; |
| 25 | | UINT16 freq; |
| 26 | | UINT16 endL; |
| 27 | | UINT16 endHloopH; |
| 28 | | UINT16 loopL; |
| 29 | | UINT16 pan; |
| 30 | | UINT16 effect; |
| 31 | | UINT16 volume; |
| 32 | | |
| 33 | | UINT16 attack; |
| 34 | | UINT16 decay; |
| 35 | | UINT16 release; |
| 36 | | |
| 37 | | UINT16 cutoff; |
| 38 | | |
| 39 | | UINT64 pos; |
| 40 | | UINT64 step; |
| 41 | | UINT16 keyon; |
| 42 | | |
| 43 | | UINT8 env_phase; |
| 44 | | double env_level; |
| 45 | | double env_step; |
| 46 | | double env_scale; |
| 47 | | }; |
| 48 | | |
| 49 | | struct rf5c400_state |
| 50 | | { |
| 51 | | INT16 *rom; |
| 52 | | UINT32 rom_length; |
| 53 | | |
| 54 | | sound_stream *stream; |
| 55 | | |
| 56 | | double env_ar_table[0x9f]; |
| 57 | | double env_dr_table[0x9f]; |
| 58 | | double env_rr_table[0x9f]; |
| 59 | | |
| 60 | | rf5c400_channel channels[32]; |
| 61 | | }; |
| 62 | | |
| 63 | 21 | static int volume_table[256]; |
| 64 | 22 | static double pan_table[0x64]; |
| 65 | 23 | |
| r21175 | r21176 | |
| 75 | 33 | #define ENV_MAX_RR 0x54 |
| 76 | 34 | |
| 77 | 35 | /* PCM type */ |
| 78 | | enum { |
| 36 | enum |
| 37 | { |
| 79 | 38 | TYPE_MASK = 0x00C0, |
| 80 | 39 | TYPE_16 = 0x0000, |
| 81 | 40 | TYPE_8LOW = 0x0040, |
| r21175 | r21176 | |
| 83 | 42 | }; |
| 84 | 43 | |
| 85 | 44 | /* envelope phase */ |
| 86 | | enum { |
| 45 | enum |
| 46 | { |
| 87 | 47 | PHASE_NONE = 0, |
| 88 | 48 | PHASE_ATTACK, |
| 89 | 49 | PHASE_DECAY, |
| r21175 | r21176 | |
| 91 | 51 | }; |
| 92 | 52 | |
| 93 | 53 | |
| 94 | | INLINE rf5c400_state *get_safe_token(device_t *device) |
| 54 | // device type definition |
| 55 | const device_type RF5C400 = &device_creator<rf5c400_device>; |
| 56 | |
| 57 | |
| 58 | //************************************************************************** |
| 59 | // LIVE DEVICE |
| 60 | //************************************************************************** |
| 61 | |
| 62 | //------------------------------------------------- |
| 63 | // rf5c400_device - constructor |
| 64 | //------------------------------------------------- |
| 65 | |
| 66 | rf5c400_device::rf5c400_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) |
| 67 | : device_t(mconfig, RF5C400, "RF5C400", tag, owner, clock), |
| 68 | device_sound_interface(mconfig, *this), |
| 69 | m_rom(NULL), |
| 70 | m_rom_length(0), |
| 71 | m_stream(NULL) |
| 95 | 72 | { |
| 96 | | assert(device != NULL); |
| 97 | | assert(device->type() == RF5C400); |
| 98 | | return (rf5c400_state *)downcast<rf5c400_device *>(device)->token(); |
| 73 | memset(m_env_ar_table, 0, sizeof(double)*0x9f); |
| 74 | memset(m_env_dr_table, 0, sizeof(double)*0x9f); |
| 75 | memset(m_env_rr_table, 0, sizeof(double)*0x9f); |
| 99 | 76 | } |
| 100 | 77 | |
| 101 | 78 | |
| 102 | | /*****************************************************************************/ |
| 103 | 79 | |
| 104 | | static UINT8 decode80(UINT8 val) |
| 105 | | { |
| 106 | | if (val & 0x80) |
| 107 | | { |
| 108 | | val = (val & 0x7f) + 0x1f; |
| 109 | | } |
| 80 | //------------------------------------------------- |
| 81 | // device_start - device-specific startup |
| 82 | //------------------------------------------------- |
| 110 | 83 | |
| 111 | | return val; |
| 84 | void rf5c400_device::device_start() |
| 85 | { |
| 86 | rf5c400_init_chip(); |
| 112 | 87 | } |
| 113 | 88 | |
| 114 | | static STREAM_UPDATE( rf5c400_update ) |
| 89 | //------------------------------------------------- |
| 90 | // sound_stream_update - handle a stream update |
| 91 | //------------------------------------------------- |
| 92 | |
| 93 | void rf5c400_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) |
| 115 | 94 | { |
| 116 | 95 | int i, ch; |
| 117 | | rf5c400_state *info = (rf5c400_state *)param; |
| 118 | | INT16 *rom = info->rom; |
| 96 | INT16 *rom = m_rom; |
| 119 | 97 | UINT32 end, loop; |
| 120 | 98 | UINT64 pos; |
| 121 | 99 | UINT8 vol, lvol, rvol, type; |
| r21175 | r21176 | |
| 127 | 105 | |
| 128 | 106 | for (ch=0; ch < 32; ch++) |
| 129 | 107 | { |
| 130 | | rf5c400_channel *channel = &info->channels[ch]; |
| 108 | rf5c400_channel *channel = &m_channels[ch]; |
| 131 | 109 | stream_sample_t *buf0 = outputs[0]; |
| 132 | 110 | stream_sample_t *buf1 = outputs[1]; |
| 133 | 111 | |
| r21175 | r21176 | |
| 189 | 167 | else |
| 190 | 168 | { |
| 191 | 169 | env_step = |
| 192 | | info->env_dr_table[decode80(channel->decay >> 8)]; |
| 170 | m_env_dr_table[decode80(channel->decay >> 8)]; |
| 193 | 171 | } |
| 194 | 172 | env_rstep = env_step * channel->env_scale; |
| 195 | 173 | } |
| r21175 | r21176 | |
| 220 | 198 | *buf1++ += sample * pan_table[rvol]; |
| 221 | 199 | |
| 222 | 200 | pos += channel->step; |
| 223 | | if ( (pos>>16) > info->rom_length || (pos>>16) > end) |
| 201 | if ( (pos>>16) > m_rom_length || (pos>>16) > end) |
| 224 | 202 | { |
| 225 | 203 | pos -= loop<<16; |
| 226 | 204 | pos &= U64(0xFFFFFF0000); |
| r21175 | r21176 | |
| 235 | 213 | } |
| 236 | 214 | } |
| 237 | 215 | |
| 238 | | static void rf5c400_init_chip(device_t *device, rf5c400_state *info) |
| 216 | |
| 217 | /*****************************************************************************/ |
| 218 | |
| 219 | UINT8 rf5c400_device::decode80(UINT8 val) |
| 239 | 220 | { |
| 221 | if (val & 0x80) |
| 222 | { |
| 223 | val = (val & 0x7f) + 0x1f; |
| 224 | } |
| 225 | |
| 226 | return val; |
| 227 | } |
| 228 | |
| 229 | void rf5c400_device::rf5c400_init_chip() |
| 230 | { |
| 240 | 231 | int i; |
| 241 | 232 | |
| 242 | | info->rom = *device->region(); |
| 243 | | info->rom_length = device->region()->bytes() / 2; |
| 233 | m_rom = *region(); |
| 234 | m_rom_length = region()->bytes() / 2; |
| 244 | 235 | |
| 245 | 236 | // init volume table |
| 246 | 237 | { |
| r21175 | r21176 | |
| 262 | 253 | double r; |
| 263 | 254 | |
| 264 | 255 | // attack |
| 265 | | r = 1.0 / (ENV_AR_SPEED * (device->clock() / 384)); |
| 256 | r = 1.0 / (ENV_AR_SPEED * (clock() / 384)); |
| 266 | 257 | for (i = 0; i < ENV_MIN_AR; i++) |
| 267 | 258 | { |
| 268 | | info->env_ar_table[i] = 1.0; |
| 259 | m_env_ar_table[i] = 1.0; |
| 269 | 260 | } |
| 270 | 261 | for (i = ENV_MIN_AR; i < ENV_MAX_AR; i++) |
| 271 | 262 | { |
| 272 | | info->env_ar_table[i] = |
| 263 | m_env_ar_table[i] = |
| 273 | 264 | r * (ENV_MAX_AR - i) / (ENV_MAX_AR - ENV_MIN_AR); |
| 274 | 265 | } |
| 275 | 266 | for (i = ENV_MAX_AR; i < 0x9f; i++) |
| 276 | 267 | { |
| 277 | | info->env_ar_table[i] = 0.0; |
| 268 | m_env_ar_table[i] = 0.0; |
| 278 | 269 | } |
| 279 | 270 | |
| 280 | 271 | // decay |
| 281 | | r = -5.0 / (ENV_DR_SPEED * (device->clock() / 384)); |
| 272 | r = -5.0 / (ENV_DR_SPEED * (clock() / 384)); |
| 282 | 273 | for (i = 0; i < ENV_MIN_DR; i++) |
| 283 | 274 | { |
| 284 | | info->env_dr_table[i] = r; |
| 275 | m_env_dr_table[i] = r; |
| 285 | 276 | } |
| 286 | 277 | for (i = ENV_MIN_DR; i < ENV_MAX_DR; i++) |
| 287 | 278 | { |
| 288 | | info->env_dr_table[i] = |
| 279 | m_env_dr_table[i] = |
| 289 | 280 | r * (ENV_MAX_DR - i) / (ENV_MAX_DR - ENV_MIN_DR); |
| 290 | 281 | } |
| 291 | 282 | for (i = ENV_MAX_DR; i < 0x9f; i++) |
| 292 | 283 | { |
| 293 | | info->env_dr_table[i] = 0.0; |
| 284 | m_env_dr_table[i] = 0.0; |
| 294 | 285 | } |
| 295 | 286 | |
| 296 | 287 | // release |
| 297 | | r = -5.0 / (ENV_RR_SPEED * (device->clock() / 384)); |
| 288 | r = -5.0 / (ENV_RR_SPEED * (clock() / 384)); |
| 298 | 289 | for (i = 0; i < ENV_MIN_RR; i++) |
| 299 | 290 | { |
| 300 | | info->env_rr_table[i] = r; |
| 291 | m_env_rr_table[i] = r; |
| 301 | 292 | } |
| 302 | 293 | for (i = ENV_MIN_RR; i < ENV_MAX_RR; i++) |
| 303 | 294 | { |
| 304 | | info->env_rr_table[i] = |
| 295 | m_env_rr_table[i] = |
| 305 | 296 | r * (ENV_MAX_RR - i) / (ENV_MAX_RR - ENV_MIN_RR); |
| 306 | 297 | } |
| 307 | 298 | for (i = ENV_MAX_RR; i < 0x9f; i++) |
| 308 | 299 | { |
| 309 | | info->env_rr_table[i] = 0.0; |
| 300 | m_env_rr_table[i] = 0.0; |
| 310 | 301 | } |
| 311 | 302 | } |
| 312 | 303 | |
| 313 | 304 | // init channel info |
| 314 | 305 | for (i = 0; i < 32; i++) |
| 315 | 306 | { |
| 316 | | info->channels[i].env_phase = PHASE_NONE; |
| 317 | | info->channels[i].env_level = 0.0; |
| 318 | | info->channels[i].env_step = 0.0; |
| 319 | | info->channels[i].env_scale = 1.0; |
| 307 | m_channels[i].env_phase = PHASE_NONE; |
| 308 | m_channels[i].env_level = 0.0; |
| 309 | m_channels[i].env_step = 0.0; |
| 310 | m_channels[i].env_scale = 1.0; |
| 320 | 311 | } |
| 321 | 312 | |
| 322 | | for (i = 0; i < ARRAY_LENGTH(info->channels); i++) |
| 313 | for (i = 0; i < ARRAY_LENGTH(m_channels); i++) |
| 323 | 314 | { |
| 324 | | device->save_item(NAME(info->channels[i].startH), i); |
| 325 | | device->save_item(NAME(info->channels[i].startL), i); |
| 326 | | device->save_item(NAME(info->channels[i].freq), i); |
| 327 | | device->save_item(NAME(info->channels[i].endL), i); |
| 328 | | device->save_item(NAME(info->channels[i].endHloopH), i); |
| 329 | | device->save_item(NAME(info->channels[i].loopL), i); |
| 330 | | device->save_item(NAME(info->channels[i].pan), i); |
| 331 | | device->save_item(NAME(info->channels[i].effect), i); |
| 332 | | device->save_item(NAME(info->channels[i].volume), i); |
| 333 | | device->save_item(NAME(info->channels[i].attack), i); |
| 334 | | device->save_item(NAME(info->channels[i].decay), i); |
| 335 | | device->save_item(NAME(info->channels[i].release), i); |
| 336 | | device->save_item(NAME(info->channels[i].cutoff), i); |
| 337 | | device->save_item(NAME(info->channels[i].pos), i); |
| 338 | | device->save_item(NAME(info->channels[i].step), i); |
| 339 | | device->save_item(NAME(info->channels[i].keyon), i); |
| 340 | | device->save_item(NAME(info->channels[i].env_phase), i); |
| 341 | | device->save_item(NAME(info->channels[i].env_level), i); |
| 342 | | device->save_item(NAME(info->channels[i].env_step), i); |
| 343 | | device->save_item(NAME(info->channels[i].env_scale), i); |
| 315 | save_item(NAME(m_channels[i].startH), i); |
| 316 | save_item(NAME(m_channels[i].startL), i); |
| 317 | save_item(NAME(m_channels[i].freq), i); |
| 318 | save_item(NAME(m_channels[i].endL), i); |
| 319 | save_item(NAME(m_channels[i].endHloopH), i); |
| 320 | save_item(NAME(m_channels[i].loopL), i); |
| 321 | save_item(NAME(m_channels[i].pan), i); |
| 322 | save_item(NAME(m_channels[i].effect), i); |
| 323 | save_item(NAME(m_channels[i].volume), i); |
| 324 | save_item(NAME(m_channels[i].attack), i); |
| 325 | save_item(NAME(m_channels[i].decay), i); |
| 326 | save_item(NAME(m_channels[i].release), i); |
| 327 | save_item(NAME(m_channels[i].cutoff), i); |
| 328 | save_item(NAME(m_channels[i].pos), i); |
| 329 | save_item(NAME(m_channels[i].step), i); |
| 330 | save_item(NAME(m_channels[i].keyon), i); |
| 331 | save_item(NAME(m_channels[i].env_phase), i); |
| 332 | save_item(NAME(m_channels[i].env_level), i); |
| 333 | save_item(NAME(m_channels[i].env_step), i); |
| 334 | save_item(NAME(m_channels[i].env_scale), i); |
| 344 | 335 | } |
| 345 | 336 | |
| 346 | | info->stream = device->machine().sound().stream_alloc(*device, 0, 2, device->clock()/384, info, rf5c400_update); |
| 337 | m_stream = stream_alloc(0, 2, clock()/384); |
| 347 | 338 | } |
| 348 | 339 | |
| 349 | 340 | |
| 350 | | static DEVICE_START( rf5c400 ) |
| 351 | | { |
| 352 | | rf5c400_state *info = get_safe_token(device); |
| 353 | | |
| 354 | | rf5c400_init_chip(device, info); |
| 355 | | } |
| 356 | | |
| 357 | 341 | /*****************************************************************************/ |
| 358 | 342 | |
| 359 | 343 | static UINT16 rf5c400_status = 0; |
| 360 | | READ16_DEVICE_HANDLER( rf5c400_r ) |
| 344 | READ16_MEMBER( rf5c400_device::rf5c400_r ) |
| 361 | 345 | { |
| 362 | 346 | switch(offset) |
| 363 | 347 | { |
| r21175 | r21176 | |
| 375 | 359 | return 0; |
| 376 | 360 | } |
| 377 | 361 | |
| 378 | | WRITE16_DEVICE_HANDLER( rf5c400_w ) |
| 362 | WRITE16_MEMBER( rf5c400_device::rf5c400_w ) |
| 379 | 363 | { |
| 380 | | rf5c400_state *info = get_safe_token(device); |
| 381 | | |
| 382 | 364 | if (offset < 0x400) |
| 383 | 365 | { |
| 384 | 366 | switch(offset) |
| r21175 | r21176 | |
| 395 | 377 | switch ( data & 0x60 ) |
| 396 | 378 | { |
| 397 | 379 | case 0x60: |
| 398 | | info->channels[ch].pos = |
| 399 | | ((info->channels[ch].startH & 0xFF00) << 8) | info->channels[ch].startL; |
| 400 | | info->channels[ch].pos <<= 16; |
| 380 | m_channels[ch].pos = |
| 381 | ((m_channels[ch].startH & 0xFF00) << 8) | m_channels[ch].startL; |
| 382 | m_channels[ch].pos <<= 16; |
| 401 | 383 | |
| 402 | | info->channels[ch].env_phase = PHASE_ATTACK; |
| 403 | | info->channels[ch].env_level = 0.0; |
| 404 | | info->channels[ch].env_step = |
| 405 | | info->env_ar_table[decode80(info->channels[ch].attack >> 8)]; |
| 384 | m_channels[ch].env_phase = PHASE_ATTACK; |
| 385 | m_channels[ch].env_level = 0.0; |
| 386 | m_channels[ch].env_step = |
| 387 | m_env_ar_table[decode80(m_channels[ch].attack >> 8)]; |
| 406 | 388 | break; |
| 407 | 389 | case 0x40: |
| 408 | | if (info->channels[ch].env_phase != PHASE_NONE) |
| 390 | if (m_channels[ch].env_phase != PHASE_NONE) |
| 409 | 391 | { |
| 410 | | info->channels[ch].env_phase = PHASE_RELEASE; |
| 411 | | if (info->channels[ch].release & 0x0080) |
| 392 | m_channels[ch].env_phase = PHASE_RELEASE; |
| 393 | if (m_channels[ch].release & 0x0080) |
| 412 | 394 | { |
| 413 | | info->channels[ch].env_step = 0.0; |
| 395 | m_channels[ch].env_step = 0.0; |
| 414 | 396 | } |
| 415 | 397 | else |
| 416 | 398 | { |
| 417 | | info->channels[ch].env_step = |
| 418 | | info->env_rr_table[decode80(info->channels[ch].release >> 8)]; |
| 399 | m_channels[ch].env_step = |
| 400 | m_env_rr_table[decode80(m_channels[ch].release >> 8)]; |
| 419 | 401 | } |
| 420 | 402 | } |
| 421 | 403 | break; |
| 422 | 404 | default: |
| 423 | | info->channels[ch].env_phase = PHASE_NONE; |
| 424 | | info->channels[ch].env_level = 0.0; |
| 425 | | info->channels[ch].env_step = 0.0; |
| 405 | m_channels[ch].env_phase = PHASE_NONE; |
| 406 | m_channels[ch].env_level = 0.0; |
| 407 | m_channels[ch].env_step = 0.0; |
| 426 | 408 | break; |
| 427 | 409 | } |
| 428 | 410 | break; |
| r21175 | r21176 | |
| 446 | 428 | |
| 447 | 429 | default: |
| 448 | 430 | { |
| 449 | | //mame_printf_debug("%s:rf5c400_w: %08X, %08X, %08X\n", device->machine().describe_context(), data, offset, mem_mask); |
| 431 | //mame_printf_debug("%s:rf5c400_w: %08X, %08X, %08X\n", machine().describe_context(), data, offset, mem_mask); |
| 450 | 432 | break; |
| 451 | 433 | } |
| 452 | 434 | } |
| 453 | | //mame_printf_debug("%s:rf5c400_w: %08X, %08X, %08X at %08X\n", device->machine().describe_context(), data, offset, mem_mask); |
| 435 | //mame_printf_debug("%s:rf5c400_w: %08X, %08X, %08X at %08X\n", machine().describe_context(), data, offset, mem_mask); |
| 454 | 436 | } |
| 455 | 437 | else |
| 456 | 438 | { |
| r21175 | r21176 | |
| 458 | 440 | int ch = (offset >> 5) & 0x1f; |
| 459 | 441 | int reg = (offset & 0x1f); |
| 460 | 442 | |
| 461 | | rf5c400_channel *channel = &info->channels[ch]; |
| 443 | rf5c400_channel *channel = &m_channels[ch]; |
| 462 | 444 | |
| 463 | 445 | switch (reg) |
| 464 | 446 | { |
| r21175 | r21176 | |
| 555 | 537 | } |
| 556 | 538 | } |
| 557 | 539 | } |
| 558 | | |
| 559 | | const device_type RF5C400 = &device_creator<rf5c400_device>; |
| 560 | | |
| 561 | | rf5c400_device::rf5c400_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) |
| 562 | | : device_t(mconfig, RF5C400, "RF5C400", tag, owner, clock), |
| 563 | | device_sound_interface(mconfig, *this) |
| 564 | | { |
| 565 | | m_token = global_alloc_clear(rf5c400_state); |
| 566 | | } |
| 567 | | |
| 568 | | //------------------------------------------------- |
| 569 | | // device_config_complete - perform any |
| 570 | | // operations now that the configuration is |
| 571 | | // complete |
| 572 | | //------------------------------------------------- |
| 573 | | |
| 574 | | void rf5c400_device::device_config_complete() |
| 575 | | { |
| 576 | | } |
| 577 | | |
| 578 | | //------------------------------------------------- |
| 579 | | // device_start - device-specific startup |
| 580 | | //------------------------------------------------- |
| 581 | | |
| 582 | | void rf5c400_device::device_start() |
| 583 | | { |
| 584 | | DEVICE_START_NAME( rf5c400 )(this); |
| 585 | | } |
| 586 | | |
| 587 | | //------------------------------------------------- |
| 588 | | // sound_stream_update - handle a stream update |
| 589 | | //------------------------------------------------- |
| 590 | | |
| 591 | | void rf5c400_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) |
| 592 | | { |
| 593 | | // should never get here |
| 594 | | fatalerror("sound_stream_update called; not applicable to legacy sound devices\n"); |
| 595 | | } |
trunk/src/emu/sound/cem3394.c
| r21175 | r21176 | |
| 89 | 89 | ********************************************************************************/ |
| 90 | 90 | |
| 91 | 91 | |
| 92 | | /* various waveforms */ |
| 92 | // various waveforms |
| 93 | 93 | #define WAVE_TRIANGLE 1 |
| 94 | 94 | #define WAVE_SAWTOOTH 2 |
| 95 | 95 | #define WAVE_PULSE 4 |
| 96 | 96 | |
| 97 | | /* keep lots of fractional bits */ |
| 97 | // keep lots of fractional bits |
| 98 | 98 | #define FRACTION_BITS 28 |
| 99 | 99 | #define FRACTION_ONE (1 << FRACTION_BITS) |
| 100 | 100 | #define FRACTION_ONE_D ((double)(1 << FRACTION_BITS)) |
| r21175 | r21176 | |
| 102 | 102 | #define FRACTION_MULT(a,b) (((a) >> (FRACTION_BITS / 2)) * ((b) >> (FRACTION_BITS - FRACTION_BITS / 2))) |
| 103 | 103 | |
| 104 | 104 | |
| 105 | | /* this structure defines the parameters for a channel */ |
| 106 | | struct cem3394_state |
| 107 | | { |
| 108 | | sound_stream * stream; /* our stream */ |
| 109 | | void (*external)(device_t *, int, short *);/* callback to generate external samples */ |
| 110 | | double vco_zero_freq; /* frequency of VCO at 0.0V */ |
| 111 | | double filter_zero_freq; /* frequency of filter at 0.0V */ |
| 105 | // device type definition |
| 106 | const device_type CEM3394 = &device_creator<cem3394_device>; |
| 112 | 107 | |
| 113 | | double values[8]; /* raw values of registers */ |
| 114 | | UINT8 wave_select; /* flags which waveforms are enabled */ |
| 108 | //************************************************************************** |
| 109 | // LIVE DEVICE |
| 110 | //************************************************************************** |
| 115 | 111 | |
| 116 | | UINT32 volume; /* linear overall volume (0-256) */ |
| 117 | | UINT32 mixer_internal; /* linear internal volume (0-256) */ |
| 118 | | UINT32 mixer_external; /* linear external volume (0-256) */ |
| 112 | //------------------------------------------------- |
| 113 | // cem3394_device - constructor |
| 114 | //------------------------------------------------- |
| 119 | 115 | |
| 120 | | UINT32 position; /* current VCO frequency position (0.FRACTION_BITS) */ |
| 121 | | UINT32 step; /* per-sample VCO step (0.FRACTION_BITS) */ |
| 122 | | |
| 123 | | UINT32 filter_position; /* current filter frequency position (0.FRACTION_BITS) */ |
| 124 | | UINT32 filter_step; /* per-sample filter step (0.FRACTION_BITS) */ |
| 125 | | UINT32 modulation_depth; /* fraction of total by which we modulate (0.FRACTION_BITS) */ |
| 126 | | INT16 last_ext; /* last external sample we read */ |
| 127 | | |
| 128 | | UINT32 pulse_width; /* fractional pulse width (0.FRACTION_BITS) */ |
| 129 | | |
| 130 | | double inv_sample_rate; |
| 131 | | int sample_rate; |
| 132 | | device_t *device; |
| 133 | | |
| 134 | | INT16 *mixer_buffer; |
| 135 | | INT16 *external_buffer; |
| 136 | | }; |
| 137 | | |
| 138 | | |
| 139 | | INLINE cem3394_state *get_safe_token(device_t *device) |
| 116 | cem3394_device::cem3394_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) |
| 117 | : device_t(mconfig, CEM3394, "CEM3394", tag, owner, clock), |
| 118 | device_sound_interface(mconfig, *this), |
| 119 | m_external(NULL), |
| 120 | m_stream(NULL), |
| 121 | m_vco_zero_freq(0.0), |
| 122 | m_filter_zero_freq(0.0), |
| 123 | m_wave_select(0), |
| 124 | m_volume(0), |
| 125 | m_mixer_internal(0), |
| 126 | m_mixer_external(0), |
| 127 | m_position(0), |
| 128 | m_step(0), |
| 129 | m_filter_position(0), |
| 130 | m_filter_step(0), |
| 131 | m_modulation_depth(0), |
| 132 | m_last_ext(0), |
| 133 | m_pulse_width(0), |
| 134 | m_inv_sample_rate(0.0), |
| 135 | m_sample_rate(0), |
| 136 | m_mixer_buffer(NULL), |
| 137 | m_external_buffer(NULL) |
| 140 | 138 | { |
| 141 | | assert(device != NULL); |
| 142 | | assert(device->type() == CEM3394); |
| 143 | | return (cem3394_state *)downcast<cem3394_device *>(device)->token(); |
| 139 | memset(m_values, 0, 8*sizeof(double)); |
| 144 | 140 | } |
| 145 | 141 | |
| 146 | 142 | |
| 147 | | /* generate sound to the mix buffer in mono */ |
| 148 | | static STREAM_UPDATE( cem3394_update ) |
| 143 | //------------------------------------------------- |
| 144 | // sound_stream_update - generate sound to the mix buffer in mono |
| 145 | //------------------------------------------------- |
| 146 | |
| 147 | void cem3394_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) |
| 149 | 148 | { |
| 150 | | cem3394_state *chip = (cem3394_state *)param; |
| 151 | | int int_volume = (chip->volume * chip->mixer_internal) / 256; |
| 152 | | int ext_volume = (chip->volume * chip->mixer_external) / 256; |
| 153 | | UINT32 step = chip->step, position, end_position = 0; |
| 149 | int int_volume = (m_volume * m_mixer_internal) / 256; |
| 150 | int ext_volume = (m_volume * m_mixer_external) / 256; |
| 151 | UINT32 step = m_step, position, end_position = 0; |
| 154 | 152 | stream_sample_t *buffer = outputs[0]; |
| 155 | 153 | INT16 *mix, *ext; |
| 156 | 154 | int i; |
| 157 | 155 | |
| 158 | 156 | /* external volume is effectively 0 if no external function */ |
| 159 | | if (!chip->external || !ENABLE_EXTERNAL) |
| 157 | if (!m_external || !ENABLE_EXTERNAL) |
| 160 | 158 | ext_volume = 0; |
| 161 | 159 | |
| 162 | 160 | /* adjust the volume for the filter */ |
| 163 | | if (step > chip->filter_step) |
| 164 | | int_volume /= step - chip->filter_step; |
| 161 | if (step > m_filter_step) |
| 162 | int_volume /= step - m_filter_step; |
| 165 | 163 | |
| 166 | 164 | /* bail if nothing's going on */ |
| 167 | 165 | if (int_volume == 0 && ext_volume == 0) |
| r21175 | r21176 | |
| 173 | 171 | /* if there's external stuff, fetch and process it now */ |
| 174 | 172 | if (ext_volume != 0) |
| 175 | 173 | { |
| 176 | | UINT32 fposition = chip->filter_position, fstep = chip->filter_step, depth; |
| 177 | | INT16 last_ext = chip->last_ext; |
| 174 | UINT32 fposition = m_filter_position, fstep = m_filter_step, depth; |
| 175 | INT16 last_ext = m_last_ext; |
| 178 | 176 | |
| 179 | 177 | /* fetch the external data */ |
| 180 | | (*chip->external)(chip->device, samples, chip->external_buffer); |
| 178 | (*m_external)(this, samples, m_external_buffer); |
| 181 | 179 | |
| 182 | 180 | /* compute the modulation depth, and adjust fstep to the maximum frequency */ |
| 183 | 181 | /* we lop off 13 bits of depth so that we can multiply by stepadjust, below, */ |
| 184 | 182 | /* which has 13 bits of precision */ |
| 185 | | depth = FRACTION_MULT(fstep, chip->modulation_depth); |
| 183 | depth = FRACTION_MULT(fstep, m_modulation_depth); |
| 186 | 184 | fstep += depth; |
| 187 | 185 | depth >>= 13; |
| 188 | 186 | |
| 189 | 187 | /* "apply" the filter: note this is pretty cheesy; it basically just downsamples the |
| 190 | 188 | external sample to filter_freq by allowing only 2 transitions for every cycle */ |
| 191 | | for (i = 0, ext = chip->external_buffer, position = chip->position; i < samples; i++, ext++) |
| 189 | for (i = 0, ext = m_external_buffer, position = m_position; i < samples; i++, ext++) |
| 192 | 190 | { |
| 193 | 191 | UINT32 newposition; |
| 194 | 192 | INT32 stepadjust; |
| r21175 | r21176 | |
| 210 | 208 | } |
| 211 | 209 | |
| 212 | 210 | /* update the final filter values */ |
| 213 | | chip->filter_position = fposition; |
| 214 | | chip->last_ext = last_ext; |
| 211 | m_filter_position = fposition; |
| 212 | m_last_ext = last_ext; |
| 215 | 213 | } |
| 216 | 214 | |
| 217 | 215 | /* if there's internal stuff, generate it */ |
| 218 | 216 | if (int_volume != 0) |
| 219 | 217 | { |
| 220 | | if (chip->wave_select == 0 && !ext_volume) |
| 221 | | logerror("%f V didn't cut it\n", chip->values[CEM3394_WAVE_SELECT]); |
| 218 | if (m_wave_select == 0 && !ext_volume) |
| 219 | logerror("%f V didn't cut it\n", m_values[CEM3394_WAVE_SELECT]); |
| 222 | 220 | |
| 223 | 221 | /* handle the pulse component; it maxes out at 0x1932, which is 27% smaller than */ |
| 224 | 222 | /* the sawtooth (since the value is constant, this is the best place to have an */ |
| 225 | 223 | /* odd value for volume) */ |
| 226 | | if (ENABLE_PULSE && (chip->wave_select & WAVE_PULSE)) |
| 224 | if (ENABLE_PULSE && (m_wave_select & WAVE_PULSE)) |
| 227 | 225 | { |
| 228 | | UINT32 pulse_width = chip->pulse_width; |
| 226 | UINT32 pulse_width = m_pulse_width; |
| 229 | 227 | |
| 230 | 228 | /* if the width is wider than the step, we're guaranteed to hit it once per cycle */ |
| 231 | 229 | if (pulse_width >= step) |
| 232 | 230 | { |
| 233 | | for (i = 0, mix = chip->mixer_buffer, position = chip->position; i < samples; i++, mix++) |
| 231 | for (i = 0, mix = m_mixer_buffer, position = m_position; i < samples; i++, mix++) |
| 234 | 232 | { |
| 235 | 233 | if (position < pulse_width) |
| 236 | 234 | *mix = 0x1932; |
| r21175 | r21176 | |
| 244 | 242 | else |
| 245 | 243 | { |
| 246 | 244 | INT16 volume = 0x1932 * pulse_width / step; |
| 247 | | for (i = 0, mix = chip->mixer_buffer, position = chip->position; i < samples; i++, mix++) |
| 245 | for (i = 0, mix = m_mixer_buffer, position = m_position; i < samples; i++, mix++) |
| 248 | 246 | { |
| 249 | 247 | UINT32 newposition = position + step; |
| 250 | 248 | if ((newposition ^ position) & ~FRACTION_MASK) |
| r21175 | r21176 | |
| 259 | 257 | |
| 260 | 258 | /* otherwise, clear the mixing buffer */ |
| 261 | 259 | else |
| 262 | | memset(chip->mixer_buffer, 0, sizeof(INT16) * samples); |
| 260 | memset(m_mixer_buffer, 0, sizeof(INT16) * samples); |
| 263 | 261 | |
| 264 | 262 | /* handle the sawtooth component; it maxes out at 0x2000, which is 27% larger */ |
| 265 | 263 | /* than the pulse */ |
| 266 | | if (ENABLE_SAWTOOTH && (chip->wave_select & WAVE_SAWTOOTH)) |
| 264 | if (ENABLE_SAWTOOTH && (m_wave_select & WAVE_SAWTOOTH)) |
| 267 | 265 | { |
| 268 | | for (i = 0, mix = chip->mixer_buffer, position = chip->position; i < samples; i++, mix++) |
| 266 | for (i = 0, mix = m_mixer_buffer, position = m_position; i < samples; i++, mix++) |
| 269 | 267 | { |
| 270 | 268 | *mix += ((position >> (FRACTION_BITS - 14)) & 0x3fff) - 0x2000; |
| 271 | 269 | position += step; |
| r21175 | r21176 | |
| 276 | 274 | /* handle the triangle component; it maxes out at 0x2800, which is 25% larger */ |
| 277 | 275 | /* than the sawtooth (should be 27% according to the specs, but 25% saves us */ |
| 278 | 276 | /* a multiplication) */ |
| 279 | | if (ENABLE_TRIANGLE && (chip->wave_select & WAVE_TRIANGLE)) |
| 277 | if (ENABLE_TRIANGLE && (m_wave_select & WAVE_TRIANGLE)) |
| 280 | 278 | { |
| 281 | | for (i = 0, mix = chip->mixer_buffer, position = chip->position; i < samples; i++, mix++) |
| 279 | for (i = 0, mix = m_mixer_buffer, position = m_position; i < samples; i++, mix++) |
| 282 | 280 | { |
| 283 | 281 | INT16 value; |
| 284 | 282 | if (position & (1 << (FRACTION_BITS - 1))) |
| r21175 | r21176 | |
| 292 | 290 | } |
| 293 | 291 | |
| 294 | 292 | /* update the final position */ |
| 295 | | chip->position = end_position; |
| 293 | m_position = end_position; |
| 296 | 294 | } |
| 297 | 295 | |
| 298 | 296 | /* mix it down */ |
| 299 | | mix = chip->mixer_buffer; |
| 300 | | ext = chip->external_buffer; |
| 297 | mix = m_mixer_buffer; |
| 298 | ext = m_external_buffer; |
| 301 | 299 | { |
| 302 | 300 | /* internal + external */ |
| 303 | 301 | if (ext_volume != 0 && int_volume != 0) |
| r21175 | r21176 | |
| 321 | 319 | } |
| 322 | 320 | |
| 323 | 321 | |
| 324 | | static DEVICE_START( cem3394 ) |
| 322 | //------------------------------------------------- |
| 323 | // device_start - device-specific startup |
| 324 | //------------------------------------------------- |
| 325 | |
| 326 | void cem3394_device::device_start() |
| 325 | 327 | { |
| 326 | | const cem3394_interface *intf = (const cem3394_interface *)device->static_config(); |
| 327 | | cem3394_state *chip = get_safe_token(device); |
| 328 | const cem3394_interface *intf = (const cem3394_interface *)static_config(); |
| 328 | 329 | |
| 329 | | chip->device = device; |
| 330 | | |
| 331 | 330 | /* copy global parameters */ |
| 332 | | chip->sample_rate = CEM3394_SAMPLE_RATE; |
| 333 | | chip->inv_sample_rate = 1.0 / (double)chip->sample_rate; |
| 331 | m_sample_rate = CEM3394_SAMPLE_RATE; |
| 332 | m_inv_sample_rate = 1.0 / (double)m_sample_rate; |
| 334 | 333 | |
| 335 | 334 | /* allocate stream channels, 1 per chip */ |
| 336 | | chip->stream = device->machine().sound().stream_alloc(*device, 0, 1, chip->sample_rate, chip, cem3394_update); |
| 337 | | chip->external = intf->external; |
| 338 | | chip->vco_zero_freq = intf->vco_zero_freq; |
| 339 | | chip->filter_zero_freq = intf->filter_zero_freq; |
| 335 | m_stream = stream_alloc(0, 1, m_sample_rate); |
| 336 | m_external = intf->external; |
| 337 | m_vco_zero_freq = intf->vco_zero_freq; |
| 338 | m_filter_zero_freq = intf->filter_zero_freq; |
| 340 | 339 | |
| 341 | 340 | /* allocate memory for a mixer buffer and external buffer (1 second should do it!) */ |
| 342 | | chip->mixer_buffer = auto_alloc_array(device->machine(), INT16, chip->sample_rate); |
| 343 | | chip->external_buffer = auto_alloc_array(device->machine(), INT16, chip->sample_rate); |
| 341 | m_mixer_buffer = auto_alloc_array(machine(), INT16, m_sample_rate); |
| 342 | m_external_buffer = auto_alloc_array(machine(), INT16, m_sample_rate); |
| 344 | 343 | |
| 345 | | device->save_item(NAME(chip->values)); |
| 346 | | device->save_item(NAME(chip->wave_select)); |
| 347 | | device->save_item(NAME(chip->volume)); |
| 348 | | device->save_item(NAME(chip->mixer_internal)); |
| 349 | | device->save_item(NAME(chip->mixer_external)); |
| 350 | | device->save_item(NAME(chip->position)); |
| 351 | | device->save_item(NAME(chip->step)); |
| 352 | | device->save_item(NAME(chip->filter_position)); |
| 353 | | device->save_item(NAME(chip->filter_step)); |
| 354 | | device->save_item(NAME(chip->modulation_depth)); |
| 355 | | device->save_item(NAME(chip->last_ext)); |
| 356 | | device->save_item(NAME(chip->pulse_width)); |
| 344 | save_item(NAME(m_values)); |
| 345 | save_item(NAME(m_wave_select)); |
| 346 | save_item(NAME(m_volume)); |
| 347 | save_item(NAME(m_mixer_internal)); |
| 348 | save_item(NAME(m_mixer_external)); |
| 349 | save_item(NAME(m_position)); |
| 350 | save_item(NAME(m_step)); |
| 351 | save_item(NAME(m_filter_position)); |
| 352 | save_item(NAME(m_filter_step)); |
| 353 | save_item(NAME(m_modulation_depth)); |
| 354 | save_item(NAME(m_last_ext)); |
| 355 | save_item(NAME(m_pulse_width)); |
| 357 | 356 | } |
| 358 | 357 | |
| 359 | 358 | |
| 360 | | INLINE double compute_db(double voltage) |
| 359 | double cem3394_device::compute_db(double voltage) |
| 361 | 360 | { |
| 362 | 361 | /* assumes 0.0 == full off, 4.0 == full on, with linear taper, as described in the datasheet */ |
| 363 | 362 | |
| r21175 | r21176 | |
| 383 | 382 | } |
| 384 | 383 | |
| 385 | 384 | |
| 386 | | INLINE UINT32 compute_db_volume(double voltage) |
| 385 | UINT32 cem3394_device::compute_db_volume(double voltage) |
| 387 | 386 | { |
| 388 | 387 | double temp; |
| 389 | 388 | |
| r21175 | r21176 | |
| 413 | 412 | } |
| 414 | 413 | |
| 415 | 414 | |
| 416 | | void cem3394_set_voltage(device_t *device, int input, double voltage) |
| 415 | void cem3394_device::set_voltage(int input, double voltage) |
| 417 | 416 | { |
| 418 | | cem3394_state *chip = get_safe_token(device); |
| 419 | 417 | double temp; |
| 420 | 418 | |
| 421 | 419 | /* don't do anything if no change */ |
| 422 | | if (voltage == chip->values[input]) |
| 420 | if (voltage == m_values[input]) |
| 423 | 421 | return; |
| 424 | | chip->values[input] = voltage; |
| 422 | m_values[input] = voltage; |
| 425 | 423 | |
| 426 | 424 | /* update the stream first */ |
| 427 | | chip->stream->update(); |
| 425 | m_stream->update(); |
| 428 | 426 | |
| 429 | 427 | /* switch off the input */ |
| 430 | 428 | switch (input) |
| 431 | 429 | { |
| 432 | 430 | /* frequency varies from -4.0 to +4.0, at 0.75V/octave */ |
| 433 | 431 | case CEM3394_VCO_FREQUENCY: |
| 434 | | temp = chip->vco_zero_freq * pow(2.0, -voltage * (1.0 / 0.75)); |
| 435 | | chip->step = (UINT32)(temp * chip->inv_sample_rate * FRACTION_ONE_D); |
| 432 | temp = m_vco_zero_freq * pow(2.0, -voltage * (1.0 / 0.75)); |
| 433 | m_step = (UINT32)(temp * m_inv_sample_rate * FRACTION_ONE_D); |
| 436 | 434 | break; |
| 437 | 435 | |
| 438 | 436 | /* wave select determines triangle/sawtooth enable */ |
| 439 | 437 | case CEM3394_WAVE_SELECT: |
| 440 | | chip->wave_select &= ~(WAVE_TRIANGLE | WAVE_SAWTOOTH); |
| 438 | m_wave_select &= ~(WAVE_TRIANGLE | WAVE_SAWTOOTH); |
| 441 | 439 | if (voltage >= -0.5 && voltage <= -0.2) |
| 442 | | chip->wave_select |= WAVE_TRIANGLE; |
| 440 | m_wave_select |= WAVE_TRIANGLE; |
| 443 | 441 | else if (voltage >= 0.9 && voltage <= 1.5) |
| 444 | | chip->wave_select |= WAVE_TRIANGLE | WAVE_SAWTOOTH; |
| 442 | m_wave_select |= WAVE_TRIANGLE | WAVE_SAWTOOTH; |
| 445 | 443 | else if (voltage >= 2.3 && voltage <= 3.9) |
| 446 | | chip->wave_select |= WAVE_SAWTOOTH; |
| 444 | m_wave_select |= WAVE_SAWTOOTH; |
| 447 | 445 | break; |
| 448 | 446 | |
| 449 | 447 | /* pulse width determines duty cycle; 0.0 means 0%, 2.0 means 100% */ |
| 450 | 448 | case CEM3394_PULSE_WIDTH: |
| 451 | 449 | if (voltage < 0.0) |
| 452 | 450 | { |
| 453 | | chip->pulse_width = 0; |
| 454 | | chip->wave_select &= ~WAVE_PULSE; |
| 451 | m_pulse_width = 0; |
| 452 | m_wave_select &= ~WAVE_PULSE; |
| 455 | 453 | } |
| 456 | 454 | else |
| 457 | 455 | { |
| 458 | 456 | temp = voltage * 0.5; |
| 459 | 457 | if (LIMIT_WIDTH) |
| 460 | 458 | temp = MINIMUM_WIDTH + (MAXIMUM_WIDTH - MINIMUM_WIDTH) * temp; |
| 461 | | chip->pulse_width = (UINT32)(temp * FRACTION_ONE_D); |
| 462 | | chip->wave_select |= WAVE_PULSE; |
| 459 | m_pulse_width = (UINT32)(temp * FRACTION_ONE_D); |
| 460 | m_wave_select |= WAVE_PULSE; |
| 463 | 461 | } |
| 464 | 462 | break; |
| 465 | 463 | |
| 466 | 464 | /* final gain is pretty self-explanatory; 0.0 means ~90dB, 4.0 means 0dB */ |
| 467 | 465 | case CEM3394_FINAL_GAIN: |
| 468 | | chip->volume = compute_db_volume(voltage); |
| 466 | m_volume = compute_db_volume(voltage); |
| 469 | 467 | break; |
| 470 | 468 | |
| 471 | 469 | /* mixer balance is a pan between the external input and the internal input */ |
| r21175 | r21176 | |
| 473 | 471 | case CEM3394_MIXER_BALANCE: |
| 474 | 472 | if (voltage >= 0.0) |
| 475 | 473 | { |
| 476 | | chip->mixer_internal = compute_db_volume(3.55 - voltage); |
| 477 | | chip->mixer_external = compute_db_volume(3.55 + 0.45 * (voltage * 0.25)); |
| 474 | m_mixer_internal = compute_db_volume(3.55 - voltage); |
| 475 | m_mixer_external = compute_db_volume(3.55 + 0.45 * (voltage * 0.25)); |
| 478 | 476 | } |
| 479 | 477 | else |
| 480 | 478 | { |
| 481 | | chip->mixer_internal = compute_db_volume(3.55 - 0.45 * (voltage * 0.25)); |
| 482 | | chip->mixer_external = compute_db_volume(3.55 + voltage); |
| 479 | m_mixer_internal = compute_db_volume(3.55 - 0.45 * (voltage * 0.25)); |
| 480 | m_mixer_external = compute_db_volume(3.55 + voltage); |
| 483 | 481 | } |
| 484 | 482 | break; |
| 485 | 483 | |
| 486 | 484 | /* filter frequency varies from -4.0 to +4.0, at 0.375V/octave */ |
| 487 | 485 | case CEM3394_FILTER_FREQENCY: |
| 488 | | temp = chip->filter_zero_freq * pow(2.0, -voltage * (1.0 / 0.375)); |
| 489 | | chip->filter_step = (UINT32)(temp * chip->inv_sample_rate * FRACTION_ONE_D); |
| 486 | temp = m_filter_zero_freq * pow(2.0, -voltage * (1.0 / 0.375)); |
| 487 | m_filter_step = (UINT32)(temp * m_inv_sample_rate * FRACTION_ONE_D); |
| 490 | 488 | break; |
| 491 | 489 | |
| 492 | 490 | /* modulation depth is 0.01 at 0V and 2.0 at 3.5V; how it grows from one to the other */ |
| 493 | 491 | /* is still unclear at this point */ |
| 494 | 492 | case CEM3394_MODULATION_AMOUNT: |
| 495 | 493 | if (voltage < 0.0) |
| 496 | | chip->modulation_depth = (UINT32)(0.01 * FRACTION_ONE_D); |
| 494 | m_modulation_depth = (UINT32)(0.01 * FRACTION_ONE_D); |
| 497 | 495 | else if (voltage > 3.5) |
| 498 | | chip->modulation_depth = (UINT32)(2.00 * FRACTION_ONE_D); |
| 496 | m_modulation_depth = (UINT32)(2.00 * FRACTION_ONE_D); |
| 499 | 497 | else |
| 500 | | chip->modulation_depth = (UINT32)(((voltage * (1.0 / 3.5)) * 1.99 + 0.01) * FRACTION_ONE_D); |
| 498 | m_modulation_depth = (UINT32)(((voltage * (1.0 / 3.5)) * 1.99 + 0.01) * FRACTION_ONE_D); |
| 501 | 499 | break; |
| 502 | 500 | |
| 503 | 501 | /* this is not yet implemented */ |
| r21175 | r21176 | |
| 507 | 505 | } |
| 508 | 506 | |
| 509 | 507 | |
| 510 | | double cem3394_get_parameter(device_t *device, int input) |
| 508 | double cem3394_device::get_parameter(int input) |
| 511 | 509 | { |
| 512 | | cem3394_state *chip = get_safe_token(device); |
| 513 | | double voltage = chip->values[input]; |
| 510 | double voltage = m_values[input]; |
| 514 | 511 | |
| 515 | 512 | switch (input) |
| 516 | 513 | { |
| 517 | 514 | case CEM3394_VCO_FREQUENCY: |
| 518 | | return chip->vco_zero_freq * pow(2.0, -voltage * (1.0 / 0.75)); |
| 515 | return m_vco_zero_freq * pow(2.0, -voltage * (1.0 / 0.75)); |
| 519 | 516 | |
| 520 | 517 | case CEM3394_WAVE_SELECT: |
| 521 | 518 | return voltage; |
| r21175 | r21176 | |
| 551 | 548 | return voltage * (1.0 / 2.5); |
| 552 | 549 | |
| 553 | 550 | case CEM3394_FILTER_FREQENCY: |
| 554 | | return chip->filter_zero_freq * pow(2.0, -voltage * (1.0 / 0.375)); |
| 551 | return m_filter_zero_freq * pow(2.0, -voltage * (1.0 / 0.375)); |
| 555 | 552 | } |
| 556 | 553 | return 0.0; |
| 557 | 554 | } |
| 558 | | |
| 559 | | const device_type CEM3394 = &device_creator<cem3394_device>; |
| 560 | | |
| 561 | | cem3394_device::cem3394_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) |
| 562 | | : device_t(mconfig, CEM3394, "CEM3394", tag, owner, clock), |
| 563 | | device_sound_interface(mconfig, *this) |
| 564 | | { |
| 565 | | m_token = global_alloc_clear(cem3394_state); |
| 566 | | } |
| 567 | | |
| 568 | | //------------------------------------------------- |
| 569 | | // device_config_complete - perform any |
| 570 | | // operations now that the configuration is |
| 571 | | // complete |
| 572 | | //------------------------------------------------- |
| 573 | | |
| 574 | | void cem3394_device::device_config_complete() |
| 575 | | { |
| 576 | | } |
| 577 | | |
| 578 | | //------------------------------------------------- |
| 579 | | // device_start - device-specific startup |
| 580 | | //------------------------------------------------- |
| 581 | | |
| 582 | | void cem3394_device::device_start() |
| 583 | | { |
| 584 | | DEVICE_START_NAME( cem3394 )(this); |
| 585 | | } |
| 586 | | |
| 587 | | //------------------------------------------------- |
| 588 | | // sound_stream_update - handle a stream update |
| 589 | | //------------------------------------------------- |
| 590 | | |
| 591 | | void cem3394_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) |
| 592 | | { |
| 593 | | // should never get here |
| 594 | | fatalerror("sound_stream_update called; not applicable to legacy sound devices\n"); |
| 595 | | } |
trunk/src/emu/sound/cem3394.h
| r21175 | r21176 | |
| 3 | 3 | #ifndef __CEM3394_H__ |
| 4 | 4 | #define __CEM3394_H__ |
| 5 | 5 | |
| 6 | | #include "devlegcy.h" |
| 7 | | |
| 8 | | |
| 9 | 6 | #define CEM3394_SAMPLE_RATE (44100*4) |
| 10 | 7 | |
| 11 | 8 | |
| 12 | | /* interface */ |
| 9 | // interface |
| 13 | 10 | struct cem3394_interface |
| 14 | 11 | { |
| 15 | | double vco_zero_freq; /* frequency at 0V for VCO */ |
| 16 | | double filter_zero_freq; /* frequency at 0V for filter */ |
| 17 | | void (*external)(device_t *, int, short *);/* external input source */ |
| 12 | double vco_zero_freq; /* frequency at 0V for VCO */ |
| 13 | double filter_zero_freq; /* frequency at 0V for filter */ |
| 14 | void (*external)(device_t*, int, short*);/* external input source */ |
| 18 | 15 | }; |
| 19 | 16 | |
| 20 | | /* inputs */ |
| 17 | // inputs |
| 21 | 18 | enum |
| 22 | 19 | { |
| 23 | 20 | CEM3394_VCO_FREQUENCY = 0, |
| r21175 | r21176 | |
| 30 | 27 | CEM3394_FINAL_GAIN |
| 31 | 28 | }; |
| 32 | 29 | |
| 33 | | /* set the voltage going to a particular parameter */ |
| 34 | | void cem3394_set_voltage(device_t *device, int input, double voltage); |
| 35 | 30 | |
| 36 | | /* get the translated parameter associated with the given input as follows: |
| 37 | | CEM3394_VCO_FREQUENCY: frequency in Hz |
| 38 | | CEM3394_MODULATION_AMOUNT: scale factor, 0.0 to 2.0 |
| 39 | | CEM3394_WAVE_SELECT: voltage from this line |
| 40 | | CEM3394_PULSE_WIDTH: width fraction, from 0.0 to 1.0 |
| 41 | | CEM3394_MIXER_BALANCE: balance, from -1.0 to 1.0 |
| 42 | | CEM3394_FILTER_RESONANCE: resonance, from 0.0 to 1.0 |
| 43 | | CEM3394_FILTER_FREQENCY: frequency, in Hz |
| 44 | | CEM3394_FINAL_GAIN: gain, in dB */ |
| 45 | | double cem3394_get_parameter(device_t *device, int input); |
| 31 | //************************************************************************** |
| 32 | // INTERFACE CONFIGURATION MACROS |
| 33 | //************************************************************************** |
| 46 | 34 | |
| 35 | #define MCFG_CEM3394_ADD(_tag, _clock) \ |
| 36 | MCFG_DEVICE_ADD(_tag, CEM3394, _clock) |
| 37 | #define MCFG_CEM3394_REPLACE(_tag, _clock) \ |
| 38 | MCFG_DEVICE_REPLACE(_tag, CEM3394, _clock) |
| 39 | |
| 40 | |
| 41 | |
| 47 | 42 | class cem3394_device : public device_t, |
| 48 | | public device_sound_interface |
| 43 | public device_sound_interface |
| 49 | 44 | { |
| 50 | 45 | public: |
| 51 | 46 | cem3394_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); |
| 52 | | ~cem3394_device() { global_free(m_token); } |
| 47 | ~cem3394_device() { } |
| 53 | 48 | |
| 54 | | // access to legacy token |
| 55 | | void *token() const { assert(m_token != NULL); return m_token; } |
| 56 | 49 | protected: |
| 57 | 50 | // device-level overrides |
| 58 | | virtual void device_config_complete(); |
| 59 | 51 | virtual void device_start(); |
| 60 | 52 | |
| 61 | 53 | // sound stream update overrides |
| 62 | 54 | virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples); |
| 55 | |
| 56 | public: |
| 57 | // Set the voltage going to a particular parameter |
| 58 | void set_voltage(int input, double voltage); |
| 59 | |
| 60 | // Get the translated parameter associated with the given input as follows: |
| 61 | // CEM3394_VCO_FREQUENCY: frequency in Hz |
| 62 | // CEM3394_MODULATION_AMOUNT: scale factor, 0.0 to 2.0 |
| 63 | // CEM3394_WAVE_SELECT: voltage from this line |
| 64 | // CEM3394_PULSE_WIDTH: width fraction, from 0.0 to 1.0 |
| 65 | // CEM3394_MIXER_BALANCE: balance, from -1.0 to 1.0 |
| 66 | // CEM3394_FILTER_RESONANCE: resonance, from 0.0 to 1.0 |
| 67 | // CEM3394_FILTER_FREQENCY: frequency, in Hz |
| 68 | // CEM3394_FINAL_GAIN: gain, in dB |
| 69 | double get_parameter(int input); |
| 70 | |
| 63 | 71 | private: |
| 64 | | // internal state |
| 65 | | void *m_token; |
| 72 | double compute_db(double voltage); |
| 73 | UINT32 compute_db_volume(double voltage); |
| 74 | |
| 75 | private: |
| 76 | void (*m_external)(device_t*, int, short*);/* callback to generate external samples */ |
| 77 | |
| 78 | sound_stream *m_stream; /* our stream */ |
| 79 | double m_vco_zero_freq; /* frequency of VCO at 0.0V */ |
| 80 | double m_filter_zero_freq; /* frequency of filter at 0.0V */ |
| 81 | |
| 82 | double m_values[8]; /* raw values of registers */ |
| 83 | UINT8 m_wave_select; /* flags which waveforms are enabled */ |
| 84 | |
| 85 | UINT32 m_volume; /* linear overall volume (0-256) */ |
| 86 | UINT32 m_mixer_internal; /* linear internal volume (0-256) */ |
| 87 | UINT32 m_mixer_external; /* linear external volume (0-256) */ |
| 88 | |
| 89 | UINT32 m_position; /* current VCO frequency position (0.FRACTION_BITS) */ |
| 90 | UINT32 m_step; /* per-sample VCO step (0.FRACTION_BITS) */ |
| 91 | |
| 92 | UINT32 m_filter_position; /* current filter frequency position (0.FRACTION_BITS) */ |
| 93 | UINT32 m_filter_step; /* per-sample filter step (0.FRACTION_BITS) */ |
| 94 | UINT32 m_modulation_depth; /* fraction of total by which we modulate (0.FRACTION_BITS) */ |
| 95 | INT16 m_last_ext; /* last external sample we read */ |
| 96 | |
| 97 | UINT32 m_pulse_width; /* fractional pulse width (0.FRACTION_BITS) */ |
| 98 | |
| 99 | double m_inv_sample_rate; |
| 100 | int m_sample_rate; |
| 101 | |
| 102 | INT16 *m_mixer_buffer; |
| 103 | INT16 *m_external_buffer; |
| 66 | 104 | }; |
| 67 | 105 | |
| 68 | 106 | extern const device_type CEM3394; |
trunk/src/mess/machine/msx_slot.c
| r21175 | r21176 | |
| 354 | 354 | { |
| 355 | 355 | if ((offset & 0xff) >= 0xe0) |
| 356 | 356 | { |
| 357 | | return k051649_test_r(drvstate->m_k051649, space, offset & 0xff); |
| 357 | return drvstate->m_k051649->test_r(space, offset & 0xff); |
| 358 | 358 | } |
| 359 | 359 | return 0xff; |
| 360 | 360 | } |
| 361 | 361 | else |
| 362 | 362 | { |
| 363 | | return k051649_waveform_r(drvstate->m_k051649, space, offset & 0x7f); |
| 363 | return drvstate->m_k051649->waveform_r(space, offset & 0x7f); |
| 364 | 364 | } |
| 365 | 365 | } |
| 366 | 366 | |
| r21175 | r21176 | |
| 431 | 431 | |
| 432 | 432 | if (offset < 0x80) |
| 433 | 433 | { |
| 434 | | k051649_waveform_w (drvstate->m_k051649, space, offset, val); |
| 434 | drvstate->m_k051649->waveform_w (space, offset, val); |
| 435 | 435 | } |
| 436 | 436 | else if (offset < 0xa0) |
| 437 | 437 | { |
| 438 | 438 | offset &= 0xf; |
| 439 | 439 | if (offset < 0xa) |
| 440 | 440 | { |
| 441 | | k051649_frequency_w (drvstate->m_k051649, space, offset, val); |
| 441 | drvstate->m_k051649->frequency_w (space, offset, val); |
| 442 | 442 | } |
| 443 | 443 | else if (offset < 0xf) |
| 444 | 444 | { |
| 445 | | k051649_volume_w (drvstate->m_k051649, space, offset - 0xa, val); |
| 445 | drvstate->m_k051649->volume_w (space, offset - 0xa, val); |
| 446 | 446 | } |
| 447 | 447 | else |
| 448 | 448 | { |
| 449 | | k051649_keyonoff_w (drvstate->m_k051649, space, 0, val); |
| 449 | drvstate->m_k051649->keyonoff_w (space, 0, val); |
| 450 | 450 | } |
| 451 | 451 | } |
| 452 | 452 | else if (offset >= 0xe0) |
| 453 | 453 | { |
| 454 | | k051649_test_w (drvstate->m_k051649, space, offset, val); |
| 454 | drvstate->m_k051649->test_w (space, offset, val); |
| 455 | 455 | } |
| 456 | 456 | } |
| 457 | 457 | else if (addr >= 0xb000 && addr < 0xb800) |
| r21175 | r21176 | |
| 2197 | 2197 | |
| 2198 | 2198 | if (reg < 0x80) |
| 2199 | 2199 | { |
| 2200 | | return k051649_waveform_r (state->m_k051649, space, reg); |
| 2200 | return state->m_k051649->waveform_r (space, reg); |
| 2201 | 2201 | } |
| 2202 | 2202 | else if (reg < 0xa0) |
| 2203 | 2203 | { |
| r21175 | r21176 | |
| 2206 | 2206 | else if (reg < 0xc0) |
| 2207 | 2207 | { |
| 2208 | 2208 | /* read wave 5 */ |
| 2209 | | return k051649_waveform_r (state->m_k051649, space, 0x80 + (reg & 0x1f)); |
| 2209 | return state->m_k051649->waveform_r (space, 0x80 + (reg & 0x1f)); |
| 2210 | 2210 | } |
| 2211 | 2211 | else if (reg < 0xe0) |
| 2212 | 2212 | { |
| 2213 | | return k051649_test_r (state->m_k051649, space, reg); |
| 2213 | return state->m_k051649->test_r (space, reg); |
| 2214 | 2214 | } |
| 2215 | 2215 | |
| 2216 | 2216 | return 0xff; |
| r21175 | r21176 | |
| 2231 | 2231 | |
| 2232 | 2232 | if (reg < 0xa0) |
| 2233 | 2233 | { |
| 2234 | | return k052539_waveform_r (state->m_k051649, space, reg); |
| 2234 | return state->m_k051649->waveform_r (space, reg); |
| 2235 | 2235 | } |
| 2236 | 2236 | else if (reg >= 0xc0 && reg < 0xe0) |
| 2237 | 2237 | { |
| 2238 | | return k051649_test_r (state->m_k051649, space, reg); |
| 2238 | return state->m_k051649->test_r (space, reg); |
| 2239 | 2239 | } |
| 2240 | 2240 | |
| 2241 | 2241 | return 0xff; |
| r21175 | r21176 | |
| 2345 | 2345 | |
| 2346 | 2346 | if (offset < 0x80) |
| 2347 | 2347 | { |
| 2348 | | k051649_waveform_w (drvstate->m_k051649, space, offset, val); |
| 2348 | drvstate->m_k051649->waveform_w (space, offset, val); |
| 2349 | 2349 | } |
| 2350 | 2350 | else if (offset < 0xa0) |
| 2351 | 2351 | { |
| r21175 | r21176 | |
| 2353 | 2353 | |
| 2354 | 2354 | if (offset < 0xa) |
| 2355 | 2355 | { |
| 2356 | | k051649_frequency_w (drvstate->m_k051649, space, offset, val); |
| 2356 | drvstate->m_k051649->frequency_w (space, offset, val); |
| 2357 | 2357 | } |
| 2358 | 2358 | else if (offset < 0x0f) |
| 2359 | 2359 | { |
| 2360 | | k051649_volume_w (drvstate->m_k051649, space, offset - 0xa, val); |
| 2360 | drvstate->m_k051649->volume_w (space, offset - 0xa, val); |
| 2361 | 2361 | } |
| 2362 | 2362 | else if (offset == 0x0f) |
| 2363 | 2363 | { |
| 2364 | | k051649_keyonoff_w (drvstate->m_k051649, space, 0, val); |
| 2364 | drvstate->m_k051649->keyonoff_w (space, 0, val); |
| 2365 | 2365 | } |
| 2366 | 2366 | } |
| 2367 | 2367 | else if (offset < 0xe0) |
| 2368 | 2368 | { |
| 2369 | | k051649_test_w (drvstate->m_k051649, space, offset, val); |
| 2369 | drvstate->m_k051649->test_w (space, offset, val); |
| 2370 | 2370 | } |
| 2371 | 2371 | } |
| 2372 | 2372 | } |
| r21175 | r21176 | |
| 2394 | 2394 | |
| 2395 | 2395 | if (offset < 0xa0) |
| 2396 | 2396 | { |
| 2397 | | k052539_waveform_w (drvstate->m_k051649, space, offset, val); |
| 2397 | drvstate->m_k051649->waveform_w (space, offset, val); |
| 2398 | 2398 | } |
| 2399 | 2399 | else if (offset < 0xc0) |
| 2400 | 2400 | { |
| r21175 | r21176 | |
| 2402 | 2402 | |
| 2403 | 2403 | if (offset < 0x0a) |
| 2404 | 2404 | { |
| 2405 | | k051649_frequency_w (drvstate->m_k051649, space, offset, val); |
| 2405 | drvstate->m_k051649->frequency_w (space, offset, val); |
| 2406 | 2406 | } |
| 2407 | 2407 | else if (offset < 0x0f) |
| 2408 | 2408 | { |
| 2409 | | k051649_volume_w (drvstate->m_k051649, space, offset - 0x0a, val); |
| 2409 | drvstate->m_k051649->volume_w (space, offset - 0x0a, val); |
| 2410 | 2410 | } |
| 2411 | 2411 | else if (offset == 0x0f) |
| 2412 | 2412 | { |
| 2413 | | k051649_keyonoff_w (drvstate->m_k051649, space, 0, val); |
| 2413 | drvstate->m_k051649->keyonoff_w (space, 0, val); |
| 2414 | 2414 | } |
| 2415 | 2415 | } |
| 2416 | 2416 | else if (offset < 0xe0) |
| 2417 | 2417 | { |
| 2418 | | k051649_test_w (drvstate->m_k051649, space, offset, val); |
| 2418 | drvstate->m_k051649->test_w (space, offset, val); |
| 2419 | 2419 | } |
| 2420 | 2420 | } |
| 2421 | 2421 | } |