我正在开发一个由 MIDI 消息控制的合成器,有时同时来自多个 MIDI 源。它基于 Teensy 4.1。
我编码使用的第一个源是USB设备MIDI——简单:在setup()中设置8个回调并在loop()中调用usbMIDI.read()。
我对其进行编码以使用的第二个源是USB Host MIDI。然而,我最终得到了重复的代码——所有回调设置代码和loop()中的read()调用。
然后我添加了更多 USB 主机 MIDI 源对象,创建了一个指向主机对象的指针数组,现在我在 setup() 和 Loop() 中迭代该数组...但仍然有该代码的副本用于 USB 设备接口。
供参考:
void setup()
{
// USB Device
usbMIDI.setHandleNoteOff(myNoteOff);
usbMIDI.setHandleNoteOn(myNoteOn);
usbMIDI.setHandlePitchChange(myPitchChange);
...
// USB Host
for(uint8_t i = 0; i < CNT_MIDI; i++)
{
midiDevices[i]->setHandleNoteOff(myNoteOff);
midiDevices[i]->setHandleNoteOn(myNoteOn);
midiDevices[i]->setHandlePitchChange(myPitchChange);
...
}
}
...
void loop()
{
usbMIDI.read();
for(uint8_t i = 0; i < CNT_MIDI; i++)
midiDevices[i]->read();
...
}
这两个类(USBdevice:usb_midi_class,USBhost:MIDIDeviceBase)的接口对于这些调用(read(),setHandle ...(函数指针))是相同的,但由于它们没有共同的基类,所以我不能只需将 USB 设备 MIDI 对象添加到 USB 主机指针数组即可。
现在,我想通过 Teensy 的硬件串行端口之一添加一个 5 针 DIN MIDI 端口,并且会拥有相同调用的三个副本......我想知道是否有更好的方法。
我的目标是在所有这三种类型上调用同名成员函数,而不需要将这些调用写入三次。如果编译器这样做,我不会介意。我也不打算在 setup() 之后添加或删除对象;我知道编译时的来源是什么。这感觉像是模板的工作,但对我来说,在不以另一种方式复制所有代码的情况下如何完成此任务并不明显。
例如,我想我可以编写一个公共基类,9个虚函数,并创建一个派生模板类来继承它,然后定义这9个虚函数,例如:
class Wrapper
{
public:
virtual bool read(uint8_t channel) = 0;
virtual ~Wrapper() = 0;
...
};
template <class T>
class TemplateWrapper : public Wrapper
{
private:
T* wrapped;
public:
TemplateWrapper(T* w) : wrapped(w) {}
bool read(uint8_t channel) { return wrapped->read(channel); }
...
};
但它仍然让我多次重写代码,只是在不同的地方。另外,我必须用函数指针写出 8 个签名。有更好的方法吗?
template <typename T>
void setupDevice(T& device) {
device.setHandleNoteOff(myNoteOff);
device.setHandleNoteOn(myNoteOn);
device.setHandlePitchChange(myPitchChange);
}
您还可以重载该函数以接受指针而不是引用,因为您在显示的代码中使用了两者。
template <typename T>
void setupDevice(T* device) {
device->setHandleNoteOff(myNoteOff);
device->setHandleNoteOn(myNoteOn);
device->setHandlePitchChange(myPitchChange);
}