aboutsummaryrefslogtreecommitdiff
path: root/examples
diff options
context:
space:
mode:
authorspicyjpeg <thatspicyjpeg@gmail.com>2022-12-18 16:05:43 +0100
committerspicyjpeg <thatspicyjpeg@gmail.com>2022-12-18 16:05:43 +0100
commitb58a37bdac753ceace35761ef474c198a3f18e12 (patch)
treef7747c222e47479b2b39ad04b39b82d991ae0f0a /examples
parentd84bc4939607442e41888521939ef10908c87ce3 (diff)
downloadpsn00bsdk-b58a37bdac753ceace35761ef474c198a3f18e12.tar.gz
Clean up MDEC and sound examples
Diffstat (limited to 'examples')
-rw-r--r--examples/mdec/strvideo/main.c93
-rw-r--r--examples/sound/cdstream/main.c4
-rw-r--r--examples/sound/cdstream/stream.vagbin4646912 -> 4687872 bytes
-rw-r--r--examples/sound/spustream/interleave.py13
-rw-r--r--examples/sound/spustream/main.c51
-rw-r--r--examples/sound/spustream/stream.vagbin1140736 -> 1140736 bytes
-rw-r--r--examples/sound/vagsample/main.c4
7 files changed, 73 insertions, 92 deletions
diff --git a/examples/mdec/strvideo/main.c b/examples/mdec/strvideo/main.c
index 842bbb8..28d39b2 100644
--- a/examples/mdec/strvideo/main.c
+++ b/examples/mdec/strvideo/main.c
@@ -31,10 +31,8 @@
* pipeline must also run in lockstep with each other to prevent frame
* corruption, hence several functions and flag variables are used to stall the
* main loop until a frame is available for decoding and the MDEC is ready.
- * Playback is stopped when a sector with the end-of-file flag set in the XA
- * subheader (added at the end of the file by most .STR encoders) is
- * encountered; in order to access the subheader, this example requests 2340
- * bytes of data for each sector (rather than the usual 2048) from the drive.
+ * Playback is stopped once the .STR header is no longer present in sectors
+ * read.
*
* Note that PSn00bSDK's bitstream decoding API only supports version 1 and 2
* bitstreams currently, so make sure your .STR files are encoded as v2 and not
@@ -42,6 +40,7 @@
*/
#include <stdint.h>
+#include <stdio.h>
#include <string.h>
#include <psxetc.h>
#include <psxapi.h>
@@ -85,15 +84,15 @@ void init_context(RenderContext *ctx) {
SetDefDispEnv(&(db->disp), 0, 0, SCREEN_XRES, SCREEN_YRES);
SetDefDrawEnv(&(db->draw), 0, SCREEN_YRES, SCREEN_XRES, SCREEN_YRES);
setRGB0(&(db->draw), BGCOLOR_R, BGCOLOR_G, BGCOLOR_B);
- db->draw.isbg = 1;
- db->draw.dtd = 1;
+ db->draw.isbg = 1;
+ db->draw.dtd = 1;
db = &(ctx->db[1]);
SetDefDispEnv(&(db->disp), 0, SCREEN_YRES, SCREEN_XRES, SCREEN_YRES);
SetDefDrawEnv(&(db->draw), 0, 0, SCREEN_XRES, SCREEN_YRES);
setRGB0(&(db->draw), BGCOLOR_R, BGCOLOR_G, BGCOLOR_B);
- db->draw.isbg = 1;
- db->draw.dtd = 1;
+ db->draw.isbg = 1;
+ db->draw.dtd = 1;
PutDrawEnv(&(db->draw));
//PutDispEnv(&(db->disp));
@@ -145,22 +144,6 @@ typedef struct {
uint32_t _reserved;
} STR_Header;
-// https://problemkaputt.de/psx-spx.htm#cdromxasubheaderfilechannelinterleave
-typedef struct {
- uint8_t file, channel;
- uint8_t submode, coding_info;
-} XA_Header;
-
-// https://problemkaputt.de/psx-spx.htm#cdromsectorencoding
-typedef struct {
- CdlLOC pos;
- XA_Header xa_header[2];
- STR_Header str_header;
- uint8_t data[2016];
- uint32_t edc;
- uint8_t ecc[276];
-} STR_Sector;
-
typedef struct {
uint16_t width, height;
uint32_t bs_data[0x2000]; // Bitstream data read from the disc
@@ -186,57 +169,52 @@ StreamContext str_ctx;
// read from the CD. Due to DMA limitations it can't be allocated on the stack
// (especially not in the interrupt callbacks' stack, whose size is very
// limited).
-STR_Sector sector_buffer;
+STR_Header sector_header;
void cd_sector_handler(void) {
- // Fetch the .STR header of the sector that has been read and check if the
- // end-of-file bit is set in the XA header.
- CdGetSector(&sector_buffer, sizeof(STR_Sector) / 4);
-
- if (
- (sector_buffer.xa_header[0].submode & (1 << 7)) ||
- (sector_buffer.xa_header[1].submode & (1 << 7))
- ) {
- CdControlF(CdlPause, 0);
+ StreamBuffer *frame = &str_ctx.frames[str_ctx.cur_frame];
+
+ // Fetch the .STR header of the sector that has been read and make sure it
+ // is valid. If not, assume the file has ended and set frame_ready as a
+ // signal for the main loop to stop playback.
+ CdGetSector(&sector_header, sizeof(STR_Header) / 4);
+
+ if (sector_header.magic != 0x0160) {
str_ctx.frame_ready = -1;
return;
}
- STR_Header *header = &sector_buffer.str_header;
- StreamBuffer *frame = &str_ctx.frames[str_ctx.cur_frame];
-
// Ignore any non-MDEC sectors that might be present in the stream.
- if (header->type != 0x8001)
+ if (sector_header.type != 0x8001)
return;
// If this sector is actually part of a new frame, validate the sectors
// that have been read so far and flip the bitstream data buffers.
- if (header->frame_id != str_ctx.frame_id) {
+ if (sector_header.frame_id != str_ctx.frame_id) {
// Do not set the ready flag if any sector has been missed.
if (str_ctx.sector_count)
str_ctx.dropped_frames++;
else
str_ctx.frame_ready = 1;
- str_ctx.frame_id = header->frame_id;
- str_ctx.sector_count = header->sector_count;
+ str_ctx.frame_id = sector_header.frame_id;
+ str_ctx.sector_count = sector_header.sector_count;
str_ctx.cur_frame ^= 1;
frame = &str_ctx.frames[str_ctx.cur_frame];
// Initialize the next frame. Dimensions must be rounded up to the
// nearest multiple of 16 as the MDEC operates on 16x16 pixel blocks.
- frame->width = (header->width + 15) & 0xfff0;
- frame->height = (header->height + 15) & 0xfff0;
+ frame->width = (sector_header.width + 15) & 0xfff0;
+ frame->height = (sector_header.height + 15) & 0xfff0;
}
// Append the payload contained in this sector to the current buffer.
- memcpy(
- &(frame->bs_data[2016 / 4 * header->sector_id]),
- sector_buffer.data,
- 2016
- );
str_ctx.sector_count--;
+ CdGetSector(
+ &(frame->bs_data[2016 / 4 * sector_header.sector_id]),
+ 2016 / 4
+ );
}
void mdec_dma_handler(void) {
@@ -262,7 +240,7 @@ void mdec_dma_handler(void) {
);
}
-void cd_event_handler(int event, uint8_t *payload) {
+void cd_event_handler(CdlIntrResult event, uint8_t *payload) {
// Ignore all events other than a sector being ready.
if (event != CdlDataReady)
return;
@@ -292,9 +270,8 @@ void init_stream(void) {
DecDCTvlcSize(0x8000);
DecDCTvlcCopyTable((DECDCTTAB *) 0x1f800000);
- str_ctx.dropped_frames = 0;
- str_ctx.cur_frame = 0;
- str_ctx.cur_slice = 0;
+ str_ctx.cur_frame = 0;
+ str_ctx.cur_slice = 0;
}
StreamBuffer *get_next_frame(void) {
@@ -310,14 +287,15 @@ StreamBuffer *get_next_frame(void) {
void start_stream(CdlFILE *file) {
str_ctx.frame_id = -1;
+ str_ctx.dropped_frames = 0;
str_ctx.sector_pending = 0;
str_ctx.frame_ready = 0;
CdSync(0, 0);
- // Configure the CD drive to read 2340-byte sectors at 2x speed and to
- // play any XA-ADPCM sectors that might be interleaved with the video data.
- uint8_t mode = CdlModeSize | CdlModeRT | CdlModeSpeed;
+ // Configure the CD drive to read at 2x speed and to play any XA-ADPCM
+ // sectors that might be interleaved with the video data.
+ uint8_t mode = CdlModeRT | CdlModeSpeed;
CdControl(CdlSetmode, (const uint8_t *) &mode, 0);
// Start reading in real-time mode (i.e. without retrying in case of read
@@ -369,6 +347,9 @@ int main(int argc, const char* argv[]) {
// ended, restart playback from the beginning.
StreamBuffer *frame = get_next_frame();
if (!frame) {
+ printf("End of file, looping...\n");
+ CdControlB(CdlPause, 0, 0);
+
start_stream(&file);
continue;
}
@@ -423,7 +404,7 @@ int main(int argc, const char* argv[]) {
int x_offset = (fb_clip->w - frame->width) / 2;
int y_offset = (fb_clip->h - frame->height) / 2;
- str_ctx.slice_pos.x = VRAM_X_COORD(fb_clip->x + x_offset);
+ str_ctx.slice_pos.x = fb_clip->x + VRAM_X_COORD(x_offset);
str_ctx.slice_pos.y = fb_clip->y + y_offset;
str_ctx.slice_pos.w = BLOCK_SIZE;
str_ctx.slice_pos.h = frame->height;
diff --git a/examples/sound/cdstream/main.c b/examples/sound/cdstream/main.c
index 636ef10..324abb2 100644
--- a/examples/sound/cdstream/main.c
+++ b/examples/sound/cdstream/main.c
@@ -308,8 +308,8 @@ void start_stream(void) {
for (int i = 0; i < NUM_CHANNELS; i++) {
SPU_CH_ADDR(i) = getSPUAddr(str_ctx.spu_addr + str_ctx.buffer_size * i);
SPU_CH_FREQ(i) = getSPUSampleRate(str_ctx.sample_rate);
- SPU_CH_ADSR1(i) = 0x80ff;
- SPU_CH_ADSR2(i) = 0x1fee;
+ SPU_CH_ADSR1(i) = 0x00ff;
+ SPU_CH_ADSR2(i) = 0x0000;
}
// Unmute the channels and route them for stereo output. You'll want to
diff --git a/examples/sound/cdstream/stream.vag b/examples/sound/cdstream/stream.vag
index a6faf74..aab82ba 100644
--- a/examples/sound/cdstream/stream.vag
+++ b/examples/sound/cdstream/stream.vag
Binary files differ
diff --git a/examples/sound/spustream/interleave.py b/examples/sound/spustream/interleave.py
index 4e68974..4f4f20f 100644
--- a/examples/sound/spustream/interleave.py
+++ b/examples/sound/spustream/interleave.py
@@ -8,7 +8,7 @@ from struct import Struct
from itertools import zip_longest
from argparse import ArgumentParser, FileType
-VAG_HEADER = Struct("> 4s I 4s 2I 12x 16s")
+VAG_HEADER = Struct("> 4s 4I 10x H 16s")
VAG_MAGIC = b"VAGp"
VAGI_MAGIC = b"VAGi"
VAG_VERSION = 0x20
@@ -17,6 +17,9 @@ CHUNK_ALIGN = 0x800
## Helpers
+def swap_endian(value, size):
+ return int.from_bytes(value.to_bytes(size, "big"), "little")
+
def align(data, size):
chunks = (len(data) + size - 1) // size
@@ -40,7 +43,7 @@ class VAGReader:
magic, _, _,
self.size,
self.sample_rate,
- self.name
+ _, _
) = VAG_HEADER.unpack(header)
if magic == VAGI_MAGIC:
@@ -116,9 +119,6 @@ def main():
size = input_files[0].size
sample_rate = input_files[0].sample_rate
- if (not args.raw) and (len(input_files) != 2):
- warn(RuntimeWarning("interleaved .VAG only supports stereo (2 input files)"))
-
for vag in input_files[1:]:
if vag.size != size:
warn(RuntimeWarning(f"{vag.file.name} has a different file size"))
@@ -135,9 +135,10 @@ def main():
header = VAG_HEADER.pack(
VAGI_MAGIC,
VAG_VERSION,
- args.buffer_size.to_bytes(4, "little"),
+ swap_endian(args.buffer_size, 4),
size,
sample_rate,
+ swap_endian(len(input_files), 2),
os.path.basename(_file.name).encode()[0:16]
)
diff --git a/examples/sound/spustream/main.c b/examples/sound/spustream/main.c
index d240433..6bf2c71 100644
--- a/examples/sound/spustream/main.c
+++ b/examples/sound/spustream/main.c
@@ -54,8 +54,6 @@
extern const uint8_t stream_data[];
-#define NUM_CHANNELS 2
-
/* Display/GPU context utilities */
#define SCREEN_XRES 320
@@ -124,7 +122,8 @@ typedef struct {
uint32_t interleave; // Little-endian, size of each channel buffer
uint32_t size; // Big-endian, in bytes
uint32_t sample_rate; // Big-endian, in Hertz
- uint32_t _reserved[3];
+ uint16_t _reserved[5];
+ uint16_t channels; // Little-endian, if 0 the file is mono
char name[16];
} VAG_Header;
@@ -147,17 +146,12 @@ typedef struct {
#define DUMMY_BLOCK_ADDR 0x1000
#define BUFFER_START_ADDR 0x1010
-typedef enum {
- STATE_IDLE,
- STATE_BUFFERING
-} StreamState;
-
typedef struct {
const uint8_t *data;
- int buffer_size, num_chunks, sample_rate;
+ int buffer_size, num_chunks, sample_rate, channels;
volatile int next_chunk, spu_addr;
- volatile int8_t db_active, state;
+ volatile int8_t db_active, buffering;
} StreamContext;
static StreamContext str_ctx;
@@ -166,13 +160,13 @@ void spu_irq_handler(void) {
// Acknowledge the interrupt to ensure it can be triggered again. The only
// way to do this is actually to disable the interrupt entirely; we'll
// enable it again once the chunk is ready.
- SPU_CTRL &= 0xffbf;
+ SPU_CTRL &= ~(1 << 6);
- int chunk_size = str_ctx.buffer_size * NUM_CHANNELS;
+ int chunk_size = str_ctx.buffer_size * str_ctx.channels;
int chunk = (str_ctx.next_chunk + 1) % (uint32_t) str_ctx.num_chunks;
str_ctx.db_active ^= 1;
- str_ctx.state = STATE_BUFFERING;
+ str_ctx.buffering = 1;
str_ctx.next_chunk = chunk;
// Configure to SPU to trigger an IRQ once the chunk that is going to be
@@ -184,7 +178,7 @@ void spu_irq_handler(void) {
str_ctx.spu_addr = addr;
SPU_IRQ_ADDR = getSPUAddr(addr);
- for (int i = 0; i < NUM_CHANNELS; i++)
+ for (int i = 0; i < str_ctx.channels; i++)
SPU_CH_LOOP_ADDR(i) = getSPUAddr(addr + str_ctx.buffer_size * i);
// Start uploading the next chunk to the SPU.
@@ -194,9 +188,9 @@ void spu_irq_handler(void) {
void spu_dma_handler(void) {
// Re-enable the SPU IRQ once the new chunk has been fully uploaded.
- SPU_CTRL |= 0x0040;
+ SPU_CTRL |= 1 << 6;
- str_ctx.state = STATE_IDLE;
+ str_ctx.buffering = 0;
}
/* Helper functions */
@@ -228,6 +222,7 @@ void init_stream(const VAG_Header *vag) {
str_ctx.buffer_size = buf_size;
str_ctx.num_chunks = (SWAP_ENDIAN(vag->size) + buf_size - 1) / buf_size;
str_ctx.sample_rate = SWAP_ENDIAN(vag->sample_rate);
+ str_ctx.channels = vag->channels ? vag->channels : 1;
str_ctx.db_active = 1;
str_ctx.next_chunk = -1;
@@ -235,18 +230,22 @@ void init_stream(const VAG_Header *vag) {
// Ensure at least one chunk is in SPU RAM by invoking the IRQ handler
// manually and blocking until the chunk has loaded.
spu_irq_handler();
- while (str_ctx.state != STATE_IDLE)
+ while (str_ctx.buffering)
__asm__ volatile("");
}
void start_stream(void) {
- int bits = 0x00ffffff >> (24 - NUM_CHANNELS);
+ int bits = 0x00ffffff >> (24 - str_ctx.channels);
+
+ // Disable the IRQ as we're going to call spu_irq_handler() manually (due
+ // to finicky SPU timings).
+ SPU_CTRL &= ~(1 << 6);
- for (int i = 0; i < NUM_CHANNELS; i++) {
+ for (int i = 0; i < str_ctx.channels; i++) {
SPU_CH_ADDR(i) = getSPUAddr(str_ctx.spu_addr + str_ctx.buffer_size * i);
SPU_CH_FREQ(i) = getSPUSampleRate(str_ctx.sample_rate);
- SPU_CH_ADSR1(i) = 0x80ff;
- SPU_CH_ADSR2(i) = 0x1fee;
+ SPU_CH_ADSR1(i) = 0x00ff;
+ SPU_CH_ADSR2(i) = 0x0000;
}
// Unmute the channels and route them for stereo output. You'll want to
@@ -257,19 +256,19 @@ void start_stream(void) {
SPU_CH_VOL_L(1) = 0x0000;
SPU_CH_VOL_R(1) = 0x3fff;
- spu_irq_handler();
SpuSetKey(1, bits);
+ spu_irq_handler();
}
// This is basically a variant of reset_spu_channels() that only resets the
// channels used to play the stream, to (again) prevent them from triggering
// the SPU IRQ while the stream is paused.
void stop_stream(void) {
- int bits = 0x00ffffff >> (24 - NUM_CHANNELS);
+ int bits = 0x00ffffff >> (24 - str_ctx.channels);
SpuSetKey(0, bits);
- for (int i = 0; i < NUM_CHANNELS; i++)
+ for (int i = 0; i < str_ctx.channels; i++)
SPU_CH_ADDR(i) = getSPUAddr(DUMMY_BLOCK_ADDR);
SpuSetKey(1, bits);
@@ -300,7 +299,7 @@ int main(int argc, const char* argv[]) {
while (1) {
FntPrint(-1, "PLAYING SPU STREAM\n\n");
FntPrint(-1, "BUFFER: %d\n", str_ctx.db_active);
- FntPrint(-1, "STATUS: %s\n\n", str_ctx.state ? "BUFFERING" : "IDLE");
+ FntPrint(-1, "STATUS: %s\n\n", str_ctx.buffering ? "BUFFERING" : "IDLE");
FntPrint(-1, "POSITION: %d/%d\n", str_ctx.next_chunk, str_ctx.num_chunks);
FntPrint(-1, "SMP RATE: %5d HZ\n\n", (sample_rate * 44100) >> 12);
@@ -350,7 +349,7 @@ int main(int argc, const char* argv[]) {
// Only set the sample rate registers if necessary.
if (pad->btn != 0xffff) {
- for (int i = 0; i < NUM_CHANNELS; i++)
+ for (int i = 0; i < str_ctx.channels; i++)
SPU_CH_FREQ(i) = sample_rate;
}
diff --git a/examples/sound/spustream/stream.vag b/examples/sound/spustream/stream.vag
index e1cb4f4..d75408b 100644
--- a/examples/sound/spustream/stream.vag
+++ b/examples/sound/spustream/stream.vag
Binary files differ
diff --git a/examples/sound/vagsample/main.c b/examples/sound/vagsample/main.c
index 6a60c19..dffa4cc 100644
--- a/examples/sound/vagsample/main.c
+++ b/examples/sound/vagsample/main.c
@@ -149,8 +149,8 @@ void play_sample(int addr, int sample_rate) {
// dummy values that disable the ADSR envelope entirely).
SPU_CH_VOL_L(ch) = 0x3fff;
SPU_CH_VOL_R(ch) = 0x3fff;
- SPU_CH_ADSR1(ch) = 0x80ff;
- SPU_CH_ADSR2(ch) = 0x1fee;
+ SPU_CH_ADSR1(ch) = 0x00ff;
+ SPU_CH_ADSR2(ch) = 0x0000;
// Start the channel.
SpuSetKey(1, 1 << ch);