Прошёл курс J.I. и могу сказать, что там учат не импровизации, а «как улучшить своё уже имеющееся немалое умение импровизировать». То есть он не пытается учить импровизации с нуля, а пытается показать типичные ошибки преподавателей и открыть студенту глаза на то, к чему стремиться.
Это первое подобное приложение для Android, но вполне может быть, что «родная» программа будет более функциональной, чем Splashtop, LogMeIn, или TeamViewer.
Если затачиваться на такую нишу, то можно оптимизировать jitter-буфер так, чтобы он не вносил никакой задержки до тех пор, пока сеть не «штормит». На репетиции тоже можно сделать ушам больно / сжечь что-нибудь.
Все правильно, можно улучшить эту часть в моей программе. Но, по-первых, у меня проблем с потерями пакетов не возникало.
Понятно. Другими словами, озвученное
главное отличие программы Music Over The World Tool от Netduetto
оказалось не супер-преимуществом
А что значит отсутствие буфера? Это значит что собеседник услышит ваш аккорд на несколько миллисекунд раньше.
, а скорее недодуманностью/недоработкой в силу недостаточного тестирования. Бывает.
Щелчки это вообще штука коварная. Пока их нет — всё хорошо и кажется что ну и фиг с ними. А вот если на дорогом/мощном оборудовании получить негладкую склейку звука от -1.0 к +1.0 (т.е. от минимума до максимума; или наоборот), да ещё и несколько раз подряд, слушателям мало не покажется. Например, при концерте через подобную систему. Да и оборудование может повредиться.
Понятно, что всё это явления вероятностные (и вероятность поймать склейку именно -1… 1 довольно низка), но раз в год и палка стреляет.
Кстати, готов поспорить что слепое сравнение задержки 20мс (без специальной буферизации) и 23-25мс (с нормальным jitter-буфером и нулевой вероятностью щелчка) не покажет сколь-либо существенной разницы (если вообще покажет хоть какую-то).
В лучшем случае первый подключившийся из виртуальной сети к хосту во внешней сети будет передавать свои аудио данные, а сам слышать ничего не будет.
Не-не, в лучшем случае всё будет работать ОК. А вот в худшем — два клиента за NAT будут посылать пакеты в один и тот же порт «нормального» третьего участника. Получится что-то типа dubstep :)
User experience разный даже при одинаковом размере ASIO-буферов — это же распределённая система :)
А вот если хочется именно best user experience у каждого участника, то, как и всегда при работе с ASIO и звуком вообще, надо каждому ставить его минимальный (работающий без щелчков) размер буфера.
Но единственной мыслью было периодически очищать буфер сокета.
Каждый раз когда при воспроизведении будет пропускаться хотя бы один пакет (очищенный), будет происходить негладкая склейка звука, то есть щелчок.
Только запамятовал, можно ли получить статистику о количестве пришедших пакетов на сокет?
Честно скажу — не знаю :) И узнавать не советую, так как всё равно по вышеописанным причинам (склейка, пропуск пакетов, потери пакетов) нужно будет делать нормальный jitter-буфер, читающий из сокета в неблокирующемся режиме. И кстати там можно и multiplexing будет спокойно сделать через один порт.
А можно на примере, не совсем понятно что такое «виртуальная подсеть» в данном контексте?
Например, если они вдвоём сидят за одним NAT'ом и получают один и тот же публичный IP-адрес.
Конечно, чудес не бывает, и за все приходится платить — нужно устанавливать на всех компьютерах участников одинаковый размер входных и выходных буферов ASIO-драйверов.
Это необязательное ограничение.
Рассмотрим случай двух участников. Если один из размеров буферов участников кратен размеру буфера другого (что часто бывает с ASIO, например 128 vs 256), то один может посылать свои 256 и они спокойно могут быть разобраны на два фрейма тем, у кого 128 — без внесения дополнительной задержки. Тот, который принимает по 128, а хочет по 256 — аналогично, вполне может группировать чужие фреймы по два.
Если подумать ещё чуть-чуть, то приходящие фреймы можно просто склеивать в очередь с операцией «верни мне N сэмплов или false», и тогда убирается ограничение на кратность. При этом можно заметить, что раз нам не нужна кратность, то без ограничения общности оно будет работать и с M участников.
Плохие новости: такой подход будет очень плохо работать при потерях пакетов и особенно при джиттере.
Если пакет в сети продолбается, то вместо него будет сеанс тишины длиной несколько миллисекунд. При этом в начале тишины и в конце тишины, судя по приведённому коду, будет резкий переход от «звук есть» к «звука нет» и обратно. Каждый такой переход — щелчок. Попробуйте послушать непрерывно звучащую долгую ноту (или просто белый шум) от другого участника, находясь через одну-две несущие стены от роутера :)
При джиттере дела обстоят ещё хуже. Если где-то по дороге произойдёт «пробка» пакетов, то вначале приведённый код выдаст несколько сеансов тишины, а потом прибежит кучка пакетов. Каждый пакет из очереди UDP-пакетов код пойдёт с радостью проигрывать, ни одного не пропуская.
Что это значит? Это значит что весь дальнейший звук удалённого участника будет задерживаться на временной размер произошедшей «пробки». И так — до тех пор, пока не потеряется достаточное количество пакетов, чтобы скомпенсировать эту задержку.
То, что буферизации нету в прикладной программе, вовсе не значит, что её нет например в самом сокете.
Вывод: буферизация в вашей системе уже есть, просто вы об этом не догадывались :)
Советую почитать что такое jitter-буфер и как он устроен в современных VoIP системах. Буферизация в подобных системах делается специально и ради очень важной части функциональности — осмысленно-контролируемом размере задержки (в противовес «как получится»).
Чтобы в распределенной p2p-сети два разных участника не слали данные в один и тот же порт, каждый участник шлет данные в порт, индекс которого является позицией абсолютного четырёхбайтового значения своего ip-адреса в отсортированной по возрастанию таблице ip-адресов всех участников сессии.
Что если два участника из трёх находятся в одной виртуальной подсети?
Кстати, обнаружил баг: в Office 2013 вооьбще не работала нумерация списка литературы по ГОСТу. Тот же документ в 2007м офисе я доделал без проблем.
До сих пор с теплотой вспоминаю «Заметки кудесника шотгана о девелоперском деле» в Game.EXE, а тут нате — Valve!
Первые два — must have, третий на удивление разочаровал.
Курс Music Production местами даже напомнил лабы по физике, в хорошем смысле :)
На мой вкус, даже при 30-35 играть становится сложно. 50мс это разве что для медленного блюза…
оказалось не супер-преимуществом
, а скорее недодуманностью/недоработкой в силу недостаточного тестирования. Бывает.
Щелчки это вообще штука коварная. Пока их нет — всё хорошо и кажется что ну и фиг с ними. А вот если на дорогом/мощном оборудовании получить негладкую склейку звука от -1.0 к +1.0 (т.е. от минимума до максимума; или наоборот), да ещё и несколько раз подряд, слушателям мало не покажется. Например, при концерте через подобную систему. Да и оборудование может повредиться.
Понятно, что всё это явления вероятностные (и вероятность поймать склейку именно -1… 1 довольно низка), но раз в год и палка стреляет.
Кстати, готов поспорить что слепое сравнение задержки 20мс (без специальной буферизации) и 23-25мс (с нормальным jitter-буфером и нулевой вероятностью щелчка) не покажет сколь-либо существенной разницы (если вообще покажет хоть какую-то).
Не-не, в лучшем случае всё будет работать ОК. А вот в худшем — два клиента за NAT будут посылать пакеты в один и тот же порт «нормального» третьего участника. Получится что-то типа dubstep :)
А вот если хочется именно best user experience у каждого участника, то, как и всегда при работе с ASIO и звуком вообще, надо каждому ставить его минимальный (работающий без щелчков) размер буфера.
Честно скажу — не знаю :) И узнавать не советую, так как всё равно по вышеописанным причинам (склейка, пропуск пакетов, потери пакетов) нужно будет делать нормальный jitter-буфер, читающий из сокета в неблокирующемся режиме. И кстати там можно и multiplexing будет спокойно сделать через один порт.
Например, если они вдвоём сидят за одним NAT'ом и получают один и тот же публичный IP-адрес.
Рассмотрим случай двух участников. Если один из размеров буферов участников кратен размеру буфера другого (что часто бывает с ASIO, например 128 vs 256), то один может посылать свои 256 и они спокойно могут быть разобраны на два фрейма тем, у кого 128 — без внесения дополнительной задержки. Тот, который принимает по 128, а хочет по 256 — аналогично, вполне может группировать чужие фреймы по два.
Если подумать ещё чуть-чуть, то приходящие фреймы можно просто склеивать в очередь с операцией «верни мне N сэмплов или false», и тогда убирается ограничение на кратность. При этом можно заметить, что раз нам не нужна кратность, то без ограничения общности оно будет работать и с M участников.
Если пакет в сети продолбается, то вместо него будет сеанс тишины длиной несколько миллисекунд. При этом в начале тишины и в конце тишины, судя по приведённому коду, будет резкий переход от «звук есть» к «звука нет» и обратно. Каждый такой переход — щелчок. Попробуйте послушать непрерывно звучащую долгую ноту (или просто белый шум) от другого участника, находясь через одну-две несущие стены от роутера :)
При джиттере дела обстоят ещё хуже. Если где-то по дороге произойдёт «пробка» пакетов, то вначале приведённый код выдаст несколько сеансов тишины, а потом прибежит кучка пакетов. Каждый пакет из очереди UDP-пакетов код пойдёт с радостью проигрывать, ни одного не пропуская.
Что это значит? Это значит что весь дальнейший звук удалённого участника будет задерживаться на временной размер произошедшей «пробки». И так — до тех пор, пока не потеряется достаточное количество пакетов, чтобы скомпенсировать эту задержку.
То, что буферизации нету в прикладной программе, вовсе не значит, что её нет например в самом сокете.
Вывод: буферизация в вашей системе уже есть, просто вы об этом не догадывались :)
Советую почитать что такое jitter-буфер и как он устроен в современных VoIP системах. Буферизация в подобных системах делается специально и ради очень важной части функциональности — осмысленно-контролируемом размере задержки (в противовес «как получится»).
Что если два участника из трёх находятся в одной виртуальной подсети?
Всё понимаю, просто исходная формулировка показалась мне эээ неточной :)