1 | ||||||||||||||||
.NET 4.x Воспроизведение аудио-потока (массива байтов)14.04.2016, 19:04. Показов 14443. Ответов 14
Метки нет (Все метки)
Добрый вечер! Имеется модем. После получения особой команды модем начинает выдавать мне блоки данных (строго по 320 байт - голос того кто говорит на другом конце телефонной связи) через каждые N миллисекунд. Сырых данных. Их можно воспроизвести только добавив соответствующий заголовок WAVE файла.
Если эти кусочки собрать воедино и добавить в начале заголовок WAVE файла, то этот файл читается и воспроизводится любым аудио-плеером. И звук чистый, как и надо. Однако у меня возникла необходимость воспроизводить этот поток в режиме "реального времени". Дабы не тратить деньги на звонки, я записал весь поток сырых данных, полученных с модема. И, разбивая их на блоки по 320 байт, пытаемся соединить в цельный поток звука. Для справки, звук: 8 кГц, 16 бит, 1 канал. Попытка решить задачу №1 (MemoryStream + SoundPlayer):
Попытка №2 (NAudio):
Попытка решить задачу №3 (с помощью библиотеки WinSound):
Так же пробовал создать таймер который каждые N миллисекунд читал бы очередной кусок данных и воспроизводил. Результат либо было хуже (откровенные прерывания из-за завышенной задержки), либо точно такой же, как и без таймера вообще.
1
|
14.04.2016, 19:04 | |
Ответы с готовыми решениями:
14
Воспроизведение потокового аудио Воспроизведение аудио Воспроизведение аудио Воспроизведение аудио потока |
Master of Orion
|
|
14.04.2016, 22:57 | 2 |
Mans7, выглядит ужасно, если честно.
Вам нужно добавлять данные в реальном времени. Ну так создавайте поток stream и играйте его. Зачем блин байтики ворочать. Конструктор каждого аудиоплеера позволяет указать именно поток, и подозреваю что конкретно для этого так и сделано. Тем более в цикле каждый раз создавать плеер такая себе идея. Один раз создается с потоком и играется до конца. Если данные будут приходить быстрее, чем плеер будет играть - проблем не будет. Если медленнее - то возможны проблемы. Как при просмотре ютуба на плохом канале - чутка поиграло, пауза, поиграло, пауза...
1
|
14.04.2016, 23:42 [ТС] | 3 |
Psilon, каюсь, со звуками почти не работал. Очень хочу научиться делать правильно. Но трудно без опыта и примеров (найденные примеры не подходят по роду задачи).
А вы не могли бы дать пример какой-нибудь, пожалуйста? Каким именно способом наиболее правильно пользоваться? Каким плеером? Плюс, мне удалось получить диаграмму звуков - оригинальный (как записалось) и как получается при чтении "в реальном времени". Во вложении картинка. Сверху - оригинальный поток. Снизу - то, что ловится по кускам. Нетрудно понять почему в этом звуке я слышу щелчки и микро-паузы. Но трудно понять как этого избежать. Хотя есть мысль попробовать пропустить и записать пару "пакетов" которые создадут некий буфер непрерывности. Зачем байтики ворочать: именно так ко мне и приходит информация. Байтиками. Я так и не разобрался на практике с аудио-потоками. Всё упирается в конкретный файл на жёстком диске или в ссылку на интернет-источник. У меня же другая ситуация. Завтра попробую реализовать. На счёт потоков - с WAV всё не так просто. Он требует в заголовке точного указания количества байт данных (самого звука). Потому поток с WAV затруднителен, в отличии от MP3. Но форматом и сжатием до MP3 я еще не занимался и не уверен что стоит это делать получая WAV данные.
0
|
Master of Orion
|
|
15.04.2016, 00:04 | 4 |
Mans7, не важно, как приходит. Вопрос, как с этим работать. Это называется уровни абстракции, и именно они позволили из счет получить компьютеры. Картинка хорошая, она показывает, что да, пакеты не успевают приходить и поэтому происходят паузы. Либо вы просто неравильно воспроизводите. Например File.ReadAllBytes - происходит мгновенно? Нужно запросить доступ к файлу у файловой системы, найти файл на диске, начать его считывать, складывать данные во временный буфер, после этого возвращать массив в .Net, возможно копировать в управляемую память, После этого с помощью LINQ делаем Skip и Take... Если это занимает хотя бы 100 мс то это уже задержка в те же 100 мс... Читать нужно потоком непрерывно данные, как только они приходят. Возможно данные набиваются достаточно быстро и проблема в этих задержках.
Со звуком я тоже не работал, но все разумные люди исходят из одних принципов, что позволяет программисту не зависеть от конкретной предметной области - очередные блага абстракции. Мне не важно, что физически происходит. Есть конструктор, принимающий поток - нужно пробовать передавать поток - готовый класс FileStream к услугам. Если на этом проблемы решены - то на этом и стоит остановиться. Если нет - нужно думать, но опять же исходить из концепции потоков. С байтиками вручную много не навоюешь.
0
|
15.04.2016, 05:57 [ТС] | 5 |
Psilon, Беда потоков в том, что нельзя сказать какой он будет длины. Если я начну поток WAVE данных, я должен сначала дополнить его WAVE заголовком, иначе он вообще воспроизводиться не будет. Как только я в поток запишу новые данные - всё, ошибка - из-за того, что заголовок уже не соответствует действительности, а в нём указывается конкретные цифры (длина блока, общая длина файла, опять же). Поэтому и трудность такая.
Почти везде где я находил потоковую загрузку аудио-файла, почему-то подразумевалось, что "поток" это некий Stream, который заранее полностью загружен (или загружен из файла на диске моментально в память) и из которого можно получить всё для формировки заголовка либо он уже сформирован. То есть технически тоже самое, что и воспроизведение файла на диске. По поводу картинки: когда модем выдаёт данные, каждые 20 миллисекунд (кажется) я получаю 320 б текста. Если я этот текст буду непрерывно писать в файл и добавлю заголовок - получим то, что на верхней картинке. Но если входящий поток сразу проигрывать на ходу, то получаем нижнюю картинку (а так же если потом записанный файл воспроизводить блоками по 320 байт через каждые 20 мс - тоже смое).
0
|
15.04.2016, 13:10 | 7 | |||||||||||||||
Сообщение было отмечено Mans7 как решение
РешениеКак я понял, воспроизводить поток семплов напрямую в аудиокарту можно только либо написав свой драйвер аудиокарты, либо используя DirectSound. Ни первое ни второе мне делать конечно не хочется. Другой вариант - создавать аудио поток в потоковом формате, и постепенно передавать в плеер. Но тогда придется упаковывать в mp3 - что тоже не хочется. В общем, сделал таким образом. Создаем отдельный класс SoundStream - наследник Stream. Этот поток будет накапливать семплы и отдавать плееру. В самом начале он передает заголовок в WAV формате, но с максимально возможной длиной (это примерно 70 часов звука для sample rate = 8000). Затем он постепенно отдает семплы, которые он накапливает во внутреннем буфере. С другой стороны, этот поток имеет метод Write, который принимает семплы для воспроизведения. Если пытаться проигрывать этот поток через стандартный SoundPlayer, то ничего не получится, потому что SoundPlayer туповатый и пытается прочитать весь "файл" целиком. Доходит где-то до 40 мегабайт и сам же выпадает в эксепшн. В общем, не вариант. Поэтому пришлось использовать NAudio для воспроизведения потока. NAudio более адекватный, он не пытается прочитать весь поток до конца, а читает постепенно, по мере надобности, что нам и нужно. В итоге, получился следующий класс SoundStream (требует NAudio и FW4): SoundStream
И для более удобной работы, класс-обвязка для воспроизведения через NAudio: StreamPlayer
StreamPlayer имеет метод PlayAsync, который начинает воспроизведение звука в отдельном потоке. И метод Write, который принимает семплы для воспроизведения. Пример использования: Кликните здесь для просмотра всего текста
Весь проект целиком: SoundStream.zip Еще можно покопаться в сурсах NAudio, что бы выцепить оттуда как он воспроизводит звук, и обойтись без NAudio.
4
|
16.04.2016, 11:47 [ТС] | 8 | ||||||||||||||||||||
Storm23, шикарный пример, отличное описание! Поразмышляв, а так же изучив предоставленный код (кое-чему научился новому для себя), рассуждал сначала так:
Ваш пример получает набор short в плеер, а не массив байтов. Попытался перегнать бассив байтов в short[] - просто тишина. Ваш метод принимает перебираемый набор short, то есть фактически int16 (со знаком). То есть, диапазон единицы входных данных -32768 : 32767. В моём случае это массив byte, в котором диапазон единицы данных 0 : 255. Запись вида:
Попробовал самое очевидное - произвести побитовое умножение ручками (так всегда понятнее становится процесс).
И когда мне показалось, что я понял как работает запись данных в буфер, я приступил к тесту: 1. Добавил методы для работы с byte, а не с short:
Однако стоит сказать, что некоторые трески всё же остались. Но я знаю, что это из-за того, что пауза между приходящими блоками звука на долю секунды больше длительности проигрывания этих приходящих блоков. Эту проблему я решу небольшим (0.1 - 0.2) ожиданием в процессе которого звук будет писаться в буфер, но не будет воспроизводиться, и лишь потом будет проигрываться и параллельно дописываться приходящими данными. Этой небольшой и незначимой для разговора паузой я обеспечу себе, так сказать, "буфер непрерывности". СПАСИБО большое вам! p.s. и да, если убрать паузу 20 миллисекунд, звук без тресков как надо. Добавлено через 7 минут И вам Psilon спасибо за помощь! Я очень рад что всё получилось и я смог научиться чему-то новому! Добавлено через 6 минут Кстати а вот об этом я вообще не думал...
2
|
16.04.2016, 18:07 | 9 | |||||
В SoundStream это уже предусмотрено, обратите внимание на параметр preloadSize. Он как раз обеспечивает буферизацию во времени. Чем он больше, тем звук будет идти с бОльшим отставанием, но зато без рывков.
Для перевода байт в short, используйте формулу:
1
|
17.04.2016, 16:02 [ТС] | 10 |
Поясните, пожалуйста? Всё работает, звук чистый. Ну если докапываться до понятия семпла, то нужно просто по 2 байта писать за раз в очередь, что и будет являться семплом при моих параметрах звука (Channels = 1, BlockAlign = 2, BitsPerSample = 16). Просто сделать попарный ввод байтов будет достаточно?
Добавлено через 6 минут Хотя какая разница, если вводится всё равно весь набор samples? Не понимаю почему что-то должно теряться. Единственное что здесь можно было бы сделать - это проверка на чётность количества байт (ввиду того, что в семпле у меня 2 байта, достаточно % 2 == 0).
0
|
17.04.2016, 16:24 | 11 |
Нет. Смотрите, семпл - это уровень амплитуды в данный момент. Но он может быть задан с разной точностью. У вас семплы заданы с точностью 8 бит на семпл. А мой поток работает с 16 битами на семпл.
Когда я записываю свои Int16 значения в байтовый поток, я сначала пишу младший байт числа, потом старший. Получается два байта на семпл. Плеер соответственно тоже раскодирует поток байт, в обратном порядке, превращая по два байта в Int16. Когда же вы пишите свои семплы по одному байту в поток, плеер будет их переводить в Int16 и ваши два семпла в виде двух байт, плеер будет считать как один семпл. И первый байт он просто будет считать младшей частью Int16, а второй байт - старшей частью Int16.
1
|
17.04.2016, 17:43 [ТС] | 12 | |||||||||||||||
Storm23, такс... ну вообще мне так же надо писать с точностью 16 бит. Я как раз хотел ещё задать вопрос из-за "щелчков" во время воспроизведения голоса (как полученного с модема, так и с микрофона при передаче через модем). Причем эти же щелчки слышит и собеседник. Несмотря на то, что в конечном итоге голос, записанный на файл чистый и без этих шумов (как входящий так и исходящий). Подозреваю, что это взаимосвязано.
В случае воспроизведения входящего голоса: значит ли это, что мне нужно пришедший пакет байтов упаковать в Int16 примерно таким способом?
А запись и отправку голоса я делаю сейчас так: Кликните здесь для просмотра всего текста
Может, некрасиво, но мне сейчас важна не красота а работа. Красоту кода предпочитаю потом наводить, если я занимаюсь чем-то в чём плохо разбираюсь. Так или иначе, тот с кем я говорил испытывал некоторый дискомфорт качества связи - небольшой но всё же слышимый периодичный "треск". Может, раз в 2-3 секунды. В принципе не критично, однако следует всё же разобраться и избавиться от него. Следует иметь ввиду, что если передавать в модем блоки более чем 320 байт, у него буфер переполняется (либо иные причины) и он уходит в перезагрузку. Я попытался реализовать связь таким образом: приходит 320 байт голоса -> воспроизводим их -> передаём 320 байт моего голоса собеседнику. А ещё, если я передаю в модем голос из wav файла таким способом: Кликните здесь для просмотра всего текста
То собеседник слышит его без какого либо треска. Идеальный чистый голос как будто с ним говорит человек. Storm23, пожалуйста посмотрите код, подскажите мне почему могут возникать вышеупомянутые симптомы. А так же приветствую критику моего ужасного кода.
0
|
17.04.2016, 18:44 | 13 | |||||
Раньше вы говорили, что у вас звук 8 битовый. Но я смотрю ваш код, вижу вот эту строку:
Что касается треска, то сори, ничем помочь не могу. Что бы понять, нужно более подробно с этим разбираться, генерировать тестовые сигналы, записать звук, смотреть где происходит щелчок и тогда пытаться понять из-за чего он. Так вот просто взять и понять откуда у вас щелчки - я не могу
0
|
17.04.2016, 19:53 [ТС] | 14 | |||||
Вот цитата с моего первого сообщения:
Главная проблема именно в том, что если записывать входящий/исходящий голос в файл, то файл читается прекрасно и без треска. Даже если его воспроизвести теми же методами которые я использую когда получаю данные с модема. Проблема возникает только при непосредственной связи с абонентом и только в этот период. И еще я заметил, что этого НЕ происходит если на линии тишина. Только при передаче голоса. Можно подумать, что какой-то блок в какой-то момент обрывается и получается резкий скачок амплитуды, но это слишком маловероятно... Добавлено через 38 минут Разобрался в причинах прерываний. Всё было гораздо проще чем казалось, и дело вовсе не в коде чтения/записи голоса. Дело в том, что для отслеживания процесса передачи/приёма данных я назначил одно из текстовых полей для вывода ответа от модема (так как модем определяет 3 порта, я каждый постоянно "слушал"). В одном получаю дежурную информацию об изменении уровня сигнала, состоянии вызова и пр., а другой интерфейсный выдаёт и принимает данные. И вот из-за того что в текстовое поле прилетает каждые 15 мс 320 байт, оно и подтормаживало процесс =). Конечно, эта функция не требуется вовсе, она была лишь служебной и тестовой. Теперь всё пучком. P.S. Почему-то нет совершенно никакой разницы в качестве звука (голоса) при несколько разных методах воспроизведения:
P.P.S. почему-то во всех "документациях" сказано, что обмен происходит периодичностью в 20 мс, однако я поставил StopWatch таймер и высчитывал среднее кол-во миллисекунд между приходящими данными и оно было 15-16
1
|
0 / 0 / 0
Регистрация: 18.06.2018
Сообщений: 35
|
|
17.01.2019, 13:19 | 15 |
Mans7,
Добрый день! Скажите пожалуйста, не осталось ли у вас какого-нибудь готового материала, которым вы бы могли поделиться тут? Мне, и многим другим было бы очень полезно
0
|
17.01.2019, 13:19 | |
17.01.2019, 13:19 | |
Помогаю со студенческими работами здесь
15
Воспроизведение аудио Воспроизведение аудио Воспроизведение аудио файла Воспроизведение аудио из б.д. access Искать еще темы с ответами Или воспользуйтесь поиском по форуму: |