Электронная библиотека книг Александра Фролова и Григория Фролова.
Shop2You.ru Создайте свой интернет-магазин
Библиотека
Братьев
Фроловых

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

© Александр Фролов, Григорий Фролов
Том 15, М.: Диалог-МИФИ, 1994, 284 стр.

[Назад] [Содеожание] [Дальше]

4.3. Приложение MIDIPL

Приложение MIDIPL (рис. 4.2) демонстрирует способы использования некоторых функций MCI для проигрывания файлов MIDI.

Рис. 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

[Назад] [Содеожание] [Дальше]