ПРОграммист 2010 03

45

Upload: edivio6610

Post on 18-Nov-2014

338 views

Category:

Documents


19 download

TRANSCRIPT

Page 2: ПРОграммист 2010 03

СОДЕРЖАНИЕ

ТЕМА НОМЕРАС новосельем ................................................. с.0х02

НЕВЕРОЯТНО, НО ФАКТЛюбопытные факты ............................................. с.0х03

LINUX ПРОГРАММИРОВАНИЕ. НАЧИНАЮЩИМВзаимодействие с сетевыми интерфейсами в Linux ............... с.0x09

АЛГОРИТМЫ. ОБЩИЕ ВОПРОСЫПоиск пути ................................................... с.0х0F

АЛГОРИТМЫ. ГРАФИКА В DELPHIКак работать с графикой на канве в среде Дельфи. Урок 5-6 .... с.0х13Создание спектрограммы в Дельфи .............................. с.0х17

ЛАБОРАТОРИЯLPT порт. Компьютер в роли контроллера. Часть 1 .............. с.0х1CПередача звука по сети. Прототип VoIP-телефона ............... с.0х20Разработка ресурса журнала. Часть 1 .......................... с.0х27

ЮМОР. ХОХМЫ ПРОГРАММИСТОВФразеологизмы и байки ........................................ с.0х2A

Издается с марта 2010. Выходит ежемесячно№3, май 2010 г.Редакция:Utkin, JTG, АлексейШульга, Ксения Павлова,Антон Бердников, Егор Горохов, Сергей БадлоДизайн и верстка:Егор Горохов, Сергей БадлоАвторский состав:Utkin, Владимир Дегтярь, Александр Уколов,Антон Бердников, Александр Терлецкий,Олег Кутков, Егор ГороховКонтакты:Авторские статьи направляйте на[email protected]Вопросы и предложения для редакции[email protected]Информационная поддержка:Международная Академия Информатизации(МАИН) РК www.academy.kzЖурнал «Радиолюбитель»www.radioliga.comКлуб ПРОграммистовwww.programmersforum.ruПримечание:Издание некоммерческое. Все материалы,товарные знаки, торговые марки и логотипы,упомянутые в журнале, принадлежат ихвладельцам. Статьи, поступающие в редакцию,рецензируются. Мнение авторов не всегдасовпадает с мнением редакции. Перепечаткаматериалов журнала и использование их влюбой форме, в том числе в электронных СМИ,возможно только с разрешения редакции.Тираж неограничен. Формат A4, 45 стр.Учредитель:Клуб ПРОграммистовwww.programmersforum.ruОбложка номера:Использован официальный талисманLinux, пингвин Tux.

архив номеров журнала ^

[ПРОграммист май 2010

Page 3: ПРОграммист 2010 03

В этом выпуске жаждущих читателей порадуетновым практическим материалом ВладимирДегтярь по использованию компьютера в качествеуправляющего контроллера. Несомненно, не забылон и о продолжении серии уроков по графике.Александр Терлецкий дебютирует со статьей посозданию спектрограммы для движка BASS. Utkinпоможет найти кратчайший путь и выведетоптимальный маршрут. Олег Кутков статьей поособенностям взаимодействия с сетевымиинтерфейсами не оставит равнодушными фанатовLinux-а. Рубрику «Лаборатория» на это разподдержал Александр Уколов с материалом попрактике приема и передачи звука по сети.Программный прототип VoIP телефона не загорами.

Еще одним подарком к новоселью сталаинформационная поддержка нашего журнала состороны Международной АкадемииИнформатизации МАИН РК www.akademy.kz ижурналом «Радиолюбитель» www.radioliga.com.Еще раз спасибо Василию www.kotoff.info и Вам,дорогие читатели, за ваш интерес к журналу иценные советы!

Рубрики журнала (плавающие)• Новости программирования• Отдел тестирования• Общие вопросы (вопросы правовогоиспользования, личные мнения и т.д.)

• Алгоритмы (без привязки к языку и платформе)•Юмор (специфические хохмы программеров)• Реализация (тонкости программирования)• Разработка (создание программных проектовот этапа ТЗ до работающей программы)

• Переводные материалы• Рубрика про железки / Лаборатория

Общие требования к материаламУ нас нет категоричных требований коформлению, но в связи с особенностями верстки(используется свободное ПО «SCRIBUS») иоблегчения труда редакторов, есть некоторыйжелательный минимум:

• статья должна иметь выраженную структуру сразделами и содержать – название статьи,сведения об авторах, экскурс или введение,сведения об используемых средствах разработки,теоретическую и/или практическую часть,заключение (выводы, чего добились) и ресурсы кстатье (код, интернет-ресурсы, литература)

• текст статьи в формате MS Word, VK WordPadили обычным текстовым файлом, шрифт Arial 10

• все рисунки, таблицы должны иметь упоминаниев тексте и иметь подпись

• рисунки к статье должны прилагаться в видеотдельных файлов в формате PNG, BMP илиTIF

• разделы статьи отделять двумя <ENTER>• не используйте табуляцию и лишние пробелыбез необходимости

• по присланным материалам автор получаетрецензию и корректирует статью согласнозамечаниям

• шаблон для написания статьи можно взять тут,бесплатный редактор VK WordPad можно взятьтут

Замечание редактора обязательно должновызывать реакцию автора. Если автор не согласенс комментарием или исправлением редактора, ондолжен разъяснить вопрос в виде комментария кстатье на ящик-копилку. Если редактор согласен стем, что ошибся, вопрос снимается. Если редакторне согласен, решает группа.

C уважением, Редакция

[ПРОграммист май 2010 ЛЮБОПЫТНЫЕ ФАКТЫС НОВОСЕЛЬЕМ

От редактора. Дорогие друзья! Добро пожаловать в майский выпуск журнала«ПРОграммист» от клуба программистов www.programmersforum.ru. Сегодня у наспраздник. Благодаря Василию Мединцеву, Алексею Шульге и Егору Горохову ужурнала появился еще один небольшой, но уютный домик. Милости просимпогостить в нашей резиденции http://procoder.info. Ну и какое-же новоселье без подарков. ВРедакции пополнение ведущим рубрики новостей Антоном Бердниковым и женскойполовиной. Рады представить Вам Ксению, второго литредактора нашего журнала...

СОДЕРЖАНИЕ 3ТЕМА НОМЕРА

Page 4: ПРОграммист 2010 03

ЛЮБОПЫТНЫЕ ФАКТЫмай 2010[ПРОграммистМесяц май выдался щедрым на события как в историческом плане, так и всовременности. Такое множество проишествий мы не в состоянии охватить однойжурнальной заметкой, но основные постараемся донести до вас, дорогиечитатели. Итак...

Антон Бердников by Aexxwww.programmersforum.ru/member.php?u=55969

1 мая 1918 года родился Башир ИскандеровичРамеев (1918-1994) – один изосновоположников вычислительнойтехники в СССР, главныйконструктор семействавычислительных машин «Урал».

Автор первого в СССР проекта «Автоматическаяцифровая электронная машина». В нем былоописание принципиальной схемы машины,определены арифметические операции в двоичнойсистеме счисления, управление работой машиныосуществлялось от датчика, считывающегопрограмму, записанную на перфоленту, иобеспечивающего выдачу результатов на такую желенту. Авторское свидетельство от 4 декабря 1948года на имя И.С. Брука и Б.И. Рамеева былопервым в СССР зарегистрированнымизобретением в области цифровой электроннойвычислительной техники.

3 мая 1912 года родился ЮрийЯковлевич Базилевский (1912-1983)– талантливый инженер-компьютерщик, главныйконструктор ЭВМ «Стрела» и

автоматизированного вычислительного комплексаприема и обработки радиолокационнойинформации для системы противовоздушнойобороны «Даль-111».

3 мая 1984 года уроженец Хьюстона (штат Техас,США) Майкл Саул Делл зарегистрировал на своеимя компанию PCs Limited, Inc., арендовалоднокомнатный офис и нанял сотрудника, которыйзанялся администрированием и финансами. Такначалась история Dell Computer Corporation (DCC)– фирмы, ставшей одним из крупнейших в мирепроизводителей персональных компьютеров.

Надувные колонки как альтернативу привычнымразработала компания Cebop. Приведениеаксессуара в рабочее состояние занимает всеголишь пару минут. Колонкиоснащены встроеннымусилителем и регуляторомгромкости, выходнаямощность их равна 10 Вт.Цена от 35 евро.

7 мая 1895 года АлександрСтепанович Попов на заседаниифизического отделения Российскогофизико-химического обществапродемонстрировал свой

грозоотметчик и прочитал доклад «Об отношенииметаллических порошков к электрическимколебаниям», здесь же он высказал мысль овозможности применения грозоотметчика дляпередачи сигналов на расстояние. 7 маяотмечается в нашей стране как День Радио.

СОДЕРЖАНИЕ 4НЕВЕРОЯТНО, НО ФАКТ

Page 5: ПРОграммист 2010 03

[ПРОграммист май 2010 ЛЮБОПЫТНЫЕ ФАКТЫ

СОДЕРЖАНИЕ 5НЕВЕРОЯТНО, НО ФАКТ

Page 6: ПРОграммист 2010 03

ЛЮБОПЫТНЫЕ ФАКТЫ[ПРОграммист май 2010

Нанопечать RFID-меток возможно станет

альтернативой штрих-коду на товарах. Донастоящего времени радиочастотные меткиизготавливались на кремниевой основе.Использование пластика или бумаги в качествеисходного материала, а также поддержка«рулонной» печати поможет многократносократить затраты на производство этихкомпонентов. Ключевым компонентом новойтехнологии являются особые чернила дляструйного принтера, содержащие углеродныенанотрубки. Эти чернила используются длясоздания тонкопленочных транзисторов, которые всвою очередь являются основой для пассивныхRFID-меток, содержащих до 16 бит информации инаносимых на бумагу или пластик методом печати.

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

помощи импульсов света. В основе работы новогофотодетектора лежат свойства полупроводника.При поступлении на устройство одного импульсасвета в его материале высвобождается нескольконосителей зарядов, которые затем высвобождаютдругие носители, создается известный эффектлавины. Вспомните наши отечественные лавинныетранзисторы ГТ338. Устройство способнопринимать оптический сигнал со скоростью 40Гбит/с и при минимальных помехах выдавать по400 млрд бит в секунду цифровой информации принапряжении всего 1.5В.

Сенсорную кожу разработали ученые изуниверситетаКарнеги-Меллонсовместно скомпаниейMicrosoft.ТехнологияSkinputпозволяетпревратить кожу человека в подобие сенсорногоэкрана. Точнее говоря, проводя или нажимаяпальцем по поверхности кожи ладони илипредплечья, можно, например, контролироватьзвонки и писать сообщения, набирать телефонныйномер, проигрывать музыку или играть хоть в

тетрис. Системаможетреагировать дажена незаметныедвижения, такиекак щипок илидерганье мыщцы.Меню

проецируется на руку, а нажатия улавливает иинтерпретирует специальный браслет спьезоэлектрическими датчиками на верхней частируки.

Телефон, читающий по губампродемонстрировала на выставке CeBIT 2010группа исследователей из технологическогоинститута города Карлсруэ (Германия).Технология позволит владельцам сотовыхтелефонов обмениваться информацией потелефону, не издавая ни единого звука.

СОДЕРЖАНИЕ 6НЕВЕРОЯТНО, НО ФАКТ

Page 7: ПРОграммист 2010 03

[ПРОграммист май 2010 ЛЮБОПЫТНЫЕ ФАКТЫ

Устройство использует методикуэлектромиографии (применяется для диагностикинекоторых нервных заболеваний), то есть измеряетэлектрическую активность движений мышц лицаво время разговора. Крошечные электрическиесигналы, производимые лицевыми мышцами, онозаписывает даже тогда, когда человек вообщебеззвучно воспроизводит слова. В настоящеевремя для снятия показаний используется 9электродов, закрепляемых на коже, но в будущемученые надеются избавиться от проводов и этиэлектроды будут встроены в мобильныеустройства. Технология имеет огромноепрактическое значение: немые смогут говорить,говорящие перестанут орать в трубку. В метро илидругом шумном месте микрофон телефона можнобудет отключить и беседовать одними губами.Сейчас устройство понимает английский,французский и немецкий, а вот с языками вродепутунхуа может возникнуть проблема, ибо тамважное значение имеет тон, который непередается движением лицевых мышц. На данныймомент эффективность распознаванияустройством человеческой мимики составляет99%.

ICQ куплена за $187,5 млн инвестиционнымфондом Digital Sky Technologies, совладельцемкоторого является российский предпринимательАлишер Усманов. Digital Sky Technologies владеетконтрольным пакетом акций Mail.ru (53,2%) иAstrum Online Entertainment, 30% социальной сетиVkontakte.ru, 25% в системе электронныхплатежей OE Investments, 100% портала HH.ru и70% эстонской Forticom (основного владельцаOdnoklassniki.ru), а также небольшой долей акцийсоциальной сети Facebook (3,5%).

Спин атомакобальтасмогли«сфотографировать»исследова-тели изуниверсите-

та Огайо. Несмотря на то, что спин (собственныймомент импульса квантовой частицы) являетсяключевым свойством квантовых частиц ииспользуется в квантовых вычислениях ужедесятки лет, его изображение представленочеловечеству впервые. Физики использовалиспециально созданный сканирующий туннельныймикроскоп, с помощью которого перемещалиатомы кобальта по марганцевой подложке. Атомыкобальта при этом меняли свой спин и наизображениях четко видна зависимость высоты иформы пиков атомов от направления спинов.Исследования показывают, что ученые могутнаблюдать и управлять спинами атомов, чтоможет привести к созданию электроники атомныхразмеров и новым направлениям спинотроники.Однако до практического применения полученныхрезультатов пока далеко. Для этого необходимонаучиться получать необходимый эффект прикомнатной температуре, а не охлаждать пластинумарганца жидким гелием до 10° К.

Новыемодификациианализаторовспектрав реальном вре-мени с техно-логией обра-ботки и записи

изображений сигнала DPX™ представилакомпания Tektronix Inc. Модели серий RSA3000B иRSA6100В поставляются в двух исполнениях сразными диапазонами перекрываемых частот: 0-3

СОДЕРЖАНИЕ 7НЕВЕРОЯТНО, НО ФАКТ

Page 8: ПРОграммист 2010 03

[ПРОграммист май 2010 ЛЮБОПЫТНЫЕ ФАКТЫ

ГГЦ или 0-8 ГГц. Технология обработкиизображений сигналов DPX, позволяетпросматривать спектр в режиме реальноговремени, при скорости обработки более 48000измерений спектра в секунду. Процессоробработки изображений сигналов с эффектом

послесвечения позволяет выявлять вдинамических сигналах отклонения и частоту ихповторения, а также обеспечивает мгновеннуюобратную связь при временных измененияхсигналов. Благодаря этому можно быстропросмотреть на экране переходные процессы исигналы, которые раньше рассмотреть былоневозможно, поскольку они маскировалисьдругими сигналами, или для их выявленияприходилось проводить автономный анализ,занимающий много времени. Основные видыанализируемых тестовых сигналов (W-CDMA,GSM/EDGE, CDMA, HSDPA, TD-SCDMA, WLAN802.11a/b/g, HDTV и даже радиочастотных метокRFID) представлены на скриншотах выше, авидеотестирование вы можете просмотреть тутhttp://raxp.radioliga.com/cnt/s.php?p=dpx.wmv

Этот удивительный электретМногим из нас знаком советский электретныймикрофон типа МКЭ-3, а те, кто не знаком, всеравно применяют его или его аналоги вповсеместной практике, даже не догадываясь озамечательныхсвойствахматериала –электрета(поляризованно-го диэлектрика),используемого внем.

СОДЕРЖАНИЕ 8НЕВЕРОЯТНО, НО ФАКТ

Page 9: ПРОграммист 2010 03

[ПРОграммист май 2010 ЛЮБОПЫТНЫЕ ФАКТЫ

Электреты – это поляризованные диэлектрики,состоящие из жестких электрических диполей,которые в электрическом поле напряженностьюоколо 10 кВ/см переводятся в аморфное твердоесостояние и сохраняют поляризацию длительноевремя. Таким образом, электреты являютсяаналогом постоянногомагнита, но обладаютне магнитным полем, аэлектростатическимполем.

Не менее примечатель-на история появленияэтого материала… В1943 году во времябоевых действий наТихом Океане американский флот захватиляпонский эсминец. Для изучения вражескойтехники на корабль прибыли специалисты флота,осмотрели все – от трюма до капитанской рубки.Связист подробно изучил систему телефоннойсвязи, она работала как часы. Одного он не могпонять, как система может работать без источникапитания, ни батарей, ни аккумуляторов не было ив помине!

В конце-концов, физики разобрались в принципеработы телефонной связи на японском корабле. Ееработа стала возможной благодаря открытиюяпонского физика Егучи, еще в 1922 годуполучившему новые материалы – электреты (с1922 года работу с электретами Егучи проводил вобстановке строжайшей секретности дляМинистерства обороны Японии). Егучи получалэлектретные материалы из смеси смолыкарнаубской пальмы и воска. Нагрев смесь дорасплавленного состояния Егучи подал на неенапряжение 10 кВ, после застывания таблеткаэлектрета сохраняла электростатический заряд

высокой напряженности в течение несколькихчасов (см. рецептуры). Современные электретымогут сохранять заряд до 100 лет, величиназаряда достигает 5*10-8 Кл/см2.

В настоящее время электреты получают из такихматериалов как: поли-тетрафторэтилен (пове-рхностный потенциалоколо 527 В),полиметилметакрилатили органическоестекло (3965 В),рутиловая керамика,смолы, воск,полимеры, титанатыщелочноземельных

металлов и даже растворы органических веществв летучих растворителях / Под ред. Сергея Бадло

Рецептура N1Нагреваем 2-5 г касторового масла до температуры 75-90 °С ивысыпаем в него при помешивании 50 г мелкоизмельченнойканифоли (если возится неохота, то возьмите и используйте парафинвместо канифоли и масла, расплавьте его и поместите междуэлектродами – после остывания электрет готов). После чего расплавналиваем в плоскую баночку (стеклянную) или на предметное стеклои помещаем его между электродами, на которые подаетсянапряжение от высоковольтного выпрямителя (5-10 кВ) илиэлектростатической машины. Толщина слоя диэлектрика должнабыть менее 4 мм. После остывания канифоль достается из баночки.Электрет готов.

Докажем, что электростатическое поле электрета имеет высокуюнапряженность. Если около неоновой лампочки быстро провестиэлектретом, то она ярко вспыхнет, так как при пересечении лампойлиний электростатического поля на электродах лампы наводитсяпеременное высокое напряжение и лампа начинает светиться.Застывший диэлектрик – электрет способен сохранять заряд втечение нескольких суток. Хранить электреты можно завернутыми валюминиевую фольгу.Более стабильные электреты можно получить при нагреведиэлектриков до температуры меньшей или равной температуреплавления, а затем охлаждая их в сильном электрическом поле. Призастывании органических растворов в сильном электрическом полеполучают так называемые «криоэлектреты». Существуют и другиеразновидности электретов: фотоэлектреты, трибоэлектреты и др.

Рецептура N2Вместо канифоли и касторового масла можно между электродами вбаночке поместить какой-нибудь полимер, например – капрон,растворенный в небольшом количестве растворителя. Подать высокоенапряжение и после полного испарения растворителя – электретготов.

СОДЕРЖАНИЕ 9НЕВЕРОЯТНО, НО ФАКТ

Page 10: ПРОграммист 2010 03

[ПРОграммист май 2010 ВЗАИМОДЕЙСТВИЕ С СЕТЕВЫМИ ИНТЕРФЕЙСАМИ В LINUX

Олег Кутковby OlegKutkov [email protected]

В последнее время программисты и простыепользователи начинают проявлять все большийинтерес к Unix- подобным операционнымсистемам, в частности к Linux. К сожалению,новичкам, не всегда просто разобраться в новойсреде, даже не смотря на то, что Linux считаетсяодной из самых хорошо документированных ОС.Информация, как правило, разбросана пофорумам, множеству отдельных статей и блогов.Основное содержимое данных материаловкасается администрирования и настройкидистрибутивов, программистам же, как правило,приходится довольствоваться man-документациейили автоматической doxygen-документацией(документация, сгенерированная автоматически,на основе комментариев в исходном коде). К томуже, как это часто бывает – наиболее интересныйматериал на английском языке. Безусловно,данную ситуацию следует исправлять.

Начало. Общие сведения

Программировать в Linux очень просто и легко.Для программистов созданы практическиидеальные условия: множество мощныхинструментов, открытые исходные коды, самаорганизация системы, множество фреймворков.Работа с файлами, строками, массивами, классами,контейнерами,в Unix- среде, практически ничем не отличается оттаковой в Windows, это касается и множествадругих стандартных функций и библиотек.Различия начинаются на более низком уровне.Предлагаю разобраться, как работать с сетевымиинтерфейсами.Как известно, сетевые интерфейсы Linuxобозначаются короткими строковыми именами -eth0, wlan0, lo и т.д. Интерфейсу можно присвоить

любое удобное имя, но лучше руководствоватьсяобщепринятыми правилами именования.

Думаю, что ни для кого не секрет*, что всеустройства в Linux представлены в виде особыхфайлов в каталоге /dev, это справедливо для всехустройств, кроме сетевых адаптеров. Но так былоне всегда, в прошлых версиях Linux ядра былидоступны устройства /dev/eth0, /dev/tap0 и т.д., вболее же новых ядрах эти устройства упразднили,и сетевые интерфейсы были перенесены в памятьв так называемое пространство сокетов.

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

Этой статьей я бы хотел открыть цикл публикаций, связанных с самым интересными захватывающим в Linux - программировании. Я не собираюсь рассказывать о том,в чем писать программы, как их компилировать и запускать, информацию поданным вопросам найти очень легко и я думаю, что любой с этим справится.Языки программирования, которые будут использоваться в статьях: C, C++, bash script...

* Комментарий автора.Следует сказать, что в другой популярной Unix - подобной ОС –FreeBSD по-прежнему, сохранен старый способ. Поэтому, если вызахотите переносить приложения с Linux на FreeBSD – следуетучитывать это и другие мелкие различия. Но это не значит, чторабота с этими устройствами каким-либо образом осложнилась, всеочень и очень просто.

СОДЕРЖАНИЕ 10LINUX ПРОГРАММИРОВАНИЕ. НАЧИНАЮЩИМ

Page 11: ПРОграммист 2010 03

[ПРОграммист май 2010

Интерфейсы управления

Для взаимодействия с устройствами, а на самомделе с драйверами устройств, в Unix имеетсяособый вызов - ioctl, означающий Input-OutputControl. Справедливости ради, следует отметить,что в Windows имеется подобный интерфейс -DEviceIoControl. Для использования данноговызова следует включить заголовочный файл<sys/ioctl.h>. Существует также возможностьопределять свои собственные ioctl вызовы, этимпользуются разработчики драйверов. Рассмотримвызов ioctl детально...

int ioctl(int d, int request, ...);

d - это открытый файловый дескриптор устройства.request - это тип запроса, для различных устройствзапросы различные, соответствующуюинформацию обычно легко найти в справочныхматериалов.третий аргумент - это указатель на void, т.е. таммогут оказаться какие угодно данные, взависимости от того, что требует конкретный типзапроса.

В случае успеха вызовом возвращается ноль. Вслучае ошибки возвращается «-1» и значениеглобальной переменной errno устанавливаетсясоответствующим образом. Чтобы было понятнее,пример использования (см. листинг 1). Сначала мыподключаем необходимые заголовочные файлы, вкоторых объявлены используемые нами функции, атак же ioctl запросы. В данном примере идетработа с последовательным портом компьютера, аточнее проверяется готовность приема данных.Вызов ioctl на открытом файловом дескрипторе,передает драйверу открытого устройства командуTIOCMGET, сохраняет и возвращает результат впеременную Serial.

Аналогично происходит процесс передачиинформации для драйвера из переменнойпараметра. Как уже говорил, тип переменнойпараметра зависит от типа запроса. Как правило,это специальная структура. Таков общий принцип

работы ioctl, как видим - ничего сложного. Теперьперейдем непосредственно к теме обсуждения -сетевым интерфейсам…

Работа с сетевыми интерфейсами

Выше был рассмотрен пример использованиявызова ioctl для получения данных из открытогодескриптора последовательного порта. Длясетевых интерфейсов работа выполняетсяаналогичная. Внимательный читатель могобратить внимание, как выше я говорил о том, чтодля сетевых устройств не существует такихспециальных файлов, соответственно eth0 нельзяоткрыть также, как и последовательный порт. Этотак сетевые устройства перенесены впространство сокетов и для доступа к этимустройствам следует использовать именнодескрипторы сокетов. Открытие сокета дляуправления сетевым устройством выполняетсятак:

ВЗАИМОДЕЙСТВИЕ С СЕТЕВЫМИ ИНТЕРФЕЙСАМИ В LINUX

#include <termios.h> ЛИСТИНГ 1#include <fcntl.h>#include <sys/ioctl.h>#include <errno.h>

int main(){int fd, serial, res; // дескриптор, параметр, результатfd = open("/dev/ttyS0", O_RDONLY); // открываем устройство

if (fd < 0) { // проверяем дескриптор и в случае ошибки и// выводим ее пользователю

printf("Открытие /dev/ttyS0 завершилось с ошибкой: %s\n",strerror(errno));

return 1;}

res = ioctl(fd, TIOCMGET, &serial); // выполняем вызов ioctl// с запросом TIOCMGET

if (res < 0) { // проверяем результат и в случае ошибки выводимprintf("Вызов ioctl завершился с ошибкой: %s\n",strerror(errno));return 1;}

if (serial & TIOCM_DTR) // проверяем результатprintf("Последовательный порт не готов\n");else printf("Последовательный порт готов\n");

close(fd); // закрываем дескрипторreturn 0;}

СОДЕРЖАНИЕ 11LINUX ПРОГРАММИРОВАНИЕ. НАЧИНАЮЩИМ

Page 12: ПРОграммист 2010 03

[ПРОграммист май 2010

int sock = socket(AF_INET, SOCK_DGRAM, 0);

Результат sock и есть дескриптор сокета, передиспользованием, его, как и все прочиедескрипторы, следует проверять на отрицательныезначения, на случай возможных ошибок. Также,для ioctl вызовов на дескрипторе сокетаприменяется особая структура параметра (serail, впримере выше) - struct ifreq. Это очень важнаяструктура, используемая во всех случаях работы ссетевыми устройствами. Разберем ее подробнее(см. листинг 2):

Структура состоит из двух полей: имениинтерфейса и объединения, каждое возможноеполе, которого выражает конкретный параметрсетевого интерфейса. Данная структура позволяет,как получать параметр интерфейса, так и задаватьего. Так как используется объединение – можнополучать и задавать толькоодин параметр за раз.

Интересуемые и используемые поля:ifr_addr - IP адрес интерфейсаifr_dstaddr - адрес сервера (для Point-to-Point)ifr_broadaddr - широковещательный адрес интерфейсаifr_netmask - маска подсетиifr_hwaddr - mac адресifr_ifinder - индекс интерфейса (внутри ядра сетевые интерфейсы

имеют уникальные индексы, для упрощения работысетевой подсистемы)

ifr_flags - различные флаги (интерфейс поднят или опущен,

интерфейс активен или неактивен и др.)ifr_metric - метрика интерфейсаifr_mtu - mtu интерфейсаifr_map - структура, содержащая в себе техническую информацию

(номер прерывания, память устройства)ifr_slave - подчиненное устройствоifr_newname - новое имя интерфейса (для переименования)

Перед любым использованием структуры следуетее обязательно обнулять с помощью memset, азатем задавать имя интересуемого интерфейсаifr_name. Теперь перейдем от теории к действию –получим IP адрес интерфейса eth0!Соответствующий пример приведен ниже (см.листинг 3):

ВЗАИМОДЕЙСТВИЕ С СЕТЕВЫМИ ИНТЕРФЕЙСАМИ В LINUX

struct ifreq { ЛИСТИНГ 2char ifr_name[IFNAMSIZ];union {

struct sockaddr ifr_addr;struct sockaddr ifr_dstaddr;struct sockaddr ifr_broadaddr;struct sockaddr ifr_netmask;struct sockaddr ifr_hwaddr;short ifr_flags;int ifr_ifindex;int ifr_metric;int ifr_mtu;struct ifmap ifr_map;char ifr_slave[IFNAMSIZ];char ifr_newname[IFNAMSIZ];char * ifr_data;

};};

#include <sys/socket.h> ЛИСТИНГ 3#include <arpa/inet.h>#include <sys/types.h>#include <sys/ioctl.h>#include <string.h>#include <net/if.h>#include <errno.h>#include <stdio.h>

int main(){int sock;// дескриптор сокетаstruct sockaddr_in*in_addr;// структура интернет-адреса (поля)struct ifreq ifdata;// структура - параметрchar *ifname = "eth0"; // имя интерфейса

sock = socket(AF_INET, SOCK_DGRAM, 0);// открываем дескриптор сокета

if (sock < 0) {printf("Не удалось открыть сокет, ошибка: %s\n",

strerror(errno));return 1;}

memset(&ifdata, 0, sizeof(ifdata)); // очищаем структуру

// задаем имя интерфейсаstrncpy(ifdata.ifr_name, ifname, sizeof(ifname));// получаем айпи адрес с помощью SIOCGIFADDR,// одновременно проверяя результатif (ioctl(sock, SIOCGIFADDR, &ifdata) < 0) {

printf("Не получить IP адрес для %s, ошибка: %s\n", ifname,strerror(errno));

close(sock);return 1;}

СОДЕРЖАНИЕ 12LINUX ПРОГРАММИРОВАНИЕ. НАЧИНАЮЩИМ

Page 13: ПРОграммист 2010 03

[ПРОграммист май 2010

В этом коде я ввел одну новую структуру и однуновую функцию. Начну со структуры:

Даная структура предназначена для хранениябазовых данных об адресе сетевого узла.Назначение ее полей:sin_family - семейство адресов, может иметь два значения:

AF_INET для IPv4 и AF_INET6 для IPv6sin_port - порт узлаsin_addr - структура адреса (о ней ниже)sin_zero - этот массив можно использовать по своему усмотрению

Эта структура состоит всего из одного поля –числа, представляющего собой собственно IPадрес. Например, «192.168.8.98», в таком формате,имеет вид 1644734656. Функция inet_ntoaпредназначена для преобразования такогочислового значения в привычный цифро-точечныйформат. В качестве аргумента, функцияпринимает struct in_addr, а возвращает указательна строку.

Теперь скажу о преобразовании из массива байт вструктуру sockaddr_in. Как было показано выше,поле ifr_addr, в структуре ifreq, имеет тип structsockaddr. Эта структура является своего рода,«упрощенной» структурой sockaddr_in:

У нее всего два поля: семейство адресов и массив14 байт, содержащий собственно адрес. Структурыsockaddr и sockaddr_in хорошо и естественноприводятся к типу друг друга, чем мы ивоспользовались. Обратная операция - заданиеадреса выполняется примерно так же. Отличиятолько два: перед вызовом ioctl, полю ifr_addrнужно задать новое значение адреса, а типзапроса будет SIOCSIFADDR. Как видно, обазапроса SIOCSIFADDR и SIOCGIFADDR отличаютсяна одну букву, которая означает Set и Get,соответственно.

Я не буду приводить пример, показывающий, какзадавать новое значение адреса, так какпредоставленных сведений уже достаточного длятого, что бы читатель разобрался сам. Дам лишьнебольшую подсказку: для преобразованиястрокового значения адреса, например«192.168.8.98», в тип struct in_addr следуетприменять функцию inet_aton:

inet_aton(const char *saddr,struct in_addr *iaddr);

saddr - указатель на строку с адресомiaddr - указатель на struct in_addr

Для получения (или задания) всех остальныхпараметров используется аналогичный способ,отличие лишь в типе запроса и использованиисоответствующего поля в объединении структурыifreq. Список всех возможных типов запроса дляполучения или задания параметров сетевогоинтерфейса:

SIOCGIFNAME - получить имя сетевого интерфейсаSIOCGIFINDEX - получить индекс сетевого интерфейсаSIOCGIFFLAGS, SIOCSIFFLAGS - получить/задать флаг интерфейса

(о флагах ниже)SIOCGIFMETRIC, SIOCSIFMETRIC - получить/задать метрику интерфейсаSIOCGIFMTU, SIOCSIFMTU - получить/задать mtu интерфейсаSIOCGIFHWADDR, SIOCSIFHWADDR - получить/задать mac адресSIOCGIFMAP, SIOCSIFMAP - получить/задать аппаратные параметры

(struct ifmap)

Наиболее интересные флаги интерфейса:

ВЗАИМОДЕЙСТВИЕ С СЕТЕВЫМИ ИНТЕРФЕЙСАМИ В LINUX

in_addr = (struct sockaddr_in *) &ifdata.ifr_addr;// преобразовываем из массива байт// в структуру sockaddr_inprintf("Интерфейс %s IP адрес: %s\n",

ifname,inet_ntoa(in_addr->sin_addr));

close(sock);return 0;}

struct sockaddr_in {short sin_family;unsigned short sin_port;struct in_addr sin_addr;char sin_zero[8];

};

struct in_addr {unsigned long s_addr; // load with inet_pton()

};

struct sockaddr {unsigned short sa_family;char sa_data[14];

};

СОДЕРЖАНИЕ 13LINUX ПРОГРАММИРОВАНИЕ. НАЧИНАЮЩИМ

Page 14: ПРОграммист 2010 03

[ПРОграммист май 2010

IFF_UP - интерфейс запущенIFF_BROADCAST - интерфейс является широковещательнымIFF_LOOPBACK - интерфейс является петлевымIFF_POINTOPOINT - point-to-point интерфейсIFF_RUNNING - интерфейс активенIFF_MULTICAST - интерфейс поддерживает многоадресность

Небольшой пример использования флагов,получение информации о том, запущен лиинтерфейс (cм. листинг 4):

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

Небольшой пример, показывающий как получитьсписок всех сетевых интерфейсов и их индексов(см. листинг 5):

Данный пример является доработанной версиейпредыдущего примера, выводя все доступныеинтерфейсы, их индексы и IP адреса. На рисункениже изображен процесс компиляции и запускаприложения с выводом всей информации. Видно,так же, сообщение об ошибке, для виртуальногоинтерфейса, с которого не удалось получитьинформацию (см. рисунок):

Рис. Процесс компиляции и запуска приложения

ВЗАИМОДЕЙСТВИЕ С СЕТЕВЫМИ ИНТЕРФЕЙСАМИ В LINUX

ioctl(sock, SIOCGIFFLAGS, &ifdata); ЛИСТИНГ 4

if (ifdata.ifr_flags && IFF_UP) {printf("Сетевой интерфейс %s запущен\n", ifdata.ifr_name);}else {printf("Сетевой интерфейс %s не запущен\n", ifdata.ifr_name);}

if (sock < 0) {printf("Не удалось открыть сокет, ошибка: %s\n",

strerror(errno));return 1;

}

ifNameIndex = if_nameindex();if (ifNameIndex) { // не удалось получить данныеwhile (ifNameIndex->if_index) { // пока имеются данныеmemset(&ifdata, 0, sizeof(ifdata)); // очищаем структуруstrncpy(ifdata.ifr_name, ifNameIndex->if_name, IFNAMSIZ);// получаем имя следующего интерфейса

// получаем IP адрес с помощью SIOCGIFADDR,// одновременно проверяя результатif (ioctl(sock, SIOCGIFADDR, &ifdata) < 0) {printf("Не получить IP адрес для %s, ошибка: %s\n",

ifdata.ifr_name, strerror(errno));close(sock);return 1;}

// преобразовываем из массива байт в структуру sockaddr_inin_addr = (struct sockaddr_in *) &ifdata.ifr_addr;printf("Интерфейс %s индекес %i IP адрес: %s\n",

ifdata.ifr_name,ifNameIndex->if_index,inet_ntoa(in_addr->sin_addr));

++ifNameIndex; // переходим к следующему интерфейсу}

}

close(sock);return 0;}

#include <sys/socket.h> ЛИСТИНГ 5#include <arpa/inet.h>#include <sys/types.h>#include <sys/ioctl.h>#include <string.h>#include <net/if.h>#include <errno.h>#include <stdio.h>

int main(){int sock; // дескриптор сокетаstruct sockaddr_in *in_addr; // структура интернет адреса (поля)struct ifreq ifdata; // структура - параметр// структура интерфейсов и их индексовstruct if_nameindex* ifNameIndex;

// открываем дескриптор сокетаsock = socket(AF_INET, SOCK_DGRAM, 0);

СОДЕРЖАНИЕ 14LINUX ПРОГРАММИРОВАНИЕ. НАЧИНАЮЩИМ

Page 15: ПРОграммист 2010 03

[ПРОграммист май 2010[ПРОграммист май 2010

Заключение

В данной статье я постарался раскрыть основныеаспекты взаимодействия с сетевымиинтерфейсами в OC Linux, многое из этого будетсправедливо и для других Unix подобныхоперационных систем. Надеюсь, что статьяполучилась интересной и полезной.

Напоследок не могу не сказать, что в Linux, работас сетью через ioctl является устаревшим способом,на смену которого приходит Netlink. Netlink – этомодуль, состоящий из двух частей, одна находитсяв ядре и взаимодействует непосредственно ссоответствующими подсистемами, вторая –библиотека на уровне пользователя. Обе частимогут обмениваться информацией между собой.Это очень мощный и удобный способуправлениями всеми параметрами сетевойподсистемы Linux, такие издания, как «Linuxjournal» рекомендуют пользоваться только Netlink.К сожалению, документации по данному API нетак много, как хотелось, и приходится собирать покрупицам. Но в будущих статьях япостараюсь рассмотреть некоторыеаспекты использования Netlink, таккак уже имею опыт данной сфере.До встречи на страницах журнала!

Литература

. Исходные коды пакета утилит NET-TOOLS

. Doxygen документация

. Christian Benvenuti. Understanding Linux networkinternals. – O'reilly Media, 2005http://www.linbai.info/computers-it/understanding-linux-network-internals.html

. Интернет ресурсы, форумы:www.linuxjournal.com, www.stackoverflow.com

ВЗАИМОДЕЙСТВИЕ С СЕТЕВЫМИ ИНТЕРФЕЙСАМИ В LINUX

СОДЕРЖАНИЕ 15LINUX ПРОГРАММИРОВАНИЕ. НАЧИНАЮЩИМ

Page 16: ПРОграммист 2010 03

[ПРОграммист май 2010 ПОИСК ПУТИ

by Utkin www.programmersforum.ru

Прокладка маршрута называется навигацией.Она бывает двух видов: автономная – когда объект(бот) самостоятельно прокладывает маршрут изодной точки карты в другую, запоминает егоиндивидуально (либо в общем, хранилищемаршрутов для одной группы юнитов), ипредварительная – когда маршруты ужепроложены во время проектирования карты (опятьже автоматически или программистом), или же наэтапе загрузки карты игрового мира. Маршрутобычно представляет собой некоторуюсовокупность точек (или их координат) со связямимежду ними, то есть это маршрут, проложенныйна графе. Сами точки называются вейпоинтами –это углы (или вершины) графа. Соответственноподавляющее большинство алгоритмов поискапути есть алгоритмы по работе с графами.

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

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

доступными точками). Также иногда некоторыемаршруты уже изначально заложены и бот«знает» куда идти, но такой вариант долженкомбинироваться с алгоритмами самостоятельнойвыработки маршрута по ряду причин – это делаетигровой процесс более динамичным, карта можетизменять свои параметры (например, произошелобрыв моста, тогда связи, отношения между двумяточками разрушаются, и требуется новый путь) ит.д.

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

Предварительный просчет точек осуществлен,например, в игре StarСraft. Это видно еслиотправить рабочего добывать ресурсы, он смелоперемещается через туман войны и понеисследованной области, способен находитьподъемы на другой уровень плоскости и сразуопределять местоположение моста.

Алгоритм Дейкстры

Этот алгоритм находит кратчайшее расстояние отодной из вершин графа до всех остальных.Сначала рассмотрим граф без примененияалгоритма Дейкстры, это упростит пониманиеалгоритма в дальнейшем. Для удобства

На пути постижения мудрости не надобояться, что свернешь не туда.

/ Пауло Коэльо

Многие начинающие игроделы сталкиваются с проблемой автоматическойпрокладки маршрутов ботами на карте. Основных проблем две – генерациявейпоинтов и прокладка по ним кратчайшего (либо оптимального по другимпараметрам) маршрута. Данная статья, делает небольшой экскурс по реализацииалгоритма поиска кратчайшего пути по ранее установленным вейпоинтам (на основеалгоритма Дейкстры)...

СОДЕРЖАНИЕ 16АЛГОРИТМЫ. ОБЩИЕ ВОПРОСЫ

Page 17: ПРОграммист 2010 03

[ПРОграммист май 2010 ПОИСК ПУТИ

восприятия обозначим каждую вершинуидентификатором, пусть это будет номер вершины(порядок нумерации на графе значения не имеет).В качестве отношения между точками возьмемрасстояние. Получается, точка имеет:идентификатор, список точек, куда можно попастьиз данной точки и расстояния до каждой издоступных точек.

Вот образец графа (см. рисунок 1):

Рис. 1. Образец типичного графа

Представьте, что нам нужно попасть из точки 1 вточку 5. Сколько имеется путей достижения точки5? Давайте посчитаем:а) 1-2-5б) 1-2-6-5в) 1-6-5г) 1-6-2-5

Итого четыре маршрута. Вычислим расстояния покаждому маршруту (суммированием расстояниймежду точками). Вот расстояния между точками:а) 1-2 расстояние 6б) 2-5 расстояние 4в) 2-6 расстояние 6г) 6-5 расстояние 5д) 1-6 расстояние 8е) 6-2 расстояние 6 (он же 2-6)

Общее расстояние всего маршрута:а) 1-2-5 расстояние 6+4=10б) 1-2-6-5 расстояние 6+6+5=17в) 1-6-5 расстояние 8+5=13

г) 1-6-2-5 расстояние 8+6+4=18

Самый оптимальный (для нашего примера)является путь (а) расстояние всего 10. Если жебудут обнаружено два и более маршрутов,имеющих одинаковую длину, то выбирается, какправило, первый (либо заранее продумано, какойиз маршрутов, лишь бы выбор был осуществлен).

Также нужно обратить внимание, что на первыйвзгляд маршруты (б) и (г)равны (казалось бы, отперемены мест слагаемыхсумма не меняется),однако следует незабывать, чтоидентификаторы здесь ненесут математическогосмысла это просто именаточек. Таким же образоммы могли бы назвать и (a),(b), (c) и т.д. Операции

идут над расстояниями, а суммы пар расстояний(1-2; 2-6) и (1-6; 2-6) не эквивалентны (не равны)между собой.

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

Перейдем к алгоритму

Итак, рассмотрим действие алгоритма для первойвершины нашего графа. Вершина 1 имеетотношения (в дальнейшем будем считатьотношения просто расстояниями междувершинами графа) с вершинами 2 и 6. Ближайшейточкой будет являться точка 2, посколькурасстояние до нее меньше и составляет 6 единиц(в примере неважно каких единиц, в игре это

СОДЕРЖАНИЕ 17АЛГОРИТМЫ. ОБЩИЕ ВОПРОСЫ

Page 18: ПРОграммист 2010 03

[ПРОграммист май 2010 ПОИСК ПУТИ

могут быть условные единицы реальных км, м ит.д., единицы местоположения юнита на карте,число пикселей с привязкой к координатной сеткеобласти отображения и т.д.). Считаем точку 1пройденной, поскольку нам известны кратчайшиерасстояния до точек 2 и 6.

Следующей рассмотрим точку 2 (потому что онаближе к 1 точке). Для нашего графа соседямиточки 2 являются 1, 6, 5 и 3. Точку 1 мырассматривать не будем, поскольку уже былоотмечено, что она была просмотрена ранее. Вотрасстояния:. до точки 6 расстояние 6;. до точки 5 расстояние 4;. до точки 3 расстояние 5.

Отсюда следует, что ближайшей точкой к вершине2 будет точка 5, поскольку расстояние до нееминимально, по сравнению с вершинами 6 и 3.

На данном этапе:. расстояние до точки 1 составляет 0;. расстояние до точки 2 составляет 6;. расстояние до точки 5 составляет 10 (6+4);. маршрут до точки 5 следующий – 1-2-5 (и никакойдругой до данной точки более нерассматривается);

. следующей точкой будет являться точка 5;

. точка 2, также как и точка 1 считаетсяотмеченной и больше не рассматривается;

. расстояние до точки 6 (маршрут 1-6) равен 8(0+8), а не 12 (маршрут 1-2-6, расстояние 6+6),поскольку хоть точка 6 и не отмечена, но текущеерасстояние до нее уже было вычислено и ономенее текущего (не забываем, что изначальнорасстояние до каждой точки равно максимальновозможному для данного графа).

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

вариант:

Представление точек графаГраф можно представлять многими способами,например, в виде таблицы смежности(ru.wikipedia.org/wiki/Список_ребер). Такой способпредставления удобен с математической точкизрения, но абсолютно не удобен с практической.Например, точку можно представить как еекоординаты и набор отношений (где отношениеможно представить как пару – указатель на точку-соседа и расстояние до точки-соседа). Еслиотношение представляет собой расстояние междудвумя точками в данной системе координат, то егоможно вычислять автоматически по формулевычисления расстояния между двумя точками(школьный курс геометрии), при этом расстояниедля системы координат с числом осей более двухвычисляется аналогично по обобщенной формуле.Собственно совокупность точек графа и будутявляться самим графом.

Отмеченные точки графаЗдесь необходимо отмечать те точки графа,которые были пройдены во время работыалгоритма, чтобы не проходить их повторно.Считаю, эти данные должны лежать отдельно отточек графа, потому как такая информациясобственно к графу отношения не имеет (а вот калгоритму самое прямое).

Таблица кратчайших расстоянийСогласно алгоритму изначально расстояние доточек должно представляться максимальновозможным расстоянием (за исключениемстартовой, до нее расстояние минимально - 0).Затем эти расстояния заполняются в процессевыполнения алгоритма. Также эти расстояниянужны для сравнения имеющихся и предлагаемыхрасстояний (расстояние до точки 6 для нашегопримера).

Маршруты от начальной до остальных точекСобственно сами маршруты, по которым вдальнейшем будут осуществляться перемещения.Вообще алгоритм Дейкстры не предназначен для

СОДЕРЖАНИЕ 18АЛГОРИТМЫ. ОБЩИЕ ВОПРОСЫ

Page 19: ПРОграммист 2010 03

[ПРОграммист май 2010 ПОИСК ПУТИ

поиска маршрутов (только расстояний), но егоработа построена таким образом, чтоформирование маршрута по данному алгоритму непредставляет никаких сложностей (в моментоценки и внесения в таблицу кратчайшегорасстояния):

Рис. 2. Тестовая утилита

Заключение

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

Исходники тестового проекта прилагаются в видересурсов в теме «Журнал клуба программистов.Третий выпуск» или непосредственно в архиве сжурналом [5].

Ресурсы

. Реализация алгоритма Дейкстры

http://plagiata.net.ru/?p=90. Описание алгоритма Дейкстры

http://algolist.ru/maths/graphs/shortpath/dijkstra.php

. Математическое и неформальное описание

алгоритма Дейкстрыhttp://ru.wikipedia.org/wiki/Алгоритм_Дейкстры

. Дополнительные материалыhttp://borisvolfson.h11.ru/show_article.php?article_id=0000020 иhttp://www.excode.ru/art6837p1.html

. Модули и проекты, использованные в статьеhttp://programmersclub.ru/pro/pro3.zip

СОДЕРЖАНИЕ 19АЛГОРИТМЫ. ОБЩИЕ ВОПРОСЫ

Page 20: ПРОграммист 2010 03

[ПРОграммист май 2010 КАК РАБОТАТЬ С ГРАФИКОЙ НА КАНВЕ В СРЕДЕ ДЕЛЬФИ

Владимир Дегтярьby DeKot [email protected]

Создание проекта с несколькимидвижущимися объектами. Урок 5

Создадим новый проект <Lesson 3> аналогичнопредыдущим. Введем в него новые движущиесяграфические объекты (в папке <data> добавленоеще четыре звездолета. Размер каждого спрайта100х80 pix. Новые звездолеты будут появляться послучайному закону (используем функциюRandomize) и двигаться будут сверху вниз. Выводфона и основного звездолета 'ship1'осуществляется также как и в предыдущемпроекте <Lesson 2>. Для новых объектов вводимдополнительно BufShipR и BufPicR. Также объявимновые переменные - координаты вывода новыхзвездолетов и приращения этих координат.Одновременно у нас будут отображаться основнойзвездолет 'ship 1' и два из 'ship 2' - 'ship 5',выбираемые по случайному закону (см. листинг 1):

В процедуре OnCreate() формы проведеминициализацию буферов и введем начальныеданные для переменных. Процедура DrawShipR()для вывода новых объектов ('ship2' - 'ship5') имеетдва параметра: i (изменение номера рисунка вфайле спрайтов) и j (переменная для номерафайла спрайтов). Так как выбор файла спрайтапроисходит по передаваемому параметру j, тоинициализация буфера BufShipR и загрузка в негофайла спрайтов находится в процедуреDrawShipR() (см. листинг 2):

Все движения организованы в обработчикетаймера. Звездолеты 'ship2' — 'ship5' выводятся вBuffer в координаты xR1, yR1 и xR2, yR2 вневидимого окна формы выше. В каждом тактетаймера происходит приращение координат дляодного dyR1 по вертикали, для другого dxR2 иdyR2 - по горизонтали и вертикали. После того,как объекты выходят за пределы видимого окнаформы внизу, вызываются методы random() длязадания новых координат xR1, xR2 и номера файласпрайта (nr1, nr2). Координаты yR1, yR2привязаны к координате yS1 , так как 'ship1'неподвижен в координатах окна формы. Функцияrandom(4) возвращает числа в дипазоне 0 .. 3, афайлы спрайтов встречных звездолетов имеютномера 2..5. Поэтому в процедуре загрузкиспрайтов BufShipR.LoadFromFile('data/ship' +

Здравствуйте, уважаемые читатели. Как и обещал, сегодня с вами мы подробнорассмотрим процедуры работы с графическими объектами, вынесенными вотдельный модуль, позволяющий использовать универсальные методы длясоздания движущихся изображений, находящихся в файлах, обычно в видеспрайтов…

Продолжение. Начало цикла смотрите в первом и втором выпускахжурнала…

var ЛИСТИНГ 1Form1: TForm1;BufFon,BufFonDop,Buffer: TBitMap;BufShip1,BufShipR: TBitMap; // буферы спрайтовBufPicS1,BufPicR: TBitMap; // буферы изображений спрайта

// координаты вывода общего буфера на формуxf,yf: integer;// приращение изменения координаты yf по вертикалиdyf: integer;// координаты звездолета 'ship1'xS1,yS1: integer;// приращение координат 'ship1' по гориз. и вертик.dxS1,dyS1: integer;// координаты звездолетов 'ship2 - ship5'xR1,yR1,xR2,yR2: integer;// приращение координат 'ship2 - 5'dyR1,dxR2,dyR2: integer;// номер спрайта и выбор ship2 - ship5ns,nr1: byte;nr2: byte = 3;

implementation

procedure DrawShipR(i,j: byte); ЛИСТИНГ 2begin

BufShipR:= TBitMap.Create;// загрузка одного спрайта в буфер рисунка из файлаBufShipR.LoadFromFile('data/ship' + IntToStr(j+2) + '.bmp');BufPicR.Canvas.CopyRect(bounds(0,0,

BufPicR.Width,BufPicR.Height),BufShipR.Canvas,bounds(i *100,0,BufPicR.Width,BufPicR.Height));

// зададим прозрачность фона рисунка спрайтаBufPicR.Transparent:= true;BufPicR.TransparentColor:= BufPicR.Canvas.Pixels[1,1];BufShipR.Free

end;

СОДЕРЖАНИЕ 20АЛГОРИТМЫ. ГРАФИКА В DELPHI

Page 21: ПРОграммист 2010 03

[ПРОграммист май 2010 КАК РАБОТАТЬ С ГРАФИКОЙ НА КАНВЕ В СРЕДЕ ДЕЛЬФИ

IntToStr(j+2) + '.bmp') номер загружаемого файлаопределяется как IntToStr(j + 2)... В остальномпроцедуры обработчиков таймера и нажатияклавиш не отличаются от проекта <Lesson 2>.

Использование универсального модулядля работы с графикой. Урок 6

Если рассмотреть внимательно код программы впроекте <Lesson 3>, можно заметить, что многиеметоды часто повторяются для разныхграфических объектов (создание буферов, загрузкаизображений из файлов, копирование и т.п.). Приэтом для упрощения, я сознательно применилфайлы спрайтов одинакового размера и с равнымколичеством рисунков в файлах. А если файловспрайтов будет не пять, а больше и есликоличество рисунков в каждом файле будетразным? Придется значительно увеличивать коддля каждого вида спрайтов. Следовательно,необходимо оптимизировать код программы.Выход здесь в написании методов обработкиобъектов, не зависящих от количества объектов иприменимых для разных изображений.

Даная задача реализована в отдельном модуле<LoadObjectToBufferMod>, позволяющийиспользовать универсальные методы для созданиядвижущихся графических объектов (находящихсяв файлах, обычно в виде спрайтов), имеющихразличный размер и разное количествоизображений отдельных рисунков.

Модуль находится в папке <Lesson 4> (см.ресурсы к статье). Принцип организации модуляследующий:• вся работа с графическими объектами проводит-ся через битовые образы TBitMap и областикопирования битовых образов TRect

• для работы с фоном используются процедурыInitFon (инициализация) и LoadFon (загрузкафона из файлов)

• функция InitSprite предназначена дляинициализации и загрузки рисунков спрайтов

• для вывода фона и изображений спрайтовиспользован общий буфер типа TBitMap

• в процедуре InitBuff происходит инициализацияобщего буфера, а в процедуре FreeBuff«переустановка», т.е. уничтожение общегобуфера и создание снова, но уже безизображений спрайтов

• в процедуре LoadBuff происходит наложениеизображений спрайтов на фон

Подробно работа модуля показана ниже…

Применение модуля LoadObjectToBufferMod

1. procedure InitFon(nw, nh: byte; FileName: string)

Создаем дополнительный и основной буферыфона:

Далее загружаем рисунок одного из фонов вдополнительный буфер:

По загруженному рисунку получаем размеродного рисунка фона (см. рисунок 1):

Рис. 1. Определение размера рисунка

Причем, размер буфера фона определяем как:

2. procedure LoadFon(xf,yf:integer; FileName: string)

Загружаем все рисунки фонов в буфер фона черездополнительный буфер (см. рисунок 2):

Рис. 2. Загрузка всех рисунков фона

BufFonD.LoadFromFile(FileName) илиLoadFromResourceName(hinstance.filename);

BufFonD:= TBitmap.Create;BufFon := TBitmap.Create;

WF:= nw * WFD;HF:= nh * HFD;

СОДЕРЖАНИЕ 21АЛГОРИТМЫ. ГРАФИКА В DELPHI

Page 22: ПРОграммист 2010 03

[ПРОграммист май 2010 КАК РАБОТАТЬ С ГРАФИКОЙ НА КАНВЕ В СРЕДЕ ДЕЛЬФИ

3. procedure initBuffer

Создаем основной буфер (Buffer) через которыйвыводим спрайты на форму:

Размер основного буфера устанавливаем равнымразмеру буфера фона WF и HF. Загружаем восновной буфер весь фон (cм. рисунок 3):

Рис. 3. Загрузка в буфер фона

4. procedure FreeBuffer

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

Восстанавливаем-же основной буфер с фоном так:

Спрайты, которые должны оставаться на форме,следует перерисовать по новому (см. процедуруInitStprite)…

5. procedure InitStprite(SpriteName: string; N_goriz,N_vertic, N_stroka, N_kadr: byte): byte

Cоздаем буфер массива спрайтов и загружаем тудафайл спрайтов (см. рисунок 4):

Рис. 4. Загрузка файла спрайтов в буфер спрайтов

Создаем буфер рисунка (одного спрайта):

Далее определяем размеры буферов массиваспрайтов и буфера рисунка, а также области (типаTRoot) загрузки рисунка. Загружаем спрайт вбуфер рисунка (см. рисунок 5):

Рис. 5. Загрузка спрайта в буфер рисунка

Задаем прозрачность рисунку:

Для вывода следующего спрайта функциявозвращает (правильнее сказать – функцияпринимает значение = Result) значениеследующего номера спрайта N_kadr:

Уничтожаем буфер массива спрайтов

6. procedure LoadBuffer(xf, yf, xs, ys, bs: integer)

В этой процедуре на Buffer выводится участокфона с координатами ранее выведенного спрайта,а затем очередное положение спрайта.Определяем область фона и областьдополнительного буфера (см. рисунок 6):

Рис. 6. Определение области фона и буфера

Выводим участок фона в буфер, т.е. затираемспрайт фоном (см. рисунок 7):

Рис. 7. Затираем спрайт фоном

BufFonD.LoadFromFile(FileName) илиLoadFromResourceName(hinstance.filename);

Buffer:= TBitmap.Create;

Buffer.Free;

Buffer.Canvas.Draw(0, 0, BufFon);

InitBuffer;

BufSprite:= TBitmap.Create;BufSprite.LoadFromFile(SpriteName) илиLoadFromResourceName(hinstance.spritename);

BufPic:= TBitmap.Create;

BufPic.Transparent:= true;

BufPic.Canvas.CopyRect(RectPic.BufSprite.Canvas, rectSprite);

Result:= N_kadr;

BufSprite.Free;

СОДЕРЖАНИЕ 22АЛГОРИТМЫ. ГРАФИКА В DELPHI

Page 23: ПРОграммист 2010 03

[ПРОграммист май 2010 КАК РАБОТАТЬ С ГРАФИКОЙ НА КАНВЕ В СРЕДЕ ДЕЛЬФИ

Выводим очередной спрайт в дополнительныйбуфер Buffer (см. рисунок 8):

Рис. 8. Вывод следующего спрайта

Уничтожаем буфер рисунка:

Далее в программе выводим дополнительныйбуфер Buffer на форму методом Draw:

Битовые образы фона (BufFon и BufFonDинициализируются (создаются - Create) впрограмме проекта всего один раз приинициализации программы (обычно вызовомпроцедуры InitFon в событиях OnCreate илиOnActivate). Методы InitBuff, FreeBuff, InitSprite,LoadBuf в программе вызываются неоднократно*.Соответственно и объекты Buffer, BufSprite, BufPicсоздаются многократно. Поэтому после окончаниядействия каждого из методов происходитуничтожение битовых образов Buffer, BufSprite,BufPic методом Free.

Как работать с модулем?

Для этого необходимо выполнить следующиедействия:

1. В процедуре FormActivate (можно в FormCreate)инициализируем буфер фона. Вызываем procedureInitFon(nw,nh: byte; FonName: string) с одним изфайлов фонов:. n раз вызываем procedure LoadFon(xf,yf: integer;

FonName: string), последовательно прикрепляярисунки фонов как бы друг к другу

. инициализируем дополнительный буфер Buffer,вызвав procedure InitBuff. Он получит размерравный сумме размеров всех файлов фонов.

2. Для вывода необходимых спрайтов в нужномместе программы вызываем functionInitSprite(SpriteName: string;N_goriz,N_vertic,N_stroka, N_kadr: byte). Функциявозвращает очередной номер спрайта дляпоследующего вывода очередного спрайта. Этотномер (N_kadr) необходимо передавать в функциюпри каждом ее вызове. Причем, функцию можноиспользовать для вывода нескольких спрайтов, незабывая передавать ей значение N_kadr длякаждых спрайтов.

Далее, вызвав процедуру LoadBuff(), выводимспрайт на канву дополнительного буфера поверхфона. Окончательный вывод дополнительногобуфера на канву формы производим методомDraw().

Заключение

Рассматриваемые вданной статьепроекты полностьюприведены в видересурсов в теме«Журнал клубапрограммистов.Третий выпуск» или непосредственно в архиве сжурналом (папка Lesson3). Продолжение нашихуроков смотрите в следующем выпуске журнала«ПРОграммист»...

Комментарий автора.Загрузку фона можно производить из n - количества файлов, cодинаковым размером не более 1024 х 1024. Для этого вызыватьпроцедуру LoadFon n -раз для разных файлов FonName и изменяякоординаты xf и yf.

Модуль можно применять и для простых объектов (один рисунок вфайле SpriteName). Присвойте переменным N_goriz и N_verticзначения = 1. Если размер спрайта не изменяется, присвойтепеременной bs значение = 0.

Можно загружать рисунки из файлов .jpg. Для этого вместо TBitMapприменять класс TJpegImage и в разделах usesLoadObjectToBufferMod и uses Unit1 добавить модуль Jpeg.

Важное замечание.Перед запуском в среде Дельфи скопируйте в папку с проектом папкуdata с графическими файлами.

Buffer.Canvas.StretchDraw(Bounds(xs, ys, WP + bs, HP + bs),BufPic);

Form1.Canvas.Draw(x, y, Buffer);

BufPic.Free;

СОДЕРЖАНИЕ 23АЛГОРИТМЫ. ГРАФИКА В DELPHI

Page 24: ПРОграммист 2010 03

[ПРОграммист май 2010 СОЗДАНИЕ СПЕКТРОГРАММЫ В ДЕЛЬФИ

Александр Терлецкийby mutabor [email protected]

Урок разбит на две части. В первой – пишем классспектрограммы и отлаживаем его, во второй –прикручиваем его к звуковому движку BASS (выможете использовать и другой движок, FMODнапример, но вам придется разобратьсясамостоятельно как получить уровни частот).Вариант с самостоятельным разбиением спектра спомощью преобразования Фурье, безиспользования каких либо звуковых библиотек, яне рассматриваю вообще, если кому оченьинтересно попробовать, ищите информацию вИнтернете, он большой и там все есть*.

Часть 1. Создание класса спектрограммы

Листинг программы смотрите в приложении кжурналу [2]. Создадим новый модуль для нашегокласса, назовем его Spectrum. В нем описываемглавный класс спектрограммы TAnalizer, ивспомагательные TChannel и TChannels дляканалов частот. Класс TAnalizer мы наследуем отTPaintBox и добавляем к нему несколько нужныхнам полей, это:. Timer (нужен для анимации ниспадающих пиков,в принципе можно было бы обойтись и безсобственного таймера, но так наш анализаторполучает большую независимость от деталей

реализации использующего его приложения). Buffer: TBitmap (буфер нужен чтобы не быломерцания при обновлении изображения). Channels (массив каналов, кол-во их будетнастраиваемое)

Сами каналы у нас будут иметь градиентнуюзаливку, которая задается полями «LowColor:TColor» и «HighColor: TColor».

Количество каналов и их координаты на экранезадается в методе TChannels.SetCount. Дляобратной связи с хозяином в классе TChannelsпредусмотрено поле Owner, которое заполняетсясразу после его создания в конструкторе классаTSpectrum. Возможно, здесь я отошел от канонов.Обычно поле Owner в VCL используется немногопо-другому, но в данном случае мне было удобнеесделать так и вообще я не претендую наакадемичность.

Подробно остановлюсь на методе Draw. В немпроисходит обновление изображения в такойпоследовательности. Очищается буфер изаливается черным фоном, затем с помощью WinAPI функции GradientFill() мы заливаемградиентом все прямоугольники каналов. ФункцияGradientFill() довольно сложная и имеет многопараметров, так что перед ее вызовом много строккода занимает заполнение нужных структур дляпередачи в нее. Затем мы проверяем, есть липики, которые стали ниже уровнясоответствующей им частоты с момента последней

Многие начинающие программисты пробуют свои силы в создании аудиоплеера.И вот когда у них более-менее получается создать функциональную основнуючасть и музыка уже играет, хочется добавить еще чего-нибудь крутого. Часто этоспектроанализатор или спектрограмма, которую многие ошибочно называютэквалайзером (эквалайзер – это средство настройки, а не анализа звука) [1]. В этом уроке мыпопробуем создать спектрограмму на Delphi, а прикручивать ее мы будем к движку BASS.Скажу сразу, что существует Delphi оболочка для BASS от нашего корейского коллеги подназванием TBassPlayer, в которой есть спектрограмма, но наша задача – научиться самим, ане использовать готовые решения. Однако вам никто не запрещает просмотреть исходный кодэтого компонента (он свободный), это никогда не повредит. Изучив этот урок, вы научитесьсоздавать свою собственную, уникальную своим внешним видом спектрограмму. Желательно,чтобы вы имели основные понятия об ООП, так как я не буду подробно останавливаться наэтом и больше внимания уделю вопросам вывода графики...

* Комментарий редакции.Далеко ходить не нужно. Достаточно заглянуть на наш форумhttp://www.programmersforum.ru/showthread.php?t=83467 или в блогhttp://pblog.ru/?p=658

СОДЕРЖАНИЕ 24АЛГОРИТМЫ. ГРАФИКА В DELPHI

Page 25: ПРОграммист 2010 03

[ПРОграммист май 2010

отрисовки, если таковые есть, обновляем ихположение. Те пики, которые наоборот стали вышетекущего уровня, мы пока не трогаем, они будутобработаны в таймере, чтобы они плавно падаливниз (но рисуются все они в этой процедуре, втаймере только смена позиций и вызов отрисовки).После этого мы закрасим черным (цветом фона)верхушки тех каналов, уровни которых нижемаксимального. Была использована функция изWinAPI – OffsetRect() для смещенияпрямоугольника канала вверх, а затем, послезакрашивания, повторный вызов OffsetRect(),только теперь вниз, для восстановления прежнихкоординат прямоугольника канала. И взавершении рисуем пики (короткиегоризонтальные линии) для всех каналов.После этого у нас имеется обновленноеизображение спектрограммы в буфере, и мывыводим его на экран, а точнее на канвунашего объекта.

Чтобы изображение не пропадало с нашегокомпонента при перерисовке окна, мыобрабатываем унаследованный метод Paint ив ней вызываем нашу процедуру обновленияDraw(). Хотя с тех пор, как я добавил таймер,это уже лишнее, можете убрать обработкуPaint() если хотите, но если нет таймера, онанеобходима.

Класс написан, пора его протестировать. Дляэтого создадим простое приложение. Длячего, поместим на форму TGroupBox, чтобыудобнее было размещать наш анализатор, идве кнопки, по нажатию одной – будетзадаваться случайный уровень для всехканалов (см. рисунок 1). По нажатию другой –пройдет волна (см. рисунок 2). Для волны нампонадобится таймер и подключение в Usesмодуля Math, так как мы будем использоватьсинус. По нажатию третьей – все каналы поочереди заполнятся пиковым значением.

Визуально это будет, похоже, как будто кто-то пальцем провел по клавишам пианино (см.рисунок 3).

Смотрите листинг, компилируйте и разбирайтесь.Если все понятно, то переходим к части 2, гдебудем подключать анализатор к плееру.

Часть 2. Подключение к BASS

Как это осуществить? Здесь я отдельно рассмотрюдва случая, подключение к компоненту-оболочкеTBassPlayer и непосредственно к BASS. Ну что-ж,приступим…

TBassPlayerХочу сразу обратить ваше внимание на нюансы с

СОЗДАНИЕ СПЕКТРОГРАММЫ В ДЕЛЬФИ

Рис. 1. Тестовая форма.Спектрограмма случайных величин

Рис. 2. Тестовая форма.Спектрограмма волновой функции

Рис. 3. Тестовая форма.Спектрограмма в виде пилы

СОДЕРЖАНИЕ 25АЛГОРИТМЫ. ГРАФИКА В DELPHI

Page 26: ПРОграммист 2010 03

[ПРОграммист май 2010

различными версиями как Delphi, так иTBassPlayer. Как вы знаете, начиная с D2009, длястрок используется Unicode. Если у вас Delphiболее ранних версий, можете дальше не читать ипереходить к коду. Если же у вас Unicode версия,то придется внести некоторые изменения висходники TBassPlayer, в его главный модуль,иначе работать не будет (у меня в D2009возникали ошибки во время выполнения,связанные с некорректным форматом строк). Хотяв последней его версии и указана совместимость сD2009, да и по времени выхода (май 2009) уже поидее должна быть поддержка юникода, но мнепришлось лезть в исходники даже последнейверсии. Исправленный модуль вы найдете вфайлах к статье [1]. Добавлю еще, что помимофункций оболочки над <bass.dll>, TBassPlayerдобавляет поддержку Winamp - плагинов,визуализации и другие функции. Подробнеечитайте в справке к компоненту.

Итак, вернемся к нашим баранам. Скопируйте впапку с проектом файл <Spectrum.pas> с нашиманализатором и подключите его в Uses. Я не сталустанавливать в среду TBassPlayer, не люблю я всеподряд устанавливать, а просто включил его вUses. Пути к компоненту можете указать всвойствах среды, а в директорию с программойнужно скопировать файл <bass.dll> подходящейверсии (разные версии TBassPlayer работают сразной версией BASS, последняя версиякомпонента 2.1 может работать с последним BASS2.4.5). В обработчике события OnCreate() главнойформы создаем экземпляр TBassPlayer иназначаем ему на событие OnNewFFTData()процедуру DisplayFFTBand. Затем создаем объектанализатора и назначаем ему количество каналовсоответствующее количеству каналов вTBassPlayer (константа NumFFTBands).

Код открытия файла и запуска я скопировал из«демки» к TBassPlayer, заодно там и отображениенекоторых свойств файла я оставил, будемвыводить их в метки TLabel. Своего я добавилтолько кнопку Play/Pause, для определениярежима работы кнопки используется ее поле Tag

(меняем с «0» на «1» попеременно), при нажатиикнопки читаем ее Tag и производим необходимыедействия: меняем название, запускаем илиостанавливаем воспроизведение и меняем Tag.

Кроме этого, мы создаем процедуру ClearChannels.В ней происходит обнуление каналов, это будетпроисходить при паузе и остановке. Ну и самоеинтересное для нас – это метод DisplayFFTBand,который привязан к событию обновления данныхспектра, в него приходит состояние каналов в видеобъекта Bands: TBandOut. Мы просто перегоняемзначения в наш Analizer.Channels и запускаемобновление Analizer.Draw. Вот и все. Как видите,подключение элементарное, инкапсуляцияпроявляет себя во всей красе, все детали скрыты вмодулях с классами, и нужно сделать всегонесколько телодвижений, для того чтобыприложение заработало.

BASS**Библиотеку BASS и API для разных языков выможете скачать с официального сайта проектаwww.un4seen.com.

В поставке с BASS идет оболочка для Delphi<bass.pas>. Скопируйте ее в папку с проектом иподключите ее в Uses. Тоже самое проделайте с<Spectrum.pas>. По сути, подключениеанализатора мало отличается от случая сTBassPlayer. Также создаем объект, назначаем емуродителя, координаты и количество каналов.

Небольшие отличия есть в механизме полученияданных о спектре. Если кто не знает, FFT –означает Fast Fourier Transorm, по-русски –Быстрое Преобразование Фурье или БПФ. Такимобразом, FFT Data переводится как БПФ данные. Втаймере мы получаем от BASS эти самые БПФданные в массив FData и запускаем процедуруSpectrumRender. В ней проверяется, остановлено

СОЗДАНИЕ СПЕКТРОГРАММЫ В ДЕЛЬФИ

** Комментарий автора.Пример кода подключения к <bass.dll> и получения данных спектралюбезно предоставил наш форумчанин Зарипов Равиль (ZuBy).Кстати он разрабатывает плеер на базе BASS, можете подробнееознакомиться на его сайте www.zubymplayer.com.

СОДЕРЖАНИЕ 26АЛГОРИТМЫ. ГРАФИКА В DELPHI

Page 27: ПРОграммист 2010 03

[ПРОграммист май 2010

ли воспроизведение, если остановлено, тообнуляется массив FData. В связи с этим хочузаметить, что в этом случае наш таймер в классеTAnalizer является «пятым колесом», так какотрисовка спектра будет вызвана все равно, дажеесли воспроизведение остановлено. Он не мешает(нагрузку большую он не дает), но и не помогает,если хотите, можете встроить в класс команду поего отключению или вообще убрать таймер, тогданемного переделать функцию Draw нужно будет.Однако, в случае с TBassPlayer собственныйтаймер нам пригодился.

Но вернемся к процедуре – SpectrumRender. Послепроверки на остановку, мы заполняем значениямиканалы анализатора и вызываем его отрисовку. Вотличие от случая с TBassPlayer, где мы простопереносили значения без изменений, здесь мыимеем сырые БПФ данные, и расшифровку их мыдолжны делать самостоятельно. В данном случаемы берем, из массива с БПФ, данные, начиная с 5-го элемента, в количестве равном количествуканалов в спектрограмме. Правильно это илинет, я не знаю, и похоже мало кто вообщезнает как правильно, и в основном всевозможно больше ориентируются накрасивый вид спектрограммы, чем на точныезначения частот. В любом случае, мы каналыне подписываем точным значением частоты,так что с нас взятки гладки.

Автор TBassPlayer применяет другой подход.Можете ознакомиться с ним в исходниках ккомпоненту [2]. А так как цель статьи – показать,как можно вывести на экран спектрограмму, а неразбор БПФ данных, то на этом скажем Фурье до-свидания и продолжим рассмотрение процедуры.

Следовательно, мы уже определились, какие

элементы из массива будем брать. Затем намнужно умножить значение на коэффициент,найденный пробным путем, он зависит от высотыспектрограммы. Судя по тому, что к полученномузначению применена функция модуля Abs(),значения могут иногда быть и отрицательными,оставляем это без изменений (напоминаю, что этачасть кода не моя, я только прикрутил сюдазаполнение анализатора). Откидываем дробнуючасть с помощью функции Trunc(), и можемзаполнять этими значениями каналы нашейспектрограммы. После заполнения вызываемотрисовку методом Analizer.Draw.

Таким образом, при каждом срабатываниитаймера, анализатор будет заполняться новымизначениями и будет вызываться его отрисовка. Вслучае, если трек будет остановлен или поставленна паузу, так же будет заполняться анализатор,только в этом случае нулями, и так же будетвызываться отрисовка. Это позволит нам увидеть,как пики плавно падают вниз (см. рисунок 4):

На этом закончим рассмотрение подключения кBASS.

Заключение

Итак***, мы создали свой класс спектрограммы,который умеет вывести себя на экран, как пособытию перерисовки, так и по таймеру, к тому жеон еще и не мерцает при отрисовке и имеетудобный интерфейс. Также мы имеем образецповторно используемого кода, мы ведь его ужеиспользовали как минимум три раза в разныхпроектах, первый раз в тесте, второй – приподключении к TBassPlayer, и третий раз приподключении напрямую к BASS.

СОЗДАНИЕ СПЕКТРОГРАММЫ В ДЕЛЬФИ

*** Комментарий автора.Возможно, вы обратили внимание на то, что я не освобождаюглобальные объекты. Так как мы создаем объект в программе толькоодин раз и при его освобождении мы не делаем ничего такогоособенного (например, освобождение DLL или других системныхресурсов), то в этом случае я предоставляю освобождение памятисборщику мусора Delphi, при закрытии программы он вернет ресурсыоперационной системе.

СОДЕРЖАНИЕ 27АЛГОРИТМЫ. ГРАФИКА В DELPHI

Рис. 4. Отрисовка спектрограммы

Page 28: ПРОграммист 2010 03

[ПРОграммист май 2010

Исходники тестовых модулей полученияспектрограмм прилагаются в виде ресурсов в теме«Журнал клуба программистов. Третий выпуск»или непосредственно в архиве с журналом.

Ресурсы

. Типичные примеры обсуждения на форуме пополучению спектрограммы через BASShttp://www.programmersforum.ru/showthread.php?t=7106http://www.programmersforum.ru/showthread.php?t=89708http://www.programmersforum.ru/showthread.php?t=94224

. Модули и проекты, использованные в статьеhttp://programmersclub.ru/pro/pro3.zip

СОЗДАНИЕ СПЕКТРОГРАММЫ В ДЕЛЬФИ

СОДЕРЖАНИЕ 28АЛГОРИТМЫ. ГРАФИКА В DELPHI

Page 29: ПРОграммист 2010 03

[ПРОграммист май 2010

Владимир Дегтярьby DeKot [email protected]

Тем не менее, на форумеwww.programmersforum.ru (даи на других также) частопоявляются вопросы: «Как скомпьютера зажечь светодиод?», «Как управлятьосвещением?» или «Можно ли компьютеромзакрывать шторы на окне?». Ответ: «…однозначнода». С помощью современного персональногокомпьютера можно создавать, воспроизводить,управлять, хранить, моделировать, обучать и ещереализовать много и много других функций.

Введение

Мы же рассмотрим возможности управлениявнешними устройствами - при этом возможноуправление, как простым вентилятором илисветодиодом, так производственнымиустройствами. Как известно, любой персональныйкомпьютер, да и не только персональный, а илюбое компьютерное (еще одно общепринятоеназвание - микропроцессорное) устройство имеетустройства ввода и вывода информации,называемые портами. Для нас пользователей – этопросто разъемы для подключения (клавиатура,мышь, модем, флешка и т. д.). Следует заметить,что по одному и тому порту (разъему) можно каквыводить информацию, так и вводить. Кроме этогопонятие порт - это не только физически внешнийэлемент (разъем), а больше наоборот - внутренняялогическая структура устройства компьютера,часть его архитектуры.

Думаю, что уже достаточно теории. Перейдем кпрактической реализации – управлениюреальными устройствами с помощью компьютера.Более всего для этого подходит «параллельный»

порт LPT. Более подробно смотрим о LPThttp://ru.wikipedia.org/wiki/IEEE_1284.

Параллельный порт LPT

Итак, почему параллельный, а неперпендикулярный? А какие еще бывают?Параллельный, потому что информация черезтакое устройство передается параллельнымспособом. А еще может передаватьсяпоследовательным – тогда устройство (порт)называется последовательным (последовательные

порты компьютера: COM-порт, USB- порт). Наглядноэто можно увидеть нарисунке 2:

Рис. 2. Методы выводаинформации

Аналогично ввод информации также может бытьпараллельным или последовательным. Порты(устройства) через которые можно вводить ивыводить информацию называютдвунаправленными устройствами илиустройствами ввода/вывода. Таким и естьпараллельный порт LPT, имеющийся вбольшинстве компьютеров. Посмотрим, что изсебя представляет этот порт (см. рисунок 3):

LPT ПОРТ. КОМПЬЮТЕР В РОЛИ КОНТРОЛЛЕРА. ЧАСТЬ 1

Большинство пользователей персонального компьютера привыкли к тому, чтовесь результат деятельности на компьютере в той или иной степени все равноотражается в самом компьютере. В крайнем случае, отправляется на принтер илив Интернет, или же запись информации происходит на внешние носители (диски,флеш-память и т.п.). И уж мало кто задумывается, что с помощью простого РС – компьютераможно управлять различными внешними физическими устройствами...

Рис. 1. «Рабочая лошадка LPT еще даст о себе знать»

СОДЕРЖАНИЕ 29ЛАБОРАТОРИЯ

Page 30: ПРОграммист 2010 03

[ПРОграммист май 2010

где: С0…С3 – регистры контроля, S3…S7 –регистры статуса, D0…D7 – регистры данных

Рис. 3. Распиновка LPT

Как видите, это такой разъем с 25-ю выводами назадней стенке системного блока компьютера.Итак, имеем физический порт LPT, которыйфактически состоит из трех регистров. Составрегистров и распределение по контактам разъемаприведены в таблице 1:

Каждый из регистров может содержать байтинформации (256 состояний). Часть битов неиспользуется или используется только вовнутренней архитектуре компьютера дляорганизации прерываний при работе с принтеромили для переключения режимов ввод/выводрегистра «Data».

Регистр данных «Data», номер регистра вшестнадцатеричной системе счисления $378 (вдесятичной 888) - двунаправленный,

восьмибитный. Данные через этотрегистр можно как вводить, так ивыводить с компьютера, программноустанавливая уровни на выходе портаили же вводить в компьютер, такжепрограммно считывая уровни,устанавливаемые внешнимиустройствами. Регистр управления«Control» $37A (890). Через него можнотолько выводить информацию изкомпьютера. На разъем LPT выводятся

четыре младших байта.Регистр статуса «Status» $379 (889). Через портможно только считывать уровни, установленныевнешними устройствами. На разъем LPT выведеныпять старших байтов.

Таким образом, на разъеме LPT задействовано 17сигнальных контактов (8 двунаправленных –регистр 888, четыре только на вывод информации– регистр 890 и пять только на ввод информации –регистр 889).

Порт LPT разрабатывался еще на заре созданияперсональных компьютеров для подключенияпечатающих устройств (принтер). Этимобъясняется специфическое наименования цепейи инверсия некоторых битов, из-за которойнаблюдается несоответствие уровней напряженияна контактах и логических кодов регистров. Всвязи с этим, операции с информацией через LPTпорт требуют учитывать эти особенностисоответствия физических уровней и логическихкодов.

Табл. 1. Распределение выводов LPT по регистрам «DATA», «STATUS» и «CONTROL»

LPT ПОРТ. КОМПЬЮТЕР В РОЛИ КОНТРОЛЛЕРА. ЧАСТЬ 1

СОДЕРЖАНИЕ 30ЛАБОРАТОРИЯ

Page 31: ПРОграммист 2010 03

[ПРОграммист май 2010

Следует отметить, что в некоторых компьютерахможет быть до трех портов LPT: LPT1, LPT2, LPT3(в современных компьютерах LPT порты зачастуювообще отсутствуют). Адрес самого LPT портасоответствует адресу регистра «Data» и можетбыть $278, $378 или $3BC. Регистры всоответствующих портах имеют адресацию +1 и+2.

Следующие таблицы (см. таблицы 2, 3) показываютэти особенности данного порта. Для регистра $378(888) соблюдается полное соответствие междууровнями напряжения на контактах и логическимкодом. А вот с регистрами $379 (889) и $37A (890)все обстоит по-другому:

Управление регистрами

В компьютерах с операционными системами MS-DOS, Windows 9x возможен доступ к портамнепосредственно из самой операционной системы,тогда как в системах с NT такой прямой доступневозможен. Для этих целей используютсядрайвера в виде библиотек (Inpout32.dll*, WinIO,Giveo). Подключив соответствующие библиотеки ксредам программирования, получаем возможностьпрограммно работать с портами (считывание

состояния порта или установка выводов порта внеобходимое состояние).

Итак, во-первых – помещаем <inpout32.dll> впапке с проектом. Далее в коде модуля Unit послераздела uses размещаем объявления необходимыхнам функций из библиотеки:

Функция Inp32(PortAdr) возвращает число (тип –байт), соответствующее коду, находящемся врегистре PortAdr. Функция Out32(PortAdr, Data)возвращает число (Data, тип – байт), котороезапишется в регистр PortAdr. Проще сказать Inp32считывает значение регистра, а Out32

* Комментарий автора.Для работы в среде Дельфи предпочитаю библиотеку <inpout32.dll>.Библиотека <inpout32.dll> свободно распространяется в Интернете(см. ресурсы к статье).

Табл. 2. Порт $37A (890) «Control» Табл. 3. Порт $379 (889) «Status»

// импорт функций inpout32.dlluses Windows, Messages, SysUtils, Variants, Classes, Graphics,

Controls, Forms, Dialogs,StdCtrls, ExtCtrls, ComCtrls;

function Inp32(PortAdr: word): byte; stdcall;external 'inpout32.dll';function Out32(PortAdr: word; Data: byte): byte; stdcall;

external 'inpout32.dll';

LPT ПОРТ. КОМПЬЮТЕР В РОЛИ КОНТРОЛЛЕРА. ЧАСТЬ 1

СОДЕРЖАНИЕ 31ЛАБОРАТОРИЯ

Page 32: ПРОграммист 2010 03

[ПРОграммист май 2010

устанавливает значение в регистр. Примерприменения функций:

При этом следует учитывать, что состояниеуровней напряжения на выходных контактах LPTпорта соответствует таблице 2 и 3. Принеобходимости управления отдельными битамирегистров можно применить следующие функциипреобразований десятичного числа в двоичное инаоборот:

Пример применения функций:

Вариант тестовой утилиты управления исчитывания состояния регистров LPT представленна рисунке 4 и приведен в ресурсах к статье [1]:

Рис. 4. Утилитасчитывания иконтроля сос-тояний регис-тров LPT порта

Заключение

В следующей статье мы рассмотрим практическиеварианты схемотехники подключения порта LPTдля ввода и вывода данных. Продолжениесмотрите в следующем выпуске журнала«ПРОграммист»...

Ресурсы

. Модули и проекты, использованные в статьеhttp://programmersclub.ru/pro/pro3.zip

. Сайт Валерия Ковтуна с множеством интересныхпрограмм для работы с LPT портомhttp://valery-us4leh.narod.ru/main.html

var val1, val2: byte;// значение регистра «data» запишется в переменную val1val1:= Inp32($378);val2:= 54;// в регистр «data» запишется число 54 (b 0 0 1 1 0 1 1 0)Out32($378,val2);

// weight (для byte(8 разр.)) = 128, j = 7;// weight( для word (16 разр.)) = 32 768, j = 15 ;

function Dec_Bin(N_dec: integer; weight: integer; _bit: byte):byte;var i ,j : byte;mas_bin: array[0..j] of byte;N_dec: integer;

beginfor i:= 0 to j dobeginmas_bin[i]:= N_dec div weight;if mas_bin[i] = 1 then N_dec:= N_dec - weight;weight:= weight div 2;end;

// возвращает значение бита (0 или 1) двоичного числа,// соответствующего N_decResult:= mas_bin[_bit]

end;

function Bin_Dec(weight: integer): integer;var i , j: byte;mas_bin: array[0..j] of byte;N_dec: integer;

beginN_dec:= 0;for i:= 0 to j dobeginN_dec:= N_dec + mas_bin[i] * weight;weight:= weight div 2;end;

// возвращает десятичное число, соответствующее двоичному// в виде массива битов mas_bin [ 0 .. j ]Result:= N_dec

end;

var bit : byte;// переменная bit принимает значение 3-го бита регистра «data»bit := Dec_Bin(Inp32($378))[3];

** Комментарий автора.Возможность использования регистра «data» ($378) в качестве портаввода определяется в настройках BIOS для параллельного порта.Следует установить Parallel Port в EPP. Переключение регистра на«вход» осуществляется программно, путем установки 5-го битарегистра «control» ($37A) в «1» (при этом все биты регистра «data»устанавливаются в «1»). Следует отметить, что данная процедуравозможна не на каждом компьютере и определяется, кроменастроек, еще и конструктивными особенностями порта LPT илиматеринской платы.

LPT ПОРТ. КОМПЬЮТЕР В РОЛИ КОНТРОЛЛЕРА. ЧАСТЬ 1

СОДЕРЖАНИЕ 32ЛАБОРАТОРИЯ

Page 33: ПРОграммист 2010 03

[ПРОграммист май 2010

Александр Владимирович Уколовby ImmortalAlexSan [email protected]

Введение

К написанию программы для передачи звука посети меня побудило желание получить-таки зачетпо УИРС (это что-то вроде НИР – научноисследовательской работы студента) упреподавателя, ведущего мой основной предмет, иявляющимся моим дипломным руководителем.Перед тем как сесть за Delphi и начать набиратькод, предварительно, я изучил кучу литературы вбумажном и электронном виде о принципахупаковки звука и его передачи, о функциях ввода ивывода в самом Delphi и многом другом [1, 2].Именно ввод и вывод заставил меня задуматься осложности преподносимого материала. Длячеловека, никогда не имевшего с этим дело,разобраться в этой области очень сложно, имеяпод рукой множество кода без комментариев снепонятными процедурами и функцияминепонятного WIN API, а если эти процедуры ифункции описаны, то это описание предназначеноне для начинающих программистов, приходилосьвсе додумывать самому: смотреть подноготнуюкаждой процедуры, и методом проб и ошибок идтимедленно, но уверенно к вершине созидания. Но вконечном итоге я добился поставленной цели.И сейчас, разложив всю информацию,предоставленную мне в кашеобразном виде, пополочкам, я готов поделиться своими знаниями свами, дорогие читатели! Итак, приступим…

Средства разработки

Прежде всего, для работы нам понадобится:. IDE Delphi версии 7.0 и выше. Библиотека Indy для Delphi 7.0

(TIdUDPServerSocket и TIdUDPClientSocket) [3, 4]. колонки и микрофон

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

Практическая часть. Создадим клиента

Передача звука в моей программе осуществляетсяс клиента на сервер, т.е. в одном направлении.Клиент может только писать и передавать, сервер– только принимать и воспроизводить. Первымделом начнем писать клиент. Для этого, создадимновый проект в Дельфи, разместим на формекнопку TButton и изменим ее свойство Caption на«начать отправку». После чего, разместим наформе компонент из библиотеки IndyTIdUDPClientSocket (см. рисунок 1):

Рис. 1. Режим проектирования формы тестовогоклиента

Так как тестирование программы будетпроводиться на локальном компьютере, тоизменим значение свойства «Host» компонента

ПЕРЕДАЧА ЗВУКА ПО СЕТИ. ПРОТОТИП VOIP-ТЕЛЕФОНА

* Комментарий автора.Описание некоторых вышеуказанных свойств выходит за рамкиданной статьи.

Данная статья будет полезна начинающим программистам, которые никогда неимели дело со звуком и его передачей по сети. Смысл этой статьи заключается визучении и применении: WINAPI функций ввода и вывода звука WaveIn() иWaveOut() в среде разработки Delphi 7.0, самих компонентов TIdUDPServerSocketи TIdUDPClientSocket. Данные компоненты можно найти в библиотеке Indy, которая в своюочередь находится в свободном распространении на просторах Internet’а...

Если вы никогда не программировали в Delphi7.0, версиями ниже или выше, если вывообще никогда не программировали наподобных языках, то эта статья не для вас.

СОДЕРЖАНИЕ 33ЛАБОРАТОРИЯ

Page 34: ПРОграммист 2010 03

[ПРОграммист май 2010

TIdUDPClientSocket на «localhost». Далее я простоперечислю свойства компонента и их значения,что должны быть установлены: Active (false),BroadCastEnabled (false), BufferSize (8192), Name(IdUDPClient1), Port (0), ReceiveTimeOut (-2), Tag(0).

Теперь, нажимаем двойным щелчком повынесенному на форму компоненту TButton ипоявится обработчик события Button1Click(), гдеButton1 – это значение свойства Name данногокомпонента. В этом обработчике пишем иликопируем следующий код (см. листинг 1):

Вы спросите, а что же такое waveInPrepareHeader?Это функция, выполняющая подготовку буферадля операции загрузки данных. Общий вид:

Здесь:HWaveIn - идентификатор открытого устройстваLpWaveInHdr - адрес структуры WaveHdr

Здесь:lpData - адрес буфера для загрузки данныхdwBufferLength - длина буфера в байтахdwBytesRecorded - для режима загрузки данных определяет количество

загруженных в буфер байтdwUser - пользовательские данныеdwFlags - флаги

Могут иметь следующие значения:WHDR_DONE - устанавливается драйвером при завершении загрузки

буфера даннымиWHDR_PREPARE - устанавливается системой. Показывает готовность

буфера к загрузке данныхWHDR_INQUEUE - устанавливается системой, когда буфер установлен в

очередьdwLoops - используется только при воспроизведении. При записи

звука всегда 0lpNext - зарезервированоreserved - зарезервированоuSize - размер структуры WaveHdr в байтах

Функция waveInPrepareHeader вызывается толькоодин раз для каждого устанавливаемого в очередьзагрузки буфера.

Что такое waveInAddBuffer()? ФункцияwaveInAddBuffer() ставит в очередь на загрузкуданными буфер памяти. Когда буфер заполнен,система уведомляет об этом приложение:

ПЕРЕДАЧА ЗВУКА ПО СЕТИ. ПРОТОТИП VOIP-ТЕЛЕФОНА

procedure TForm1.Button1Click(Sender: Tobject); ЛИСТИНГ 1begin// если на кнопке написано «начать отправку» то:// готовим заголовок для буфера, здесь WaveIn – для указания// идентификатора устройства ввода (микрофона например),// @WaveHdr – указатель на структуру TWaveHdr,// sizeof(Twavehdr) – размер данной структуры в байтах

If button1.Caption='Начать отправку' then BeginwaveInPrepareHeader(waveIn,@WaveHdr,sizeof(Twavehdr));// заносим данные в буферwaveInAddBuffer(wavein,@WaveHdr,sizeof(TwaveHdr));// активируем сокет клиентаIdUDPClient1.Active:= true;// считываем данные с микрофонаwaveInStart(waveIn);// в едит для наглядности заносим количество записанных байт// (делал для себя, чтобы проверять, пишется звук или нет)Edit1.Text:= inttostr(WaveHdr.dwBufferLength);// меняем название кнопки, чтобы создать возможность// прервать отправку пакетовbutton1.Caption:='Остановить отправку'

end else Begin // если «остановить отправку», тоbutton1.Caption:='Начать отправку';//закрываем сокет клиентаIdUDPClient1.Active:=false;//разгружаем буферwaveInUnprepareHeader(Wavein,@WaveHdr,sizeof(TwaveHdr));// приостанавливаем считывание. ЗАМЕТЬТЕ! ПРИОСТАНАВЛИВАЕМ!// Если мы напишем waveInClose(Wavein), то устройство будет// закрыто, и при повторном нажатии на кнопку, не будет// никакого результатаwaveInStop(Wavein);// смотрим кол-во не записанных байтEdit1.Text:=inttostr(Wavehdr.dwBytesRecorded)

endend;

type TWaveHdr = recordlpData: PChar; // указатель на буферdwBufferLength: DWORD; // длина буфераdwBytesRecorded: DWORD; // записанные байтыdwUser: DWORD; // пользовательская переменнаяdwFlags: DWORD; // флагиdwLoops: DWORD; // повторlpNext: PWaveHdr; // переменная для драйвераreserved: DWORD; // зарезервированно

end;

function waveInPrepareHeader(hWaveIn: HWAVEIN;lpWaveInHdr: PWaveHdr;uSize: UINT

): MMRESULT; stdcall;

СОДЕРЖАНИЕ 34ЛАБОРАТОРИЯ

Page 35: ПРОграммист 2010 03

[ПРОграммист май 2010

Здесь:hWaveIn - идентификатор Waveform audio устройства вводаlpWaveInHdr - адрес структуры TWaveHdruSize - размер WaveHdr в байтах

Что такое waveInStart(), waveInStop(),waveInClose()? Общий вид записи таков:

function waveInStart(hWaveIn: HWAVEIN):MMRESULT; stdcall;

waveInStop(), waveInClose() имеют совершенноодинаковый параметр – как и WaveInStart(),которую описывать не имеет смысла, ибо и такпонятно, что она начинает считывать данные сустройства ввода, а вот waveInClose() закрываетустройство для записи, и его снова придетсяоткрывать с помощью WaveInOpen(), но об этомниже… А вот waveInStop(), ставит запись как бы напаузу, и нам не надо повторно использоватьWaveInOpen().

Что такое waveInUnprepareHeader? Функцияаналогичная waveInPrepareHeader(), однако онавозвращает выделенную память на буфер, т.е. какбы «уничтожая» его.

Как узнать, что можно передавать данные?

Мы разобрали некоторые функции WIN API,относящиеся к вводу данных. Не устали? Нет?Тогда двигаемся дальше! Создадим собственнуюпроцедуру для определения завершения передачиданных в блок памяти посредствомWaveInAddBuffer(). А выглядит она так (см.листинг 2):

В этой процедуре используются уже известныевам функции, по этому второй раз описывать их небудем. Пишем её сразу после строки {$R *.dfm}. Аописываем эту процедуру в разделе private классаTForm1, как:

procedure OnWaveMessage(var msg:TMessage);message MM_WIM_DATA;

Эта процедура будет выполняться каждый раз кактолько передача данных в буфер будет завершенаи система сгенерирует сообщение WIM_DATA.Заполним обработчик события формы OnClose()(см. листинг 3):

И конечно же, заполним обработчик событияформы OnCreate() (см. листинг 4):

ПЕРЕДАЧА ЗВУКА ПО СЕТИ. ПРОТОТИП VOIP-ТЕЛЕФОНА

function waveInAddBuffer(hWaveIn: HWAVEIN;lpWaveInHdr: PWaveHdr;uSize: UINT

): MMRESULT; stdcall;

procedure TForm1.OnWaveMessage(var msg:TMessage); ЛИСТИНГ 2beginwaveInPrepareHeader(waveIn,@WaveHdr,sizeof(Twavehdr));waveInAddBuffer(wavein,@WaveHdr,sizeof(TwaveHdr));// отправляем буфер на сервер, где WaveHdr.lpData^ -

// это ссылка на память, где хранятся считанные с// микрофона данные уже преобразованные в// последовательность нулей и единиц,// WaveHdr.dwBufferLength – длина буфера данныхidUDPClient1.Sendbuffer(WaveHdr.lpData^,

WaveHdr.dwBufferLength);// в переменную заносим количество отправленных байтBytes:= Bytes+WaveHdr.dwBufferLength;

// формат строки. Посмотрите в google фразу format дельфиCaption:= Format ('%u',[Bytes]);UpDate

end;

procedure TForm1.FormCreate(Sender: TObject); ЛИСТИНГ 4begin// with – оператор, благодаря которому можно не писать// переменные, а указывать сразу их свойства.// В данном случае WaveFormat: TWAVEFORMATEX// отвечает за сигнал, т.е. за все его характеристикиwith waveformat do beginnChannels := 1;wFormatTag := WAVE_FORMAT_PCM;nSamplesPerSec:= 8000;wBitsPerSample:= 8;

procedure TForm1.FormClose(Sender: TObject; ЛИСТИНГ 3var Action: TCloseAction);

begin// завершаем все действияAction:= caFree;// деактивируем сокетIdUDPClient1.Active:=false;// закрываем устройство записиwaveInClose(Wavein);

end;

СОДЕРЖАНИЕ 35ЛАБОРАТОРИЯ

Page 36: ПРОграммист 2010 03

[ПРОграммист май 2010

Что же такое WaveInOpen()?

Функция waveInOpen() открывает имеющеесяустройство ввода Waveform Audio для оцифровкисигнала. Типичная ее структура выглядитследующим образом:

Здесь:lphWaveIn - указатель на идентификатор открытого Waveform audio

устройстваuDeviceID - номер открываемого устройства (см. waveInGetNumDevs),

это может быть также идентификатор уже открытого ранееустройства. Вы можете использовать значение WAVE_MAPPERдля того, чтобы функция автоматически выбраласовместимое с требуемым форматом данных устройство

lpFormatEx - указатель на структуру типа TWaveFormatEx

В этой структуре значения полей следующие:wFormatTag - формат Waveform audio. Мы будем использовать

значение WAVE_FORMAT_PCM (это означает импульсно-кодовая модуляция) другие возможные значениясмотрите в заголовочном файле MMREG.H

nChannels - количество каналов. Обычно 1 (моно) или 2(стерео)nSamplesPerSec - частота дискретизации. Для формата PCM - в

классическом смысле, т.е. количество выборок всекунду. Согласно теореме отсчетов должна вдвоепревышать частоту оцифровываемого сигнала. Обычнонаходится в диапазоне от 8000 до 44100 выборок всекунду

nAvgBytesPerSec - средняя скорость передачи данных. Для PCM равнаnSamplesPerSec*nBlockAlign

nBlockAlign - для PCM равен (nChannels*wBitsPerSample)/8wBitsPerSample - количество бит в одной выборке. Для PCM равно 8

или 16cbSize - равно 0. Подробности в Microsoft Multimedia

Programmer's ReferencedwCallback - адрес callback-функции, идентификатор окна или

потока, вызываемого при наступлении событияdwInstance - пользовательский параметр в callback-механизме.

(cам по себе не используется)dwFlags - флаги для открываемого устройства:CALLBACK_EVENTdwCallback - параметр – код сообщения (an event handle)

CALLBACK_FUNCTION - параметр - адрес процедуры-обработчикаCALLBACK_NULL - параметр не используетсяCALLBACK_THREAD - параметр - идентификатор потока командCALLBACK_WINDOW - параметр - идентификатор окнаWAVE_FORMAT_DIRECT - если указан этот флаг, ACM-драйвер не

выполняет преобразование данныхWAVE_FORMAT_QUERY - функция запрашивает устройство для определения

поддерживает ли оно указанный формат, но неоткрывает его

Мы использовали callback функцию в

ПЕРЕДАЧА ЗВУКА ПО СЕТИ. ПРОТОТИП VOIP-ТЕЛЕФОНА

nBlockAlign := 1;nAvgBytesPerSec:= 8000;cbSize := 0;

end;

// для удобства загоняем размер буфера в переменную,// которую будем вызыватьbufsize:= waveformat.nAvgBytesPerSec*2 div 16;// размеру буфера сокета присваиваем размер буфера bufsizeIdUDPClient1.BufferSize:=bufsize;

// waveInOpen опишем чуть ниже, как и обещал,// WAVE_MAPPER – система сама выбирает устройствоwaveInOpen(@Wavein,

WAVE_MAPPER,addr(waveformat),self.Handle, 0, CALLBACK_WINDOW);

// выделяем память под заголовок буфера данныхWaveHdr.lpData:=Pchar(GlobalAlloc(GMEM_FIXED, bufsize));// присваиваем длину буфера TWaveHdr’уWaveHdr.dwBufferLength:=bufsize;// сбрасываем флагиWaveHdr.dwFlags:=0;// устанавливаем порт подключения для клиентаIdUDPClient1.Port:= 10090

end;

function waveInOpen(lphWaveIn: PHWAVEIN;uDeviceID: UINT;lpFormatEx: PWaveFormatEx;dwCallback,dwInstance,dwFlags: DWORD

): MMRESULT; stdcall;

type TWaveFormatEx = packed recordwFormatTag: Word; // format typenChannels: Word; // number of channels (mono, stereo, etc.)nSamplesPerSec: DWORD; // sample ratenAvgBytesPerSec: DWORD; // for buffer estimationnBlockAlign: Word; // block size of datawBitsPerSample: Word; // number of bits sample of mono datacbSize: Word; // the count in bytes of the size of

end;

СОДЕРЖАНИЕ 36ЛАБОРАТОРИЯ

Page 37: ПРОграммист 2010 03

[ПРОграммист май 2010

OnWaveMessage(). В последнюю очередь я опишупеременные, которые использовались (см. листинг5):

Так же для работы программы необходимодобавить модуль MMSystem в раздел uses. Клиентготов! Как видите, не так страшен черт, как егомалюют! Перед тем как перейти к написаниюсервера, я бы вам настоятельно рекомендовал быпокопаться в генофонде всех выше описанныхфункций и самостоятельно глубже разобраться втом, как они устроены. Так для болееуглубленного изучения, советую переворошитьсодержимое таких компонентов из серии ACM какAcmIn, AcmOut. Только самообучением можночего-нибудь добиться.

А что же сервер?

С чистой перед клиентом совестью, можемприступить к написанию сервера! Возможно, этапроцедура покажется вам более сложной, но,разобравшись в ней, вы поймете, что это не так.Единственно, что работать мы будем не с однимбуфером, а с восемью, для удобства

воспроизведения звука. В один записываем,воспроизводим, очищаем, готовим, записываем ит.д. по очереди каждый из восьми. Так же будетрассмотрена работа с флагами (dwflags) и приемапотока данных (TMemoryStream) на сервер.Приступим, нетерпеливые мои!

Как обычно, создадим новый проект и вынесем наформу компонент TMemo (name=memo1) (опятьже-таки я использовал его в целях определенияполучения потока данных, перегоняя его вшестнадцатиричный формат), кнопку TButton иIdUDPServerSocket (см. рисунок 2):

Рис. 2. Режим проектирования формы тестовогосервера

Пожалуй, начнем с простого. Напишем нижеприведенный код в обработчике события OnClose()формы (см. листинг 6):

Далее займемся обработчиком события OnClick()кнопки TButton1 (см. листинг 7):

Теперь напишем процедуру, которую мы будемиспользовать для воспроизведения принятогозвука (см. листинг 8):

ПЕРЕДАЧА ЗВУКА ПО СЕТИ. ПРОТОТИП VOIP-ТЕЛЕФОНА

type ЛИСТИНГ 5TForm1 = class(TForm)

IdUDPClient1: TIdUDPClient;Button1: TButton;Edit1: TEdit;procedure Button1Click(Sender: TObject);procedure FormClose(Sender: TObject; var Action:

TCloseAction);procedure FormCreate(Sender: TObject);

privateprocedure OnWaveMessage(var msg:TMessage); message

MM_WIM_DATA;{ Private declarations }

public{ Public declarations }

Wavein:HWAVEIN;WaveHdr:TWaveHdr;bufsize:Cardinal;end;

varForm1: TForm1;WaveDataLength:integer;bytes:integer;device:word;waveformat: TWAVEFORMATEX;a:integer;

procedure TForm1.Button1Click(Sender: TObject); ЛИСТИНГ 7begin// активируем сокет сервераIdUDPServer1.Active:= not IdUDPServer1.Active;if IdUDPServer1.Active then button1.Caption:= 'Выкл сервер'else button1.Caption:= 'Вкл сервер'

end;

procedure TForm1.FormClose(Sender: TObject; ЛИСТИНГ 6var Action: TCloseAction);

begin// завершаем действияAction:= caFree;// выключаем серверIdUDPServer1.Active:= False

end;

СОДЕРЖАНИЕ 37ЛАБОРАТОРИЯ

Page 38: ПРОграммист 2010 03

[ПРОграммист май 2010

Процедура разобрана, осталось ейвоспользоваться… Как это осуществить? Всепросто, достаточно в обработчике событияOnUDPRead() idUDPServerSocket-a написатьследующий код (листинг 9):

И не забыть при создании формыпроинициализировать наши аудиоустройства. Дляэтого в обработчике OnCreate() формы запишем(см. листинг 10):

Уважаемые читатели, здесь я пишу безкомментариев только для того, что дать вамвозможность самим додуматься, что здесь к чему,это не так сложно, тем более, что вы это уже всезнаете (мы с вами выше подробно разбирали этианалогичные функции ввода и вывода и работы ссокетами).

ПЕРЕДАЧА ЗВУКА ПО СЕТИ. ПРОТОТИП VOIP-ТЕЛЕФОНА

procedure TForm1.IdUDPServer1UDPRead( ЛИСТИНГ 9Sender: TObject; AData: TStream; ABinding: TIdSocketHandle);

Begin// если мы воспроизвели последний буфер то, начинаем всё// сначала (с первого)If a = CWaveBufferCount thena:= 0;

procedure TForm1.playsound(s:Tstream); ЛИСТИНГ 8var msg: Tmessage;begin// пока а не равно нашему количеству буферов выполняемWhile a<>CWaveBufferCount do Begin// проверку пользовательской установки на то, что буфер// готов к записиIf FHeaders[a].dwUser = 0 then begin// записываем в буфер данные из потока, пришедшего от клиентаs.Read(Fheaders[a].lpdata^,bufsize);// процедура waveOutPrepareHeader аналогична процедуре /// waveInPrepareHeaderwaveOutPrepareHeader(WaveOut,@FHeaders[a],sizeof(FHeaders));// Процедура waveOutWrite аналогична процедуре// waveInAddBuffer, только она осуществляет воспроизведение// данных из буфераwaveOutWrite(WaveOut, @FHeaders[a], sizeof(FHeaders));

memo1.Lines.Add('...Двоичный код потока...');// обнуляем флаги буфера/ов в циклеFHeaders[a].dwFlags:= 0;

// уже знакомая нам структураWith FHeaders[a] do begindwBufferLength := bufsize;dwBytesRecorded:= 0;dwUser := 0;dwLoops := 1;// А вот здесь мы присваиваем флагу только что// воспроизведенного буфера значение, которое отвечает за то// что буфер установлен в очередь, т.е. мы как бы// циклично используем эти 8 буферовdwFlags:= WHDR_INQUEUE

end;

// Увеличиваем индекс, чтобы перейти к следующему буферуinc(a);// соответственно после воспроизведения и подготовки нам// больше не нужен цикл и мы выходим из негоexit

endend

end;

procedure TForm1.FormCreate(Sender: TObject); ЛИСТИНГ 10beginbytes:= 0;WaveOut:= 0;With WaveFormatOut do beginnChannels:= 1;wFormatTag:= WAVE_FORMAT_PCM;nSamplesPerSec:= 8000;wBitsPerSample:= 8;nBlockAlign:= 1;nAvgBytesPerSec:= 8000;cbSize:= 0

end;

bufsize:= WaveFormatOut.nAvgBytesPerSec*2 div 16;For a:= 0 to CWaveBufferCount-1 doWith FHeaders[a] do begindwFlags:= WHDR_INQUEUE;dwBufferLength:= bufsize;dwBytesRecorded:= 0;dwUser:= 0;dwLoops:= 1;GetMem(Fheaders[a].lpData, bufsize);

end;

IdUDPServer1.BufferSize:= bufsize;IdUDPServer1.DefaultPort:= 10090;waveOutOpen(@WaveOut,

WAVE_MAPPER,@WaveFormatOut,self.Handle, 0, CALLBACK_WINDOW);

end;

//вызываем нашу процедуру, в скобках пишем наш поток, пришедший// на сервер, смотрите процедуру сокетаplaysound(Adata);

// определяем сколько байт мы принялиBytes:=Bytes + aData.Size;// показываем это в названии формыCaption:= 'Принятых байт' + Format('%u', [Bytes]);// обновляем формуUpDate

end;

СОДЕРЖАНИЕ 38ЛАБОРАТОРИЯ

Page 39: ПРОграммист 2010 03

[ПРОграммист май 2010

Далее осталось описать переменные и константы(см. листинг 11):

Я не стал описывать процедуру перегонки потока вHEX-формат, так как писал ради передачи данныхв TMемо. В конце концов, вы сами запростоможете убрать ненужные строки, относящиеся кней.

Заключение

Хочу заметить, что размеры буферов сокетов насервере и клиенте должны быть равны размерамбуферов структуры TWaveHdr, иначе вы неполучите никаких звуков на выходе, кромешипения с прерываниями, равными подлительности размеру вашего воспроизводимого

буфера. Также для более быстрой реакции насобытия приема звука используйте меньшиеразмеры буферов, но и соответственно увеличьтеих количество (8-ми вполне хватит). При желании,лучше использовать динамический.

Статья была написана специально для форумаКлуба ПРОграммистов www.programmersforum.ru.Исходники тестового проекта (клиента и сервера)прилагаются в виде ресурсов в теме «Журналклуба программистов. Третий выпуск» илинепосредственно в архиве с журналом [5].Выражаю огромную благодарность человеку, чейник на вышеуказанном форуме raxp, которыйактивно помогал мне в изучении этого материалакодами и советами.

Ресурсы

. П.В. Румянцев. Азбука программирования Win32API. - М., Радио и связь, 2000, 4-е издание

. Описание звуковых функцийhttp://www.delphikingdom.com/asp/viewitem.asp?catalogid=213

. Репозитарий Indy 9 (имя пользователя: Indy-Public-RO) https://svn.atozed.com:444/svn/Indy9

. Репозитарий Indy 10 (имя пользователя: Indy-Public-RO) https://svn.atozed.com:444/svn/Indy10

. Модули и проекты, использованные в статьеhttp://programmersclub.ru/pro/pro3.zip

. Обсуждение на форуме разработки прототипаVoIP телефона http://www.programmersforum.ru/showthread.php?t=91506

ПЕРЕДАЧА ЗВУКА ПО СЕТИ. ПРОТОТИП VOIP-ТЕЛЕФОНА

СОДЕРЖАНИЕ 39ЛАБОРАТОРИЯ

Const ЛИСТИНГ 11CwaveBufferCount = 8;type

TForm1 = class(TForm)IdUDPServer1: TIdUDPServer;Button1: TButton;Memo1: TMemo;procedure IdUDPServer1UDPRead(Sender: TObject; AData:

TStream; ABinding: TIdSocketHandle);procedure FormCreate(Sender: TObject);procedure FormClose(Sender: TObject; var Action:

TCloseAction);procedure Button1Click(Sender: TObject);procedure playsound(s:Tstream);

privatehdr: PwaveHdr;{ Private declarations }

public{ Public declarations }WaveOut:HWAVEOUT;WaveHdrOut,WaveHdrOut2:TWaveHdr;WaveFormatOut:tWAVEFORMATEX;bufsize:word;FBuffer:Pointer;FSndBuffer:Pointer;FHeaders:array[0..CWaveBufferCount-1] of TWAVEHDR;FBufSize:Cardinal;

end;

varForm1: TForm1;bytes:Cardinal;

WaveOut: HWAVEOUT;WaveHdrOut,WaveHdrOut2: TWaveHdr;WaveFormatOut: tWAVEFORMATEX;bufsize:word;a:integer;

Page 40: ПРОграммист 2010 03

[ПРОграммист май 2010 РАЗРАБОТКА РЕСУРСА ЖУРНАЛА. ЧАСТЬ 1

СОДЕРЖАНИЕ 40ЛАБОРАТОРИЯ

Здравствуйте, уважаемые читатели! Для начала оговорим, что мы решили разбитьстатью на несколько частей, так как данный материал не поместится в объемеодного выпуска. В первой части вы узнаете о написании ленты новостей, гостевойкниги и вообще всего интерфейса пользователя, а остальные будут посвященынаписанию панели администратора. Сегодня рубрику буду вести я, Егор Горохов akaRevival001, далее меня сменит Алексей Шульга aka Levsha100. Мы будем помогать другдругу в этом нелегком труде. Итак, начнем...

Егор Гороховby Revival001 [email protected]

1. Дизайн

Для начала был нарисован PSD шаблон сайта вграфическом редакторе Photoshop. Оттуда же мыпотом и «выдирали» картинки для сайта. В

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

достаточно сменить фоновую картинку и немногоподкорректировать таблицы стилей CSS.

2. Верстка

Для создания закругленных углов использовалосьсвойство border-radius. К сожалению, пока чтосвойство работает не во всех браузерах, например

в IE 6...8 оно игнорируется. Конечно существуют иальтернативные способы создания круглых углов,но они зачастую требуют до 9 (!) картинок блока

Рис. 1. Предварительный редизайн сайта журнала

Page 41: ПРОграммист 2010 03

[ПРОграммист май 2010

СОДЕРЖАНИЕ 41ЛАБОРАТОРИЯ

РАЗРАБОТКА РЕСУРСА ЖУРНАЛА. ЧАСТЬ 1

(4 угла, 4 стороны и центр). Так как мы «оченьленивые», да и острые углы не очень портятдизайн, мы использовали простейшее решение, снадеждой на то, что вскоре все браузеры будутподдерживать данное свойство...Для полупрозрачности главного div не былонайдено (возможно мы плохо «гуглили»)кроссбраузерного решения, поэтому мы пошли нахитрость, создав однопиксельный полупрозрачныйpng, и поставили его на фон. Но и этот способ, какоказалось имеет свои минусы. Например*,ненавистный всеми дизайнерами/верстальщикамиIE6 его игнорирует.

3. Написание скриптов

Для начала, вынесем все наиболее частоиспользуемые переменные в отдельный файл<config.php>, и потом будем его «инклудить» вовсе скрипты. Мы поместили туда логин\пароль отБД, путь к сайту и т.п. В дальнейшем, если мызахотим изменить что-либо, то это займет всего-лишь несколько секунд, и не потребуется вноситьизменения во множество скриптов.

Итак, при заходе на сайт пользователь долженвидеть: ленту новостей, журналы доступные дляскачивания, гостевую книгу. Реализцияпроисходит практически одинаково. Покажу напримере гостевой книги (см. листинг 1):

А теперь скрипт добавляющий записи в БД (см.листинг 2):

<?php ЛИСТИНГ 1require_once "config.php"; // Подключение файла конфигурации// Функция обрабатывает строку, и заменяет все bb-коды на htmlfunction bbcodes($str) {$bbcode = array("/\[b\](.*?)\[\/b\]/is" => "<strong>$1</strong>","/\[u\](.*?)\[\/u\]/is" => "<u>$1</u>","/\[url\=(.*?)\](.*?)\[\/url\]/is" => "<a href='$1'>$2</a>","/\[color\=(.*?)\](.*?)\[\/color\]/is" => "<fontcolor='$1'>$2</font>","/\[i\](.*?)\[\/i\]/is" => "<i>$1</i>","/\[quote\](.*?)\[\/quote\]/is" =>

* Комментарий автора.Кстати, я уже давно не видел людей использующих IE6. Все моизнакомые, даже если они мало понимают в компьютерах (напримердевушки) используют браузеры типа Opera и Mozilla Firefox. Поэтомупричин продолжать верстать под MSIE6 я уже не вижу.

Ознакомится с CSS можно по адресу: http://procoder.info/style.css

"<div class=\"quote\">$1</div>", "/\[img\](.*?)\[\/img\]/is" =>"<img src=\"$1\">" );$str = preg_replace(array_keys($bbcode), array_values($bbcode),$str);return $str;} // Функция взята из мануалов по PHP

// Подключение к БД$db = mysql_connect($db_server, $db_user, $db_pass);mysql_select_db($db_name);// Выбираем из таблицы table_gb последние 30 записей// и сортируем их по id$db_query=mysql_query("SELECT * FROM `table_gb` ORDER BY `id`DESC LIMIT 0 , 30", $db);while ($res = mysql_fetch_array($db_query)) // Вывод записей{echo "<div class=\"guest_comment\"><b>".$res['name']."-".$res['date']."</b><br>";echo bbcodes(wordwrap("<pre>".$res['text']."</pre>", 75));echo "</div><br>";}?> // Тут находится форма, для добавления записей в БД,

// которая передает скрипту gb.php данные

<?php ЛИСТИНГ 2require_once "config.php";$db = mysql_connect($db_server, $db_user, $db_pass);mysql_select_db($db_name);

// Получаем данные и убираем из них спец-символы,// для защиты от XSS-атак$name = htmlspecialchars($_POST['name']);$text = htmlspecialchars($_POST['text']);$date_array = getdate(time());$date = $date_array['mday'].".".$date_array['mon'].".".$date_array['year'];if (($text=='') or ($text=='Введите текст')){echo "<script>alert('Запись не добавлена.');</script>";echo '<meta http-equiv="Refresh" content="0; URL='."$index_path".'">';}else{if (($name=='Ваше имя') or ($name=='')) {$name='Аноним';}

// Добавляем запись в БД$db_query = mysql_query("INSERT INTO `table_db` (

`id` ,`name` ,

`text` ,`date`

) VALUES ('', '$name', '$text', '$date');", $db);

mysql_close($db);echo "<script>alert('Запись добавлена');</script>";echo '<meta http-equiv="Refresh" content="0; URL='."$index_path".'">';}?>

Page 42: ПРОграммист 2010 03

[ПРОграммист май 2010

СОДЕРЖАНИЕ 42ЛАБОРАТОРИЯ

РАЗРАБОТКА РЕСУРСА ЖУРНАЛА. ЧАСТЬ 1

Аналогичным образом выводятся записи дляновостей, журналов для закачки и другаяинформация (см. рисунок 2):

Подведем предварительные итоги...

Что есть и что планируется? Пока сделано оченьмало, и нововведения** будут появлятьсяпрактически каждый день. Совсем скоро будетсистема регистрации, что позволиткомментировать статьи, новости и выставлять имоценки. У каждого пользователя будет личныйкабинет. Создавать отдельный форум для сайта непланируется, так как это совместный проект КлубаПРОграммистов www.programmersclub.ru и форумуже есть. Существующие алгоритмы тоже будутулучшены. Все предложения оставляйте либо нафоруме, либо отправляйте на электронный ящикредакции.

** Комментарий автора.Мы с Алексеем заметили, что у нас серьезные проблемы сбезопасностью, поэтому если вы нашли уязвимость, то пожалуйстасообщите о ней на ящик журнала [email protected].

Рис. 2. Скрин тестовой работы скриптов

Page 43: ПРОграммист 2010 03

ФРАЗЕОЛОГИЗМЫ И БАЙКИ[ПРОграммист май 2010

Мы изучили Ваше коммерческое предложение поразработке информационной системы и принялирешение приобрести некоторое количество травы,которую вы курите.

Разговаривают два программиста:- А у меня микроволновка не включается...- Что пишет?

Заходит жена программиста в комнату и говоритмужу, который поглощён работой за компьютером:- Твой сын может читать только печатные буквы, апрописные не понимает!Программист, не отрываясь от компьютера:- Поставьте более новую версию «FineReader»...

(пауза)- Что ты сказала, дорогая?

Новый вирус – «Бомж!» Он просто роется в«Корзине»...

В отдельном кабинете за столом сидит админ,читает журнал. Дверь открывается, влетает чутьне плача девушка и запинаясь говорит - «у меняВорд завис, не знаю что делать...» Админ,добродушный дяденька, не меняя позы и неотрываясь от журнала говорит: «Выйдите изайдите снова». Девушка, покраснев, выходит изкабинета закрыв дверь. Через три секунды дверьвновь открывается и девушка запинаясь ещебольше: «У меня Ворд завис, не знаю что делать...»

Имя: ЕкатеринаФамилия: скрытоДата рождения: скрытоE-mail: [email protected]

Одна девушка-программистка характеризуетсвоего знакомого: «У него в алгоритме ухаживанияни одного if не предусмотрено, не говоря уже отаких сложных конструкциях, как while или until»!

Трудное детство... килобайтные игрушки...

- Для чего нужны бухгалтерские программы?- Как для чего? Для снижения безработицы,

конечно! Там, где раньше сидел один бухгалтер,теперь сидят еще оператор, программист,электронщик, пять наладчиков и начальникотдела вычислительной техники...

Программист жене: СD молча и не DVD меня добелого каления!!!

Если у вас нет отца, то кликните на рабочем столеправой кнопкой мыши и дальше выберите«Создать / ПАПКУ".

СОДЕРЖАНИЕ 43ЮМОР. ХОХМЫ ПРОГРАММИСТОВ

Page 44: ПРОграммист 2010 03

ФРАЗЕОЛОГИЗМЫ И БАЙКИ[ПРОграммист май 2010

Приходит веб-мастер сдавать на права. Опрос:- Цвет глаз- Пишет: #44ff99

Заядлый компьютерщик в ЗАГСе интересуется:- А Вы регистрируете детей с именами длиннее 8букв?

Гаишник тормозит машину... Открываетсяводительская дверь и вываливается пьяный мужик.- Ваши права!- Аддммминистратор!

Идет программист по стройке, вдруг с кранасрывается огромный блок и падает программистуна голову.Тетрис, успелподуматьпрограммист.

Новаярусскоязычная поисковаясистема«ИванСусанин».

Кнопка скрестиком в правом верхнем углу экрана – шлюз вреальный мир...

Был случай лет 15 назад... мы тогда прикручивалиодну из первых АСУ ТП к ДНС-КНС в ЗападнойСибири. Написали голосовых сообщений, дажеусилитель поставили, чтобы оператор на улицеслышал. Все бы ничего, но мне вздумалосьповеселиться. Записал такой текст: Внимание,аварийная ситуация! Нарушен технологическийрежим! Установка будет взорвана через 5 минут.Срочная эвакуация! Ну и тому подобное собратным отсчетом. Получилось оченьправдоподобно. А потом... прикрутил сиепроизведение на алгоритм, который вообщеникогда (как я думал) не произойдет, потому чтоэто прямое нарушение техпроцесса. Однакофигушки! Произошло. Через месяц после

внедрения. Оператор там совсем безбашенный навахту заступил.Ну че, я на том объекте больше не появлялся:тамошние горячие башкирские мужики грозилисьменя прирезать... ибо убегали они в 40- градусныймороз при сработке моего аларма в лес, километра3 от установки. Причем спросонья, в однихтапочках по сугробам. На разборке у руководствапредъявил алгоритм, оператора наказали... но, вобщем меня тоже по головке не погладили зашутку. Хотя отнеслись с юмором и пониманиемтого уровня знания техпроцесса операторами.

Аналогичная установка на Саранском пивномзаводе. Там местный умелец прикрутил к WinCC

сообщения,зачитанныеприятнымречевымдвижком.Толькосделал он этокак-тостранно:записалтекст,скормилдвижку,

получил .wav'ы и их уже прикрутил. Не судьбабыла сразу прикрутить? Операторы намжаловались, что по ночам спать мешает. А таккак сеть вся MPI (вы никогда не видели 5-7проходных коннекторов MPI/Profibus, вставленныхв одно гнездо друг за другом так, что шкаф незакрывается?) и длина ее критическая, то раз в 10минут все отваливается и восстанавливаетсяснова. Соответственно женщина из ящика сначалаговорит, что все отключилось, а потом, что всевключилось. Процесс внушает! Но, больше всегодоставал один дедушка - оператор. Он с нейразговаривал:

- Афанасий, крепкое запущено на фильтрацию.- Правильно!- Сепаратор разогнан.- Молодец!

СОДЕРЖАНИЕ 44ЮМОР. ХОХМЫ ПРОГРАММИСТОВ

Page 45: ПРОграммист 2010 03

ФРАЗЕОЛОГИЗМЫ И БАЙКИ[ПРОграммист май 2010

Заходит программер к врачу:- Доктор, я кашляю. Это к вам?- Да, вы попали по адресу. Я - терапевт.- Ой, нет. ТЕРАпевт - слишком круто! Мне бы кГИГАпевту. Или даже к МЕГАпевту!

Задачка, которую хороший программист решит вминуту, а хороший физик сойдет с ума: Ася весит4,2 метра. За сколько времени она скачается, еслиширина канала - 5 кило в секунду?

- С вас 283 рубля. Завернуть?- А? Нет, не надо, просто в пакет положите… Один

вопрос - гарантия есть?- Простите, а для какого случая гарантия?- Ну, если не прочитается?- То есть как?- Ну несовместимость требований, например…Брак там, туда-сюда…

- Вся прогрессивная молодежь в курсе, что обычносначала туда-сюда, а потом брак. В худшемслучае… Не прочитается… А, я понял - вы попрофессии, как бы это сказать, минимумпродвинутый пользователь?

- Да, я программист. Но как вы догадались?- Молодой человек, будь на моем месте ШерлокХолмс, он бы сказал - по очкам, грязным джинсам,торчащему из кармана рюкзака винчестеру, трембутылкам пива в руках и клавиатуре под мышкой.Но я не он…. И все намного проще. Это бумажнаякнига, она обязательно прочтется…

- Алло?- Алло! Доброе утро, молодой человек! Умоляювас, не кладите трубку! Скажите мне - кто я?! Гдея сейчас?! Куда мне идти?!

Дорогие радиослушатели, вы прослушаликороткую радиопостановку «Утро DHCP сервера».

Коктейль дляпрограммиста:водка с пивом640 на 480.

СОДЕРЖАНИЕ 45ЮМОР. ХОХМЫ ПРОГРАММИСТОВ