mirror of
https://github.com/ekeeke/Genesis-Plus-GX.git
synced 2024-12-27 19:51:48 +01:00
+implemented Blip Buffer linear interpolation in SN76489 core
+optimized SN76489 core +added configurable SN76489 Noise boost +added savestate version check
This commit is contained in:
parent
5f3fce01ec
commit
1cfb7e6f44
20
HISTORY.txt
20
HISTORY.txt
@ -5,22 +5,26 @@ Genesis Plus GX 1.3.2 (??/??/????) (Eke-Eke)
|
|||||||
[Core]
|
[Core]
|
||||||
------
|
------
|
||||||
|
|
||||||
* modified SN76489 cut-off frequency
|
* optimized SN76489 core
|
||||||
|
* modified SN76489 cut-off frequency (thanks to Steve Snake)
|
||||||
|
* added SN76489 linear interpolation using Blip Buffer (thanks to Blargg)
|
||||||
|
* added an option to boost SN76489 Noise Channel
|
||||||
* removed now outdated Gens YM2612 core
|
* removed now outdated Gens YM2612 core
|
||||||
* improved MAME YM2612 emulation accuracy (SSG-EG, CSM mode...), thanks to Nemesis for his tests on real hardware
|
* improved YM2612 emulation accuracy (SSG-EG, CSM mode...) (thanks to Nemesis for his tests on real hardware)
|
||||||
* fixed FM context saving/loading.
|
* fixed YM2612 context saving/loading.
|
||||||
* improved sprites masking emulation: fixes 3D level in Mickey Mania, thanks to Nemesis for his test ROM.
|
* added 3 Band Equalizer for improved & fully configurable sound filtering (taken from Softdev's NeoCD Redux)
|
||||||
|
* improved sprites masking emulation: fixes 3D level in Mickey Mania (thanks to Nemesis for his test program)
|
||||||
* fixed lightgun autodetection: fixes cursor position in Lethal Enforcers II
|
* fixed lightgun autodetection: fixes cursor position in Lethal Enforcers II
|
||||||
* various code cleanup & optimization
|
* various code cleanup & optimization
|
||||||
|
|
||||||
[Gamecube/Wii]
|
[Gamecube/Wii]
|
||||||
|
|
||||||
* improved audio/video synchronization: fixes video skipping issues with 60Hz modes
|
* improved audio/video synchronization: fixes video skipping issues in 60Hz modes
|
||||||
* fixed stability issues and a few (potential) memory leaks
|
* fixed stability issues and some (potential) memory leaks
|
||||||
* added screenshot feature
|
* added internal screenshot feature
|
||||||
* improved lightgun cursors
|
* improved lightgun cursors
|
||||||
* new FONT & GUI engines: use internal IPL FONT, GX hardware & multithreading for fast rendering.
|
* new FONT & GUI engines: use internal IPL FONT, GX hardware & multithreading for fast rendering.
|
||||||
* new GUI layout: includes IR pointing, ROM snapshots, menu effects, sound effects & more (check the README for more details)
|
* new interface: incl. IR pointing, ROM snapshots, menu effects, sound effects, BGM... (check the README for more details)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -66,13 +66,14 @@ void config_default(void)
|
|||||||
strncpy(config.version,VERSION,16);
|
strncpy(config.version,VERSION,16);
|
||||||
|
|
||||||
/* sound options */
|
/* sound options */
|
||||||
config.psg_preamp = 150;
|
config.psg_preamp = 150;
|
||||||
config.fm_preamp = 100;
|
config.fm_preamp = 100;
|
||||||
config.hq_fm = 1;
|
config.hq_fm = 1;
|
||||||
config.filter = 1;
|
config.psgBoostNoise = 0;
|
||||||
config.lg = 1.0;
|
config.filter = 1;
|
||||||
config.mg = 1.0;
|
config.lg = 1.0;
|
||||||
config.hg = 1.0;
|
config.mg = 1.0;
|
||||||
|
config.hg = 1.0;
|
||||||
|
|
||||||
/* system options */
|
/* system options */
|
||||||
config.region_detect = 0;
|
config.region_detect = 0;
|
||||||
|
@ -32,6 +32,7 @@ typedef struct
|
|||||||
{
|
{
|
||||||
char version[16];
|
char version[16];
|
||||||
uint8 hq_fm;
|
uint8 hq_fm;
|
||||||
|
uint8 psgBoostNoise;
|
||||||
int32 psg_preamp;
|
int32 psg_preamp;
|
||||||
int32 fm_preamp;
|
int32 fm_preamp;
|
||||||
uint8 filter;
|
uint8 filter;
|
||||||
|
@ -120,7 +120,15 @@ static int FAT_ManageFile(char *filename, u8 direction, u8 filetype)
|
|||||||
sram.crc = crc32 (0, sram.sram, 0x10000);
|
sram.crc = crc32 (0, sram.sram, 0x10000);
|
||||||
system_reset ();
|
system_reset ();
|
||||||
}
|
}
|
||||||
else state_load(savebuffer); /* STATE */
|
else
|
||||||
|
{
|
||||||
|
/* STATE */
|
||||||
|
if (!state_load(savebuffer))
|
||||||
|
{
|
||||||
|
GUI_WaitPrompt("Error","File version is not compatible !");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sprintf (fname, "Loaded %d bytes successfully", done);
|
sprintf (fname, "Loaded %d bytes successfully", done);
|
||||||
GUI_WaitPrompt("Information",fname);
|
GUI_WaitPrompt("Information",fname);
|
||||||
|
@ -217,12 +217,13 @@ static gui_item items_options[5] =
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* Audio options menu */
|
/* Audio options menu */
|
||||||
static gui_item items_audio[7] =
|
static gui_item items_audio[8] =
|
||||||
{
|
{
|
||||||
{NULL,NULL,"High-Quality FM: LINEAR", "Setup YM2612 resampling", 52,132,276,48},
|
{NULL,NULL,"High-Quality FM: LINEAR", "Setup YM2612 resampling", 52,132,276,48},
|
||||||
|
{NULL,NULL,"PSG Noise Boost: OFF", "Boost PSG Noise Channel", 52,132,276,48},
|
||||||
{NULL,NULL,"PSG Volume: 2.50", "Adjust SN76489 output level", 52,132,276,48},
|
{NULL,NULL,"PSG Volume: 2.50", "Adjust SN76489 output level", 52,132,276,48},
|
||||||
{NULL,NULL,"FM Volume: 1.00", "Adjust YM2612 output level", 52,132,276,48},
|
{NULL,NULL,"FM Volume: 1.00", "Adjust YM2612 output level", 52,132,276,48},
|
||||||
{NULL,NULL,"Filtering: 3-BAND EQ", "Setup Audio filtering", 52,132,276,48},
|
{NULL,NULL,"Filtering: 3-BAND EQ", "Setup Audio filtering", 52,132,276,48},
|
||||||
{NULL,NULL,"Low Gain: 1.00", "Adjust EQ Low Gain", 52,132,276,48},
|
{NULL,NULL,"Low Gain: 1.00", "Adjust EQ Low Gain", 52,132,276,48},
|
||||||
{NULL,NULL,"Middle Gain: 1.00", "Adjust EQ Middle Gain", 52,132,276,48},
|
{NULL,NULL,"Middle Gain: 1.00", "Adjust EQ Middle Gain", 52,132,276,48},
|
||||||
{NULL,NULL,"High Gain: 1.00", "Adjust EQ High Gain", 52,132,276,48},
|
{NULL,NULL,"High Gain: 1.00", "Adjust EQ High Gain", 52,132,276,48},
|
||||||
@ -436,7 +437,7 @@ static gui_menu menu_audio =
|
|||||||
{
|
{
|
||||||
"Audio Settings",
|
"Audio Settings",
|
||||||
0,0,
|
0,0,
|
||||||
7,4,6,
|
8,4,6,
|
||||||
items_audio,
|
items_audio,
|
||||||
buttons_list,
|
buttons_list,
|
||||||
bg_list,
|
bg_list,
|
||||||
@ -701,21 +702,22 @@ static void soundmenu ()
|
|||||||
if (config.hq_fm == 0) sprintf (items[0].text, "High-Quality FM: OFF");
|
if (config.hq_fm == 0) sprintf (items[0].text, "High-Quality FM: OFF");
|
||||||
else if (config.hq_fm == 1) sprintf (items[0].text, "High-Quality FM: LINEAR");
|
else if (config.hq_fm == 1) sprintf (items[0].text, "High-Quality FM: LINEAR");
|
||||||
else sprintf (items[0].text, "High-Quality FM: SINC");
|
else sprintf (items[0].text, "High-Quality FM: SINC");
|
||||||
sprintf (items[1].text, "PSG Volume: %1.2f", psg_volume);
|
sprintf (items[1].text, "PSG Noise Boost: %s", config.psgBoostNoise ? "ON":"OFF");
|
||||||
sprintf (items[2].text, "FM Volume: %1.2f", (double)config.fm_preamp/100.0);
|
sprintf (items[2].text, "PSG Volume: %1.2f", psg_volume);
|
||||||
if (config.filter == 2) sprintf (items[3].text, "Filtering: 3-BAND EQ");
|
sprintf (items[3].text, "FM Volume: %1.2f", (double)config.fm_preamp/100.0);
|
||||||
else if (config.filter == 1) sprintf (items[3].text, "Filtering: LOW PASS");
|
if (config.filter == 2) sprintf (items[4].text, "Filtering: 3-BAND EQ");
|
||||||
else sprintf (items[3].text, "Filtering: OFF");
|
else if (config.filter == 1) sprintf (items[4].text, "Filtering: LOW PASS");
|
||||||
sprintf (items[4].text, "Low Gain: %1.2f", config.lg);
|
else sprintf (items[4].text, "Filtering: OFF");
|
||||||
sprintf (items[5].text, "Middle Gain: %1.2f", config.mg);
|
sprintf (items[5].text, "Low Gain: %1.2f", config.lg);
|
||||||
sprintf (items[6].text, "High Gain: %1.2f", config.hg);
|
sprintf (items[6].text, "Middle Gain: %1.2f", config.mg);
|
||||||
|
sprintf (items[7].text, "High Gain: %1.2f", config.hg);
|
||||||
|
|
||||||
GUI_InitMenu(m);
|
GUI_InitMenu(m);
|
||||||
|
|
||||||
if (config.filter < 2)
|
if (config.filter < 2)
|
||||||
m->max_items = 4;
|
m->max_items = 5;
|
||||||
else
|
else
|
||||||
m->max_items = 7;
|
m->max_items = 8;
|
||||||
|
|
||||||
GUI_SlideMenuTitle(m,strlen("Audio "));
|
GUI_SlideMenuTitle(m,strlen("Audio "));
|
||||||
|
|
||||||
@ -745,59 +747,65 @@ static void soundmenu ()
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 1:
|
case 1:
|
||||||
GUI_OptionBox(m,0,"PSG Volume",(void *)&psg_volume,0.01,0.0,5.0,0);
|
config.psgBoostNoise ^= 1;
|
||||||
sprintf (items[1].text, "PSG Volume: %1.2f", psg_volume);
|
sprintf (items[1].text, "PSG Noise Boost: %s", config.psgBoostNoise ? "ON":"OFF");
|
||||||
config.psg_preamp = (int)(psg_volume * 100.0);
|
SN76489_BoostNoise(config.psgBoostNoise);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 2:
|
case 2:
|
||||||
GUI_OptionBox(m,0,"FM Volume",(void *)&fm_volume,0.01,0.0,5.0,0);
|
GUI_OptionBox(m,0,"PSG Volume",(void *)&psg_volume,0.01,0.0,5.0,0);
|
||||||
sprintf (items[2].text, "FM Volume: %1.2f", (double)config.fm_preamp/100.0);
|
sprintf (items[2].text, "PSG Volume: %1.2f", psg_volume);
|
||||||
config.fm_preamp = (int)(fm_volume * 100.0);
|
config.psg_preamp = (int)(psg_volume * 100.0);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 3:
|
case 3:
|
||||||
|
GUI_OptionBox(m,0,"FM Volume",(void *)&fm_volume,0.01,0.0,5.0,0);
|
||||||
|
sprintf (items[3].text, "FM Volume: %1.2f", (double)config.fm_preamp/100.0);
|
||||||
|
config.fm_preamp = (int)(fm_volume * 100.0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 4:
|
||||||
config.filter ++;
|
config.filter ++;
|
||||||
if (config.filter > 2) config.filter = 0;
|
if (config.filter > 2) config.filter = 0;
|
||||||
if (config.filter == 2)
|
if (config.filter == 2)
|
||||||
sprintf (items[3].text, "Filtering: 3-BAND EQ");
|
sprintf (items[4].text, "Filtering: 3-BAND EQ");
|
||||||
else if (config.filter == 1)
|
else if (config.filter == 1)
|
||||||
sprintf (items[3].text, "Filtering: LOW PASS");
|
sprintf (items[4].text, "Filtering: LOW PASS");
|
||||||
else
|
else
|
||||||
sprintf (items[3].text, "Filtering: OFF");
|
sprintf (items[4].text, "Filtering: OFF");
|
||||||
|
|
||||||
if (config.filter < 2)
|
if (config.filter < 2)
|
||||||
{
|
{
|
||||||
/* reset menu selection */
|
/* reset menu selection */
|
||||||
m->offset = 0;
|
m->offset = 1;
|
||||||
m->selected = 3;
|
m->selected = 3;
|
||||||
m->max_items = 4;
|
m->max_items = 5;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* enable items */
|
/* enable items */
|
||||||
m->max_items = 7;
|
m->max_items = 8;
|
||||||
|
|
||||||
/* intialize EQ */
|
/* intialize EQ */
|
||||||
audio_init_equalizer();
|
audio_init_equalizer();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 4:
|
|
||||||
GUI_OptionBox(m,0,"Low Gain",(void *)&config.lg,0.01,0.0,2.0,0);
|
|
||||||
sprintf (items[4].text, "Low Gain: %1.2f", config.lg);
|
|
||||||
audio_set_equalizer();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 5:
|
case 5:
|
||||||
GUI_OptionBox(m,0,"Middle Gain",(void *)&config.mg,0.01,0.0,2.0,0);
|
GUI_OptionBox(m,0,"Low Gain",(void *)&config.lg,0.01,0.0,2.0,0);
|
||||||
sprintf (items[5].text, "Middle Gain: %1.2f", config.mg);
|
sprintf (items[5].text, "Low Gain: %1.2f", config.lg);
|
||||||
audio_set_equalizer();
|
audio_set_equalizer();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 6:
|
case 6:
|
||||||
|
GUI_OptionBox(m,0,"Middle Gain",(void *)&config.mg,0.01,0.0,2.0,0);
|
||||||
|
sprintf (items[6].text, "Middle Gain: %1.2f", config.mg);
|
||||||
|
audio_set_equalizer();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 7:
|
||||||
GUI_OptionBox(m,0,"High Gain",(void *)&config.hg,0.01,0.0,2.0,0);
|
GUI_OptionBox(m,0,"High Gain",(void *)&config.hg,0.01,0.0,2.0,0);
|
||||||
sprintf (items[6].text, "High Gain: %1.2f", config.hg);
|
sprintf (items[7].text, "High Gain: %1.2f", config.hg);
|
||||||
audio_set_equalizer();
|
audio_set_equalizer();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -807,7 +815,7 @@ static void soundmenu ()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m->max_items = 7;
|
m->max_items = 8;
|
||||||
GUI_DeleteMenu(m);
|
GUI_DeleteMenu(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,9 +27,9 @@
|
|||||||
|
|
||||||
#define DEFAULT_PATH "/genplus"
|
#define DEFAULT_PATH "/genplus"
|
||||||
#ifdef HW_RVL
|
#ifdef HW_RVL
|
||||||
#define VERSION "version 1.3.2W"
|
#define VERSION "version 1.3.2bW"
|
||||||
#else
|
#else
|
||||||
#define VERSION "version 1.3.2G"
|
#define VERSION "version 1.3.2bG"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* globals */
|
/* globals */
|
||||||
|
163
source/sound/blip.c
Normal file
163
source/sound/blip.c
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
/* http://www.slack.net/~ant/ */
|
||||||
|
|
||||||
|
#include "blip.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
/* Copyright (C) 2003-2008 Shay Green. This module 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
|
||||||
|
module 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 */
|
||||||
|
|
||||||
|
enum { buf_extra = 2 }; /* extra samples to save past end */
|
||||||
|
enum { time_bits = 16 }; /* bits in fraction of fixed-point sample counts */
|
||||||
|
enum { time_unit = 1 << time_bits };
|
||||||
|
enum { phase_bits = 15 }; /* bits in fraction of deltas in buffer */
|
||||||
|
enum { phase_count = 1 << phase_bits };
|
||||||
|
enum { phase_shift = time_bits - phase_bits };
|
||||||
|
|
||||||
|
typedef int buf_t; /* type of element in delta buffer */
|
||||||
|
|
||||||
|
struct blip_buffer_t
|
||||||
|
{
|
||||||
|
int factor; /* clocks to samples conversion factor */
|
||||||
|
int offset; /* fractional position of clock 0 in delta buffer */
|
||||||
|
int amp; /* current output amplitude (sum of all deltas up to now) */
|
||||||
|
int size; /* size of delta buffer */
|
||||||
|
buf_t buf [65536]; /* delta buffer, only size elements actually allocated */
|
||||||
|
};
|
||||||
|
|
||||||
|
blip_buffer_t* blip_alloc( int clock_rate, int sample_rate, int size )
|
||||||
|
{
|
||||||
|
/* Allocate space for structure and delta buffer */
|
||||||
|
blip_buffer_t* s = (blip_buffer_t*) malloc(
|
||||||
|
offsetof (blip_buffer_t, buf) + (size + buf_extra) * sizeof (buf_t) );
|
||||||
|
if ( s != NULL )
|
||||||
|
{
|
||||||
|
/* Calculate output:input ratio and convert to fixed-point */
|
||||||
|
double ratio = (double) sample_rate / clock_rate;
|
||||||
|
s->factor = (int) (ratio * time_unit + 0.5);
|
||||||
|
|
||||||
|
s->size = size;
|
||||||
|
blip_clear( s );
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
void blip_free( blip_buffer_t* s )
|
||||||
|
{
|
||||||
|
free( s );
|
||||||
|
}
|
||||||
|
|
||||||
|
void blip_clear( blip_buffer_t* s )
|
||||||
|
{
|
||||||
|
s->offset = 0;
|
||||||
|
s->amp = 0;
|
||||||
|
memset( s->buf, 0, (s->size + buf_extra) * sizeof (buf_t) );
|
||||||
|
}
|
||||||
|
|
||||||
|
void blip_add( blip_buffer_t* s, int clocks, int delta )
|
||||||
|
{
|
||||||
|
/* Convert to fixed-point time in terms of output samples */
|
||||||
|
int fixed_time = clocks * s->factor + s->offset;
|
||||||
|
|
||||||
|
/* Extract whole and fractional parts */
|
||||||
|
int index = fixed_time >> time_bits; /* whole */
|
||||||
|
int phase = fixed_time >> phase_shift & (phase_count - 1); /* fraction */
|
||||||
|
|
||||||
|
/* Split delta between first and second samples */
|
||||||
|
int second = delta * phase;
|
||||||
|
int first = delta * phase_count - second;
|
||||||
|
|
||||||
|
/* Be sure index is within buffer */
|
||||||
|
assert( index >= 0 && index+1 < s->size + buf_extra );
|
||||||
|
|
||||||
|
/* Add deltas to buffer */
|
||||||
|
s->buf [index ] += first;
|
||||||
|
s->buf [index+1] += second;
|
||||||
|
}
|
||||||
|
|
||||||
|
int blip_clocks_needed( const blip_buffer_t* s, int samples )
|
||||||
|
{
|
||||||
|
int fixed_needed;
|
||||||
|
if ( samples > s->size )
|
||||||
|
samples = s->size;
|
||||||
|
|
||||||
|
/* Fixed-point number of samples needed in addition to those in buffer */
|
||||||
|
fixed_needed = samples * time_unit - s->offset;
|
||||||
|
|
||||||
|
/* If more are needed, convert to clocks and round up */
|
||||||
|
return (fixed_needed <= 0) ? 0 : (fixed_needed - 1) / s->factor + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void blip_end_frame( blip_buffer_t* s, int clocks )
|
||||||
|
{
|
||||||
|
s->offset += clocks * s->factor;
|
||||||
|
|
||||||
|
/* Ensure time wasn't past end of buffer */
|
||||||
|
assert( blip_samples_avail( s ) <= s->size );
|
||||||
|
}
|
||||||
|
|
||||||
|
int blip_samples_avail( const blip_buffer_t* s )
|
||||||
|
{
|
||||||
|
return s->offset >> time_bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Removes n samples from buffer */
|
||||||
|
static void remove_samples( blip_buffer_t* s, int n )
|
||||||
|
{
|
||||||
|
int remain = blip_samples_avail( s ) + buf_extra - n;
|
||||||
|
|
||||||
|
s->offset -= n * time_unit;
|
||||||
|
assert( s->offset >= 0 );
|
||||||
|
|
||||||
|
/* Copy remaining samples to beginning of buffer and clear the rest */
|
||||||
|
memmove( s->buf, &s->buf [n], remain * sizeof (buf_t) );
|
||||||
|
memset( &s->buf [remain], 0, n * sizeof (buf_t) );
|
||||||
|
}
|
||||||
|
|
||||||
|
int blip_read_samples( blip_buffer_t* s, short out [], int count, int stereo )
|
||||||
|
{
|
||||||
|
/* can't read more than available */
|
||||||
|
int avail = blip_samples_avail( s );
|
||||||
|
if ( count > avail )
|
||||||
|
count = avail;
|
||||||
|
|
||||||
|
if ( count )
|
||||||
|
{
|
||||||
|
/* Sum deltas and write out */
|
||||||
|
int i;
|
||||||
|
for ( i = 0; i < count; ++i )
|
||||||
|
{
|
||||||
|
int sample;
|
||||||
|
|
||||||
|
/* Apply slight high-pass filter */
|
||||||
|
s->amp -= s->amp >> 9;
|
||||||
|
|
||||||
|
/* Add next delta */
|
||||||
|
s->amp += s->buf [i];
|
||||||
|
|
||||||
|
/* Calculate output sample */
|
||||||
|
sample = s->amp >> phase_bits;
|
||||||
|
|
||||||
|
/* Keep within 16-bit sample range */
|
||||||
|
if ( sample < -32768 ) sample = -32768;
|
||||||
|
if ( sample > +32767 ) sample = +32767;
|
||||||
|
|
||||||
|
out [i << stereo] = sample;
|
||||||
|
}
|
||||||
|
|
||||||
|
remove_samples( s, count );
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
51
source/sound/blip.h
Normal file
51
source/sound/blip.h
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
/* Fast sound synthesis buffer for use in real-time emulators of electronic
|
||||||
|
sound generator chips like those in early video game consoles. Uses linear
|
||||||
|
interpolation. Higher-quality versions are available that use sinc-based
|
||||||
|
band-limited synthesis. */
|
||||||
|
|
||||||
|
#ifndef BLIP_H
|
||||||
|
#define BLIP_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Creates a new blip_buffer with specified input clock rate, output
|
||||||
|
sample rate, and size (in samples), or returns NULL if out of memory. */
|
||||||
|
typedef struct blip_buffer_t blip_buffer_t;
|
||||||
|
blip_buffer_t* blip_alloc( int clock_rate, int sample_rate, int size );
|
||||||
|
|
||||||
|
/* Frees memory used by a blip_buffer. No effect if NULL is passed. */
|
||||||
|
void blip_free( blip_buffer_t* );
|
||||||
|
|
||||||
|
/* Removes all samples and clears buffer. */
|
||||||
|
void blip_clear( blip_buffer_t* );
|
||||||
|
|
||||||
|
/* Adds an amplitude transition of delta at specified time in source clocks.
|
||||||
|
Delta can be negative. */
|
||||||
|
void blip_add( blip_buffer_t*, int time, int delta );
|
||||||
|
|
||||||
|
/* Number of additional clocks needed until n samples will be available.
|
||||||
|
If buffer cannot even hold n samples, returns number of clocks until buffer
|
||||||
|
becomes full. */
|
||||||
|
int blip_clocks_needed( const blip_buffer_t*, int samples_needed );
|
||||||
|
|
||||||
|
/* Ends current time frame of specified duration and make its samples available
|
||||||
|
(along with any still-unread samples) for reading with read_samples(), then
|
||||||
|
begins a new time frame at the end of the current frame. */
|
||||||
|
void blip_end_frame( blip_buffer_t*, int duration );
|
||||||
|
|
||||||
|
/* Number of samples available for reading with read(). */
|
||||||
|
int blip_samples_avail( const blip_buffer_t* );
|
||||||
|
|
||||||
|
/* Reads at most n samples out of buffer into out, removing them from from
|
||||||
|
the buffer. Returns number of samples actually read and removed. If stereo is
|
||||||
|
true, increments 'out' one extra time after writing each sample, to allow
|
||||||
|
easy interleving of two channels into a stereo output buffer. */
|
||||||
|
int blip_read_samples( blip_buffer_t*, short out [], int n, int stereo );
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
@ -19,47 +19,62 @@
|
|||||||
- Removed SN76489_GetValues().
|
- Removed SN76489_GetValues().
|
||||||
- Removed some unused variables.
|
- Removed some unused variables.
|
||||||
|
|
||||||
25/04/07 Eke-Eke
|
25/04/07 Eke-Eke (Genesis Plus GX)
|
||||||
Modified for use with GenesisPlus Gamecube's port:
|
- Removed stereo GG support (unused)
|
||||||
- made SN76489_Update outputs 16bits mono samples
|
- Rade SN76489_Update outputs 16bits mono samples
|
||||||
- replaced volume table with VGM plugin's one
|
- Replaced volume table with VGM plugin's one
|
||||||
|
|
||||||
05/01/2009 Eke-Eke
|
05/01/09 Eke-Eke (Genesis Plus GX)
|
||||||
Modified Cut-Off frequency (according to Steve Snake: http://www.smspower.org/forums/viewtopic.php?t=1746)
|
- Modified Cut-Off frequency (according to Steve Snake: http://www.smspower.org/forums/viewtopic.php?t=1746)
|
||||||
|
|
||||||
|
25/05/09 Eke-Eke (Genesis Plus GX)
|
||||||
|
- Removed multichip support (unused)
|
||||||
|
- Removed alternate volume table, panning & mute support (unused)
|
||||||
|
- Removed configurable Feedback and Shift Register Width (always use Sega ones)
|
||||||
|
- Added linear resampling using Blip Buffer (Blargg's implementation: http://www.smspower.org/forums/viewtopic.php?t=11376)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "shared.h"
|
#include "shared.h"
|
||||||
|
#include "blip.h"
|
||||||
|
#include <float.h>
|
||||||
|
|
||||||
#include <float.h> // for FLT_MIN
|
/* Initial state of shift register */
|
||||||
#include <string.h> // for memcpy
|
#define NoiseInitialState 0x8000
|
||||||
|
|
||||||
#define NoiseInitialState 0x8000 /* Initial state of shift register */
|
/* Value below which PSG does not output */
|
||||||
#define PSG_CUTOFF 0x1 /* Value below which PSG does not output */
|
/*#define PSG_CUTOFF 0x6*/
|
||||||
|
#define PSG_CUTOFF 0x1
|
||||||
|
|
||||||
static const int PSGVolumeValues[2][16] = {
|
static const int PSGVolumeValues[16] =
|
||||||
|
{
|
||||||
/* These values are taken from a real SMS2's output */
|
/* These values are taken from a real SMS2's output */
|
||||||
{892,892,892,760,623,497,404,323,257,198,159,123,96,75,60,0}, /* I can't remember why 892... :P some scaling I did at some point */
|
/*{892,892,892,760,623,497,404,323,257,198,159,123,96,75,60,0}, *//* I can't remember why 892... :P some scaling I did at some point */
|
||||||
/* these values are true volumes for 2dB drops at each step (multiply previous by 10^-0.1), normalised at 760 */
|
/* these values are true volumes for 2dB drops at each step (multiply previous by 10^-0.1), normalised at 760 */
|
||||||
{1516,1205,957,760,603,479,381,303,240,191,152,120,96,76,60,0}
|
1516,1205,957,760,603,479,381,303,240,191,152,120,96,76,60,0
|
||||||
};
|
};
|
||||||
|
|
||||||
static SN76489_Context SN76489[MAX_SN76489];
|
static SN76489_Context SN76489;
|
||||||
|
|
||||||
void SN76489_Init(int which, int PSGClockValue, int SamplingRate)
|
void SN76489_Init(int PSGClockValue, int SamplingRate)
|
||||||
{
|
{
|
||||||
SN76489_Context *p = &SN76489[which];
|
SN76489_Context *p = &SN76489;
|
||||||
p->dClock=(float)(PSGClockValue)/16.0/(float)SamplingRate;
|
|
||||||
SN76489_Config(which, MUTE_ALLON, VOL_FULL, FB_SEGAVDP, SRW_SEGAVDP, 1);
|
/* first unallocate memory */
|
||||||
SN76489_Reset(which);
|
SN76489_Shutdown();
|
||||||
|
|
||||||
|
/* SamplingRate*16 instead of PSGClockValue/16 since division would lose some
|
||||||
|
precision. blip_alloc doesn't care about the absolute sampling rate, just the
|
||||||
|
ratio to clock rate. */
|
||||||
|
p->blip_buffer = blip_alloc(PSGClockValue, SamplingRate * 16, SamplingRate / 4);
|
||||||
|
|
||||||
|
SN76489_Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SN76489_Reset(int which)
|
void SN76489_Reset()
|
||||||
{
|
{
|
||||||
SN76489_Context *p = &SN76489[which];
|
SN76489_Context *p = &SN76489;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
p->PSGStereo = 0xFF;
|
|
||||||
|
|
||||||
for(i = 0; i <= 3; i++)
|
for(i = 0; i <= 3; i++)
|
||||||
{
|
{
|
||||||
/* Initialise PSG state */
|
/* Initialise PSG state */
|
||||||
@ -73,11 +88,8 @@ void SN76489_Reset(int which)
|
|||||||
/* Set flip-flops to 1 */
|
/* Set flip-flops to 1 */
|
||||||
p->ToneFreqPos[i] = 1;
|
p->ToneFreqPos[i] = 1;
|
||||||
|
|
||||||
/* Set intermediate positions to do-not-use value */
|
/* Clear current amplitudes in Blip delta buffer */
|
||||||
p->IntermediatePos[i] = FLT_MIN;
|
p->chan_amp[i] = 0;
|
||||||
|
|
||||||
/* Set panning to centre */
|
|
||||||
p->panning[0]=127;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
p->LatchedRegister=0;
|
p->LatchedRegister=0;
|
||||||
@ -85,38 +97,30 @@ void SN76489_Reset(int which)
|
|||||||
/* Initialise noise generator */
|
/* Initialise noise generator */
|
||||||
p->NoiseShiftRegister=NoiseInitialState;
|
p->NoiseShiftRegister=NoiseInitialState;
|
||||||
|
|
||||||
/* Zero clock */
|
/* Clear Blip delta buffer */
|
||||||
p->Clock=0;
|
blip_clear(p->blip_buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SN76489_Shutdown(void)
|
void SN76489_Shutdown(void)
|
||||||
{
|
{
|
||||||
|
SN76489_Context *p = &SN76489;
|
||||||
|
if (p->blip_buffer) blip_free(p->blip_buffer);
|
||||||
|
p->blip_buffer = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SN76489_Config(int which, int mute, int volume, int feedback, int sr_width, int boost_noise)
|
void SN76489_SetContext(uint8 *data)
|
||||||
{
|
{
|
||||||
SN76489_Context *p = &SN76489[which];
|
memcpy(&SN76489, data, sizeof(SN76489_Context));
|
||||||
|
|
||||||
p->Mute = mute;
|
|
||||||
p->VolumeArray = volume;
|
|
||||||
p->WhiteNoiseFeedback = feedback;
|
|
||||||
p->SRWidth = sr_width;
|
|
||||||
p->BoostNoise = boost_noise;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SN76489_SetContext(int which, uint8 *data)
|
void SN76489_GetContext(uint8 *data)
|
||||||
{
|
{
|
||||||
memcpy(&SN76489[which], data, sizeof(SN76489_Context));
|
memcpy(data, &SN76489, sizeof(SN76489_Context));
|
||||||
}
|
}
|
||||||
|
|
||||||
void SN76489_GetContext(int which, uint8 *data)
|
uint8 *SN76489_GetContextPtr(void)
|
||||||
{
|
{
|
||||||
memcpy(data, &SN76489[which], sizeof(SN76489_Context));
|
return (uint8 *)&SN76489;
|
||||||
}
|
|
||||||
|
|
||||||
uint8 *SN76489_GetContextPtr(int which)
|
|
||||||
{
|
|
||||||
return (uint8 *)&SN76489[which];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int SN76489_GetContextSize(void)
|
int SN76489_GetContextSize(void)
|
||||||
@ -124,9 +128,9 @@ int SN76489_GetContextSize(void)
|
|||||||
return sizeof(SN76489_Context);
|
return sizeof(SN76489_Context);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SN76489_Write(int which, int data)
|
void SN76489_Write(int data)
|
||||||
{
|
{
|
||||||
SN76489_Context *p = &SN76489[which];
|
SN76489_Context *p = &SN76489;
|
||||||
|
|
||||||
if (data&0x80)
|
if (data&0x80)
|
||||||
{
|
{
|
||||||
@ -165,137 +169,146 @@ void SN76489_Write(int which, int data)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SN76489_GGStereoWrite(int which, int data)
|
/* Updates channel amplitude in delta buffer. Call whenever amplitude might have changed. */
|
||||||
|
static inline void UpdateChanAmplitude(SN76489_Context* chip, int i, int time)
|
||||||
{
|
{
|
||||||
SN76489_Context *p = &SN76489[which];
|
/* Build stereo result into buffer */
|
||||||
p->PSGStereo=data;
|
int buffer = chip->Channels[i];
|
||||||
}
|
|
||||||
|
|
||||||
void SN76489_Update(int which, INT16 *buffer, int length)
|
/* Update amplitudes in left and right buffers */
|
||||||
{
|
int delta = buffer - chip->chan_amp[i];
|
||||||
SN76489_Context *p = &SN76489[which];
|
if (delta != 0)
|
||||||
int i, j;
|
|
||||||
|
|
||||||
for(j = 0; j < length; j++)
|
|
||||||
{
|
{
|
||||||
/* update output */
|
chip->chan_amp[i] = buffer;
|
||||||
for (i=0;i<=2;++i)
|
blip_add(chip->blip_buffer, time, delta);
|
||||||
if (p->IntermediatePos[i]!=FLT_MIN)
|
|
||||||
p->Channels[i]=(short)((p->Mute >> i & 0x1)*PSGVolumeValues[p->VolumeArray][p->Registers[2*i+1]]*p->IntermediatePos[i]);
|
|
||||||
else
|
|
||||||
p->Channels[i]=(p->Mute >> i & 0x1)*PSGVolumeValues[p->VolumeArray][p->Registers[2*i+1]]*p->ToneFreqPos[i];
|
|
||||||
|
|
||||||
p->Channels[3]=(short)((p->Mute >> 3 & 0x1)*PSGVolumeValues[p->VolumeArray][p->Registers[7]]*(p->NoiseShiftRegister & 0x1));
|
|
||||||
|
|
||||||
if (p->BoostNoise) p->Channels[3]<<=1; /* double noise volume */
|
|
||||||
buffer[j] =0;
|
|
||||||
for (i=0;i<=3;++i) buffer[j] += p->Channels[i];
|
|
||||||
|
|
||||||
/* update tone */
|
|
||||||
p->Clock+=p->dClock;
|
|
||||||
p->NumClocksForSample=(int)p->Clock; /* truncates */
|
|
||||||
p->Clock-=p->NumClocksForSample; /* remove integer part */
|
|
||||||
/* Looks nicer in Delphi... */
|
|
||||||
/* Clock:=Clock+p->dClock; */
|
|
||||||
/* NumClocksForSample:=Trunc(Clock); */
|
|
||||||
/* Clock:=Frac(Clock); */
|
|
||||||
|
|
||||||
/* Decrement tone channel counters */
|
|
||||||
for (i=0;i<=2;++i)
|
|
||||||
p->ToneFreqVals[i]-=p->NumClocksForSample;
|
|
||||||
|
|
||||||
/* Noise channel: match to tone2 or decrement its counter */
|
|
||||||
if (p->NoiseFreq==0x80) p->ToneFreqVals[3]=p->ToneFreqVals[2];
|
|
||||||
else p->ToneFreqVals[3]-=p->NumClocksForSample;
|
|
||||||
|
|
||||||
/* Tone channels: */
|
|
||||||
for (i=0;i<=2;++i) {
|
|
||||||
if (p->ToneFreqVals[i]<=0) { /* If it gets below 0... */
|
|
||||||
if (p->Registers[i*2]>PSG_CUTOFF) {
|
|
||||||
/* Calculate how much of the sample is + and how much is - */
|
|
||||||
/* Go to floating point and include the clock fraction for extreme accuracy :D */
|
|
||||||
/* Store as long int, maybe it's faster? I'm not very good at this */
|
|
||||||
p->IntermediatePos[i]=(p->NumClocksForSample-p->Clock+2*p->ToneFreqVals[i])*p->ToneFreqPos[i]/(p->NumClocksForSample+p->Clock);
|
|
||||||
p->ToneFreqPos[i]=-p->ToneFreqPos[i]; /* Flip the flip-flop */
|
|
||||||
} else {
|
|
||||||
p->ToneFreqPos[i]=1; /* stuck value */
|
|
||||||
p->IntermediatePos[i]=FLT_MIN;
|
|
||||||
}
|
|
||||||
p->ToneFreqVals[i]+=p->Registers[i*2]*(p->NumClocksForSample/p->Registers[i*2]+1);
|
|
||||||
} else p->IntermediatePos[i]=FLT_MIN;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Noise channel */
|
|
||||||
if (p->ToneFreqVals[3]<=0) { /* If it gets below 0... */
|
|
||||||
p->ToneFreqPos[3]=-p->ToneFreqPos[3]; /* Flip the flip-flop */
|
|
||||||
if (p->NoiseFreq!=0x80) /* If not matching tone2, reset counter */
|
|
||||||
p->ToneFreqVals[3]+=p->NoiseFreq*(p->NumClocksForSample/p->NoiseFreq+1);
|
|
||||||
if (p->ToneFreqPos[3]==1) { /* Only once per cycle... */
|
|
||||||
int Feedback;
|
|
||||||
if (p->Registers[6]&0x4) { /* White noise */
|
|
||||||
/* Calculate parity of fed-back bits for feedback */
|
|
||||||
switch (p->WhiteNoiseFeedback) {
|
|
||||||
/* Do some optimised calculations for common (known) feedback values */
|
|
||||||
case 0x0003: /* SC-3000, BBC %00000011 */
|
|
||||||
case 0x0009: /* SMS, GG, MD %00001001 */
|
|
||||||
/* If two bits fed back, I can do Feedback=(nsr & fb) && (nsr & fb ^ fb) */
|
|
||||||
/* since that's (one or more bits set) && (not all bits set) */
|
|
||||||
Feedback=((p->NoiseShiftRegister&p->WhiteNoiseFeedback) && ((p->NoiseShiftRegister&p->WhiteNoiseFeedback)^p->WhiteNoiseFeedback));
|
|
||||||
break;
|
|
||||||
default: /* Default handler for all other feedback values */
|
|
||||||
Feedback=p->NoiseShiftRegister&p->WhiteNoiseFeedback;
|
|
||||||
Feedback^=Feedback>>8;
|
|
||||||
Feedback^=Feedback>>4;
|
|
||||||
Feedback^=Feedback>>2;
|
|
||||||
Feedback^=Feedback>>1;
|
|
||||||
Feedback&=1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else /* Periodic noise */
|
|
||||||
Feedback=p->NoiseShiftRegister&1;
|
|
||||||
|
|
||||||
p->NoiseShiftRegister=(p->NoiseShiftRegister>>1) | (Feedback << (p->SRWidth-1));
|
|
||||||
|
|
||||||
/* Original code: */
|
|
||||||
/* p->NoiseShiftRegister=(p->NoiseShiftRegister>>1) | ((p->Registers[6]&0x4?((p->NoiseShiftRegister&0x9) && (p->NoiseShiftRegister&0x9^0x9)):p->NoiseShiftRegister&1)<<15); */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*void SN76489_UpdateOne(int which, int *l, int *r)
|
/* Updates tone amplitude in delta buffer. Call whenever amplitude might have changed. */
|
||||||
|
static inline void UpdateToneAmplitude(SN76489_Context* chip, int i, int time)
|
||||||
{
|
{
|
||||||
INT16 tl,tr;
|
/* Tone channels */
|
||||||
INT16 *buff[2]={&tl,&tr};
|
chip->Channels[i]= PSGVolumeValues[chip->Registers[2 * i + 1]] * chip->ToneFreqPos[i];
|
||||||
SN76489_Update(which,buff,1);
|
|
||||||
*l=tl;
|
|
||||||
*r=tr;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
int SN76489_GetMute(int which)
|
UpdateChanAmplitude(chip, i, time);
|
||||||
{
|
|
||||||
return SN76489[which].Mute;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SN76489_SetMute(int which, int val)
|
/* Updates noise amplitude in delta buffer. Call whenever amplitude might have changed. */
|
||||||
|
static inline void UpdateNoiseAmplitude(SN76489_Context* chip, int time)
|
||||||
{
|
{
|
||||||
SN76489[which].Mute=val;
|
/* Noise channel */
|
||||||
|
chip->Channels[3] = PSGVolumeValues[chip->Registers[7]] * ( chip->NoiseShiftRegister & 0x1 );
|
||||||
|
|
||||||
|
/* Boost noise volume */
|
||||||
|
chip->Channels[3] <<= chip->BoostNoise;
|
||||||
|
|
||||||
|
UpdateChanAmplitude(chip, 3, time);
|
||||||
}
|
}
|
||||||
|
|
||||||
int SN76489_GetVolType(int which)
|
/* Runs tone channel for clock_length clocks */
|
||||||
|
static inline void RunTone(SN76489_Context* chip, int i, int clock_length)
|
||||||
{
|
{
|
||||||
return SN76489[which].VolumeArray;
|
int time;
|
||||||
|
|
||||||
|
/* Update in case a register changed etc. */
|
||||||
|
UpdateToneAmplitude(chip, i, 0);
|
||||||
|
|
||||||
|
/* Time of next transition */
|
||||||
|
time = chip->ToneFreqVals[i];
|
||||||
|
|
||||||
|
/* Process any transitions that occur within clocks we're running */
|
||||||
|
while (time < clock_length)
|
||||||
|
{
|
||||||
|
if (chip->Registers[i*2]>PSG_CUTOFF) {
|
||||||
|
/* Flip the flip-flop */
|
||||||
|
chip->ToneFreqPos[i] = -chip->ToneFreqPos[i];
|
||||||
|
} else {
|
||||||
|
/* stuck value */
|
||||||
|
chip->ToneFreqPos[i] = 1;
|
||||||
|
}
|
||||||
|
UpdateToneAmplitude(chip, i, time);
|
||||||
|
|
||||||
|
/* Advance to time of next transition */
|
||||||
|
time += chip->Registers[i*2] + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calculate new value for register, now that next transition is past number of clocks we're running */
|
||||||
|
chip->ToneFreqVals[i] = time - clock_length;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SN76489_SetVolType(int which, int val)
|
/* Runs noise channel for clock_length clocks */
|
||||||
|
static inline void RunNoise(SN76489_Context* chip, int clock_length)
|
||||||
{
|
{
|
||||||
SN76489[which].VolumeArray=val;
|
int time;
|
||||||
|
|
||||||
|
/* Noise channel: match to tone2 if in slave mode */
|
||||||
|
int NoiseFreq = chip->NoiseFreq;
|
||||||
|
if (NoiseFreq == 0x80)
|
||||||
|
{
|
||||||
|
NoiseFreq = chip->Registers[2*2];
|
||||||
|
chip->ToneFreqVals[3] = chip->ToneFreqVals[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update in case a register changed etc. */
|
||||||
|
UpdateNoiseAmplitude(chip, 0);
|
||||||
|
|
||||||
|
/* Time of next transition */
|
||||||
|
time = chip->ToneFreqVals[3];
|
||||||
|
|
||||||
|
/* Process any transitions that occur within clocks we're running */
|
||||||
|
while ( time < clock_length )
|
||||||
|
{
|
||||||
|
/* Flip the flip-flop */
|
||||||
|
chip->ToneFreqPos[3] = -chip->ToneFreqPos[3];
|
||||||
|
if (chip->ToneFreqPos[3] == 1) {
|
||||||
|
/* On the positive edge of the square wave (only once per cycle) */
|
||||||
|
int Feedback;
|
||||||
|
if ( chip->Registers[6] & 0x4 ) {
|
||||||
|
/* White noise */
|
||||||
|
/* Calculate parity of fed-back bits for feedback */
|
||||||
|
/* Do some optimised calculations for common (known) feedback values */
|
||||||
|
/* If two bits fed back, I can do Feedback=(nsr & fb) && (nsr & fb ^ fb) */
|
||||||
|
/* since that's (one or more bits set) && (not all bits set) */
|
||||||
|
Feedback = ( ( chip->NoiseShiftRegister & FB_SEGAVDP)
|
||||||
|
&& ( (chip->NoiseShiftRegister & FB_SEGAVDP) ^ FB_SEGAVDP) );
|
||||||
|
} else /* Periodic noise */
|
||||||
|
Feedback=chip->NoiseShiftRegister&1;
|
||||||
|
|
||||||
|
chip->NoiseShiftRegister=(chip->NoiseShiftRegister>>1) | (Feedback << (SRW_SEGAVDP-1));
|
||||||
|
UpdateNoiseAmplitude(chip, time);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Advance to time of next transition */
|
||||||
|
time += NoiseFreq + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calculate new value for register, now that next transition is past number of clocks we're running */
|
||||||
|
chip->ToneFreqVals[3] = time - clock_length;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SN76489_SetPanning(int which, int ch0, int ch1, int ch2, int ch3)
|
void SN76489_Update(INT16 *buffer, int length)
|
||||||
{
|
{
|
||||||
SN76489[which].panning[0]=ch0;
|
int i;
|
||||||
SN76489[which].panning[1]=ch1;
|
|
||||||
SN76489[which].panning[2]=ch2;
|
SN76489_Context *p = &SN76489;
|
||||||
SN76489[which].panning[3]=ch3;
|
|
||||||
|
/* Determine how many clocks we need to run until 'length' samples are available */
|
||||||
|
int clock_length = blip_clocks_needed(p->blip_buffer, length);
|
||||||
|
|
||||||
|
/* Run noise first, since it might use current value of third tone frequency counter */
|
||||||
|
RunNoise(p, clock_length);
|
||||||
|
|
||||||
|
/* Run tone channels */
|
||||||
|
for( i = 0; i <= 2; ++i )
|
||||||
|
RunTone(p, i, clock_length);
|
||||||
|
|
||||||
|
/* Read samples into output buffer */
|
||||||
|
blip_end_frame(p->blip_buffer,clock_length);
|
||||||
|
blip_read_samples(p->blip_buffer,buffer,length,0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SN76489_BoostNoise(int boost)
|
||||||
|
{
|
||||||
|
SN76489.BoostNoise = boost;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -19,86 +19,61 @@
|
|||||||
- Removed SN76489_GetValues().
|
- Removed SN76489_GetValues().
|
||||||
- Removed some unused variables.
|
- Removed some unused variables.
|
||||||
|
|
||||||
25/04/07 Eke-Eke
|
25/04/07 Eke-Eke (Genesis Plus GX)
|
||||||
Modified for use with GenesisPlus Gamecube's port:
|
- Removed stereo GG support (unused)
|
||||||
- made SN76489_Update outputs 16bits mono samples
|
- Rade SN76489_Update outputs 16bits mono samples
|
||||||
- replaced volume table with VGM plugin's one
|
- Replaced volume table with VGM plugin's one
|
||||||
|
|
||||||
|
05/01/09 Eke-Eke (Genesis Plus GX)
|
||||||
|
- Modified Cut-Off frequency (according to Steve Snake: http://www.smspower.org/forums/viewtopic.php?t=1746)
|
||||||
|
|
||||||
|
25/05/09 Eke-Eke (Genesis Plus GX)
|
||||||
|
- Removed multichip support (unused)
|
||||||
|
- Removed alternate volume table, panning & mute support (unused)
|
||||||
|
- Removed configurable Feedback and Shift Register Width (always use Sega ones)
|
||||||
|
- Added linear resampling using Blip Buffer (Blargg's implementation: http://www.smspower.org/forums/viewtopic.php?t=11376)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _SN76489_H_
|
#ifndef _SN76489_H_
|
||||||
#define _SN76489_H_
|
#define _SN76489_H_
|
||||||
|
|
||||||
#define MAX_SN76489 1
|
/* SN76489 clone in Sega's VDP chips (315-5124, 315-5246, 315-5313, Game Gear) */
|
||||||
|
#define FB_SEGAVDP 0x0009
|
||||||
/*
|
#define SRW_SEGAVDP 16
|
||||||
More testing is needed to find and confirm feedback patterns for
|
|
||||||
SN76489 variants and compatible chips.
|
|
||||||
*/
|
|
||||||
enum feedback_patterns {
|
|
||||||
FB_BBCMICRO = 0x8005, /* Texas Instruments TMS SN76489N (original) from BBC Micro computer */
|
|
||||||
FB_SC3000 = 0x0006, /* Texas Instruments TMS SN76489AN (rev. A) from SC-3000H computer */
|
|
||||||
FB_SEGAVDP = 0x0009, /* SN76489 clone in Sega's VDP chips (315-5124, 315-5246, 315-5313, Game Gear) */
|
|
||||||
};
|
|
||||||
|
|
||||||
enum sr_widths {
|
|
||||||
SRW_SC3000BBCMICRO = 15,
|
|
||||||
SRW_SEGAVDP = 16
|
|
||||||
};
|
|
||||||
|
|
||||||
enum volume_modes {
|
|
||||||
VOL_TRUNC = 0, /* Volume levels 13-15 are identical */
|
|
||||||
VOL_FULL = 1, /* Volume levels 13-15 are unique */
|
|
||||||
};
|
|
||||||
|
|
||||||
enum mute_values {
|
|
||||||
MUTE_ALLOFF = 0, /* All channels muted */
|
|
||||||
MUTE_TONE1 = 1, /* Tone 1 mute control */
|
|
||||||
MUTE_TONE2 = 2, /* Tone 2 mute control */
|
|
||||||
MUTE_TONE3 = 4, /* Tone 3 mute control */
|
|
||||||
MUTE_NOISE = 8, /* Noise mute control */
|
|
||||||
MUTE_ALLON = 15, /* All channels enabled */
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
int Mute; // per-channel muting
|
/* Configuration */
|
||||||
int VolumeArray;
|
int BoostNoise; // double noise volume when non-zero
|
||||||
int BoostNoise; // double noise volume when non-zero
|
|
||||||
|
|
||||||
/* Variables */
|
|
||||||
float Clock;
|
|
||||||
float dClock;
|
|
||||||
int PSGStereo;
|
|
||||||
int NumClocksForSample;
|
|
||||||
int WhiteNoiseFeedback;
|
|
||||||
int SRWidth;
|
|
||||||
|
|
||||||
/* PSG registers: */
|
|
||||||
int Registers[8]; /* Tone, vol x4 */
|
|
||||||
int LatchedRegister;
|
|
||||||
int NoiseShiftRegister;
|
|
||||||
int NoiseFreq; /* Noise channel signal generator frequency */
|
|
||||||
|
|
||||||
/* Output calculation variables */
|
|
||||||
int ToneFreqVals[4]; /* Frequency register values (counters) */
|
|
||||||
int ToneFreqPos[4]; /* Frequency channel flip-flops */
|
|
||||||
int Channels[4]; /* Value of each channel, before stereo is applied */
|
|
||||||
float IntermediatePos[4]; /* intermediate values used at boundaries between + and - (does not need double accuracy)*/
|
|
||||||
|
|
||||||
int panning[4]; /* fake stereo - 0..127..254 */
|
/* PSG registers: */
|
||||||
|
int Registers[8]; /* Tone, vol x4 */
|
||||||
|
int LatchedRegister;
|
||||||
|
int NoiseShiftRegister;
|
||||||
|
int NoiseFreq; /* Noise channel signal generator frequency */
|
||||||
|
|
||||||
|
/* Output calculation variables */
|
||||||
|
int ToneFreqVals[4]; /* Frequency register values (counters) */
|
||||||
|
int ToneFreqPos[4]; /* Frequency channel flip-flops */
|
||||||
|
int Channels[4]; /* Value of each channel, before stereo is applied */
|
||||||
|
|
||||||
|
/* Blip-Buffer variables */
|
||||||
|
struct blip_buffer_t* blip_buffer; /* delta resampler */
|
||||||
|
int chan_amp[4]; /* current channel amplitudes in delta buffers */
|
||||||
|
|
||||||
} SN76489_Context;
|
} SN76489_Context;
|
||||||
|
|
||||||
/* Function prototypes */
|
/* Function prototypes */
|
||||||
extern void SN76489_Init(int which, int PSGClockValue, int SamplingRate);
|
extern void SN76489_Init(int PSGClockValue, int SamplingRate);
|
||||||
extern void SN76489_Reset(int which);
|
extern void SN76489_Reset(void);
|
||||||
extern void SN76489_Config(int which, int mute, int volume, int feedback, int sw_width, int boost_noise);
|
extern void SN76489_Shutdown(void);
|
||||||
extern void SN76489_SetContext(int which, uint8 *data);
|
extern void SN76489_SetContext(uint8 *data);
|
||||||
extern void SN76489_GetContext(int which, uint8 *data);
|
extern void SN76489_GetContext(uint8 *data);
|
||||||
extern uint8 *SN76489_GetContextPtr(int which);
|
extern uint8 *SN76489_GetContextPtr(void);
|
||||||
extern int SN76489_GetContextSize(void);
|
extern int SN76489_GetContextSize(void);
|
||||||
extern void SN76489_Write(int which, int data);
|
extern void SN76489_Write(int data);
|
||||||
extern void SN76489_Update(int which, INT16 *buffer, int length);
|
extern void SN76489_Update(INT16 *buffer, int length);
|
||||||
|
extern void SN76489_BoostNoise(int boost);
|
||||||
|
|
||||||
#endif /* _SN76489_H_ */
|
#endif /* _SN76489_H_ */
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@ static inline void psg_update()
|
|||||||
if(snd.psg.curStage - snd.psg.lastStage > 0)
|
if(snd.psg.curStage - snd.psg.lastStage > 0)
|
||||||
{
|
{
|
||||||
int16 *tempBuffer = snd.psg.buffer + snd.psg.lastStage;
|
int16 *tempBuffer = snd.psg.buffer + snd.psg.lastStage;
|
||||||
SN76489_Update (0, tempBuffer, snd.psg.curStage - snd.psg.lastStage);
|
SN76489_Update(tempBuffer, snd.psg.curStage - snd.psg.lastStage);
|
||||||
snd.psg.lastStage = snd.psg.curStage;
|
snd.psg.lastStage = snd.psg.curStage;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -74,15 +74,11 @@ void sound_init(int rate)
|
|||||||
|
|
||||||
/* YM2612 is emulated at original frequency (VLCK/144) */
|
/* YM2612 is emulated at original frequency (VLCK/144) */
|
||||||
if (config.hq_fm)
|
if (config.hq_fm)
|
||||||
{
|
|
||||||
m68cycles_per_sample[0] = 144;
|
m68cycles_per_sample[0] = 144;
|
||||||
}
|
|
||||||
|
|
||||||
/* initialize sound chips */
|
/* initialize sound chips */
|
||||||
SN76489_Init(0, (int)zclk, rate);
|
SN76489_Init((int)zclk,rate);
|
||||||
SN76489_Config(0, MUTE_ALLON, VOL_FULL, FB_SEGAVDP, SRW_SEGAVDP, 0);
|
YM2612Init((int)vclk,rate);
|
||||||
|
|
||||||
YM2612Init ((int)vclk, rate);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void sound_update(int fm_len, int psg_len)
|
void sound_update(int fm_len, int psg_len)
|
||||||
@ -127,5 +123,5 @@ void psg_write(unsigned int cpu, unsigned int data)
|
|||||||
{
|
{
|
||||||
snd.psg.curStage = psg_sample_cnt(cpu);
|
snd.psg.curStage = psg_sample_cnt(cpu);
|
||||||
psg_update();
|
psg_update();
|
||||||
SN76489_Write(0, data);
|
SN76489_Write(data);
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
#include "shared.h"
|
#include "shared.h"
|
||||||
|
|
||||||
|
|
||||||
static unsigned char state[STATE_SIZE];
|
static unsigned char state[STATE_SIZE];
|
||||||
|
|
||||||
#define load_param(param, size) \
|
#define load_param(param, size) \
|
||||||
@ -32,7 +33,7 @@ static unsigned char state[STATE_SIZE];
|
|||||||
memcpy(&state[bufferptr], param, size); \
|
memcpy(&state[bufferptr], param, size); \
|
||||||
bufferptr+= size;
|
bufferptr+= size;
|
||||||
|
|
||||||
void state_load(unsigned char *buffer)
|
int state_load(unsigned char *buffer)
|
||||||
{
|
{
|
||||||
/* buffer size */
|
/* buffer size */
|
||||||
int bufferptr = 0;
|
int bufferptr = 0;
|
||||||
@ -43,6 +44,14 @@ void state_load(unsigned char *buffer)
|
|||||||
outbytes = STATE_SIZE;
|
outbytes = STATE_SIZE;
|
||||||
uncompress ((Bytef *)state, &outbytes, (Bytef *)(buffer + 4), inbytes);
|
uncompress ((Bytef *)state, &outbytes, (Bytef *)(buffer + 4), inbytes);
|
||||||
|
|
||||||
|
/* version check */
|
||||||
|
char version[16];
|
||||||
|
load_param(version,16);
|
||||||
|
if (strncmp(version,STATE_VERSION,16))
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* reset system */
|
/* reset system */
|
||||||
system_reset();
|
system_reset();
|
||||||
m68k_memory_map[0].base = default_rom;
|
m68k_memory_map[0].base = default_rom;
|
||||||
@ -81,7 +90,7 @@ void state_load(unsigned char *buffer)
|
|||||||
bufferptr+= YM2612GetContextSize();
|
bufferptr+= YM2612GetContextSize();
|
||||||
|
|
||||||
// PSG
|
// PSG
|
||||||
load_param(SN76489_GetContextPtr (0),SN76489_GetContextSize ());
|
load_param(SN76489_GetContextPtr(),SN76489_GetContextSize());
|
||||||
|
|
||||||
// 68000
|
// 68000
|
||||||
uint16 tmp16;
|
uint16 tmp16;
|
||||||
@ -108,12 +117,19 @@ void state_load(unsigned char *buffer)
|
|||||||
|
|
||||||
// Z80
|
// Z80
|
||||||
load_param(&Z80, sizeof(Z80_Regs));
|
load_param(&Z80, sizeof(Z80_Regs));
|
||||||
|
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int state_save(unsigned char *buffer)
|
int state_save(unsigned char *buffer)
|
||||||
{
|
{
|
||||||
/* buffer size */
|
/* buffer size */
|
||||||
int bufferptr = 0;
|
int bufferptr = 16;
|
||||||
|
|
||||||
|
/* version string */
|
||||||
|
char version[16];
|
||||||
|
strncpy(version,STATE_VERSION,16);
|
||||||
|
save_param(version, 16);
|
||||||
|
|
||||||
// GENESIS
|
// GENESIS
|
||||||
save_param(work_ram, sizeof(work_ram));
|
save_param(work_ram, sizeof(work_ram));
|
||||||
@ -145,7 +161,7 @@ int state_save(unsigned char *buffer)
|
|||||||
save_param(YM2612GetContextPtr(),YM2612GetContextSize());
|
save_param(YM2612GetContextPtr(),YM2612GetContextSize());
|
||||||
|
|
||||||
// PSG
|
// PSG
|
||||||
save_param(SN76489_GetContextPtr (0),SN76489_GetContextSize ());
|
save_param(SN76489_GetContextPtr(),SN76489_GetContextSize());
|
||||||
|
|
||||||
// 68000
|
// 68000
|
||||||
uint16 tmp16;
|
uint16 tmp16;
|
||||||
|
@ -23,10 +23,11 @@
|
|||||||
#ifndef _STATE_H_
|
#ifndef _STATE_H_
|
||||||
#define _STATE_H_
|
#define _STATE_H_
|
||||||
|
|
||||||
#define STATE_SIZE 0x28000
|
#define STATE_SIZE 0x28000
|
||||||
|
#define STATE_VERSION "GENPLUS-GX 1.3.2"
|
||||||
|
|
||||||
/* Function prototypes */
|
/* Function prototypes */
|
||||||
extern void state_load(unsigned char *buffer);
|
extern int state_load(unsigned char *buffer);
|
||||||
extern int state_save(unsigned char *buffer);
|
extern int state_save(unsigned char *buffer);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -272,7 +272,7 @@ void system_reset (void)
|
|||||||
vdp_reset ();
|
vdp_reset ();
|
||||||
render_reset ();
|
render_reset ();
|
||||||
io_reset();
|
io_reset();
|
||||||
SN76489_Reset(0);
|
SN76489_Reset();
|
||||||
|
|
||||||
/* Sound Buffers */
|
/* Sound Buffers */
|
||||||
if (snd.psg.buffer) memset (snd.psg.buffer, 0, SND_SIZE);
|
if (snd.psg.buffer) memset (snd.psg.buffer, 0, SND_SIZE);
|
||||||
@ -288,6 +288,7 @@ void system_shutdown (void)
|
|||||||
gen_shutdown ();
|
gen_shutdown ();
|
||||||
vdp_shutdown ();
|
vdp_shutdown ();
|
||||||
render_shutdown ();
|
render_shutdown ();
|
||||||
|
SN76489_Shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
/****************************************************************
|
/****************************************************************
|
||||||
|
Loading…
Reference in New Issue
Block a user