summaryrefslogtreecommitdiff
path: root/apps/plugins
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2010-07-01 00:26:01 +0000
committerMichael Sevakis <jethead71@rockbox.org>2010-07-01 00:26:01 +0000
commitcf73340f1e85d3b39dc452d57e28d2a9a9081051 (patch)
tree27a89d40fae023d994c7a59d3950f7af32dc00aa /apps/plugins
parent10b8e327d8c0d37b8a9f1997e33e4bf1a97bb39b (diff)
downloadrockbox-cf73340f1e85d3b39dc452d57e28d2a9a9081051.zip
rockbox-cf73340f1e85d3b39dc452d57e28d2a9a9081051.tar.gz
rockbox-cf73340f1e85d3b39dc452d57e28d2a9a9081051.tar.bz2
rockbox-cf73340f1e85d3b39dc452d57e28d2a9a9081051.tar.xz
Enable game sounds in PacBox. Sound is OFF by default but can be enabled from the menu. Enable a function for SWCODEC in the middle of the plugin API, so plugins must be made incompatible (full update).
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@27202 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/plugins')
-rw-r--r--apps/plugins/pacbox/SOURCES1
-rw-r--r--apps/plugins/pacbox/arcade.c58
-rw-r--r--apps/plugins/pacbox/arcade.h2
-rw-r--r--apps/plugins/pacbox/hardware.c3
-rw-r--r--apps/plugins/pacbox/pacbox.c142
-rw-r--r--apps/plugins/pacbox/pacbox.h3
-rw-r--r--apps/plugins/pacbox/wsg3.c148
-rw-r--r--apps/plugins/pacbox/wsg3.h124
8 files changed, 467 insertions, 14 deletions
diff --git a/apps/plugins/pacbox/SOURCES b/apps/plugins/pacbox/SOURCES
index fdda4ca..65a5cf1 100644
--- a/apps/plugins/pacbox/SOURCES
+++ b/apps/plugins/pacbox/SOURCES
@@ -2,6 +2,7 @@ arcade.c
pacbox.c
hardware.c
z80.c
+wsg3.c
#if defined(CPU_PP502x) && (LCD_WIDTH >= 288) && (LCD_HEIGHT >= 224)
pacbox_arm.S
#else
diff --git a/apps/plugins/pacbox/arcade.c b/apps/plugins/pacbox/arcade.c
index b8a0f10..ecf6d10 100644
--- a/apps/plugins/pacbox/arcade.c
+++ b/apps/plugins/pacbox/arcade.c
@@ -24,8 +24,10 @@
*
****************************************************************************/
+#include "pacbox.h"
#include "arcade.h"
#include "hardware.h"
+#include "wsg3.h"
#include <string.h>
#include "plugin.h"
@@ -92,6 +94,43 @@ enum {
FlipXY = 0x03
};
+// Namco 3-channel Wave Sound Generator wave data (8 waveforms with 32 4-bit entries each)
+static unsigned char default_sound_prom[] =
+{
+ 0x07, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0D, 0x0E,
+ 0x0E, 0x0E, 0x0D, 0x0D, 0x0C, 0x0B, 0x0A, 0x09,
+ 0x07, 0x05, 0x04, 0x03, 0x02, 0x01, 0x01, 0x00,
+ 0x00, 0x00, 0x01, 0x01, 0x02, 0x03, 0x04, 0x05,
+ 0x07, 0x0C, 0x0E, 0x0E, 0x0D, 0x0B, 0x09, 0x0A,
+ 0x0B, 0x0B, 0x0A, 0x09, 0x06, 0x04, 0x03, 0x05,
+ 0x07, 0x09, 0x0B, 0x0A, 0x08, 0x05, 0x04, 0x03,
+ 0x03, 0x04, 0x05, 0x03, 0x01, 0x00, 0x00, 0x02,
+ 0x07, 0x0A, 0x0C, 0x0D, 0x0E, 0x0D, 0x0C, 0x0A,
+ 0x07, 0x04, 0x02, 0x01, 0x00, 0x01, 0x02, 0x04,
+ 0x07, 0x0B, 0x0D, 0x0E, 0x0D, 0x0B, 0x07, 0x03,
+ 0x01, 0x00, 0x01, 0x03, 0x07, 0x0E, 0x07, 0x00,
+ 0x07, 0x0D, 0x0B, 0x08, 0x0B, 0x0D, 0x09, 0x06,
+ 0x0B, 0x0E, 0x0C, 0x07, 0x09, 0x0A, 0x06, 0x02,
+ 0x07, 0x0C, 0x08, 0x04, 0x05, 0x07, 0x02, 0x00,
+ 0x03, 0x08, 0x05, 0x01, 0x03, 0x06, 0x03, 0x01,
+ 0x00, 0x08, 0x0F, 0x07, 0x01, 0x08, 0x0E, 0x07,
+ 0x02, 0x08, 0x0D, 0x07, 0x03, 0x08, 0x0C, 0x07,
+ 0x04, 0x08, 0x0B, 0x07, 0x05, 0x08, 0x0A, 0x07,
+ 0x06, 0x08, 0x09, 0x07, 0x07, 0x08, 0x08, 0x07,
+ 0x07, 0x08, 0x06, 0x09, 0x05, 0x0A, 0x04, 0x0B,
+ 0x03, 0x0C, 0x02, 0x0D, 0x01, 0x0E, 0x00, 0x0F,
+ 0x00, 0x0F, 0x01, 0x0E, 0x02, 0x0D, 0x03, 0x0C,
+ 0x04, 0x0B, 0x05, 0x0A, 0x06, 0x09, 0x07, 0x08,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+ 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08,
+ 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+};
+
/* Putting this in IRAM actually slows down the iPods, but is good for
the Coldfire
*/
@@ -110,6 +149,12 @@ void init_PacmanMachine(int dip)
z80_reset();
rb->memset( &ram_[0x4000], 0xFF, 0x1000 );
+ /* Initialize the WSG3 */
+ wsg3_init(SoundClock);
+
+ /* Set the sound PROM to the default values for the original Namco chip */
+ wsg3_set_sound_prom(default_sound_prom);
+
/* Initialize parameters */
port1_ = 0xFF;
port2_ = 0xFF;
@@ -595,6 +640,19 @@ void renderSprites( unsigned char * buffer )
}
}
+void playSound( int * buf, int len )
+{
+ /* Clear the buffer */
+ memset( buf, 0, sizeof (int)*len);
+
+ /* Exit now if sound is disabled */
+ if( (output_devices_ & SoundEnabled) == 0 )
+ return;
+
+ /* Let the chip play the sound */
+ wsg3_play_sound( buf, len );
+}
+
/* Enables/disables the speed hack. */
int setSpeedHack( int enabled )
{
diff --git a/apps/plugins/pacbox/arcade.h b/apps/plugins/pacbox/arcade.h
index 32ec0b4..33dd6d6 100644
--- a/apps/plugins/pacbox/arcade.h
+++ b/apps/plugins/pacbox/arcade.h
@@ -123,6 +123,8 @@ void init_PacmanMachine(int dip);
int run(void);
void reset_PacmanMachine(void);
void decodeROMs(void);
+void playSound( int * buf, int len );
+
/**
Tells the emulator that the status of an input device has changed.
diff --git a/apps/plugins/pacbox/hardware.c b/apps/plugins/pacbox/hardware.c
index f5a7184..bfdcf39 100644
--- a/apps/plugins/pacbox/hardware.c
+++ b/apps/plugins/pacbox/hardware.c
@@ -26,6 +26,7 @@
#include "plugin.h"
#include "hardware.h"
+#include "wsg3.h"
/* The main data for Pacman */
@@ -140,7 +141,7 @@ void writeByte( unsigned addr, unsigned char b )
default:
if( addr >= 0x5040 && addr < 0x5060 ) {
// Sound registers
- //SOUND sound_chip_.setRegister( addr-0x5040, b );
+ wsg3_set_register( addr-0x5040, b );
}
else if( addr >= 0x5060 && addr < 0x5070 ) {
// Sprite coordinates, x/y pairs for 8 sprites
diff --git a/apps/plugins/pacbox/pacbox.c b/apps/plugins/pacbox/pacbox.c
index badf171..9bbc00d 100644
--- a/apps/plugins/pacbox/pacbox.c
+++ b/apps/plugins/pacbox/pacbox.c
@@ -28,6 +28,7 @@
#include "arcade.h"
#include "pacbox.h"
#include "pacbox_lcd.h"
+#include "wsg3.h"
#include "lib/configfile.h"
#include "lib/playback_control.h"
@@ -40,10 +41,12 @@ struct pacman_settings {
int bonus;
int ghostnames;
int showfps;
+ int sound;
};
static struct pacman_settings settings;
static struct pacman_settings old_settings;
+static bool sound_playing = false;
#define SETTINGS_VERSION 1
#define SETTINGS_MIN_VERSION 1
@@ -53,7 +56,7 @@ static char* difficulty_options[] = { "Normal", "Hard" };
static char* numlives_options[] = { "1", "2", "3", "5" };
static char* bonus_options[] = {"10000", "15000", "20000", "No Bonus"};
static char* ghostnames_options[] = {"Normal", "Alternate"};
-static char* showfps_options[] = {"No", "Yes"};
+static char* yesno_options[] = {"No", "Yes"};
static struct configdata config[] =
{
@@ -65,7 +68,9 @@ static struct configdata config[] =
{TYPE_ENUM, 0, 2, { .int_p = &settings.ghostnames }, "Ghost Names",
ghostnames_options},
{TYPE_ENUM, 0, 2, { .int_p = &settings.showfps }, "Show FPS",
- showfps_options},
+ yesno_options},
+ {TYPE_ENUM, 0, 2, { .int_p = &settings.sound }, "Sound",
+ yesno_options},
};
static bool loadFile( const char * name, unsigned char * buf, int len )
@@ -173,9 +178,21 @@ static bool pacbox_menu(void)
{ "Alternate", -1 },
};
+ enum
+ {
+ PBMI_DIFFICULTY = 0,
+ PBMI_PACMEN_PER_GAME,
+ PBMI_BONUS_LIFE,
+ PBMI_GHOST_NAMES,
+ PBMI_DISPLAY_FPS,
+ PBMI_SOUND,
+ PBMI_RESTART,
+ PBMI_QUIT,
+ };
+
MENUITEM_STRINGLIST(menu, "Pacbox Menu", NULL,
"Difficulty", "Pacmen Per Game", "Bonus Life",
- "Ghost Names", "Display FPS",
+ "Ghost Names", "Display FPS", "Sound",
"Restart", "Quit");
rb->button_clear_queue();
@@ -189,7 +206,7 @@ static bool pacbox_menu(void)
switch(result)
{
- case 0:
+ case PBMI_DIFFICULTY:
new_setting=settings.difficulty;
rb->set_option("Difficulty", &new_setting, INT,
difficulty_options , 2, NULL);
@@ -198,7 +215,7 @@ static bool pacbox_menu(void)
need_restart=true;
}
break;
- case 1:
+ case PBMI_PACMEN_PER_GAME:
new_setting=settings.numlives;
rb->set_option("Pacmen Per Game", &new_setting, INT,
numlives_options , 4, NULL);
@@ -207,7 +224,7 @@ static bool pacbox_menu(void)
need_restart=true;
}
break;
- case 2:
+ case PBMI_BONUS_LIFE:
new_setting=settings.bonus;
rb->set_option("Bonus Life", &new_setting, INT,
bonus_options , 4, NULL);
@@ -216,7 +233,7 @@ static bool pacbox_menu(void)
need_restart=true;
}
break;
- case 3:
+ case PBMI_GHOST_NAMES:
new_setting=settings.ghostnames;
rb->set_option("Ghost Names", &new_setting, INT,
ghostname_options , 2, NULL);
@@ -225,11 +242,15 @@ static bool pacbox_menu(void)
need_restart=true;
}
break;
- case 4: /* Show FPS */
+ case PBMI_DISPLAY_FPS:
rb->set_option("Display FPS",&settings.showfps,INT,
noyes, 2, NULL);
break;
- case 5: /* Restart */
+ case PBMI_SOUND:
+ rb->set_option("Sound",&settings.sound, INT,
+ noyes, 2, NULL);
+ break;
+ case PBMI_RESTART:
need_restart=true;
menu_quit=1;
break;
@@ -252,16 +273,93 @@ static bool pacbox_menu(void)
restart game
usb connected
*/
- return (result==6);
+ return (result==PBMI_QUIT);
+}
+
+/* Sound is emulated in ISR context, so not much is done per sound frame */
+#define NBSAMPLES 128
+static uint32_t sound_buf[NBSAMPLES];
+static int raw_buf[NBSAMPLES] IBSS_ATTR;
+
+/*
+ Audio callback
+ */
+static void get_more(unsigned char **start, size_t *size)
+{
+ int i;
+ int32_t *out;
+ int *raw;
+
+ /* Emulate the audio for the current register settings */
+ playSound(raw_buf, NBSAMPLES);
+
+ out = sound_buf;
+ raw = raw_buf;
+
+ /* Normalize the audio and convert to stereo */
+ for (i = 0; i < NBSAMPLES; i++)
+ {
+ uint32_t sample = (uint16_t)*raw++ << 6;
+ *out++ = sample | (sample << 16);
+ }
+
+ *start = (unsigned char *)sound_buf;
+ *size = NBSAMPLES*sizeof(sound_buf[0]);
+}
+
+/*
+ Start the sound emulation
+*/
+static void start_sound(void)
+{
+ int sr_index;
+
+ if (sound_playing)
+ return;
+
+#ifndef PLUGIN_USE_IRAM
+ /* Ensure control of PCM - stopping music isn't obligatory */
+ rb->plugin_get_audio_buffer(NULL);
+#endif
+
+ /* Get the closest rate >= to what is preferred */
+ sr_index = rb->round_value_to_list32(PREFERRED_SAMPLING_RATE,
+ rb->hw_freq_sampr, HW_NUM_FREQ, false);
+
+ if (rb->hw_freq_sampr[sr_index] < PREFERRED_SAMPLING_RATE
+ && sr_index > 0)
+ {
+ /* Round up */
+ sr_index--;
+ }
+
+ wsg3_set_sampling_rate(rb->hw_freq_sampr[sr_index]);
+
+ rb->pcm_set_frequency(rb->hw_freq_sampr[sr_index]);
+ rb->pcm_play_data(get_more, NULL, 0);
+
+ sound_playing = true;
}
+/*
+ Stop the sound emulation
+*/
+static void stop_sound(void)
+{
+ if (!sound_playing)
+ return;
+
+ rb->pcm_play_stop();
+ rb->pcm_set_frequency(HW_SAMPR_DEFAULT);
+
+ sound_playing = false;
+}
/*
Runs the game engine for one frame.
*/
static int gameProc( void )
{
- int x;
int fps;
char str[80];
int status;
@@ -269,6 +367,9 @@ static int gameProc( void )
int frame_counter = 0;
int yield_counter = 0;
+ if (settings.sound)
+ start_sound();
+
while (1)
{
/* Run the machine for one frame (1/60th second) */
@@ -289,14 +390,25 @@ static int gameProc( void )
|| status == PACMAN_RC_MENU
#endif
) {
+ bool menu_res;
+
end_time = *rb->current_tick;
- x = pacbox_menu();
+
+ stop_sound();
+
+ menu_res = pacbox_menu();
+
rb->lcd_clear_display();
#ifdef HAVE_REMOTE_LCD
rb->lcd_remote_clear_display();
rb->lcd_remote_update();
#endif
- if (x == 1) { return 1; }
+ if (menu_res)
+ return 1;
+
+ if (settings.sound)
+ start_sound();
+
start_time += *rb->current_tick-end_time;
}
@@ -368,6 +480,9 @@ static int gameProc( void )
}
}
}
+
+ stop_sound();
+
return 0;
}
@@ -392,6 +507,7 @@ enum plugin_status plugin_start(const void* parameter)
settings.bonus = 0; /* 10000 points */
settings.ghostnames = 0; /* Normal names */
settings.showfps = 0; /* Do not show FPS */
+ settings.sound = 0; /* Sound off by default */
if (configfile_load(SETTINGS_FILENAME, config,
sizeof(config)/sizeof(*config),
diff --git a/apps/plugins/pacbox/pacbox.h b/apps/plugins/pacbox/pacbox.h
index 1ff51e2..f4cc738 100644
--- a/apps/plugins/pacbox/pacbox.h
+++ b/apps/plugins/pacbox/pacbox.h
@@ -299,4 +299,7 @@
#define FPS 20
#endif
+/* 16kHz sounds pretty good - use it if available */
+#define PREFERRED_SAMPLING_RATE SAMPR_16
+
#endif
diff --git a/apps/plugins/pacbox/wsg3.c b/apps/plugins/pacbox/wsg3.c
new file mode 100644
index 0000000..3c86131
--- /dev/null
+++ b/apps/plugins/pacbox/wsg3.c
@@ -0,0 +1,148 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Pacbox - a Pacman Emulator for Rockbox
+ *
+ * Based on PIE - Pacman Instructional Emulator
+ *
+ * Namco custom waveform sound generator 3 (Pacman hardware)
+ *
+ * Copyright (c) 2003,2004 Alessandro Scotti
+ * http://www.ascotti.org/
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#include "plugin.h"
+#include "wsg3.h"
+
+struct wsg3 wsg3;
+
+static bool wsg3_get_voice(struct wsg3_voice *voice, int index)
+{
+ int base = 5*index;
+ const unsigned char *regs = wsg3.sound_regs;
+ unsigned x;
+
+ x = regs[0x15 + base] & 0x0F;
+
+ if (x == 0)
+ return false;
+
+ voice->volume = x;
+
+ x = (regs[0x14 + base] & 0x0F);
+ x = (x << 4) | (regs[0x13 + base] & 0x0F);
+ x = (x << 4) | (regs[0x12 + base] & 0x0F);
+ x = (x << 4) | (regs[0x11 + base] & 0x0F);
+ x = (x << 4);
+
+ if (index == 0)
+ {
+ /* The first voice has an extra 4-bit of data */
+ x |= regs[0x10 + base] & 0x0F;
+ }
+
+ if (x == 0)
+ return false;
+
+ voice->frequency = x;
+
+ voice->waveform = regs[0x05 + base] & 0x07;
+
+ return true;
+}
+
+void wsg3_play_sound(int * buf, int len)
+{
+ struct wsg3_voice voice;
+
+ if (wsg3_get_voice(&voice, 0))
+ {
+ unsigned offset = wsg3.wave_offset[0];
+ unsigned step = voice.frequency * wsg3.resample_step;
+ int * wave_data = wsg3.sound_wave_data + 32 * voice.waveform;
+ int volume = voice.volume;
+ int i;
+
+ for (i = 0; i < len; i++)
+ {
+ /* Should be shifted right by 15, but we must also get rid
+ * of the 10 bits used for decimals */
+ buf[i] += wave_data[(offset >> 25) & 0x1F] * volume;
+ offset += step;
+ }
+
+ wsg3.wave_offset[0] = offset;
+ }
+
+ if (wsg3_get_voice(&voice, 1))
+ {
+ unsigned offset = wsg3.wave_offset[1];
+ unsigned step = voice.frequency * wsg3.resample_step;
+ int * wave_data = wsg3.sound_wave_data + 32 * voice.waveform;
+ int volume = voice.volume;
+ int i;
+
+ for (i = 0; i < len; i++)
+ {
+ /* Should be shifted right by 15, but we must also get rid
+ * of the 10 bits used for decimals */
+ buf[i] += wave_data[(offset >> 25) & 0x1F] * volume;
+ offset += step;
+ }
+
+ wsg3.wave_offset[1] = offset;
+ }
+
+ if (wsg3_get_voice(&voice, 2))
+ {
+ unsigned offset = wsg3.wave_offset[2];
+ unsigned step = voice.frequency * wsg3.resample_step;
+ int * wave_data = wsg3.sound_wave_data + 32 * voice.waveform;
+ int volume = voice.volume;
+ int i;
+
+ for (i = 0; i < len; i++)
+ {
+ /* Should be shifted right by 15, but we must also get rid
+ * of the 10 bits used for decimals */
+ buf[i] += wave_data[(offset >> 25) & 0x1F] * volume;
+ offset += step;
+ }
+
+ wsg3.wave_offset[2] = offset;
+ }
+}
+
+void wsg3_set_sampling_rate(unsigned sampling_rate)
+{
+ wsg3.sampling_rate = sampling_rate;
+ wsg3.resample_step = (wsg3.master_clock << 10) / sampling_rate;
+}
+
+void wsg3_set_sound_prom( const unsigned char * prom )
+{
+ int i;
+
+ for (i = 0; i < 32*8; i++)
+ wsg3.sound_wave_data[i] = (int)*prom++ - 8;
+}
+
+void wsg3_init(unsigned master_clock)
+{
+ memset(&wsg3, 0, sizeof (struct wsg3));
+ wsg3.master_clock = master_clock;
+}
diff --git a/apps/plugins/pacbox/wsg3.h b/apps/plugins/pacbox/wsg3.h
new file mode 100644
index 0000000..1ee385c
--- /dev/null
+++ b/apps/plugins/pacbox/wsg3.h
@@ -0,0 +1,124 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Pacbox - a Pacman Emulator for Rockbox
+ *
+ * Based on PIE - Pacman Instructional Emulator
+ *
+ * Namco custom waveform sound generator 3 (Pacman hardware)
+ *
+ * Copyright (c) 2003,2004 Alessandro Scotti
+ * http://www.ascotti.org/
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#ifndef WSG3_H
+#define WSG3_H
+
+/**
+ Namco 3-channel sound generator voice properties.
+
+ This information is only needed by applications that want to do their own
+ sound rendering, as the playSound() function already plays and mixes all
+ three voices.
+
+ @see PacmanMachine::playSound
+*/
+struct wsg3_voice
+{
+ /** Volume (from 0 to 15) */
+ unsigned volume;
+ /** Index into the 4-bit 32-entry waveform table (0 to 7) */
+ unsigned waveform;
+ /** Frequency */
+ unsigned frequency;
+};
+
+
+struct wsg3
+{
+ unsigned master_clock;
+ unsigned sampling_rate;
+ unsigned char sound_regs[0x20];
+ unsigned char sound_prom[32*8];
+ unsigned resample_step;
+ unsigned wave_offset[3];
+ int sound_wave_data[32*8];
+};
+
+extern struct wsg3 wsg3;
+
+/**
+ Constructor.
+
+ @param masterClock clock frequency of sound chip (in Hertz)
+
+ @see #wsg3_play_sound
+*/
+void wsg3_init(unsigned master_clock);
+
+/**
+ Sets the 256 byte PROM that contains the waveform table used by the sound chip.
+*/
+void wsg3_set_sound_prom( const unsigned char * prom );
+
+/**
+ Sets the value of the specified register.
+*/
+static inline void wsg3_set_register(unsigned reg, unsigned char value)
+ { wsg3.sound_regs[reg] = value; }
+
+/**
+ Returns the value of the specified register.
+*/
+static inline unsigned char wsg3_get_register(unsigned reg)
+ { return wsg3.sound_regs[reg]; }
+
+/**
+ Reproduces the sound that is currently being generated by the sound
+ chip into the specified buffer.
+
+ The sound chip has three independent voices that generate 8-bit signed
+ PCM audio. This function resamples the voices at the currently specified
+ sampling rate and mixes them into the output buffer. The output buffer
+ can be converted to 8-bit (signed) PCM by dividing each sample by 3 (since
+ there are three voices) or it can be expanded to 16-bit by multiplying
+ each sample by 85 (i.e. 256 divided by 3). If necessary, it is possible
+ to approximate these values with 4 and 64 in order to use arithmetic
+ shifts that are usually faster to execute.
+
+ Note: this function does not clear the content of the output buffer before
+ mixing voices into it.
+
+ @param buf pointer to sound buffer that receives the audio samples
+ @param len length of the sound buffer
+*/
+void wsg3_play_sound(int * buf, int len);
+
+/**
+ Returns the sampling rate currently in use for rendering sound.
+*/
+static inline unsigned wsg3_get_sampling_rate(void)
+ { return wsg3.sampling_rate; }
+
+/**
+ Sets the output sampling rate for playSound().
+
+ @param samplingRate sampling rate in Hertz (samples per second)
+*/
+void wsg3_set_sampling_rate(unsigned sampling_rate);
+
+#endif /* WSG3_H */