diff options
| author | spicyjpeg <thatspicyjpeg@gmail.com> | 2022-12-18 16:05:43 +0100 |
|---|---|---|
| committer | spicyjpeg <thatspicyjpeg@gmail.com> | 2022-12-18 16:05:43 +0100 |
| commit | b58a37bdac753ceace35761ef474c198a3f18e12 (patch) | |
| tree | f7747c222e47479b2b39ad04b39b82d991ae0f0a /examples | |
| parent | d84bc4939607442e41888521939ef10908c87ce3 (diff) | |
| download | psn00bsdk-b58a37bdac753ceace35761ef474c198a3f18e12.tar.gz | |
Clean up MDEC and sound examples
Diffstat (limited to 'examples')
| -rw-r--r-- | examples/mdec/strvideo/main.c | 93 | ||||
| -rw-r--r-- | examples/sound/cdstream/main.c | 4 | ||||
| -rw-r--r-- | examples/sound/cdstream/stream.vag | bin | 4646912 -> 4687872 bytes | |||
| -rw-r--r-- | examples/sound/spustream/interleave.py | 13 | ||||
| -rw-r--r-- | examples/sound/spustream/main.c | 51 | ||||
| -rw-r--r-- | examples/sound/spustream/stream.vag | bin | 1140736 -> 1140736 bytes | |||
| -rw-r--r-- | examples/sound/vagsample/main.c | 4 |
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(§or_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(§or_header, sizeof(STR_Header) / 4); + + if (sector_header.magic != 0x0160) { str_ctx.frame_ready = -1; return; } - STR_Header *header = §or_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 Binary files differindex a6faf74..aab82ba 100644 --- a/examples/sound/cdstream/stream.vag +++ b/examples/sound/cdstream/stream.vag 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 Binary files differindex e1cb4f4..d75408b 100644 --- a/examples/sound/spustream/stream.vag +++ b/examples/sound/spustream/stream.vag 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); |
