Redesign and reimplement the audio API, let the frontends handle more stuff. Probably affects #161

This commit is contained in:
Lior Halphon 2019-06-15 23:22:27 +03:00
parent 083b4a2970
commit e268efefef
13 changed files with 146 additions and 149 deletions

View File

@ -55,6 +55,12 @@ enum model {
bool rewind;
bool modelsChanging;
NSCondition *audioLock;
GB_sample_t *audioBuffer;
size_t audioBufferSize;
size_t audioBufferPosition;
size_t audioBufferNeeded;
}
@property GBAudioClient *audioClient;
@ -67,6 +73,7 @@ enum model {
- (void) printImage:(uint32_t *)image height:(unsigned) height
topMargin:(unsigned) topMargin bottomMargin: (unsigned) bottomMargin
exposure:(unsigned) exposure;
- (void) gotNewSample:(GB_sample_t *)sample;
@end
static void vblank(GB_gameboy_t *gb)
@ -118,6 +125,12 @@ static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height,
[self printImage:image height:height topMargin:top_margin bottomMargin:bottom_margin exposure:exposure];
}
static void audioCallback(GB_gameboy_t *gb, GB_sample_t *sample)
{
Document *self = (__bridge Document *)GB_get_user_data(gb);
[self gotNewSample:sample];
}
@implementation Document
{
GB_gameboy_t gb;
@ -133,6 +146,7 @@ static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height,
has_debugger_input = [[NSConditionLock alloc] initWithCondition:0];
debugger_input_queue = [[NSMutableArray alloc] init];
console_output_lock = [[NSRecursiveLock alloc] init];
audioLock = [[NSCondition alloc] init];
}
return self;
}
@ -184,6 +198,7 @@ static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height,
GB_set_camera_update_request_callback(&gb, cameraRequestUpdate);
GB_set_highpass_filter_mode(&gb, (GB_highpass_mode_t) [[NSUserDefaults standardUserDefaults] integerForKey:@"GBHighpassFilter"]);
GB_set_rewind_length(&gb, [[NSUserDefaults standardUserDefaults] integerForKey:@"GBRewindLength"]);
GB_apu_set_sample_callback(&gb, audioCallback);
}
- (void) vblank
@ -201,13 +216,57 @@ static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height,
}
}
- (void)gotNewSample:(GB_sample_t *)sample
{
[audioLock lock];
if (self.audioClient.isPlaying) {
if (audioBufferPosition == audioBufferSize) {
if (audioBufferSize >= 0x4000) {
audioBufferPosition = 0;
[audioLock unlock];
return;
}
if (audioBufferSize == 0) {
audioBufferSize = 512;
}
else {
audioBufferSize += audioBufferSize >> 2;
}
audioBuffer = realloc(audioBuffer, sizeof(*sample) * audioBufferSize);
}
audioBuffer[audioBufferPosition++] = *sample;
}
if (audioBufferPosition == audioBufferNeeded) {
[audioLock signal];
audioBufferNeeded = 0;
}
[audioLock unlock];
}
- (void) run
{
running = true;
GB_set_pixels_output(&gb, self.view.pixels);
GB_set_sample_rate(&gb, 96000);
self.audioClient = [[GBAudioClient alloc] initWithRendererBlock:^(UInt32 sampleRate, UInt32 nFrames, GB_sample_t *buffer) {
GB_apu_copy_buffer(&gb, buffer, nFrames);
[audioLock lock];
if (audioBufferPosition < nFrames) {
audioBufferNeeded = nFrames;
[audioLock wait];
}
if (audioBufferPosition >= nFrames && audioBufferPosition < nFrames + 4800) {
memcpy(buffer, audioBuffer, nFrames * sizeof(*buffer));
memmove(audioBuffer, audioBuffer + nFrames, (audioBufferPosition - nFrames) * sizeof(*buffer));
audioBufferPosition = audioBufferPosition - nFrames;
}
else {
memcpy(buffer, audioBuffer + (audioBufferPosition - nFrames), nFrames * sizeof(*buffer));
audioBufferPosition = 0;
}
[audioLock unlock];
} andSampleRate:96000];
if (![[NSUserDefaults standardUserDefaults] boolForKey:@"Mute"]) {
[self.audioClient start];
@ -227,6 +286,11 @@ static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height,
}
}
[hex_timer invalidate];
[audioLock lock];
memset(audioBuffer, 0, (audioBufferSize - audioBufferPosition) * sizeof(*audioBuffer));
audioBufferPosition = audioBufferNeeded;
[audioLock signal];
[audioLock unlock];
[self.audioClient stop];
self.audioClient = nil;
self.view.mouseHidingEnabled = NO;
@ -329,6 +393,9 @@ static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height,
if (cameraImage) {
CVBufferRelease(cameraImage);
}
if (audioBuffer) {
free(audioBuffer);
}
}
- (void)windowControllerDidLoadNib:(NSWindowController *)aController {

View File

@ -1,6 +1,7 @@
#include <stdint.h>
#include <math.h>
#include <string.h>
#include <assert.h>
#include "gb.h"
#define likely(x) __builtin_expect((x), 1)
@ -117,7 +118,7 @@ static double smooth(double x)
return 3*x*x - 2*x*x*x;
}
static void render(GB_gameboy_t *gb, bool no_downsampling, GB_sample_t *dest)
static void render(GB_gameboy_t *gb)
{
GB_sample_t output = {0,0};
@ -147,7 +148,7 @@ static void render(GB_gameboy_t *gb, bool no_downsampling, GB_sample_t *dest)
}
}
if (likely(gb->apu_output.last_update[i] == 0 || no_downsampling)) {
if (likely(gb->apu_output.last_update[i] == 0)) {
output.left += gb->apu_output.current_sample[i].left * multiplier;
output.right += gb->apu_output.current_sample[i].right * multiplier;
}
@ -205,17 +206,9 @@ static void render(GB_gameboy_t *gb, bool no_downsampling, GB_sample_t *dest)
}
}
if (dest) {
*dest = filtered_output;
return;
}
while (gb->apu_output.copy_in_progress);
while (!__sync_bool_compare_and_swap(&gb->apu_output.lock, false, true));
if (gb->apu_output.buffer_position < gb->apu_output.buffer_size) {
gb->apu_output.buffer[gb->apu_output.buffer_position++] = filtered_output;
}
gb->apu_output.lock = false;
assert(gb->apu_output.sample_callback);
gb->apu_output.sample_callback(gb, &filtered_output);
}
static uint16_t new_sweep_sample_legnth(GB_gameboy_t *gb)
@ -504,56 +497,12 @@ void GB_apu_run(GB_gameboy_t *gb)
if (gb->apu_output.sample_rate) {
gb->apu_output.cycles_since_render += cycles;
if (gb->apu_output.sample_cycles > gb->apu_output.cycles_per_sample) {
if (gb->apu_output.sample_cycles >= gb->apu_output.cycles_per_sample) {
gb->apu_output.sample_cycles -= gb->apu_output.cycles_per_sample;
render(gb, false, NULL);
render(gb);
}
}
}
void GB_apu_copy_buffer(GB_gameboy_t *gb, GB_sample_t *dest, size_t count)
{
if (gb->sgb) {
if (GB_sgb_render_jingle(gb, dest, count)) return;
}
gb->apu_output.copy_in_progress = true;
/* TODO: Rewrite this as a proper cyclic buffer. This is a workaround to avoid a very rare crashing race condition */
size_t buffer_position = gb->apu_output.buffer_position;
if (!gb->apu_output.stream_started) {
// Intentionally fail the first copy to sync the stream with the Gameboy.
gb->apu_output.stream_started = true;
gb->apu_output.buffer_position = 0;
buffer_position = 0;
}
if (count > buffer_position) {
// GB_log(gb, "Audio underflow: %d\n", count - gb->apu_output.buffer_position);
GB_sample_t output;
render(gb, true, &output);
for (unsigned i = 0; i < count - buffer_position; i++) {
dest[buffer_position + i] = output;
}
if (buffer_position) {
if (gb->apu_output.buffer_size + (count - buffer_position) < count * 3) {
gb->apu_output.buffer_size += count - buffer_position;
gb->apu_output.buffer = realloc(gb->apu_output.buffer,
gb->apu_output.buffer_size * sizeof(*gb->apu_output.buffer));
gb->apu_output.stream_started = false;
}
}
count = buffer_position;
}
memcpy(dest, gb->apu_output.buffer, count * sizeof(*gb->apu_output.buffer));
memmove(gb->apu_output.buffer, gb->apu_output.buffer + count, (buffer_position - count) * sizeof(*gb->apu_output.buffer));
gb->apu_output.buffer_position -= count;
gb->apu_output.copy_in_progress = false;
}
void GB_apu_init(GB_gameboy_t *gb)
{
memset(&gb->apu, 0, sizeof(gb->apu));
@ -1009,26 +958,21 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
gb->io_registers[reg] = value;
}
size_t GB_apu_get_current_buffer_length(GB_gameboy_t *gb)
void GB_set_sample_rate(GB_gameboy_t *gb, unsigned sample_rate)
{
return gb->apu_output.buffer_position;
}
void GB_set_sample_rate(GB_gameboy_t *gb, unsigned int sample_rate)
{
if (gb->apu_output.buffer) {
free(gb->apu_output.buffer);
}
gb->apu_output.buffer_size = sample_rate / 25; // 40ms delay
gb->apu_output.buffer = malloc(gb->apu_output.buffer_size * sizeof(*gb->apu_output.buffer));
gb->apu_output.sample_rate = sample_rate;
gb->apu_output.buffer_position = 0;
if (sample_rate) {
gb->apu_output.highpass_rate = pow(0.999958, GB_get_clock_rate(gb) / (double)sample_rate);
}
GB_apu_update_cycles_per_sample(gb);
}
void GB_apu_set_sample_callback(GB_gameboy_t *gb, GB_sample_callback_t callback)
{
gb->apu_output.sample_callback = callback;
}
void GB_set_highpass_filter_mode(GB_gameboy_t *gb, GB_highpass_mode_t mode)
{
gb->apu_output.highpass_mode = mode;

View File

@ -46,6 +46,8 @@ enum GB_CHANNELS {
GB_N_CHANNELS
};
typedef void (*GB_sample_callback_t)(GB_gameboy_t *gb, GB_sample_t *sample);
typedef struct
{
bool global_enable;
@ -126,14 +128,6 @@ typedef enum {
typedef struct {
unsigned sample_rate;
GB_sample_t *buffer;
size_t buffer_size;
size_t buffer_position;
bool stream_started; /* detects first copy request to minimize lag */
volatile bool copy_in_progress;
volatile bool lock;
double sample_cycles; // In 8 MHz units
double cycles_per_sample;
@ -147,13 +141,13 @@ typedef struct {
GB_highpass_mode_t highpass_mode;
double highpass_rate;
GB_double_sample_t highpass_diff;
GB_sample_callback_t sample_callback;
} GB_apu_output_t;
void GB_set_sample_rate(GB_gameboy_t *gb, unsigned int sample_rate);
void GB_apu_copy_buffer(GB_gameboy_t *gb, GB_sample_t *dest, size_t count);
size_t GB_apu_get_current_buffer_length(GB_gameboy_t *gb);
void GB_set_sample_rate(GB_gameboy_t *gb, unsigned sample_rate);
void GB_set_highpass_filter_mode(GB_gameboy_t *gb, GB_highpass_mode_t mode);
void GB_apu_set_sample_callback(GB_gameboy_t *gb, GB_sample_callback_t callback);
#ifdef GB_INTERNAL
bool GB_apu_is_DAC_enabled(GB_gameboy_t *gb, unsigned index);
void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value);

View File

@ -455,12 +455,12 @@ static lvalue_t debugger_evaluate_lvalue(GB_gameboy_t *gb, const char *string,
case 'p': if (string[1] == 'c') return (lvalue_t){LVALUE_REG16, .register_address = &gb->pc};
}
}
GB_log(gb, "Unknown register: %.*s\n", (unsigned int) length, string);
GB_log(gb, "Unknown register: %.*s\n", (unsigned) length, string);
*error = true;
return (lvalue_t){0,};
}
GB_log(gb, "Expression is not an lvalue: %.*s\n", (unsigned int) length, string);
GB_log(gb, "Expression is not an lvalue: %.*s\n", (unsigned) length, string);
*error = true;
return (lvalue_t){0,};
}
@ -564,8 +564,8 @@ value_t debugger_evaluate(GB_gameboy_t *gb, const char *string,
}
// Search for lowest priority operator
signed int depth = 0;
unsigned int operator_index = -1;
unsigned int operator_pos = 0;
unsigned operator_index = -1;
unsigned operator_pos = 0;
for (int i = 0; i < length; i++) {
if (string[i] == '(') depth++;
else if (string[i] == ')') depth--;
@ -593,7 +593,7 @@ value_t debugger_evaluate(GB_gameboy_t *gb, const char *string,
}
}
if (operator_index != -1) {
unsigned int right_start = (unsigned int)(operator_pos + strlen(operators[operator_index].string));
unsigned right_start = (unsigned)(operator_pos + strlen(operators[operator_index].string));
value_t right = debugger_evaluate(gb, string + right_start, length - right_start, error, watchpoint_address, watchpoint_new_value);
if (*error) goto exit;
if (operators[operator_index].lvalue_operator) {
@ -661,7 +661,7 @@ value_t debugger_evaluate(GB_gameboy_t *gb, const char *string,
goto exit;
}
GB_log(gb, "Unknown register or symbol: %.*s\n", (unsigned int) length, string);
GB_log(gb, "Unknown register or symbol: %.*s\n", (unsigned) length, string);
*error = true;
goto exit;
}
@ -675,7 +675,7 @@ value_t debugger_evaluate(GB_gameboy_t *gb, const char *string,
}
uint16_t literal = (uint16_t) (strtol(string, &end, base));
if (end != string + length) {
GB_log(gb, "Failed to parse: %.*s\n", (unsigned int) length, string);
GB_log(gb, "Failed to parse: %.*s\n", (unsigned) length, string);
*error = true;
goto exit;
}
@ -880,13 +880,13 @@ static bool breakpoint(GB_gameboy_t *gb, char *arguments, char *modifiers, const
condition += strlen(" if ");
/* Verify condition is sane (Todo: This might have side effects!) */
bool error;
debugger_evaluate(gb, condition, (unsigned int)strlen(condition), &error, NULL, NULL);
debugger_evaluate(gb, condition, (unsigned)strlen(condition), &error, NULL, NULL);
if (error) return true;
}
bool error;
value_t result = debugger_evaluate(gb, arguments, (unsigned int)strlen(arguments), &error, NULL, NULL);
value_t result = debugger_evaluate(gb, arguments, (unsigned)strlen(arguments), &error, NULL, NULL);
uint32_t key = BP_KEY(result);
if (error) return true;
@ -949,7 +949,7 @@ static bool delete(GB_gameboy_t *gb, char *arguments, char *modifiers, const deb
}
bool error;
value_t result = debugger_evaluate(gb, arguments, (unsigned int)strlen(arguments), &error, NULL, NULL);
value_t result = debugger_evaluate(gb, arguments, (unsigned)strlen(arguments), &error, NULL, NULL);
uint32_t key = BP_KEY(result);
if (error) return true;
@ -1065,13 +1065,13 @@ print_usage:
/* To make $new and $old legal */
uint16_t dummy = 0;
uint8_t dummy2 = 0;
debugger_evaluate(gb, condition, (unsigned int)strlen(condition), &error, &dummy, &dummy2);
debugger_evaluate(gb, condition, (unsigned)strlen(condition), &error, &dummy, &dummy2);
if (error) return true;
}
bool error;
value_t result = debugger_evaluate(gb, arguments, (unsigned int)strlen(arguments), &error, NULL, NULL);
value_t result = debugger_evaluate(gb, arguments, (unsigned)strlen(arguments), &error, NULL, NULL);
uint32_t key = WP_KEY(result);
if (error) return true;
@ -1132,7 +1132,7 @@ static bool unwatch(GB_gameboy_t *gb, char *arguments, char *modifiers, const de
}
bool error;
value_t result = debugger_evaluate(gb, arguments, (unsigned int)strlen(arguments), &error, NULL, NULL);
value_t result = debugger_evaluate(gb, arguments, (unsigned)strlen(arguments), &error, NULL, NULL);
uint32_t key = WP_KEY(result);
if (error) return true;
@ -1234,7 +1234,7 @@ static bool _should_break(GB_gameboy_t *gb, value_t addr, bool jump_to)
}
bool error;
bool condition = debugger_evaluate(gb, gb->breakpoints[index].condition,
(unsigned int)strlen(gb->breakpoints[index].condition), &error, NULL, NULL).value;
(unsigned)strlen(gb->breakpoints[index].condition), &error, NULL, NULL).value;
if (error) {
/* Should never happen */
GB_log(gb, "An internal error has occured\n");
@ -1273,7 +1273,7 @@ static bool print(GB_gameboy_t *gb, char *arguments, char *modifiers, const debu
}
bool error;
value_t result = debugger_evaluate(gb, arguments, (unsigned int)strlen(arguments), &error, NULL, NULL);
value_t result = debugger_evaluate(gb, arguments, (unsigned)strlen(arguments), &error, NULL, NULL);
if (!error) {
switch (modifiers[0]) {
case 'a':
@ -1319,7 +1319,7 @@ static bool examine(GB_gameboy_t *gb, char *arguments, char *modifiers, const de
}
bool error;
value_t addr = debugger_evaluate(gb, arguments, (unsigned int)strlen(arguments), &error, NULL, NULL);
value_t addr = debugger_evaluate(gb, arguments, (unsigned)strlen(arguments), &error, NULL, NULL);
uint16_t count = 32;
if (modifiers) {
@ -1371,7 +1371,7 @@ static bool disassemble(GB_gameboy_t *gb, char *arguments, char *modifiers, cons
}
bool error;
value_t addr = debugger_evaluate(gb, arguments, (unsigned int)strlen(arguments), &error, NULL, NULL);
value_t addr = debugger_evaluate(gb, arguments, (unsigned)strlen(arguments), &error, NULL, NULL);
uint16_t count = 5;
if (modifiers) {
@ -1468,7 +1468,7 @@ static bool backtrace(GB_gameboy_t *gb, char *arguments, char *modifiers, const
}
GB_log(gb, " 1. %s\n", debugger_value_to_string(gb, (value_t){true, bank_for_addr(gb, gb->pc), gb->pc}, true));
for (unsigned int i = gb->backtrace_size; i--;) {
for (unsigned i = gb->backtrace_size; i--;) {
GB_log(gb, "%3d. %s\n", gb->backtrace_size - i + 1, debugger_value_to_string(gb, (value_t){true, gb->backtrace_returns[i].bank, gb->backtrace_returns[i].addr}, true));
}
@ -1937,7 +1937,7 @@ static bool _GB_debugger_test_write_watchpoint(GB_gameboy_t *gb, value_t addr, u
}
bool error;
bool condition = debugger_evaluate(gb, gb->watchpoints[index].condition,
(unsigned int)strlen(gb->watchpoints[index].condition), &error, &addr.value, &value).value;
(unsigned)strlen(gb->watchpoints[index].condition), &error, &addr.value, &value).value;
if (error) {
/* Should never happen */
GB_log(gb, "An internal error has occured\n");
@ -1982,7 +1982,7 @@ static bool _GB_debugger_test_read_watchpoint(GB_gameboy_t *gb, value_t addr)
}
bool error;
bool condition = debugger_evaluate(gb, gb->watchpoints[index].condition,
(unsigned int)strlen(gb->watchpoints[index].condition), &error, &addr.value, NULL).value;
(unsigned)strlen(gb->watchpoints[index].condition), &error, &addr.value, NULL).value;
if (error) {
/* Should never happen */
GB_log(gb, "An internal error has occured\n");
@ -2169,7 +2169,7 @@ void GB_debugger_load_symbol_file(GB_gameboy_t *gb, const char *path)
}
if (length == 0) continue;
unsigned int bank, address;
unsigned bank, address;
char symbol[length];
if (sscanf(line, "%02x:%04x %s", &bank, &address, symbol) == 3) {

View File

@ -135,9 +135,6 @@ void GB_free(GB_gameboy_t *gb)
if (gb->rom) {
free(gb->rom);
}
if (gb->apu_output.buffer) {
free(gb->apu_output.buffer);
}
if (gb->breakpoints) {
free(gb->breakpoints);
}

View File

@ -555,7 +555,7 @@ struct GB_gameboy_internal_s {
uint16_t addr_for_call_depth[0x200];
/* Backtrace */
unsigned int backtrace_size;
unsigned backtrace_size;
uint16_t backtrace_sps[0x200];
struct {
uint16_t bank;

View File

@ -126,13 +126,13 @@ static uint8_t read_rom(GB_gameboy_t *gb, uint16_t addr)
if (!gb->rom_size) {
return 0xFF;
}
unsigned int effective_address = (addr & 0x3FFF) + gb->mbc_rom0_bank * 0x4000;
unsigned effective_address = (addr & 0x3FFF) + gb->mbc_rom0_bank * 0x4000;
return gb->rom[effective_address & (gb->rom_size - 1)];
}
static uint8_t read_mbc_rom(GB_gameboy_t *gb, uint16_t addr)
{
unsigned int effective_address = (addr & 0x3FFF) + gb->mbc_rom_bank * 0x4000;
unsigned effective_address = (addr & 0x3FFF) + gb->mbc_rom_bank * 0x4000;
return gb->rom[effective_address & (gb->rom_size - 1)];
}

View File

@ -1,6 +1,7 @@
#include "gb.h"
#include "random.h"
#include <math.h>
#include <assert.h>
#define INTRO_ANIMATION_LENGTH 200
@ -461,8 +462,13 @@ static void render_boot_animation (GB_gameboy_t *gb)
}
}
static void render_jingle(GB_gameboy_t *gb, size_t count);
void GB_sgb_render(GB_gameboy_t *gb)
{
if (gb->apu_output.sample_rate) {
render_jingle(gb, gb->apu_output.sample_rate / (GB_get_clock_rate(gb) / LCDC_PERIOD));
}
if (gb->sgb->intro_animation < INTRO_ANIMATION_LENGTH) gb->sgb->intro_animation++;
if (!gb->screen || !gb->rgb_encode_callback) return;
@ -689,7 +695,7 @@ static double random_double(void)
return ((signed)(GB_random32() % 0x10001) - 0x8000) / (double) 0x8000;
}
bool GB_sgb_render_jingle(GB_gameboy_t *gb, GB_sample_t *dest, size_t count)
static void render_jingle(GB_gameboy_t *gb, size_t count)
{
const double frequencies[7] = {
466.16, // Bb4
@ -701,15 +707,17 @@ bool GB_sgb_render_jingle(GB_gameboy_t *gb, GB_sample_t *dest, size_t count)
1567.98, // G6
};
assert(gb->apu_output.sample_callback);
if (gb->sgb->intro_animation < 0) {
GB_sample_t sample = {0, 0};
for (unsigned i = 0; i < count; i++) {
dest->left = dest->right = 0;
dest++;
gb->apu_output.sample_callback(gb, &sample);
}
return true;
return;
}
if (gb->sgb->intro_animation >= INTRO_ANIMATION_LENGTH) return false;
if (gb->sgb->intro_animation >= INTRO_ANIMATION_LENGTH) return;
signed jingle_stage = (gb->sgb->intro_animation - 64) / 3;
double sweep_cutoff_ratio = 2000.0 * pow(2, gb->sgb->intro_animation / 20.0) / gb->apu_output.sample_rate;
@ -718,6 +726,7 @@ bool GB_sgb_render_jingle(GB_gameboy_t *gb, GB_sample_t *dest, size_t count)
sweep_cutoff_ratio = 1;
}
GB_sample_t stereo;
for (unsigned i = 0; i < count; i++) {
double sample = 0;
for (signed f = 0; f < 7 && f < jingle_stage; f++) {
@ -738,10 +747,10 @@ bool GB_sgb_render_jingle(GB_gameboy_t *gb, GB_sample_t *dest, size_t count)
sample += gb->sgb_intro_sweep_previous_sample * pow((120 - gb->sgb->intro_animation) / 120.0, 2) * 0.8;
}
dest->left = dest->right = sample * 0x7000;
dest++;
stereo.left = stereo.right = sample * 0x7000;
gb->apu_output.sample_callback(gb, &stereo);
}
return true;
return;
}

View File

@ -54,7 +54,6 @@ struct GB_sgb_s {
void GB_sgb_write(GB_gameboy_t *gb, uint8_t value);
void GB_sgb_render(GB_gameboy_t *gb);
void GB_sgb_load_default_data(GB_gameboy_t *gb);
bool GB_sgb_render_jingle(GB_gameboy_t *gb, GB_sample_t *dest, size_t count);
#endif

View File

@ -8,7 +8,7 @@
#include <sys/time.h>
#endif
static const unsigned int GB_TAC_TRIGGER_BITS[] = {512, 8, 32, 128};
static const unsigned GB_TAC_TRIGGER_BITS[] = {512, 8, 32, 128};
#ifndef DISABLE_TIMEKEEPING
static int64_t get_nanoseconds(void)
@ -251,8 +251,8 @@ void GB_emulate_timer_glitch(GB_gameboy_t *gb, uint8_t old_tac, uint8_t new_tac)
/* Glitch only happens when old_tac is enabled. */
if (!(old_tac & 4)) return;
unsigned int old_clocks = GB_TAC_TRIGGER_BITS[old_tac & 3];
unsigned int new_clocks = GB_TAC_TRIGGER_BITS[new_tac & 3];
unsigned old_clocks = GB_TAC_TRIGGER_BITS[old_tac & 3];
unsigned new_clocks = GB_TAC_TRIGGER_BITS[new_tac & 3];
/* The bit used for overflow testing must have been 1 */
if (gb->div_counter & old_clocks) {

View File

@ -33,6 +33,7 @@ static double clock_mutliplier = 1.0;
static char *filename = NULL;
static typeof(free) *free_function = NULL;
static char *battery_save_path_ptr;
static bool skip_audio;
SDL_AudioDeviceID device_id;
@ -336,6 +337,8 @@ static void vblank(GB_gameboy_t *gb)
}
do_rewind = rewind_down;
handle_events(gb);
skip_audio = (SDL_GetQueuedAudioSize(device_id) / sizeof(GB_sample_t)) > have_aspec.freq / 20;
}
@ -355,17 +358,13 @@ static void debugger_interrupt(int ignore)
GB_debugger_break(&gb);
}
static void audio_callback(void *gb, Uint8 *stream, int len)
static void gb_audio_callback(GB_gameboy_t *gb, GB_sample_t *sample)
{
if (GB_is_inited(gb)) {
GB_apu_copy_buffer(gb, (GB_sample_t *) stream, len / sizeof(GB_sample_t));
}
else {
memset(stream, 0, len);
}
if (skip_audio) return;
SDL_QueueAudio(device_id, sample, sizeof(*sample));
}
static bool handle_pending_command(void)
{
switch (pending_command) {
@ -436,6 +435,7 @@ restart:
GB_set_highpass_filter_mode(&gb, configuration.highpass_mode);
GB_set_rewind_length(&gb, configuration.rewind_length);
GB_set_update_input_hint_callback(&gb, handle_events);
GB_apu_set_sample_callback(&gb, gb_audio_callback);
}
SDL_DestroyTexture(texture);
@ -612,9 +612,6 @@ int main(int argc, char **argv)
}
#endif
want_aspec.callback = audio_callback;
want_aspec.userdata = &gb;
device_id = SDL_OpenAudioDevice(0, 0, &want_aspec, &have_aspec, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE);
/* Start Audio */

View File

@ -12,6 +12,7 @@ SOURCES_C := $(CORE_DIR)/Core/gb.c \
$(CORE_DIR)/Core/sm83_cpu.c \
$(CORE_DIR)/Core/joypad.c \
$(CORE_DIR)/Core/save_state.c \
$(CORE_DIR)/Core/random.c \
$(CORE_DIR)/libretro/agb_boot.c \
$(CORE_DIR)/libretro/cgb_boot.c \
$(CORE_DIR)/libretro/dmg_boot.c \

View File

@ -150,34 +150,22 @@ static void GB_update_keys_status(GB_gameboy_t *gb, unsigned port)
}
static void audio_callback(void *gb)
static void audio_callback(GB_gameboy_t *gb, GB_sample_t *sample)
{
size_t length = GB_apu_get_current_buffer_length(gb);
while (length > sizeof(soundbuf) / 4)
{
GB_apu_copy_buffer(gb, (GB_sample_t *) soundbuf, 1024);
audio_batch_cb(soundbuf, 1024);
length -= 1024;
}
if (length) {
GB_apu_copy_buffer(gb, (GB_sample_t *) soundbuf, length);
audio_batch_cb(soundbuf, length);
if ((audio_out == GB_1 && gb == &gameboy[0]) ||
(audio_out == GB_2 && gb == &gameboy[1])) {
audio_batch_cb((void*)sample, 1);
}
}
static void vblank1(GB_gameboy_t *gb)
{
vblank1_occurred = true;
if (audio_out == GB_1)
audio_callback(gb);
}
static void vblank2(GB_gameboy_t *gb)
{
vblank2_occurred = true;
if (audio_out == GB_2)
audio_callback(gb);
}
static bool bit_to_send1 = true, bit_to_send2 = true;
@ -383,6 +371,7 @@ static void init_for_current_model(unsigned id)
GB_set_pixels_output(&gameboy[i],(unsigned int*)(frame_buf + i * ((model[i] == MODEL_SGB || model[i] == MODEL_SGB2) ? SGB_VIDEO_PIXELS : VIDEO_PIXELS)));
GB_set_rgb_encode_callback(&gameboy[i], rgb_encode);
GB_set_sample_rate(&gameboy[i], AUDIO_FREQUENCY);
GB_apu_set_sample_callback(&gameboy[i], audio_callback);
/* todo: attempt to make these more generic */
GB_set_vblank_callback(&gameboy[0], (GB_vblank_callback_t) vblank1);