Перейти к содержимому

Fingercomp

Гуру
  • Публикации

    1 629
  • Зарегистрирован

  • Посещение

  • Победитель дней

    283

Все публикации пользователя Fingercomp

  1. Полагаю, к реактору надо как раз поставить адаптер, а от него проводами к компу.
  2. Совсем небольшой апдейтик, добавляющий ещё 12 килобайт кода. 1. Добавлена карточка LFSR шума Я как-то про неё забыл, из-за чего она на меня обиделась. Но справедливость восторжествовала, и она стала доступна для установки. Я без понятия, как оно работает, автор мода тоже в неведении пребывает, такие дела. Поэтому играйтесь. 2. Улучшен плоттер Во-первых, я разобрался, как надо уметь в зум, поэтому теперь можно отдельно прописывать сэмплрейт и зум. Кроме того, починил плоттеры шума (LFSR и белый шум), которые раньше эмулировали скорее мычащее стадо мух, чем шум для серьёзных дяденек. Ну и в сетапах с модуляторами можно модуляторы на отдельный график теперь ставить (раньше тупо нуль рисовал). 3. Добавлена PCM-карточка Теперь звук можно записывать путём попроще, чем редиректить его из кубача в файл. Подключите все нужные каналы, как и у звуковой карты, к PCM-карте и затем тыкните правой кнопой мышки. Задайте параметры вывода: Sample rate - частота дискретизации. Количество измерений в секунду. Должно быть минимум в два с небольшим раза больше максимальной частоты в выхлопе (напомню, что боковые частоты у модуляторов могут быть выше частоты несущего сигнала). 44100 заявлено как качество CD и является дефолтным сэмплрейтом звуковой карточки, 48000 - качество DVD. Depth - глубина дискретизации. Размер одного сэмпла в битах. Чем больше глубина, тем точнее кодируется амплитуда, но реально разница между 16 битами, например, и 32 не особо заметная для игрушечных выводах в этой проге. Length (in secs) - длина [аудио-файла] (в секундах), внезапно. Format - формат файла. На самом деле, один фиг будет генерироваться LPCM, только если выставить формат в WAV, то за счёт 44 байт можно будет проигрывать полученный файл в любом плеере без шаманских плясок. Path - путь. Туда побежит звук. Очевидно, что файл будет перезаписан (и вас об этом не спросят, потому что это же суровая прога для суровых ребят, а не мягкая комната). Total file size - размер файла, кто бы мог подумать. В кибибайтах - ну там и так написано, так-то. Размер = частота дискретизации × (глубина дискретизации / 8) × длина + (формат == WAV ? 44 : 0). Generate - это кнопка, по которой люди тыкают, чтобы скормить звук в файл. Справа от кнопки прогресс бар, показывающий прогресс. Как-то так. WAV сразу в плеер посылать можно, а для PCM надо указывать формат. Пример для ffplay: $ ffplay -f s${DEPTH}le -ar $SAMPLERATE -ac 1 -i out.pcm Не переиграйтесь с размером: диски в OC не резиновые. 4. Пофикшены баги Самые разные, в основном довольно тупые. Обновить $ oppm update $ # или $ hpm oppm:install -r synth
  3. Циферка нашлась и вернулась домой.
  4. Не, , хватит повсюду оффтопить. Не знаешь - молчи. Автору: гугл вам в помощь, либа же реально в самых первых ссылках. http://computercraft.ru/topic/1951-gps-oc/
  5. Продолжаю рассказывать про Computronics и, в частности, про офигенную звуковую карточку из этого мода. На очереди модуляция: частотная и амплитудная. Помимо этого восполняю долг по основам. Юзать будем мою прогу synth, которую я недавно зарелизил. Она здесь невероятно поможет. Звуковая волна Вы же знаете, как выглядит звуковая волна? Вот, например, синусоида. Как видно, здесь есть некоторый фрагмент, который повторяется несколько раз. Частота показывает, сколько раз в секунду этот фрагмент повторяется, и измеряется в герцах (Гц или Hz). Чем выше частота, тем больше волна "сжата", скажем так, с боков. Вот как выглядят три синусоиды с разными частотами: 110 Гц, 220 Гц, 440 Гц - на одинаковом масштабе. Кроме частоты, звуковая волна характеризуется таким параметром, как амплитудой. Это, скажем так, расстояние от нуля до самого большого по модулю значения волны. Чем больше амплитуда, тем громче звук. Мы примем за единицу амплитуду сигнала на звуковой карте при максимальной громкости. При нулевой амплитуде на графике будет прямая линия вдоль горизонтальной оси. При максимальной громкости у простой синусоиды пиковые значения будут в точках +1 и -1. Они плавно сменяются. Прикольно, да. Синусоиды - это офигенные штуки, но на них клин светом не сошёлся. Из основных типов волн, помимо синусоид, есть меандр, треугольная волна и пилообразная. Они в таком же порядке изображены на рисунках ниже. (по поводу первой и третьей пикчи: вообразите, что волны не разомкнуты, а просто резко переходят с одного конца на другой. Небольшая хрень, над которой нужно побиться.) На самом деле, их можно представить в виде набора бесконечного числа синусоид, о чём когда-то поведал Фурье, но мы с этим заморачиваться не будем: раскладывать на синусоиды в звуковой карте не принято. Поведали об этом для красного словца - и то хорошо. Теперь, когда рассказал про основы, можно переходить к более весёлым и сложным вещам. То бишь к модуляции. Модуляция Модуляция - это изменение одного сигнала (несущего) другим (модулятором). Изменять можно по-разному - мы будем говорить об амплитудной модуляции и частотной модуляции. С модуляцией у нас появляется уникальнейший шанс получить офигенные и красивые звуки, поэтому не будем ждать и сразу перепрыгнем к мясу. Амплитудная модуляция Как я сказал, для модуляции нужны два сигнала: несущий и сам модулятор. Поэтому здесь и далее я привожу на рисунках сразу три графика: несущий сигнал, модулирующий сигнал и результат модуляции. Например, выставим две синусоиды с частотой 440 Гц. Итак, амплитудная модуляция - это умножение одного сигнала на другой. A(t) = C(t) × M(t), где t - время, C - функция, возвращающая значение несущей волны на моменте времени t, M - то же, но для модулятора. Однако не всё так просто. Перед умножением к значениям с модулятора прибавляется единица. Получается, что самая верхняя точка будет на +2, а самая нижняя - на 0. Иными словами, волна перенесена вверх. На низких частотах - до 20-30 Гц, откуда начинается граница слышимого человеком звука, - графики будут выглядеть как-то так, медленно увеличивая амплитуду от 0 до 4 и обратно. И звучать оно будет как увеличение и уменьшение громкости (количество таких увеличений и уменьшений равно частоте модулятора). Однако когда частота модулятора становится больше, наблюдаем вот такую картину (частота модулятора равна здесь 330 Гц). Если прислушаться, то станет заметно, что одновременно будто бы проигрываются три звуковых волны. Одна волна имеет такую же амплитуду и частоту, как и несущая. Две другие частоты, задающие боковую полосу частот (диапазон частот, сконцентрированных рядом с какой-либо), находятся так: f1 = |c - m|, f2 = c + m, где c - частота несущей волны, а m - частота модулятора. Возникающие звуковые волны около этих частот вдвое тише, чем несущая. В звуковой карте можно поставить амплитудный модулятор следующим образом: sound.setAM(carrierChan: number, modulatorChan: number). Амплитудная модуляция - забавная вещь, и в то же время тут очень трудно подобрать что-то интересное и красивое. Поэтому переходим к частотной модуляции - там веселья дофига. Частотная модуляция В частотной модуляции модулятор изменяет частоту несущей волны. Как ни странно. Почему частотная модуляция уделывает амплитудную? Здесь может быть гораздо больше боковых частот. И потому звучать оно может гораздо круче. При когда значение на модуляторе увеличивается, повышается и частота на несущей волне. Звуковая карта поддерживает индекс модуляции. Он задаёт максимальное изменение частоты несущей волны. При индексе, равном 100, частота несущей волны может меняться на 100 Гц вверх и на 100 Гц вниз. Если индекс равен 1000, то частота меняться может на 1000 Гц вверх и на 1000 Гц вниз. Ну и так далее. Иными словами, индекс задаёт силу модуляции. Если частота модулятора будет очень низкой (например, 4 Гц), то получим что-то вроде сирены. c = 440 Гц; m = 4 Гц; i = 200 С повышением частоты получим вибрирующий звук. И потом услышим дополнительные частоты. c = 220 Гц; m = 880 Гц; i = 660 Как видно, получившаяся волна получилась довольно сложной. Установить модулятор в звуковой карте можно с помощью функции sound.setFM(carrierChan: number, modulatorChan: number, index: number). И не забывайте про ADSR-огибающую: из однообразного тона можно получить довольно интересный звук. Как работает эта штука, я рассказывал. На этом всё. Из всех фич остался неразобранным лишь шум LFSR, но там штука очень и очень странная и непонятная. Наверняка всё равно остались некоторые вопросы по поводу модуляции. Поэтому за дополнительной информацией я предлагаю обратиться к другим сайтам. Вот несколько очень полезных ссылочек, где есть примеры звука и детальное описание: FM Synthesis - The Synthesizer Academy Frequency modulation synthesis - Wikipedia FM Synthesis - Music and Computers Modulation synthesis - Wikibooks (здесь рассказывается про амплитудную модуляцию в том числе). Ну и используйте прогу synth, чтобы удобно было изучать звуковую карту.
  6. И в самом деле, забыл в Container добавить проверку на nil перед использованием функции patch. Исправил эту багу и удалил ненужную функцию (я тогда думал, как бы получше реализовать гистограммы) в @1.3.1. Автоматическое масштабирование я делать не стал, так как я не знаю, как нужно оно энд-юзеру. Нужно ли ему, например, вытянуть часть графика за левелом, сделать единый масштаб или как-либо ещё. Так что просто нужно пробежаться по всем значениям и выставить нужные параметры. local min, max = 0, 0 for _, v in pairs(hist.values) do min, max = math.min(min, v), math.max(max, v) end max = max + (min == max and 1 or 0) hist.min = min hist.max = max
  7. Функция вызывается при получении ивента touch, ей передаются параметры ивента. Обработка ивентов происходит во время работы функции event.pull (или os.sleep, которая внутри всё равно вызывает event.pull).
  8. Прога для тех, кто прочитал мой прошлый гайд по звуковой карте в моём блоге и теперь хочет творить звуки. Потому что вслепую набирать код очень скучно. Synth - это, наконец-то, внятная гуишка для звуковой карты. Она не умеет сохранять полученную конфигурацию в файл и считывать её оттуда, например. Зато можно с лёгкостью создать что-то монстрообразное и потом перенести настройки себе в код. Довольно прелюдий. Это прога является огромным монстром, поэтому ниже я расскажу, как ею пользоваться. 1. Установка Установить прогу можно с помощью oppm или, что лучше (ибо быстрее), hpm. $ hpm oppm:cache update $ hpm oppm:install synth Прога весит 40 кБ, но с ней идут 160 кБ либ, поэтому установка займёт энное время. 2. Запуск Запускать командой synth. Появится красивенькая юишка. 3. Использование Первоначально на поле (а эта синь как раз является полем) ничего нет. Исправить это недоразумение можно с помощью тыка правкой кнопкой по полю. Внизу кнопа для закрытия меню, сверху комбобокс, в котором можно выбрать желаемую карточку. Начнём, пожалуй, с канала ("Channel"). После нажатия по нужному пункту появится карточка в месте, где тыкнули пкм. Таким же образом я создаю карточки "Wave", "Volume", "Frequency". Сами карточки белые, но у них есть цветные штуки. Эти цветные штуки называются пинами. Те пины, которые находятся снаружи карточки, - выходы (на них обычно ">" или "v" написано); которые внутри карточки, - входы. Разноцветные они не просто потому, что мне захотелось, а для помощи. Каждый выход можно подключить ко входу только с таким же цветом. А как подключать-то? Изи: сначала тыкнуть по выходному пину и затем по любому подсвеченному входу. Таким же образом можно отцепить два пина, если они уже подключены друг к другу. Соединения видны глазом даже без какого-либо вооружения. Настройки, которые мы скормили каналу, не особо впечатляют, прямо скажем. Синусоида, 99.99% громкости, 440 Гц частота. Пф. И это недоразумение тоже фиксится правым кликом! Только теперь по карточке. Например, по карточке "Wave". Внизу кнопки удаления объекта (т.е. карточки) и закрытия меню. На большинстве карт этим всё не ограничивается и можно настроить настройки, покрутить крутилки. Вот, например, на скрине выше для карточки "Wave" можно задать тип волны, тыкнув по комбобоксу. После этого можно выйти из меню и наблюдать изменения. Так-так-так, что-то скучновато просто цеплять и отцеплять картонки. Во-первых, их можно таскать по полю: зажать по белой части и потом двигать в тёплые страны. Во-вторых, можно таскать поле: зажать по его свободной части и подвигать. В-третьих, мы можем создать карточку "Sound card". Как ни странно, эта карточка смотрит, что вы к ней подключили, и на основе увиденного ужаса генерирует звук. Для этого её заспавним где-нибудь, а затем к любому из пинов (не столь важно, к какому именно, это ни на что не влияет) подключить канал. Слева у этой карты есть пин громкости - выставит для всех подключенных каналов сразу такую. Но её можно и не ставить. Сразу после этого вы услышите свой звук. Можно таким же образом поставить и второй канал. Если долго эту какофонию слушать, могут отпасть уши, поэтому вместо звуковой карты - её безжалостно удаляем - саммоним график. Особо тут расписывать нечего, вообще. Так же без разницы, куда подключены каналы; через пкм можно настроить зум. Попробуем теперь поиграться с модуляцией. Сначала амплитудная: она попроще. Подключаем канал, который будет модулировать (например, первый), к пину "AM" другого канала. Красота. Даже на график не влезает. Хотя особой проблемы тут нет: как видно, рисуется график не очень красиво (я потом надеюсь это как-нибудь поправить), но представление о виде волны есть. Неплохо, на первый взгляд. Ровно до того, как вы подключите оба канала к звуковой карте. Теперь давайте устроим тотальную частотную модуляцию. Саммоним частотный модулятор, к вернему пину подключаем модулирующий канал, а выхлоп ведём к пину "FM" на другом канале. Звучать оно сразу стало иначе. Можно поменять интенсивность модуляции и частоты на каналах. Последняя карточка в программе - это ADSR. Особого толка в ней, вообще, нет, потому что при подключении её услышите какую-то дичь, но зато на ней есть миленький график изменения амплитуды со временем (вся шкала - это 6 секунд). Так можно наглядно подобрать желаемые значения для этой огибающей. Собственно, это и все карточки, которые есть в этой программе. Ставьте, играйтесь и радуйтесь. А как наиграетесь со звуковой картой, можно выйти из программы (через Ctrl-C) и заняться серьёзными вещами. P. S. Сырцы лежат на OpenPrograms (где б им ещё быть?): https://github.com/OpenPrograms/Fingercomp-Programs/tree/master/synth
  9. Как я понял, в messarr хранится строка до пробела и строка после; в params - до запятой и после запятой. Тут всё довольно просто. Как-то так примерно. Я тут приблизительно код данный привёл. local messarr = {msg:match("^(%S*)%s?(.*)$")} if messarr[1] == "&say" then if checkAccess(user, ACCESS_LEVELS.Moderator) then local params = {messarr[2]:match("^([^,]*),(.*)$")} say {user = params[1], message = params[2]} end end
  10. Попробовал эту либу. Торжественно заявляю, что она невероятно охрененная. Что самое охрененное - она юзает doubleBuffering, благодаря чему я смог её заюзать даже в середине написания проги, когда любая другая либа бы дала сбой. Очень мне понравился набор компонентов, а также возможность на изи добавить свои. Пожелания, сформировавшиеся во время использования либы: Метод :destroy для всех объектов либы, рекурсивно удаляющий объект и его дочерние. Хотя, мб, он и есть, но я не заметил. Поддержка Lua 5.3. Решается банальным пиханием math.floor(var + 0.5) перед битовыми операциями. Насколько я помню, пришлось пропатчить всякие методы типа HEXtoRGB в либе color. Ресайз лейаута. Сейчас там строки и столбцы забиты при создании, что не особо удобно, когда, например, объект лейаута передаётся в какой-нибудь метод, общий для нескольких классов, для изменения. Пока решается прибитием гвоздями контейнера посередине, созданием новго контейнера в оставшемся месте и передаче уже его в метод, который может там сам добавить лейауты, сколько влезет. Скроллинг в комбобокс. У меня просто крашилась прога, когда размер комбобокса превышал размер экрана. Не трогать папку /lib совсем. Вместо этого класть все либы в /usr/lib. Это касается инсталлятора. Либа сериализации, например, затирала системную, из-за чего все программы, которые её юзали (а их было довольно дофига), просто крашились. Даже lua. Вряд ли так было сделано специально, но чтобы не случалось такого в дальнейшем, лучше всё же перетащить. Я бы также подумал над подключением репы с прогами к OpenPrograms: создать файл programs.cfg, прописать пакеты и пути к файлам, а затем тыкнуть Vexatos, чтобы он добавил репу в список. Я, например, пишу в репу на OpenPrograms свои программы и стараюсь избегать использования всех либ, которые огромные, но не присутствуют в OpenPrograms. Потому что, например, моя программка - это 40 килобайт кода, а либы, которые приходится бандлить вместе с нею, - это 160 килобайт. На каждую прогу тянуть по 160к килобайт - это слишком.
  11. Unreal Tournament: Resurrection -- Этап #2 завершён. Победители: Первое место: @Totoro. Второе место: @FluttyProger. Третье место: @LeshaInc. @astral17 на четвёртом месте, так как не явился на финальную битву. Спасибо за участие!
  12. Вкратце проедемся про изменениям с невероятно старой версии 1.5.5 до самой новой, 1.6.3. 1.5.6 / 2015-07-24 Большинство блоков из мода можно покрасить, тыкнув по ним красителем. Интеграция с модом Flamingo (самая нужная фича, конечно): через компы можно заставлять фламинго качаться. Интеграция с Armourer's Workshop. Улучшен улучшенный шифратор. Ключи быстрее генерируются. 1.5.7 / 2015-09-12 Разноцветный апгрейд добавлен, который делает роботов разноцветными. Можно вызвать component.colors.setColor(color: number) и покрасить корпус робота в желаемый цвет. Интересная штука. 1.5.8 / 2015-10-11 Эффект hive_mind для наномашинок. Часть интеграции с Forestry. Включив, можно тыкнуть пропитанной палкой из Forestry по желаемому улью с пчёлами - пчёлы станут летать над головой. Палкой же направлять можно пчёл на желаемую цель: пчёлы полетят к ней и станут жалить. Если отключить эффект, то пчёлы будут жалить игрока. 1.5.9 / 2015-10-22 Чатбоксики креативные стали писать во все измерения. 1.6.0 / 2015-11-28 Аудиокабели добавились. Их можно подключать к кассетным проигрывателям и к динамикам. Динамики будут воспроизводить звук вместо проигрывателя. Убрана поддержка NedoComputers, потому что этот мод сдох. Спустя год выпускается огромнейший апдейт мода, поэтому дальше расписываю фичи детальнее. 1.6.1 / 2016-11-12 Мод портирован на 1.8.9, 1.9.4 и 1.10. На новых версиях майна используется улучшенный кодек DFPWM1a. Старые записи будут очень тихими, поэтому их нужно переконвертировать с помощью LionRay. С новым кодеком гораздо меньше шума будет. Добавлена шумовая карта. Отключается от пищащей карты тем, что можно для каждого из 8 каналов задать тип волны: синусоида, меандр, треугольная и пилообразная. Допольнительно появляется буфер операций: можно добавить ноты и затем вызвать component.noise.process(), чтобы проиграть. Добавлена звуковая карта. Как работает этот монстр, я описывал в предыдущей записи. Потом опишу функции покруче, а пока можно побикать. Интеграция с TIS-3D, модом от Sangar. Это и новые модули: разноцветный, считыватель дискет, самовыпиливатель - и документация в мануале TIS-3D. Интеграция с OC 1.6. Добавлены дополнительные штуки для серверов. Плата с лампочками. Ключом можно менять конфигурацию, изменяя положение и количество лампочек. Как компонент предоставляет функции для включения/отключения лампочек и изменения цвета (True color). SSD. Расшифровывается как Server Self-Destructor. Очередная версия самой нужной вещи - самовыпиливателя, но для серверов. Если взорвать через функцию специальную, в серверной стойке пропадут все вещи. Серверный вариант батарейки, которую можно пихнуть в стойку. Можно ещё считывать количество энергии. Плата с переключателями. Можно тыкать по кнопочкам, включая или выключая их. А сервер потом может считывать положение переключателей. По просьбам трудящихся. [*]Добавлена функция getPosition() в проигрыватель, чтобы узнать текущую позицию на кассете. Именно: её несколько лет не было. Ну что поделать, бывает. [*]Стандартная программка для работы с проигрывателем tape починена для работы с HTTPS. Можно менять размер чанка и таймаут ожидания. И tape wipe добавилась для стирания всей инфы на кассете. [*]1.7.10 Цифровый сигнальный контроллер (интеграция с RailCraft). Надеюсь, когда-нибудь дойдут руки рассказать, как она работает. Но не тут. [*]1.7.10 Цифровый сигнальный приёмник (интеграция с RailCraft). [*]1.7.10 Рецепты для Gregtech 6. [*]1.8+ Больше не требуется устаналивать Asielib: либа встроена в мод. 1.6.2 / 2017-02-25 Фиксы, фиксы и ещё фиксы. Фиксы крашей, фикс тихих звуков в звуковых карточках, дискеты можно теперь получить, наконец, с помощью ключа. 1.6.3 / 2017-04-21 Добавлен синтезатор речи. Так как это охрененная штука, без пинка её так просто не завести. Чтобы она работала, нужно поставить сам синтезатор речи (MaryTTS с поддержкой Forge), файл голоса и файл языка. Женский голос и английский язык, например. К счастью, эти файлы нужны только на сервере. На клиент тянуть их не нужно. Работать так: speech_box.say(text: string) -- что-то сказать. speech_box.stop() -- остановить воспроизведение речи. speech_box.isProcessing():boolean -- проверить, воспроизводится ли сейчас речь. speech_box.setVolume(volume: number) -- установить громкость (от 0 до 1). Блоки теперь крафтятся не из земли и палок, наконец-то, а из чего-то повеселее. Используются предметы из OpenComputers. Динамики могут смотреть в любую из шести сторон. Директория для кассет перемещена в папку мира. При обновлении нужно перетащить её из папки кубача в папку мира, соответственно. Модуляция из-за глупой ошибки не работала вообще. Ну и ADSR не работал, если время Attack (нарастания) равно было нулю. Как-то так. За мною остаётся ещё долг в виде продолжения обзора этого интересного модика, но всё как-то лень. Пока можно спрашивать меня, как работают вещи из CX, читать страницы в мануале и документацию к методам. Для большинства вещей этого более чем достаточно. Have fun :P
  13. Завтра один из участников будет занят, поэтому решающая битва переносится на день вперёд, 2017-04-30 в 12:00 UTC. UPD. С момента опубликования дата изменилась.
  14. Спасибо за наезд на организаторов. Жаль, не сказали пилить сайт для магазина трусов. Полезнее же, чем аааа супер сложные баттлы для Дуба и аааа ужасно скучные баттлы для Квертика делать. Особенно полезен наезд через полторы недели после ивента. Я, конечно, подумаю, что пилить на последний этап. Полгода есть. Но теперь мне как-то всё больше хочется просто покемонов заспаунить и заставить людей ловить их. Квертик придумает офигенную стратегию заодно. И скучно не будет. И порог вхождения минимальный. Я же о людях должен думать, так?
  15. А если бы да кабы... Хочешь либу - пиши нужные методы. Как напишешь - поймёшь, что OC/OpenOS уже сто лет как все "базовые функции для управления" имеет. Ты же сам даже ни в одном этапе не поучаствовал. Что тогда за всех участников отдуваешься?
  16. Ну если хочешь, пиши программу на третий этап и делай там стратегию нужную. Я бы посмотрел на это. А то ребята и с базы выехать не смогли 15 апреля, а ты тут про секторы, главнокомандующих и группировки говоришь
  17. Ну мы эволюционируем потихоньку. Сначала тест алгоритмов пути, потом препятствия. В следующем этапе можно какой-нибудь лабиринт зафигачить или что-то типа этого. Посмотрим. Есть полгода на придумывание чего-то действительно интересного.
  18. Перед тем, как я начну, хочу сразу обратиться к забугорным ребятам, читающим эту запись. Hey! If you are reading this, please tell Vexatos to document the sound card. The in-game manual page is a meaningless piece of text that explains nothing. Documentation is written to help others to get an idea of how something works. The sound card is a complex thing that definitely needs a how-to guide to be used in programs. So if he wants the sound card to be used in programs, he should write the proper documentation with the detailed description of each concept, feature, and component method. The following is the result of efforts put to understand how the sound card works. It took me several months until I realized how to make a simple beep. How am I supposed to create more complex waves? >_> Ну да ладно. Хей! Разбирался я недавно, как работает звуковая карта из CX, и поэтому хочу предоставить результаты своих трудов. Как и написано сверху, доков нет, автор мода бездействует, везде уныние и отчаяние, поэтому пришлось действовать по-хардкорному. То есть чтением исходников и научнейшим тыком. Итак, есть компьютер со звуковой картой. Нужен звук. Звучит просто, м? Звуковая карта даёт нам замечательные функции вроде установки ADSR-огибающей, типа волны, шума простого или из LFSR, амплитудной и частотной модуляции. Но мы тут решили заняться простым звуком, поэтому всякие LFSR, AM, FM безжалостно выкидываем. Получаем такой план: Открыть канал. Поставить тип волны. Задать частоту волны. Установить громкость. Обработать. 1. Открыть канал Э, извините, а что за каналы? ... В звуковой карте звук генерируется с помощью инструкций, которые выполняются друг за другом. Почти все работают с определённым каналом. Если бы канал был один, мы бы смогли играть в один момент времени только одну, условно, ноту. Чтобы параллельно шло сразу несколько звуковых волн, нужно использовать несколько каналов. На каждом можно задать свои тип и частоту волны, громкость, ADSR-огибающую. По умолчанию таких каналов в звуковой карте 8 штук. С каналами всё довольно просто: открытый канал генерирует звук, закрытый канал не генерирует звук (правда, нужно будет здесь кое-что поправить, когда будет изучать ADSR). Открыть канал можно с помощью функции sound.open(channel: number). Закрыть: sound.close(channel: number). 2. Поставить тип волны Всего типов звуковая карта поддерживает пять: шум (noise), меандр (square), синусоида (sine), треугольная (triangle) и пилообразная (sawtooth). Waveforms ru [CC BY-SA 3.0 (http://creativecommons.org/licenses/by-sa/3.0) или GFDL (http://www.gnu.org/copyleft/fdl.html)], автор Omegatron (original work) Kirill Borisenko (Russian translation; see File:Waveforms for Cyrillic Segment.png) Incnis Mrsi (ultimate work), с Викисклада Очевидно, что разные типы волн звучат по-разному. Установить тип волны можно с помощью функции sound.setWave(channel: number, type: number). Видно, что тип волны должен быть числом. Соответствующее типу волны число можно получить с помощью таблицы sound.modes. Например, sound.modes.sine или sound.modes.noise. 3. Задать частоту волны Чем больше частота, тем выше звук. Частота 440 Hz соответствует ноте ля в первой октаве. Другие частоты можно найти по табличке на Википедии. Используем функцию sound.setFrequency(channel: number, freq: number). 4. Установить громкость Громкость может быть в пределах от 0 до 1 включительно. Половина максимальной громкости - 0.5. И так далее. Функция: sound.setVolume(channel: number, volume: number). 5. Обработать Чтобы обработать все инструкции, нужно вызвать sound.process(). Но если вы просто возьмёте и вызовете функцию, то ничего не услышите. Дело в том, что, достигнув конца инструкций, звуковая карта прекращает генерировать звук. Поэтому нужно выставить некоторую задержку в конце всех других инструкций, чтобы звуковая карта подождала перед прекращением воспроизведения звука. Для этого служит sound.delay(delay: number). Задержка указывается в миллисекундах. Например, sound.delay(2500). При этом эта функция тоже является инструкцией, поэтому её можно выставить несколько раз. Однако если задержка превышает 5000 (5 секунд), то звуковая карта выдаст ошибку. Проиграв несколько типов волн, мы всё равно останемся неудовлетворёнными. Ну где это видано, что звук мгновенно нарастает и затухает? Чтобы как-то это контроллировать, мы воспользуемся ADSR-огибающей. На Википедии довольно хорошо написано, в чём смысл этого. Скопируем сюда оттуда пикчу: И вкратце распишем суть. Если вы хоть когда-либо слышали музыкальные инструменты, вы могли заметить, что громкость звука со временем меняется. У гитары громкость мгновенно нарастает и постепенно затухает, а у флейты нарастает не сразу, например. Чтобы очень-очень грубо моделировать эти инструменты, мы используем ADSR. Attack - это время нарастания громкости звука до максимальной (последняя устанавливается через sound.volume). Decay - время затухания сигнала сразу после достижения максимальной отметки до следующего параметра. Sustain - отметка громкости, к которой стремится Decay. Значение 1 - это максимальная громкость звука на канале (установленная с помощью sound.setVolume), 0 - отсутствие звука вовсе. При достижении отметки Sustain звук остаётся на этом уровне до "отпускания клавиши" (т.е. закрытия канала). Release - время затухания сигнала после закрытия канала, от отметки Sustain до нуля. Время везде в миллисекундах. Попробуем? Вызываем функцию sound.setADSR(channel: number, attack: number, decay: number, sustain: number, release: number) до пятого шага. Например sound.setADSR(1, 1000, 500, 0.33, 1000). И замечаем, что громкость меняется со временем. Yay! На этом счастливом моменте повествование обрывается. Итоговый код, который можно запустить и послушать примитивный бип: local sound = require("component").sound sound.close(1) sound.close(2) sound.open(1) sound.setWave(1, sound.modes.sine) sound.setFrequency(1, 440) sound.setVolume(1, 1) sound.setADSR(1, 1000, 500, 0.33, 1000) sound.delay(1000) sound.open(2) sound.setWave(2, sound.modes.noise) sound.setFrequency(2, 440) sound.setVolume(2, 0.6) sound.setADSR(2, 1, 250, 0, 1) sound.delay(1500) sound.process() К остальному, надеюсь, я ещё вернусь в другой записи. Сначала нужно самому понять, как оно работает в версии для CX. Поэтому наслаждаться пока приходится только бибикалкой.
  19. Ну естественно, что в кубаче работать ну будет. Интернет-карта же у нас мгновенно подключается, да? Нет. И .read у нас читает сразу все эн мегабайт? Ну ясен пень, что нет. Так как топик дублирован вместо апа пршлой темы, я из принципа здесь не буду отвечать. Только скажу юзать циклы и перед использованием проверять finishConnect.
  20. Fingercomp

    Палитры OpenComputers

    Немногие знают, как работают палитры в OpenComputers. Расскажу здесь, как избавиться от необходимости прописывать гектары цветов в палитре, покажу, как упаковываются цвета в OpenComputers и дам пару алгоритмов для работы с индексами. Сразу условимся, что индексы палитр у нас будут начинаться с нуля. На каждой из трёх уровней видеокарт и мониторов своя поддерживаемая палитра цветов. Будем двигаться снизу вверх. Первый уровень Палитра состоит из двух цветов: чёрного и заданного в конфиге (по умолчанию белого). Конвертация цвета в индекс палитры тривиальна: цвет нулевой — и индекс нулевой (чёрный цвет); цвет ненулевой — индекс единичный. Цвет в индекс (deflate) и обратно (inflate) превращать — одно удовольствие: local palette = { 0x000000, CONFIG.monochromeColor } local function t1deflate(index) if index == 0 then return 0 else return 1 end end local function t1inflate(index) return palette[index + 1] end Как и говорил. Второй уровень В палитре второго уровня имеется 16 закреплённых цветов: local palette = {0xFFFFFF, 0xFFCC33, 0xCC66CC, 0x6699FF, 0xFFFF33, 0x33CC33, 0xFF6699, 0x333333, 0xCCCCCC, 0x336699, 0x9933CC, 0x333399, 0x663300, 0x336600, 0xFF3333, 0x000000} При конвертации цвета в индекс палитры вернётся ближайший к данному цвет из палитры. Насколько цвета друг к другу близки, рассчитывается по специальной формуле, которая учитывает, что человеческий глаз лучше воспринимает зелёный, нежели красный и синий. В коде этим занимается функция delta. Вот как она выглядит (вместе с функций extract, выделяющей из числа вида 0xABCDEF числа 0xAB, 0xCD, 0xEF): local function extract(color) color = color % 0x1000000 local r = math.floor(color / 0x10000) local g = math.floor((color - r * 0x10000) / 0x100) local b = color - r * 0x10000 - g * 0x100 return r, g, b end local function delta(color1, color2) local r1, g1, b1 = extract(color1) local r2, g2, b2 = extract(color2) local dr = r1 - r2 local dg = g1 - g2 local db = b1 - b2 return (0.2126 * dr^2 + 0.7152 * dg^2 + 0.0722 * db^2) end Теперь можно конвертировать цвет в индекс палитры. Суть такова: выбираем из двух цветов ближайший и возвращаем его. local function t2deflate(color) -- Сначала проверяем, совпадает ли данный цвет -- с каким-либо из палитры for idx, v in pairs(palette) do if v == color then return idx end end -- Составляем таблицу разниц между цветами local deltas = {} for idx, v in pairs(palette) do table.append(deltas, {idx, delta(v, color)}) end -- Сортируем по увеличению разницы table.sort(deltas, function(a, b) return a[2] < b[2] end) -- Первый элемент будет с наименьшей разницей, -- то есть искомый. Возвращаем индекс. return deltas[1][1] - 1 end Обратная же процедура — превращение индекса палитры в цвет — неизменна. local t2inflate = t1inflate Третий уровень Палитра третьего уровня содержит уже 256 цветов: первые 16 цветов изменяются, а остальные соответствуют цветам палитры RGB-6-8-5. Это означает, что можно смешивать 6 оттенков красного, 8 оттенков зелёного и 5 оттенков синего. В общем-то, довольно очевидна причина такого выбора: человеческий глаз лучше всего различает оттенки зелёного и хуже всего — оттенки синего. В любом случае, здесь алгоритмец будет посложнее. Сначала нужно сгенерировать палитру. Начнём с первых 16 цветов. Они не включаются в палитру RGB-6-8-5, поэтому их заполнять нужно отдельно. В OpenComputers по умолчанию они содержат оттенки серого. Так как чёрный и белый уже включены в основную, зафиксированную палитру, то заново их дублировать смысла нет. local palette = {} -- grayscale for i = 1, 16, 1 do palette[i] = 0xFF * i / (16 + 1) * 0x10101 end Таким образом в таблице получаются следующие оттенки серого: 0x0F, 0x1E, 0x2D, 0x3C, 0x4B, 0x5A, 0x69, 0x78, 0x87, 0x96, 0xA5, 0xB4, 0xC3, 0xD2, 0xE1, 0xF0 Эти цвета мы записываем в индексы от 0 до 15. Теперь нужно сгенерировать остальные цвета — они не изменяются. Здесь будет посложнее. Посмотрим на картинку с палитрой: В OpenComputers левая верхняя ячейка палитры (0x000000) имеет индекс 16, а правая нижняя (0xFFFFFF) имеет индекс 255. Индексы распределяются слева направо, сверху вниз. То есть правая верхняя ячейка (0x00FFFF) имеет индекс 55, а вторая сверху и левая (0x330000) — это номер 56. Отсюда вытекает следующий алгоритм нахождения цвета: сначала найти индексы отдельно по R, G, B, затем для каждого из этих трёх индексов найти соответствующий ему оттенок цвета, а затем всё сложить. for idx = 16, 255, 1 do local i = idx - 16 local iB = i % 5 local iG = (i / 5) % 8 local iR = (i / 5 / 8) % 6 local r = math.floor(iR * 0xFF / (6 - 1) + 0.5) local g = math.floor(iG * 0xFF / (8 - 1) + 0.5) local b = math.floor(iB * 0xFF / (5 - 1) + 0.5) palette[idx + 1] = r * 0x10000 + g * 0x100 + b end Идея следующая. Каждый из трёх каналов принимает значение от 0 до 255 (0xFF). Разбиваем их на определённое число ступеней (по-умному — квантуем): 6 для красного, 8 для зелёного и 5 для синего. Например, синий канал мы разобьём так: 0/4 · 255 = 0 1/4 · 255 = 63.75 2/4 · 255 = 127.5 3/4 · 255 = 191.25 4/4 · 255 = 255 В знаменателе тут 4, а не 5, потому что считаем с нуля. Затем округляем до ближайшего целого конструкцией math.floor(x + 0.5). Перебрав все комбинации, мы получим все 6 × 5 × 8 = 240 цветов неизменяемой части палитры. Всё. Палитра есть, теперь можно, наконец-то, конвертировать индексы между цветами. Из индексов получить цвет довольно просто. Достаточно использовать ту же функцию, что и для предыдущих уровней: t3inflate = t2inflate С обратной же конвертацией всё несколько сложнее. Функция, используемая в OC, подбирает ближайший цвет хитрым алгоритмом, который я привожу ниже. local function t3deflate(color) local paletteIndex = t2deflate(color) -- Если цвет из палитры, то используем значение выше for k, v in pairs(palette) do if v == color then return paletteIndex end end -- Иначе используем хитромудрый код local r, g, b = extract(color) local idxR = math.floor(r * (6 - 1) / 0xFF + 0.5) local idxG = math.floor(g * (8 - 1) / 0xFF + 0.5) local idxB = math.floor(b * (5 - 1) / 0xFF + 0.5) local deflated = 16 + idxR * 8 * 5 + idxG * 5 + idxB if (delta(t3inflate(deflated % 0x100), color) < delta(t3inflate(paletteIndex & 0x100), color)) then return deflated else return paletteIndex end end Хитромудрость здесь не сильно сложная, на самом деле. Мы сначала находим индекс самого близкого цвета из изменяемой части палитры (paletteIndex). Дальше высчитываем индекс цвета из неизменяемой части (deflated), для чего в каждом канале отдельно ищем номер ближайшей ступени, на которые ранее квантовали. Последний if сравнивает 2 варианта и возвращает самый похожий (с точки зрения человека) на исходный. В общем-то, это всё. Показал портированный со Scala на Lua код, который используется в OpenComputers. С помощью этого можно оптимизировать операции с экраном, выбирая поддерживаемые монитором цвета. И заодно избавиться от таблиц цветов, которые некоторые буквально берут и копипастят в файл, даже не задумываясь об изменяемых цветах палитры. Особенно это важно, когда берётся значение цвета через gpu.get, потому что следующий код всегда вернёт false: local gpu = require("component").gpu gpu.setForeground(0x20AFFF) gpu.setBackground(0x20AFFF) gpu.set(1, 1, "HI") return select(2, gpu.get(1, 1)) == 0x20AFFF И всё потому, что gpu.get возвращает уже приведённый к индексу из палитры цвет. А 0x20AFFF в палитре, если не менять первые 16 цветов, не имеется. Enjoy :P
  21. Да, очепятался. Люблю это делать. Поправил.
  22. Итак. Дано: Файл формата "НИК,ОЧКИ" по строку на каждую запись. Путь к файлу: /etc/scores.csv Задача: Прочитать файл, отсортировать по очкам и вывести первые 3 записи. Решаем! Во-первых, прочтём файл. Так как у нас всё по строкам, можно использовать функцию io.lines. Т.е. что-то типа такого потребуется: for line in io.lines("/etc/scores.csv") do -- здесь обработчик end Строку надо разделить на две части, побив на запятой. Здесь нам помогут шаблоны. Выглядеть это будет как-то так: local player, score = line:match("^(.*),([0-9]+)$") -- score у нас будет в виде текстовой переменной, поэтому переведём её в числовую score = tonumber(score) Строку разбили, теперь надо её отсортировать. Сходу мы это сделать не сможем, надо добавлять значения в массив. Вне цикла создадим массив scores, а внутри цикла будем туда помещать пары "ник-очки". Как-то так: local scores = {} for line in io.lines("/etc/scores.csv") do local player, score = line:match("^(.*),([0-9]+)$") score = tonumber(score) table.insert(scores, {player, score}) end Итак, с чтением из файла разобрались. Теперь надо отсортировать таблицу. Если бы это была просто таблица типа {1, 100, 25}, мы бы могли воспользоваться table.sort. Но у нас в качестве значений таблицы ещё одни таблицы. То есть так: {{"Vasya", 100}, {"Ivan", 121}, {"moo", 9}}. Обратимся к мануалу: Ага! Можно задать функцию, которая будет сортировать элементы. Работает она так: функции передаётся пара элементов, а она возвращает false, если нужно их поменять местами. Когда нам не нужно менять элементы местами? Когда элемент слева больше или равен элементу справа: нам же нужно отсортировать от большего к меньшему. То есть сделать что-то вроде оператора >=, который будет сравнивать очки. Почему бы и нет. Вот как это будет выглядеть: table.sort(scores, function(lhs, rhs) return lhs[2] >= lhs[2] end) Здесь я использую безымянную функцию, которая сразу передаётся в table.sort, чтобы не городить код. Теперь нужно просто выввести первые три записи на экран. Воспользуемся очень удобной функцией table.concat, которая возвращает строку, склеенную из элементов переданной таблицы, используя в качестве разделителя данным символ (например, table.concat({"hello", "world", 42}, " ") вернёт "hello world 42"): for i = 1, 3, 1 do -- Может случиться так, что в файле будет меньше 3 строк, -- поэтому проверяем, что мы не вышли за пределы. if not scores[i] then -- Если всё же вышли, то прерываем цикл. break end print(table.concat(scores[i], ",")) end Задача выполнена. Итоговый код: local scores = {} for line in io.lines("/etc/scores.csv") do local player, score = line:match("^(.*),([0-9]+)$") score = tonumber(score) table.insert(scores, {player, score}) end table.sort(scores, function(lhs, rhs) return lhs[2] >= lhs[2] end) for i = 1, 3, 1 do if not scores[i] then break end print(table.concat(scores[i], ",")) end
  23. Обновление от 15 апреля 2017 года. Последнее. Эпично. Так закончилась история со второй главой Unreal Tournament: Resurrection. Потому пора подвести итоги. Выражаю благодарность всем тем людям, которые так или иначе участвовали в разработке этого ивента. Организаторы @Fiender. Он был хостером всего ивента. Сделал сервер, сделал из набора модов нормальную сборку, собрал клиент (очень много изменений в нём, отрезал многие штуки ненужные), подключил лончер и сервер авторизации. По сути, на пару с ним работал и я. В мои заслуги можно записать софт, карту, геймплей, правила, обсуждение и так далее. @Totoro - автор оригинальной идеи, участвовал в обсуждении, помогал выбирать рациональные решения. Участники @astral17. Начал пилить программу за час-полчаса до старта, но, тем не менее, даже с такой прогой он победил, потому что выбрал самый рациональный путь к победе. @Totoro. Начал пилить прогу этой ночью, в итоге роботы агрились друг на друга, но не смогли правильно рассчитать угол пушки, поэтому так и не получилось полностью экстерминироваться. @FluttyProger. Написал единственных роботов, которые отъехали от базы. И хотя в последнем раунде его роботы к чёрту перестреляли друг друга сразу после старта, получилось довольно забавно и прикольно. Прочая помощь @Doob. Нарисовал фон для лончера. Сделал прошивку для бомб, правда, мы заменили микроконтроллеры потом на отдельный блок из мода. Над этим ивентом мы работали 51 день. Прошли от обсуждения видоизменения первой части до сразу двух ивентов. Сейчас я невероятно устал, конечно. И, подумав, решил, что снова мучаться в таком же режиме над UT#3 никому не хочется. Поэтому заключительный этап Unreal Tournament: Resurrection будет проходить в конце года. Начнём где-нибудь в середине осени и закончим к январю-февралю. Что же дальше? Это ещё предстоит выяснить. У нас есть полгода на то, чтобы продумать идею для третьего этапа. Как обычно, первыми о грядущих ивентах узнают люди на нашем IRC-канале #cc.ru @ irc.esper.net. Погодите. А где места? ... После некоторого обсуждения, решено было убрать какие-либо места в рамках этого ивента. Почему? У нас не получилось боя, как такового. Все роботы были невероятно забагованы. Победа зависела бы не от алгоритма, а от удачи, чего, конечно, я не хочу совершенно. Но что за ивент без мест и призов? Поэтому 29 апреля, в субботу через 2 недели, состоится ещё одна битва роботов. С теми же правилами, с тем же софтом, на той же карте, на той же сборке и на том же сервере. Однако теперь у участников есть 2 недели на то, чтобы всё основательно подготовить в своих программах. У них сейчас имеется абсолютно всё, чтобы сделать нормальных роботов, которые осмелятся выползти с базы, отрефакторить в спешке написанный код и дописать недописанные фичи. Надеюсь, участники подготовят своих воинов и выйдут сражаться в гораздо более интересном бою. Спасибо за то, что следили за ивентом, задавали вопросы, участвовали в обсуждении. See ya! P. S. Если есть видео с ивента, просьба поделиться ими здесь.
  24. Открою секрет. На каждой ветке были задействованы компы.
×
×
  • Создать...