Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

MIDI input library


  • Please log in to reply
90 replies to this topic
ToS
  • Guests
  • Last active:
  • Joined: --
Realtively simple solution to reading if your note is on or off is to check the value with:

if (getNoteOn(note, channel)==0) ...


If I was reading right, that was your problem.

automaticman
  • Members
  • 658 posts
  • Last active: Nov 20 2012 06:10 PM
  • Joined: 27 Oct 2006
I added "program change" events support to the midi input library, unfortunately I could not install Visual C++ 2008 Express Edition successfully here, in the middle of the installation I get an error message.

I am sending the new, modified source here, in the hope that someone might be so kind and compile it to a new .dll file. Thanks in advance.

Here are the two files:

midi_in.cpp:
#include <windows.h>
#include <stdio.h>
#include "midi_in.h"

#define DEVNAME_BUFFER_SIZE 100
#define DEVNAME_NONE "No input"

const int isBlackKey[12] = {0,1,0,1,0,0,1,0,1,0,1,0};

HMIDIIN hMidiIn = NULL;
HWND hTargetWindow = NULL;
int curDevID = -1;
char curDevName[DEVNAME_BUFFER_SIZE] = DEVNAME_NONE;
char namebuf[DEVNAME_BUFFER_SIZE];

// Every element of these arrays is a message
// number to send to the target window

UINT ruleNoteOn[128][16];
UINT ruleNoteOff[128][16];
UINT ruleCC[128][16];
UINT ruleWheel[16];
UINT ruleAT[16];
UINT ruleProgramChange[16];

int minControllerDataInterval=5;

int noteDown[128][16];
int ccValue[128][16];
int wheelValue[16];
int chanATValue[16];
int programchangeValue[16];


//#define DEBUG
#ifdef DEBUG
void debug_output(char* string, int value1, int value2) {
	char buffer[4000];
	sprintf(buffer, string, value1, value2);
	MessageBox(0,buffer,"debug output from midi_in.dll", MB_OK);
}
#endif
BOOL WINAPI DllMain( HANDLE hinstDLL, DWORD dwReason, LPVOID lpvReserved) {
	switch (dwReason) {
		case DLL_PROCESS_ATTACH:
			removeAllListeners();
			break;

		case DLL_PROCESS_DETACH:
			stop();
			close();
	}
	return TRUE;
}

void CALLBACK midiInProc(HMIDIIN handle, UINT wMsg, DWORD dwInstance, DWORD midi_message, DWORD midi_timestamp) {
	BYTE statusbyte, byte2, byte3;
	UINT outMsg;
	if (wMsg == MIM_DATA) {
		statusbyte = (midi_message & 0x0000FF);
		byte2      = (midi_message & 0x007F00)>>8;
		byte3      = (midi_message & 0x7F0000)>>16;
		if (statusbyte > 0xEF) return; // status >= 0xF0 are machine control codes, sysex etc..
		int channel = statusbyte & 0x0f;
#ifdef DEBUG
//		debug_output("callback function called\nmidi_message = %x\nbyte2 = %i", midi_message, byte2);
#endif

		switch (statusbyte & 0xf0) {
			// note off
			case 0x80:
case0x80: // goto statement is 10 lines down.

				noteDown[byte2][channel] = 0;
				if (outMsg = ruleNoteOff[byte2][channel])
					// wParam = note number, lParam = velocity
					PostMessage(hTargetWindow, outMsg, byte2, byte3);
				break;

			// note on
			case 0x90:
				if (byte3 == 0) goto case0x80; 
				noteDown[byte2][channel] = byte3;
				if (outMsg = ruleNoteOn[byte2][channel])
					// wParam = note number, lParam = velocity
					PostMessage(hTargetWindow, outMsg, byte2, byte3);
				break;

			// control change
			case 0xB0:
				ccValue[byte2][channel] = byte3;
				if (outMsg = ruleCC[byte2][channel])
					// wParam = cc number, lParam = cc value
					PostMessage(hTargetWindow, outMsg, byte2, byte3);
				break;

			// channel aftertouch
			case 0xD0:	
				chanATValue[channel] = byte2;
				if (outMsg = ruleAT[channel])
					// wParam = ChanAT value
					PostMessage(hTargetWindow, outMsg, byte2, 0);
				break;

			// program change
			case 0xC0:	
				programchangeValue[channel] = byte2;
				if (outMsg = ruleProgramChange[channel])
					// wParam = ProgramChange value
					PostMessage(hTargetWindow, outMsg, byte2, 0);
				break;

			// pitch wheel
			case 0xE0:	
				wheelValue[channel] = (byte3 << 7) | byte2;
				if (outMsg = ruleWheel[channel])
					// wParam = pitch wheel value
					PostMessage(hTargetWindow, outMsg, wheelValue[channel], 0);
				break;
		}
	}
	return;
}

extern "C" {
DLLEXPORT int open(HWND hWnd, int deviceID) {
	MMRESULT result;
	if (hWnd == NULL) 
		return 50;
	else hTargetWindow = hWnd;

	if (hMidiIn != NULL)
		return 60;

	if ((result = midiInOpen(&hMidiIn, deviceID, (DWORD_PTR)midiInProc, 0, CALLBACK_FUNCTION)) != MMSYSERR_NOERROR)
		return result;

	curDevID = deviceID;
	strcpy_s(curDevName, DEVNAME_BUFFER_SIZE, getDevName(curDevID));

	return 0;
}

DLLEXPORT int close() {
	if (hMidiIn) {
		MMRESULT result = midiInClose(hMidiIn);
		if (result)
			return result;
		else
			hMidiIn = NULL;
	}

	curDevID = -1;
	strcpy_s(curDevName, DEVNAME_BUFFER_SIZE, DEVNAME_NONE);

	return 0;
}
DLLEXPORT void stop() {
	if (hMidiIn)
		midiInStop(hMidiIn);
}
DLLEXPORT void start() {
#ifdef DEBUG
	debug_output("start() called\nhMidiIn = %x", (int)hMidiIn,0);
#endif
	if (hMidiIn)
		midiInStart(hMidiIn);
}



/*
  	listenNoteOnOff(rangeStart, rangeEnd, msgNumber, modeFlags=0x18, channel=0)
		
		wParam - note number
		lParam - velocity (0x00 - 0x7F)

		rangeStart, rangeEnd
			(0x00 - 0x7F)
	
		msgNumber
			the (first) message number to be captured with OnMessage()

		modeFlags (default = 0)
			& 0x01 - use increasing msgNumbers for subsequent notes, where every note increments msgNumber by either 1 or 2 (see flag 0x08)
				If disabled, NoteOffs make velocity=0
			& 0x02 - ignore black keys on note range
			& 0x04 - ignore white keys on note range

			(& 0x08) - sends NoteOffs to msgNumber 1 higher than the corresponding NoteOn (NoteOn vel=0 -> NoteOff)
				If disabled, send both NoteOn and NoteOff to same msgNumber, (NoteOff -> NoteOn vel=0)
			(& 0x10) - uses relative note numbering for wParam inside the range if flag 0x01 is not used

		channel
			if 0, listen to all channels (default), otherwise 1-16


		Returns:
			0 - success
			1 - bad note range
			2 - bad channel

*/
DLLEXPORT int listenNoteRange(int rangeStart, int rangeEnd, int modeFlags, int channel, int msgNumber) {
	int firstMsgNumber = msgNumber;
	if (rangeStart > 127 || rangeStart < 0 || rangeEnd > 127 || rangeEnd < 0 || rangeStart > rangeEnd)
		return -1;

	if (channel < 0 || channel > 16)
		return -2;

	if ((modeFlags & 0x06) == 0x06)
		return 0;

	int blackKeyCounter = rangeStart % 12;

	for (int currNote = rangeStart; currNote <= rangeEnd; currNote++, blackKeyCounter++) {
		if (blackKeyCounter > 11) blackKeyCounter -= 12;
		if ((modeFlags & 0x02) && isBlackKey[blackKeyCounter]) continue;
		else if ((modeFlags & 0x04) && !isBlackKey[blackKeyCounter]) continue;

		if (channel == 0) for (int i=0; i<16; i++) {
			ruleNoteOn[currNote][i] = msgNumber;
			ruleNoteOff[currNote][i] = msgNumber;
		}
		else {
			ruleNoteOn[currNote][channel-1] = msgNumber;
			ruleNoteOff[currNote][channel-1] = msgNumber;
		}

		if (modeFlags & 0x01) msgNumber++;
	}
	return msgNumber - firstMsgNumber + 1;
}

DLLEXPORT int listenNote(int noteNumber, int channel, int msgNumber) {
	if (noteNumber < 0 || noteNumber > 127)
		return -1;
	if (channel < 0 || channel > 16)
		return -2;

	if (channel == 0) for (int i=0; i<16; i++) {
		ruleNoteOn[noteNumber][i] =
		ruleNoteOff[noteNumber][i] = msgNumber;
	}
	else {
		ruleNoteOn[noteNumber][channel-1] =
		ruleNoteOff[noteNumber][channel-1] = msgNumber;
	}

	return 0;
}

DLLEXPORT int listenCC(int ccNumber, int channel, int msgNumber) {
	if (ccNumber < 0 || ccNumber > 127)
		return -1;
	if (channel < 0 || channel > 16)
		return -2;

	if (channel == 0) for (int i=0; i<16; i++)
		ruleCC[ccNumber][i] = msgNumber;
	else
		ruleCC[ccNumber][channel-1] = msgNumber;
	return 0;
}

DLLEXPORT int listenWheel(int channel, int msgNumber) {
	if (channel < 0 || channel > 16)
		return -2;

	if (channel == 0) for (int i=0; i<16; i++)
		ruleWheel[i] = msgNumber;
	else
		ruleWheel[channel-1] = msgNumber;
	return 0;
}                                                  

DLLEXPORT int listenChanAT(int channel, int msgNumber) {
	if (channel < 0 || channel > 16)
		return -2;

	if (channel == 0) for (int i=0; i<16; i++)
		ruleAT[i] = msgNumber;
	else
		ruleAT[channel-1] = msgNumber;
	return 0;
}

DLLEXPORT int listenProgramChange(int channel, int msgNumber) {
	if (channel < 0 || channel > 16)
		return -2;

	if (channel == 0) for (int i=0; i<16; i++)
		ruleProgramChange[i] = msgNumber;
	else
		ruleProgramChange[channel-1] = msgNumber;
	return 0;
}

	DLLEXPORT void removeAllListeners() {
		for (int j=0; j<16; j++) {
			for (int i=0; i<128; i++) {
				ruleNoteOn[i][j]=0;
				ruleNoteOff[i][j]=0;
				ruleCC[i][j]=0;
				noteDown[i][j]=0;
				ccValue[i][j]=0;
			}
			ruleWheel[j]=0;
			ruleAT[j]=0;
			wheelValue[j]=0;
			chanATValue[j]=0;
			programchangeValue[j]=0;
		}

	}

	DLLEXPORT void setMinInterval(int minInterval) {
		minControllerDataInterval = minInterval;
	}

	DLLEXPORT int getNoteOn(int noteNumber, int channel) {
		if (noteNumber < 0 || noteNumber > 127) return -1;
		if (channel < 1 || channel > 16) return -2;
		return noteDown[noteNumber][channel-1];
	}
	DLLEXPORT int getCC(int ccNumber, int channel) {
		if (ccNumber < 0 || ccNumber > 127) return -1;
		if (channel < 1 || channel > 16) return -2;
		return ccValue[ccNumber][channel-1];
	}
	DLLEXPORT int getWheel(int channel) {
		if (channel < 1 || channel > 16) return -2;
		return wheelValue[channel-1];
	}
	DLLEXPORT int getChanAT(int channel) {
		if (channel < 1 || channel > 16) return -2;
		return chanATValue[channel-1];
	}
	DLLEXPORT int getProgramChange(int channel) {
		if (channel < 1 || channel > 16) return -2;
		return programchangeValue[channel-1];
	}

	DLLEXPORT int getNumDevs() {
		return midiInGetNumDevs();
	}

	DLLEXPORT char* getDevName(int deviceID) {
		MIDIINCAPS caps;

		MMRESULT result = midiInGetDevCaps(deviceID, &caps, sizeof(caps));
		if (result != MMSYSERR_NOERROR)
			return NULL;
		
		strcpy_s(namebuf, DEVNAME_BUFFER_SIZE, caps.szPname);
		return namebuf;
	}

	DLLEXPORT char* getCurDevName() {
		return curDevName;
	}

	DLLEXPORT int getCurDevID() {
		return curDevID;
	}

}



midi_in.h:
#include <windows.h>

#define DLLEXPORT __declspec(dllexport)

extern "C" {
	DLLEXPORT int open(HWND targetWindowHandle, int deviceID);
	DLLEXPORT int close();
	DLLEXPORT void start();
	DLLEXPORT void stop();

	DLLEXPORT int getNumDevs();
	DLLEXPORT char* getDevName(int deviceID);
	DLLEXPORT char* getCurDevName();
	DLLEXPORT int getCurDevID();

	DLLEXPORT int listenNoteRange(int rangeStart, int rangeEnd, int modeFlags, int channel, int msgNumber);
	DLLEXPORT int listenNote(int noteNumber, int channel, int msgNumber);
	DLLEXPORT int listenCC(int ccNumber, int channel, int msgNumber);
	DLLEXPORT int listenWheel(int channel, int msgNumber);
	DLLEXPORT int listenChanAT(int channel, int msgNumber);
	DLLEXPORT int listenProgramChange(int channel, int msgNumber);

	DLLEXPORT void setMinInterval(int minInterval);

	DLLEXPORT void removeAllListeners();

	DLLEXPORT int getNoteOn(int noteNumber, int channel);
	DLLEXPORT int getCC(int ccNumber, int channel);
	DLLEXPORT int getWheel(int channel);
	DLLEXPORT int getChanAT(int channel);
	DLLEXPORT int getProgramChange(int channel);

}


arsan
  • Members
  • 105 posts
  • Last active: Jan 24 2011 06:54 AM
  • Joined: 02 Jan 2010
This is so useful. Thanks for sharing. :D

I modified one of the earlier posted scripts to play my midi keyboard alongwith MidiIllustrator. The "midi hotkey functions" work perfectly well. The problem is that after practicing for the session, when I want to close the script using the defined hotkey ^Esc, nothing happens. When I right-click on the tray icon, no menu shows up. The hotkey and right-clicking the tray icon work fine for sometime after starting the script but after a while they don't work at all! I usually have to close the script with Process Explorer.

This is the script I use:
#NoEnv
SendMode Input 
SetWorkingDir %A_ScriptDir%
Menu, TRAY, Icon, icon4.ico

OnExit, sub_exit 
if (midi_in_Open(0)) 
   ExitApp 

;--------------------  Midi "hotkey" mappings  ----------------------- 
listenNote(36, "note36")
listenNote(37, "note37")

return 
;----------------------End of auto execute section-------------------- 

sub_exit: 
	midi_in_Close() 
	ExitApp 

;-------------------------Miscellaneous hotkeys----------------------- 
^Esc:: ExitApp 

;-------------------------Midi "hotkey" functions--------------------- 
note36(note, vel)
{ 
	If (vel)
		Send ^{Home}		; move to the beginning of staff
} 

note37(note, vel) 
{ 
	If (vel)
		Send {Space}		; play/pause
} 

;-------------------------  Midi input library  ---------------------- 
#include midi_in_lib.ahk

Can anyone help me see what's happening?

automaticman
  • Members
  • 658 posts
  • Last active: Nov 20 2012 06:10 PM
  • Joined: 27 Oct 2006
arsan, use this part instead
sub_exit:
	midi_in_Stop()
	midi_in_Close() 
ExitApp
then it should close immediately, correctly.

arsan
  • Members
  • 105 posts
  • Last active: Jan 24 2011 06:54 AM
  • Joined: 02 Jan 2010
Thanks pal. :D I will try out your suggestion as soon as I reach home.

EDIT:
I followed the suggestion and now the code is:
#NoEnv
SendMode Input 
SetWorkingDir %A_ScriptDir%
Menu, TRAY, Icon, icon4.ico

OnExit, sub_exit 
if (midi_in_Open(0)) 
   ExitApp 

;--------------------  Midi "hotkey" mappings  ----------------------- 
listenNote(36, "note36")
listenNote(37, "note37")

return 
;----------------------End of auto execute section-------------------- 

[color=red]sub_exit:
   midi_in_Stop()
   midi_in_Close()
ExitApp[/color]

;-------------------------Miscellaneous hotkeys----------------------- 
^Esc:: ExitApp 

;-------------------------Midi "hotkey" functions--------------------- 
note36(note, vel)
{ 
	If (vel)
		Send ^{Home}		; move to the beginning of staff
} 

note37(note, vel) 
{ 
	If (vel)
		Send {Space}		; play/pause
} 

;-------------------------  Midi input library  ---------------------- 
#include midi_in_lib.ahk
But the behaviour is exactly the same as before. And I noticed that once I give the exit command by the specified hotkey or by the tray menu:
1. the script refuses to exit;
2. the right-click on the tray icon doesn't show the menu anymore; and
3. the script stops responding to the midi keyboard.

I then have to close the script with Process Explorer.

yomannnn
  • Members
  • 1 posts
  • Last active: May 24 2010 11:42 AM
  • Joined: 24 May 2010
Hi!
First of all I would like to thank everybody for the useful code ....

I have no problem to use the functions listenCC() and midiOutShortMsg() separately in my script. However when I want to convert a midi message to another midi message using both functions together I get the following error message: "There was an Error Sending the midi event (5,0)"

Any idea what the problem is?
Thanks in advance ....

#::midiOutShortMsg(h_midiout, "CC", 1, 1, 0xFF)  ; OK
listenCC(27, "TestBox", 0)    ; OK
listenCC(28, "TestMsg", 0)    ; Error
return


TestBox(Note,Vel)
{
	if (vel) ; vel == 0 means note off
   {
      MsgBox TestBox
   }
}
return

TestMsg(Note,Vel)
{
	if (vel) ; vel == 0 means note off
   {
	  midiOutShortMsg(h_midiout, "CC", 1, 1, 0xFF)
   }
}
return


Ivan68
  • Members
  • 13 posts
  • Last active: Dec 22 2010 05:59 PM
  • Joined: 15 Jun 2010

Thanks pal. :D I will try out your suggestion as soon as I reach home.

EDIT:
I followed the suggestion and now the code is:

#NoEnv
SendMode Input 
SetWorkingDir %A_ScriptDir%
Menu, TRAY, Icon, icon4.ico

OnExit, sub_exit 
if (midi_in_Open(0)) 
   ExitApp 

;--------------------  Midi "hotkey" mappings  ----------------------- 
listenNote(36, "note36")
listenNote(37, "note37")

return 
;----------------------End of auto execute section-------------------- 

[color=red]sub_exit:
   midi_in_Stop()
   midi_in_Close()
ExitApp[/color]

;-------------------------Miscellaneous hotkeys----------------------- 
^Esc:: ExitApp 

;-------------------------Midi "hotkey" functions--------------------- 
note36(note, vel)
{ 
	If (vel)
		Send ^{Home}		; move to the beginning of staff
} 

note37(note, vel) 
{ 
	If (vel)
		Send {Space}		; play/pause
} 

;-------------------------  Midi input library  ---------------------- 
#include midi_in_lib.ahk
But the behaviour is exactly the same as before. And I noticed that once I give the exit command by the specified hotkey or by the tray menu:
1. the script refuses to exit;
2. the right-click on the tray icon doesn't show the menu anymore; and
3. the script stops responding to the midi keyboard.

I then have to close the script with Process Explorer.


I have the same problem.

Also orbik's first script example (page 1) desn't close properly.

genmce
  • Members
  • 144 posts
  • Last active: May 21 2015 03:09 PM
  • Joined: 10 Jan 2009
Hey - I will try to post something today, that will care of port selection and midi cleanup on close as well as, provide a few examples of midi input with midi output translations.
I have plenty of working code, but need to boil it down to something legible.
BTW -
I am not using the midi_in.dll due to required dependencies (missing from some Os's). Also - why call a dll to call another dll if you can call the first directly.

I am using orbiks direct winmm.dll midi calls. It will require a different approach besides the listenXX functions. Once you get used to it is is not much different tho.
I will try to do this today, but will depend on my workload ...
I may start a new thread of midi I/O with examples for those, few, of us actually working on this.
At any rate, I'll try to clean things up and add legible comments.

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


genmce
  • Members
  • 144 posts
  • Last active: May 21 2015 03:09 PM
  • Joined: 10 Jan 2009
As promised.

<!-- m -->http://www.autohotke... ... 408#363408<!-- m -->

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


Kumpan
  • Members
  • 7 posts
  • Last active: Feb 19 2012 08:47 PM
  • Joined: 08 Dec 2010
Orbik, very much thanks for your cool library!

i already wrote in the german autohotkey forum but as my problem is a little specific to midi_in_lib i will post it here, too.

i want to code a function that emulates a pressed middle mousebutton as long as i hold down a note key on my midi keyboard and press the left mouse button.

Monitoring the midi key works fine but I am very new to autohotkey so i wonder were to add my query for GetKeyState(LButton) as it should also be ran in a loop while listenNoteRange is active...not?

;-------------------------  Midi input library  ---------------------- 
#include midi_in_lib.ahk

SendMode Input
SetTitleMatchMode, 2
SetWorkingDir, %A_ScriptDir%

listenNoteRange(0, 127, "capture_noteon", 0) 
 return

capture_noteon(note, vel)
{
	Velocity := getNoteOn(note, 16)	 


	if (note == 48 && Velocity != 0)		;  note C pressed
	{
			; something like nanoFL() function here?
			; if i place here a loop that does a GetKeyState(LButton), CPU load gets 50%...
			; I also cannot break this loop, beause i can't recognize when note key is releases (Velocity == 0)
	}

}


nanoFL()
{
	If GetKeyState("LButton","P")
		{
		MsgBox, you hold down midi key AND press LeftMouseBtn!
		}
	
	return
}


  • Guests
  • Last active:
  • Joined: --
You might take a look at

<!-- m -->http://www.autohotke... ... light=midi<!-- m -->

Kumpan
  • Members
  • 7 posts
  • Last active: Feb 19 2012 08:47 PM
  • Joined: 08 Dec 2010

You might take a look at

<!-- m -->http://www.autohotke... ... light=midi<!-- m -->


Sounds good, thanks! I will check deeply later today!

BlobVanDam
  • Members
  • 6 posts
  • Last active: Jun 04 2011 06:33 PM
  • Joined: 30 May 2011
Hi,
Sorry for bumping an old thread here, but I've just gotten this happily working in my current (time sensitive) project, but I'm having a few problems with the stability of this script. I'm having the problem that has been mentioned, that the script doesn't close properly. That's not a huge deal.

But the bigger problem is that after a little while of having the script active (maybe half an hour? maybe 15 minutes? Not quite sure), the script stops responding - ie, it stops converting/sending midi presses. This is a big problem for my current project, which requires very reliable midi input for extended periods without user intervention.


I took a look at the combined MIDI input/output thread linked above, but that thing is too overblown for me to understand as an autohotkey noob, or even know if it's designed to be used for something so simple as just taking midi input and converting to keystrokes.

Is there an updated version of this plugin that fixes these problems, or is there a stripped down version of the updated midi input/output script that can do the same job?
I could post my completed .AHK script if anyone wants to see the simple extent of what it does, or see if maybe there's something in my script causing the problem (although it's just a modified version of the demo script that it comes with).

Thanks for any help. :)

genmce
  • Members
  • 144 posts
  • Last active: May 21 2015 03:09 PM
  • Joined: 10 Jan 2009
Yeah - I feel your pain.

I will dig thru my archive and try to find a stripped down version.
It would help if you stated exactly what you need.
Can you deal with hard coding the midi input port?
Or do you need port selection and gui, ini file to remember your port?
Do you need a gui to know what midi input you have coming in or will you use some other program like midiox to obtain the midi info?

What type of midi data will you use the trigger the keystrokes?
CC, Notes, Program changes?
All of the above?
What else do you need.

The generic midi program will do what you want, however, I understand that there is a lot in there.

I have been meaning to create a small script for my fcb1010 to convert to keystrokes.

Reply with more info if you like.

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


BlobVanDam
  • Members
  • 6 posts
  • Last active: Jun 04 2011 06:33 PM
  • Joined: 30 May 2011
Thanks for the help. I appreciate it.
I will probably need some kind of basic selection for input port. Saving to ini isn't necessary, although it would be nice if it doesn't complicate it too much. I don't need any midi output support. I want to limit any extra necessary programs such as midiox for ease of setup, so there probably won't be any extra to help with midi routing.
It will be running on a fixed input channel. I will need to read note input, pitch bend, and possibly at least mod wheel (so some basic cc values). I'm not entirely sure how I'll be getting numerical values like ccs mapped and into my program yet (maybe through mouse since the user will only be using the midi keyboard and not the pc), so I don't think there will be much else required. Program change isn't needed. Notes and pitch bender are the only essential parameters.
All it really needs to do is read the notes and convert them to specific keypresses, and read the pitch bend and convert it to an input I can read. The only user input required is choosing an input somehow.
I hope that about covers it. :)