Сделай сам на Delphi: Визуализационный плагин к Winamp

(плагины + исходники на Delphi)

Работать с плеером Winamp мы научились в статье Управление Winamp. Теперь перейдем к разработке плагинов для него.
Плагины к Winamp бывают пяти видов:

  1. Input – плагины для проигрывания различных форматов;
  2. Output – для записи музыки в различных форматах;
  3. General Purpose – плагины общего назначения;
  4. DSP/Effect – для обработки звука;
  5. Visualization – плагины, которые делают что-нибудь в такт музыке.

Плагин к Winamp представляет из себя обычную динамическую библиотеку (DLL – Dynamic Link Library), которая должна экспортировать необходимую для данного плагина информацию. Шаблон библиотеки для любого типа плагинов можно взять на сайте Winamp’a – http://www.winamp.com. В этой статье мы рассмотрим процесс создания самого впечатляющего, на мой взгляд, типа плагинов – визуализационного.
В составе визуализационного плагина можно выделить заголовок и модули. Вот формат этих частей:

Заголовок плагина
winampVisHeader = packed record
  version: Integer;
  description: PChar;
  getModule: function( which: integer ): PwinampVisModule; cdecl;
end;

version – версия, для данного типа плагинов должна быть равна $101 (знак ‘$’ указывает на шестнадцатеричную систему счисления).
description – описание плагина.
getModule – функция, которая должна возвратить указатель на тело плагина.

Модуль плагина
winampVisModule = packed record
  //описание модуля
  description: PChar;
  //идентификатор окна Winamp'a (заполняет Winamp)
  hwndParent: HWND;
  // идентификатор библиотеки (заполняет Winamp)
  hDllInstance: HWND;
  //sample rate (заполняет Winamp)
  sRate: Integer;
  //количество каналов (заполняет Winamp)
  nCh: Integer;
  //задержка между получением данных от Winamp'a и
  //реакцией на них (в мс)
  latencyMs: Integer;
  //количество мс между получениями данных от Winamp'a
  delayMs: Integer;
  //следующие 4 параметра и есть те данные о звуке, которые мы
  //будем через некоторый промежуток времени получать от Winamp'a
  spectrumNch: Integer;
  waveformNch: Integer;
  spectrumData: array[0..1, 0..575] of byte;
  waveformData: array[0..1, 0..575] of byte;
  //процедура, которая будет вызываться при нажатии на кнопку "Конфигурация"
  Config: procedure( thismod: PwinampVisModule ); cdecl;
  //функция, вызываемая при инициализации плагина, должна возвратить 0, если всё в порядке
  Init: function( thismod: PwinampVisModule ): integer; cdecl;
  //функция, вызываемая при получении очередной порции данных
  //от Winamp'a, именно здесь нужно реагировать на изменение
  //музыкальных данных, должна возвратить 0, если всё в порядке,
  // или 1, если плагин завершает свою работу
  Render: function( thismod: PwinampVisModule ): Integer; cdecl;
  //процедура, вызываемая при завершении работы плагина
  Quit: procedure( thismod: PwinampVisModule ); cdecl;
  //здесь можно хранить указатель на свои данные
  userData: pointer;
end;

Ну а теперь сделаем простой и абсолютно бесполезный визуализационный плагин, маленький анализатор спектра. Создайте в Delphi DLL. После этого создайте форму и положите на неё шесть компонентов TProgressBar, для которых свойство Orientation установите в положение pbVertical, а Max – в 300. Пример расположения компонент можете посмотреть на рис.1.

Плагин к Winamp

Всё, форму можно больше не трогать, займёмся теперь DLL. Вот код библиотеки:

library vis_MyFirstPlug;
uses
  Windows, forms,
  Unit1 in 'Unit1.pas' {Form1};
const
  //версия
  VIS_HDRVER = $101;
  //класс окна
  szAppName = 'OurPlug';
  //описание плагина
  descr = 'Мой первый VIS плагин';
  //описание модуля
  mod1_descr = 'А это единственный модуль моего плагина';
type
  //типы заголовка и тела плагина
  PwinampVisModule = ^winampVisModule;
  winampVisModule = packed record
  description: PChar;
  hwndParent: HWND;
  hDllInstance: HWND;
  sRate: integer;
  nCh: integer;
  latencyMs: integer;
  delayMs: integer;
  spectrumNch: integer;
  waveformNch: integer;
  spectrumData: array[0..1,0..575] of byte;
  waveformData: array[0..1,0..575] of byte;
  Config: procedure(thismod: PwinampVisModule);cdecl;
  Init: function(thismod: PwinampVisModule): integer;cdecl;
  Render: function(thismod: PwinampVisModule): integer;cdecl;
  Quit: procedure(thismod: PwinampVisModule);cdecl;
  userData: pointer;
end;
PwinampVisHeader = ^winampVisHeader;
winampVisHeader = packed record
  version: integer;
  description: PChar;
  getModule: function( which : integer ) : PwinampVisModule; cdecl;
end;
var
  //заголовок плагина
  hdr: winampVisHeader;
  //модуль плагина
  mod1: winampVisModule;

//далее следуют процедуры, назначение которых описано выше
function getModule(which: integer): PwinampVisModule;cdecl;
begin
  case which of
    0: result := @mod1;
    else
      result := nil
  end;
end;

function winampVisGetHeader: PwinampVisHeader; cdecl;
begin
  result := @hdr;
end;

procedure config(this_mod: PwinampVisModule); cdecl;
begin
  MessageBox(mod1.hwndParent, 'Настроек нет', 'Окно настроек', mb_OK);
end;

function init(this_mod: PwinampVisModule): integer; cdecl;
begin
  //показываем форму с ProgressBar'ами
  Form1.Show;
  result := 0;
end;

function render(this_mod: PwinampVisModule): integer; cdecl;
begin
  //при поступлении новых данных от Winamp'a устанавливаем
  //высоту столбиков ProgressBar'ов в соответствии со значением
  //музыкальных данных шести колонок
  Form1.ProgressBar1.Position := this_mod.spectrumData[0,1];
  Form1.ProgressBar2.Position := this_mod.spectrumData[0,2];
  Form1.ProgressBar3.Position := this_mod.spectrumData[0,3];
  Form1.ProgressBar4.Position := this_mod.spectrumData[0,4];
  Form1.ProgressBar5.Position := this_mod.spectrumData[0,5];
  Form1.ProgressBar6.Position := this_mod.spectrumData[0,6];
  result := 0;
end;
procedure quit(this_mod: PwinampVisModule); cdecl;
begin
end;

//экспортируемая функция
exports
  winampVisGetHeader;
begin
  //заполняем начальные параметры плагина
  hdr.version := VIS_HDRVER;
  hdr.description := PChar(descr);
  hdr.getModule := @getModule;
  mod1.description := PChar(mod1_descr);
  mod1.hwndParent := 0;
  mod1.hDllInstance := 0;
  mod1.sRate := 0;
  mod1.nCh := 0;
  mod1.latencyMs := 25;
  mod1.delayMs := 100;
  mod1.spectrumNch := 2;
  mod1.waveformNch := 0;
  mod1.Config := @config;
  mod1.Init := @init;
  mod1.Render := @render;
  mod1.Quit := @quit;
  //создаём форму с ProgressBar'ами
  Application.CreateForm(TForm1, Form1);
end.

Полученный плагин нужно скопировать в папку Winamp\Plugins. После этого в окне свойств Winamp’a будет доступен новый плагин (рис.2).

Плагин к Winamp 

Этот пример призван продемонстрировать, как использовать всю мощь VCL в Delphi при создании плагинов для Winamp’a. Но размер такого плагина будет в районе 300-400 кб (зависит от версии Delphi). В связи с этим, лучше отказаться от использования VCL, а писать плагины на WinApi. Далее приведён код плагина, который заставляет мигать лампочки на клавиатуре (Num Lock, Caps Lock и Scroll Lock) в такт музыке.

library KeybLight;
uses Windows;
const
  VIS_HDRVER = $101;
  szAppName = 'KeybLight';
  descr = 'Лампочки на клавиатуре (zavsoft.ru)';
  //описание плагина
  mod1_descr = 'Лампочки на клавиатуре';
type
  //указатель на структуру плагина
  PwinampVisModule = ^winampVisModule;
  //структура плагина
  winampVisModule = packed record
    description: PChar;
    hwndParent: HWND;
    hDllInstance: HWND;
    sRate: Integer;
    nCh: Integer;
    latencyMs: Integer;
    delayMs: Integer;
    spectrumNch: Integer;
    waveformNch: Integer;
    spectrumData: array[0..1, 0..575] of Byte;
    waveformData: array[0..1, 0..575] of Byte;
    Config: procedure( thismod: PwinampVisModule ); cdecl;
    Init: function( thismod: PwinampVisModule ): Integer; cdecl;
    Render: function( thismod: PwinampVisModule ): Integer; cdecl;
    Quit: procedure( thismod: PwinampVisModule ); cdecl;
    userData: pointer;
  end;
  //заголовок плагина
  PwinampVisHeader = ^winampVisHeader;
  winampVisHeader = packed record
    version: Integer;
    description: PChar;
    getModule: function( which: integer ): PwinampVisModule; cdecl;
  end;
var
  hdr: winampVisHeader;
  mod1: winampVisModule;

//процедура для установки состояния клавиш Caps, Num, Scroll Lock
procedure SetLock(N: Byte; State: Boolean);
var
  KeyState: TKeyboardState;
  c: Byte;
begin
  case N of
    0: c := VK_NUMLOCK;
    1: c := VK_CAPITAL;
    2: c := VK_SCROLL;
  end;
  GetKeyboardState(KeyState);
  if (State and (not((KeyState[c] and 1) = 1)) or ((not State) and ((KeyState[c] and 1) = 1))) then
    //key press
    keybd_event(c, $45, (KEYEVENTF_EXTENDEDKEY or 0), 0);
    //key release
    keybd_event( c, $45, (KEYEVENTF_EXTENDEDKEY or KEYEVENTF_KEYUP), 0);
  end;

//возвращает указатель на нужный модуль
function getModule(which: integer): PwinampVisModule; cdecl;
begin
  case which of
    0: result := @mod1;
    else result := nil
  end;
end;

//передача заголовка плагина Winamp'у
function winampVisGetHeader: PwinampVisHeader; cdecl;
begin
  result := @hdr;
end;

//эта процедура вызывается при нажатии на кнопку "Конфигурация"
procedure config( this_mod: PwinampVisModule ); cdecl;
begin
  MessageBox(this_mod.hwndParent, 'Автор Иван Ширко (ishyrko@gmail.com)'+#13+' https://zavsoft.ru', 'Лампочки на клавиатуре', MB_OK)
end;

//инициализация плагина, если всё нормально, то должны вернуть 0
function init( this_mod: PwinampVisModule ): integer; cdecl;
begin
  result:=0;
end;

//именно эта функция заставляет лампочки мигать
function render(this_mod: PwinampVisModule): integer; cdecl;
begin
  //в зависимости от уровня звука гасим\зажигаем
  //соответствующие лампочки
  if (this_mod.spectrumData[0,1] > 40) then
    SetLock(0, True)
  else
    SetLock(0, False);
  if (this_mod.spectrumData[0,5] > 40) then
    SetLock(1, True)
  else
    SetLock(1, False);
  if (this_mod.spectrumData[0,10] > 40) then
    SetLock(2, True)
  else
    SetLock(2, False);
  result := 0;
end;

//наши действия при закрытии плагина
procedure quit( this_mod: PwinampVisModule ); cdecl;
begin
end;

//экспортируемая функция
exports
  winampVisGetHeader;
begin
  //задаём начальные значения для структуры плагина
  hdr.version := VIS_HDRVER;
  hdr.description := PChar(descr);
  hdr.getModule := @getModule;
  mod1.description := PChar(mod1_descr);
  mod1.hwndParent := 0;
  mod1.hDllInstance := 0;
  mod1.sRate := 0;
  mod1.nCh := 0;
  mod1.latencyMs := 25;
  mod1.delayMs := 25;
  mod1.spectrumNch := 2;
  mod1.waveformNch := 0;
  mod1.Config := @config;
  mod1.Init := @init;
  mod1.Render := @render;
  mod1.Quit := @quit;
end.

После компиляции в Delphi такой плагин занимает менее 20 килобайт!
Готовую библиотеку и исходные тексты можно найти на форуме. К сожалению, мне не удалось найти вариант зажигания лампочек под WinXP без изменения состояния клавиш, так что печатать при включенном таком плагине будет проблематично. Но в качестве примера сгодится:)
Как видите, всю работу по обработке звука Winamp берёт на себя, поэтому создавать плагины к этому популярнейшему плееру может даже человек, весьма далёкий от музыки. В следующий раз мы поговорим о плагинах общего назначения.

16 Replies to “Сделай сам на Delphi: Визуализационный плагин к Winamp”

  1. Спасибо за модули. В выходные попробую разобраться подробнее

  2. Статья очень пригодилась, все просто и понятно. Огромное спасибо автору!:)

  3. Люблю когда по сути, мне понравилось.

  4. У меня коряво получается, но все равно спасибо.

  5. Спасибо большое!!! Одно но : в 2 плагине в коде нужно заменить «********* keybd_event(c, $45, (KEYEVENTF_EXTENDEDKEY or 0), 0); //key release *****» на «********* keybd_event(c, $45, (KEYEVENTF_EXTENDEDKEY or 0), 0) else //key release *****». Иначе просто все лампочки всегда выключаються.

  6. хм…интересно..спасибо за идею, давненько хотел что-либо подобное попробовать…

  7. Извините, может не по теме, но возможно вы подскажете, как сделать, что бы винамп запускался с моим скином на любом компе (на флешке если принесу и т.д.)… Т.е. сделать свой скин скином по умолчанию…

  8. Полезная информация. Я уже много лет пользуюсь Винампом но никогда не приходило на мысль что с помощью средств среды Delphi можно менять шаблон.

Comments are closed.