trunk/src/mame/audio/flower.c
| r23465 | r23466 | |
| 19 | 19 | #define MIXER_DEFGAIN 48 |
| 20 | 20 | |
| 21 | 21 | |
| 22 | | /* this structure defines the parameters for a channel */ |
| 23 | | struct sound_channel |
| 24 | | { |
| 25 | | UINT32 start; |
| 26 | | UINT32 pos; |
| 27 | | UINT16 freq; |
| 28 | | UINT8 volume; |
| 29 | | UINT8 voltab; |
| 30 | | UINT8 oneshot; |
| 31 | | UINT8 active; |
| 32 | | UINT8 effect; |
| 33 | | UINT32 ecount; |
| 22 | const device_type FLOWER = &device_creator<flower_sound_device>; |
| 34 | 23 | |
| 35 | | }; |
| 36 | | |
| 37 | | |
| 38 | | struct flower_sound_state |
| 24 | flower_sound_device::flower_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) |
| 25 | : device_t(mconfig, FLOWER, "Flower Custom Sound", tag, owner, clock), |
| 26 | device_sound_interface(mconfig, *this) |
| 39 | 27 | { |
| 40 | | emu_timer *m_effect_timer; |
| 41 | | |
| 42 | | /* data about the sound system */ |
| 43 | | sound_channel m_channel_list[8]; |
| 44 | | sound_channel *m_last_channel; |
| 45 | | |
| 46 | | /* global sound parameters */ |
| 47 | | const UINT8 *m_sample_rom; |
| 48 | | const UINT8 *m_volume_rom; |
| 49 | | sound_stream * m_stream; |
| 50 | | |
| 51 | | /* mixer tables and internal buffers */ |
| 52 | | INT16 *m_mixer_table; |
| 53 | | INT16 *m_mixer_lookup; |
| 54 | | short *m_mixer_buffer; |
| 55 | | |
| 56 | | UINT8 m_soundregs1[0x40]; |
| 57 | | UINT8 m_soundregs2[0x40]; |
| 58 | | }; |
| 59 | | |
| 60 | | INLINE flower_sound_state *get_safe_token( device_t *device ) |
| 61 | | { |
| 62 | | assert(device != NULL); |
| 63 | | assert(device->type() == FLOWER); |
| 64 | | |
| 65 | | return (flower_sound_state *)downcast<flower_sound_device *>(device)->token(); |
| 66 | 28 | } |
| 67 | 29 | |
| 68 | | /* build a table to divide by the number of voices; gain is specified as gain*16 */ |
| 69 | | static void make_mixer_table(device_t *device, int voices, int gain) |
| 70 | | { |
| 71 | | flower_sound_state *state = get_safe_token(device); |
| 72 | | int count = voices * 128; |
| 73 | | int i; |
| 30 | //------------------------------------------------- |
| 31 | // device_config_complete - perform any |
| 32 | // operations now that the configuration is |
| 33 | // complete |
| 34 | //------------------------------------------------- |
| 74 | 35 | |
| 75 | | /* allocate memory */ |
| 76 | | state->m_mixer_table = auto_alloc_array(device->machine(), INT16, 256 * voices); |
| 77 | | |
| 78 | | /* find the middle of the table */ |
| 79 | | state->m_mixer_lookup = state->m_mixer_table + (128 * voices); |
| 80 | | |
| 81 | | /* fill in the table - 16 bit case */ |
| 82 | | for (i = 0; i < count; i++) |
| 83 | | { |
| 84 | | int val = i * gain * 16 / voices; |
| 85 | | if (val > 32767) val = 32767; |
| 86 | | state->m_mixer_lookup[ i] = val; |
| 87 | | state->m_mixer_lookup[-i] =-val; |
| 88 | | } |
| 89 | | } |
| 90 | | |
| 91 | | |
| 92 | | /* generate sound to the mix buffer in mono */ |
| 93 | | static STREAM_UPDATE( flower_update_mono ) |
| 36 | void flower_sound_device::device_config_complete() |
| 94 | 37 | { |
| 95 | | flower_sound_state *state = get_safe_token(device); |
| 96 | | stream_sample_t *buffer = outputs[0]; |
| 97 | | sound_channel *voice; |
| 98 | | short *mix; |
| 99 | | int i; |
| 100 | | |
| 101 | | /* zap the contents of the mixer buffer */ |
| 102 | | memset(state->m_mixer_buffer, 0, samples * sizeof(short)); |
| 103 | | |
| 104 | | /* loop over each voice and add its contribution */ |
| 105 | | for (voice = state->m_channel_list; voice < state->m_last_channel; voice++) |
| 106 | | { |
| 107 | | int f = voice->freq; |
| 108 | | int v = voice->volume; |
| 109 | | |
| 110 | | if (!voice->active) |
| 111 | | continue; |
| 112 | | |
| 113 | | // effects |
| 114 | | // bit 0: volume slide down? |
| 115 | | if (voice->effect & 1 && !voice->oneshot) |
| 116 | | { |
| 117 | | // note: one-shot samples are fixed volume |
| 118 | | v -= (voice->ecount >> 4); |
| 119 | | if (v < 0) v = 0; |
| 120 | | } |
| 121 | | // bit 1: used often, but hard to figure out what for |
| 122 | | // bit 2: probably pitch slide |
| 123 | | if (voice->effect & 4) |
| 124 | | { |
| 125 | | f -= (voice->ecount << 7); |
| 126 | | if (f < 0) f = 0; |
| 127 | | } |
| 128 | | // bit 3: not used much, maybe pitch slide the other way? |
| 129 | | |
| 130 | | v |= voice->voltab; |
| 131 | | mix = state->m_mixer_buffer; |
| 132 | | |
| 133 | | for (i = 0; i < samples; i++) |
| 134 | | { |
| 135 | | // add sample |
| 136 | | if (voice->oneshot) |
| 137 | | { |
| 138 | | UINT8 sample = state->m_sample_rom[(voice->start + voice->pos) >> 7 & 0x7fff]; |
| 139 | | if (sample == 0xff) |
| 140 | | { |
| 141 | | voice->active = 0; |
| 142 | | break; |
| 143 | | } |
| 144 | | else |
| 145 | | *mix++ += state->m_volume_rom[v << 8 | sample] - 0x80; |
| 146 | | } |
| 147 | | else |
| 148 | | { |
| 149 | | UINT8 sample = state->m_sample_rom[(voice->start >> 7 & 0x7e00) | (voice->pos >> 7 & 0x1ff)]; |
| 150 | | *mix++ += state->m_volume_rom[v << 8 | sample] - 0x80; |
| 151 | | } |
| 152 | | |
| 153 | | // update counter |
| 154 | | voice->pos += f; |
| 155 | | } |
| 156 | | } |
| 157 | | |
| 158 | | /* mix it down */ |
| 159 | | mix = state->m_mixer_buffer; |
| 160 | | for (i = 0; i < samples; i++) |
| 161 | | *buffer++ = state->m_mixer_lookup[*mix++]; |
| 162 | 38 | } |
| 163 | 39 | |
| 164 | | /* clock sound channel effect counters */ |
| 165 | | static TIMER_CALLBACK( flower_clock_effect ) |
| 166 | | { |
| 167 | | flower_sound_state *state = (flower_sound_state *)ptr; |
| 168 | | sound_channel *voice; |
| 169 | | state->m_stream->update(); |
| 40 | //------------------------------------------------- |
| 41 | // device_start - device-specific startup |
| 42 | //------------------------------------------------- |
| 170 | 43 | |
| 171 | | for (voice = state->m_channel_list; voice < state->m_last_channel; voice++) |
| 172 | | voice->ecount += (voice->ecount < (1<<22)); |
| 173 | | } |
| 174 | | |
| 175 | | |
| 176 | | |
| 177 | | static DEVICE_START( flower_sound ) |
| 44 | void flower_sound_device::device_start() |
| 178 | 45 | { |
| 179 | | flower_sound_state *state = get_safe_token(device); |
| 180 | | running_machine &machine = device->machine(); |
| 181 | | sound_channel *voice; |
| 182 | | int i; |
| 46 | flower_sound_channel *voice; |
| 183 | 47 | |
| 184 | | state->m_effect_timer = machine.scheduler().timer_alloc(FUNC(flower_clock_effect), state); |
| 185 | | state->m_stream = device->machine().sound().stream_alloc(*device, 0, 1, MIXER_SAMPLERATE, 0, flower_update_mono); |
| 186 | | state->m_mixer_buffer = auto_alloc_array(device->machine(), short, MIXER_SAMPLERATE); |
| 187 | | make_mixer_table(device, 8, MIXER_DEFGAIN); |
| 48 | m_effect_timer = timer_alloc(TIMER_CLOCK_EFFECT); |
| 49 | m_stream = machine().sound().stream_alloc(*this, 0, 1, MIXER_SAMPLERATE, this); |
| 50 | m_mixer_buffer = auto_alloc_array(machine(), short, MIXER_SAMPLERATE); |
| 51 | make_mixer_table(8, MIXER_DEFGAIN); |
| 188 | 52 | |
| 189 | 53 | /* extract globals from the interface */ |
| 190 | | state->m_last_channel = state->m_channel_list + 8; |
| 54 | m_last_channel = m_channel_list + 8; |
| 191 | 55 | |
| 192 | | state->m_sample_rom = machine.root_device().memregion("sound1")->base(); |
| 193 | | state->m_volume_rom = machine.root_device().memregion("sound2")->base(); |
| 56 | m_sample_rom = machine().root_device().memregion("sound1")->base(); |
| 57 | m_volume_rom = machine().root_device().memregion("sound2")->base(); |
| 194 | 58 | |
| 195 | 59 | /* register for savestates */ |
| 196 | | for (i = 0; i < 8; i++) |
| 60 | for (int i = 0; i < 8; i++) |
| 197 | 61 | { |
| 198 | | voice = &state->m_channel_list[i]; |
| 62 | voice = &m_channel_list[i]; |
| 199 | 63 | |
| 200 | | device->save_item(NAME(voice->freq), i+1); |
| 201 | | device->save_item(NAME(voice->pos), i+1); |
| 202 | | device->save_item(NAME(voice->volume), i+1); |
| 203 | | device->save_item(NAME(voice->voltab), i+1); |
| 204 | | device->save_item(NAME(voice->effect), i+1); |
| 205 | | device->save_item(NAME(voice->ecount), i+1); |
| 206 | | device->save_item(NAME(voice->oneshot), i+1); |
| 207 | | device->save_item(NAME(voice->active), i+1); |
| 208 | | device->save_item(NAME(voice->start), i+1); |
| 64 | save_item(NAME(voice->freq), i+1); |
| 65 | save_item(NAME(voice->pos), i+1); |
| 66 | save_item(NAME(voice->volume), i+1); |
| 67 | save_item(NAME(voice->voltab), i+1); |
| 68 | save_item(NAME(voice->effect), i+1); |
| 69 | save_item(NAME(voice->ecount), i+1); |
| 70 | save_item(NAME(voice->oneshot), i+1); |
| 71 | save_item(NAME(voice->active), i+1); |
| 72 | save_item(NAME(voice->start), i+1); |
| 209 | 73 | } |
| 210 | 74 | } |
| 211 | 75 | |
| 212 | | static DEVICE_RESET( flower_sound ) |
| 76 | //------------------------------------------------- |
| 77 | // device_reset - device-specific reset |
| 78 | //------------------------------------------------- |
| 79 | |
| 80 | void flower_sound_device::device_reset() |
| 213 | 81 | { |
| 214 | | flower_sound_state *state = get_safe_token(device); |
| 215 | | sound_channel *voice; |
| 82 | flower_sound_channel *voice; |
| 216 | 83 | attotime period; |
| 217 | | int i; |
| 218 | 84 | |
| 219 | 85 | /* reset effect timer, period is unknown/guessed */ |
| 220 | 86 | period = attotime::from_hz(MIXER_SAMPLERATE / 256); |
| 221 | | state->m_effect_timer->adjust(period, 0, period); |
| 87 | m_effect_timer->adjust(period, 0, period); |
| 222 | 88 | |
| 223 | 89 | /* reset all the voices */ |
| 224 | | for (i = 0; i < 8; i++) |
| 90 | for (int i = 0; i < 8; i++) |
| 225 | 91 | { |
| 226 | | voice = &state->m_channel_list[i]; |
| 92 | voice = &m_channel_list[i]; |
| 227 | 93 | |
| 228 | 94 | voice->freq = 0; |
| 229 | 95 | voice->pos = 0; |
| r23465 | r23466 | |
| 235 | 101 | voice->active = 0; |
| 236 | 102 | voice->start = 0; |
| 237 | 103 | } |
| 104 | |
| 105 | memset(m_soundregs1, 0, 0x40); |
| 106 | memset(m_soundregs2, 0, 0x40); |
| 238 | 107 | } |
| 239 | 108 | |
| 109 | /* build a table to divide by the number of voices; gain is specified as gain*16 */ |
| 110 | void flower_sound_device::make_mixer_table(int voices, int gain) |
| 111 | { |
| 112 | int count = voices * 128; |
| 113 | |
| 114 | /* allocate memory */ |
| 115 | m_mixer_table = auto_alloc_array(machine(), INT16, 256 * voices); |
| 116 | |
| 117 | /* find the middle of the table */ |
| 118 | m_mixer_lookup = m_mixer_table + (128 * voices); |
| 119 | |
| 120 | /* fill in the table - 16 bit case */ |
| 121 | for (int i = 0; i < count; i++) |
| 122 | { |
| 123 | int val = i * gain * 16 / voices; |
| 124 | if (val > 32767) val = 32767; |
| 125 | m_mixer_lookup[ i] = val; |
| 126 | m_mixer_lookup[-i] =-val; |
| 127 | } |
| 128 | } |
| 129 | |
| 130 | |
| 131 | /* clock sound channel effect counters */ |
| 132 | void flower_sound_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) |
| 133 | { |
| 134 | switch (id) |
| 135 | { |
| 136 | case TIMER_CLOCK_EFFECT: |
| 137 | flower_sound_channel *voice; |
| 138 | m_stream->update(); |
| 139 | |
| 140 | for (voice = m_channel_list; voice < m_last_channel; voice++) |
| 141 | voice->ecount += (voice->ecount < (1<<22)); |
| 142 | break; |
| 143 | |
| 144 | default: |
| 145 | assert_always(FALSE, "Unknown id in flower_sound_device::device_timer"); |
| 146 | } |
| 147 | } |
| 148 | |
| 240 | 149 | /********************************************************************************/ |
| 241 | 150 | |
| 242 | 151 | #if FLOWER_VERBOSE |
| 243 | 152 | static int r_numwrites[2][8] = {{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0}}; |
| 244 | | static void show_soundregs(device_t *device) |
| 153 | void flower_sound_device::show_soundregs() |
| 245 | 154 | { |
| 246 | | flower_sound_state *state = get_safe_token(device); |
| 247 | 155 | int set,reg,chan; |
| 248 | 156 | char text[0x100]; |
| 249 | 157 | char message[0x1000] = {0}; |
| 250 | | UINT8 *base = state->m_soundregs1; |
| 158 | UINT8 *base = m_soundregs1; |
| 251 | 159 | |
| 252 | 160 | for (set=0;set<2;set++) |
| 253 | 161 | { |
| r23465 | r23466 | |
| 265 | 173 | strcat(message,text); |
| 266 | 174 | } |
| 267 | 175 | strcat(message,"\n"); |
| 268 | | base = state->m_soundregs2; |
| 176 | base = m_soundregs2; |
| 269 | 177 | } |
| 270 | 178 | popmessage("%s",message); |
| 271 | 179 | } |
| r23465 | r23466 | |
| 301 | 209 | |
| 302 | 210 | */ |
| 303 | 211 | |
| 304 | | WRITE8_DEVICE_HANDLER( flower_sound1_w ) |
| 212 | WRITE8_MEMBER( flower_sound_device::sound1_w ) |
| 305 | 213 | { |
| 306 | | flower_sound_state *state = get_safe_token(device); |
| 307 | | sound_channel *voice = &state->m_channel_list[offset >> 3 & 7]; |
| 214 | flower_sound_channel *voice = &m_channel_list[offset >> 3 & 7]; |
| 308 | 215 | int c = offset & 0xf8; |
| 309 | | UINT8 *base1 = state->m_soundregs1; |
| 310 | | // UINT8 *base2 = state->m_soundregs2; |
| 216 | UINT8 *base1 = m_soundregs1; |
| 217 | // UINT8 *base2 = m_soundregs2; |
| 311 | 218 | |
| 312 | | state->m_stream->update(); |
| 219 | m_stream->update(); |
| 313 | 220 | base1[offset] = data; |
| 314 | 221 | #if FLOWER_VERBOSE |
| 315 | 222 | r_numwrites[0][offset & 7]++; |
| 316 | | show_soundregs(device); |
| 223 | show_soundregs(); |
| 317 | 224 | #endif |
| 318 | 225 | |
| 319 | 226 | // recompute voice parameters |
| r23465 | r23466 | |
| 321 | 228 | voice->volume = base1[c+7] >> 4; |
| 322 | 229 | } |
| 323 | 230 | |
| 324 | | WRITE8_DEVICE_HANDLER( flower_sound2_w ) |
| 231 | WRITE8_MEMBER( flower_sound_device::sound2_w ) |
| 325 | 232 | { |
| 326 | | flower_sound_state *state = get_safe_token(device); |
| 327 | | sound_channel *voice = &state->m_channel_list[offset >> 3 & 7]; |
| 233 | flower_sound_channel *voice = &m_channel_list[offset >> 3 & 7]; |
| 328 | 234 | int i, c = offset & 0xf8; |
| 329 | | UINT8 *base1 = state->m_soundregs1; |
| 330 | | UINT8 *base2 = state->m_soundregs2; |
| 235 | UINT8 *base1 = m_soundregs1; |
| 236 | UINT8 *base2 = m_soundregs2; |
| 331 | 237 | |
| 332 | | state->m_stream->update(); |
| 238 | m_stream->update(); |
| 333 | 239 | base2[offset] = data; |
| 334 | 240 | #if FLOWER_VERBOSE |
| 335 | 241 | r_numwrites[1][offset & 7]++; |
| 336 | | show_soundregs(device); |
| 242 | show_soundregs(); |
| 337 | 243 | #endif |
| 338 | 244 | |
| 339 | 245 | // reg 7 is start trigger! |
| r23465 | r23466 | |
| 354 | 260 | } |
| 355 | 261 | |
| 356 | 262 | |
| 357 | | const device_type FLOWER = &device_creator<flower_sound_device>; |
| 358 | | |
| 359 | | flower_sound_device::flower_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) |
| 360 | | : device_t(mconfig, FLOWER, "Flower Custom", tag, owner, clock), |
| 361 | | device_sound_interface(mconfig, *this) |
| 362 | | { |
| 363 | | m_token = global_alloc_clear(flower_sound_state); |
| 364 | | } |
| 365 | | |
| 366 | 263 | //------------------------------------------------- |
| 367 | | // device_config_complete - perform any |
| 368 | | // operations now that the configuration is |
| 369 | | // complete |
| 264 | // sound_stream_update - handle a stream update |
| 370 | 265 | //------------------------------------------------- |
| 371 | 266 | |
| 372 | | void flower_sound_device::device_config_complete() |
| 267 | void flower_sound_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) |
| 373 | 268 | { |
| 374 | | } |
| 269 | stream_sample_t *buffer = outputs[0]; |
| 270 | flower_sound_channel *voice; |
| 271 | short *mix; |
| 272 | int i; |
| 375 | 273 | |
| 376 | | //------------------------------------------------- |
| 377 | | // device_start - device-specific startup |
| 378 | | //------------------------------------------------- |
| 274 | /* zap the contents of the mixer buffer */ |
| 275 | memset(m_mixer_buffer, 0, samples * sizeof(short)); |
| 379 | 276 | |
| 380 | | void flower_sound_device::device_start() |
| 381 | | { |
| 382 | | DEVICE_START_NAME( flower_sound )(this); |
| 383 | | } |
| 277 | /* loop over each voice and add its contribution */ |
| 278 | for (voice = m_channel_list; voice < m_last_channel; voice++) |
| 279 | { |
| 280 | int f = voice->freq; |
| 281 | int v = voice->volume; |
| 384 | 282 | |
| 385 | | //------------------------------------------------- |
| 386 | | // device_reset - device-specific reset |
| 387 | | //------------------------------------------------- |
| 283 | if (!voice->active) |
| 284 | continue; |
| 388 | 285 | |
| 389 | | void flower_sound_device::device_reset() |
| 390 | | { |
| 391 | | DEVICE_RESET_NAME( flower_sound )(this); |
| 392 | | } |
| 286 | // effects |
| 287 | // bit 0: volume slide down? |
| 288 | if (voice->effect & 1 && !voice->oneshot) |
| 289 | { |
| 290 | // note: one-shot samples are fixed volume |
| 291 | v -= (voice->ecount >> 4); |
| 292 | if (v < 0) v = 0; |
| 293 | } |
| 294 | // bit 1: used often, but hard to figure out what for |
| 295 | // bit 2: probably pitch slide |
| 296 | if (voice->effect & 4) |
| 297 | { |
| 298 | f -= (voice->ecount << 7); |
| 299 | if (f < 0) f = 0; |
| 300 | } |
| 301 | // bit 3: not used much, maybe pitch slide the other way? |
| 393 | 302 | |
| 394 | | //------------------------------------------------- |
| 395 | | // sound_stream_update - handle a stream update |
| 396 | | //------------------------------------------------- |
| 303 | v |= voice->voltab; |
| 304 | mix = m_mixer_buffer; |
| 397 | 305 | |
| 398 | | void flower_sound_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) |
| 399 | | { |
| 400 | | // should never get here |
| 401 | | fatalerror("sound_stream_update called; not applicable to legacy sound devices\n"); |
| 306 | for (i = 0; i < samples; i++) |
| 307 | { |
| 308 | // add sample |
| 309 | if (voice->oneshot) |
| 310 | { |
| 311 | UINT8 sample = m_sample_rom[(voice->start + voice->pos) >> 7 & 0x7fff]; |
| 312 | if (sample == 0xff) |
| 313 | { |
| 314 | voice->active = 0; |
| 315 | break; |
| 316 | } |
| 317 | else |
| 318 | *mix++ += m_volume_rom[v << 8 | sample] - 0x80; |
| 319 | } |
| 320 | else |
| 321 | { |
| 322 | UINT8 sample = m_sample_rom[(voice->start >> 7 & 0x7e00) | (voice->pos >> 7 & 0x1ff)]; |
| 323 | *mix++ += m_volume_rom[v << 8 | sample] - 0x80; |
| 324 | } |
| 325 | |
| 326 | // update counter |
| 327 | voice->pos += f; |
| 328 | } |
| 329 | } |
| 330 | |
| 331 | /* mix it down */ |
| 332 | mix = m_mixer_buffer; |
| 333 | for (i = 0; i < samples; i++) |
| 334 | *buffer++ = m_mixer_lookup[*mix++]; |
| 402 | 335 | } |