From 9560a1427aec1681c5d0c2bc30190ce4b1ad8557 Mon Sep 17 00:00:00 2001 From: spicyjpeg Date: Sun, 31 Jul 2022 18:04:59 +0200 Subject: Rewrite libpsxspu in C and update sound examples --- examples/sound/spustream/main.c | 48 ++++++++++++++--------------------------- 1 file changed, 16 insertions(+), 32 deletions(-) (limited to 'examples/sound/spustream') diff --git a/examples/sound/spustream/main.c b/examples/sound/spustream/main.c index 6b9db93..2ad122c 100644 --- a/examples/sound/spustream/main.c +++ b/examples/sound/spustream/main.c @@ -123,8 +123,8 @@ typedef struct { } DB; typedef struct { - DB db[2]; - uint32_t db_active; + DB db[2]; + int db_active; } CONTEXT; void init_context(CONTEXT *ctx) { @@ -170,23 +170,13 @@ void display(CONTEXT *ctx) { /* Stream interrupt handlers */ -// This is a silent looping sample used to keep unused SPU channels busy, -// preventing them from accidentally triggering the SPU RAM interrupt and -// throwing off the timing (all channels are always reading sample data, even -// when "stopped"). It is 64 bytes as that is the minimum size for SPU DMA -// transfers, however only the first 16 bytes are kept. The rest is going to be -// overwritten by chunks. -// https://problemkaputt.de/psx-spx.htm#spuinterrupt -const uint8_t SPU_DUMMY_BLOCK[] = { - 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -}; - // The first 4 KB of SPU RAM are reserved for capture buffers, so we have to -// place stream buffers after those. Sony's SPU library additionally places a -// dummy sample at 0x1000; we are going to do the same with the block above. +// place stream buffers after those. A dummy sample is additionally placed by +// default by the SPU library at 0x1000; it is going to be used here to keep +// unused SPU channels busy, preventing them from accidentally triggering the +// SPU RAM interrupt and throwing off the timing (all channels are always +// reading sample data, even when "stopped"). +// https://problemkaputt.de/psx-spx.htm#spuinterrupt #define DUMMY_BLOCK_ADDR 0x1000 #define BUFFER_START_ADDR 0x1010 #define CHUNK_SIZE (BUFFER_SIZE * NUM_CHANNELS) @@ -207,7 +197,7 @@ static volatile StreamContext str_ctx; // read from the CD and uploaded to SPU RAM. Due to DMA limitations it can't be // allocated on the stack (especially not in the interrupt callbacks' stack, // whose size is very limited). -static uint8_t sector_buffer[2048]; +static uint32_t sector_buffer[512]; void spu_irq_handler(void) { // Acknowledge the interrupt to ensure it can be triggered again. The only @@ -231,7 +221,7 @@ void spu_irq_handler(void) { str_ctx.spu_addr = BUFFER_START_ADDR + CHUNK_SIZE * str_ctx.db_active; SPU_IRQ_ADDR = SPU_RAM_ADDR(str_ctx.spu_addr); - for (uint32_t i = 0; i < NUM_CHANNELS; i++) + for (int i = 0; i < NUM_CHANNELS; i++) SPU_CH_LOOP_ADDR(i) = SPU_RAM_ADDR(str_ctx.spu_addr + BUFFER_SIZE * i); // Start loading the next chunk. cd_event_handler() will be called @@ -241,7 +231,7 @@ void spu_irq_handler(void) { CdControlF(CdlReadN, &pos); } -void cd_event_handler(int32_t event, uint8_t *payload) { +void cd_event_handler(int event, uint8_t *payload) { // Ignore all events other than a sector being ready. // TODO: read errors should be handled properly if (event != CdlDataReady) @@ -255,7 +245,7 @@ void cd_event_handler(int32_t event, uint8_t *payload) { // other buffer, as we're overriding loop addresses) at the end. // NOTE: this isn't actually necessary here as the stream converter script // already sets these flags in the file. - /*for (uint32_t i = 0; i < NUM_CHANNELS; i++) { + /*for (int i = 0; i < NUM_CHANNELS; i++) { if ( str_ctx.spu_pos >= (BUFFER_SIZE * i - 2048) && str_ctx.spu_pos < (BUFFER_SIZE * i) @@ -268,7 +258,7 @@ void cd_event_handler(int32_t event, uint8_t *payload) { // just treat the chunk as a single blob of data and copy it as-is; we only // have to trim the padding at the end (if any) to avoid overwriting other // data in SPU RAM. - uint32_t length = CHUNK_SIZE - str_ctx.spu_pos; + size_t length = CHUNK_SIZE - str_ctx.spu_pos; if (length > 2048) length = 2048; @@ -288,15 +278,9 @@ void cd_event_handler(int32_t event, uint8_t *payload) { /* Stream helpers */ void init_spu_channels(void) { - // Upload the dummy block to the SPU and play it on all channels, locking - // them up and stopping them from messing with the SPU interrupt. - // TODO: is this really necessary? (needs testing on real hardware) - SpuSetTransferStartAddr(DUMMY_BLOCK_ADDR); - SpuWrite(SPU_DUMMY_BLOCK, 64); - SPU_KEY_OFF = 0x00ffffff; - for (uint32_t i = 0; i < 24; i++) + for (int i = 0; i < 24; i++) SPU_CH_ADDR(i) = SPU_RAM_ADDR(DUMMY_BLOCK_ADDR); SPU_KEY_ON = 0x00ffffff; @@ -330,7 +314,7 @@ void init_stream(CdlFILE *file) { void start_stream(void) { SPU_KEY_OFF = CHANNEL_MASK; - for (uint32_t i = 0; i < NUM_CHANNELS; i++) { + for (int i = 0; i < NUM_CHANNELS; i++) { SPU_CH_ADDR(i) = SPU_RAM_ADDR(BUFFER_START_ADDR + BUFFER_SIZE * i); SPU_CH_FREQ(i) = SAMPLE_RATE; SPU_CH_ADSR(i) = 0x1fee80ff; // or 0x9fc080ff, 0xdff18087 @@ -429,7 +413,7 @@ int main(int argc, const char* argv[]) { // Only set the sample rate registers if necessary. if (pad->btn != 0xffff) { - for (uint32_t i = 0; i < NUM_CHANNELS; i++) + for (int i = 0; i < NUM_CHANNELS; i++) SPU_CH_FREQ(i) = sample_rate; } -- cgit v1.2.3 From 7abb3b78727c8d4672197951e62b1c5916b3a54a Mon Sep 17 00:00:00 2001 From: spicyjpeg Date: Thu, 11 Aug 2022 14:23:05 +0200 Subject: Clean up and add audio file to sound/spustream example --- examples/README.md | 11 +++++------ examples/graphics/gte/main.c | 2 +- examples/sound/spustream/CMakeLists.txt | 7 +++---- examples/sound/spustream/main.c | 28 ++++++++++++++++------------ examples/sound/spustream/stream.bin | Bin 0 -> 4685824 bytes libpsn00b/psxspu/common.c | 17 ++++++++++------- 6 files changed, 35 insertions(+), 30 deletions(-) create mode 100644 examples/sound/spustream/stream.bin (limited to 'examples/sound/spustream') diff --git a/examples/README.md b/examples/README.md index 4025a56..82d7698 100644 --- a/examples/README.md +++ b/examples/README.md @@ -25,7 +25,7 @@ Additional information may be found in the source code of each example. | [`io/system573`](./io/system573) | Konami System 573 (PS1-based arcade board) example | CD | | | [`lowlevel/cartrom`](./lowlevel/cartrom) | ROM firmware for cheat devices written using GNU GAS | ROM | 4 | | [`mdec/mdecimage`](./mdec/mdecimage) | Displays a (raw) MDEC format image using libpsxpress | EXE | | -| [`sound/spustream`](./sound/spustream) | Custom (non XA) CD-ROM audio streaming using the SPU | CD | 1 | +| [`sound/spustream`](./sound/spustream) | Custom (non XA) CD-ROM audio streaming using the SPU | CD | | | [`sound/vagsample`](./sound/vagsample) | Demonstrates playing VAG sound files using the SPU | EXE | | | [`system/childexec`](./system/childexec) | Loading a child program and returning to parent | EXE | | | [`system/console`](./system/console) | TTY based text console that interrupts gameplay | EXE | | @@ -35,10 +35,9 @@ Additional information may be found in the source code of each example. Notes: -1. `cdrom/cdxa` and `sound/spustream` do not come with example audio files. In - order to run these examples you'll have to provide your own files (and, in - the case of `spustream`, convert them using the included Python script) and - build the CD image manually. +1. `cdrom/cdxa` does not come with an example XA audio file. In order to run + this example you'll have to provide your own file and build the CD image + manually. 2. `demos/n00bdemo` suffers from flickering on real hardware, especially when masking/stencil buffering is used. 3. `io/pads` seems to work on real hardware, but fails to automatically enable @@ -85,4 +84,4 @@ are for rebuilding the examples *after* the SDK has been installed. CD images for each example. ----------------------------------------- -_Last updated on 2022-02-06 by spicyjpeg_ +_Last updated on 2022-08-11 by spicyjpeg_ diff --git a/examples/graphics/gte/main.c b/examples/graphics/gte/main.c index ba96ace..1257c88 100644 --- a/examples/graphics/gte/main.c +++ b/examples/graphics/gte/main.c @@ -265,7 +265,7 @@ void init() { SetDefDrawEnv( &db[0].draw, SCREEN_XRES, 0, SCREEN_XRES, SCREEN_YRES ); /* Enable draw area clear and dither processing */ - setRGB0( &db[0].draw, 0, 255, 0 ); + setRGB0( &db[0].draw, 63, 0, 127 ); db[0].draw.isbg = 1; db[0].draw.dtd = 1; diff --git a/examples/sound/spustream/CMakeLists.txt b/examples/sound/spustream/CMakeLists.txt index 9e84fa3..397796a 100644 --- a/examples/sound/spustream/CMakeLists.txt +++ b/examples/sound/spustream/CMakeLists.txt @@ -14,12 +14,11 @@ project( # TODO: add rules to actually generate a valid STREAM.BIN file file(GLOB _sources *.c) psn00bsdk_add_executable(spustream STATIC ${_sources}) -#psn00bsdk_add_cd_image(spustream_iso spustream iso.xml DEPENDS spustream) +psn00bsdk_add_cd_image(spustream_iso spustream iso.xml DEPENDS spustream) install( FILES - #${PROJECT_BINARY_DIR}/spustream.bin - #${PROJECT_BINARY_DIR}/spustream.cue - ${PROJECT_BINARY_DIR}/spustream.exe + ${PROJECT_BINARY_DIR}/spustream.bin + ${PROJECT_BINARY_DIR}/spustream.cue TYPE BIN ) diff --git a/examples/sound/spustream/main.c b/examples/sound/spustream/main.c index 2ad122c..6179179 100644 --- a/examples/sound/spustream/main.c +++ b/examples/sound/spustream/main.c @@ -51,10 +51,9 @@ * +----------+----------+----------+----------+----------+----------+---- * \________________________Chunk________________________/ * - * Such file isn't provided as PSn00bSDK doesn't yet have a tool for audio - * transcoding. A Python script is included to generate STREAM.BIN from one or - * more SPU ADPCM (.VAG) files, one for each channel (the .VAG format only - * supports mono). + * A Python script is included to generate STREAM.BIN from one or more SPU + * ADPCM (.VAG) files, one for each channel (the .VAG format only supports + * mono). * * Of course SPU streaming isn't the only way to play music, as the CD drive * can play CD-DA tracks and XA files natively with zero CPU overhead. However @@ -101,7 +100,7 @@ // size can be increased to get more idle time between CD reads, however it is // usually best to keep it to 1-2 seconds as SPU RAM is only 512 KB. #define SAMPLE_RATE 0x1000 // 44100 Hz -#define BUFFER_SIZE 26624 // (26624 / 16 * 28) / 44100 = 1.05 seconds +#define BUFFER_SIZE 0x6800 // (0x6800 / 16 * 28) / 44100 = 1.05 seconds #define NUM_CHANNELS 2 #define CHANNEL_MASK 0x03 @@ -250,7 +249,7 @@ void cd_event_handler(int event, uint8_t *payload) { str_ctx.spu_pos >= (BUFFER_SIZE * i - 2048) && str_ctx.spu_pos < (BUFFER_SIZE * i) ) - sector[(BUFFER_SIZE * i - str_ctx.spu_pos) - 15] = 0x03; + sector_buffer[(BUFFER_SIZE * i - str_ctx.spu_pos) - 15] = 0x03; }*/ // Copy the sector to SPU RAM, appending it to the buffer that is not @@ -268,7 +267,6 @@ void cd_event_handler(int event, uint8_t *payload) { // If the buffer has been filled completely, stop reading and re-enable the // SPU IRQ. - // TODO TODO: preload first sector if (str_ctx.spu_pos >= CHUNK_SIZE) { CdControlF(CdlPause, 0); SPU_CTRL |= 0x0040; @@ -277,11 +275,17 @@ void cd_event_handler(int event, uint8_t *payload) { /* Stream helpers */ -void init_spu_channels(void) { +// This isn't actually required for this example, however it is necessary if +// you want to allocate the stream buffers into a region of SPU RAM that was +// previously used (to make sure the IRQ isn't going to be triggered by any +// inactive channels). +void reset_spu_channels(void) { SPU_KEY_OFF = 0x00ffffff; - for (int i = 0; i < 24; i++) + for (int i = 0; i < 24; i++) { SPU_CH_ADDR(i) = SPU_RAM_ADDR(DUMMY_BLOCK_ADDR); + SPU_CH_FREQ(i) = 0x1000; + } SPU_KEY_ON = 0x00ffffff; } @@ -308,7 +312,7 @@ void init_stream(CdlFILE *file) { spu_irq_handler(); while (str_ctx.spu_pos < CHUNK_SIZE) - __asm__("nop"); + __asm__ volatile(""); } void start_stream(void) { @@ -317,7 +321,7 @@ void start_stream(void) { for (int i = 0; i < NUM_CHANNELS; i++) { SPU_CH_ADDR(i) = SPU_RAM_ADDR(BUFFER_START_ADDR + BUFFER_SIZE * i); SPU_CH_FREQ(i) = SAMPLE_RATE; - SPU_CH_ADSR(i) = 0x1fee80ff; // or 0x9fc080ff, 0xdff18087 + SPU_CH_ADSR(i) = 0x1fee80ff; } // Unmute the channels and route them for stereo output. You'll want to @@ -345,7 +349,7 @@ int main(int argc, const char* argv[]) { SHOW_STATUS("INITIALIZING\n"); SpuInit(); CdInit(); - init_spu_channels(); + reset_spu_channels(); SHOW_STATUS("LOCATING STREAM FILE\n"); diff --git a/examples/sound/spustream/stream.bin b/examples/sound/spustream/stream.bin new file mode 100644 index 0000000..e53b726 Binary files /dev/null and b/examples/sound/spustream/stream.bin differ diff --git a/libpsn00b/psxspu/common.c b/libpsn00b/psxspu/common.c index c1e8cab..55a3dba 100644 --- a/libpsn00b/psxspu/common.c +++ b/libpsn00b/psxspu/common.c @@ -46,13 +46,6 @@ void SpuInit(void) { SPU_EXT_VOL_L = 0; SPU_EXT_VOL_R = 0; - for (int i = 0; i < 24; i++) { - SPU_CH_VOL_L(i) = 0; - SPU_CH_VOL_R(i) = 0; - SPU_CH_FREQ(i) = 0; - SPU_CH_ADDR(i) = 0; - } - DMA_DPCR |= 0x000b0000; // Enable DMA4 DMA_CHCR(4) = 0x00000201; // Stop DMA4 @@ -68,8 +61,18 @@ void SpuInit(void) { for (int i = 7; i; i--) SPU_DATA = 0x0000; + // "Play" the dummy block on all channels. This will reset the start + // address and ADSR envelope status of each channel. + for (int i = 0; i < 24; i++) { + SPU_CH_VOL_L(i) = 0; + SPU_CH_VOL_R(i) = 0; + SPU_CH_FREQ(i) = 0x1000; + SPU_CH_ADDR(i) = WRITABLE_AREA_ADDR; + } + // Sony's implementation leaves everything muted, however it makes sense to // turn up at least the master and CD audio volume by default. + SPU_KEY_ON = 0x00ffffff; SPU_MASTER_VOL_L = 0x3fff; SPU_MASTER_VOL_R = 0x3fff; SPU_CD_VOL_L = 0x3fff; -- cgit v1.2.3