// license:???// copyright-holders:Anthony Kruize/*************************************************************************************** Game Boy sound emulation (c) Anthony Kruize (trandor@labyrinth.net.au)** Anyways, sound on the Game Boy consists of 4 separate 'channels'* Sound1 = Quadrangular waves with SWEEP and ENVELOPE functions (NR10,11,12,13,14)* Sound2 = Quadrangular waves with ENVELOPE functions (NR21,22,23,24)* Sound3 = Wave patterns from WaveRAM (NR30,31,32,33,34)* Sound4 = White noise with an envelope (NR41,42,43,44)** Each sound channel has 2 modes, namely ON and OFF... whoa** These tend to be the two most important equations in* converting between Hertz and GB frequency registers:* (Sounds will have a 2.4% higher frequency on Super GB.)* gb = 2048 - (131072 / Hz)* Hz = 131072 / (2048 - gb)** Changes:** 10/2/2002 AK - Preliminary sound code.* 13/2/2002 AK - Added a hack for mode 4, other fixes.* 23/2/2002 AK - Use lookup tables, added sweep to mode 1. Re-wrote the square* wave generation.* 13/3/2002 AK - Added mode 3, better lookup tables, other adjustments.* 15/3/2002 AK - Mode 4 can now change frequencies.* 31/3/2002 AK - Accidently forgot to handle counter/consecutive for mode 1.* 3/4/2002 AK - Mode 1 sweep can still occur if shift is 0. Don't let frequency* go past the maximum allowed value. Fixed Mode 3 length table.* Slight adjustment to Mode 4's period table generation.* 5/4/2002 AK - Mode 4 is done correctly, using a polynomial counter instead* of being a total hack.* 6/4/2002 AK - Slight tweak to mode 3's frequency calculation.* 13/4/2002 AK - Reset envelope value when sound is initialized.* 21/4/2002 AK - Backed out the mode 3 frequency calculation change.* Merged init functions into gameboy_sound_w().* 14/5/2002 AK - Removed magic numbers in the fixed point math.* 12/6/2002 AK - Merged SOUNDx structs into one SOUND struct.* 26/10/2002 AK - Finally fixed channel 3!****************************************************************************************/#include"emu.h"#include"gb.h"/*************************************************************************** CONSTANTS***************************************************************************/#define NR10 0x00#define NR11 0x01#define NR12 0x02#define NR13 0x03#define NR14 0x04#define NR21 0x06#define NR22 0x07#define NR23 0x08#define NR24 0x09#define NR30 0x0A#define NR31 0x0B#define NR32 0x0C#define NR33 0x0D#define NR34 0x0E#define NR41 0x10#define NR42 0x11#define NR43 0x12#define NR44 0x13#define NR50 0x14#define NR51 0x15#define NR52 0x16#define AUD3W0 0x20#define AUD3W1 0x21#define AUD3W2 0x22#define AUD3W3 0x23#define AUD3W4 0x24#define AUD3W5 0x25#define AUD3W6 0x26#define AUD3W7 0x27#define AUD3W8 0x28#define AUD3W9 0x29#define AUD3WA 0x2A#define AUD3WB 0x2B#define AUD3WC 0x2C#define AUD3WD 0x2D#define AUD3WE 0x2E#define AUD3WF 0x2F#define LEFT 1#define RIGHT 2#define FIXED_POINT 16/* Represents wave duties of 12.5%, 25%, 50% and 75% */staticconstfloatwave_duty_table[4]={8.0f,4.0f,2.0f,1.33f};// device type definitionconstdevice_typeGAMEBOY=&device_creator<gameboy_sound_device>;//**************************************************************************// LIVE DEVICE//**************************************************************************//-------------------------------------------------// gameboy_sound_device - constructor//-------------------------------------------------gameboy_sound_device::gameboy_sound_device(constmachine_config&mconfig,constchar*tag,device_t*owner,UINT32clock):device_t(mconfig,GAMEBOY,"LR35902 Sound",tag,owner,clock,"gameboy_sound",__FILE__),device_sound_interface(mconfig,*this){}//-------------------------------------------------// device_config_complete - perform any// operations now that the configuration is// complete//-------------------------------------------------voidgameboy_sound_device::device_config_complete(){}//-------------------------------------------------// device_start - device-specific startup//-------------------------------------------------voidgameboy_sound_device::device_start(){m_channel=machine().sound().stream_alloc(*this,0,2,machine().sample_rate());m_rate=machine().sample_rate();save_item(NAME(m_snd_regs));// sound controlsave_item(NAME(m_snd_control.on));save_item(NAME(m_snd_control.vol_left));save_item(NAME(m_snd_control.vol_right));save_item(NAME(m_snd_control.mode1_left));save_item(NAME(m_snd_control.mode1_right));save_item(NAME(m_snd_control.mode2_left));save_item(NAME(m_snd_control.mode2_right));save_item(NAME(m_snd_control.mode3_left));save_item(NAME(m_snd_control.mode3_right));save_item(NAME(m_snd_control.mode4_left));save_item(NAME(m_snd_control.mode4_right));// sound 1save_item(NAME(m_snd_1.on));save_item(NAME(m_snd_1.channel));save_item(NAME(m_snd_1.length));save_item(NAME(m_snd_1.pos));save_item(NAME(m_snd_1.period));save_item(NAME(m_snd_1.count));save_item(NAME(m_snd_1.mode));save_item(NAME(m_snd_1.duty));save_item(NAME(m_snd_1.env_value));save_item(NAME(m_snd_1.env_direction));save_item(NAME(m_snd_1.env_length));save_item(NAME(m_snd_1.env_count));save_item(NAME(m_snd_1.signal));save_item(NAME(m_snd_1.frequency));save_item(NAME(m_snd_1.swp_shift));save_item(NAME(m_snd_1.swp_direction));save_item(NAME(m_snd_1.swp_time));save_item(NAME(m_snd_1.swp_count));save_item(NAME(m_snd_1.level));save_item(NAME(m_snd_1.offset));save_item(NAME(m_snd_1.dutycount));save_item(NAME(m_snd_1.ply_step));save_item(NAME(m_snd_1.ply_value));// sound 2save_item(NAME(m_snd_2.on));save_item(NAME(m_snd_2.channel));save_item(NAME(m_snd_2.length));save_item(NAME(m_snd_2.pos));save_item(NAME(m_snd_2.period));save_item(NAME(m_snd_2.count));save_item(NAME(m_snd_2.mode));save_item(NAME(m_snd_2.duty));save_item(NAME(m_snd_2.env_value));save_item(NAME(m_snd_2.env_direction));save_item(NAME(m_snd_2.env_length));save_item(NAME(m_snd_2.env_count));save_item(NAME(m_snd_2.signal));save_item(NAME(m_snd_2.frequency));save_item(NAME(m_snd_2.swp_shift));save_item(NAME(m_snd_2.swp_direction));save_item(NAME(m_snd_2.swp_time));save_item(NAME(m_snd_2.swp_count));save_item(NAME(m_snd_2.level));save_item(NAME(m_snd_2.offset));save_item(NAME(m_snd_2.dutycount));save_item(NAME(m_snd_2.ply_step));save_item(NAME(m_snd_2.ply_value));// sound 3save_item(NAME(m_snd_3.on));save_item(NAME(m_snd_3.channel));save_item(NAME(m_snd_3.length));save_item(NAME(m_snd_3.pos));save_item(NAME(m_snd_3.period));save_item(NAME(m_snd_3.count));save_item(NAME(m_snd_3.mode));save_item(NAME(m_snd_3.duty));save_item(NAME(m_snd_3.env_value));save_item(NAME(m_snd_3.env_direction));save_item(NAME(m_snd_3.env_length));save_item(NAME(m_snd_3.env_count));save_item(NAME(m_snd_3.signal));save_item(NAME(m_snd_3.frequency));save_item(NAME(m_snd_3.swp_shift));save_item(NAME(m_snd_3.swp_direction));save_item(NAME(m_snd_3.swp_time));save_item(NAME(m_snd_3.swp_count));save_item(NAME(m_snd_3.level));save_item(NAME(m_snd_3.offset));save_item(NAME(m_snd_3.dutycount));save_item(NAME(m_snd_3.ply_step));save_item(NAME(m_snd_3.ply_value));// sound 4save_item(NAME(m_snd_4.on));save_item(NAME(m_snd_4.channel));save_item(NAME(m_snd_4.length));save_item(NAME(m_snd_4.pos));save_item(NAME(m_snd_4.period));save_item(NAME(m_snd_4.count));save_item(NAME(m_snd_4.mode));save_item(NAME(m_snd_4.duty));save_item(NAME(m_snd_4.env_value));save_item(NAME(m_snd_4.env_direction));save_item(NAME(m_snd_4.env_length));save_item(NAME(m_snd_4.env_count));save_item(NAME(m_snd_4.signal));save_item(NAME(m_snd_4.frequency));save_item(NAME(m_snd_4.swp_shift));save_item(NAME(m_snd_4.swp_direction));save_item(NAME(m_snd_4.swp_time));save_item(NAME(m_snd_4.swp_count));save_item(NAME(m_snd_4.level));save_item(NAME(m_snd_4.offset));save_item(NAME(m_snd_4.dutycount));save_item(NAME(m_snd_4.ply_step));save_item(NAME(m_snd_4.ply_value));}//-------------------------------------------------// device_reset//-------------------------------------------------voidgameboy_sound_device::device_reset(){memset(&m_snd_1,0,sizeof(m_snd_1));memset(&m_snd_2,0,sizeof(m_snd_2));memset(&m_snd_3,0,sizeof(m_snd_3));memset(&m_snd_4,0,sizeof(m_snd_4));/* Calculate the envelope and sweep tables */for(inti=0;i<8;i++){m_env_length_table[i]=(i*((1<<FIXED_POINT)/64)*m_rate)>>FIXED_POINT;m_swp_time_table[i]=(((i<<FIXED_POINT)/128)*m_rate)>>(FIXED_POINT-1);}/* Calculate the period tables */for(inti=0;i<MAX_FREQUENCIES;i++){m_period_table[i]=((1<<FIXED_POINT)/(131072/(2048-i)))*m_rate;m_period_mode3_table[i]=((1<<FIXED_POINT)/(65536/(2048-i)))*m_rate;}/* Calculate the period table for mode 4 */for(inti=0;i<8;i++){for(intj=0;j<16;j++){// i is the dividing ratio of frequencies// j is the shift clock frequencym_period_mode4_table[i][j]=((1<<FIXED_POINT)/(524288/((i==0)?0.5:i)/(1<<(j+1))))*m_rate;}}/* Calculate the length table */for(inti=0;i<64;i++)m_length_table[i]=((64-i)*((1<<FIXED_POINT)/256)*m_rate)>>FIXED_POINT;/* Calculate the length table for mode 3 */for(inti=0;i<256;i++)m_length_mode3_table[i]=((256-i)*((1<<FIXED_POINT)/256)*m_rate)>>FIXED_POINT;sound_w_internal(NR52,0x00);m_snd_regs[AUD3W0]=0xac;m_snd_regs[AUD3W1]=0xdd;m_snd_regs[AUD3W2]=0xda;m_snd_regs[AUD3W3]=0x48;m_snd_regs[AUD3W4]=0x36;m_snd_regs[AUD3W5]=0x02;m_snd_regs[AUD3W6]=0xcf;m_snd_regs[AUD3W7]=0x16;m_snd_regs[AUD3W8]=0x2c;m_snd_regs[AUD3W9]=0x04;m_snd_regs[AUD3WA]=0xe5;m_snd_regs[AUD3WB]=0x2c;m_snd_regs[AUD3WC]=0xac;m_snd_regs[AUD3WD]=0xdd;m_snd_regs[AUD3WE]=0xda;m_snd_regs[AUD3WF]=0x48;}/*************************************************************************** IMPLEMENTATION***************************************************************************/READ8_MEMBER(gameboy_sound_device::wave_r){/* TODO: properly emulate scrambling of wave ram area when playback is active */returnm_snd_regs[AUD3W0+offset]|m_snd_3.on;}READ8_MEMBER(gameboy_sound_device::sound_r){switch(offset){case0x05:case0x0f:return0xff;caseNR52:return0x70|m_snd_regs[offset];default:returnm_snd_regs[offset];}}WRITE8_MEMBER(gameboy_sound_device::wave_w){m_snd_regs[AUD3W0+offset]=data;}WRITE8_MEMBER(gameboy_sound_device::sound_w){/* change in registers so update first */m_channel->update();/* Only register NR52 is accessible if the sound controller is disabled */if(!m_snd_control.on&&offset!=NR52)return;sound_w_internal(offset,data);}voidgameboy_sound_device::sound_w_internal(intoffset,UINT8data){/* Store the value */m_snd_regs[offset]=data;switch(offset){/*MODE 1 */caseNR10:/* Sweep (R/W) */m_snd_1.swp_shift=data&0x7;m_snd_1.swp_direction=(data&0x8)>>3;m_snd_1.swp_direction|=m_snd_1.swp_direction-1;m_snd_1.swp_time=m_swp_time_table[(data&0x70)>>4];break;caseNR11:/* Sound length/Wave pattern duty (R/W) */m_snd_1.duty=(data&0xc0)>>6;m_snd_1.length=m_length_table[data&0x3f];break;caseNR12:/* Envelope (R/W) */m_snd_1.env_value=data>>4;m_snd_1.env_direction=(data&0x8)>>3;m_snd_1.env_direction|=m_snd_1.env_direction-1;m_snd_1.env_length=m_env_length_table[data&0x7];break;caseNR13:/* Frequency lo (R/W) */m_snd_1.frequency=((m_snd_regs[NR14]&0x7)<<8)|m_snd_regs[NR13];m_snd_1.period=m_period_table[m_snd_1.frequency];break;caseNR14:/* Frequency hi / Initialize (R/W) */m_snd_1.mode=(data&0x40)>>6;m_snd_1.frequency=((m_snd_regs[NR14]&0x7)<<8)|m_snd_regs[NR13];m_snd_1.period=m_period_table[m_snd_1.frequency];if(data&0x80){if(!m_snd_1.on)m_snd_1.pos=0;m_snd_1.on=1;m_snd_1.count=0;m_snd_1.env_value=m_snd_regs[NR12]>>4;m_snd_1.env_count=0;m_snd_1.swp_count=0;m_snd_1.signal=0x1;m_snd_regs[NR52]|=0x1;}break;/*MODE 2 */caseNR21:/* Sound length/Wave pattern duty (R/W) */m_snd_2.duty=(data&0xc0)>>6;m_snd_2.length=m_length_table[data&0x3f];break;caseNR22:/* Envelope (R/W) */m_snd_2.env_value=data>>4;m_snd_2.env_direction=(data&0x8)>>3;m_snd_2.env_direction|=m_snd_2.env_direction-1;m_snd_2.env_length=m_env_length_table[data&0x7];break;caseNR23:/* Frequency lo (R/W) */m_snd_2.period=m_period_table[((m_snd_regs[NR24]&0x7)<<8)|m_snd_regs[NR23]];break;caseNR24:/* Frequency hi / Initialize (R/W) */m_snd_2.mode=(data&0x40)>>6;m_snd_2.period=m_period_table[((m_snd_regs[NR24]&0x7)<<8)|m_snd_regs[NR23]];if(data&0x80){if(!m_snd_2.on)m_snd_2.pos=0;m_snd_2.on=1;m_snd_2.count=0;m_snd_2.env_value=m_snd_regs[NR22]>>4;m_snd_2.env_count=0;m_snd_2.signal=0x1;m_snd_regs[NR52]|=0x2;}break;/*MODE 3 */caseNR30:/* Sound On/Off (R/W) */m_snd_3.on=(data&0x80)>>7;break;caseNR31:/* Sound Length (R/W) */m_snd_3.length=m_length_mode3_table[data];break;caseNR32:/* Select Output Level */m_snd_3.level=(data&0x60)>>5;break;caseNR33:/* Frequency lo (W) */m_snd_3.period=m_period_mode3_table[((m_snd_regs[NR34]&0x7)<<8)+m_snd_regs[NR33]];break;caseNR34:/* Frequency hi / Initialize (W) */m_snd_3.mode=(data&0x40)>>6;m_snd_3.period=m_period_mode3_table[((m_snd_regs[NR34]&0x7)<<8)+m_snd_regs[NR33]];if(data&0x80){if(!m_snd_3.on){m_snd_3.pos=0;m_snd_3.offset=0;m_snd_3.duty=0;}m_snd_3.on=1;m_snd_3.count=0;m_snd_3.duty=1;m_snd_3.dutycount=0;m_snd_regs[NR52]|=0x4;}break;/*MODE 4 */caseNR41:/* Sound Length (R/W) */m_snd_4.length=m_length_table[data&0x3f];break;caseNR42:/* Envelope (R/W) */m_snd_4.env_value=data>>4;m_snd_4.env_direction=(data&0x8)>>3;m_snd_4.env_direction|=m_snd_4.env_direction-1;m_snd_4.env_length=m_env_length_table[data&0x7];break;caseNR43:/* Polynomial Counter/Frequency */m_snd_4.period=m_period_mode4_table[data&0x7][(data&0xF0)>>4];m_snd_4.ply_step=(data&0x8)>>3;break;caseNR44:/* Counter/Consecutive / Initialize (R/W) */m_snd_4.mode=(data&0x40)>>6;if(data&0x80){if(!m_snd_4.on)m_snd_4.pos=0;m_snd_4.on=1;m_snd_4.count=0;m_snd_4.env_value=m_snd_regs[NR42]>>4;m_snd_4.env_count=0;m_snd_4.signal=machine().rand();m_snd_4.ply_value=0x7fff;m_snd_regs[NR52]|=0x8;}break;/* CONTROL */caseNR50:/* Channel Control / On/Off / Volume (R/W) */m_snd_control.vol_left=data&0x7;m_snd_control.vol_right=(data&0x70)>>4;break;caseNR51:/* Selection of Sound Output Terminal */m_snd_control.mode1_right=data&0x1;m_snd_control.mode1_left=(data&0x10)>>4;m_snd_control.mode2_right=(data&0x2)>>1;m_snd_control.mode2_left=(data&0x20)>>5;m_snd_control.mode3_right=(data&0x4)>>2;m_snd_control.mode3_left=(data&0x40)>>6;m_snd_control.mode4_right=(data&0x8)>>3;m_snd_control.mode4_left=(data&0x80)>>7;break;caseNR52:/* Sound On/Off (R/W) *//* Only bit 7 is writable, writing to bits 0-3 does NOT enable or disable sound. They are read-only */m_snd_control.on=(data&0x80)>>7;if(!m_snd_control.on){sound_w_internal(NR10,0x80);sound_w_internal(NR11,0x3F);sound_w_internal(NR12,0x00);sound_w_internal(NR13,0xFE);sound_w_internal(NR14,0xBF);// sound_w_internal(NR20, 0xFF);sound_w_internal(NR21,0x3F);sound_w_internal(NR22,0x00);sound_w_internal(NR23,0xFF);sound_w_internal(NR24,0xBF);sound_w_internal(NR30,0x7F);sound_w_internal(NR31,0xFF);sound_w_internal(NR32,0x9F);sound_w_internal(NR33,0xFF);sound_w_internal(NR34,0xBF);// sound_w_internal(NR40, 0xFF);sound_w_internal(NR41,0xFF);sound_w_internal(NR42,0x00);sound_w_internal(NR43,0x00);sound_w_internal(NR44,0xBF);sound_w_internal(NR50,0x00);sound_w_internal(NR51,0x00);m_snd_1.on=0;m_snd_2.on=0;m_snd_3.on=0;m_snd_4.on=0;m_snd_regs[offset]=0;}break;}}//-------------------------------------------------// sound_stream_update - handle a stream update//-------------------------------------------------voidgameboy_sound_device::sound_stream_update(sound_stream&stream,stream_sample_t**inputs,stream_sample_t**outputs,intsamples){stream_sample_tsample,left,right,mode4_mask;while(samples-->0){left=right=0;/* Mode 1 - Wave with Envelope and Sweep */if(m_snd_1.on){sample=m_snd_1.signal*m_snd_1.env_value;m_snd_1.pos++;if(m_snd_1.pos==(UINT32)(m_snd_1.period/wave_duty_table[m_snd_1.duty])>>FIXED_POINT){m_snd_1.signal=-m_snd_1.signal;}elseif(m_snd_1.pos>(m_snd_1.period>>FIXED_POINT)){m_snd_1.pos=0;m_snd_1.signal=-m_snd_1.signal;}if(m_snd_1.length&&m_snd_1.mode){m_snd_1.count++;if(m_snd_1.count>=m_snd_1.length){m_snd_1.on=0;m_snd_regs[NR52]&=0xFE;}}if(m_snd_1.env_length){m_snd_1.env_count++;if(m_snd_1.env_count>=m_snd_1.env_length){m_snd_1.env_count=0;m_snd_1.env_value+=m_snd_1.env_direction;if(m_snd_1.env_value<0)m_snd_1.env_value=0;if(m_snd_1.env_value>15)m_snd_1.env_value=15;}}if(m_snd_1.swp_time){m_snd_1.swp_count++;if(m_snd_1.swp_count>=m_snd_1.swp_time){m_snd_1.swp_count=0;if(m_snd_1.swp_direction>0){m_snd_1.frequency-=m_snd_1.frequency/(1<<m_snd_1.swp_shift);if(m_snd_1.frequency<=0){m_snd_1.on=0;m_snd_regs[NR52]&=0xFE;}}else{m_snd_1.frequency+=m_snd_1.frequency/(1<<m_snd_1.swp_shift);if(m_snd_1.frequency>=MAX_FREQUENCIES){m_snd_1.frequency=MAX_FREQUENCIES-1;}}m_snd_1.period=m_period_table[m_snd_1.frequency];}}if(m_snd_control.mode1_left)left+=sample;if(m_snd_control.mode1_right)right+=sample;}/* Mode 2 - Wave with Envelope */if(m_snd_2.on){sample=m_snd_2.signal*m_snd_2.env_value;m_snd_2.pos++;if(m_snd_2.pos==(UINT32)(m_snd_2.period/wave_duty_table[m_snd_2.duty])>>FIXED_POINT){m_snd_2.signal=-m_snd_2.signal;}elseif(m_snd_2.pos>(m_snd_2.period>>FIXED_POINT)){m_snd_2.pos=0;m_snd_2.signal=-m_snd_2.signal;}if(m_snd_2.length&&m_snd_2.mode){m_snd_2.count++;if(m_snd_2.count>=m_snd_2.length){m_snd_2.on=0;m_snd_regs[NR52]&=0xFD;}}if(m_snd_2.env_length){m_snd_2.env_count++;if(m_snd_2.env_count>=m_snd_2.env_length){m_snd_2.env_count=0;m_snd_2.env_value+=m_snd_2.env_direction;if(m_snd_2.env_value<0)m_snd_2.env_value=0;if(m_snd_2.env_value>15)m_snd_2.env_value=15;}}if(m_snd_control.mode2_left)left+=sample;if(m_snd_control.mode2_right)right+=sample;}/* Mode 3 - Wave patterns from WaveRAM */if(m_snd_3.on){/* NOTE: This is extremely close, but not quite right. The problem is for GB frequencies above 2000 the frequency gets clipped. This is caused because m_snd_3.pos is never 0 at the test.*/sample=m_snd_regs[AUD3W0+(m_snd_3.offset/2)];if(!(m_snd_3.offset%2)){sample>>=4;}sample=(sample&0xF)-8;if(m_snd_3.level)sample>>=(m_snd_3.level-1);elsesample=0;m_snd_3.pos++;if(m_snd_3.pos>=((UINT32)(((m_snd_3.period)>>21))+m_snd_3.duty)){m_snd_3.pos=0;if(m_snd_3.dutycount==((UINT32)(((m_snd_3.period)>>FIXED_POINT))%32)){m_snd_3.duty--;}m_snd_3.dutycount++;m_snd_3.offset++;if(m_snd_3.offset>31){m_snd_3.offset=0;m_snd_3.duty=1;m_snd_3.dutycount=0;}}if(m_snd_3.length&&m_snd_3.mode){m_snd_3.count++;if(m_snd_3.count>=m_snd_3.length){m_snd_3.on=0;m_snd_regs[NR52]&=0xFB;}}if(m_snd_control.mode3_left)left+=sample;if(m_snd_control.mode3_right)right+=sample;}/* Mode 4 - Noise with Envelope */if(m_snd_4.on){/* Similar problem to Mode 3, we seem to miss some notes */sample=m_snd_4.signal&m_snd_4.env_value;m_snd_4.pos++;if(m_snd_4.pos==(m_snd_4.period>>(FIXED_POINT+1))){/* Using a Polynomial Counter (aka Linear Feedback Shift Register) Mode 4 has a 7 bit and 15 bit counter so we need to shift the bits around accordingly */mode4_mask=(((m_snd_4.ply_value&0x2)>>1)^(m_snd_4.ply_value&0x1))<<(m_snd_4.ply_step?6:14);m_snd_4.ply_value>>=1;m_snd_4.ply_value|=mode4_mask;m_snd_4.ply_value&=(m_snd_4.ply_step?0x7f:0x7fff);m_snd_4.signal=(INT8)m_snd_4.ply_value;}elseif(m_snd_4.pos>(m_snd_4.period>>FIXED_POINT)){m_snd_4.pos=0;mode4_mask=(((m_snd_4.ply_value&0x2)>>1)^(m_snd_4.ply_value&0x1))<<(m_snd_4.ply_step?6:14);m_snd_4.ply_value>>=1;m_snd_4.ply_value|=mode4_mask;m_snd_4.ply_value&=(m_snd_4.ply_step?0x7f:0x7fff);m_snd_4.signal=(INT8)m_snd_4.ply_value;}if(m_snd_4.length&&m_snd_4.mode){m_snd_4.count++;if(m_snd_4.count>=m_snd_4.length){m_snd_4.on=0;m_snd_regs[NR52]&=0xF7;}}if(m_snd_4.env_length){m_snd_4.env_count++;if(m_snd_4.env_count>=m_snd_4.env_length){m_snd_4.env_count=0;m_snd_4.env_value+=m_snd_4.env_direction;if(m_snd_4.env_value<0)m_snd_4.env_value=0;if(m_snd_4.env_value>15)m_snd_4.env_value=15;}}if(m_snd_control.mode4_left)left+=sample;if(m_snd_control.mode4_right)right+=sample;}/* Adjust for master volume */left*=m_snd_control.vol_left;right*=m_snd_control.vol_right;/* pump up the volume */left<<=6;right<<=6;/* Update the buffers */*(outputs[0]++)=left;*(outputs[1]++)=right;}m_snd_regs[NR52]=(m_snd_regs[NR52]&0xf0)|m_snd_1.on|(m_snd_2.on<<1)|(m_snd_3.on<<2)|(m_snd_4.on<<3);}