Questo sito utilizza cookies solo per scopi di autenticazione sul sito e nient'altro. Nessuna informazione personale viene tracciata. Leggi l'informativa sui cookies.
Username: Password: oppure
WaveProof - WaveDataManager.vb

WaveDataManager.vb

Caricato da: Totem
Scarica il programma completo

  1. Imports System.Math
  2.  
  3. ''' <summary>
  4. ''' Classe che gestisce i dati audio ottenuti dal Wave Reader, e si preoccupa di comprimerli
  5. ''' e visualizzarli.
  6. ''' </summary>
  7. Public Class WaveDataManager
  8.     Private _AudioData() As Int16
  9.     Private _MinimumAplitude, _MaximumAmplitude As Int16
  10.     Private _ByteRate As Int64
  11.     Private _Visualizer As IVisualizer
  12.     Private DefaultVisualizer As WaveFormVisualizer
  13.  
  14.     ''' <summary>
  15.     ''' I dati audio estratti dal file, espressi come array di Int16. Ogni valore rappresenta
  16.     ''' l'ampiezza dell'onda in un dato punto.
  17.     ''' </summary>
  18.     Public ReadOnly Property AudioData() As Int16()
  19.         Get
  20.             Return _AudioData
  21.         End Get
  22.     End Property
  23.  
  24.     ''' <summary>
  25.     ''' Minima ampiezza assunta dall'onda in tutto il brano.
  26.     ''' </summary>
  27.     Public ReadOnly Property MinimumAmplitude() As Int16
  28.         Get
  29.             Return _MinimumAplitude
  30.         End Get
  31.     End Property
  32.  
  33.     ''' <summary>
  34.     ''' Massima ampiezza assunta dall'onda in tutto il brano.
  35.     ''' </summary>
  36.     Public ReadOnly Property MaximumAmplitude() As Int16
  37.         Get
  38.             Return _MaximumAmplitude
  39.         End Get
  40.     End Property
  41.  
  42.     ''' <summary>
  43.     ''' Numero di byte letti per secondo.
  44.     ''' </summary>
  45.     Public ReadOnly Property ByteRate() As Int64
  46.         Get
  47.             Return _ByteRate
  48.         End Get
  49.     End Property
  50.  
  51.     ''' <summary>
  52.     ''' Rappresenta il Visualizer che questo oggetto userà per disegnare la visualizzazione
  53.     ''' dell'onda.
  54.     ''' </summary>
  55.     Public Property Visualizer() As IVisualizer
  56.         Get
  57.             If _Visualizer Is Nothing Then
  58.                 _Visualizer = Me.DefaultVisualizer
  59.             End If
  60.             Return _Visualizer
  61.         End Get
  62.         Set(ByVal value As IVisualizer)
  63.             _Visualizer = value
  64.         End Set
  65.     End Property
  66.  
  67.     Sub New(ByVal Data() As Int16, ByVal ByteRate As Int64)
  68.         _AudioData = Data
  69.         Me._ByteRate = ByteRate
  70.  
  71.         _MinimumAplitude = Short.MaxValue
  72.         _MaximumAmplitude = Short.MinValue
  73.         For I As Int64 = 0 To Me.AudioData.Length - 1
  74.             If Me.AudioData(I) < Me.MinimumAmplitude Then
  75.                 _MinimumAplitude = Me.AudioData(I)
  76.             End If
  77.             If Me.AudioData(I) > Me.MaximumAmplitude Then
  78.                 _MaximumAmplitude = Me.AudioData(I)
  79.             End If
  80.         Next
  81.  
  82.         DefaultVisualizer = New WaveFormVisualizer(Me)
  83.         Me.Visualizer = DefaultVisualizer
  84.     End Sub
  85.  
  86.     ''' <summary>
  87.     ''' Comprime i dati audio estratti dal file wave, effettuando una scernita di valori da tenere,
  88.     ''' e altri da scartare. Infatti, se il refresh viene fatto ogni 10ms, è inutile tenere in memoria,
  89.     ''' inutilizzati, tutti i dati che corrispondono a intervalli minori di 10ms. L'algoritmo è
  90.     ''' indubbiamente data-lossy, ma dato che serve solo per la visualizzazione, non è rilevante
  91.     ''' scartare valori che non possiamo emmeno percepire.
  92.     ''' </summary>
  93.     ''' <param name="RefreshTime">Tempo di refresh dell'immagine (in secondi).</param>
  94.     Public Sub CompressData(ByVal RefreshTime As Double)
  95.         Dim ShortPerInterval As Int64 = RefreshTime * _ByteRate / 2
  96.         Dim TempData() As Int16
  97.  
  98.         ReDim TempData(Math.Ceiling(Me.AudioData.Length / ShortPerInterval) - 1)
  99.  
  100.         For I As Int64 = 0 To Me.AudioData.Length - 1 Step ShortPerInterval
  101.             TempData(I / ShortPerInterval) = Me.AudioData(I)
  102.         Next
  103.  
  104.         Me._ByteRate /= ShortPerInterval
  105.         _AudioData = TempData
  106.         TempData = Nothing
  107.     End Sub
  108.  
  109.     ''' <summary>
  110.     ''' Ottiene una bitmap che rappresenta la forma d'onda di una parte o di tutto il file.
  111.     ''' </summary>
  112.     ''' <param name="Width">Larghezza dell'immagine da generare.</param>
  113.     ''' <param name="Height">Altezza dell'immagine da generare.</param>
  114.     ''' <param name="From">Punto da cui far iniziare il rendering: è espresso in numero di secondi
  115.     ''' trascorsi dall'inizio del brano.</param>
  116.     ''' <param name="To">Punto in cui terminare il rendering: è espresso in numero di secondi
  117.     ''' trascorsi dall'inizio del brano.</param>
  118.     ''' <returns>Restituisce una bitmap delle dimensioni specificate, con i colori impostati
  119.     ''' nel Visualizer. Se Width e Height sono 0, restituisce Nothing.</returns>
  120.     Public Function GetWaveForm(ByVal Width As Int16, ByVal Height As Int16, Optional ByVal [From] As Double = -1, Optional ByVal [To] As Double = -1) As Bitmap
  121.         Return DefaultVisualizer.Render(Width, Height, [From], [To])
  122.     End Function
  123.  
  124.     ''' <summary>
  125.     ''' Imposta come Visualizer quello di default.
  126.     ''' </summary>
  127.     Public Sub SetDefaultVisualizer()
  128.         Me.Visualizer = DefaultVisualizer
  129.     End Sub
  130. End Class
  131.  
  132. ''' <summary>
  133. ''' Definisce i metodi e le proprietà standard di un Visualizer, ossia di un oggetto che, ricevendo
  134. ''' in input i dati audio passatagli dal WaveDataManager, crea un'immagine che rappresenti le
  135. ''' caratteristiche della forma d'onda.
  136. ''' </summary>
  137. ''' <remarks>Ogni Visualizer deve implementare l'interfaccia IVisualizer.</remarks>
  138. Public Interface IVisualizer
  139.     ''' <summary>
  140.     ''' Crea e restituisce l'immagine creata dal Visualizer.
  141.     ''' </summary>
  142.     ''' <param name="Width">Larghezza dell'immagine da generare.</param>
  143.     ''' <param name="Height">Altezza dell'immagine da generare.</param>
  144.     ''' <param name="From">Punto da cui far iniziare il rendering: è espresso in numero di secondi
  145.     ''' trascorsi dall'inizio del brano.</param>
  146.     ''' <param name="To">Punto in cui terminare il rendering: è espresso in numero di secondi
  147.     ''' trascorsi dall'inizio del brano.</param>
  148.     ''' <returns>Restituisce una bitmap delle dimensioni specificate, con i colori impostati
  149.     ''' nel Visualizer. Se Width e Height sono 0, restituisce Nothing.</returns>
  150.     Function Render(ByVal Width As Int16, ByVal Height As Int16, Optional ByVal [From] As Double = -1, Optional ByVal [To] As Double = -1) As Bitmap
  151.     ''' <summary>
  152.     ''' Indica il WaveDataManager associato a questo Visualizer.
  153.     ''' </summary>
  154.     ''' <remarks>Questa proprietà è necessaria per evitare di ripassare tutti i dati alla funzione
  155.     ''' di rendering ad ogni refresh, il che comporterebbe un enorme spreco di memoria.</remarks>
  156.     ReadOnly Property AssociatedManager() As WaveDataManager
  157.     ''' <summary>
  158.     ''' Colore usato per lo sfondo.
  159.     ''' </summary>
  160.     Property BackgroundColor() As Color
  161.     ''' <summary>
  162.     ''' Colore usato per la visualizzazione.
  163.     ''' </summary>
  164.     Property ForeColor() As Color
  165. End Interface
  166.  
  167. ''' <summary>
  168. ''' Visualzer standard: disegna la forma d'onda associata ai dati di input.
  169. ''' </summary>
  170. ''' <remarks></remarks>
  171. Public Class WaveFormVisualizer
  172.     Implements IVisualizer
  173.  
  174.     Private _AssociatedManager As WaveDataManager
  175.     Private _WaveColor As Color
  176.     Private _BackgroundColor As Color
  177.  
  178.     Public ReadOnly Property AssociatedManager() As WaveDataManager Implements IVisualizer.AssociatedManager
  179.         Get
  180.             Return _AssociatedManager
  181.         End Get
  182.     End Property
  183.  
  184.     Public Property WaveColor() As Color Implements IVisualizer.ForeColor
  185.         Get
  186.             Return _WaveColor
  187.         End Get
  188.         Set(ByVal value As Color)
  189.             _WaveColor = value
  190.         End Set
  191.     End Property
  192.  
  193.     Public Property BackgroundColor() As Color Implements IVisualizer.BackgroundColor
  194.         Get
  195.             Return _BackgroundColor
  196.         End Get
  197.         Set(ByVal value As Color)
  198.             _BackgroundColor = value
  199.         End Set
  200.     End Property
  201.  
  202.     Public Function Render(ByVal Width As Short, ByVal Height As Short, Optional ByVal From As Double = -1.0, Optional ByVal [To] As Double = -1.0) As System.Drawing.Bitmap Implements IVisualizer.Render
  203.         If Width = 0 Or Height = 0 Then
  204.             Return Nothing
  205.         End If
  206.  
  207.         Dim B As New Bitmap(Width, Height)
  208.         Dim G As Graphics = Graphics.FromImage(B)
  209.         Dim Step1 As Single
  210.         Dim Step2 As Single
  211.         Dim Step3 As Single
  212.         Dim Points As New List(Of PointF)
  213.         Dim IndexFrom, IndexTo As Int64
  214.  
  215.         With Me.AssociatedManager
  216.             'Calcola l'indice di partenza e quello di arrivo sulla base dei limiti di tempo
  217.             'forniti con i parametri From e To
  218.             If [From] > -1 And [To] > -1 And [To] > [From] Then
  219.                 IndexFrom = (.AudioData.Length * ([From] / (.AudioData.Length * 2 / .ByteRate)))
  220.                 IndexTo = (.AudioData.Length * ([To] / (.AudioData.Length * 2 / .ByteRate)))
  221.             ElseIf [From] <= -1 And [To] > -1 Then
  222.                 IndexFrom = 0
  223.                 IndexTo = (.AudioData.Length * ([To] / (.AudioData.Length * 2 / .ByteRate)))
  224.             Else
  225.                 IndexFrom = 0
  226.                 IndexTo = .AudioData.Length - 1
  227.             End If
  228.  
  229.             For I As Int64 = IndexFrom To IndexTo
  230.                 If I < 0 Then
  231.                     Points.Add(New PointF(CSng(Width * (I - IndexFrom) / (IndexTo - IndexFrom)), 0))
  232.                     Continue For
  233.                 End If
  234.  
  235.                 If I > .AudioData.Length - 1 Then
  236.                     Exit For
  237.                 End If
  238.  
  239.                 'Assegna all'ordinata di questo punto un valora tanto maggiore quando più
  240.                 'l'ampiezza è grande. Step1 è sempre comprso tra 0 e 1, e indica il rapporto
  241.                 'tra l'ampiezza dell'onda in questo punto e la massima oscillazione avuta
  242.                 'nell'onda stessa
  243.                 Step1 = .AudioData(I) / (CSng(.MaximumAmplitude) - CSng(.MinimumAmplitude))
  244.                 'Step2 assume un valore che indica il valore reale dell'altezza dell'onda
  245.                 Step2 = Step1 * Height
  246.                 'Step3 è l'ordinata del punto
  247.                 Step3 = Step2 + Height / 2
  248.                 Points.Add(New PointF(CSng(Width * (I - IndexFrom) / (IndexTo - IndexFrom)), Step3))
  249.             Next
  250.         End With
  251.  
  252.         G.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias
  253.         G.Clear(Me.BackgroundColor)
  254.         G.DrawCurve(New Pen(Me.WaveColor), Points.ToArray)
  255.  
  256.         Return B
  257.     End Function
  258.  
  259.     Sub New(ByVal Manager As WaveDataManager)
  260.         _AssociatedManager = Manager
  261.         _WaveColor = Color.Black
  262.         _BackgroundColor = Color.White
  263.     End Sub
  264. End Class
  265.  
  266. ''' <summary>
  267. ''' Visualizer a rettangoli: disegna due rettangoli la cui lunghezza è proporzionale al valore di picco
  268. ''' raggiunto dall'onda al termina dell'intervallo considerato.
  269. ''' </summary>
  270. Public Class RectangleVisualizer
  271.     Implements IVisualizer
  272.  
  273.     Private _AssociatedManager As WaveDataManager
  274.     Private PreviousImage As Bitmap
  275.     Private _WaveColor As Color
  276.     Private _BackgroundColor As Color
  277.  
  278.     Public ReadOnly Property AssociatedManager() As WaveDataManager Implements IVisualizer.AssociatedManager
  279.         Get
  280.             Return _AssociatedManager
  281.         End Get
  282.     End Property
  283.  
  284.     Public Property RectangleColor() As Color Implements IVisualizer.ForeColor
  285.         Get
  286.             Return _WaveColor
  287.         End Get
  288.         Set(ByVal value As Color)
  289.             _WaveColor = value
  290.         End Set
  291.     End Property
  292.  
  293.     Public Property BackgroundColor() As Color Implements IVisualizer.BackgroundColor
  294.         Get
  295.             Return _BackgroundColor
  296.         End Get
  297.         Set(ByVal value As Color)
  298.             _BackgroundColor = value
  299.         End Set
  300.     End Property
  301.  
  302.     Public Function Render(ByVal Width As Short, ByVal Height As Short, Optional ByVal From As Double = -1.0, Optional ByVal [To] As Double = -1.0) As System.Drawing.Bitmap Implements IVisualizer.Render
  303.         Dim B As New Bitmap(Width, Height)
  304.         Dim G As Graphics = Graphics.FromImage(B)
  305.         Dim IndexTo As Int64
  306.         Dim Length As Single = 1
  307.  
  308.         With Me.AssociatedManager
  309.             IndexTo = (.AudioData.Length * ([To] / (.AudioData.Length * 2 / .ByteRate)))
  310.             If IndexTo <= 0 Then
  311.                 IndexTo = 1
  312.             End If
  313.             If IndexTo > .AudioData.Length - 2 Then
  314.                 IndexTo = .AudioData.Length - 2
  315.             End If
  316.  
  317.             'Se questo è un punto di minimo o di massimo, aggiorna l'immagine, altrimenti
  318.             'usa quella vecchia
  319.             If Abs(.AudioData(IndexTo)) > Abs(.AudioData(IndexTo - 1)) And Abs(.AudioData(IndexTo)) > Abs(.AudioData(IndexTo + 1)) Then
  320.                 Length = Abs(.AudioData(IndexTo) / (CSng(.MaximumAmplitude) - CSng(.MinimumAmplitude))) * Width
  321.  
  322.                 G.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias
  323.                 G.Clear(Me.BackgroundColor)
  324.                 G.FillRectangle(New SolidBrush(Me.RectangleColor), New Rectangle(Width / 2 - Length, Height / 2 - 20, Length, 40))
  325.                 G.FillRectangle(New SolidBrush(Me.RectangleColor), New Rectangle(Width / 2, Height / 2 - 20, Length, 40))
  326.  
  327.                 PreviousImage = B
  328.             Else
  329.                 B = PreviousImage
  330.             End If
  331.         End With
  332.  
  333.         Return B
  334.     End Function
  335.  
  336.     Sub New(ByVal Manager As WaveDataManager)
  337.         _AssociatedManager = Manager
  338.     End Sub
  339. End Class
  340.  
  341. ''' <summary>
  342. ''' Visualizer a fasce: disegna un certo numero di fasce di colore (pari a Width/5). Ogni fascia è
  343. ''' più chiara (ossia più vicino al colore di sfondo) se l'onda, in quell'intervallo, assume valori
  344. ''' di ampiezza bassi; è invece più scura (ossia più vicino al colore di visualizzazione) se l'onda,
  345. ''' in quell'intervallo, assume valori di ampiezza alti.
  346. ''' </summary>
  347. Public Class LoudnessVisualizer
  348.     Implements IVisualizer
  349.  
  350.     Private _AssociatedManager As WaveDataManager
  351.     Private PreviousImage As Bitmap
  352.     Private _WaveColor As Color
  353.     Private _BackgroundColor As Color
  354.  
  355.     Public ReadOnly Property AssociatedManager() As WaveDataManager Implements IVisualizer.AssociatedManager
  356.         Get
  357.             Return _AssociatedManager
  358.         End Get
  359.     End Property
  360.  
  361.     Public Property ForeColor() As Color Implements IVisualizer.ForeColor
  362.         Get
  363.             Return _WaveColor
  364.         End Get
  365.         Set(ByVal value As Color)
  366.             _WaveColor = value
  367.         End Set
  368.     End Property
  369.  
  370.     Public Property BackgroundColor() As Color Implements IVisualizer.BackgroundColor
  371.         Get
  372.             Return _BackgroundColor
  373.         End Get
  374.         Set(ByVal value As Color)
  375.             _BackgroundColor = value
  376.         End Set
  377.     End Property
  378.  
  379.     Public Function Render(ByVal Width As Short, ByVal Height As Short, Optional ByVal From As Double = -1.0, Optional ByVal [To] As Double = -1.0) As System.Drawing.Bitmap Implements IVisualizer.Render
  380.         Dim B As New Bitmap(Width, Height)
  381.         Dim G As Graphics = Graphics.FromImage(B)
  382.         Dim IndexTo, IndexFrom As Int64
  383.         Dim Length As Single = 1
  384.         Dim Brush As New Drawing2D.LinearGradientBrush(New Point(0, 0), New Point(Width, 0), Me.ForeColor, Me.BackgroundColor)
  385.         Dim SplitPoints As New List(Of Single)
  386.         Dim Colors As New List(Of Color)
  387.  
  388.         With Me.AssociatedManager
  389.             If [From] > -1 And [To] > -1 And [To] > [From] Then
  390.                 IndexFrom = (.AudioData.Length * ([From] / (.AudioData.Length * 2 / .ByteRate)))
  391.                 IndexTo = (.AudioData.Length * ([To] / (.AudioData.Length * 2 / .ByteRate)))
  392.             ElseIf [From] <= -1 And [To] > -1 Then
  393.                 IndexFrom = 0
  394.                 IndexTo = (.AudioData.Length * ([To] / (.AudioData.Length * 2 / .ByteRate)))
  395.             Else
  396.                 IndexFrom = 0
  397.                 IndexTo = .AudioData.Length - 1
  398.             End If
  399.  
  400.             Dim NewIndexFrom As Int32 = (IndexFrom \ 5) * 5
  401.             Dim NewIndexTo As Int64 = IndexFrom + ((IndexTo - IndexFrom) \ 5 + 1) * 5
  402.             For I As Int64 = NewIndexFrom To NewIndexTo Step 5
  403.                 If I < 0 Then
  404.                     SplitPoints.Add(0)
  405.                     Colors.Add(Color.FromArgb(0, Me.ForeColor))
  406.                     Continue For
  407.                 End If
  408.  
  409.                 If I > .AudioData.Length - 1 Or I + 4 > .AudioData.Length - 1 Then
  410.                     Exit For
  411.                 End If
  412.  
  413.                 'Calcola la massima oscillazione nei 5 punti successivi a questo
  414.                 Dim Max, Min As Int16
  415.                 Dim Percent As Single
  416.                 Max = Int16.MinValue
  417.                 Min = Int16.MaxValue
  418.                 For J As Int64 = I To (I + 4)
  419.                     If .AudioData(J) > Max Then
  420.                         Max = .AudioData(J)
  421.                     End If
  422.                     If .AudioData(J) < Min Then
  423.                         Min = .AudioData(J)
  424.                     End If
  425.                 Next
  426.                 'Ottiene un valore percentuale tra 0 e 1, confrontandolo con la
  427.                 'massima oscillazione di tutta l'onda
  428.                 Percent = (CSng(Max) - CSng(Min)) / (CSng(.MaximumAmplitude) - CSng(.MinimumAmplitude))
  429.  
  430.                 'Aggiunge un punto di sfumatura, il cui colore è tanto più trasparente
  431.                 'quando più debole è l'oscillazione compiuta
  432.                 SplitPoints.Add((I - NewIndexFrom) / (NewIndexTo - NewIndexFrom))
  433.                 Colors.Add(Color.FromArgb(255 * Percent, Me.ForeColor))
  434.             Next
  435.  
  436.             G.Clear(Me.BackgroundColor)
  437.             Dim Blend As New Drawing2D.ColorBlend(Colors.Count)
  438.             If SplitPoints(0) <> 0 Then
  439.                 SplitPoints.Insert(0, 0)
  440.                 Colors.Insert(0, Color.FromArgb(0, Me.ForeColor))
  441.             End If
  442.             If SplitPoints(SplitPoints.Count - 1) <> 1 Then
  443.                 SplitPoints.Add(1)
  444.                 Colors.Add(Color.FromArgb(0, Me.ForeColor))
  445.             End If
  446.             Blend.Colors = Colors.ToArray()
  447.             Blend.Positions = SplitPoints.ToArray()
  448.             Brush.InterpolationColors = Blend
  449.             G.FillRectangle(Brush, New Rectangle(0, 0, Width, Height))
  450.         End With
  451.  
  452.         Return B
  453.     End Function
  454.  
  455.     Sub New(ByVal Manager As WaveDataManager)
  456.         _AssociatedManager = Manager
  457.     End Sub
  458. End Class