# HG changeset patch # User Michael Pavone # Date 1561098498 25200 # Node ID fa4745d420936c6e98b9eaa61d8038df8dcf2922 # Parent 84f16a804ce5a4f6636472331a8c48add47a80b2# Parent 96323d73b8ab30d5c7d347c6a666b5c2ff24964c Merge diff -r 96323d73b8ab -r fa4745d42093 Makefile --- a/Makefile Sun Jun 02 23:16:15 2019 -0700 +++ b/Makefile Thu Jun 20 23:28:18 2019 -0700 @@ -197,7 +197,7 @@ AUDIOOBJS=ym2612.o psg.o wave.o CONFIGOBJS=config.o tern.o util.o paths.o NUKLEAROBJS=$(FONT) nuklear_ui/blastem_nuklear.o nuklear_ui/sfnt.o -RENDEROBJS=ppm.o controller_info.o +RENDEROBJS=ppm.o controller_info.o render_audio.o ifdef USE_FBDEV RENDEROBJS+= render_fbdev.o else diff -r 96323d73b8ab -r fa4745d42093 psg.c --- a/psg.c Sun Jun 02 23:16:15 2019 -0700 +++ b/psg.c Thu Jun 20 23:28:18 2019 -0700 @@ -4,7 +4,6 @@ BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. */ #include "psg.h" -#include "render.h" #include "blastem.h" #include #include diff -r 96323d73b8ab -r fa4745d42093 psg.h --- a/psg.h Sun Jun 02 23:16:15 2019 -0700 +++ b/psg.h Thu Jun 20 23:28:18 2019 -0700 @@ -8,7 +8,7 @@ #include #include "serialize.h" -#include "render.h" +#include "render_audio.h" typedef struct { audio_source *audio; diff -r 96323d73b8ab -r fa4745d42093 render.h --- a/render.h Sun Jun 02 23:16:15 2019 -0700 +++ b/render.h Thu Jun 20 23:28:18 2019 -0700 @@ -92,7 +92,6 @@ #define RENDER_NOT_MAPPED -2 #define RENDER_NOT_PLUGGED_IN -3 -typedef struct audio_source audio_source; typedef void (*drop_handler)(const char *filename); typedef void (*window_close_handler)(uint8_t which); typedef void (*ui_render_fun)(void); @@ -109,9 +108,7 @@ void render_set_video_standard(vid_std std); void render_toggle_fullscreen(); void render_update_caption(char *title); -void render_wait_quit(vdp_context * context); -uint32_t render_audio_buffer(); -uint32_t render_sample_rate(); +void render_wait_quit(void); void process_events(); int render_width(); int render_height(); @@ -133,14 +130,6 @@ uint32_t render_elapsed_ms(void); void render_sleep_ms(uint32_t delay); uint8_t render_has_gl(void); -audio_source *render_audio_source(uint64_t master_clock, uint64_t sample_divider, uint8_t channels); -void render_audio_source_gaindb(audio_source *src, float gain); -void render_audio_adjust_clock(audio_source *src, uint64_t master_clock, uint64_t sample_divider); -void render_put_mono_sample(audio_source *src, int16_t value); -void render_put_stereo_sample(audio_source *src, int16_t left, int16_t right); -void render_pause_source(audio_source *src); -void render_resume_source(audio_source *src); -void render_free_source(audio_source *src); void render_config_updated(void); void render_set_gl_context_handlers(ui_render_fun destroy, ui_render_fun create); void render_set_ui_render_fun(ui_render_fun); diff -r 96323d73b8ab -r fa4745d42093 render_audio.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/render_audio.c Thu Jun 20 23:28:18 2019 -0700 @@ -0,0 +1,375 @@ +#include +#include +#include +#include +#include "render_audio.h" +#include "util.h" +#include "config.h" +#include "blastem.h" + +static uint8_t output_channels; +static uint32_t buffer_samples, sample_rate; +static uint32_t missing_count; + +static audio_source *audio_sources[8]; +static audio_source *inactive_audio_sources[8]; +static uint8_t num_audio_sources; +static uint8_t num_inactive_audio_sources; + +static float overall_gain_mult, *mix_buf; +static int sample_size; + +typedef void (*conv_func)(float *samples, void *vstream, int sample_count); + +static void convert_null(float *samples, void *vstream, int sample_count) +{ + memset(vstream, 0, sample_count * sample_size); +} + +static void convert_s16(float *samples, void *vstream, int sample_count) +{ + int16_t *stream = vstream; + for (int16_t *end = stream + sample_count; stream < end; stream++, samples++) + { + float sample = *samples; + int16_t out_sample; + if (sample >= 1.0f) { + out_sample = 0x7FFF; + } else if (sample <= -1.0f) { + out_sample = -0x8000; + } else { + out_sample = sample * 0x7FFF; + } + *stream = out_sample; + } +} + +static void clamp_f32(float *samples, void *vstream, int sample_count) +{ + for (; sample_count > 0; sample_count--, samples++) + { + float sample = *samples; + if (sample > 1.0f) { + sample = 1.0f; + } else if (sample < -1.0f) { + sample = -1.0f; + } + *samples = sample; + } +} + +static int32_t mix_f32(audio_source *audio, float *stream, int samples) +{ + float *end = stream + samples; + int16_t *src = audio->front; + uint32_t i = audio->read_start; + uint32_t i_end = audio->read_end; + float *cur = stream; + float gain_mult = audio->gain_mult * overall_gain_mult; + size_t first_add = output_channels > 1 ? 1 : 0, second_add = output_channels > 1 ? output_channels - 1 : 1; + if (audio->num_channels == 1) { + while (cur < end && i != i_end) + { + *cur += gain_mult * ((float)src[i]) / 0x7FFF; + cur += first_add; + *cur += gain_mult * ((float)src[i++]) / 0x7FFF; + cur += second_add; + i &= audio->mask; + } + } else { + while(cur < end && i != i_end) + { + *cur += gain_mult * ((float)src[i++]) / 0x7FFF; + cur += first_add; + *cur += gain_mult * ((float)src[i++]) / 0x7FFF; + cur += second_add; + i &= audio->mask; + } + } + if (!render_is_audio_sync()) { + audio->read_start = i; + } + if (cur != end) { + debug_message("Underflow of %d samples, read_start: %d, read_end: %d, mask: %X\n", (int)(end-cur)/2, audio->read_start, audio->read_end, audio->mask); + return (cur-end)/2; + } else { + return ((i_end - i) & audio->mask) / audio->num_channels; + } +} + +static conv_func convert; + + +int mix_and_convert(unsigned char *byte_stream, int len, int *min_remaining_out) +{ + int samples = len / sample_size; + float *mix_dest = mix_buf ? mix_buf : (float *)byte_stream; + memset(mix_dest, 0, samples * sizeof(float)); + int min_buffered = INT_MAX; + int min_remaining_buffer = INT_MAX; + for (uint8_t i = 0; i < num_audio_sources; i++) + { + int buffered = mix_f32(audio_sources[i], mix_dest, samples); + int remaining = (audio_sources[i]->mask + 1) / audio_sources[i]->num_channels - buffered; + min_buffered = buffered < min_buffered ? buffered : min_buffered; + min_remaining_buffer = remaining < min_remaining_buffer ? remaining : min_remaining_buffer; + audio_sources[i]->front_populated = 0; + render_buffer_consumed(audio_sources[i]); + } + convert(mix_dest, byte_stream, samples); + if (min_remaining_out) { + *min_remaining_out = min_remaining_buffer; + } + return min_buffered; +} + +uint8_t all_sources_ready(void) +{ + uint8_t num_populated = 0; + num_populated = 0; + for (uint8_t i = 0; i < num_audio_sources; i++) + { + if (audio_sources[i]->front_populated) { + num_populated++; + } + } + return num_populated == num_audio_sources; +} + +#define BUFFER_INC_RES 0x40000000UL + +void render_audio_adjust_clock(audio_source *src, uint64_t master_clock, uint64_t sample_divider) +{ + src->buffer_inc = ((BUFFER_INC_RES * (uint64_t)sample_rate) / master_clock) * sample_divider; +} + +void render_audio_adjust_speed(float adjust_ratio) +{ + for (uint8_t i = 0; i < num_audio_sources; i++) + { + audio_sources[i]->buffer_inc = ((double)audio_sources[i]->buffer_inc) + ((double)audio_sources[i]->buffer_inc) * adjust_ratio + 0.5; + } +} + +audio_source *render_audio_source(uint64_t master_clock, uint64_t sample_divider, uint8_t channels) +{ + audio_source *ret = NULL; + uint32_t alloc_size = render_is_audio_sync() ? channels * buffer_samples : nearest_pow2(render_min_buffered() * 4 * channels); + render_lock_audio(); + if (num_audio_sources < 8) { + ret = calloc(1, sizeof(audio_source)); + ret->back = malloc(alloc_size * sizeof(int16_t)); + ret->front = render_is_audio_sync() ? malloc(alloc_size * sizeof(int16_t)) : ret->back; + ret->front_populated = 0; + ret->opaque = render_new_audio_opaque(); + ret->num_channels = channels; + audio_sources[num_audio_sources++] = ret; + } + render_unlock_audio(); + if (!ret) { + fatal_error("Too many audio sources!"); + } else { + render_audio_adjust_clock(ret, master_clock, sample_divider); + double lowpass_cutoff = get_lowpass_cutoff(config); + double rc = (1.0 / lowpass_cutoff) / (2.0 * M_PI); + ret->dt = 1.0 / ((double)master_clock / (double)(sample_divider)); + double alpha = ret->dt / (ret->dt + rc); + ret->lowpass_alpha = (int32_t)(((double)0x10000) * alpha); + ret->buffer_pos = 0; + ret->buffer_fraction = 0; + ret->last_left = ret->last_right = 0; + ret->read_start = 0; + ret->read_end = render_is_audio_sync() ? buffer_samples * channels : 0; + ret->mask = render_is_audio_sync() ? 0xFFFFFFFF : alloc_size-1; + ret->gain_mult = 1.0f; + } + render_audio_created(ret); + + return ret; +} + + +static float db_to_mult(float gain) +{ + return powf(10.0f, gain/20.0f); +} + +void render_audio_source_gaindb(audio_source *src, float gain) +{ + src->gain_mult = db_to_mult(gain); +} + +void render_pause_source(audio_source *src) +{ + uint8_t found = 0, remaining_sources; + render_lock_audio(); + for (uint8_t i = 0; i < num_audio_sources; i++) + { + if (audio_sources[i] == src) { + audio_sources[i] = audio_sources[--num_audio_sources]; + found = 1; + remaining_sources = num_audio_sources; + break; + } + } + + render_unlock_audio(); + if (found) { + render_source_paused(src, remaining_sources); + } + inactive_audio_sources[num_inactive_audio_sources++] = src; +} + +void render_resume_source(audio_source *src) +{ + render_lock_audio(); + if (num_audio_sources < 8) { + audio_sources[num_audio_sources++] = src; + } + render_unlock_audio(); + for (uint8_t i = 0; i < num_inactive_audio_sources; i++) + { + if (inactive_audio_sources[i] == src) { + inactive_audio_sources[i] = inactive_audio_sources[--num_inactive_audio_sources]; + } + } + render_source_resumed(src); +} + +void render_free_source(audio_source *src) +{ + render_pause_source(src); + + free(src->front); + if (render_is_audio_sync()) { + free(src->back); + render_free_audio_opaque(src->opaque); + } + free(src); + num_inactive_audio_sources--; +} + +static int16_t lowpass_sample(audio_source *src, int16_t last, int16_t current) +{ + int32_t tmp = current * src->lowpass_alpha + last * (0x10000 - src->lowpass_alpha); + current = tmp >> 16; + return current; +} + +static void interp_sample(audio_source *src, int16_t last, int16_t current) +{ + int64_t tmp = last * ((src->buffer_fraction << 16) / src->buffer_inc); + tmp += current * (0x10000 - ((src->buffer_fraction << 16) / src->buffer_inc)); + src->back[src->buffer_pos++] = tmp >> 16; +} + +static uint32_t sync_samples; +void render_put_mono_sample(audio_source *src, int16_t value) +{ + value = lowpass_sample(src, src->last_left, value); + src->buffer_fraction += src->buffer_inc; + uint32_t base = render_is_audio_sync() ? 0 : src->read_end; + while (src->buffer_fraction > BUFFER_INC_RES) + { + src->buffer_fraction -= BUFFER_INC_RES; + interp_sample(src, src->last_left, value); + + if (((src->buffer_pos - base) & src->mask) >= sync_samples) { + render_do_audio_ready(src); + } + src->buffer_pos &= src->mask; + } + src->last_left = value; +} + +void render_put_stereo_sample(audio_source *src, int16_t left, int16_t right) +{ + left = lowpass_sample(src, src->last_left, left); + right = lowpass_sample(src, src->last_right, right); + src->buffer_fraction += src->buffer_inc; + uint32_t base = render_is_audio_sync() ? 0 : src->read_end; + while (src->buffer_fraction > BUFFER_INC_RES) + { + src->buffer_fraction -= BUFFER_INC_RES; + + interp_sample(src, src->last_left, left); + interp_sample(src, src->last_right, right); + + if (((src->buffer_pos - base) & src->mask)/2 >= sync_samples) { + render_do_audio_ready(src); + } + src->buffer_pos &= src->mask; + } + src->last_left = left; + src->last_right = right; +} + +static void update_source(audio_source *src, double rc, uint8_t sync_changed) +{ + double alpha = src->dt / (src->dt + rc); + int32_t lowpass_alpha = (int32_t)(((double)0x10000) * alpha); + src->lowpass_alpha = lowpass_alpha; + if (sync_changed) { + uint32_t alloc_size = render_is_audio_sync() ? src->num_channels * buffer_samples : nearest_pow2(render_min_buffered() * 4 * src->num_channels); + src->back = realloc(src->back, alloc_size * sizeof(int16_t)); + if (render_is_audio_sync()) { + src->front = malloc(alloc_size * sizeof(int16_t)); + } else { + free(src->front); + src->front = src->back; + } + src->mask = render_is_audio_sync() ? 0xFFFFFFFF : alloc_size-1; + src->read_start = 0; + src->read_end = render_is_audio_sync() ? buffer_samples * src->num_channels : 0; + src->buffer_pos = 0; + } +} + +uint8_t old_audio_sync; +void render_audio_initialized(render_audio_format format, uint32_t rate, uint8_t channels, uint32_t buffer_size, int sample_size_in) +{ + sample_rate = rate; + output_channels = channels; + buffer_samples = buffer_size; + sample_size = sample_size_in; + if (mix_buf) { + free(mix_buf); + mix_buf = NULL; + } + switch(format) + { + case RENDER_AUDIO_S16: + convert = convert_s16; + mix_buf = calloc(output_channels * buffer_samples, sizeof(float)); + break; + case RENDER_AUDIO_FLOAT: + convert = clamp_f32; + break; + case RENDER_AUDIO_UNKNOWN: + convert = convert_null; + mix_buf = calloc(output_channels * buffer_samples, sizeof(float)); + break; + } + uint32_t syncs = render_audio_syncs_per_sec(); + if (syncs) { + sync_samples = rate / syncs; + } else { + sync_samples = buffer_samples; + } + char * gain_str = tern_find_path(config, "audio\0gain\0", TVAL_PTR).ptrval; + overall_gain_mult = db_to_mult(gain_str ? atof(gain_str) : 0.0f); + uint8_t sync_changed = old_audio_sync != render_is_audio_sync(); + old_audio_sync = render_is_audio_sync(); + double lowpass_cutoff = get_lowpass_cutoff(config); + double rc = (1.0 / lowpass_cutoff) / (2.0 * M_PI); + render_lock_audio(); + for (uint8_t i = 0; i < num_audio_sources; i++) + { + update_source(audio_sources[i], rc, sync_changed); + } + render_unlock_audio(); + for (uint8_t i = 0; i < num_inactive_audio_sources; i++) + { + update_source(inactive_audio_sources[i], rc, sync_changed); + } +} \ No newline at end of file diff -r 96323d73b8ab -r fa4745d42093 render_audio.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/render_audio.h Thu Jun 20 23:28:18 2019 -0700 @@ -0,0 +1,57 @@ +#ifndef RENDER_AUDIO_H_ +#define RENDER_AUDIO_H_ + +#include +typedef enum { + RENDER_AUDIO_S16, + RENDER_AUDIO_FLOAT, + RENDER_AUDIO_UNKNOWN +} render_audio_format; + +typedef struct { + void *opaque; + int16_t *front; + int16_t *back; + double dt; + uint64_t buffer_fraction; + uint64_t buffer_inc; + float gain_mult; + uint32_t buffer_pos; + uint32_t read_start; + uint32_t read_end; + uint32_t lowpass_alpha; + uint32_t mask; + int16_t last_left; + int16_t last_right; + uint8_t num_channels; + uint8_t front_populated; +} audio_source; + +//public interface +audio_source *render_audio_source(uint64_t master_clock, uint64_t sample_divider, uint8_t channels); +void render_audio_source_gaindb(audio_source *src, float gain); +void render_audio_adjust_clock(audio_source *src, uint64_t master_clock, uint64_t sample_divider); +void render_put_mono_sample(audio_source *src, int16_t value); +void render_put_stereo_sample(audio_source *src, int16_t left, int16_t right); +void render_pause_source(audio_source *src); +void render_resume_source(audio_source *src); +void render_free_source(audio_source *src); +//interface for render backends +void render_audio_initialized(render_audio_format format, uint32_t rate, uint8_t channels, uint32_t buffer_size, int sample_size); +int mix_and_convert(unsigned char *byte_stream, int len, int *min_remaining_out); +uint8_t all_sources_ready(void); +void render_audio_adjust_speed(float adjust_ratio); +//to be implemented by render backend +uint8_t render_is_audio_sync(void); +void render_buffer_consumed(audio_source *src); +void *render_new_audio_opaque(void); +void render_free_audio_opaque(void *opaque); +void render_lock_audio(void); +void render_unlock_audio(void); +uint32_t render_min_buffered(void); +uint32_t render_audio_syncs_per_sec(void); +void render_audio_created(audio_source *src); +void render_do_audio_ready(audio_source *src); +void render_source_paused(audio_source *src, uint8_t remaining_sources); +void render_source_resumed(audio_source *src); +#endif //RENDER_AUDIO_H_ diff -r 96323d73b8ab -r fa4745d42093 render_sdl.c --- a/render_sdl.c Sun Jun 02 23:16:15 2019 -0700 +++ b/render_sdl.c Thu Jun 20 23:28:18 2019 -0700 @@ -46,151 +46,41 @@ static uint32_t last_frame = 0; -static uint8_t output_channels; -static uint32_t buffer_samples, sample_rate; -static uint32_t missing_count; - static SDL_mutex * audio_mutex; static SDL_cond * audio_ready; static uint8_t quitting = 0; -struct audio_source { - SDL_cond *cond; - int16_t *front; - int16_t *back; - double dt; - uint64_t buffer_fraction; - uint64_t buffer_inc; - float gain_mult; - uint32_t buffer_pos; - uint32_t read_start; - uint32_t read_end; - uint32_t lowpass_alpha; - uint32_t mask; - int16_t last_left; - int16_t last_right; - uint8_t num_channels; - uint8_t front_populated; -}; - -static audio_source *audio_sources[8]; -static audio_source *inactive_audio_sources[8]; -static uint8_t num_audio_sources; -static uint8_t num_inactive_audio_sources; static uint8_t sync_to_audio; static uint32_t min_buffered; -static float overall_gain_mult, *mix_buf; -static int sample_size; -typedef void (*conv_func)(float *samples, void *vstream, int sample_count); - -static void convert_null(float *samples, void *vstream, int sample_count) -{ - memset(vstream, 0, sample_count * sample_size); -} - -static void convert_s16(float *samples, void *vstream, int sample_count) +uint32_t render_min_buffered(void) { - int16_t *stream = vstream; - for (int16_t *end = stream + sample_count; stream < end; stream++, samples++) - { - float sample = *samples; - int16_t out_sample; - if (sample >= 1.0f) { - out_sample = 0x7FFF; - } else if (sample <= -1.0f) { - out_sample = -0x8000; - } else { - out_sample = sample * 0x7FFF; - } - *stream = out_sample; - } + return min_buffered; } -static void clamp_f32(float *samples, void *vstream, int sample_count) +uint8_t render_is_audio_sync(void) { - for (; sample_count > 0; sample_count--, samples++) - { - float sample = *samples; - if (sample > 1.0f) { - sample = 1.0f; - } else if (sample < -1.0f) { - sample = -1.0f; - } - *samples = sample; - } + return sync_to_audio; } -static int32_t mix_f32(audio_source *audio, float *stream, int samples) +void render_buffer_consumed(audio_source *src) { - float *end = stream + samples; - int16_t *src = audio->front; - uint32_t i = audio->read_start; - uint32_t i_end = audio->read_end; - float *cur = stream; - float gain_mult = audio->gain_mult * overall_gain_mult; - size_t first_add = output_channels > 1 ? 1 : 0, second_add = output_channels > 1 ? output_channels - 1 : 1; - if (audio->num_channels == 1) { - while (cur < end && i != i_end) - { - *cur += gain_mult * ((float)src[i]) / 0x7FFF; - cur += first_add; - *cur += gain_mult * ((float)src[i++]) / 0x7FFF; - cur += second_add; - i &= audio->mask; - } - } else { - while(cur < end && i != i_end) - { - *cur += gain_mult * ((float)src[i++]) / 0x7FFF; - cur += first_add; - *cur += gain_mult * ((float)src[i++]) / 0x7FFF; - cur += second_add; - i &= audio->mask; - } - } - if (!sync_to_audio) { - audio->read_start = i; - } - if (cur != end) { - debug_message("Underflow of %d samples, read_start: %d, read_end: %d, mask: %X\n", (int)(end-cur)/2, audio->read_start, audio->read_end, audio->mask); - return (cur-end)/2; - } else { - return ((i_end - i) & audio->mask) / audio->num_channels; - } + SDL_CondSignal(src->opaque); } -static conv_func convert; - static void audio_callback(void * userdata, uint8_t *byte_stream, int len) { - uint8_t num_populated; SDL_LockMutex(audio_mutex); + uint8_t all_ready; do { - num_populated = 0; - for (uint8_t i = 0; i < num_audio_sources; i++) - { - if (audio_sources[i]->front_populated) { - num_populated++; - } - } - if (!quitting && num_populated < num_audio_sources) { - fflush(stdout); + all_ready = all_sources_ready(); + if (!quitting && !all_ready) { SDL_CondWait(audio_ready, audio_mutex); } - } while(!quitting && num_populated < num_audio_sources); - int samples = len / sample_size; - float *mix_dest = mix_buf ? mix_buf : (float *)byte_stream; - memset(mix_dest, 0, samples * sizeof(float)); + } while(!quitting && !all_ready); if (!quitting) { - for (uint8_t i = 0; i < num_audio_sources; i++) - { - mix_f32(audio_sources[i], mix_dest, samples); - audio_sources[i]->front_populated = 0; - SDL_CondSignal(audio_sources[i]->cond); - } + mix_and_convert(byte_stream, len, NULL); } - convert(mix_dest, byte_stream, samples); SDL_UnlockMutex(audio_mutex); } @@ -208,23 +98,10 @@ //underflow last frame, but main thread hasn't gotten a chance to call SDL_PauseAudio yet return; } - cur_min_buffered = 0x7FFFFFFF; - min_remaining_buffer = 0xFFFFFFFF; - float *mix_dest = mix_buf ? mix_buf : (float *)byte_stream; - int samples = len / sample_size; - memset(mix_dest, 0, samples * sizeof(float)); - for (uint8_t i = 0; i < num_audio_sources; i++) - { - - int32_t buffered = mix_f32(audio_sources[i], mix_dest, samples); - cur_min_buffered = buffered < cur_min_buffered ? buffered : cur_min_buffered; - uint32_t remaining = (audio_sources[i]->mask + 1)/audio_sources[i]->num_channels - buffered; - min_remaining_buffer = remaining < min_remaining_buffer ? remaining : min_remaining_buffer; - } - convert(mix_dest, byte_stream, samples); + cur_min_buffered = mix_and_convert(byte_stream, len, &min_remaining_buffer); } -static void lock_audio() +void render_lock_audio() { if (sync_to_audio) { SDL_LockMutex(audio_mutex); @@ -233,7 +110,7 @@ } } -static void unlock_audio() +void render_unlock_audio() { if (sync_to_audio) { SDL_UnlockMutex(audio_mutex); @@ -249,127 +126,55 @@ SDL_CondSignal(audio_ready); SDL_UnlockMutex(audio_mutex); SDL_CloseAudio(); + /* + FIXME: move this to render_audio.c if (mix_buf) { free(mix_buf); mix_buf = NULL; } + */ } -#define BUFFER_INC_RES 0x40000000UL - -void render_audio_adjust_clock(audio_source *src, uint64_t master_clock, uint64_t sample_divider) +void *render_new_audio_opaque(void) { - src->buffer_inc = ((BUFFER_INC_RES * (uint64_t)sample_rate) / master_clock) * sample_divider; + return SDL_CreateCond(); } -audio_source *render_audio_source(uint64_t master_clock, uint64_t sample_divider, uint8_t channels) +void render_free_audio_opaque(void *opaque) { - audio_source *ret = NULL; - uint32_t alloc_size = sync_to_audio ? channels * buffer_samples : nearest_pow2(min_buffered * 4 * channels); - lock_audio(); - if (num_audio_sources < 8) { - ret = malloc(sizeof(audio_source)); - ret->back = malloc(alloc_size * sizeof(int16_t)); - ret->front = sync_to_audio ? malloc(alloc_size * sizeof(int16_t)) : ret->back; - ret->front_populated = 0; - ret->cond = SDL_CreateCond(); - ret->num_channels = channels; - audio_sources[num_audio_sources++] = ret; - } - unlock_audio(); - if (!ret) { - fatal_error("Too many audio sources!"); - } else { - render_audio_adjust_clock(ret, master_clock, sample_divider); - double lowpass_cutoff = get_lowpass_cutoff(config); - double rc = (1.0 / lowpass_cutoff) / (2.0 * M_PI); - ret->dt = 1.0 / ((double)master_clock / (double)(sample_divider)); - double alpha = ret->dt / (ret->dt + rc); - ret->lowpass_alpha = (int32_t)(((double)0x10000) * alpha); - ret->buffer_pos = 0; - ret->buffer_fraction = 0; - ret->last_left = ret->last_right = 0; - ret->read_start = 0; - ret->read_end = sync_to_audio ? buffer_samples * channels : 0; - ret->mask = sync_to_audio ? 0xFFFFFFFF : alloc_size-1; - ret->gain_mult = 1.0f; - } + SDL_DestroyCond(opaque); +} + +void render_audio_created(audio_source *source) +{ if (sync_to_audio && SDL_GetAudioStatus() == SDL_AUDIO_PAUSED) { SDL_PauseAudio(0); } - return ret; -} - -static float db_to_mult(float gain) -{ - return powf(10.0f, gain/20.0f); -} - -void render_audio_source_gaindb(audio_source *src, float gain) -{ - src->gain_mult = db_to_mult(gain); } -void render_pause_source(audio_source *src) +void render_source_paused(audio_source *src, uint8_t remaining_sources) { - uint8_t need_pause = 0; - lock_audio(); - for (uint8_t i = 0; i < num_audio_sources; i++) - { - if (audio_sources[i] == src) { - audio_sources[i] = audio_sources[--num_audio_sources]; - if (sync_to_audio) { - SDL_CondSignal(audio_ready); - } - break; - } - } - if (!num_audio_sources) { - need_pause = 1; - } - unlock_audio(); - if (need_pause) { - SDL_PauseAudio(1); + if (sync_to_audio) { + SDL_CondSignal(audio_ready); } - inactive_audio_sources[num_inactive_audio_sources++] = src; + if (!remaining_sources) { + SDL_PauseAudio(0); + } } -void render_resume_source(audio_source *src) +void render_source_resumed(audio_source *src) { - lock_audio(); - if (num_audio_sources < 8) { - audio_sources[num_audio_sources++] = src; - } - unlock_audio(); - for (uint8_t i = 0; i < num_inactive_audio_sources; i++) - { - if (inactive_audio_sources[i] == src) { - inactive_audio_sources[i] = inactive_audio_sources[--num_inactive_audio_sources]; - } - } if (sync_to_audio) { SDL_PauseAudio(0); } } -void render_free_source(audio_source *src) -{ - render_pause_source(src); - - free(src->front); - if (sync_to_audio) { - free(src->back); - SDL_DestroyCond(src->cond); - } - free(src); -} -static uint32_t sync_samples; -static void do_audio_ready(audio_source *src) +void render_do_audio_ready(audio_source *src) { if (sync_to_audio) { SDL_LockMutex(audio_mutex); while (src->front_populated) { - SDL_CondWait(src->cond, audio_mutex); + SDL_CondWait(src->opaque, audio_mutex); } int16_t *tmp = src->front; src->front = src->back; @@ -390,60 +195,6 @@ } } -static int16_t lowpass_sample(audio_source *src, int16_t last, int16_t current) -{ - int32_t tmp = current * src->lowpass_alpha + last * (0x10000 - src->lowpass_alpha); - current = tmp >> 16; - return current; -} - -static void interp_sample(audio_source *src, int16_t last, int16_t current) -{ - int64_t tmp = last * ((src->buffer_fraction << 16) / src->buffer_inc); - tmp += current * (0x10000 - ((src->buffer_fraction << 16) / src->buffer_inc)); - src->back[src->buffer_pos++] = tmp >> 16; -} - -void render_put_mono_sample(audio_source *src, int16_t value) -{ - value = lowpass_sample(src, src->last_left, value); - src->buffer_fraction += src->buffer_inc; - uint32_t base = sync_to_audio ? 0 : src->read_end; - while (src->buffer_fraction > BUFFER_INC_RES) - { - src->buffer_fraction -= BUFFER_INC_RES; - interp_sample(src, src->last_left, value); - - if (((src->buffer_pos - base) & src->mask) >= sync_samples) { - do_audio_ready(src); - } - src->buffer_pos &= src->mask; - } - src->last_left = value; -} - -void render_put_stereo_sample(audio_source *src, int16_t left, int16_t right) -{ - left = lowpass_sample(src, src->last_left, left); - right = lowpass_sample(src, src->last_right, right); - src->buffer_fraction += src->buffer_inc; - uint32_t base = sync_to_audio ? 0 : src->read_end; - while (src->buffer_fraction > BUFFER_INC_RES) - { - src->buffer_fraction -= BUFFER_INC_RES; - - interp_sample(src, src->last_left, left); - interp_sample(src, src->last_right, right); - - if (((src->buffer_pos - base) & src->mask)/2 >= sync_samples) { - do_audio_ready(src); - } - src->buffer_pos &= src->mask; - } - src->last_left = left; - src->last_right = right; -} - static SDL_Joystick * joysticks[MAX_JOYSTICKS]; static int joystick_sdl_index[MAX_JOYSTICKS]; static uint8_t joystick_index_locked[MAX_JOYSTICKS]; @@ -1113,6 +864,7 @@ static int source_frame_count; static int frame_repeat[60]; +static uint32_t sample_rate; static void init_audio() { SDL_AudioSpec desired, actual; @@ -1137,27 +889,20 @@ if (SDL_OpenAudio(&desired, &actual) < 0) { fatal_error("Unable to open SDL audio: %s\n", SDL_GetError()); } - buffer_samples = actual.samples; sample_rate = actual.freq; - output_channels = actual.channels; debug_message("Initialized audio at frequency %d with a %d sample buffer, ", actual.freq, actual.samples); - sample_size = SDL_AUDIO_BITSIZE(actual.format) / 8; + render_audio_format format = RENDER_AUDIO_UNKNOWN; if (actual.format == AUDIO_S16SYS) { debug_message("signed 16-bit int format\n"); - convert = convert_s16; - mix_buf = calloc(output_channels * buffer_samples, sizeof(float)); + format = RENDER_AUDIO_S16; } else if (actual.format == AUDIO_F32SYS) { debug_message("32-bit float format\n"); - convert = clamp_f32; - mix_buf = NULL; + format = RENDER_AUDIO_FLOAT; } else { debug_message("unsupported format %X\n", actual.format); warning("Unsupported audio sample format: %X\n", actual.format); - convert = convert_null; - mix_buf = calloc(output_channels * buffer_samples, sizeof(float)); } - char * gain_str = tern_find_path(config, "audio\0gain\0", TVAL_PTR).ptrval; - overall_gain_mult = db_to_mult(gain_str ? atof(gain_str) : 0.0f); + render_audio_initialized(format, actual.freq, actual.channels, actual.samples, SDL_AUDIO_BITSIZE(actual.format) / 8); } void window_setup(void) @@ -1352,26 +1097,6 @@ } #include static int in_toggle; -static void update_source(audio_source *src, double rc, uint8_t sync_changed) -{ - double alpha = src->dt / (src->dt + rc); - int32_t lowpass_alpha = (int32_t)(((double)0x10000) * alpha); - src->lowpass_alpha = lowpass_alpha; - if (sync_changed) { - uint32_t alloc_size = sync_to_audio ? src->num_channels * buffer_samples : nearest_pow2(min_buffered * 4 * src->num_channels); - src->back = realloc(src->back, alloc_size * sizeof(int16_t)); - if (sync_to_audio) { - src->front = malloc(alloc_size * sizeof(int16_t)); - } else { - free(src->front); - src->front = src->back; - } - src->mask = sync_to_audio ? 0xFFFFFFFF : alloc_size-1; - src->read_start = 0; - src->read_end = sync_to_audio ? buffer_samples * src->num_channels : 0; - src->buffer_pos = 0; - } -} void render_config_updated(void) { @@ -1438,18 +1163,6 @@ init_audio(); render_set_video_standard(video_standard); - double lowpass_cutoff = get_lowpass_cutoff(config); - double rc = (1.0 / lowpass_cutoff) / (2.0 * M_PI); - lock_audio(); - for (uint8_t i = 0; i < num_audio_sources; i++) - { - update_source(audio_sources[i], rc, old_sync_to_audio != sync_to_audio); - } - unlock_audio(); - for (uint8_t i = 0; i < num_inactive_audio_sources; i++) - { - update_source(inactive_audio_sources[i], rc, old_sync_to_audio != sync_to_audio); - } drain_events(); in_toggle = 0; if (!was_paused) { @@ -1462,6 +1175,12 @@ return main_window; } +uint32_t render_audio_syncs_per_sec(void) +{ + //sync samples with audio thread approximately every 8 lines when doing sync to video + return sync_to_audio ? 0 : source_hz * (video_standard == VID_PAL ? 313 : 262) / 8; +} + void render_set_video_standard(vid_std std) { video_standard = std; @@ -1491,8 +1210,6 @@ } source_frame = 0; source_frame_count = frame_repeat[0]; - //sync samples with audio thread approximately every 8 lines - sync_samples = sync_to_audio ? buffer_samples : 8 * sample_rate / (source_hz * (std == VID_PAL ? 313 : 262)); max_repeat++; min_buffered = (((float)max_repeat * (float)sample_rate/(float)source_hz)/* / (float)buffer_samples*/);// + 0.9999; //min_buffered *= buffer_samples; @@ -1808,10 +1525,8 @@ } if (adjust_ratio != 0.0f) { average_change = 0; - for (uint8_t i = 0; i < num_audio_sources; i++) - { - audio_sources[i]->buffer_inc = ((double)audio_sources[i]->buffer_inc) + ((double)audio_sources[i]->buffer_inc) * adjust_ratio + 0.5; - } + render_audio_adjust_speed(adjust_ratio); + } while (source_frame_count > 0) { @@ -1909,7 +1624,7 @@ return overscan_top[video_standard]; } -void render_wait_quit(vdp_context * context) +void render_wait_quit(void) { SDL_Event event; while(SDL_WaitEvent(&event)) { @@ -2077,16 +1792,6 @@ need_ui_fb_resize = 1; } -uint32_t render_audio_buffer() -{ - return buffer_samples; -} - -uint32_t render_sample_rate() -{ - return sample_rate; -} - void render_errorbox(char *title, char *message) { SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, title, message, NULL); diff -r 96323d73b8ab -r fa4745d42093 vdp.c --- a/vdp.c Sun Jun 02 23:16:15 2019 -0700 +++ b/vdp.c Thu Jun 20 23:28:18 2019 -0700 @@ -144,7 +144,7 @@ context->cur_buffer = FRAMEBUFFER_ODD; context->fb = render_get_framebuffer(FRAMEBUFFER_ODD, &context->output_pitch); } - context->sprite_draws = MAX_DRAWS; + context->sprite_draws = MAX_SPRITES_LINE; context->fifo_write = 0; context->fifo_read = -1; context->regs[REG_HINT] = context->hint_counter = 0xFF; @@ -269,40 +269,63 @@ static void render_sprite_cells(vdp_context * context) { + if (context->cur_slot > MAX_SPRITES_LINE) { + context->cur_slot--; + return; + } + if (context->cur_slot < 0) { + return; + } sprite_draw * d = context->sprite_draw_list + context->cur_slot; context->serial_address = d->address; - if (context->cur_slot >= context->sprite_draws) { - - uint16_t dir; - int16_t x; - if (d->h_flip) { - x = d->x_pos + 7; - dir = -1; - } else { - x = d->x_pos; - dir = 1; + uint16_t dir; + int16_t x; + if (d->h_flip) { + x = d->x_pos + 7 + 8 * (d->width - 1); + dir = -1; + } else { + x = d->x_pos; + dir = 1; + } + if (d->x_pos) { + context->flags |= FLAG_CAN_MASK; + if (!(context->flags & FLAG_MASKED)) { + x -= 128; + //printf("Draw Slot %d of %d, Rendering sprite cell from %X to x: %d\n", context->cur_slot, context->sprite_draws, d->address, x); + + for (uint16_t address = d->address; address != ((d->address+4) & 0xFFFF); address++) { + if (x >= 0 && x < 320) { + if (!(context->linebuf[x] & 0xF)) { + context->linebuf[x] = (context->vdpmem[address] >> 4) | d->pal_priority; + } else if (context->vdpmem[address] >> 4) { + context->flags2 |= FLAG2_SPRITE_COLLIDE; + } + } + x += dir; + if (x >= 0 && x < 320) { + if (!(context->linebuf[x] & 0xF)) { + context->linebuf[x] = (context->vdpmem[address] & 0xF) | d->pal_priority; + } else if (context->vdpmem[address] & 0xF) { + context->flags2 |= FLAG2_SPRITE_COLLIDE; + } + } + x += dir; + } } - //printf("Draw Slot %d of %d, Rendering sprite cell from %X to x: %d\n", context->cur_slot, context->sprite_draws, d->address, x); - context->cur_slot--; - for (uint16_t address = d->address; address != ((d->address+4) & 0xFFFF); address++) { - if (x >= 0 && x < 320) { - if (!(context->linebuf[x] & 0xF)) { - context->linebuf[x] = (context->vdpmem[address] >> 4) | d->pal_priority; - } else if (context->vdpmem[address] >> 4) { - context->flags2 |= FLAG2_SPRITE_COLLIDE; - } - } - x += dir; - if (x >= 0 && x < 320) { - if (!(context->linebuf[x] & 0xF)) { - context->linebuf[x] = (context->vdpmem[address] & 0xF) | d->pal_priority; - } else if (context->vdpmem[address] & 0xF) { - context->flags2 |= FLAG2_SPRITE_COLLIDE; - } - } - x += dir; + } else if (context->flags & FLAG_CAN_MASK) { + context->flags |= FLAG_MASKED; + context->flags &= ~FLAG_CAN_MASK; + } + if (d->width) { + d->width--; + } + if (d->width) { + d->address += d->height * 4; + if (!d->h_flip) { + d->x_pos += 8; } } else { + d->x_pos = 0; context->cur_slot--; } } @@ -695,47 +718,13 @@ } else { address = ((tileinfo & 0x7FF) << 5) + row * 4; } - int16_t x = ((context->vdpmem[att_addr+ 2] & 0x3) << 8 | context->vdpmem[att_addr + 3]) & 0x1FF; - if (x) { - context->flags |= FLAG_CAN_MASK; - } else if(context->flags & (FLAG_CAN_MASK | FLAG_DOT_OFLOW)) { - context->flags |= FLAG_MASKED; - } - - context->flags &= ~FLAG_DOT_OFLOW; - int16_t i; - if (context->flags & FLAG_MASKED) { - for (i=0; i < width && context->sprite_draws; i++) { - --context->sprite_draws; - context->sprite_draw_list[context->sprite_draws].x_pos = -128; - context->sprite_draw_list[context->sprite_draws].address = address + i * height * 4; - } - } else { - x -= 128; - int16_t base_x = x; - int16_t dir; - if (tileinfo & MAP_BIT_H_FLIP) { - x += (width-1) * 8; - dir = -8; - } else { - dir = 8; - } - //printf("Sprite %d | x: %d, y: %d, width: %d, height: %d, pal_priority: %X, row: %d, tile addr: %X\n", context->sprite_info_list[context->cur_slot].index, x, context->sprite_info_list[context->cur_slot].y, width, height, pal_priority, row, address); - for (i=0; i < width && context->sprite_draws; i++, x += dir) { - --context->sprite_draws; - context->sprite_draw_list[context->sprite_draws].address = address + i * height * 4; - context->sprite_draw_list[context->sprite_draws].x_pos = x; - context->sprite_draw_list[context->sprite_draws].pal_priority = pal_priority; - context->sprite_draw_list[context->sprite_draws].h_flip = (tileinfo & MAP_BIT_H_FLIP) ? 1 : 0; - } - } - //Used to be i < width - //TODO: Confirm this is the right condition on hardware - if (!context->sprite_draws) { - context->flags |= FLAG_DOT_OFLOW; - } - } else { - context->flags |= FLAG_DOT_OFLOW; + context->sprite_draws--; + context->sprite_draw_list[context->sprite_draws].x_pos = ((context->vdpmem[att_addr+ 2] & 0x3) << 8 | context->vdpmem[att_addr + 3]) & 0x1FF; + context->sprite_draw_list[context->sprite_draws].address = address; + context->sprite_draw_list[context->sprite_draws].pal_priority = pal_priority; + context->sprite_draw_list[context->sprite_draws].h_flip = (tileinfo & MAP_BIT_H_FLIP) ? 1 : 0; + context->sprite_draw_list[context->sprite_draws].width = width; + context->sprite_draw_list[context->sprite_draws].height = height; } } context->cur_slot++; @@ -2581,6 +2570,9 @@ CHECK_LIMIT SPRITE_RENDER_H40(254) case 255: + if (context->cur_slot >= 0 && context->sprite_draw_list[context->cur_slot].x_pos) { + context->flags |= FLAG_DOT_OFLOW; + } render_map_3(context); scan_sprite_table(context->vcounter, context);//Just a guess CHECK_LIMIT @@ -2592,8 +2584,7 @@ //so we set cur_slot to slot_counter and let it wrap around to //the beginning of the list context->cur_slot = context->slot_counter; - context->sprite_draws = MAX_DRAWS; - context->flags &= (~FLAG_CAN_MASK & ~FLAG_MASKED); + context->sprite_draws = MAX_SPRITES_LINE; CHECK_LIMIT COLUMN_RENDER_BLOCK(2, 1) COLUMN_RENDER_BLOCK(4, 9) @@ -2626,7 +2617,7 @@ //sprite render to line buffer starts case 163: OUTPUT_PIXEL(163) - context->cur_slot = MAX_DRAWS-1; + context->cur_slot = MAX_SPRITES_LINE-1; memset(context->linebuf, 0, LINEBUF_SIZE); render_border_garbage( context, @@ -2634,6 +2625,7 @@ context->tmp_buf_a, context->buf_a_off, context->col_1 ); + context->flags &= ~FLAG_MASKED; render_sprite_cells(context); CHECK_LIMIT case 164: @@ -2782,6 +2774,9 @@ CHECK_LIMIT SPRITE_RENDER_H32(250) case 251: + if (context->cur_slot >= 0 && context->sprite_draw_list[context->cur_slot].x_pos) { + context->flags |= FLAG_DOT_OFLOW; + } render_map_1(context); scan_sprite_table(context->vcounter, context);//Just a guess CHECK_LIMIT @@ -2807,8 +2802,7 @@ //filled rather than the number of available slots //context->slot_counter = MAX_SPRITES_LINE - context->slot_counter; context->cur_slot = context->slot_counter; - context->sprite_draws = MAX_DRAWS_H32; - context->flags &= (~FLAG_CAN_MASK & ~FLAG_MASKED); + context->sprite_draws = MAX_SPRITES_LINE_H32; CHECK_LIMIT COLUMN_RENDER_BLOCK(2, 1) COLUMN_RENDER_BLOCK(4, 9) @@ -2838,7 +2832,7 @@ //sprite render to line buffer starts case 131: OUTPUT_PIXEL(131) - context->cur_slot = MAX_DRAWS_H32-1; + context->cur_slot = MAX_SPRITES_LINE_H32-1; memset(context->linebuf, 0, LINEBUF_SIZE); render_border_garbage( context, @@ -2846,6 +2840,7 @@ context->tmp_buf_a, context->buf_a_off, context->col_1 ); + context->flags &= ~FLAG_MASKED; render_sprite_cells(context); CHECK_LIMIT case 132: @@ -3072,7 +3067,7 @@ buf_clear_slot = 163; index_reset_slot = 167; bg_end_slot = BG_START_SLOT + LINEBUF_SIZE/2; - max_draws = MAX_DRAWS-1; + max_draws = MAX_SPRITES_LINE-1; max_sprites = MAX_SPRITES_LINE; index_reset_value = 0x80; vint_slot = VINT_SLOT_H40; @@ -3081,7 +3076,7 @@ jump_dest = 229; } else { bg_end_slot = BG_START_SLOT + (256+HORIZ_BORDER)/2; - max_draws = MAX_DRAWS_H32-1; + max_draws = MAX_SPRITES_LINE_H32-1; max_sprites = MAX_SPRITES_LINE_H32; buf_clear_slot = 128; index_reset_slot = 132; @@ -3934,8 +3929,10 @@ } } +#define VDP_STATE_VERSION 1 void vdp_serialize(vdp_context *context, serialize_buffer *buf) { + save_int8(buf, VDP_STATE_VERSION); save_int8(buf, VRAM_SIZE / 1024);//VRAM size in KB, needed for future proofing save_buffer8(buf, context->vdpmem, VRAM_SIZE); save_buffer16(buf, context->cram, CRAM_SIZE); @@ -3990,13 +3987,15 @@ save_int8(buf, context->sprite_draws); save_int8(buf, context->slot_counter); save_int8(buf, context->cur_slot); - for (int i = 0; i < MAX_DRAWS; i++) + for (int i = 0; i < MAX_SPRITES_LINE; i++) { sprite_draw *draw = context->sprite_draw_list + i; save_int16(buf, draw->address); save_int16(buf, draw->x_pos); save_int8(buf, draw->pal_priority); save_int8(buf, draw->h_flip); + save_int8(buf, draw->width); + save_int8(buf, draw->height); } for (int i = 0; i < MAX_SPRITES_LINE; i++) { @@ -4015,7 +4014,17 @@ void vdp_deserialize(deserialize_buffer *buf, void *vcontext) { vdp_context *context = vcontext; - uint8_t vramk = load_int8(buf); + uint8_t version = load_int8(buf); + uint8_t vramk; + if (version == 64) { + vramk = version; + version = 0; + } else { + vramk = load_int8(buf); + } + if (version > VDP_STATE_VERSION) { + warning("Save state has VDP version %d, but this build only understands versions %d and lower", version, VDP_STATE_VERSION); + } load_buffer8(buf, context->vdpmem, (vramk * 1024) <= VRAM_SIZE ? vramk * 1024 : VRAM_SIZE); if ((vramk * 1024) > VRAM_SIZE) { buf->cur_pos += (vramk * 1024) - VRAM_SIZE; @@ -4077,13 +4086,50 @@ context->sprite_draws = load_int8(buf); context->slot_counter = load_int8(buf); context->cur_slot = load_int8(buf); - for (int i = 0; i < MAX_DRAWS; i++) - { - sprite_draw *draw = context->sprite_draw_list + i; - draw->address = load_int16(buf); - draw->x_pos = load_int16(buf); - draw->pal_priority = load_int8(buf); - draw->h_flip = load_int8(buf); + if (version == 0) { + int cur_draw = 0; + for (int i = 0; i < MAX_SPRITES_LINE * 2; i++) + { + if (cur_draw < MAX_SPRITES_LINE) { + sprite_draw *last = cur_draw ? context->sprite_draw_list + cur_draw - 1 : NULL; + sprite_draw *draw = context->sprite_draw_list + cur_draw++; + draw->address = load_int16(buf); + draw->x_pos = load_int16(buf); + draw->pal_priority = load_int8(buf); + draw->h_flip = load_int8(buf); + draw->width = 1; + draw->height = 8; + + if (last && last->width < 4 && last->h_flip == draw->h_flip && last->pal_priority == draw->pal_priority) { + int adjust_x = draw->x_pos + draw->h_flip ? -8 : 8; + int height = draw->address - last->address /4; + if (last->x_pos == adjust_x && ( + (last->width > 1 && height == last->height) || + (last->width == 1 && (height == 8 || height == 16 || height == 24 || height == 32)) + )) { + //current draw appears to be part of the same sprite as the last one, combine it + cur_draw--; + last->width++; + } + } + } else { + load_int16(buf); + load_int16(buf); + load_int8(buf); + load_int8(buf); + } + } + } else { + for (int i = 0; i < MAX_SPRITES_LINE; i++) + { + sprite_draw *draw = context->sprite_draw_list + i; + draw->address = load_int16(buf); + draw->x_pos = load_int16(buf); + draw->pal_priority = load_int8(buf); + draw->h_flip = load_int8(buf); + draw->width = load_int8(buf); + draw->height = load_int8(buf); + } } for (int i = 0; i < MAX_SPRITES_LINE; i++) { diff -r 96323d73b8ab -r fa4745d42093 vdp.h --- a/vdp.h Sun Jun 02 23:16:15 2019 -0700 +++ b/vdp.h Thu Jun 20 23:28:18 2019 -0700 @@ -24,8 +24,6 @@ #define LINEBUF_SIZE (320+HORIZ_BORDER) //H40 + full border #define SCROLL_BUFFER_SIZE 32 #define BORDER_BOTTOM 13 //TODO: Replace with actual value -#define MAX_DRAWS 40 -#define MAX_DRAWS_H32 32 #define MAX_DRAWS_H32_MODE4 8 #define MAX_SPRITES_LINE 20 #define MAX_SPRITES_LINE_H32 16 @@ -133,6 +131,8 @@ int16_t x_pos; uint8_t pal_priority; uint8_t h_flip; + uint8_t width; + uint8_t height; } sprite_draw; typedef struct { @@ -195,7 +195,7 @@ uint16_t hscroll_b; uint16_t h40_lines; uint16_t output_lines; - sprite_draw sprite_draw_list[MAX_DRAWS]; + sprite_draw sprite_draw_list[MAX_SPRITES_LINE]; sprite_info sprite_info_list[MAX_SPRITES_LINE]; uint8_t sat_cache[SAT_CACHE_SIZE]; uint16_t col_1; diff -r 96323d73b8ab -r fa4745d42093 ym2612.h --- a/ym2612.h Sun Jun 02 23:16:15 2019 -0700 +++ b/ym2612.h Thu Jun 20 23:28:18 2019 -0700 @@ -9,7 +9,7 @@ #include #include #include "serialize.h" -#include "render.h" +#include "render_audio.h" #define NUM_PART_REGS (0xB7-0x30) #define NUM_CHANNELS 6