В данной статье рассматривается функция 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 – может принимать одно из следующих значений:
- Test – служит для определения возможности выполнения команды, при этом сама команда устройству не отправляется.
- Wait – при этом флаге управление программе передаётся только после выполнения команды.
- 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