Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations gkittelson on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Winmm Midiin translate from VB to VFP 2

Status
Not open for further replies.

Jan Flikweert

Programmer
Mar 20, 2022
85
NL
Hi all,

I am working on a midiin solution using WINAPI winmm.dll. I mange to open,start and close midiinput. I do not manage to get response on midiinput messages, except that VFP crashes. Here I have the ost important parts of a VB example, hoping this will help.

I can provide the not fully working solution. For those who are interested I have a solution using MIDI-OX. The goal of my project is creating a Virtual Pipe Organ using REAPER (a digital audio workstation) and Garritan Aria audio player. Using MIDI-OX this works. Now with winmm.dll

Who can give me a help, a hint?

Code:
Public Declare Function midiInOpen Lib "winmm.dll" (ByRef hMidiIn As Integer, ByVal uDeviceID As Integer, ByVal dwCallback As MidiInCallback, ByVal dwInstance As Integer, ByVal dwFlags As Integer) As Integer
Public Delegate Function MidiInCallback(ByVal hMidiIn As Integer, ByVal wMsg As UInteger, ByVal dwInstance As Integer, ByVal dwParam1 As Integer, ByVal dwParam2 As Integer) As Integer
Public ptrCallback As New MidiInCallback(AddressOf MidiInProc)
Public Const CALLBACK_FUNCTION As Integer = &H30000
Public Const MIDI_IO_STATUS = &H20

Function MidiInProc(ByVal hMidiIn As Integer, ByVal wMsg As UInteger, ByVal dwInstance As Integer, ByVal dwParam1 As Integer, ByVal dwParam2 As Integer) As Integer
	If MonitorActive = True Then
		TextBox1.Invoke(New DisplayDataDelegate(AddressOf DisplayData), New Object() {dwParam1})
	End If
End Function

Private Sub ComboBox1_SelectedIndexChanged(sender As System.Object, e As System.EventArgs) Handles ComboBox1.SelectedIndexChanged
	ComboBox1.Enabled = False
	Dim DeviceID As Integer = ComboBox1.SelectedIndex
	midiInOpen(hMidiIn, DeviceID, ptrCallback, 0, CALLBACK_FUNCTION Or MIDI_IO_STATUS)
	midiInStart(hMidiIn)
	MonitorActive = True
	Button2.Text = "Stop monitor"
End Sub

And here the VFP code:
Code:
CLOSE ALL
CLEAR ALL

#DEFINE MAXPNAMELEN 32
#DEFINE MIDI_STATUS_PLAYNOTE 9
#DEFINE MIDI_STATUS_PATCH 12
#DEFINE CALLBACK_NULL 0
#DEFINE  CALLBACK_FUNCTION    0x30000        &&  dwCallback is a FARPROC
#DEFINE  CALLBACK_NULL    0x0                &&  no callback
#DEFINE  CALLBACK_TASK    0x20000            &&  dwCallback is a HTASK
#DEFINE  CALLBACK_TYPEMASK    0x70000        &&  callback type mask
#DEFINE  CALLBACK_WINDOW    0x10000          &&  dwCallback is a HWND
#DEFINE  MIM_CLOSE 962
#DEFINE  MIM_OPEN 961
#DEFINE  MIM_DATA 963
#DEFINE MIM_MOREDATA 972
#DEFINE MIM_LONGDATA 964
#DEFINE MIDI_IO_STATUS 32
#DEFINE MIDI_ERROR 965
#DEFINE MIDI_LONGERROR 966
#DEFINE MMSYSERR_ERROR 1
#DEFINE MMSYSERR_BADDEVICEID 2
#DEFINE MMSYSERR_NOTENABLED 3
#DEFINE MMSYSERR_ALLOCATED 4
#DEFINE MMSYSERR_INVALHANDLE 5
#DEFINE MMSYSERR_NODRIVER 6
#DEFINE MMSYSERR_NOMEM 7
#DEFINE MMSYSERR_NOTSUPPORTED 8
#DEFINE MMSYSERR_BADERRNUM 9
#DEFINE MMSYSERR_INVALFLAG 10
#DEFINE MMSYSERR_INVALPARAM 11
#DEFINE MMSYSERR_HANDLEBUSY 12
#DEFINE MMSYSERR_INVALIDALIAS 13
#DEFINE MMSYSERR_BADDB 14
#DEFINE MMSYSERR_KEYNOTFOUND 15
#DEFINE MMSYSERR_READERROR 16
#DEFINE MMSYSERR_WRITEERROR 17
#DEFINE MMSYSERR_DELETEERROR 18
#DEFINE MMSYSERR_VALNOTFOUND 19
#DEFINE MMSYSERR_NODRIVERCB 20
#DEFINE MMSYSERR_BASE  0
PUBLIC result AS LONG,HMIDIOUT AS LONG ,outHandle AS LONG, inHandle AS LONG, hDevice AS Long,Vrsltn,Hrsltn
Vrsltn=Sysmetric(2)
Hrsltn=Sysmetric(1)
_SCREEN.WIDTH=SYSMETRIC(1)
_SCREEN.HEIGHT=SYSMETRIC(2)
#INCLUDE vfp2c.h
SET LIBRARY TO vfp2c32.fll ADDITIVE 
DO declare
*PUBLIC nloopmidia,nCME

PUBLIC Cmeout
LOCAL nCount, nIndex, nBufsize, cBuffer
nCount = midiOutGetNumDevs()
FOR nIndex=0 TO nCount-1
	nBufsize = 1024
	cBuffer = REPLICATE(CHR(0), nBufsize)
	IF midiOutGetDevCaps(nIndex, @cBuffer, nBufsize) = 0
		LOCAL oMidiOutCaps As MIDIOUTCAPS
		oMidiOutCaps = CREATEOBJECT("MIDIOUTCAPS",@cBuffer)
		DO CASE
			CASE oMidiOutCaps.szPname=="CME U2MIDI" && Please replace with your midi input device
			nCmeout=nIndex+1
		ENDCASE
	ENDIF
NEXT

PUBLIC nCmein, HDMIIN
nCount = midiInGetNumDevs()
FOR nIndex=0 TO nCount-1
	nBufsize = 1024
	cBuffer = REPLICATE(CHR(0), nBufsize)
	IF midiInGetDevCaps(nIndex, @cBuffer, nBufsize) = 0
		LOCAL oMidiInCaps As MIDIINCAPS
		oMidiInCaps = CREATEOBJECT("MIDIINCAPS",@cBuffer)
		DO CASE
			CASE oMidiInCaps.szPname=="CME U2MIDI"
			nCmein=nIndex+1
		ENDCASE
	ENDIF
NEXT

PUBLIC loCallback,dwInstance,ptrCallback
PUBLIC iMsg,lParam1,lParam2
dwInstance=0
loCallBack = CREATEOBJECT('cls_callback')
ptrCallback=loCallBack.Address
nResult1 = midiInOpen(@hDevice,0, ptrCallback, 0, CALLBACK_FUNCTION + MIDI_IO_STATUS  )
MESSAGEBOX("Midi In Open: "+STR(nResult1))
nResult2 = midiInStart(hDevice)
MESSAGEBOX("Midi In Start:"+STR(nResult2))
nResult2 = midiInStart(hDevice)
*!*	test=.t.
*!*	DO WHILE test=.t.
*!*		ON KEY test=.f.
*!*		sleep(10)
*!*	ENDDO
READ EVENTS
MidiInStop(hDevice)
MidiInReset(hDevice)
midiInClose(hDevice)
loCallBack.Destroy
CLEAR EVENTS
*DEFINE CLASS cls_callback AS Exception
DEFINE CLASS cls_callback AS Session
	Address = 0
	Datasession=2
	FUNCTION Init
		THIS.Address = CreateCallbackFunc('Test_Callback_function','VOID','INTEGER,INTEGER,INTEGER,INTEGER,INTEGER',THIS)		
	ENDFUNC

	FUNCTION Destroy
		IF THIS.Address != 0
			DestroyCallbackFunc(THIS.Address)
		ENDIF
	ENDFUNC
	
	FUNCTION Test_Callback_function(hDevice,iMsg,dwInstance,lParam1,lParam2)
		DO CASE
			CASE hDevice>0
				_VFP.AutoYield = .F.
				MESSAGEBOX(STR(hDevice))
				_VFP.AutoYield = .T.
			CASE lParam1>0
				_VFP.AutoYield = .F.
				MESSAGEBOX(STR(lParam1))
				_VFP.AutoYield = .T.
			CASE lParam2>0
				_VFP.AutoYield = .F.
				MESSAGEBOX(STR(lParam2))				
				_VFP.AutoYield = .T.
			CASE iMsg=MIM_DATA
				_VFP.AutoYield = .F.
				MESSAGEBOX("Data: De callback functie werd gebruikt voor data input."+CHR(13)+STR(lParam1))
				_VFP.AutoYield = .T.
			CASE iMsg=MIM_OPEN
				_VFP.AutoYield = .F.
				MESSAGEBOX("Open: De callback functie werd gebruikt voor open input.")
				_VFP.AutoYield = .T.
			CASE iMsg=MIM_CLOSE
				_VFP.AutoYield = .F.
				MESSAGEBOX("Open: De callback functie werd gebruikt voor close input.")
				_VFP.AutoYield = .T.
			CASE iMsg=MIM_MOREDATA
				_VFP.AutoYield = .F.
				MESSAGEBOX("More: De callback functie werd gebruikt voor more input."+CHR(13)+STR(lParam1))
				_VFP.AutoYield = .F.
			CASE iMsg= MIM_LONGDATA				
				_VFP.AutoYield = .F.
				MESSAGEBOX("Data: De callback functie werd gebruikt voor long data input."+CHR(13)+STR(lParam1))
				_VFP.AutoYield = .T.
			CASE iMsg= MIDI_ERROR			
				_VFP.AutoYield = .F.
				MESSAGEBOX("Error")
				_VFP.AutoYield = .T.
			CASE iMsg= MIDI_LONGERROR			
				_VFP.AutoYield = .F.
				MESSAGEBOX("Error")
				_VFP.AutoYield = .T.
			OTHERWISE
				_VFP.AutoYield = .F.
				MESSAGEBOX("Other: "+STR(lParam1))
				_VFP.AutoYield = .T.
		ENDCASE
	ENDFUNC
ENDDEFINE

DEFINE CLASS MIDIINCAPS As Session
#DEFINE MAXPNAMELEN 32
	wMid=0
	wPid=0
	vDriverVersion=0
	szPname=""
	wTechnology=0
	wVoices=0
	wNotes=0
	wChannelMask=0
	dwSupport=0

PROCEDURE Init(cBuffer)
	THIS.wMid = buf2word(SUBSTR(cBuffer, 1, 2))
	THIS.wPid = buf2word(SUBSTR(cBuffer, 3, 2))
	THIS.vDriverVersion = buf2dword(SUBSTR(cBuffer, 5, 4))

	THIS.szPname = SUBSTR(cBuffer, 9, MAXPNAMELEN) + CHR(0)
	THIS.szPname = SUBSTR(THIS.szPname, 1, AT(CHR(0),THIS.szPname)-1)

	THIS.wTechnology = buf2word(SUBSTR(cBuffer, MAXPNAMELEN+9, 2))
	THIS.wVoices = buf2word(SUBSTR(cBuffer, MAXPNAMELEN+11, 2))
	THIS.wNotes = buf2word(SUBSTR(cBuffer, MAXPNAMELEN+13, 2))
	THIS.wChannelMask = buf2word(SUBSTR(cBuffer, MAXPNAMELEN+15, 2))
	THIS.dwSupport = buf2dword(SUBSTR(cBuffer, MAXPNAMELEN+17, 4))

ENDDEFINE

*
FUNCTION buf2dword(cBuffer)
RETURN Asc(SUBSTR(cBuffer, 1,1)) + ;
	BitLShift(Asc(SUBSTR(cBuffer, 2,1)),  8) +;
	BitLShift(Asc(SUBSTR(cBuffer, 3,1)), 16) +;
	BitLShift(Asc(SUBSTR(cBuffer, 4,1)), 24)

FUNCTION buf2word(lcBuffer)
RETURN Asc(SUBSTR(lcBuffer, 1,1)) + ;
       Asc(SUBSTR(lcBuffer, 2,1)) * 256  
       
PROCEDURE declare
Declare INTEGER midiInStart In Winmm integer hmi
Declare INTEGER midiInStop In Winmm integer hmi
Declare Integer midiOutGetNumDevs In Winmm
Declare INTEGER midiInGetNumDevs In Winmm
Declare Integer midiOutClose In Winmm Integer hmo
Declare INTEGER midiInClose In Winmm Integer hmi
Declare Integer midiOutReset In Winmm Integer hmo
Declare INTEGER midiInReset In Winmm integer hmi 
Declare Sleep In kernel32 Integer dwMilliseconds
DECLARE INTEGER midiOutGetDevCaps IN Winmm;
	INTEGER uDeviceID, STRING @lpMidiOutCaps,;
	INTEGER cbMidiOutCaps
Declare INTEGER midiInGetDevCaps In Winmm;
	INTEGER uDeviceID, STRING @lpMidiInCaps,;
	INTEGER cbMidiInCaps
Declare Integer midiOutOpen In Winmm;
	INTEGER @lphmo, Integer uDeviceID, Integer dwCallback,;
	INTEGER dwCallbackInstance, Integer dwFlags
Declare INTEGER midiInOpen In Winmm;
	INTEGER @hDevice, INTEGER nDevice,;
	INTEGER ptrCallback , INTEGER dwInstance, INTEGER dwFlags
Declare Integer midiOutShortMsg In Winmm;
	INTEGER hmo, Long dwMsg
Declare INTEGER midiInMessage In Winmm;
	INTEGER hmi, Long midiInMsg , Integer para1 , integer para2
RETURN

Kind regards,

Jan Flikweert
 
Chris,

I have an idea. Based on the VB script I think I found the next steps. I attached a pdf with a more readable version.
On this base I am going to search.
STEP ZERO
ptrCallback if defined in MidiInopen.
STEP ONE
I think I should define in VFP a class (VB Public Delegate Function):
MidiInCallback(hMidiIn as INTEGER,wMsg AS UNSIGNED INTEGER (so an INTEGER) ,dwInstance as INTEGER,dwParam1 as INTEGER,dwParam2 AS INTEGER) as integer
STEP TWO
Second I should create in VFP a Procedure/Function MidiInProc:
MidiInProc(hMidiIn,wMsg,dwInstance,dwPararm1,dwParam2)
display data using dwParam1
ENDFUNCTION
STEP THREE
Create in VFP a new object based on MidiInCallback(see step one) with addres to MidiInProc according step two. This should return the pointer to the new callback.
ptrCallback=CREATEOBJECT(“MidiInCallback”, address of MidiInProc)

Final remark: So we have two pointers: ptrCallback and Address of MidiInProc.

Kind regards,

Jan Flikweert
 
 https://files.engineering.com/getfile.aspx?folder=d1d4de68-7d93-4d85-94cc-8f62c8cb91b0&file=STEP_ZERO.pdf
Hi all,

An other approach is the next example:
Code:
#INCLUDE vfp2c.h
SET LIBRARY TO vfp2c32.fll ADDITIVE && Library with callback funtion
ptrCallback=_SCREEN.HWnd
hndl=BINDEVENTSEX(ptrCallback,[highlight #CC0000]WM_MESSAGE????[/highlight],NULL,'MidiInProc','[highlight #CC0000]param1????[/highlight]')
PROCEDURE MidiInProc(param1)
	MESSAGEBOX(STR(param1))
ENDPROC

There remain two questions regarding the highlighted parameters:
Which Windows Message constant is needed for [highlight #CC0000]WM_MESSAGE????[/highlight]
And what is the format of the resulting message [highlight #CC0000]param1????[/highlight].

Of course I am googling for both, but i can not find it. Ideas how and where to google are welcome.

Kind regards,


Jan Flikweert
 
Jan,

I'll dive into this with your samples and tip on the virtual midi device, but this has to wait.

I wonder why you're going for BINDEVENTSEX(), though. Wouldn't the native BINDEVENT do?

The documentation of MidiInCallback ( says MidiInProc is a placeholder for the application-supplied function name. It
s not a function you declare or call, it's just specified to know the parameter signature.

The topic says you'll have to react for windows messages "such as MIM_DATA". And when you go to that topic, the TOC on the left shows a list of messages that all stat with MIM. Well, there are more, all of which are Midi Messages:
Here are some of the message constants:
Code:
#define MIM_OPEN 0x3C1
#define MIM_CLOSE 0x3C2
#define MIM_DATA 0x3C3
#define MIM_LONGDATA 0x3C4
#define MIM_ERROR 0x3C5
#define MIM_LONGERROR 0x3C6

With BINEVENT to windows messages, the parameter signature must be the same as WindowProc, as the help says:
BindEvent help said:
When trapping for Windows Message (Win Msg) events, the cDelegate method must include a PARAMETERS statement to accept four parameters that get passed to the method. The format of the parameters is identical to the format of the Windows WindowProc function. See MSDN (the Microsoft Developer Network) for information about the Windows WindowProc function. The method must return an integer value.

And this means, as is shown in several samples about Windows event handling:
Code:
LPARAMETERS hWnd as Integer, Msg as Integer, wParam as Integer, lParam as Integer

The simples VFP method to have as delegate for handling windows messages would just store these into a DBF for processing them in a context that's not sensitive to blocking further events, etc., like something mentioned in much more simplified:

Code:
PUBLIC goMessageHandler
goMessageHandler = CreateObject("screenevents")
BINDEVENT(_Screen.hwnd, MIM_OPEN, goMessageHandler,'HandleEvent')
BINDEVENT(_Screen.hwnd, MIM_DATA, goMessageHandler,'HandleEvent')
...


Define class screenevents as Custom
 nOldProc = 0

 function Init
 declare integer GetWindowLong in Win32API ;
 integer hWnd, integer nIndex
 declare integer CallWindowProc in Win32API ;
 integer lpPrevWndFunc, integer hWnd, integer Msg, ;
 integer wParam, integer lParam

  This.nOldProc = GetWindowLong(_screen.hWnd, GWL_WNDPROC)
 endfunc

 function HandleEvent(hWnd, Msg, wParam, lParam)
   * store the message data minus hwnd, which will be _screen.hwnd and is therefore constant.
   insert into yourmesssagestable(Msg,wParam,lParam)

   return CallWindowProc(This.nOldProc, hWnd, Msg, wParam, lParam)
 endfunc
Enddefine

And then the hwnd you pass into midiInOpen must be _screen.hwnd so tht you tell the midi deice to send messages to the vfp screen.

As said at the start, I'll be diving into this later, including to see how the parameters map. Maybe it helps you already.

I don't see whether VFPs native BindEvent doesn't cover these messages and you need the BindeEvnetsEx function, which I assume is part of the FLL and covers cases VFPs BINDEVENT doesn't manage (therefore the Ex as Extended in its name). It should be documented in the FLL download, the likeliest place to find that on the internet is in the GitHub project, if it's not in the download as hlp file or readme.

Chriss
 
Chris,

I agree native Bindevents will be enough.

I can not express how deeply thankfull I am you are "diving" in to this. Please tell if you need me something to do.

The last code you provided in your last post I am going to study. I strongly believe that is the key to success.

I agree the MIM_OPEN and/or MIM_OPEN should be the second paramter in bindevent. I suppose using MIM_MOREDATA requery CALLBACK_WINDOW+MIDI_IO_STATUS.

Kind regards,


Jan Flikweert
 
Well, if you want to get all events related to midi, you'll simply bind to all midi messages. You may skip some that are exotic and/or don't occur, but in the first step I'd bind to all of them and see whether you get messages that result in data in a table, which I didn't define, but should be easy enough to construct to store the three integers for message, wparam and lparam.

The meaning of the data then can be investigated in detail in time, first just check whether telling the midi device via midiInOpen that you'd like to get feedback not by callbacks but by messages sent to _screen.

I can imagine nothing happening, as already said, open is just one starting point to do more. I don't know yet what to expect, as I have not yet dived in, it's one plan for the weekend, but also pardon me, if there'll be something else prioritized. If even waiting for any message to come back leads to nothing, then it's neither the point to give up on message nor on callbacks. As far as I can see from the little I know the callbacks provide more specific feedback, as their parameterization can vary. The pro for bindevent is that this can be done in VFP without any helper FLL as the callback mechanism is not used.

Chriss
 
Chriss,

[highlight #204A87][highlight #D3D7CF]YESSSS, That did it!!!!!!!!!!!![/highlight][/highlight]

I am very thankfull for your help. You did a great job.

This returns exact the data I need. This code returns f.e. 144 36 100 and 144 36 0. That is a simple note on/off.

I prefer this way using callback_window above callback_function. Probably ingteresting to solve callback_function to help other people.

The next thing I am going to do is use midiinopen twice because I have two midi input devices.

Code:
#DEFINE MIM_DATA 963
#DEFINE MIM_MOREDATA 972
#DEFINE MM_MIM_DATA 963
#DEFINE MM_MIM_MOREDATA 972
#DEFINE MIDI_IO_STATUS 32
#DEFINE CALLBACK_WINDOW 65536
#DEFINE GWL_WNDPROC -4

ptrCallback=_Screen.hwnd
goMessageHandler = CreateObject("screenevents")
BINDEVENT(_Screen.hwnd, MIM_DATA, goMessageHandler,'HandleEvent')

nResult_midi_open = midiInOpen(@hDevice,nCme,ptrCallback, 0,CALLBACK_WINDOW+MIDI_IO_STATUS)

Define class screenevents as Custom
 nOldProc = 0

 function Init
 declare integer GetWindowLong in Win32API ;
 integer hWnd, integer nIndex
 declare integer CallWindowProc in Win32API ;
 integer lpPrevWndFunc, integer hWnd, integer Msg, ;
 integer wParam, integer lParam

  This.nOldProc = GetWindowLong(_screen.hWnd, GWL_WNDPROC)
 endfunc

 function HandleEvent(hWnd, Msg, wParam, lParam)
   * store the message data minus hwnd, which will be _screen.hwnd and is therefore constant.
   			PUBLIC statust,dat1,dat2
			statust=STR(BITAND(lParam,255))
			dat1=STR(BITRSHIFT(BITAND(lParam,65280),8))
			dat2=STR(BITRSHIFT(BITAND(lParam,16711680),16))
			BEGIN TRANSACTION
			SELECT ymt
			APPEND BLANK IN ymt
			REPLACE stts WITH statust IN ymt
			REPLACE dt1 WITH dat1 IN ymt
			REPLACE dt2 WITH dat2 IN ymt	
			END TRANSACTION	
   return CallWindowProc(This.nOldProc, hWnd, Msg, wParam, lParam)
 endfunc
Enddefine

Kind regards,


Jan Flikweert
 
Good to hear that this works.

I think there could be more done, when you get MM_LONGDATA. Without looking at the docs, just by the name this would mean more data than just two integers, so I guess that would be an adress to memory containing data and then you might use SYS(2600) (see help) but ma also get the job to deallocate memory, just in the same way as the mechanism of WM_COPYDATA works.

Chriss
 
Chris,

Chris Miller said:
I think there could be more done, when you get MM_LONGDATA.

Theoretical MM_LONGDATA can do more. It is used to process SYSEX messages. For my purpose I do not use these SYSEX messages.

Kind regards,

Jan Flikweert
 
Hi all,

The nice thing is you can process midi in messages and connect the same in port with any midi out trough port.

Code:
nResult = midiInOpen(@hDevice_In, nMpIn-1, ptrCallback_mpl, 0, CALLBACK_WINDOW+MIDI_IO_STATUS)
nResult = midiOutOpen(@hDevice_Out, nMpOut-1, 0, 0, CALLBACK_NULL)
nResult=MidiConnect(hDevice_In,hDevice_Out,0)
ptrCallback_mpl=Main.CallBackWindow_CME.HWnd
BindEvent(ptrCallback_mpl, MIM_DATA,Main.CallBackWindow_CME.mpl,'handlevent')

Kind regards,

Jan Flikweert
 
Jan,

nice to see you're having fun with all this.
I assume routing midi out of one device to the next means you could play two midi instruments through one. Right?

Chriss
 
Chriss,

Yes, you can play one midi device using the other. This is not so spectacular because programs like REAPER, MIDIOX can do the same.

More spectacular is I have 153 different organ stops, 153 different sounds in REAPER which I can play. And the benefit is I do not need to process note on/off in my program but connect that part and thechnicly said I use midiin callback to process the messages from the expression pedal to crescendo/dynamics.

In the mean time I tested the organ with heavy duty and it works fine.

To give an impression of the heavy duty of midi: I have 10 fingers and two feet wich I almost all can use to play notes. That notes can be sent to 153 instruments divided bout 3 manuals and one foot-pedal. They can be coupled to each other. And not all organ music is slow.[thumbsup] So much notes in Toccata style is heave duty.

Kind regards,

Jan Flikweert
 
I remember when mp3 became a thing someone argued there already was a much better format called MOD that is something like a hybrid of MIDI and WAV. Actually I think in short it can be described as midi with sets of wav samples of your own choice. And I bet that's a midi thing anyway.

has a lot of them but mostly they sound like a below average home synthesizer. Yet they surely are very small file sizes.

Chriss
 
Chris,

My digital organ has for each stop and each key a small recording of a real church organ which runs in a loop. We call that a soundfont, like a character font. I recorded this and did some modifications. And now my computer processes the sound,reverb/chorus.

Kind regards,


Jan Flikweert
 
Soundfont, I see, I thought of instrument libraries.

The quality of the recordngs does increase the quality of how MIDI sounds. My first soundcard by Creative in the 90s had Midi and the sounds it had were still mostly not real instrument recordings, but synthesized. From a little bit of piano playing I know the same note can be played in a lot of different ways, too. By how you handle the key and the pedals. So a good soundfont has all these ways of playing a note, doesn't it? Or is one sample modulated. You can surely easily alter the sound volume.

Chriss
 
Chris,

Indeed the synthesized can sound nice, but not real. Do not forget you can record each instrument, piano, organ, guitar,trumpet, violin etc. and create a soundfont. Depending on the quality of sound processing this can be amazing good and real.

Chris Miller said:
My first soundcard by Creative in the 90s had Midi and the sounds it had were still mostly not real instrument recordings, but synthesized.

There are recordings of great pianos with for each key and each way of playing a different sample. For other instruments too. Church Organ is easy. There are two different ways of playing: how quick do you press a key and how quick do you release it. The way a organ sounds at the beginning and the end is the same. You can record the reverb or add it.

Chris Miller said:
I know the same note can be played in a lot of different ways, too. By how you handle the key and the pedals.

Kind regards,

Jan Flikweert
 
Hello Jan,

I found you on Youtube, by the way.

That's 6 years old, and maybe before any VFP experiments in MIDI, but now it's fun to know VFP is working here in some manner in the background.
Coming weekend I'll have more time to spend on the topic as it's quite interesting.

Have a nice weekend


Chriss
 
Chris,

Yes VFP works partly on the background. On the foreground VFP has a screen to toggle stops on/off, choose/store user defined(up to 30) combinations for each user defined piece of music, tremelo,dynamics and coupling manuals. The youtube link you refer to is indeed recorded before VFP experiments using jOrgan. A Java based solution. It is open source. I do not want to depend on open source with all it's risks.

Chris Miller said:
That's 6 years old, and maybe before any VFP experiments in MIDI, but now it's fun to know VFP is working here in some manner in the background.

I am glad you like to look at the subject. I am also interested in the callback_function option. I have much spare time: so if you give me ideas how to perform experiments with callback_function I will do that.

Kind regards,

Jan Flikweert
 
Chris,

I attached a print screen of the VFP console. At the moment all works fine.
vfp_organ_console_weosqg.jpg


Kind regards,

Jan Flikweert
 
 https://files.engineering.com/getfile.aspx?folder=fed695fd-7378-4ebc-a6e8-17976282214d&file=vfp_organ_console.jpg
Just for sake of having an example: The vfp2c32examples.pjx comes with a simple callback example to make use of Windows API EnumWindows in ccallback.prg.

This only uses CreateCallbackFunc and DestroyCallbackFunc, not BindEventsEx(). It's not MIDI related but shows how this can be used for anything requiring a callback function. As far as I know the FLL generates a simple C callback function at the actual address given that actually calls back so the FLL can forward that callback to a VFP function you give, receives your return value and returns that into the C world, so the FLL is a moderator between C and VFP.

It helps to open up this ccallback.prg to follow the description:

The general idea of a callback is that a function you call doesn't simple return one return value as a usual function does, but instead you tell it what call to make to return something, and the advantage over a more complex return object that could have multiple return value is, that this callback can also be made multiple times.

In the example, for every existing Window EnumWindows makes a callback. And in this case the callback function has a prototype of BOOL CALLBACK EnumWindowsProc(HWND hwnd,LPARAM lParam). That's just so for EnumWindows, it isn't generally valid for any callback, so that's what needs to be adapted in the parameters of CreateCallbackFunc following the VFP name of a method: That is the function type (BOOL) and the parameter signature 'LONG, LONG', in detail (HWND, LPARAM), but you only can use the simple types here, like BOOL, LONG, FLOAT, STRING, etc.

On the FoxPro side all you need to have is that the method given by its VFP name also has (LONG,LONG) parameters and also returns a bool. That way everything can be "wired" from the C world over the FLL that bridges C and VFP to your VFP code and then back to the FLL and C.

As VFP is not strictly typed what only matters for the parameter signature 'LONG, LONG' is that your method has 2 parameters, and what comes passed in will be two LONG values aka integers. And the VFP method also needs to return a BOOL (in the PRG it returns .T., the VFP logical type is totally okay for this).

When I will play with the midiInOpen function as initially called function, we already know the fdwOpen parameter allows to mix flags for several callback options, and that's the detail to work on. In case of fdwOpen=CALLBACK_FUNCTION I guess the callback function has the signature 'VOID', 'LONG,LONG,LONG,LONG,LONG'.

We'll see in detail later, more likely Sunday.

Chriss
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top