diff --git a/Makefile b/Makefile index 50111cd..b631ab7 100644 --- a/Makefile +++ b/Makefile @@ -48,6 +48,8 @@ hello_bs: $(MAKE) -C hello_bs all hello_str: $(MAKE) -C hello_str all +hello_strplay: + $(MAKE) -C hello_strplay all clean: $(MAKE) -C hello_2pads clean @@ -76,6 +78,7 @@ clean: $(MAKE) -C hello_xa cleansub $(MAKE) -C hello_bs cleansub $(MAKE) -C hello_str cleansub + $(MAKE) -C hello_strplay cleansub all: $(MAKE) -C hello_2pads @@ -102,11 +105,12 @@ all: $(MAKE) -C hello_xa all $(MAKE) -C hello_bs all $(MAKE) -C hello_str all + $(MAKE) -C hello_strplay all # declare phony rules .PHONY: hello_2pads hello_cube hello_cubetex hello_poly_fun hello_gte_opti \ hello_light hello_multivag hello_pad hello_poly hello_poly_ft hello_poly_gt \ hello_poly_gt_tw hello_poly_inline hello_sio hello_sprt hello_tile \ - hello_vag hello_world hello_cdda hello_cd hello_xa hello_bs hello_str \ + hello_vag hello_world hello_cdda hello_cd hello_xa hello_bs hello_str hello_strplay \ hello_poly_stp hello_cubetex_stp \ clean all diff --git a/hello_strplay/Makefile b/hello_strplay/Makefile new file mode 100644 index 0000000..b292801 --- /dev/null +++ b/hello_strplay/Makefile @@ -0,0 +1,12 @@ +.PHONY: all cleansub +all: + mkpsxiso -y ./isoconfig.xml +cleansub: + $(MAKE) clean + rm -f hello_strplay.cue hello_strplay.bin + +TARGET = hello_strplay + +SRCS = hello_strplay.c \ + +include ../common.mk diff --git a/hello_strplay/README.md b/hello_strplay/README.md new file mode 100644 index 0000000..5e62972 --- /dev/null +++ b/hello_strplay/README.md @@ -0,0 +1,36 @@ +This example show how to use Lameguy64's [STR playback library](https://github.com/ABelliqueux/nolibgs_hello_worlds/tree/main/hello_str#str-playback-library). + +For a barebone example, see the [hello_str](https://github.com/ABelliqueux/nolibgs_hello_worlds/tree/main/hello_str) example. + +## Compiling + +You need [mkpsxiso](https://github.com/Lameguy64/mkpsxiso) in your $PATH to generate a PSX disk image. +Typing +```bash +make +``` +in a terminal will compile and generate the bin/cue files. + +Typing +```bash +make cleansub +``` +will clean the current directory. + +## STR playback library + +@Lameguy64 has spent some time making a STR playback library that's easily included in a project : + +> One thing that I find somewhat missing here is a decent piece of code for playing STR video files easily. So, what I did was take the old and messy PsyQ STR player example, clean it up entirely, and finally make it into a cute little c library for easy implementation. + +Original post : http://www.psxdev.net/forum/viewtopic.php?t=507 +Original download link : https://www.mediafire.com/download/s61u86sxd1djncy/strplay.7z +Mirror : http://psx.arthus.net/code/strplay.7z + +## Video encoding and more informations + +See the [wiki](https://github.com/ABelliqueux/nolibgs_hello_worlds/wiki/STR). + +## Video credits + +The video and song used in this example are by Nina Paley : https://archive.org/details/CopyingIsNotTheft-ScratchTrack1280X720Hdv diff --git a/hello_strplay/hello_strplay.c b/hello_strplay/hello_strplay.c new file mode 100644 index 0000000..2257d5e --- /dev/null +++ b/hello_strplay/hello_strplay.c @@ -0,0 +1,115 @@ +// Using the strplay library. +// Schnappy 07-2021 +// based on Lameguy64 strplay library : http://www.psxdev.net/forum/viewtopic.php?t=507 +// Original PsyQ sample code : /psyq/addons/cd/MOVIE +// Video to STR conversion : https://github.com/ABelliqueux/nolibgs_hello_worlds/wiki/STR +#include +#include +#include +#include +#include +// CD library +#include +// CODEC library +#include +// include Lameguy64's library +#include "strplay.c" + +#define VMODE 0 // Video Mode : 0 : NTSC, 1: PAL +#define TRUECOL 1 // 0 : 16bpp, 1: 24bpp +#define SCREENXRES 320 // Screen width +#define SCREENYRES 240 + (VMODE << 4) // Screen height : If VMODE is 0 = 240, if VMODE is 1 = 256 +#define CENTERX SCREENXRES/2 // Center of screen on x +#define CENTERY SCREENYRES/2 // Center of screen on y +#define MARGINX 8 // margins for text display +#define MARGINY 16 +#define FONTSIZE 8 * 7 // Text Field Height +DISPENV disp[2]; // Double buffered DISPENV and DRAWENV +DRAWENV draw[2]; +short db = 0; // index of which buffer is used, values 0, 1 + +STRFILE StrFile[] = { + // File name Resolution Frame count + "\\COPY.STR;1", 320, 240, 893 +}; + +// When using RGB24, a special routine has to be setup as a callback function to avoid MDEC/CDROM conflict +// See http://psx.arthus.net/sdk/Psy-Q/DOCS/LibRef47.pdf , p.713 +static void strCheckRGB24(); + +void init(void) +{ + ResetCallback(); + ResetGraph(0); // Initialize drawing engine with a complete reset (0) + SetDefDispEnv(&disp[0], 0, 0 , SCREENXRES, SCREENYRES); // Set display area for both &disp[0] and &disp[1] + SetDefDispEnv(&disp[1], 0, SCREENYRES, SCREENXRES, SCREENYRES); // &disp[0] is on top of &disp[1] + SetDefDrawEnv(&draw[0], 0, SCREENYRES, SCREENXRES, SCREENYRES); // Set draw for both &draw[0] and &draw[1] + SetDefDrawEnv(&draw[1], 0, 0 , SCREENXRES, SCREENYRES); // &draw[0] is below &draw[1] + // Set video mode + #if VMODE + SetVideoMode(MODE_PAL); + disp[0].disp.y = 8; + disp[1].disp.y = 8; + #endif + SetDispMask(1); // Display on screen + setRGB0(&draw[0], 155, 0, 150); // set color for first draw area + setRGB0(&draw[1], 155, 0, 150); // set color for second draw area + draw[0].isbg = 0; // set mask for draw areas. 1 means repainting the area with the RGB color each frame + draw[1].isbg = 0; + #if TRUECOL + disp[0].isrgb24 = 1; + disp[1].isrgb24 = 1; + #endif + PutDispEnv(&disp[db]); // set the disp and draw environnments + PutDrawEnv(&draw[db]); + FntLoad(960, 0); // Load font to vram at 960,0(+128) + FntOpen(MARGINX, MARGINY, SCREENXRES - MARGINX * 2, FONTSIZE, 0, 280 ); // FntOpen(x, y, width, height, black_bg, max. nbr. chars +} +void display(void) +{ + DrawSync(0); // Wait for all drawing to terminate + VSync(0); // Wait for the next vertical blank + PutDispEnv(&disp[db]); // set alternate disp and draw environnments + PutDrawEnv(&draw[db]); + db = !db; // flip db value (0 or 1) +} + +void main() { + + // Reset and initialize stuff + ResetCallback(); + CdInit(); + PadInit(0); + ResetGraph(0); + SetGraphDebug(0); + + // Play the video in loop + while (1) { + + if (PlayStr(320, 240, 0, 0, &StrFile[0]) == 0) // If player presses Start + break; // Exit the loop + + } + +} + +static void strCheckRGB24() { + + /* From http://psx.arthus.net/sdk/Psy-Q/DOCS/, p.713 + * When playing a movie in 24-bit mode, there is a potential hardware conflict between the CD subsystem + * and the MDEC image decompression system which can result in corrupted data. To avoid this, + * StCdInterrupt() may defer transferring a sector and instead set a flag variable called StCdInterFlag to + * indicate that a CD sector is ready to be transferred. Once the MDEC is finished transferring data, your + * application should check StCdIntrFlag and call StCdInterrupt() directly if it is set. + */ + #if TRUECOL + extern u_long StCdIntrFlag; + // If flag was set + if ( StCdIntrFlag ) { + // Trigger data transfer + StCdInterrupt(); + // Reset flag + StCdIntrFlag = 0; + } + #endif +} diff --git a/hello_strplay/isoconfig.xml b/hello_strplay/isoconfig.xml new file mode 100644 index 0000000..f7d34f5 --- /dev/null +++ b/hello_strplay/isoconfig.xml @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hello_strplay/str/copyings.str b/hello_strplay/str/copyings.str new file mode 100644 index 0000000..153d209 Binary files /dev/null and b/hello_strplay/str/copyings.str differ diff --git a/hello_strplay/strplay.c b/hello_strplay/strplay.c new file mode 100644 index 0000000..8745a4d --- /dev/null +++ b/hello_strplay/strplay.c @@ -0,0 +1,407 @@ +/* + + Simple STR Player Library by Lameguy64 + (?) 2014 Meido-Tek Productions/Lame Studios + + Original PsyQ sample programmed by: + Yutaka + Suzu + Masa + Ume + + Code heavily refined by: + Lameguy64 + + What Lameguy did to the original code: + - Removed all of the icky yucky UTF-16 junk + - Fixed all crap-English comments + - Greatly improved code formatting + - Renamed variables with better names + - Buffer arrays are now initialized only when the playback routine is called + + Libraries Required: + libetc + libgte + libgpu + libcd + + Function list: + + int PlayStr(int xres, int yres, int xpos, int ypos, STRFILE *str) + + Parameters: + xres, yres - Video resolution. + xpos, ypos - Framebuffer offset on where to draw the video. + STRFILE *str - STRFILE entry to play. + + Notes: + Just make sure that you have at least 192KB of free memory before calling + the PlayStr function otherwise, the console will crash. As for the video + resolution, it must be equal or less than 256 as the second buffer + is located directly below the first buffer. + + Note: + + If compiling the sample fails, open your psyq.ini file located in \psyq\bin and + append the following into the stdlib line: + + libds.lib libpress.lib + +*/ + +#define IS_RGB24 1 // 0:16-bit playback, 1:24-bit playback (recommended for quality) +#define RING_SIZE 32 // Ring Buffer size (32 sectors seems good enough) + +#if IS_RGB24==1 + #define PPW 3/2 // pixels per short word + #define DCT_MODE 3 // Decode mode for DecDCTin routine +#else + #define PPW 1 + #define DCT_MODE 2 +#endif + + +// A simple struct to make STR handling a bit easier +typedef struct { + char FileName[32]; + int Xres; + int Yres; + int NumFrames; +} STRFILE; + +// Decode environment +typedef struct { + u_long *VlcBuff_ptr[2]; // Pointers to the VLC buffers + u_short *ImgBuff_ptr[2]; // Pointers to the frame slice buffers + RECT rect[2]; // VRAM parameters on where to draw the frame data to + RECT slice; // Frame slice parameters for loading into VRAM + int VlcID; // Current VLC buffer ID + int ImgID; // Current slice buffer ID + int RectID; // Current video buffer ID + int FrameDone; // Frame decode completion flag +} STRENV; + +// A bunch of internal variables +static STRENV strEnv; + +static int strScreenWidth=0,strScreenHeight=0; +static int strFrameX=0,strFrameY=0; +static int strNumFrames=0; + +static int strFrameWidth=0,strFrameHeight=0; // Frame size of STR file +static int strPlayDone=0; // Playback completion flag + +// Main function prototypes +int PlayStr(int xres, int yres, int xpos, int ypos, STRFILE *str); + +// Internal function prototypes +static void strDoPlayback(STRFILE *str); +static void strCallback(); +static void strNextVlc(STRENV *strEnv); +static void strSync(STRENV *strEnv, int mode); +static u_long *strNext(STRENV *strEnv); +static void strKickCD(CdlLOC *loc); + + +int PlayStr(int xres, int yres, int xpos, int ypos, STRFILE *str) { + + /* + Main STR playback routine. + + Returns: + 0 - Playback failed or was skipped. + 1 - Playback was finished. + */ + + strNumFrames=str->NumFrames; + strScreenWidth=xres; + strScreenHeight=yres; + strFrameX=xpos; + strFrameY=ypos; + + strPlayDone=0; + strDoPlayback(str); + + if (strPlayDone == 0) + return(0); + else + return(1); + +} + +static void strDoPlayback(STRFILE *str) { + + /* + Does the actual STR playback. + */ + + int id; // Display buffer ID + DISPENV disp; // Display environment + CdlFILE file; // File info of video file + + // Buffers initialized here so we won't waste too much memory for playing FMVs + // (just make sure you have at least 192KB of free memory before calling this routine) + u_long RingBuff[RING_SIZE*SECTOR_SIZE]; // Ring buffer + u_long VlcBuff[2][str->Xres/2*str->Yres]; // VLC buffers + u_short ImgBuff[2][16*PPW*str->Yres]; // Frame 'slice' buffers + + // Set display mask so we won't see garbage while the stream is being prepared + SetDispMask(0); + + // Get the CD location of the STR file to play + if (CdSearchFile(&file, str->FileName) == 0) { + #ifdef DEBUG + printf("ERROR: I cannot find video file %s\n", str->FileName); + #endif + SetDispMask(1); + return; + } + + // Setup the buffer pointers + strEnv.VlcBuff_ptr[0] = &VlcBuff[0][0]; + strEnv.VlcBuff_ptr[1] = &VlcBuff[1][0]; + strEnv.VlcID = 0; + strEnv.ImgBuff_ptr[0] = &ImgBuff[0][0]; + strEnv.ImgBuff_ptr[1] = &ImgBuff[1][0]; + strEnv.ImgID = 0; + + // Setup the display buffers on VRAM + strEnv.rect[0].x = strFrameX; // First page + strEnv.rect[0].y = strFrameY; + strEnv.rect[1].x = strFrameX; // Second page + strEnv.rect[1].y = strFrameY+strScreenHeight; + strEnv.RectID = 0; + + // Set the parameters for uploading frame slices + strEnv.slice.x = strFrameX; + strEnv.slice.y = strFrameY; + strEnv.slice.w = 16*PPW; + strEnv.FrameDone = 0; + + // Reset the MDEC + DecDCTReset(0); + // Set callback routine + DecDCToutCallback(strCallback); + // Set ring buffer + StSetRing(RingBuff, RING_SIZE); + // Set streaming parameters + StSetStream(IS_RGB24, 1, 0xffffffff, 0, 0); + // Begin streaming! + strKickCD(&file.pos); + + // Load the first frame of video before entering main loop + strNextVlc(&strEnv); + + while (1) { + + // Decode the compressed frame data + DecDCTin(strEnv.VlcBuff_ptr[strEnv.VlcID], DCT_MODE); + + // Prepare to receive the decoded image data from the MDEC + DecDCTout((u_long*)strEnv.ImgBuff_ptr[strEnv.ImgID], strEnv.slice.w*strEnv.slice.h/2); + + // Get the next frame + strNextVlc(&strEnv); + + // Wait for the frame to finish decoding + strSync(&strEnv, 0); + + // Switch between the display buffers per frame + id = strEnv.RectID? 0: 1; + SetDefDispEnv(&disp, 0, strScreenHeight*id, strScreenWidth*PPW, strScreenHeight); + + // Set parameters for 24-bit color mode + #if IS_RGB24 == 1 + disp.isrgb24 = IS_RGB24; + disp.disp.w = disp.disp.w*2/3; + #endif + + VSync(0); // VSync to avoid screen tearing + PutDispEnv(&disp); // Apply the video parameters + SetDispMask(1); // Remove the display mask + + if(strPlayDone == 1) { + break; + } + + if(PadRead(1) & PADstart) { // stop button pressed exit animation routine + break; + } + + } + + // Shutdown streaming + DecDCToutCallback(0); + StUnSetRing(); + CdControlB(CdlPause, 0, 0); + +} +static void strCallback() { + + /* + Callback routine which is called whenever a slice has finished decoding. + All it does is transfer the decoded slice into VRAM. + */ + + RECT TransferRect; + int id; + + // In 24-bit color, StCdInterrupt must be called in every callback + #if IS_RGB24==1 + extern u_long StCdIntrFlag; + if (StCdIntrFlag) { + StCdInterrupt(); + StCdIntrFlag = 0; + } + #endif + + id = strEnv.ImgID; + TransferRect = strEnv.slice; + + // Switch slice buffers + strEnv.ImgID = strEnv.ImgID? 0:1; + + // Step to next slice + strEnv.slice.x += strEnv.slice.w; + + // Frame not yet decoded completely? + if (strEnv.slice.x < strEnv.rect[strEnv.RectID].x + strEnv.rect[strEnv.RectID].w) { + + // Prepare for next slice + DecDCTout((u_long*)strEnv.ImgBuff_ptr[strEnv.ImgID], strEnv.slice.w*strEnv.slice.h/2); + + } else { // Frame has been decoded completely + + // Set the FrameDone flag + strEnv.FrameDone = 1; + + // Switch display buffers + strEnv.RectID = strEnv.RectID? 0: 1; + strEnv.slice.x = strEnv.rect[strEnv.RectID].x; + strEnv.slice.y = strEnv.rect[strEnv.RectID].y; + + } + + // Transfer the slice into VRAM + LoadImage(&TransferRect, (u_long *)strEnv.ImgBuff_ptr[id]); + +} +static void strNextVlc(STRENV *strEnv) { + + /* + Performs VLC decoding and grabs a frame from the stream. + */ + + int cnt=WAIT_TIME; + u_long *next; + u_long *strNext(); + + // Grab a frame from the stream + while ((next = strNext(strEnv)) == 0) { + + if (--cnt == 0) // Timeout handler + return; + + } + + // Switch VLC buffers + strEnv->VlcID = strEnv->VlcID? 0: 1; + + // Decode the VLC + DecDCTvlc(next, strEnv->VlcBuff_ptr[strEnv->VlcID]); + + // Free the ring buffer + StFreeRing(next); + +} +static u_long *strNext(STRENV *strEnv) { + + /* + Grabs a frame of video from the stream. + */ + + u_long *addr; + StHEADER *sector; + int cnt = WAIT_TIME; + + // Grab a frame + while (StGetNext((u_long **)&addr,(u_long **)§or)) { + + if (--cnt == 0) // Timeout handler + return(0); + + } + + // If the frame's number has reached number of frames the video has, + // set the strPlayDone flag. + if (sector->frameCount >= strNumFrames) + strPlayDone = 1; + + + // if the resolution is differ to previous frame, clear frame buffer + if (strFrameWidth != sector->width || strFrameHeight != sector->height) { + + RECT rect; + setRECT(&rect, 0, 0, strScreenWidth * PPW, strScreenHeight*2); + ClearImage(&rect, 0, 0, 0); + + strFrameWidth = sector->width; + strFrameHeight = sector->height; + + } + + + // set STRENV according to the data on the STR format + strEnv->rect[0].w = strEnv->rect[1].w = strFrameWidth*PPW; + strEnv->rect[0].h = strEnv->rect[1].h = strFrameHeight; + strEnv->slice.h = strFrameHeight; + + return(addr); + +} +static void strSync(STRENV *strEnv, int mode) { + + /* + Waits for the frame to finish decoding. + */ + + u_long cnt = WAIT_TIME; + + // Wait for the frame to finish decoding + while (strEnv->FrameDone == 0) { + if (--cnt == 0) { // Timeout handler + // If a timeout occurs, force switching buffers + #ifdef DEBUG + printf("ERROR: A frame cannot be played!\n"); + #endif + strEnv->FrameDone = 1; + strEnv->RectID = strEnv->RectID? 0: 1; + strEnv->slice.x = strEnv->rect[strEnv->RectID].x; + strEnv->slice.y = strEnv->rect[strEnv->RectID].y; + } + } + + strEnv->FrameDone = 0; + +} +static void strKickCD(CdlLOC *loc) { + + /* + Begins CD streaming. + */ + + u_char param=CdlModeSpeed; + + loop: + + // Seek to the STR file to play + while (CdControl(CdlSetloc, (u_char *)loc, 0) == 0); + while (CdControl(CdlSetmode, ¶m, 0) == 0); + + VSync(3); // Wait for 3 screen cycles before changing drive speed + + // Start streaming + if(CdRead2(CdlModeStream|CdlModeSpeed|CdlModeRT) == 0) + goto loop; // If it fails, try again + +} diff --git a/hello_strplay/system.cnf b/hello_strplay/system.cnf new file mode 100644 index 0000000..ae2db18 --- /dev/null +++ b/hello_strplay/system.cnf @@ -0,0 +1,4 @@ +BOOT=cdrom:\SCES_313.37;1 +TCB=4 +EVENT=10 +STACK=801FFFF0