-- Adaptive Predictive PCM
-- L. Stewart, last updated: August 28, 1979 11:14 AM
DIRECTORY
AdpcmDefs: FROM "AdpcmDefs",
AdpcmPrivateDefs: FROM "AdpcmPrivateDefs",
InlineDefs: FROM "InlineDefs";
Adpcm: PROGRAM
IMPORTS InlineDefs
EXPORTS AdpcmDefs, AdpcmPrivateDefs =
BEGIN OPEN AdpcmDefs, AdpcmPrivateDefs, InlineDefs;
mMode: CARDINAL ← 14;
qTabs: QuantizerTable;
bTabs: BreakpointTable;
pTab: PredictorTable;
BrkAry: BreakpointTable = [ 0, 1, 2, 3, 4, 6, 9, 13, 19, 27, 40, 55, 78, 108, 0, 0, 0, 0, 0, 0];
LevHiAry: ARRAY [0..NModes) OF QVal = [ 1, 2, 3, 4, 6, 8, 12, 18, 26, 36, 54, 74, 104, 145, 0, 0, 0, 0, 0, 0];
LevLoAry: ARRAY [0..NModes) OF QVal = [ 0, 1, 1, 2, 3, 4, 6, 9, 13, 18, 27, 37, 52, 73, 0, 0, 0, 0, 0, 0];
InitialAdpcmState: AdpcmState = AdpcmState[0, 0, 0, 0, 0];
InitAdpcm: PUBLIC PROCEDURE =
BEGIN
i: INTEGER;
-- fill in tables
FOR i IN [0..NModes) DO
bTabs[i] ← BrkAry[i];
qTabs[i] ← [LevLoAry[i], LevHiAry[i]];
ENDLOOP;
FOR i IN Ex200 DO
pTab[i] ← ((i*85)/100); -- second guess of good value
ENDLOOP;
END;
InitAdpcmState: PUBLIC PROCEDURE [statep: POINTER TO AdpcmState] =
BEGIN
statep↑ ← InitialAdpcmState;
END;
GetqTabs: PUBLIC PROCEDURE RETURNS [POINTER TO QuantizerTable] =
BEGIN
RETURN[@qTabs];
END;
GetbTabs: PUBLIC PROCEDURE RETURNS [POINTER TO BreakpointTable] =
BEGIN
RETURN[@bTabs];
END;
GetpTab: PUBLIC PROCEDURE RETURNS [POINTER TO PredictorTable] =
BEGIN
RETURN[@pTab];
END;
CompressBlock: PUBLIC PROCEDURE [pcmData: PcmHandle, cData: AdpcmHandle, count: INTEGER, initialState: AdpcmState] RETURNS [AdpcmState] =
BEGIN
i: INTEGER;
c, index, out, error, bits: INTEGER;
privQC: QControl ←
[
BTabs: @bTabs,
QTabs: @qTabs,
PTab: @pTab,
state: initialState
];
FOR i IN [0..count) DO
c ← pcmData[i];
-- expects input in range [-128..127]
c ← IF c>127 THEN c-256 ELSE c; -- sign extend
error ← c-privQC.state.pred;
bits ← Quant[@privQC, error];
out ← DeQuant[@privQC, bits];
NewMode[@privQC.state, bits];
index ← privQC.state.pred+out;
index ← MIN[127, MAX[-128, index]]; -- limit
-- Mesa should handle the excess 200B automatically
privQC.state.pred ← privQC.PTab[index];
-- save partial word
-- We shift 2 bits at a time into PVal from the right. When PVal becomes full (as determined by privQC.state.now=0), then we stuff PVal into the output buffer and set PVal←0.
bits ← BITSHIFT[privQC.state.PVal, 2]+bits;
IF privQC.state.now=0 THEN privQC.state.PVal←0
ELSE privQC.state.PVal ← bits;
cData[i/8]←bits;
ENDLOOP;
RETURN[privQC.state];
END;
DeCompressBlock: PUBLIC PROCEDURE [pcmData: PcmHandle, cData: AdpcmHandle, count: INTEGER, initialState: AdpcmState] RETURNS [AdpcmState] =
BEGIN
i: INTEGER;
out, index, bits: INTEGER;
privQC: QControl ←
[
BTabs: @bTabs,
QTabs: @qTabs,
PTab: @pTab,
state: initialState
];
FOR i IN [0..count) DO
-- emulate microcode
IF privQC.state.now=0 THEN privQC.state.PVal←cData[i/8];
bits←BITSHIFT[privQC.state.PVal, -14];
privQC.state.PVal←BITSHIFT[privQC.state.PVal, 2];
-- expects input in range [0..3]
out ← DeQuant[@privQC, bits];
NewMode[@privQC.state, bits];
index ← out+privQC.state.pred;
index ← MIN[127, MAX[-128, index]]; -- limit
-- Mesa should handle the excess 200B automatically
privQC.state.pred ← privQC.PTab[index];
IF index<0 THEN index←index+256; -- to 8 bit TC
pcmData[i]←index;
ENDLOOP;
RETURN[privQC.state];
END;
NewMode: PUBLIC PROCEDURE [p: POINTER TO AdpcmState, bits: INTEGER ] =
BEGIN OPEN p;
sum: INTEGER ← 0;
-- put new bits into history registers
now ← IF now>=7 THEN 0 ELSE now+1;
-- compute formula based on history
sum ← (hist*4) + bits;
hist ← bits;
SELECT sum FROM -- clearly total is IN[0..15]
2, 8 => -- smaller stepsize
mode ← IF mode>0 THEN mode-1 ELSE 0;
5, 15 => -- larger stepsize
mode ← IF mode<mMode-1 THEN mode+1 ELSE mMode-1;
ENDCASE;
END;
DeQuant: PROCEDURE [p: POINTER TO QControl, bits: INTEGER ] RETURNS [INTEGER] =
BEGIN OPEN p;
SELECT bits FROM
0 => RETURN[qTabs[state.mode].lo];
1 => RETURN[qTabs[state.mode].hi];
2 => RETURN[-qTabs[state.mode].lo];
3 => RETURN[-qTabs[state.mode].hi];
ENDCASE => ERROR;
END;
-- takes 16 bit TC
Quant: PROCEDURE [p: POINTER TO QControl, c: INTEGER ]
RETURNS [INTEGER] =
BEGIN OPEN p;
sign: BOOLEAN ← c<0;
i: INTEGER ← IF sign THEN -c ELSE c;
out: INTEGER;
IF i > bTabs[state.mode] THEN out←1 ELSE out←0;
IF sign THEN out←out+2;
RETURN [out];
END;
-- Main Line Code
InitAdpcm[];
END.