Results 1 to 2 of 2

Thread: Simulate a Realistic Phone Ringback Tone in VB.NET

  1. #1

    Thread Starter
    Fanatic Member Peter Porter's Avatar
    Join Date
    Jul 2013
    Location
    Germany
    Posts
    581

    Simulate a Realistic Phone Ringback Tone in VB.NET

    Here is a simple VB.NET example that simulates the actual sound of a phone ringing (ringback tone). It generates a classic two-tone ring sound (440 Hz and 480 Hz) for 2 seconds, followed by a 3-second pause, just like a real phone call ringing pattern.

    This example uses System.Media.SoundPlayer to play the generated WAV data directly from memory, without needing any external sound files. It also uses a timer to loop the ringing pattern, and plays the sound asynchronously to keep the UI responsive.

    How it works:

    • The ring tone is generated on the fly as a 16-bit mono WAV with a 2-second duration.
    • The two-tone frequency combination and a quick fade-out produce a realistic ring sound.
    • A timer triggers the ring tone every 5 seconds (2s tone + 3s silence).
    • Buttons allow starting and stopping the ringing simulation. The buttons must be named btnStartRing and btnStopRing.




    Code:
    Imports System
    Imports System.IO
    Imports System.Media
    Imports System.Threading
    
    Public Class Form1
    
        ' Declare a Timer that controls the periodic ringing
        Private ringTimer As System.Windows.Forms.Timer
    
        ' Constructor: called when the form is initialized
        Public Sub New()
            InitializeComponent() ' Initialize form controls
    
            ' Create a new timer that fires every 5 seconds
            ' The ring pattern is: 2 seconds sound + 3 seconds silence
            ringTimer = New System.Windows.Forms.Timer()
            ringTimer.Interval = 5000
    
            ' Attach the tick event to a handler that plays the ring tone
            AddHandler ringTimer.Tick, AddressOf OnRingTimerTick
        End Sub
    
        ' Event handler for the Timer's Tick event
        ' Called every 5 seconds while the timer is running
        Private Sub OnRingTimerTick(ByVal sender As Object, ByVal e As EventArgs)
            ' Run the audio playback in a background thread to avoid freezing the UI
            ThreadPool.QueueUserWorkItem(AddressOf PlayRingbackTone)
        End Sub
    
        ' This method plays a 2-second ringback tone
        Private Sub PlayRingbackTone(ByVal state As Object)
            ' Generate raw WAV data for a 2-second ring tone
            Dim waveData = GenerateRingbackTone(durationSeconds:=2.0)
    
            ' Use a MemoryStream to play the audio from memory (no file I/O)
            Using ms As New MemoryStream(waveData)
                Using player As New SoundPlayer(ms)
                    player.PlaySync() ' Play synchronously (blocks for 2 seconds)
                End Using
            End Using
        End Sub
    
        ' Generates a ringback tone waveform (440 Hz + 480 Hz)
        ' Returns a WAV-format byte array
        Private Function GenerateRingbackTone(ByVal durationSeconds As Double) As Byte()
            Const sampleRate As Integer = 44100 ' Standard CD-quality sample rate
            Dim samplesCount As Integer = CInt(sampleRate * durationSeconds)
            Dim amplitude As Short = 8000 ' Volume of the tone (safe for playback)
    
            ' Duration of fade-out at the end of the tone (in seconds)
            Dim fadeOutDuration As Double = 0.1 ' 100 milliseconds
            Dim fadeStartSample As Integer = CInt(samplesCount - (sampleRate * fadeOutDuration))
    
            ' Frequencies used in US-style ringback tones
            Dim freq1 As Double = 440.0 ' A4
            Dim freq2 As Double = 480.0
    
            ' Each sample is 2 bytes (16-bit), so we need double the sample count
            Dim data(samplesCount * 2 - 1) As Byte
    
            ' Generate sine wave samples and apply fade-out
            For i As Integer = 0 To samplesCount - 1
                Dim t As Double = i / sampleRate ' Time position in seconds
    
                ' Combine two sine waves to create a dual-tone signal
                Dim sampleValue As Double = (Math.Sin(2 * Math.PI * freq1 * t) + Math.Sin(2 * Math.PI * freq2 * t)) / 2
    
                ' Apply a quadratic fade-out at the end of the tone
                If i >= fadeStartSample Then
                    Dim fadeProgress As Double = (samplesCount - i) / (samplesCount - fadeStartSample)
                    sampleValue *= fadeProgress * fadeProgress
                End If
    
                ' Clamp the sample to [-1, 1] to avoid distortion
                sampleValue = Math.Max(-1, Math.Min(1, sampleValue))
    
                ' Convert to 16-bit signed PCM and store in little-endian format
                Dim intSample As Short = CShort(sampleValue * amplitude)
                data(i * 2) = CByte(intSample And &HFF) ' Low byte
                data(i * 2 + 1) = CByte((intSample >> 8) And &HFF) ' High byte
            Next
    
            ' Wrap the PCM data in a standard WAV file format and return
            Return CreateWav(data, sampleRate, 1, 16)
        End Function
    
        ' Creates a proper WAV file header and appends the PCM data
        ' sampleRate: samples per second (e.g., 44100)
        ' channels: 1 = mono, 2 = stereo
        ' bitsPerSample: typically 16 for CD-quality audio
        Private Function CreateWav(ByVal pcmData() As Byte, ByVal sampleRate As Integer, ByVal channels As Short, ByVal bitsPerSample As Short) As Byte()
            Dim dataLength As Integer = pcmData.Length
            Dim headerLength As Integer = 44 ' Standard WAV header size
            Dim totalLength As Integer = headerLength + dataLength
    
            Using ms As New MemoryStream()
                Using bw As New BinaryWriter(ms)
                    ' Write the RIFF header
                    bw.Write(System.Text.Encoding.ASCII.GetBytes("RIFF"))
                    bw.Write(totalLength - 8) ' File size minus 8 bytes for RIFF and WAVE
                    bw.Write(System.Text.Encoding.ASCII.GetBytes("WAVE"))
    
                    ' Format chunk
                    bw.Write(System.Text.Encoding.ASCII.GetBytes("fmt "))
                    bw.Write(16) ' Subchunk size for PCM
                    bw.Write(CType(1, Short)) ' Audio format 1 = PCM
                    bw.Write(channels)
                    bw.Write(sampleRate)
                    bw.Write(sampleRate * channels * bitsPerSample \ 8) ' Byte rate
                    bw.Write(CShort(channels * bitsPerSample \ 8)) ' Block align
                    bw.Write(bitsPerSample)
    
                    ' Data chunk
                    bw.Write(System.Text.Encoding.ASCII.GetBytes("data"))
                    bw.Write(dataLength)
                    bw.Write(pcmData)
    
                    bw.Flush()
                    Return ms.ToArray() ' Return the full WAV byte array
                End Using
            End Using
        End Function
    
        ' Event handler for the "Start Ring" button
        ' Starts the timer and plays the first tone immediately
        Private Sub btnStartRing_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnStartRing.Click
            ringTimer.Start() ' Start the repeating ring
            ThreadPool.QueueUserWorkItem(AddressOf PlayRingbackTone) ' Play the first tone immediately
            btnStartRing.Enabled = False
            btnStopRing.Enabled = True
        End Sub
    
        ' Event handler for the "Stop Ring" button
        ' Stops the timer and halts ringing
        Private Sub btnStopRing_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnStopRing.Click
            ringTimer.Stop()
            btnStartRing.Enabled = True
            btnStopRing.Enabled = False
        End Sub
    
        ' Stop and dispose the timer to release system resources when the form is closing
        Private Sub Form1_FormClosing(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
            ringTimer.Stop()
            ringTimer.Dispose()
        End Sub
    End Class
    Last edited by Peter Porter; Jul 27th, 2025 at 02:50 PM.

  2. #2
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    9,017

    Re: Simulate a Realistic Phone Ringback Tone in VB.NET

    Nice. That is sweet!
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena

    Copy/move files using Windows Shell | I'm not wanted

    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. - jmcilhinney

    The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width