From a93f027edafce789d92207434006a6399120c095 Mon Sep 17 00:00:00 2001 From: ABelliqueux Date: Sat, 14 Aug 2021 16:56:32 +0200 Subject: [PATCH] New multi VAG example --- VAG/README.md | 57 +------- hello_multi_vag/Makefile | 15 +++ hello_multi_vag/README.md | 3 + hello_multi_vag/hello_multivag.c | 225 +++++++++++++++++++++++++++++++ hello_vag/README.md | 2 +- hello_xa/README.md | 154 +-------------------- 6 files changed, 248 insertions(+), 208 deletions(-) create mode 100644 hello_multi_vag/Makefile create mode 100644 hello_multi_vag/README.md create mode 100644 hello_multi_vag/hello_multivag.c diff --git a/VAG/README.md b/VAG/README.md index dac9fd9..e51a251 100644 --- a/VAG/README.md +++ b/VAG/README.md @@ -1,56 +1,3 @@ -# VAG files +See here for more informations about the VAG fileformat and tools : -> VAG is the PlayStation single waveform data format for ADPCM-encoded data of sampled sounds, such as -piano sounds, explosions, and music. The typical extension in DOS is “.VAG”. - -See [FileFormat47.pdf](http://psx.arthus.net/sdk/Psy-Q/DOCS/FileFormat47.pdf), p.209 - -## Audio to VAG conversion - -We have to convert the audio file to RAW data first : - -```bash -ffmpeg -i input.mp3 -f s16le -ac 1 -ar 44100 tmp.dat -``` - -then use [`wav2vag`](https://github.com/ColdSauce/psxsdk/blob/master/tools/wav2vag.c) to convert the data, making sure the `-freq=` parameter matches the `-ar` value used above : - -```bash -wav2vag tmp.dat output.vag -sraw16 -freq=44100 -``` - -## VAGedit - -You can find a graphical editor in the [PsyQ sdk](http://psx.arthus.net/sdk/Psy-Q/PSYQ_SDK.zip) named `VAGEDIT.exe`. - -## C VAG structure - -Here is a structure definition to access the VAG header : - -See [FileFormat47.pdf](http://psx.arthus.net/sdk/Psy-Q/DOCS/FileFormat47.pdf), p.209 for full description. - -```c -typedef struct VAGhdr { // 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; -``` - -## VAG & SPU Docs - -See - * libformat47.pdf p.209 - * libover47.pdf, p.271 - * libref47.pdf, p.980 - - * [http://psx.arthus.net/code/VAG/](http://psx.arthus.net/code/VAG) - -## Ressources - - * [wav2vag utility](https://github.com/ColdSauce/psxsdk/blob/master/tools/wav2vag.c) +https://github.com/ABelliqueux/nolibgs_hello_worlds/wiki/VAG diff --git a/hello_multi_vag/Makefile b/hello_multi_vag/Makefile new file mode 100644 index 0000000..ddb845b --- /dev/null +++ b/hello_multi_vag/Makefile @@ -0,0 +1,15 @@ +TARGET = hello_multivag_new + +SRCS = hello_multivag_new.c \ +../VAG/hello.vag \ +../VAG/poly.vag \ +../VAG/0_come.vag \ +../VAG/1_cuek.vag \ +../VAG/2_erro.vag \ +../VAG/3_hehe.vag \ +../VAG/4_m4a1.vag \ +../VAG/5_punc.vag \ +../VAG/7_wron.vag \ +../VAG/8_yooo.vag \ + +include ../common.mk diff --git a/hello_multi_vag/README.md b/hello_multi_vag/README.md new file mode 100644 index 0000000..e51a251 --- /dev/null +++ b/hello_multi_vag/README.md @@ -0,0 +1,3 @@ +See here for more informations about the VAG fileformat and tools : + +https://github.com/ABelliqueux/nolibgs_hello_worlds/wiki/VAG diff --git a/hello_multi_vag/hello_multivag.c b/hello_multi_vag/hello_multivag.c new file mode 100644 index 0000000..1d861c0 --- /dev/null +++ b/hello_multi_vag/hello_multivag.c @@ -0,0 +1,225 @@ +// VAGDEMO2020 by Schnappy +// December 2020 +// Based on VAGDEMO_FIXED by Yagotzirck +// Based on VAGDEMO by Shadow +// based on psyq/addons/sound/TUTO3.C +// +// +// Load two VAG file to SPU sound buffer and play them back alternatively or simultaneously. +// +// WAV creation: use ffmpeg to create a 16-bit ADPCM mono WAV file - change -ar to reduce filesize (and quality) +// $ ffmpeg -i input.ext -f s16le -ac 1 -ar 44100 tmp.dat +// +// 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 tmp.dat 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/ +// https://github.com/ABelliqueux/nolibgs_hello_worlds/tree/main/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 hello_spu_address; // address allocated in memory for first sound file +u_long poly_spu_address; // address allocated in memory for second sound file +// DEBUG : these allow printing values for debugging +u_long hello_spu_start_address; +u_long hello_get_start_addr; +u_long hello_transSize; +u_long poly_spu_start_address; +u_long poly_get_start_addr; +u_long poly_transSize; +#define HELLO SPU_0CH // Play first vag on channel 0 +#define POLY SPU_2CH // Play second vag on channel 2 +// 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.vag - 44100 Khz +extern unsigned char _binary____VAG_hello_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_vag_end[]; // https://discord.com/channels/642647820683444236/663664210525290507/780866265077383189 +// poly.vag - 44100 Khz +extern unsigned char _binary____VAG_poly_vag_start[]; +extern unsigned char _binary____VAG_poly_vag_end[]; +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; + } + SetDispMask(1); // Display on screen + 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]); + 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(unsigned long fx){ + SpuSetKey(SpuOn, fx); +} +int main(void) +{ + short counter = 0; + const VAGhdr * HellofileHeader = (VAGhdr *) _binary____VAG_hello_vag_start; // get header of first VAG file + const VAGhdr * PolyfileHeader = (VAGhdr *) _binary____VAG_poly_vag_start; // get header of second 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 Hellopitch = (SWAP_ENDIAN32(HellofileHeader->samplingFrequency) << 12) / 44100L; + unsigned int Polypitch = (SWAP_ENDIAN32(PolyfileHeader->samplingFrequency) << 12) / 44100L; + SpuInit(); // Initialize SPU. Called only once. + initSnd(); + // First VAG + hello_spu_address = SpuMalloc(SWAP_ENDIAN32(HellofileHeader->dataSize)); // Allocate an area of dataSize bytes in the sound buffer. + hello_spu_start_address = SpuSetTransferStartAddr(hello_spu_address); // Sets a starting address in the sound buffer + hello_get_start_addr = SpuGetTransferStartAddr(); // SpuGetTransferStartAddr() returns current sound buffer transfer start address. + hello_transSize = sendVAGtoRAM(SWAP_ENDIAN32(HellofileHeader->dataSize), _binary____VAG_hello_vag_start); + // First VAG + poly_spu_address = SpuMalloc(SWAP_ENDIAN32(PolyfileHeader->dataSize)); // Allocate an area of dataSize bytes in the sound buffer. + poly_spu_start_address = SpuSetTransferStartAddr(poly_spu_address); // Sets a starting address in the sound buffer + poly_get_start_addr = SpuGetTransferStartAddr(); // SpuGetTransferStartAddr() returns current sound buffer transfer start address. + poly_transSize = sendVAGtoRAM(SWAP_ENDIAN32(PolyfileHeader->dataSize), _binary____VAG_poly_vag_start); + // set VAG to channel + setVoiceAttr(Hellopitch, HELLO, hello_spu_address); // SPU_0CH == hello + setVoiceAttr(Polypitch, POLY, poly_spu_address); // SPU_2CH == poly + initGraph(); + while (1) + { + if(!counter){ + playSFX(HELLO); // Play first VAG + counter = 240; + } + if(counter == 160){ + playSFX(POLY); // Play second VAG + } + if(counter == 80){ + playSFX(HELLO|POLY); // Play both VAGs simultaneously + } + FntPrint("First VAG:"); + FntPrint("\nPitch : %08x-%dKhz", Hellopitch, (SWAP_ENDIAN32(HellofileHeader->samplingFrequency)) ); + FntPrint("\nSet Start addr : %08x", hello_spu_address); + FntPrint("\nReturn start addr : %08x", hello_spu_start_address); + FntPrint("\nGet Start addr : %08x", hello_get_start_addr); + FntPrint("\nSend size : %08x", SWAP_ENDIAN32(HellofileHeader->dataSize)); + FntPrint("\nReturn size : %08x\n", hello_transSize); + FntPrint("\nSecond VAG:"); + FntPrint("\nPitch : %08x-%dKhz", Polypitch, (SWAP_ENDIAN32(HellofileHeader->samplingFrequency)) ); + FntPrint("\nSet Start addr : %08x", poly_spu_address); + FntPrint("\nReturn start addr : %08x", poly_spu_start_address); + FntPrint("\nGet Start addr : %08x", poly_get_start_addr); + FntPrint("\nSend size : %08x", SWAP_ENDIAN32(PolyfileHeader->dataSize)); + FntPrint("\nReturn size : %08x\n", poly_transSize); + FntPrint("\nCounter : %d\n", counter); + FntFlush(-1); + counter --; + display(); + } + return 0; + } diff --git a/hello_vag/README.md b/hello_vag/README.md index 55753d5..e51a251 100644 --- a/hello_vag/README.md +++ b/hello_vag/README.md @@ -1,3 +1,3 @@ See here for more informations about the VAG fileformat and tools : -https://github.com/ABelliqueux/nolibgs_hello_worlds/tree/main/VAG +https://github.com/ABelliqueux/nolibgs_hello_worlds/wiki/VAG diff --git a/hello_xa/README.md b/hello_xa/README.md index 17f4ded..7b78d37 100644 --- a/hello_xa/README.md +++ b/hello_xa/README.md @@ -1,153 +1,3 @@ -## XA playback +See here for more informations about the XA fileformat and tools : -You need [mkpsxiso](https://github.com/Lameguy64/mkpsxiso) in your $PATH to generate a PSX disk image. -You also need [`psxavenc` and `xainterleave`](https://github.com/ABelliqueux/candyk-psx/tree/master/toolsrc/). - -## WAV creation - -Use ffmpeg to create a 16-bit ADPCM mono WAV file - change -ar to reduce filesize (and quality) - -```bash -$ ffmpeg -i input.mp3 -acodec pcm_s16le -ac 2 -ar 44100 output.wav -``` - -You can use Audacity to edit sound, but as mentionned in [xatut.pdf](http://psx.arthus.net/sdk/Psy-Q/DOCS/XATUT.pdf), p17 : - -> At this point it is worth mentioning that certain sound editing packages add some custom information at -> the end of a .WAV file such as author and package used to create the file. This is normally not a -> problem as the .WAV header contains details of the length of the data. However MovConv and -> MovPack do not use the header information in this way, and so will convert .WAV files to .XA using -> all the data after the .WAV header up to the end of the file. This can lead too .XA files with audible -> noise such as clicks or pops where the custom information has been converted to sound data at the end -> of the sample. - -### Generate interleaved XA file - -```bash -psxavenc -f 37800 -t xa -b 4 -c 2 -F 1 -C 0 "../hello_cdda/audio/beach.wav" "xa/beach.xa" -psxavenc -f 37800 -t xa -b 4 -c 2 -F 1 -C 0 "../hello_cdda/audiofunk.wav" "xa/funk.xa" -xainterleave 1 xa/interleave4.txt xa/inter4.xa -xainterleave 1 xa/interleave8.txt xa/inter8.xa -``` - -Alternatively, you can use the windows tool [`MC32.EXE`](https://psx.arthus.net/tools/pimp-psx.zip) to interleave several PSX media files. - -### Compile - -This will compile and build an iso image : - -```bash -make -``` - -### Clean directory - -```bash -make cleansub -``` - -## Encoding to XA - -You can use a modified version of [`psxavenc`](https://github.com/ABelliqueux/candyk-psx/tree/master/toolsrc/psxavenc) to convert your audio file to a 2336 bytes XA file : - -```bash -./psxavenc -f 37800 -t xa -b 4 -c 2 -F 1 -C 1 "input.wav" "output.xa" -``` - -You can read it back with `XAPLAY.EXE`, that's in `psyq/bin/XAplay`. - -### PSXavenc usage - -``` -./psxavenc -Usage: psxavenc [-f freq] [-b bitdepth] [-c channels] [-F num] [-C num] [-t xa|xacd|spu|str2] - - -f freq Use specified frequency - -t format Use specified output type: - xa [A.] .xa 2336-byte sectors - xacd [A.] .xa 2352-byte sectors - spu [A.] raw SPU-ADPCM data - str2 [AV] v2 .str video 2352-byte sectors - -b bitdepth Use specified bit depth (only 4 bits supported) - -c channels Use specified channel count (1 or 2) - -F num [.xa] Set the file number to num (0-255) - -C num [.xa] Set the channel number to num (0-31) -``` - -## Interleaving XA files - -You can use [`MC32.EXE`](https://psx.arthus.net/tools/pimp-psx.zip) or [`xainterleave`](https://github.com/ABelliqueux/candyk-psx/tree/master/toolsrc/xainterleave) to interleave several PSX media files. - -## xainterleave usage - -`xainterleave ` - -`mode` can be 0 for full raw sectors or 1 for just XA (divisible by 2336) - -`in.txt` is a manifest txt file as seen [here](https://github.com/ABelliqueux/nolibgs_hello_worlds/blob/main/hello_xa/xa/interleave4.txt) - -Example for 1 music file, to be played at 1x CD speed (4 channels): - -``` -1 xa test.xa 1 0 -1 null -1 null -1 null -``` - -Add 4 more 1 null lines for 2x (8 channels). - -``` - 1 xa menu.xa 1 0 -sectors type file xa_file number (0-255) xa_channel number (0-31) -``` - -The format seems to correspond to the [entry_t struct](https://github.com/ABelliqueux/candyk-psx/blob/db71929903cc09398f5efc23973f9e136d123bbb/toolsrc/xainterleave/xainterleave.c#L35). - -## mkpsxiso - -You can use the following syntax to include your XA file in the CD image : - -```xml - -``` - -See here for more details : https://github.com/Lameguy64/mkpsxiso/blob/c44b78e37bbc115591717ac4dd534af6db499ea4/examples/example.xml#L85 - -## PsyQ XA Tools - -[XAPLAY.EXE](https://docs.google.com/uc?export=download&confirm=G9cM&id=0B_GAaDjR83rLZGVaZ2pvV2tjSVE) : Single channel XA playback -[XATOOL.EXE](http://psx.arthus.net/code/XA/xatut.zip) : XA structure inspector -[MC32.EXE](https://psx.arthus.net/tools/pimp-psx.zip) : Converts WAV > XA > Interleaved XA - -## More - -XA tutorial : http://psx.arthus.net/code/XA/XATUT.pdf - -Full XAtut archive : http://psx.arthus.net/code/XA/xatut.zip - https://web.archive.org/web/20060316213726/http://dev.paradogs.com/bin/docs/xatut.zip - -XA ADPCM documentation : http://psx.arthus.net/code/XA/XA%20ADPCM%20documentation.txt - -https://psx-spx.consoledev.net/cdromdrive/#cdrom-xa-audio-adpcm-compression - -PsyQ XA player example : `psyq/addons/scee/CD/XAPLAYER` - -XA SCEE Technical note - July 1998 : http://psx.arthus.net/sdk/Psy-Q/DOCS/CONF/SCEE/98July/xa_sound.pdf - -PSX audio tools : https://forum.xentax.com/viewtopic.php?t=10136 - -PIMP tools : https://psx.arthus.net/tools/pimp-psx.zip - -Source : https://discord.com/channels/642647820683444236/663664210525290507/843211084609617930 - -## Music credits - -Track 1 : -Beach Party by Kevin MacLeod -Link: https://incompetech.filmmusic.io/song/3429-beach-party -License: https://filmmusic.io/standard-license - -Track 2: -Funk Game Loop by Kevin MacLeod -Link: https://incompetech.filmmusic.io/song/3787-funk-game-loop -License: https://filmmusic.io/standard-license +https://github.com/ABelliqueux/nolibgs_hello_worlds/wiki/XA