204 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
		
		
			
		
	
	
			204 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| 
								 | 
							
								// Part of SAASound copyright 1998-2018 Dave Hooper <dave@beermex.com>
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// SAAAmp.cpp: implementation of the CSAAAmp class.
							 | 
						||
| 
								 | 
							
								// This class handles Tone/Noise mixing, Envelope application and
							 | 
						||
| 
								 | 
							
								// amplification.
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								//////////////////////////////////////////////////////////////////////
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#include "SAASound.h"
							 | 
						||
| 
								 | 
							
								#include "types.h"
							 | 
						||
| 
								 | 
							
								#include "SAANoise.h"
							 | 
						||
| 
								 | 
							
								#include "SAAEnv.h"
							 | 
						||
| 
								 | 
							
								#include "SAAFreq.h"
							 | 
						||
| 
								 | 
							
								#include "SAAAmp.h"
							 | 
						||
| 
								 | 
							
								#include "defns.h"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								//////////////////////////////////////////////////////////////////////
							 | 
						||
| 
								 | 
							
								// Construction/Destruction
							 | 
						||
| 
								 | 
							
								//////////////////////////////////////////////////////////////////////
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								CSAAAmp::CSAAAmp(CSAAFreq * const ToneGenerator, const CSAANoise * const NoiseGenerator, const CSAAEnv * const EnvGenerator)
							 | 
						||
| 
								 | 
							
								:
							 | 
						||
| 
								 | 
							
								m_pcConnectedToneGenerator(ToneGenerator),
							 | 
						||
| 
								 | 
							
								m_pcConnectedNoiseGenerator(NoiseGenerator),
							 | 
						||
| 
								 | 
							
								m_pcConnectedEnvGenerator(EnvGenerator),
							 | 
						||
| 
								 | 
							
								m_bUseEnvelope(EnvGenerator != NULL)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									leftlevel = 0;
							 | 
						||
| 
								 | 
							
									leftlevela0x0e = 0;
							 | 
						||
| 
								 | 
							
									rightlevel = 0;
							 | 
						||
| 
								 | 
							
									rightlevela0x0e = 0;
							 | 
						||
| 
								 | 
							
									m_nMixMode = 0;
							 | 
						||
| 
								 | 
							
									m_bMute=true;
							 | 
						||
| 
								 | 
							
									m_bSync = false;
							 | 
						||
| 
								 | 
							
									m_nOutputIntermediate=0;
							 | 
						||
| 
								 | 
							
									last_level_byte=0;
							 | 
						||
| 
								 | 
							
									SetAmpLevel(0x00);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								CSAAAmp::~CSAAAmp()
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									// Nothing to do
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void CSAAAmp::SetAmpLevel(BYTE level_byte)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									// if level unchanged since last call then do nothing
							 | 
						||
| 
								 | 
							
									if (level_byte != last_level_byte)
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										last_level_byte = level_byte;
							 | 
						||
| 
								 | 
							
										leftlevel = level_byte & 0x0f;
							 | 
						||
| 
								 | 
							
										leftlevela0x0e = leftlevel & 0x0e;
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
										rightlevel = (level_byte >> 4) & 0x0f;
							 | 
						||
| 
								 | 
							
										rightlevela0x0e = rightlevel & 0x0e;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void CSAAAmp::SetToneMixer(BYTE bEnabled)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									if (bEnabled == 0)
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										// clear mixer bit
							 | 
						||
| 
								 | 
							
										m_nMixMode &= ~(0x01);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									else
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										// set mixer bit
							 | 
						||
| 
								 | 
							
										m_nMixMode |= 0x01;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void CSAAAmp::SetNoiseMixer(BYTE bEnabled)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									if (bEnabled == 0)
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										m_nMixMode &= ~(0x02);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									else
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										m_nMixMode |= 0x02;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void CSAAAmp::Mute(bool bMute)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									// m_bMute refers to the GLOBAL mute setting (register 28 bit 0)
							 | 
						||
| 
								 | 
							
									// NOT the per-channel mixer settings !!
							 | 
						||
| 
								 | 
							
									m_bMute = bMute;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void CSAAAmp::Sync(bool bSync)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									// m_bSync refers to the GLOBAL sync setting (register 28 bit 1)
							 | 
						||
| 
								 | 
							
									m_bSync = bSync;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void CSAAAmp::Tick(void)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									// updates m_nOutputIntermediate to 0, 1 or 2
							 | 
						||
| 
								 | 
							
									//
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// connected oscillator always ticks (this isn't really connected to the amp)
							 | 
						||
| 
								 | 
							
									int level = m_pcConnectedToneGenerator->Tick();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									switch (m_nMixMode)
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										case 0:
							 | 
						||
| 
								 | 
							
											// no tone or noise for this channel
							 | 
						||
| 
								 | 
							
											m_nOutputIntermediate = 0;
							 | 
						||
| 
								 | 
							
											break;
							 | 
						||
| 
								 | 
							
										case 1:
							 | 
						||
| 
								 | 
							
											// tone only for this channel
							 | 
						||
| 
								 | 
							
											m_nOutputIntermediate = level * 2;
							 | 
						||
| 
								 | 
							
											// NOTE: ConnectedToneGenerator returns either 0 or 1
							 | 
						||
| 
								 | 
							
											break;
							 | 
						||
| 
								 | 
							
										case 2:
							 | 
						||
| 
								 | 
							
											// noise only for this channel
							 | 
						||
| 
								 | 
							
											m_nOutputIntermediate = m_pcConnectedNoiseGenerator->Level() * 2;
							 | 
						||
| 
								 | 
							
											// NOTE: ConnectedNoiseGenerator()->Level() returns either 0 or 1
							 | 
						||
| 
								 | 
							
											break;
							 | 
						||
| 
								 | 
							
										case 3:
							 | 
						||
| 
								 | 
							
											// tone+noise for this channel ... mixing algorithm :
							 | 
						||
| 
								 | 
							
											//  tone   noise   output
							 | 
						||
| 
								 | 
							
											//   0       0       0
							 | 
						||
| 
								 | 
							
											//   1       0       2
							 | 
						||
| 
								 | 
							
											//   0       1       0
							 | 
						||
| 
								 | 
							
											//   1       1       1
							 | 
						||
| 
								 | 
							
											// = 2 * tone - 1 * (tone & noise)
							 | 
						||
| 
								 | 
							
											// = tone * (2 - noise)
							 | 
						||
| 
								 | 
							
											m_nOutputIntermediate = level * (2 - m_pcConnectedNoiseGenerator->Level());
							 | 
						||
| 
								 | 
							
											break;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									// intermediate is between 0 and 2
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								inline int CSAAAmp::EffectiveAmplitude(int amp, int env) const
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									// Return the effective amplitude of the low-pass-filtered result of the logical
							 | 
						||
| 
								 | 
							
									// AND of the amplitude PDM and envelope PDM patterns.  This is a more accurate
							 | 
						||
| 
								 | 
							
									// evaluation of the SAA than simply returning amp * env , based on how the SAA
							 | 
						||
| 
								 | 
							
									// implements pulse-density modulation.
							 | 
						||
| 
								 | 
							
									static const int pdm[16][16] = {
							 | 
						||
| 
								 | 
							
									{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
							 | 
						||
| 
								 | 
							
									{0,0,0,0,2,2,2,2,2,2,2,2,4,4,4,4},
							 | 
						||
| 
								 | 
							
									{0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8},
							 | 
						||
| 
								 | 
							
									{0,1,1,2,4,5,5,6,6,7,7,8,10,11,11,12},
							 | 
						||
| 
								 | 
							
									{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15},
							 | 
						||
| 
								 | 
							
									{0,1,2,3,6,7,8,9,10,11,12,13,16,17,18,19},
							 | 
						||
| 
								 | 
							
									{0,2,3,5,6,8,9,11,12,14,15,17,18,20,21,23},
							 | 
						||
| 
								 | 
							
									{0,2,3,5,8,10,11,13,14,16,17,19,22,24,25,27},
							 | 
						||
| 
								 | 
							
									{0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30},
							 | 
						||
| 
								 | 
							
									{0,2,4,6,10,12,14,16,18,20,22,24,28,30,32,34},
							 | 
						||
| 
								 | 
							
									{0,3,5,8,10,13,15,18,20,23,25,28,30,33,35,38},
							 | 
						||
| 
								 | 
							
									{0,3,5,8,12,15,17,20,22,25,27,30,34,37,39,42},
							 | 
						||
| 
								 | 
							
									{0,3,6,9,12,15,18,21,24,27,30,33,36,39,42,45},
							 | 
						||
| 
								 | 
							
									{0,3,6,9,14,17,20,23,26,29,32,35,40,43,46,49},
							 | 
						||
| 
								 | 
							
									{0,4,7,11,14,18,21,25,28,32,35,39,42,46,49,53},
							 | 
						||
| 
								 | 
							
									{0,4,7,11,16,20,23,27,30,34,37,41,46,50,53,57}
							 | 
						||
| 
								 | 
							
									};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									return(pdm[amp][env] * 4);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void CSAAAmp::TickAndOutputStereo(unsigned int & left, unsigned int & right)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									// This returns a value between 0 and 480 inclusive.
							 | 
						||
| 
								 | 
							
									// This represents the full dynamic range of one output mixer (tone, or noise+tone, at full volume,
							 | 
						||
| 
								 | 
							
									// without envelopes enabled).  Note that, with envelopes enabled, the actual dynamic range
							 | 
						||
| 
								 | 
							
									// is reduced on-chip to just over 88% of this (424), so the "loudest" output requires disabling envs.
							 | 
						||
| 
								 | 
							
									// NB for 6 channels at full volume, with simple additive mixing, you would see a combined
							 | 
						||
| 
								 | 
							
									// output of 2880, and a multiplier of 11 (=31680) fits comfortably within 16-bit signed output range.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if (m_bSync)
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										// TODO check this
							 | 
						||
| 
								 | 
							
										left = right = 0;
							 | 
						||
| 
								 | 
							
										return;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// first, do the Tick:
							 | 
						||
| 
								 | 
							
									Tick();
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									// now calculate the returned amplitude for this sample:
							 | 
						||
| 
								 | 
							
									////////////////////////////////////////////////////////
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if (m_bMute)
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										left = right = 0;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									else if (m_bUseEnvelope && m_pcConnectedEnvGenerator->IsActive())
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										left = EffectiveAmplitude(m_pcConnectedEnvGenerator->LeftLevel(), leftlevela0x0e) * (2 - m_nOutputIntermediate);
							 | 
						||
| 
								 | 
							
										right = EffectiveAmplitude(m_pcConnectedEnvGenerator->RightLevel(), rightlevela0x0e) * (2 - m_nOutputIntermediate);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									else
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										left = leftlevel * m_nOutputIntermediate * 16;
							 | 
						||
| 
								 | 
							
										right = rightlevel * m_nOutputIntermediate * 16;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 |