08-17-2022, 02:43 PM
(08-17-2022, 02:19 PM)mnrvovrfc Wrote: The following line could produce trouble:
_SndRaw Amplitude * Waveform
If that is computed to an absolute value greater than one, it's going to cause digital distortion. Check the code to make sure that value doesn't run out of control away from the range of -1.0 to 1.0.
Thanks for your reply. I tried your suggestion, replacing _SndRaw Amplitude * Waveform with
Code: (Select All)
Dim sngSoundRaw As Single
...
sngSoundRaw = Amplitude * Waveform
If sngSoundRaw < -1 Then
sngSoundRaw = -1
ElseIf sngSoundRaw > 1 Then
sngSoundRaw = 1
End If
_SndRaw sngSoundRaw
(line 230, 284-290 in the below code) but it's still making that noise.
How is it possible I can mess this up?
Code: (Select All)
' FM (Frequency modulation) sound with _SNDRAW
' http://www.qb64.net/forum/index.php?topic=11395.0
Const FALSE = 0
Const TRUE = Not FALSE
'FM_Sound_Test1
FM_Sound_Test2
End
' /////////////////////////////////////////////////////////////////////////////
' Plays 2 sounds based on angros47's parameters:
'
' 1. sounds okay for about a second, then just plays harsh noise without
' stopping - how do you turn it off without killing the program?
'
' 2. sounds pretty cool! But it goes on forever, does it ever stop?
' (how do you stop _SNDRAW sounds once they start playing?)
' TODO: check the value of _SNDRAWLEN and make sure it doesn't exceed
' 3 seconds, and don't add any more sounds until the value
' falls below that threshold.
Sub FM_Sound_Test2
Dim iSoundFrequency As Integer
Dim iSoundDuration As Integer
Dim iSoundMaxVolume As Integer
Dim sngCarrierAttack As Single
Dim sngCarrierDecay As Single
Dim sngCarrierSustain As Single
Dim sngCarrierRelease As Single
Dim iModulatorFrequency As Integer
Dim sngModulatorPhase As Single
Dim iModulatorMaxLevel As Integer
Dim sngSoundAttack As Single
Dim sngSoundDecay As Single
Dim sngSoundSustain As Single
Dim sngSoundRelease As Single
Dim in$
Do
Input "Press ENTER to play sound #1, 's' to skip, or 'q' to quit"; in$
If in$ = "q" Then Exit Do
If in$ <> "s" Then
iSoundFrequency = 500
iSoundDuration = 182
iSoundMaxVolume = 256
sngCarrierAttack = 0
sngCarrierDecay = 0.1
sngCarrierSustain = 0.01
sngCarrierRelease = 0.5
iModulatorFrequency = 500
sngModulatorPhase = 0.5
iModulatorMaxLevel = 30
sngSoundAttack = 0
sngSoundDecay = 0.1
sngSoundSustain = 0.5
sngSoundRelease = 0.6
FM_Sound _
iSoundFrequency, _
iSoundDuration, _
iSoundMaxVolume, _
sngCarrierAttack, _
sngCarrierDecay, _
sngCarrierSustain, _
sngCarrierRelease, _
iModulatorFrequency, _
sngModulatorPhase, _
iModulatorMaxLevel, _
sngSoundAttack, _
sngSoundDecay, _
sngSoundSustain, _
sngSoundRelease
End If
Do
_Limit 3000
Loop While _SndRawLen > 0
Input "Press ENTER to play sound #2, 's' to skip, or 'q' to quit"; in$
If in$ = "q" Then Exit Do
If in$ <> "s" Then
iSoundFrequency = 3000
iSoundDuration = 182
iSoundMaxVolume = 256
sngCarrierAttack = 0.5
sngCarrierDecay = 0.2
sngCarrierSustain = 1
sngCarrierRelease = 0.1
iModulatorFrequency = 10
sngModulatorPhase = 0.5
iModulatorMaxLevel = 1000
sngSoundAttack = 0.6
sngSoundDecay = 0.2
sngSoundSustain = 0.7
sngSoundRelease = 0.2
FM_Sound _
iSoundFrequency, _
iSoundDuration, _
iSoundMaxVolume, _
sngCarrierAttack, _
sngCarrierDecay, _
sngCarrierSustain, _
sngCarrierRelease, _
iModulatorFrequency, _
sngModulatorPhase, _
iModulatorMaxLevel, _
sngSoundAttack, _
sngSoundDecay, _
sngSoundSustain, _
sngSoundRelease
End If
Do
_Limit 3000
Loop While _SndRawLen > 0
Loop
End Sub ' FM_Sound_Test2
' /////////////////////////////////////////////////////////////////////////////
' This version prompts for parameters.
' TODO: simple mouse or keyboard interface for realtime input?
Sub FM_Sound_Test1
Dim iSoundFrequency As Integer
Dim iSoundDuration As Integer
Dim iSoundMaxVolume As Integer
Dim sngCarrierAttack As Single
Dim sngCarrierDecay As Single
Dim sngCarrierSustain As Single
Dim sngCarrierRelease As Single
Dim iModulatorFrequency As Integer
Dim sngModulatorPhase As Single
Dim iModulatorMaxLevel As Integer
Dim sngSoundAttack As Single
Dim sngSoundDecay As Single
Dim sngSoundSustain As Single
Dim sngSoundRelease As Single
Dim in$
Do
Print "--- Sound ---"
Input "Frequency"; iSoundFrequency
Input "Duration"; iSoundDuration
Input "Maximum Volume"; iSoundMaxVolume
Print "--- Carrier ---"
Input "Attack"; sngCarrierAttack
Input "Decay"; sngCarrierDecay
Input "Sustain"; sngCarrierSustain
Input "Release"; sngCarrierRelease
Print "--- Modulator ---"
Input "Frequency"; iModulatorFrequency
Input "Phase"; sngModulatorPhase
Input "Maximum level"; iModulatorMaxLevel
Print "--- ADSR ---"
Input "Attack"; sngSoundAttack
Input "Decay"; sngSoundDecay
Input "Sustain"; sngSoundSustain
Input "Release"; sngSoundRelease
FM_Sound _
iSoundFrequency, _
iSoundDuration, _
iSoundMaxVolume, _
sngCarrierAttack, _
sngCarrierDecay, _
sngCarrierSustain, _
sngCarrierRelease, _
iModulatorFrequency, _
sngModulatorPhase, _
iModulatorMaxLevel, _
sngSoundAttack, _
sngSoundDecay, _
sngSoundSustain, _
sngSoundRelease
Input "Type 'q' to quit or any key to continue"; in$
If in$ = "q" Then Exit Do
Loop
End Sub ' FM_Sound_Test1
' /////////////////////////////////////////////////////////////////////////////
' Version 2 of angros47's function, modified by madscijr:
' - more descriptive variable names,
' - user can press ESC to quit.
' TODO: check the value of _SNDRAWLEN and make sure it doesn't exceed
' 3 seconds, and don't add any more sounds until the value
' falls below that threshold.
' https://github.com/QB64Official/qb64/wiki/_SNDRAWLEN
' TODO: if user presses ESC, stop playing the sound. How??
Sub FM_Sound( _
iSoundFrequency as integer, _
iSoundDuration as integer, _
iSoundMaxVolume as integer, _
sngCarrierAttack as single, _
sngCarrierDecay as single, _
sngCarrierSustain as single, _
sngCarrierRelease as single, _
iModulatorFrequency as integer, _
sngModulatorPhase as single, _
iModulatorMaxLevel as integer, _
sngSoundAttack as single, _
sngSoundDecay as single, _
sngSoundSustain as single, _
sngSoundRelease as single)
Dim nSamples As Long
Dim CS As Single
Dim MS As Single
Dim CEnvelopeInc As Double
Dim CEnvelopeDecD As Double
Dim CEnvelopeDecR As Double
Dim MEnvelopeInc As Double
Dim MEnvelopeDecD As Double
Dim MEnvelopeDecR As Double
Dim iLoop As Integer
Dim sngSoundRaw As Single
nSamples = _SndRate * Int(iSoundDuration / 18.2) ' seconds
CS = 1 - sngCarrierAttack - sngCarrierDecay - sngCarrierRelease
MS = 1 - sngSoundAttack - sngSoundDecay - sngSoundRelease
CEnvelopeInc = 100 * iSoundMaxVolume / (nSamples * sngCarrierAttack + 1)
CEnvelopeDecD = 100 * iSoundMaxVolume * (1 - sngCarrierSustain) / (nSamples * sngCarrierDecay + 1)
CEnvelopeDecR = 100 * iSoundMaxVolume * sngCarrierSustain / (nSamples * sngCarrierRelease + 1)
sngCarrierDecay = sngCarrierDecay + sngCarrierAttack
CS = CS + sngCarrierDecay
sngCarrierRelease = sngCarrierRelease + CS
MEnvelopeInc = iModulatorMaxLevel / (nSamples * sngSoundAttack + 1)
MEnvelopeDecD = iModulatorMaxLevel * (1 - sngSoundSustain) / (nSamples * sngSoundDecay + 1)
MEnvelopeDecR = iModulatorMaxLevel * sngSoundSustain / (nSamples * sngSoundRelease + 1)
sngSoundDecay = sngSoundDecay + sngSoundAttack
MS = MS + sngSoundDecay
sngSoundRelease = sngSoundRelease + MS
Pi2 = 8 * Atn(1) '2 * pi
Amplitude = .000001
For iLoop = 0 To nSamples
Do While _SndRawLen > 3.0
_Limit 3000
If _KeyDown(27) Then Exit Do
Loop
If iLoop <= sngCarrierAttack * nSamples Then
Volume = Volume + CEnvelopeInc
ElseIf iLoop < sngCarrierDecay * nSamples Then
Volume = Volume - CEnvelopeDecD
ElseIf iLoop < CS * nSamples Then
ElseIf iLoop < sngCarrierRelease * nSamples Then
Volume = Volume - CEnvelopeDecR
End If
If iLoop <= sngSoundAttack * nSamples Then
Mamp = Mamp + MEnvelopeInc
ElseIf iLoop < sngSoundDecay * nSamples Then
Mamp = Mamp - MEnvelopeDecD
ElseIf iLoop < MS * nSamples Then
ElseIf iLoop < sngSoundRelease * nSamples Then
Mamp = Mamp - MEnvelopeDecR
End If
Modulator = Cos(Pi2 / _SndRate * iLoop * iModulatorFrequency + sngModulatorPhase) * Mamp
Waveform = Sin(Pi2 / _SndRate * iLoop * iSoundFrequency + Modulator) * Volume
sngSoundRaw = Amplitude * Waveform
If sngSoundRaw < -1 Then
sngSoundRaw = -1
ElseIf sngSoundRaw > 1 Then
sngSoundRaw = 1
End If
_SndRaw sngSoundRaw
If InKey$ = Chr$(27) Then Exit For ' GIVE THE USER A WAY TO EXIT
Next iLoop
Do
If InKey$ = Chr$(27) Then Exit Do ' GIVE THE USER A WAY TO EXIT
Loop While _SndRawLen
End Sub ' FM_Sound
' /////////////////////////////////////////////////////////////////////////////
' Original version of the code by angros47
' -----------------------------------------------------------------------------
' angros47
' « on: September 15, 2013, 12:19:04 pm »
' http://www.qb64.net/forum/index.php?topic=11395.0
'
' Years ago, I made a program to generate sound effects in FreeBasic...
' just for fun, I tried to port it to QB64, too (the _SNDRAW helped, of course).
' Have fun!
' -----------------------------------------------------------------------------
' LeChuck
' « Reply #1 on: September 15, 2013, 02:27:54 pm »
' http://www.qb64.net/forum/index.php?topic=11395.msg97452#msg97452
'
' Hey angros47,
' Can you add some demo values as well because I can't seem to generate any
' sound.
' Thanks
' No disaster occurs for any single reason.
' -----------------------------------------------------------------------------
' angros47
' « Reply #2 on: September 16, 2013, 08:03:22 am »
' http://www.qb64.net/forum/index.php?topic=11395.msg97464#msg97464
'
' Frequency 500
' Duration 182
' Maximum Volume 256
'
' Carrier
' Attack 0
' Decay 0.1
' Sustain 0.01
' Release 0.5
'
' Modulator
' Frequency 500
' Phase 0.5
' Maximum level 30
'
' Attack 0
' Decay 0.1
' Sustain 0.5
' Release 0.6
'
' Or
'
' Frequency 3000
' Duration 182
' Maximum Volume 256
'
' Carrier
' Attack 0.5
' Decay 0.2
' Sustain 1
' Release 0.1
'
' Modulator
' Frequency 10
' Phase 0.5
' Maximum level 1000
'
' Attack 0.6
' Decay 0.2
' Sustain 0.7
' Release 0.2
' -----------------------------------------------------------------------------
' OlDosLover
' « Reply #3 on: September 16, 2013, 06:54:06 pm »
' http://www.qb64.net/forum/index.php?topic=11395.msg97469#msg97469
'
' Hi all,
' Wow! Very impressive. I think this might be QB64's first sound generator.
' Thank you for sharing this valuable tool.
' OlDosLover.
' -----------------------------------------------------------------------------
Sub FM_Sound_v1
Input "Frequency"; Frequency
Input "Duration"; Duration
Input "Maximum Volume"; MaxVol
Print "--- Carrier ---"
Input "Attack"; ca
Input "Decay"; cd
Input "Sustain"; csl
Input "Release"; cr
Print "--- Modulator ---"
Input "Frequency"; MFrequency
Input "Phase"; ModStart
Input "Maximum level"; MaxModulator
Input "Attack"; Ma
Input "Decay"; md
Input "Sustain"; msl
Input "Release"; mr
Dim nSamples As Long
Dim CS As Single, MS As Single
nSamples = _SndRate * Int(Duration / 18.2) ' seconds
CS = 1 - ca - cd - cr
MS = 1 - Ma - md - mr
Dim CEnvelopeInc As Double, CEnvelopeDecD As Double, CEnvelopeDecR As Double
CEnvelopeInc = 100 * MaxVol / (nSamples * ca + 1)
CEnvelopeDecD = 100 * MaxVol * (1 - csl) / (nSamples * cd + 1)
CEnvelopeDecR = 100 * MaxVol * csl / (nSamples * cr + 1)
cd = cd + ca
CS = CS + cd
cr = cr + CS
Dim MEnvelopeInc As Double, MEnvelopeDecD As Double, MEnvelopeDecR As Double
MEnvelopeInc = MaxModulator / (nSamples * Ma + 1)
MEnvelopeDecD = MaxModulator * (1 - msl) / (nSamples * md + 1)
MEnvelopeDecR = MaxModulator * msl / (nSamples * mr + 1)
md = md + Ma
MS = MS + md
mr = mr + MS
Pi2 = 8 * Atn(1) '2 * pi
Amplitude = .000001
For i = 0 To nSamples
If i <= ca * nSamples Then
Volume = Volume + CEnvelopeInc
ElseIf i < cd * nSamples Then
Volume = Volume - CEnvelopeDecD
ElseIf i < CS * nSamples Then
ElseIf i < cr * nSamples Then
Volume = Volume - CEnvelopeDecR
End If
If i <= Ma * nSamples Then
Mamp = Mamp + MEnvelopeInc
ElseIf i < md * nSamples Then
Mamp = Mamp - MEnvelopeDecD
ElseIf i < MS * nSamples Then
ElseIf i < mr * nSamples Then
Mamp = Mamp - MEnvelopeDecR
End If
Modulator = Cos(Pi2 / _SndRate * i * MFrequency + ModStart) * Mamp
Waveform = Sin(Pi2 / _SndRate * i * Frequency + Modulator) * Volume
_SndRaw Amplitude * Waveform
Next
Do: Loop While _SndRawLen
End Sub ' FM_Sound_v1