#include "emu.h" #include "streams.h" #include "tms36xx.h" #define VERBOSE 1 #define LOG(x) do { if (VERBOSE) logerror x; } while (0) #define VMIN 0x0000 #define VMAX 0x7fff /* the frequencies are later adjusted by "* clock / FSCALE" */ #define FSCALE 1024 typedef struct _tms_state tms_state; struct _tms_state { char *subtype; /* subtype name MM6221AA, TMS3615 or TMS3617 */ sound_stream * channel; /* returned by stream_create() */ int samplerate; /* output sample rate */ int basefreq; /* chip's base frequency */ int octave; /* octave select of the TMS3615 */ int speed; /* speed of the tune */ int tune_counter; /* tune counter */ int note_counter; /* note counter */ int voices; /* active voices */ int shift; /* shift toggles between 0 and 6 to allow decaying voices */ int vol[12]; /* (decaying) volume of harmonics notes */ int vol_counter[12];/* volume adjustment counter */ int decay[12]; /* volume adjustment rate - dervied from decay */ int counter[12]; /* tone frequency counter */ int frequency[12]; /* tone frequency */ int output; /* output signal bits */ int enable; /* mask which harmoics */ int tune_num; /* tune currently playing */ int tune_ofs; /* note currently playing */ int tune_max; /* end of tune */ const tms36xx_interface *intf; }; #define C(n) (int)((FSCALE<<(n-1))*1.18921) /* 2^(3/12) */ #define Cx(n) (int)((FSCALE<<(n-1))*1.25992) /* 2^(4/12) */ #define D(n) (int)((FSCALE<<(n-1))*1.33484) /* 2^(5/12) */ #define Dx(n) (int)((FSCALE<<(n-1))*1.41421) /* 2^(6/12) */ #define E(n) (int)((FSCALE<<(n-1))*1.49831) /* 2^(7/12) */ #define F(n) (int)((FSCALE<<(n-1))*1.58740) /* 2^(8/12) */ #define Fx(n) (int)((FSCALE<<(n-1))*1.68179) /* 2^(9/12) */ #define G(n) (int)((FSCALE<<(n-1))*1.78180) /* 2^(10/12) */ #define Gx(n) (int)((FSCALE<<(n-1))*1.88775) /* 2^(11/12) */ #define A(n) (int)((FSCALE<vol[voice] > VMIN ) \ { \ /* decay of first voice */ \ tms->vol_counter[voice] -= tms->decay[voice]; \ while( tms->vol_counter[voice] <= 0 ) \ { \ tms->vol_counter[voice] += samplerate; \ if( tms->vol[voice]-- <= VMIN ) \ { \ tms->frequency[voice] = 0; \ tms->vol[voice] = VMIN; \ break; \ } \ } \ } #define RESTART(voice) \ if( tunes[tms->tune_num][tms->tune_ofs*6+voice] ) \ { \ tms->frequency[tms->shift+voice] = \ tunes[tms->tune_num][tms->tune_ofs*6+voice] * \ (tms->basefreq << tms->octave) / FSCALE; \ tms->vol[tms->shift+voice] = VMAX; \ } #define TONE(voice) \ if( (tms->enable & (1<frequency[voice] ) \ { \ /* first note */ \ tms->counter[voice] -= tms->frequency[voice]; \ while( tms->counter[voice] <= 0 ) \ { \ tms->counter[voice] += samplerate; \ tms->output ^= 1 << voice; \ } \ if (tms->output & tms->enable & (1 << voice)) \ sum += tms->vol[voice]; \ } INLINE tms_state *get_safe_token(running_device *device) { assert(device != NULL); assert(device->type() == SOUND_TMS36XX); return (tms_state *)downcast(device)->token(); } static STREAM_UPDATE( tms36xx_sound_update ) { tms_state *tms = (tms_state *)param; int samplerate = tms->samplerate; stream_sample_t *buffer = outputs[0]; /* no tune played? */ if( !tunes[tms->tune_num] || tms->voices == 0 ) { while (--samples >= 0) buffer[samples] = 0; return; } while( samples-- > 0 ) { int sum = 0; /* decay the twelve voices */ DECAY( 0) DECAY( 1) DECAY( 2) DECAY( 3) DECAY( 4) DECAY( 5) DECAY( 6) DECAY( 7) DECAY( 8) DECAY( 9) DECAY(10) DECAY(11) /* musical note timing */ tms->tune_counter -= tms->speed; if( tms->tune_counter <= 0 ) { int n = (-tms->tune_counter / samplerate) + 1; tms->tune_counter += n * samplerate; if( (tms->note_counter -= n) <= 0 ) { tms->note_counter += VMAX; if (tms->tune_ofs < tms->tune_max) { /* shift to the other 'bank' of voices */ tms->shift ^= 6; /* restart one 'bank' of voices */ RESTART(0) RESTART(1) RESTART(2) RESTART(3) RESTART(4) RESTART(5) tms->tune_ofs++; } } } /* update the twelve voices */ TONE( 0) TONE( 1) TONE( 2) TONE( 3) TONE( 4) TONE( 5) TONE( 6) TONE( 7) TONE( 8) TONE( 9) TONE(10) TONE(11) *buffer++ = sum / tms->voices; } } static void tms36xx_reset_counters(tms_state *tms) { tms->tune_counter = 0; tms->note_counter = 0; memset(tms->vol_counter, 0, sizeof(tms->vol_counter)); memset(tms->counter, 0, sizeof(tms->counter)); } void mm6221aa_tune_w(running_device *device, int tune) { tms_state *tms = get_safe_token(device); /* which tune? */ tune &= 3; if( tune == tms->tune_num ) return; LOG(("%s tune:%X\n", tms->subtype, tune)); /* update the stream before changing the tune */ stream_update(tms->channel); tms->tune_num = tune; tms->tune_ofs = 0; tms->tune_max = 96; /* fixed for now */ } void tms36xx_note_w(running_device *device, int octave, int note) { tms_state *tms = get_safe_token(device); octave &= 3; note &= 15; if (note > 12) return; LOG(("%s octave:%X note:%X\n", tms->subtype, octave, note)); /* update the stream before changing the tune */ stream_update(tms->channel); /* play a single note from 'tune 4', a list of the 13 tones */ tms36xx_reset_counters(tms); tms->octave = octave; tms->tune_num = 4; tms->tune_ofs = note; tms->tune_max = note + 1; } static void tms3617_enable(tms_state *tms, int enable) { int i, bits = 0; /* duplicate the 6 voice enable bits */ enable = (enable & 0x3f) | ((enable & 0x3f) << 6); if (enable == tms->enable) return; /* update the stream before changing the tune */ stream_update(tms->channel); LOG(("%s enable voices", tms->subtype)); for (i = 0; i < 6; i++) { if (enable & (1 << i)) { bits += 2; /* each voice has two instances */ switch (i) { case 0: LOG((" 16'")); break; case 1: LOG((" 8'")); break; case 2: LOG((" 5 1/3'")); break; case 3: LOG((" 4'")); break; case 4: LOG((" 2 2/3'")); break; case 5: LOG((" 2'")); break; } } } /* set the enable mask and number of active voices */ tms->enable = enable; tms->voices = bits; LOG(("%s\n", bits ? "" : " none")); } void tms3617_enable_w(running_device *device, int enable) { tms_state *tms = get_safe_token(device); tms3617_enable(tms, enable); } static DEVICE_START( tms36xx ) { int j; tms_state *tms = get_safe_token(device); int enable; tms->intf = (const tms36xx_interface *)device->baseconfig().static_config(); tms->channel = stream_create(device, 0, 1, device->clock() * 64, tms, tms36xx_sound_update); tms->samplerate = device->clock() * 64; tms->basefreq = device->clock(); enable = 0; for (j = 0; j < 6; j++) { if( tms->intf->decay[j] > 0 ) { tms->decay[j+0] = tms->decay[j+6] = VMAX / tms->intf->decay[j]; enable |= 0x41 << j; } } tms->speed = (tms->intf->speed > 0) ? VMAX / tms->intf->speed : VMAX; tms3617_enable(tms,enable); LOG(("TMS36xx samplerate %d\n", tms->samplerate)); LOG(("TMS36xx basefreq %d\n", tms->basefreq)); LOG(("TMS36xx decay %d,%d,%d,%d,%d,%d\n", tms->decay[0], tms->decay[1], tms->decay[2], tms->decay[3], tms->decay[4], tms->decay[5])); LOG(("TMS36xx speed %d\n", tms->speed)); } /************************************************************************** * Generic get_info **************************************************************************/ DEVICE_GET_INFO( tms36xx ) { switch (state) { /* --- the following bits of info are returned as 64-bit signed integers --- */ case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(tms_state); break; /* --- the following bits of info are returned as pointers to data or functions --- */ case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( tms36xx ); break; case DEVINFO_FCT_STOP: /* Nothing */ break; case DEVINFO_FCT_RESET: /* Nothing */ break; /* --- the following bits of info are returned as NULL-terminated strings --- */ case DEVINFO_STR_NAME: strcpy(info->s, "TMS36XX"); break; case DEVINFO_STR_FAMILY: strcpy(info->s, "TI PSG"); break; case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; } } DEFINE_LEGACY_SOUND_DEVICE(TMS36XX, tms36xx);