Компьютерные программы как музыка. Результат функции как мелодия

Сергей Бадло, Елена Бадло (г. Запорожье)

Многие наверняка видели ролик на ютубе http://www.youtube.com/watch?v=tCRPUv8V22o, посвященный визуализации и представлению результатов работы различных алгоритмов в звуковом ряде. Вообще, занятный опыт. Однако, по большому счету, не ново, если совершить небольшой экскурс в компьютерную историю (вспомним Спектрумы и магнитофоны в качестве дисководов, и т.п.). Понятно, что в случае магнитофонных записей тогдашних ПК мы имели байт-код на ленте, который вследствие способа хранения воспринимался нами на слух как «звуковой набор данных». Нам стало любопытно, а как бы звучали программы сейчас? Да что там программы, вообще любые файлы. Как известно из любой книги по ЦОС, к примеру, из [1], в случае, представления фиксированного результата функции во времени, ее можно перенести в частотно-временную область. Например, условиться, что результат функции, значение – есть частота на текущий момент. Оно, значение, может занимать некоторые экстремумы, вот от них и будем отталкиваться…

Краткий экскурс…

Каким образом будет реализовываться алгоритм представления бинарных данных в звуке? Откройте в блокноте любой файл, совершенно любой, и выберите вариант представления в HEX (16-тиричной кодировке).

22

Сигнатура файла (кодировка HEX)

Что мы имеем? А имеем наборы по два байта, составляющих сигнатуру файла. Каждые из этих двух байт могут иметь значение не более 255 = $FF. Если мы будем последовательно считывать с начала и до конца файла все эти наборы, то условно получим значение функции в текущий (мгновенный) момент времени. Теперь вспомним, диапазон восприятия человеческого слуха составляет ~ от 20 до 20000 Гц, условно (в серой же массе восприимчивость еще меньше). Если значение функции перейдет в область ультразвука или инфразвука, вы его не услышите.

Кроме того, нужно учитывать возможности самой аудиокарты (не всякий интегрированный чип потянет из-за ограниченной частоты дискретизации). Вот тут нам и понадобятся максимумы и минимумы. Условно примем максимум результата функции (те самые $FF) за 20 кГц, минимум (пусть будет $0) за 20 Гц. Таким образом, необходимо будет провести нормирование результата функции для звукового диапазона:

freq:= func() * koef / $ff; (1)

где:

freq – частота в Гц,

func() – значение функции (от нуля до $FF),

koef – условный коэффициент (для смещения области восприятия вверх или вниз).

В итоге достаточно воспользоваться Waveform Audio Win32 API или, что еще лучше, MIDI-устройством для генерации заданного тона в текущий момент времени, и вы его услышите.

MIDI (Musical Instrument Digital Interface) – цифровой интерфейс музыкальных инструментов. Разработан в 1983 году ведущими производителями электронных музыкальных инструментов – Yamaha, Roland, Korg, E-mu и др. Изначально был предназначен для замены, принятого в то время, управления музыкальными инструментами при помощи аналоговых сигналов, управлением при помощи информационных сообщений, передаваемых по цифровому интерфейсу.

MIDI представляет собой событийно-ориентированный протокол связи между инструментами.  Спецификация MIDI состоит из аппаратной спецификации самого интерфейса и спецификации формата данных, или протокола – описания системы передаваемых сообщений. Соответственно, различается аппаратный MIDI- интерфейс и формат MIDI-данных (так называемая MIDI-партитура);

интерфейс используется для физического соединения источника и приемника сообщений [2].

Предпосылки и реализация ПО

Итак, приступим к основной задаче. Для работы нам понадобится следующее:

 

  • отладка и компиляция тестового проекта с помощью IDE среды TurboDelphi Lite (TDL предназначен для некоммерческого использования) [3];
  • MSDN-справка по WinAPI функциям http://msdn.microsoft.com/en-us/library/windows/;
  • файлы для тестирования :).

Всю разработку осуществим для консоли на WinAPI. Формы нам не понадобятся. Да и зачем? Нужно лишь воспроизведение тона (звука). Да и сам код будет мизерным.

<img src=»picture3.jpg» width=»400″ height=»300″ border=»0″ align=»right» hspace=»10″ alt=»»>Прежде всего, запустим среду TurboDelphi-Lite portable и создадим пустой проект:

33

Окно проекта в IDE TDL

Для упрощения работы с программой вызов диалога выбора файла для “экзекуции” определим при запуске (в самом начале). API реализация данного вызова представлена в листинге 1:

ЛИСТИНГ 1

// API диалог —————————

Uses commdlg;

function get_file(dir: string): string;

var ofn: openfilename;

begin

 // выделяем память под структуру

 zeromemory(@ofn, sizeof(openfilename));

 with ofn do begin

  lstructsize:=sizeof(openfilename);

  lpstrinitialdir:= pansichar(dir);

  // текстовое значение заголовка диалога выбора

  lpstrtitle:= ‘выберите файл для воспроизведения…’;

  nmaxfile:= 255;

  lpstrfile:=virtualalloc(0, 255, mem_commit, page_readwrite);

  // устанавиливаем маску для показа всех файлов

  lpstrfilter:=’ALL’#0+’*.*’+#0#0;

  flags:= ofn_filemustexist + ofn_hidereadonly + ofn_pathmustexist

 end;

 if getopenfilename(ofn) then

  result:= ofn.lpstrfile;

 virtualfree(ofn.lpstrfile, 0, mem_release)

end;

Для работы с MIDI подключим к проекту модуль MMSYSTEM. Оттуда нам понадобятся всего три функции: midiOutOpen() – для открытия устройства MIDI, midiOutClose() – для закрытия, midiOutShortMsg() – для передачи команд устройству. Синтаксис использования данных функций представлен в [4…6].

К слову сказать, количество доступных инструментов для MIDI равно 127-ми: ‘AcousticGrandPiano’, ‘BrightAcousticPiano’, ‘ElectricGrandPiano’, ‘HonkyTonkPiano’, ‘ElectricPiano1’, ‘ElectricPiano2’, ‘Harpsichord’, ‘Clavinet’, ‘Celesta’, ‘Glockenspiel’, ‘MusicBox’, ‘Vibraphone’, ‘Marimba’, ‘Xylophone’, ‘TubularBells’, ‘Dulcimer’, ‘DrawbarOrgan’, ‘PercussiveOrgan’, ‘RockOrgan’, ‘ChurchOrgan’, ‘ReedOrgan’, ‘Accordion’, ‘Harmonica’, ‘TangoAccordion’, ‘AcousticNylonGuitar’, ‘ AcousticSteelGuitar’, ‘JazzElectricGuitar’, ‘CleanElectricGuitar’, ‘MutedElectricGuitar’, ‘OverdrivenGuitar’, ‘DistortionGuitar’, ‘GuitarHarmonics’, ‘AcousticBass’, ‘FingeredElectricBass’, ‘PickedElectricBass’, ‘FretlessBass’, ‘SlapBass1’, ‘SlapBass2’, ‘SynthBass1’, ‘SynthBass2’, ‘Violin’, ‘Viola’, ‘Cello’, ‘Contrabass’, ‘TremoloStrings’, ‘PizzicatoStrings’, ‘OrchestralHarp’, ‘Timpani’, ‘StringEnsemble1’, ‘StringEnsemble2’, ‘SynthStrings1’, ‘SynthStrings2’, ‘ChoirAahs’, ‘VoiceOohs’, ‘SynthVoice’, ‘OrchestraHit’, ‘Trumpet’, ‘Trombone’, ‘Tuba’, ‘MutedTrumpet’, ‘FrenchHorn’, ‘BrassSection’, ‘SynthBrass1’, ‘SynthBrass2’, ‘SopranoSax’, ‘AltoSax’, ‘TenorSax’, ‘BaritoneSax’, ‘Oboe’, ‘EnglishHorn’, ‘Bassoon’, ‘Clarinet’, ‘Piccolo’, ‘Flute’, ‘Recorder’, ‘PanFlute’, ‘BlownBottle’, ‘Shakuhachi’, ‘Whistle’, ‘Ocarina’, ‘SquareLead’, ‘SawtoothLead’, ‘CalliopeLead’, ‘ChiffLead’, ‘CharangLead’, ‘VoiceLead’, ‘FifthsLead’, ‘BassandLead’, ‘NewAgePad’, ‘WarmPad’, ‘PolySynthPad’, ‘ChoirPad’, ‘BowedPad’, ‘MetallicPad’, ‘HaloPad’, ‘SweepPad’, ‘SynthFXRain’, ‘SynthFXSoundtrack’, ‘SynthFXCrystal’, ‘SynthFXAtmosphere’, ‘SynthFXBrightness’, ‘SynthFXGoblins’, ‘SynthFXEchoes’, ‘SynthFXSciFi’, ‘Sitar’, ‘Banjo’, ‘Shamisen’, ‘Koto’, ‘Kalimba’, ‘Bagpipe’, ‘Fiddle’, ‘Shanai’, ‘TinkleBell’, ‘Agogo’, ‘SteelDrums’, ‘Woodblock’, ‘TaikoDrum’, ‘MelodicTom’, ‘SynthDrum’, ‘ReverseCymbal’, ‘GuitarFretNoise’, ‘BreathNoise’, ‘Seashore’, ‘BirdTweet’, ‘TelephoneRing’, ‘Helicopter’, ‘Applause’, ‘Gunshot’.

Не правда-ли, неплохой наборчик? Выбор инструмента из данного массива осуществляется по нехитрому алгоритму:

Instr = $C0 + $100 * n; (2)

где:

Instr – значение для выбора инструмента из набора,

$C0 и $100 – значение коэффициентов,

n – номер инструмента в вышеуказанном массиве.

Установка частоты (ноты), громкости и канала воспроизведения осуществляется по следующему алгоритму:

Msg:= $90 + $100*nota + $10000*vol + chan; (3)

где:

Msg – значение сообщения для настройки MIDI устройства,

nota – нормированная частота из формулы (1),

vol – величина громкости в канале (0..255),

chan – номер канала (по умолчанию примем за нуль).

Причем сообщения в канале делятся на два типа: голосовые и сообщения режима канала. Первые связаны со звукообразованием, вторые являются управляющими для MIDI-устройства. Голосовое сообщение заставляет тон-генератор произвести какое-либо изменение в звуке. Например, если исполнитель нажимает клавишу, в тон-генератор посылается сообщение Note On, на которое тот реагирует воспроизведением ноты. Если исполнитель поворачивает колесо модуляции, тон-генератор изменяет глубину модуляции звучащей ноты [7].

Чтение байтов из файла осуществим c помощью мультимедийного таймера, для этого используем функцию TimeSetEvent() [8] через обычный класс TMemoryStream. Метод Read() данного класса достаточно быстр при работе с любыми файлами. Реализация подобного подхода представлена в листинге 2:

ЛИСТИНГ 2

{ОСНОВНОЙ АЛГОРИТМ}

var fDAT: tmemorystream;

    buf: byte; // размер

    freq, i, FTimer: integer;

    t: string;

     // параметры для midi

    _Out: HMIDIOUT;

    msg : DWORD;       // вывод управления

    nota: dword;       // нота

    vol : dword = 127; // громкость

    chan: dword = 0;   // канал

    n   : dword;       // номер инструмента

type

  TFNTimeCallBack = procedure(uTimerID, uMessage: UINT;

    dwUser, dw1, dw2: DWORD) stdcall;

 function timeSetEvent(uDelay, uResolution: UINT;

  lpFunction: TFNTimeCallBack; dwUser: DWORD; uFlags: UINT): uint; stdcall; external ‘winmm.dll’

 function timeKillEvent(uTimerID: UINT): uint; stdcall; external ‘winmm.dll’

<?php

require_once «../../../utils.php»;

$dn = basename(dirname(__FILE__));

?>

<!DOCTYPE HTML PUBLIC «-//W3C//DTD HTML 4.0 Transitional//EN» >

<html>

<head>

            <title>Магия ПК</title>

            <link rel=»stylesheet» type=»text/css» href=»<?= $root ?>/main.css»>

            <script language=»javascript» src=»<?= $root ?>/main.js»></script>

            <style>

                        #art_<?= $dn ?> a.llmenu {color: #ffffff}

            </style>

</head>

<body bgcolor=#ffffff leftmargin=0 topmargin=0 marginwidth=0 marginheight=0 onload=»return findParent(‘art_<?= $dn ?>’)»>

<?php

include «../../../header3.php»;

bindTOC(dirname(dirname(__FILE__)), dirname(dirname($_SERVER[«PHP_SELF»])));

include «../../../middle3.php»;

bindPager(dirname(__FILE__));

?>

Компьютерные программы как музыка

procedure RemoteTimeProc(uID,uMsg:UINT;dwUser,dw1,dw2:DWORD); stdcall;

begin

 inc(i);

  fDAT.Read(buf, SIZEOF(buf));

  // нормировка

 freq:= buf * 79 div $ff;

  nota:= freq;

 Msg:= $90 + $100*nota + $10000*vol + chan; // стоп: $80+$100*nota+chan

  n:= 10; // n — номер инструмента

 midiOutShortMsg(_Out, $C0 + $100 * n);

  // воспроизведение

 midiOutShortMsg(_Out, Msg)

end;

begin

 // вызов диалога выбора файла

 t:= get_file(paramstr(0));

 // если ничего не выбрали, завершаем программу

 if t=» then exit;

  // создаем класс для загрузки файла

 fDAT:= TMemoryStream.Create;

 // загружаем файл

 fDAT.LoadFromFile(t);

  // открываем MIDI по-умолчанию и выдираем хэндл на него

 midiOutOpen(@_Out,0,0,0,0);

  i:= 0;

 // период таймера устанавливаем = 400 мс

 FTimer:= timeSetEvent(400, 0, @RemoteTimeProc, 0, 1);

// пока не будет считан последний байт, таймер не убивать

 while (i < fdat.Size-1) do ;

 timeKillEvent(FTimer);

  // по окончании считывания закрываем MIDI устройство

 midiOutClose(_Out);

 // подчищаем память

 fdat.Free

end.

Теперь быстренько жмакаем кнопку <F9> для компиляции проекта и выбираем любой файл для воспроизведения, хотя бы и сам себя:

44

Диалог выбора файла для озвучивания

Пошла мелодия? Во-о-т, теперь можно поиграться различными инструментами для воспроизведения. К примеру, довольно оригинально звучит под ElectricGrandPiano.

Полные исходные тексты и компиляцию тестового проекта звукового транслятора файлов вы можете скачать по ссылке ниже.

Ресурсы

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *