Skip to content

VGM structure by simple words (noob's investigation)

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

Moderator: Staff

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

Post by SeregaZ »

yes, it just give one smd proccesor, even no second - noise sound cpu. but for my case no need full vgm features, just sega's 2612. i want to make converter from any vgm sega music to gems. now it just gems game to gems game compatibility... (you can see it in dune, where play battletech song, Jim, Comixzone and other gems song, but awesome music for example Rock n Roll Racing, or Alien 3 now cant used for it. so i want to try make it.)


so player part still not finish. i am not understand how to set ValleyBell OPN.dll play samples... not by PlayDACSample procedure, but cpu registers. what sequence of commands must be?
1. DAC Enable:
OPN_Write(0, $2B, $80)
2. cycle:
For i = addresofsample to addresofsample + samplesize
OPN_Write(0, $2A, ReadByte(i))
DelayMicroSeconds(45)
Next

but it get nothing. silence. if add before it some like this: OPN_Write(0, $28, $F0) - it get some scratch sound, but not sample :) data is correct, i am sure - becouse if i set this data to PlayDACSample it play fine. and my plan was use this PlayDACSample, but ValleyBell says it get some "wait" effect, so song will be play a little wrong. that is why i am already a few days kick my head on monitor for get info how to make this cpu play samples... i read a few times documentation http://www.smspower.org/maxim/Documents/YM2612#reg2b , but didnt understand what he want from me :((((...
  • SeregaZ Offline
  • Posts: 98
  • Joined: 2015-08-08, 13:56:52

Post by SeregaZ »

again i am a little stuck... prelude:
Image

this theory correct? so my question is why my case is works a conversely?
PlayDACSample(0, 10k, Adress, 6000) play fine
PlayDACSample(0, 10k, Adress, 10000) play is eat by middle

how it can be? by hear in winamp that sample definitly is 10k. 6k is too low sound. and 6k must be longer, but it plays fine. shorter 10k is eat.


it was manual setup of frequence as i hear. but it need to programm count frequence. so i make some collection of pauses and x8n command. then i need to count frequence. as far i remember:
filesize / frequence = time of playing song in secunds.
so frequence is:
filesize / time = frequence

i have only summ of pauses. but one pause it is... 1s/44100hz (44100 it is frequence of vgm files. it means in 1 sec vgm cycle make 44100 actions).
1s/44100 = 0.000 022 675736961
millisec microsec even i dont know what it is...

so summofpauses need to * 0.000022675736961

then we come to:
filesize / (summofpauses * 0.000022675736961) = frequence of sample
(i think i am as always some do is wrong... i am dumbass, remember? i hope some god father will say how to count it sure)

so my case value was 12 300 - sounds of sample is right, as winamp play, but it eat my samples!!!!1111oneone :oops: only 6 000 is nice, not eat, but is sounds much lower, than winamp do... so i am still "want to belive" and "truth is out there"...
  • SeregaZ Offline
  • Posts: 98
  • Joined: 2015-08-08, 13:56:52

Post by SeregaZ »

and this stuped piano hiding in bushes was found:
Image

in my case samples starts only after xE0 command. and plays, until x8n comes. but this place have DAC disabled and no new xE0 for start new sample. so my sample shutdown. but it is interesting - with 6k frequency it have no stop :) play is continue. but 10k is interrupt. maybe to much data is lost with 10k and PlayDACSample decides to turn off this concert...

it means need to monitoring this x8n more, and restart sounds sample after DAC disables... this 2A unsupport is killing me... so many problem...
  • SeregaZ Offline
  • Posts: 98
  • Joined: 2015-08-08, 13:56:52

Post by SeregaZ »

as requared i make short code for illustration of it:

Code: Select all

Enumeration
  #Window
  
  #Play
  #Play6000
EndEnumeration

DataSection

  StartVGMFile: 
    IncludeBinary "E:\SHORT.vgm"                  
  EndOfVGMFile: 
  
EndDataSection

OPNhdll = OpenLibrary(0, "OPN_DLL.dll")

If OPNhdll
  Prototype OpenDriver(int.i)
  Prototype CloseDriver()
  Prototype OPNWrite(int.i,int2.i,int3.i)  
  Prototype OPNPlayDACSample(chip.i, size.i, *Data, freq.i) 

  Global OpenOPNDriver.OpenDriver = GetFunction(0, "OpenOPNDriver")
  Global CloseOPNDriver.CloseDriver = GetFunction(0, "CloseOPNDriver")
  Global OPN_Write.OPNWrite = GetFunction(0, "OPN_Write") 
  Global PlayDACSample.OPNPlayDACSample = GetFunction(0, "PlayDACSample")
Else
  ;error
  End
EndIf

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

Global autofreq = 1

; shutdown sound procedure when track is done, or stop by button
Procedure EndOfPlay(sh.i)

  OPN_Write(0, $2B, $00) ; Disable Dac
  
  ; probably RR param need to high level for shutdown
  OPN_Write(0, $80 + sh, $0F)
  OPN_Write(0, $81 + sh, $0F)
  OPN_Write(0, $82 + sh, $0F)
  OPN_Write(0, $84 + sh, $0F)
  OPN_Write(0, $85 + sh, $0F)
  OPN_Write(0, $86 + sh, $0F)
  OPN_Write(0, $88 + sh, $0F)
  OPN_Write(0, $89 + sh, $0F)
  OPN_Write(0, $8A + sh, $0F)
  OPN_Write(0, $8C + sh, $0F)
  OPN_Write(0, $8D + sh, $0F)
  OPN_Write(0, $8E + sh, $0F)
 
  If sh = 0
    OPN_Write(0, $28, $00) ; turn off
    OPN_Write(0, $28, $01)
    OPN_Write(0, $28, $02)
  Else
    OPN_Write(0, $28, $04)
    OPN_Write(0, $28, $05)
    OPN_Write(0, $28, $06) 
  EndIf
  
EndProcedure

; procedure work in another thread
Procedure ReadAndPlay(*Value) 
  
  ;get vgm adress without head
  ot = ?StartVGMFile + 64
  do = ?EndOfVGMFile
  
  ; reset main array
  Dim VGMARR(0) 
  
  Number.a = 0
  WavAddres = 0
  Arrayind = 0
  samplecoordinates = -1
  
  ; cycle of reading
  For i = ot To do
    
    ; Reads an ascii character (1 byte) from the specified memory address. 
    Number = PeekA(i)
    
    Select Number
        
      Case $67 ; wav data block adress
        
        ; Reads an integer (4 bytes in 32-bit executable, 8 bytes in 64-bit executable) number from the specified memory address. 
        WavDataSize = PeekI(i + 3)
        
        ; get this adress in mem
        WavAddres = i + 7
        
        ; jump reads to end of wav data block
        i + 6 + WavDataSize
        
      Case $52
        VGMARR(Arrayind)\type = 1
        VGMARR(Arrayind)\reg = PeekA(i+1) ; get register
        VGMARR(Arrayind)\val = PeekA(i+2) ; get value of register
        
        ; temporaly make ignore $2A. maybe somewhen i fix it to right
        If VGMARR(Arrayind)\reg <> $2A 
          Arrayind + 1
          ReDim VGMARR(Arrayind) ; +1 to array size
        EndIf
        i+2 ; make jump to next pointer
        
      Case $53
        VGMARR(Arrayind)\type = 2
        VGMARR(Arrayind)\reg = PeekA(i+1)
        VGMARR(Arrayind)\val = PeekA(i+2)
        Arrayind + 1
        ReDim VGMARR(Arrayind)
        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)
        
        ; it is not very correct... but if i will have working $2A - it will no need this.
        If flagpausehunt
          VGMARR(numforsizecount)\summofpauses + VGMARR(Arrayind)\pause
          flagpausehunt = 0
        EndIf
        
        Arrayind + 1
        ReDim VGMARR(Arrayind)
        i+2
        
      Case $70 To $7F ; wait n+1 samples, n can range from 0 to 15.
        VGMARR(Arrayind)\type = 3
        VGMARR(Arrayind)\pause = ((Number - $70) + 1)
        If flagpausehunt
          VGMARR(numforsizecount)\summofpauses + VGMARR(Arrayind)\pause
          flagpausehunt = 0
        EndIf
        Arrayind + 1
        ReDim VGMARR(Arrayind)
        
      Case $E0 ; 0xE0	dddddddd	seek to offset dddddddd (Intel byte order) in PCM data bank
        ; prepear adress for next 0x8n
        samplecoordinates = PeekI(i + 1)
        samplecoordinates = WavAddres + samplecoordinates        
        
        ; jump to next command
        i + 4
        
      Case $80 To $8F
        If samplecoordinates > -1
          VGMARR(Arrayind)\type = 4
          VGMARR(Arrayind)\sampleadress = samplecoordinates
          VGMARR(Arrayind)\summofpauses = 0
          numforsizecount = Arrayind ; remember array position for count size and freq
          
          Arrayind + 1
          ReDim VGMARR(Arrayind)
          samplecoordinates = -1 ; no need more, just for once work per sample
        EndIf
        
        ; start collect size per each 0x8n
        VGMARR(numforsizecount)\samplesize = VGMARR(numforsizecount)\samplesize + 1
        
        flagpausehunt = 1 ; flag for start collect pauses too
        
        ; get n - as pause, and remember it, if exists
        If Number > $80
          VGMARR(Arrayind)\type = 3
          VGMARR(Arrayind)\pause = (Number - $80)
          VGMARR(numforsizecount)\summofpauses + VGMARR(Arrayind)\pause
          Arrayind + 1
          ReDim VGMARR(Arrayind)
        EndIf
        
      Case $4F ; 0x4F	dd	Game Gear PSG stereo, write dd to port 0x06 ; dune with samples
        ; ignore until ValleyBell add support SN76489/SN76496 to OPN.dll :)))))))
        i + 1
        
      Case $50 ; 0x50	dd	PSG (SN76489/SN76496) write value dd ; dune with samples
        ; again ignore and jump far
        i + 1
        
      Case $62 ; wait 735 samples (60th of a second), a shortcut for 0x61 0xdf 0x02 ; Lego Tune
        VGMARR(Arrayind)\type = 3
        VGMARR(Arrayind)\pause = 735
        If flagpausehunt
          VGMARR(numforsizecount)\summofpauses + VGMARR(Arrayind)\pause
          flagpausehunt = 0
        EndIf
        Arrayind + 1
        ReDim VGMARR(Arrayind)
        
      Case $63 ; wait 882 samples (50th of a second), a shortcut For 0x61 0x72 0x03
        VGMARR(Arrayind)\type = 3
        VGMARR(Arrayind)\pause = 882
        If flagpausehunt
          VGMARR(numforsizecount)\summofpauses + VGMARR(Arrayind)\pause
          flagpausehunt = 0
        EndIf
        Arrayind + 1
        ReDim VGMARR(Arrayind)
        
      Case $66 ; end of sound
        Break  
        
    EndSelect
    
  Next
  
  
  
  ; it is for organization of pauses less, than millisec
  PlayedTicks.i = 0
  PlayedUS.i = 0
  CurrentUS.i = 0
  StartMS.i = ElapsedMilliseconds()
  
  ; cycle of playing
  For i = 0 To ArraySize(VGMARR())-1
    Select VGMARR(i)\type
        
      Case 1
        OPN_Write(0, VGMARR(i)\reg, VGMARR(i)\val)
        
      Case 2
        ; probably cpu have some command with $53, but no need +256... now i set for all +256
        tmp = VGMARR(i)\reg+256
        OPN_Write(0, tmp, VGMARR(i)\val)
        
      Case 3        
        ; that cursed pause...        
        PlayedTicks + VGMARR(i)\pause
        PlayedUS = PlayedTicks * 22.675736961 ;(this 22 - As 1s/44100hz. and 22 it means microsec)
        While (CurrentUS < PlayedUS)
          Delay(1)
          CurrentUS.i = (ElapsedMilliseconds() - StartMS) * 1000
        Wend
        
      Case 4
        If autofreq = 1
          ; count frequency
          freq = VGMARR(i)\samplesize / (VGMARR(i)\summofpauses * 0.000022675736961)
          PlayDACSample(0, VGMARR(i)\samplesize, VGMARR(i)\sampleadress, freq)
        Else
          PlayDACSample(0, VGMARR(i)\samplesize, VGMARR(i)\sampleadress, 6000)
        EndIf
        
    EndSelect
  Next
  
  ; shutdown of sound
  EndOfPlay(0)
  EndOfPlay(256)
  
  ; enable plays button
  DisableGadget(#Play, 0)
  DisableGadget(#Play6000, 0)
  
EndProcedure

OpenOPNDriver(1)

If OpenWindow(#Window, 100, 100, 300, 100, "play", #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_ScreenCentered)
  
  ButtonGadget(#Play, 10, 10, 200, 30, "play auto freq")
  
  ButtonGadget(#Play6000, 10, 50, 200, 30, "play 6k freq")
  
  OPN_Write(0, $2B, $80)
  
  Repeat
    
    Event = WaitWindowEvent()
    
    Select Event

       Case #PB_Event_Gadget
         
         Select EventGadget()
           Case #Play
             DisableGadget(#Play, 1) ; disable for avoid double press button
             DisableGadget(#Play6000, 1)
             autofreq = 1
             CreateThread(@ReadAndPlay(), 1)
             
           Case #Play6000
             DisableGadget(#Play, 1)
             DisableGadget(#Play6000, 1)
             autofreq = 0
             CreateThread(@ReadAndPlay(), 1)

             
         EndSelect
         
       Case #PB_Event_CloseWindow
         stopflag = 1
         Quit = 1
     EndSelect
   Until Quit = 1
   
   CloseOPNDriver()
   CloseLibrary(0)
   
EndIf
and attach exe file. lesson third snare! by VGM it is kick + snare doubled sample as one. but this second part PlayDac is eat. and eat when 10k or 12k. when 6k - no eat and PlayDac work fine. so it means PlayDac have some cache... with 10k PlayDac work very fast and this cache is ends. with 6k this cache not depleted and play is continue after DAC Disable and Enable.

ops... attach not want to sends.
https://www.dropbox.com/s/wbfg6bcsf94gzcc/demo.zip?dl=1
  • SeregaZ Offline
  • Posts: 98
  • Joined: 2015-08-08, 13:56:52

Post by SeregaZ »

solution still "is out there" :( who is C-programmer? any idea what need fix in this code for avoid that sample eating on middle? as ValleyBell say DAC Disable command must just make silence for PlayDACSample, not stop. but practice shows it is stops.

Code: Select all

#define MulDivRoundU(Mul1, Mul2, Div)	(UINT32)( ((UINT64)Mul1 * Mul2 + Div / 2) / Div)
void OPNAPI PlayDACSample(UINT8 ChipID, UINT32 DataSize, const UINT8* Data, UINT32 SmplFreq)
{
	DAC_STATE* TempDAC;
	
	if (ChipID >= OPN_CHIPS)
		return;
	
	EnterCriticalSection(&write_sect);
	
	TempDAC = &DACState[ChipID];
	TempDAC->DataSize = DataSize;
	TempDAC->Data = Data;
	if (SmplFreq)
		TempDAC->Frequency = SmplFreq;
	TempDAC->Delta = MulDivRoundU(0x10000, TempDAC->Frequency, SampleRate);
	TempDAC->SmplPos = 0x00;
	
	// Resume Stream
	NullSamples = 0;
	PauseStream(false);
	LeaveCriticalSection(&write_sect);
	
	return;
}
what for this MulDivRoundU(0x10000, TempDAC->Frequency, SampleRate) - 0x10000 - it is can be cach? what will happen if this param set to bigger value? and i thought this function must have For cycle some like this:
For i = 0 to samplesize
OPN_Write(0, $2A, ReadByte(adressofsample+i))
Pause(22microsec)
Next

flowchart
Image
  • User avatar
  • ValleyBell Offline
  • Posts: 4768
  • Joined: 2011-12-01, 20:20:07
  • Location: Germany

Post by ValleyBell »

For reference, I posted DACTest_OnOnly.vgz in IRC to allow you to bug-test your player.
If I inject the VGM into the player, everything plays at the wrong frequency due to a bug in the sample detection. (And this is the main problem, no matter what you think. Fix this and everything else will magically work.)

Bonus: DACTest_OnOff.vgz - same file, but the DAC is disabled after the last data byte (every the sound) is sent and enabled when it starts playing a new sound.

You should notice that the amount of time the DAC is not muted in the last VGM matches the time it takes to play a sound in Winamp.
The PlayDACSample function just streams the DAC data to the YM2612 chip at whatever frequency you tell it. It doesn't care about DAC enable/disable and will continue to stream even if the DAC is disabled/muted. (because that's what actual sound drivers would do)

Bonus 2: the SMPS file (Sonic 1 format) that I made and recorded above
  • SeregaZ Offline
  • Posts: 98
  • Joined: 2015-08-08, 13:56:52

Post by SeregaZ »

resume:
SeregaZ_DACTest.vgm - wrong start time of samples, wrong frequency - plays slow, but play full length of sample - no eat and it is main thing.
SeregaZ_DACTest_OnOff.vgm - eat samples. a loooot of eat :) and probably wrong start time of this samples who was eat, but main thing - it is eat samples.

so my first problem is this eating. not wrong start play time, not frequency - becouse they wrong only for your test song and i can fix it... probably... for Alien it is even no need fix - it is ok and frequency and start play time. only eating is main problem.

Image

as you can se Disable DAC is shutdown of sound, what was start by PlayDAC procedure. and it is not wrong staring time fault - because Alien have correct starting time of sample. it is PlayDAC procedure. and evedence of it - you can see stright line of silence. even it is a fault of wrong time - some of piece anyway must be seen on audio log. some part of sample anyway must be hear in place, where they have intersection - but none. it means it is not starting time problem. it is PlayDac procedure.
Image
  • SeregaZ Offline
  • Posts: 98
  • Joined: 2015-08-08, 13:56:52

Post by SeregaZ »

my investigation desides to move sample-question into long box.

now i think time to start make table of register values for notes A0 and A4 for all notes. then, when i will get this value from vgm file - just check my table for get note value and then count of pitch. i think it will be problem to see difference where need to use pitch effect without stop note command, and where is stop note command and start new one. will see...

for vgm convert case (as you know gems have 1/24 note as minimum, vgm have almost no limit for size) i think will need two thread for process. one is read&play, second is a timer, that was make some tik in repeat and read register, what set vgm file. every tik = 1 delay for gems.

at the begin i think i will work only with nosamples vgm songs. it is more easy now :) then i will return to that my cursed of samples playing and new part - samples converting.

that is some demo: http://www.emu-land.net/forum/index.php ... ach=159838 rom for sega mega drive emulator. just press "A" for play :) pitch was made only ones, and wrong, it have 3 track, not 5 as original, no have loops, and it is eat last one note :) but anyway it is coooooool :) it is not vgm-2-gems, but by use a tonns of difference programm, but now time is come for making direct convert vgm-2-gems :) (oooops! vgm-2-code-2-gems :))

by my plan it will be a path for making convert any vgm-smd songs from database to Dune (or another gems-using games)
  • SeregaZ Offline
  • Posts: 98
  • Joined: 2015-08-08, 13:56:52

Post by SeregaZ »

i am start rewrite whole project again :) and at begin i make some registers monitor. it is eat cpu, becouse it need to refresh 6 listicongadget. maybe later i find some solution for that.

and it have grafic monitor too. but same problem with cpu eating make this monitor not fine :)

i take another track. RRR song it too hard for my test. this slide... i hate this slide :) so i get some song without slide. but it have samples. probably i will need to get another song again. i need song without slide and without samples.

slide is a stuped system with pitch parameter - i am still not done that well.
samples - i have no idea yet how to get length in "delay" for gems.

then next small problem - when you choice channel - anothers channels still hear... a lower, but still hear. i will think about it too.

and i didnt plug function, that can count and export instruments from track. becouse, as you can see in sample-track, it have 6 channel for samples. A6+256 and A2+256 registers is 0, but LRAmFm is change per track a few times. so my system can think it is a few new instruments.

and one more - this track have some scratch before start. it is mistake of creators of vgm file of this song?
Attachments
demo.zip
pres load, and wait
(165.63 KiB) Downloaded 265 times
  • SeregaZ Offline
  • Posts: 98
  • Joined: 2015-08-08, 13:56:52

Post by SeregaZ »

full day i am fight with backconvert function :) idea was get $A4 and $A0 registers value and convert it into note and pitch values.

so by this simple convert you can see result :) as i check by GYM logs - GEMS set register same value as VGM do.

at the middle of song come asynchronously and it was only 2 channels from 6 original... but i am still work on it :)

(old attachment was delete, new one is a bottom:))
Last edited by SeregaZ on 2016-09-07, 22:03:49, edited 1 time in total.
  • SeregaZ Offline
  • Posts: 98
  • Joined: 2015-08-08, 13:56:52

Post by SeregaZ »

so my investigation comes to much far :) i am already have:
correct instruments
correct volume
almost correct notes (i mean 100% same as VGM have. i just need to fill special table of values $A0 for 100%)
almost synchronously (this system probably i will need to remake - becouse now it no slide support)


so slides left and samples. and SN76489 (i have no any idea about this SN76489:)).

with samples i need in some re-sampling procedures for bass.dll. second day no hepl at bass.dll forum :((( idea was make correct re-sample for samples, becouse my system count as 15-20k frequency for samples. i need 10.4k as max. i want to save as 15-20k sounds, but use 10.4 for it. no slow\low sound.
Attachments
Player.zip
synchronously + volume (both was made by 1 click:))
(10.58 KiB) Downloaded 264 times
  • SeregaZ Offline
  • Posts: 98
  • Joined: 2015-08-08, 13:56:52

Post by SeregaZ »

first wav support. but i will make second one - more smart. i mean will try :) it have too difficult algorithm. i mean user can hear 3 drums, but real it is one drum, with 3 different frequency. so i need to somehow explain for my programm what she must do :) it need some kind of borders for frequency.

https://www.youtube.com/watch?v=v81qVqbz8nY
  • SeregaZ Offline
  • Posts: 98
  • Joined: 2015-08-08, 13:56:52

Post by SeregaZ »

it need to little clear about extra header.
Image
  • SeregaZ Offline
  • Posts: 98
  • Joined: 2015-08-08, 13:56:52

Post by SeregaZ »

after lot of brain explosivs of ValleyBell was born this art of painting:
Image
  • SeregaZ Offline
  • Posts: 98
  • Joined: 2015-08-08, 13:56:52

Post by SeregaZ »

i have this ValleyBell's function:

Code: Select all

Private Function GetOPNNote(ByVal Note As Byte, ByVal Pitch As Integer) As Integer

    Dim FreqHz As Double
    Dim BlkNum As Integer
    Dim FNum As Double
    Dim CurNote As Double
    
    CurNote = Note + Pitch / 128#
    
    FreqHz = 440# * 2# ^ ((CurNote - 69) / 12#)
    
    ' must be Note, not CurNote, to avoid changing octaves
    BlkNum = (Note \ 12) - 1
    If BlkNum < &H0 Then
        BlkNum = &H0
    ElseIf BlkNum > &H7 Then
        BlkNum = &H7
    End If
    'FNum = (144 * FreqHz * 2 ^ 20 / 7670454) / 2 ^ (BlkNum - 1)
    FNum = (144 * FreqHz / 7670454) * 2 ^ (21 - BlkNum)
    FNum = Int(FNum + 0.5)
    If FNum < 0 Then
        FNum = 0
    ElseIf FNum > &H7FF Then
        FNum = &H7FF
    End If
    
    GetOPNNote = FNum Or BlkNum * &H800

End Function
we convert it into my language for convert code file note values into registers. until now it work fine. but now i have problem with a pitches. it count uncorrect and have limit.
GetOPNNote($13+12, 1667) it shows $FFF. any higher pitch will return same $FFF. can this part of function be wrong?

Code: Select all

    ElseIf FNum > &H7FF Then
        FNum = &H7FF
    End If
i remove it, and add check at the end of function:

Code: Select all

  ret = FNum | (BlkNum * $800)
  If ret > $34BF
    ret = $34BF
  EndIf
  
  ProcedureReturn ret
$34BF - it is 95 note - hieghest note of GEMS... ops... 95 + 12 probably... will need to test and change this limit :) but idea was correct?

if yes - limit is fixed. now uncorrect pitch counting:
GetOPNNote($13+12, 272) shows as $0C43, but GEMS with this 272 pitch shows as $0C01.
then i change this part code 128 to 256

Code: Select all

CurNote = Note + Pitch / 256
GetOPNNote($13+12, 272) start shows $0C02 :) almost! :) this idea is correct?
Post Reply