MILC logo

IndexVorigeVolgendeLeeg

ADPCM and PCM conversion routines
Xelasoft, 00-00-95


    
; **************************************
; * ADPCM and PCM conversion routines
; * Copyright (c) 1995 XelaSoft
; *   A. Wulms
; *   Pelikaanhof 127 c
; *   2312 EG Leiden
; * email: awulms@wi.leidenuniv.nl
; *
; * These routines may be freely used by any means as long
; * as you mention in your programs that you have made use of
; * XelaSoft ADPCM and PCM conversion routines (XADPCM for short)
; *
; * The following routines are provided:
; * pcm2adp:  convert a PCM sample into an ADPCM sample, params:
; *           HL = PCM buffer, DE = ADPCM buffer, BC = #ADPCM bytes to process
; * adp2pcm:  convert an ADPCM sample into a PCM sample, params:
; *           HL = ADPCM buffer, DE = PCM buffer, BC = #ADPCM bytes to process
; * init_adpcmpcm: initialize PCM to ADPCM conversion and vice versa
; * pcm_adpcm: convert the PCM code in A into a ADPCM code in A
; * adpcm_pcm: convert the ADPCM code in A into a PCM code in A
; *
; * The PCM data are in 8 bit unsigned format
; * The ADPCM data are in (packed) 4 bit format, it is the
; * format which is used by the MSX AUDIO processor. It is not
; * compatible with the 4 bit format of the Soundblaster and compatibles
; *
; * note: MSX is a registered trademark of ASCII corporation
; *       Soundblaster is a registered trademark of Creative Labs Inc.

; **************************************
; * some usefull macros
divhl2:		macro
		srl	h
		rr	l
		endm

ld_de_hl:	macro
		ld	e,l
		ld	d,h
		endm

; **************************************
; * convert a 8 bit unsigned PCM sample
; * into a 4 bit ADPCM sample
; * in:  HL = buffer containing the PCM sample
; *      DE = buffer for the ADPCM sample
; *      BC = #bytes to place in the ADPCM buffer
; * out: HL = HL+2*BC
; *      DE = DE+BC
; *      BC = 0
; * changes: HL', DE', BC', AF
pcm2adp:	exx
		call	init_adpcmpcm	; init. conversion
		exx
pcm2adp2:	ld	a,(hl)		; get PCM code
		inc	hl
		exx			; save registers
		call	pcm_adpcm	; convert to ADPCM
		rrca
		rrca
		rrca
		rrca			; put ADPCM code in MSB of A
		push	af
		exx			; restore registers
		ld	a,(hl)		; get second PCM code
		inc	hl
		exx
		call	pcm_adpcm	; convert also this one into ADPCM
		pop	bc		; B = previous ADPCM code
		or	b		; A now contains both ADPCM codes
		exx
		ld	(de),a		; store ADPCM codes
		inc	de
		dec	bc
		ld	a,b
		or	c
		jr	nz,pcm2adp2	; and continue with the remainder
		ret

; **************************************
; * convert a 4 bit ADPCM sample
; * into a 8 bit unsigned PCM sample
; * in:  HL = buffer containing the ADPCM sample
; *      DE = buffer for the PCM sample
; *      BC = #bytes in the ADPCM buffer
; * out: HL = HL+BC
; *      DE = DE+2*BC
; *      BC = 0
; * changes: HL', DE', BC', AF
adp2pcm:	exx
		call	init_adpcmpcm	; init. conversion
		exx
adp2pcm2:	ld	a,(hl)		; get ADPCM code
		rrca
		rrca
		rrca
		rrca			; put 1st ADPCM code in LSB
		exx			; save registers
		call	adpcm_pcm	; convert into PCM
		exx
		ld	(de),a		; and store in memory
		inc	de
		ld	a,(hl)		; get ADPCM code again
		inc	hl
		exx
		call	adpcm_pcm	; convert into PCM
		exx
		ld	(de),a		; and store again
		inc	de
		dec	bc
		ld	a,b
		or	c
		jr	nz,adp2pcm2	; continue with the remainder
		ret

; **************************************
; * Initialise the conversion parameters
; * changes: HL
init_adpcmpcm:	ld	hl,#8000
		ld	(sample),hl
		ld	hl,#7f
		ld	(delta_n),hl
		ret

; **************************************
; * convert PCM code A into ADPCM code A
; * in:  A = PCM code (unsigned format)
; * out: A = ADPCM code
; * changes: HL, DE, BC
pcm_adpcm:	ld	h,a
		xor	a
		ld	l,a		; HL = 256 * PCM code
		ld	de,(sample)	; DE = previous sample
		sbc	hl,de		; HL = delta sample
		jr	nc,pcm_a2	; positive
		ex	de,hl		; DE = - abs(delta sample)
		ld	hl,1
		sbc	hl,de		; HL = 1 - C-flg - -abs(delta sample)
pcm_a2:		rla			; C-flg into bit 0 => l4: sign bit
		ld	de,(delta_n)	; calculate l3..l1
		sbc	hl,de		; HL = delta sample - delta n
		jr	nc,pcm_a3	; delta s > delta n ==> set l3
		add	hl,de		;         <         ==> restore HL
pcm_a3:		rla			; shift (not L3) into A
		srl	d
		rr	e
		or	a
		sbc	hl,de		; compare with delta_n/2
		jr	nc,pcm_a4
		add	hl,de
pcm_a4:		rla			; shift (not L2) into A
		srl	d
		rr	e
		or	a
		sbc	hl,de		; compare with delta_n/4
		rla			; shift (not L1) into A
		xor	7		; A = l4 l3 l2 l1 = ADPCM code
		push	af
		call	adpcm_pcm	; calculate new sample and delta_n
		pop	af
		ret

; **************************************
; * convert ADPCM code A into PCM code A
; * in:  A = ADPCM code
; * out: A = PCM code (unsigned formaat)
; * changes: HL, DE, BC
adpcm_pcm:	and	15
		ld	c,a		; remember PCM code
		ld	hl,(delta_n)
		ld_de_hl		; DE := HL = delta_n (dn for short)
		divhl2			; HL = delta_n/2
		rra			; examine l1 (bit 0 of ADPCM code)
		jr	nc,$+3
		add	hl,de		; HL = l1 * dn + dn/2
		divhl2			; HL = l1*dn/2 + dn/4
		rra			; examine l2 (bit 1 of ADPCM code)
		jr	nc,$+3
		add	hl,de		; HL = l2*dn + l1*dn/2 + dn/4
		divhl2			; HL = l2*dn/2 + l1*dn/4 + dn/8
		rra			; examine l3 (bit 2 of ADPCM code)
		jr	nc,$+3
		add	hl,de		; HL = l3*dn + l2*dn/2 + l1*dn/4 + dn/8
		ld	de,(sample)	; DE = previous sample value
		and	1		; examine l4
		jr	nz,adpcm_pmin	; negative ==> substract
		add	hl,de
		jp	nc,adpcm_p2	; JP is faster then JR
		ld	hl,#ffff	; value was to large ==> take maximum
		jp	adpcm_p2
adpcm_pmin:	ex	de,hl		; DE = (2*(l and 7)+1)*dn/8, HL = samp.
		sbc	hl,de
		jp	nc,adpcm_p2
		ld	hl,0
adpcm_p2:	ld	(sample),hl	; store new sample value
		res	3,c		; C = abs(l) = abs(ADPCM code)
		ld	hl,factors
		ld	b,0
		add	hl,bc		; HL = addr of correct factor
		ld	a,(hl)		; A = f(l) * #40
		ld	h,b
		ld	l,b		; HL = 0
		ld	de,(delta_n)	; start calculation of dn = dn*f(l)
		ld	b,6		; use 6 bit fixed point arithmetic
adpcm_p3:	rra
		jr	nc,$+3
		add	hl,de
		divhl2
		djnz	adpcm_p3	; HL = delta_n * (f(l) and 63)
		rra
		jr	nc,$+3
		add	hl,de		; HL = delta_n * (f(l) and 127)
		rra
		jr	nc,$+4
		add	hl,de
		add	hl,de		; HL = delta_n * f(l)
		ex	de,hl		; DE = delta_n * f(l)
		ld	hl,#a000
		add	hl,de		; DE < #6000 => this is allowed
		jr	c,adpcm_p4	; error => DE > #6000 ==> to large
		ld	hl,#ff81	; - 127
		add	hl,de		; check if DE < 127
		jp	c,adpcm_p5	; no => everything ok
		ld	de,127		; use minimum value
		jp	adpcm_p5
adpcm_p4:	ld	de,#6000	; use maximum value
adpcm_p5:	ld	(delta_n),de	; store new delta_n
		ld	a,(sample+1)	; A = msb of sample = PCM value
		ret

sample:		dw	#8000		; last used sample value
delta_n:	dw	#7f		; last used delta_n

factors:	db	#39,#39,#39,#39	; multiply factors to calculate
		db	#4d,#66,#80,#99	; a new delta_n


    

Index

Vorige

Volgende