Add sound effects

This commit is contained in:
Lucas S. Vieira 2024-07-22 01:43:00 -03:00
parent b4d7be0cda
commit a33a11b716
14 changed files with 147 additions and 34 deletions

2
assets/sfx/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
\*.WAV

BIN
assets/sfx/JUMP.VAG Normal file

Binary file not shown.

BIN
assets/sfx/JUMP.ogg Normal file

Binary file not shown.

BIN
assets/sfx/RING.VAG Normal file

Binary file not shown.

BIN
assets/sfx/RING.ogg Normal file

Binary file not shown.

BIN
assets/sfx/SKIDDING.VAG Normal file

Binary file not shown.

BIN
assets/sfx/SKIDDING.ogg Normal file

Binary file not shown.

8
assets/sfx/convert.sh Executable file
View file

@ -0,0 +1,8 @@
#!/bin/bash
for f in *.ogg; do
ffmpeg -y -i "$f" -acodec pcm_s16le -ac 1 -ar 22050 "${f%%.ogg}.WAV";
wav2vag "${f%%.ogg}.WAV" "${f%%.ogg}.VAG";
rm "${f%%.ogg}.WAV";
done

View file

@ -4,6 +4,27 @@
#include <stdint.h>
#include <psxcd.h>
// .VAG audio header
typedef struct {
uint32_t magic;
uint32_t version;
uint32_t interleave; // Unused in mono
uint32_t size; // Big-endian, bytes
uint32_t sample_rate; // Big-endian, Hz
uint16_t _reserved[5];
uint16_t channels; // Unused in mono
char name[16];
} VAGHeader;
// Struct holding information on an uploaded sample.
typedef struct {
uint32_t addr;
uint32_t sample_rate;
} SoundEffect;
// "VAGp" text
#define VAG_MAGIC 0x70474156
// .XA audio header
typedef struct {
uint8_t file;
@ -37,7 +58,9 @@ void sound_init(void);
void sound_reset_mem(void);
void sound_update(void);
uint32_t sound_upload_sample(const uint32_t *data, uint32_t size);
SoundEffect sound_load_vag(const char *filename);
uint32_t sound_upload_vag(const uint32_t *data, uint32_t size);
void sound_play_vag(SoundEffect sfx);
uint32_t sound_get_cd_status(void);
void sound_play_xa(const char *filename, int double_speed,

18
iso.xml
View file

@ -12,7 +12,9 @@
<!-- <license file="${PROJECT_SOURCE_DIR}/LCNSFILE/LICENSEA.DAT" /> -->
<directory_tree>
<file name="SYSTEM.CNF" type="data" source="${PROJECT_SOURCE_DIR}/system.cnf" />
<file name="SONIC.EXE" type="data" source="sonic.exe" />
<dir name="SPRITES">
<file name="SONIC.TIM"
type="data"
@ -21,11 +23,25 @@
type="data"
source="${PROJECT_SOURCE_DIR}/assets/sprites/SONIC.CHARA" />
</dir>
<dir name="AUDIO">
<dir name="SFX">
<file name="JUMP.VAG"
type="data"
source="${PROJECT_SOURCE_DIR}/assets/sfx/JUMP.VAG" />
<file name="RING.VAG"
type="data"
source="${PROJECT_SOURCE_DIR}/assets/sfx/RING.VAG" />
<file name="SKIDDING.VAG"
type="data"
source="${PROJECT_SOURCE_DIR}/assets/sfx/SKIDDING.VAG" />
</dir>
<dir name="BGM">
<file name="BGM001.XA"
type="xa"
source="${PROJECT_SOURCE_DIR}/assets/bgm/BGM001.XA" />
</dir>
<dummy sectors="1024"/>
</directory_tree>
</track>

View file

@ -14,7 +14,7 @@ load_chara(Chara *chara, const char *filename, TIM_IMAGE *tim)
bytes = file_read(filename, &length);
if(bytes == NULL) {
printf("Error reading %s from the CD.\n", bytes);
printf("Error reading CHARA file %s from the CD.\n", filename);
return;
}

View file

@ -14,10 +14,10 @@
#include "input.h"
#include "player.h"
#define SPRTSZ 56
/* #define SPRTSZ 56 */
static int x = (SPRTSZ >> 1), y = (SPRTSZ >> 1);
static int dx = 1, dy = 1;
/* static int x = (SPRTSZ >> 1), y = (SPRTSZ >> 1); */
/* static int dx = 1, dy = 1; */
static SVECTOR vertices[] = {
{ -64, -64, -64, 0 },
@ -67,11 +67,14 @@ engine_init()
}
load_player(&player, "\\SPRITES\\SONIC.CHARA;1", &tim);
player.pos = (VECTOR){ (uint32_t)(SCREEN_XRES) << 9, (uint32_t)(SCREEN_YRES) << 11, 0 };
player.pos = (VECTOR){
(uint32_t)(SCREEN_XRES) << 9,
(uint32_t)(SCREEN_YRES) << 11,
0
};
// Start playback after we don't need the CD anymore.
sound_play_xa("\\AUDIO\\BGM001.XA;1", 0, music_channel, 7100);
sound_play_xa("\\BGM\\BGM001.XA;1", 0, music_channel, 7100);
}
void
@ -80,21 +83,17 @@ engine_update()
sound_update();
pad_update();
if(x < (SPRTSZ >> 1) || x > (SCREEN_XRES - 32)) dx = -dx;
if(y < (SPRTSZ >> 1) || y > (SCREEN_YRES - 32)) dy = -dy;
/* if(x < (SPRTSZ >> 1) || x > (SCREEN_XRES - 32)) dx = -dx; */
/* if(y < (SPRTSZ >> 1) || y > (SCREEN_YRES - 32)) dy = -dy; */
x += dx;
y += dy;
/* x += dx; */
/* y += dy; */
rotation.vx += 6;
rotation.vy -= 8;
rotation.vz -= 12;
int channel_changed = 0;
/* if(pad_pressed(PAD_CROSS)) { */
/* music_channel = (music_channel + 1) % MUSIC_NUM_CHANNELS; */
/* sound_xa_set_channel(music_channel); */
/* } */
if(pad_pressed(PAD_R1)) {
music_channel++;
channel_changed = 1;
@ -120,19 +119,19 @@ engine_draw()
player_draw(&player);
// Gouraud-shaded SQUARE
POLY_G4 *poly = (POLY_G4 *) get_next_prim();
setPolyG4(poly);
setXY4(poly,
x - (SPRTSZ >> 1), y - (SPRTSZ >> 1),
x + (SPRTSZ >> 1), y - (SPRTSZ >> 1),
x - (SPRTSZ >> 1), y + (SPRTSZ >> 1),
x + (SPRTSZ >> 1), y + (SPRTSZ >> 1));
setRGB0(poly, 255, 0, 0);
setRGB1(poly, 0, 255, 0);
setRGB2(poly, 0, 0, 255);
setRGB3(poly, 255, 255, 0);
sort_prim(poly, 1);
increment_prim(sizeof(POLY_G4));
/* POLY_G4 *poly = (POLY_G4 *) get_next_prim(); */
/* setPolyG4(poly); */
/* setXY4(poly, */
/* x - (SPRTSZ >> 1), y - (SPRTSZ >> 1), */
/* x + (SPRTSZ >> 1), y - (SPRTSZ >> 1), */
/* x - (SPRTSZ >> 1), y + (SPRTSZ >> 1), */
/* x + (SPRTSZ >> 1), y + (SPRTSZ >> 1)); */
/* setRGB0(poly, 255, 0, 0); */
/* setRGB1(poly, 0, 255, 0); */
/* setRGB2(poly, 0, 0, 255); */
/* setRGB3(poly, 255, 255, 0); */
/* sort_prim(poly, 1); */
/* increment_prim(sizeof(POLY_G4)); */
// Gouraud-shaded cube
RotMatrix(&rotation, &world);

View file

@ -1,10 +1,12 @@
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "player.h"
#include "util.h"
#include "input.h"
#include "render.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "sound.h"
#define TMP_ANIM_SPD 7
#define ANIM_IDLE_TIMER_MAX 180
@ -22,6 +24,8 @@
#define ANIM_CROUCHDOWN 0x104802fd
#define ANIM_LOOKUP 0x067001db
SoundEffect sfx_jump = { 0 };
void
load_player(Player *player,
const char *chara_filename,
@ -36,6 +40,8 @@ load_player(Player *player,
player->anim_dir = 1;
player->idle_timer = ANIM_IDLE_TIMER_MAX;
player->grnd = player->jmp = 0;
if(sfx_jump.addr == 0) sfx_jump = sound_load_vag("\\SFX\\JUMP.VAG;1");
}
void
@ -153,6 +159,7 @@ player_update(Player *player)
player->grnd = 0;
player->jmp = 1;
player_set_animation_direct(player, ANIM_ROLLING);
sound_play_vag(sfx_jump);
}
}

View file

@ -4,6 +4,7 @@
#include <psxspu.h>
#include <psxapi.h>
#include <assert.h>
#include <stdlib.h>
// First 4KB of SPU RAM are reserved for capture buffers.
// psxspu additionally uploads a dummy sample (16 bytes) at 0x1000
@ -18,6 +19,11 @@
// everything (in this case, resetting this pointer).
static uint32_t next_sample_addr = SPU_ALLOC_START_ADDR;
// Address of next channel for playing .VAG samples.
// Channels are always cycling.
#define MAX_CHANNELS 24
static int32_t next_channel = 0;
// Used by the CD handler callback as a temporary area for
// sectors read from the CD. Due to DMA limitations, it can't
// be allocated on the stack -- especially not in the interrupt
@ -34,6 +40,7 @@ static uint32_t _xa_loopback_sector = 0;
#define CD_MAX_ERR_THRESHOLD 10
static uint8_t _cd_err_threshold = 0;
// Elapsed CD sectors from start of .XA playback
static uint32_t _cd_elapsed_sectors = 0;
void
@ -63,7 +70,7 @@ sound_get_cd_status(void)
}
uint32_t
sound_upload_sample(const uint32_t *data, uint32_t size)
sound_upload_vag(const uint32_t *data, uint32_t size)
{
uint32_t addr = next_sample_addr;
// Round size up to a multiple of 64, since DMA transfers
@ -83,6 +90,57 @@ sound_upload_sample(const uint32_t *data, uint32_t size)
return addr;
}
SoundEffect
sound_load_vag(const char *filename)
{
uint8_t *bytes;
uint32_t length;
bytes = file_read(filename, &length);
if(bytes == NULL) {
printf("Error reading VAG file %s from the CD.\n", filename);
return (SoundEffect){ 0 };
}
const VAGHeader *hdr = (const VAGHeader *)bytes;
const uint32_t *data = (const uint32_t *)(bytes + sizeof(VAGHeader));
uint32_t sample_rate = __builtin_bswap32(hdr->sample_rate);
uint32_t addr = sound_upload_vag(data, __builtin_bswap32(hdr->size));
free(bytes);
return (SoundEffect) { addr, sample_rate };
}
int32_t
_get_next_channel()
{
int32_t ch = next_channel;
next_channel = (ch + 1) % MAX_CHANNELS;
return ch;
}
void
sound_play_vag(SoundEffect sfx)
{
int ch = _get_next_channel();
SpuSetKey(0, 1 << ch);
// SPU expects sample rate to be in 4.12 fixed-point format
// (with 1.0 = 44100 Hz), and the address must be in 8-byte
// units.
SPU_CH_FREQ(ch) = getSPUSampleRate(sfx.sample_rate);
SPU_CH_ADDR(ch) = getSPUAddr(sfx.addr);
// Set channel volume and ADSR parameters.
// 0x80ff and 0x1fee are dummy values that disable ADSR envelope entirely.
SPU_CH_VOL_L(ch) = 0x3fff;
SPU_CH_VOL_R(ch) = 0x3fff;
SPU_CH_ADSR1(ch) = 0x00ff;
SPU_CH_ADSR2(ch) = 0x0000;
// Start playback
SpuSetKey(1, 1 << ch);
}
void _xacd_event_callback(CdlIntrResult, uint8_t *);
void