Talkline - How it works. ~~~~~~~~~~~~~~~~~~~~~~~~ Talkline is a sound of short duration, appended to the end of a message. In this fist version, up to 5 seconds of sound can be added. The purpose of this text file is to explain how the sound is coded and appended to the message. This first version is called version "A" and has the following characterization: 1-Sampling. - The sound bandwidth supports human voice only and so is sampled at 5012.5Hz Hz, 8bits, with zero level at value 80h. 2-Compression - A - The signal is converted to 4 bits, first subtracting DC value (80h) and using a log (base 2) conversion of the absolute value: in out 0 -> 0 0. .1 -> 1 2. .3 -> 2 4. .7 -> 3 8. .15 -> 4 16. .31 -> 5 32. .63 -> 6 64. .127 -> 7 Now the signal is 4 bits with zero level at 0 (-16 to 15). B - The samples are packed 2 samples per byte, high nibble corresponding to first sample and the low nibble to next one EX: t = 0 0000hhhh <-- 4 bit sample 1 0000LLLL pack -> hhhhLLLL C - Run lenght encoded, using FFh as flag character. EX: 00 32 84 84 84 84 84 84 34 35 FF 54 54 FE FE FE FE FE FE is converted to: 00 32 FF 05 84 34 35 FF 01 FF 54 54 FF 06 FE 3-Coding - Mode A: The signal is coded using 7 bits, from 30h to AFh. EX: 0 - AAAAaaaa - > 0 - 0AAAAaaa + 30h 1 - BBBBbbbb 1 - 0BBBBbbb + 30h 2 - CCCCcccc 2 - 0CCCCccc + 30h 3 - DDDDdddd 3 - 0DDDDddd + 30h 4 - EEEEeeee 4 - 0EEEEeee + 30h 5 - FFFFffff 5 - 0FFFFfff + 30h 6 - GGGGgggg 6 - 0GGGGggg + 30h 7 - 0abcdefg + 30h Mode B: The signal is coded using 6 bits, from 30h to 70h. EX: 0 - AAAAaaaa - > 0 - 00AAAAaa + 30h 1 - BBBBbbbb 1 - 00BBBBbb + 30h 2 - CCCCcccc 2 - 00CCCCcc + 30h 3 - 00aabbcc + 30h 4-Saving - The result is appended to the end of message at 64 bytes per line, with a header: "[TALK]" - 6 bytes - Identify a talkline 1Bh,"[8m" - 4 bytes - ANSI command to disable output, to avoid trash on mail readers/terminal emulators without sound capabilities but with full ANSI suport. "A" - 1 byte - A = coding in 7 bits B = coding in 6 bits Letters A to H are reserved. "00000" - 5 bytes - The size of the sound in bytes, after coding and without line feeds (E3h). E3h - 1 byte - The character used as LF by the QWK file format. .. follow first 64 bytes,E3h, and so on... ========================================================================= As example, the routines, used to compress and expand the voice signal. //----------------------------------------------------------------------- UINT WaveCompress (LPSTR Pk, LPSTR Wav, UINT nS) // // Wav - intput signal // Pk - output signal // nS - number of data points // { register UINT k, T; char M; UINT Ncomp, nS; Log2(Wav, nS); ;4 bit convert Ncomp = nS >> 1; // pack data _asm { LES SI,Wav ;ES:SI -> Wav MOV DI,SI MOV CX,Ncomp JCXZ DONE L1: MOV AX,ES:[SI] SHL AL,4 ;shift to HI nibble AND AX,0FF0h ;mask OR AL,AH ;merge STOSB ;save ADD SI,2 LOOP L1 DONE: } // nS = 0; Wav[Ncomp] = (char)(Wav[Ncomp-1]+1); //make last different for(k=0; k 2 || M == '\xFF') { Wav[nS++] = 0xFF; //mark Wav[nS++] =(char)T; //total k = k + T - 1; //next } Wav[nS++] = M; //save data } Ncomp = Pack(Pk, Wav, nS); //7 bits convert return Ncomp; } //----------------------------------------------------------------- LONG WaveExpand (LPSTR Wav, LPSTR Pk, UINT nS) // // Pk - input signal // Wav - output signal // nS - number of data points // { register UINT T, k; UINT Cnt; LONG nExp, Pt; // nSamp = Unpack(Pk, Pk, nSamp); // 8 bits convert nExp = 0; // run lenght decode for(Pt=0; (UINT)Pt < nSamp; Pt++) { if(Pk[Pt] == (char) 0xFF ) { Cnt = (BYTE) Pk[++Pt]; T = (char)Pk[++Pt]; for(k = 0; k < Cnt; k++) Wav[nExp++] = (char)T; } else { Wav[nExp++] = Pk[Pt]; } } Cnt =(UINT)nExp; //unpack _asm { LES DI, Wav MOV BX,Cnt OR BX,BX JZ DONE MOV SI,DI ADD SI,BX ;end of data ADD BX,BX ;end of unpacked data L1: MOV AL,ES:[SI] MOV AH,AL ;copy SHR AL,4 ;do inverse operation AND AX,0F0Fh ;mask MOV ES:[DI+BX],AX ;save DEC SI SUB BX,2 JAE L1 ;for all data points DONE: } // nExp = nExp + nExp; Exp2(Wave, nExp); ;8 bit convert return nExp; } //======================================================================= Assebly routines to convert to and from 7 bits. ;------------------------------------------------------------------------- ; UINT = Pack (LPSTR, LPSTR, UINT); ; _Pack PROC FAR PUSH BP MOV BP,SP PUSHF CLD ;up PUSH DS PUSH SI PUSH DI XOR AX,AX ;zero return value MOV CX,[BP+14] ;size (up to 64k) JCXZ PACK9 LES DI,[BP+6]; LDS SI,[BP+10]; ;DS:SI -> string XOR BX,BX ;byte count ADD CX,SI ;offset end of input string PUSH DI ;save it ; PACK1: MOV DX,BX ;insert a 0E3h each 64 bytes AND DX,3FH ;test JNZ @F MOV AL,0E3h ;LF (on QWK...) STOSB ;insert @@: CMP SI,CX ;end of processing? JAE PACK2 LODSW ;-- 1 & 2 SHR AL,1 ;-> 7, bit 0 to CY RCL DH,1 ;CY to bit 0 on DH SHR AH,1 ;repeat to next byte RCL DH,1 ADD AX,3030h ;translate to 30-A0h STOSW ;ok, save it LODSW SHR AL,1 ;repeat for bytes 3 & 4 RCL DH,1 SHR AH,1 RCL DH,1 ADD AX,3030h STOSW LODSW SHR AL,1 ;and 5 & 6 RCL DH,1 SHR AH,1 RCL DH,1 ADD AX,3030h STOSW LODSB SHR AL,1 ;the last one... RCL DH,1 ; MOV AH,DH ;save DH (7 saved bits) ADD AX,3030h ; STOSW ; ADD BX,8 ;total JMP PACK1 PACK2: MOV AX,DI ;new position POP DI ;initial position SUB AX,DI ;return total processed bytes PACK9: XOR DX,DX ;AX:DX POP DI POP SI POP DS POPF MOV SP,BP POP BP RET _Pack ENDP ;------------------------------------------------------------- ; UINT = Unpack (LPSTR, LPSTR, UINT); ; _Unpack PROC FAR PUSH BP MOV BP,SP PUSHF PUSH DS PUSH SI PUSH DI CLD ;up XOR AX,AX MOV CX,[BP+14] ;size JCXZ UNPCK9 LES DI,[BP+6]; ;ES:DI -> string LDS SI,[BP+10]; ;DS:SI -> string PUSH DI ; UNPCK0: LODSB ;-- pcbord/qmail file corruption fix CMP AL,30H JAE @F CMP AL,'#' ;<<< JNE UNPK2 MOV AL,'@' ;<<< @@: CMP AL,0B0H JA UNPK2 STOSB UNPK2: LOOP UNPCK0 ; ;------------ MOV AX,DI POP DI SUB AX,DI ;total "clean" bytes LDS SI,[BP+6] LES DI,[BP+6] ;works only on destination ADD AX,7 SHR AX,3 ;number of loops MOV CX,AX MOV BX,7 MUL BX ;number of bytes MOV BX,AX ;save on BX UNPCK1: MOV DH,[SI+7] ;get saved bit SUB DH,30h ;convert to 0 - 7Fh RCL DH,1 LODSW SUB AX,3030h ;convert data too RCL DH,1 ;bit -> CY RCL AL,1 ;insert on byte RCL DH,1 ;next byte RCL AH,1 STOSW ;save LODSW SUB AX,3030h ;next 2 bytes RCL DH,1 RCL AL,1 RCL DH,1 RCL AH,1 STOSW LODSW SUB AX,3030h ;bytes 5 & 6 RCL DH,1 RCL AL,1 RCL DH,1 RCL AH,1 STOSW LODSW SUB AL,30h ;the last... RCL DH,1 RCL AL,1 STOSB LOOP UNPCK1 ;repeat all MOV AX,BX UNPCK9: XOR DX,DX ;return total processed. POP DI POP SI POP DS POPF MOV SP,BP POP BP RET _Unpack ENDP ;--------------------------------------------------------------------- ; Assembly routines to convert from 8 to 4 bits and from 4 to 8 ;--------------------------------------------------------------------- ; Log2 (LPSTR, UINT); ; _Log2 PROC FAR PUSH BP MOV BP,SP PUSHF PUSH ES PUSH DI CLD MOV BX,[BP+10] ;size to CX OR BX,BX JZ LOGC9 LES DI,[BP+6]; ;ES:DI -> in/out - in place conversion LOGC1: MOV AL,ES:[DI] XOR CH,CH ;zero MOV DL,CH ; sub al,80h ;remove DC value (-128..127) OR AL,AL JNS @F ;if positive, go NEG AL ;signal change MOV DL,8 ;signal bit @@: AND AL,7FH SHL AL,1 MOV CL,7 @@: SHL AL,1 JC @F LOOP @B @@: OR CL,DL ;insert signal MOV AL,CL ; STOSB ;save DEC BX ;end test JNZ LOGC1 ;no, more... LOGC9: POP DI POP ES POPF MOV SP,BP POP BP RET _Log2 ENDP ;------------------------------------------------------------------- ; Exp2 (LPSTR, UINT); ; _LogExp PROC FAR PUSH BP MOV BP,SP PUSHF PUSH ES PUSH DI CLD ;up XOR AX,AX MOV BX,[BP+10] ;size OR BX,BX JZ LOGE9 LES DI,[BP+6]; ;ES:DI -> in/out XOR CH,CH LOGE1: MOV AL,ES:[DI] XOR AH,AH ; MOV CL,AL ;copy AND CL,7 ;signal mask STC RCL AH,CL ;exp TEST AL,8 JZ @F ;if positive NEG AH @@: ADD AH,80H ;recover offset MOV AL,AH STOSB ;save DEC BX JNZ LOGE1 ;loop LOGE9: POP DI POP ES POPF MOV SP,BP POP BP RET _Exp2 ENDP ;======================================================================== OBS: The music appended to the end of messages is a .MID file only coded into 7 bits. The file header is the same, only the identifier "TALK" change to "MIDI". ========================================================================== Carlos Pires, Rio, March 1993. ========================================================================== NOTE: Unpack routine correction to avoid file corruption over some BBSs systems, the change of "@" by "#". June, 1993. ========================================================================== NOTE: Coding scheme "B" using 6 bits only to avoid some old fashioned BBS. This solve the problem of bit 7 stripping. September 1993. ==========================================================================