Toon posts:

[vb.net] fourier transformaties

Pagina: 1
Acties:

Verwijderd

Topicstarter
Ik heb het volgende probleem, ik gebruik vb.net en DirectX9.

Met behulp van DirectX9 capture ik samples van de geluidskaart die ik wil weergeven in 8 frequentiebereiken op een form.

Sampling rate is 11025 Hz

zodra ik mijn buffer leeg heb ik ongeveer 2700 samples, met behulp van fourier transform ik deze naar het frequentiedomein en ga ook terug naar een array met maar 128 samples. Enig probleem, na de transformatie als ik van de items uit mijn array probeer weer te geven krijg ik als waarde NaN. Ik heb geen idee wat ik hiermee kan, heb net MSDN doorgezocht en heb weinig gevonden.

Iemand een idee wat ik fout doe? Als het nodig is kan ik wat functies plakken, maar het geheel is nogal lang.

Alvast bedankt,
Martin

  • Stubby
  • Registratie: Januari 2002
  • Laatst online: 22:14
Lijkt mij op een fout in de fourier transformatie, ik ben bang dat je toch ff code moet gaan pasten, want NaN is meestal het resultaat van een ongeldige berekening...

Verwijderd

Topicstarter
ok, hier dan

Visual Basic:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
    Public Sub FourierTransform(ByVal NumSamples As Integer, ByRef RealIn() As Double, ByRef RealOut() As Double, ByVal DoInverse As Boolean)

        Static NumBits As Byte
        Static i As Long, j As Long, k As Long, n As Long
        Static BlockSize As Long, BlockEnd As Long

        Static DeltaAngle As Double, DeltaAr As Double
        Static Alpha As Double, Beta As Double
        Static TR As Double, TI As Double, AR As Double, AI As Double
        Static ImagOut() As Double
        ReDim ImagOut(NumSamples - 1)

        Static rBits() As Long
        Static LastNumSamples
        Static nIter As Long

        If LastNumSamples <> NumSamples Then
            LastNumSamples = NumSamples
            ReDim rBits(NumSamples - 1)
            NumBits = NumberOfBitsNeeded(NumSamples)
            For i = 0 To NumSamples - 1
                rBits(i) = ReverseBits(i, NumBits)
                RealOut(rBits(i)) = CSng(RealIn(i))
                ImagOut(rBits(i)) = 0
            Next i
        Else
            For i = 0 To NumSamples - 1
                RealOut(rBits(i)) = CSng(RealIn(i))
                ImagOut(rBits(i)) = 0
            Next i
        End If

        BlockEnd = 1
        BlockSize = 2

        Do
            DeltaAngle = PI2 / BlockSize * IIf(DoInverse, -1.0#, 1.0#)
            Alpha = 2.0# * Math.Sin(0.5 * DeltaAngle) ^ 2
            Beta = Math.Sin(DeltaAngle)

            For i = 0 To NumSamples - 1 Step BlockSize
                AR = 1.0#
                AI = 0.0#

                j = i
                For n = 0 To BlockEnd - 1
                    k = j + BlockEnd
                    TR = AR * RealOut(k) - AI * ImagOut(k)
                    TI = AI * RealOut(k) + AR * ImagOut(k)
                    RealOut(k) = RealOut(j) - TR
                    ImagOut(k) = ImagOut(j) - TI
                    RealOut(j) = RealOut(j) + TR
                    ImagOut(j) = ImagOut(j) + TI
                    DeltaAr = Alpha * AR + Beta * AI
                    AI = AI - (Alpha * AI - Beta * AR)
                    AR = AR - DeltaAr
                    j = j + 1
                Next n
            Next i

            BlockEnd = BlockSize
            BlockSize = BlockSize * 2
        Loop While BlockSize <= NumSamples

    End Sub


dit is trouwens een algemene functie van fourrier. heb er zelf niks aan gewijzigt. ik zie net dat ik het imationaire (sp?) getallen array er uit moet halen aangezien ik deze toch niet gebruikt.

[ Voor 6% gewijzigd door Verwijderd op 20-03-2003 18:38 ]


Verwijderd

Topicstarter
ok zie ik net dat mijn ingaande array maar tot en met item 380 ergens maar informatie bevat, de rest is gewoon 0, niet dat dit terzake doet aangezien ik toch maar de eerste 128 gebruik, maar toch een beetje vreemd.

in RealOut() zie ik vooral -1#.IND en -1#.INF waarden als ik in de variable explorer kijk... ik heb geen idee wat er fout gaat.

[ Voor 24% gewijzigd door Verwijderd op 20-03-2003 19:05 ]


Verwijderd

Topicstarter
ok het probleem is verholpen, begon dat ik de buffer uitlas met een double, en had een byte moeten zijn, daarna heb ik realout herschreven naar een integer en alles werkt prima, ook heb ik de CSng() er uit genomen aangezien deze meer problemen gaf dan goede dingen, hij is nu niet zo super precies, maar dat hoeft ook eigenlijk niet.

Verwijderd

Topicstarter
ok gaan we hier verder,

Nu ik eindelijk mijn fourier goed werkend heb (denk ik, ik krijg geen errors meer) heb ik het volgende: Een array die uit de fourier functie komt (256 items)

Hoe verdeel ik deze nu goed over een een aantal frequentie banden. De array bevat zowel positieve als negatieve waarden. Wat ik nu heb gedaan is 8 frequentie bereiken genomen dus index 0 tot en met 31 is de lage frequentie band, enz.. voor alle andere banden. Deze uitgezet in een grafiekie, maar komt niet echt overeen met de equalizer van winamp.

Iemand een idee hoe ik dit moet doen? Heb iets gelezen over een gain, en vermenigvuldigen met de max frequentie etc.. maar zit nu een beetje vast. Als ik alles goed heb werken wil ik alle libs wel open source stellen.

Martin

  • Adion
  • Registratie: Januari 2001
  • Laatst online: 23:21
Die 256 items zijn zowel je reeele als imaginaire deel?
Wat je eerst kan doen is een array maken, waar je telkens het kwadraat van het imaginaire deel optelt bij het kwadraat van het reeele deel.
Als dit enkel de reeele waardes zijn, gaat het denk ik ook wel werken als je gewoon de absolute waarde neemt om van de negatieve getallen af te raken.

Als je het wil verdelen in frequentiebanden, moet je dat logaritmisch doen.
Dus voor je eerste band neem je bijvoorbeeld slechts 1 waarde uit de array, voor de tweede de som van de 2 volgende, voor de de band de 4 volgende, ....

VirtualDJ 2026 - Fast Image Resizer - Instagram


  • djazete
  • Registratie: Juli 1999
  • Laatst online: 07-02-2020

djazete

steel

het imaginaire deel kan je denk ik sowieso wel laten liggen (als je het al gebruikt) omdat dat de phase informatie is volgens mij
Waarom het er raar uitziet kan idd wel zoals Adion hierboven zegt te maken hebben met je schaalverdeling, 1 oktaaf omhoog is een verdubbeling van de frequentie, dus precies zoals hierboven beschreven wordt.

De informatie die nu in je reele deel staan van je FFT array, zijn dus per frequentieband (het is immers al verdeeld in 256 banden?) de gemiddelde gain. Weet je ook op welke schaal dat is (waarschijnlijk dBFS)
Als je dus het aantal banden weer opdeeld moet je dus ook rekening houden dat je die gain weer goed uitrekent maar dat lijkt me voor de hand liggend
dr zit trouwens ook geen window over je data heen, dus dat geeft zowieso al wel een andere output dan de gemiddelde fourier transformatie denk ik
..
dat waren even mijn gedachten hierover.. succes!

Verwijderd

Topicstarter
Ik heb nu een BlackManWindow gebruikt, heb ook iets andere source met een reele en imagionair gedeelte. Dus de wortel uit het kwadraat van de reele en imagionaire getallen, dan ben ik nu alleen nog een beetje de weg kwijt hoe ik de frequenties moet uitzetten.

  • Adion
  • Registratie: Januari 2001
  • Laatst online: 23:21
Wat je dus moet doen als je het spectrum in banden wil weergeven is die banden eerst berekenen.
Je hebt dus nu 256 waardes, als je die gewoon tekent zou je normaal een tekening moeten krijgen waar de eerste waarden altijd veel groter zijn dan de laatste waarden.
Indien je die wilt verdelen in banden moet je eigenlijk gewoon wat optellingen maken.
Je tekent de waarde 1 voor band 1, waarde 2 + waarde 3 voor band 2, waarde 4 + waarde 5 + waarde 6 voor band 3, ...

De volgende code gebruik ik zelf (voor vb6). De code is wel bedoeld voor fft's op 1024 samples, dus 512 waardes.
Als je ze wil toepassen op 256 waardes zal je de waardes d moeten delen door 2 om geen index out of bounds error te krijgen.

De reden dat al die waardes voor d er zo los instaan is omdat ik deze waardes op een ander forum gevonden heb, en dat deze waardes ook door winamp zouden gebruikt worden.

Visual Basic:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
Public Function vis_GetSpectrumCount(ByVal a As Long, Optional ByVal Width = 32)
Dim d As Long
If Width = 32 Then
    If a = 0 Then d = 1
    If a = 1 Then d = 1
    If a = 2 Then d = 1
    If a = 3 Then d = 1
    If a = 4 Then d = 1
    If a = 5 Then d = 2
    If a = 6 Then d = 2
    If a = 7 Then d = 2
    If a = 8 Then d = 2
    If a = 9 Then d = 3
    If a = 10 Then d = 3
    If a = 11 Then d = 4
    If a = 12 Then d = 4
    If a = 13 Then d = 5
    If a = 14 Then d = 5
    If a = 15 Then d = 6
    If a = 16 Then d = 6
    If a = 17 Then d = 8
    If a = 18 Then d = 8
    If a = 19 Then d = 9
    If a = 20 Then d = 10
    If a = 21 Then d = 11
    If a = 22 Then d = 12
    If a = 23 Then d = 14
    If a = 24 Then d = 15
    If a = 25 Then d = 17
    If a = 26 Then d = 18
    If a = 27 Then d = 20
    If a = 28 Then d = 21
    If a = 29 Then d = 24
    If a = 30 Then d = 25
    If a = 31 Then d = 27
ElseIf Width = 16 Then
    If a = 0 Then d = 2
    If a = 1 Then d = 2
    If a = 2 Then d = 3
    If a = 3 Then d = 4
    If a = 4 Then d = 5
    If a = 5 Then d = 7
    If a = 6 Then d = 9
    If a = 7 Then d = 11
    If a = 8 Then d = 14
    If a = 9 Then d = 17
    If a = 10 Then d = 21
    If a = 11 Then d = 30 '26
    If a = 12 Then d = 35 '32
    If a = 13 Then d = 40 '38
    If a = 14 Then d = 50 '45
    If a = 15 Then d = 60 '52
End If
    vis_GetSpectrumCount = d
End Function

Public Sub vis_SimplifySpectrum(ByRef src() As Single, ByRef dst() As Single)
Dim up As Long, a As Long, b As Long, d As Long, c As Long, cnt As Double, wdth As Long
Static max16(15) As Double
Static max32(31) As Double

Dim tmpDbl As Double
up = UBound(dst)
If up >= 31 Then
    wdth = 32
ElseIf up >= 15 Then
    wdth = 16
Else
    Exit Sub
End If

For a = 0 To UBound(dst)
    d = vis_GetSpectrumCount(a, wdth)

    cnt = 0
    For c = 1 To d
        If UBound(src) >= b Then
            cnt = cnt + src(b)
        End If
        b = b + 1
    Next
    
    If cnt > 0 Then
        If cnt > 0 Then tmpDbl = sqr(cnt)
        If wdth = 16 Then
            If tmpDbl > max16(a) Then max16(a) = tmpDbl
            If max16(a) > 0 Then
                tmpDbl = tmpDbl / max16(a)
            Else
                tmpDbl = 0
            End If
        ElseIf wdth = 32 Then
            If tmpDbl > max32(a) Then max32(a) = tmpDbl
            If max32(a) > 0 Then
                tmpDbl = tmpDbl / max32(a)
            Else
                tmpDbl = 0
            End If
        End If
    Else
        tmpDbl = 0
    End If
    dst(a) = tmpDbl
Next

End Sub


Edit: Deze code normalizeert de waardes ook. Dit geeft vind ik persoonlijk het mooiste resultaat, maar als je originele fft functie goed was zou het ook zonder normalizatie goed moeten gaan, en dan ook een juister resultaat geven.

[ Voor 5% gewijzigd door Adion op 26-03-2003 17:14 ]

VirtualDJ 2026 - Fast Image Resizer - Instagram

Pagina: 1