Recent

Author Topic: Midi Sequencer, conversion from Delphii, errors...  (Read 6136 times)

u2o

  • Jr. Member
  • **
  • Posts: 72
  • No message
Midi Sequencer, conversion from Delphii, errors...
« on: July 20, 2018, 01:57:24 am »
Hi!

I'm trying to convert Midi Sequencer from Delphi, to Lazarus...

  Project site: https://sourceforge.net/projects/midisequencer/

After some modifications, it's compiled...

Replaced functions
Code: Pascal  [Select][+][-]
  1. WideStrings() to WideString()

Adding Units
Code: Pascal  [Select][+][-]
  1. uses
  2.    Windows ...

But, I don't undestand one error... "RunError(127)"...

It's produced when is executed AllocateHwnd and DeallocateHwnd...


Code from MSC_Out_Device.pas

Code: Pascal  [Select][+][-]
  1. unit MSC_Out_Device;
  2.  
  3. {$MODE Delphi}      
  4.  
  5. type
  6.   TMIDI_Device_Output = class (TMIDI_Device)
  7.     protected
  8.       procedure MidiOutput (var Message: TMessage);  
  9.     public
  10.       constructor Create (AOwner:TComponent); override;
  11.       destructor Destroy; override;  
  12. end;
  13.  
  14. procedure Register;
  15.  
  16. {-------------------------------------------------------------------}
  17. implementation
  18.  
  19. {-------------------------------------------------------------------}
  20. procedure Register;
  21. begin
  22.    RegisterComponents (MSC_Package_Name, [TMIDI_Device_Output]);
  23. end; // Register
  24.  
  25. {-------------------------------------------------------------------}
  26. constructor TMIDI_Device_Output.Create(AOwner:TComponent);  
  27. begin
  28.    inherited Create(AOwner);
  29.  
  30.    FState   := mosClosed;
  31.    FNumdevs := midiOutGetNumDevs;
  32.  
  33.    { Create the window for callback notification }
  34.    if not (csDesigning in ComponentState) then
  35.    begin
  36.       Handle := AllocateHwnd(MidiOutput);
  37.    end; // if
  38.  
  39.  
  40. end; // Create //
  41.  
  42. {-------------------------------------------------------------------}
  43. destructor TMIDI_Device_Output.Destroy;
  44. begin
  45.    if FState = mosOpen then Close;
  46.    if (PCtlInfo <> Nil) then GlobalSharedLockedFree( PCtlinfo^.hMem, PCtlInfo );
  47.  
  48.    DeallocateHwnd(Handle);
  49.  
  50.    inherited Destroy;
  51. end; // Destroy //  
  52.  
  53. {-------------------------------------------------------------------}
  54. procedure TMIDI_Device_Output.midioutput (var Message: TMessage);
  55. { Triggered when sysex output from PutLong is complete }
  56. var MyMidiHdr: TMyMidiHdr;
  57.     thisHdr: PMidiHdr;
  58. begin
  59.  
  60.    if Message.Msg = Mom_Done then
  61.       begin
  62.       { Find the MIDIHDR we used for the output. Message.lParam is its address }
  63.       thisHdr := PMidiHdr(Message.lParam);
  64.  
  65.       { Remove it from the output device }
  66.       midiOutUnprepareHeader(FMidiHandle, thisHdr, sizeof(TMIDIHDR));
  67.  
  68.       { Get the address of the MyMidiHdr object containing this MIDIHDR structure.
  69.          We stored this address in the PutLong procedure }
  70.       MyMidiHdr := TMyMidiHdr(thisHdr^.dwUser);
  71.  
  72.       { Header and copy of sysex data no longer required since output is complete }
  73.       MyMidiHdr.Free;
  74.  
  75.  
  76.       { Call the user's event handler if any }
  77.       if Assigned(FOnmidioutput) then
  78.          FOnmidioutput(Self);
  79.       end;
  80. // there is no case for MOM_PLAYBACK_DONE
  81. end; // midioutput //        

The procedure
Code: Pascal  [Select][+][-]
  1. procedure TMIDI_Device_Output.midioutput (var Message: TMessage);

is not executed, fails on
Code: Pascal  [Select][+][-]
  1. Handle := AllocateHwnd(MidiOutput);

Can someone give me a hand?

Thaddy

  • Hero Member
  • *****
  • Posts: 14164
  • Probably until I exterminate Putin.
Re: Midi Sequencer, conversion from Delphii, errors...
« Reply #1 on: July 20, 2018, 09:17:26 am »
Try explicitly adding lclintf to the uses clause.
But I believe they are actually stubs.
« Last Edit: July 21, 2018, 09:01:39 am by Thaddy »
Specialize a type, not a var.

u2o

  • Jr. Member
  • **
  • Posts: 72
  • No message
Re: Midi Sequencer, conversion from Delphii, errors...
« Reply #2 on: July 21, 2018, 12:15:21 am »
Hi Thaddy!

lclintf is in the uses clause in all units.

I will keep trying to find the problem, Thanks you!

taazz

  • Hero Member
  • *****
  • Posts: 5368
Re: Midi Sequencer, conversion from Delphii, errors...
« Reply #3 on: July 21, 2018, 03:42:11 am »
Hi Thaddy!

lclintf is in the uses clause in all units.

I will keep trying to find the problem, Thanks you!
make sure that
1) lclintf comes after the unit classes in the uses clause. If lclintf is in the interface section and classes in the implementation either move both in the same section or qualify the call with lclintf eg
Code: Pascal  [Select][+][-]
  1. constructor TMIDI_Device_Output.Create(AOwner:TComponent);  
  2. begin
  3.    inherited Create(AOwner);
  4.  
  5.    FState   := mosClosed;
  6.    FNumdevs := midiOutGetNumDevs;
  7.  
  8.    { Create the window for callback notification }
  9.    if not (csDesigning in ComponentState) then
  10.    begin
  11.       Handle := lclintf.AllocateHwnd(MidiOutput);
  12.    end; // if
  13.  
  14.  
  15. end; // Create //
  16.  
2) depending on the mode you might have to qualify the callback method with the symbol "@" eg.
Code: Pascal  [Select][+][-]
  1. constructor TMIDI_Device_Output.Create(AOwner:TComponent);  
  2. begin
  3. ....
  4.       Handle := lclintf.AllocateHwnd(@MidiOutput);
  5. ..end; // Create //
  6.  

in any case please post the error message you get.

Good judgement is the result of experience … Experience is the result of bad judgement.

OS : Windows 7 64 bit
Laz: Lazarus 1.4.4 FPC 2.6.4 i386-win32-win32/win64

Thaddy

  • Hero Member
  • *****
  • Posts: 14164
  • Probably until I exterminate Putin.
Re: Midi Sequencer, conversion from Delphii, errors...
« Reply #4 on: July 21, 2018, 10:07:41 am »
They are stubs indeed, e.g. not a working implementation. AllocateHwnd always returns zero.
This has been a recurring issue for many years if you search the forum and the development lists.
For Windows it should not be too difficult to write one. X-platform is another issue.
It's main purpose is  message handling, so in Lazarus you can create a hidden form to get around it.
Specialize a type, not a var.

Thaddy

  • Hero Member
  • *****
  • Posts: 14164
  • Probably until I exterminate Putin.
Re: Midi Sequencer, conversion from Delphii, errors...
« Reply #5 on: July 21, 2018, 10:09:17 am »
Note KOL  has an implemention of AllocateHwnd. Windows 32 only.
I have knocked up some code based on that, but it is as yet untested. After testing:
Code: Pascal  [Select][+][-]
  1. unit winalloc;
  2. {$mode delphi}{$ifndef win32}{$error This unit is for 32 bit Windows only}{$endif}
  3. interface
  4.   uses windows, messages;
  5. type
  6.   TWndMethod = procedure(var Message: TMessage) of object;
  7.  
  8. function MakeObjectInstance(Method: TWndMethod): Pointer;
  9. procedure FreeObjectInstance(ObjectInstance: Pointer);
  10. function AllocateHWnd(Method: TWndMethod): HWND;
  11. procedure DeallocateHWnd(Wnd: HWND);
  12.  
  13. implementation
  14.  
  15. type
  16.   PObjectInstance = ^TObjectInstance;
  17.   TObjectInstance = packed record
  18.     Code: Byte;
  19.     Offset: Integer;
  20.     case Integer of
  21.       0: (Next: PObjectInstance);
  22.       1: (Method: TWndMethod);
  23.   end;
  24.  
  25. type
  26.   PInstanceBlock = ^TInstanceBlock;
  27.   TInstanceBlock = packed record
  28.     Next: PInstanceBlock;
  29.     Code: array[1..2] of Byte;
  30.     WndProcPtr: Pointer;
  31.     Instances: array[0..100] of TObjectInstance;
  32.   end;
  33.  
  34. var
  35.   InstBlockList: PInstanceBlock;
  36.   InstBlockCount: integer;
  37.   InstFreeList: PObjectInstance;
  38.  
  39. { Standard window procedure }
  40. { In    ECX = Address of method pointer }
  41. { Out   EAX = Result }
  42.  
  43. function StdWndProc(Window: HWND; Message, WParam: Longint;
  44.   LParam: Longint): Longint; stdcall; assembler;
  45. asm
  46.         XOR     EAX,EAX
  47.         PUSH    EAX
  48.         PUSH    LParam
  49.         PUSH    WParam
  50.         PUSH    Message
  51.         MOV     EDX,ESP
  52.         MOV     EAX,[ECX].Longint[4]
  53.         CALL    [ECX].Pointer
  54.         ADD     ESP,12
  55.         POP     EAX
  56. end;
  57.  
  58. { Allocate an object instance }
  59.  
  60. function CalcJmpOffset(Src, Dest: Pointer): Longint;
  61. begin
  62.   Result := Longint(Dest) - (Longint(Src) + 5);
  63. end;
  64.  
  65. function MakeObjectInstance(Method: TWndMethod): Pointer;
  66. const
  67.   BlockCode: array[1..2] of Byte = (
  68.     $59,       { POP ECX }
  69.     $E9);      { JMP StdWndProc }
  70.   PageSize = 4096;
  71. var
  72.   Block: PInstanceBlock;
  73.   Instance: PObjectInstance;
  74. begin
  75.   if InstFreeList = nil then
  76.   begin
  77.     Block := VirtualAlloc(nil, PageSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
  78.     Block^.Next := InstBlockList;
  79.     Move(BlockCode, Block^.Code, SizeOf(BlockCode));
  80.     Block^.WndProcPtr := Pointer(CalcJmpOffset(@Block^.Code[2], @StdWndProc));
  81.     Instance := @Block^.Instances;
  82.     repeat
  83.       Instance^.Code := $E8;  { CALL NEAR PTR Offset }
  84.       Instance^.Offset := CalcJmpOffset(Instance, @Block^.Code);
  85.       Instance^.Next := InstFreeList;
  86.       InstFreeList := Instance;
  87.       Inc(Longint(Instance), SizeOf(TObjectInstance));
  88.     until Longint(Instance) - Longint(Block) >= SizeOf(TInstanceBlock);
  89.     InstBlockList := Block;
  90.   end;
  91.   Result := InstFreeList;
  92.   Instance := InstFreeList;
  93.   InstFreeList := Instance^.Next;
  94.   Instance^.Method := Method;
  95.   inc(InstBlockCount);
  96. end;
  97.  
  98. { Free an object instance }
  99.  
  100. procedure FreeObjectInstance(ObjectInstance: Pointer);
  101. begin
  102.   if (ObjectInstance <> nil) and (InstBlockCount > 0) then
  103.   begin
  104.     PObjectInstance(ObjectInstance)^.Next := InstFreeList;
  105.     InstFreeList := ObjectInstance;
  106.     Dec(InstBlockCount);
  107.     if InstBlockCount = 0 then begin
  108.        VirtualFree(InstBlockList, 0, MEM_RELEASE);
  109.        InstBlockList := nil;
  110.        ObjectInstance := nil;
  111.     end;
  112.   end;
  113. end;
  114.  
  115. var
  116.   UtilWindowClass: TWndClass = (
  117.     style: 0;
  118.     lpfnWndProc: @DefWindowProc;
  119.     cbClsExtra: 0;
  120.     cbWndExtra: 0;
  121.     hInstance: 0;
  122.     hIcon: 0;
  123.     hCursor: 0;
  124.     hbrBackground: 0;
  125.     lpszMenuName: nil;
  126.     lpszClassName: 'KOLFakeUtilWindow');
  127.  
  128. function AllocateHWnd(Method: TWndMethod): HWND;
  129. var
  130.   TempClass: TWndClass;
  131.   ClassRegistered: Boolean;
  132. begin
  133.   UtilWindowClass.hInstance := HInstance;
  134.   ClassRegistered := GetClassInfo(HInstance, UtilWindowClass.lpszClassName,
  135.     TempClass);
  136.   if not ClassRegistered or (TempClass.lpfnWndProc <> @DefWindowProc) then
  137.   begin
  138.     if ClassRegistered then
  139.       Windows.UnregisterClass(UtilWindowClass.lpszClassName, HInstance);
  140.     Windows.RegisterClass(UtilWindowClass);
  141.   end;
  142.   Result := CreateWindowEx(WS_EX_TOOLWINDOW, UtilWindowClass.lpszClassName,
  143.     '', WS_POPUP {!0}, 0, 0, 0, 0, 0, 0, HInstance, nil);
  144.   if Assigned(Method) then
  145.     SetWindowLong(Result, GWL_WNDPROC, Longint(MakeObjectInstance(Method)));
  146. end;
  147.  
  148. procedure DeallocateHWnd(Wnd: HWND);
  149. var
  150.   Instance: Pointer;
  151. begin
  152.   Instance := Pointer(GetWindowLong(Wnd, GWL_WNDPROC));
  153.   DestroyWindow(Wnd);
  154.   if Instance <> @DefWindowProc then FreeObjectInstance(Instance);
  155. end;
  156. end.

Note that is the reason that KOL works with Freepascal too.
I have to check if KOL64 has a 64 bit implementation. I suspect YES! for the same reason. (unit objects.pas from KOL)
« Last Edit: July 21, 2018, 11:38:42 am by Thaddy »
Specialize a type, not a var.

u2o

  • Jr. Member
  • **
  • Posts: 72
  • No message
Re: Midi Sequencer, conversion from Delphii, errors...
« Reply #6 on: July 22, 2018, 12:07:02 am »
I really appreciate the dedication of both. But the interrelation of modules in Midi Sequencer it's too much ... I'm studying it about a month ago, it's getting too complex and I can not make it work.

Looking for some alternative, I found another project MIDIPLEX (https://github.com/stascorp/MIDIPLEX), that at least today I could make it work (implementing generics.collections).
The main form has 16000 lines, something big to abstract ...

My intention is to abstract all the methods of a midi sequencer, because I really do not have the knowledge to create it from scratch by myself.

The idea is to create a parameterized midi sequencer, which allows to mix "instruments execution patterns" and play them simultaneously. Therefore, it would be similar to the operation of any DAW (Digital Audio Workstation). In principle, and speaking only of the "play song" event.

The difference will be in the way of creating the music. A simplified interface, which helps soloists, to create their songs, without losing the inspiration in the implementation of musical ideas ... And of course, avoid (as much as possible) hours and hours of midi editing to create the imagined ...

If I don't lose the constancy ... maybe, a monster of open source code (in Lazarus) will be born, with indefinite possibilities of expansion. It can be cross-platform, but for now I'm not focused on it.

As soon as I have a functional demo, I'll show you.

Thank you both for your time.
« Last Edit: July 22, 2018, 12:35:09 am by u2o »

avra

  • Hero Member
  • *****
  • Posts: 2514
    • Additional info
Re: Midi Sequencer, conversion from Delphii, errors...
« Reply #7 on: July 23, 2018, 10:44:33 am »
ct2laz - Conversion between Lazarus and CodeTyphon
bithelpers - Bit manipulation for standard types
pasettimino - Siemens S7 PLC lib

u2o

  • Jr. Member
  • **
  • Posts: 72
  • No message
Re: Midi Sequencer, conversion from Delphii, errors...
« Reply #8 on: July 24, 2018, 01:54:09 am »
Hi avra!

Yes, I have read the thread that you comment. But I'm searching a sequencer in which events can be inserted in real time in different tracks. So far, I was able to run MidiPlex, in Lazarus with Midi0 with a single track, but with MIDI 1 single track or multiple tracks, it fails...

You will have an example? of how to use your pl_Win_MIDI package as a sequencer...

 

TinyPortal © 2005-2018