QB64 Phoenix Edition
Make own Echo - Printable Version

+- QB64 Phoenix Edition (https://qb64phoenix.com/forum)
+-- Forum: QB64 Rising (https://qb64phoenix.com/forum/forumdisplay.php?fid=1)
+--- Forum: Prolific Programmers (https://qb64phoenix.com/forum/forumdisplay.php?fid=26)
+---- Forum: Petr (https://qb64phoenix.com/forum/forumdisplay.php?fid=52)
+---- Thread: Make own Echo (/showthread.php?tid=1498)



Make own Echo - Petr - 02-24-2023

Hello. I wrote a function that you just push a music file, set the echo length and volume (in the range from 0 to 1) and the program will generate a new sound straight away. I used _SndNew for this.



Code: (Select All)
Snd& = _SndOpen("SongEight.mod")

For Echo = 1 To 3
    Print "Creating Echo level: "; Echo
    _Delay .1
    news& = MakeEcho&(Snd&, Echo / 10, .85)
    _Delay .1
    _SndClose Snd&
    Snd& = _SndCopy(news&)
Next
_SndPlay news&

Print "Program create new sound handle ("; news&; " )"

Print " This new handle is not full compatible:"
Print "_SndLen return: "; _SndLen(news&)
Sleep 2
Print "_SndGetPos return: "; _SndGetPos(news&)
Sleep 4
Print "Trying use _SndSetPos to begin this track..."
_SndSetPos news&, 0






Function MakeEcho& (InputSound As Long, SoundDelay As Single, EchoVolume As Single)
    If SoundDelay < 0 Then Print "Error: EchoDelay must be higher than zero.": Exit Function
    'EchoVolume in range 0 to 1!

    Dim SourceSound As _MEM
    SourceSound = _MemSound(InputSound&, 0)

    Select Case GetBites(SourceSound)
        Case 1, 2: Multiply = 4
        Case 3: Multiply = 2
        Case 4: Multiply = 1
    End Select

    Select Case SourceSound.TYPE
        Case 260 ' 32-bit floating point
            If SourceSound.ELEMENTSIZE = 4 Then
                SndChannels = 1
            ElseIf SourceSound.ELEMENTSIZE = 8 Then
                SndChannels = 2
            End If
        Case 132 ' 32-bit integer
            If SourceSound.ELEMENTSIZE = 4 Then
                SndChannels = 1
            ElseIf SourceSound.ELEMENTSIZE = 8 Then
                SndChannels = 2
            End If
        Case 130: ' 16-bit integer
            If SourceSound.ELEMENTSIZE = 2 Then
                SndChannels = 1
            ElseIf SourceSound.ELEMENTSIZE = 4 Then
                SndChannels = 2
            End If
        Case 1153: ' 8-bit unsigned integer
            If SourceSound.ELEMENTSIZE = 1 Then
                SndChannels = 1
            ElseIf SourceSound.ELEMENTSIZE = 2 Then
                SndChannels = 2
            End If
    End Select

    Dim As Long i, BB
    Dim As Double sampL, sampL2, sampR, sampR2


    BB& = Multiply * _SndRate * SoundDelay * SndChannels

    Dim TrackTime As _Float

    TrackTime = ConvertOffset(SourceSound.SIZE) \ _SndRate \ ConvertOffset(SourceSound.ELEMENTSIZE) + _Ceil(SoundDelay)

    Print "New track time: "; TrackTime; "[sec]"

    MakeEch& = _SndNew((TrackTime + SoundDelay) * _SndRate, SndChannels, Multiply * 8)


    '    Print MakeEch&, SndChannels, Multiply, _SndLen(InputSound)
    Dim ME As _MEM
    ME = _MemSound(MakeEch&, 0)

    Do Until i >= SourceSound.SIZE - SourceSound.ELEMENTSIZE

        Select Case SndChannels
            Case 1
                Select Case SourceSound.TYPE
                    Case 260: sampL = _MemGet(SourceSound, SourceSound.OFFSET + i, Single) ' 32-bit floating point
                    Case 132: sampL = _MemGet(SourceSound, SourceSound.OFFSET + i, Long) / 2147483648 ' 32-bit integer
                    Case 130: sampL = _MemGet(SourceSound, SourceSound.OFFSET + i, Integer) / 32768 ' 16-bit integer
                    Case 1153: sampL = (_MemGet(SourceSound, SourceSound.OFFSET + i, _Unsigned _Byte) - 128) / 128 ' 8-bit unsigned integer
                End Select
            Case 2
                Select Case SourceSound.TYPE
                    Case 260: sampL = _MemGet(SourceSound, SourceSound.OFFSET + i, Single): sampR = _MemGet(SourceSound, SourceSound.OFFSET + i + SourceSound.ELEMENTSIZE \ 2, Single) ' 32-bit floating point
                    Case 132: sampL = _MemGet(SourceSound, SourceSound.OFFSET + i, Long) / 2147483648: sampR = _MemGet(SourceSound, SourceSound.OFFSET + i + SourceSound.ELEMENTSIZE \ 2, Long) / 2147483648 ' 32-bit integer
                    Case 130: sampL = _MemGet(SourceSound, SourceSound.OFFSET + i, Integer) / 32768: sampR = _MemGet(SourceSound, SourceSound.OFFSET + i + SourceSound.ELEMENTSIZE \ 2, Integer) / 32768 ' 16-bit integer
                    Case 1153: sampL = (_MemGet(SourceSound, SourceSound.OFFSET + i, _Unsigned _Byte) - 128) / 128: sampR = (_MemGet(SourceSound, SourceSound.OFFSET + i + SourceSound.ELEMENTSIZE \ 2, _Unsigned _Byte) - 128) / 128 ' 8-bit unsigned integer
                End Select
        End Select

        If i& > BB& Then
            Select Case SndChannels
                Case 1
                    Select Case SourceSound.TYPE
                        Case 260: sampL2 = _MemGet(SourceSound, SourceSound.OFFSET + i - BB&, Single) ' 32-bit floating point
                        Case 132: sampL2 = _MemGet(SourceSound, SourceSound.OFFSET + i - BB&, Long) / 2147483648 ' 32-bit integer
                        Case 130: sampL2 = _MemGet(SourceSound, SourceSound.OFFSET + i - BB&, Integer) / 32768 ' 16-bit integer
                        Case 1153: sampL2 = (_MemGet(SourceSound, SourceSound.OFFSET + i - BB&, _Unsigned _Byte) - 128) / 128 ' 8-bit unsigned integer
                    End Select
                Case 2
                    Select Case SourceSound.TYPE
                        Case 260: sampL2 = _MemGet(SourceSound, SourceSound.OFFSET + i - BB&, Single): sampR2 = _MemGet(SourceSound, SourceSound.OFFSET + i + SourceSound.ELEMENTSIZE \ 2 - BB&, Single) ' 32-bit floating point
                        Case 132: sampL2 = _MemGet(SourceSound, SourceSound.OFFSET + i - BB&, Long) / 2147483648: sampR2 = _MemGet(SourceSound, SourceSound.OFFSET + i + SourceSound.ELEMENTSIZE \ 2 - BB&, Long) / 2147483648 ' 32-bit integer
                        Case 130: sampL2 = _MemGet(SourceSound, SourceSound.OFFSET + i - BB&, Integer) / 32768: sampR2 = _MemGet(SourceSound, SourceSound.OFFSET + i + SourceSound.ELEMENTSIZE \ 2 - BB&, Integer) / 32768 ' 16-bit integer
                        Case 1153: sampL2 = (_MemGet(SourceSound, SourceSound.OFFSET + i - BB&, _Unsigned _Byte) - 128) / 128: sampR2 = (_MemGet(SourceSound, SourceSound.OFFSET + i + SourceSound.ELEMENTSIZE \ 2 - BB&, _Unsigned _Byte) - 128) / 128 ' 8-bit unsigned integer
                    End Select
            End Select
        End If
        sampL2 = sampL2 * EchoVolume
        sampR2 = sampR2 * EchoVolume

        LeftOut = (sampL * (1 - EchoVolume)) + sampL2
        RightOut = (sampR * (1 - EchoVolume)) + sampR2

        Select Case SndChannels
            Case 1
                Select Case ME.TYPE
                    Case 260: _MemPut ME, ME.OFFSET + i, LeftOut As SINGLE ' 32-bit floating point
                    Case 132: _MemPut ME, ME.OFFSET + i, LeftOut * 2147483648 As LONG ' 32-bit integer
                    Case 130: _MemPut ME, ME.OFFSET + i, LeftOut * 32768 As INTEGER ' 16-bit integer
                    Case 1153: _MemPut ME, ME.OFFSET + i, 128 - (LeftOut * 128) As _UNSIGNED _BYTE ' 8-bit unsigned integer
                End Select
            Case 2
                Select Case ME.TYPE
                    Case 260: _MemPut ME, ME.OFFSET + i, LeftOut As SINGLE: _MemPut ME, ME.OFFSET + i + ME.ELEMENTSIZE \ 2, RightOut As SINGLE ' 32-bit floating point
                    Case 132: _MemPut ME, ME.OFFSET + i, LeftOut * 2147483648 As LONG: _MemPut ME, ME.OFFSET + i + ME.ELEMENTSIZE \ 2, RightOut * 2147483648 As LONG ' 32-bit integer
                    Case 130: _MemPut ME, ME.OFFSET + i, LeftOut * 32768 As INTEGER: _MemPut ME, ME.OFFSET + i + ME.ELEMENTSIZE \ 2, RightOut * 32768 As INTEGER ' 16-bit integer
                    Case 1153: _MemPut ME, ME.OFFSET + i, 128 - (LeftOut * 128) As _UNSIGNED _BYTE: _MemPut ME, ME.OFFSET + i + ME.ELEMENTSIZE \ 2, 128 - (128 * RightOut) As _UNSIGNED _BYTE ' 8-bit unsigned integer
                End Select
        End Select
        i = i + Multiply * 2
    Loop
    MakeEcho& = _SndCopy(MakeEch&)
    _SndClose MakeEch&
    If ME.SIZE Then _MemFree ME
    If SourceSound.SIZE Then _MemFree SourceSound
End Function


Function GetBites (handle As _MEM)
    Select Case handle.TYPE
        Case 260: GetBites = 1 ' 32-bit floating point SINGLE
        Case 132: GetBites = 2 ' 32-bit integer LONG
        Case 130: GetBites = 3 ' 16-bit integer INTEGER
        Case 1153: GetBites = 4 ' 8-bit unsigned integer
    End Select
End Function


Sub PlaySound (handle As Long, rs As Long)
    Dim SampleData As _MEM
    Dim channels As _Unsigned _Byte
    Dim sampL As Single, sampR As Single
    Dim i As _Offset

    channels = SndChannels(handle)

    SampleData = _MemSound(handle, 0)
    If SampleData.SIZE = 0 Then
        Print "PlaySound: Sample data array is empty."
        Exit Sub
    End If

    Do Until i = SampleData.SIZE - SampleData.ELEMENTSIZE
        Select Case channels
            Case 1
                Select Case SampleData.TYPE
                    Case 260: sampL = _MemGet(SampleData, SampleData.OFFSET + i, Single) ' 32-bit floating point
                    Case 132: sampL = _MemGet(SampleData, SampleData.OFFSET + i, Long) / 2147483648 ' 32-bit integer
                    Case 130: sampL = _MemGet(SampleData, SampleData.OFFSET + i, Integer) / 32768 ' 16-bit integer
                    Case 1153: sampL = (_MemGet(SampleData, SampleData.OFFSET + i, _Unsigned _Byte) - 128) / 128 ' 8-bit unsigned integer
                End Select
            Case 2
                Select Case SampleData.TYPE
                    Case 260: sampL = _MemGet(SampleData, SampleData.OFFSET + i, Single): sampR = _MemGet(SampleData, SampleData.OFFSET + i + SampleData.ELEMENTSIZE \ 2, Single) ' 32-bit floating point
                    Case 132: sampL = _MemGet(SampleData, SampleData.OFFSET + i, Long) / 2147483648: sampR = _MemGet(SampleData, SampleData.OFFSET + i + SampleData.ELEMENTSIZE \ 2, Long) / 2147483648 ' 32-bit integer
                    Case 130: sampL = _MemGet(SampleData, SampleData.OFFSET + i, Integer) / 32768: sampR = _MemGet(SampleData, SampleData.OFFSET + i + SampleData.ELEMENTSIZE \ 2, Integer) / 32768 ' 16-bit integer
                    Case 1153: sampL = (_MemGet(SampleData, SampleData.OFFSET + i, _Unsigned _Byte) - 128) / 128: sampR = (_MemGet(SampleData, SampleData.OFFSET + i + SampleData.ELEMENTSIZE \ 2, _Unsigned _Byte) - 128) / 128 ' 8-bit unsigned integer
                End Select
        End Select
        If channels Mod 2 = 0 Then
            _SndRaw sampL, sampR, rs 'stereo
        Else
            _SndRaw sampL, sampL, rs 'mono = left channel in both speakers
        End If
        i = i + SampleData.ELEMENTSIZE
    Loop
    _MemFree SampleData
End Sub

Function ConvertOffset&& (value As _Offset)
    $Checking:Off
    Dim m As _MEM 'Define a memblock
    m = _Mem(value) 'Point it to use value
    $If 64BIT Then
            'On 64 bit OSes, an OFFSET is 8 bytes in size.  We can put it directly into an Integer64
            _MEMGET m, m.OFFSET, ConvertOffset&& 'Get the contents of the memblock and put the values there directly into ConvertOffset&&
    $Else
        'However, on 32 bit OSes, an OFFSET is only 4 bytes.  We need to put it into a LONG variable first
        _MemGet m, m.OFFSET, temp& 'Like this
        ConvertOffset&& = temp& 'And then assign that long value to ConvertOffset&&
    $End If
    _MemFree m 'Free the memblock
    $Checking:On
End Function



RE: Make own Echo - bplus - 02-24-2023

Hi Petr,

Do you think this might work with the Text To Speech thing Windows has?

That might be fun!


RE: Make own Echo - SMcNeill - 02-24-2023

(02-24-2023, 07:48 PM)bplus Wrote: Hi Petr,

Do you think this might work with the Text To Speech thing Windows has?

That might be fun!

The Powershell Text to Speech stuff that I did will let you export to a sound file, so I'm certain you could get it working like that, though it might have some load/save/conversion time adding to the overall lag with it.


RE: Make own Echo - mnrvovrfc - 02-25-2023

(02-24-2023, 07:48 PM)bplus Wrote: Do you think this might work with the Text To Speech thing Windows has?

If you're clever enough you could create a QB64PE program that runs the text-to-speech thing (erm, as Steve just said), exports to wave, loads the wave and then uses the code employed in this topic.

Just change the first line of this code to the file that was exported from the text-to-speech thing. The only barrier is being able to use "SHELL" to call Powershell.


RE: Make own Echo - Petr - 02-25-2023

I haven't found a way (only in the paid PowerShell branch) to get the WAV file. The normal redirect character > or >> in the text to speech program for including streams will create an just empty file. Help with this is high appreciated.


RE: Make own Echo - Petr - 03-03-2023

Repaired bug: Division by zero

Code: (Select All)
Snd& = _SndOpen("e.mp3")

For Echo = 1 To 4
    Print "Creating Echo level: "; Echo
    _Delay .1
    news& = MakeEcho&(Snd&, 0.05, .5 - Echo / 10)
    _Delay .1
    _SndClose Snd&
    Snd& = news&
Next

'_SndPlay news&

Print "Program create new sound handle ("; news&; " )"

Print " This new handle is not full compatible:"
Print "_SndLen return: "; _SndLen(news&)
Sleep 2
Print "_SndGetPos return: "; _SndGetPos(news&)
Sleep 4
Print "Trying use _SndSetPos to begin this track..."
_SndSetPos news&, 0

Print "SNDGETPOS with _SNDNEW NOT WORK? OK - see row 20 on the screen :)"

PlaySound news&
_SndClose news&

End

Function MakeEcho& (InputSound As Long, SoundDelay As Single, EchoVolume As Single)
    If SoundDelay < 0 Then Print "Error: EchoDelay must be higher than zero.": Exit Function
    If EchoVolume <= 0 Then Print "Error: Echo volume must be higher than zero.": Exit Function
    'EchoVolume in range 0 to 1!

    Dim SourceSound As _MEM
    SourceSound = _MemSound(InputSound&, 0)

    Select Case GetBites(SourceSound)
        Case 1, 2: Multiply = 4
        Case 3: Multiply = 2
        Case 4: Multiply = 1
    End Select

    Select Case SourceSound.TYPE
        Case 260 ' 32-bit floating point
            If SourceSound.ELEMENTSIZE = 4 Then
                SndChanels = 1
            ElseIf SourceSound.ELEMENTSIZE = 8 Then
                SndChanels = 2
            End If
        Case 132 ' 32-bit integer
            If SourceSound.ELEMENTSIZE = 4 Then
                SndChanels = 1
            ElseIf SourceSound.ELEMENTSIZE = 8 Then
                SndChanels = 2
            End If
        Case 130: ' 16-bit integer
            If SourceSound.ELEMENTSIZE = 2 Then
                SndChanels = 1
            ElseIf SourceSound.ELEMENTSIZE = 4 Then
                SndChanels = 2
            End If
        Case 1153: ' 8-bit unsigned integer
            If SourceSound.ELEMENTSIZE = 1 Then
                SndChanels = 1
            ElseIf SourceSound.ELEMENTSIZE = 2 Then
                SndChanels = 2
            End If
    End Select

    Dim As Long i, BB
    Dim As Double sampL, sampL2, sampR, sampR2


    BB& = Multiply * _SndRate * SoundDelay * SndChanels

    Dim TrackTime As _Float

    '   Print "80"
    '   Print "SourceSound.SIZE"; ConvertOffset(SourceSound.SIZE); "SndRate: "; _SndRate; "SourceSound.elementsize"; ConvertOffset(SourceSound.ELEMENTSIZE)

    TrackTime = ConvertOffset(SourceSound.SIZE) \ _SndRate \ ConvertOffset(SourceSound.ELEMENTSIZE) + _Ceil(SoundDelay)


    '  Print "82"
    Print "New track time: "; TrackTime; "[sec]"

    MakeEch& = _SndNew((TrackTime + SoundDelay) * _SndRate, SndChanels, Multiply * 8)


    '    Print MakeEch&, SndChannels, Multiply, _SndLen(InputSound)
    Dim ME As _MEM
    ME = _MemSound(MakeEch&, 0)

    Do Until i >= SourceSound.SIZE - SourceSound.ELEMENTSIZE

        Select Case SndChanels
            Case 1
                Select Case SourceSound.TYPE
                    Case 260: sampL = _MemGet(SourceSound, SourceSound.OFFSET + i, Single) ' 32-bit floating point
                    Case 132: sampL = _MemGet(SourceSound, SourceSound.OFFSET + i, Long) / 2147483648 ' 32-bit integer
                    Case 130: sampL = _MemGet(SourceSound, SourceSound.OFFSET + i, Integer) / 32768 ' 16-bit integer
                    Case 1153: sampL = (_MemGet(SourceSound, SourceSound.OFFSET + i, _Unsigned _Byte) - 128) / 128 ' 8-bit unsigned integer
                End Select
            Case 2
                Select Case SourceSound.TYPE
                    Case 260: sampL = _MemGet(SourceSound, SourceSound.OFFSET + i, Single): sampR = _MemGet(SourceSound, SourceSound.OFFSET + i + SourceSound.ELEMENTSIZE \ 2, Single) ' 32-bit floating point
                    Case 132: sampL = _MemGet(SourceSound, SourceSound.OFFSET + i, Long) / 2147483648: sampR = _MemGet(SourceSound, SourceSound.OFFSET + i + SourceSound.ELEMENTSIZE \ 2, Long) / 2147483648 ' 32-bit integer
                    Case 130: sampL = _MemGet(SourceSound, SourceSound.OFFSET + i, Integer) / 32768: sampR = _MemGet(SourceSound, SourceSound.OFFSET + i + SourceSound.ELEMENTSIZE \ 2, Integer) / 32768 ' 16-bit integer
                    Case 1153: sampL = (_MemGet(SourceSound, SourceSound.OFFSET + i, _Unsigned _Byte) - 128) / 128: sampR = (_MemGet(SourceSound, SourceSound.OFFSET + i + SourceSound.ELEMENTSIZE \ 2, _Unsigned _Byte) - 128) / 128 ' 8-bit unsigned integer
                End Select
        End Select

        If i& > BB& Then
            Select Case SndChanels
                Case 1
                    Select Case SourceSound.TYPE
                        Case 260: sampL2 = _MemGet(SourceSound, SourceSound.OFFSET + i - BB&, Single) ' 32-bit floating point
                        Case 132: sampL2 = _MemGet(SourceSound, SourceSound.OFFSET + i - BB&, Long) / 2147483648 ' 32-bit integer
                        Case 130: sampL2 = _MemGet(SourceSound, SourceSound.OFFSET + i - BB&, Integer) / 32768 ' 16-bit integer
                        Case 1153: sampL2 = (_MemGet(SourceSound, SourceSound.OFFSET + i - BB&, _Unsigned _Byte) - 128) / 128 ' 8-bit unsigned integer
                    End Select
                Case 2
                    Select Case SourceSound.TYPE
                        Case 260: sampL2 = _MemGet(SourceSound, SourceSound.OFFSET + i - BB&, Single): sampR2 = _MemGet(SourceSound, SourceSound.OFFSET + i + SourceSound.ELEMENTSIZE \ 2 - BB&, Single) ' 32-bit floating point
                        Case 132: sampL2 = _MemGet(SourceSound, SourceSound.OFFSET + i - BB&, Long) / 2147483648: sampR2 = _MemGet(SourceSound, SourceSound.OFFSET + i + SourceSound.ELEMENTSIZE \ 2 - BB&, Long) / 2147483648 ' 32-bit integer
                        Case 130: sampL2 = _MemGet(SourceSound, SourceSound.OFFSET + i - BB&, Integer) / 32768: sampR2 = _MemGet(SourceSound, SourceSound.OFFSET + i + SourceSound.ELEMENTSIZE \ 2 - BB&, Integer) / 32768 ' 16-bit integer
                        Case 1153: sampL2 = (_MemGet(SourceSound, SourceSound.OFFSET + i - BB&, _Unsigned _Byte) - 128) / 128: sampR2 = (_MemGet(SourceSound, SourceSound.OFFSET + i + SourceSound.ELEMENTSIZE \ 2 - BB&, _Unsigned _Byte) - 128) / 128 ' 8-bit unsigned integer
                    End Select
            End Select
        End If
        sampL2 = sampL2 * EchoVolume
        sampR2 = sampR2 * EchoVolume

        LeftOut = (sampL * (1 - EchoVolume)) + sampL2
        RightOut = (sampR * (1 - EchoVolume)) + sampR2

        Select Case SndChanels
            Case 1
                Select Case ME.TYPE
                    Case 260: _MemPut ME, ME.OFFSET + i, LeftOut As SINGLE ' 32-bit floating point
                    Case 132: _MemPut ME, ME.OFFSET + i, LeftOut * 2147483648 As LONG ' 32-bit integer
                    Case 130: _MemPut ME, ME.OFFSET + i, LeftOut * 32768 As INTEGER ' 16-bit integer
                    Case 1153: _MemPut ME, ME.OFFSET + i, 128 - (LeftOut * 128) As _UNSIGNED _BYTE ' 8-bit unsigned integer
                End Select
            Case 2
                Select Case ME.TYPE
                    Case 260: _MemPut ME, ME.OFFSET + i, LeftOut As SINGLE: _MemPut ME, ME.OFFSET + i + ME.ELEMENTSIZE \ 2, RightOut As SINGLE ' 32-bit floating point
                    Case 132: _MemPut ME, ME.OFFSET + i, LeftOut * 2147483648 As LONG: _MemPut ME, ME.OFFSET + i + ME.ELEMENTSIZE \ 2, RightOut * 2147483648 As LONG ' 32-bit integer
                    Case 130: _MemPut ME, ME.OFFSET + i, LeftOut * 32768 As INTEGER: _MemPut ME, ME.OFFSET + i + ME.ELEMENTSIZE \ 2, RightOut * 32768 As INTEGER ' 16-bit integer
                    Case 1153: _MemPut ME, ME.OFFSET + i, 128 - (LeftOut * 128) As _UNSIGNED _BYTE: _MemPut ME, ME.OFFSET + i + ME.ELEMENTSIZE \ 2, 128 - (128 * RightOut) As _UNSIGNED _BYTE ' 8-bit unsigned integer
                End Select
        End Select
        i = i + Multiply * 2
    Loop
    MakeEcho& = MakeEch&

    If ME.SIZE Then _MemFree ME
    If SourceSound.SIZE Then _MemFree SourceSound
End Function


Function GetBites (handle As _MEM)
    Select Case handle.TYPE
        Case 260: GetBites = 1 ' 32-bit floating point SINGLE
        Case 132: GetBites = 2 ' 32-bit integer LONG
        Case 130: GetBites = 3 ' 16-bit integer INTEGER
        Case 1153: GetBites = 4 ' 8-bit unsigned integer
    End Select
End Function


Sub PlaySound (handle As Long)
    Dim SampleData As _MEM
    Dim channels As _Unsigned _Byte
    Dim sampL As Single, sampR As Single
    Dim i As _Offset

    channels = SndChannels(handle)

    SampleData = _MemSound(handle, 0)
    If SampleData.SIZE = 0 Then
        Print "PlaySound: Sample data array is empty."
        Exit Sub
    End If

    Do Until i = SampleData.SIZE - SampleData.ELEMENTSIZE
        Select Case channels
            Case 1
                Select Case SampleData.TYPE
                    Case 260: sampL = _MemGet(SampleData, SampleData.OFFSET + i, Single) ' 32-bit floating point
                    Case 132: sampL = _MemGet(SampleData, SampleData.OFFSET + i, Long) / 2147483648 ' 32-bit integer
                    Case 130: sampL = _MemGet(SampleData, SampleData.OFFSET + i, Integer) / 32768 ' 16-bit integer
                    Case 1153: sampL = (_MemGet(SampleData, SampleData.OFFSET + i, _Unsigned _Byte) - 128) / 128 ' 8-bit unsigned integer
                End Select
            Case 2
                Select Case SampleData.TYPE
                    Case 260: sampL = _MemGet(SampleData, SampleData.OFFSET + i, Single): sampR = _MemGet(SampleData, SampleData.OFFSET + i + SampleData.ELEMENTSIZE / 2, Single) ' 32-bit floating point
                    Case 132: sampL = _MemGet(SampleData, SampleData.OFFSET + i, Long) / 2147483648: sampR = _MemGet(SampleData, SampleData.OFFSET + i + SampleData.ELEMENTSIZE / 2, Long) / 2147483648 ' 32-bit integer
                    Case 130: sampL = _MemGet(SampleData, SampleData.OFFSET + i, Integer) / 32768: sampR = _MemGet(SampleData, SampleData.OFFSET + i + SampleData.ELEMENTSIZE / 2, Integer) / 32768 ' 16-bit integer
                    Case 1153: sampL = (_MemGet(SampleData, SampleData.OFFSET + i, _Unsigned _Byte) - 128) / 128: sampR = (_MemGet(SampleData, SampleData.OFFSET + i + SampleData.ELEMENTSIZE / 2, _Unsigned _Byte) - 128) / 128 ' 8-bit unsigned integer
                End Select
        End Select
        If channels Mod 2 = 0 Then
            _SndRaw sampL, sampR 'stereo
        Else
            _SndRaw sampL, sampL 'mono = left channel in both speakers
        End If
        i = i + SampleData.ELEMENTSIZE
        Locate 20
        Print "PlaySound: Track time:"; CSng(ConvertOffset(i / SampleData.ELEMENTSIZE) / _SndRate); "[sec]    "
        Do Until _SndRawLen < 0.1: Loop
    Loop
    _MemFree SampleData
End Sub

Function ConvertOffset&& (value As _Offset)
    $Checking:Off
    Dim m As _MEM 'Define a memblock
    m = _Mem(value) 'Point it to use value
    $If 64BIT Then
            'On 64 bit OSes, an OFFSET is 8 bytes in size.  We can put it directly into an Integer64
            _MEMGET m, m.OFFSET, ConvertOffset&& 'Get the contents of the memblock and put the values there directly into ConvertOffset&&
    $Else
        'However, on 32 bit OSes, an OFFSET is only 4 bytes.  We need to put it into a LONG variable first
        _MemGet m, m.OFFSET, temp& 'Like this
        ConvertOffset&& = temp& 'And then assign that long value to ConvertOffset&&
    $End If
    _MemFree m 'Free the memblock
    $Checking:On
End Function


' This function returns the number of sound channels for a valid sound "handle"
' Note that we are assuming that the sound can have at most 2 channels
' In reality miniaudio can handle sounds with more than 2 channels
' 2 = stereo, 1 = mono, 0 = error
Function SndChannels~%% (handle As Long)
    Dim SampleData As _MEM
    ' Check if the sound is valid
    SampleData = _MemSound(handle, 0)
    If SampleData.SIZE = 0 Then
        Print "SndChannels: MemSound return ZERO for audio data size!"
        Exit Function
    End If

    ' Check the data type and then decide if the sound is stereo or mono
    Select Case SampleData.TYPE
        Case 260 ' 32-bit floating point
            If SampleData.ELEMENTSIZE = 4 Then
                SndChannels = 1
            ElseIf SampleData.ELEMENTSIZE = 8 Then
                SndChannels = 2
            End If
        Case 132 ' 32-bit integer
            If SampleData.ELEMENTSIZE = 4 Then
                SndChannels = 1
            ElseIf SampleData.ELEMENTSIZE = 8 Then
                SndChannels = 2
            End If
        Case 130: ' 16-bit integer
            If SampleData.ELEMENTSIZE = 2 Then
                SndChannels = 1
            ElseIf SampleData.ELEMENTSIZE = 4 Then
                SndChannels = 2
            End If
        Case 1153: ' 8-bit unsigned integer
            If SampleData.ELEMENTSIZE = 1 Then
                SndChannels = 1
            ElseIf SampleData.ELEMENTSIZE = 2 Then
                SndChannels = 2
            End If
    End Select
    _MemFree SampleData
End Function