diff --git a/README.md b/README.md index 92d3787..ef57c96 100644 --- a/README.md +++ b/README.md @@ -117,6 +117,7 @@ That's it! When you'll type `make` next time, it should convert your files to .o # Links and Doc * [https://psx.arthus.net/starting.html](Getting started) + * [http://psx.arthus.net/sdk/Psy-Q/DOCS/](PsyQ Doc) * [https://ps1.consoledev.net/](Ps1 dev links) * [http://psxdev.net/](psxdev.net) * [https://discord.com/invite/N2mmwp?utm_source=Discord%20Widget&utm_medium=Connect](psxdev Discord) diff --git a/VAG/hello_poly.vag b/VAG/hello_poly.vag new file mode 100644 index 0000000..fd984d8 Binary files /dev/null and b/VAG/hello_poly.vag differ diff --git a/hello_vag.c b/hello_vag.c new file mode 100644 index 0000000..3ea79fb --- /dev/null +++ b/hello_vag.c @@ -0,0 +1,275 @@ +// VAGDEMO2020 by Schnappy +// December 2020 +// Based on VAGDEMO_FIXED by Yagotzirck +// Based on VAGDEMO by Shadow +// based on psyq/addons/sound/TUTO3.C +// +// +// Load a VAG file to SPU sound buffer and play it back. +// +// WAV creation: use ffmpeg to create a 16-bit ADPCM mono WAV file - change -ar to reduce filesize (and quality) +// $ ffmpeg -i input.mp3 -acodec pcm_s16le -ac 1 -ar 44100 output.wav +// +// WAV to VAG convertion using WAV2VAG : https://github.com/ColdSauce/psxsdk/blob/master/tools/wav2vag.c +// change -freq according to the -ar setting above +// $ wav2vag input.wav output.vag -sraw16 -freq=44100 (-L) +// +// Alternatively, you can use PsyQ VAGEDIT.EXE to change the sampling frequency of an existing VAG file. +// +// Docs : see libformat47.pdf p.209 +// libover47.pdf, p.271 +// libref47.pdf, p.980 +// URLS : http://psx.arthus.net/code/VAG/ + +#include +#include + +#include +#include +#include + + +// Sound system +#include +#include + +#define VMODE 0 // Video Mode : 0 : NTSC, 1: PAL + +#define SCREENXRES 320 +#define SCREENYRES 240 + +#define CENTERX SCREENXRES/2 +#define CENTERY SCREENYRES/2 + +#define MARGINX 0 // margins for text display +#define MARGINY 32 + +#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 + +// Sound stuff + +#define MALLOC_MAX 3 // Max number of time we can call SpuMalloc + +//~ // convert Little endian to Big endian +#define SWAP_ENDIAN32(x) (((x)>>24) | (((x)>>8) & 0xFF00) | (((x)<<8) & 0x00FF0000) | ((x)<<24)) + +typedef struct VAGheader{ // All the values in this header must be big endian + char id[4]; // VAGp 4 bytes -> 1 char * 4 + unsigned int version; // 4 bytes + unsigned int reserved; // 4 bytes + unsigned int dataSize; // (in bytes) 4 bytes + unsigned int samplingFrequency;// 4 bytes + char reserved2[12]; // 12 bytes -> 1 char * 12 + char name[16]; // 16 bytes -> 1 char * 16 + // Waveform data after that +}VAGhdr; + +SpuCommonAttr commonAttributes; // structure for changing common voice attributes +SpuVoiceAttr voiceAttributes ; // structure for changing individual voice attributes + +u_long vag_spu_address; // address allocated in memory for first sound file +u_long vag1_spu_address; // address allocated in memory for second sound file + +// DEBUG : these allow printing values for debugging + +u_long spu_start_address; +u_long get_start_addr; +u_long transSize; + +// Memory management table ; allow MALLOC_MAX calls to SpuMalloc() - ibref47.pdf p.1044 +char spu_malloc_rec[SPU_MALLOC_RECSIZ * (2 + MALLOC_MAX+1)]; + +// VAG files + +// We're using GrumpyCoder's Nugget wrapper to compile the code with a modern GCC : https://github.com/grumpycoders/pcsx-redux/tree/main/src/mips/psyq +// To include binary files in the exe, add your VAG files to the SRCS variable in Makefile +// and in common.mk, add this rule to include *.vag files : +// +//~ %.o: %.vag + //~ $(PREFIX)-objcopy -I binary --set-section-alignment .data=4 --rename-section .data=.rodata,alloc,load,readonly,data,contents -O elf32-tradlittlemips -B mips $< $@ + + +// hello_poly.vag - 44100 Khz +extern unsigned char _binary_VAG_hello_poly_vag_start[]; // filename must begin with _binary_ followed by the full path, with . and / replaced, and then suffixed with _ and end with _start[]; or end[]; +extern unsigned char _binary_VAG_hello_poly_vag_end[]; // https://discord.com/channels/642647820683444236/663664210525290507/780866265077383189 + +void initGraph(void) +{ + ResetGraph(0); + + SetDefDispEnv(&disp[0], 0, 0, SCREENXRES, SCREENYRES); + SetDefDispEnv(&disp[1], 0, SCREENYRES, SCREENXRES, SCREENYRES); + + SetDefDrawEnv(&draw[0], 0, SCREENYRES, SCREENXRES, SCREENYRES); + SetDefDrawEnv(&draw[1], 0, 0, SCREENXRES, SCREENYRES); + + if (VMODE) + { + SetVideoMode(MODE_PAL); + disp[0].screen.y += 8; + disp[1].screen.y += 8; + } + + setRGB0(&draw[0], 50, 50, 50); + setRGB0(&draw[1], 50, 50, 50); + + draw[0].isbg = 1; + draw[1].isbg = 1; + + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + + FntLoad(960, 0); + FntOpen(8, 60, 304, 200, 0, 500 ); + +} + +void display(void) +{ + DrawSync(0); + VSync(0); + + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + + SetDispMask(1); + + db = !db; + +} + + +// Audio initialisation & functions + +void initSnd(void){ + + SpuInitMalloc(MALLOC_MAX, spu_malloc_rec); // Maximum number of blocks, mem. management table address. + + commonAttributes.mask = (SPU_COMMON_MVOLL | SPU_COMMON_MVOLR); // Mask which attributes to set + commonAttributes.mvol.left = 0x3fff; // Master volume left + commonAttributes.mvol.right = 0x3fff; // see libref47.pdf, p.1058 + + SpuSetCommonAttr(&commonAttributes); // set attributes + + SpuSetIRQ(SPU_OFF); +} + +u_long sendVAGtoRAM(unsigned int VAG_data_size, unsigned char *VAG_data){ + u_long size; + + SpuSetTransferMode(SpuTransByDMA); // DMA transfer; can do other processing during transfer + + size = SpuWrite (VAG_data + sizeof(VAGhdr), VAG_data_size); // transfer VAG_data_size bytes from VAG_data address to sound buffer + + SpuIsTransferCompleted (SPU_TRANSFER_WAIT); // Checks whether transfer is completed and waits for completion + + return size; +} + +void setVoiceAttr(unsigned int pitch, long channel, unsigned long soundAddr ){ + + voiceAttributes.mask= //~ Attributes (bit string, 1 bit per attribute) + ( + SPU_VOICE_VOLL | + SPU_VOICE_VOLR | + SPU_VOICE_PITCH | + SPU_VOICE_WDSA | + SPU_VOICE_ADSR_AMODE | + SPU_VOICE_ADSR_SMODE | + SPU_VOICE_ADSR_RMODE | + SPU_VOICE_ADSR_AR | + SPU_VOICE_ADSR_DR | + SPU_VOICE_ADSR_SR | + SPU_VOICE_ADSR_RR | + SPU_VOICE_ADSR_SL + ); + + voiceAttributes.voice = channel; //~ Voice (low 24 bits are a bit string, 1 bit per voice ) + + voiceAttributes.volume.left = 0x1000; //~ Volume + voiceAttributes.volume.right = 0x1000; //~ Volume + + voiceAttributes.pitch = pitch; //~ Interval (set pitch) + voiceAttributes.addr = soundAddr; //~ Waveform data start address + + voiceAttributes.a_mode = SPU_VOICE_LINEARIncN; //~ Attack rate mode = Linear Increase - see libref47.pdf p.1091 + voiceAttributes.s_mode = SPU_VOICE_LINEARIncN; //~ Sustain rate mode = Linear Increase + voiceAttributes.r_mode = SPU_VOICE_LINEARDecN; //~ Release rate mode = Linear Decrease + + voiceAttributes.ar = 0x0; //~ Attack rate + voiceAttributes.dr = 0x0; //~ Decay rate + voiceAttributes.rr = 0x0; //~ Release rate + voiceAttributes.sr = 0x0; //~ Sustain rate + voiceAttributes.sl = 0xf; //~ Sustain level + + SpuSetVoiceAttr(&voiceAttributes); // set attributes + +} + +void playSFX(void){ + SpuSetKey(SpuOn,SPU_0CH | SPU_2CH); // Set several channels by ORing each channel bit ; channel 0 and 2 here. +} + +int main(void) +{ + short counter = 0; + + const VAGhdr * VAGfileHeader = (VAGhdr *) _binary_VAG_hello_poly_vag_start; // get header of VAG file + + // From libover47.pdf : + // The sampling frequency of the original audio file can be used to determine the pitch + // at which to play the VAG. pitch = (sampling frequency << 12)/44100L + // Ex: 44.1kHz=0x1000 22.05kHz=0x800 etc + + unsigned int pitch = (SWAP_ENDIAN32(VAGfileHeader->samplingFrequency) << 12) / 44100L; + + SpuInit(); // Initialize SPU. Called only once. + + initSnd(); + + //~ // First VAG + + vag_spu_address = SpuMalloc(SWAP_ENDIAN32(VAGfileHeader->dataSize)); // Allocate an area of dataSize bytes in the sound buffer. + + spu_start_address = SpuSetTransferStartAddr(vag_spu_address); // Sets a starting address in the sound buffer + + get_start_addr = SpuGetTransferStartAddr(); // SpuGetTransferStartAddr() returns current sound buffer transfer start address. + + transSize = sendVAGtoRAM(SWAP_ENDIAN32(VAGfileHeader->dataSize), _binary_VAG_hello_poly_vag_start); + + // set VAG to channel + + setVoiceAttr(pitch, SPU_0CH, vag_spu_address); + + initGraph(); + + while (1) + { + + if(!counter){ + playSFX(); + counter = 180; + } + + FntPrint("\nPitch : %08x-%dKhz", pitch, (SWAP_ENDIAN32(VAGfileHeader->samplingFrequency)) ); + FntPrint("\nSet Start addr : %08x", vag_spu_address); + FntPrint("\nReturn start addr : %08x", spu_start_address); + FntPrint("\nGet Start addr : %08x", get_start_addr); + FntPrint("\nSend size : %08x", SWAP_ENDIAN32(VAGfileHeader->dataSize)); + FntPrint("\nReturn size : %08x\n", transSize); + FntPrint("\nCounter : %d\n", counter); + + FntFlush(-1); + + counter --; + + display(); + + } + return 0; + } diff --git a/hello_world.c b/hello_world.c new file mode 100644 index 0000000..ac96f85 --- /dev/null +++ b/hello_world.c @@ -0,0 +1,86 @@ +// This is stolen from Lameguy64 tutorial : http://lameguy64.net/svn/pstutorials/chapter1/1-display.html + +#include +#include +#include +#include +#include + +#define VMODE 0 // Video Mode : 0 : NTSC, 1: PAL + +#define SCREENXRES 320 // Screen width +#define SCREENYRES 240 // Screen height + +#define CENTERX SCREENXRES/2 // Center of screen on x +#define CENTERY SCREENYRES/2 // Center of screen on y + +#define MARGINX 0 // margins for text display +#define MARGINY 32 + +#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 + +void init(void) +{ + 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] + + if (VMODE) // PAL + { + SetVideoMode(MODE_PAL); + disp[0].screen.y += 8; // add offset : 240 + 8 + 8 = 256 + disp[1].screen.y += 8; + } + + setRGB0(&draw[0], 50, 50, 50); // set color for first draw area + setRGB0(&draw[1], 50, 50, 50); // set color for second draw area + + draw[0].isbg = 1; // set mask for draw areas. 1 means repainting the area with the RGB color each frame + draw[1].isbg = 1; + + 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, SCREENYRES - MARGINY - FONTSIZE, 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]); + + SetDispMask(1); // Display on screen + + db = !db; // flip db value (0 or 1) +} + + +int main(void) +{ + + init(); // execute init() + + while (1) // infinite loop + { + FntPrint("Hello world !"); // Send string to print stream + + FntFlush(-1); // Draw printe stream + + display(); // Execute display() + } + return 0; + }