Author Topic: sound encode into DPCM  (Read 389 times)

SeregaZ

  • Posts: 23
sound encode into DPCM
« on: 1 Nov '20 - 09:22 »
i have some limitations for samples for old Genesis\Mega Drive 2 games: mono, 8bit, 10.4 to 5khz, PCM. i mean some kind of database with that samples. and i have target - mono, 4bit, 6500mhz, DPCM.
i am trying to do this by this steps:
1. first is convert any other mhz exept 6500 - into that 6500. i am do this will bass.dll and it works fine (probably :))
2. then i try to encode this 6500 8bit PCM into 4bit DPCM.

i have some table, that decode original samples - it have 16 values. with decode no any problem. then i try to make same like table, but for back operation. and with original samples - no any problem. encode and decode work fine. but when i try to use another 8bit samples - it encode something wrong... and a few days i try to find all my mistake. and probably i found them and fix and looks like fine now, but anyway final quality is not very well.

is this some secret or trick exist? just remember what quality of sound with Mortal Kombat 3 for Genesis\Mega Drive 2 game. when imperor says some Finish Him, Flawless Victory, when says names of characters - quality is nice. it is same 4bit DPCM, but decoded into 8bit 6500mhz with that table for playing. can i get same quality? what i should do? table have a severe restrictions - so it cant be very high piks of wav. maybe for that high piks i need some procedure for a little cut values and then make encode?

white - original 8bit, blue - that was encoded and decoded back. it is not mk3 samples, it is not prepeared another game. just resampling into 6500mhz (from 10400mhz).


Code: [Select]
DataSection
  datajim8bit:
  ;IncludeBinary "D:\Temp\MK3\sounddump.bin" 
  IncludeBinary "D:\Temp\MK3\GEMS1\030\sample_59.snd"
  enddatajim8bit:

  ; for sound playing. winapi not wants to play 6500mhz sample. or maybe i am just make wrong wav header
  ; so i use another dll for play sample. can be kicked
  StartOPNDLL:
  IncludeBinary "F:\DISTR\SEREGASOFT\DUNE\DUE\DUEmodules\OPN_DLL.dll"                 
  EndOPNDLL:
 
EndDataSection

Enumeration
  #Window
  #Canvas
  #Button
  #Button2
  #TrackBar
  #Progr
EndEnumeration

; can be kicked. needed for play button
XIncludeFile "F:\DISTR\SEREGASOFT\DUNE\DUE\DUEmodules\DllFromMememory.pbi"
XIncludeFile "F:\DISTR\SEREGASOFT\DUNE\DUE\DUEmodules\OPNDLLImport.pb"

; original decode table.
Global Dim pikarray.b(15) ;{
pikarray(0)  = 0
pikarray(1)  = 1
pikarray(2)  = 3
pikarray(3)  = 7
pikarray(4)  = $D   ; 13
pikarray(5)  = $15  ; 21
pikarray(6)  = $1F  ; 31
pikarray(7)  = $2B  ; 43
pikarray(8)  = 0
pikarray(9)  = -1
pikarray(10) = -3
pikarray(11) = -7
pikarray(12) = -$D  ; -13
pikarray(13) = -$15 ; -21
pikarray(14) = -$1F ; -31
pikarray(15) = -$2B ; -43
;}

;{ bits operations
Macro NumToBit(Num)
  (1<<(Num))
EndMacro
Macro GetBits(Var, StartPos, EndPos)
  ((Var>>(StartPos))&(NumToBit((EndPos)-(StartPos)+1)-1))
EndMacro
;}

; paint image on a window
Procedure CanvPaint(forot.l, fordo.l, box.a, xshif.a)
 
  If StartDrawing(CanvasOutput(#Canvas))
    If box
      Box(0, 0, 880, 280, 0)
      Line(0, $80, 880, 1, RGB(0, 200, 0))
      color = RGB(240, 240, 240)
    Else
      color = RGB(80, 80, 250)
    EndIf
    x = 10
    oldx = 0
    oldy = $80
    For m = forot To fordo
      y = PeekA(m)
      ;If box : Debug y : EndIf
      ; count direction
      ; x always bigger oldx
      If y <> oldy
        height = oldy - y       
      Else
        height = 1
      EndIf
      Line(x, y, oldx - x, height, color)
      oldx = x
      oldy = y
      x + xshif
    Next
    StopDrawing()
  EndIf
 
EndProcedure

; i try to make table for back operation
Procedure.a GetEncodeValue(value.w)
 
  ret.a = 0
 
  Select value
    Case 0
      ret = 0
    Case 1 To 2
      ret = 1
    Case 3 To 6
      ret = 2
    Case 7 To 12
      ret = 3
    Case 13 To 20
      ret = 4
    Case 21 To 30
      ret = 5
    Case 31 To 42
      ret = 6
    Case 43 To 300
      ret = 7
    Case -2 To -1
      ret = 9
    Case -6 To -3
      ret = 10
    Case -12 To -7
      ret = 11
    Case -20 To -13
      ret = 12
    Case -30 To -21
      ret = 13
    Case -42 To -31
      ret = 14
    Case -300 To -43
      ret = 15
  EndSelect
 
  ProcedureReturn ret
 
EndProcedure

; main encode procedure 8bit into 8bit
Procedure DPCMEncode(forstart.l, forend.l, memory.l)
 
  Number.a
  OldNumber.w
  TestValue.w
  FlagOrder.a
  First.a
  Second.a
  MemShift.l
 
  OldNumber = $80 ; 0x80
  FlagOrder = 0
  MemShift  = 0
  For m = forstart To forend             ; 8bit memory cycle
    If FlagOrder = 0
      FlagOrder = 1
     
      Number = PeekA(m)                  ; read from mem
      TestValue = Number - OldNumber     ; count value
      First = GetEncodeValue(TestValue)  ; get value from table
      OldNumber = OldNumber + pikarray(First) ;Number
    Else
      FlagOrder = 0
     
      Number = PeekA(m)                  ; read from mem
      TestValue = Number - OldNumber     ; count value
      Second = GetEncodeValue(TestValue) ; get value from table
      OldNumber = OldNumber + pikarray(Second) ;Number
     
      PokeA(memory + MemShift, second << 4 + first) ; write into memory encoded byte
      MemShift + 1                       ; move memory pointer to next byte
    EndIf
  Next
 
EndProcedure

; decode 4bit into 8bit
Procedure DPCMDecode(forstart.l, size.l, memory.l)
 
  Number.a
  MemShift.l
  MemWriteValue.b
 
  MemShift = 0
  MemWriteValue = $80
  For m = forstart To forstart + size - 1 ; 4bit memory cycle
    Number = PeekA(m)
   
    ; split 8bit value into two 4bit
    DPCMfirst  = GetBits(Number, 0, 3) ; get %0000xxxx
    DPCMsecond = GetBits(Number, 4, 7) ; get %xxxx0000
   
    MemWriteValue + pikarray(DPCMfirst)
    PokeB(memory + MemShift, MemWriteValue)
    MemShift + 1
   
    MemWriteValue + pikarray(DPCMsecond)
    PokeB(memory + MemShift, MemWriteValue)
    MemShift + 1
  Next
 
EndProcedure

Global OldTrackBarValue
Global oldposition
Global DecodedMem
Global DecodedMemSize

; scroll bar events. not accurate, but fine
Procedure ProgrProc()   
 
  tmp = GetGadgetState(#Progr)
  If tmp <> oldposition
    oldposition = tmp

    startfrom = ?datajim8bit + oldposition
    If startfrom > ?enddatajim8bit
      startfrom = ?enddatajim8bit - (880 / OldTrackBarValue)
    EndIf
    ; paint original 8bit
    CanvPaint(startfrom, startfrom + (880 / OldTrackBarValue), 1, OldTrackBarValue)
   
    startfrom = DecodedMem + oldposition
    If startfrom > DecodedMem + DecodedMemSize
      startfrom = DecodedMem + DecodedMemSize - (880 / OldTrackBarValue)
    EndIf
    ; paint encoded and then decoded for compare
    CanvPaint(startfrom, startfrom + DecodedMemSize - 1, 0, OldTrackBarValue)
   
  EndIf
 
EndProcedure

; can be kicked. for playing
OpenOPNDriver(1)
OPN_Write(0, $2B, $80)

OldTrackBarValue = 5
If OpenWindow(#Window, 100, 100, 900, 340, "")
 
  CanvasGadget(#Canvas, 10, 10, 880, 280)
  ScrollBarGadget(#Progr, 10, 290, 880, 10, 1, 100, 10)
 
  ButtonGadget(#Button, 10, 310, 50, 20, "play")
 
  TrackBarGadget(#TrackBar, 100, 310, 100, 20, 1, 10)
  SetGadgetState(#TrackBar, OldTrackBarValue)

  ButtonGadget(#Button2, 400, 310, 50, 20, "dump")
 
 
  CanvPaint(?datajim8bit, ?enddatajim8bit - 1, 1, OldTrackBarValue)
 
  ; encode
  size = ?enddatajim8bit - ?datajim8bit ; count memory size, what need for encoded
  size = size / 2
  If size
    EncodedMem = AllocateMemory(size)
    If EncodedMem                           ; * 2 = for avoide odd-numbered
      DPCMEncode(?datajim8bit, ?datajim8bit + (size * 2), EncodedMem)
    Else
      Debug "mem problem"
    EndIf
  EndIf
 
  ; decode
  If EncodedMem
   
    decodedsize = size * 2
    DecodedMem = AllocateMemory(decodedsize)
    If DecodedMem
      DPCMDecode(EncodedMem, size, DecodedMem)
      CanvPaint(DecodedMem, DecodedMem + decodedsize - 1, 0, OldTrackBarValue)
     
      SetGadgetAttribute(#Progr, #PB_ScrollBar_Maximum, decodedsize)

      DecodedMemSize = decodedsize
    Else
      Debug "mem problem"
    EndIf
   
  EndIf
 
  BindGadgetEvent(#Progr, @ProgrProc())
 
  Repeat
     Select WaitWindowEvent()

       Case #PB_Event_Gadget

         Select EventGadget()
           
           Case #Button ; play
             If EventType() = #PB_EventType_LeftClick
               If DecodedMem
                 PlayDACSample(0, decodedsize, DecodedMem, 6500)
               EndIf
             EndIf
             
           Case #Button2 ; dump
             If EventType() = #PB_EventType_LeftClick
               startfrom = ?datajim8bit + GetGadgetState(#Progr)
               If CreateFile(0, "D:\sounddump.bin")
                 WriteData(0, startfrom, 880 / OldTrackBarValue)
                 CloseFile(0)
               EndIf
             EndIf
             
           Case #TrackBar ; resolution of image
             If EventType() = #PB_EventType_LeftClick
               NewTrackBarValue = GetGadgetState(#TrackBar)
               If NewTrackBarValue <> OldTrackBarValue
                 OldTrackBarValue = NewTrackBarValue
                 CanvPaint(?datajim8bit, ?enddatajim8bit - 1, 1, OldTrackBarValue)
                 If DecodedMem
                   CanvPaint(DecodedMem, DecodedMem + decodedsize - 1, 0, OldTrackBarValue)
                 EndIf
               EndIf
             EndIf

         EndSelect

       Case #PB_Event_CloseWindow
         qiut = 1
   
     EndSelect
   Until qiut = 1

EndIf

End

this is one original mk3's sample. not seen white line, because blue exactly 100% on white. and it have high pik, but they reach for a few steps, not like before from low to high at one step. one step cant be bigger than 43 value.



any ideas how they are, at 90's, get some nice quality of phrases and how to get somelike quality with encoded-decoded? or i need not 6500 as input samples, but some high quality 16-32 bit 44100mhz samples to get same with output?
« Last Edit: 1 Nov '20 - 09:35 by SeregaZ »