Операции над сигналами
Учимся комбинировать сигналы: из этих операций собираются все сложные обработчики.
Операции над сигналами — поэлементные (сложение, умножение, масштаб) и по оси времени (сдвиг, инверсия, прореживание) преобразования последовательностей отсчётов.
Сложный эффект — это всегда комбинация простых операций. Эхо — это сложение с задержанной копией. Эквалайзер — взвешенная сумма отфильтрованных версий. Чтобы строить такие схемы, надо твёрдо владеть базовыми операциями.
Поэлементные операции
Складываем и умножаем сигналы поотсчётно; масштабируем амплитуду умножением на число.
a = [1, 2, 3, 4]
b = [10, 20, 30, 40]
add = [x + y for x, y in zip(a, b)] # сумма (смешивание)
mul = [x * y for x, y in zip(a, b)] # произведение (модуляция/окно)
scale = [3 * x for x in a] # масштаб амплитуды
print("сумма: ", add)
print("произвед:", mul)
print("масштаб:", scale)
Вывод:
сумма: [11, 22, 33, 44] произвед: [10, 40, 90, 160] масштаб: [3, 6, 9, 12]
Сложение — это микширование (два голоса в одной дорожке). Умножение на другой сигнал — модуляция или наложение окна. Масштаб — регулятор громкости.
Операции по оси времени
Сдвиг задерживает или опережает сигнал; инверсия разворачивает его во времени; прореживание выбрасывает часть отсчётов (понижает частоту дискретизации).
def shift(x, k):
# k>0 — задержка (вправо), нули слева
if k >= 0:
return ([0] * k + x)[:len(x)]
k = -k
return (x[k:] + [0] * k)
x = [1, 2, 3, 4, 5]
print("исходный: ", x)
print("задержка на 2: ", shift(x, 2))
print("опережение на 1:", shift(x, -1))
print("инверсия: ", x[::-1])
print("прореживание /2:", x[::2])
Вывод:
исходный: [1, 2, 3, 4, 5] задержка на 2: [0, 0, 1, 2, 3] опережение на 1: [2, 3, 4, 5, 0] инверсия: [5, 4, 3, 2, 1] прореживание /2: [1, 3, 5]
Линейная комбинация: простое эхо
Соберём из этих операций эффект эха: сигнал плюс его задержанная и ослабленная копия.
def shift(x, k):
return ([0] * k + x)[:len(x)]
x = [10, 8, 0, 0, 0, 0, 0, 0] # короткий «хлопок»
delay, gain = 3, 0.5
echo = [round(s + gain * d, 1) for s, d in zip(x, shift(x, delay))]
print("Сигнал с эхом:", echo)
Вывод:
Сигнал с эхом: [10.0, 8.0, 0.0, 5.0, 4.0, 0.0, 0.0, 0.0]
Исходный хлопок в начале, а через 3 отсчёта — его половинная копия. Это и есть эхо, собранное из сдвига, масштаба и сложения.
Как работает под капотом
Ключевое свойство операций — линейность. Сложение и умножение на константу сохраняют принцип суперпозиции: реакция системы на сумму сигналов равна сумме реакций. Именно поэтому из этих кирпичей строят линейные системы, которые так удобно анализировать. А вот умножение двух сигналов (не на константу) — операция нелинейная: она создаёт новые частоты (суммарную и разностную). На этом основана модуляция, которую разберём в следующем уроке. Прореживание опасно: выбрасывая отсчёты, мы понижаем fs и без предварительной фильтрации легко получаем алиасинг.
Частые ошибки
- Сдвигать без обнуления краёв. При задержке слева должны появиться нули, иначе сигнал «зациклится» или сдвинется некорректно.
- Прореживать без антиалиасингового фильтра. Понижение
fsтребует сначала срезать высокие частоты, иначе они наложатся. - Путать сложение и умножение сигналов. Сложение микширует и линейно; умножение модулирует и создаёт новые частоты.
Итог
- Поэлементные операции: сложение (микс), умножение (модуляция/окно), масштаб (громкость).
- Операции по времени: сдвиг (задержка), инверсия, прореживание.
- Сложные эффекты (эхо, эквалайзер) — линейные комбинации этих операций.
- Сложение и масштаб линейны; умножение двух сигналов нелинейно и рождает новые частоты.