All streams
Search
Write a publication
Pull to refresh
56
0

Пользователь

Send message
Всё верно, при использовании FFT на обратном преобразовании возникают щелчки на границах кадров, что хорошо заметно в демо-приложении. По сути, MDCT является видоизменённым FFT, применяемым для устранения описанного дефекта.

Чтобы осознать принцип работы MDCT, в первую очередь необходимо понимать, как работает FFT, что тоже нетривиальная задача для человека малознакомого с обработкой сигналов, поэтому в статье используется упрощённая схема.
Модифицированное дискретное косинус-преобразование
Поскольку МДКП является преобразованием с перекрытием, оно немного отличается от других преобразований Фурье. В МДКП в два раза меньше выходов, чем входов (в отличие от других преобразований, где выходов ровно столько же, сколько входов).

Обратное МДКП известно как ОМДКП. Поскольку они отличаются количеством входов и выходов, то на первый взгляд может показаться, что МДКП нельзя преобразовать в обратное. Однако наилучшая обратимость преобразования достигается применением (i) ОМДКП к перекрывающимся блокам, и является причиной устранения ошибок перед извлечением исходных данных. Этот способ известен как принцип устранения временных помех (ПУВП).
Это, по логике вещей, соблюдается лишь при маленьких степенях сжатия, когда удаляются лишь частоты, которых совершенно нет в сигнале. Дальше уже начинается появление искажений. Тут многое зависит и от рода сигнала, понятно, если мы запишем «тишину», а потом отбросим все гармоники, то на выходи и получим тишину, а степень сжатия будет максимальной, и человеческим ухом тут мало что различишь :)
Не знаю точно насчёт косинусного преобразования, но при переводе N целочисленных отсчётов из временной области в частотную с помощью преобразования Фурье после «бабочки» мы получим массив комплексных чисел тоже из N элементов, причём он будет состоять из двух зеркальных половин, поэтому для дальнейшей обработки достаточно N/2 элементов.

Если на входе у нас были целочисленные значения short размером 2 байта, то на выходе мы имеем комплексные числа, которые состоят из двух вещественных компонент (реальной и мнимой части). Чтобы не возникало большой избыточности, лучше сохранять значения этих компонент в виде float (4 байта), а не double (8 байт).
см. размеры числовых типов

То есть на входе у нас было sizeof(short)*N=2N байт информации, а на выходе 2*sizeof(float)*(N/2)=4N, то есть мы не потеряли информации, а получили даже некоторую избыточность, но зато это даст нам возможность восстановить оригинальный сигнал без искажений обратным преобразованием.

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

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

Сразу возникает вопрос, как же тогда работает архивирование без потерь, когда мы точно восстанавливаем файл по сжатой копии. А причина в том, во-первых, побайтовое кодирование (1 байт = 8 бит) зачастую является избыточным с информационной точки зрения. Просто работать с цифровой информацией побайтово, гораздо удобнее, чем побитово. Во-вторых, часто сама информация заключает в себе избыточность.

Но даже 1 бит, по-моему, не нужно воспринимать, как неделимый квант информации. Пускай, у нас есть файлы A и B равного размера (по 1Gb), которые нужно скопировать для возможности восстановления в случае повреждения. Классически можно создать Copy-A и Copy-B, затратив дополнительно 2Gb, но можно и создать файл C размером в 1Gb через операцию XOR. Парадокс заключается в том, что при утрате файла A мы с помощью B и С легко можем его восстановить, равно как и при утрате B с помощью A и C произведём восстановление. То есть файлы получаются информационно связаны (аля запутанны). Мы экономим 1Gb пространства на носителе за счёт некоторого снижения надёжности (если оба файла A и B будут одновременно повреждены, то мы не сможем их восстановить, опираясь только на C).

Возвращаясь к косинусному преобразованию, думаю, что должен соблюдаться этот закон сохранения неизбыточной информации :)

Спасибо за уточнения!

Не являюсь специалистом в данной области, всего лишь стало интересно, за счёт чего же получается такое ощутимое сжатие при кодировании аудио. Решил проверить базовые принципы и поделиться результатами, не уходя в глубокие дебри. Ведь алгоритмы работы кодеков и проблемы, возникающие при их проектировании, часто кажутся чем-то невероятно сложным и далёким, но, думаю, такая статья внесёт немного ясности.
Совершенно согласен с вашим замечанием, но цель статьи — без лишних усложнений рассказать о фундаментальных принципах компрессии аудиосигналов, а также продемонстрировать их работоспособность. Углубляться же в эту тему можно очень и очень долго…
Стоит учитывать тот факт, что разработка первых кодеков велась во времена, когда ещё не было гигабайтных винчестеров и скоростного интернета, поэтому война шла за каждый килобайт, из-за чего появлялось много форматов, патентов, алгоритмов. Поэтому над аудиокомпрессией навис ореол архисложности, который держится до сих пор, хотя основную суть способен понять человек даже мало-мальски знакомый с преобразованием Фурье.
В некоторой степени получается так. Самый значимый вклад, не побоюсь предположить, не менее 50-70% сжатия происходит благодаря удалению очевидно тихих гармоник. Дальше уже борьба идёт за качество звучания и дополнительную компрессию, а также скорость кодирования/декодирования сигнала.
1. Лямбда выражения просто заменить на методы, поэтому читабельность кода из-за этого вряд ли сильно ухудшится. Во многих же случаях, лямбда-выражение описывает нужную функциональность одной-двумя строками кода, тогда как аналогичный метод занимает при стандартном форматировании минимум пять строк. Эта проблема отчасти решена в C# 6.

2. Можно, конечно. Всегда существуют альтернативные решения. Но в плане соотношения лаконичности и читаемости кода, на мой личный взгляд, предложенный в статье подход очень даже хорош.

3. Например, в методе-обработчике события PropertyChanged есть if- или switch-конструкции, которые в зависимости от имени свойства выполняют ту или иную логику. Вполне может так случиться, что при изменении первого свойства, нужно поменять второе, из-за чего мы, например, рекурсивно зайдём в этот же обработчик, что не слишком удобно для дебага.
Приложения профилировали на тему утечек памяти?
Конечно! В библиотеке много внимания этому уделено. Например, реализация контекстных команд (Context Commands) использует механизм слабых привязок на событие CanExecuteChanged, ведь это распространённая проблема, когда статическая вью-модель удерживает контролы, подписавшиеся на это событие, от сборки мусора.

Также динамическая локализация реализована путём расширения привязок (Binding Extensions), потому что привязка — это медиатор, который не удерживает от сборки мусора ни представление, ни вью-модель, но на это мало обращается внимания, хотя момент очень важный.

Насчёт подписок на PropertyChanged, да, точка входа (как правило метод Expose [это как Dispose, только наоборот]) «распухает лямбдами», но это намного удобнее — контролировать подписки и вызовы в одном месте, чем искать в разбросанном виде по самим свойствам, а также устанавливать очерёдность вызовов.
Подписываться на PropertyChanged внутри самого INotify… инстанса я не представляю зачем.
Почему нет? Взгляните на примеры, это распространённая и удобная практика. Например, при изменении свойства нужно выполнить какие-либо действия внутри вью-модели. Конечно, можно поместить вызов нужной логики в сеттер свойства, но это не очень красиво.

А снаружи правильнее реализовать WeakEventManager с подпиской на конкретное свойство. Тогда и отписка необязательна.
На текущий момент ни в одном проекте, где применялась библиотека, реальной необходимости в слабых подписках на PropertyChanged не возникало, поэтому сейчас всё реализовано классическим образом с упором на хорошую производительность. Но ничто не мешает использовать другой подход, ведь исходный код открыт.

Поэтому неуверен, что описанная фича вообще будет востребована.
В тех проектах, где использовалась библиотека, эта фича была востребована :)

Спасибо, что высказываете своё мнение!
Логичный вопрос. Обычно в реальной жизни необходимости отписки не возникает, но если всё-таки нужно, то лямбда-выражение заменяем классическим методом и отписываемся, как и всегда.

this[() => Text].PropertyChanged += OnTextChanged;
//...
this[() => Text].PropertyChanged -= OnTextChanged;

Если подписка выполняется внутри объекта this, то никаких утечек не произойдёт, поскольку всё замкнуто на сам объект.
При внешней подписке нужно предусмотреть отписку или ожидать момента, когда слушатель будет собран сборщиком мусора уже вместе с нашим объектом одновременно.
В папке Foundation есть файлик FYI.txt

Полностью поддерживаются:
• Windows Desktop (WPF)
• Windows Phone 7, 8 (Silverlight based)

Поддерживаются с небольшими ограничениями:
• Windows Store (RT)
• Universal Apps
• Xamarin
Ожидаешь увидеть нечто концептуально новое, а получаешь килограм синтаксического сахара и ведро костылей, которые после пары лет разработки в WPF каждый сам себе пишет в том или ином виде.

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

Уверен, что при внимательном их изучении вы откроете для себя достаточно нового в области XAML-ориентированной разработки.
Спасибо за замечания! Исправил спорные моменты. Да, инжекции в конструктор вью-моделей не лучший вариант.
Слеши нужны для определения минимальных и максимальных размеров колонок и строк. Во многих случаях можно обойтись и без них. Думаю, это не столь сложная логическая задача, понять принцип их действия. Зато при таком способе очень легко изменять сетку ячеек даже во время работы приложения, что можно увидеть в примере HelloAero.
Спасибо за замечание!
Сейчас у вас поверхностное видение библиотеки и это нормально, чтобы начать различать концептуальные моменты следует применить немного больше усилий для изучения.
Зачастую для гитарных тюнеров больше подходит алгоритм автокорреляции, чем обычное преобразование Фурье (см. тут).
Что касается преобразования Фурье, то существует метод уточнения частоты гармоники по её фазе (описание и реализация на C#).

На Windows Phone, с использованием языка C# для математических вычислений и xaml для визуализации, при грамотной реализации заметных проблем с производительностью не наблюдается.

Information

Rating
Does not participate
Registered
Activity