Как стать автором
Обновить

Трекерная музыка. Приобщаемся к великому

Время на прочтение 8 мин
Количество просмотров 17K
 Представляю вашему вниманию подборку из 15 моих любимых V2M-композиций от товарищей Farbrausch. Для прослушивания достаточно запустить Exe-шник. Обращаем внимание на его размер и на качество звучания!

narod.ru/disk/9788383000/q.exe.html

(Это не вирус, клянусь кармой, сам лично компилил)

Сразу оговорюсь, не работает в Висте и Windows 7, почему — не знаю =(, т.к. не использую и полный профан в системном программировании, если кто подскажет, почему — буду благодарен. В *nix под Wine должно работать.

Прежде чем начать повествование рекомендую освежить в памяти эти хабратопики

Music. The code inside. Razor 1911, Farbrausch…
Деморолики. Искусство программирования.

 Как то так случилось, что я увлекся всякими трекерными мелодиями.
У меня в универе был лектор (по ТФКП), который любил на лекциях пофилософствовать о связи музыки и математики, и довольно интересно, надо сказать. И, по-моему, трекерная музыка — это именно та музыка, которая ближе всего находится к математике.

 Для непосвещенных скажу, что это музыка, звучащая в кряках/кейгенах, многих компьютерных играх, демосценерских роликах… По правде сказать, я искренне не понимаю, почему, например, именно midi-мелодии так прочно укоренились в мобильниках, ведь трекерная музычка весит примерно столько же, а звучит гораздо лучше. Форматов трекерного музона очень много: .mod, .it, .xm, .s3m, ..., и т.д. Основная их особенность — очень малый «вес» (десятки-сотни килобайт, хотя зависит от битности семплов, могут быть и мегабайты), при этом красивое (по сравнению с миди-музоном, а, имхо, и просто красивое) звучание. Более подробную информацию можно с легкостью отыскать на бескрайних просторах интернета.

 Опять отвлекусь и дам ссылку на другие «самые-самые» трекерные композиции: http://websound.ru/tracked-music.htm (и вообще всячески рекомендую этот сайт о музыке websound.ru). Только убедительно заклинаю слушать их не через Winamp (а он может), а через XMPlay — по моему опыту — самое адекватное воспроизведение модулей.

 Ребята из Farbrausch (в лице kb) пошли дальше и создали свой музыкальный движок v2. Музыка, созданная в нем и звучит в потрясающих демках этого демомейкерского коллектива. Собственно, я подозреваю, самой v2m-музыки в природе существует не так много (если неправ — поправьте, буду приятно удивлен и благодарен), но то что есть можно скачать, например, тут (около 200 изумительно звучащих мелодий размером всего 1 Мб) для проигрывания рекомендую in_v2m.dll плагин для винампа отсюда (т.к. сдается, что новая версия 1.5 плагина, доступная на авторском сайте у меня проигрывала некоторые v2m-ки некорректно). Отличие этого формата от классических трекерных мелодий в том, что в нем совершенно не используются семплы, а все инструменты и эффекты, включая синтез голоса, программно генерируются движком.

 Ну и вот мне захотелось немножко «приобщиться к великому» и попробовать скомпилять экзешник с музычкой, во-первых чтоб узнать, как же эти гении-демосценеры добиваются таких крошечных размеров демок, а во-вторых просто чтоб поделиться с вами своими любимыми v2m-композициями.

 Я взял за основу файлик tinyplayer.cpp приведенный как пример использования API (отсюда). Он представлял собой исходник проигрывания одной музычки, которая вкомпилировалась в него прямо в коде типа такого:

const unsigned char theTune[] = {
0xe0, 0x01, 0x00, 0x00, 0xd7, 0x84, 0x02, ...


подключаемом в .h файле. Мне надо было создать аналогичный .h-файл но с нужными мне композициями.

 Скопировал нужные v2m-ки в папку, создал там скриптик gen_tunes.py содержанием:

import glob

out = open('tune1.h', 'w')
listOut = open('list.txt','w')

def writeTune(i, data):
  out.write('\nconst unsigned char tune_%s[] = {\n\t' % i)
  
  t = 0
  
  lst = []
  for b in data:
    lst.append(hex(ord(b)))
    t+=1
    if t % 16 == 0:
      lst[-1] += '\n\t'
      
  out.write(', '.join(lst))
  out.write('};\n\n')

def main():
  i = 0

  for fn in glob.glob('*.v2m'):
    f = open(fn, 'rb')
    data = f.read()
    
    writeTune(i, data)
    listOut.write("%s, %s\n" % (i, fn))
    
    i += 1
    
main()  


* This source code was highlighted with Source Code Highlighter.


и запустив его, получил файл tune1.h «весом» 7.84 Мб, суммарный вес файликов .v2m же был 1.46 Мб.

И как я уже говорил, tinyplayer.cpp был дополнен следующим образом:

//
// tinyplayer: An example for using libv2 and a lesson in getting
//       Win32 executables small. Written by Tammo "kb"
//       Hinrichs in 2004. This file (as well as the .sln
//       and the .vcproj file) is in the public domain,
//       do with it what you want.
//
//       farbrausch consumer consulting, Dec. 2004
//

// it is advised to look into the compiler settings to see why the
// final exe takes only 52k (or 12.5k after being packed with an
// executable packer)

// we need: windows...
#define WIN32_LEAN_AND_MEAN
#include <windows.h>

// ... the libv2 ...
#include "libv2.h"

// ... and our tune.
//#include "tune.h"
#include "tune1.h"

// --------------------------------------------------------------------------------

// a few fakes coming up because this gets compiled without any stdlib and so
// we don't even have printf() :

static HANDLE stdout;

static void print(const char *text)
{
  unsigned long bw;
  int len=0;
  while (text[len]) len++; // yeah, strlen() also is a luxury. :)
  WriteFile(stdout,text,len,&bw,0);
}

static const char* digits[]={"0","1","2","3","4","5","6","7","8","9"};
static void print(long num) {
  int nums[20];
  int n = 0;

  if (num == 0) {
    print("0");
  }

  while (num != 0) {
    nums[n++] = num % 10;
    num = num / 10;
  };
  n--;

  for (int nn=n;nn>=0;nn--) {
    print(digits[nums[nn]]);
  }
}

// VC needs this symbol as soon as something uses floating point numbers
// (and libv2 does):
extern "C" int _fltused;
int _fltused;

// --------------------------------------------------------------------------------
const char* songs[] = {
  "acid in space",
  "agony remix",
  "breeze",
  "chiptown",
  "cracked zone",
  "fr-014 garbage collection - main",
  "fr-037 the code inside",
  "full access",
  "gamma projection",
  "josie3",
  "multiscenist",
  "trance style",
  "ultra short",
  "welcome to vectronix disco",
  "zuibath"
};

long songDurations[] = { // in sec
  233,
  296,
  181,
  157,
  267,
  128,
  211,
  225,
  152,
  207,
  177,
  465,
  186,
  208,
  170
};

const unsigned char* songData[] = {
  tune_0,tune_1,tune_2,tune_3,tune_4,tune_5,tune_6,tune_7,
  tune_8,tune_9,tune_10,tune_11,tune_12,tune_13,tune_14
};

static void writeSongLine(int current) {
  print(current+1);print(" - ");print(songs[current]);print(" (");
  long duration = songDurations[current];
  print(duration/60); // min
  print(":");
  long sec = duration%60;
  if (sec<10)
    print("0");
  print(sec);
  print(")");
}

static void playSong(int current) {
  print("\r                               \r");
  writeSongLine(current);
  ssStop();
  ssClose();
  ssInit(songData[current],GetForegroundWindow());
  ssPlay();
}

int SONG_NUM_MAX = 14;

int main(int argc, char *argv[])
{
  // we need this for print() to work
  stdout=GetStdHandle(STD_OUTPUT_HANDLE);

  // print a bunch of senseless info..
  print("Farbrausch Tiny Music Player v0.2\n");
  print("Code and Synthesizer (C) 2000-2004 kb/Farbrausch\n");

  print("Compiled by: Xonix\n\n");

  print("Tunes:\n\n");

  for (int i=0; i<=SONG_NUM_MAX; i++) {
    writeSongLine(i);
    print("\n");
  }

  print("\n\n");

  int current = 0;

  print("Now playing (ESC to quit, UP/DOWN to select):\n");
  playSong(current);

  for ( ; ; ) {
    if ( GetAsyncKeyState ( VK_ESCAPE ) != 0 )
      break;
    else if ( GetAsyncKeyState ( VK_UP ) != 0 ) {
      if (current > 0)
        current--;
      else
        current = SONG_NUM_MAX;

      playSong(current);

      while(GetAsyncKeyState ( VK_UP ) != 0) {Sleep(10);}
    }
    else if ( GetAsyncKeyState ( VK_DOWN ) != 0 ) {
      if (current < SONG_NUM_MAX)
        current++;
      else
        current = 0;

      playSong(current);

      while(GetAsyncKeyState ( VK_DOWN ) != 0) {Sleep(10);}
    }

    if (ssGetTime() > (songDurations[current] + 1) * 1000) { // song ended
      if (current < SONG_NUM_MAX)
        current++;
      else
        current = 0;

      playSong(current);
    }
    Sleep ( 10 );
  }

  // stop and deinit the player
  ssStop();
  ssClose();

  // ... and we're done.
  ExitProcess(0);
}


* This source code was highlighted with Source Code Highlighter.


 Обращаю ваше внимание, что в программе используется только windows.h (помимо, собственно, libv2.h и tune1.h), и ничего более (все для компактности получаемого .exe, поэтому print и форматирование мы вынуждены писать собственноручно).

 Для компиляции использовал как ни странно QtCreator + Mingw, ибо Visual Studio, простите, не осилил (люблю когда все явно, т.е. я вижу что запускается при компиляции и т.д.).

Тут обращаю внимание товарищей, потянувшихся к кнопочке "-", что это пожалуй первый, осознанно скомпиленный мною C++ код =)

 Проект выглядел так:
# -------------------------------------------------
# Project created by QtCreator 2009-06-10T03:58:51
# -------------------------------------------------
QT -= core \
gui
TARGET = v2m_palyer_qt
CONFIG += console
CONFIG -= app_bundle
TEMPLATE = app
SOURCES += main.cpp
HEADERS += tune.h tune1.h \
libv2.h
OTHER_FILES +=
LIBS += -L"C:\Program Files\Microsoft Visual Studio .NET 2003\Vc7\PlatformSDK\Lib" \
-L"D:/TEST/v2m/libv2_1.0/v2m_palyer_qt" -llibv2 -ldsound -luser32 -lkernel32


 В результате компиляции получаем файлик v2m_palyer_qt.exe. Но это еще не все ). Теперь надо «ужать» этот файлик. Для этого я использовал обычный для таких задач executables compressor upx с настройкой --best, ужавший мой экзешник с 1.48 Мб до 72 Кб.

 Так же пробовал beroexepacker — ужал лучше, но остановился на первом варианте, т.к. антивирусы знают upx и умеют его распаковывать в поисках вирусов, а после этого товарища — не умеют.

И наконец пробовал «ужиматор» kkrunchy — именно им пользуются сами ребята из Farbrausch, и действительно он ужал ровнехонько до 64000 байт, но при этом exe-шник ну ооооочень долго стартовал.

Из недостатков: екзешник использует глобальные хуки для перелистывания/выхода. Как сделать иначе — не знаю, а автор kb по этому поводу оставил комментарий
// yep, I know this will stop even if we don't have focus. I simply don't care.
я тоже не стал заморачиваться.

Спасибо за внимание.
Теги:
Хабы:
+60
Комментарии 81
Комментарии Комментарии 81

Публикации

Истории

Ближайшие события

Московский туристический хакатон
Дата 23 марта – 7 апреля
Место
Москва Онлайн
Геймтон «DatsEdenSpace» от DatsTeam
Дата 5 – 6 апреля
Время 17:00 – 20:00
Место
Онлайн