Click here to Skip to main content
15,885,944 members
Articles / Game Development

Sound in games - Rooms

Rate me:
Please Sign up or sign in to vote.
5.00/5 (7 votes)
31 Jul 2012CPOL16 min read 31.5K   571   14  
How to calculate a reverbration time in a room
Class MainWindow
    'The different properties of the listview
    Private _lstYAbsorbers As New System.Collections.ObjectModel.ObservableCollection(Of Absorber)
    Private _lstXAbsorbers As New System.Collections.ObjectModel.ObservableCollection(Of Absorber)
    Private _lstZAbsorbers As New System.Collections.ObjectModel.ObservableCollection(Of Absorber)

    'Stors all the results after calculations
    Private _lstResult2 As New System.Collections.ObjectModel.ObservableCollection(Of Absorber)

    'Stores the results
    Private Sabine As New Absorber
    Private Eyring As New Absorber
    Private Miller As New Absorber
    Private Fritzroy As New Absorber

    'Some interestiong parameters for the room
    'Calculated using Sabines formula
    Private Schroeder As Double
    Private RoomRadius As Double

    'Room constants
    Private Volume As Double
    Private _Height, _Width, _Length, _TotalSurfaceArea As Double
    Private TotalSufraceArea As Double

    'Acoustic constants for calculations
    Private Humidity As Integer
    Private C0 As Double = 343
    Private rho0 As Double = 1.21

    'Set up binding to the listviews
    Private Sub Window_Loaded(sender As System.Object, e As System.Windows.RoutedEventArgs) Handles MyBase.Loaded
        lstX.ItemsSource = _lstXAbsorbers
        lstY.ItemsSource = _lstYAbsorbers
        lstZ.ItemsSource = _lstZAbsorbers

        'For testing only
        'Dim test As New Absorber
        'test.Name = "Something"
        'test.Area = 50
        'For i As Integer = 0 To test.Count - 1
        '    test.SetItem(i, 0.25)
        'Next

        '_lstXAbsorbers.Add(test)
        '_lstYAbsorbers.Add(test)
        '_lstZAbsorbers.Add(test)

        'txtHeightRoom.Text = 5
        'txtWidthRoom.Text = 5
        'txtLengthRoom.Text = 5

    End Sub

#Region "Add absorbers"
    Private Sub btnZabsorbers_Click(sender As System.Object, e As System.Windows.RoutedEventArgs) Handles btnZabsorbers.Click
        Dim temp As New Absorber
        Dim dlg As New frmAbsorber(temp)
        With dlg
            If dlg.ShowDialog Then
                _lstZAbsorbers.Add(temp)
            End If
        End With
    End Sub

    Private Sub btnYabsorbers_Click(sender As System.Object, e As System.Windows.RoutedEventArgs) Handles btnYabsorbers.Click
        Dim temp As New Absorber
        Dim dlg As New frmAbsorber(temp)
        With dlg
            If dlg.ShowDialog Then
                _lstYAbsorbers.Add(temp)
            End If
        End With
    End Sub

    Private Sub btnXabsorbers_Click(sender As System.Object, e As System.Windows.RoutedEventArgs) Handles btnXabsorbers.Click
        Dim temp As New Absorber
        Dim dlg As New frmAbsorber(temp)
        With dlg
            If dlg.ShowDialog Then
                _lstXAbsorbers.Add(temp)
            End If
        End With
    End Sub
#End Region

#Region "Textbox changes"
    Private Sub txtHeightRoom_TextChanged(sender As System.Object, e As System.Windows.Controls.TextChangedEventArgs) Handles txtHeightRoom.TextChanged
        If Not txtHeightRoom.Text = "" Then
            _Height = CDbl(txtHeightRoom.Text)
            If Not _Height = Nothing And Not _Width = Nothing And Not _Length = Nothing Then
                lblZarea.Content = 2 * _Width * _Length
                lblXrea.Content = 2 * _Width * _Height
                lblYrea.Content = 2 * _Length * _Height
                _TotalSurfaceArea = CDbl(lblZarea.Content) + CDbl(lblXrea.Content) + CDbl(lblYrea.Content)
                btnZabsorbers.IsEnabled = True
                btnXabsorbers.IsEnabled = True
                btnYabsorbers.IsEnabled = True

            ElseIf Not _Width = Nothing And Not _Length = Nothing Then
                'Floor ceiling
                lblZarea.Content = 2 * _Width * _Length
            ElseIf Not _Width = Nothing And Not Height = Nothing Then
                'Front and back walls
                lblXrea.Content = 2 * _Width * _Height
            ElseIf Not _Length = Nothing And Not Height = Nothing Then
                'Side walls
                lblYrea.Content = 2 * _Length * _Height
            End If
        End If
    End Sub

    Private Sub txtWidthRoom_TextChanged(sender As System.Object, e As System.Windows.Controls.TextChangedEventArgs) Handles txtWidthRoom.TextChanged
        If Not txtWidthRoom.Text = "" Then
            _Width = CDbl(txtWidthRoom.Text)
            If Not _Height = Nothing And Not _Width = Nothing And Not _Length = Nothing Then
                lblZarea.Content = 2 * _Width * _Length
                lblXrea.Content = 2 * _Width * _Height
                lblYrea.Content = 2 * _Length * _Height
                _TotalSurfaceArea = CDbl(lblZarea.Content) + CDbl(lblXrea.Content) + CDbl(lblYrea.Content)
                btnZabsorbers.IsEnabled = True
                btnXabsorbers.IsEnabled = True
                btnYabsorbers.IsEnabled = True

            ElseIf Not _Width = Nothing And Not _Length = Nothing Then
                'Floor ceiling
                lblZarea.Content = 2 * _Width * _Length
            ElseIf Not _Width = Nothing And Not Height = Nothing Then
                'Front and back walls
                lblXrea.Content = 2 * _Width * _Height
            ElseIf Not _Length = Nothing And Not Height = Nothing Then
                'Side walls
                lblYrea.Content = 2 * _Length * _Height
            End If
        End If
    End Sub

    Private Sub txtLengthRoom_TextChanged(sender As System.Object, e As System.Windows.Controls.TextChangedEventArgs) Handles txtLengthRoom.TextChanged
        If Not txtLengthRoom.Text = "" Then
            _Length = CDbl(txtLengthRoom.Text)
            If Not _Height = Nothing And Not _Width = Nothing And Not _Length = Nothing Then
                lblZarea.Content = 2 * _Width * _Length
                lblXrea.Content = 2 * _Width * _Height
                lblYrea.Content = 2 * _Length * _Height
                _TotalSurfaceArea = CDbl(lblZarea.Content) + CDbl(lblXrea.Content) + CDbl(lblYrea.Content)
                btnZabsorbers.IsEnabled = True
                btnXabsorbers.IsEnabled = True
                btnYabsorbers.IsEnabled = True
            ElseIf Not _Width = Nothing And Not _Length = Nothing Then
                'Floor ceiling
                lblZarea.Content = 2 * _Width * _Length
            ElseIf Not _Width = Nothing And Not Height = Nothing Then
                'Front and back walls
                lblXrea.Content = 2 * _Width * _Height
            ElseIf Not _Length = Nothing And Not Height = Nothing Then
                'Side walls
                lblYrea.Content = 2 * _Length * _Height
            End If
        End If
    End Sub
#End Region

    'Calculatign all the parameters based on information supplied
    Private Sub btnCalculate_Click(sender As System.Object, e As System.Windows.RoutedEventArgs) Handles btnCalculate.Click
        Dim FullOctaveBand() As String
        Dim OctaveBandString As String = "31,63,125,250,500,1000,2000,4000,8000,16000"
        FullOctaveBand = OctaveBandString.Split(",")

        If Not _lstXAbsorbers.Count = 0 And Not _lstYAbsorbers.Count = 0 And Not _lstZAbsorbers.Count = 0 Then
            TotalSufraceArea = _TotalSurfaceArea

            Humidity = CInt(txtHumidityRoom.Text)

            Sabine.Name = "Sabine"
            Eyring.Name = "Eyring"
            Miller.Name = "Millington-Sette"
            Fritzroy.Name = "Fritzroy"

            Volume = _Height * _Length * _Width

            For i As Integer = 0 To Sabine.Count - 1

                Dim SabineTempZ, SabineTempY, SabineTempX As Double
                SabineTempX = 0
                SabineTempY = 0
                SabineTempZ = 0

                Dim EyringTempZ, EyringTempY, EyringTempX As Double
                EyringTempZ = 0
                EyringTempY = 0
                EyringTempX = 0

                Dim MillingtonSetteTempZ, MillingtonSetteTempY, MillingtonSetteTempX As Double
                MillingtonSetteTempZ = 0
                MillingtonSetteTempY = 0
                MillingtonSetteTempX = 0

                For Each p As Absorber In _lstZAbsorbers
                    SabineTempZ += p.Area * p.GetItem(i)
                    EyringTempZ += p.Area * p.GetItem(i) / _TotalSurfaceArea
                    MillingtonSetteTempZ += p.Area * CalculateAlfa(p.GetItem(i))
                Next

                For Each p As Absorber In _lstYAbsorbers
                    SabineTempY += p.Area * p.GetItem(i)
                    EyringTempY += p.Area * p.GetItem(i) / _TotalSurfaceArea
                    MillingtonSetteTempY += p.Area * CalculateAlfa(p.GetItem(i))
                Next

                For Each p As Absorber In _lstXAbsorbers
                    SabineTempX += p.Area * p.GetItem(i)
                    EyringTempX += p.Area * p.GetItem(i) / _TotalSurfaceArea
                    MillingtonSetteTempX += p.Area * CalculateAlfa(p.GetItem(i))
                Next

                'Taken from Fundamentals of Acoustics
                Dim m As Double = 5.5 * 10 ^ (-4) * (50 / Humidity) * (CDbl(FullOctaveBand(i)) / 1000) ^ (1.7)

                'Calculationg total absorbtion areas pluss air annutaion 
                Dim AbsorbtionAreaSabine As Double = (SabineTempX + SabineTempY + SabineTempZ) + 4 * m * Volume
                Dim AbsorbtionAreaEyring As Double = _TotalSurfaceArea * CalculateAlfa(EyringTempX + EyringTempY + EyringTempZ) + 4 * m * Volume
                Dim AbsorbtionAreaMillingtonSette As Double = MillingtonSetteTempX + MillingtonSetteTempY + MillingtonSetteTempZ + 4 * m * Volume

                Dim FritzroyTempZ, FritzroyTempY, FritzroyTempX As Double
                FritzroyTempX = CDbl(lblXrea.Content) / (CalculateAlfa(EyringTempX) + (4 * m * Volume / 3) * (CDbl(lblXrea.Content) / _TotalSurfaceArea ^ 2))
                FritzroyTempY = CDbl(lblYrea.Content) / (CalculateAlfa(EyringTempY) + (4 * m * Volume / 3) * (CDbl(lblYrea.Content) / _TotalSurfaceArea ^ 2))
                FritzroyTempZ = CDbl(lblZarea.Content) / (CalculateAlfa(EyringTempZ) + (4 * m * Volume / 3) * (CDbl(lblZarea.Content) / _TotalSurfaceArea ^ 2))

                'Save the calculated reverberation time in different octave bands
                Sabine.SetItem(i, ReverberationTime(AbsorbtionAreaSabine))
                Eyring.SetItem(i, ReverberationTime(AbsorbtionAreaEyring))
                Miller.SetItem(i, ReverberationTime(AbsorbtionAreaMillingtonSette))
                Fritzroy.SetItem(i, (0.16 * Volume / (_TotalSurfaceArea ^ 2)) * (FritzroyTempX + FritzroyTempY + FritzroyTempZ))
            Next

            Dim AverageReverberationTimeSabine As Double
            AverageReverberationTimeSabine = 0
            For i As Integer = 0 To Sabine.Count - 1
                AverageReverberationTimeSabine += Sabine.GetItem(i) / Sabine.Count
            Next

            'Calculate the Schroeder frequency based on Sabine
            Schroeder = 2000 * Math.Sqrt(AverageReverberationTimeSabine / Volume)

            'Room radius is calculated besed on a omnidirectional source, meaning D = 1
            RoomRadius = Math.Sqrt(55.26 * Volume / (16 * Math.PI * C0 * AverageReverberationTimeSabine))

            lblSchroder.Content = Math.Round(Schroeder, 0).ToString
            lblRoomRadius.Content = Math.Round(RoomRadius, 2).ToString

            _lstResult2.Add(Sabine)
            _lstResult2.Add(Eyring)
            _lstResult2.Add(Miller)
            _lstResult2.Add(Fritzroy)

            lstResult.ItemsSource = _lstResult2

            For Each p As Absorber In _lstResult2
                PlotReverbrationTime(p, False)
            Next

        Else
            MessageBox.Show("missing absorbers")
        End If
    End Sub

    'Using this instead of -ln(1-alfa). This is the result of the taylor expantion of ln(1-alfa) 
    Private Function CalculateAlfa(ByVal N As Double) As Double
        Dim result As Double
        For i As Integer = 1 To 20
            result += (N ^ i) / i
        Next
        Return result
    End Function

    ''' <summary>
    ''' Calculates reverbration time based on absorbtion coefficient
    ''' </summary>
    ''' <param name="Ab"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Function ReverberationTime(ByVal Ab As Double) As Double
        Dim result As Double = 55.26 * Volume / (C0 * Ab)
        Return result
    End Function

#Region "Plotting"
    Public Class PlotLinePoint
        Private _Value As Double
        Public Property Value() As Double
            Get
                Return _Value
            End Get
            Set(ByVal value As Double)
                _Value = value
            End Set
        End Property

        Private _Freq As String
        Public Property Freq() As String
            Get
                Return _Freq
            End Get
            Set(ByVal value As String)
                _Freq = value
            End Set
        End Property
    End Class

    Private Sub PlotReverbrationTime(ByVal CalculatedReverberationTime As Absorber, Optional ByVal Clear As Boolean = True)
        Dim str() As String
        Dim str2 As String = "31Hz,63Hz,125Hz,250Hz,500Hz,1kHz, 2kHz,4kHz,8kHz,16kHz"
        str = str2.Split(",")

        If Clear Then
            chrt2.Graphs.Clear()
        End If

        Dim TheTotalCurve As New System.Collections.ObjectModel.ObservableCollection(Of PlotLinePoint)

        Dim BindingSource As New Binding
        BindingSource.Source = TheTotalCurve

        For i As Integer = 0 To CalculatedReverberationTime.Count - 1
            Dim IndividualCurvePoint As New PlotLinePoint()
            IndividualCurvePoint.Freq = str(i)
            IndividualCurvePoint.Value = CalculatedReverberationTime.GetItem(i)
            TheTotalCurve.Add(IndividualCurvePoint)
        Next

        Dim NewLineCurve As New AmCharts.Windows.Line.LineChartGraph
        chrt2.Graphs.Add(NewLineCurve)

        NewLineCurve.SetBinding(AmCharts.Windows.Core.SerialGraph.DataItemsSourceProperty, BindingSource)
        NewLineCurve.SeriesIDMemberPath = "Freq"
        NewLineCurve.ValueMemberPath = "Value"

        NewLineCurve.LineThickness = 2
        NewLineCurve.Title = CalculatedReverberationTime.Name

        chrt2.Refresh()
    End Sub
#End Region

End Class

Public Class Absorber
    'http://www.cirrusresearch.co.uk/blog/2011/11/what-are-octave-and-third-octave-band-filters-on-a-sound-level-meter/
    '   31Hz	 63Hz 	125Hz	 250Hz	500Hz	1kHz 	2kHz	4kHz	8kHz	16kHz
    '22/07/2010

    Sub New()

    End Sub

    Private _Name As String
    Public Property Name() As String
        Get
            Return _Name
        End Get
        Set(ByVal value As String)
            _Name = value
        End Set
    End Property

    Private _Area As Double
    Public Property Area() As Double
        Get
            Return _Area
        End Get
        Set(ByVal value As Double)
            _Area = value
        End Set
    End Property

    Private _Procent As Double
    Public Property Procent() As Double
        Get
            Return _Procent
        End Get
        Set(ByVal value As Double)
            _Procent = value
        End Set
    End Property

#Region "Frequency"
    Private _31Hz As Double
    Public Property f31Hz() As Double
        Get
            Return _31Hz
        End Get
        Set(ByVal value As Double)
            _31Hz = value
        End Set
    End Property

    Private _63Hz As Double
    Public Property f63Hz() As Double
        Get
            Return _63Hz
        End Get
        Set(ByVal value As Double)
            _63Hz = value
        End Set
    End Property

    Private _125Hz As Double
    Public Property f125Hz() As Double
        Get
            Return _125Hz
        End Get
        Set(ByVal value As Double)
            _125Hz = value
        End Set
    End Property

    Private _250Hz As Double
    Public Property f250Hz() As Double
        Get
            Return _250Hz
        End Get
        Set(ByVal value As Double)
            _250Hz = value
        End Set
    End Property

    Private _500Hz As Double
    Public Property f500Hz() As Double
        Get
            Return _500Hz
        End Get
        Set(ByVal value As Double)
            _500Hz = value
        End Set
    End Property

    Private _1000Hz As Double
    Public Property f1000Hz() As Double
        Get
            Return _1000Hz
        End Get
        Set(ByVal value As Double)
            _1000Hz = value
        End Set
    End Property

    Private _2000Hz As Double
    Public Property f2000Hz() As Double
        Get
            Return _2000Hz
        End Get
        Set(ByVal value As Double)
            _2000Hz = value
        End Set
    End Property

    Private _4000Hz As Double
    Public Property f4000Hz() As Double
        Get
            Return _4000Hz
        End Get
        Set(ByVal value As Double)
            _4000Hz = value
        End Set
    End Property

    Private _8000Hz As Double
    Public Property f8000Hz() As Double
        Get
            Return _8000Hz
        End Get
        Set(ByVal value As Double)
            _8000Hz = value
        End Set
    End Property

    Private _16000Hz As Double
    Public Property f16000Hz() As Double
        Get
            Return _16000Hz
        End Get
        Set(ByVal value As Double)
            _16000Hz = value
        End Set
    End Property

#End Region

    Public Function Count() As Integer
        Return 10
    End Function

    Public Function GetItem(ByVal i As Integer) As Double
        Select Case i
            Case 0
                Return _31Hz
            Case 1
                Return _63Hz
            Case 2
                Return _125Hz
            Case 3
                Return _250Hz
            Case 4
                Return _500Hz
            Case 5
                Return _1000Hz
            Case 6
                Return _2000Hz
            Case 7
                Return _4000Hz
            Case 8
                Return _8000Hz
            Case 9
                Return _16000Hz
            Case Else
                Throw New IndexOutOfRangeException
        End Select
    End Function

    Public Sub SetItem(ByVal i As Integer, ByVal Value As Double)
        Select Case i
            Case 0
                _31Hz = Value
            Case 1
                _63Hz = Value
            Case 2
                _125Hz = Value
            Case 3
                _250Hz = Value
            Case 4
                _500Hz = Value
            Case 5
                _1000Hz = Value
            Case 6
                _2000Hz = Value
            Case 7
                _4000Hz = Value
            Case 8
                _8000Hz = Value
            Case 9
                _16000Hz = Value
            Case Else
                Throw New IndexOutOfRangeException
        End Select
    End Sub
End Class

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Chief Technology Officer
Norway Norway
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions