• No se han encontrado resultados

This derived class implements two major features. First, it provides a function, SetEchoOutput(), that accepts a pointer to the CMaxMidiOut object that will serve as the output device for echoed data. Second, it implements a new version of ProcessMidiData(), overriding the default (empty) one in the CMaxMidiIn base class. This new implementation is called for each received MIDI event. If the echo output device exists (i.

e., if SetEchoOutput() has been called), the event is sent to that device.

Page 146

Adding Device Menus

By now, our simple MIDI application has reached the almost-useful state.

Let's take it one step further and allow the user to select the desired MIDI input and MIDI output devices from two drop-down menus. These menu lists are built dynamically--when the program starts up--and list all of the devices that are available on the system. All of the menu

construction and selection handling are done by two classes:

CMidiInDeviceMenu and CMidiOutDeviceMenu. The drop-down lists are

automatically created by these classes, and devices are opened and closed as needed in response to menu selections.

The third example program, Ch10Ex3, adds instances of these two classes, along with the necessary changes to hook them into the proper menu

messages. There are changes to the CMainFrame class as well as changes to the view class. The two menu class implementation files are added to the project. Here is the modified source code for the new program. Although the class names have changed (again!), most of the code is the same as example 2.

// from MainFrm.cpp - MainFrame class implementation file int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) {

if (CFrameWnd::OnCreate(lpCreateStruct) == -1) return -1;

// since we don't have separate ON_UPDATE_COMMAND_UI handlers

// or ON_COMMAND handlers for the device menu items we must // clear this flag so that the Input Device and Output Device // menu items are all enabled

m_bAutoMenuEnable = FALSE;

return 0;

}

// from Ch10Ex3View.h - CCh10Ex3View class declaration header

#include "MaxMidi.h"

#include "MyMidiIn.h"

class CCh10Ex3View : public CView {

public:

CMaxMidiOut MidiOut;

MyMidiIn MidiIn;

// Input and Output Device Menus CMidiInDeviceMenu InMenu;

CMidiOutDeviceMenu OutMenu;

protected: // create from serialization only CCh10Ex3View();

}

Page 147

// from CCh10Ex3View.cpp - view class implementation file int CCh10Ex3View::OnCreate(LPCREATESTRUCT lpCreateStruct) {

if (CView::OnCreate(lpCreateStruct) == -1) return -1;

// attach the input and output devices to this window MidiOut.Attach(GetSafeHwnd());

MidiIn.Attach(GetSafeHwnd());

// get the parent menu

CMenu* ParentMenu = GetParent()->GetMenu();

// create the device menus and select the first devices InMenu.Create(ParentMenu->GetSafeHmenu(),

ParentMenu->GetMenuItemCount() - 1, "&Input Device", IDM_INPUT);

InMenu.Attach(&MidiIn);

InMenu.SelectDevice(0);

OutMenu.Create(ParentMenu->GetSafeHmenu(),

ParentMenu->GetMenuItemCount() - 1,

BOOL CCh10Ex3View::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo)

return CView::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);

}

Page 148

The first notable change is in the CMainFrame class. Since the device menu classes cannot know how many MIDI devices are available until

runtime (on some systems there may be nearly a dozen inputs and outputs), these classes do not implement separate ON_UPDATE_COMMAND_UI or

ON_COMMAND handlers for each menu item. Instead, the classes check every WM_COMMAND message, and respond to the range of messages that correspond to the devices in the menu.

Normally, MFC disables menu items by default. They are enabled when the menu is shown by MFC-generated calls to ON_UPDATE_COMMAND_UI handlers, one for each item in the menu. To change the MFC's default behavior, the CMainFrame class provides the m_bAutoMenuEnable member variable. Setting this Boolean value to FALSE will prevent MFC from disabling the menu items.

The modified view class adds an instance of each of the two menu classes, CMidiInDeviceMenu and CMidiOutDeviceMenu. These classes are created and attached to the main menu during the OnCreate() function. Each class' Create() function accepts four parameters: a menu handle, the relative

position in the menu for the new menu item, the item's display name, and a base message number. The new menu item will be inserted at the

specified location, where 0 specifies the leftmost item. In this example, each menu, with the specified name, is inserted just to the left of the Help menu item by specifying GetMenuItemCount() - 1 as the relative location. The base message number is used to number each of the devices in the menu. The first device's menu item will have a message ID equal to the base message number and each subsequent device will have a higher value.

In the example, the base input ID is IDM_INPUT (101), and the base output ID is IDM_OUTPUT (201). These two symbols are specified using the

Resource Symbols dialog box. To do this, select Resource Symbols... from the compiler's View menu. Click on the New... button and type in the symbol name and value for each of the two symbols.

The final modification is to add the OnCmdMsg() function in the view class--using the ClassWizard--and to insert the two device menu class SelectDevice() calls. The nCode parameter is 0 if the corresponding WM_COMMAND message originates from a menu. If it does, both of the SelectDevice() functions are called; one for input devices and one for output devices. These functions ignore any message IDs outside of their valid range (i.e., from the base message number up to the base value plus the number of devices) and return FALSE. If the ID is inside the range handled by the device menu class, the corresponding input or output device will be selected and the function returns TRUE.

With these changes, the example program will echo incoming MIDI messages from any user-selected input device to any selected output device. This program illustrates how

Page 149

to handle MIDI input and output, without synchronization, using the ToolKit's C++ classes. In the next chapter, we'll take this program and expand it into a System Exclusive librarian that will receive sysex bulk dumps, store the data in a widely supported file format, and send those saved sysex messages back out.

chapter 11

Documento similar