# PureBasic Forum

 It is currently Tue Nov 24, 2020 6:47 pm

 All times are UTC + 1 hour

 Page 4 of 8 [ 117 posts ] Go to page Previous  1, 2, 3, 4, 5, 6, 7, 8  Next
 Print view Previous topic | Next topic
Author Message
 Post subject: Re: Noob's investigation of VGMPosted: Tue Sep 27, 2016 1:51 pm
 PureBasic Expert

Joined: Sun Aug 08, 2004 5:21 am
Posts: 3700
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.15 Catalina, Windows 10

Top

 Post subject: Re: Noob's investigation of VGMPosted: Tue Sep 27, 2016 4:05 pm
 Enthusiast

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

Top

 Post subject: Re: Noob's investigation of VGMPosted: Tue Sep 27, 2016 4:53 pm
 PureBasic Expert

Joined: Sun Aug 08, 2004 5:21 am
Posts: 3700
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.15 Catalina, Windows 10

Top

 Post subject: Re: Noob's investigation of VGMPosted: Tue Sep 27, 2016 9:14 pm
 Enthusiast

Joined: Fri Feb 20, 2009 9:24 am
Posts: 582
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)
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

#ProgressPlay

#Volume1
#Volume2

EndEnumeration

XIncludeFile "SN76489 module.pb"

Structure VGMFSt
type.i
reg.a
val.u
pause.u
samplenum.a
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()

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

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)

;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

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
For i = 1 To chipcount
If PeekB(readfrom) = 0 ; 0 - it is id of SN
Debug extrafrequency ; f..king!!!!!!!1111oneoneone
If extrafrequency
Clock(\$40000000 + extrafrequency, 1)
EndIf
Break
EndIf
Next
EndIf

If shift > 4 ; it means need to check volume
volshift = PeekL(*FileMem+\$C8)
If volshift
volchipcount = PeekB(*FileMem+\$C8+volshift)
If volchipcount
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)
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).
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.
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

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

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

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

Debug "запустилось"
LengthToWrite = size;FileSize("1test.dds")
*MemoryID = AllocateMemory(LengthToWrite)
strm.Z_STREAM
strm\next_in = *TmpMem
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

ButtonGadget(#OpenButton, 130, 20, 50, 20, "open")

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

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)

TrackBarGadget(#Volume2, 213, 166, 20, 85, 0, 10, #PB_TrackBar_Vertical)

Repeat

Event = WaitWindowEvent()

Select Event

;- 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
If File\$
Mute()
For i = #Ch1R To #Ch4L2
Next
GGStereoWrite(255)
GGStereoWrite(255, 1)
;GGStereoWrite(0)
;GGStereoWrite(34, 1)

TormozFlag = 1
EndIf
EndIf

If *MemoryID
FreeMemory(*MemoryID)
*MemoryID = 0
EndIf

length = Lof(#File)

;unpacked VGM
Debug "unpacked"

*MemoryID = AllocateMemory(length)         ; allocate the needed memory
If *MemoryID
FileSeek(#File, 0)
EndIf
Else
Debug "packed"
;get size unpacked
FileSeek(#File, length - 4)

If unpsz

If *TmpMem
FreeMemory(*TmpMem)
*TmpMem = 0
EndIf

*TmpMem = AllocateMemory(length)         ; allocate the needed memory
If *TmpMem
FileSeek(#File, 0)

*MemoryID = InflatePayload(*TmpMem, #ENABLE_GZIP, unpsz) ;unpack

EndIf

EndIf

EndIf

CloseFile(#File)

TormozFlag = 0

If *MemoryID
Else
EndIf

EndIf

EndIf

Case #Ch1R To #Ch4L
forstereo = 0
For i = #Ch1R To #Ch4L
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
If value
SetBit(forstereo, NumToBit(i-#Ch1R2))
Else
ClearBit(forstereo, NumToBit(i-#Ch1R2))
EndIf
Next
GGStereoWrite(forstereo, 1)

Case #Play
If *MemoryID
TormozFlag = 1
EndIf

TormozFlag = 0

EndIf

Case #Stop
TormozFlag = 1

Case #Volume1
VolumeL.u
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
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)
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

 Post subject: Re: Noob's investigation of VGMPosted: Wed Sep 28, 2016 4:59 am
 PureBasic Expert

Joined: Sun Aug 08, 2004 5:21 am
Posts: 3700
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.15 Catalina, Windows 10

Top

 Post subject: Re: Noob's investigation of VGMPosted: Wed Sep 28, 2016 6:42 am
 Enthusiast

Joined: Fri Feb 20, 2009 9:24 am
Posts: 582
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
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

 Post subject: Re: Noob's investigation of VGMPosted: Wed Sep 28, 2016 9:33 am
 PureBasic Expert

Joined: Sun Aug 08, 2004 5:21 am
Posts: 3700
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.15 Catalina, Windows 10

Top

 Post subject: Re: Noob's investigation of VGMPosted: Wed Sep 28, 2016 1:26 pm
 Enthusiast

Joined: Fri Feb 20, 2009 9:24 am
Posts: 582
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

Top

 Post subject: Re: Noob's investigation of VGMPosted: Thu Sep 29, 2016 9:45 am
 PureBasic Expert

Joined: Sun Aug 08, 2004 5:21 am
Posts: 3700
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.15 Catalina, Windows 10

Top

 Post subject: Re: Noob's investigation of VGMPosted: Sat Oct 01, 2016 6:12 pm
 PureBasic Expert

Joined: Sun Aug 08, 2004 5:21 am
Posts: 3700
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
reserved.ztype
EndStructure

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

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

Protected.i file, size, *m, *vgm
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

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
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
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)
EnableVGM = #True

MessageRequester("VGM", "Playing")

PSG_Terminate()

_________________
macOS 10.15 Catalina, Windows 10

Top

 Post subject: Re: Noob's investigation of VGMPosted: Sat Oct 01, 2016 9:46 pm
 Enthusiast

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

Top

 Post subject: Re: Noob's investigation of VGMPosted: Sun Oct 02, 2016 5:34 am
 PureBasic Expert

Joined: Sun Aug 08, 2004 5:21 am
Posts: 3700
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.15 Catalina, Windows 10

Top

 Post subject: Re: Noob's investigation of VGMPosted: Sun Oct 02, 2016 8:00 am
 Enthusiast

Joined: Fri Feb 20, 2009 9:24 am
Posts: 582
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

 Post subject: Re: Noob's investigation of VGMPosted: Sun Oct 02, 2016 8:06 am
 Enthusiast

Joined: Fri Feb 20, 2009 9:24 am
Posts: 582
Location: Almaty (Kazakhstan)
viewtopic.php?f=13&t=62919

maybe some one can help with pauses for your system?

Top

 Post subject: Re: Noob's investigation of VGMPosted: Sun Oct 02, 2016 10:40 am
 PureBasic Expert

Joined: Sun Aug 08, 2004 5:21 am
Posts: 3700
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:

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.15 Catalina, Windows 10

Top

 Display posts from previous: All posts1 day7 days2 weeks1 month3 months6 months1 year Sort by AuthorPost timeSubject AscendingDescending
 Page 4 of 8 [ 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 16 guests

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

Search for:
 Jump to:  Select a forum ------------------ PureBasic    Coding Questions    Game Programming    3D Programming    Assembly Programming    The PureBasic Editor    The PureBasic Form Designer    General Discussion    Feature Requests and Wishlists    Tricks 'n' Tips Bug Reports    Bugs - Windows    Bugs - Linux    Bugs - Mac OSX    Bugs - IDE    Bugs - Documentation OS Specific    AmigaOS    Linux    Windows    Mac OSX Miscellaneous    Announcement    Off Topic Showcase    Applications - Feedback and Discussion    PureFORM & JaPBe    TailBite