Source

gb_emulator / gb_emulator / include / gb_emulator / gb_sound.hpp

/*  Copyright © 2011 Chris Spencer <spencercw@gmail.com>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */

#ifndef GB_SOUND_HPP_A0F40D80_8AD9_11E0_B88E_0002A5D5C51B
#define GB_SOUND_HPP_A0F40D80_8AD9_11E0_B88E_0002A5D5C51B

#include <stdint.h>

#include <boost/filesystem/path.hpp>
#include <boost/function.hpp>
#include <boost/shared_ptr.hpp>

class Gb;
class GbMemory;
class GbSoundData;
class SndfileHandle;

//! GameBoy sound output emulator.
class GbSound
{
	friend class GbMemory;

public:
	//! Constructor; sets the associated emulator container.
	GbSound(Gb &gb);

	//! Destructor.
	virtual ~GbSound();

	//! Resets the sound emulator to its default state.
	void reset();

	//! Generates a single sample.
	/**
	 * The base class implementation does nothing except return the number of CPU cycles that should
	 * be executed before poll() is called again.
	 */
	virtual double poll();

	//! Checks for and handles any events then returns.
	/**
	 * The base class implementation does nothing.
	 */
	virtual void pollEvents();

	//! Sets the callback function to be called at the given frequency based on the audio clock.
	/**
	 * There is no guarantee that the callback will be called exactly at the given frequency or at
	 * consistent intervals. It will however average to the requested frequency and will be suitable
	 * for generating sound data at exactly the same rate as the audio system requires it.
	 *
	 * The callback may be called in any thread and it should not block.
	 */
	virtual void setTimerCallback(double frequency, boost::function<void ()> callback) = 0;

	//! Handles a write to a sound register.
	void writeIoPort(uint8_t ptr, uint8_t val);

	//! Resets all the sound registers to their initial states.
	void resetRegisters(GbMemory &mem) const;

	//! Starts recording to the given file.
	/**
	 * This does nothing if already recording.
	 */
	void record(const boost::filesystem::path &path);

	//! Stops recording.
	/**
	 * This does nothing if recording is not active.
	 */
	void stopRecording();

	//! Saves the current state of the sound emulator into the given message.
	void save(GbSoundData &data) const;

	//! Loads the sound emulator state from the given message.
	void load(const GbSoundData &data);

protected:
	Gb &gb_;

	// Values of registers that are persisted across power cycles
	uint8_t savedRegisters[5];

	// File into which the generated sound is saved, if enabled
	boost::shared_ptr<SndfileHandle> soundFile_;

	// Sample rate of the audio device
	unsigned sampleRate_;
	// The number of CPU cycles per audio sample
	double sampleCycles_;
	// Current frequency in GameBoy format
	uint16_t gbFrequency1_, gbFrequency2_, gbFrequency3_;
	// Current frequency in Hz
	double actFrequency1_, actFrequency2_, actFrequency3_, actFrequency4_;
	// Whether the square wave is currently in the high or low part
	bool hi1_, hi2_;
	// Remaining playback duration
	double duration1_, duration2_, duration3_, duration4_;
	// Wave pattern duty
	double duty1_, duty2_;
	// Number of samples before toggling the high or low part of the square wave
	double countdown1_, countdown2_, countdown3_, countdown4_;
	// Whether the sweep shift is enabled
	bool sweepEnabled1_;
	// The next frequency that will be played in the sweep
	uint16_t sweepNextFrequency1_;
	// Amount the frequency is shifted by at each sweep shift
	uint8_t sweepShifts1_;
	// Type of shift. 0 = addition, 1 = subtraction
	bool sweepType1_;
	// Number of samples between sweep shifts
	double sweepStep1_;
	// Number of samples to next sweep shift
	double sweepCountdown1_;
	// Current envelope state
	uint8_t envelope1_, envelope2_, envelope4_;
	// Direction of the envelope sweep. 0 = decrease, 1 = increase
	bool envelopeDirection1_, envelopeDirection2_, envelopeDirection4_;
	// Number of samples between envelope steps
	double envelopeStep1_, envelopeStep2_, envelopeStep4_;
	// Number of samples to the next envelope step
	double envelopeCountdown1_, envelopeCountdown2_, envelopeCountdown4_;
	// Current half-byte index into wavePattern3_
	uint8_t waveIndex3_;
	// Wave pattern for sound mode 3
	uint8_t wavePattern3_[0x10];
	// Output level for sound mode 3 (i.e., number of bits to shift right by)
	uint8_t outLevel3_;
	// The counter stage selected for sound channel 4. This points to either lfsr7 or lfsr15 in
	// gb_sound_tables.h.
	const uint32_t *counterData4_;
	// Size of the counterData4_ array
	unsigned counterDataSize4_;
	// Current index into the LFSR counter array
	unsigned counterIndex4_;

	// Adjusts the given envelope parameters
	void adjustEnvelope(uint8_t &envelope, bool envelopeDirection, double envelopeStep,
		double &envelopeCountdown);

	// Functions for each of the sound modes. The output is mixed in the main poll() function
	double sound1();
	double sound2();
	double sound3();
	double sound4();

	// Generates a single sample
	void generateSample(double &left, double &right);

	// Performs the channel 1 sweep
	bool doSweep();

	// Adds the given channel amplitude to the appropriate output channels based on the value of the
	// control bits
	void doChannel(double amplitude, uint8_t bit, double &left, double &right);

	// Disabled operations
	GbSound(const GbSound &);
	GbSound & operator=(const GbSound &);
};

#endif
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.