Re: Lowest CPU utilization audio codec?

Jim Leonard <[email protected]>
Newsgroups gmane.comp.video.xine.codec.devel
Message-ID <[email protected]>
Mike Melanson wrote:

> 	There is really no reason that ADPCM has to be transmitted as a
> series of 4-bit samples. I have seen (or at least heard about) 5-bit,
> 3-bit, and I think 2-bit ADPCM variants, of the IMA algorithm, no less.

Yes, the original Sound Blaster actually has 3-bit and 2-bit ADPCM 
variations.  But believe me when I tell you that they are simply 
terrible-sounding.  The 2-bit in particular is a joke.

  	"adaptive ADPCM" = "adaptive adaptive differential PCM"?...:)

He made fun of it at the time as well :-)

> Anyway, the 3:1 and 6:1 thing...sounds like it could be MACE compression--
> Macintosh Audio Compression and Expansion. It came in 3:1 and 6:1 flavors,
> which were denoted by fourccs 'MAC3' and 'MAC6', respectively. ffmpeg/lavc
> has code to decode both, but not compress it. I'm not sure about quality
> as I have never worked much with it.

No, that's not it at all.  He wrote an article on it in the July 1992 
issue of Dr. Dobbs' Journal (http://www.ddj.com/articles/1992/9207/), of 
which I seem to have misplaced :-(  It was ADPCM, but it was frame-based 
and switched between different translation tables.  It also had the 
usual special-case stuff (ie silence encoding).  If Dr. Dobbs didn't 
CHARGE for access to the articles I would be able to tell you more 
(grumble)...

...okay, some creative searching shows that he called it "ACOMP" and I 
managed to track down the assmebler source for both compressor and 
decompressor, including tables.  I've attached it.
-- 
Jim Leonard ([email protected])
World's largest electronic gaming project:    http://www.MobyGames.com/
A delicious slice of the demoscene:        http://www.MindCandyDVD.com/
Various oldskool PC rants and ramblings:       http://www.oldskool.org/
acomp.asc (text/plain, 19.6 KB)
_AUDIO COMPRESSION_
by John W. Ratcliff

[LISTING ONE]

;; AC.ASM -> ACOMP assembly language compressor. Written by John W. Ratcliff, 
;; 1991. Uses Turbo Assembler IDEAL mode and makes HEAVE use of macros. This
;; algorithm performs an exhaustive search for the best delta mod for each 
;; section of the waveform. It is very CPU intensive, and the algorithm can
;; be a little difficult to follow in assembly language.

    IDEAL           ; Enter Turbo Assembler's IDEAL mode.
    JUMPS           ; Allow automatic jump sizing.
    INCLUDE "prologue.mac"  ; Include usefull assembly lanugage macro file.

SEGMENT _TEXT   BYTE PUBLIC 'CODE'
    ASSUME  CS:_TEXT
;; This macro computes the amount of error in a frame of data, at this
;; bit resolution and this delta mod location.
Macro   DoError BITS
    LOCAL   @@NO
    mov bx,[COMP]       ; Current delta mod value.
    mov bh,BITS         ; Current bit resolution.
    push    [MINERR]        ; Pass minimum error so far.
    push    [PREV]          ; Pass previous data point.
    call    ComputeError        ; Compute error for frame.
    add sp,4            ; Balance stack.
    cmp dx,[MINERR]     ; Less than previous minimum?
    jge @@NO            ; no, don't update.
    mov [MINERR],dx     ; Save as new miniume
    mov [BESTPREV],ax       ; Best previous location.
    xor ah,ah           ;
    mov al,bl           ; Get best delta modution.
    mov [BEST1],ax      ; save it.
    mov al,bh           ; Get best bits.
    mov [BEST2],ax      ; Save it.
@@NO:
    endm

SQLCH   equ 64      ; Squelch bit.
RESYNC  equ 128     ; Resynchronization bit.

DELTAMOD equ    00110000b   ; Bit mask for delta mod bits.

ONEBIT  equ 00010000b   ; Bit pattern for one bit delta mod.
TWOBIT  equ 00100000b   ; Bit pattern for two bit delta mod.
FOURBIT equ 00110000b   ; Bit pattern for two bit delta mod.

;; This macro echos a message to the text screen, so that we can
;; monitor the progress of the compression algorithm.
Macro   Note    MSG
    push    ax
    lea ax,[MSG]
    push    ax
    call    Notify
    add sp,2
    pop ax
    endm

    public  _CompressAudio
;;This the the ACOMP compression procedure.
;;int far CompressAudio(unsigned char far *shand, 
;;                                           Address of audio data to compress.
;;          unsigned char far *dhand, 
;;                                      Destination address of compressed data.
;;          unsigned int slen, Length of audio data to compress.
;;          int squelch,       Squelch value allowed.
;;          int freq,      Playback frequency of audio data.
;;          int frame,     Frame size.
;;          int maxerr);       Maximum error allowed.
Proc    _CompressAudio  far
        ARG     SHAN:DWORD,DHAN:DWORD,SLEN:WORD,SQUELCH:WORD,FREQ:WORD,
                                                         FRAME:WORD,MAXERR:WORD
    LOCAL   PREV:WORD,COMP:WORD,MINERR:WORD,BEST1:WORD,BEST2:
                                                WORD,BESTPREV:WORD = LocalSpace
    PENTER  LocalSpace
    PushCREGS

    lds si,[SHAN]       ; Get source address.
    les di,[DHAN]       ; Get destination address.
    mov cx,[SLEN]       ; Get length of audio data.

    mov ax,cx       ; Get length of audio sample into AX
    stosw           ; Store it.
    mov ax,[FREQ]   ; Get frequency of recording.
    stosw           ; Store it.
    mov ax,[FRAME]  ; Get the frame size.
    stosb           ; Store it.
    mov ax,[SQUELCH]    ; Get squelch size
    stosb           ; Store it.
    mov ax,[MAXERR] ; Get maximum error allowed.
    stosw           ; Save it.
    xor ax,ax
    lodsb           ; Get first data sample
    mov [PREV],ax
    stosb           ; Store first data sample.
    dec cx      ; Decrement sample size count.
    jz  @@DONE

@@SQU:  mov ah,0Bh      ; Test keyboard status.
    int 21h
    or  al,al
    jz  @@NOK
    mov ah,08h      ; If a key was pressed get that key
    int 21h     ; value, and see if it was the
    cmp al,27       ; escape key.
    jne @@NOK
    xor ax,ax       ; If escape, return to caller, with abort.
    jmp @@EXIT
@@NOK:
    xor ax,ax
    mov dx,[SQUELCH]    ; Get squelch value.
    push    cx      ; Save remaining data count.
    push    si      ; Save si.
@@CK1:  lodsb           ; Get data byte.
    sub ax,[PREV]   ; Difference from previous data sample?
    jns @@CK2       ; if positive leave it alone.
    neg ax      ; Make it positive.
@@CK2:  cmp ax,dx       ; Is it within the squelch range?
    jg  @@NOS       ; yes, keep checking!
    loop    @@CK1       ; Keep going.
    inc si      ; Plus one, this last one counts.
@@NOS:  pop ax      ; Get back start SI
    mov dx,si       ; DX contains current address.
    sub dx,ax       ; Compute number of squelch bytes encountered.
    dec dx      ; Less, last non squelch byte.
    cmp dx,3        ; At least three?
    jle @@NOSQ      ; no, don't squelch it.
@@SQS:  cmp dx,63       ; Is it under 63?
    jle @@SEND
    mov ax,(63 + SQLCH)  ; Send count.
    sub dx,63       ; Less the 63 count we just sent.
    stosb           ; Write squelch byte out.
    jmp short @@SQS     ; Continue.
@@SEND: mov ax,dx       ; Get remaining count.
    or  ax,SQLCH    ; Or squelch bit on.
    stosb           ; Send squelch count out.
    dec si      ; Back up to last data point.
    pop ax      ; Pull CX off of stack, use current count.
    Note    msg0
    jmp short @@NXT
@@NOSQ: mov si,ax       ; Replace where source was.
    pop cx      ; Get back remaining data count.

@@NXT:  jcxz    @@DONE      ; Exit if done.
    cmp cx,[FRAME]  ; Below current frame size?
    jae @@GO        ; no, go ahead.
@@FIN:  lodsb           ; Get raw sample.
    shr al,1        ; Down to closest aproximated value.
    or  al,RESYNC   ; Add resync bit to it.
    stosb           ; Store out.
    loop    @@FIN       ; Keep sending final bytes.
    jmp @@DONE      ; exit, after sending final bytes.

@@GO:   mov [MINERR],07FFFh
    push    cx
    mov cx,[FRAME]  ; Set CX to frame size.

    mov [COMP],1
@@ALL1: DoError 1       ; Try one bit mode, +/-1.
    inc [COMP]
    cmp [COMP],17   ; Try delta comp values clean up to 16!!
    jne @@ALL1

    mov ax,[MINERR]
    cmp ax,[MAXERR]
    jle @@BCMP      ; Not good enough...
    mov [COMP],1
@@ALL2: DoError 2       ; Try two bit mode, +/-1.
    inc [COMP]
    cmp [COMP],17   ; Try delta comp values clean up to 16!!
    jne @@ALL2

    mov ax,[MINERR]
    cmp ax,[MAXERR]
    jle @@BCMP
    mov [COMP],1
@@ALL4: DoError 8       ; Try four bit mode, +/-1.
    inc [COMP]
    cmp [COMP],17   ; Try delta comp values clean up to 16!!
    jne @@ALL4

    mov ax,[MINERR] ; Get what the minimum error was.
    cmp ax,[MAXERR] ; Minimum error > maximum error?
    jle @@BCMP      ; no, then send frame.
    pop cx      ; Get back CX
    lodsb           ; Get data sample.
    and al,(NOT 1)  ; Strip off bottom bit.
    xor ah,ah
    mov [PREV],ax   ; New previous.
    shr al,1        ; /2
    or  al,RESYNC   ; Or resync bit on.
    stosb           ; Store it out into data stream.
    Note    msg1
    loop    @@SQU       ; Go check squelching.
    jmp @@DONE      ; Done, if this was last data sample.
@@BCMP: mov bx,[BEST1]  ; Get best comp.
    mov ax,[BEST2]  ; Get best bit size.
    mov bh,al       ; Into BH
    mov ax,32000
    push    ax
    push    [PREV]      ; Pass prev.
    call    ComputeError    ; Re-compute error term.
    add sp,4
    mov [PREV],ax   ; New previous.
;; Now time to store results!
    mov bx,[BEST1]  ; Get best comp.
    cmp [BEST2],1   ; 1 bit?
    jne @@NXT1
    call    Fold1Bit    ; Fold 1 bit data.
    Note    msg2
    jmp short @@IN      ; Reenter.
@@NXT1: cmp [BEST2],2   ; 2 bit data?
    jne @@NXT2
    call    Fold2Bit
    Note    msg3
    jmp short @@IN
@@NXT2:
    call    Fold4Bit
    Note    msg4
@@IN:   mov ax,[FRAME]
    pop cx      ; Get back CX
    add si,ax       ; Advance source
    sub cx,ax       ; Decrement data count.
    jnz @@SQU       ; Continue, if not at end.

@@DONE:
    mov ax,di       ; Size of compressed file.
    les di,[DHAN]
    sub ax,di       ; Difference.

@@EXIT:
    PopCREGS
    PLEAVE
    ret
    endp

;; Compute error:  Registers on entry are:
;;         DS:SI -> source data.
;;         CX    -> number of bytes to compute error term in.
;;         DX    -> total error incurred.
;;         BL    -> delta comp size.
;;         BH    -> maximum bit size value, positive or negative.
;; Exit: CX,DS:SI stay the same.
;;   DX -> total error term.
;;   AX -> new previous.
Proc    ComputeError    near
    ARG PREV:WORD,MINERR:WORD
    LOCAL   CUR:WORD = LocalSpace
    PENTER  LocalSpace

    push    cx
    push    si
    push    di      ; Save destination address.
    xor dx,dx       ; Initally no error.

@@CERR: lodsb           ; Get a data byte.
    xor ah,ah       ; Zero high byte.
    mov [CUR],ax    ; Save as current sample.
    sub ax,[PREV]
    cmp bl,1
    je  @@ND
    idiv    bl      ; Divided by delta mod size.
@@ND:   or  al,al
    js  @@DON       ; Do negative side.
    jnz @@CNT       ; If not zero then continue.
    inc al      ; Can't represent a zero, make it one.
@@CNT:  cmp al,bh       ; > max representative size?
    jle @@OK        ; no, it fit as is.
    mov al,bh       ; Make it the max representative size.
    jmp short @@OK      ;
@@DON:  neg al      ; Make it positive.
    cmp al,bh       ; > max representative size?
    jbe @@K2        ; no, use it.
    mov al,bh       ; Make it the max representative size.
@@K2:   neg al      ; Make it negative again.
@@OK:
    stosb           ; Store data value out.
    imul    bl      ; Times delta comp value.
    add ax,[PREV]   ; Add to previous data point.
    js  @@CS        ; Do signed case.
    cmp ax,255      ; Did it over flow?
    jle @@K3        ; No, then it fit byte sized.
    mov ax,255      ; Make it byte sized.
    jmp short @@K3      ; Re-enter
@@CS:   xor ax,ax       ; Close as we can get, underflow.
@@K3:   mov [PREV],ax   ; This is our new aproximated value.
    sub ax,[CUR]    ; Less actual value.
    jns @@K4        ; if positive then fine.
    neg ax      ; Take absolute value.
@@K4:   add dx,ax       ; Add into total error.
    cmp dx,[MINERR] ; Greater than minimum error allowed?
    jg  @@OUT
    loop    @@CERR
@@OUT:  mov ax,[PREV]   ; Current previous data point.
    pop di      ; Restore destination address.
    pop si      ; Reset SI back to start.
    pop cx      ; Reset CX back to start.
    PLEAVE
    ret
    endp
Macro   BuildByte
    LOCAL   @@HOP1,@@HOP2
    lodsb
    or  al,al       ; Is it signed?
    jns @@HOP1
    shl ah,1        ; Rotate.
    jmp short @@HOP2
@@HOP1: stc
    rcl ah,1
@@HOP2:
    endm
;; Fold 1 bit data.
;; ES:DI -> points to data ready to fold out.
;; CX-> frame size.
;; BL-> contains delta size.
Proc    Fold1Bit    near
    push    ds
    push    si
    push    di      ; Header byte address.
    push    es
    pop ds      ; DS=ES
    mov si,di       ; Source and dest.
    inc di      ; skip past header byte.
@@FOLD: xor ah,ah       ; Dest byte to be built, zero it.
    BuildByte
    BuildByte
    BuildByte
    BuildByte
    BuildByte
    BuildByte
    BuildByte
    BuildByte
    mov al,ah
    stosb           ; Store it out.
    sub cx,8        ; Less the 8 samples just folded up.
    jnz @@FOLD      ; Continue.

    pop si      ; Get back header byte address.
    mov al,bl       ; Get delta comp size.
    dec al      ; Less one.
    or  al,ONEBIT   ; Or the One Bit mode flag on.
    mov [ds:si],al  ; Store header byte.

    pop si
    pop ds
    ret
    endp

;; 2 Bit Format:  00 -> -2
;;        01 -> -1
;;        10 -> +1
;;        11 -> +2
Macro   BByte
    LOCAL   @@HOP1,@@HOP2
    lodsb
    or  al,al       ; Is it signed?
    jns @@HOP1
    add al,2        ; Adjust it.
    jmp short @@HOP2
@@HOP1: inc al      ; Plus 1 to fit into format size.
@@HOP2: shl ah,1
    shl ah,1
    or  ah,al       ; Place bits into byte being built.
    endm
;; Fold 2 bit data.
;; ES:DI -> points to data ready to fold out.
;; CX-> frame size.
;; BL-> contains delta size.
Proc    Fold2Bit    near
    push    ds
    push    si
@@F2:
    push    di      ; Header byte address.

    push    es
    pop ds      ; DS=ES
    mov si,di       ; Source and dest.
    inc di      ; skip past header byte.
@@FOLD: xor ah,ah       ; Dest byte to be built, zero it.
    BByte
    BByte
    BByte
    BByte
    mov al,ah
    stosb           ; Store it out.
    sub cx,4        ; Folded up 4 samples.
    jnz @@FOLD      ; Continue.

    pop si      ; Get back header byte address.
    mov al,bl       ; Get delta comp size.
    dec al      ; Less one.
    or  al,TWOBIT   ; Or the One Bit mode flag on.
    mov [ds:si],al  ; Store header byte.

    pop si
    pop ds
    ret
    endp
;; Four bit format:
;; 0 -> -8
;; 1 -> -7
;; 2 -> -6
;; 3 -> -5
;; 4 -> -4
;; 5 -> -3
;; 6 -> -2
;; 7 -> -1
;; 8 -> +1
;; 9 -> +2
;;10 -> +3
;;11 -> +4
;;12 -> +5
;;13 -> +6
;;14 -> +7
;;15 -> +8
Macro   Adjust4bit
    LOCAL   @@HOP1,@@HOP2
    lodsb
    or  al,al
    jns @@HOP1
    add al,8        ; Adjust it.
    jmp short @@HOP2
@@HOP1: add al,7        ; Adjust it.
@@HOP2:
    endm
;; Fold 4 bit data.
;; ES:DI -> points to data ready to fold out.
;; CX-> frame size.
;; BL-> contains delta size.
Proc    Fold4Bit    near
    push    ds
    push    si

    push    di      ; Header byte address.

    push    es
    pop ds      ; DS=ES
    mov si,di       ; Source and dest the same.
    inc di      ; skip past header byte.
@@FOLD: Adjust4bit      ; Get first sample.
    ShiftL  al,4        ; Into high nibble.
    mov ah,al       ; Into AH
    Adjust4bit      ; Get next nibble.
    or  al,ah       ; One whole byte.
    stosb           ; Store it out.
    sub cx,2        ; Folded up 4 samples.
    jnz @@FOLD      ; Continue.

    pop si      ; Get back header byte address.
    mov al,bl       ; Get delta comp size.
    dec al      ; Less one.
    or  al,FOURBIT  ; Or the One Bit mode flag on.
    mov [ds:si],al  ; Store header byte.

    pop si
    pop ds
    ret
    endp
msg0    db  "SQUELCH"
msg1    db  "RESYNC "
msg2    db  "1 BIT  "
msg3    db  "2 BIT  "
msg4    db  "4 BIT  "

Proc    Notify  near
    ARG MSG:WORD
    PENTER  0
    PushAll

    push    cs
    pop ds
    mov ax,0B800h
    mov es,ax
    mov si,[MSG]
    xor di,di
    mov ah,1Fh
    mov cx,7
@@SND:  lodsb
    stosw
    loop    @@SND

    PopAll
    PLEAVE
    ret
    endp

    ENDS
    END



[LISTING TWO]

;; UC.ASM		-> Uncompress ACOMP compressed audio data.
;;			   Written by John W. Ratcliff, 1991.
;;			   Uses Turbo Assembler IDEAL mode.

	IDEAL		; Enter Turbo Assembler IDEAL mode.
	JUMPS		; Allow automatic jump sizing.

	INCLUDE "prologue.mac"  ; Include common useful assembly macros.

SMALL_MODEL	equ	0   ;: true only if trying to generate near calls

	SETUPSEGMENT			; Setup _TEXT segment.

Macro	CPROC	name
	public	_&name
IF	SMALL_MODEL
Proc	_&name	near
ELSE
Proc	_&name	far
ENDIF
	endm

SQLCH	equ	64		; Squelch byte flag
RESYNC	equ	128		; Resync byte flag.

DELTAMOD equ	00110000b	; Bit mask for delta mod bits.

ONEBIT	equ	00010000b	; Bit pattern for one bit delta mod.
TWOBIT	equ	00100000b	; Bit pattern for two bit delta mod.
FOURBIT equ	00110000b	; Bit pattern for two bit delta mod.


base	dw	?		; Base address inside translate table.


TRANS	db	-8,-7,-6,-5,-4,-3,-2,-1,1,2,3,4,5,6,7,8
	db	-16,-14,-12,-10,-8,-6,-4,-2,2,4,6,8,10,12,14,16
	db	-24,-21,-18,-15,-12,-9,-6,-3,3,6,9,12,15,18,21,24
	db	-32,-28,-24,-20,-16,-12,-8,-4,4,8,12,16,20,24,28,32
	db	-40,-35,-30,-25,-20,-15,-10,-5,5,10,15,20,25,30,35,40
	db	-48,-42,-36,-30,-24,-18,-12,-6,6,12,18,24,30,36,42,48
	db	-56,-49,-42,-35,-28,-21,-14,-7,7,14,21,28,35,42,49,56
	db	-64,-56,-48,-40,-32,-24,-16,-8,8,16,24,32,40,48,56,64
	db	-72,-63,-54,-45,-36,-27,-18,-9,9,18,27,36,45,54,63,72
	db	-80,-70,-60,-50,-40,-30,-20,-10,10,20,30,40,50,60,70,80
	db	-88,-77,-66,-55,-44,-33,-22,-11,11,22,33,44,55,66,77,88
	db	-96,-84,-72,-60,-48,-36,-24,-12,12,24,36,48,60,72,84,96
	db	-104,-91,-78,-65,-52,-39,-26,-13,13,26,39,52,65,78,91,104
	db	-112,-98,-84,-70,-56,-42,-28,-14,14,28,42,56,70,84,98,112
	db	-120,-105,-90,-75,-60,-45,-30,-15,15,30,45,60,75,90,105,120
	db	-128,-112,-96,-80,-64,-48,-32,-16,16,32,48,64,80,96,112,127

CPROC	GetFreq 		; Report playback frequency for an ACOMP file.
	ARG	SOURCE:DWORD
	PENTER	0
	push	es
	les	bx,[SOURCE]
	mov	ax,[es:bx+2]
	pop	es
	PLEAVE
	ret
	endp

;; DX contains PREVIOUS.
;; AH contains bit mask being rotated out.
;; BX up/down 1 bit value.
Macro	Delta1
	LOCAL	@@UP,@@STORE
	shl	ah,1	; Rotate bit mask out.
	jc	@@UP
	sub	dx,bx
	jns	@@STORE
	xor	dx,dx	; Zero it out.
	jmp short @@STORE
@@UP:	add	dx,bx
	or	dh,dh
	jz	@@STORE
	mov	dx,255
@@STORE:mov	al,dl		; Store result.
	stosb
	endm

;; BX-> base address of translate table.
;; DX-> previous.
;; AL-> index.
Macro	DeModulate
	LOCAL	@@HIGH,@@OK
	xlat	[cs:bx] ; Translate into lookup table.
	cbw		; Make it a signed word.
	add	dx,ax	; Do word sized add, into previous.
	jns	@@HIGH
	xor	dx,dx	; Underflowed.
@@HIGH: or	dh,dh	; Did it overflow?
	jz	@@OK
	mov	dx,255	; Maxed out.
@@OK:	mov	al,dl
	stosb
	endm


;;unsigned int	  far UnCompressAudio(unsigned char far *source,unsigned char far *dest);
;; UnCompressAudio will decompress data which was compressed using ACOMP
;; into the destination address provided.  UnCompressAudio returns the
;; total size, in bytes, of the uncompressed audio data.
CPROC	UnCompressAudio
	ARG	SHAN:DWORD,DHAN:DWORD
	LOCAL	SLEN:WORD,FREQ:WORD,FRAME:WORD,BITS:WORD = LocalSpace
	PENTER	LocalSpace
	PushCREGS

	lds	si,[SHAN]		; Get source segment
	les	di,[DHAN]		; Get destination segment

	lodsw				; Get length.
	mov	[SLEN],ax		; Save length.
	mov	cx,ax			; Into CX
	lodsw				; Frequency.
	mov	[FREQ],ax		; Save frequency
	lodsb				; Get frame size.
	xor	ah,ah			; Zero high byte
	mov	[FRAME],ax		; Save it.
	lodsb				; Get squelch, and skip it.
	lodsw				; Get maximum error, and skip it.
	lodsb				; Get initial previous data point.
	stosb				; Store it.
	xor	ah,ah			; zero high byte.
	mov	dx,ax		; Save into previous word.
	dec	cx		; Decrement total by one.
	jz	@@DONE		; Exit
	mov	ah,al		; AH, always the previous.
@@DCMP: lodsb			; Get sample.
	test	al,RESYNC	; Resync byte?
	jz	@@NOTR		; no, skip.
	shl	al,1		; Times two.
	mov	dl,al		; Into previous.
	xor	dh,dh		; Zero high word.
	stosb			; Store it.
	loop	@@DCMP		; Next one.
	jmp	@@DONE

@@NOTR: test	al,SQLCH	; Squelch byte?
	jz	@@FRAM		; no, then it is a frame.
	and	al,00111111b	; Leave just the count.
	push	cx		; Save current countdown counter.
	mov	cl,al		; get repeat count
	xor	ch,ch		; zero high byte of CX
	mov	bx,cx		; Repeat count in DX
	mov	al,dl		; Repeat of previous.
	rep	stosb		; Repeat it.
	pop	cx		; Get back remaining count.
	sub	cx,bx		; Less.
	jnz	@@DCMP		; Keep going.
	jmp	@@DONE

@@FRAM:
	mov	bx,ax		; command byte into BX
	and	bx,0Fh		; Multiplier being used.
	ShiftL	bx,4		; Times 16.
	add	bx,offset TRANS ; Plus address of translate table.
	and	al,DELTAMOD	; Leave just delta mod.
	push	cx
	mov	cx,[FRAME]	; Get frame size.
	cmp	al,ONEBIT	; In one bit delta mod?
	jne	@@NEXT1 	; no, try other.
	ShiftR	cx,3		; /8
	mov	bl,[cs:bx+8]	; Get up amount
	xor	bh,bh		; Zero high byte.
@@GO:	lodsb
	xchg	al,ah		; Place prev in AL, Bit mask in AH
	Delta1
	Delta1
	Delta1
	Delta1
	Delta1
	Delta1
	Delta1
	Delta1
	mov	ah,al
	loop	@@GO
	jmp	@@RENTER

@@NEXT1:cmp	al,TWOBIT	; In two bit delta mod mode?
	jne	@@NEXT2
	add	bx,6		; Point at +- 2 bit's in table.
	shr	cx,1
	shr	cx,1		; 4 samples per byte.
@@GOGO: lodsb
	ShiftR	al,6
	DeModulate
	mov	al,[ds:si-1]
	ShiftR	al,4
	and	al,3
	DeModulate
	mov	al,[ds:si-1]
	ShiftR	al,2
	and	al,3
	DeModulate
	mov	al,[ds:si-1]
	and	al,3
	DeModulate
	loop	@@GOGO
	jmp short @@RENTER
@@NEXT2:shr	cx,1		; Two samples per byte.
@@GO2:	lodsb			; Get sample.
	ShiftR	al,4
	DeModulate
	mov	al,[ds:si-1]
	and	al,0Fh
	DeModulate
	loop	@@GO2

@@RENTER:
	pop	cx
	sub	cx,[FRAME]
	jnz	@@DCMP		; Continue decompress

@@DONE:
	mov	ax,[SLEN]	; Uncompressed length.

	PopCREGS
	PLEAVE
	ret
	endp


	ENDS
	END
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.