It is currently Wed Aug 21, 2019 2:39 pm

All times are UTC + 1 hour




Post new topic Reply to topic  [ 117 posts ]  Go to page Previous  1, 2, 3, 4, 5, 6, 7, 8  Next
Author Message
 Post subject: Re: Noob's investigation of VGM
PostPosted: Tue Sep 27, 2016 1:51 pm 
Offline
PureBasic Expert
PureBasic Expert

Joined: Sun Aug 08, 2004 5:21 am
Posts: 3374
Location: Netherlands
I updated the function. It's called now
SetChipVolume(volume.u, chip = 0)
The volume is a value between 0 and $400 where $400 means 4 times multiplication.
That sounds like enough for me as it's already causing distortion with a 4 times multiplication.

_________________
macOS 10.14 Mojave, PB 5.62 x64


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

Joined: Fri Feb 20, 2009 9:24 am
Posts: 561
Location: Almaty (Kazakhstan)
this value need to get from extra, when it have?


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

Joined: Sun Aug 08, 2004 5:21 am
Posts: 3374
Location: Netherlands
SeregaZ wrote:
this value need to get from extra, when it have?

Yes, the VGM v1.70 Extra Header can have both information about the clock setting of a chip and about the volume of a chip.

_________________
macOS 10.14 Mojave, PB 5.62 x64


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

Joined: Fri Feb 20, 2009 9:24 am
Posts: 561
Location: Almaty (Kazakhstan)
it have one small problem :) you say command set a multiple, but:
Quote:
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).

it can be multiple, and i understand how it work - multiple is multiple, but what does it mean absolute? it means maximum volume for volume 0 in a table? chip have 15 values... which one of them is this "absolute"? it is black hole :) no examples, no clear explanation of ValleyBell... i set any values for VGM and play in winamp - 0 to $FF - silence, $100 to $FFFF - i think same volume. no louder, no lower...

since 224 string it starts.
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
 
  #ProgressLoad
  #ProgressPlay
 
  #Volume1
  #Volume2
 
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

Global PlayPos
Global Arrayind

;{
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 Mute()
 
  Write(%10011111)
  Write(%10111111)
  Write(%11011111)
  Write(%11111111)
 
  Write(%10011111, 1)
  Write(%10111111, 1)
  Write(%11011111, 1)
  Write(%11111111, 1)
 
EndProcedure

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

  For i = start To Arrayind

    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
   
    SetGadgetState(#ProgressPlay, i)
   
    If PlayPos
      i = PlayPos
      PlayPos = 0
    EndIf   
   
  Next
 
  Debug "end of playing"
 
  ;silence when stop
  Mute()
 
  TormozFlag = 1
 
EndProcedure

Procedure ParsePlay(*FileMem)
 
  memsize = MemorySize(*FileMem)
  SetGadgetAttribute(#ProgressLoad, #PB_ProgressBar_Maximum, memsize)

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

  ver = Val(ver$) 
  If ver < 151
    ot = *FileMem + 64      ; 64 - it is vgm header. no need it yet
  Else
    If ver < 171
      ot = *FileMem + 192   ; 192 header from 1.51 to 1.70
    Else
      ot = *FileMem + 256   ; 256 header from 1.71
    EndIf

    If ver > 159
      Debug "Volume modifier " + Str(PeekW(*FileMem + $7C))
    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
         
          If shift > 4 ; it means need to check volume           
            volshift = PeekL(*FileMem+$C8)           
            If volshift
              volchipcount = PeekB(*FileMem+$C8+volshift)
              If volchipcount
                readfrom = *FileMem+$C8+volshift + 1
                For i = 1 To volchipcount
                  ;If bit 7 is set, it's the volume for a paired chip. (e.g. the AY-part of
                  ;the YM2203)
                  checkvalue = PeekB(readfrom)
                  If TestBit(checkvalue, NumToBit(7))
                    ;it means for both?
                  EndIf
                 
                  If GetBits(checkvalue, 0, 6) = 0 ; id of SN                 
                    ;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).
                    checkvalue = PeekU(readfrom + 2)
                    volvolue.u   = GetBits(checkvalue, 0, 14)
                    If TestBit(checkvalue, NumToBit(15))
                      ;multiplied
                      volvolue = ((volvolue & $7FFF) / $100)
                    EndIf                 
                 
                    ;If bit 0 is set, it's the volume for the second chip.
                    checkvalue = PeekB(readfrom + 1)
                    If TestBit(checkvalue, NumToBit(0))
                      ;or this place deside for both or only for second?
                      ;if it is - what for was first "paired" check?
                      ;probably that paires is non PSG and can be ignored?
                      SetChipVolume(volvolue, 1)
                    Else
                      SetChipVolume(volvolue)
                      SetChipVolume(volvolue, 1)
                    EndIf
                    Debug volvolue
                 
                    Break
                  EndIf

                  readfrom + 4
                Next               
              EndIf             
            EndIf           
          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 
 
  progload = *FileMem
  fulllength = 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)
        fulllength + VGMARR(Arrayind)\pause
        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)
        fulllength + VGMARR(Arrayind)\pause
        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
        If Number > $80
          VGMARR(Arrayind)\type = 3
          VGMARR(Arrayind)\pause = (Number - $80)
          fulllength + 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
        fulllength + VGMARR(Arrayind)\pause
        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
        fulllength + VGMARR(Arrayind)\pause
        Arrayind + 1
       
      Case $66 ; end of sound
        Debug "end"       
        Break
       
      Default
        ;Debug "unknown command " + Hex(Number)
       
       
    EndSelect
   
    If TormozFlag
      Break
    EndIf
   
    If progload < i
      SetGadgetState(#ProgressLoad, i-*FileMem)     
      progload + 1000
    EndIf

  Next
 
  If TormozFlag = 0
    SetGadgetState(#ProgressLoad, memsize) ; fill 100%
    SetGadgetAttribute(#ProgressPlay, #PB_ProgressBar_Maximum, Arrayind)
    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, 240, 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")
 
  ProgressBarGadget(#ProgressLoad, 10, 42, 120, 2, 0, 100)
 
  ProgrX      =  10
  ProgrY      =  44
  ProgrWidth  = 120
  ProgrHeight =  10
  ProgressBarGadget(#ProgressPlay, ProgrX, ProgrY, ProgrWidth, ProgrHeight, 0, 100)
 
 
  TextGadget(#Text, 10, 60, 170, 50, "WARNING! it plays only SN76489. YM2612 is ignored. any VGM with other chips can crash programm.")
 
  StringGadget(#From, 10, 120, 65, 20, "0");"550")
  GadgetToolTip(#From, "starts from...")
 
  ButtonGadget(#Play, 80, 120, 50, 20, "play")
  ButtonGadget(#Stop, 135, 120, 50, 20, "stop")
 
  FrameGadget(#Fr01, 1, 158, 120, 97, "1 chip")
  FrameGadget(#Fr02, 120, 158, 120, 97, "2 chip")
 
 
  CheckBoxGadget(#Ch1L, 9, 170, 70, 20, "L   1Ch   R")
  CheckBoxGadget(#Ch2L, 9, 190, 70, 20, "L   2Ch   R")
  CheckBoxGadget(#Ch3L, 9, 210, 70, 20, "L   3Ch   R")
  CheckBoxGadget(#Ch4L, 9, 230, 70, 20, "L   4Ch   R")
 
  CheckBoxGadget(#Ch1R, 79, 170, 16, 20, "")
  CheckBoxGadget(#Ch2R, 79, 190, 16, 20, "")
  CheckBoxGadget(#Ch3R, 79, 210, 16, 20, "")
  CheckBoxGadget(#Ch4R, 79, 230, 16, 20, "")
 
  CheckBoxGadget(#Ch1L2, 128, 170, 70, 20, "L   1Ch   R")
  CheckBoxGadget(#Ch2L2, 128, 190, 70, 20, "L   2Ch   R")
  CheckBoxGadget(#Ch3L2, 128, 210, 70, 20, "L   3Ch   R")
  CheckBoxGadget(#Ch4L2, 128, 230, 70, 20, "L   4Ch   R")
 
  CheckBoxGadget(#Ch1R2, 198, 170, 16, 20, "")
  CheckBoxGadget(#Ch2R2, 198, 190, 16, 20, "")
  CheckBoxGadget(#Ch3R2, 198, 210, 16, 20, "")
  CheckBoxGadget(#Ch4R2, 198, 230, 16, 20, "")
 
  TrackBarGadget(#Volume1, 95, 166, 20, 85, 0, 10, #PB_TrackBar_Vertical)
  SetGadgetState(#Volume1, 5)
 
  TrackBarGadget(#Volume2, 213, 166, 20, 85, 0, 10, #PB_TrackBar_Vertical)
  SetGadgetState(#Volume2, 5)
 
 
 
 
  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$
              Mute()
              For i = #Ch1R To #Ch4L2
                SetGadgetState(i, 1)
              Next
              GGStereoWrite(255)
              GGStereoWrite(255, 1)             
              ;SetGadgetState(#Ch2R2, 1)             
              ;SetGadgetState(#Ch2L2, 1)
              ;GGStereoWrite(0)
              ;GGStereoWrite(34, 1)
             
              TormozFlag = 1
              If IsThread(PlThr)
                KillThread(PlThr)
                If IsThread(PlThr)
                  WaitThread(PlThr)
                EndIf
              EndIf
              SetGadgetText(#PathString, File$)
              SetGadgetState(#ProgressLoad, 0)
             
              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 unpsz
                 
                    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   
                 
                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
           
          Case #Volume1
            VolumeL.u
            Select GetGadgetState(#Volume1)
              Case 10
                VolumeL = $400
              Case 9
                VolumeL = $300
              Case 8
                VolumeL = $200
              Case 7
                VolumeL = $150
              Case 6
                VolumeL = $120
              Case 5
                VolumeL = $100
              Case 4
                VolumeL = $80
              Case 3
                VolumeL = $60
              Case 2
                VolumeL = $40
              Case 1
                VolumeL = $20             
              Case 0
                VolumeL = $0
            EndSelect
            SetChipVolume(VolumeL)
           
          Case #Volume2
            VolumeL.u
            Select GetGadgetState(#Volume2)
              Case 10
                VolumeL = $400
              Case 9
                VolumeL = $300
              Case 8
                VolumeL = $200
              Case 7
                VolumeL = $150
              Case 6
                VolumeL = $120
              Case 5
                VolumeL = $100
              Case 4
                VolumeL = $80
              Case 3
                VolumeL = $60
              Case 2
                VolumeL = $40
              Case 1
                VolumeL = $20             
              Case 0
                VolumeL = $0
            EndSelect
            SetChipVolume(VolumeL, 1)
           
        EndSelect
       
      Case 512 ;{ mousemove
        xmouse = WindowMouseX(0)
        ymouse = WindowMouseY(0) 
        ;}
       
      Case 513 ;{ leftclick
         If IsThread(PlThr) And (xmouse > ProgrX And xmouse < ProgrX + ProgrWidth) And (ymouse > ProgrY And ymouse < ProgrY + ProgrHeight)
           Mute()
           getoneproc.f   = GetGadgetAttribute(#ProgressPlay, #PB_ProgressBar_Maximum) / 100
           getwidthproc.f = ProgrWidth / 100           
           getabsvalue    = xmouse - ProgrX
           PlayPosF.f     = getabsvalue / getwidthproc
           PlayPosF * getoneproc
           PlayPos = PlayPosF
         EndIf
       ;}     

      Case #PB_Event_CloseWindow
        TormozFlag = 1
        Delay(10)
        If IsThread(PlThr)
          KillThread(PlThr)
          If IsThread(PlThr)
            WaitThread(PlThr)
          EndIf
        EndIf
        Quit = 1
         
    EndSelect

  Until Quit = 1
 
EndIf

End


Last edited by SeregaZ on Wed Sep 28, 2016 7:00 am, edited 2 times in total.

Top
 Profile  
Reply with quote  
 Post subject: Re: Noob's investigation of VGM
PostPosted: Wed Sep 28, 2016 4:59 am 
Offline
PureBasic Expert
PureBasic Expert

Joined: Sun Aug 08, 2004 5:21 am
Posts: 3374
Location: Netherlands
SeregaZ wrote:
it have one small problem :) you say command set a multiple, but:
Quote:
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).

it can be multiple, and i understand how it work - multiple is multiple, but what does it mean absolute? it means maximum volume for volume 0 in a table? chip have 15 values... which one of them is this "absolute"? it is black hole :) no examples, no clear explanation of ValleyBell... i set any values for VGM and play in winamp - 0 to $FF - silence, $100 to $FFFF - i think same volume. no louder, no lower...

As far as I understand for the SN76489 the ratio is always 2:1 .
So a relative volume of $100 is the same as an absolute volume of $80.
A relative volume of $200 is the same as an absolute volume of $100.
In other words, with the same value for absolute and relative, absolute should be 3 dB louder.

I found another volume related thing :?
v1.60 and above have Volume Modifier (header offset $7c).
Don't know if it's important or not.

_________________
macOS 10.14 Mojave, PB 5.62 x64


Top
 Profile  
Reply with quote  
 Post subject: Re: Noob's investigation of VGM
PostPosted: Wed Sep 28, 2016 6:42 am 
Offline
Enthusiast
Enthusiast

Joined: Fri Feb 20, 2009 9:24 am
Posts: 561
Location: Almaty (Kazakhstan)
i am forgot set break and id 0 checking :)

anyway - where to get examples for check how correct this values is reads and apply? that test vgm - i make, and i am not sure how correct i made it. i think if some values make silence - it is right place, but why volume have only two cases - sound or silence :) i didnt hear difference by set any values.

and i am start hate VGM :) maybe we are will kick out extra volume header and will use only this volume modifier? btw this train song says this modifier is 24.

so volume is:
note volume 15 cases
extra header setting
volume modifier

too much for volume :)

note volume 15 cases - individual note
extra header setting - for both, or just for second.
volume modifier - for both chips?


Top
 Profile  
Reply with quote  
 Post subject: Re: Noob's investigation of VGM
PostPosted: Wed Sep 28, 2016 9:33 am 
Offline
PureBasic Expert
PureBasic Expert

Joined: Sun Aug 08, 2004 5:21 am
Posts: 3374
Location: Netherlands
SeregaZ wrote:
and i am start hate VGM :) maybe we are will kick out extra volume header and will use only this volume modifier?

I think for now it might be best to ignore both that volume modifier and the difference between absolute and relative volume setting.
The volume modifier is not related to a specific chip but to the entire player in general.
The difference between absolute and relative volume varies per chip model. :?

I posted an update of the module
I added SetNoisePattern which allows to set the noise pattern that is used.
Clock is renamed now to SetClock for consistency with other procedure names.

_________________
macOS 10.14 Mojave, PB 5.62 x64


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

Joined: Fri Feb 20, 2009 9:24 am
Posts: 561
Location: Almaty (Kazakhstan)
Quote:
2016-09-28 12:41:44 ValleyBell 0x7C is a global setting used to makes quiet VGMs louder.
2016-09-28 12:57:23 ValleyBell (tl;dr: It is "Replay Gain" for VGMs.)


Quote:
I think for now it might be best to ignore both that volume modifier and the difference between absolute and relative volume setting.

i agree :) frequency for chip will be enough. other params can go to hell :twisted:


Top
 Profile  
Reply with quote  
 Post subject: Re: Noob's investigation of VGM
PostPosted: Thu Sep 29, 2016 9:45 am 
Offline
PureBasic Expert
PureBasic Expert

Joined: Sun Aug 08, 2004 5:21 am
Posts: 3374
Location: Netherlands
I posted a small update.
The bit from the clock frequency which indicates a second chip should only be looked at when the clock for the first chip is set.
With the previous version, setting the clock of the second chip without the bit set put it back to one chip again.

v 1.43 fixes a noise frequency problem.

_________________
macOS 10.14 Mojave, PB 5.62 x64


Top
 Profile  
Reply with quote  
 Post subject: Re: Noob's investigation of VGM
PostPosted: Sat Oct 01, 2016 6:12 pm 
Offline
PureBasic Expert
PureBasic Expert

Joined: Sun Aug 08, 2004 5:21 am
Posts: 3374
Location: Netherlands
Here's a rough attempt at a playback which works on Windows and MacOS also (my main OS).
The Delay() method wan't accurate enough for that on MacOS.

Here's v 1.43 of the include file
viewtopic.php?p=494943#p494943

Code:
XIncludeFile "SN76489.pbi"

CompilerSelect #PB_Compiler_OS
  CompilerCase #PB_OS_Windows
    Macro ztype:l:EndMacro 
    #ZLIB_Filename = "zlib.lib"
  CompilerCase #PB_OS_Linux
    Macro ztype:i:EndMacro 
    #ZLIB_Filename = #PB_Compiler_Home + "purelibraries/linux/libraries/zlib.a"
  CompilerDefault
    Macro ztype:i:EndMacro 
    #ZLIB_Filename = "-lz"
CompilerEndSelect

Structure zstream_ Align #PB_Structure_AlignC
  *next_in
  avail_in.ztype
  total_in.ztype
  *next_out
  avail_out.ztype
  total_out.ztype
  *msg
  *state
  *zalloc
  *zfree
  *opaque
  data_type.ztype
  adler.ztype
  reserved.ztype
EndStructure

ImportC #ZLIB_Filename
  inflate(*strm, flush)
  inflateEnd(*strm)
  inflateInit2_(*strm, windowBits, *version, stream_size)
  zlibVersion()
EndImport

;-Catch and Load VGM files

Procedure.i CatchVGM(*VGM, Size = -1)
  Protected strm.zstream_, *m, *l.Long = *vgm
  Protected.i done, vgmsize
  If *l
    Size & $3fffffff
    If *l\l = $206d6756
      *l + 4 : vgmsize = *l\l + 4
      *l = AllocateMemory(vgmsize)
      If *l And vgmsize <= Size
        CopyMemory(*vgm, *l, vgmsize)
        done = #True
      EndIf
    ElseIf *l\l & $ffff = $8b1f
      *l = AllocateMemory(65536)
      If *l
        strm\next_in = *vgm
        strm\avail_in = Size
        strm\next_out = *l
        strm\avail_out = 65536
        If inflateInit2_(@strm, 31, zlibVersion(), SizeOf(zstream_)) = 0
          inflate(@strm, 0)
          If *l\l = $206d6756
            *l + 4 : vgmsize = *l\l + 4 : *l - 4
            If vgmsize
              *m = ReAllocateMemory(*l, vgmsize)
              If *m
                *l = *m
                If strm\total_out < vgmsize
                  strm\avail_out = vgmsize - 65536
                  inflate(@strm, 4)
                EndIf
                If strm\total_out = vgmsize
                  done = #True
                EndIf
              EndIf
            EndIf
          EndIf
        EndIf
      EndIf
    EndIf
  EndIf
  If *l And Not done
    FreeMemory(*l) : *l = 0
  EndIf
  ProcedureReturn *l 
EndProcedure

Procedure.i LoadVGM(Filename.s)
  Protected.i file, size, *m, *vgm
  file = ReadFile(#PB_Any, Filename)
  If file
    size = Lof(file) : *m = AllocateMemory(size)
    If *m
      If ReadData(file, *m, size) = size
        *vgm = CatchVGM(*m)
      EndIf
      CloseFile(file)
      FreeMemory(*m) 
    EndIf
  EndIf
  ProcedureReturn *vgm
EndProcedure

Procedure FreeVGM(*VGM)
  If *VGM : FreeMemory(*VGM) : EndIf
EndProcedure



Structure VGMData
  l.l[0] : u.u[0] : a.a[0]
EndStructure

Structure VGMCommand
  cmd.a : u.u[0] : a.a[0]
EndStructure

DataSection
  SkipTable:
  ;      0 1 2 3 4 5 6 7 8 9 a b c d e f
  Data.a 1,1,1,2,3,3,0,1,1,0,3,3,4,4,5,5  ; cmd skip
  Data.a 5,5,6,11,2,5                     ; DAC skip
EndDataSection

Global *VGM, *CurrentPos, lOffset, EnableVGM, Wait

Procedure RenderVGM(*buf, len.l)
  Protected.l version, dOffset, lOffset, i, skip
  Protected *cmd.VGMCommand, cmd.a
  Protected.VGMData *v_, *v = *VGM, *skipTable = ?SkipTable
  If EnableVGM And *v And *v\l[0] = $206d6756
   
    ; >> header <<
    If *CurrentPos - *VGM < $40
      version = *v\l[2]                       ; VGM version
      lOffset = *v\l[7]                       ; loop offset
      If lOffset : lOffset + $1C : EndIf
      dOffset = *v\l[13] + $34                ; data offset
      If version < $150 Or dOffset < $40
        dOffset = $40
      EndIf
      SN76489::SetClock(*v\l[3])              ; SN76489 clock
      If version < $110
        SN76489::SetNoisePattern($100009)     ; SN76489 noise
      Else
        SN76489::SetNoisePattern(*v\l[10])
      EndIf
      If dOffset >= $C0                       ;  extra header
        *v = *v\l[47] + $BC
        If *v >= $C0 And dOffset > *v
          *v + *VGM
          If *v\l[0] >= 8
            If *v\l[1]
              *v_ = *v + *v\l[1] + 4
              i = *v_\a[0] : *v_ + 1
              While i : i - 1
                If *v_\a[0] = 0; SN76489 ?
                  *v_ + 1
                  SN76489::SetClock(*v_\l[0], 1)
                  *v_ + 4
                Else
                  *v_ + 5
                EndIf
              Wend
            EndIf
          EndIf
          If *v\l[0] >= 12
            If *v\l[2]
              *v_ = *v + *v\l[2] + 8
              i = *v_\a[0] : *v_ + 1
              While i : i - 1
                If *v_\a[0] = 0; SN76489 ?
                  SN76489::SetChipVolume(*v_\u[1], *v_\a[1] & 1)
                EndIf
                *v_ + 4
              Wend
            EndIf
          EndIf
        EndIf 
      EndIf
      *CurrentPos = *VGM + dOffset
    EndIf
   
    ; >> commands <<
    If *CurrentPos - *VGM >= $40
     
      If Wait
        If Wait >= len
          SN76489::Render(*buf, len)
          Wait - len
          ProcedureReturn
        Else
          SN76489::Render(*buf, Wait)
          *buf + Wait << 2 : len - Wait
          Wait = 0
        EndIf
      EndIf 
     
      *cmd = *CurrentPos
      While len
        wait = 0
        While wait = 0
          cmd = *cmd\cmd
          Select cmd
            Case $50, $30   : SN76489::Write(*cmd\a[0], cmd >> 5 & 1) : *cmd + 2
            Case $4F, $3F   : SN76489::GGStereoWrite(*cmd\a[0], cmd >> 5 & 1) : *cmd + 2
            Case $70 To $7F : wait = cmd & $f + 1 : *cmd + 1
            Case $61        : wait = *cmd\u[0] : *cmd + 3
            Case $62        : wait = 735 : *cmd + 1
            Case $63        : wait = 882 : *cmd + 1
            Case $67        : *v_ = *cmd + 3 : *cmd + 7 + (*v_\l[0] & $7fffffff)
            Case $68        : *cmd + 12
            Case $90 To $95 : *cmd + *skipTable\a[(cmd & $f) | $10]
            Case $66:
              ; normal end of data => loop
              Debug "end"
              If lOffset
                *cmd = *VGM + lOffset
              Else
                EnableVGM = #False
                wait = 10000
              EndIf         
            Default:
              skip = *skipTable\a[cmd >> 4] : *cmd + skip
              If skip = 0
                ; unknown command, quit
                EnableVGM = #False
              EndIf
          EndSelect
        Wend
       
        If Wait > len
          SN76489::Render(*buf, len) : *buf + len << 2
          Wait - len
          Break
        EndIf
        SN76489::Render(*buf, wait) : *buf + wait << 2
        len - wait
       
      Wend
      *CurrentPos = *cmd
     
    EndIf
   
  Else
    FillMemory(*buf, len << 2)
  EndIf
EndProcedure



SN76489::SN76489()

CompilerIf #PB_Compiler_OS = #PB_OS_MacOS
 
  Import "-stdlib=libc++ -mmacosx-version-min=10.7" : EndImport
 
  DataSection
    dOut:
    Data.l $61756f75,$64656620,$6170706c,0,0
    PCM16Bit:
    Data.l 0,$40E58880,$6C70636D,12,4,1,4,2,16,0
  EndDataSection
 
  Global PSG_Unit.i
 
  Procedure PSG_Init(*CallbackFunction, BufferSize.l = 2048)
    Protected Dim CallbackStruct.i(1) : CallbackStruct(0) = *CallbackFunction
    AudioComponentInstanceNew_(AudioComponentFindNext_(#Null, ?dOut), @PSG_Unit)
    AudioUnitSetProperty_(PSG_Unit, 23, 1, 0, @CallbackStruct(), SizeOf(Integer) << 1)
    AudioUnitSetProperty_(PSG_Unit, 8, 1, 0, ?PCM16Bit, 40)
    AudioUnitSetProperty_(PSG_Unit, $6673697a, 1, 0, @BufferSize, 4)
    AudioUnitInitialize_(PSG_Unit)
  EndProcedure
 
  Procedure PSG_Start()
    ProcedureReturn Bool(AudioOutputUnitStart_(PSG_Unit) = 0)
  EndProcedure
 
  Procedure PSG_Stop()
    AudioOutputUnitStop_(PSG_Unit)
  EndProcedure
 
  Procedure PSG_Terminate()
    If PSG_Unit
      AudioOutputUnitStop_(PSG_Unit)
      AudioUnitUninitialize_(PSG_Unit)
      AudioComponentInstanceDispose_(PSG_Unit)
      PSG_Unit = 0
    EndIf
  EndProcedure
 
  ProcedureC Callback(*inRefcon, *ioActionFlags, *inTimeStamp, inBusNumber.l, inNumberFrames.l, *ioData)
    RenderVGM(PeekI(*ioData + SizeOf(Integer) + 8), inNumberFrames)
    ProcedureReturn 0
  EndProcedure
 
  PSG_Init(@Callback())
  PSG_Start()
 
CompilerElse
 
  #PSG_BUFFERS = 4
  Global Dim wavebuf.w(#PSG_BUFFERS - 1, 2047)
  Global Dim wavehdr.WAVEHDR(#PSG_BUFFERS - 1)
  Global.i PSG_Unit, WMME_Playing
 
  DataSection
    PCM16BitSigned:
    Data.w 1,2,$AC44,0,$B110,2,4,16,0
  EndDataSection 
 
  Procedure WMME_Callback(hwo, uMsg, dwInstance, *wavehdr.WAVEHDR, dwParam2)
    If WMME_Playing And uMSG = #WOM_DONE
      RenderVGM(*wavehdr\lpData, 1024)
      waveOutWrite_(hwo, *wavehdr, SizeOf(WAVEHDR))
    EndIf
  EndProcedure
 
  Procedure PSG_Terminate()
    Protected i.i
    If PSG_Unit
      WMME_Playing = #False
      waveOutReset_(PSG_Unit)
      While i < #PSG_BUFFERS     
        waveOutUnprepareHeader_(PSG_Unit, @wavehdr(i), SizeOf(WAVEHDR))
        i + 1
      Wend       
      waveOutClose_(PSG_Unit)
      PSG_Unit = 0
    EndIf
  EndProcedure 
 
  Procedure PSG_Init()
    If waveOutOpen_(@PSG_Unit, #WAVE_MAPPER, ?PCM16BitSigned, @WMME_Callback(), 0, #CALLBACK_FUNCTION)
      MessageRequester("Error", "Unable to initialize PSG")
    Else
      While i < #PSG_BUFFERS
        wavehdr(i)\lpData = @wavebuf(i, 0)
        wavehdr(i)\dwBufferLength = 4096
        waveOutPrepareHeader_(PSG_Unit, @wavehdr(i), SizeOf(WAVEHDR))
        waveOutWrite_(PSG_Unit, @wavehdr(i), SizeOf(WAVEHDR))
        i + 1
      Wend
      WMME_Playing = #True
    EndIf
  EndProcedure
 
  PSG_Init()
 
CompilerEndIf

FileName.s = OpenFileRequester("Choose VGM file", "", "", 0)
*VGM = LoadVGM(FileName)
EnableVGM = #True

MessageRequester("VGM", "Playing")

PSG_Terminate()

_________________
macOS 10.14 Mojave, PB 5.62 x64


Top
 Profile  
Reply with quote  
 Post subject: Re: Noob's investigation of VGM
PostPosted: Sat Oct 01, 2016 9:46 pm 
Offline
Enthusiast
Enthusiast

Joined: Fri Feb 20, 2009 9:24 am
Posts: 561
Location: Almaty (Kazakhstan)
i see you are not read vgm to array, but directly play?


Top
 Profile  
Reply with quote  
 Post subject: Re: Noob's investigation of VGM
PostPosted: Sun Oct 02, 2016 5:34 am 
Offline
PureBasic Expert
PureBasic Expert

Joined: Sun Aug 08, 2004 5:21 am
Posts: 3374
Location: Netherlands
SeregaZ wrote:
i see you are not read vgm to array, but directly play?

Yes, that's true.
Is there any reason you prefer to use an array ?

_________________
macOS 10.14 Mojave, PB 5.62 x64


Top
 Profile  
Reply with quote  
 Post subject: Re: Noob's investigation of VGM
PostPosted: Sun Oct 02, 2016 8:00 am 
Offline
Enthusiast
Enthusiast

Joined: Fri Feb 20, 2009 9:24 am
Posts: 561
Location: Almaty (Kazakhstan)
for PSG probably not. for YM it need for DAC samples playing. problem is - that ValleyBell's dll play samples by external command. it need frequency, size and adress of sample.

by theory samples is play by chip - by using $2A registers, but it didnt work, so ValleyBell make that external command. system must be some kind of your PSG samples play (btw your not plays too :))) some scratch and noise hear).

then come problem with this frequency and size - VGM no have frequency settings for samples and size. that is why i read into array, then make puzzles for samples, only then play. even this case not very well helps. this opn.dll have some problem with save cpu - it is shutdown of playing, if samples have silence. for example: sample length 2 sec, at begin it have some sound, at middle some silence, at the end some play - this dll play first part, then shutdown. he think samples is end if pause more than 0.5 sec.

second problem: samples starts VGM by setting address of sample in wav bank - some kind of setting pointer - i catch this moment and start collect sample byte per byte. and it work and frequency count correct and sample size, BUT! if track have two samples one and second and they lays at wav bank near each other one and second - setting of adress of second samples can be not happen. pointer just continue move far. and beetween of this samples can be large pause - this moment is broke all my super system :))) that is why i start this topic - find help to solve this samples problem.

i think check all track, get all adresses, cut wav bank, then check oversize of sample. if this oversize is happen - it means at this moment starts second sample. it will work, if track will have this second marker of adress, but if is not - deam again :)


Top
 Profile  
Reply with quote  
 Post subject: Re: Noob's investigation of VGM
PostPosted: Sun Oct 02, 2016 8:06 am 
Offline
Enthusiast
Enthusiast

Joined: Fri Feb 20, 2009 9:24 am
Posts: 561
Location: Almaty (Kazakhstan)
and about your delays - we talk about it here:
viewtopic.php?f=13&t=62919

maybe some one can help with pauses for your system?


Top
 Profile  
Reply with quote  
 Post subject: Re: Noob's investigation of VGM
PostPosted: Sun Oct 02, 2016 10:40 am 
Offline
PureBasic Expert
PureBasic Expert

Joined: Sun Aug 08, 2004 5:21 am
Posts: 3374
Location: Netherlands
SeregaZ wrote:
for YM it need for DAC samples playing. problem is - that ValleyBell's dll play samples by external command. it need frequency, size and adress of sample.

Sounds difficult to get it right. A FM chip is already more complicated. What I like about the PSG is the relative simplicity of it.

SeregaZ wrote:
and about your delays - we talk about it here:

I thought about it some more. It could also be caused by different ringbuffer implementations on different OS.
Usually there are multiple sample buffers which are queued. Depending on when this is done, timing can be a bit off if you don't make changes at sample accurate timing.

_________________
macOS 10.14 Mojave, PB 5.62 x64


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, 7, 8  Next

All times are UTC + 1 hour


Who is online

Users browsing this forum: No registered users and 3 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