LINUX.ORG.RU

Как правильно запилить звуковой буфер с кроссфейдером?

 , ,


0

1

Мои размышления:

Хочу запилить штуковину, которая будет держать в буфере некоторое кол-во RAWPCM фреймов. Для простоты будем считать что у нас mono.

К примеру внутренняя частота даунмикса будет 44100, тогда если длина кроссфейдера равна 5 секундам, значит надо хранить 44100*5, причем для обоих входных сигналов. Ведь в тот момент когда трек А заканчивается, мы должны начать его заглушать, а трек Б поднимать в громкости. Тестово я уже прогонял этот прикол, волны суммирует, все ок.

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

Для нормальной работы мне нужно забить данными как минимум CrossfadeTime*2 времени. Почему? Потому, что допустим трек Б за 5 сек поднялся в громкости на 100% и где-то далее будет флажок «вот на таком то фрейме пора делай fadeout т.к. трек заканчивается». В этот момент в буфере А я уже типа положил данные для fadein.

А вот если на вход подается мп3, тем более в VBR то хрен я подсчитаю его длину, распакованные данные вообще возвращаются как попало, за одну отправку пачки фреймов в декодер на его выходе может быть вообще 0 (НОЛЬ!) PCM-фреймов. Тут конечно каждый может сказать - читай заголовки. Я их читаю. А ты можешь на 100% доверять инфе в заголовках?

Как быть? Что делать? Есть мысли?

Кастану кого знаю что со звуком работали: waker, i-rinat. Правильно ли я мыслю? Как обойти проблемку с сжатыми форматами, в частности мп3?

deep-purple ★★★★★
() автор топика

Со своей колокольни я бы всё ж отделил работу со звуком от его чтения. Пусть у тебя будет отдельный класс/коллбэк, у которого ты будешь просить PCM-фреймы и микшировать их.

uuwaan ★★
()
Ответ на: комментарий от uuwaan

В том то и прикол, что я не могу быть уверен что получу нужное кол-во фреймов для буфера при следующем обращении к «читалке файлов». Так то да - это разные сущности, ну т.е. фейдер обращается к читалке, а читалка должна уметь разные форматы, ну или обращаться к «врапперу»(?) читалок умеющих разные форматы, не суть - я беспокоюсь что под конец трека мне могут вернуть фреймов не на всю длину фейдера.

deep-purple ★★★★★
() автор топика
Ответ на: комментарий от Waterlaz

Хотеть. Я же могу не раскрывать причину потребности? Пусть будет - мне интересно.

deep-purple ★★★★★
() автор топика
Ответ на: комментарий от deep-purple

Буфер — это задержка во воспроизведении. А это не комильфо.

Я вижу только чтение длины из заговока и за N секунд до конца трека уменьшение громкости и начало воспроизведения другого трека. real-time.

PS: а ещё у макскома можно отдолжить машину времени и точно узнать, когда трек закончится. ;)

beastie ★★★★★
()
Последнее исправление: beastie (всего исправлений: 1)
Ответ на: комментарий от beastie

Буфер — это задержка во воспроизведении. А это не комильфо.

Я могу и в файл класть, короче на задержку плевать, главное чтоб не отвалилось.

за N секунд до конца трека

Ну вот я писал в первом посте что мп3 может не вернуть кратный (запакованному размеру) размер распакованых фреймов.

deep-purple ★★★★★
() автор топика
Ответ на: комментарий от deep-purple

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

uuwaan ★★
()
Ответ на: комментарий от beastie

Он не битый, он к примеру VBR - переменного битрейта.

deep-purple ★★★★★
() автор топика
Ответ на: комментарий от uuwaan

Если заголовок будет зафейлен - мне не влом проверить и длину файла и попытаться понять что там зафейлено.

deep-purple ★★★★★
() автор топика
Ответ на: комментарий от deep-purple

Немногое ты поймёшь, не залезая в дебри конкретного формата и не читая файл последовательно до самого конца.

uuwaan ★★
()
Ответ на: комментарий от uuwaan

Для wav таки все просто, читаем заголовок и там все уже будет, и частотка и кол-во каналов, и длина трека и битность. Отсюда можно провалидировать длину файла в байтах, если не совпало - фейл.

deep-purple ★★★★★
() автор топика

Нужна очередь длиной в crossfade. С одной стороны впихиваешь декодированные данные, с другой забираешь. Когда впихивать станет нечего, то есть когда кончится первый файл, начинай смешивать его со вторым. Смешивать можно на ходу, делать это именно в буфере не обязательно.

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

Собсно вопрос как на 100% узнать что файл уже заканчивается и пора фейдить? Нельзя на 100%? А как можно защититься чтоб выходная волна не обрывалась?

deep-purple ★★★★★
() автор топика
Ответ на: комментарий от deep-purple

Когда файл закончится, ты узнаешь об этом сразу же. Но у тебя ещё останется полная очередь семплов, которой хватит как раз на crossfade, ведь её длина специально так выбрана.

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

Значит как я написал в первом посте нужно буферить crossfadeTime*2 фреймов, на всякий случай, если там будет какой-то хвостик, я так узнаю что фейдить начинать гдето посреди (ближе к концу) буфера, например. Но это фейдаут. А для фейдина придется так же читать(раскодирывать) файл наперед на crossfadeTime*2 фреймов чтобы быть уверенным что его хватит минимум на то чтобы он смог и зафейдиниться и зафейдаутиться. Во ))) Верно жи?

deep-purple ★★★★★
() автор топика
Ответ на: комментарий от i-rinat

Не суть что в реальном, задержка не критична совершенно. Почему не сокс? Хочу запилить сам, познать все нюансы.

deep-purple ★★★★★
() автор топика
Ответ на: комментарий от i-rinat

А для общей информативности, можно про сокс подробнее? Может я чонить почерпну из исходников его, или как ты предлагал его внедрить?

deep-purple ★★★★★
() автор топика
Ответ на: комментарий от deep-purple

sox это универсальный ответ на все вопросы про пакетные манипуляции со звуком. Но как там что-то делать, я не знаю. Я его и не запускал ни разу, кажется.

i-rinat ★★★★★
()

мыслишь ты правильно. но про mp3 не понял в чем проблема. ты ж его сначала все равно должен в PCM перегнать перед декодированием, не?

а если не, значит ты юзаешь что-то вроде gstreamer, и тогда тебе надо просто начать играть следующий трек за 5 секунд до окончания предыдущего, и крутить громкость, вот и весь тебе кроссфейд (так делают некоторые плееры).

waker ★★★★★
()
Последнее исправление: waker (всего исправлений: 1)

вот типичный API для DSP:

void process(PCMBuffer input, PCMBuffer output);

тебе на вход идет сигнал в PCM.

внутри кроссфейдера, ты сэмплы должен накапливать чтобы в буфере было 5 секунд звука, и только _после_ этого начинать их отдавать.

в момент когда ты определил, что начало буфера внутри кроссфейдера пересекло границу «5 секунд до конца» — начинай примешивать к нему данные из следующего трека. ессно, тебе надо иметь какие-то метки, где во внутреннем буфере идут сэмплы от предыдущего трека, а где уже от следующего.

waker ★★★★★
()
Последнее исправление: waker (всего исправлений: 1)
Ответ на: комментарий от waker

Не, я без гстримера, через либмп3, ну шоп поменьше зависимостей было, вообще вкомпилить его туда хочу чтоб цельный один бинарик был и все.

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

deep-purple ★★★★★
() автор топика
Ответ на: комментарий от waker

И вот собсно в чем трабла с чтением файлов, в частности - мп3, предположим я открыл очередной файл, прочитал заголовок, доверился данным, начал слать дату на вход декодера, а он мне возвращает «ничего» уже достаточно долго, я пол файла прочитал, а толку нет. Как вот защититься от битых файлов? Чтоб хотябы не упасть и максимально сохранить «качество» выходной волны?

deep-purple ★★★★★
() автор топика
Ответ на: комментарий от deep-purple

Тут если рилтайм (хоть и с задержкой) - то звук может на выходе станет обрываться пока я буду декодировать так много мп3.

нужно делать буферизацию в отдельном потоке. блокировать поток, который пишет сигнал в output buffer, нельзя. поток, который пишет в алсу, должен читать данные после обработки dsp.

т.е. что-то вроде такого

поток 1:

читаем из файла, прогоняем через dsp, складываем результат в буфер, пока он не заполнится. у dsp свой буфер (>5 секунд в твоем случае).

поток 2:

берем готовый результат из буфера, который получился в потоке 1, и пишем в алсу.

Как вот защититься от битых файлов?

пропускать битые фреймы. если битых фреймов >N — считать что файл кончился, и играть другой.

waker ★★★★★
()
Ответ на: комментарий от waker

пропускать битые фреймы. если битых фреймов >N — считать что файл кончился, и играть другой

Хе-хе, тут трабла выходит, либа если не сможет декодировать, то или вернет код-статус ошибки и/или ошибки не будет, но в выходной буфер добавится ноль PCM-фреймов. Тут уж если и ловить, то основываясь на «скорости» заполнения буфера, не успел заполниться за 3 чекунды например - все, файл битый.

Ага, но тогда, время уже прошло, и очередной трек тоже может быть в мп3, и может опять битый. Тогда как не крути - я не успею забить буфер даже в отдельном потоке.

Да, я могу подложить «тишину» на то время пока буду искать валидный файл. Только теперь тут придется угадывать в каком месте-времени при каких фейл-условиях эту тишину в буфер начинать писать.

deep-purple ★★★★★
() автор топика

Та же техника что tcp/ip: тебе заранее не известно сколько придёт инфы. Тебе нужен, например, кольцевой буфер достаточного размера который будет забит на 5сек или больше.

Т.е. сделай функцию read() которая всегда возвращает заданное кол-во секунд из потока. Ну а внутри нее уже вся гразная логика по чтению из mp3.

А вообще, наверняка в каком-нить gstreamer эта задача вообще решается тривиальным пайплайном из командной строки.

true_admin ★★★★★
()
Ответ на: комментарий от deep-purple

Ага, но тогда, время уже прошло, и очередной трек тоже может быть в мп3, и может опять битый. Тогда как не крути - я не успею забить буфер даже в отдельном потоке.

А почему ты считаешь, что время декодирование == времени воспроизведения?

Зачем привязываться к воспроизведению? Тебе надо держать буфер - держи. За 3секунды можно заполнить этот буфер тысячи раз. В чем проблема?

anonymous
()
Ответ на: комментарий от deep-purple

Да, я могу подложить «тишину» на то время пока буду искать валидный файл.

я делаю то же самое. только не «пока ищу валидный файл», и «пока буфер пустой».

waker ★★★★★
()
Ответ на: комментарий от anonymous

А почему ты считаешь, что время декодирование == времени воспроизведения?

Тысячи раз не получится, в зависимости от мощи машинки, например на мей старой х386 10 секунд PCM из мп3 декодировались около 1 секунды.

deep-purple ★★★★★
() автор топика
Ответ на: комментарий от true_admin

Та же техника что tcp/ip

Для tcp/ip проканает - можно и подождать пока данные заполнятся. Со звуком дольше чем время буфера ждать нельзя - выходная волна поломается.

deep-purple ★★★★★
() автор топика
Ответ на: комментарий от waker

только не «пока ищу валидный файл», и «пока буфер пустой»

Вот это интересует. Можешь словами аписнить как ты выкручиваешься?

deep-purple ★★★★★
() автор топика
Ответ на: комментарий от deep-purple

Тысячи раз не получится

Датычё, я не поленился - пошел проверил. У меня это 1074 раза. Это декодирование mpg123 файлеца созданного lame --preset insane -V 0

$ time mpg123 -w - 123.mp3 > /dev/null 
High Performance MPEG 1.0/2.0/2.5 Audio Player for Layers 1, 2 and 3
 version 1.21.0; written and copyright by Michael Hipp and others
 free software (LGPL) without any warranty but with best wishes

Playing MPEG stream 1 of 1: 123.mp3 ...

MPEG 1.0 layer III, VBR, 44100 Hz joint-stereo

[53:17] Decoding of 123.mp3 finished.

real 0m2.970s
user 0m2.944s
sys 0m0.028s

в зависимости от мощи машинки

Что из этого следует? Это итак ясно.

например на мей старой х386 10 секунд PCM из мп3 декодировались около 1 секунды.

Не знаю как и на чем ты там мерил, но любой более-менее вменяемый х86(кора2+) даст х100 минимум. Даже задрипанный атом.

Тем более к чему твой ответ? Ты же сам доказал, что у тебя х10 минимум. Т.е. за 5секунд твоего буфера ты может спокойно прочитать минуту. Этого хватит за глаза.

Дак в чем, собственно, твоя проблема?

anonymous
()
Ответ на: комментарий от deep-purple

пока поток декодера медленно декодирует файл (например, если он битый, или если диск медленный) - другой поток, который пишет в алсу, сожрет все, что было декодировано ранее, и начнет голодать. размер буфера будет =0. ты можешь легко определить, что это произошло, если количество данных в буфере декодера < чем размер буфера алсы.

waker ★★★★★
()
Ответ на: комментарий от deep-purple

Со звуком дольше чем время буфера ждать нельзя - выходная волна поломается.

Так источник-то mp3, никаких проблем нет раскодировать столько сколько нужно.

true_admin ★★★★★
()
Ответ на: комментарий от true_admin

Ище раз - ты не можешь быть уверен на все сто, что «успеешь» раскодировать нужное кол-во данных для буфера, мп3 он такой, размер запакованных данных совершенно не пропорционален размеру распакованых, кроме того как waker писал выше - может диск медленный, может файл битый, или даже несколько подряд битых.

Вобщем то ответы мне уже помогли. Теперь только асилить нормально реализацию.

deep-purple ★★★★★
() автор топика
Ответ на: комментарий от deep-purple

рассматривай битые файлы и медленный диск как нестандартную ситуацию. т.е. если это произошло — юзер не будет сильно удивлен, если плеер будет лагать/заикаться. в первую очередь, сделай чтобы все хорошо работало на быстром диске и нормальных файлах. потом натрави код на битые файлы или медленный диск, или вообще в качестве теста добавь usleep в декодер mp3, чтобы симулировать худший случай, и дальше смотри где можно что улучшить, если это вообще надо.

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

waker ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.