Piano Player

Post your working scripts, libraries and tools for AHK v1.1 and older
euras
Posts: 429
Joined: 05 Nov 2015, 12:56

Piano Player

08 Mar 2018, 15:24

I'm not an author (it's Lazsko I think) of that functionality, I just made the GUI for that functionality and give that player for you.

Code: Select all

#SingleInstance Force
#NoEnv
SetBatchLines -1
#MaxHotkeysPerInterval 199

nz = 2A.1E.2C.1F.2D.2E.21.2F.22.30.23.31.32.25.33.26.34.35.28.136.1C         ; Scan Acodes for Shift+CapsLock rows

KEYS := ".", MidiDevice := 0
Program1  := 16, Program2  :=  1
Velocity1 := 64, Velocity2 := 127
BaseNote1 := 60, BaseNote2 := 36    ; C4 = 261.63Hz, C2 = 65.41Hz

OnExit CleanUp
OpenCloseMidiAPI()
h_midiout := midiOutOpen(MidiDevice)
midiOutShortMsg(h_midiout, 191, 1, Program1, 0) ; Program Change
midiOutShortMsg(h_midiout, 191, 2, Program2, 0) ; Program Change

wid := 45
Gui, +Border
gui, color, 331a00
gui, add, edit, x0 y0 w0 h0 vKEYS, 
gui, font, s18 cWhite bold, Verdana
gui, add, text, x400 y5, YAMAHA
gui, font, s10 cWhite, Verdana
Gui, add, text, x+170 y12, Program 1
gui, add, progress, x+10 y14 w12 h12 Background000000 c00ff00 vprog1, 100
Gui, add, text, x+50 y12, Program 2
gui, add, progress, x+10 y14 w12 h12 Background000000 c00ff00 vprog2, 0

Loop, 21
	Gui, add, progress, % "y50 x" wid * (A_Index-1) " +Border +BackgroundTrans Backgroundffff80 cffffb3 w45 h200 vWhite" A_Index, 100
gui, add, progress, % "y50 x" wid * 2 / 3 " Background333333 c000000 w30 h130 vBlack1", 100
gui, add, progress, % "y50 x" wid * 5 / 3 " Background333333 c000000 w30 h130 vBlack2", 100
gui, add, progress, % "y50 x" wid * 11 / 3 " Background333333 c000000 w30 h130 vBlack3", 100
gui, add, progress, % "y50 x" wid * 14 / 3 " Background333333 c000000 w30 h130 vBlack4", 100
gui, add, progress, % "y50 x" wid * 17 / 3 " Background333333 c000000 w30 h130 vBlack5", 100

gui, add, progress, % "y50 x" wid * 23 / 3 " Background333333 c000000 w30 h130 vBlack6", 100
gui, add, progress, % "y50 x" wid * 26 / 3 " Background333333 c000000 w30 h130 vBlack7", 100
gui, add, progress, % "y50 x" wid * 32 / 3 " Background333333 c000000 w30 h130 vBlack8", 100
gui, add, progress, % "y50 x" wid * 35 / 3 " Background333333 c000000 w30 h130 vBlack9", 100
gui, add, progress, % "y50 x" wid * 38 / 3 " Background333333 c000000 w30 h130 vBlack10", 100

gui, add, progress, % "y50 x" wid * 44 / 3 " Background333333 c000000 w30 h130 vBlack11", 100
gui, add, progress, % "y50 x" wid * 47 / 3 " Background333333 c000000 w30 h130 vBlack12", 100
gui, add, progress, % "y50 x" wid * 53 / 3 " Background333333 c000000 w30 h130 vBlack13", 100
gui, add, progress, % "y50 x" wid * 56 / 3 " Background333333 c000000 w30 h130 vBlack14", 100
gui, add, progress, % "y50 x" wid * 59 / 3 " Background333333 c000000 w30 h130 vBlack15", 100
Gui, Show, w945 h250, My Piano

#IfWinActive My Piano ahk_class AutoHotkeyGUI
Loop Parse, nz, .                   ; Shift-Z key row
{
   n%A_LoopField% := A_Index - 1
   c%A_LoopField% := 2              ; note played in channel 2
   HotKey sc%A_LoopField%, KEY
   HotKey sc%A_LoopField% UP, KEYup
}
return

Up::
   Program2 += Program2 > 126 ? 0 : 1
   GuiControl, , prog1, 0
   GuiControl, , prog2, 100
   midiOutShortMsg(h_midiout, 191, 2, Program2, 0) ; Program Change
Return

Down::
   Program2 -= Program2 < 1 ? 0 : 1
   GuiControl, , prog1, 100
   GuiControl, , prog2, 0
   midiOutShortMsg(h_midiout, 191, 2, Program2, 0) ; Program Change
Return


KEY:
   k := SubStr(A_ThisHotKey,3)
   IfInString KEYS, %k%, Return
   IfEqual n%k%,, Return
   channel := c%k%,  KEYS .= k . "."
   Piano_Keyboard()
   midiOutShortMsg(h_midiout, 143, channel, BaseNote%channel% + n%k%, Velocity%channel%) ; NoteOn
   GuiControl,,KEYS, %KEYS%
   Key_Item =
Return

KEYup:
   k := SubStr(A_ThisHotKey,3,StrLen(A_ThisHotKey)-5)
   channel := c%k%
   StringReplace KEYS, KEYS, .%k%., ., All
   Piano_Keyboard()
   midiOutShortMsg(h_midiout, 127, channel, BaseNote%channel% + n%k%, Velocity%channel%) ; NoteOff
   GuiControl,,KEYS, %KEYS%
   Key_Item =
Return

Piano_Keyboard(){
	global
	if (A_ThisHotKey = "sc1E" or A_ThisHotKey = "sc1E UP"){
		Key_Item := "Black6"
		if A_ThisHotKey not contains UP
			GuiControl, , %Key_Item%, 0
		if A_ThisHotKey contains UP
			GuiControl, , %Key_Item%, 100
	}
	if (A_ThisHotKey = "sc1F" or A_ThisHotKey = "sc1F UP"){
		Key_Item := "Black7"
		if A_ThisHotKey not contains UP
			GuiControl, , %Key_Item%, 0
		if A_ThisHotKey contains UP
			GuiControl, , %Key_Item%, 100
	}
	if (A_ThisHotKey = "sc21" or A_ThisHotKey = "sc21 UP"){
		Key_Item := "Black8"
		if A_ThisHotKey not contains UP
			GuiControl, , %Key_Item%, 0
		if A_ThisHotKey contains UP
			GuiControl, , %Key_Item%, 100
	}
	if (A_ThisHotKey = "sc22" or A_ThisHotKey = "sc22 UP"){
		Key_Item := "Black9"
		if A_ThisHotKey not contains UP
			GuiControl, , %Key_Item%, 0
		if A_ThisHotKey contains UP
			GuiControl, , %Key_Item%, 100
	}
	if (A_ThisHotKey = "sc23" or A_ThisHotKey = "sc23 UP"){
		Key_Item := "Black10"
		if A_ThisHotKey not contains UP
			GuiControl, , %Key_Item%, 0
		if A_ThisHotKey contains UP
			GuiControl, , %Key_Item%, 100
	}
	if (A_ThisHotKey = "sc25" or A_ThisHotKey = "sc25 UP"){
		Key_Item := "Black11"
		if A_ThisHotKey not contains UP
			GuiControl, , %Key_Item%, 0
		if A_ThisHotKey contains UP
			GuiControl, , %Key_Item%, 100
	}
	if (A_ThisHotKey = "sc26" or A_ThisHotKey = "sc26 UP"){
		Key_Item := "Black12"
		if A_ThisHotKey not contains UP
			GuiControl, , %Key_Item%, 0
		if A_ThisHotKey contains UP
			GuiControl, , %Key_Item%, 100
	}
	if (A_ThisHotKey = "sc28" or A_ThisHotKey = "sc28 UP"){
		Key_Item := "Black13"
		if A_ThisHotKey not contains UP
			GuiControl, , %Key_Item%, 0
		if A_ThisHotKey contains UP
			GuiControl, , %Key_Item%, 100
	}
	if (A_ThisHotKey = "sc1C" or A_ThisHotKey = "sc1C UP"){
		Key_Item := "Black14"
		if A_ThisHotKey not contains UP
			GuiControl, , %Key_Item%, 0
		if A_ThisHotKey contains UP
			GuiControl, , %Key_Item%, 100
	}
	if (A_ThisHotKey = "sc2A" or A_ThisHotKey = "sc2A UP"){
		Key_Item := "White8"
		if A_ThisHotKey not contains UP
			{
				GuiControl, , %Key_Item%, 0
				GuiControl, , Black6, 100 
			}
		if A_ThisHotKey contains UP
			{
				GuiControl, , %Key_Item%, 100
				GuiControl, , Black6, 100
			}
	}
	if (A_ThisHotKey = "sc2C" or A_ThisHotKey = "sc2C UP"){
		Key_Item := "White9"
		if A_ThisHotKey not contains UP
			{
				GuiControl, , %Key_Item%, 0
				GuiControl, , Black6, 100 
				GuiControl, , Black7, 100 
			}
		if A_ThisHotKey contains UP
			{
				GuiControl, , %Key_Item%, 100
				GuiControl, , Black6, 100
				GuiControl, , Black7, 100 
			}
	}
	if (A_ThisHotKey = "sc2D" or A_ThisHotKey = "sc2D UP"){
		Key_Item := "White10"
		if A_ThisHotKey not contains UP
			{
				GuiControl, , %Key_Item%, 0
				GuiControl, , Black7, 100 
			}
		if A_ThisHotKey contains UP
			{
				GuiControl, , %Key_Item%, 100
				GuiControl, , Black7, 100 
			}
	}
	if (A_ThisHotKey = "sc2E" or A_ThisHotKey = "sc2E UP"){
		Key_Item := "White11"
		if A_ThisHotKey not contains UP
			{
				GuiControl, , %Key_Item%, 0
				GuiControl, , Black8, 100 
			}
		if A_ThisHotKey contains UP
			{
				GuiControl, , %Key_Item%, 100
				GuiControl, , Black8, 100 
			}
	}
	if (A_ThisHotKey = "sc2F" or A_ThisHotKey = "sc2F UP"){
		Key_Item := "White12"
		if A_ThisHotKey not contains UP
			{
				GuiControl, , %Key_Item%, 0
				GuiControl, , Black8, 100 
				GuiControl, , Black9, 100 
			}
		if A_ThisHotKey contains UP
			{
				GuiControl, , %Key_Item%, 100
				GuiControl, , Black8, 100
				GuiControl, , Black9, 100 
			}
	}
	if (A_ThisHotKey = "sc30" or A_ThisHotKey = "sc30 UP"){
		Key_Item := "White13"
		if A_ThisHotKey not contains UP
			{
				GuiControl, , %Key_Item%, 0
				GuiControl, , Black9, 100 
				GuiControl, , Black10, 100 
			}
		if A_ThisHotKey contains UP
			{
				GuiControl, , %Key_Item%, 100
				GuiControl, , Black9, 100
				GuiControl, , Black10, 100 
			}
	}
	if (A_ThisHotKey = "sc31" or A_ThisHotKey = "sc31 UP"){
		Key_Item := "White14"
		if A_ThisHotKey not contains UP
			{
				GuiControl, , %Key_Item%, 0
				GuiControl, , Black10, 100 
			}
		if A_ThisHotKey contains UP
			{
				GuiControl, , %Key_Item%, 100
				GuiControl, , Black10, 100 
			}
	}
	if (A_ThisHotKey = "sc32" or A_ThisHotKey = "sc32 UP"){
		Key_Item := "White15"
		if A_ThisHotKey not contains UP
			{
				GuiControl, , %Key_Item%, 0
				GuiControl, , Black11, 100 
			}
		if A_ThisHotKey contains UP
			{
				GuiControl, , %Key_Item%, 100
				GuiControl, , Black11, 100 
			}
	}
	if (A_ThisHotKey = "sc33" or A_ThisHotKey = "sc33 UP"){
		Key_Item := "White16"
		if A_ThisHotKey not contains UP
			{
				GuiControl, , %Key_Item%, 0
				GuiControl, , Black11, 100 
				GuiControl, , Black12, 100 
			}
		if A_ThisHotKey contains UP
			{
				GuiControl, , %Key_Item%, 100
				GuiControl, , Black11, 100
				GuiControl, , Black12, 100 
			}
	}
	if (A_ThisHotKey = "sc34" or A_ThisHotKey = "sc34 UP"){
		Key_Item := "White17"
		if A_ThisHotKey not contains UP
			{
				GuiControl, , %Key_Item%, 0
				GuiControl, , Black12, 100 
			}
		if A_ThisHotKey contains UP
			{
				GuiControl, , %Key_Item%, 100
				GuiControl, , Black12, 100 
			}
	}
	if (A_ThisHotKey = "sc35" or A_ThisHotKey = "sc35 UP"){
		Key_Item := "White18"
		if A_ThisHotKey not contains UP
			{
				GuiControl, , %Key_Item%, 0
				GuiControl, , Black13, 100 
			}
		if A_ThisHotKey contains UP
			{
				GuiControl, , %Key_Item%, 100
				GuiControl, , Black13, 100 
			}
	}
	if (A_ThisHotKey = "sc136" or A_ThisHotKey = "sc136 UP"){
		Key_Item := "White19"
		if A_ThisHotKey not contains UP
			{
				GuiControl, , %Key_Item%, 0
				GuiControl, , Black13, 100 
				GuiControl, , Black14, 100 
			}
		if A_ThisHotKey contains UP
			{
				GuiControl, , %Key_Item%, 100
				GuiControl, , Black13, 100
				GuiControl, , Black14, 100 
			}
	}
}

OpenCloseMidiAPI() {  ; at the beginning to load, at the end to unload winmm.dll
   Static hModule
   If hModule
      DllCall("FreeLibrary", UInt,hModule), hModule := ""
   If (0 = hModule := DllCall("LoadLibrary",Str,"winmm.dll")) {
      MsgBox Cannot load libray winmm.dll
      ExitApp
   }
}

midiOutOpen(uDeviceID = 0) { ; Open midi port for sending individual midi messages --> handle
   strh_midiout = 0000
   result := DllCall("winmm.dll\midiOutOpen", UInt,&strh_midiout, UInt,uDeviceID, UInt,0, UInt,0, UInt,0, UInt)
   If (result or ErrorLevel) {
      MsgBox There was an error opening the midi port.`nError code %result%`nErrorLevel = %ErrorLevel%
      Return -1
   }
   Return NumGet(&strh_midiout)
}

midiOutShortMsg(h_midiout, Event, Channel, Param1, Param2) {
; Event: NoteOn 143, NoteOff 127, CC 175, PolyAT 159, ChanAT 207, PChange 191, Wheel 223
  result := DllCall("winmm.dll\midiOutShortMsg", UInt,h_midiout, UInt, Event+Channel|(Param1<<8)|(Param2<<16), UInt)
  If (result or ErrorLevel)  {
    MsgBox Error sending the midi event: (%result%`, %ErrorLevel%)
    Return -1
  }
}

midiOutClose(h_midiout) {  ; Close MidiOutput
   Loop {
      result := DllCall("winmm.dll\midiOutClose", UInt,h_midiout)
      If !(result or ErrorLevel)
         Return
      If (A_Index > 3) {
         MsgBox Error [%result%]-[%ErrorLevel%] in closing the midi output port.`nThere may still be midi events being processed.
         Return -1  ; result MIDIERR_STILLPLAYING 65, MMSYSERR_INVALHANDLE 5, MMSYSERR_NOMEM 7
      }
      Sleep 500
   }
}

MidiOutsEnumerate() { ; Returns #midi output devices, creates global array MidiOutPortName with their names
  Local NumPorts, PortID
  VarSetCapacity(MidiOutCaps, 50, 0)
  NumPorts := DllCall("winmm.dll\midiOutGetNumDevs") ; #midi output devices on system, first device ID = 0

  Loop %NumPorts% {
    PortID := A_Index -1
    result := DllCall("winmm.dll\midiOutGetDevCapsA", UInt,uDeviceID, UInt,&MidiOutCaps, UInt,50, UInt)
    If (result OR ErrorLevel) {
      MsgBox Error %result% (EL = %ErrorLevel%) in retrieving the name of midi output %uDeviceID%
      Return -1
    }
    VarSetCapacity(PortName, 32)                                         ; PortNameSize 32
    DllCall("RtlMoveMemory", Str,PortName, Uint,&MidiOutCaps+8, Uint,32) ; PortNameOffset 8, PortNameSize 32
    MidiOutPortName%PortID% := PortName
  }
  Return NumPorts
}

CleanUp:
   midiOutClose(h_midiout)
   OpenCloseMidiAPI()
ExitApp

/* MidiStatus byte: http://www.harmony-central.com/MIDI/Doc/table1.html
MIDIOUTCAPS struct
  WORD      wMid;
  WORD      wPid;
  MMVERSION vDriverVersion;
  CHAR      szPname[MAXPNAMELEN];
  WORD      wTechnology;
  WORD      wVoices;
  WORD      wNotes;
  WORD      wChannelMask;
  DWORD     dwSupport;
*/
User avatar
rommmcek
Posts: 1470
Joined: 15 Aug 2014, 15:18

Re: Piano Player

08 Mar 2018, 23:19

Great GUI! (I miss mouse click functionality)

P.s.: I knew this Laszlo's work, but I followed his example to load song into buffer. I wasn't able to enlarge the buffer and couldn't load entire lengthy song. Now I have different approach and was tempted to try the same with this function too. (It's a quick adaptation and may contain bugs!)

Addendum: This in essence moderat, but ever increasing downloads indicate for me some degree of Midi interest!
rommmcek wrote:(It's a quick adaptation and may contain bugs!)
To all who download (or will) rihanna-man_down: The correct code at line ~14 is: DllCall("Winmm\timeBeginPeriod", uint, TimePeriod:= 1), otherwise rithem can be pretty wobbly!
Attachments
rihanna-man_down_Piano.zip
(211.09 KiB) Downloaded 950 times
rihanna-man_down.ahk
(831.99 KiB) Downloaded 1518 times
Last edited by rommmcek on 19 Dec 2019, 09:01, edited 4 times in total.
User avatar
rommmcek
Posts: 1470
Joined: 15 Aug 2014, 15:18

Re: Piano Player

19 Dec 2019, 08:55

Only recently have seen this question thread.
Here are two further versions of this interesting project:
SpeedMaster's proposal
My simple dynamic proposal
It can get of course more complex: See gif of my current Piano, based on euras's project implementing teadrinker's proposal.
ntnvsl
Posts: 3
Joined: 23 Jun 2021, 21:33

Re: Piano Player

14 Mar 2023, 21:19

I made myself a similar script recently. Its layout is chromatic, which turns it from a toy into almost a real instrument that allows you to play real music, like piano/synth improvisations, acoustic arrangements of classics, or guitar solos.

https://github.com/untaun/ahkordion

Code: Select all

layout = 10 1E 2C 11 1F 2D 12 20 2E 13 21 2F 14 22 30 15 23 31 16 24 32 17 25 33 18 26 34 19 27 35 1A 28 36 1B 2B 148 1C

global midiPort    := 1
     , midiChannel := 0
     , isSustain   := 0
     , isBends     := 1
     , firstNote   := 40
     , octaveIndex := 0
     , velocity    := 110
     , lowVelocity := 90
     , bendRange   := 2
     , ccNumber    := 1
     , ccValue     := 0
, winmm, devOut, devIn
, anyKey := 0
, pressedKeys := {}
, savedKeys := {}
, savedMidi := {}
, buttonsAmount := 0
, lastVelocity := 0
, isPalmMute := 0
, guiWindow := "ahk_class AutoHotkeyGUI"

#NoEnv
menu tray, icon, imageres.dll, 206
#MaxHotkeysPerInterval 999
#SingleInstance force
SetBatchLines -1
#KeyHistory 0
ListLines off
OnExit("exit")

gui +LastFound +AlwaysOnTop -SysMenu
WinSet trans, 200
gui font, s11, Segoe UI
gui add, text, vguiText w180 h300
gui add, StatusBar
gui show

winmm := DllCall("LoadLibrary", "Str", "winmm")
DllCall("winmm\midiOutOpen", "UInt*", devOut, "UInt", midiPort, "UPtr", 0, "UPtr", 0, "UInt", 0)
DllCall("winmm\midiInOpen", "UInt*", devIn, "UInt", 0, "UInt", WinExist(), "UInt", 0, "UInt", 0x10000)
DllCall("winmm\midiInStart", "UInt", devIn)
OnMessage(0x3C1, "midiInput"), OnMessage(0x3C2, "midiInput"), OnMessage(0x3C3, "midiInput")

setCC(), updateInfo()

for key, code in StrSplit(layout, " ") {
  press := func("keyPress").bind(key)
  hotkey IfWinActive, % guiWindow
  hotkey % GetKeyName("SC" code), % press
  release := func("keyRelease").bind(key)
  hotkey IfWinActive, % guiWindow
  hotkey % GetKeyName("SC" code) " up", % release
  buttonsAmount++
}

#if WinActive(guiWindow)
       RAlt:: isSustain := !isSustain, updateInfo()
    AppsKey:: isBends := !isBends, updateInfo()
   Space up:: (isPalmMute) or mute()
          2:: mute()
          3::
          4:: mute(0)
          1:: isBends ? bend(2, 1)  : octaveShift()
       1 up:: isBends ? bend(-2)    :
        Tab:: isBends ? bend(1, 1)  : octaveShift(1)
     Tab up:: isBends ? bend(-1)    :
   CapsLock:: isBends ? bend(-1, 1) : octaveShift(-1)
CapsLock up:: isBends ? bend(1)     :
     LShift:: isBends ? bend(-2, 1) :
  LShift up:: isBends ? bend(2)     :
         BS:: bend(-2, 1, 1.5), mute(), DllCall("Sleep", "UInt", 175), pitch(0)
 ScrollLock:: bendRange := bendRange = 2 ? 12 : 2
       Left:: octaveShift(-1)
      Right:: octaveShift(1)
       Down:: octaveShift()
         F3:: mute(), midiChannel -= midiChannel > 0, updateInfo()
         F4:: mute(), midiChannel += midiChannel < 15, updateInfo()
         F6:: velocity -= (velocity > 20) * (10 - 3 * (velocity = 127)), updateInfo()
         F7:: velocity += (velocity < 127) * (10 - 3 * (velocity = 120)), updateInfo()
        F11:: firstNote -= firstNote > 21, updateInfo()
        F12:: firstNote += firstNote < 72, updateInfo()
       SC29:: (ccOn) or setCC(128), ccOn := 1
    SC29 up:: ccOn := 0, setCC(-128)
    WheelUp:: setCC(10)
  WheelDown:: setCC(-10)
#if

keyPress(k) {
  if pressedKeys[k]
    return
  pressedKeys[k] := 1
  , isSustain and !anyKey and !pedal() and mute()
  , anyKey++
  , m := keyToMidi(k)
  , savedMidi[m] and playNote(-m)
  , playNote(m)
  , savedKeys[k] := octaveIndex
  , savedMidi[m] := m
  , guiUpdate()
}

keyRelease(k) {
  pressedKeys[k] := 0
  if !(anyKey and savedKeys.HasKey(k))
    return
  anyKey--
  if isSustain or pedal()
    return
  m := keyToMidi(k, savedKeys[k])
  , playNote(-m)
  , savedKeys.delete(k)
  , savedMidi.delete(m)
  , guiUpdate()
}

mute(noStrum:=1) {
  critical -1
  for m in savedMidi
    playNote(-m), noStrum or playNote(m)
  (noStrum) and (anyKey := 0, savedKeys := {}, savedMidi := {})
  , guiUpdate()
}

bend(semitones, value:=0, ms:=1) {
  critical -1
  static middle
  middle := 0
  , semi := abs(semitones)
  , limit := round(100 / (A_ThisHotkey = "BS" ? 2 : bendRange) * semitones)
  , step := limit / (bendRange = semi or bendRange * semi = 12 ? 25 : 20)
  , value and value := limit
  if step > 0
    while !middle and pitch() < value
      pitch(step), DllCall("Sleep", "UInt", 2 * ms)
  else
    while !middle and pitch() > value
      pitch(step), DllCall("Sleep", "UInt", 2 * ms)
  (value) or (pitch(0), middle := 1)
}

pitch(value:="") {
  static savedPitch := 0
  if value is number
    savedPitch := !value ? 0 : savedPitch + value
    , savedPitch := savedPitch < -100 ? -100 : savedPitch > 100 ? 100 : savedPitch
    , newPitch -= (newPitch := (100 + savedPitch) / 200 * 0x4000) = 0x4000
    , midiSend(0xE0, newPitch & 0x7F, (newPitch >> 7) & 0x7F)
  return savedPitch
}

pedal() {
  return !isBends and (GetKeyState("LShift","P") or GetKeyState("Space","P"))
}

playNote(midi) {
  lastVelocity := !isBends or !GetKeyState("Space","P") ? velocity : lowVelocity
  , midiSend(0x90, abs(midi), (midi > 0) * lastVelocity)
  , isPalmMute := isBends and velocity != lastVelocity
}

setCC(value:=0) {
  ccValue += value
  , ccValue := ccValue < 0 ? 0 : ccValue > 127 ? 127 : ccValue
  , midiSend(0xB0, ccNumber, ccValue)
}

midiSend(command, data1, data2:=0) {
  if WinActive(guiWindow)
    DllCall("winmm\midiOutShortMsg", "UInt", devOut, "UInt", command + midiChannel | data1 << 8 | data2 << 16)
}

midiInput(hInput, midiMsg, wMsg) {
  if WinActive(guiWindow)
    return
  status := midiMsg & 0xF0
  , data1 := (midiMsg >> 8) & 0xFF
  , data2 := (midiMsg >> 16) & 0xFF
  if status between 128 and 159
    key := 1 - (firstNote - data1) - 12 * octaveIndex
    , data2 and status = 0x90 ? keyPress(key) : keyRelease(key)
  else if (status = 0xB0 and data1 = 0x7B)
    mute()
}

octaveShift(value:=0) {
  octaveIndex += value > 0 ? octaveIndex < 3 : value < 0 ? -(octaveIndex > -2) : octaveIndex := 0
  , updateInfo()
}

keyToMidi(key, octave:=7) {
  return firstNote - 1 + key + 12 * (octave = 7 ? octaveIndex : octave)
}

noteName(midi, withOctave:=1) {
  static b := chr(0x266D), # := chr(0x266F), notes := ["C","C"#,"D","E"b,"E","F","F"#,"G","A"b,"A","B"b,"B"]
  return notes[mod(midi, 12) + 1] (withOctave ? midi // 12 - 1 : "")
}

guiUpdate() {
  loop % buttonsAmount
    rowNumber := mod(A_Index - 1, 3)
    , row%rowNumber% .= chr(0x26AA + savedKeys.HasKey(A_Index))
  for i, midi in savedMidi, codes := [], redundantCodes := {} {
    for j, nextMidi in savedMidi
      (j > i) and mod(nextMidi, 12) = mod(midi, 12) and redundantCodes[nextMidi] := 1
    (redundantCodes[midi]) or codes.push(midi)
  }
  chordLength := codes.count()
  , letter := codes.1
  loop % chordLength {
    chordVariant := getChord(codes)
    , chord .= "`n" chordVariant.1 (!chordVariant.2 and codes.1 != letter ? "/" noteName(letter, 0) : "")
    , transposedNote := codes.RemoveAt(1)
    while transposedNote <= codes[chordLength - 1]
      transposedNote += 12
    codes.push(transposedNote)
  }
  GuiControl,, guiText, % row0 "`n " row1 "`n   " row2 chord
}

getChord(ByRef codes) {
  static degrees := ["n1","b2","n2","b3","n3","n4","b5","n5","b6","n6","b7","n7"]
  , b := chr(0x266D), # := chr(0x266F), hd := chr(0xF8), d := chr(0x2070)
  for _, midi in codes
    deg := degrees[mod(abs(midi - codes.1), 12) + 1], %deg% := 1
  mi3     := b3 and !n3
  , ma3   := !b3 and n3
  , no3   := !b3 and !n3 and n5
  , aug   := ma3 and b6 and !n5
  , dim   := mi3 and b5 and !n5 and !n7
  , dim7  := dim and n6
  , hdim  := dim and !n6 and b7
  , sus2  := no3 and n2 and !n4
  , sus4  := no3 and !n2 and n4
  , is6   := !dim and n6
  , is7   := !dim and (b7 or n7)
  , is9   := !sus2 and n2
  , is11  := !sus4 and n4
  , b9    := b2 ? "(" b "9)" : ""
  , n9    := !is7 and is9 ? "(9)" : ""
  , s9    := b3 and n3 ? "(" # "9)" : ""
  , n11   := !is7 and is11 ? "(11)" : ""
  , s11   := !dim and b5 ? "(" (n5 ? # 11 : b 5) ")" : ""
  , b13   := !aug and b6 ? "(" (n5 ? b 13 : # 5) ")" : ""
  , root  := noteName(codes.1, 0)
  , type  := no3 and codes.count() = 2 ? 5 : aug ? "+" : hdim ? hd : dim7 ? d 7 : dim ? d : mi3 ? "m" : ""
  , six   := !is7 and is6 ? 6 : ""
  , maj   := is7 and !b7 ? "maj" : ""
  , dom   := is7 ? is6 ? 13 : is11 ? 11 : is9 ? 9 : 7 : ""
  , sus   := sus2 ? "sus2" : sus4 ? "sus4" : ""
  , add   := StrReplace(b9 n9 s9 n11 s11 b13, ")(", ", ", addCounter)
  , chord := root type six maj dom sus (add ? " " add : "")
  for _, deg in degrees
    %deg% := 0
  return [chord, !!add + addCounter]
}

updateInfo() {
  WinSetTitle % noteName(firstNote) " (" octaveIndex + 2 "-" octaveIndex + 5 "), vel " velocity ", ch " midiChannel + 1
  SB_SetParts(100, 100)
  SB_SetText("sustain " (isSustain ? "ON" : "OFF"))
  SB_SetText("bends " (isBends ? "ON" : "OFF"), 2)
}

guiClose() {
  ExitApp
}

exit() {
  OnMessage(0x3C1, ""), OnMessage(0x3C2, ""), OnMessage(0x3C3, "")
  DllCall("winmm\midiInStop", "UInt", devIn)
  DllCall("winmm\midiInClose", "UInt", devIn)
  DllCall("winmm\midiOutReset", "UInt", devOut)
  DllCall("winmm\midiOutClose", "UInt", devOut)
  DllCall("FreeLibrary", "UPtr", winmm)
  ExitApp
}

Return to “Scripts and Functions (v1)”

Who is online

Users browsing this forum: gwarble and 112 guests