Мы переехали

Новый адрес - http://delphiblog.ru

среда, 5 декабря 2007 г.

Мультимедиа под контролем или Функция mciSendString

    В данной статье рассматривается функция mciSendString, которая находится в библиотеке winmm.dll. Эта функция может подавать команды любому MCI (Media Control Interface) устройству (поддерживаемое системой мультимедиа устройство: WAV, MIDI, CDAudio, Video и т.п.).
Вот её синтаксис:
MCIERROR mciSendString(
  LPCTSTR lpszCommand,
  LPTSTR lpszReturnString,
  UINT cchReturn,
  HANDLE hwndCallback
);

lpszCommand – команда;
lpszReturnString – строка результата;
cchReturn – размер в символах строки результата;
hwndCallback – окно отзыва (используется только при установленном в первом параметре флага "notify").
    Все дальнейшие примеры будут написаны на Delphi, но их перевод на другие языки, я думаю, не составит особого труда.
    Теперь условимся с параметрами функция mciSendString в последующих примерах:

  • HwndCallback: будет принимать значение 0;
  • CchReturn: будет принимать значение 64 (в MSDN написано, что это максимальная длина ошибки, которая может быть возвращена параметром lpszReturnString);
  • LpszReturnString: в этом параметре будем использовать переменную sbReturn: array [1..64] of char;
    Пришло время перейти к самому главному параметру функции – lpszCommand. Эта команда составляется при помощи специальных операторов, часть которых рассматривается далее.

Open
Эта команда поддерживается всеми устройствами. Она служит для инициализации устройства. Синтаксис команды:
'open lpszDeviceID lpszOpenFlags lpszFlags'
Параметры:
LpszDeviceID – идентификатор одного из устройств (или его псевдоним), прописанных в разделе [MCI] файла System.ini или в реестре. Может указывать также на драйвер. Например: cdaudio, sequencer, waveaudio, MyDriver.drv.
LpszOpenFlags – флаг, определяющий дополнительные параметры инициализации устройства. Я не буду приводить весь список значений этого параметра для каждого устройства, а упомяну лишь значение "alias device_alias type device_type", которое открывает устройство типа device_type под псевдонимом device_alias.
LpszFlags – может принимать одно из следующих значений:
  1. Test – служит для определения возможности выполнения команды, при этом сама команда устройству не отправляется.
  2. Wait – при этом флаге управление программе передаётся только после выполнения команды.
  3. Notify – при этом флаге программа получит специальное сообщение, при помощи которого сможет узнать о завершении выполнения команды, а управление передаётся без промедления.
Пример:
mciSendString('open d:\Sound.wav type waveaudio alias MyWave wait', nil, 0, 0); - связывает устройство WaveAudio под псевдонимом MyWave с файлом d:\Sound.wav.

Play
Эта команда запускает проигрывание для одного из следующих устройств: CD audio, digital-video, MIDI sequencer, videodisc, VCR, и waveform-audio.
Синтаксис команды:
'play lpszDeviceID lpszPlayFlags lpszFlags'
Параметры:
  • LpszDeviceID – идентификатор одного из устройств (или его псевдоним), прописанных в разделе [MCI] файла System.ini. Например: cdaudio, sequencer, waveaudio, avivideo.
  • LpszPlayFlags – флаг, определяющий тип проигрывания устройства. В Таблице 1 приведён список значений этого параметра для каждого устройства, а в Таблице 2 даны пояснения для этих значений.




Пример:
mciSendString('play cdaudio', nil, 0, 0); - музыкальный компакт-диск начинает проигрываться либо с начала, либо с позиции, зафиксированной командой "Пауза".

Status
Данная команда служит для определения различных параметров. Параметров много, поэтому все их приводить не буду. Остановлюсь лишь на командах для музыкальных компакт-дисков.
  • cdaudio type track number – для определения типа дорожки с номером number
  • current track – для определения номера текущей композиции length – для определения длины диска length track number – для определения длины композиции с номером number media present – для определения наличия диска в CD-ROM mode – для определения состояния проигрывания: playing, stopped, paused, open, not ready, parked, recording или seeking.
  • number of tracks – для определения количества дорожек на диске position – для определения текущей позиции диска position track number – для определения начальной позиции дорожки с номером number ready – возвращает истину, если устройство может принимать другие команды start position – начальная позиция диска
  • time format – формат времени, используемый в данной сессии работы с устройством.

    На этом закончим с теорией и перейдём к практике. Напишем при помощи функции mciSendString проигрыватель музыкальных компакт-дисков. Конечно, мы рассмотрели не все команды, которые нам понадобятся, но, я думаю, что проблем не возникнет, т.к. остальные команды достаточно просты в употреблении.
Создайте в Delphi новый проект и приведите форму к нужному виду (см. рис.1).



Для этого понадобятся следующие компоненты: TLabel (5 штук), TButton (7 штук), TListBox, TTrackBar и TTimer. К списку модулей добавьте MMSystem. Теперь объявите две глобальные переменные:
var
  sbReturn: array [1..64] of char; //для возвращаемых значений
  com: pchar; //посылаемая команда

После этого можно писать функции для управления проигрыванием:
//переход к дорожке с номером Track
procedure gototrack(Track: integer);
var
  com:pchar;
begin
  //установка формата времени в "Дорожка:Минуты:Секунды:Фреймы"
  com:='set cdaudio time format tmsf';
  mciSendString(com, @sbReturn, 64, 0);
  //начинаем проигрывание дорожки Track
  com:=pchar('play cdaudio from '+inttostr(Track));
  mciSendString(com, @sbReturn, 64, 0);
  //устанавливаем формат времени в миллисекунды
  com:='set cdaudio time format ms';
  mciSendString(com, @sbReturn, 64, 0);
end;

//получение номера текущей композиции
function GetCurrentTrack:byte;
var
  com:pchar;
  st:string;
begin
  result := 0;
  com := 'status cdaudio current track wait';
  if (mciSendString(com, @sbReturn, 64, 0) <> 0) then
    exit;
  st := trim(sbReturn);
  if (length(st) > 0) then
    result := strtoint(st);
end;

//количество композиций на диске
function GetTracksCnt: integer;
var
  st:string;
begin
  result := 0;
  com := 'status cdaudio number of tracks wait';
  if (mciSendString(com, @sbReturn, 64, 0) <> 0) then
    exit;
  st := trim(sbreturn);
  if (length(st) > 0) then
    result := strtoint(st);
end;

//переход к следующей композиции
procedure NextTrack;
var
  cur:integer;
begin
  cur:=GetCurrentTrack;
  //если текущая композиция – последняя, то переходим к первой
  if (cur <>then
    GoToTrack(cur+1)
  else
    GoToTrack(1);
end;

//переход к предыдущей композиции
procedure PrevTrack;
var
  cur:integer;
begin
  cur := getcurrentTrack;
  if (cur > 1) then
    GoToTrack(cur-1)
  //если текущая композиция – первая, то переходим к последней  
  else
    GoToTrack(GetTracksCnt);
end;

//длина композиции
function GetTrackLength(Track: integer): string;
begin
  com := pchar('status cdaudio length track '+inttostr(Track)+' wait');
  mciSendString(com, @sbReturn, 64, 0);
  result := trim(sbReturn);
end;

//длина диска
function GetCDLength:string;
begin
  com := pchar('status cdaudio length wait');
  mciSendString(com, @sbReturn, 64, 0);
  result := trim(sbReturn);
end;

//статус проигрывания
function GetStatus:string;
begin
  com := 'status cdaudio mode wait';
  mciSendString(com, @sbReturn, 64, 0);
  result := trim(sbReturn);
end;

//есть ли диск
function IsCDReady:string;
begin
  com := 'status cdaudio ready wait';
  mciSendString(com, @sbReturn, 64, 0);
  result := trim(sbReturn);
end;

//Начать проигрывание
procedure PlayCD;
begin
  mciSendString('play cdaudio', @sbReturn, 64, 0);
end;

//Пауза
procedure PauseCD;
begin
  mciSendString('pause cdaudio wait', @sbReturn, 64, 0);
end;

//Остановить проигрывание
procedure StopCD;
begin
  mciSendString('stop cdaudio wait', @sbReturn, 64, 0);
end;

//начальная позиция композиции
function GetTrackPos(Track:word): string;
begin
  com := pchar('status cdaudio position track '+inttostr(Track)+' wait');
  mciSendString(com, @sbReturn, 64, 0);
  result := trim(sbReturn);
end;

//текущая позиция диска
function GetCDPos: string;
begin
  com := pchar('status cdaudio position wait');
  mciSendString(com, @sbReturn, 64, 0);
  result := trim(sbReturn);
end;

А теперь напишем обработчики различных событий для компонентов:
//инициализируем устройство при загрузке
procedure TForm1.FormCreate(Sender: TObject);
begin
  mciSendString('open cdaudio', @sbReturn, 64, 0);
end;

//при выходе закрываем устройство
procedure TForm1.FormDestroy(Sender: TObject);
begin
  mciSendString('close cdaudio wait', @sbReturn, 64, 0);
end;

{при появлении формы записываем
в ListBox список композиций}
procedure TForm1.FormShow(Sender: TObject);
var
  i: word;
begin
  for i := 1 to GetTracksCnt do
  begin
    Listbox1.Items.Add(inttostr(i)+' '+GetTrackLength(i));
  end;
end
;

//кнопка Play
procedure TForm1.Button1Click(Sender: TObject);
begin
  PlayCD;
end;

//кнопка Pause
procedure TForm1.Button2Click(Sender: TObject);
begin
  PauseCD;
end;

//кнопка Stop
procedure TForm1.Button3Click(Sender: TObject);
begin
  StopCD;
end;

//кнопка Next (следующая композиция)
procedure TForm1.Button4Click(Sender: TObject);
begin
  NextTrack;
end;

//кнопка Prev (переход к предыдущей композиции)
procedure TForm1.Button5Click(Sender: TObject);
begin
  PrevTrack;
end;

//процедура для таймера, повторяющаяся каждую секунду
procedure TForm1.Timer1Timer(Sender: TObject);
var
  cur, i: word;
  st: string;
  cnt: byte;
  hour: word;
  min, sec: byte;
  t: integer;
begin
  //выводим состояние проигрывания
  label5.Caption:='Состояние: '+GetStatus;
  if (GetStatus <>'playing') and
      (GetStatus<>'stopped') and
      (GetStatus<>'paused') then exit;
  //устанавливаем формат времени в миллисекунды
  com:='set cdaudio time format ms wait';
  mciSendString(com, @sbReturn, 64, 0);
  cur:=GetCurrentTrack;
  //выделяем в списке композиций текущую
  ListBox1.ItemIndex:=cur-1;
  {выводим формат времени, эти строки только для примера, т.к. мы сами недавно   установили формат в миллисекунды}
  com:='status cdaudio time format wait';
  mciSendString(com, @sbReturn, 64, 0);
  //выводим информацию информацию
  label2.Caption:='Формат времени: '+trim(sbReturn);
  label3.Caption:='Начальная позиция: '+GetTrackPos(cur);
  label4.Caption:='Текущая позиция: '+GetCDPos;
  {устанавливаем ползунок TrackBar'a в нужную позицию, соответствующую текущему   положению проигрываемой композиции}
  TrackBar1.Max:=strtoint(GetTrackLength(cur)) div 1000;
  t:=strtoint(GetCDPos)-strtoint(GetTrackPos(cur));
  t:=t div 1000;
  TrackBar1.Position:=t;
  hour:=t div 3600;
  t:=t mod 3600;
  min:=t div 60;
  t:=t mod 60;
  sec:=t;
  st:=format('%d:%d',[min,sec]);
  if (hour > 0) then
  st:=inttostr(hour)+':'+st;
  //выводим время проигрывания текущей композиции
  label1.Caption:=st;
end;

//при двойном щелчке по композиции из списка начинаем её проигрывание
procedure TForm1.ListBox1DblClick(Sender: TObject);
begin
  GoToTrack(ListBox1.ItemIndex+1);
end;

//перемотка композиции на 5 секунд вперёд
procedure TForm1.Button6Click(Sender: TObject);
var
  t: integer;
begin
  t := strtoint(GetCDPos)+5000;
  StopCD;
  com := pchar('seek cdaudio to '+inttostr(t)+' wait');
  mciSendString(com, @sbReturn, 64, 0);
  PlayCD;
end;

//перемотка композиции на 5 секунд назад
procedure TForm1.Button7Click(Sender: TObject);
var
  t: integer;
begin
  t := strtoint(GetCDPos)-5000;
  StopCD;
  com := pchar('seek cdaudio to '+inttostr(t)+' wait');
  mciSendString(com, @sbReturn, 64, 0);
  PlayCD;
end;

    Вот и готов CD-проигрыватель, написанный на достаточно низком уровне при помощи, в принципе, всего одной функции, вынесенной в заглавие статьи. Вот такая вот функция! А ведь она может не только с музыкальным компакт-диском управляться: не следует забывать и про видеодиски, устройства записи и проигрывания мультимедиа и т.п.
Конечно, в CD-проигрывателе почти отсутствует контроль ошибок, и некоторые участки кода можно оптимизировать, но свою функцию эта программа в данной статье выполняет: демонстрирует применение функции mciSendString для различных задач.

Иван Ширко
ishyrko@gmail.com

Комментариев нет: