Page 2 of 3

Re: Help converting from Visual Basic

Posted: Sat Jan 02, 2016 10:25 pm
by infratec
Hi,

I just checked your code and it seems Ok.

One thing:
The formula for a keyboard is

Code: Select all

freq.f = 440 * Pow(2, (KeyNo - 49) / 12)
Key 1 is 27.5Hz (A0)
Key 88 is 4186.01Hz (C8)

That's valid for an 88 key piano.

Bernd

Re: Help converting from Visual Basic

Posted: Sun Jan 03, 2016 12:56 am
by SeregaZ
for my case i get note value from midi as $xx, then from this procedure get 2 value for 2 registers of cpu and send to this cpu.

and in this days i make rom for emulator sega mega drive and start it and make dump of sound cpu command and see it difference :(((( but now some man says it is shift of octaves. and sure :) $10 + 12 = $1C and this $1C shows correct values $A4 = B and $A0 = 2B

false alarm :) i am as always - dubmass...

Re: Help converting from Visual Basic

Posted: Mon Jan 04, 2016 2:33 pm
by SeregaZ
it is a little offtop of converting, but still about this function:
now i want to make back convert operation of two registers values to another one: note and pitch value. my idea is first make some array of notes and values of notes, fill it, and then compare with input data:

Code: Select all

Global Dim tiks.i(12)
tiks(1)  = 7
tiks(2)  = 7
tiks(3)  = 6
tiks(4)  = 6
tiks(5)  = 6
tiks(6)  = 5
tiks(7)  = 5
tiks(8)  = 5
tiks(9)  = 5
tiks(10) = 4
tiks(11) = 4
tiks(12) = 4

Structure RegistersVal
  
  note.i  ; C
  A4A0.i  ;
  ptik.f  ;
  
EndStructure

Global Dim RegistersDataBase.RegistersVal(0)

Structure notptc
  note.i
  pitch.i
EndStructure

Procedure.i GetOPNNote(Note.i, Pitch.i)
  
  Protected.d FreqHz, CurNote
  Protected.i BlkNum, FNum
  
  CurNote = Note + Pitch / 128
  
  FreqHz = 440 * Pow(2, (CurNote - 69) / 12)
  
  BlkNum = Note / 12 - 1

  If BlkNum < 0
    BlkNum = 0
  ElseIf BlkNum > 7
    BlkNum = 7
  EndIf  
 
  FNum = Round((144 * FreqHz / 7670454) * Pow(2, 21 - BlkNum), #PB_Round_Nearest)
  If FNum < 0
    FNum = 0
  ElseIf FNum > $7FF
    FNum = $7FF
  EndIf 

  ProcedureReturn FNum | (BlkNum * $800)

EndProcedure

Procedure.i RetNotAndPithc(A4A0.i)
  
  For i = ArraySize(RegistersDataBase()) To 1 Step -1
    If RegistersDataBase(i)\A4A0 < A4A0
      ;Debug "$" + RSet(Hex(RegistersDataBase(i)\note), 2, "0")
      ;Debug "$" + RSet(Hex((A4A0 - RegistersDataBase(i)\A4A0) * RegistersDataBase(i)\ptik), 4, "0")
      
      tmp$ = RSet(Bin((A4A0 - RegistersDataBase(i)\A4A0) * RegistersDataBase(i)\ptik), 16, "0")
      tmp$ = Bin(RegistersDataBase(i)\note) + tmp$
      tmp = Val("%" + tmp$)
      
      Break
    EndIf
  Next
  
  ProcedureReturn tmp
  
EndProcedure

;FNum = GetOPNNote($0C, 0)
;Debug "$A4 " + RSet(Hex(FNum >> 8), 2, "0")
;Debug "$A0 " + RSet(Hex(FNum & $FF), 2, "0")


  tik = 1
  For n = $0C To $5F
  
    FNum = GetOPNNote(n, 0) 
    ReDim RegistersDataBase(ArraySize(RegistersDataBase())+1)
    RegistersDataBase(ArraySize(RegistersDataBase()))\note = n
    RegistersDataBase(ArraySize(RegistersDataBase()))\A4A0 = FNum
    RegistersDataBase(ArraySize(RegistersDataBase()))\ptik = tiks(tik)
    tik = tik + 1
    If tik = 13
      tik = 1
    EndIf  
  Next

  NAP = RetNotAndPithc($2B9F)
  
how to return two values from procedure more elegant? without allocate memory - it will be used in thread, i think this allocate will make some little mess in future.
tmp$ = RSet(Bin((A4A0 - RegistersDataBase(i)\A4A0) * RegistersDataBase(i)\ptik), 16, "0")
tmp$ = Bin(RegistersDataBase(i)\note) + tmp$
tmp = Val("%" + tmp$)
this part of code is terrible. i am just not full understans this >>, << and &... and many other operation. this my NAP must be split into 2 value.

Re: Help converting from Visual Basic

Posted: Mon Jan 04, 2016 3:25 pm
by Wolfram
SeregaZ wrote:for my case i get note value from midi as $xx, then from this procedure get 2 value for 2 registers of cpu and send to this cpu.

and in this days i make rom for emulator sega mega drive and start it and make dump of sound cpu command and see it difference :(((( but now some man says it is shift of octaves. and sure :) $10 + 12 = $1C and this $1C shows correct values $A4 = B and $A0 = 2B

false alarm :) i am as always - dubmass...
I'm not sure that I understand your problem right. But if you work with MIDI you should know that some companies use note no. 60 as C3 and some as C4.
But C3 is the right note.

Re: Help converting from Visual Basic

Posted: Mon Jan 04, 2016 3:28 pm
by SeregaZ
with midi to game no problem :) almost ready and work fine (just instruments left). now time of vgm to game part :)

Re: Help converting from Visual Basic

Posted: Mon Jan 04, 2016 8:25 pm
by SeregaZ
i am not sure... but i make some experiments with >>, << and & commands... and i think:

Code: Select all

tmp$ = RSet(Bin((A4A0 - RegistersDataBase(i)\A4A0) * RegistersDataBase(i)\ptik), 16, "0")
tmp$ = Bin(RegistersDataBase(i)\note) + tmp$
tmp = Val("%" + tmp$)
to change:

Code: Select all

tmp = RegistersDataBase(i)\note << 16 + ((A4A0 - RegistersDataBase(i)\A4A0) * RegistersDataBase(i)\ptik)
and then read it some kind of this:

Code: Select all

Debug tmp >> 16
Debug tmp & $FF
__________________________________________________
Code tags added
04.01.2016
RSBasic

__________________________________________________
Thanks
SeregaZ

Re: Help converting from Visual Basic

Posted: Tue Jan 05, 2016 8:23 am
by Lunasole
Not sure it is needed anymore, but I write something about your previous code 8)

Code: Select all

128#  - # marks "double" number in VB6, kind of variable extensions like String$ in PB. Should be translated as 128.0
^     - equivalent of Pow ()
\     - in VB6 it was "integer division", division with always rounding result down, unlike / which returns floating result and rounding it to nearest integer. PB equivalent for \ is /
And   - in VB6 logical AND, both used in comparing and calculating. PB has more stupid mess with it, it uses AND with IF statements and "&" to calculate something

What about <<, I didn't tested your code, but if just need to to assemble variable from several lesser ones, maybe you can use something like

Code: Select all

Structure DWORD 
	LOWORD.u ; represents the first 2 bytes of LONG
	HIWORD.u ; the second part of LONG
EndStructure

Structure ALLTHESTUFF 
	StructureUnion
		LONG.l
		_LONG.DWORD
	EndStructureUnion
EndStructure

Re: Help converting from Visual Basic

Posted: Fri Mar 17, 2023 8:50 pm
by marcos.exe
SeregaZ wrote: Tue Aug 18, 2015 8:52 am FNum >> 8 i read something about it... it some kind of shift, right?
FNum & $FF what this & means? some kind of +, but more as + for string, not mathematiks?
Hey!
Does this code still work in 2023?

Error:
[ERROR] Invalid memory access. (write error at address 0)
Content:
OPNhdll = OpenLibrary(0, "OPN_DLL.dll")
I tried:
OPNhdll = OpenLibrary(#PB_Any, "OPN_DLL.dll")
But "OPNhdll " remains 0.

Note: I already had the "OPN_DLL.dll" library on my pc, which came from the original visual basic project mentioned above.

Did the code work well for you?

Re: Help converting from Visual Basic

Posted: Fri Mar 17, 2023 9:47 pm
by infratec
The dll needs to be by side of the executable.
If you use the 64bit PB you need a 64bit dll.

Re: Help converting from Visual Basic

Posted: Fri Mar 17, 2023 10:11 pm
by infratec
According to the source code of opn_dll
https://forums.sonicretro.org/index.php ... 337/page-4
it should look like this:

Code: Select all

OPNhdll = OpenLibrary(#PB_Any, "OPN_DLL.dll")
If OPNhdll
  Prototype.a Prototype_OpenOPNDriver(Chips.a)
  Prototype Prototype_CloseOPNDriver()
  Prototype Prototype_OPNWrite(ChipID.a, Register.u, Dat.a)
  
  
  Global OpenOPNDriver.Prototype_OpenOPNDriver = GetFunction(OPNhdll, "OpenOPNDriver")
  Global CloseOPNDriver.Prototype_CloseOPNDriver = GetFunction(OPNhdll, "CloseOPNDriver")
  Global OPN_Write.Prototype_OPNWrite = GetFunction(OPNhdll, "OPN_Write")  
EndIf

Re: Help converting from Visual Basic

Posted: Fri Mar 17, 2023 11:00 pm
by infratec
An extended version: (compiles and works with PB Windows x86)

Code: Select all

EnableExplicit

; https://newt.phys.unsw.edu.au/jw/notes.html
Enumeration 60  ; middle C key on a MIDI kbd (C4)
  #Note_Do
  #Note_Do_
  #Note_Re
  #Note_Re_
  #Note_Mi
  #Note_Fa
  #Note_Fa_
  #Note_Sol
  #Note_Sol_
  #Note_La
  #Note_La_
  #Note_Si
EndEnumeration

#YM2612_CLOCK	= 7670454


Procedure.i GetOPNNote(MidiKey.i, Pitch.i)
  
  Protected.d FreqHz, CurNote
  Protected.i BlkNum, FNum
  
  
  CurNote = MidiKey + Pitch / 128
  FreqHz = 440 * Pow(2, (MidiKey - 69) / 12)
  
  BlkNum = MidiKey / 12 - 1
  If BlkNum < 0
    BlkNum = 0
  ElseIf BlkNum > 7
    BlkNum = 7
  EndIf
  
  FNum = Round((144 * FreqHz / #YM2612_CLOCK) * Pow(2, 21 - BlkNum), #PB_Round_Nearest)
  If FNum < 0
    FNum = 0
  ElseIf FNum > $7FF
    FNum = $7FF
  EndIf 
  
  ProcedureReturn FNum | (BlkNum * $800)
  
EndProcedure

; Resampling Modes
#OPT_RSMPL_HIGH	= $00     ; high quality linear resampling [Default]
#OPT_RSMPL_LQ_DOWN = $01  ; low quality downsampling, high quality upsampling
#OPT_RSMPL_LOW = $02      ; low quality resampling

; Chip Sample Rate Modes
#OPT_CSMPL_NATIVE = $00   ; native chip sample rate [Default]
#OPT_CSMPL_HIGHEST = $01  ; highest sample rate (native Or custom)
#OPT_CSMPL_CUSTOM = $02   ; custom sample rate



Define.i OPNhdll, Event, Quit, KeyNo, FNum


OPNhdll = OpenLibrary(#PB_Any, "OPN_DLL.dll")

If OPNhdll
  Prototype Prototype_SetOPNOptions(OutSmplRate.l, ResmplMode.a, ChipSmplMode.a, ChipSmplRate.l)
  Prototype.a Prototype_OpenOPNDriver(Chips.a)
  Prototype Prototype_CloseOPNDriver()
  
  Prototype Prototype_OPN_Write(ChipID.a, Register.u, Dat.a)
  Prototype Prototype_OPN_Mute(ChipID.a, MuteMask.a)
  
  Prototype Prototype_PlayDACSample(ChipID.a, DataSize.l, *Data.Ascii, SmplFreq.l)
  Prototype Prototype_SetDACFrequency(ChipID.a, SmplFreq.l)
  Prototype Prototype_SetDACVolume(ChipID.a, Volume.u) ;	$100 = 100%

  
  Global SetOPNOptions.Prototype_SetOPNOptions = GetFunction(OPNhdll, "SetOPNOptions")
  Global OpenOPNDriver.Prototype_OpenOPNDriver = GetFunction(OPNhdll, "OpenOPNDriver")
  Global CloseOPNDriver.Prototype_CloseOPNDriver = GetFunction(OPNhdll, "CloseOPNDriver")
  
  Global OPN_Write.Prototype_OPN_Write = GetFunction(OPNhdll, "OPN_Write")
  Global OPN_Mute.Prototype_OPN_Mute = GetFunction(OPNhdll, "OPN_Mute")
  
  Global PlayDACSample.Prototype_PlayDACSample = GetFunction(OPNhdll, "PlayDACSample")
  Global SetDACFrequency.Prototype_SetDACFrequency = GetFunction(OPNhdll, "SetDACFrequency")
  Global SetDACVolume.Prototype_SetDACVolume = GetFunction(OPNhdll, "SetDACVolume")
  
  
  ;- Setup
  If OpenOPNDriver(1) = 0
    
    ; https://plutiedev.com/ym2612-registers
    
    OPN_Write(0, $30, $74)  ; multiplier and detune
    OPN_Write(0, $34, $72)  ; multiplier and detune
    OPN_Write(0, $38, $74)  ; multiplier And detune
    OPN_Write(0, $3C, $71)  ; multiplier And detune
    
    OPN_Write(0, $40, $23)  ; total level
    OPN_Write(0, $44, $26)  ; total level
    OPN_Write(0, $48, $2A)  ; total level
    OPN_Write(0, $4C, $00)  ; total level
    
    OPN_Write(0, $50, $1F)  ; attack rate an rate scaling
    OPN_Write(0, $54, $1F)  ; attack rate an rate scaling
    OPN_Write(0, $58, $19)  ; attack rate an rate scaling
    OPN_Write(0, $5C, $12)  ; attack rate an rate scaling
    
    OPN_Write(0, $60, $00)  ; decay rate and am enable
    OPN_Write(0, $64, $00)  ; decay rate and am enable
    OPN_Write(0, $68, $0E)  ; decay rate and am enable
    OPN_Write(0, $6C, $07)  ; decay rate and am enable
    
    OPN_Write(0, $70, $00)  ; sustain rate
    OPN_Write(0, $74, $00)  ; sustain rate
    OPN_Write(0, $78, $00)  ; sustain rate
    OPN_Write(0, $7C, $00)  ; sustain rate
    
    OPN_Write(0, $80, $07)  ; release rate and sustain level
    OPN_Write(0, $84, $08)  ; release rate and sustain level
    OPN_Write(0, $88, $24)  ; release rate and sustain level
    OPN_Write(0, $8C, $18)  ; release rate and sustain level
    
    OPN_Write(0, $90, $00)  ; SSG-EG (better set to 0)
    OPN_Write(0, $94, $00)  ; SSG-EG (better set to 0)
    OPN_Write(0, $98, $00)  ; SSG-EG (better set to 0)
    OPN_Write(0, $9C, $00)  ; SSG-EG (better set to 0)
    
    OPN_Write(0, $B0, $3B)  ; algorithm and feedback
    
    OPN_Write(0, $B4, $04)  ; panning, PMS, AMS
    OPN_Write(0, $B4, $C0)  ; panning, PMS, AMS
    
    
    If OpenWindow(0, 100, 100, 430, 160, "Piano", #PB_Window_MinimizeGadget|#PB_Window_ScreenCentered)
      
      CanvasGadget(#Note_Do, 20, 30, 30, 90)
      CanvasGadget(#Note_Do_, 55, 30, 20, 60)
      CanvasGadget(#Note_Re, 80, 30, 30, 90)
      CanvasGadget(#Note_Re_, 115, 30, 20, 60)
      CanvasGadget(#Note_Mi, 140, 30, 30, 90)
      CanvasGadget(#Note_Fa, 200, 30, 30, 90)
      CanvasGadget(#Note_Fa_, 235, 30, 20, 60)
      CanvasGadget(#Note_Sol, 260, 30, 30, 90)
      CanvasGadget(#Note_Sol_, 295, 30, 20, 60)
      CanvasGadget(#Note_La, 320, 30, 30, 90)
      CanvasGadget(#Note_La_, 355, 30, 20, 60)
      CanvasGadget(#Note_Si, 380, 30, 30, 90)
      
      If StartDrawing(CanvasOutput(#Note_Do_))
        Box(0, 0, 20, 60, 0)
        StopDrawing()
      EndIf
      
      If StartDrawing(CanvasOutput(#Note_Re_))
        Box(0, 0, 20, 60, 0)
        StopDrawing()
      EndIf
      
      If StartDrawing(CanvasOutput(#Note_Fa_))
        Box(0, 0, 20, 60, 0)
        StopDrawing()
      EndIf
      
      If StartDrawing(CanvasOutput(#Note_Sol_))
        Box(0, 0, 20, 60, 0)
        StopDrawing()
      EndIf
      
      If StartDrawing(CanvasOutput(#Note_La_))
        Box(0, 0, 20, 60, 0)
        StopDrawing()
      EndIf
      
      Repeat
        
        Event = WaitWindowEvent()
        Select Event
          Case #PB_Event_Gadget
            Select EventType()
              Case #PB_EventType_LeftButtonDown
                KeyNo = EventGadget()
                Debug KeyNo
                FNum = GetOPNNote(KeyNo, 0)
                OPN_Write(0, $A4, FNum >> 8)  ; frequency first channel high
                OPN_Write(0, $A0, FNum & $FF) ; frequency first channel low
                
                OPN_Write(0, $28, $F0)        ; Note On - Resume Stream
                
              Case #PB_EventType_LeftButtonUp
                OPN_Write(0, $28, $00)
                
            EndSelect
            
          Case #PB_Event_CloseWindow
            Quit = 1
            
        EndSelect
        
      Until Quit = 1
      
      CloseOPNDriver()
      CloseLibrary(OPNhdll)
      
    EndIf
  EndIf
EndIf

Re: Help converting from Visual Basic

Posted: Sat Mar 18, 2023 11:17 am
by Kiffi
infratec wrote: Fri Mar 17, 2023 11:00 pm

Code: Select all

#Note_Do = 66
#Note_Do_ = #Note_Do + 1
#Note_Re = #Note_Do_ + 1
#Note_Re_ = #Note_Re + 1
#Note_Mi = #Note_Re_ + 1
#Note_Fa = #Note_Mi + 1
#Note_Fa_ = #Note_Fa + 1
#Note_Sol = #Note_Fa_ + 1
#Note_Sol_ = #Note_Sol + 1
#Note_La = #Note_Sol_ + 1
#Note_La_ = #Note_La + 1
#Note_Si = #Note_La_ + 1
Small suggestion for improvement:

Code: Select all

Enumeration 66
  #Note_Do
  #Note_Do_
  #Note_Re
  #Note_Re_
  #Note_Mi
  #Note_Fa
  #Note_Fa_
  #Note_Sol
  #Note_Sol_
  #Note_La
  #Note_La_
  #Note_Si
EndEnumeration

Re: Help converting from Visual Basic

Posted: Sat Mar 18, 2023 11:45 am
by infratec
It was not planned this way, :mrgreen:
I had not the half tones in the original code

Re: Help converting from Visual Basic

Posted: Sat Mar 18, 2023 12:28 pm
by infratec
I corrected the version above and added a link for explanations.

Now it represents middle octave C4 - B4 (261.63Hz to 493.88Hz)

But in general: it makes more sense to use midi directly instead of this old dll.

Re: Help converting from Visual Basic

Posted: Sat Mar 18, 2023 9:35 pm
by marcos.exe
infratec wrote: Sat Mar 18, 2023 12:28 pm I corrected the version above and added a link for explanations.

Now it represents middle octave C4 - B4 (261.63Hz to 493.88Hz)

But in general: it makes more sense to use midi directly instead of this old dll.

It was simpler than I thought. I'm running in Pure Basic in the 32bit version, instead of the 64bit version..
It's working fine.
Thank you very much.

But, I'm looking through the documentation to see how to play different channels, and I can't find such an easy to understand explanation.
I even looked for help in the gpt chat, but that doesn't know how to program in PureBasic correctly.
Could you help me with this situation more?

Remembering that I don't want to write anything too complex. I just want to write a program that plays notes in real time.
Because I'm totally blind, I can't use VST's, deflemask, among other programs on the market very well. So I'm having to write my own. And what I've achieved so far is helping me a lot.
I've already created a program for MIDI, and despite some bugs, it works fine for me. Then I will improve it.
But what I need now is to write this one.
Thank you so much again in advance, for everything so far!