How to synchronize the container application with the control |
|
In order to allow managing feedback coming from the component's code to the container application code, Active MIDI DJ Console for .NET gives the possibility to setup a certain number of callback delegates that inform about events occurring between the component and external MIDI devices; the list of supported events is available on the table below:
Situations |
Corresponding callback delegate |
Method for initializing the delegate |
|
|
|
The configuration of MIDI devices is changed |
||
A MIDI device, previously opened through the MidiDevices.Open method, is disconnected from the system after a manual removal |
||
A short MIDI event is received from a MIDI input device |
||
A raw MIDI event is received from a MIDI input device |
||
A physical button mapped into the DJ Console profile has been pressed |
||
A physical button mapped into the DJ Console profile has been released. |
||
A physical range control (sliders, jog wheels, rotary knobs, etc.) mapped into the DJ Console profile has been moved |
||
A manual action (for example the pressing of a button, the movement of a slider or of a jog wheel) is performed on a console's physical control which has not been mapped into the DJ Console profile loaded inside the instance of the component or when the reception of events specific for mapped controls has been disabled through a call to the MidiDjConsole.EnableMappedEvents method. |
||
One of the keys on the virtual piano keyboard, previously created through the MidiVirtualKeyboard.Create method, is pressed or released |
Events monitoring is performed inside secondary working threads of the component so, when a delegate is needed to update user interface elements of your application, for example changing the status of a checkbox or filling a combo box, the multi-threading situation must be considered: the relationship between threads and delegates is secondary threads cannot just call methods like the application's primary thread, so a function pointer is needed and delegates act as function pointers.
Let's see a couple of code snippets that clarify how a callback delegate can be instantiated and managed.
Visual Basic .NET |
// import the component's namespace Imports ActiveDjConsoleNetApi
Namespace ProfileEditor Public Partial Class FormMain Inherits Form Public Sub New() InitializeComponent() End Sub
' instance the component Dim m_djConsole As ActiveDjConsoleNetApi.ActiveDjConsoleNetApi = New ActiveDjConsoleNetApi.ActiveDjConsoleNetApi()
' predispose the delegate and the callback Private Delegate Sub ConfigChangedCallbackDelegate(ByVal nChangeType As enumMidiDevicesConfigChanges, ByVal nUserData As Int32) Private addrCallbackMidiDevicesConfigChanged As CallbackMidiDevicesConfigChanged
' the subroutine that manages the callback Private Sub MidiDevicesConfigChangedCallback(ByVal nChangeType As enumMidiDevicesConfigChanges, ByVal nUserData As Int32) ' check if we are being invoked from a secondary thread If Me.InvokeRequired Then ' this callback is being called from a secondary thread so, in order to access eventual controls on the form, we must ' call Invoke using this same function as a delegate Me.Invoke(New ConfigChangedCallbackDelegate(AddressOf MidiDevicesConfigChangedCallback), nChangeType, nUserData) Return End If
' display a debug message about the occurred event System.Diagnostics.Debug.WriteLine("MidiDevicesConfigChanged: " & nChangeType.ToString())
' we can now safely access controls on the user interface ...
End Sub
Private Sub PredisposeCallbacks() ' predispose the callback that will notify about a change within the configuration of MIDI devices addrCallbackMidiDevicesConfigChanged = New CallbackMidiDevicesConfigChanged(AddressOf MidiDevicesConfigChangedCallback) m_djConsole.CallbackMidiDevicesConfigChangedSet(addrCallbackMidiDevicesConfigChanged, 0) End Sub
Private Sub FormMain_Load(ByVal sender As Object, ByVal e As EventArgs) Handles MyBase.Load
' initialize the component m_djConsole.Init()
' predispose the callbacks PredisposeCallbacks()
End Sub End Class End Namespace
|
Visual C# |
// add the component's namespace using ActiveDjConsoleNetApi;
namespace ProfileEditor { public partial class FormMain : Form { public FormMain() { InitializeComponent(); }
// instance the component ActiveDjConsoleNetApi.ActiveDjConsoleNetApi m_djConsole = new ActiveDjConsoleNetApi.ActiveDjConsoleNetApi();
// predispose the delegate and the callback delegate void ConfigChangedCallbackDelegate(enumMidiDevicesConfigChanges nChangeType, Int32 nUserData); CallbackMidiDevicesConfigChanged addrCallbackMidiDevicesConfigChanged;
// the function that manages the callback void MidiDevicesConfigChangedCallback(enumMidiDevicesConfigChanges nChangeType, Int32 nUserData) { // check if we are being invoked from a secondary thread if (this.InvokeRequired) { // this callback is being called from a secondary thread so, in order to access controls on the form, we must // call Invoke using this same function as a delegate this.Invoke(new ConfigChangedCallbackDelegate(MidiDevicesConfigChangedCallback), nChangeType, nUserData); return; }
// display a debug message about the occurred event System.Diagnostics.Debug.WriteLine("MidiDevicesConfigChanged: " + nChangeType.ToString());
// we can now safely access controls on the user interface ... }
private void PredisposeCallbacks() { // predispose the callback that will notify about a change within the configuration of MIDI devices addrCallbackMidiDevicesConfigChanged = new CallbackMidiDevicesConfigChanged(MidiDevicesConfigChangedCallback); m_djConsole.CallbackMidiDevicesConfigChangedSet(addrCallbackMidiDevicesConfigChanged, 0); }
private void FormMain_Load(object sender, EventArgs e) { // initialize the component m_djConsole.Init();
// predispose the callbacks PredisposeCallbacks()
} } }
|
IMPORTANT NOTE ABOUT DELEGATES AND THEIR SCOPE: When an instance of a callback delegate is passed to one of the API functions, the delegate object is not reference counted. This means that the .NET framework would not know that it might still being used by the API so the Garbage Collector might remove the delegate instance if the variable holding the delegate is not declared as global. As a general rule, make sure to always keep your delegate instance in a variable which lives as long as the API needs it by using a global variable or member.