주요 콘텐츠

pitch

오디오 신호의 기본주파수 추정

설명

f0 = pitch(audioIn,fs)는 샘플 레이트 fs를 사용하여 오디오 입력 audioIn에 대한 시간 경과에 따른 기본주파수 추정값을 반환합니다. 입력값의 열은 개별 채널로 취급됩니다.

예제

f0 = pitch(audioIn,fs,Name=Value)는 하나 이상의 이름-값 인수를 사용하여 옵션을 지정합니다.

예제

[f0,loc] = pitch(___)는 기본주파수 추정값과 관련된 위치 loc를 반환합니다. 위에 열거된 모든 구문에 나와 있는 입력을 조합하여 지정할 수 있습니다.

예제

pitch(___)에 출력 인수를 지정하지 않으면 추정된 피치를 시간에 대해 플로팅합니다.

예제

예제

모두 축소

오디오 신호를 읽어옵니다. pitch를 호출하여 시간 경과에 따른 기본주파수를 추정합니다.

[audioIn,fs] = audioread("Hey-16-mono-6secs.ogg");

f0 = pitch(audioIn,fs);

오디오 신호를 듣고 신호와 피치를 플로팅합니다. pitch 함수는 시간 경과에 따른 기본주파수를 추정하지만, 추정값은 고조파 영역에 대해서만 유효합니다.

sound(audioIn,fs)

tiledlayout(2,1)

nexttile
t = (0:length(audioIn)-1)/fs;
plot(t,audioIn)
xlabel("Time (s)")
ylabel("Amplitude")
grid minor
axis tight

nexttile
pitch(audioIn,fs)

Figure contains 2 axes objects. Axes object 1 with xlabel Time (s), ylabel Amplitude contains an object of type line. Axes object 2 with xlabel Time (s), ylabel f0 (Hz) contains an object of type line.

오디오 신호를 읽어오고 피치를 추출합니다.

[x,fs] = audioread("SingingAMajor-16-mono-18secs.ogg");
t = (0:size(x,1)-1)/fs;

winLength = round(0.05*fs);
overlapLength = round(0.045*fs);
[f0,idx] = pitch(x,fs,Method="SRH",WindowLength=winLength,OverlapLength=overlapLength);
tf0 = idx/fs;

오디오를 듣고 오디오와 피치 추정값을 플로팅합니다.

sound(x,fs)

figure
tiledlayout(2,1)

nexttile
plot(t,x)
ylabel("Amplitude")
title("Audio Signal")
axis tight

nexttile
pitch(x,fs,Method="SRH",WindowLength=winLength,OverlapLength=overlapLength)
title("Pitch Estimations")

Figure contains 2 axes objects. Axes object 1 with title Audio Signal, ylabel Amplitude contains an object of type line. Axes object 2 with title Pitch Estimations, xlabel Time (s), ylabel f0 (Hz) contains an object of type line.

pitch 함수는 중첩된 분석 윈도우의 피치를 추정합니다. 피치 추정값은 분석 윈도우에 고조파 성분이 있는 경우에만 유효합니다. 피치 검출에 사용된 것과 동일한 윈도우 및 중첩 길이를 사용하여 harmonicRatio 함수를 호출합니다. 오디오, 피치, 고조파 비율을 플로팅합니다.

hr = harmonicRatio(x,fs,Window=hamming(winLength,"periodic"),OverlapLength=overlapLength);

figure
tiledlayout(3,1)

nexttile
plot(t,x)
ylabel("Amplitude")
title("Audio Signal")
axis tight

nexttile
pitch(x,fs,Method="SRH",WindowLength=winLength,OverlapLength=overlapLength)
title("Pitch Estimations")
xlabel("")

nexttile
harmonicRatio(x,fs,Window=hamming(winLength,"periodic"),OverlapLength=overlapLength)
title("Harmonic Ratio")

Figure contains 3 axes objects. Axes object 1 with title Audio Signal, ylabel Amplitude contains an object of type line. Axes object 2 with title Pitch Estimations, ylabel f0 (Hz) contains an object of type line. Axes object 3 with title Harmonic Ratio, xlabel Time (s), ylabel Harmonic Ratio contains an object of type line.

유효한 피치 결정에 사용할 임계값으로 고조파 비율을 사용합니다. 고조파 비율이 임계값보다 작은 경우 피치 결정을 NaN으로 설정합니다. 결과를 플로팅합니다.

threshold = 0.9;
f0(hr < threshold) = nan;

figure
plot(tf0,f0)
xlabel("Time (s)")
ylabel("Pitch (Hz)")
title("Pitch Estimations")
grid on

Figure contains an axes object. The axes object with title Pitch Estimations, xlabel Time (s), ylabel Pitch (Hz) contains an object of type line.

여성의 목소리로 "volume up"이라고 다섯 번 말하는 오디오 신호를 읽어옵니다. 오디오를 들어봅니다.

[femaleVoice,fs] = audioread("FemaleVolumeUp-16-mono-11secs.ogg");
sound(femaleVoice,fs)

남성의 목소리로 "volume up"이라고 다섯 번 말하는 오디오 신호를 읽어옵니다. 오디오를 들어봅니다.

maleVoice = audioread("MaleVolumeUp-16-mono-6secs.ogg");
sound(maleVoice,fs)

여성의 녹음과 남성의 녹음 모두에서 피치를 추출합니다. 남성과 여성의 오디오 녹음에 대한 피치 추정값을 보여주는 히스토그램을 플로팅합니다. 히스토그램의 모양은 유사합니다. 이는 피치 결정에 무성음과 무음 영역에 대한 결과가 포함되기 때문입니다.

f0Female = pitch(femaleVoice,fs);
f0Male = pitch(maleVoice,fs);

figure
numBins = 20;
histogram(f0Female,numBins,Normalization="probability");
hold on
histogram(f0Male,numBins,Normalization="probability");
legend("Female Voice","Male Voice")
xlabel("Pitch (Hz)")
ylabel("Probability")
hold off

Figure contains an axes object. The axes object with xlabel Pitch (Hz), ylabel Probability contains 2 objects of type histogram. These objects represent Female Voice, Male Voice.

detectSpeech 함수를 사용하여 오디오 신호에서 음성 영역을 분리한 다음 이 음성 영역에서만 피치를 추출합니다.

speechIndices = detectSpeech(femaleVoice,fs);
f0Female = [];
for ii = 1:size(speechIndices,1)
    speechSegment = femaleVoice(speechIndices(ii,1):speechIndices(ii,2));
    f0Female = [f0Female;pitch(speechSegment,fs)];
end

speechIndices = detectSpeech(maleVoice,fs);
f0Male = [];
for ii = 1:size(speechIndices,1)
    speechSegment = maleVoice(speechIndices(ii,1):speechIndices(ii,2));
    f0Male = [f0Male;pitch(speechSegment,fs)];
end

남성과 여성의 오디오 녹음에 대한 피치 추정값을 보여주는 히스토그램을 플로팅합니다. 피치 분포가 이제 예상대로 표시됩니다.

figure
histogram(f0Female,numBins,Normalization="probability");
hold on
histogram(f0Male,numBins,Normalization="probability");
legend("Female Voice","Male Voice")
xlabel("Pitch (Hz)")
ylabel("Probability")

Figure contains an axes object. The axes object with xlabel Pitch (Hz), ylabel Probability contains 2 objects of type histogram. These objects represent Female Voice, Male Voice.

엘리제를 위하여(Für Elise)의 도입부 오디오 파일과 오디오의 샘플 레이트를 불러옵니다.

load FurElise.mat song fs
sound(song,fs)

피치 추정 필터(PEF), 탐색 범위 50~800Hz, 윈도우 지속 시간 80ms, 중첩 지속 시간 70ms, 중앙값 필터 길이 10을 사용하여 pitch 함수를 호출합니다.

method = "PEF";
range = [50, 800]; % hertz
winDur = 0.08; % seconds
overlapDur = 0.07; % seconds
medFiltLength = 10; % frames

winLength = round(winDur*fs);
overlapLength = round(overlapDur*fs);
[f0,loc] = pitch(song,fs, ...
    Method=method, ...
    Range=range, ...
    WindowLength=winLength, ...
    OverlapLength=overlapLength, ...
    MedianFilterLength=medFiltLength);

추정한 피치를 시간에 대해 플로팅합니다.

pitch(song,fs, ...
    Method=method, ...
    Range=range, ...
    WindowLength=winLength, ...
    OverlapLength=overlapLength, ...
    MedianFilterLength=medFiltLength);

Figure contains an axes object. The axes object with xlabel Time (s), ylabel f0 (Hz) contains an object of type line.

오디오를 프레임별로 읽어오기 위해 dsp.AudioFileReader 객체를 만듭니다.

fileReader = dsp.AudioFileReader("SingingAMajor-16-mono-18secs.ogg");

스트리밍 오디오에 목소리가 들어 있는지 검출하기 위해 voiceActivityDetector 객체를 만듭니다.

VAD = voiceActivityDetector;

읽지 않은 샘플이 더 이상 없을 때까지 파일에서 이를 읽어오고 프레임에 음성 활동이 포함될 확률을 확인합니다. 프레임에 음성 활동이 포함된 경우 pitch를 호출하여 오디오 프레임의 기본주파수를 추정합니다. 프레임에 음성 활동이 포함되지 않은 경우 기본주파수를 NaN으로 선언합니다.

f0 = [];
while ~isDone(fileReader)
    x = fileReader();
    
    if VAD(x) > 0.99
        decision = pitch(x,fileReader.SampleRate, ...
            WindowLength=size(x,1), ...
            OverlapLength=0, ...
            Range=[200,340]);
    else
        decision = NaN;
    end
    f0 = [f0;decision];
end

시간에 따라 검출된 피치 등고선을 플로팅합니다.

t = linspace(0,(length(f0)*fileReader.SamplesPerFrame)/fileReader.SampleRate,length(f0));
plot(t,f0)
ylabel("Fundamental Frequency (Hz)")
xlabel("Time (s)")
grid on

Figure contains an axes object. The axes object with xlabel Time (s), ylabel Fundamental Frequency (Hz) contains an object of type line.

피치를 추정하는 방법은 잡음 강인성, 정확도, 최적 지연 및 계산 비용 측면에서 각기 다른 장단점이 있습니다. 이 예제에서는 다양한 잡음 조건에서의 총 피치 오차(GPE) 및 계산 시간을 기준으로 서로 다른 피치 검출 알고리즘의 성능을 비교합니다.

테스트 신호 준비하기

오디오 파일을 불러오고 이 파일에 있는 샘플 수를 확인합니다. 오디오 파일에 대응하는 실제 피치도 불러옵니다. 실제 피치는 깨끗한 음성 파일에 대한 몇몇 타사 알고리즘들의 평균값으로 결정됩니다.

[audioIn,fs] = audioread('Counting-16-44p1-mono-15secs.wav');
numSamples = size(audioIn,1);
load TruePitch.mat truePitch

주어진 SNR에서 오디오 신호에 잡음을 추가하여 테스트 신호를 생성합니다. mixSNR 함수는 이 예제에만 유효한 편의 함수이며, 신호, 잡음 및 요청된 SNR을 받아 이 요청된 SNR에서의 잡음 있는 신호를 반환합니다.

testSignals = zeros(numSamples,4);

turbine = audioread('Turbine-16-44p1-mono-22secs.wav');
testSignals(:,1) = mixSNR(audioIn,turbine,20);
testSignals(:,2) = mixSNR(audioIn,turbine,0);

whiteNoiseMaker = dsp.ColoredNoise('Color','white','SamplesPerFrame',size(audioIn,1));
testSignals(:,3) = mixSNR(audioIn,whiteNoiseMaker(),20);
testSignals(:,4) = mixSNR(audioIn,whiteNoiseMaker(),0);

레이블 지정과 인덱싱을 위해 잡음 조건과 알고리즘 이름을 셀형 배열로 저장합니다.

noiseConditions = {'Turbine (20 dB)','Turbine (0 dB)','WhiteNoise (20 dB)','WhiteNoise (0 dB)'};
algorithms = {'NCF','PEF','CEP','LHS','SRH'};

피치 검출 알고리즘 실행하기

각각의 알고리즘과 잡음 조건 쌍, 그리고 시간 측정 정보에 대한 피치 결정을 보관하기 위해 배열을 사전할당합니다. 루프에서 알고리즘과 잡음 조건의 각 조합에 대해 pitch 함수를 호출합니다. 각 알고리즘은 이와 관련된 최적의 윈도우 길이를 갖습니다. 문제를 단순화하기 위해 이 예제에서는 모든 알고리즘에 디폴트 윈도우 길이를 사용합니다. 요소를 3개 가진 중앙값 필터를 사용하여 피치 결정을 평탄화합니다.

f0 = zeros(numel(truePitch),numel(algorithms),numel(noiseConditions));
algorithmTimer = zeros(numel(noiseConditions),numel(algorithms));

for k = 1:numel(noiseConditions)
    x = testSignals(:,k);
    for i = 1:numel(algorithms)
        tic
        f0temp = pitch(x,fs, ...
            'Range',[50 300], ...
            'Method',algorithms{i}, ...
            'MedianFilterLength',3);
        algorithmTimer(k,i) = toc;
        f0(1:max(numel(f0temp),numel(truePitch)),i,k) = f0temp;
    end
end

총 피치 오차 비교하기

총 피치 오차(GPE: Gross Pitch Error)는 피치 검출 알고리즘을 비교할 때 많이 사용되는 메트릭입니다. GPE는 상대 오차가 지정된 임계값보다 높은 피치 결정의 비율로 정의되며, 전통적으로 음성 연구에서는 20%입니다. GPE를 계산하여 명령 창에 출력합니다.

idxToCompare = ~isnan(truePitch);
truePitch = truePitch(idxToCompare);
f0 = f0(idxToCompare,:,:);

p = 0.20;
GPE = mean( abs(f0(1:numel(truePitch),:,:) - truePitch) > truePitch.*p).*100;

for ik = 1:numel(noiseConditions)
    fprintf('\nGPE (p = %0.2f), Noise = %s.\n',p,noiseConditions{ik});
    for i = 1:size(GPE,2)
        fprintf('- %s : %0.1f %%\n',algorithms{i},GPE(1,i,ik))
    end
end
GPE (p = 0.20), Noise = Turbine (20 dB).
- NCF : 0.9 %
- PEF : 0.4 %
- CEP : 8.2 %
- LHS : 8.2 %
- SRH : 6.0 %
GPE (p = 0.20), Noise = Turbine (0 dB).
- NCF : 5.6 %
- PEF : 24.5 %
- CEP : 11.6 %
- LHS : 9.4 %
- SRH : 46.8 %
GPE (p = 0.20), Noise = WhiteNoise (20 dB).
- NCF : 0.9 %
- PEF : 0.0 %
- CEP : 12.9 %
- LHS : 6.9 %
- SRH : 2.6 %
GPE (p = 0.20), Noise = WhiteNoise (0 dB).
- NCF : 0.4 %
- PEF : 0.0 %
- CEP : 23.6 %
- LHS : 7.3 %
- SRH : 1.7 %

각 알고리즘에 대해 1초간의 데이터를 처리하는 데 걸리는 평균 시간을 계산하고 결과를 출력합니다.

aT = sum(algorithmTimer)./((numSamples/fs)*numel(noiseConditions));
for ik = 1:numel(algorithms)
    fprintf('- %s : %0.3f (s)\n',algorithms{ik},aT(ik))
end
- NCF : 0.020 (s)
- PEF : 0.068 (s)
- CEP : 0.021 (s)
- LHS : 0.025 (s)
- SRH : 0.047 (s)

입력 인수

모두 축소

오디오 입력 신호로, 벡터나 행렬로 지정됩니다. 행렬의 열은 개별 오디오 채널로 취급됩니다.

데이터형: single | double

입력 신호의 샘플 레이트(단위: Hz)로, 양의 스칼라로 지정됩니다.

샘플 레이트는 탐색 범위 상한의 2배보다 크거나 같아야 합니다. 탐색 범위는 Range 이름-값 쌍을 사용하여 지정합니다.

데이터형: single | double

이름-값 인수

모두 축소

선택적 인수 쌍을 Name1=Value1,...,NameN=ValueN으로 지정합니다. 여기서 Name은 인수 이름이고 Value는 대응값입니다. 이름-값 인수는 다른 인수 뒤에 와야 하지만, 인수 쌍의 순서는 상관없습니다.

R2021a 이전 릴리스에서는 쉼표를 사용하여 각 이름과 값을 구분하고 Name을 따옴표로 묶으십시오.

예: pitch(audioIn,fs,Range=[50,150],Method="PEF")

피치 추정값 탐색 범위로, 증가하는 양의 정수 값을 갖는, 요소를 2개 가진 행 벡터로 지정됩니다. 함수는 Method로 지정된 알고리즘에 따라 벡터로 지정된 상한 및 하한 대역 경계 내에서 기본주파수에 대한 최선의 추정값을 탐색합니다. 범위는 구간의 끝점을 포함하며 단위는 Hz입니다.

탐색 범위의 유효한 값은 샘플 레이트 fsWindowLengthMethod의 값에 따라 달라집니다.

방법최소 범위최대 범위
"NCF"fs/WindowLength < Range(1)Range(2) < fs/2
"PEF"10 < Range(1)Range(2) < min(4000,fs/2)
"CEP"fs/(2^nextpow2(2*WindowLength-1)) < Range(1)Range(2) < fs/2
"LHS"1 < Range(1)Range(2) < fs/5 - 1
"SRH"1 < Range(1)Range(2) < fs/5 - 1

데이터형: single | double

분석 윈도우의 샘플 수로, [1, min(size(audioIn,1), 192000)] 범위의 정수로 지정됩니다. 일반적인 분석 윈도우의 범위는 20~100ms입니다. 디폴트 윈도우 길이는 52ms입니다.

데이터형: single | double

인접한 분석 윈도우 간의 중첩 샘플 수로, (-inf,WindowLength) 범위의 정수로 지정됩니다. 음수 중첩 길이는 중첩되지 않는 분석 윈도우를 나타냅니다.

데이터형: single | double

피치 추정에 사용되는 방법으로, "NCF", "PEF","CEP", "LHS" 또는 "SRH"로 지정됩니다. 피치를 계산하는 방법은 잡음 강인성, 정확도 및 계산 비용 측면에서 각기 다른 장단점이 있습니다. 피치를 계산하는 데 사용되는 알고리즘은 다음 논문을 기반으로 합니다.

  • "NCF" –– 정규화된 상관 함수 [1]

  • "PEF" –– 피치 추정 필터 [2]. 함수는 논문에 설명된 진폭 압축을 사용하지 않습니다.

  • "CEP" –– 켑스트럼 피치 결정 [3]

  • "LHS" –– 로그 고조파 합산 [4]

  • "SRH" –– 잔차 고조파 합산 [5]

데이터형: char | string

시간 경과에 따라 피치 추정값을 평활화하는 데 사용되는 중앙값 필터 길이로, 양의 정수로 지정됩니다. 디폴트 값인 1은 중앙값 필터링이 없음에 해당합니다. 중앙값 필터링은 피치를 추정하는 동안 이상값을 제거하는 데 사용되는 후처리 기법입니다. 함수는 지정된 Method를 사용하여 피치를 추정한 후 movmedian을 사용합니다.

데이터형: single | double

출력 인수

모두 축소

추정된 기본주파수(단위: Hz)로, 스칼라, 벡터 또는 행렬로 반환됩니다. 반환되는 행 개수는 WindowLengthOverlapLength 이름-값 쌍의 값과 입력 신호 크기에 따라 달라집니다. 반환되는 열(채널) 개수는 입력 신호 크기의 열 개수에 따라 달라집니다.

데이터형: single | double

기본주파수 추정값과 관련된 위치로, f0과 크기가 같은 스칼라, 벡터 또는 행렬로 반환됩니다.

기본주파수는 WindowLength 샘플 영역에 걸쳐 국소적으로 추정됩니다. loc 값은 기본주파수를 추정하는 데 사용된 가장 최근 샘플(가장 큰 샘플 번호)에 대응됩니다.

데이터형: single | double

알고리즘

pitch 함수는 WindowLength 인수와 OverlapLength 인수에 따라 오디오 입력을 분할합니다. 기본주파수는 각 프레임에 대해 추정됩니다. 위치 출력값 loc에는 대응하는 프레임의 가장 최근 샘플(가장 큰 샘플 번호)이 포함됩니다.

기본주파수를 추정하는 데 사용되는 알고리즘에 대한 설명은 해당 참고 문헌을 참조하십시오.

  • "NCF" –– 정규화된 상관 함수 [1]

  • "PEF" –– 피치 추정 필터 [2]. 함수는 논문에 설명된 진폭 압축을 사용하지 않습니다.

  • "CEP" –– 켑스트럼 피치 결정 [3]

  • "LHS" –– 로그 고조파 합산 [4]

  • "SRH" –– 잔차 고조파 합산 [5]

참고 문헌

[1] Atal, B.S. "Automatic Speaker Recognition Based on Pitch Contours." The Journal of the Acoustical Society of America. Vol. 52, No. 6B, 1972, pp. 1687–1697.

[2] Gonzalez, Sira, and Mike Brookes. "A Pitch Estimation Filter robust to high levels of noise (PEFAC)." 19th European Signal Processing Conference. Barcelona, 2011, pp. 451–455.

[3] Noll, Michael A. "Cepstrum Pitch Determination." The Journal of the Acoustical Society of America. Vol. 31, No. 2, 1967, pp. 293–309.

[4] Hermes, Dik J. "Measurement of Pitch by Subharmonic Summation." The Journal of the Acoustical Society of America. Vol. 83, No. 1, 1988, pp. 257–264.

[5] Drugman, Thomas, and Abeer Alwan. "Joint Robust Voicing Detection and Pitch Estimation Based on Residual Harmonics." Proceedings of the Annual Conference of the International Speech Communication Association, INTERSPEECH. 2011, pp. 1973–1976.

확장 기능

모두 확장

C/C++ 코드 생성
MATLAB® Coder™를 사용하여 C 코드나 C++ 코드를 생성할 수 있습니다.

버전 내역

R2018a에 개발됨