psx.wiki

SPU ADPCM: Difference between revisions

Revision as of 02:46, 23 October 2026
RemmyLee
(GTE, MDEC, controllers, Memory Card, SIO and expansion port at register depth)
Revision as of 02:46, 23 October 2026
RemmyLee
(SPU and CD-ROM at register depth; CPU overview rewritten)
Line 1:Line 1:
+---
+title: SPU ADPCM
+type: format
+summary: 16-byte compressed sample blocks played by the SPU voices.
+category: Hardware
+category: File formats
+---
+
+## Overview
+
+The [SPU](/hardware/spu/) plays only ADPCM sample data. Sony calls the format VAG: compressed audio arranged in 16-byte blocks, about 3.5 to 1 smaller than the 16-bit source, and Sony treats the compression method as proprietary.[^ovr163] psx-spx describes it as an extension of the SNES bit rate reduction (BRR) format that uses 16-byte blocks in place of 9-byte blocks and five filters in place of four.[^spxintro] Uncompressed samples are not supported.[^spxadpcm]
+
+A [VAG](/formats/vag/) file adds a 48-byte header to the blocks. The header must be removed before the data is played, and the first block of every VAG is 16 bytes of zero data. Sony calls this the lead-in and explains that it initializes the SPU so that the sample starts without a click.[^ovr163]
+
+## Block Format
+
+Each block holds 28 samples in 16 bytes. The first two bytes are the block header and the other fourteen bytes each hold two 4-bit samples. The layout is shown below.[^spxadpcm]
+
+Table: ADPCM block
+
+| Offset | Contents |
+| --- | --- |
+| 00h | Shift and filter |
+| 01h | Flag bits |
+| 02h | Samples 1 (low nibble) and 2 (high nibble) |
+| 03h | Samples 3 and 4 |
+| 04h to 0Fh | Samples 5 to 28, two per byte |
+
+The 28 samples of a 16-byte block therefore take 4.57 bits per sample (16 x 8 / 28), where PCM takes 16 bits. The shift and filter byte is reported to have the same layout as in XA-ADPCM, where bits 0 to 3 hold the shift and the bits above hold the filter.[^spxadpcm][^spxxahdr]
+
+## Flag Bits
+
+The flag byte controls looping and the end of the sample.[^spxadpcm]
+
+Table: ADPCM flag bits
+
+| Bit | Name | Set means |
+| --- | --- | --- |
+| 0 | Loop End | Set the ENDX bit and jump to LSAX after this block |
+| 1 | Loop Repeat | Keep playing at the loop. When clear with bit 0 set, the voice goes to release with the envelope at zero. |
+| 2 | Loop Start | Copy the current address to LSAX |
+| 3 to 7 | Unknown | Usually 0 |
+
+Bits 0 and 1 together give four codes. Code 0 continues at the next block and code 2 acts the same. Code 3 jumps to the loop address and sets ENDX, and code 1 does the same but also forces release with the envelope at zero.[^spxadpcm]
+
+The flags allow the whole sample or only its last blocks to repeat. There is no flag that stops output. A one-shot sample therefore ends in a block with Loop Start and Loop End both set and all samples zero, which repeats forever without sound.[^spxadpcm]
+
+Sony's tools add this block to every one-shot VAG and call it the SPU IRQ clear block. It reads "00077777 77777777 77777777 77777777" or "00070000 00000000 00000000 00000000", and looping VAGs do not have it.[^ovr164] The block keeps the voice from running on through the sound buffer and raising stray SPU interrupts. Sony allows the block to be removed when no SPU IRQ function is used. However, SpuGetIRQ(), SpuGetIRQAddr(), SpuSetIRQ(), SpuSetIRQAddr(), SpuSetIRQCallback() and the SpuSt streaming functions all use the SPU IRQ.[^ovr164]
+
+## Decoding
+
+The hardware expands each 4-bit sample to 16 bits, shifts it right by the shift value and adds a prediction from the two previous output samples. The filter selects the prediction weights, and the result is clamped to -8000h to +7FFFh.[^spxxadec] The weights are the same as for XA-ADPCM, but the SPU accepts filter 4, which XA does not.[^spxxatab]
+
+Table: ADPCM filter coefficients
+
+| Filter | Weight of old sample | Weight of older sample |
+| --- | --- | --- |
+| 0 | 0 | 0 |
+| 1 | +60 | 0 |
+| 2 | +115 | -52 |
+| 3 | +98 | -55 |
+| 4 | +122 | -60 |
+
+The decoding of one sample is as follows. The weights are in 64ths, so the prediction is divided by 64 with rounding.[^spxxadec]
+
+```
+f0 = pos_table[filter]
+f1 = neg_table[filter]
+t = signed 4-bit sample
+s = (t << 12) >> shift
+s = s + ((old * f0 + older * f1 + 32) / 64)
+s = clamp(s, -8000h, +7FFFh)
+older = old
+old = s
+```
+
+## SPU-ADPCM and XA-ADPCM
+
+The PlayStation has two ADPCM formats. The decoder algorithm is the same for both. However, XA-ADPCM is decoded by the CD-ROM controller and goes to the SPU mixer directly. It uses neither the sound buffer nor a voice.[^spxvsxa]
+
+The two formats also differ in layout. XA arranges its nibbles in a different order and holds 2 x 28 nibbles per block. It can be mono or stereo, has only two sample rates and has no looping.[^spxvsxa] The [XA audio](/hardware/cd-rom/xa-audio/) page describes the XA side.
+
+## Interpolation
+
+Each voice interpolates between samples with a 4-point Gaussian filter. The four most recent samples are weighted by entries of a 512-entry table, and bits 4 to 11 of the pitch counter give the 8-bit index i into it.[^spxgauss]
+
+```
+out = (gauss[0FFh - i] * oldest) >> 15
+out = out + (gauss[1FFh - i] * older) >> 15
+out = out + (gauss[100h + i] * old) >> 15
+out = out + (gauss[000h + i] * new) >> 15
+```
+
+In theory each set of four weights sums to 8000h. In practice the sums are 7F7Fh to 7F81h, which keeps the result within range.[^spxgauss] The table is as follows (eight entries per line):[^spxgauss]
+
+```
+-001h,-001h,-001h,-001h,-001h,-001h,-001h,-001h ; 000h
+-001h,-001h,-001h,-001h,-001h,-001h,-001h,-001h
+0000h,0000h,0000h,0000h,0000h,0000h,0000h,0001h
+0001h,0001h,0001h,0002h,0002h,0002h,0003h,0003h
+0003h,0004h,0004h,0005h,0005h,0006h,0007h,0007h
+0008h,0009h,0009h,000Ah,000Bh,000Ch,000Dh,000Eh
+000Fh,0010h,0011h,0012h,0013h,0015h,0016h,0018h
+0019h,001Bh,001Ch,001Eh,0020h,0021h,0023h,0025h
+0027h,0029h,002Ch,002Eh,0030h,0033h,0035h,0038h
+003Ah,003Dh,0040h,0043h,0046h,0049h,004Dh,0050h
+0054h,0057h,005Bh,005Fh,0063h,0067h,006Bh,006Fh
+0074h,0078h,007Dh,0082h,0087h,008Ch,0091h,0096h
+009Ch,00A1h,00A7h,00ADh,00B3h,00BAh,00C0h,00C7h
+00CDh,00D4h,00DBh,00E3h,00EAh,00F2h,00FAh,0101h
+010Ah,0112h,011Bh,0123h,012Ch,0135h,013Fh,0148h
+0152h,015Ch,0166h,0171h,017Bh,0186h,0191h,019Ch
+01A8h,01B4h,01C0h,01CCh,01D9h,01E5h,01F2h,0200h ; 080h
+020Dh,021Bh,0229h,0237h,0246h,0255h,0264h,0273h
+0283h,0293h,02A3h,02B4h,02C4h,02D6h,02E7h,02F9h
+030Bh,031Dh,0330h,0343h,0356h,036Ah,037Eh,0392h
+03A7h,03BCh,03D1h,03E7h,03FCh,0413h,042Ah,0441h
+0458h,0470h,0488h,04A0h,04B9h,04D2h,04ECh,0506h
+0520h,053Bh,0556h,0572h,058Eh,05AAh,05C7h,05E4h
+0601h,061Fh,063Eh,065Ch,067Ch,069Bh,06BBh,06DCh
+06FDh,071Eh,0740h,0762h,0784h,07A7h,07CBh,07EFh
+0813h,0838h,085Dh,0883h,08A9h,08D0h,08F7h,091Eh
+0946h,096Fh,0998h,09C1h,09EBh,0A16h,0A40h,0A6Ch
+0A98h,0AC4h,0AF1h,0B1Eh,0B4Ch,0B7Ah,0BA9h,0BD8h
+0C07h,0C38h,0C68h,0C99h,0CCBh,0CFDh,0D30h,0D63h
+0D97h,0DCBh,0E00h,0E35h,0E6Bh,0EA1h,0ED7h,0F0Fh
+0F46h,0F7Fh,0FB7h,0FF1h,102Ah,1065h,109Fh,10DBh
+1116h,1153h,118Fh,11CDh,120Bh,1249h,1288h,12C7h
+1307h,1347h,1388h,13C9h,140Bh,144Dh,1490h,14D4h ; 100h
+1517h,155Ch,15A0h,15E6h,162Ch,1672h,16B9h,1700h
+1747h,1790h,17D8h,1821h,186Bh,18B5h,1900h,194Bh
+1996h,19E2h,1A2Eh,1A7Bh,1AC8h,1B16h,1B64h,1BB3h
+1C02h,1C51h,1CA1h,1CF1h,1D42h,1D93h,1DE5h,1E37h
+1E89h,1EDCh,1F2Fh,1F82h,1FD6h,202Ah,207Fh,20D4h
+2129h,217Fh,21D5h,222Ch,2282h,22DAh,2331h,2389h
+23E1h,2439h,2492h,24EBh,2545h,259Eh,25F8h,2653h
+26ADh,2708h,2763h,27BEh,281Ah,2876h,28D2h,292Eh
+298Bh,29E7h,2A44h,2AA1h,2AFFh,2B5Ch,2BBAh,2C18h
+2C76h,2CD4h,2D33h,2D91h,2DF0h,2E4Fh,2EAEh,2F0Dh
+2F6Ch,2FCCh,302Bh,308Bh,30EAh,314Ah,31AAh,3209h
+3269h,32C9h,3329h,3389h,33E9h,3449h,34A9h,3509h
+3569h,35C9h,3629h,3689h,36E8h,3748h,37A8h,3807h
+3867h,38C6h,3926h,3985h,39E4h,3A43h,3AA2h,3B00h
+3B5Fh,3BBDh,3C1Bh,3C79h,3CD7h,3D35h,3D92h,3DEFh
+3E4Ch,3EA9h,3F05h,3F62h,3FBDh,4019h,4074h,40D0h ; 180h
+412Ah,4185h,41DFh,4239h,4292h,42EBh,4344h,439Ch
+43F4h,444Ch,44A3h,44FAh,4550h,45A6h,45FCh,4651h
+46A6h,46FAh,474Eh,47A1h,47F4h,4846h,4898h,48E9h
+493Ah,498Ah,49D9h,4A29h,4A77h,4AC5h,4B13h,4B5Fh
+4BACh,4BF7h,4C42h,4C8Dh,4CD7h,4D20h,4D68h,4DB0h
+4DF7h,4E3Eh,4E84h,4EC9h,4F0Eh,4F52h,4F95h,4FD7h
+5019h,505Ah,509Ah,50DAh,5118h,5156h,5194h,51D0h
+520Ch,5247h,5281h,52BAh,52F3h,532Ah,5361h,5397h
+53CCh,5401h,5434h,5467h,5499h,54CAh,54FAh,5529h
+5558h,5585h,55B2h,55DEh,5609h,5632h,565Bh,5684h
+56ABh,56D1h,56F6h,571Bh,573Eh,5761h,5782h,57A3h
+57C3h,57E2h,57FFh,581Ch,5838h,5853h,586Dh,5886h
+589Eh,58B5h,58CBh,58E0h,58F4h,5907h,5919h,592Ah
+593Ah,5949h,5958h,5965h,5971h,597Ch,5986h,598Fh
+5997h,599Eh,59A4h,59A9h,59ADh,59B0h,59B2h,59B3h
+```
+
+## References
+
+[^ovr163]: Sony Computer Entertainment Inc. Run-Time Library Overview. January 1999. Page 16-3.
+[^ovr164]: Sony Computer Entertainment Inc. Run-Time Library Overview. January 1999. Page 16-4.
+[^spxintro]: psx-spx. Sound Processing Unit (SPU). https://psx-spx.consoledev.net/soundprocessingunitspu/
+[^spxadpcm]: psx-spx. Sound Processing Unit (SPU), SPU ADPCM Samples. https://psx-spx.consoledev.net/soundprocessingunitspu/
+[^spxvsxa]: psx-spx. Sound Processing Unit (SPU), SPU-ADPCM vs XA-ADPCM. https://psx-spx.consoledev.net/soundprocessingunitspu/
+[^spxgauss]: psx-spx. Sound Processing Unit (SPU), 4-Point Gaussian Interpolation. https://psx-spx.consoledev.net/soundprocessingunitspu/
+[^spxxadec]: psx-spx. CDROM Format, CDROM XA Audio ADPCM Compression, decode_28_nibbles. https://psx-spx.consoledev.net/cdromformat/
+[^spxxahdr]: psx-spx. CDROM Format, CDROM XA Audio ADPCM Compression, XA-ADPCM Header Bytes. https://psx-spx.consoledev.net/cdromformat/
+[^spxxatab]: psx-spx. CDROM Format, CDROM XA Audio ADPCM Compression, Pos/neg Tables. https://psx-spx.consoledev.net/cdromformat/