sameboy: brip bruffer

This commit is contained in:
nattthebear 2017-07-18 17:46:21 -04:00
parent f7bb894753
commit 721a6c0470
8 changed files with 493 additions and 181 deletions

View File

@ -62,9 +62,9 @@ static int16_t step_lfsr(uint16_t lfsr, bool uses_7_bit)
static void GB_apu_run_internal(GB_gameboy_t *gb)
{
while (!__sync_bool_compare_and_swap(&gb->apu_lock, false, true));
uint32_t steps = gb->apu.apu_cycles / (CPU_FREQUENCY/APU_FREQUENCY);
if (!steps) goto exit;
if (!steps)
return;
gb->apu.apu_cycles %= (CPU_FREQUENCY/APU_FREQUENCY);
for (uint8_t i = 0; i < 4; i++) {
@ -118,13 +118,9 @@ static void GB_apu_run_internal(GB_gameboy_t *gb)
// Back to frequency
gb->apu.wave_channels[0].wave_length = (2048 - temp) * (APU_FREQUENCY / 131072);
gb->apu.wave_channels[0].cur_sweep_steps = gb->apu.wave_channels[0].sweep_steps;
}
}
exit:
gb->apu_lock = false;
}
void GB_apu_get_samples_and_update_pcm_regs(GB_gameboy_t *gb, GB_sample_t *samples)
@ -185,90 +181,9 @@ void GB_apu_get_samples_and_update_pcm_regs(GB_gameboy_t *gb, GB_sample_t *sampl
void GB_apu_run(GB_gameboy_t *gb)
{
if (gb->sample_rate == 0) {
if (gb->apu.apu_cycles > 0xFF00) {
GB_sample_t dummy;
GB_apu_get_samples_and_update_pcm_regs(gb, &dummy);
}
return;
}
while (gb->audio_copy_in_progress);
double ticks_per_sample = (double) CPU_FREQUENCY / gb->sample_rate;
if (gb->audio_quality == 0) {
GB_sample_t sample;
GB_apu_get_samples_and_update_pcm_regs(gb, &sample);
gb->current_supersample.left += sample.left;
gb->current_supersample.right += sample.right;
gb->n_subsamples++;
}
else if (gb->audio_quality != 1) {
double ticks_per_subsample = ticks_per_sample / gb->audio_quality;
if (ticks_per_subsample < 1) {
ticks_per_subsample = 1;
}
if (gb->apu_subsample_cycles > ticks_per_subsample) {
gb->apu_subsample_cycles -= ticks_per_subsample;
}
GB_sample_t sample;
GB_apu_get_samples_and_update_pcm_regs(gb, &sample);
gb->current_supersample.left += sample.left;
gb->current_supersample.right += sample.right;
gb->n_subsamples++;
}
if (gb->apu_sample_cycles > ticks_per_sample) {
gb->apu_sample_cycles -= ticks_per_sample;
if (gb->audio_position == gb->buffer_size) {
/*
if (!gb->turbo) {
GB_log(gb, "Audio overflow\n");
}
*/
}
else {
if (gb->audio_quality == 1) {
GB_apu_get_samples_and_update_pcm_regs(gb, &gb->audio_buffer[gb->audio_position++]);
}
else {
gb->audio_buffer[gb->audio_position].left = round(gb->current_supersample.left / gb->n_subsamples);
gb->audio_buffer[gb->audio_position].right = round(gb->current_supersample.right / gb->n_subsamples);
gb->n_subsamples = 0;
gb->current_supersample = (GB_double_sample_t){0, };
gb->audio_position++;
}
}
}
}
void GB_apu_copy_buffer(GB_gameboy_t *gb, GB_sample_t *dest, unsigned int count)
{
gb->audio_copy_in_progress = true;
if (!gb->audio_stream_started) {
// Intentionally fail the first copy to sync the stream with the Gameboy.
gb->audio_stream_started = true;
gb->audio_position = 0;
}
if (count > gb->audio_position) {
// GB_log(gb, "Audio underflow: %d\n", count - gb->audio_position);
if (gb->audio_position != 0) {
for (unsigned i = 0; i < count - gb->audio_position; i++) {
dest[gb->audio_position + i] = gb->audio_buffer[gb->audio_position - 1];
}
}
else {
memset(dest + gb->audio_position, 0, (count - gb->audio_position) * sizeof(*gb->audio_buffer));
}
count = gb->audio_position;
}
memcpy(dest, gb->audio_buffer, count * sizeof(*gb->audio_buffer));
memmove(gb->audio_buffer, gb->audio_buffer + count, (gb->audio_position - count) * sizeof(*gb->audio_buffer));
gb->audio_position -= count;
gb->audio_copy_in_progress = false;
GB_sample_t sample;
GB_apu_get_samples_and_update_pcm_regs(gb, &sample);
gb->sample_callback(gb, sample, gb->cycles_since_epoch);
}
void GB_apu_init(GB_gameboy_t *gb)
@ -485,13 +400,3 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
break;
}
}
void GB_set_audio_quality(GB_gameboy_t *gb, unsigned quality)
{
gb->audio_quality = quality;
}
unsigned GB_apu_get_current_buffer_length(GB_gameboy_t *gb)
{
return gb->audio_position;
}

View File

@ -14,12 +14,6 @@ typedef struct
int16_t right;
} GB_sample_t;
typedef struct
{
double left;
double right;
} GB_double_sample_t;
/* Not all used on all channels */
/* All lengths are in APU ticks */
typedef struct
@ -60,13 +54,6 @@ typedef struct
GB_apu_channel_t wave_channels[4];
} GB_apu_t;
void GB_set_sample_rate(GB_gameboy_t *gb, unsigned int sample_rate);
/* Quality is the number of subsamples per sampling, for the sake of resampling.
1 means on resampling at all, 0 is maximum quality. Default is 4. */
void GB_set_audio_quality(GB_gameboy_t *gb, unsigned quality);
void GB_apu_copy_buffer(GB_gameboy_t *gb, GB_sample_t *dest, unsigned int count);
unsigned GB_apu_get_current_buffer_length(GB_gameboy_t *gb);
#ifdef GB_INTERNAL
void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value);
uint8_t GB_apu_read(GB_gameboy_t *gb, uint8_t reg);

View File

@ -1,6 +1,7 @@
#include <stdint.h>
#include "../emulibc/emulibc.h"
#include "../emulibc/waterboxcore.h"
#include "blip_buf/blip_buf.h"
#define _Static_assert static_assert
@ -14,7 +15,6 @@ static GB_gameboy_t GB;
static void VBlankCallback(GB_gameboy_t *gb)
{
}
static void LogCallback(GB_gameboy_t *gb, const char *string, GB_log_attributes attributes)
@ -24,22 +24,19 @@ static void LogCallback(GB_gameboy_t *gb, const char *string, GB_log_attributes
static uint32_t RgbEncodeCallback(GB_gameboy_t *gb, uint8_t r, uint8_t g, uint8_t b)
{
return b | g << 8 | r << 16 | 0xff000000;
return b | g << 8 | r << 16 | 0xff000000;
}
static void InfraredCallback(GB_gameboy_t *gb, bool on, long cycles_since_last_update)
{
}
static void RumbleCallback(GB_gameboy_t *gb, bool rumble_on)
{
}
static void SerialStartCallback(GB_gameboy_t *gb, uint8_t byte_to_send)
{
}
static uint8_t SerialEndCallback(GB_gameboy_t *gb)
@ -47,55 +44,83 @@ static uint8_t SerialEndCallback(GB_gameboy_t *gb)
return 0;
}
static blip_t *leftblip;
static blip_t *rightblip;
const int SOUND_RATE_GB = 2097152;
const int SOUND_RATE_SGB = 2147727;
static uint64_t sound_start_clock;
static GB_sample_t sample_gb;
static void SampleCallback(GB_gameboy_t *gb, GB_sample_t sample, uint64_t clock)
{
int l = sample.left - sample_gb.left;
int r = sample.right - sample_gb.right;
if (l)
blip_add_delta(leftblip, clock - sound_start_clock, l);
if (r)
blip_add_delta(rightblip, clock - sound_start_clock, r);
sample_gb = sample;
}
ECL_EXPORT bool Init(bool cgb)
{
if (cgb)
GB_init_cgb(&GB);
else
GB_init(&GB);
if (GB_load_boot_rom(&GB, "boot.rom") != 0)
return false;
if (GB_load_rom(&GB, "game.rom") != 0)
return false;
if (cgb)
GB_init_cgb(&GB);
else
GB_init(&GB);
if (GB_load_boot_rom(&GB, "boot.rom") != 0)
return false;
GB_set_vblank_callback(&GB, VBlankCallback);
GB_set_log_callback(&GB, LogCallback);
GB_set_rgb_encode_callback(&GB, RgbEncodeCallback);
GB_set_infrared_callback(&GB, InfraredCallback);
GB_set_rumble_callback(&GB, RumbleCallback);
if (GB_load_rom(&GB, "game.rom") != 0)
return false;
GB_set_sample_rate(&GB, 44100);
GB_set_audio_quality(&GB, 1);
GB_set_vblank_callback(&GB, VBlankCallback);
GB_set_log_callback(&GB, LogCallback);
GB_set_rgb_encode_callback(&GB, RgbEncodeCallback);
GB_set_infrared_callback(&GB, InfraredCallback);
GB_set_rumble_callback(&GB, RumbleCallback);
GB_set_sample_callback(&GB, SampleCallback);
return true;
leftblip = blip_new(1024);
rightblip = blip_new(1024);
blip_set_rates(leftblip, SOUND_RATE_GB, 44100);
blip_set_rates(rightblip, SOUND_RATE_GB, 44100);
return true;
}
struct MyFrameInfo : public FrameInfo
{
};
static int FrameOverflow;
ECL_EXPORT void FrameAdvance(MyFrameInfo &f)
{
GB_set_pixels_output(&GB, f.VideoBuffer);
GB_set_pixels_output(&GB, f.VideoBuffer);
for (int i = 0; i < (int)GB_KEY_MAX; i++)
GB_set_key_state(&GB, (GB_key_t)i, false);
sound_start_clock = GB_epoch(&GB);
f.Cycles = GB_run_cycles(&GB, 35112);
f.Samples = GB_apu_get_current_buffer_length(&GB);
GB_apu_copy_buffer(&GB, (GB_sample_t*)f.SoundBuffer, f.Samples);
uint32_t target = 35112 - FrameOverflow;
f.Cycles = GB_run_cycles(&GB, target);
FrameOverflow = f.Cycles - target;
blip_end_frame(leftblip, f.Cycles);
blip_end_frame(rightblip, f.Cycles);
f.Samples = blip_read_samples(leftblip, f.SoundBuffer, 2048, 1);
blip_read_samples(rightblip, f.SoundBuffer + 1, 2048, 1);
f.Width = 160;
f.Height = 144;
}
static void SetMemoryArea(MemoryArea *m, GB_direct_access_t access, const char* name, int32_t flags)
static void SetMemoryArea(MemoryArea *m, GB_direct_access_t access, const char *name, int32_t flags)
{
size_t size;
m->Name = name;
m->Data = GB_get_direct_access(&GB, access, &size, nullptr);
m->Size = size;
m->Flags = flags;
size_t size;
m->Name = name;
m->Data = GB_get_direct_access(&GB, access, &size, nullptr);
m->Size = size;
m->Flags = flags;
}
ECL_EXPORT void GetMemoryAreas(MemoryArea *m)

View File

@ -0,0 +1,344 @@
/* blip_buf 1.1.0. http://www.slack.net/~ant/ */
#include "blip_buf.h"
#include <assert.h>
#include <limits.h>
#include <string.h>
#include <stdlib.h>
/* Library Copyright (C) 2003-2009 Shay Green. This library is free software;
you can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. This
library 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 Lesser General Public License for more
details. You should have received a copy of the GNU Lesser General Public
License along with this module; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
#if defined (BLARGG_TEST) && BLARGG_TEST
#include "blargg_test.h"
#endif
/* Equivalent to ULONG_MAX >= 0xFFFFFFFF00000000.
Avoids constants that don't fit in 32 bits. */
#if ULONG_MAX/0xFFFFFFFF > 0xFFFFFFFF
typedef unsigned long fixed_t;
enum { pre_shift = 32 };
#elif defined(ULLONG_MAX)
typedef unsigned long long fixed_t;
enum { pre_shift = 32 };
#else
typedef unsigned fixed_t;
enum { pre_shift = 0 };
#endif
enum { time_bits = pre_shift + 20 };
static fixed_t const time_unit = (fixed_t) 1 << time_bits;
enum { bass_shift = 9 }; /* affects high-pass filter breakpoint frequency */
enum { end_frame_extra = 2 }; /* allows deltas slightly after frame length */
enum { half_width = 8 };
enum { buf_extra = half_width*2 + end_frame_extra };
enum { phase_bits = 5 };
enum { phase_count = 1 << phase_bits };
enum { delta_bits = 15 };
enum { delta_unit = 1 << delta_bits };
enum { frac_bits = time_bits - pre_shift };
/* We could eliminate avail and encode whole samples in offset, but that would
limit the total buffered samples to blip_max_frame. That could only be
increased by decreasing time_bits, which would reduce resample ratio accuracy.
*/
/** Sample buffer that resamples to output rate and accumulates samples
until they're read out */
struct blip_t
{
fixed_t factor;
fixed_t offset;
int avail;
int size;
int integrator;
};
typedef int buf_t;
/* probably not totally portable */
#define SAMPLES( buf ) ((buf_t*) ((buf) + 1))
/* Arithmetic (sign-preserving) right shift */
#define ARITH_SHIFT( n, shift ) \
((n) >> (shift))
enum { max_sample = +32767 };
enum { min_sample = -32768 };
#define CLAMP( n ) \
{\
if ( (short) n != n )\
n = ARITH_SHIFT( n, 16 ) ^ max_sample;\
}
static void check_assumptions( void )
{
int n;
#if INT_MAX < 0x7FFFFFFF || UINT_MAX < 0xFFFFFFFF
#error "int must be at least 32 bits"
#endif
assert( (-3 >> 1) == -2 ); /* right shift must preserve sign */
n = max_sample * 2;
CLAMP( n );
assert( n == max_sample );
n = min_sample * 2;
CLAMP( n );
assert( n == min_sample );
assert( blip_max_ratio <= time_unit );
assert( blip_max_frame <= (fixed_t) -1 >> time_bits );
}
blip_t* blip_new( int size )
{
blip_t* m;
assert( size >= 0 );
m = (blip_t*) malloc( sizeof *m + (size + buf_extra) * sizeof (buf_t) );
if ( m )
{
m->factor = time_unit / blip_max_ratio;
m->size = size;
blip_clear( m );
check_assumptions();
}
return m;
}
void blip_delete( blip_t* m )
{
if ( m != NULL )
{
/* Clear fields in case user tries to use after freeing */
memset( m, 0, sizeof *m );
free( m );
}
}
void blip_set_rates( blip_t* m, double clock_rate, double sample_rate )
{
double factor = time_unit * sample_rate / clock_rate;
m->factor = (fixed_t) factor;
/* Fails if clock_rate exceeds maximum, relative to sample_rate */
assert( 0 <= factor - m->factor && factor - m->factor < 1 );
/* Avoid requiring math.h. Equivalent to
m->factor = (int) ceil( factor ) */
if ( m->factor < factor )
m->factor++;
/* At this point, factor is most likely rounded up, but could still
have been rounded down in the floating-point calculation. */
}
void blip_clear( blip_t* m )
{
/* We could set offset to 0, factor/2, or factor-1. 0 is suitable if
factor is rounded up. factor-1 is suitable if factor is rounded down.
Since we don't know rounding direction, factor/2 accommodates either,
with the slight loss of showing an error in half the time. Since for
a 64-bit factor this is years, the halving isn't a problem. */
m->offset = m->factor / 2;
m->avail = 0;
m->integrator = 0;
memset( SAMPLES( m ), 0, (m->size + buf_extra) * sizeof (buf_t) );
}
int blip_clocks_needed( const blip_t* m, int samples )
{
fixed_t needed;
/* Fails if buffer can't hold that many more samples */
assert( samples >= 0 && m->avail + samples <= m->size );
needed = (fixed_t) samples * time_unit;
if ( needed < m->offset )
return 0;
return (needed - m->offset + m->factor - 1) / m->factor;
}
void blip_end_frame( blip_t* m, unsigned t )
{
fixed_t off = t * m->factor + m->offset;
m->avail += off >> time_bits;
m->offset = off & (time_unit - 1);
/* Fails if buffer size was exceeded */
assert( m->avail <= m->size );
}
int blip_samples_avail( const blip_t* m )
{
return m->avail;
}
static void remove_samples( blip_t* m, int count )
{
buf_t* buf = SAMPLES( m );
int remain = m->avail + buf_extra - count;
m->avail -= count;
memmove( &buf [0], &buf [count], remain * sizeof buf [0] );
memset( &buf [remain], 0, count * sizeof buf [0] );
}
int blip_read_samples( blip_t* m, short out [], int count, int stereo )
{
assert( count >= 0 );
if ( count > m->avail )
count = m->avail;
if ( count )
{
int const step = stereo ? 2 : 1;
buf_t const* in = SAMPLES( m );
buf_t const* end = in + count;
int sum = m->integrator;
do
{
/* Eliminate fraction */
int s = ARITH_SHIFT( sum, delta_bits );
sum += *in++;
CLAMP( s );
*out = s;
out += step;
/* High-pass filter */
sum -= s << (delta_bits - bass_shift);
}
while ( in != end );
m->integrator = sum;
remove_samples( m, count );
}
return count;
}
/* Things that didn't help performance on x86:
__attribute__((aligned(128)))
#define short int
restrict
*/
/* Sinc_Generator( 0.9, 0.55, 4.5 ) */
static short const bl_step [phase_count + 1] [half_width] =
{
{ 43, -115, 350, -488, 1136, -914, 5861,21022},
{ 44, -118, 348, -473, 1076, -799, 5274,21001},
{ 45, -121, 344, -454, 1011, -677, 4706,20936},
{ 46, -122, 336, -431, 942, -549, 4156,20829},
{ 47, -123, 327, -404, 868, -418, 3629,20679},
{ 47, -122, 316, -375, 792, -285, 3124,20488},
{ 47, -120, 303, -344, 714, -151, 2644,20256},
{ 46, -117, 289, -310, 634, -17, 2188,19985},
{ 46, -114, 273, -275, 553, 117, 1758,19675},
{ 44, -108, 255, -237, 471, 247, 1356,19327},
{ 43, -103, 237, -199, 390, 373, 981,18944},
{ 42, -98, 218, -160, 310, 495, 633,18527},
{ 40, -91, 198, -121, 231, 611, 314,18078},
{ 38, -84, 178, -81, 153, 722, 22,17599},
{ 36, -76, 157, -43, 80, 824, -241,17092},
{ 34, -68, 135, -3, 8, 919, -476,16558},
{ 32, -61, 115, 34, -60, 1006, -683,16001},
{ 29, -52, 94, 70, -123, 1083, -862,15422},
{ 27, -44, 73, 106, -184, 1152,-1015,14824},
{ 25, -36, 53, 139, -239, 1211,-1142,14210},
{ 22, -27, 34, 170, -290, 1261,-1244,13582},
{ 20, -20, 16, 199, -335, 1301,-1322,12942},
{ 18, -12, -3, 226, -375, 1331,-1376,12293},
{ 15, -4, -19, 250, -410, 1351,-1408,11638},
{ 13, 3, -35, 272, -439, 1361,-1419,10979},
{ 11, 9, -49, 292, -464, 1362,-1410,10319},
{ 9, 16, -63, 309, -483, 1354,-1383, 9660},
{ 7, 22, -75, 322, -496, 1337,-1339, 9005},
{ 6, 26, -85, 333, -504, 1312,-1280, 8355},
{ 4, 31, -94, 341, -507, 1278,-1205, 7713},
{ 3, 35, -102, 347, -506, 1238,-1119, 7082},
{ 1, 40, -110, 350, -499, 1190,-1021, 6464},
{ 0, 43, -115, 350, -488, 1136, -914, 5861}
};
/* Shifting by pre_shift allows calculation using unsigned int rather than
possibly-wider fixed_t. On 32-bit platforms, this is likely more efficient.
And by having pre_shift 32, a 32-bit platform can easily do the shift by
simply ignoring the low half. */
void blip_add_delta( blip_t* m, unsigned time, int delta )
{
unsigned fixed = (unsigned) ((time * m->factor + m->offset) >> pre_shift);
buf_t* out = SAMPLES( m ) + m->avail + (fixed >> frac_bits);
int const phase_shift = frac_bits - phase_bits;
int phase = fixed >> phase_shift & (phase_count - 1);
short const* in = bl_step [phase];
short const* rev = bl_step [phase_count - phase];
int interp = fixed >> (phase_shift - delta_bits) & (delta_unit - 1);
int delta2 = (delta * interp) >> delta_bits;
delta -= delta2;
/* Fails if buffer size was exceeded */
assert( out <= &SAMPLES( m ) [m->size + end_frame_extra] );
out [0] += in[0]*delta + in[half_width+0]*delta2;
out [1] += in[1]*delta + in[half_width+1]*delta2;
out [2] += in[2]*delta + in[half_width+2]*delta2;
out [3] += in[3]*delta + in[half_width+3]*delta2;
out [4] += in[4]*delta + in[half_width+4]*delta2;
out [5] += in[5]*delta + in[half_width+5]*delta2;
out [6] += in[6]*delta + in[half_width+6]*delta2;
out [7] += in[7]*delta + in[half_width+7]*delta2;
in = rev;
out [ 8] += in[7]*delta + in[7-half_width]*delta2;
out [ 9] += in[6]*delta + in[6-half_width]*delta2;
out [10] += in[5]*delta + in[5-half_width]*delta2;
out [11] += in[4]*delta + in[4-half_width]*delta2;
out [12] += in[3]*delta + in[3-half_width]*delta2;
out [13] += in[2]*delta + in[2-half_width]*delta2;
out [14] += in[1]*delta + in[1-half_width]*delta2;
out [15] += in[0]*delta + in[0-half_width]*delta2;
}
void blip_add_delta_fast( blip_t* m, unsigned time, int delta )
{
unsigned fixed = (unsigned) ((time * m->factor + m->offset) >> pre_shift);
buf_t* out = SAMPLES( m ) + m->avail + (fixed >> frac_bits);
int interp = fixed >> (frac_bits - delta_bits) & (delta_unit - 1);
int delta2 = delta * interp;
/* Fails if buffer size was exceeded */
assert( out <= &SAMPLES( m ) [m->size + end_frame_extra] );
out [7] += delta * delta_unit - delta2;
out [8] += delta2;
}

View File

@ -0,0 +1,72 @@
/** \file
Sample buffer that resamples from input clock rate to output sample rate */
/* blip_buf 1.1.0 */
#ifndef BLIP_BUF_H
#define BLIP_BUF_H
#ifdef __cplusplus
extern "C" {
#endif
/** First parameter of most functions is blip_t*, or const blip_t* if nothing
is changed. */
typedef struct blip_t blip_t;
/** Creates new buffer that can hold at most sample_count samples. Sets rates
so that there are blip_max_ratio clocks per sample. Returns pointer to new
buffer, or NULL if insufficient memory. */
blip_t* blip_new( int sample_count );
/** Sets approximate input clock rate and output sample rate. For every
clock_rate input clocks, approximately sample_rate samples are generated. */
void blip_set_rates( blip_t*, double clock_rate, double sample_rate );
enum { /** Maximum clock_rate/sample_rate ratio. For a given sample_rate,
clock_rate must not be greater than sample_rate*blip_max_ratio. */
blip_max_ratio = 1 << 20 };
/** Clears entire buffer. Afterwards, blip_samples_avail() == 0. */
void blip_clear( blip_t* );
/** Adds positive/negative delta into buffer at specified clock time. */
void blip_add_delta( blip_t*, unsigned int clock_time, int delta );
/** Same as blip_add_delta(), but uses faster, lower-quality synthesis. */
void blip_add_delta_fast( blip_t*, unsigned int clock_time, int delta );
/** Length of time frame, in clocks, needed to make sample_count additional
samples available. */
int blip_clocks_needed( const blip_t*, int sample_count );
enum { /** Maximum number of samples that can be generated from one time frame. */
blip_max_frame = 4000 };
/** Makes input clocks before clock_duration available for reading as output
samples. Also begins new time frame at clock_duration, so that clock time 0 in
the new time frame specifies the same clock as clock_duration in the old time
frame specified. Deltas can have been added slightly past clock_duration (up to
however many clocks there are in two output samples). */
void blip_end_frame( blip_t*, unsigned int clock_duration );
/** Number of buffered samples available for reading. */
int blip_samples_avail( const blip_t* );
/** Reads and removes at most 'count' samples and writes them to 'out'. If
'stereo' is true, writes output to every other element of 'out', allowing easy
interleaving of two buffers into a stereo sample stream. Outputs 16-bit signed
samples. Returns number of samples actually read. */
int blip_read_samples( blip_t*, short out [], int count, int stereo );
/** Frees buffer. No effect if NULL is passed. */
void blip_delete( blip_t* );
/* Deprecated */
typedef blip_t blip_buffer_t;
#ifdef __cplusplus
}
#endif
#endif

View File

@ -92,7 +92,6 @@ void GB_init(GB_gameboy_t *gb)
gb->input_callback = default_input_callback;
gb->async_input_callback = default_async_input_callback;
gb->cartridge_type = &GB_cart_defs[0]; // Default cartridge type
gb->audio_quality = 4;
GB_reset(gb);
}
@ -107,7 +106,6 @@ void GB_init_cgb(GB_gameboy_t *gb)
gb->input_callback = default_input_callback;
gb->async_input_callback = default_async_input_callback;
gb->cartridge_type = &GB_cart_defs[0]; // Default cartridge type
gb->audio_quality = 4;
GB_reset(gb);
}
@ -127,9 +125,6 @@ void GB_free(GB_gameboy_t *gb)
if (gb->rom) {
free(gb->rom);
}
if (gb->audio_buffer) {
free(gb->audio_buffer);
}
if (gb->breakpoints) {
free(gb->breakpoints);
}
@ -262,15 +257,18 @@ uint64_t GB_run_cycles(GB_gameboy_t *gb, uint32_t cycles)
while (gb->cycles_since_epoch < target) {
GB_run(gb);
if (gb->vblank_just_occured) {
// TODO: fix these up
GB_update_joyp(gb);
GB_rtc_run(gb);
break;
}
}
return gb->cycles_since_epoch - start;
}
uint64_t GB_epoch(GB_gameboy_t *gb)
{
return gb->cycles_since_epoch;
}
void GB_set_pixels_output(GB_gameboy_t *gb, uint32_t *output)
{
gb->screen = output;
@ -340,6 +338,11 @@ void GB_set_rumble_callback(GB_gameboy_t *gb, GB_rumble_callback_t callback)
gb->rumble_callback = callback;
}
void GB_set_sample_callback(GB_gameboy_t *gb, GB_sample_callback_t callback)
{
gb->sample_callback = callback;
}
void GB_set_serial_transfer_start_callback(GB_gameboy_t *gb, GB_serial_transfer_start_callback_t callback)
{
gb->serial_transfer_start_callback = callback;
@ -370,17 +373,6 @@ void GB_serial_set_data(GB_gameboy_t *gb, uint8_t data)
gb->io_registers[GB_IO_IF] |= 8;
}
void GB_set_sample_rate(GB_gameboy_t *gb, unsigned int sample_rate)
{
if (gb->audio_buffer) {
free(gb->audio_buffer);
}
gb->buffer_size = sample_rate / 25; // 40ms delay
gb->audio_buffer = malloc(gb->buffer_size * sizeof(*gb->audio_buffer));
gb->sample_rate = sample_rate;
gb->audio_position = 0;
}
void GB_disconnect_serial(GB_gameboy_t *gb)
{
gb->serial_transfer_start_callback = NULL;

View File

@ -171,6 +171,7 @@ typedef void (*GB_infrared_callback_t)(GB_gameboy_t *gb, bool on, long cycles_si
typedef void (*GB_rumble_callback_t)(GB_gameboy_t *gb, bool rumble_on);
typedef void (*GB_serial_transfer_start_callback_t)(GB_gameboy_t *gb, uint8_t byte_to_send);
typedef uint8_t (*GB_serial_transfer_end_callback_t)(GB_gameboy_t *gb);
typedef void (*GB_sample_callback_t)(GB_gameboy_t *gb, GB_sample_t sample, uint64_t clock);
typedef struct {
bool state;
@ -379,26 +380,11 @@ struct GB_gameboy_internal_s {
/* I/O */
uint32_t *screen;
GB_sample_t *audio_buffer;
bool keys[GB_KEY_MAX];
/* Timing */
uint64_t cycles_since_epoch;
/* Audio */
unsigned buffer_size;
unsigned sample_rate;
unsigned audio_position;
bool audio_stream_started; /* detects first copy request to minimize lag */
volatile bool audio_copy_in_progress;
volatile bool apu_lock;
double apu_sample_cycles;
double apu_subsample_cycles;
GB_double_sample_t current_supersample;
unsigned n_subsamples;
unsigned audio_quality;
/* Callbacks */
void *user_data;
GB_log_callback_t log_callback;
@ -412,6 +398,7 @@ struct GB_gameboy_internal_s {
GB_rumble_callback_t rumble_callback;
GB_serial_transfer_start_callback_t serial_transfer_start_callback;
GB_serial_transfer_end_callback_t serial_transfer_end_callback;
GB_sample_callback_t sample_callback;
/* IR */
long cycles_since_ir_change;
@ -466,6 +453,7 @@ void GB_reset(GB_gameboy_t *gb);
void GB_switch_model_and_reset(GB_gameboy_t *gb, bool is_cgb);
void GB_run(GB_gameboy_t *gb);
uint64_t GB_run_cycles(GB_gameboy_t *gb, uint32_t cycles);
uint64_t GB_epoch(GB_gameboy_t *gb);
typedef enum {
GB_DIRECT_ACCESS_ROM,
@ -510,6 +498,7 @@ void GB_set_async_input_callback(GB_gameboy_t *gb, GB_input_callback_t callback)
void GB_set_rgb_encode_callback(GB_gameboy_t *gb, GB_rgb_encode_callback_t callback);
void GB_set_infrared_callback(GB_gameboy_t *gb, GB_infrared_callback_t callback);
void GB_set_rumble_callback(GB_gameboy_t *gb, GB_rumble_callback_t callback);
void GB_set_sample_callback(GB_gameboy_t *gb, GB_sample_callback_t callback);
/* These APIs are used when using internal clock */
void GB_set_serial_transfer_start_callback(GB_gameboy_t *gb, GB_serial_transfer_start_callback_t callback);

View File

@ -68,8 +68,6 @@ void GB_advance_cycles(GB_gameboy_t *gb, uint8_t cycles)
// Not affected by speed boost
gb->hdma_cycles += cycles;
gb->apu_sample_cycles += cycles;
gb->apu_subsample_cycles += cycles;
gb->apu.apu_cycles += cycles;
gb->cycles_since_ir_change += cycles;
gb->cycles_since_input_ir_change += cycles;