Мультимедиа для Windows

         

MIDIPL



Рис. 4.2. Приложение MIDIPL

По своей структуре оно напоминает приложение MCIWAWER, которое может проигрывать и записывать wav-файлы. Поэтому мы рассмотрим только отличия, специфические для работы с устройством sequencer.

Основной файл исходных текстов приложения MIDIPL приведен в листинге 4.1.

Листинг 4.1. Файл midipl/midipl.cpp // ------------------------------------------------ // Приложение MIDIPL // Проигрывание файлов MIDI // с помощью интерфейса сообщений MCI // ------------------------------------------------ #define STRICT #include <windows.h> #include <windowsx.h> #include <mmsystem.h> #include <mem.h> #pragma hdrstop #include "midipl.hpp" #include "midiio.hpp" // Идентификатор таймера #define BEEP_TIMER 1 // Идентификатор полосы просмотра #define ID_SCROLL 10 // Длина полосы просмотра #define SCROLL_SIZE 400 // Прототипы функций BOOL InitApp(HINSTANCE); LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM); // Глобальные переменные int nMode = MODE_STOP; MMTIME mmtimeOut; BOOL fFileLoaded = FALSE; int nPosition; HWND hScroll; UINT wOutDeviceID; BYTE szFileName[128]; DWORD dwFileSize;


char const szClassName[] = "MCIMIDIClass"; char const szWindowTitle[] = "MIDI Player"; HINSTANCE hInst; // ===================================== // Функция WinMain // ===================================== #pragma argsused int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) { MSG msg; // структура для работы с сообщениями HWND hwnd; // идентификатор главного окна приложения if(hPrevInstance) return FALSE; if(!InitApp(hInstance)) return FALSE; hInst = hInstance; hwnd = CreateWindow( szClassName, // имя класса окна szWindowTitle, // заголовок окна WS_OVERLAPPEDWINDOW, // стиль окна CW_USEDEFAULT, // размеры и расположение окна CW_USEDEFAULT, 450, 120, 0, 0, hInstance, NULL); if(!hwnd) return FALSE; ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd); while(GetMessage(&msg, 0, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } // ===================================== // Функция InitApp // Выполняет регистрацию класса окна // ===================================== BOOL InitApp(HINSTANCE hInstance) { ATOM aWndClass; // атом для кода возврата WNDCLASS wc; // структура для регистрации memset(&wc, 0, sizeof(wc)); wc.lpszMenuName = "APP_MENU"; wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = (WNDPROC) WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = LoadIcon(hInstance, "APPICON"); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wc.lpszClassName = (LPSTR)szClassName; aWndClass = RegisterClass(&wc); return (aWndClass != 0); } // ===================================== // Функция WndProc // ===================================== LRESULT CALLBACK _export WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { HDC hdc; PAINTSTRUCT ps; int rc; switch (msg) { // ------------------------------------------------------------ // WM_CREATE // Создание главного окна приложения // ------------------------------------------------------------ case WM_CREATE: { nMode = MODE_STOP; fFileLoaded = FALSE; wOutDeviceID = 0; // Создаем таймер SetTimer(hwnd, BEEP_TIMER, 100, NULL); // Создаем полосу просмотра hScroll = CreateWindow("scrollbar", NULL, WS_CHILD | WS_VISIBLE | SBS_HORZ, 10, 40, SCROLL_SIZE, 15, hwnd, (HMENU) ID_SCROLL, hInst, NULL); // Устанавливаем текущую позицию nPosition = 0; // Устанавливаем минимальное и максимальное // значения для полосы просмотра SetScrollRange(hScroll, SB_CTL, 1, SCROLL_SIZE, TRUE); // Устанавливаем ползунок SetScrollPos(hScroll, SB_CTL, nPosition, TRUE); return 0; } // ------------------------------------------------------------ // WM_PAINT // Рисование в окне // ------------------------------------------------------------ case WM_PAINT: { // Получаем контекст отображения для // рисования во внутренней области окна hdc = BeginPaint(hwnd, &ps); // Отображаем текущий режим работы if(nMode == MODE_STOP) TextOut(hdc, 10, 10, "Остановлено", 11); else if(nMode == MODE_PLAYING) TextOut(hdc, 10, 10, "Идет проигрывание...", 20); else if(nMode == MODE_PLAYINGPAUSED) TextOut(hdc, 10, 10, "Проигрывание остановлено", 24); else TextOut(hdc, 10, 10, "Неправильный режим!", 19); // Освобождаем контекст отображения EndPaint(hwnd, &ps); return 0; } // ------------------------------------------------------------ // WM_COMMAND // Обработка сообщений от меню // ------------------------------------------------------------ case WM_COMMAND: { switch (wParam) { // ------------------------------------------------- // Строка "About" меню "Help" // ------------------------------------------------- case CM_HELPABOUT: { MessageBox(hwnd, "MIDI Player, v.1.0\n" "(C) Frolov A.V., 1994", "About MIDIPL", MB_OK | MB_ICONINFORMATION); return 0; } // ------------------------------------------------- // Строка "Open" меню "File" // ------------------------------------------------- case CM_FILEOPEN: { char szTitle[256]; // Загружаем новый файл if(!mcimidiSelectFile(szFileName)) return 0; // Отображаем в заголовке окна путь к файлу lstrcpy(szTitle, szWindowTitle); lstrcat(szTitle, " - "); lstrcat(szTitle, szFileName); SetWindowText(hwnd, szTitle); // Если было запущено воспроизведение, // останавливаем его и закрываем устройство вывода if(wOutDeviceID) { mcimidiStop(wOutDeviceID); mcimidiClose(wOutDeviceID); wOutDeviceID = 0; // Новый режим nMode = MODE_STOP; // Перерисовываем окно для отображения строки, // соответствующей новому режиму InvalidateRect(hwnd, NULL, TRUE); } // Устанавливаем движок в начало полосы просмотра nPosition = 0; SetScrollPos(hScroll, SB_CTL, nPosition, TRUE); // Устанавливаем флаг загрузки файла fFileLoaded = TRUE; return 0; } // ------------------------------------------------- // Строка "Play!" // Проигрывание загруженного файла MIDI // ------------------------------------------------- case CM_CTLPLAY: { // Если файл загружен и не проигрывается, // запускаем проигрывание файла if((fFileLoaded == TRUE) && (nMode == MODE_STOP)) { // Новый режим nMode = MODE_PLAYING; // Перерисовываем окно для отображения строки, // соответствующей новому режиму InvalidateRect(hwnd, NULL, TRUE); // Если устройство не было открыто раньше, // открываем его if(!wOutDeviceID) wOutDeviceID = mcimidiOpen((LPSTR)szFileName); // Проигрываем файл mcimidiPlay(hwnd, wOutDeviceID); } return 0; } // ------------------------------------------------- // Строка "Stop!" // Останов проигрывания или записи файла MIDI // ------------------------------------------------- case CM_CTLSTOP: { if(nMode == MODE_PLAYING || nMode == MODE_PLAYINGPAUSED) { // Останавливаем проигрывание mcimidiStop(wOutDeviceID); } // Устанавливаем движок в начало полосы просмотра nPosition = 0; SetScrollPos(hScroll, SB_CTL, nPosition, TRUE); // Новый режим nMode = MODE_STOP; InvalidateRect(hwnd, NULL, TRUE); return 0; } // ------------------------------------------------- // Строка "Pause!" // Временный останов проигрывания // ------------------------------------------------- case CM_CTLPAUSE: { if(nMode == MODE_PLAYING) { // Временный останов проигрывания mcimidiPause(wOutDeviceID); nMode = MODE_PLAYINGPAUSED; } InvalidateRect(hwnd, NULL, TRUE); return 0; } // ------------------------------------------------- // Строка "Resume!" // Продолжение проигрывания после останова // ------------------------------------------------- case CM_CTLRESUME: { if(nMode == MODE_PLAYINGPAUSED) { // Продолжаем проигрывание mcimidiPlayCurrent(hwnd, wOutDeviceID); nMode = MODE_PLAYING; InvalidateRect(hwnd, NULL, TRUE); } return 0; } // ------------------------------------------------- // Строка "Exit" меню "File" // Завершение работы приложения // ------------------------------------------------- case CM_FILEEXIT: { DestroyWindow(hwnd); return 0; } default: return 0; } } // ------------------------------------------------------------ // MM_MCINOTIFY // ------------------------------------------------------------ case MM_MCINOTIFY: { // Если находились в режиме воспроизведения, останавливаем // и закрываем устройство вывода if(nMode == MODE_PLAYING) { if(wOutDeviceID) { mcimidiStop(wOutDeviceID); mcimidiClose(wOutDeviceID); wOutDeviceID=0; nMode = MODE_STOP; InvalidateRect(hwnd, NULL, TRUE); } } return 0; } // ------------------------------------------------------------ // WM_TIMER // Сообщение от таймера // ------------------------------------------------------------ case WM_TIMER: { MCI_STATUS_PARMS mciStatus; DWORD dwPos; // Режим воспроизведения if(nMode == MODE_PLAYING) { // Определяем текущую позицию внутри блока mciStatus.dwItem = MCI_STATUS_POSITION; mciSendCommand(wOutDeviceID, MCI_STATUS, MCI_STATUS_ITEM, (DWORD)(LPVOID)&mciStatus); dwPos = mciStatus.dwReturn; // Вычисляем новое положение движка полосы просмотра nPosition = ((DWORD)SCROLL_SIZE * dwPos) / dwFileSize; // Ограничиваем пределы изменения текущей // позиции значениями от 1 до SCROLL_SIZE if(nPosition > SCROLL_SIZE) nPosition = SCROLL_SIZE; if(nPosition < 1) nPosition = 1; // Устанавливаем ползунок полосы просмотра // в соответствии с новым значением текущей позиции SetScrollPos(hScroll, SB_CTL, nPosition, TRUE); } return 0; } // ------------------------------------------------------------ // WM_DESTROY // Уничтожение главного окна приложения // ------------------------------------------------------------ case WM_DESTROY: { // Удаляем таймер и полосу просмотра KillTimer(hwnd, BEEP_TIMER); DestroyWindow(hScroll); // Если находимся в режиме проигрывания, останавливаем // запись и закрываем устройство вывода if(fFileLoaded) { if(nMode == MODE_PLAYING || nMode == MODE_PLAYINGPAUSED) { mcimidiStop(wOutDeviceID); mcimidiClose(wOutDeviceID); } nMode = MODE_STOP; } PostQuitMessage(0); return 0; } default: break; } return DefWindowProc(hwnd, msg, wParam, lParam); }

Обратим ваше внимание на то, как приложение MIDIPL выполняет продолжение проигрывания после временного останова. Так как драйвер mciseq.drv не поддерживает команду MCI_RESUME, для продолжения проигрывания используется команда MCI_PLAY без указания начальной позиции. Эта команда выдается функцией mcimidiPlayCurrent, вызываемой для продолжения проигрывания с текущего места.

Определения констант для приложения MIDIPL находятся в файле midipl.hpp (листинг 4.2).

Листинг 4.2. Файл midipl/midipl.hpp #define CM_HELPABOUT 301 #define CM_FILEEXIT 302 #define CM_FILEOPEN 303 #define CM_CTLPLAY 401 #define CM_CTLRESUME 402 #define CM_CTLPAUSE 403 #define CM_CTLSTOP 404

Файл midiio.cpp содержит определение функций, предназначенных для работы с интерфейсом MCI (листинг 4.3).

Листинг 4.3. Файл midipl/midiio.cpp #define STRICT #include <windows.h> #include <windowsx.h> #include <commdlg.h> #include <mmsystem.h> #include <mem.h> #pragma hdrstop #include "midiio.hpp" // Глобальные переменные extern int nMode; extern int nPosition; extern DWORD dwFileSize; //----------------------------------------------------- // mcimidiOpen // Открытие устройства вывода //----------------------------------------------------- UINT mcimidiOpen(LPSTR szFileName) { MCI_OPEN_PARMS mciOpen; MCI_STATUS_PARMS mciStatus; DWORD dwrc; DWORD dwFlags; // Готовим блок параметров mciOpen.lpstrDeviceType= (LPSTR)"sequencer"; mciOpen.lpstrElementName = (LPSTR)szFileName; mciOpen.dwCallback = 0; mciOpen.wDeviceID = 0; mciOpen.wReserved0 = 0; mciOpen.lpstrAlias = NULL; // Устанавливаем флаги dwFlags = MCI_OPEN_TYPE | MCI_OPEN_ELEMENT | MCI_WAIT; // Открываем устройство dwrc = mciSendCommand(0, MCI_OPEN, dwFlags, (DWORD)(LPVOID)&mciOpen); if(dwrc) { mcimidiError(dwrc); return 0; } // Если устройство открыто успешно, определяем // длительность звучания в миллисекундах else { mciStatus.dwItem = MCI_STATUS_LENGTH; dwrc = mciSendCommand(mciOpen.wDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_WAIT, (DWORD)(LPVOID)&mciStatus); if(dwrc) { mcimidiError(dwrc); return 0; } // Сохраняем длительность звучания в глобальной // переменной и возвращаем идентификатор устройства вывода dwFileSize = mciStatus.dwReturn; return mciOpen.wDeviceID; } } //----------------------------------------------------- // mcimidiPlay // Проигрывание загруженного файла MIDI //----------------------------------------------------- DWORD mcimidiPlay(HWND hwnd, UINT wDeviceID) { MCI_PLAY_PARMS mciPlayParms; DWORD dwrc; // Позиционирование на начало фрагмента dwrc = mciSendCommand(wDeviceID, MCI_SEEK, MCI_WAIT | MCI_SEEK_TO_START, NULL); // Идентификатор окна, функция которого получит // сообщение MM_MCINOTIFY mciPlayParms.dwCallback = (DWORD)hwnd; // Запускаем проигрывание dwrc=mciSendCommand(wDeviceID, MCI_PLAY, MCI_NOTIFY, (DWORD)(LPVOID)&mciPlayParms); return dwrc; } //----------------------------------------------------- // mcimidiPlayCurrent // Проигрывание загруженного файла MIDI // с текущей позиции //----------------------------------------------------- DWORD mcimidiPlayCurrent(HWND hwnd, UINT wDeviceID) { MCI_PLAY_PARMS mciPlayParms; DWORD dwrc; // Идентификатор окна, функция которого получит // сообщение MM_MCINOTIFY mciPlayParms.dwCallback = (DWORD)hwnd; // Запускаем проигрывание dwrc=mciSendCommand(wDeviceID, MCI_PLAY, MCI_NOTIFY, (DWORD)(LPVOID)&mciPlayParms); return dwrc; } //----------------------------------------------------- // mcimidiStop // Останов проигрывания загруженного файла MIDI //----------------------------------------------------- DWORD mcimidiStop(UINT wDeviceID) { MCI_GENERIC_PARMS mcigen; DWORD dwrc; dwrc = mciSendCommand(wDeviceID, MCI_STOP, MCI_WAIT, (DWORD)(LPMCI_GENERIC_PARMS)&mcigen); if(dwrc) { mcimidiError(dwrc); } return dwrc; } //----------------------------------------------------- // mcimidiPause // Временный останов проигрывания загруженного файла MIDI //----------------------------------------------------- DWORD mcimidiPause(UINT wDeviceID) { MCI_GENERIC_PARMS mcigen; DWORD dwrc; dwrc = mciSendCommand(wDeviceID, MCI_PAUSE, MCI_WAIT, (DWORD)(LPMCI_GENERIC_PARMS)&mcigen); if(dwrc) { mcimidiError(dwrc); } return dwrc; } //----------------------------------------------------- // mcimidiSelectFile // Выбор файла MIDI //----------------------------------------------------- BOOL mcimidiSelectFile(LPSTR lpszFileName) { OPENFILENAME ofn; char szFile[256]; char szFileTitle[256]; char szFilter[256] = "MIDI Files\0*.mid;*.rmi\0Any Files\0*.*\0"; szFile[0] = '\0'; memset(&ofn, 0, sizeof(OPENFILENAME)); // Инициализируем нужные нам поля ofn.lStructSize = sizeof(OPENFILENAME); ofn.hwndOwner = NULL; ofn.lpstrFilter = szFilter; ofn.nFilterIndex = 1; ofn.lpstrFile = szFile; ofn.nMaxFile = sizeof(szFile); ofn.lpstrFileTitle = szFileTitle; ofn.nMaxFileTitle = sizeof(szFileTitle); ofn.lpstrInitialDir = NULL; ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY; // Выбираем входной файл if (GetOpenFileName(&ofn)) { // Копируем путь к выбранному файлу lstrcpy(lpszFileName, (LPSTR)szFile); return TRUE; } else return FALSE; } //----------------------------------------------------- // mcimidiClose // Закрытие устройства вывода //----------------------------------------------------- void mcimidiClose(UINT wDeviceID) { MCI_GENERIC_PARMS mcigen; DWORD dwrc; dwrc = mciSendCommand(wDeviceID, MCI_CLOSE, MCI_WAIT, (DWORD)(LPMCI_GENERIC_PARMS)&mcigen); if(dwrc) { mcimidiError(dwrc); return; } } //----------------------------------------------------- // mcimidiError // Обработка ошибок //----------------------------------------------------- void mcimidiError(DWORD dwrc) { BYTE szBuf[MAXERRORLENGTH]; if(mciGetErrorString(dwrc, (LPSTR)szBuf, MAXERRORLENGTH)) MessageBox(NULL, szBuf, "MIDIPL Error", MB_ICONEXCLAMATION); else MessageBox(NULL, "Неизвестная ошибка", "MIDIPL Error", MB_ICONEXCLAMATION); }

Функция mcimidiOpen предназначена для открытия устройства sequencer. При подготовке блока параметров в поле lpstrDeviceType структуры mciOpen указано имя устройства: mciOpen.lpstrDeviceType= (LPSTR)"sequencer";

Функция mcimidiPlayCurrent предназначена для проигрывания с текущей позиции. В отличие от функции mcimidiPlay в ней не выполняется позиционирование на начало.

Файл midiio.hpp (листинг 4.4) содержит определения констант и прототипы функций для файла midiio.cpp.

Листинг 4.4. Файл midipl/midiio.hpp #include <windows.h> #include <mmsystem.h> #define MODE_STOP 0 #define MODE_PLAYING 1 #define MODE_PLAYINGPAUSED 2 UINT mcimidiOpen(LPSTR szFileName); BOOL mcimidiSelectFile(LPSTR lpszFileName); void mcimidiClose(UINT wDeviceID); DWORD mcimidiPlay(HWND hwnd, UINT wDeviceID); DWORD mcimidiPlayCurrent(HWND hwnd, UINT wDeviceID); void mcimidiError(DWORD dwrc); DWORD mcimidiStop(UINT wDeviceID); DWORD mcimidiPause(UINT wDeviceID); DWORD mcimidiResume(UINT wDeviceID);

Файл определения ресурсов midipl.rc приведен в листинге 4.5.

Листинг 4.5. Файл midipl/midipl.rc #include "midipl.hpp" APPICON ICON "midipl.ico" APP_MENU MENU BEGIN POPUP "&File" BEGIN MENUITEM "&Open...", CM_FILEOPEN MENUITEM SEPARATOR MENUITEM "E&xit", CM_FILEEXIT END MENUITEM "&Play!", CM_CTLPLAY MENUITEM "&Stop!", CM_CTLSTOP MENUITEM "Resu&me!", CM_CTLRESUME MENUITEM "P&ause!", CM_CTLPAUSE POPUP "&Help" BEGIN MENUITEM "&About...", CM_HELPABOUT END END

Файл определения модуля для приложения MIDIPL приведен в листинге 4.6.

Листинг 4.6. Файл midipl/midipl.def NAME MIDIPL DESCRIPTION 'Приложение MIDIPL, (C) 1994, Frolov A.V.' EXETYPE windows STUB 'winstub.exe' STACKSIZE 10240 HEAPSIZE 1024 CODE preload moveable discardable DATA preload moveable multiple



Содержание    Назад