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
This commit is contained in:
Michael Sevakis 2010-07-01 00:26:01 +00:00
parent 10b8e327d8
commit cf73340f1e
10 changed files with 473 additions and 24 deletions

View file

@ -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,

View file

@ -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);

View file

@ -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

View file

@ -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 )
{

View file

@ -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.

View file

@ -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

View file

@ -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),

View file

@ -299,4 +299,7 @@
#define FPS 20
#endif
/* 16kHz sounds pretty good - use it if available */
#define PREFERRED_SAMPLING_RATE SAMPR_16
#endif

148
apps/plugins/pacbox/wsg3.c Normal file
View file

@ -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;
}

124
apps/plugins/pacbox/wsg3.h Normal file
View file

@ -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 */