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 |