New Midi Keyboard
Posted: Mon Mar 11, 2013 9:12 pm
As there are some new users interested in Midi, here is an updated version of this oldie.
Cheers!
Cheers!
Code: Select all
;PB MIDI Keyboard - by Einander
;Updated for PB 5.x
;Improved PC Keyboard input
;New General MIDI Menu
EnableExplicit
Enumeration
#Container
#MouseOver
#Sustain
#Sounds
#ShutUp
#Volume
#ShowVolume
#Img
#ImGad
#Quit
EndEnumeration
#Sust="Sustained "
#KeyPrevState = $40000000 ; bit 30
;
Structure MIDI
MIDIIn.A
MIDIOut.A
Stat.A
Dat1.A
Dat2.A
EndStructure
;
Structure I3: I1.I: I2.I: I3.I:EndStructure
Global _MIDI.MIDI,_hMIDIIn,_HMIDIout,_DrawING,_Keep$,_AutoPan=1
Global _TGMou,_TGkey, _XKbd, _YKbd, W_KBD, _WhiteKeyWidth, _WhiteKeyHeight, _Wid2, _Wid3, _Wid4, _Wid5, _OctaveWidth, _H2
Global Dim _Sounds.S(127), Dim _KNote.B(6), Dim _Pair.I(86)
Global _chromatic$="C C#D EbE F F#G AbA BbB C " ; mixed # b
Define Font1,Font2,I,Ev,KeySel,MousNote,MouseOver
Define X,Y,Sound$,RGB,KbNT,KBD$,T1,Ti,LParam,K$
Define T$,Finds,Ova,HIWO
Font1=FontID(LoadFont(-1,"arial",10))
Font2=FontID(LoadFont(-1,"wingdings",10))
_KNote(0)=0 : _KNote(1)=2 :_KNote(2)=4 : _KNote(3)=5 :_KNote(4)=7 :_KNote(5)=9 :_KNote(6)=11
;
Macro HIWO(A) : ((A>>16)&$FFFF) : EndMacro ;- HiWord(A)
;
Macro GadgetBottom(Gad) : GadgetY(Gad)+GadgetHeight(Gad) : EndMacro ;
;
Macro GadgetRight(Gad) : GadgetX(Gad)+GadgetWidth(Gad) : EndMacro :
;
Macro MMx : WindowMouseX(EventWindow()) : EndMacro ;
;
Macro MMy : WindowMouseY(EventWindow()) : EndMacro ;
;
Macro MMk
Abs(GetAsyncKeyState_(#VK_LBUTTON) +GetAsyncKeyState_(#VK_RBUTTON)*2+GetAsyncKeyState_(#VK_MBUTTON)*3)/$8000
EndMacro ;
;
Macro GadRGB(Gad,RGB1=#LBLUE,RGB2=#DBLUE)
SetGadgetColor(Gad,1,RGB1)
SetGadgetColor(Gad,2,RGB2)
EndMacro ;
;
Macro StopDraw
If _DrawING:StopDrawing():_DrawING=0:EndIf
EndMacro ;
;
Macro DrawImg(ImgNum) ;- DrawImg(ImgNum)
StopDraw
_DrawING=StartDrawing(ImageOutput(ImgNum))
EndMacro ;
;
Macro MIDI_NT(Note) Name one Note
Mid(_Chromatic$, (Note % 12) * 2 + 1, 2)
EndMacro ;
;
Macro SetSounds(Channel, Sounds) ;- SetSounds - From 0 to 127
midiOutShortMsg_(_HMIDIout, $C0 | Channel | Sounds<< 8 )
EndMacro ;
;
Macro SetPan(Chan,Pan)
midiOutShortMsg_(_HMIDIout,$B0 |Chan | $A00 | Pan<< 16 )
EndMacro ;
;
Macro PlayNote(Channel, Note, Loudness) ;- PlayNote - Loudness 0 = no Sound
If _AutoPan
SetPan(Channel,Note)
EndIf
midiOutShortMsg_(_HMIDIout,$90 | Channel | Note << 8 | Loudness << 16 )
EndMacro
;
Macro MIDIVolume(Channel,Volume)
midiOutShortMsg_(_HMIDIout,$B0 | Channel | $700 | Volume << 16 )
EndMacro
;
Procedure AllOff() ; mute all channels
Protected I
For I=0 To 15
midiOutShortMsg_(_HMIDIout, $B0 | I | $7B00 )
Next
EndProcedure
;
Procedure MIDIInProc(hMIDIIn, WMsg, DuMMy, D1, D2) ; get NoteOn,NoteOff)
With _MIDI
Select WMsg ; process some MIDI in events; here you can add more events
Case #MM_MIM_DATA
If D1 & 255 =144
\Stat=(D1 >> 8) & $FF ;Note
\Dat1=(D1 >> 16) & $FF ;Velocity
; If \Dat1 : SetWindowTitle(0,"Note On")
; Else : SetWindowTitle(0,"Note Off")
; EndIf
EndIf
EndSelect
EndWith
EndProcedure
;
Procedure MIDIinit(*MIDIInProc=-1,Instrument=0) ;MIDIinproc default solo muestra Note on, Note off
Protected OutDev , InDev
If *MIDIinproc=-1:*MIDIinproc=@MIDIinproc():EndIf
If midiInOpen_(@_hMIDIIn, InDev, *MIDIInProc, 0, #CALLBACK_FUNCTION) = #MMSYSERR_NOERROR
If midiInStart_(_hMIDIIn) <> #MMSYSERR_NOERROR : MessageRequester("Error","Can't start MIDI IN",0) :End: EndIf
EndIf
midiOutOpen_(@_HMIDIout, OutDev, 0, 0, 0)
midiOutShortMsg_(_HMIDIout, 192 | Instrument<<8 )
If _hMIDIIn And _HMIDIout
If midiConnect_(_hMIDIIn, _HMIDIout, 0)
MessageRequester("Error","Can't connect MIDI",0) :End
EndIf
EndIf
EndProcedure
;
Procedure MenuSounds()
Protected GMMenu=CreatePopupMenu(#PB_Any),I, J ,A$
Restore GeneralMIDISounds
For J=0 To 127 Step 8
Read.S A$
If A$="_Last_":Break:EndIf
OpenSubMenu(A$)
For I=J To J+7
Read.S A$
MenuItem(I,Str(I+1)+" "+A$)
_Sounds(I)=A$
Next
CloseSubMenu()
Next
ProcedureReturn GMMenu
EndProcedure
;
Procedure.S MIDI_2_Notes(Note$) ; naming multiple Notes
Protected I, Note,Nt$
For I = 0 To Len(Note$) - 1
Note = PeekA(@Note$ + I)
Nt$ + Trim(Mid(_Chromatic$, (Note % 12) * 2 + 1, 2)) + Str(Note / 12) + " "
Next I
ProcedureReturn Nt$
EndProcedure
;
Procedure GetNote() ; get Selected MIDINote
Protected R.RECT,Octave,Note,Side
R\Right=W_KBD-1
R\Bottom=_WhiteKeyHeight
If PtInRect_(R,MMx|MMy<<32)
Octave = (MMx / _WhiteKeyWidth) / 7 * 12 ;octave
Note=Octave+_KNote((MMx / _WhiteKeyWidth) % 7 ) ;White Key
Side= (MMx / _Wid2)%14 ;position on the Left ot Right half of each white Key
If MMy>_H2 Or Side=0 Or Side=5 Or Side=6 Or Side=13 : ProcedureReturn Note ;white Key
ElseIf Side=1 Or Side=3 Or Side=7 Or Side=9 Or Side=11 : ProcedureReturn Note + 1 ;Left black Key
Else
ProcedureReturn Note - 1 ;Right black Key
EndIf
EndIf
ProcedureReturn -1
EndProcedure
;
Procedure DrawKBD(Nt, KeySelected) ; Draw MIDI Key
Protected KeyColor,Octave,Last,A,X
If Nt < 128
DrawImg(#Img)
If KeySelected = #Black : KeyColor = $DCF5FB
Else : KeyColor = KeySelected
EndIf
Octave = Nt / 12
Last=Nt
Nt % 12
X = Octave * _OctaveWidth
Select Nt
Case 1, 3 , 6 , 8, 10 ; black keys
If Nt > 5 : A = 1 : EndIf
X + (Nt + A) * _Wid2 + 1
Box (X + _Wid3-2, 1,_Wid2, _H2-3 ,$AF8F8F)
Box (X + _Wid3-1, 2, _Wid2-2, _H2 - 5, KeySelected)
Default ; white keys
Select Nt
Case 0 : Box(X + 1, 0, _Wid5, _H2, KeyColor)
Case 2 : X + _WhiteKeyWidth : Box(X + _Wid3, 0, _Wid4, _H2, KeyColor)
Case 4 : X + _WhiteKeyWidth * 2 : Box(X + _Wid3, 0, _Wid5, _H2, KeyColor)
Case 5 : X + _WhiteKeyWidth * 3 : Box(X + 1, 0, _Wid5, _H2,KeyColor)
Case 7 : X + _WhiteKeyWidth * 4 : Box(X + _Wid3, 0, _Wid4, _H2, KeyColor)
Case 9 : X + _WhiteKeyWidth * 5 : Box(X + _Wid3, 0, _Wid4, _H2, KeyColor)
Case 11: X + _WhiteKeyWidth * 6 : Box(X + _Wid3, 0, _Wid5, _H2, KeyColor)
EndSelect
Box (X + 1, _H2, _WhiteKeyWidth - 1, _H2-1, KeyColor)
Box (X + 2, _H2*2-1, _WhiteKeyWidth - 3, 1, KeyColor)
EndSelect
If Last = 127
Box(X + _Wid3, 0, _Wid5, _H2, KeyColor)
EndIf
StopDraw
SetGadgetState(#ImGad,ImageID(#Img))
EndIf
EndProcedure
;
Procedure.S SortString(A$)
Protected I,Le = Len(A$) - 1
Protected Dim Sort.B(Le)
For I = 0 To Le
Sort(I) = PeekA(@A$ + I)
Next
SortArray(Sort(), 0)
For I = 0 To Le
PokeA(@A$ + I, Sort(I))
Next
ProcedureReturn A$
EndProcedure
;
Procedure Sustain(Note, Sustain) ; Add / remove / mute sustained Notes
Protected A$,I
If Note > - 1 ; Add Note
A$ = Chr(Note)
If FindString(_Keep$, A$, 1) = 0
_Keep$ + A$
EndIf
EndIf
If Len(_Keep$) > Sustain
For I = 1 To Len(_Keep$) - Sustain
PlayNote(1, Asc(Mid(_Keep$, I, 1)), 0) ; mute unwanted Notes
Next I
_Keep$ = Right(_Keep$, Sustain) ; remove Notes
EndIf
If Len(_Keep$) : SetGadgetText(_TGMou, MIDI_2_Notes(SortString(_Keep$)) )
Else : SetGadgetText(_TGMou,"")
EndIf
EndProcedure
; _______________________________________-
ExamineDesktops()
Define _X = DesktopWidth(0)
_XKbd = 50 : _YKbd = 120 ; Key width it's up to screen width
_WhiteKeyWidth = (_X - 100) / 77
_WhiteKeyWidth + (_WhiteKeyWidth & 1) ; white Key width always even
_WhiteKeyHeight =_X/14 ; white key Height
w_KBD= _WhiteKeyWidth * 75 ; Keyboard width
_H2 = _WhiteKeyHeight/ 2 ; half Key height, to find black or white Keys
_Wid2 = _WhiteKeyWidth / 2 ; half Key width, for black Keys
_Wid3 = _WhiteKeyWidth / 3 ; black Keys position
_Wid4 = _WhiteKeyWidth - _Wid3 * 2 + 1 ; ditto
_Wid5 = _WhiteKeyWidth - _Wid3 ; ditto
_OctaveWidth = _WhiteKeyWidth * 7 ; 7 Keys = one octave width
Define Sustain = 1
;<<<<<<<<<<<<<<<<<<<<<<<<<<
Define Title$= "PB MIDI Keyboard < Ctrl > Or Right MouseButton to Stop Sound."
Define hWnd = OpenWindow(0, _XKbd, _YKbd, W_KBD, _WhiteKeyHeight + 62,Title$, #PB_Window_SystemMenu |#PB_Window_Invisible)
#BKRGB=$C94714
SetWindowColor(0,#BKRGB)
StickyWindow(0,1)
RemoveKeyboardShortcut(0,#PB_Shortcut_Tab)
Define GMMenu=MenuSounds()
CreateImage(#Img,WindowWidth(0),_WhiteKeyHeight)
ImageGadget(#ImGad,0,0,0,0,ImageID(#Img))
Define Sounds=49 ; starting Sound
Define Loudness = 120 ; Choose From 0 To 127 : Loudness 0 = no Sound
Define MIDIVolume=100
Restore MapKeys ; map keys to MIDInotes : default configuration for Spanish keyboard layout
; For other layouts some keys may need different values <<<<<<<<<<<<<<<<<<<<<<
; You can map different pairs to simulate other instruments (like Guitar, bandoneon, concertina...)
Repeat
Read.I X
If X=$ACABA:Break:EndIf
Read.I Y
_Pair(X)=Y
ForEver
ContainerGadget(#Container, 0, WindowHeight(0) - 60, WindowWidth(0)-100, 24 )
SetGadgetColor(#Container,2,#BKRGB)
Define TGSustain=HyperLinkGadget(#PB_Any,0,2,50,20,"Sustain",#White)
GadRGB(TGSustain,#White,#BKRGB)
SpinGadget(#Sustain, 50, 2, 30, 20, 0, 8)
CheckBoxGadget(#mouseover,GadgetRight(#Sustain)+30,2,100,20,"Mouse Over")
HyperLinkGadget (#ShutUp,GadgetRight(#MouseOver)+30,2,80,18,"Stop Sound",#Red)
GadRGB(#ShutUp,#White,#BKRGB)
HyperLinkGadget (#Sounds,GadgetRight(#ShutUp)+30,2,140,18,"Sound "+Str(Sounds+1)+" : "+_Sounds(Sounds),#Red)
GadRGB(#Sounds,#White,#BKRGB)
SetGadgetState(#Sustain, Sustain)
SetGadgetText(#Sustain, Str(Sustain) )
TextGadget(#ShowVolume,GadgetRight(#Sounds),2,100,18,"Vol "+Str(MIDIVolume),#PB_Text_Right)
TrackBarGadget(#Volume,GadgetRight(#ShowVolume)+4,2,100,18,0,127)
SetGadgetState(#Volume,MIDIVolume)
CloseGadgetList()
GadRGB(#ShowVolume,#White,#BKRGB)
ButtonGadget (#Quit,GadgetRight(#Container)-70,WindowHeight(0)-20,50,18,"Quit")
GadgetToolTip(TGSustain,"Simultaneous Sounding Notes")
GadgetToolTip(#Sustain,"Simultaneous Sounding Notes")
GadgetToolTip(#MouseOver,"Play notes moving mouse")
GadgetToolTip(#ShutUp,"< Ctrl > Or Right MouseButton to Stop Sound")
GadgetToolTip(#Sounds,"Select 128 General MIDI Sounds")
GadgetToolTip(#Volume,"Volume Control")
_TGMou=HyperLinkGadget(#PB_Any,4,GadgetBottom(#Container)+3,250,20,"",0)
_TGkey=HyperLinkGadget(#PB_Any,GadgetRight(_TGMou)+4,GadgetY(_TGMou),250,20,"",0)
SetGadgetFont(_TGMou,Font1)
SetGadgetFont(_TGkey,Font1)
GadgetToolTip(_TGMou,"Show Notes Played with Mouse")
GadgetToolTip(_TGkey,"Show Notes Played with PC Keyboard")
GadRGB(_TGMou,0,#Red)
GadRGB(_TGkey,0,#Green)
DrawImg(#Img)
Box(0, 0, W_KBD + 1, _WhiteKeyHeight + 2,$673D06) ; background & space between Keys
For I = 0 To 127 ; Draw all keys - full MIDIKeyboard
DrawKBD(I, #Black)
Next I
MIDIinit()
Define OldNote=-1, LastNote=-1
SetSounds(1, Sounds)
AddKeyboardShortcut(0,$A1,302) ;Right shift para MIDINote 67
HideWindow(0,0)
Repeat
If GetAsyncKeyState_(27)&$8000 : End : EndIf
Ev = WaitWindowEvent(1)
If MMk=2 Or GetAsyncKeyState_(#VK_CONTROL)
AllOff()
For I = 0 To 127
DrawKBD(I, #Black)
Next I
_Keep$=""
SetGadgetText(_TGkey, "")
If KeySel : KeySel=0: EndIf
SetGadgetText(_TGMou,"")
ElseIf MMk=0 And Sustain=0 And OldNote>-1
AllOff()
OldNote=-1
EndIf
Select Ev
Case #PB_Event_Gadget
Select EventGadget()
Case #Sustain
Sustain = GetGadgetState(#Sustain)
SetGadgetText(#Sustain, Str(Sustain))
If Sustain : Sustain(-1, Sustain)
Else : SetGadgetText(_TGkey, "")
EndIf
SetActiveGadget(#ImGad)
WindowEvent() ; avoid endless event-Loops
Case #Sounds
X=WindowX(0)+GadgetX(#Container)+GadgetX(#Sounds)
Y=WindowY(0)+GadgetY(#Container)+GadgetY(#Sounds)+46
DisplayPopupMenu(GMMenu, WindowID(0),X,Y)
Case #ShutUp :AllOff()
SetGadgetText(_TGkey, "")
If KeySel : DrawKBD(KeySel - 1, #Black)
KeySel=0
ElseIf MousNote : DrawKBD(MousNote,#Black)
EndIf
Case #Volume
MIDIVolume=GetGadgetState(#Volume)
SetGadgetText(#ShowVolume,"Vol "+Str(MIDIVolume))
MIDIVolume(1,MIDIVolume)
Case #MouseOver
MouseOver=GetGadgetState(#mouseover)
SetGadgetText(_TGMou,"")
SetActiveGadget(#Container)
Case #Quit : Break
EndSelect
Case #PB_Event_Menu
Select EventMenu()
Case 0 To 127
Sounds=EventMenu()
SetSounds(1, Sounds) ; many Sound cards have Sounds only From 0 to 108, so some keys may be muted
Sound$=GetMenuItemText(GMMenu,EventMenu())
SetGadgetText(#Sounds,"Sound "+Str(Sounds+1)+" : "+_Sounds(Sounds))
AllOff()
Case 302 : KbNT=67 ; right shift assigned to "qwerty" line
EndSelect
Default
MousNote=GetNote()
If MousNote>-1
If MMk=1 Or MouseOver
If MousNote<>LastNote
If Sustain
PlayNote(1, MousNote, 0) ; mute MouseNote
ElseIf OldNote>-1
AllOff()
OldNote=-1
EndIf
LastNote=-1
PlayNote(1, MousNote,Loudness)
OldNote=MousNote
If Sustain:Sustain(MousNote, Sustain):EndIf
EndIf
LastNote=MousNote
If MousNote + 1 <> KeySel ; +1 because 0 is a legal MIDINote
If KeySel
DrawKBD(KeySel - 1, #Black)
If GetGadgetState(#Sustain) = 0 : PlayNote(1, KeySel - 1, 0) : EndIf
EndIf
If MouseOver
PlayNote(1, MousNote,Loudness)
If Sustain : Sustain(MousNote, Sustain) : EndIf
EndIf
DrawKBD(MousNote,#Red ) ; Mouse pressed Note
EndIf
KeySel = MousNote + 1
Else
LastNote=-1
EndIf
EndIf
EndSelect
; in some PC keyboards is possible to hit 2 or more keys siMoultaneously to Play Chords
If Ev= #WM_KEYDOWN Or Ev= #WM_KEYUP
LParam=EventlParam()
If Ev=#WM_KEYDOWN
If (LParam & #KeyPrevState)=0 ; avoid repeateds
HIWO=HIWO(LParam)
Else
KbNT=-1
EndIf
Else
HIWO=HIWO(LParam)-$C000
EndIf
Select HIWO
Case 2 To 28, 30 To 54,58,86
KbNT=_Pair(HIWO)
K$=Chr(KbNT)
Finds=FindString(KBD$,K$,1)
If Ev=#WM_KEYDOWN
RGB=#Green
If Finds=0
PlayNote(1,KbNT,Loudness)
KBD$+K$
EndIf
ElseIf Finds
PlayNote(1,KbNT,0)
KBD$=ReplaceString(KBD$,K$,"")
EndIf
If Len(KBD$) : SetGadgetText(_TGkey, MIDI_2_Notes(KBD$))
Else : SetGadgetText(_TGkey, "")
EndIf
DrawKBD(KbNT,RGB)
Default
KbNT=-1
EndSelect
Else
RGB=#Black
EndIf
Until Ev = 16 ;#PB_Event_CloseWindow
midiOutClose_(_HMIDIout)
CloseWindow(0)
End
;<<<<<<<<<<<<<<<<<<<<
DataSection
GeneralMIDISounds:
Data.S "Keyboards","Piano 1", "Piano 2","Piano 3","Honky-tonk","Electric Piano 1","Electric Piano 2","HarpsiChord","Clavinet",
"Chromatic Percusion","Celesta","Glockenspiel", "Music Box","Vibraphone","Marimba","Xylophone","Tubular bell","Dulcimer",
"Organ/Accordion","Organ 1","Organ 2","Organ 3","Church organ 1","Reed Organ","French Accordion","Harmonica","Bandoneon",
"Guitars","Nylon-string Guitar","Steel-string Guitar","Jazz Guitar","Clean Guitar","Muted Guitar","Overdrive Guitar","Distortion Guitar","Guitar Harmonics",
"Bass","Acoustic Bass","Fingered Bass","Picked Bass","Fretless Bass","Slap Bass 1","Slap Bass 2","Synth Bass 1","Synth Bass 2",
"Strings","Violin","Viola","Cello","Contrabass","Tremolo Strings","Pizzicato","Harp","Timpani",
"Orchestral","Strings","Slow Strings","Synth Strings 1","Synth Strings 2","Choir Aahs","Voice Oohs","SynVox","Orchestra Hit",
"Brass","Trumpet","Trombone","Tuba","Muted Trumpet","French Horn","Brass Section","Synth Brass 1","Synth Brass 2",
"Reed","Soprano Sax","Alto Sax","Tenor Sax","Baritone Sax","Oboe","English Horn","Basson","Clarinet",
"Woodwinds","Piccolo","Flute","Recorder","Pan Flute","Bottle Blow","Shakuhachi","Whistle","Ocarina",
"Lead Synth","Square Wave","Saw Wave","Synth Calliope","Chiffer Lead","Charang","Solo Vox","5th Saw Wave","Bass&Lead",
"Synth Pad","Fantasia","Warm pad","PolySynth","Space Voice","Bowed Glass","Metal pad","Halo pad","Sweep pad",
"Synth Effects","Ice Rain","Sound Track","Crystal","Atmosphere","BRightness","Goblin","ECho Drops","Star Theme",
"Ethnic","Sitar","Banjo","Shamisen","Koto","Kalimba","Bag Pipe","Fiddle","Shanai",
"Percussion","Tinkle bell","Agogo","Steel Drums","Woodblock","Taiko Drum","Melodic Tom 1","Synth Drum","Reverse Cymb",
"Sound Effects","Guitar FretNoise","Breath Noise","Seashore","Bird","Telephone","Helicopter","Applause","Gun Shot","_Last_"
;--------------------------------------------------------------------------------------------------------
MapKeys: ; PC Keyboard config ; assign pairs (MIDINotes to each Key) to simulate a Piano Keyboard
; This configuration is for Spanish keyboard layout ; first data=key, 2nd data=midinote <<<<<<<<<<<<<<<<<<<<<<
Data.I 41 , 65 ;F ;row 1234...
Data.I 2 , 66 ;F#
Data.I 3 , 68 ;Ab
Data.I 4 , 70 ;Bb
Data.I 5 , 72 ;C
Data.I 6 , 73 ;C#
Data.I 7 , 75 ;Eb
Data.I 8 , 77 ;F
Data.I 9 , 78 ;F#
Data.I 10 , 80 ;Ab
Data.I 11 , 82 ;Bb
Data.I 12 , 84 ;C
Data.I 13 , 85 ;C#
Data.I 14 , 87 ;Eb
Data.I 15 , 65 ;F ;row qwerty ; C Scale
Data.I 16 , 67 ;G
Data.I 17 , 69 ;A
Data.I 18 , 71 ;B
Data.I 19 , 72 ;C
Data.I 20 , 74 ;D
Data.I 21 , 76 ;E
Data.I 22 , 77 ;F
Data.I 23 , 79 ;G
Data.I 24 , 81 ;A
Data.I 25 , 83 ;B
Data.I 26 , 84 ;C
Data.I 27 , 86 ;D
Data.I 28 , 88 ;E
Data.I 58 , 48 ;C ;row asdf ; Db Scale
Data.I 30 , 49 ;Db
Data.I 31 , 51 ;Eb
Data.I 32 , 53 ;F
Data.I 33 , 54 ;Gb
Data.I 34 , 56 ;Ab
Data.I 35 , 58 ;Bb
Data.I 36 , 60 ;C
Data.I 37 , 61 ;Db
Data.I 38 , 63 ;Eb
Data.I 39 , 65 ;F
Data.I 40 , 66 ;Gb
Data.I 43 , 68 ;Ab
Data.I 42 , 47 ;B ;row zxcv...
Data.I 86 , 48 ;C
Data.I 44 , 50 ;D
Data.I 45 , 52 ;E
Data.I 46 , 53 ;F
Data.I 47 , 55 ;G
Data.I 48 , 57 ;A
Data.I 49 , 59 ;B
Data.I 50 , 60 ;C
Data.I 51 , 62 ;D
Data.I 52 , 64 ;E
Data.I 53 , 65 ;F
Data.I 54 , 67 ;G
Data.I $ACABA
EndDataSection