Главная » Delphi » Музыка без медиаплеера

0

Неплохо бы сопровождать показ слайдов тихой музыкой, да? Но т. к. мы не делаем тут программу-проигрыватель (вот их, безусловно, не меньше миллиона итак имеется на все вкусы), то нельзя ли обойтись минимальными средствами, без всяких медиаплееров и ActiveX? Запросто, и для этого есть API-функция PiaySound, которая делает все замечательно, кроме одного — она умеет проигрывать только WAV-файлы. Но мы ведь хотели обойтись минимальными средствами… Так что переживем — превратить любой файл в WAV при надобности очень легко, для этого есть множество программ. А так— можно использовать, например, файлы рингтонов для мобильников. Вопрос не в файлах и не в способе их воспроизведения, а в том, чтобы сделать все это удобным для пользования. И как мы сейчас увидим, осуществить это с "полтычка" не получится.

Два музыкальных клипа с WAV-файлами для отработки программы я расположил в отдельной папке внутри папки с проектом (GIaval5\Music). Ограничимся тем, что будем воспроизводить музыку лишь во время автоматической демонстрации. В предложение usee модуля slide.pas добавим модуль mmSystem и введем две новых переменных:

fmusic:strings’1; FlagMusic:byte=0;

В строке fmusic мы будем хранить имя файла с клипом, а флаг FiagMusic нам понадобится для того, чтобы запускать функцию PiaySound только один paj, иначе плавного воспроизведения нам не добиться. Теперь добавим в меню Демонстрация после пункта Запуск пустой разделительный пункт (знак "-" в Caption), а после него два пункта: Музыкальное сопровождение (Musici, <Ctrl>+<M>) и Выбрать клип (BrowseMusic, <Ctrl>+<K>). На форму поставим еще один диалог открытия файла (0penDiaiog2), и в свойстве Filter у него запишем Файлы WAVI * .wav.

В процедуру по таймеру (TimeriTimer) допишем такой фраг мент:

if fmusico” then {если какое-то имя файла есть/

if Musicl.Checked=True then

begin

if FlagMusic=0 then /если флаг =01 begin

if not PiaySound(PChar(fmusic),0,SND_NOSTOP+SND_ASYNC)

than Application.MessageBox

(‘ Устройство занято или неправильный формат’,’Ошибка’,МВ_ОК); FlagMusic:=l; (в следующий раз не воспроизводим/ end; end;

Здесь, если у нас имя файла с клипом не пустое, и при этом пункт Музыкальное сопровождение отмечен (на самом деле это перекрывающиеся требования— мы в дальнейшем сделаем так, чтобы при пустом имени файла нельзя было бы отметить Musici, но хуже не будет), мы при первом событии таймера вызываем функцию Piaysound. С первым ее параметром все ясно, равенство второго нулю означает, что звук берется не из ресурса, а из внешнего файла, а набор констант в третьем (snd nostop+snd async) означает, что звук будет воспроизводиться только, если ничего не воспроизводится другого (не будет прерывать уже запущенное воспроизведение) и при этом функция сразу передаст управление системе — т. е. звучать будет до тех пор (при необходимости вкруговую), пока мы все это дело не остановим. Если звук включен, то при следующем событии таймера обращения к Piaysound не будет (FlagMusic устанавливается в 1), иначе будут заметные щелчки. Сообщение "Устройство занято…" на самом деле возникнет только, если функция не может проиграть файл по причине занятости устройства, а при неправильном имени файла почему-то значение False не возвращается, но это не очень важно — мы и не будем вводить имя вручную.

Теперь в первую очередь наладим выключение. В процедуру Runclick добавим при остановке демонстрации следующее:

end else (иначе, если демонстрация уже идет! begin

Run.Caption "’Запуск';

(меняем название пункта меню обратно на Запуск) Timerl.Enabled:=False; (таймер остановлен) PlaySound(Pchar(fmusic),О, SND_PURGE); (выключили музыку) FlagMusic:=0;(в следующий раз опять запустим) end;

Константа snd purge и означает остановку звучания. Теперь надо наладить обращение к пунктам меню, в первую очередь загрузку имени файла:

procedure TForml.BrowseMusicClick(Sender: TObject); var st:string;

begin /пункт меню Выбрать клип) If OpenDialog2.Execute then begin

fmusic:=OpenDi alog2.FileName; FlagMusic:=0; Iбудем играть заново) PlaySound(Pchar(fmusic),0, SND_PURGE); end

else {ничего не нашли) begin

fmusic:=”; {если не наши файл, то очистили имЛ) Musicl.Checked:=False; (остановили музыку) PlaySound(Pchar(fmusic),0, SND_PURGE); end;

st:=StaticText1.Caption;

if (st=") or ( (st [1]=’K’) and (sto’M) then (т. е. или пусто, или надпись "Клип" по-русски) StaticTextl.Capt ion: =’Клип: ‘+ Zmusi c; end;

Здесь (начнем рассмотрение снизу) у нас имя загруженного файла для контроля выведется в строку staticTexti, где обычно располагается имя папки с картинками — но только в том случае, если она пустая или там было отображено именно имя клипа, т. е. если мы загружаем его в самом начале после запуска программы. Если там уже есть имя папки, то клипом придется пожертвовать. Если мы в диалоге нажали кнопку Отмена, то имя очистится, и снимается отметка с пункта меню Musicl. При этом также останавливается воспроизведение, если оно было запущено. И, наконец, при успешном завершении диалога мы останавливаем воспроизведение, и в следующий раз оно начнется заново только при условии, что пункт меню Musicl отмечен — это надо сделать отдельно. К чему мы сейчас и перейдем, вот какой обработчик- обращения к пункту Musicl мы сделаем:

procedure TForml.MusiclClick(Sender: TObject);

begin {пункт меню Музыка)

if fmusic=” then exit;

if Musicl.Checked=False

then

begin {подготавливаем клип к запуску) Musicl.Checked:=True; FlagMusic:=0; end else

begin {иначе выключаем) Musicl.Checked:=False;

PlaySound(Pchar(fmusic),О, SND_PURGE); end; end;

Здесь все, кажется, понятно — по первому обращению пункт становится отмеченным и взводится флаг FiagMusic, по второму— отметка снимается, а звучание останавливается (с флагом при этом ничего делать не надо). Резюмируем логику работы: музыка играет тогда, когда мы установили флажок в пункте меню Музыкальное сопровождение (нажали <Ctrl>+<M>), если предварительно было загружено имя музыкального клипа. Если нажать повторно, музыка остановится. Если в процессе загрузить другой клип, то первый прервется и начнется второй. Можно ничего не загружать, если нажать в диалоге открытия файла кнопку Отмена, в этом случае имя файла окажется пустым.

Манипулируя с диалогом 0penDialog2, мы не должны забывать про openDialogl — ведь мы меняем текущую папку (именно на этот случай я в списке файлов с изображениями сохранял полное имя файла с путем к нему). Поэтому позаботимся о том, чтобы устанавливать для openDialogl начальную папку принудительно (для окна открытия папки этого делать не надо). В процедуре openciick добавим в самом начале оператор: OpenDialogl.InitialDir:=stpath;

Все, что мы сейчас проделали, есть типичный пример функциональности, внести которую можно только вручную, никакие автоматизированные системы проектирования вам тут помочь не смогут. Надо держать в голове всю логику наших действий и реакцию программы, иначе легко наворотить такого, что программой вообще пользоваться будет нельзя. Вы заметили, например, что я здесь не выводил почти никаких сообщений (кроме редко возникающего "Устройство занято…") — не надо "грузить" пользователя лишней информацией. Если он не удосужится прочесть справку, то ничего страшного не произойдет, когда музыка вдруг не заиграет, это совершенно второстепенная функция. Но вот в справке все надо объяснить подробно.

Но это еще не все! Нам надо, конечно, все это запоминать — если заставлять пользователя загружать клип при каждом запуске, то можно было бы ничего и не затевать с музыкой вообще. Обратимся к нашей главной по этой части процедуре FormlCreate и по условию if SectionExists (‘Main’) добавим такие строки:

{музыка:}

fmusic:=ReadString(‘Main’,’Musicfile’,”) ;

{по умолчанию пусто)

Musicl.Checked:=Read3ool(‘Main’,’Music*,False); if fmusico” then

begin lуста новки диалога для поиска клипа) StaticTextl .Caption:=’KiMn: ‘+?music;;

{отображаем имя клипа! 0penDialog2 . InitialDir :-F,xtractFilePath (fmusic) ; 0penDialog2.FileName:=ExtractFileName(fmusic); end else {если имя клипа пусто/ 0penDialog2.InitialDir:=ExtractFilerath(Application.ExeName);

To есть при пустом имени клипа мы будем искать, начиная с папки с программой, вряд ли в папке с картинками найдется клип. Далее мы создаем эти два пункта в первый раз (ниже по условию "если секция еще не создана"):

WriteString(‘Main’,’Musicfile’,”); /по умолчанию пусто/ WriteBool(‘Main’,’Music’, False);

Л собственно запоминание мы осуществим при выходе из программы ч имеющейся у нас уже процедуре FormDestroy:

procedure TForml.FormDestroy(Sender: TObjeet); begin

FileList.Destroy; {уюмтожаем стски) Sizelist.Destroy; {запоминаем музыку:)

IniFile:=TlniFile.Create(ChangeFileExt(ParamStr(0), ‘.ini’));

{открываем inij

IniFile.WriteString(‘Main’,’Musicfile’,fmusic); {имя файла клипа I IniFile.WriteBool(‘Main’,’Music’,Musicl.Checked); IniFile.Destroy; end;

Заметки на полях

У читателя уже, несомненно, возник недоуменный вопрос — а зачем было возиться с запоминанием установок в разных местах для других параметров, нельзя ли их также было собрать на закрытие программы? Отвечаю: те установки были более важными с точки зрения основной функциональности. Если у вас при запущенной программе вдруг выключится компьютер, то все установки при следующем запуске восстановятся — все, кроме музыкальных, которые не успели сохраниться. И мы тут этим сознательно жертвуем, чтобы не усложнять программу, но когда дело доходит до вещей существенных, то следует всегда их сохранять в дисковом файле своевременно. Мне даже не очень понятно, для чего в Word, например, придумана такая сложная и довольно "глючная" система восстановления файлов после сбоев, если можно просто своевременно закрывать дисковые файлы после каждого сохранения. Возможно, что я действительно чего-то недопонимаю, или, скорее всего, все было задумано "как надо", а получилось "как всегда". Даже если у вас текущее содержимое находится ао временном файле (что разумно), его также можно держать закрытым, т. е. с заведомо сохранной информацией, и лишь периодически сбрасывать туда последние изменения из оперативной памяти. Тем более что есть прекрасный механизм безопасной двухступенчатой записи, который легко воспроизаести и самостоятельно. Заключается он а следующем: на первом шеге запись производится в некий временный файл (LOG), после чего файл, который действительно необходимо перезаписать, помечается, как находящийся а переходном состоянии. Затем производится запись в зтот файл. И только после этого с файла снимается пометка о переходном состоянии. Если сбой произойдет а процессе первой перезаписи (LOG-фвйла), то все останется, как было. Если же а процессе второй, то после восстановления программа просто продолжит перезапись. Так устроена, например, запись в реестр Windows ХР.

Вот теперь, кажется, все в порядке — если и есть какие-то несообразности, то мне они незаметны, и я предлагаю читателям самим протестировать программу окончательно. Не зря же корпорации даже нанимают тестировщиков за деньги, а наша программа уже вполне вышла на тот уровень, когда один человек отловить сразу все ошибки не в состоянии. Учитывая, особенно, что мы все еще ее не доделали, теперь займемся, наконец, "превьюшками".

Источник: Ревнч Ю. В.  Нестандартные приемы программирования на Delphi. — СПб.: БХВ-Петербург, 2005. — 560 е.: ил.

По теме:

  • Комментарии