function NormLogFormants = CrossLangNormalise(GroupingIndices,RawLogFormants) % % function NormLogFormants = CrossLangNormalise(GroupingIndices,RawLogFormants) % % Geoffrey Stewart Morrison, 20 April 2007 % % Constant-log-interval normalisation of formant values for cross-language or cross-dialect data. % Also normalises speakers' L2 vowels on basis of L1 data. % % Procedure described in: % Morrison, G. S., & Nearey, T. M. (2006). A cross-language vowel normalisation procedure. % Canadian Acoustics, 34(3), 94–95. % % Additional details: % Each subgroup is given equal weight, e.g., if there are more tokens for /i/ % than /I/, and more female than male speakers, means per speaker are calculated % with equal weight given to each vowel, and means per language are calculated with % equal weight given to male and female speaker subgroups. % % As an alternative to calculating and adding/subtracting GbarL: % GdoublebarA [Glang(1)] is added to formant values of speakers with L1=A [lang1=1], % GdoublebarB [Glang(2)] is added to formant values of speakers with L1=B [lang1=2], etc. % % INPUTS: % GroupingIndices: A matrix of numeric grouping indices. Rows are tokens. % Columns are [lang1, (lang_spoken,) gender, speaker, vowel_cat] % % "lang1" indicates speaker's first language or dialect % "lang_spoken" (optional) indicates language being spoken % "speaker" ids must be unique, i.e., use combinations such as % [lang1=1 speaker=1; lang1=1 speaker=2; ... lang1=2 speaker=11; lang1=2 speaker=12] % not [lang1=1 speaker=1; lang1=1 speaker=2; ... lang1=2 speaker=1; lang1=2 speaker=2] % "vowel_cat" ids must be unique % % RawLogFormants: A matrix of formant values (in log Hertz). Rows are tokens. % Columns are measures taken at points on formant tracks, e.g., [F1@25 F1@75 F2@25 F2@75] % (use same number of points from each formant) % % OR % % A unidimensional cell array. Cells are tokens. % Each cell contains a matrix of formant tracks, e.g., [F1_track F2_track F3_track] % (time can be along either dimension) % % OUTPUT: % NormLogFormants: Normalised formant values (in log Hertz) arranged in same format as "RawLogFormants". % sample input data (only one token per vowel cat per speaker, only two male and two female speakers per lang) if nargin==0 % speaker: 1=Spanish 2=English gender: 0=Female 1=Male % vowel: 1=Sp/i/ 2=Sp/ei/ 3=Sp/e/ 4=Eng/i/ 5=Eng/I/ 6=Eng/e/ 7=Eng/E/ GroupingIndices =... [1 0 206 1 1 0 206 2 1 0 206 3 1 0 209 1 1 0 209 2 1 0 209 3 1 1 200 1 1 1 200 2 1 1 200 3 1 1 201 1 1 1 201 2 1 1 201 3 2 0 165 4 2 0 165 5 2 0 165 6 2 0 165 7 2 0 167 4 2 0 167 5 2 0 167 6 2 0 167 7 2 1 166 4 2 1 166 5 2 1 166 6 2 1 166 7 2 1 175 4 2 1 175 5 2 1 175 6 2 1 175 7]; RawLogFormants =... [5.8859 5.8547 7.9167 7.9151 6.3490 6.0536 7.7448 7.8548 6.2899 6.2698 7.7153 7.7413 5.9041 5.8424 7.8288 7.8694 6.3401 5.9541 7.7615 7.8931 6.1871 6.2079 7.7229 7.7252 5.8675 5.8075 7.5662 7.5898 6.2724 5.9672 7.4233 7.5649 6.2422 6.2359 7.3461 7.3526 5.7206 5.6800 7.5838 7.5709 6.0621 5.7661 7.4527 7.6007 6.0514 6.0679 7.4051 7.4289 5.6702 5.7970 7.8323 7.8269 6.2297 6.4688 7.6098 7.5711 6.1980 6.1327 7.7110 7.7680 6.5402 6.6139 7.5188 7.4597 5.9205 5.9394 7.9543 7.9476 6.4793 6.5030 7.5792 7.5800 6.3094 6.1912 7.8575 7.9044 6.6902 6.7667 7.5510 7.5219 5.9513 5.7251 7.6815 7.6926 6.1013 6.1083 7.5013 7.4530 6.1748 6.0765 7.5503 7.6127 6.3397 6.4229 7.4091 7.3083 5.6346 5.6906 7.6608 7.6606 6.1442 6.1783 7.3817 7.3173 6.0755 6.0236 7.5348 7.6898 6.2842 6.3597 7.3283 7.3083]; end % Gtoken: mean of formant values for each token if iscell(RawLogFormants) Gtoken=cellfun(@mean,RawLogFormants,'UniformOutput',0); Gtoken=cellfun(@mean,Gtoken,'UniformOutput',1); else Gtoken=mean(RawLogFormants,2); end % if "lang_spoken" is not provided, assume pure cross-language situation (assume missing column is "lang_spoken") if size(GroupingIndices,2)==4 GroupingIndices=[GroupingIndices(:,1),GroupingIndices(:,1),GroupingIndices(:,2:4)]; end % "GroupingIndices" cols: 1 2 3 4 5 % [lang1, (lang_spoken,) gender, speaker, vowel_cat] % CALCULATE NORMALISATION FACTORS % Gspeaker: mean of formant values for each speaker % Glang: mean of formant values for each language langs=unique(GroupingIndices(:,1)); num_langs=length(langs); genders=unique(GroupingIndices(:,3)); num_genders=length(genders); speakers=unique(GroupingIndices(:,4)); num_speakers=length(speakers); II_gender=cell(num_genders,1); for Igender=1:num_genders II_gender{Igender}=find(GroupingIndices(:,3)==genders(Igender)); end II_speaker=cell(num_speakers,1); for Ispeaker=1:num_speakers II_speaker{Ispeaker}=find(GroupingIndices(:,4)==speakers(Ispeaker)); end Glang_gender=zeros(num_langs,num_genders); Gspeaker=zeros(num_speakers,1); II_lang=cell(num_langs,1); for Ilang=1:num_langs II_lang{Ilang}=find(GroupingIndices(:,1)==langs(Ilang) & GroupingIndices(:,2)==langs(Ilang)); %L1 data only for Igender=1:num_genders II_lang_gender=intersect(II_lang{Ilang},II_gender{Igender}); speakers_sub=unique(GroupingIndices(II_lang_gender,4)); num_speakers_sub=length(speakers_sub); Gspeaker_sub=zeros(num_speakers_sub,1); for Ispeaker_sub=1:num_speakers_sub Ispeaker=find(speakers==speakers_sub(Ispeaker_sub)); II_lang_gender_speaker=intersect(II_lang_gender,II_speaker{Ispeaker}); vowels_sub=unique(GroupingIndices(II_lang_gender_speaker,5)); num_vowels_sub=length(vowels_sub); Gvowel_sub=zeros(num_vowels_sub,1); for Ivowel_sub=1:num_vowels_sub II_vowel_sub=GroupingIndices(II_lang_gender_speaker,5)==vowels_sub(Ivowel_sub); Gvowel_sub(Ivowel_sub)=mean(Gtoken(II_lang_gender_speaker(II_vowel_sub))); end %Ivowel_sub Gspeaker(Ispeaker)=mean(Gvowel_sub); Gspeaker_sub(Ispeaker_sub)=Gspeaker(Ispeaker); end % Ispeaker_sub Glang_gender(Ilang,Igender)=mean(Gspeaker_sub); end %Igender end %Ilang Glang=mean(Glang_gender,2); % NORMALISE FORMANT DATA if iscell(RawLogFormants) NormLogFormants=cell(size(RawLogFormants)); else NormLogFormants=zeros(size(RawLogFormants)); end for Ispeaker=1:num_speakers Ilang=langs==GroupingIndices(II_speaker{Ispeaker}(1),1); if iscell(RawLogFormants) NormLogFormants(II_speaker{Ispeaker})=cellfun(@(x) x-Gspeaker(Ispeaker)+Glang(Ilang),RawLogFormants(II_speaker{Ispeaker}),'UniformOutput',0); else NormLogFormants(II_speaker{Ispeaker},:)=RawLogFormants(II_speaker{Ispeaker},:)-Gspeaker(Ispeaker)+Glang(Ilang); end end return