Skip to content

GEMS samples format

Technical discussion which is not directly related to VGM files. Talk about Hardware and Software.

Moderator: Staff

  • SeregaZ Offline
  • Posts: 98
  • Joined: 2015-08-08, 13:56:52

GEMS samples format

Post by SeregaZ »

most popular format for GEMS game is mono 8bit PCM wav. they can have different kbs. for example:
shell's splitter make sfx files, where this type is shows: FLAGS =$45 - in this case second 5 is marker for kbs.
5 = 10400
6 = 8700
7 = 7300
8 = 6500
9 = 5800
$A = 5200

with this no any problem.



but then comes some special cases, where i have no idea. for example: Mortal Kombat have atmost $88 flag. so second is 8 = 6500kbs and looks like fine, but this first 8... as ValleyBell say it was DPCM 4bit. and give a table, with i need to make 8 bit sound. this table is:

Code: Select all

dc.b	 00H, 01H, 03H, 07H, 0DH, 15H, 1FH, 2BH
dc.b	-00H,-01H,-03H,-07H,-0DH,-15H,-1FH,-2BH
and second - you need read 4bit from back to forward:
%0000xxxx as first value
%xxxx0000 as second
and this values is marker for array - table.

then for 8bit convert you need take $80 value + value from table (for example it was 03H), then you need $83 + next value... and etc.

so with this case no problem. btw - WWF Arcade Game probably have same DPCM, becouse i am play WWF samples with same code, as MK3.



but then... come Comix Zone. first it have 3bytes per... idk what, but it is not same as other games have. second: samples have FLAGS =$E6 or FLAGS =$C6... i want to kill somebody... why it need so many different cases - i am not understand.

becouse this game have $45 flags too, i think it is not some DPCM. but any way i need to check it with ready to eat MK3 code... aaaand???? and hiss, noice, soddom and gomorra. it is sure not DPCM. ok. lets try to use 4bit PCM, as ValleyBell say, just need to x16. again - wrong.

so what it is format?
Attachments
Comix Zone.zip
(1.54 MiB) Downloaded 297 times
  • SeregaZ Offline
  • Posts: 98
  • Joined: 2015-08-08, 13:56:52

Post by SeregaZ »

probably Ooze have same type of samples. this game have as minimum 3 of that FLAGS =$C6
  • SeregaZ Offline
  • Posts: 98
  • Joined: 2015-08-08, 13:56:52

Post by SeregaZ »

lets get back to MK3 DPCM. i am try to make some sound manager for MK3 sound, same like i made it for Zero Tolerance. that games have some special cases... so standart method is not work :) MK3 (and WWF Arcade how it names) have additional samples and sequences banks = 6 banks, not 4 as usual. and samples is DPCM 4bit. now i start to work with it and almost everywhere flag for samples is $88. with that table of values i can decode that samples - it is not problem. but as i can see one sample have $A8 flag. 014 track for first sequences and samples banks. it is baby crying and it is 2 samples inside of this track. one is $88, but another one is $A8. i try to decode same way, as $88 do and sounds probably fine... but maybe it need to make something another? what that flag means? frequency, as i can understand is 6500mhz for this and any another all samples. but how to know sure what that flag means?

second sequences and second samples banks have that $A8 case too, and another one 027 track - $B8 flag. and now i start worry about that different flags :)

by theory it will work like this: all 4bit samples will be changed into 8bit for demo playing folder. it just for playing. when some sound inserting will happen - it will check input sound and encode samples from 10.4k for example to 6.5k and same 8bit. but when user will build rom - 8bit will be encoded into 4bit with $88 flag. but as i can see it can have $A8 and $B8. i want to know how important this thing and can i replace this flags to $88 as all another flags? just kick out that strange flags :)
  • User avatar
  • ValleyBell Offline
  • Posts: 4768
  • Joined: 2011-12-01, 20:20:07
  • Location: Germany

Post by ValleyBell »

About the sample flags, I can just tell you what I got thanks to the GEMSPlay source code:
  • bit 0-3 (0Fh): playback speed
  • bit 4 (10h): looping sample
  • bit 5 (20h): clip at note off (This one is checked in the NOTEOFFDIG function.)
  • bit 7 (80h): 4-bit sample data (bit unset = 8-bit)
  • SeregaZ Offline
  • Posts: 98
  • Joined: 2015-08-08, 13:56:52

Post by SeregaZ »

Code: Select all

RAW 'sample_1B.snd'
FLAGS =$B8
SKIP  =$0000
FIRST =$09B1
LOOP  =$09B1
END   =$FFFF
thanks. i thought if LOOP param have some data - it will loop, but now i can see it need to additional set this flag.

and it means decode with same way is correct. just need to encrease LOOP, END and SKIP. and size - FIRST param at twice. then get back them, when compile banks, before include in a rom file.
  • SeregaZ Offline
  • Posts: 98
  • Joined: 2015-08-08, 13:56:52

Post by SeregaZ »

i thought i am solve that problem with encode... but it works with old original samples. and today i start work with another samples - i have fail :) wrong sound, noice and even song it self is stop by some reason... :?

i think it should be some residue, when i am encode new sample. and that residue is need to be count with next step probably. another wrong case is a limitations for one of the "table" value.

but i am still didnt catch this moment in my head...

Code: Select all


Procedure.a GetEncodeValue(value.b)
  
  ret.a = 0
  
  ; this case is not fine. it need to some more tune limitations
  Select value
    Case 0
      ret = 0
    Case 1
      ret = 1
    Case 2 To 3
      ret = 2
    Case 4 To 7
      ret = 3
    Case 8 To 13
      ret = 4
    Case 14 To 21
      ret = 5
    Case 22 To 31
      ret = 6
    Case 32 To 127
      ret = 7
      
    Case -1
      ret = 9
    Case -3 To -2
      ret = 10
    Case -7 To -4
      ret = 11
    Case -13 To -8
      ret = 12
    Case -21 To -14
      ret = 13
    Case -31 To -22
      ret = 14
    Case -127 To -32
      ret = 15
  EndSelect
  
  ProcedureReturn ret
  
EndProcedure


; this is how i try to encode:
Debug "into 4bit"
f = 0
OutText$ = ""
tik = 0
tik2 = 0
WriteValue.a
Jim4BitMem = AllocateMemory(24)          ; reserve memory for convertin result
oldvalue = $80                           ; starting value
For m = ?datajim8bit To ?enddatajim8bit  ; cycle from start memory to end of memory with this data
  If f = 0
    f = 1                                ; flag, order of bits
    newvalue = PeekA(m)                  ; read from memory
    TestNumberb = newvalue - oldvalue    ; get distinction
    second = GetEncodeValue(TestNumberb) ; get value from "back-table"
    oldvalue = newvalue
  Else
    f = 0                                ; flag, order of bits
    newvalue = PeekA(m)                  ; read from memory
    TestNumberb = newvalue - oldvalue    ; get distinction
    first = GetEncodeValue(TestNumberb)  ; get value from "back-table"
    oldvalue = newvalue
    
    WriteValue = first << 4 + second     ; move bytes and make value for write
    OutText$ + "$" + RSet(Hex(WriteValue, #PB_Ascii), 2, "0") + " " ; text for showing in debug window
    tik + 1
    
    If tik = 12
      tik = 0
      OutText$ + Chr(10) ; next string in a window
    EndIf 
    
    tik2 + 1
    PokeA(Jim4BitMem + tik2, WriteValue) ; write into memory
    
  EndIf 
Next
Debug OutText$

and result is:

Code: Select all

8bit orig
$80 $80 $80 $80 $80 $80 $80 $80 $80 $80 $80 $80 
$80 $80 $80 $7F $7F $7F $7F $7F $7D $7C $7B $7B 
$7D $7F $82 $87 $8B $8C $8A $86 $81 $7E $7E $7E 
$80 $84 $86 $83 $7E $79 $76 $72 $6F $70 $71 $75 

into 4bit
$00 $00 $00 $00 $00 $00 $00 $90 $00 $00 $9A $09 
$22 $32 $13 $BA $AB $00 $32 $A2 $BB $BA $1A $31 

back, 4 => 8
$80 $80 $80 $80 $80 $80 $80 $80 $80 $80 $80 $80 
$80 $80 $80 $80 $80 $7F $7F $7F $7F $7F $7C $7B 
$7A $7A $7D $80 $83 $8A $91 $92 $8F $88 $81 $7E 
$7E $7E $81 $88 $8B $88 $81 $7A $77 $70 $6D $6E 
  • SeregaZ Offline
  • Posts: 98
  • Joined: 2015-08-08, 13:56:52

Post by SeregaZ »

i little change count "old value", but final result anyway have too many hiss:

Code: Select all

DataSection

  datajim8bit: ; original
  Data.a  $80,$7F,$80,$80,$80,$7F,$81,$7D
  Data.a  $82,$81,$7D,$83,$7E,$80,$81,$7E
  Data.a  $80,$80,$7D,$81,$7F,$7F,$80,$80
  Data.a  $7F,$80,$80,$7E,$81,$7E,$80,$7F
  Data.a  $7F,$80,$80,$7D,$82,$7E,$7F,$80
  Data.a  $7F,$81,$7E,$7F,$80,$7F,$80,$81
  Data.a  $7D,$83,$7E,$80,$7F,$81,$7C,$83
  Data.a  $7C,$83,$7C,$85,$7A,$87,$76,$8B
  Data.a  $6E,$A0,$6C,$60,$AB,$4D,$A1,$75
  Data.a  $73,$82,$85,$73,$85,$81,$75,$88
  Data.a  $77,$84,$7E,$7C,$81,$7D,$80,$82
  Data.a  $7B,$82,$7D,$81,$7E,$7F,$81,$7D
  Data.a  $80,$7F,$7F,$81,$7E,$80,$7F,$7F
  Data.a  $80,$7F,$7E,$81,$7C,$80,$80,$7F
  Data.a  $7D,$81,$7F,$7D,$80,$7F,$7E,$7F
  Data.a  $81,$7E,$7F,$83,$7B,$83,$7E,$7D
  Data.a  $82,$7C,$81,$7F,$7E,$83,$7E,$7E
  Data.a  $82,$7D,$81,$7E,$7F,$7F,$7E,$80
  Data.a  $7F,$7F,$80,$80,$7E,$80,$80,$7D
  Data.a  $80,$7E,$7F,$7E,$80,$80,$7E,$80
  Data.a  $80,$7D,$81,$7F,$7E,$80,$7E,$81
  Data.a  $7D,$80,$7F,$7F,$80,$80,$7F,$7F
  Data.a  $7F,$80,$7F,$7E,$81,$7E,$7E,$80
  enddatajim8bit:
  
EndDataSection

Enumeration
  #Window
  #Canvas
  #Button
  #TrackBar
EndEnumeration

; original decode table. not need to edit. as is.
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)
      ; 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

Procedure.a GetEncodeValue(value.b)
  
  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 127
      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 -127 To -43
      ret = 15
  EndSelect
  
  ProcedureReturn ret
  
EndProcedure

Procedure DPCMEncode(forstart.l, forend.l, memory.l)
  
  Number.a
  OldNumber.a
  TestValue.b
  FlagOrder.a
  First.a
  Second.a
  MemShift.l
  
  OldNumber = $80 ; 0x80
  FlagOrder = 0
  MemShift  = 0
  For m = forstart To forend
    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

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
    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

Procedure WavHeaderCreation(*memst, freq.l, size.l, bits.a)
  
             ;RIFF
             PokeB(*memst, $52):PokeB(*memst+1, $49):PokeB(*memst+2,$46):PokeB(*memst+3, $46)
             
             ;size
             PokeL(*memst+4, size+44-8)
             
             ;WAVE
             PokeB(*memst+8, $57):PokeB(*memst+9, $41):PokeB(*memst+10,$56):PokeB(*memst+11, $45)
    
             ;fmt
             PokeB(*memst+12, $66):PokeB(*memst+13, $6d):PokeB(*memst+14,$74):PokeB(*memst+15, $20) 
    
             ;size
             PokeB(*memst+16, $10)
    
             ;PCM 01
             PokeB(*memst+20, $01)
    
             ;mono stereo
             PokeB(*memst+22, $01)
    
             ;freq - 10400
             PokeL(*memst+24, freq)
    
             ;kbs
             PokeL(*memst+28, freq)
    
             ;bytes - 1
             PokeB(*memst+32, $01)
    
             ;bit
             PokeB(*memst+34, bits)
        
             ;data    
             PokeB(*memst+36, $64)
             PokeB(*memst+37, $61)
             PokeB(*memst+38, $74)
             PokeB(*memst+39, $61)
             
             ;sizedata
             PokeL(*memst+4, size)             
  
EndProcedure

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

  
  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)
      WavMem = AllocateMemory(decodedsize + 44)
      If WavMem
        CopyMemory(DecodedMem, WavMem+44, decodedsize)
        WavHeaderCreation(WavMem, 6500, decodedsize, 8)
      EndIf
    Else
      Debug "mem problem"
    EndIf
    
  EndIf
  
  Repeat
     Select WaitWindowEvent()

       Case #PB_Event_Gadget

         Select EventGadget()
           
           Case #Button
             If EventType() = #PB_EventType_LeftClick
               If WavMem
                 ; not plays :((( too short piece?
                 sndPlaySound_(WavMem, #SND_MEMORY | #SND_ASYNC | #SND_NODEFAULT)
               EndIf
             EndIf
           Case #TrackBar
             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
Image


maybe before encode - i need to make some a little lower volume for that part, where is too high piks? because table of MK3 can have 43 as maximum.
Post Reply