1

我正在努力使用 Lazarus / FreePascal 实现 OSX CoreMidi MidiCallback 程序。

在 MIDIServices 单元中,回调例程 MIDIReadProc 定义为:

MIDIReadProc = procedure( (*const*) pktlist: MIDIPacketListPtr; readProcRefCon: UnivPtr; srcConnRefCon: UnivPtr );       

当接收到 midi 事件时,在 CoreMidi 拥有的单独的高优先级线程上调用此例程。

我定义了一个回调过程来处理接收到的 midi 事件:

Type procedure MyMidiCallback(pktList: MIDIPacketListPtr;readProcRefCon: UnivPtr; srcConnRefCon: UnivPtrMy);     

procedure TMainForm.MyMidiCallback(pktList: MIDIPacketListPtr;readProcRefCon: UnivPtr; srcConnRefCon: UnivPtr);
begin
  //  handle midi packets
end;     

midi 回调挂钩在“MidiInputPortCreate”的以下代码中定义:

procedure TMainForm.ReceiveMidiTestClick(Sender: TObject);
var
  NumOfSources, NumOfDestinations: ItemCount;
  x: byte;
  MIDIDestinationPointer, MidiSourcePointer: MIDIEndpointRef;
  EndPointName: CFStringRef;
  MidiClient: MidiClientRef;
  InputPort: MidiPortRef;
  MidiCallback: MidiReadProc;
begin
  NumOfDestinations := MIDIGetNumberOfDestinations;
  NumOfSources := MIDIGetNumberOfSources;
  Memo.Lines.Add('Number of Midi Sources: ' + IntToStr(NumOfSources));
  EndPointName := nil;
  MidiClient := nil;
  InputPort := nil;
  MidiCallback := @TMainform.MyMidiCallback;

  for x := 0 to NumOfDestinations -1 do  // show destinations
    begin
      MidiDestinationPointer := MidiGetDestination(x);
      MIDIObjectGetStringProperty(MidiDestinationPointer, kMIDIPropertyName, EndPointName);
      Memo.Lines.Add('Destination ' + IntToStr(x) + ': ' + CFStrToAnsiStr(EndPointName));
    end;

  for x := 0 to NumOfSources -1 do  // show sources
    begin
      MidiSourcePointer := MIDIGetSource(x);
      MIDIObjectGetStringProperty(MidiSourcePointer, kMIDIPropertyName, EndPointName);
      Memo.Lines.Add('Source ' + IntToStr(x) + ': ' + CFStrToAnsiStr(EndPointName));
    end;

  MidiClientCreate(CFSTRP('Midi Input Client'), nil, nil, MidiClient);
  MidiInputPortCreate(MidiClient, CFSTRP('Input'), MidiCallback, nil, InputPort);  //     MidiCallback
  MIDISourcePointer := MIDIGetSource(0);  // select source(0) = midi keyboard
  MidiPortConnectSource(InputPort, MIDISourcePointer, nil);
end;                       

编译生成以下错误消息:

mainunit.pas(480,19) Error: Incompatible types: got "<procedure variable type of procedure(MIDIPacketListPtr,Pointer,Pointer) of object;Register>" expected "<procedure variable type of procedure(MIDIPacketListPtr,Pointer,Pointer);MWPascal>"  

我现在被困在这里;希望有人可以提供帮助。

--------------------------------- 更新 #1 -------------- --------------------

上面的代码确实有点奇怪,所以我重写了一些东西:

procedure TMainForm.ReceiveMidiTestClick(Sender: TObject);
var
  MidiClient: MidiClientRef;
  InputPort: MidiPortRef;
  MidiCallback: MIDIReadProc;
begin
  MidiCallback := MyMidiCallback;
  MidiClientCreate(CFSTRP('Midi Input Client'), nil, nil, MidiClient);
  MidiInputPortCreate(MidiClient, CFSTRP('Input'), MidiCallback, nil, InputPort);
  MidiPortConnectSource(InputPort, MIDIGetSource(0), nil);
end;

procedure MyMidiCallback(pktList: MIDIPacketListPtr; readProcRefCon: UnivPtr;    srcConnRefCon: UnivPtr);
begin
//  handle midi packets
end;   

现在代码编译没有错误,但只要我在 midi 键盘上按下一个键,应用程序就会崩溃并显示以下错误消息:

'错误项目...在地址 FFFFD96F 处引发异常类'外部:Sigtrap'

(FFFFD96F 可能是指向 MidiCallback 例程的指针)。

基本上,我遇到的问题是如何让 MidiInputPortCreate 中的 MidiCallback 指针正确指向我处理 midi 事件的 MyMidiCallback 过程。

顺便说一句,发送 Midi 事件工作正常。

4

1 回答 1

1

让我们将错误声明放在单独的行中:

mainunit.pas(480,19) 错误:不兼容的类型:得到“

对象的过程(MIDIPacketListPtr,Pointer,Pointer);Register>" 预期 "<过程变量类型的过程(MIDIPacketListPtr,Pointer,Pointer);MWPascal>"

请注意两个重要的区别:

1)错误中两个过程声明中的“对象”差异意味着您传递了一个方法而不是一个正确的过程。

2)除此之外,调用约定似乎不匹配,一个是mwpascal;一是注册。注册是大多数模式的默认设置,因此没有调用约定修饰符意味着注册。

您问题的“回调”部分很奇怪。您将类型定义为过程,但提供方法作为实现?

于 2013-09-25T18:44:24.450 回答