04-01-2025, 03:52 PM
For the attempt at formant speech synthesis (see post #1, post #2),
I need to play a sound (in some cases square wave in others noise (whether white/pink/brown noise is TBD)
for a given duration (# of milliseconds)
at a given amplitude (input range is 0-32767)
where the sound starts at frequency1
and the frequency changes as the sound plays
so that at the end of duration it is at frequency2
(input range for frequency is 160-3951, but needs to be converted so it is in human hearing range).
I based the code on the _SndRaw example in the wiki,
maybe _SndRaw isn't what I want, but whatever command is used,
the end goal is to play a sound given the following parameters
Anyway, the code below is making sound, but it isn't really working the way I expect:
If someone could look at this and figure out why the frequency and amplitude aren't working as expected, and why you don't hear anything without SngToStr$ that would be a much appreciated, because I'm stuck!
I need to play a sound (in some cases square wave in others noise (whether white/pink/brown noise is TBD)
for a given duration (# of milliseconds)
at a given amplitude (input range is 0-32767)
where the sound starts at frequency1
and the frequency changes as the sound plays
so that at the end of duration it is at frequency2
(input range for frequency is 160-3951, but needs to be converted so it is in human hearing range).
I based the code on the _SndRaw example in the wiki,
maybe _SndRaw isn't what I want, but whatever command is used,
the end goal is to play a sound given the following parameters
- frequency1 = start frequency
- frequency2 = end frequency
- amplitude
- duration = milliseconds
- inflection = TBD
- vowel = TBD
- pitch = (offset for frequency)
- filter
- volume = offset for amplitude
- rate = how long sound plays (faster or slower should not affect frequency)
- stress = TBD
Anyway, the code below is making sound, but it isn't really working the way I expect:
- frequency isn't matching the values (it resets to low even if nextFreq is higher)
- frequency isn't changing as the sound plays
- what's really weird is the sound does not play if I disable the call to SngToStr$ (used to display the value of freqDiff in non-scientific notation)
- volume isn't getting gradually lower each iteration even though the value of amplitude is decreasing by even amounts
If someone could look at this and figure out why the frequency and amplitude aren't working as expected, and why you don't hear anything without SngToStr$ that would be a much appreciated, because I'm stuck!
Code: (Select All)
Const Pi = 4 * Atn(1)
Const Pi2 = 8 * Atn(1)
' SOUND PARAMTER VALUES
Dim frequency1 As Integer
Dim frequency2 As Integer
Dim amplitude As Integer
Dim duration As Integer
Dim inflection As Integer
Dim vowel As Integer
Dim pitch As Integer
Dim filter As Integer
Dim volume As Integer
Dim rate As Integer
Dim stress As Integer
' CONVERT INPUT VALUES TO VALUES THAT CAN BE PLAYED
Dim amplitude1 As Single
Dim freqDiff As Single ' how much to increase frequency to get to frequency2 by duration
Dim nextFreq As Integer ' the current frequency we are playing
' OTHER SOUND VARIABLES
Dim SampleRate&
Dim FRate As Single
Dim SndLoop!
' OTHER VARIABLES
Dim in$
' INITIALIZE INPUT VALUES
' If frequency1 <> frequency2
' then start at frequency1 and over duration
' raise/lower pitch so that by end we are playing frequency2
' value range = 160-3951
' (not sure if we need to convert it for _SNDRAW)
frequency1 = 160
frequency2 = frequency1 + 100
' value range = 0-32767
amplitude = 32767
' value range = 5-9
' not sure what time this represents, probably a fraction of a second
' do we need to convert this to # of samples, ticks (1/18th second), etc.?
duration = 7
' TBD:
inflection = 0
' TBD:
vowel = 1
' TBD (probably offset for frequency1, frequency2)
' currently always 64
pitch = 64
' TBD (probably frequency to filter)
' currently always 0
filter = 0
' TBD (probalby offset for amplitude)
' currently always 63
volume = 63
' speed at which sound plays (without affecting pitch)
' currently always 10
rate = 10
' TBD:
stress = 0
' PLAY THE SOUND, CHANGING THE VALUES EACH ITERATION
Do
' -----------------------------------------------------------------------------
' CONVERT INPUT VALUES TO PLAYABLE RANGE
' convert amplitude to amplitude1 which must be from -1.0 to 1.0
amplitude1 = (2 * (amplitude / 32767)) - 1
SampleRate& = _SndRate ' sets the sample rate
' calculate how much to change frequency each step
' to get from frequency1 to frequency2 in SampleRate& steps
freqDiff = (frequency2 - frequency1) / SampleRate&
nextFreq = frequency1
' -----------------------------------------------------------------------------
' SHOW VALUES FOR SOUND THAT WILL PLAY NEXT
Print "SOUND PARAMETERS TO PLAY NEXT:"
Print " frequency1 = " + _Trim$(Str$(frequency1))
Print " frequency2 = " + _Trim$(Str$(frequency2))
Print " amplitude = " + _Trim$(Str$(amplitude))
Print " amplitude1 = " + SngToStr$(amplitude1)
Print " duration = " + _Trim$(Str$(duration))
Print " SampleRate& = " + _Trim$(Str$(SampleRate&))
Print " freqDiff = " + SngToStr$(freqDiff)
Print
' -----------------------------------------------------------------------------
' PROMPT USER TO PLAY SOUND OR QUIT
Input "Press Enter to play the sound or Q to quit"; in$
in$ = Left$(UCase$(_Trim$(in$)), 1)
If in$ = "Q" Then Exit Do
' -----------------------------------------------------------------------------
' PLAY SOUND FOR DURATION
SndLoop! = 0 ' INIT COUNTER FOR DURATION
Do While SndLoop! < SampleRate&
FRate = nextFreq / SampleRate&
'_SNDRAW SIN((2 * 4 * ATN(1) * SndLoop! / SampleRate&) * frq!) * EXP(-(SndLoop! / SampleRate&) * 3)
'_SNDRAW amplitude1 * SIN(Pi2 * Duration * FRate) 'sine wave
_SndRaw amplitude1 * Sgn(Sin(Pi2 * duration * FRate)) ' square wave
' INCREMENT FREQUENCY BY AMOUNT DETERMINED, SO FINAL FREQUENCY IS frequency2:
nextFreq = nextFreq + freqDiff
' INCREMENT SAMPLE COUNTER
SndLoop! = SndLoop! + 1
Loop
_SndRawDone
Do: Loop While _SndRawLen ' flush the sound playing buffer
_Delay 0.2 ' Add a short delay after speaking, important for some sound cards.
' -----------------------------------------------------------------------------
' NOW CHANGE THE INPUT VALUES FOR NEXT STEP
' value range = 160-3951
frequency1 = frequency1 + 100
frequency2 = frequency1 + 100
If frequency1 > 3951 Then
frequency1 = 160: frequency2 = frequency1 + 100
End If
' value range = 5-9
duration = duration + 1
If duration > 9 Then duration = 5
' value range = 0-32767
amplitude = amplitude - 2000
If amplitude < 100 Then
amplitude = 32767
End If
Loop ' Until _KeyHit = 27
End
' /////////////////////////////////////////////////////////////////////////////
' TODO: verify this works
' Scientific notation - QB64 Wiki
' https://www.qb64.org/wiki/Scientific_notation
' Example: A string function that displays extremely small or large exponential decimal values.
Function SngToStr$ (n!)
value$ = UCase$(LTrim$(Str$(n!)))
Xpos% = InStr(value$, "D") + InStr(value$, "E") 'only D or E can be present
If Xpos% Then
expo% = Val(Mid$(value$, Xpos% + 1))
If Val(value$) < 0 Then
sign$ = "-": valu$ = Mid$(value$, 2, Xpos% - 2)
Else valu$ = Mid$(value$, 1, Xpos% - 1)
End If
dot% = InStr(valu$, "."): L% = Len(valu$)
If expo% > 0 Then add$ = String$(expo% - (L% - dot%), "0")
If expo% < 0 Then min$ = String$(Abs(expo%) - (dot% - 1), "0"): DP$ = "."
For n = 1 To L%
If Mid$(valu$, n, 1) <> "." Then num$ = num$ + Mid$(valu$, n, 1)
Next
Else SngToStr$ = value$: Exit Function
End If
SngToStr$ = _Trim$(sign$ + DP$ + min$ + num$ + add$)
End Function ' SngToStr$
