// license:GPL-2.0+
// copyright-holders:David Viens, Peter Trauner
/***************************************************************************
PeT mess@utanet.at
main part in video/
Refined with recording/analysis on MPT-03 (PAL UVI chip) by plgDavid
NTSC UVI sound clock: 15734Hz (arcadia)
PAL UVI sound clock: 15625Hz (Soundic MPT-03 - owned by plgDavid)
***************************************************************************/
#include "emu.h"
#include "includes/arcadia.h"
//known UVI audio clocks
#define UVI_NTSC 15734
#define UVI_PAL 15625
/* we need to create pulse transitions that sound 'decent'
with the current mess/mame interp scheme
this is not needed anymore with the new trick in streams.c
*/
#define OSAMP 1
//lfsr is 9 bits long (and same as Atari TIA pure noise)
#define LFSR_MASK (1<<8)
//makes alien invaders samples noise sync.
#define LFSR_INIT 0x00f0
//lfsr states at resynch borders
//0x01c1
//0x01e0
//0x00f0 //good synch
//0x0178
//0x01bc
// device type definition
DEFINE_DEVICE_TYPE(ARCADIA_SOUND, arcadia_sound_device, "arcadia_sound", "Arcadia Custom Sound")
//-------------------------------------------------
// arcadia_sound_device - constructor
//-------------------------------------------------
arcadia_sound_device::arcadia_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, ARCADIA_SOUND, tag, owner, clock)
, device_sound_interface(mconfig, *this)
{
}
//-------------------------------------------------
// device_start - device-specific startup
//-------------------------------------------------
void arcadia_sound_device::device_start()
{
m_channel = machine().sound().stream_alloc(*this, 0, 1, UVI_PAL*OSAMP);
m_lfsr = LFSR_INIT;
m_tval = 1;
logerror("arcadia_sound start\n");
}
//-------------------------------------------------
// device_start - device-specific reset
//-------------------------------------------------
void arcadia_sound_device::device_reset()
{
memset(m_reg, 0, sizeof(m_reg));
m_omode = 0;
m_pos = 0;
}
//-------------------------------------------------
// sound_stream_update - handle update requests for
// our sound stream
//-------------------------------------------------
void arcadia_sound_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
{
int i;
stream_sample_t *buffer = outputs[0];
for (i = 0; i < samples; i++, buffer++)
{
*buffer = 0;
//if minimal pitch ?
if (m_reg[1]){
switch (m_mode){
//dont play anything
case 0:break;
//tone only
case 1:
*buffer = m_volume * m_tval;
break;
//noise only
case 2:
*buffer = m_volume * m_nval;
break;
//tone AND noise (bitwise and)
case 3:
*buffer = m_volume * (m_tval & m_nval);
break;
}
//counter
m_pos++;
if (m_pos >= m_size){
//calculate new noise bit ( taps: 0000T000T)
unsigned char newBit = m_lfsr & 1; //first tap
newBit = (newBit ^ ((m_lfsr & 0x10)?1:0) );//xor with second tap
m_nval = m_lfsr & 1; //taking new output from LSB
m_lfsr = m_lfsr >> 1;//shifting
//insert new bit at end position (size-1) (only if non null)
if (newBit)
m_lfsr |= LFSR_MASK;
//invert tone
m_tval = !m_tval;
m_pos = 0;
}
}
}
}
//-------------------------------------------------
// soundport_w
//-------------------------------------------------
WRITE8_MEMBER(arcadia_sound_device::write)
{
m_channel->update();
m_reg[offset] = data;
//logerror("arcadia_sound write:%x=%x\n",offset,data);
switch (offset)
{
case 1:
//as per Gobbler samples:
//the freq counter is only applied on the next change in the flip flop
m_size = (data & 0x7f)*OSAMP;
//logerror("arcadia_sound write: frq:%d\n",data);
//reset LFSR
if(!m_size)
m_lfsr = LFSR_INIT;
break;
case 2:
m_volume = (data & 0x07) * 0x800;
m_mode = (data & 0x18) >> 3;
//logerror("arcadia_sound write: vol:%d mode:%d\n",m_volume,m_mode );
if (m_mode != m_omode){
//not 100% sure about this, maybe we should not reset anything
//m_pos = 0;
m_tval = 0;
}
m_omode = m_mode;
break;
}
}