Вы генерируете свой сигнал с
a * sin(2πf * t + con)
, где t
находится в диапазоне [0 .. CHUNK/RATE)
.
Когда вы запускаете следующий блок, t
сбрасывается в ноль.Чтобы создать непрерывный сигнал, вам нужно изменить con
, чтобы сгенерировать то же значение результирующей фазы, что и в предыдущем примере.
Использование БПФ не будет работать, поскольку генерируемый вами сигнал не являетсяточное значение, кратное окну сэмпла, плюс вам действительно интересна фаза в конце окна сэмпла, а не фаза в начале.
Вместо этого вам просто нужна функция генерации значение фазы при t = t_end, по модулю 2π.
Т.е. вы можете просто использовать:
con = 2.0 * np.pi * f * CHUNK/RATE + con
, но это значение будет расти, что может вызвать возможные числовые проблемы, если вы объедините много кусковвместе, где частоты высокие.Поскольку функция синуса периодическая, вам просто нужно нормализовать конечную фазу в диапазоне от 0 до 2π:
con = math.fmod(2.0 * np.pi * f * CHUNK/RATE + con, 2.0 * np.pi)
Если вы измените свою функцию генерации так:
a * sin(2π * (f * t + con))
затем con
представляет долю полного цикла, перенесенного вперед от предыдущего патрона, и вы можете избежать деления по модулю на 2π, что может немного повысить точность.
con = math.modf(f * CHUNK/RATE + con)[0]
Попытка aБолее четкое объяснение:
Примечание. Этот метод работает только потому, что вы точно знаете уравнение генерации для предыдущего фрагмента и генерируете следующий фрагмент.Если какое-либо из этих условий изменится, вам понадобится другая техника для соответствия кускам.
Предыдущий блок генерируется с использованием sin()
следующей последовательности:
2πf₁*0/RATE+con₁, 2πf₁*1/RATE+con₁, ..., 2πf₁*1022/RATE+con₁, 2πf₁*1023/RATE+con₁
Itдолжно быть ясно, что для плавного перехода к следующему чанку этот чанк должен начинаться с sin()
из 2πf₁*1024/RATE+con₁
Следующий чанк начинается с sin()
из 2πf₂*0/RATE+con₂
.
Поэтому у нас будет плавный переход, если:
2πf₂*0/RATE + con₂ = 2πf₁*1024/RATE + con₁
или
0 + con₂ = 2πf₁*1024/RATE + con₁
или
con₂ = 2πf₁*1024/RATE + con₁
, которые можно записать в generate_sine
функционирует как:
con = 2.0 * np.pi * f * CHUNK/RATE + con
- это уравнение, которое появилось "из ниоткуда" в моем ответе выше.С этой точки зрения, поскольку функция sin()
является периодической 2π, я просто выполняю уменьшение по модулю 2π, чтобы аргумент sin()
не увеличивался без границ, вызывая неточности чисел.
Надеюсь, что это делает вещияснее.