Noob's investigation of VGM and DMF and SMD audio drivers
Re: Noob's investigation of VGM
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.
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.
Windows (x64)
Raspberry Pi OS (Arm64)
Raspberry Pi OS (Arm64)
-
- Enthusiast
- Posts: 619
- Joined: Fri Feb 20, 2009 9:24 am
- Location: Almaty (Kazakhstan. not Borat, but Triple G)
- Contact:
Re: Noob's investigation of VGM
this value need to get from extra, when it have?
Re: Noob's investigation of VGM
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.SeregaZ wrote:this value need to get from extra, when it have?
Windows (x64)
Raspberry Pi OS (Arm64)
Raspberry Pi OS (Arm64)
-
- Enthusiast
- Posts: 619
- Joined: Fri Feb 20, 2009 9:24 am
- Location: Almaty (Kazakhstan. not Borat, but Triple G)
- Contact:
Re: Noob's investigation of VGM
it have one small problem you say command set a multiple, but:
since 224 string it starts.
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...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).
since 224 string it starts.
Code: Select all
;{
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.
Re: Noob's investigation of VGM
As far as I understand for the SN76489 the ratio is always 2:1 .SeregaZ wrote:it have one small problem you say command set a multiple, but: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...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).
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.
Windows (x64)
Raspberry Pi OS (Arm64)
Raspberry Pi OS (Arm64)
-
- Enthusiast
- Posts: 619
- Joined: Fri Feb 20, 2009 9:24 am
- Location: Almaty (Kazakhstan. not Borat, but Triple G)
- Contact:
Re: Noob's investigation of VGM
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?
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?
Re: Noob's investigation of VGM
I think for now it might be best to ignore both that volume modifier and the difference between absolute and relative volume setting.SeregaZ wrote:and i am start hate VGM maybe we are will kick out extra volume header and will use only this volume modifier?
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.
Windows (x64)
Raspberry Pi OS (Arm64)
Raspberry Pi OS (Arm64)
-
- Enthusiast
- Posts: 619
- Joined: Fri Feb 20, 2009 9:24 am
- Location: Almaty (Kazakhstan. not Borat, but Triple G)
- Contact:
Re: Noob's investigation of VGM
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.)
i agree frequency for chip will be enough. other params can go to hellI think for now it might be best to ignore both that volume modifier and the difference between absolute and relative volume setting.
Re: Noob's investigation of VGM
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.
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.
Windows (x64)
Raspberry Pi OS (Arm64)
Raspberry Pi OS (Arm64)
Re: Noob's investigation of VGM
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
http://www.purebasic.fr/english/viewtop ... 43#p494943
The Delay() method wan't accurate enough for that on MacOS.
Here's v 1.43 of the include file
http://www.purebasic.fr/english/viewtop ... 43#p494943
Code: Select all
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()
Windows (x64)
Raspberry Pi OS (Arm64)
Raspberry Pi OS (Arm64)
-
- Enthusiast
- Posts: 619
- Joined: Fri Feb 20, 2009 9:24 am
- Location: Almaty (Kazakhstan. not Borat, but Triple G)
- Contact:
Re: Noob's investigation of VGM
i see you are not read vgm to array, but directly play?
Re: Noob's investigation of VGM
Yes, that's true.SeregaZ wrote:i see you are not read vgm to array, but directly play?
Is there any reason you prefer to use an array ?
Windows (x64)
Raspberry Pi OS (Arm64)
Raspberry Pi OS (Arm64)
-
- Enthusiast
- Posts: 619
- Joined: Fri Feb 20, 2009 9:24 am
- Location: Almaty (Kazakhstan. not Borat, but Triple G)
- Contact:
Re: Noob's investigation of VGM
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
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
-
- Enthusiast
- Posts: 619
- Joined: Fri Feb 20, 2009 9:24 am
- Location: Almaty (Kazakhstan. not Borat, but Triple G)
- Contact:
Re: Noob's investigation of VGM
and about your delays - we talk about it here:
http://www.purebasic.fr/english/viewtop ... 13&t=62919
maybe some one can help with pauses for your system?
http://www.purebasic.fr/english/viewtop ... 13&t=62919
maybe some one can help with pauses for your system?
Re: Noob's investigation of VGM
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: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.
I thought about it some more. It could also be caused by different ringbuffer implementations on different OS.SeregaZ wrote:and about your delays - we talk about it here:
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.
Windows (x64)
Raspberry Pi OS (Arm64)
Raspberry Pi OS (Arm64)