democoder.ru

База знаний
Статьи
Исходники
Книги
Библиотеки
Инструменты
Программы
Музыка
База трекеров
Все платформы

Amiga
Amstrad
Apple
Archimedes
Atari
BeOS
C64
DOS
GameBoy
GP32
iPhone
Linux
MacOS
MSX
NeoGeo
Nintendo
Palm
Pocket
PSP
SAM Coupé
SEGA
Symbian
VIC-20
Windows
WinMobile
ZX-Spectrum
БК-0010/11

login

password


Регистрация

Работа над звуковой системой к Fr-08

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

1. Концепция

Вернемся в лето 2000, когда Chaos/Ex-Sanity (который чем-то похож на всем известного Powa из Elitegroup, но это уже совсем другая история) спросил, хочу ли я участвовать в создании музыки к, как он ее назвал, "лучшей 64kb интро всех времен". Когда я попросил его показать интро, он сказал, что еще ничего не закончено, но он и Fiver2 покажут кое-что в ноябре, и это меня наверняка убедит.

Так оно и случилось. Черт. Каждый год я хочу отдохнуть от всей этой сценерской жизни, и каждый год одна и та же история: "Давайте выиграем The Party". Мне НИКОГДА не удастся скрыться от этого.

Ну и что? Надо просто перестать делать вид, что у меня есть какая-то "личная жизнь", и, кроме того, это весело и у меня есть идеи, дремлющие в моей голове и ждущие, когда их испробуют. Так что начнем!

1.1 Решение, которое не сосет?

Но с ЧЕГО начать? Я имею ввиду, я никогда не был хорошим музыкантом, создающим чип-тюны: я всегда тратил столько памяти, сколько мне хотелось или требовалось, и в недалеком прошлом я сочинял, используя только "настоящее" музыкальное оборудование и все остальное. И, наконец, я действительно пристрастился к вокалу :)

Итак, требовалось как-то запихнуть саундтрек в 16kb в сжатый exe-шник, который мне предоставил Chaos... мне осталось лишь поместить туда столько моих любимых вещей, сколько возможно.

Но как? Давайте взглянем на решения, которые существовали раньше:

1. Обычная трекерная музыка с использованием MXMPlay/W или MiniFMOD или чего-нибудь еще Ну, если коротко - НЕТ. Сэмплы занимают место, и даже эти чипповатые, похожие на шум сэмплы, которые мне бы пришлось использовать, заняли бы слишком много. Кроме того, звучание всех этих 64kb интр - конечно, есть музыканты, способные на создание хороших мелодий при таких небольших ресурсах, но большинство 64kb-интро саундтреков не прожили бы долго в "реальном мире".

2. Использование модулей с прекалькулированными сэмплами в softsynth'е (путь Stash) Неплохо. Но все еще не то, что мне надо... Требуется держать программный синтезатор И проигрыватель модулей в exe-шнике, и не остается такого понятия, как "удобство в использовании"... Я уважаю Probe'а и других за полученные ими результаты, но я как музыкант лучше уйду со сцены, чем буду составлять музыку к демо, набирая шестнадцатеричные числа в softsynth'е, перекомпилируя его, генерируя сэмплы, переходя в трекер, загружая сэмплы заново, понимая, что они оказывается не подходят и т.д. Это решение может работать, но убивает весь рабочий настрой и креативность. К тому же, я пробовал это годом ранее, и независимо от грустного факта, что наша интро тогда так и не была сделана даже наполовину, музыка, которую я сделал для Mekka&Symposium 2000 invitation intro, используя этот метод, была действительно отстойная.

3. Да, еще же остаются FM synth и General Midi wavetable чипы на современных звуковых картах. Но, так как все, что они могут это звучать "дешево и стильно", это вовсе не решение. Но главное, нельзя быть уверенным, что композиция будет звучать одинаково на всех компьютерах.

Если подумать, то трекерная музыка в любом случае - это отстой. Взгляните: весь формат модулей, способ, каким паттерны показываются, располагаются в памяти и проигрываются, был создан по одной простой причине: Paula. MOD'ы были созданы, чтобы самым простым из возможных способов ("простым для кодера и процессора", а не "простым для музыканта") заставить амижный звуковой чип (который называется Paula) играть сэмплы, только и всего. И к сожалению все другие программы последовали за этой парадигмой, хотя MOD формат был создан временно, до тех пор пока не появится лучший амижный процессор или даже лучший софтверный микшер. И, что еще хуже, все новые и известные трекеры содержат ТОННЫ лишнего, чтобы сделать их как можно более похожими на оригинальный SoundTracker. И IMHO, без убедительной причины.

К тому же мы живем в 2001 году. Процессоры сейчас будут побыстрее, чем тогда, музыкальные программы эволюционировали, кульные программы типа Buzz'а или плагинов для всех форматов сейчас в изобилии... и я просто не могу понять, почему мы должны работать с моно-сэмплами без каких-либо эффектов или модуляций для создания хорошей музыки. Говоря о качестве, каждый ребенок, пишущий музыку, может просто посмеяться над возможностями трекеров... и будет прав.

Ok, давайте подведем итог: все существующие решения сосут. Но что же делать? Я хочу гибкую, хорошо звучащую звуковую систему, подходящую для создания произведения искусства с качественным звуком (и вокалом :)... И надо, чтобы она поместилась в 16kb со всеми музыкальными данными. Звучит нереально, не так ли?

1.2 Нововведение в создании идей.

Не совсем.

Давайте пороемся в одном из самых старых компьютерных музыкальных стандартах: MIDI. Когда я говорю о МИДИ, я подразумеваю оригинальный "Music Instrumentsd Digital Interface" стандарт, который включает определения для серийного интерфейса (стандартный RS232 асинхронный протокол, 32500 бит/сек, один стартовый и один стоп-бит, без четности, без handshaking, используя TTL напряжение и этот всем известный 5-ти пиновый DIN разъем и эти еще более известные цепи нарушения связи [надеюсь, я правильно перевел]) и определения простого протокола, способного передавать события такие, как "клавиша нажата", "клавиша отпущена" или "колесо модуляции немного прокручено вверх". Я также имею в виду файловый формат SMF (стандартные миди файлы, эти .mid), который содержит не более, чем просто "временное отображение" всех событий, относящихся к музыке. То что я НЕ имею в виду, это "General MIDI" стандарт (и все его производные), который определяет различные стандартные звуки и, то, о чем большинство людей думают, когда слышат слово МИДИ. Это неплохой способ распространять плохие кавер-версии популярных песен по сети или для старых досовских игр и тому подобного. Как было сказано ранее, это звучит, э-э-э, дешево.

"Итак... Современные процессоры довольно быстрые. Также, сейчас век TnL 3D карт, поэтому процессору не нужно почти ничего делать в демке. У меня есть все время в мире!"

(на что Chaos ответил что-то вроде "если мое интро будет тормозить со скоростью 2 фрейма из-за твоего synth'а, ты умрешь", но кого это волнует)

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

1. .mid плеер ГОРАЗДО меньше XM плеера... просто читает файл, ждет следующего события и отправляет некоторые команды synth'у... нет обработки эффектов, ничего. Хорошее качество звука, реалтаймовый синтез гораздо круче прекалькулированных сэмплов... Линии будут звучать более "естественно" (как с аналоговым оборудованием, без двух одинаково звучащих звуков), и мы сможем сделать длинное фильтрование разверток и модуляций без того, чтобы жертвовать несколькими мегабайтами памяти и драгоценным секундами прекалькуляции для этих звуков.

2. Если synth все равно создает МИДИ-команды, почему бы ни подключить небольшой GUI для редактирования звуков в нем и подсоединить его к MIDI секвенсорной программе по вашему вкусу? Таким образом вы сможете сочинить все песню в Cubase (или Logic или Cakewalk или zTracker или Modix или то, что предпочитаете вы) и любое изменение звука будет всего лишь два мышиных клика (или нажатие кнопки в плате MIDI контроллера:) и работает немедленно, вместо "изменить, срендерить, загрузить, попробовать, не подходит, изменить, срендерить...".

3. Один побочный эффект заключается в том, что музыкальные данные в .mid файлах хранятся в немного более чувствительном виде, чем в трекерных паттернах (которые хранились для более простого доступа к ним процессора M68000 и после этого не изменившихся) и как правило меньше (причем для меня все равно не достаточно, но об этом позже) Возможно, кто-то из вас жалуется, что этим способом он не сможет проигрывать сэмплы. Но добавление проигрывателя сэмплов в softsynth - это 20 строчек в ассемблере или 5 строчек в C++ при хорошем знании этих языков. Может это больше программирования, но это не то, что я называю проблемой. Ok, в следующем разделе вам потребуется знание MIDI-протокола и возможно .mid формата, так что я оставлю это как домашнее задание до продолжения чтения.

2. Почему SMF файлы меньше?

2.1 MIDI файлы меньше? wtf...

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

Предположим сначала, что нам не требуется сжимать музыкальные данные. EXEшник будет все равно сжат, скорее всего RLE или LZ (и, возможно, с entropy coding). Поэтому никто не запрещает нам сделать 1 мегабайт нулей - они превратятся во что-то вроде 4 байт после сжатия. Повторяющиеся или похожие вещи также неплохо сожмутся.

Модуль состоит из паттернов, что является преимуществом в размере, поскольку вы можете повторять их. Проблема: в приведенном выше предположении, это нисколько не поможет. Если вы просто напишете все данные из паттернов друг под другом, хороший LZ алгоритм поймет, что структуры повторяются и просто поставит ссылки на последнее появление паттерна. Так что вся таблица очереди и вся обработка кода - это чистая потеря времени.

Вторая проблема заключается в том, что все модуль-плееры, существующие на сегодняшний день (единственное исключение - это, ИМХО, Digitrakker), хранят данные в "по-рядовой", а не в "по-канальной" форме, а это означает, что данные, которые часто повторяются (например ваш басовый барабан на каналах 1,2,3:) чередуется или "разбрасывается" с изменяющимися данными (мелодия, аккорды, и т.д.), так что LZ-паковщик найдет что-нибудь, но сможет сжать лишь небольшие куски данных, а не полностью оптимально.

Да, но стандартные МИДИ файлы гораздо хуже. SMF действительно "компактный" формат, он фокусируется на сохранение каждого байта. Это круто для несжатого размера файла, но с ним не справятся стандартные алгоритмы сжатия. Тот факт, что такой файл - это один поток данных, даже не разделенный на каналы (это справедливо для 0 формата, файлы формата 1 могут иметь произвольное количество треков, но это все равно вскоре станет бесполезным), также ничем нам не поможет.

2.2 Как удовлетворить LZ-компрессор.

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

  • МИДИ-канал.
  • Тип события (Note, Control Change, Program Change, Pitch Bend, Channel Pressure, Rest)
  • для контроллерных событий: номер контроллера.

После разбивки у меня было несколько сотен отдельных потоков (некоторые из них, конечно, пустые), которые содержали данные типа "все изменения контроллера 2 на канале 10". Вы видите, что в нашем примере с басовым барабаном, хороший LZ-компрессор обработает поток с "нотами в канале 1" и подумает что-то вроде "ох, одно и то же событие каждую четверть ноты по всей песне" и заменит все эти ноты одним словом: "tekkno".

Но, конечно, этого недостаточно. Если в вашей песне больше одного басового барабана, она скорее всего содержит последовательности, которые переходят в другие питчи. Обычно, LZ-паковщик не распознает такие перемешанные паттерны, и он также не распознает продолжительный controller slide от 0 до 127. Поэтому применим дельта-кодинг ко всему... к timestamps, к номерам нот, к информации о скорости, к значению контроллера, просто ко всему. Controller slide от 0 до 127 станет чем-то вроде {always_the_same_time_delta,1} за событие и все последовательности нот станут "разъединенными" от своих базовых нот, т.к. кодируются только разница питчей нот, и LZ-компрессор определит эти слабые повторения как надо.

Также, почти все EXE-паковщики сжимают данные byte-wise способом. Проверьте, что используемый вами компрессор хорошо пакует ваши структуры потоков. Я не буду обсуждать все это дальше (чтобы оставить себе хоть небольшое преимущество), но вы поймете, что я имею в виду, если подумаете над этим, обещаю:)

Вы можете убедиться (надеюсь, убедитесь), что написать .mid плеер, который должен иметь дело с около 200 дельта-кодированными потоками и их timestamps в памяти и расписывать каждое событие в правильное время - это не слишком тривиальное дело. Фактически, оказывается, что код плеера будет больше - обычный .mid плеер может поместиться в 500 байт, плеер моего измененного формата занимает около 1.5K, несжатый и в неоптимизированном C++. Но это все равно оплачивается потОм. 11-минутная основная тема в Fr-08 занимает около 120K в .mid формате (PKZIP делает из этого что-то вроде 20K). После преобразования и добавления нескольких сотен байт звуковых данных, готовый для проигрывания файл занимает около 180K... а после применения EXE-компрессора остается всего 4K. Отношение сжатого файла к несжатому - 1:30, что ИМХО круто (и безусловно лучше, чем отношение 1:10, которое я бы получил со стандартными .mid файлами).

Итак у нас есть концепция, файловый формат для музыки... давайте же получим звук из PC :)

3. Простейшая система.

3.1 Введение.

Ok. Мы выбрали оружие и файловый формат, так что можно начинать. Сразу появляется следующая проблема: как получить звук из этого серого гроба?

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

Первый вопрос: какой звуковой API использовать? Предполагая, что мы хотим использовать Windows для воспроизведения звука, у нас есть две возможности: waveOut ("нормальный" способ, которым PC создает звук)

Мой выбор - Direct Sound, по простым причинам:

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

3.2 DirectSound плеер.

Инициализация процедуры DirectSound довольно проста (см. DirectSound SDK для дальнейшего разъяснения): вызовите IDirectSound интерфейс с помощью DirectSoundCreate, установите кооперативный уровень на установку "priority" ("exclusive" было бы еще лучше для демок, только проблема в том, что она не поддерживается, а фактически является тем же самым, что и "priority", по крайней мере с DirectX8), создайте первичный и вторичный буфер, установите оба на предпочитаемый вами формат вывода (я рекомендую 16bit 44.1KHz signed stereo PCM), заблокируйте весь вторичный буфер, очистите его, разблокируйте его снова...

... затем проиграйте все это.

Так, ЧТО проиграть-то? Нам надо будет как-нибудь заполнить буфер данными. Опять же здесь два способа:

Использование второго thread (также, как и таймер IRQ в Досовские времена):

Мы создаем thread, ответственный за рендеринг звука. Этот thread будет работать независимо от нашей основной программы, ему не потребуется Windows messaging или что-то еще, так что это действительно удобно. А если мы поставим приоритет thread'а на значение, большее чем обычно, он скорее всего не будет прерываться другими процессами. Единственная проблема - наш звуковой thread будет тратить процессорное время как раз в тот момент, когда нужно закончить рендеринг фрейма прежде, чем следующий видео фрейм должен начать обрабатываться, и таким образом может повлиять на частоту отображения фреймов очень неприятным образом.
Поместить его в главный цикл:

Мы просто вызываем процедуру рендеринга звука в главном цикле. Тогда она не будет мешать остальным процедурам, но останется одна большая проблема: если один проход нашего основного цикла будет больше, чем звуковой буфер, то звук будет глючить. Если вы пробовали запускать Quake 1 на 486-ом компьютере, то вы знаете, что я имею в виду.

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

  • Получал текущую проигрываемую позицию буфера
  • Генерировал и заполнял буфер с последней известной позиции до текущей
  • Ждал немного
  • Переходил на начало, если переменная "exit sound thread request" не установлена.

Я знаю, DirectSound SDK и многие другие источники могут составить впечатление, что double-buffering и печально известный DirectSound's Position Notifications жизненно необходимы, но на самом деле это не так. Единственное, что необходимо - это вовремя заполнять буфер, а способ выбора, что значит "вовремя" - это полностью ваше решение. Например, моя команда ожидания ждала где-то около одной четверти размера буфера, так что всегда оставалось много запаса в буфере.

Теперь по поводу проблемы интерференции с процессором. Я хотел, чтобы рендерер synth'а был синхронизирован с движком видео-рендера без жертвования преимуществами отличного фонового проигрывания. Я достиг этого, определив событие синхронизации (см. Win32 SDK для nearer спецификаций), которое могло "запустить" цикл звукового thread'а, так как я заменил команду Sleep() на WaitForSingleObject(), которая выходит, либо по истечении указанного времени, либо, если событие было установлено.

Таким образом, я получил возможность запуска события в основном цикле с помощью SetEvent(). Из-за внутренних действий виндового планировщика (scheduler), а также из-за того, что мой thread работает на высоком уровне приоритета, основной thread приостанавливается, а звуковой thread делает один проход цикла. Как только он достигает WaitForSingleObject() снова, основной thread продолжается. Так что это похоже на непосредственный вызов в процедуре генерации звука - и как только ваш главный цикл будет занимать достаточно большое количество времени, чтобы звук работал стабильно, значение timeout'а звукового thread'а начинает действовать и снова "создает звук на фоне".

Если вы хотите избежать слишком частого вызова звукового thread'а, просто вставьте в цикл "minimum time check", который пропускает рендер, если недостаточное число сэмплов было проиграно со времени предыдущего вызова.

3.3. Время ожидания и синхронизация.

Давайте вспомним ключевое свойство того, что мы сейчас делаем:
"Цель этой звуковой системы - воспроизведение музыки."

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

Никого не будет беспокоить? Если уж я хотел синхронизовать видео и звук, то лучше все-таки БЕСПОКОИТЬСЯ о том, когда на самом деле будет играться звук. Большинство людей попытаются уменьшить время ожидания как только возможно, для того, чтобы приблизить видео к звуку по мере своих сил.

Но они забывают одну вещь: реальное время ожидания известно. Это - ровно длина одного звукового буфера (плюс, возможно, 20мс дополнительно на время ожидания DirectSound микширования, но в большинстве случаев вы можете без проблем пренебречь ей). Так что же нас останавливает "перевести назад" наши часы на время одного звукового буфера? Ничего. После чего мы счастливо обнаружим, что находимся в полной синхронизации.

Итак, код синхронизации нашей демки выглядит следующим образом:

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

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

Так как это было бы слииииишком легко, то конечно вам следует принять к сведению несколько вещей: функция GetPosition в DirectSound'е может быть иногда неточной. Вы ДОЛЖНЫ определить DSBCAPS_GETCURRENTPOSITION2 для второго буфера, вы ДОЛЖНЫ изолировать все функции (цикл звукового thread кроме Sleep()/WaitForSingleObject() вызова и всю функцию GetTimer()) в критические разделы или mutexe'ы (загляните в Win32 SDK), поскольку иначе у вас будут проблемы с синхронизацией...

... но даже после этого величина таймера может скакать немного через каждые несколько секунд, особенно с плохо написанными драйверами саундкарт (can you spell creative?). Единственное найденное мной решение - проверять разницу (дельту) таймера от последнего до текущего вызова. Если она была больше, скажем, половины размера буфера, текущая позиция игнорировалась и моя функция возвращала вместо этого последнюю известную позицию. Это далеко не идеальное решение, но как я уже сказал - это происходит только один раз за 20-30 секунд, и никто не заметит небольшой рассинхронизации время от времени.

Если вам нужно синхронизовать демо с определенными нотами/событиями в музыке, не тратьте времени, пытаясь синхронизовать позицию музыки и часы (это возможно с небольшой FIFO очередью, которая получает корреляцию между положением и количеством сгенерированных сэмплов, когда плеер проходит позицию и которая считывается функцией GetSongPosition до "реального" значения таймера, но кого это волнует) - просто расширьте ваш плеер функциями, которые вычисляют таймерную величину из позиции песни и наоборот, используйте их в вашей авторской программе и храните только таймерные величины для событий уже в реальном демо. Это значительно упрощает жизнь (а еще сокращает длину плеера, без потери способности ультра синхронизации).

3.4. Цикл рендера.

А теперь перейдем к рендеру. Имеет смысл использовать определенную гранулярность, так как синтез будет скорее всего иметь "частоту фреймов" и выравнивание блоков рендера по этой частоте хорошо сработает в большинстве случаев. Только запомните одну вещь:

Все же делать размер буфера степенью двойки - это плохая идея.

Времена, когда ассемблерные кодеры использовали операцию AND для масок в буферных оффсетах закончились. Один или два цикла для операций сравнения сильно не навредят. Так что нет смысла делать размер буфера степенью двойки, если конечно вы не привыкли к этому. На самом деле даже лучше будет, если это не так. Я не буду вдаваться в подробности, но если вы знаете как работает кэш, вы поймете, что процессор может управлять кэшем лучше, если буфер начинается со "странных" адресов, особенно, если вы используете несколько буферов одновременно (например, в одном цикле). Просто сделайте адреса буферов кратными 32, но не степенью двойки (или оставьте небольшое пространство между буферами, даже одного dword'а достаточно) или вам же хуже.

Кроме того, используйте по меньшей мере 32bit integer буфер или, еще лучше, 32bit float буфер для "финального" сигнала на выходе, когда он покидает этап рендера. Это утверждение также применимо ко всем промежуточным смешивающим буферам, поскольку 16bit точность слишком медленная (их обработка создаст слишком много звукового шума, если сделать ее больше чем пару раз) и у вас не будет НИКАКОГО места, если сигнал будет обрезаться. Для integer'овых буферов - рассматривайте их как 1:7:24 fixed point values, а для float'овых буферов, нормализация сигнала при 1.0 - это неплохая идея.

Итак, "рендерная" часть звукового thread цикла выглядит вроде этого:

  • вычесть последнюю позицию из текущей позиции (modulo the buffer size) (это даст вам количество семплов для генерации за один проход цикла)
  • необязательно: выровняйте ее по вашей буферной гранулярности (newsize = size - (size MOD granularity), если newsize становится равным нулю, чтож, это нормально, придется генерировать не меньше, чем 4 гигабайта данных:)
  • далее вызовите функцию генерации, чтобы сгенерировать определенное количество сэмплов в промежуточный буфер
  • заблокируйте DirectSound буфер начиная от последней сыгранной позиции.
  • сконвертируйте и clip (!!) буфер вывода в знаковые 16битов и скопируйте его в DirectSound буфер.
  • разблокируйте DirectSound буфер
  • прибавьте число сгенерированных сэмплов к последней позиции (MOD buffersize - по модулю buffersize)

Таким образом ваша функция-рендер будет вызываться просто с destination буфером и количеством сэмплов, и вы сможете написать свой синтезатор или плеер или что угодно полностью ОС/платформенно независимо. Чтобы портировать систему, просто перепишите код, отвечающий за вывод звука. То же самое относится к использованию waveOut или .wav рекордеров или вашего любимого MP3 плеерного плагина, или если вы хотите сделать все это VST2 плагином (используйте нормализованный и pre-clipped флоатовый буфер в этом случае:) и т.д.

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

"Да, действительно, нам теперь нужен синтезатор"

Хорошо, но это будет позже. У вас теперь достаточно работы, если вы дочитали до этих строк, а после этого все станет гораздо серьезнее. И второе - я еще не закончил этот цикл статей, так что ждите.

В любом случае, я надеюсь, что все это вам чем-то помогло, если у вас есть вопросы, комментарии или предложения - просто напишите на kb@kebby.org или найдите меня на IRC:)

До встречи... Таммо "kb/farbrausch" Хинричс.

[существует еще одна статья kb из этого цикла, которая посвящена программным синтезаторам, и которую я еще не перевел]

Перевод - Dimouse, 2003-2004.


Автор: kb/farbrausch [Перевод - Dimouse]
Источник: Incube #1
Добавлено на сайт: 09 Марта 2007 23:31
Кто добавил на сайт: Zuf

Ваши вопросы и комментарии к статье вы можете оставить на нашем форуме

Панель пользователя

Oneliners:

15:25 kbsync
00:13 ура посоны, епты
14:47 Ура вех с праздником ! (Darkman007)
05:38 C прошедшим ГУФ!!!
21:33 С новым годом ЗУФ!!!
18:11 3LN stopped dat shit. nice.
21:52 3LN
15:09 128
06:34 180
06:34 180

[RSS]

Опрос:

Каково ваше отношение к рекламе на сайте?






[Результаты]

Активные темы

  1. Демосценеры едут в Сколково
  2. Методы сравнения двух изображений
  3. Реверс алгоритма вычисления параметра "kbsync" в iTunes
  4. Хороший сайт
  5. Помогите распаковать exe файл демосцены cdak !

democoder.ru engine v0.2.2
Valid XHTML 1.0, CSS, RSS
Время работы скрипта: 16.0 ms