summaryrefslogblamecommitdiffstatshomepage
path: root/src/devices/sound/c352.cpp
blob: 38d9987d0910752eda6e17ed28eded2d074c28dd (plain) (tree)
1
2
3
4
5
6
7
8
9
                       
                                         
  




                                                         
 
                                                                                          
 




                                                      

   
                

                 


                     
 
                         
                                                           
 







                                                                            
                                                                                                         



                                                    
 

 








                                                   
                                               
 
                                   
 



                                                                      


            
                         
 
                                              
 
                                             


                                                       
 
                                             
 

                                                                             
                                             

                                                                             
                                             

                                                                                  

                                                                    
















                                                                                    
                                            

                         

                    
                                                                       

                 

 
                                                                 
 


                                                          
 
 

                                                                                                                             
                
                  
                             



                                                
                        
 
                    
 
                              
         
                                              
 

                                 
 

                                         
 

                                                    
                                                                  
 
                                                          
                                 

                                                        
 






                                                                       
 
                                                                 
 
                                              
 

                                                                     
                                                                                                         
                         
 
                               
                                                                                                 
                                                                                                 

                                

                                                                                                 
                 
 



                                                     


         
                                                       
 
                           
 
                              
         







                                                                     
          
 
                           
                                                                             

                                 

                         
 
                 

 
                                                                        
 
                           
 
                              
         







                                                                     

          
              
 
                           
         
                                                                            
         
                                 
         
                                

                                                                    



                                                           
                                                              
                         
                                                                                                       

                                                       
                                                            
                                                             
 

                                                                                         
 

                                                                                      
                         
                                                               

                                                                                      
                                                             


                         

 








                                                                                           
                                
 
              
 
                                                 
 
                                                                                   
 

















                                                                       
 


                                   





                                                            
                                                         





                                                           
         

                                   

 
                                
 
                                    
                                                     
 
                               

                          

 
                                  
 
                                   

 
                                    
 
                               
         
                                          





                                                                              
// license:BSD-3-Clause
// copyright-holders:R. Belmont, superctr
/*
    c352.c - Namco C352 custom PCM chip emulation
    v2.0
    By R. Belmont
    Rewritten and improved by superctr
    Additional code by cync and the hoot development team

    Thanks to Cap of VivaNonno for info and The_Author for preliminary reverse-engineering

    Chip specs:
    32 voices
    Supports 8-bit linear and 8-bit muLaw samples
    Output: digital, 16 bit, 4 channels
    Output sample rate is the input clock / (288 * 2).
 */

#include "emu.h"
#include "c352.h"

//#define VERBOSE 1
#include "logmacro.h"


// device type definition
DEFINE_DEVICE_TYPE(C352, c352_device, "c352", "Namco C352")

//**************************************************************************
//  LIVE DEVICE
//**************************************************************************

//-------------------------------------------------
//  c352_device - constructor
//-------------------------------------------------

c352_device::c352_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, C352, tag, owner, clock)
	, device_sound_interface(mconfig, *this)
	, device_rom_interface(mconfig, *this, 24)
	, m_stream(nullptr)
{
}

//-------------------------------------------------
//  rom_bank_updated - the rom bank has changed
//-------------------------------------------------

void c352_device::rom_bank_updated()
{
	m_stream->update();
}

void c352_device::fetch_sample(c352_voice_t* v)
{
	v->last_sample = v->sample;

	if(v->flags & C352_FLG_NOISE)
	{
		m_random = (m_random>>1) ^ ((-(m_random&1)) & 0xfff6);
		v->sample = m_random;
	}
	else
	{
		int8_t s;

		s = (int8_t)read_byte(v->pos);

		if(v->flags & C352_FLG_MULAW)
			v->sample = m_mulawtab[s&0xff];
		else
			v->sample = s<<8;

		uint16_t pos = v->pos&0xffff;

		if((v->flags & C352_FLG_LOOP) && v->flags & C352_FLG_REVERSE)
		{
			// backwards>forwards
			if((v->flags & C352_FLG_LDIR) && pos == v->wave_loop)
				v->flags &= ~C352_FLG_LDIR;
			// forwards>backwards
			else if(!(v->flags & C352_FLG_LDIR) && pos == v->wave_end)
				v->flags |= C352_FLG_LDIR;

			v->pos += (v->flags&C352_FLG_LDIR) ? -1 : 1;
		}
		else if(pos == v->wave_end)
		{
			if((v->flags & C352_FLG_LINK) && (v->flags & C352_FLG_LOOP))
			{
				v->pos = (v->wave_start<<16) | v->wave_loop;
				v->flags |= C352_FLG_LOOPHIST;
			}
			else if(v->flags & C352_FLG_LOOP)
			{
				v->pos = (v->pos&0xff0000) | v->wave_loop;
				v->flags |= C352_FLG_LOOPHIST;
			}
			else
			{
				v->flags |= C352_FLG_KEYOFF;
				v->flags &= ~C352_FLG_BUSY;
				v->sample=0;
			}
		}
		else
		{
			v->pos += (v->flags&C352_FLG_REVERSE) ? -1 : 1;
		}
	}
}

void c352_device::ramp_volume(c352_voice_t* v,int ch,uint8_t val)
{
	int16_t vol_delta = v->curr_vol[ch] - val;
	if(vol_delta != 0)
		v->curr_vol[ch] += (vol_delta>0) ? -1 : 1;
}

void c352_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
{
	int i,j;
	int16_t s;
	int32_t next_counter;
	stream_sample_t *buffer_fl = outputs[0];
	stream_sample_t *buffer_fr = outputs[1];
	stream_sample_t *buffer_rl = outputs[2];
	stream_sample_t *buffer_rr = outputs[3];
	c352_voice_t* v;

	long out[4];

	for(i=0;i<samples;i++)
	{
		out[0]=out[1]=out[2]=out[3]=0;

		for(j=0;j<32;j++)
		{

			v = &m_c352_v[j];
			s = 0;

			if(v->flags & C352_FLG_BUSY)
			{
				next_counter = v->counter+v->freq;

				if(next_counter & 0x10000)
				{
					fetch_sample(v);
				}

				if((next_counter^v->counter) & 0x18000)
				{
					ramp_volume(v,0,v->vol_f>>8);
					ramp_volume(v,1,v->vol_f&0xff);
					ramp_volume(v,2,v->vol_r>>8);
					ramp_volume(v,3,v->vol_r&0xff);
				}

				v->counter = next_counter&0xffff;

				s = v->sample;

				// Interpolate samples
				if((v->flags & C352_FLG_FILTER) == 0)
					s = v->last_sample + (v->counter*(v->sample-v->last_sample)>>16);
			}

			// Left
			out[0] += (((v->flags & C352_FLG_PHASEFL) ? -s : s) * v->curr_vol[0])>>8;
			out[2] += (((v->flags & C352_FLG_PHASERL) ? -s : s) * v->curr_vol[2])>>8;

			// Right
			out[1] += (((v->flags & C352_FLG_PHASEFR) ? -s : s) * v->curr_vol[1])>>8;
			out[3] += (((v->flags & C352_FLG_PHASEFR) ? -s : s) * v->curr_vol[3])>>8;
		}

		*buffer_fl++ = (int16_t) (out[0]>>3);
		*buffer_fr++ = (int16_t) (out[1]>>3);
		*buffer_rl++ = (int16_t) (out[2]>>3);
		*buffer_rr++ = (int16_t) (out[3]>>3);
	}
}

uint16_t c352_device::read_reg16(unsigned long address)
{
	m_stream->update();

	const int reg_map[8] =
	{
		offsetof(c352_voice_t,vol_f) / sizeof(uint16_t),
		offsetof(c352_voice_t,vol_r) / sizeof(uint16_t),
		offsetof(c352_voice_t,freq) / sizeof(uint16_t),
		offsetof(c352_voice_t,flags) / sizeof(uint16_t),
		offsetof(c352_voice_t,wave_bank) / sizeof(uint16_t),
		offsetof(c352_voice_t,wave_start) / sizeof(uint16_t),
		offsetof(c352_voice_t,wave_end) / sizeof(uint16_t),
		offsetof(c352_voice_t,wave_loop) / sizeof(uint16_t),
	};

	if(address < 0x100)
		return *((uint16_t*)&m_c352_v[address/8]+reg_map[address%8]);
	else if(address == 0x200)
		return m_control;
	else
		return 0;

	return 0;
}

void c352_device::write_reg16(unsigned long address, unsigned short val)
{
	m_stream->update();

	const int reg_map[8] =
	{
		offsetof(c352_voice_t,vol_f) / sizeof(uint16_t),
		offsetof(c352_voice_t,vol_r) / sizeof(uint16_t),
		offsetof(c352_voice_t,freq) / sizeof(uint16_t),
		offsetof(c352_voice_t,flags) / sizeof(uint16_t),
		offsetof(c352_voice_t,wave_bank) / sizeof(uint16_t),
		offsetof(c352_voice_t,wave_start) / sizeof(uint16_t),
		offsetof(c352_voice_t,wave_end) / sizeof(uint16_t),
		offsetof(c352_voice_t,wave_loop) / sizeof(uint16_t),
	};

	int i;

	if(address < 0x100)
	{
		*((uint16_t*)&m_c352_v[address/8]+reg_map[address%8]) = val;
	}
	else if(address == 0x200)
	{
		m_control = val;
		logerror("C352 control register write: %04x\n",val);
	}
	else if(address == 0x202) // execute keyons/keyoffs
	{
		for(i=0;i<32;i++)
		{
			if(m_c352_v[i].flags & C352_FLG_KEYON)
			{
				m_c352_v[i].pos = (m_c352_v[i].wave_bank<<16) | m_c352_v[i].wave_start;

				m_c352_v[i].sample = 0;
				m_c352_v[i].last_sample = 0;
				m_c352_v[i].counter = 0xffff;

				m_c352_v[i].flags |= C352_FLG_BUSY;
				m_c352_v[i].flags &= ~(C352_FLG_KEYON|C352_FLG_LOOPHIST);

				m_c352_v[i].curr_vol[0] = m_c352_v[i].curr_vol[1] = 0;
				m_c352_v[i].curr_vol[2] = m_c352_v[i].curr_vol[3] = 0;
			}
			if(m_c352_v[i].flags & C352_FLG_KEYOFF)
			{
				m_c352_v[i].flags &= ~(C352_FLG_BUSY|C352_FLG_KEYOFF);
				m_c352_v[i].counter = 0xffff;
			}
		}
	}
}

void c352_device::device_clock_changed()
{
	m_sample_rate_base = clock() / m_divider;
	if (m_stream != nullptr)
		m_stream->set_sample_rate(m_sample_rate_base);
	else
		m_stream = machine().sound().stream_alloc(*this, 0, 4, m_sample_rate_base);
}

void c352_device::device_start()
{
	int i;

	m_sample_rate_base = clock() / m_divider;

	m_stream = machine().sound().stream_alloc(*this, 0, 4, m_sample_rate_base);

	// generate mulaw table (Output similar to namco's VC emulator)
	int j = 0;
	for(int i=0;i<128;i++)
	{
		m_mulawtab[i] = j<<5;
		if(i < 16)
			j += 1;
		else if(i < 24)
			j += 2;
		else if(i < 48)
			j += 4;
		else if(i < 100)
			j += 8;
		else
			j += 16;
	}
	for(int i=0;i<128;i++)
		m_mulawtab[i+128] = (~m_mulawtab[i])&0xffe0;

	// register save state info
	for (i = 0; i < 32; i++)
	{
		save_item(NAME(m_c352_v[i].pos), i);
		save_item(NAME(m_c352_v[i].counter), i);
		save_item(NAME(m_c352_v[i].sample), i);
		save_item(NAME(m_c352_v[i].last_sample), i);
		save_item(NAME(m_c352_v[i].vol_f), i);
		save_item(NAME(m_c352_v[i].vol_r), i);
		save_item(NAME(m_c352_v[i].curr_vol), i);
		save_item(NAME(m_c352_v[i].freq), i);
		save_item(NAME(m_c352_v[i].flags), i);
		save_item(NAME(m_c352_v[i].wave_bank), i);
		save_item(NAME(m_c352_v[i].wave_start), i);
		save_item(NAME(m_c352_v[i].wave_end), i);
		save_item(NAME(m_c352_v[i].wave_loop), i);
	}
	save_item(NAME(m_random));
	save_item(NAME(m_control));
}

void c352_device::device_reset()
{
	// clear all channels states
	memset(m_c352_v, 0, sizeof(c352_voice_t)*32);

	// init noise generator
	m_random = 0x1234;
	m_control = 0;
}

READ16_MEMBER( c352_device::read )
{
	return(read_reg16(offset));
}

WRITE16_MEMBER( c352_device::write )
{
	if (mem_mask == 0xffff)
	{
		write_reg16(offset, data);
	}
	else
	{
		logerror("C352: byte-wide write unsupported at this time!\n");
	}
}