주파수 사이를 부드럽게 전환 할 수있는 사인파 발생기를 만드는 방법


27

오디오 용 기본 사인파 발생기를 작성할 수 있지만 한 주파수에서 다른 주파수로 부드럽게 전환 할 수 있기를 원합니다. 하나의 주파수 생성을 중단하고 즉시 다른 주파수로 전환하면 신호가 끊기고 "클릭"이 들립니다.

내 질문은 250Hz에서 시작하여 클릭을 유도하지 않고 300Hz로 전환하는 웨이브를 생성하는 좋은 알고리즘은 무엇입니까? 알고리즘에 선택적 글라이드 / 포르타멘토 시간이 포함되어 있다면 훨씬 좋습니다.

오버 샘플링과 저역 통과 필터 또는 웨이브 테이블 사용과 같은 몇 가지 가능한 접근 방법을 생각할 수 있지만, 이것이 일반적인 방법으로 처리하는 일반적인 문제라고 확신합니다.


2
왜 전이 기간 동안 선형 주파수 전이를 사용하지 않았습니까? 예를 들어, 시간 t0에서 주파수 f0에서 시간 t1에서 주파수 f1로 전환해야하는 이유는 왜 전환 주파수 f (t) = f0 * (1-q) + f1 * q를 도입하지 않는 것입니다. 여기서 q = (t -t0) / (t1-t0), 신호 A (t) = sin (2 * Pi * f (t) * t)를 생성합니까?
mbaitoff 2012 년

답변:


24

과거에 사용한 한 가지 방법은 파형 조회 테이블의 인덱스로 사용되는 위상 누산기를 유지하는 것입니다. 위상 델타 값은 각 샘플 간격마다 누산기에 추가됩니다.

phase_index += phase_delta

주파수를 변경하려면 각 샘플에서 위상 누산기에 추가되는 위상 델타를 변경합니다.

phase_delta = N * f / Fs

어디에:

phase_delta is the number of LUT samples to increment
freq is the desired output frequency
Fs is the sample rate

이것은 주파수 변화, FM 등과 같이 phase_delta를 동적으로 변경하더라도 출력 파형이 연속적임을 보장합니다.

주파수 (포르타멘토)의 부드러운 변화를 위해 순간적으로 변경하는 것보다 적절한 수의 샘플 간격 동안 이전 값과 새 값 사이의 phase_delta 값을 증가시킬 수 있습니다.

주의 phase_indexphase_delta모두가, 정수 및 소수 구성 요소가 그들이 포인트 또는 고정 소수점 부동해야 즉. phase_index의 정수 부분 (모듈로 테이블 크기)은 파형 LUT에 대한 인덱스로 사용되며 분수 부분은 고품질 출력 및 / 또는 작은 LUT 크기를 위해 인접한 LUT 값 사이의 보간에 선택적으로 사용될 수 있습니다.


고마워, 나는 대답에 LUT가 포함될 것으로 기대했다. 1Hz에서 하나의 파형을 포함하는 LUT (예 : Fs 항목)를 사용하려고했습니다. LUT의 최적 크기를 결정하는 경험 법칙이 있습니까?

4
다양한 사인파에 따라 달라집니다. 순수한 사인파인지, 복잡한 파형인지, 인접한 LUT 엔트리 사이를 보간 할 것인지 또는 잘라낼 것인지 등 다양한 요소에 따라 달라집니다. 단일 사분면 테이블이 있고 인덱싱 산술 및 부호 반전을 직접 처리하거나 전체 사분면 테이블이 있어야합니다. 개인적으로 나는 매우 간단하고 16 비트 "소비자"오디오에 대해 좋은 결과를 제공해야하기 때문에 보간이없는 1024 포인트 (NB : 2 ^ N은 모듈로 인덱싱에 적합 함) 4 사분면 테이블로 시작합니다.
Paul R

1
좋은 대답입니다, 폴 얼마 전에 게시 된 주제에 대해서도 비슷한 질문 이 있습니다 . 더 많은 정보는 항상 도움이됩니다.
Jason R

4
이 접근법을 보는 또 다른 방법은 전압 제어 발진기 (VCO)의 에뮬레이션입니다. VCO의 출력 주파수는 입력 전압 (보통 입력 전압의 선형 함수)에 따라 다르지만 입력 전압이 순간적으로 전환 되더라도 출력 신호는 연속 위상을 갖습니다 . 출력은 여기서 는 연속 시간의 함수, 출력 주파수는 위상의 유도체이며, 동일하면서 여기서 되는 정지 주파수. ϕ ( t ) ω 0 + k x ( t ) ω 0
sin(ϕ(t))=sin(0tω0+kx(τ)dτ)
ϕ(t)
ω0+kx(t)
ω0
Dilip Sarwate

1
누산기 아이디어 (근사로 인해 작동하지 않는 직접 계산을 사용하고 있음) 덕분에 동일한 문제가 발생했습니다. : jsfiddle.net/sebpiq/p3ND5/12
sebpiq

12

사인파를 만드는 가장 좋은 방법 중 하나는 재귀 적 업데이트가있는 복잡한 페이저를 사용하는 것입니다. 즉

z[n+1]=z[n]Ω

Ω=exp(jω)ωnz[n]z[n]=a+jbz[n]

aa+bb=1

따라서 우리는 여전히 그렇다면 때때로 한 번 확인하고 그에 따라 수정할 수 있습니다. 정확한 수정은

z[n]=z[n]aa+bb

aa+bb1/xx=1

1x3x2

그래서 수정은 단순화

z[n]=z[n]3a2b22

수백 개의 샘플마다이 간단한 보정을 적용하면 오실레이터를 영원히 안정적으로 유지할 수 있습니다.

주파수를 연속적으로 변화시키기 위해서는 승수 W가 그에 따라 업데이트되어야한다. 멀티 플라이어가 비 연속적으로 변경 되더라도 연속 발진기 기능이 유지됩니다. 주파수 램핑이 필요한 경우 업데이트를 몇 단계로 나누거나 동일한 오실레이터 알고리즘을 사용하여 승수 자체를 업데이트 할 수 있습니다 (단일 이득 복잡한 위상이기 때문에).


이 답변에 감사드립니다. 실제 코드로 전환하기에 충분히 이해하는 데 약간의 시간이 걸릴 수 있지만 시도해 볼만한 흥미로운 대안처럼 보입니다.
Mark Heath

2
: 나는 참조 golang에서이 솔루션을 구현 github.com/rmichela/Acoustico/blob/...
라이언 미카엘라

불행히도 일정한 시간 기반을 사용하는 경우에만 잘 작동하는 아름다운 솔루션입니다. 그렇지 않은 경우 올바른 복소 회전을 계산하려면 sin 및 cos를 계산해야합니다.
Cameron Tacklind

2

에서 이 사이트 :

한 주파수에서 다른 주파수로 또는 다른 진폭에서 다른 진폭으로 부드럽게 전환하려면, 불완전 사인파를 추가 된 섹션으로 수정하여 while 루프의 각 반복 후 결과 파형이 x 축에서 끝나도록해야합니다.

제대로 작동하는 것 같습니다.

(실제로 전환시 x 축에서 동기화 된 경우 점진적 전환이 필요하지 않은 것으로 가정합니다.)


1
ω00ω10

2

위상 누산기 사용에 대한 이전 제안에 동의합니다. 기본적으로 제어 입력은 스텝 당 또는 클록주기마다 (또는 인터럽트 당 또는 기타) 위상 진행 량이므로 해당 값을 변경하면 위상의 불연속없이 주파수가 변경됩니다. 그런 다음 LUT를 통해 또는 sin (theta) 또는 cos (theta)의 계산을 통해 축적 된 위상 값으로부터 웨이브 진폭이 결정됩니다.

이것은 본질적으로 NCO (Numerically Controlled Oscillator) 또는 DDS (Direct Digital Synthesizer)로 알려져 있습니다. 이러한 용어에 대해 웹 검색을 수행하면 이론과 실제 작동 방식에 대해 알고 싶은 것보다 많은 결과를 얻을 수 있습니다.

추가 어큐뮬레이터를 추가하면 원하는 경우 위상 전진 값의 변화율을 제어하여 원하는대로 주파수 간 원활한 전환이 가능합니다. 이를 DDA (Digital Differential Analyzer)라고도합니다.


좋은 추가 정보. 에릭, 반가워요. 우리는 알고리즘 장관을 사용할 수있었습니다.
Jason R

1

1 차 순서로, 새로운 주파수 정현파의 시작 위상을 조정하여 1 차 전이 샘플 포인트에서 이전 정현파의 위상과 같아야합니다. 첫 번째 주파수를 계산하고 두 번째 주파수에 대해 위상을 사용하십시오.

두 번째 옵션은 d_phase를 한 주파수의 주파수에서 여러 샘플에 대한 다음 주파수로 램프하는 것입니다. 이렇게하면 1 차 유도체의 연속성이 정리되고 글라이드가 제공됩니다.

세 번째 옵션은 d_phase 램핑 속도에서 상승 코사인과 같은 스무딩 창을 사용하는 것입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.