15.07.2008, autor: Ing. Robert Krejčí, kategorie: Signály

Implementace principu kytarové ladičky na signálovém procesoru

Návrh adaptivního algoritmu
Španělská kytara

Zkusíme navrhnout algoritmus pro implementaci principu kytarové ladičky do signálového procesoru TMS320C54x. Vstupem bude záznam zvukového signálu ladění kytarové struny E6. Výstupem bude signál s intenzitou odpovídající shodě vstupní frekvence s referenční (správnou) frekvencí 83 Hz.

Tip: Pokud máte elektrickou kytaru nebo basovku a chcete ji připojit do zesilovače, pak si u nás můžete vybrat také nějaký kytarový kabel.

Teoretický úvod

Kytara, jak známo, má 6 strun. Frekvence tónů jednotlivých strun jsou poměrně nízké a i při uvažování vyšších harmonických není nijak náročné její zvuk číslicově zpracovávat. Frekvence strun jsou následující:

Tabulka 1: Frekvence strun kytary při běžném ladění
StrunaFrekvence [Hz]
E682.41
A5110.00
D4146.83
G3196.00
B2246.94
E1329.63

Kytarové elektronické ladičky většinou fungují tak, že ze vstupního signálu (mikrofonu) automaticky detekují nejsilnější tón a zobrazují odchylku od správné frekvence, na kterou se má struna naladit. Dokonalejší ladičky umí detekovat jakýkoliv tón v rozsahu několika oktáv a uživatel je může podle potřeby i překalibrovat až o půl tónu.

Možnosti detekce tónu

Nabízejí se dvě základní metody pro detekci tónu:

  1. Ve frekvenční oblasti: z dostatečně dlouhé posloupnosti vstupních vzorků se vypočítá FFT, následně se určí nejvýraznější frekvence, k ní se vypočítá nejbližší referenční tón a určí se odchylka od reference. Při měření je vhodné provádět průměrování, aby se odstranil vliv šumu.

  2. V časové oblasti: pomocí adaptivního filtru – viz dále.

Adaptivní filtrace

Princip adaptivního filtru je následující:

  • Koeficienty adaptivního filtru jsou proměnlivé v čase za účelem optimalizace daného požadavku.

  • Nejčastěji používaným algoritmem pro adaptaci koeficientů je LMS (Least Mean Square).

  • Filtry jsou nejčastěji FIR z důvodu stability.

Obrázek 1: Princip adaptivního filtru
Obrázek 1: Princip adaptivního filtru

Algoritmus LMS

Pro každý vzorek:

  • Filtr filtruje vstupní signál pomocí variabilních koeficientů bi.

  • Podle chybové funkce upraví koeficienty bi.

Obrázek 2: Princip algoritmu LMS
Obrázek 2: Princip algoritmu LMS

Přesnost konvergence filtru závisí na:

  • Adaptačním kroku δ: čím vyšší δ, tím rychleji filtr konverguje, ale s nižší přesností.

  • Počtu koeficientů N: zbytková chyba je úměrná počtu koeficientů N, tedy řádu filtru.

Podstata algoritmu LMS

  • Pro každý vzorek:

    • Spočítá chybu: en=rn-yn

    • Váhuje chybu adaptačním krokem δ: en'= δ·en

  • Pro každý vzorek a každý koeficient:

    • Pronásobení váhované chyby a signálu: ei=en' · xn-i

    • Násobení xn-i · bi a akumulace

    • Výpočet nových koeficientů: novýbi=bi+ei

    • Aktualizace koeficientů: bi=novýbi

Implementace

Implementaci tohoto algoritmu si ukážeme v jazyce C, v Matlabu a v assembleru.

Adaptivní filtr v jazyce C

Firma Texas Instruments dodává na výukovém CD kompletní program kytarové ladičky v jazyce C napsaný pro vývojovou desku s procesorem TMS320C5416. Pro základní funkci detekce tónu jedné struny stačí převzít minimum. Uvádíme základ algoritmu adaptivní filtrace ze souboru na výukovém CD adaptive_filter.c (s vynecháním některých komentářů z důvodu úspory místa):


#define N 12
#define BETA 5600

static signed int x[N];  /* Array to hold input samples */
static signed int w[N];  /* Weights (adaptive filter coefficients)*/
static signed int error; /* Difference between desired and actual. */

void adaptive_filter_initialize(void)
{
 unsigned int i;
 for ( i = 0 ; i < N ; i++)
   {
      w[i] = 0;
      x[i] = 0;
   }
 error = 0;
}

signed int i;
signed long y;            /* Output of adaptive filter */
signed long difference;


signed long adaptive_filter( signed int desired, signed int latest_measurement)
{
  for ( i = N-1 ; i > 0 ; i--)
    {
      x[i] = x[i-1];
    }
  x[0] = latest_measurement;
  difference =  error * BETA;
  error = (signed int) (difference >> 15); /* Update error */
  y = 0; /* Start accumulation at zero */
  for ( i = N-1 ; i >= 0 ; i--)
    {
      y += ((long)(x[i] * w[i]) >> 0 ); /* Prevent overflows */
      difference = ( (long) error * x[i] ) >> 14;
      if ( difference & 0x0001)
        {
          if ( difference >= 0)
            {
              difference++ ; /* Round up when above zero */
            }
          else
            {
              difference--;  /* Round down when below zero */
            }
        }
      w[i] += (signed int) ( difference >> 1);
    } // end for
  y >>= 15;  /* Remove remains of fractional part */
  error = desired - (signed int) y;
  return(  ( y << 16 ) | (long) error ); /* Return y OR error */
}

Simulace adaptivní filtrace v Matlabu

Algoritmus napsaný v jazyce C je celkem snadno pochopitelný. Dále je uveden přepsaný do Matlabu, aby bylo možné ho odsimulovat se skutečným signálem laděné struny E6. Výpis souboru adaptivni_filtr.m:


%%% Adaptive filter

% Definice konstant
% N is number of elements in adaptive filter
N=34; % in C: 12, in ASM: 34

% BETA is the amount of the output used for feedback to determine the new
% filter weights. 56 represents 56/32767 i.e. 0.002
% We could also approximate 0.002 to 1/512 i.e. divide by 2 ^ 9
beta=5600; % in C: 5600, in ASM: 560


L=20000; % delka vzorkovaneho signalu
fs=11025; % [Hz] - vzorkovaci frekvence
fstring=83; % [Hz] - struna E
amp=8191; %8191 - zesileni

%% inicializace
% vnitrni promenne
x_ar=zeros(1,N); % Array to hold input samples
w_ar=zeros(1,N); % Weights (adaptive filter coefficients)
difference=0; % rozdil mezi pozadovanou a skutecnou hodnotou
i=0; % promenna pro cykly

% vystupni promenne
y=0; % vystup filtru
error=0; % Difference between desired and actual.
% vystupni promenne pro plot
y_ar=zeros(1,L);
error_ar=zeros(1,L);


%% Definice poli
desired=sinus(fstring,L,fs).*amp; % vytvoreni referencni frekvence
wavplay(desired./amp,fs);

%% nahraju zvuk
if(0)
    zvuk=getsample(L,fs);
    zvuk=zvuk.*amp; % normalizace zvuku
else
    load zvuk;
    zvuk=zvuk.*amp; % normalizace zvuku
    wavplay(zvuk./amp,fs);
end;


%% vlastni filtrace
for k=1:L
    latest_measurement=zvuk(k);

    % posunuti vzorku v pameti filtru; na 1. pozici dosadim aktualni vzorek
    for i=N : -1 : 2
        x_ar(i)=x_ar(i-1);
    end;

    x_ar(1)=latest_measurement;


    % rozdil
    difference =  error * beta;

    % aktualizace chyby
    error=bitshift(difference,-15); %-15

    y = 0; % Start accumulation at zero

    % FIR filtr
    for i=N : -1 : 1
        y=y+x_ar(i)*w_ar(i); % nasobeni a akumulace
        difference=bitshift(error*x_ar(i),-14); %-14
        % Remove fractional part then update coeffient W[i]
        if (abs(difference)>=1)%bitand(difference,1)
            if ( difference >= 0)
              difference=difference+1; % Round up when above zero
            else
              difference=difference-1;  % Round down when below zero
            end;
        end; % endif
        w_ar(i)=w_ar(i)+bitshift(difference,-1); %-1
    end; % end for

    y=bitshift(y,-15);%-15 % Remove remains of fractional part
    error=desired(k)-round(y);

    %ret=y*(2^16) + error; % Return y OR error

    y_ar(k)=y;
    error_ar(k)=error;

end; % end for


%% Vykresleni vysledku
figure(2);
subplot(411);
plot(zvuk);
title 'Vstupni signal';

subplot(412);
specgram(zvuk);
title 'Spektrogram vstupniho signalu';

subplot(413);
plot(y_ar);
title 'Vystup filtru';

subplot(414);
plot(error_ar);
title 'Chybova  funkce';

wavplay(y_ar./(amp/8),fs);
wavplay(error_ar./(amp/2),fs);

Funkce getsample.m zajistí nahrání zvuku z mikrofonu připojeného do zvukové karty počítače:


function y=getsample(delkavz,fs)
    a=questdlg('Bude se nahravat vzorek. Pripraven?');
    if strcmp(a,'Yes')
        for i=100000:-1:0
            % zpozdovaci odpocitavani - aby byl cas se pripravit
            k=i
        end;
        y = wavrecord(delkavz, fs, 'double'); % Nahrani zaznamu
    elseif strcmp(a,'No') % synteticky signal
        f=83;
        t=1:delkavz;
        y=chirp(t , f-1 , delkavz*4 , f+1);
    end

    figure(1);
    subplot(221);
    plot(y);
    title('Vstupni zvukovy signal');
    axis([0 delkavz -1 1]);
    subplot(222);
    specgram(y);
    title('Spektrum vstupniho zvukoveho signalu');

    y = normal(y); % Normovani na 1
    figure(1);
    subplot(223);
    plot(y);
    axis([0 delkavz -1 1]);
    title('Normovany vstupni signal');
    subplot(224);
    ffty=abs(fft(y));plot(ffty);
    axis([0 delkavz/2 0 max(ffty)*0.75]);
    title('Spektralni slozky vstupniho signalu');
    wavplay(y, fs);

Funkce sinus.m vytvoří sinusový signál o dané frekvenci, počtu vzorků a vzorkovací frekvenci:


function ret=sinus(f,delkavz,fs)
    t=1:delkavz; % casova osa
    ret=sin(2*pi*t*f/fs);

Výsledky simulace

Obr. 3: Výsledky simulace v Matlabu
Obr. 3: Výsledky simulace v Matlabu

Během doznívání tónu kytary by se adaptivní filtr měl naladit na frekvenci 83 Hz a chybová funkce by měla konvergovat k nule.

Implementace v assembleru procesoru TMS320C5416

Uvádíme zde přepis algoritmu adaptivního filtru do assembleru, který jsme s úpravami převzali rovněž z výukového CD firmy Texas Instruments. Některé komentáře jsou z důvodu úspory místa vynechány.


    .mmregs
FP  .set AR7

; Number of elements in filter X[]
;#define N 12
N   .set   34

; Feedback amount. Affects convergence rate of filter.
;#define BETA 5600
BETA .set   560

; Storage on stack
INPUT .set 9

; Variables used in function

;static signed int x[N];  /* Array to hold input samples */
.bss _X_1,N,0,0
.bss _X_2,N,0,0
;static signed int w[N];  /* Weights (adaptive filter coefficients)*/
.bss _W_1,N,0,0
.bss _W_2,N,0,0
;static signed int error; /* Difference between desired and actual. */
.bss _error_1,1,0,0
.bss _error_2,1,0,0

  .bss  _desired,1,0,0

; Functions callable from C code
.sect ".text"
.global _adaptive_filters_asm_initialise
.global _adaptive_filter_asm_1
  .global _adaptive_filter_asm_2

;void adaptive_filter_initialize(void)
;{
_adaptive_filters_asm_initialise:
; unsigned int i;
PSHM       AR3            ; Keep original value of AR3 - push into stack
; for ( i = 0 ; i < N ; i++)
;   {
LD         #0, A          ; Clear accumulator A
;      w[i] = 0;
STM        #_W_1, AR3
RPT        #(N-2)
STL        A, *AR3+       ; Fill Weights with zeroes

STM        #_W_2, AR3
RPT        #(N-2)
STL        A, *AR3+       ; Fill Weights with zeroes
;      x[i] = 0;
STM        #_X_1, AR3
RPTZ       A, #(N-2)      ; Fill 0 to N-1. Less one for repeats
STL        A, *AR3+       ; Fill X array with zeroes

STM        #_X_2, AR3
RPTZ       A, #(N-2)      ; Fill 0 to N-1. Less one for repeats
STL        A, *AR3+       ; Fill X array with zeroes
;   }
; error = 0;
    STL        A, *(_error_1) ; Clear errors to zero
    STL        A, *(_error_2)
;}
POPM       AR3            ; Restore original value of AR3
FRET                 ; Far return
; 1st parameter in accumulator = desired (or reference) waveform.
; 2nd parameter on stack = input signal (unknown) to be compared with the reference.

; Desired is copied from accumulator A to stack frame.

; Returns Y in AH, error in AL
; RETURNS: Filter output y in high word. Error in low word

;signed int i;
;signed long y;            /* Output of adaptive filter */
;signed long difference;

;signed long adaptive_filter( signed int desired, signed int latest_measurement)
;{
_adaptive_filter_asm_1:
    PSHM       ST0                ; Keep original values of flags
    PSHM       ST1
    PSHM       AR3                ; Keep original values of auxiliary registers
    PSHM       AR4
    FRAME      #-3                ; Setup stack frame
    SSBX       SXM                ; Turn on sign-extension mode
    SSBX       FRCT               ; Extra shift for multiplications
    SSBX       OVM                ; Prevent overflow
    LD         #0, ASM            ; Required for correct behaviour of ST || MPY
                                  ; nulovy bitovy posun
    STL        A, *(_desired)     ; Copy desired from Accumulatr A to local storage
    STM        #(_X_1 + N-2), AR3 ; AR3 points to X[N-2]
;  /* Shuffle first and update */
;  for ( i = N-1 ; i > 0 ; i--)
;    {
;      x[i] = x[i-1];
;    }
    RPT        #(N-1-1)
    DELAY      *AR3-              ; Shuffle values along one place
    MVDK       *SP(INPUT), *(_X_1); Copy in latest value to X[0]
    LD         *(_error_1), T     ; T = error
    MPY        #BETA, A           ; A = error * BETA
    SFTA       A, -16, A          ; Move AH to AL
    STLM       A, T               ; T = error * BETA
; Setup pointers to X[0] and W[0]
    STM        #(_X_1), AR3       ; AR3 points to X[0]
    STM        #(_W_1), AR4       ; AR4 points to W[0]
    SUB        B, B               ; Clear accumulator B
    MPY        *AR3, A            ; A = error * BETA * X[]
; Perform Least Mean Squares calculations.
    STM        #(N-1), BRC        ; Set number of block repeats
    RPTB       end_lms_1 - 1      ; Repeat instructions up to end_lms
    LMS        *AR4, *AR3+        ; A  = W[] + error * BETA * X[]
                                  ; B += W[] * X[]

    ST         A, *AR4+           ; Update W[]. Point to next W[]
    ||         MPY *AR3, A        ; Last multiply is ignored

end_lms_1:
    ; Build up return value of output of filter Y in AH and error in AL
    LD         B, -16, A          ; BH = Y. Copy Y to AL
    NEG        A                  ; A = (-Y)
    ADD        *(_desired), A     ; AL = Desired + (-Y) = error
    STL        A, *(_error_1)     ; Save error for next time through
    PSHM       BH                 ; Copy Y in BH to AH
    POPM       AH                 ; Combined return value. AH = Y. AL = error
    FRAME      #3                 ; Restore stack frame
    POPM       AR4                ; Restore auxiliary registers
    POPM       AR3
    POPM       ST1                ; Restore FRCT, SXM, OVM, C
    POPM       ST0

    FRET                          ; Far return

Použitá literatura

[1] Davídek, V., Sovka, P.: Číslicové zpracování signálů a implementace, Vydavatelství ČVUT, 2002

[2] Výukové CD „C5000 Teaching ROM“, Texas Instruments, 2003

[3] TMS320C54x DSP – Reference Set – Volume 2: Mnemonic Instruction Set, Texas Instruments, 2001 (SPRU172C.pdf)

 
{e_like}
 
 
Nahoru