AutoHotkey Community

It is currently May 27th, 2012, 1:00 pm

All times are UTC [ DST ]




Post new topic Reply to topic  [ 5 posts ] 
Author Message
PostPosted: October 25th, 2011, 2:51 am 
Offline

Joined: June 11th, 2010, 2:24 am
Posts: 78
Task: Change system volume via a Midi controller (I use the BCF2000 with physical sliders)


Edited: 10/27/2011: Added Gui that starts if there is an error or via tray menu

Requires:

Audio control functions from http://www.autohotkey.com/forum/topic23792.html
com from http://www.autohotkey.com/forum/viewtopic.php?t=22923 (not needed for AHK_L)


Use

Make sure you have VA.ahk and com.ahk present to include (s.a.)

Use a Midi controller to set the system volume. On startup, checks whether device is present. If not, opens GUI to set device. Once device is set, you can set Channel, and CC number. The GUI has a "listen" mode - it sets the channel and CC to a control that's moved on the device (once the device is selected)

To-do:
- explore how to set other specifics, like Mike sensitivity etc

Input welcome


Code:
/*

 setting system volume via Midi
 by dorfl68
 for AHK basic
 
 should work with AHK_L if certain lines below are removed as marked (not tested)
 
 uses Audio control functions from http://www.autohotkey.com/forum/topic23792.html
 uses com from  http://www.autohotkey.com/forum/viewtopic.php?t=22923
  */



#singleinstance force


#include com.ahk ; delete this for AHK_l (I think) ; download from http://www.autohotkey.com/forum/viewtopic.php?t=22923
com_init() ; delete this for AHK_l (I think)

#include va.ahk ; download from http://www.autohotkey.com/forum/topic23792.html

menu, tray, add, Select Midi Device and Channel,gui   

Iniread,watched_channel,midi_system_volume.ini,settings,watched_channel,1
Iniread,watched_cc,midi_system_volume.ini,settings,watched_cc,1
Iniread,watched_device,midi_system_volume.ini,settings,watched_device,0
Iniread,start_gui,midi_system_volume.ini,settings,start_gui,0


in_midi_gui := 0 ; set to 1 if in the GUI so we can detect this in the Midi in routine


start_midi()




if start_gui
     open_gui(2)
    
    
return


start_midi()
  {
    global watched_channel, watched_cc, watched_device
   midi_error = 0 ; midi error count
    DeviceID := watched_device ; hardcoded for POC
                 ;replace with a read from ini, set by a GUI, all that good stuff
   
   
    MidiDLL := DllCall("LoadLibrary",Str,"winmm.dll")  ; load midi DLL
   
    ; create the Midi Gui to track what's coming in
    Gui, 99:Destroy ; better safe than error
    Gui, 99: +LastFound +AlwaysOnTop +Caption +ToolWindow  ; +ToolWindow avoids a taskbar button and an alt-tab menu item.
   
   hWnd := WinExist()
    CALLBACK_WINDOW := 0x10000    ; from orbiks code for midi input

    _DBMessage("Starting Midi in for controller, device ID#" . DeviceID,a_thisfunc)
   h_MidiIn =VarSetCapacity(h_MidiIn, 4, 0)
    result := DllCall("winmm.dll\midiInOpen", UInt,&h_MidiIn, UInt,watched_device, UInt,hWnd, UInt,0, UInt,CALLBACK_WINDOW, "UInt")
   If result
      {
     _DBMessage("Error in MidiInOpen DLL:" . result,a_thisfunc)
      midi_error++
      }
      
   h_MidiIn := NumGet(h_MidiIn) ; because midiInOpen writes the value in 32 bit binary Number, AHK stores it as a string
   result := DllCall("winmm.dll\midiInStart", UInt,h_MidiIn)
   If result
      {
     _DBMessage("Error in MidiInStart DLL:" . result,a_thisfunc)
      midi_error++
      }
      
   If Midi_error ; some error occured
      Open_gui(1)
   
      
      
   ; check here for error
   OnMessage(0x3C1, "MidiMsgDetect")
   OnMessage(0x3C2, "MidiMsgDetect")
   OnMessage(0x3C3, "MidiMsgDetect")
   OnMessage(0x3C5, "MidiMsgDetect")
   OnMessage(0x3C6, "MidiMsgDetect")
   
   
      
    Return
  } ; start Midi function


/*
Gui to set an detect device/channel/cc
*/

gui:
{
open_gui(2)
return
}

open_gui(m)
{
  global watched_channel, watched_cc, watched_device,midi_in_selected_device, in_midi_gui , channel_handle,cc_handle
 
  Iniwrite,0 ,midi_system_volume.ini,settings,start_gui ; only once allowed
 
    if m= 1
     message:="Error Opening Device"
   if m=2
      message="Select Device, Channel, and CC"
      
    
    Midi_in_list := MidiInsList(numPorts) ; get list of Midi Device names
   midi_in_selected_device:= watched_device +1 ; increment by one for pull-down
   
   Gui, Font, S12 CDefault, Verdana
   
   if numports = 0
    {
    Gui, Add, Text, section, No Midi Devices found
    gui, add,button, xs section gByeBYe, Exit
    gui, add,button, Ys gGuiClose, Try Again
       
   
    }
   Else
    {   
       ; we actually have a device present
      Gui, Add, Text, section, %message%
      Gui, Add, Text, xs section, Device:
      Gui, Add, ComboBox, ys x100 Choose%Midi_in_selected_device% vMidi_in_selected_device AltSubmit, %Midi_in_list% ; --- midi in listing of ports
      if m=2
          {
            Gui, Add, Text, xs section, Channel:
            Gui, Add, edit, ys x100 w75 edit r1 readonly hwndchannel_handle,%watched_channel%
            gui, add, updown, vwatched_channel range0-127 , %watched_channel%
            in_midi_gui:=1 ; for CC detection
            gui, add,  checkbox, ys checked%in_midi_gui% vin_midi_gui gcheckChange, Listen for Control
            Gui, Add, Text, xs section, CC:
            Gui, Add, edit, ys x100 w75 edit r1 readonly hwndcc_handle,%watched_cc%
            gui, add, updown, vwatched_cc range0-127 , %watched_cc%
            gui, add,button, xs section gset_device, Use these settings
          }
      gui, add,button, xs section grestart_with_listen, Use Device and listen for channel
   
   }
   
   Gui, show
 
  return
 }
 
 
GuiClose:
guiEscape:
gui, destroy
reload
return

ByeBye:
ExitApp
 
restart_with_listen:
Set_device:
gui,submit
gui, destroy
watched_device := midi_in_selected_device-1 ; decrement by one for pull-down
Iniwrite,%watched_channel%,midi_system_volume.ini,settings,watched_channel
Iniwrite,%watched_cc%,midi_system_volume.ini,settings,watched_cc
Iniwrite,%watched_device% ,midi_system_volume.ini,settings,watched_device

if (a_thislabel = "Set_device")
  Iniwrite,0 ,midi_system_volume.ini,settings,start_gui
Else
  Iniwrite,1 ,midi_system_volume.ini,settings,start_gui

reload
return







checkChange:
gui, submit,nohide
return
 

MidiMsgDetect(hInput, midiMsg, wMsg)
 {
   global watched_channel, watched_cc, watched_device,channel_handle,cc_handle, in_midi_gui
  _statusbyte    :=  midiMsg & 0xFF         ; EXTRACT THE STATUS BYTE (WHAT KIND OF MIDI MESSAGE IS IT?)
  _chan       := (_statusbyte & 0x0f) + 1   ; WHAT MIDI CHANNEL IS THE MESSAGE ON?
  _cc       := (midiMsg >> 8) & 0xFF   ; THIS IS DATA1 VALUE = NOTE NUMBER OR CC NUMBER
  _midi_value       := (midiMsg >> 16) & 0xFF   ; DATA2 VALUE IS NOTE VELOCITY OR CC VALUE
  _DBMessage("Midi received- channel: " . _chan . ", statusbyte: 0x" . convert_base(_statusbyte,10,16) . ", data1: " . _cc . ", data2: " . _midi_value,A_ThisFunc)
  _DBMessage("Midi handle:" . hinput,a_thisfunc)

 /* proof of concept
  */
 
 
 
  If ((_statusbyte&240) = 176) ; check status byte for cc
    {
      _DBmessage("->CC identified - channel:" . _chan . ", control: " . _cc . ", value: " . _midi_value ,A_ThisFunc)
      
      if (in_midi_gui)
        {
         ; we are in the Gui. Instead of setting the volume, set the variables in the Gui to the channel and CC detected
         controlsettext,,%_chan%, ahk_id %channel_handle%
         controlsettext,,%_cc%, ahk_id %cc_handle%
            
        }
        Else
         {      
      
         ; test here what where why how
         ; test: check for channel 1 control 1
         if (_chan = watched_channel) and (_cc = watched_cc)
           {
           _volume := _midi_value * 100/127 ; scale from 7 bit midi to 0-100%
           VA_SetMasterVolume(_volume)
           _dbmessage("Volume set to " . _volume, a_thisfunc)
           }
       }
    }
Return
}



_DBmessage(message,func)
{
outputdebug,[%func%] %message%
return
}



convert_base(num,inputbase,outputbase)
; from the AHK boards
/*
base("100",2,10) ->100 binary to 4  decimal
base("10",10,2) ->10 decimal to 1010 binary
base("15",10,16) ->15 decimal to f hexadecimal
base("f",16,2) ->fdecimal to 1111 binary
*/
{
   VarSetCapacity(S,65,0)
   toDec := DllCall("msvcrt\_strtoui64", Str,num, Uint,0, Int,inputbase, "CDECL Int64")
   DllCall("msvcrt\_i64toa", Int64,toDec, Str,S, Int,outputbase)
  return, S
}









; Midi core functions



  ;========================================================================
;===========  core Midi functions from AHK boards                   =====
;============from orbik and lazslo and a few others                 ====
;=========== but adapted and edited by Dorfl                        =====
;========================================================================


MidiInsList(ByRef NumPorts)
  { ; Returns a "|"-separated list of midi output devices
    local List, MidiInCaps, PortName, result
    VarSetCapacity(MidiInCaps, 50, 0)
    VarSetCapacity(PortName, 32)                       ; PortNameSize 32

    NumPorts := DllCall("winmm.dll\midiInGetNumDevs") ; #midi output devices on system, First device ID = 0

    Loop %NumPorts%
      {
        result := DllCall("winmm.dll\midiInGetDevCapsA", UInt,A_Index-1, UInt,&MidiInCaps, UInt,50, UInt)
        If (result OR ErrorLevel) {
            List .= "|-Error-"
        Continue
      }
    DllCall("RtlMoveMemory", Str,PortName, UInt,&MidiInCaps+8, UInt,32) ; PortNameOffset 8, PortNameSize 32
    List .= "|" PortName
  }
Return SubStr(List,2)
  }

MidiInGetNumDevs()
  { ; Get number of midi output devices on system, first device has an ID of 0
    Return DllCall("winmm.dll\midiInGetNumDevs")
  }

 
  MidiInNameGet(uDeviceID = 0) { ; Get name of a midiOut device for a given ID

  ;MIDIOUTCAPS struct
  ;    WORD      wMid;
  ;    WORD      wPid;
  ;    MMVERSION vDriverVersion;
  ;    CHAR      szPname[MAXPNAMELEN];
  ;    WORD      wTechnology;
  ;    WORD      wVoices;
  ;    WORD      wNotes;
  ;    WORD      wChannelMask;
  ;    DWORD     dwSupport;

VarSetCapacity(MidiInCaps, 50, 0)  ; allows for szPname to be 32 bytes
OffsettoPortName := 8, PortNameSize := 32
result := DllCall("winmm.dll\midiInGetDevCapsA", UInt,uDeviceID, UInt,&MidiInCaps, UInt,50, UInt)

If (result OR ErrorLevel)

  {
   
    m = Error %result% (ErrorLevel = %ErrorLevel%) in retrieving the name of midi Input %uDeviceID%
   msgbox, %m%
   _DBmessage(m, a_thisfunc)
    Return -1
  }

VarSetCapacity(PortName, PortNameSize)
DllCall("RtlMoveMemory", Str,PortName, Uint,&MidiInCaps+OffsettoPortName, Uint,PortNameSize)
Return PortName
  }

MidiInsEnumerate()
  { ; Returns number of midi output devices, creates global array MidiOutPortName with their names
    local NumPorts, PortID
    MidiInPortName =
    NumPorts := MidiInGetNumDevs()

    Loop %NumPorts%
      {
        PortID := A_Index -1
        MidiInPortName%PortID% := MidiInNameGet(PortID)
      }
    Return NumPorts
  }


UInt@(ptr)
{
  Return *ptr | *(ptr+1) << 8 | *(ptr+2) << 16 | *(ptr+3) << 24
  }

PokeInt(p_value, p_address) { ; Windows 2000 and later
DllCall("ntdll\RtlFillMemoryUlong", UInt,p_address, UInt,4, UInt,p_value)
  }





_________________
Paddy For Lightroom!
Use AHK for keyboard mapping, Midi support, external keyboards, and more in Adobe Lightroom


Last edited by dorfl68 on October 27th, 2011, 11:25 pm, edited 1 time in total.

Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: October 25th, 2011, 3:55 pm 
I can't even test this thing, errors and links to other posts.
Put it together then upload.


Report this post
Top
  
Reply with quote  
 Post subject:
PostPosted: October 26th, 2011, 1:53 am 
Offline

Joined: June 11th, 2010, 2:24 am
Posts: 78
Nice, given that this works for me on two machines. I stated pretty clearly that it's a test script and that you need to two modules form other posts via the #include.

But I will indeed work on this and post a version with a GUI :-)

_________________
Paddy For Lightroom!
Use AHK for keyboard mapping, Midi support, external keyboards, and more in Adobe Lightroom


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: October 27th, 2011, 11:25 pm 
Offline

Joined: June 11th, 2010, 2:24 am
Posts: 78
Added Gui to the above

_________________
Paddy For Lightroom!
Use AHK for keyboard mapping, Midi support, external keyboards, and more in Adobe Lightroom


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: November 5th, 2011, 11:56 am 
Offline

Joined: January 10th, 2009, 1:34 pm
Posts: 135
Location: Virginia
Hey there - nice to see you still working on the midi!

I have not touched any ahk midi in a while, since you did the system exclusive stuff. Been playing guitar, instead!

So on my first read of this I was a bit confused.
"System Volume" - for some reason I started thinking about hard drives...

Then I re read - volume - like sound, computer sound card sound.... duh!

I have not tried it yet.
I'll try with my bcr - perhaps later today, if I get some time.

Thanks

_________________
KeyMce/GenMce - mackie emulator for pc keyboard/Convert your controller to mackie.
Midi I/O - Want to play with midi/ahk?


Report this post
Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 5 posts ] 

All times are UTC [ DST ]


Who is online

Users browsing this forum: Bon, sks and 20 guests


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

Search for:
Powered by phpBB® Forum Software © phpBB Group