mame/src/mess/audio/wswan.c

251 lines
7.0 KiB
C

/**************************************************************************************
Wonderswan sound emulation
Wilbert Pol
Sound emulation is very preliminary and far from complete
**************************************************************************************/
#include "includes/wswan.h"
// device type definition
const device_type WSWAN = &device_creator<wswan_sound_device>;
//**************************************************************************
// LIVE DEVICE
//**************************************************************************
//-------------------------------------------------
// wswan_sound_device - constructor
//-------------------------------------------------
wswan_sound_device::wswan_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
: device_t(mconfig, WSWAN, "WonderSwan Custom", tag, owner, clock),
device_sound_interface(mconfig, *this),
m_channel(NULL),
m_sweep_step(0),
m_sweep_time(0),
m_sweep_count(0),
m_noise_type(0),
m_noise_reset(0),
m_noise_enable(0),
m_sample_address(0),
m_audio2_voice(0),
m_audio3_sweep(0),
m_audio4_noise(0),
m_mono(0),
m_voice_data(0),
m_output_volume(0),
m_external_stereo(0),
m_external_speaker(0),
m_noise_shift(0),
m_master_volume(0)
{
}
//-------------------------------------------------
// device_start - device-specific startup
//-------------------------------------------------
void wswan_sound_device::device_start()
{
m_channel = stream_alloc(0, 2, machine().sample_rate());
m_audio1.on = 0;
m_audio1.signal = 16;
m_audio1.pos = 0;
m_audio2.on = 0;
m_audio2.signal = 16;
m_audio2.pos = 0;
m_audio3.on = 0;
m_audio3.signal = 16;
m_audio3.pos = 0;
m_audio4.on = 0;
m_audio4.signal = 16;
m_audio4.pos = 0;
}
//-------------------------------------------------
// sound_stream_update - handle a stream update
//-------------------------------------------------
void wswan_sound_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
{
stream_sample_t sample, left, right;
while( samples-- > 0 )
{
left = right = 0;
if ( m_audio1.on ) {
sample = m_audio1.signal;
m_audio1.pos++;
if ( m_audio1.pos >= m_audio1.period / 2 ) {
m_audio1.pos = 0;
m_audio1.signal = -m_audio1.signal;
}
left += m_audio1.vol_left * sample;
right += m_audio1.vol_right * sample;
}
if ( m_audio2.on ) {
if ( m_audio2_voice ) {
left += (m_voice_data - 128)*(m_master_volume & 0x0f);
right += (m_voice_data - 128)*(m_master_volume & 0x0f);
} else {
sample = m_audio2.signal;
m_audio2.pos++;
if ( m_audio2.pos >= m_audio2.period / 2 ) {
m_audio2.pos = 0;
m_audio2.signal = -m_audio2.signal;
}
left += m_audio2.vol_left * sample;
right += m_audio2.vol_right * sample;
}
}
if ( m_audio3.on ) {
sample = m_audio3.signal;
m_audio3.pos++;
if ( m_audio3.pos >= m_audio3.period / 2 ) {
m_audio3.pos = 0;
m_audio3.signal = -m_audio3.signal;
}
if ( m_audio3_sweep && m_sweep_time ) {
m_sweep_count++;
if ( m_sweep_count >= m_sweep_time ) {
m_sweep_count = 0;
m_audio3.freq += m_sweep_step;
m_audio3.period = machine().sample_rate() / (3072000 / ((2048 - (m_audio3.freq & 0x7ff)) << 5));
}
}
left += m_audio3.vol_left * sample;
right += m_audio3.vol_right * sample;
}
if ( m_audio4.on ) {
if ( m_audio4_noise ) {
sample = 0;
} else {
sample = m_audio4.signal;
m_audio4.pos++;
if ( m_audio4.pos >= m_audio4.period / 2 ) {
m_audio4.pos = 0;
m_audio4.signal = -m_audio4.signal;
}
}
left += m_audio4.vol_left * sample;
right += m_audio4.vol_right * sample;
}
left <<= 5;
right <<= 5;
*(outputs[0]++) = left;
*(outputs[1]++) = right;
}
}
void wswan_sound_device::wswan_ch_set_freq( CHAN *ch, UINT16 freq )
{
freq &= 0x7ff; // docs say freq is 11bits and a few games (Morita Shougi, World Stadium + others) write 0x800 causing a divide by 0 crash
ch->freq = freq;
ch->period = machine().sample_rate() / (3072000 / ((2048 - freq) << 5));
}
WRITE8_MEMBER( wswan_sound_device::wswan_sound_port_w )
{
m_channel->update();
switch( offset ) {
case 0x80: /* Audio 1 freq (lo) */
wswan_ch_set_freq(&m_audio1, (m_audio1.freq & 0xff00) | data);
break;
case 0x81: /* Audio 1 freq (hi) */
wswan_ch_set_freq(&m_audio1, (data << 8 ) | (m_audio1.freq & 0x00ff));
break;
case 0x82: /* Audio 2 freq (lo) */
wswan_ch_set_freq(&m_audio2, (m_audio2.freq & 0xff00) | data);
break;
case 0x83: /* Audio 2 freq (hi) */
wswan_ch_set_freq(&m_audio2, (data << 8 ) | (m_audio2.freq & 0x00ff));
break;
case 0x84: /* Audio 3 freq (lo) */
wswan_ch_set_freq(&m_audio3, (m_audio3.freq & 0xff00) | data);
break;
case 0x85: /* Audio 3 freq (hi) */
wswan_ch_set_freq(&m_audio3, (data << 8) | (m_audio3.freq & 0x00ff));
break;
case 0x86: /* Audio 4 freq (lo) */
wswan_ch_set_freq(&m_audio4, (m_audio4.freq & 0xff00) | data);
break;
case 0x87: /* Audio 4 freq (hi) */
wswan_ch_set_freq(&m_audio4, (data << 8) | (m_audio4.freq & 0x00ff));
break;
case 0x88: /* Audio 1 volume */
m_audio1.vol_left = ( data & 0xF0 ) >> 4;
m_audio1.vol_right = data & 0x0F;
break;
case 0x89: /* Audio 2 volume */
m_voice_data = data;
m_audio2.vol_left = ( data & 0xF0 ) >> 4;
m_audio2.vol_right = data & 0x0F;
break;
case 0x8A: /* Audio 3 volume */
m_audio3.vol_left = ( data & 0xF0 ) >> 4;
m_audio3.vol_right = data & 0x0F;
break;
case 0x8B: /* Audio 4 volume */
m_audio4.vol_left = ( data & 0xF0 ) >> 4;
m_audio4.vol_right = data & 0x0F;
break;
case 0x8C: /* Sweep step */
m_sweep_step = (INT8)data;
break;
case 0x8D: /* Sweep time */
m_sweep_time = space.machine().sample_rate() / ( 3072000 / ( 8192 * (data + 1) ) );
break;
case 0x8E: /* Noise control */
m_noise_type = data & 0x07;
m_noise_reset = ( data & 0x08 ) >> 3;
m_noise_enable = ( data & 0x10 ) >> 4;
break;
case 0x8F: /* Sample location */
m_sample_address = data << 6;
break;
case 0x90: /* Audio control */
m_audio1.on = data & 0x01;
m_audio2.on = ( data & 0x02 ) >> 1;
m_audio3.on = ( data & 0x04 ) >> 2;
m_audio4.on = ( data & 0x08 ) >> 3;
m_audio2_voice = ( data & 0x20 ) >> 5;
m_audio3_sweep = ( data & 0x40 ) >> 6;
m_audio4_noise = ( data & 0x80 ) >> 7;
break;
case 0x91: /* Audio output */
m_mono = data & 0x01;
m_output_volume = ( data & 0x06 ) >> 1;
m_external_stereo = ( data & 0x08 ) >> 3;
m_external_speaker = 1;
break;
case 0x92: /* Noise counter shift register (lo) */
m_noise_shift = ( m_noise_shift & 0xFF00 ) | data;
break;
case 0x93: /* Noise counter shift register (hi) */
m_noise_shift = ( data << 8 ) | ( m_noise_shift & 0x00FF );
break;
case 0x94: /* Master volume */
m_master_volume = data;
break;
}
}