It is currently Wed Oct 21, 2020 6:25 am

All times are UTC + 1 hour




Post new topic Reply to topic  [ 117 posts ]  Go to page Previous  1, 2, 3, 4, 5, 6 ... 8  Next
Author Message
 Post subject: Re: Noob's investigation of VGM
PostPosted: Sat Sep 24, 2016 10:12 am 
Offline
Enthusiast
Enthusiast

Joined: Fri Feb 20, 2009 9:24 am
Posts: 573
Location: Almaty (Kazakhstan)
wilbert, have you idea how to correct count bytes, from that it need to get frequency value for second chip? as i understand it can be 3 cases:
1. value lays x0C adress of file. and it is .l type. if this value lower, than $40000000 - it means chip is one and used this frequency.
2. value bigger, than $40000000. it means value from file - $40000000 = this will be frequency for each chips.
3. same case, that 2 have, but file have extra header, where sets frequency for second chip. (or maybe not for second, but probably can both)

probably it have 4 case:
Quote:
Note: Bit 31 (0x80000000) is used on combination with the dual-chip-bit to indicate that this is a T6W28. (PSG variant used in Neo Geo Pocket)


so problem is - how to get that extra header value :) it write on wiki a little confuse for me.


Joris, no, VGM have a lot of chips. my Mulder's investigation have only part of VGM universe: that have Sega Mega Drive II - YM2612 and SN76489. wilbert make this convert for SN. YM was made by ValleyBell and as dll, not PB. i have sourse, but it is too big code - i will shoot my self if try to convert, i even cant correct convert SN :) and they plays both fine. for me fine :) maybe they have some asynchronously, but main idea for my program it is convert VGM to back in game Dune (and maybe in a future to other GEMS audio driver games). this player it is only the tip of the iceberg. before work of wilbert - i have only YM sound playing. tracks sounds not full, but it will be better, than nothing. but now i have full house :)

send code happen by command "Write" from module of wilbert. value for send get from VGM files (that part of VGM, that have SN chip). and then sends by timer from array (will be better make play at same time, when player start read file, but now it is read in array step - long step, then play array. for SN only - read happen fast, but for YM it takes long time.)


Top
 Profile  
Reply with quote  
 Post subject: Re: Noob's investigation of VGM
PostPosted: Sat Sep 24, 2016 11:45 am 
Offline
Enthusiast
Enthusiast

Joined: Fri Feb 20, 2009 9:24 am
Posts: 573
Location: Almaty (Kazakhstan)
i am make brain explosion of ValleyBell by my stuped questions, but probably i something understand:
Image


Last edited by SeregaZ on Sat Sep 24, 2016 2:40 pm, edited 1 time in total.

Top
 Profile  
Reply with quote  
 Post subject: Re: Noob's investigation of VGM
PostPosted: Sat Sep 24, 2016 2:16 pm 
Offline
Enthusiast
Enthusiast

Joined: Fri Feb 20, 2009 9:24 am
Posts: 573
Location: Almaty (Kazakhstan)
cant to start engine :((((

Image


Last edited by SeregaZ on Sat Sep 24, 2016 2:41 pm, edited 1 time in total.

Top
 Profile  
Reply with quote  
 Post subject: Re: Noob's investigation of VGM
PostPosted: Sat Sep 24, 2016 2:28 pm 
Offline
PureBasic Expert
PureBasic Expert

Joined: Sun Aug 08, 2004 5:21 am
Posts: 3678
Location: Netherlands
@SeregaZ, my ISP seems to be blocking the domain where you put your images.
I can't see them so I don't know what you are talking about :(

The extra header can be found by looking at the header offset (address 0xBC of the VGM file).
I'll probably try to convert the ActionScript based parser as well somewhere next week. It provides sample accurate playback without timing issues.
(your delay approach works pretty well on Windows but is not accurate enough on Mac).

@Joris, it's about emulating the sound chips and playing music on those emulated chips.
I didn't know about it until I encountered this thread but I like the relative simplicity of the SN76489.
It's really '80s and the cpu load of the emulated chip is < 1% at my computer.

_________________
macOS 10.15 Catalina, Windows 10


Top
 Profile  
Reply with quote  
 Post subject: Re: Noob's investigation of VGM
PostPosted: Sat Sep 24, 2016 2:49 pm 
Offline
Enthusiast
Enthusiast

Joined: Fri Feb 20, 2009 9:24 am
Posts: 573
Location: Almaty (Kazakhstan)
i change images host. it work?
Quote:
PureBasic.asm[1532]
mov[edx + 4 + rcx],eax
error: invalid address

(PB 5.31, x86, XP)

wilbert wrote:
your delay approach works pretty well on Windows but is not accurate enough on Mac.


try to play with:
Code:
PlayedUS = PlayedTicks * 22.675736961;90.702947844;22.675736961

this 22.675736961 i count from... it need to remember...

delay have millisec, but i need microsec. VGM plays as 44100 in 1 sec. 1 sec have 1000 millisec. so 1 delay it is a little more, than 1/44100. that is why need microsec. so i somehow get this 22.67blablabla from that millisec...

Debug 44100 * 22.675736961
and it is almost 1 sec :)))) that is how i get that.


Last edited by SeregaZ on Sat Sep 24, 2016 4:03 pm, edited 1 time in total.

Top
 Profile  
Reply with quote  
 Post subject: Re: Noob's investigation of VGM
PostPosted: Sat Sep 24, 2016 3:27 pm 
Offline
PureBasic Expert
PureBasic Expert

Joined: Sun Aug 08, 2004 5:21 am
Posts: 3678
Location: Netherlands
SeregaZ wrote:
i change images host. it work?
Quote:
PureBasic.asm[1532]
mov[edx + 4 + rcx],eax
error: invalid address

(PB 5.31, x86, XP)

Yes, I can see the images now. :)
The error should be fixed; small change.

_________________
macOS 10.15 Catalina, Windows 10


Top
 Profile  
Reply with quote  
 Post subject: Re: Noob's investigation of VGM
PostPosted: Sat Sep 24, 2016 3:55 pm 
Offline
Enthusiast
Enthusiast

Joined: Fri Feb 20, 2009 9:24 am
Posts: 573
Location: Almaty (Kazakhstan)
it is play, no error - but gaps return. and second chip now plays that was must be play first. probably that is why gaps happen - they eat each other.

i set frequency correct?
o.l = PeekL(*FileMem+$0C)
Clock(o)
set that big value from normal header, then check extraheader if this extra header have some value for 0 chip - get that value and set as for second:
Clock(extrafrequency, 1)

ops! my fault :)))) write is changes too :)))



about extra - i make some ugly way for get that value... ugly as always i am do :)))
Code:
;{
   ProgName.s=GetFilePart(ProgramFilename())
   a = CreateSemaphore_(#Null,0,1,@ProgName)
   If a<>0 And GetLastError_()=#ERROR_ALREADY_EXISTS
     CloseHandle_(a)
     End
   EndIf
;}
   
Enumeration
 
  #Window
 
  #PathString
  #OpenButton
  #File
 
  #Text
 
  #Fr01
  #Fr02
 
 
  #Ch1R
  #Ch2R
  #Ch3R
  #Ch4R
  #Ch1L
  #Ch2L
  #Ch3L
  #Ch4L
 
 
  #Ch1R2
  #Ch2R2
  #Ch3R2
  #Ch4R2
  #Ch1L2
  #Ch2L2
  #Ch3L2
  #Ch4L2
 
  #Play
  #Stop
  #From
 
EndEnumeration

XIncludeFile "SN76489 module.pb"

Structure VGMFSt
  type.i
  reg.a
  val.u
  pause.u
  samplenum.a
  sampleadress.i
  samplesize.i
  summofpauses.i
EndStructure
Global Dim VGMARR.VGMFSt(0)

Global TormozFlag = 1
Global PlThr

;{
Macro SetBit(Var, Bit)
  Var | (Bit)
EndMacro
 
Macro ClearBit(Var, Bit)
  Var & (~(Bit))
EndMacro

Macro TestBit(Var, Bit)
  Bool(Var & (Bit))
EndMacro
 
Macro NumToBit(Num)
  (1<<(Num))
EndMacro

Macro GetBits(Var, StartPos, EndPos)
  ((Var>>(StartPos))&(NumToBit((EndPos)-(StartPos)+1)-1))
EndMacro
;}

Procedure Play(*Value)
 
  PlayedTicks.i = 0
  PlayedUS.i = 0
  CurrentUS.i = 0
  StartMS.i = ElapsedMilliseconds()
 
  start = Val(GetGadgetText(#From))

  For i = start To ArraySize(VGMARR())-1

    Select VGMARR(i)\type
      Case 3 ; pauses   
        PlayedTicks + VGMARR(i)\pause
        PlayedUS = PlayedTicks * 22.675736961;90.702947844;22.675736961
        While (CurrentUS < PlayedUS)
          Delay(1)
          CurrentUS.i = (ElapsedMilliseconds() - StartMS) * 1000
        Wend

      Case 5 ; PSG
        Write(VGMARR(i)\val)
       
      Case 6 ; PSG
        Write(VGMARR(i)\val, 1)
       
      Case 7 ; stereo flags
        GGStereoWrite(VGMARR(i)\val)
       
      Case 8 ; stereo flags
        GGStereoWrite(VGMARR(i)\val, 1)
       
    EndSelect
   
    If TormozFlag     
      Break
    EndIf
   
  Next
 
  ;silence when stop
  Write(%10011111)
  Write(%10111111)
  Write(%11011111)
  Write(%11111111)
 
  Write(%10011111, 1)
  Write(%10111111, 1)
  Write(%11011111, 1)
  Write(%11111111, 1)
 
EndProcedure

Procedure ParsePlay(*FileMem)
 
  memsize = MemorySize(*FileMem) 

  ;get version of vgm. ugly code? :)))
  ver$ = ""
  tmp = PeekA(*FileMem + 11)
  ver$ = Str(GetBits(tmp, 4, 7))
  ver$ + Str(GetBits(tmp, 0, 3))
  tmp = PeekA(*FileMem + 10)
  ver$ + Str(GetBits(tmp, 4, 7))
  ver$ + Str(GetBits(tmp, 0, 3))
  tmp = PeekA(*FileMem + 9)
  ver$ + Str(GetBits(tmp, 4, 7))
  ver$ + Str(GetBits(tmp, 0, 3))
  tmp = PeekA(*FileMem + 8)
  ver$ + Str(GetBits(tmp, 4, 7))
  ver$ + Str(GetBits(tmp, 0, 3))
 
  o.l = PeekL(*FileMem+$0C) 
  Clock(o) ; set clock per track. idk will it make effect, or not...
  ;Debug "frequency is " + Str(o)
 
  If Val(ver$) < 151
    ot = *FileMem + 64      ; 64 - it is vgm header. no need it yet
  Else
    If Val(ver$) < 171
      ot = *FileMem + 192   ; 192 header from 1.51 to 1.70
    Else
      ot = *FileMem + 256   ; 256 header from 1.71
    EndIf
   
    ;+extrahead
    If PeekL(*FileMem+$BC) = 4
      ot + PeekL(*FileMem+$C0) ; extra head data size
     
      ;check frequency values
      If o > $40000000 ; it means dual and need to check extra
        shift = PeekL(*FileMem+$C4) ; offset of "Chip Clock" sub header
        If shift
          chipcount = PeekB(*FileMem+$C4+shift) ; to know how many 5 bytes blocks have
          If chipcount
            readfrom = *FileMem+$C4+shift + 1
            For i = 1 To chipcount
              If PeekB(readfrom) = 0 ; 0 - it is id of SN
                extrafrequency = PeekL(readfrom + 1)
                Debug extrafrequency ; f..king!!!!!!!1111oneoneone
                If extrafrequency
                  Clock($40000000 + extrafrequency, 1)
                EndIf
                Break
              EndIf
              readfrom + 5
            Next
          EndIf
        EndIf
      EndIf
     
    EndIf
   
  EndIf 
  do = *FileMem + memsize

  ; create array with size as filesize. array will get less size, than file, but it is ok
  Dim VGMARR(memsize)

  Number.a = 0
  PSGvalue.a
  Arrayind = 0 
 
  For i = ot To do
   
    Number = PeekA(i)
   
    Select Number     
       
      Case $67  ;  0x67 0x66 tt ss ss ss ss (data)
        ; it is big wav data block       
        ; get size of wav data block
        WavDataSize = PeekI(i + 3)
        ; get adress of wav data block
        WavAddres = i + 7
        ; jump to end of wav data block
        i + 6 + WavDataSize
       
      Case $52 ; $52 - register +0
        i+2
       
      Case $53 ; $53 - register +256
        i+2
       
      Case $61 ; $61 - can range from 0 to 65535 (approx 1.49 seconds)
        VGMARR(Arrayind)\type = 3
        VGMARR(Arrayind)\pause = PeekU(i + 1)
        If flagpausehunt
          VGMARR(numforsizecount)\summofpauses + VGMARR(Arrayind)\pause
          flagpausehunt = 0
        EndIf
        Arrayind + 1
        i+2
       
      Case $70 To $7F ; wait n+1 samples, n can range from 0 to 15.
        VGMARR(Arrayind)\type = 3
        VGMARR(Arrayind)\pause = ((Number - $70) + 1)
        If flagpausehunt
          VGMARR(numforsizecount)\summofpauses + VGMARR(Arrayind)\pause
          flagpausehunt = 0
        EndIf
        Arrayind + 1
       
      Case $E0 ; 0xE0   dddddddd   seek to offset dddddddd (Intel byte order) in PCM data bank
        ;jump far
        i + 4
       
      Case $80 To $8F
        flagpausehunt = 1       
        If Number > $80
          VGMARR(Arrayind)\type = 3
          VGMARR(Arrayind)\pause = (Number - $80)
          VGMARR(numforsizecount)\summofpauses + VGMARR(Arrayind)\pause
          Arrayind + 1
        EndIf
       
      Case $4F ; 0x4F   dd   Game Gear PSG stereo, write dd to port 0x06 ; dune with samples
        i + 1
        PSGvalue = PeekA(i)
        VGMARR(Arrayind)\type = 7
        VGMARR(Arrayind)\val = PSGvalue
        Arrayind + 1       
       
      Case $3F ; 0x4F   dd   Game Gear PSG stereo, write dd to port 0x06 ; dune with samples
        i + 1
        PSGvalue = PeekA(i)
        VGMARR(Arrayind)\type = 8
        VGMARR(Arrayind)\val = PSGvalue
        Arrayind + 1       
       
      ;MAIN COMMAND :))
      Case $50 ; 0x50   dd   PSG (SN76489/SN76496) write value dd ; dune with samples
        i + 1
        PSGvalue = PeekA(i)
        VGMARR(Arrayind)\type = 5
        VGMARR(Arrayind)\val = PSGvalue
        Arrayind + 1
       
      Case $30 ; 0x50   dd   PSG (SN76489/SN76496) write value dd ; dune with samples
        i + 1
        PSGvalue = PeekA(i)
        VGMARR(Arrayind)\type = 6
        VGMARR(Arrayind)\val = PSGvalue
        Arrayind + 1 
       
       
      Case $62 ; wait 735 samples (60th of a second), a shortcut for 0x61 0xdf 0x02 ; Lego Tune
        VGMARR(Arrayind)\type = 3
        VGMARR(Arrayind)\pause = 735
        If flagpausehunt
          VGMARR(numforsizecount)\summofpauses + VGMARR(Arrayind)\pause
          flagpausehunt = 0
        EndIf
        Arrayind + 1
       
      Case $63 ; wait 882 samples (50th of a second), a shortcut For 0x61 0x72 0x03
        VGMARR(Arrayind)\type = 3
        VGMARR(Arrayind)\pause = 882
        If flagpausehunt
          VGMARR(numforsizecount)\summofpauses + VGMARR(Arrayind)\pause
          flagpausehunt = 0
        EndIf
        Arrayind + 1
       
      Case $66 ; end of sound
        Debug "end"       
        Break
       
      Default
        ;Debug "unknown command " + Hex(Number)
       
       
    EndSelect
   
    If TormozFlag
      Break
    EndIf

  Next
 
  If TormozFlag = 0
    Play(0)
  EndIf
 
EndProcedure

#ENABLE_GZIP = 16
; #ZLIB_VERSION = "1.2.8"

#Z_NULL = 0
#Z_OK = 0
#Z_STREAM_END = 1
#Z_FINISH = 4
#Z_BLOCK         =  5

Structure Z_STREAM
  *next_in.Byte
  avail_in.l
  total_in.l
  *next_out.Byte
  avail_out.l
  total_out.l
  *msg.Byte
  *state
  zalloc.l     
  zfree.l 
  opaque.l
 
  CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
    PB_Alignment1.b[4]
  CompilerEndIf
 
  data_type.i
  adler.l
  reserved.l
 
  CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
    PB_Alignment2.b[8]
  CompilerEndIf 
EndStructure

ImportC "zlib.lib"
  zlibVersion()
  inflateInit2_(*strm, windowBits.i, Version.s, strm_size)
  inflate(*strm, flush.i)
  inflateEnd(*strm)
  deflateInit2_(*strm, level.i, method.i, windowBits.i, memlevel.i, strategy.i, Version.s, strm_size) 
  deflateBound(*strm, sourceLen.l)
  deflate(*strm, flush.i)
  deflateEnd(*strm)
EndImport : Global ZLIB_VERSION$ = PeekS(zlibVersion(), -1, #PB_Ascii)

Procedure InflatePayload(*TmpMem, windowBits.i, size.i)
  Debug "запустилось"
  LengthToRead = MemorySize(*TmpMem)
  LengthToWrite = size;FileSize("1test.dds")
  *MemoryID = AllocateMemory(LengthToWrite)
  strm.Z_STREAM
  strm\next_in = *TmpMem
  strm\avail_in = LengthToRead
  strm\next_out = *MemoryID
  strm\avail_out = LengthToWrite
  strm\zalloc = #Z_NULL
  strm\zfree = #Z_NULL
  strm\opaque = #Z_NULL
  inflateInit2_(@strm, windowBits, ZLIB_VERSION$, SizeOf(Z_STREAM))
  inflate(@strm, #Z_FINISH)
  inflateEnd(@strm)
  ProcedureReturn *MemoryID
EndProcedure




If OpenWindow(#Window, 100, 200, 195, 260, "PureBasic Window", #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget)
 
  StringGadget(#PathString, 10, 20, 120, 20, "", #PB_String_ReadOnly)
  ButtonGadget(#OpenButton, 130, 20, 50, 20, "open")
 
  TextGadget(#Text, 10, 40, 170, 90, "WARNING! see what chips was used by this VGM. it plays only one SN76489. YM2612 is ignored. any VGM with other chips can crash programm.")
 
  StringGadget(#From, 10, 120, 65, 20, "550")
  GadgetToolTip(#From, "starts from...")
 
  ButtonGadget(#Play, 80, 120, 50, 20, "play")
  ButtonGadget(#Stop, 135, 120, 50, 20, "stop")
 
  FrameGadget(#Fr01, 1, 158, 93, 97, "1 chip")
  FrameGadget(#Fr02, 95, 158, 93, 97, "2 chip")
 
 
  CheckBoxGadget(#Ch1L, 5, 170, 70, 20, "L   1Ch   R")
  CheckBoxGadget(#Ch2L, 5, 190, 70, 20, "L   2Ch   R")
  CheckBoxGadget(#Ch3L, 5, 210, 70, 20, "L   3Ch   R")
  CheckBoxGadget(#Ch4L, 5, 230, 70, 20, "L   4Ch   R")
 
  CheckBoxGadget(#Ch1R, 75, 170, 16, 20, "")
  CheckBoxGadget(#Ch2R, 75, 190, 16, 20, "")
  CheckBoxGadget(#Ch3R, 75, 210, 16, 20, "")
  CheckBoxGadget(#Ch4R, 75, 230, 16, 20, "")
 
  CheckBoxGadget(#Ch1L2, 100, 170, 70, 20, "L   1Ch   R")
  CheckBoxGadget(#Ch2L2, 100, 190, 70, 20, "L   2Ch   R")
  CheckBoxGadget(#Ch3L2, 100, 210, 70, 20, "L   3Ch   R")
  CheckBoxGadget(#Ch4L2, 100, 230, 70, 20, "L   4Ch   R")
 
  CheckBoxGadget(#Ch1R2, 170, 170, 16, 20, "")
  CheckBoxGadget(#Ch2R2, 170, 190, 16, 20, "")
  CheckBoxGadget(#Ch3R2, 170, 210, 16, 20, "")
  CheckBoxGadget(#Ch4R2, 170, 230, 16, 20, "")
 
 
 
 
  Repeat
   
    Event = WaitWindowEvent()
   
    Select Event

      Case #PB_Event_Gadget
        Select EventGadget()
          ;- Event 
          Case #OpenButton
            If File$
              StandardFile$ = GetPathPart(File$)
            Else
              StandardFile$ = "C:\" ;GetPathPart(ProgramFilename());         ; initial path + file
            EndIf
            Pattern$ = "VGM files (*.vgm)|*.vgm;*.vgz;";*.vgz|"   ; set first pattern  (index = 0)
            Pattern = 0    ; use the second of the five possible patterns as standard

            ; Now we open a filerequester, you can change the pattern and will get the index after closing
            File$ = OpenFileRequester("Please choose file to load", StandardFile$, Pattern$, Pattern)
            If File$
              ;For i = #Ch1R To #Ch4L2
              ;  SetGadgetState(i, 1)
              ;Next
              ;GGStereoWrite(255)
              ;GGStereoWrite(255 + 256)             
              SetGadgetState(#Ch2R2, 1)             
              SetGadgetState(#Ch2L2, 1)
              GGStereoWrite(0)
              GGStereoWrite(34, 1)
             
              TormozFlag = 1
              If IsThread(PlThr)
                WaitThread(PlThr)
              EndIf
              SetGadgetText(#PathString, File$)
             
              If ReadFile(#File, File$)
               
                If *MemoryID
                  FreeMemory(*MemoryID)
                  *MemoryID = 0
                EndIf
               
                length = Lof(#File)
               
                If ReadByte(#File) = $56 And ReadByte(#File) = $67 And ReadByte(#File) = $6D
                  ;unpacked VGM
                  Debug "unpacked"
                 
                  *MemoryID = AllocateMemory(length)         ; allocate the needed memory
                  If *MemoryID
                    FileSeek(#File, 0)
                    ReadData(#File, *MemoryID, length)   ; read all data into the memory block
                  EndIf
                Else                 
                  Debug "packed"
                  ;get size unpacked
                  FileSeek(#File, length - 4)
                  unpsz = ReadLong(#File)
                 
                  If *TmpMem
                    FreeMemory(*TmpMem)
                    *TmpMem = 0
                  EndIf
                 
                  *TmpMem = AllocateMemory(length)         ; allocate the needed memory
                  If *TmpMem
                    FileSeek(#File, 0)
                    ReadData(#File, *TmpMem, length)   ; read all data into the memory block
                   
                    *MemoryID = InflatePayload(*TmpMem, #ENABLE_GZIP, unpsz) ;unpack 
                   
                  EndIf
                 
                EndIf                 

                CloseFile(#File)
               
                TormozFlag = 0
               
                If *MemoryID
                  PlThr = CreateThread(@ParsePlay(), *MemoryID)
                Else
                  MessageRequester("error", "cant read file")
                EndIf               
               
              EndIf
             
            EndIf
           
          Case #Ch1R To #Ch4L
            forstereo = 0
            For i = #Ch1R To #Ch4L
              value = GetGadgetState(i)
              If value
                SetBit(forstereo, NumToBit(i-#Ch1R))
              Else
                ClearBit(forstereo, NumToBit(i-#Ch1R))
              EndIf
            Next
            GGStereoWrite(forstereo)
           
          Case #Ch1R2 To #Ch4L2
            forstereo = 0
            For i = #Ch1R2 To #Ch4L2
              value = GetGadgetState(i)
              If value
                SetBit(forstereo, NumToBit(i-#Ch1R2))
              Else
                ClearBit(forstereo, NumToBit(i-#Ch1R2))
              EndIf
            Next
            GGStereoWrite(forstereo, 1)
           
          Case #Play
            If *MemoryID
              TormozFlag = 1
              If IsThread(PlThr)
                WaitThread(PlThr)
              EndIf
             
              TormozFlag = 0
              PlThr = CreateThread(@Play(), *MemoryID)
             
            EndIf
           
          Case #Stop
            TormozFlag = 1
           
           
        EndSelect
           
           

      Case #PB_Event_CloseWindow
         Quit = 1
         
    EndSelect

  Until Quit = 1
 
EndIf

End


he is not like when i sets 2 000 000 value.
Clock(extrafrequency, 1)

probably he want this one:
Clock($40000000 + extrafrequency, 1)
that is work :))))) now 1 in 1 sounds as winamp do... for this cases... who knows where ValleyBell set grand piano in a bush...


Top
 Profile  
Reply with quote  
 Post subject: Re: Noob's investigation of VGM
PostPosted: Sun Sep 25, 2016 12:02 pm 
Offline
PureBasic Expert
PureBasic Expert

Joined: Sun Aug 08, 2004 5:21 am
Posts: 3678
Location: Netherlands
The VGM specification also mentions Chip Volume Header.
Do you understand it ?
It's not very clear to me how it should affect the output of the chip.

_________________
macOS 10.15 Catalina, Windows 10


Top
 Profile  
Reply with quote  
 Post subject: Re: Noob's investigation of VGM
PostPosted: Sun Sep 25, 2016 1:26 pm 
Offline
Enthusiast
Enthusiast

Joined: Fri Feb 20, 2009 9:24 am
Posts: 573
Location: Almaty (Kazakhstan)
ValleyBell says it used for all chip and SN76489 too, but he not have examples of VGM for this case. if we no have examples - how we know how right it sounds?

Quote:
Chip Volume Header

1 byte - Entry Count (chips with user-defined volumes)
[4 bytes - List Entry 1]
[4 bytes - List Entry 2]
...
Each list entry has the format:

1 byte - Chip ID (chip order follows the header)

Note: If bit 7 is set, it's the volume for a paired chip. (e.g. the AY-part of the YM2203)
1 byte - Flags

Note: If bit 0 is set, it's the volume for the second chip.
2 bytes - volume for the chip

Note: If Bit 15 is 0, this is an absolute volume setting. If Bit 15 is 1, it's relative and the chip volume gets multiplied by ((Value & 0x7FFF) / 0x0100).


i am vote for ignore it :) study black hole by watching them in telescope is suks.

i see this as additional value for volume table:
\vt[0] = 4095 * somevalue : \vt[1] = 4095 * somevalue : \vt[2] = 4095 * somevalue
\vt[3] =

where this somevalue = 1 for basic, and some 1.1 for louder, 0.9 for lower... but how to get logic - i dont know :) ValleyBell not give answer for this case. as i say - grand piano in a bush :) i think this extra volume values is more rare, then PSG with samples.


Top
 Profile  
Reply with quote  
 Post subject: Re: Noob's investigation of VGM
PostPosted: Mon Sep 26, 2016 8:07 am 
Offline
PureBasic Expert
PureBasic Expert

Joined: Sun Aug 08, 2004 5:21 am
Posts: 3678
Location: Netherlands
I got a new version you can try; rewrote a lot so it would need some testing.
Code:
; SN76489 module v1.43 by Wilbert
; modeled after ActionScript code by Shiru

DeclareModule SN76489
 
  Declare SN76489()
  Declare SetChipVolume(volume.u, chip = 0)
  Declare SetClock(freq.l, chip = 0)
  Declare SetNoisePattern(pattern.l, chip = 0)
  Declare Reset(chip = 0)
  Declare Render(*buf, len.l)
  Declare GGStereoWrite(val.a, chip = 0)
  Declare Write(val.a, chip = 0)
 
EndDeclareModule

Module SN76489
 
  EnableExplicit
  DisableDebugger 
  EnableASM
 
  ;- Structures
 
  Structure Chip      ; 100 bytes
    ticksCount.l      ; offset  0
    ticksPerSample.l  ; offset  4
    vol.a[4]          ; offset  8
    div.u[4]          ; offset 12
    cnt.w[4]          ; offset 20
    out.u[4]          ; offset 28
    noiseLFSR.u       ; offset 36
    noiseTap.u        ; offset 38
    flipflop.a[4]     ; offset 40
    channel_mix.l[4]  ; offset 44
    lpfFeedback.u[4]  ; offset 60
    volume.u          ; offset 68
    noiseRegShift.u   ; offset 70
    latchedChan.u     ; offset 72
    latchedVol.u      ; offset 74
    reserved.u[12]    ; offset 76
  EndStructure
 
  Structure SN76489_State
    bufpos.l          ; [-16432]
    buftotal.l[2]     ; [-16428]
    buffer.l[4096]    ; [-16420]
    vt.u[16]          ; [   -36]   volume table
    flags.l           ; [    -4]   bit 31 = T6W28, bit 30 = dual chip
    chip.Chip[2]      ; [     0]
  EndStructure
   
  ;-Global variables
 
  Global SN76489_State.SN76489_State
 
  ;- Render core
 
  CompilerIf #PB_Compiler_Processor = #PB_Processor_x86
    Macro rax : eax : EndMacro
    Macro rbx : ebx : EndMacro
    Macro rcx : ecx : EndMacro
    Macro rdx : edx : EndMacro
    Macro rsi : esi : EndMacro
    Macro rdi : edi : EndMacro
  CompilerEndIf 
 
  Macro M_Channel(chip, channel)
    sub word [rsi+20 + chip*100 + channel*2], 1     ; cnt - 1
    !jnc .c1#chip#channel                           ; cnt >= 0 => c#channel
    movzx ecx, word [rsi+12 + chip*100 + channel*2] ; get div
    CompilerIf channel = 3
      ; >> noise <<
      !and ecx, 3
      !mov eax, 0x10
      !shl al, cl
      !jns .n#chip
      movzx eax, word [rsi+12 + chip*100 + 2*2]     ; get div of channel 2
      !.n#chip:
      !shl eax, 1
      mov [rsi+20 + chip*100 + channel*2], ax       ; set cnt
      movzx eax, word [rsi+36 + chip*100]           ; noiseLFSR
      movzx edx, word [rsi+38 + chip*100]           ; noiseTap
      movzx ecx, word [rsi+70 + chip*100]           ; noise reg width
      !and edx, eax                                 ; apply noiseTap to noiseLFSR
      !xor dl, dh                                   ; get parity
      !setnp dl
      !shl edx, cl
      !shr eax, 1
      !or eax, edx
      mov [rsi+36 + chip*100], ax                   ; update noiseLFSR
      !and eax, 1
      !jz .c0#chip#channel
      movzx ecx, byte [rsi+8 + chip*100 + channel]  ; get noise vol
    CompilerElse
      ; >> tone <<
      !xor eax, eax
      mov [rsi+20 + chip*100 + channel*2], cx       ; set cnt
      !cmp ecx, 1                                   ; div = 1 ?
      !je .c0#chip#channel
      movzx ecx, byte [rsi+8 + chip*100 + channel]  ; get vol
      !jb .t#chip#channel
      movzx eax, byte [rsi+40 + chip*100 + channel] ; get flipflop
      !or ecx, eax
      !xor eax, 0xf                                 ; invert
      mov [rsi+40 + chip*100 + channel], al         ; set flipflop
      !.t#chip#channel: 
    CompilerEndIf
    movzx eax, word [rsi-36 + rcx*2]                ; lookup vol in table
    movzx ecx, word [rsi+68 + chip*100]             ; combine with chip volume
    !imul eax, ecx
    !shr eax, 8
    !.c0#chip#channel:
    mov [rsi+28 + chip*100 + channel*2], ax         ; set out value
    !.c1#chip#channel:
    movzx eax, word [rsi+28 + chip*100 + channel*2] ; low pass filter
    movzx ecx, word [rsi+60 + chip*100 + channel*2]
    !lea eax, [eax*3]
    !lea ecx, [ecx*5]
    !add eax, ecx
    !shr eax, 3
    mov [rsi+60 + chip*100 + channel*2], ax
  EndMacro
 
  Macro M_Tick(chip)
    sub dword [rsi + chip*100], 0x01000000          ; update ticksCount
    !ja .rloop#chip
    mov eax, [rsi+4 + chip*100]                     ; ticksCount+=ticksPerSample
    add [rsi + chip*100], eax
  EndMacro
   
  Macro M_ChannelOut(chip, channel)
    movzx ecx, word [rsi+60 + chip*100 + channel*2]
    imul ecx, [rsi+44 + chip*100 + channel*4]       ; apply channel mix
    !add eax, ecx
  EndMacro
   
  Procedure Render(*buf, len.l)
    !mov ecx, [p.v_len]
    !test ecx, ecx
    !jz .exit
    mov rax, *buf
    mov rdx, SN76489_State
    push rbx
    push rsi
    push rdi
    lea rsi, [rdx+16432]                            ; point to first chip
    mov ebx, [rsi-4]
    mov rdi, rax
    !and ecx, 0x3fffffff
    !or ebx, ecx
    !.rloop0:
    M_Channel(0,0)
    M_Channel(0,1)
    M_Channel(0,2)
    M_Channel(0,3)
    M_Tick(0)
    !bt ebx, 30
    !jnc .rskip0
    !.rloop1:
    M_Channel(1,0)
    M_Channel(1,1)
    M_Channel(1,2)
    M_Channel(1,3)
    M_Tick(1)
    !.rskip0:
    !xor eax, eax
    M_ChannelOut(0,0)
    M_ChannelOut(0,1)
    M_ChannelOut(0,2)
    M_ChannelOut(0,3)
    !bt ebx, 30
    !jnc .rskip1
    M_ChannelOut(1,0)
    M_ChannelOut(1,1)
    M_ChannelOut(1,2)
    M_ChannelOut(1,3)
    !shr eax, 1                                     ; divide by 2 if 2 chips
    !and eax, 0x7fff7fff
    !.rskip1:
    mov ecx, [rsi-16432]                            ; get and update buffer index
    !add ecx, 1
    !and ecx, 4095
    mov [rsi-16432], ecx
    mov edx, [rsi-16420 + rcx*4]                    ; remove old buffer sample
    mov [rsi-16420 + rcx*4], eax                    ; write new buffer sample
    !sub dx, ax
    !movsx ecx, dx
    !or edx, 0xffff
    !sub edx, eax
    !sar edx, 16
    sub [rsi-16428], ecx                            ; update totals
    sub [rsi-16424], edx
    mov edx, [rsi-16428]                            ; >> process left channel <<
    !shr edx, 12
    !movzx ecx, ax
    !sub ecx, edx
    !shl ecx, 2
    !cmp ecx, 32767
    !jng .clip0
    !mov ecx, 32767
    !.clip0:
    mov [rdi], cx                                   ; write left output sample
    mov edx, [rsi-16424]                            ; >> process right channel <<
    !shr edx, 12
    !shr eax, 16
    !sub eax, edx
    !shl eax, 2
    !cmp eax, 32767
    !jng .clip1
    !mov eax, 32767
    !.clip1:
    mov [rdi+2], ax                                 ; write right output sample
    add rdi, 4
    !sub ebx ,1
    !test ebx, 0x3fffffff
    !jnz .rloop0
    pop rdi
    pop rsi
    pop rbx
    !.exit:
  EndProcedure
 
  ;- Other procedures
 
  Procedure SetChipVolume(volume.u, chip = 0)
    volume & $7fff : If volume > $400 : volume = $400 : EndIf
    SN76489_State\chip[chip]\volume = volume
  EndProcedure
     
  Procedure SetClock(freq.l, chip = 0)
    !mov eax, [p.v_freq]
    !test eax, eax
    !jz .clock_exit
    !and eax, 0x003fffff
    !cdq
    !mov ecx, 0x01000000
    !mul ecx
    !mov ecx, 16 * 44100
    !div ecx
    !mov ecx, [p.v_chip]
    !and ecx, 1
    !imul ecx, 100
    mov rdx, SN76489_State
    add rdx, 16432                                  ; point to first chip
    mov [rdx+4 + rcx], eax
    !and ecx, ecx
    !jnz .clock_exit
    !mov eax, [p.v_freq]
    !and eax, 0xc0000000
    mov [rdx-4], eax
    !.clock_exit:
  EndProcedure
 
  Procedure SetNoisePattern(pattern.l, chip = 0)
    SN76489_State\chip[chip]\noiseTap = pattern
    SN76489_State\chip[chip]\noiseRegShift = pattern >> 16 - 1
  EndProcedure
   
  Procedure Reset(chip = 0)
    Protected i.i
    With SN76489_State\chip[chip]
      \ticksCount = \ticksPerSample
      For i = 0 To 3
        \vol[i] = 15 : \div[i] = 1 : \cnt[i] = 0 : \out[i] = 0
        \flipflop[i] = 0 : \channel_mix[i] = $00010001 : \lpfFeedback[i] = 0
      Next
      SetNoisePattern($100009, chip)
      \noiseLFSR = 1 << \noiseRegShift : \div[3] = 0
      \volume = $100 : \latchedChan = 0 : \latchedVol = 0
    EndWith
  EndProcedure
 
  Procedure SN76489()
    Protected i.i, v.d = 2047
    FillMemory(@SN76489_State, SizeOf(SN76489_State))
    For i = 0 To 14 : SN76489_State\vt[i] = v : v * 0.79432823 : Next
    SetClock(3579545) : SetClock(3579545, 1)
    Reset() : Reset(1)
  EndProcedure
 
  Procedure Write(val.a, chip = 0)
    Protected.l chan, cdiv
    chip & 1
    With SN76489_State\chip[chip]
      If val & $80
        chan = val >> 5 & 3
        \latchedChan = chan
        \latchedVol = val >> 4 & 1
        cdiv = (\div[chan] & $3f0) | (val & $f)
      Else
        chan = \latchedChan
        cdiv = (\div[chan] & $f) | (val << 4 & $3f0)
      EndIf
      If \latchedVol
        \vol[chan] = val & $f
      Else
        \div[chan] = cdiv
        If chan = 3
          \noiseTap = cdiv << 1 & 8 + 1
          \noiseLFSR = $8000     
        EndIf
      EndIf
    EndWith
  EndProcedure
 
  Procedure GGStereoWrite(val.a, chip = 0)
    Protected.l i
    chip & 1
    For i = 0 To 3
      SN76489_State\chip[chip]\channel_mix[val >> 6 & 4 + i] = (val >> (i+4) & 1) | (val << (16-i) & $10000)
    Next
  EndProcedure
   
EndModule

_________________
macOS 10.15 Catalina, Windows 10


Last edited by wilbert on Sat Oct 01, 2016 2:19 pm, edited 9 times in total.

Top
 Profile  
Reply with quote  
 Post subject: Re: Noob's investigation of VGM
PostPosted: Mon Sep 26, 2016 1:16 pm 
Offline
Enthusiast
Enthusiast

Joined: Fri Feb 20, 2009 9:24 am
Posts: 573
Location: Almaty (Kazakhstan)
0% cpu eating. all plays fine.


Last edited by SeregaZ on Tue Sep 27, 2016 10:16 am, edited 1 time in total.

Top
 Profile  
Reply with quote  
 Post subject: Re: Noob's investigation of VGM
PostPosted: Tue Sep 27, 2016 8:44 am 
Offline
PureBasic Expert
PureBasic Expert

Joined: Sun Aug 08, 2004 5:21 am
Posts: 3678
Location: Netherlands
I posted an update with a new procedure SetMasterVolume.
viewtopic.php?p=494943#p494943
In general you should be able to use 1.5 without getting distortion but you can experiment for yourself.

_________________
macOS 10.15 Catalina, Windows 10


Top
 Profile  
Reply with quote  
 Post subject: Re: Noob's investigation of VGM
PostPosted: Tue Sep 27, 2016 9:52 am 
Offline
Enthusiast
Enthusiast

Joined: Fri Feb 20, 2009 9:24 am
Posts: 573
Location: Almaty (Kazakhstan)
SetMasterVolume(volume.f = 1.0) - it no have chip select?
SetMasterVolume(volume.f = 1.0, chip = 0) i think it need to be some like this


Last edited by SeregaZ on Tue Sep 27, 2016 9:15 pm, edited 2 times in total.

Top
 Profile  
Reply with quote  
 Post subject: Re: Noob's investigation of VGM
PostPosted: Tue Sep 27, 2016 11:15 am 
Offline
PureBasic Expert
PureBasic Expert

Joined: Sun Aug 08, 2004 5:21 am
Posts: 3678
Location: Netherlands
SeregaZ wrote:
SetMasterVolume(volume.f = 1.0) - it no have chip select?
SetMasterVolume(volume.f = 1.0, chip = 0) i think it need to be some like this

The master volume is done after both chips have been mixed which is easier. Setting the chip volume is more complicated.
I can add it but only for a limited range (for example 0.0x - 2.0x) which seems to be incompatible with the VGM additional volume header (I prefer it to be compatible).
It looks like the VGM volume header allows a multiplication in the range of 0.0 - 128.0. :?
I have no idea why you would want such a range.

_________________
macOS 10.15 Catalina, Windows 10


Top
 Profile  
Reply with quote  
 Post subject: Re: Noob's investigation of VGM
PostPosted: Tue Sep 27, 2016 12:23 pm 
Offline
Enthusiast
Enthusiast

Joined: Fri Feb 20, 2009 9:24 am
Posts: 573
Location: Almaty (Kazakhstan)
this extra volume for PSG probably is not exists. and about chip splitting:
Quote:
Note: If bit 0 is set, it's the volume for the second chip.

so first chip can be default, second can be some tuned. that is why i am talk about it.

about 128 - probably we can make some custom VGM file, and see what winamp will do. but i think PSG no have so big values. if PSG have any values for this volume :)

https://www.dropbox.com/s/owrb3x0ge4h75 ... t.vgm?dl=1

i dont know what i must hear, but i am hear nothing :)
xD6 when it have 0F FF value. or i just set wrong values...


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 117 posts ]  Go to page Previous  1, 2, 3, 4, 5, 6 ... 8  Next

All times are UTC + 1 hour


Who is online

Users browsing this forum: No registered users and 13 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
Jump to:  

 


Powered by phpBB © 2008 phpBB Group
subSilver+ theme by Canver Software, sponsor Sanal Modifiye