text: // Nintendo Game Boy sound hardware emulator with save state support
  
  // Gb_Snd_Emu 0.2.0
  #pragma once
  
  #include "Gb_Oscs.h"
  
  class Gb_Apu
  {
  public:
  	// Basics
  
  	// Clock rate that sound hardware runs at.
  	enum { clock_rate = 4194304 * GB_APU_OVERCLOCK };
  
  	// Sets buffer(s) to generate sound into. If left and right are NULL, output is mono.
  	// If all are NULL, no output is generated but other emulation still runs.
  	// If chan is specified, only that channel's output is changed, otherwise all are.
  	enum { osc_count = 4 }; // 0: Square 1, 1: Square 2, 2: Wave, 3: Noise
  	void set_output(Blip_Buffer *center, Blip_Buffer *left = nullptr, Blip_Buffer *right = nullptr, int chan = osc_count);
  
  	// Resets hardware to initial power on state BEFORE boot ROM runs. Mode selects
  	// sound hardware. Additional AGB wave features are enabled separately.
  	enum mode_t
  	{
  		mode_dmg, // Game Boy monochrome
  		mode_cgb, // Game Boy Color
  		mode_agb // Game Boy Advance
  	};
  	void reset(mode_t mode = mode_cgb, bool agb_wave = false);
  
  	// Reads and writes must be within the start_addr to end_addr range, inclusive.
  	// Addresses outside this range are not mapped to the sound hardware.
  	enum { start_addr = 0xFF10 };
  	enum { end_addr = 0xFF3F };
  	enum { register_count = end_addr - start_addr + 1 };
  
  	// Times are specified as the number of clocks since the beginning of the
  	// current time frame.
  
  	// Emulates CPU write of data to addr at specified time.
  	void write_register(blip_time_t time, unsigned addr, int data);
  
  	// Emulates CPU read from addr at specified time.
  	int read_register(blip_time_t time, unsigned addr);
  
  	// Emulates sound hardware up to specified time, ends current time frame, then
  	// starts a new frame at time 0.
  	void end_frame(blip_time_t frame_length);
  
  	// Sound adjustments
  
  	// Sets overall volume, where 1.0 is normal.
  	void volume(double);
  
  	// If true, reduces clicking by disabling DAC biasing. Note that this reduces
  	// emulation accuracy, since the clicks are authentic.
  	void reduce_clicks(bool reduce = true);
  
  	// Sets treble equalization.
  	void treble_eq(const blip_eq_t &);
  
  	// Treble and bass values for various hardware.
  	enum
  	{
  		speaker_treble = -47, // speaker on system
  		speaker_bass = 2000,
  		dmg_treble = 0, // headphones on each system
  		dmg_bass = 30,
  		cgb_treble = 0,
  		cgb_bass = 300, // CGB has much less bass
  		agb_treble = 0,
  		agb_bass = 30
  	};
  
  	// Sets frame sequencer rate, where 1.0 is normal. Meant for adjusting the
  	// tempo in a game music player.
  	void set_tempo(double);
  
  	Gb_Apu();
  
  private:
  	// noncopyable
  	Gb_Apu(const Gb_Apu &);
  	Gb_Apu &operator=(const Gb_Apu &);
  
  	Gb_Osc *oscs[osc_count];
  	blip_time_t last_time; // time sound emulator has been run to
  	blip_time_t frame_period; // clocks between each frame sequencer step
  	double volume_;
  	bool reduce_clicks_;
  
  	Gb_Sweep_Square square1;
  	Gb_Square square2;
  	Gb_Wave wave;
  	Gb_Noise noise;
  	blip_time_t frame_time; // time of next frame sequencer action
  	int frame_phase; // phase of next frame sequencer step
  	enum { regs_size = register_count + 0x10 };
  	uint8_t regs[regs_size];// last values written to registers
  
  	// large objects after everything else
  	Gb_Osc::Good_Synth good_synth;
  	Gb_Osc::Med_Synth med_synth[2];
  
  	void reset_lengths();
  	void reset_regs();
  	int calc_output(int osc) const;
  	void apply_stereo();
  	void apply_volume();
  	void synth_volume(int);
  	void run_until_(blip_time_t);
  	void run_until(blip_time_t);
  	void silence_osc(Gb_Osc &);
  	void write_osc(int index, int reg, int old_data, int data);
  
  public:
  	int read_status();
  };
  