Имхо , очередной велосипед... А зачем оно надо? Вед были же разные саунд-фонты и т.п..... Какая собсно глобальная цель?
gammaker
> А mid-файл это просто источник информации о нотах.
я в курсе, но там очень много всякой доп информации может храниться: громкость трека, панормама, велосити, питч... Для меня разобрать весь формат показалось сложным.
UncleMike
> Имхо , очередной велосипед... А зачем оно надо? Вед были же разные саунд-фонты
> и т.п..... Какая собсно глобальная цель?
Midi есть не на всех платформах вроде как. А так будет везде. Буду в играх использовать музыку в формате midi, потому что она мало занимает. А что за саунд фонты? Не слышал об этом.
radiantor
> громкость трека, панормама, велосити, питч...
Громкость трека и темп кстати могут ещё и меняться. В первой версии я не знал об этом, в итоге у меня sonic3 неверно воспроизводился. Velocity это громкость отдельной ноты. Про панораму не слышал, не знаю что такое. А pitch вроде как-то нигде и не использовался, поэтому я его не реализовывал. Ещё всё усложняется тем, что есть разные типы midi: 0, 1 и 2. Я реализовал только 1. Нулевой обычно некорректно работает. Там на одной дорожке все инструменты понамешаны, а мой код ожидает, что у каждого инструмента своя дорожка.
gammaker
> Про панораму не слышал,
баланс между правым и левым каналами :)
radiantor
> баланс между правым и левым каналами :)
А, это... У меня пока только один канал.
Новая версия со стримингом: http://www.gamedev.ru/files/?id=97447
Как у вас работает X-Files и Genie in a bootle? У меня тормозят на нетбуке.
Синусы оптимизировал, теперь пила тормозит. Вот текущий код:
struct Sawtooth { Sawtooth(float updownRatio=1) {updown_value=updownRatio/( updownRatio+1); c1=2/updown_value; c2=2/( 1-updown_value);} void SetParams( float frequency, double step) {freq=frequency; step;} float operator( )( float t) const { float sawPos=Math::fract( t*freq+updown_value*0.5f); //Math::fract(x)=x-floor(x) return sawPos<updown_value? sawPos*c1-1: 1-( sawPos-updown_value)*c2; } private: float freq=0; float updown_ratio; float updown_value, c1, c2; };
Тут пила с произвольным соотношением (фронт : спад) - updownRatio. Частные случаи симметричной пилы и зубчатой, которые можно гораздо эффективнее реализовать, я почти не использую. Есть способ оптимизировать этот код?
И ещё вопрос по огибающим. У меня сейчас сделано всё некорректно, хочу переделать. У меня реализовано два типа огибающих:
1) Экспоненциальное затухание
2) Таблица с линейной интерполяцией между элементами от 0 до 1: нота по времени делится на N равных промежутков, где N - размер таблицы. Такая таблица позволяет реализовать какое угодно затухание и нарастание, но работает очень медленно.
Оба типа растягиваются на всю ноту, независимо от её длительности. То есть долгие ноты затухают (нарастают в зависимости от таблицы) медленно, а короткие - быстро. Таким образом, я сейчас для пианино, гитары и некоторых других инструментов использую экспоненциальное затухание, а для духовых, органов, скрипки и других - таблицу с плавным нарастанием и убыванием.
Я собрался переделать всё это безобразие и хочу первым делом избавиться от тормозных таблиц, и думаю, как можно упростить схему. Ведь такая универсальность и не нужна, по крайней мере в большинстве инструментов? Достаточно ли будет просто сделать параметр атаки и затухания? Но как их лучше интерполировать? Линейно как-то не очень наверное, надо какую-то плавную кривую сделать. Посоветуйте, какую лучше функцию выбрать, чтобы она была достаточно плавной, но в то же время быстро считалась. Нужно что-то типа smoothstep, только быстрое.
А то судя по графикам ADSR, которые я виде, там какая-то ломаная, а не плавная кривая.
gammaker
> Есть способ оптимизировать этот код?
Конечно, вычисляешь фиксированный прирост и все, одно сложение и проверка на переполнение без умножений.
Tonal
> Конечно, вычисляешь фиксированный прирост и все, одно сложение и проверка на
> переполнение без умножений.
Ну на некотором моменте после прироста идёт спад, а потом опять прирост и так далее. Понадобятся ветвления, чтобы обнаруживать, когда надо заменить прирост на спад. Да ещё надо сделать так, чтобы погрешность огромная не накапливалась при большой частоте.
gammaker
можно и без ветвлений если подумать хорошо, в твоем коде я насчитал 4-ре ненужных умножения,
там вобще дожно быть только одно сложение в цикле, и все.
Tonal
> можно и без ветвлений если подумать хорошо
Что-то ничего в голову не лезет.
Tonal
> в твоем коде я насчитал 4-ре ненужных умножения
Так их всего 4. Конечно одно из них можно убрать, введя новую перемменную, а второе сделав через приращение некоторой позиции на константу dt*freq. А как убрать умножения во второй строке operator()? Как убрать ветвление? Как убрать fract?
Tonal
> там вобще дожно быть только одно сложение в цикле, и все.
Если бы это была пила вида /|/|/|/|, то я бы понял, но у меня же общий случай. Как его можно свести к одному сложению?
gammaker
> но у меня же общий случай. Как его можно свести к одному сложению?
тебе заранее известно сколько тактов длится подьем и спад, заряжаешь счетчик цикла и вперед, в цикле одно сложение.
Tonal
> тебе заранее известно сколько тактов длится подьем и спад, заряжаешь счетчик
> цикла и вперед, в цикле одно сложение.
Я думал об этом, но если частота высокая, то на максимумах и минимумах всё поедет же. Либо придётся между этими отдельными периодами делать варьирующийся, а это тогда придётся городить двойной цикл с ветвлениями во внешнем.
gammaker
> двойной цикл с ветвлениями во внешнем.
а что здесь плохого, ветвления во внешнем цикле дешевле в N раз, N - кол-во семплов на подбем или спад.
Тема в архиве.