From cf73340f1e85d3b39dc452d57e28d2a9a9081051 Mon Sep 17 00:00:00 2001 From: Michael Sevakis Date: Thu, 1 Jul 2010 00:26:01 +0000 Subject: [PATCH] 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 --- apps/plugin.c | 2 - apps/plugin.h | 14 ++-- apps/plugins/pacbox/SOURCES | 1 + apps/plugins/pacbox/arcade.c | 58 +++++++++++++ apps/plugins/pacbox/arcade.h | 2 + apps/plugins/pacbox/hardware.c | 3 +- apps/plugins/pacbox/pacbox.c | 142 ++++++++++++++++++++++++++++--- apps/plugins/pacbox/pacbox.h | 3 + apps/plugins/pacbox/wsg3.c | 148 +++++++++++++++++++++++++++++++++ apps/plugins/pacbox/wsg3.h | 124 +++++++++++++++++++++++++++ 10 files changed, 473 insertions(+), 24 deletions(-) create mode 100644 apps/plugins/pacbox/wsg3.c create mode 100644 apps/plugins/pacbox/wsg3.h diff --git a/apps/plugin.c b/apps/plugin.c index 56d2be9f40..51ef40f010 100644 --- a/apps/plugin.c +++ b/apps/plugin.c @@ -637,9 +637,7 @@ static const struct plugin_api rockbox_api = { get_codec_filename, find_array_ptr, remove_array_ptr, -#if defined(HAVE_RECORDING) && (defined(HAVE_LINE_IN) || defined(HAVE_MIC_IN)) round_value_to_list32, -#endif #endif /* CONFIG_CODEC == SWCODEC */ get_metadata, mp3info, diff --git a/apps/plugin.h b/apps/plugin.h index b0f4e051c5..edad8c4e9b 100644 --- a/apps/plugin.h +++ b/apps/plugin.h @@ -144,12 +144,12 @@ void* plugin_get_buffer(size_t *buffer_size); #define PLUGIN_MAGIC 0x526F634B /* RocK */ /* increase this every time the api struct changes */ -#define PLUGIN_API_VERSION 187 +#define PLUGIN_API_VERSION 188 /* update this to latest version if a change to the api struct breaks backwards compatibility (and please take the opportunity to sort in any new function which are "waiting" at the end of the function table) */ -#define PLUGIN_MIN_API_VERSION 187 +#define PLUGIN_MIN_API_VERSION 188 /* plugin return codes */ enum plugin_status { @@ -782,12 +782,10 @@ struct plugin_api { const char *(*get_codec_filename)(int cod_spec); void ** (*find_array_ptr)(void **arr, void *ptr); int (*remove_array_ptr)(void **arr, void *ptr); -#if defined(HAVE_RECORDING) && (defined(HAVE_LINE_IN) || defined(HAVE_MIC_IN)) -int (*round_value_to_list32)(unsigned long value, - const unsigned long list[], - int count, - bool signd); -#endif + int (*round_value_to_list32)(unsigned long value, + const unsigned long list[], + int count, + bool signd); #endif /* CONFIG_CODEC == SWCODEC */ bool (*get_metadata)(struct mp3entry* id3, int fd, const char* trackname); bool (*mp3info)(struct mp3entry *entry, const char *filename); diff --git a/apps/plugins/pacbox/SOURCES b/apps/plugins/pacbox/SOURCES index fdda4ca2d7..65a5cf1cb9 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 b8a0f105bb..ecf6d10af8 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 #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 32ec0b45be..33dd6d6981 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 f5a7184475..bfdcf39362 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 badf171927..9bbc00d094 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 1ff51e2872..f4cc738b02 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 0000000000..3c861312b9 --- /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 0000000000..1ee385cdf9 --- /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 */