// license:BSD-3-Clause // copyright-holders:Juergen Buchmueller #include "emu.h" #include "tms36xx.h" #define VERBOSE 1 #define LOG(x) do { if (VERBOSE) logerror x; } while (0) /* the frequencies are later adjusted by "* clock / FSCALE" */ #define FSCALE 1024 #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< TMS36XX_VMIN ) \ { \ /* decay of first voice */ \ m_vol_counter[voice] -= m_decay[voice]; \ while( m_vol_counter[voice] <= 0 ) \ { \ m_vol_counter[voice] += samplerate; \ if( m_vol[voice]-- <= TMS36XX_VMIN ) \ { \ m_frequency[voice] = 0; \ m_vol[voice] = TMS36XX_VMIN; \ break; \ } \ } \ } #define RESTART(voice) \ if( tunes[m_tune_num][m_tune_ofs*6+voice] ) \ { \ m_frequency[m_shift+voice] = \ tunes[m_tune_num][m_tune_ofs*6+voice] * \ (m_basefreq << m_octave) / FSCALE; \ m_vol[m_shift+voice] = TMS36XX_VMAX; \ } #define TONE(voice) \ if( (m_enable & (1<; //************************************************************************** // LIVE DEVICE //************************************************************************** //------------------------------------------------- // tms36xx_device - constructor //------------------------------------------------- tms36xx_device::tms36xx_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : device_t(mconfig, TMS36XX, "TMS36XX", tag, owner, clock, "tms36xx", __FILE__), device_sound_interface(mconfig, *this), m_subtype(nullptr), m_channel(nullptr), m_samplerate(0), m_basefreq(0), m_octave(0), m_speed(0), m_tune_counter(0), m_note_counter(0), m_voices(0), m_shift(0), m_output(0), m_enable(0), m_tune_num(0), m_tune_ofs(0), m_tune_max(0) { memset(m_vol, 0, sizeof(int)*12); memset(m_vol_counter, 0, sizeof(int)*12); memset(m_decay, 0, sizeof(int)*12); memset(m_counter, 0, sizeof(int)*12); memset(m_frequency, 0, sizeof(int)*12); } //------------------------------------------------- // device_start - device-specific startup //------------------------------------------------- void tms36xx_device::device_start() { int enable = 0; m_channel = stream_alloc(0, 1, clock() * 64); m_samplerate = clock() * 64; m_basefreq = clock(); for (int j = 0; j < 6; j++) { if (m_decay_time[j] > 0) { m_decay[j+0] = m_decay[j+6] = TMS36XX_VMAX / m_decay_time[j]; enable |= 0x41 << j; } } tms3617_enable(enable); LOG(("TMS36xx samplerate %d\n", m_samplerate)); LOG(("TMS36xx basefreq %d\n", m_basefreq)); LOG(("TMS36xx decay %d,%d,%d,%d,%d,%d\n", m_decay[0], m_decay[1], m_decay[2], m_decay[3], m_decay[4], m_decay[5])); LOG(("TMS36xx speed %d\n", m_speed)); save_item(NAME(m_octave)); save_item(NAME(m_tune_counter)); save_item(NAME(m_note_counter)); save_item(NAME(m_voices)); save_item(NAME(m_shift)); save_item(NAME(m_vol)); save_item(NAME(m_vol_counter)); save_item(NAME(m_counter)); save_item(NAME(m_frequency)); save_item(NAME(m_output)); save_item(NAME(m_enable)); save_item(NAME(m_tune_num)); save_item(NAME(m_tune_ofs)); save_item(NAME(m_tune_max)); } //------------------------------------------------- // sound_stream_update - handle a stream update //------------------------------------------------- void tms36xx_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) { int samplerate = m_samplerate; stream_sample_t *buffer = outputs[0]; /* no tune played? */ if( !tunes[m_tune_num] || m_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 */ m_tune_counter -= m_speed; if( m_tune_counter <= 0 ) { int n = (-m_tune_counter / samplerate) + 1; m_tune_counter += n * samplerate; if( (m_note_counter -= n) <= 0 ) { m_note_counter += TMS36XX_VMAX; if (m_tune_ofs < m_tune_max) { /* shift to the other 'bank' of voices */ m_shift ^= 6; /* restart one 'bank' of voices */ RESTART(0) RESTART(1) RESTART(2) RESTART(3) RESTART(4) RESTART(5) m_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 / m_voices; } } //------------------------------------------------- // MM6221AA interface functions //------------------------------------------------- void tms36xx_device::mm6221aa_tune_w(int tune) { /* which tune? */ tune &= 3; if( tune == m_tune_num ) return; LOG(("%s tune:%X\n", m_subtype, tune)); /* update the stream before changing the tune */ m_channel->update(); m_tune_num = tune; m_tune_ofs = 0; m_tune_max = 96; /* fixed for now */ } //------------------------------------------------- // TMS3615/17 interface functions //------------------------------------------------- void tms36xx_device::tms36xx_note_w(int octave, int note) { octave &= 3; note &= 15; if (note > 12) return; LOG(("%s octave:%X note:%X\n", m_subtype, octave, note)); /* update the stream before changing the tune */ m_channel->update(); /* play a single note from 'tune 4', a list of the 13 tones */ tms36xx_reset_counters(); m_octave = octave; m_tune_num = 4; m_tune_ofs = note; m_tune_max = note + 1; } //------------------------------------------------- // TMS3617 interface functions //------------------------------------------------- void tms36xx_device::tms3617_enable_w(int enable) { tms3617_enable(enable); } //------------------------------------------------- // Locals //------------------------------------------------- void tms36xx_device::tms36xx_reset_counters() { m_tune_counter = 0; m_note_counter = 0; memset(m_vol_counter, 0, sizeof(m_vol_counter)); memset(m_counter, 0, sizeof(m_counter)); } void tms36xx_device::tms3617_enable(int enable) { int i, bits = 0; /* duplicate the 6 voice enable bits */ enable = (enable & 0x3f) | ((enable & 0x3f) << 6); if (enable == m_enable) return; /* update the stream before changing the tune */ m_channel->update(); LOG(("%s enable voices", m_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 */ m_enable = enable; m_voices = bits; LOG(("%s\n", bits ? "" : " none")); }