2016-03-30 23:07:55 +03:00
|
|
|
#ifndef apu_h
|
|
|
|
#define apu_h
|
|
|
|
#include <stdbool.h>
|
|
|
|
#include <stdint.h>
|
2018-05-27 19:30:23 +03:00
|
|
|
#include <stddef.h>
|
2022-05-20 18:36:54 +03:00
|
|
|
#include <stdio.h>
|
2021-11-07 13:39:18 +02:00
|
|
|
#include "defs.h"
|
2017-07-21 18:24:28 +03:00
|
|
|
|
|
|
|
#ifdef GB_INTERNAL
|
2018-02-16 18:01:50 +02:00
|
|
|
/* Speed = 1 / Length (in seconds) */
|
2019-05-25 19:12:09 +03:00
|
|
|
#define DAC_DECAY_SPEED 20000
|
|
|
|
#define DAC_ATTACK_SPEED 20000
|
2018-10-19 23:53:01 +03:00
|
|
|
|
2018-02-16 18:01:50 +02:00
|
|
|
|
2018-01-06 11:58:07 +02:00
|
|
|
/* Divides nicely and never overflows with 4 channels and 8 (1-8) volume levels */
|
2018-02-07 15:27:28 -05:00
|
|
|
#ifdef WIIU
|
2018-02-10 15:02:22 +02:00
|
|
|
/* Todo: Remove this hack once https://github.com/libretro/RetroArch/issues/6252 is fixed*/
|
2018-02-16 01:26:37 +02:00
|
|
|
#define MAX_CH_AMP (0xFF0 / 2)
|
2018-02-07 15:27:28 -05:00
|
|
|
#else
|
2018-02-16 01:26:37 +02:00
|
|
|
#define MAX_CH_AMP 0xFF0
|
2018-02-07 15:27:28 -05:00
|
|
|
#endif
|
2018-01-06 11:58:07 +02:00
|
|
|
#define CH_STEP (MAX_CH_AMP/0xF/8)
|
2017-07-21 18:24:28 +03:00
|
|
|
#endif
|
2016-03-30 23:07:55 +03:00
|
|
|
|
2018-02-16 18:01:50 +02:00
|
|
|
|
|
|
|
|
2017-08-02 21:14:23 +03:00
|
|
|
/* APU ticks are 2MHz, triggered by an internal APU clock. */
|
2016-03-30 23:07:55 +03:00
|
|
|
|
2016-06-10 15:28:50 +03:00
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
int16_t left;
|
|
|
|
int16_t right;
|
|
|
|
} GB_sample_t;
|
|
|
|
|
2017-08-15 21:14:55 +03:00
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
double left;
|
|
|
|
double right;
|
|
|
|
} GB_double_sample_t;
|
|
|
|
|
2017-07-21 18:24:28 +03:00
|
|
|
enum GB_CHANNELS {
|
|
|
|
GB_SQUARE_1,
|
|
|
|
GB_SQUARE_2,
|
|
|
|
GB_WAVE,
|
|
|
|
GB_NOISE,
|
|
|
|
GB_N_CHANNELS
|
|
|
|
};
|
2016-03-30 23:07:55 +03:00
|
|
|
|
2021-02-25 15:43:38 +02:00
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
bool locked:1;
|
|
|
|
bool clock:1; // Represents FOSY on channel 4
|
|
|
|
unsigned padding:6;
|
|
|
|
} GB_envelope_clock_t;
|
|
|
|
|
2019-06-15 23:22:27 +03:00
|
|
|
typedef void (*GB_sample_callback_t)(GB_gameboy_t *gb, GB_sample_t *sample);
|
|
|
|
|
2016-03-30 23:07:55 +03:00
|
|
|
typedef struct
|
|
|
|
{
|
2016-09-13 01:21:47 +03:00
|
|
|
bool global_enable;
|
2021-12-29 00:43:10 +02:00
|
|
|
uint16_t apu_cycles;
|
2019-05-15 12:39:08 +02:00
|
|
|
|
2017-07-21 18:24:28 +03:00
|
|
|
uint8_t samples[GB_N_CHANNELS];
|
|
|
|
bool is_active[GB_N_CHANNELS];
|
2019-05-15 12:39:08 +02:00
|
|
|
|
2017-08-02 21:14:23 +03:00
|
|
|
uint8_t div_divider; // The DIV register ticks the APU at 512Hz, but is then divided
|
|
|
|
// once more to generate 128Hz and 64Hz clocks
|
2019-05-15 12:39:08 +02:00
|
|
|
|
2017-08-11 17:57:08 +03:00
|
|
|
uint8_t lf_div; // The APU runs in 2MHz, but channels 1, 2 and 4 run in 1MHZ so we divide
|
|
|
|
// need to divide the signal.
|
2019-05-15 12:39:08 +02:00
|
|
|
|
2017-07-27 23:11:33 +03:00
|
|
|
uint8_t square_sweep_countdown; // In 128Hz
|
2017-08-15 22:05:20 +03:00
|
|
|
uint8_t square_sweep_calculate_countdown; // In 2 MHz
|
2020-11-28 19:31:25 +02:00
|
|
|
uint16_t sweep_length_addend;
|
2020-02-27 18:11:10 +01:00
|
|
|
uint16_t shadow_sweep_sample_length;
|
2021-01-02 14:56:45 +02:00
|
|
|
bool unshifted_sweep;
|
2021-01-08 16:43:00 +02:00
|
|
|
bool enable_zombie_calculate_stepping;
|
2021-11-07 01:10:58 +02:00
|
|
|
|
|
|
|
uint8_t channel_1_restart_hold;
|
|
|
|
uint16_t channel1_completed_addend;
|
2017-07-27 23:11:33 +03:00
|
|
|
struct {
|
2017-08-02 21:14:23 +03:00
|
|
|
uint16_t pulse_length; // Reloaded from NRX1 (xorred), in 256Hz DIV ticks
|
2017-07-27 23:11:33 +03:00
|
|
|
uint8_t current_volume; // Reloaded from NRX2
|
|
|
|
uint8_t volume_countdown; // Reloaded from NRX2
|
2021-11-07 01:10:58 +02:00
|
|
|
uint8_t current_sample_index;
|
|
|
|
bool sample_surpressed;
|
2019-05-15 12:39:08 +02:00
|
|
|
|
2017-08-10 19:42:23 +03:00
|
|
|
uint16_t sample_countdown; // in APU ticks (Reloaded from sample_length, xorred $7FF)
|
2017-08-02 21:14:23 +03:00
|
|
|
uint16_t sample_length; // From NRX3, NRX4, in APU ticks
|
2017-07-27 23:11:33 +03:00
|
|
|
bool length_enabled; // NRX4
|
2021-11-07 01:10:58 +02:00
|
|
|
GB_envelope_clock_t envelope_clock;
|
2022-03-12 00:49:43 +02:00
|
|
|
uint8_t delay; // Hack for CGB D/E phantom step due to how sample_countdown is implemented in SameBoy
|
|
|
|
bool did_tick;
|
2017-07-27 23:11:33 +03:00
|
|
|
} square_channels[2];
|
2019-05-15 12:39:08 +02:00
|
|
|
|
2017-07-21 18:24:28 +03:00
|
|
|
struct {
|
|
|
|
bool enable; // NR30
|
2017-08-02 21:14:23 +03:00
|
|
|
uint16_t pulse_length; // Reloaded from NR31 (xorred), in 256Hz DIV ticks
|
2017-07-21 18:24:28 +03:00
|
|
|
uint8_t shift; // NR32
|
|
|
|
uint16_t sample_length; // NR33, NR34, in APU ticks
|
|
|
|
bool length_enabled; // NR34
|
2019-05-15 12:39:08 +02:00
|
|
|
|
2017-08-10 19:42:23 +03:00
|
|
|
uint16_t sample_countdown; // in APU ticks (Reloaded from sample_length, xorred $7FF)
|
2017-07-21 18:24:28 +03:00
|
|
|
uint8_t current_sample_index;
|
2021-10-17 02:06:33 +03:00
|
|
|
uint8_t current_sample_byte; // Current sample byte.
|
2017-07-21 18:24:28 +03:00
|
|
|
bool wave_form_just_read;
|
2021-11-07 01:10:58 +02:00
|
|
|
bool pulsed;
|
2021-12-19 19:27:01 +02:00
|
|
|
uint8_t bugged_read_countdown;
|
2017-07-21 18:24:28 +03:00
|
|
|
} wave_channel;
|
2019-05-15 12:39:08 +02:00
|
|
|
|
2017-08-11 17:57:08 +03:00
|
|
|
struct {
|
|
|
|
uint16_t pulse_length; // Reloaded from NR41 (xorred), in 256Hz DIV ticks
|
|
|
|
uint8_t current_volume; // Reloaded from NR42
|
|
|
|
uint8_t volume_countdown; // Reloaded from NR42
|
|
|
|
uint16_t lfsr;
|
|
|
|
bool narrow;
|
2019-05-15 12:39:08 +02:00
|
|
|
|
2020-12-12 16:02:25 +02:00
|
|
|
uint8_t counter_countdown; // Counts from 0-7 to 0 to tick counter (Scaled from 512KHz to 2MHz)
|
|
|
|
uint16_t counter; // A bit from this 14-bit register ticks LFSR
|
2017-08-11 17:57:08 +03:00
|
|
|
bool length_enabled; // NR44
|
2019-05-15 12:39:08 +02:00
|
|
|
|
2017-08-12 19:50:39 +03:00
|
|
|
uint8_t alignment; // If (NR43 & 7) != 0, samples are aligned to 512KHz clock instead of
|
|
|
|
// 1MHz. This variable keeps track of the alignment.
|
2021-11-07 01:10:58 +02:00
|
|
|
bool current_lfsr_sample;
|
|
|
|
int8_t delta;
|
|
|
|
bool countdown_reloaded;
|
|
|
|
uint8_t dmg_delayed_start;
|
|
|
|
GB_envelope_clock_t envelope_clock;
|
2017-08-11 17:57:08 +03:00
|
|
|
} noise_channel;
|
2019-05-15 12:39:08 +02:00
|
|
|
|
2021-11-07 01:10:58 +02:00
|
|
|
enum {
|
|
|
|
GB_SKIP_DIV_EVENT_INACTIVE,
|
|
|
|
GB_SKIP_DIV_EVENT_SKIPPED,
|
|
|
|
GB_SKIP_DIV_EVENT_SKIP,
|
|
|
|
} skip_div_event:8;
|
2020-05-10 00:37:52 +03:00
|
|
|
uint8_t pcm_mask[2]; // For CGB-0 to CGB-C PCM read glitch
|
2016-03-30 23:07:55 +03:00
|
|
|
} GB_apu_t;
|
|
|
|
|
2017-08-15 21:14:55 +03:00
|
|
|
typedef enum {
|
|
|
|
GB_HIGHPASS_OFF, // Do not apply any filter, keep DC offset
|
|
|
|
GB_HIGHPASS_ACCURATE, // Apply a highpass filter similar to the one used on hardware
|
|
|
|
GB_HIGHPASS_REMOVE_DC_OFFSET, // Remove DC Offset without affecting the waveform
|
2017-12-23 21:11:44 +02:00
|
|
|
GB_HIGHPASS_MAX
|
2017-08-15 21:14:55 +03:00
|
|
|
} GB_highpass_mode_t;
|
|
|
|
|
2022-05-20 18:36:54 +03:00
|
|
|
typedef enum {
|
|
|
|
GB_AUDIO_FORMAT_RAW, // Native endian
|
|
|
|
GB_AUDIO_FORMAT_AIFF, // Native endian
|
|
|
|
GB_AUDIO_FORMAT_WAV,
|
|
|
|
} GB_audio_format_t;
|
|
|
|
|
2017-07-21 23:06:02 +03:00
|
|
|
typedef struct {
|
|
|
|
unsigned sample_rate;
|
2019-05-15 12:39:08 +02:00
|
|
|
|
2021-12-26 15:20:46 +02:00
|
|
|
unsigned sample_cycles; // Counts by sample_rate until it reaches the clock frequency
|
2019-05-15 12:39:08 +02:00
|
|
|
|
2017-07-21 23:06:02 +03:00
|
|
|
// Samples are NOT normalized to MAX_CH_AMP * 4 at this stage!
|
|
|
|
unsigned cycles_since_render;
|
|
|
|
unsigned last_update[GB_N_CHANNELS];
|
|
|
|
GB_sample_t current_sample[GB_N_CHANNELS];
|
|
|
|
GB_sample_t summed_samples[GB_N_CHANNELS];
|
2018-02-16 18:01:50 +02:00
|
|
|
double dac_discharge[GB_N_CHANNELS];
|
2019-05-15 12:39:08 +02:00
|
|
|
|
2017-08-15 21:14:55 +03:00
|
|
|
GB_highpass_mode_t highpass_mode;
|
|
|
|
double highpass_rate;
|
|
|
|
GB_double_sample_t highpass_diff;
|
2019-06-15 23:22:27 +03:00
|
|
|
|
|
|
|
GB_sample_callback_t sample_callback;
|
2019-10-08 15:10:24 +03:00
|
|
|
|
2020-12-31 00:06:36 +02:00
|
|
|
double interference_volume;
|
|
|
|
double interference_highpass;
|
2022-05-20 18:36:54 +03:00
|
|
|
|
|
|
|
FILE *output_file;
|
|
|
|
GB_audio_format_t output_format;
|
|
|
|
int output_error;
|
2017-07-21 23:06:02 +03:00
|
|
|
} GB_apu_output_t;
|
|
|
|
|
2019-06-15 23:22:27 +03:00
|
|
|
void GB_set_sample_rate(GB_gameboy_t *gb, unsigned sample_rate);
|
2022-05-20 18:36:54 +03:00
|
|
|
unsigned GB_get_sample_rate(GB_gameboy_t *gb);
|
2019-11-03 22:02:33 +02:00
|
|
|
void GB_set_sample_rate_by_clocks(GB_gameboy_t *gb, double cycles_per_sample); /* Cycles are in 8MHz units */
|
2017-08-15 21:14:55 +03:00
|
|
|
void GB_set_highpass_filter_mode(GB_gameboy_t *gb, GB_highpass_mode_t mode);
|
2020-12-31 00:06:36 +02:00
|
|
|
void GB_set_interference_volume(GB_gameboy_t *gb, double volume);
|
2019-06-15 23:22:27 +03:00
|
|
|
void GB_apu_set_sample_callback(GB_gameboy_t *gb, GB_sample_callback_t callback);
|
2022-05-20 18:36:54 +03:00
|
|
|
int GB_start_audio_recording(GB_gameboy_t *gb, const char *path, GB_audio_format_t format);
|
|
|
|
int GB_stop_audio_recording(GB_gameboy_t *gb);
|
2017-04-17 20:16:17 +03:00
|
|
|
#ifdef GB_INTERNAL
|
2021-11-07 14:13:52 +02:00
|
|
|
internal bool GB_apu_is_DAC_enabled(GB_gameboy_t *gb, unsigned index);
|
|
|
|
internal void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value);
|
|
|
|
internal uint8_t GB_apu_read(GB_gameboy_t *gb, uint8_t reg);
|
|
|
|
internal void GB_apu_div_event(GB_gameboy_t *gb);
|
|
|
|
internal void GB_apu_div_secondary_event(GB_gameboy_t *gb);
|
|
|
|
internal void GB_apu_init(GB_gameboy_t *gb);
|
2021-12-29 00:43:10 +02:00
|
|
|
internal void GB_apu_run(GB_gameboy_t *gb, bool force);
|
2017-04-17 20:16:17 +03:00
|
|
|
#endif
|
2016-03-30 23:07:55 +03:00
|
|
|
|
|
|
|
#endif /* apu_h */
|