QB64.org Forum

Active Forums => Programs => Topic started by: chlorophyll-zz on September 17, 2021, 12:20:53 pm

Title: Random Melodies and Chords (first complete and second rewrite - Bug Question)
Post by: chlorophyll-zz on September 17, 2021, 12:20:53 pm
I have wrote a Program that Generates Melodies and Chords (in F-Major Intervals).
Most of the variable names come from Geman, but I translated the Prints to english.
How you like this? (Modifications are always welcome)

Code: QB64: [Select]
  1. Dim tonmat(30) As Integer
  2. Dim diff(30) As Integer
  3. Dim m(28) As String:
  4. m(1) = "V50O1C": m(2) = "V50O1D": m(3) = "V50O1E": m(4) = "V40O1F": m(5) = "V40O1G": m(6) = "V40O1A": m(7) = "V40O1B"
  5. m(8) = "V30O2C": m(9) = "V30O2D": m(10) = "V30O2E": m(11) = "V20O2F": m(12) = "V20O2G": m(13) = "V20O2A": m(14) = "V20O2B"
  6. m(15) = "V20O3C": m(16) = "V20O3D": m(17) = "V10O3E": m(18) = "V10O3F": m(19) = "V10O3G": m(20) = "V10O3A": m(21) = "V10O3B"
  7. Dim lm(7) As Integer
  8. lm(1) = 1: lm(2) = 2: lm(3) = 4: lm(4) = 8: lm(5) = 16: lm(6) = 32: lm(7) = 64
  9.  
  10. Print "<<<***---Welcome to Melodies and Chords v0.0.4beta by chlorophyll-zz---***>>>"
  11. Print "Values: Default in brackets,"
  12. Print "For default press Enter."
  13. Print "Were starting ;-)"
  14. start:
  15. Input "Automatic or Manual Mode? a/m (a)"; auto
  16. If auto <> "m" Then auto = "a"
  17. If auto = "a" Then Print "Automatic Mode" Else Print "Manual Mode"
  18. If auto = "m" Then
  19.     Input "Lowest Basetone? (C1 to B2) 1-14(1)"; minton: If minton < 1 Or minton > 14 Then minton = 1
  20.     Print
  21.     Input "Highest Basetone? (C1 to B2) 1-14(14)"; maxton: If maxton < 1 Or maxton > 14 Then maxton = 14
  22.     Print
  23. If auto = "a" Then
  24.     minton = 1
  25.     maxton = 14
  26. Print "Basetone from "; Mid$(m(minton), 5, 3); " to "; Mid$(m(maxton), 5, 3)
  27. If auto = "m" Then
  28.     Print "Maximum Duration of Tones? 1/ 1-2-4-8-16"
  29.     Input "                              1 2 3 4  5  1-6 (2) "; minlang
  30.     Print
  31.     If minlang < 1 Or minlang > 5 Then minlang = 2
  32.  
  33.     Print "Minimum Duration of Tones? 1/ 1-2-4-8-16"
  34.     Input "                              1 2 3 4  5  1-6 (4) "; maxlang
  35.     Print
  36.     If maxlang < 1 Or maxlang > 5 Then maxlang = 4
  37. If auto = "a" Then
  38.     minlang = 2
  39.     maxlang = 4
  40. Print "Duration 1/" + LTrim$(Str$(lm(minlang))) + " to 1/"; LTrim$(Str$(lm(maxlang)))
  41. If auto = "m" Then
  42.     Dim aws As String
  43.     Input "How much Chords Possibility in %? 0-100(25)"; aws: If aws = "" Or Val(aws) > 100 Then aws = "25"
  44.     Print
  45.     proz = Val(aws)
  46. If auto = "a" Then proz = 25
  47. Print LTrim$(Str$(proz)) + "% Chords Possibility"
  48. If auto = "m" Then
  49.     Input "1. Interval from Basetone? 1-7? (C-E=2) (2)"; intmin: If intmin < 1 Or intmin > 7 Then intmin = 2
  50.     Print
  51.     Input "2. Interval from Basetone? 1-7? (C-G=4) (4)"; intmax: If intmax < 1 Or intmax > 7 Then intmax = 4
  52.     Print
  53. If auto = "a" Then
  54.     intmin = 2
  55.     intmax = 4
  56. Print "1. Interval"; intmin; "Tones from Basetone, 2. Interval"; intmax; "Tones from Basetone"
  57.  
  58. debug = 0
  59.     anfang = anfang + 1
  60.     If anfang = 1 Then
  61.         For i = 20 To 1 Step -1
  62.             tonmat(i) = CInt(Rnd * (maxton - minton)) + minton
  63.             If debug = 1 Then Print ; Str$(i) + ". Tone:" + Str$(tonmat(i)) + ",";
  64.             diff(i) = tonmat(i) - tonmat(i + 1)
  65.             If debug = 1 Then Print "Difference " + Str$(i); ". "; diff(i)
  66.         Next i
  67.  
  68.     End If
  69.  
  70.     t = CInt(Rnd * (maxton - minton)) + minton
  71.     If debug = 1 And (t < 1 Or t > 28) Then Print "Tone below 1 or over 28 t="; t: Input t
  72.     n = m(t)
  73.  
  74.     l = CInt(Rnd * (maxlang - minlang)) + minlang
  75.     If debug = 1 And (l < 1 Or l > 7) Then Print "Duration below 1 or over 7 l="; l: Input l
  76.  
  77.  
  78.     lt = lm(l)
  79.  
  80.     rn = Rnd
  81.     If rn < (proz / 100) Then
  82.         akk = 1
  83.         n = n + "," + m(t + intmin) + "," + m(t + intmax)
  84.     End If
  85.  
  86.     If akk = 1 And l >= minlang + 1 And Rnd * .66 >= (proz / 100) Then
  87.         lt = lm(minlang)
  88.     End If
  89.     akk = 0
  90.  
  91.     If rn > (proz / 100) Then n = m(t)
  92.  
  93.     tone = "L" + LTrim$(Str$(lt)) + n
  94.  
  95.     z = z + 1
  96.     If z = 1 Then
  97.         Print "<<<*-For Restart press n-*>>>"
  98.         Print
  99.     End If
  100.     If z = 21 Then z = 0
  101.  
  102.     Print "Duration: 1/" + LTrim$(Str$(lt)) + Chr$(9) + "Note(s): ";
  103.     If Len(n) > 6 Then Print ; Mid$(n, 5, 2) + ", " + Mid$(n, 12, 2) + ", "; Mid$(n, 19, 2)
  104.     If Len(n) = 6 Then Print ; Mid$(n, 5, 2)
  105.     'V25O1C,V25O1G,V25A1
  106.     Print
  107.     Play tone
  108.  
  109.  
  110.  
  111.     For i = 20 To 1 Step -1
  112.         tonmat(i) = tonmat(i - 1)
  113.         If i = 1 Then tonmat(i) = t
  114.         If debug = 1 Then Print ; Str$(i) + ". Tone:" + Str$(tonmat(i)) + ",";
  115.         diff(i) = tonmat(i) - tonmat(i + 1)
  116.         If debug = 1 Then Print "Difference " + Str$(i); ". "; diff(i)
  117.     Next i
  118.  
  119.     If debug = 1 Then Sleep
  120.  
  121.     x = _KeyHit
  122.     If x Then
  123.         If x = -110 Then
  124.             x = 0
  125.             GoTo start:
  126.         End If
  127.     End If
  128.  

I have also rewrote the Program to have FM-Synth, because a tester suggested that.
First the Program did run as expected but then I edited something I forgot, and the Chords are now sounding garbled.
I think something is wrong about _SNDRAW or _SNDRAWLEN .
Could you have a quick look? Thank you in advance.

Code: QB64: [Select]
  1. Dim fm(88) As Double
  2. Dim tn(88) As String
  3.  
  4. Print "Welcone to Random Tones and Chords Generator v0.1b"
  5. Input "Please specify Chord possibility in % (25)"; uaw$
  6. If uaw$ = "" Or Val(uaw$) < 0 Or Val(uaw$) > 100 Then
  7.     aw = 25
  8. Else aw = Val(uaw$)
  9. Print "Chords possibility:"; aw; "%"
  10. bpm = 120
  11. 'interval 1 and 2 (semitones)
  12. int1 = 2
  13. int2 = 7
  14. 'interval possibility percent
  15. 'aw = 25
  16. 'tone minimum and maximum
  17. tmin = 35
  18. tmax = 47
  19. 'lengths min and max
  20. lmin = 2
  21. lmax = 4
  22. 'fill the tone names
  23. For i = 1 To 88
  24.     f = ((2 ^ ((i - 49) / 12)) * 440)
  25.     fm(i) = f
  26.     Select Case i Mod 12
  27.         Case 1: tn(i) = "A"
  28.         Case 2: tn(i) = "A#"
  29.         Case 3: tn(i) = "B"
  30.         Case 4: tn(i) = "C"
  31.         Case 5: tn(i) = "C#"
  32.         Case 6: tn(i) = "D"
  33.         Case 7: tn(i) = "D#"
  34.         Case 8: tn(i) = "E"
  35.         Case 9: tn(i) = "F"
  36.         Case 10: tn(i) = "F#"
  37.         Case 11: tn(i) = "G"
  38.         Case 0: tn(i) = "G#"
  39.     End Select
  40.     'Select Case Int(i / 12)
  41.     '    Case 0: tn(i) = tn(i) + "0"
  42.  
  43.     'End Select
  44.     'DEBUG
  45.     'Print f; tn(i)
  46.     'If i > 24 Then Sleep
  47.  
  48. 'delete all sharp tones
  49. For i = 1 To 88
  50.     If Mid$(tn(i), 2, 1) = "#" Then
  51.         tn(i) = tn(i - 1)
  52.         fm(i) = fm(i - 1)
  53.     End If
  54. 'DEBUG fm(49)="A"
  55. 'Print fm(49)
  56.  
  57. 'fill the lengths
  58. For i = 1 To 7
  59.     lm(i) = 2 ^ i / 2
  60.     'DEBUG
  61.     'Print lm(i)
  62.  
  63.  
  64.     'tones between tmin and tmax
  65.     i = CInt((Rnd * (tmax - tmin)) + tmin)
  66.  
  67.     lr = lm(CInt(Rnd * (lmax - lmin)) + lmin) 'Length out of the lenght fields between 2 and 4
  68.     l = 1 / lr * 60 / bpm * 4 'Lenght is parts of a second from the length fields multiplied by bpm
  69.  
  70.     If Rnd < aw / 100 Then
  71.         akk = 1
  72.     Else akk = 0
  73.     End If
  74.     t = 0
  75.     akk = 1
  76.     If akk = 1 Then
  77.         Print "Length 1/"; LTrim$(Str$(lr)); " Tone:"; tn(i); "+"; tn(i + 4); "+"; tn(i + 7)
  78.     Else Print "Length 1/"; LTrim$(Str$(lr)); " Tone:"; tn(i)
  79.     End If
  80.     Do
  81.         'queue some sound
  82.         Do While t < l 'you may wish to adjust this
  83.             sample = Sin(t * fm(i) * Atn(1) * 8) '440Hz sine wave (t * 440 * 2Ï€)
  84.             sample = sample * Exp(-t * l) 'fade out eliminates clicks after sound
  85.             _SndRaw sample
  86.             t = t + 1 / _SndRate 'sound card sample frequency determines time
  87.  
  88.             If akk = 1 Then
  89.                 sample2 = Sin(t * fm(i + int1) * Atn(1) * 8)
  90.                 sample2 = sample2 * Exp(-t * l)
  91.                 _SndRaw sample2
  92.                 sample3 = Sin(t * fm(i + int2) * Atn(1) * 8)
  93.                 sample3 = sample3 * Exp(-t * l)
  94.                 _SndRaw sample3
  95.                 t = t + 2 / _SndRate
  96.             End If
  97.  
  98.  
  99.         Loop
  100.  
  101.  
  102.     Loop While t < l 'play for l seconds
  103.  
  104.     Do While _SndRawLen > 0 'Finish any left over queued sound!
  105.     Loop
  106.  
  107.     in = InKey$
  108. Loop While in = ""
  109.  
Title: Re: Random Melodies and Chords (first complete and second rewrite - Bug Question)
Post by: bplus on September 17, 2021, 12:31:19 pm
Hi welcome @chlorophyll-zz

Are you talking about slight buzz I am getting on chords or do I have too many crumbs in/on my speakers?

Sorry don't know much about sound Petr pretty good and Dav, speaking of whom have you checked this out?
https://www.qb64.org/forum/index.php?topic=2563.0
 
Title: Re: Random Melodies and Chords (first complete and second rewrite - Bug Question)
Post by: chlorophyll-zz on September 17, 2021, 01:21:13 pm
Are you talking about slight buzz I am getting on chords or do I have too many crumbs in/on my speakers?
Yes, the Buzz is the problem.

I looked in the Program you suggested, and that Program uses a slightly different method, _SNDPLAY, to play the sound of the Drum-Machine.
My Program uses _SNDRAW to generate the sound waves sample by sample, which it does obviously wrong in some way.

Title: Re: Random Melodies and Chords (first complete and second rewrite - Bug Question)
Post by: bplus on September 17, 2021, 01:26:48 pm
Here's a good reference, Fellippe's tut on sound, there is something new to QB64 about capturing wave patterns:
https://www.qb64.org/forum/index.php?topic=3919.0
Title: Re: Random Melodies and Chords (first complete and second rewrite - Bug Question)
Post by: chlorophyll-zz on September 17, 2021, 01:50:49 pm
Here's a good reference, Fellippe's tut on sound, there is something new to QB64 about capturing wave patterns:
https://www.qb64.org/forum/index.php?topic=3919.0
Thank you. I saw the Video a few days ago and that was the reason I discovered FM Synth Sample generation with _SNDRAW.
For the Video part with _SNDRAW, he basically covers the Example code from _SNDRAW in the Wiki,
which I also modified to get the samples. I think I will have to investigate further what it takes to generate a chord,
because I think I have made somehow a wrong assumption that I could just play the _SNDRAW samples after another to generate the Chords.
Title: Re: Random Melodies and Chords (first complete and second rewrite - Bug Question)
Post by: chlorophyll-zz on September 17, 2021, 02:20:32 pm
I think I discovered a bug. Even with a slighly modified example code from _SNDRAW Wiki, the buzz is there:
The Problem is not present with 2 Samples, but appears after 3 Samples and persists with any new Sample that you play after another.

Code: QB64: [Select]
  1. t = 0
  2. tmp$ = "Sample = ##.#####   Time = ##.#####"
  3. Locate 1, 60: Print "Rate:"; _SndRate
  4.     'queue some sound
  5.     Do While _SndRawLen < 3 'you may wish to adjust this
  6.         sample = Sin(t * 440 * Atn(1) * 8) '440Hz sine wave (t * 440 * 2Ï€)
  7.         sample = sample * Exp(-t * 3) 'fade out eliminates clicks after sound
  8.         _SndRaw sample
  9.         t = t + 1 / _SndRate 'sound card sample frequency determines time
  10.         sample = Sin(t * 220 * Atn(1) * 8) '440Hz sine wave (t * 440 * 2Ï€)
  11.         sample = sample * Exp(-t * 3) 'fade out eliminates clicks after sound
  12.         _SndRaw sample
  13.         t = t + 1 / _SndRate
  14.         sample = Sin(t * 220 * Atn(1) * 8) '440Hz sine wave (t * 440 * 2Ï€)
  15.         sample = sample * Exp(-t * 3) 'fade out eliminates clicks after sound
  16.         _SndRaw sample
  17.         t = t + 1 / _SndRate
  18.  
  19.     Loop
  20.  
  21.     'do other stuff, but it may interrupt sound
  22.     Locate 1, 1: Print Using tmp$; sample; t
  23. Loop While t < 3.0 'play for 3 seconds
  24.  
  25. Do While _SndRawLen > 0 'Finish any left over queued sound!
  26.  
  27.  
Title: Re: Random Melodies and Chords (first complete and second rewrite - Bug Question)
Post by: chlorophyll-zz on September 17, 2021, 02:26:14 pm
I have edited the code, so it only play two tones in a chord (now without buzz).
I suggest 100% Chords possibility for maximal comfort, thats why I made it default ;-)

Code: QB64: [Select]
  1. Dim fm(88) As Double
  2. Dim tn(88) As String
  3.  
  4. Print "Welcone to Random Tones and Chords Generator v0.1b"
  5. Input "Please specify Chord possibility in % (25)"; uaw$
  6. If uaw$ = "" Or Val(uaw$) < 0 Or Val(uaw$) > 100 Then
  7.     aw = 100
  8. Else aw = Val(uaw$)
  9. Print "Chords possibility:"; aw; "%"
  10. bpm = 120
  11. 'interval 1 and 2 (semitones)
  12. int1 = 4
  13. int2 = 7
  14. 'interval possibility percent
  15. 'aw = 25
  16. 'tone minimum and maximum
  17. tmin = 35
  18. tmax = 47
  19. 'lengths min and max
  20. lmin = 2
  21. lmax = 4
  22. 'fill the tone names
  23. For i = 1 To 88
  24.     f = ((2 ^ ((i - 49) / 12)) * 440)
  25.     fm(i) = CInt(f)
  26.     Select Case i Mod 12
  27.         Case 1: tn(i) = "A"
  28.         Case 2: tn(i) = "A#"
  29.         Case 3: tn(i) = "B"
  30.         Case 4: tn(i) = "C"
  31.         Case 5: tn(i) = "C#"
  32.         Case 6: tn(i) = "D"
  33.         Case 7: tn(i) = "D#"
  34.         Case 8: tn(i) = "E"
  35.         Case 9: tn(i) = "F"
  36.         Case 10: tn(i) = "F#"
  37.         Case 11: tn(i) = "G"
  38.         Case 0: tn(i) = "G#"
  39.     End Select
  40.     'Select Case Int(i / 12)
  41.     '    Case 0: tn(i) = tn(i) + "0"
  42.  
  43.     'End Select
  44.     'DEBUG
  45.     'Print f; tn(i)
  46.     'If i > 24 Then Sleep
  47.  
  48. 'delete all sharp tones
  49. For i = 1 To 88
  50.     If Mid$(tn(i), 2, 1) = "#" Then
  51.         tn(i) = tn(i - 1)
  52.         fm(i) = fm(i - 1)
  53.     End If
  54. 'DEBUG fm(49)="A"
  55. 'Print fm(49)
  56.  
  57. 'fill the lengths
  58. For i = 1 To 7
  59.     lm(i) = 2 ^ i / 2
  60.     'DEBUG
  61.     'Print lm(i)
  62.  
  63.  
  64.     'tones between tmin and tmax
  65.     i = CInt((Rnd * (tmax - tmin)) + tmin)
  66.  
  67.     lr = lm(CInt(Rnd * (lmax - lmin)) + lmin) 'Length out of the lenght fields between 2 and 4
  68.     l = 1 / lr * 60 / bpm * 4 'Lenght is parts of a second from the length fields multiplied by bpm
  69.  
  70.     If Rnd < aw / 100 Then
  71.         akk = 1
  72.     Else akk = 0
  73.     End If
  74.     t = 1 / _SndRate
  75.     If akk = 1 Then
  76.         Print "Length 1/"; LTrim$(Str$(lr)); " Tone:"; tn(i); "+"; tn(i + int1) '; "+"; tn(i + 7)
  77.     Else Print "Length 1/"; LTrim$(Str$(lr)); " Tone:"; tn(i)
  78.     End If
  79.     Do
  80.         'queue some sound
  81.         Do While t < l 'you may wish to adjust this
  82.             sample = Sin(t * fm(i) * Atn(1) * 8) '440Hz sine wave (t * 440 * 2Ï€)
  83.             sample = sample * Exp(-t * l) 'fade out eliminates clicks after sound
  84.             _SndRaw sample
  85.             t = t + 1 / _SndRate 'sound card sample frequency determines time
  86.  
  87.             If akk = 1 Then
  88.                 sample2 = Sin(t * fm(i + int1) * Atn(1) * 8)
  89.                 sample2 = sample2 * Exp(-t * l)
  90.                 _SndRaw sample2
  91.                 t = t + 1 / _SndRate
  92.                 'sample = Sin(t * fm(i + int2) * Atn(1) * 8)
  93.                 'sample = sample3 * Exp(-t * l)
  94.                 '_SndRaw sample
  95.                 't = t + 1 / _SndRate
  96.             End If
  97.  
  98.         Loop
  99.  
  100.  
  101.     Loop While t < l 'play for l seconds
  102.  
  103.     Do While _SndRawLen > 0 'Finish any left over queued sound!
  104.     Loop
  105.  
  106.  
  107.     in = InKey$
  108. Loop While in = ""
  109.  
  110.  
Title: Re: Random Melodies and Chords (first complete and second rewrite - Bug Question)
Post by: Petr on September 17, 2021, 02:42:53 pm
Hi. Try, if this is repaired or is still broken:

Code: QB64: [Select]
  1. DIM fm(88) AS DOUBLE
  2. DIM tn(88) AS STRING
  3.  
  4. PRINT "Welcone to Random Tones and Chords Generator v0.1b"
  5. INPUT "Please specify Chord possibility in % (25)"; uaw$
  6. IF uaw$ = "" OR VAL(uaw$) < 0 OR VAL(uaw$) > 100 THEN
  7.     aw = 25
  8. ELSE aw = VAL(uaw$)
  9. PRINT "Chords possibility:"; aw; "%"
  10. bpm = 120
  11. 'interval 1 and 2 (semitones)
  12. int1 = 2
  13. int2 = 7
  14. 'interval possibility percent
  15. 'aw = 25
  16. 'tone minimum and maximum
  17. tmin = 35
  18. tmax = 47
  19. 'lengths min and max
  20. lmin = 2
  21. lmax = 4
  22. 'fill the tone names
  23. FOR i = 1 TO 88
  24.     f = ((2 ^ ((i - 49) / 12)) * 440)
  25.     fm(i) = f
  26.     SELECT CASE i MOD 12
  27.         CASE 1: tn(i) = "A"
  28.         CASE 2: tn(i) = "A#"
  29.         CASE 3: tn(i) = "B"
  30.         CASE 4: tn(i) = "C"
  31.         CASE 5: tn(i) = "C#"
  32.         CASE 6: tn(i) = "D"
  33.         CASE 7: tn(i) = "D#"
  34.         CASE 8: tn(i) = "E"
  35.         CASE 9: tn(i) = "F"
  36.         CASE 10: tn(i) = "F#"
  37.         CASE 11: tn(i) = "G"
  38.         CASE 0: tn(i) = "G#"
  39.     END SELECT
  40.     'Select Case Int(i / 12)
  41.     '    Case 0: tn(i) = tn(i) + "0"
  42.  
  43.     'End Select
  44.     'DEBUG
  45.     'Print f; tn(i)
  46.     'If i > 24 Then Sleep
  47.  
  48. 'delete all sharp tones
  49. FOR i = 1 TO 88
  50.     IF MID$(tn(i), 2, 1) = "#" THEN
  51.         tn(i) = tn(i - 1)
  52.         fm(i) = fm(i - 1)
  53.     END IF
  54. 'DEBUG fm(49)="A"
  55. 'Print fm(49)
  56.  
  57. 'fill the lengths
  58. FOR i = 1 TO 7
  59.     lm(i) = 2 ^ i / 2
  60.     'DEBUG
  61.     'Print lm(i)
  62.  
  63.  
  64.     'tones between tmin and tmax
  65.     i = CINT((RND * (tmax - tmin)) + tmin)
  66.  
  67.     lr = lm(CINT(RND * (lmax - lmin)) + lmin) 'Length out of the lenght fields between 2 and 4
  68.     l = 1 / lr * 60 / bpm * 4 'Lenght is parts of a second from the length fields multiplied by bpm
  69.  
  70.     IF RND < aw / 100 THEN
  71.         akk = 1
  72.     ELSE akk = 0
  73.     END IF
  74.     t = 0
  75.     akk = 1
  76.     IF akk = 1 THEN
  77.         PRINT "Length 1/"; LTRIM$(STR$(lr)); " Tone:"; tn(i); "+"; tn(i + 4); "+"; tn(i + 7)
  78.     ELSE PRINT "Length 1/"; LTRIM$(STR$(lr)); " Tone:"; tn(i)
  79.     END IF
  80.     DO
  81.         'queue some sound
  82.         DO WHILE t < l 'you may wish to adjust this
  83.             sample = SIN(t * fm(i) * ATN(1) * 8) '440Hz sine wave (t * 440 * 2?)
  84.             sample = sample * EXP(-t * l) 'fade out eliminates clicks after sound
  85.             '_SNDRAW sample
  86.             t = t + 1 / _SNDRATE 'sound card sample frequency determines time
  87.  
  88.             IF akk = 1 THEN
  89.                 sample2 = SIN(t * fm(i + int1) * ATN(1) * 8)
  90.                 sample2 = sample2 * EXP(-t * l)
  91.                 '   _SNDRAW sample2
  92.                 sample = (sample + sample2) / 2
  93.  
  94.                 sample3 = SIN(t * fm(i + int2) * ATN(1) * 8)
  95.                 sample3 = sample3 * EXP(-t * l)
  96.                 '   _SNDRAW sample3
  97.  
  98.                 sample = (sample + sample3) / 2
  99.  
  100.                 t = t + 2 / _SNDRATE
  101.             END IF
  102.  
  103.             _SNDRAW sample
  104.  
  105.         LOOP
  106.  
  107.  
  108.     LOOP WHILE t < l 'play for l seconds
  109.  
  110.     DO WHILE _SNDRAWLEN < 0 'Finish any left over queued sound!
  111.     LOOP
  112.  
  113.     in = INKEY$
  114. LOOP WHILE in = ""
  115.  
  116.  
  117.  

Title: Re: Random Melodies and Chords (first complete and second rewrite - Bug Question)
Post by: Petr on September 17, 2021, 02:52:26 pm
Very nice work, this all! Good job! I like it!
Title: Re: Random Melodies and Chords (first complete and second rewrite - Bug Question)
Post by: Petr on September 17, 2021, 03:15:19 pm
I also encountered problems with slowing down or distorting the sound if I called the _SNDRAW command multiple times in a row. In order for the audio data to be transmitted as I intended, I first mix it using a mathematical average, and only finally, when the data stream is ready, I pass it all at once to the _SNDRAW command. This is the most reliable way to achieve the expected result.
Title: Re: Random Melodies and Chords (first complete and second rewrite - Bug Question)
Post by: chlorophyll-zz on September 17, 2021, 03:21:14 pm
Hi. Try, if this is repaired or is still broken:

The tones are now double the frequency and half the duration as they should.
But the Buzz is gone.

I edited your edits in to the example code. Is this right?
I think the t is only added 1/_SNDRATE because only one sample gets played.

This produces one tone, I think of 440 Hz.

Code: QB64: [Select]
  1. t = 0
  2. tmp$ = "Sample = ##.#####   Time = ##.#####"
  3. Locate 1, 60: Print "Rate:"; _SndRate
  4.     'queue some sound
  5.     Do While _SndRawLen < 3 'you may wish to adjust this
  6.         sample1 = Sin(t * 440 * Atn(1) * 8) '440Hz sine wave (t * 440 * 2Ï€)
  7.         sample1 = sample1 * Exp(-t * 3) 'fade out eliminates clicks after sound
  8.         '_SndRaw sample
  9.         t = t + 1 / _SndRate 'sound card sample frequency determines time
  10.  
  11.         sample2 = Sin(t * 110 * Atn(1) * 8) '440Hz sine wave (t * 440 * 2Ï€)
  12.         sample2 = sample2 * Exp(-t * 3) 'fade out eliminates clicks after sound
  13.         '_SndRaw sample
  14.         't = t + 1 / _SndRate
  15.  
  16.         sample1 = (sample1 + sample2) / 2
  17.  
  18.         sample3 = Sin(t * 220 * Atn(1) * 8) '440Hz sine wave (t * 440 * 2Ï€)
  19.         sample3 = sample3 * Exp(-t * 3) 'fade out eliminates clicks after sound
  20.  
  21.         sample1 = (sample1 + sample3) / 2
  22.  
  23.         't = t + 1 / _SndRate
  24.         _SndRaw sample1
  25.     Loop
  26.  
  27.  
  28.     'do other stuff, but it may interrupt sound
  29.     Locate 1, 1: Print Using tmp$; sample; t
  30. Loop While t < 3.0 'play for 3 seconds
  31.  
  32. Do While _SndRawLen > 0 'Finish any left over queued sound!
Title: Re: Random Melodies and Chords (first complete and second rewrite - Bug Question)
Post by: Petr on September 17, 2021, 04:11:16 pm
You are very right. So I'm trying everything here now. Well, I think I've found a solution. Also, thanks to that, I finally understood what the _SNDRAWOPEN command was doing, which I had quietly trodden around in the past and couldn't understand. And now your clear code makes perfect sense to him. This ensures that the individual sounds do not mix. This program should work properly, it shows what I tried. I think that's what you're looking for.

Code: QB64: [Select]
  1. t = 0
  2. tmp$ = "Sample = ##.#####   Time = ##.#####"
  3. LOCATE 1, 60: PRINT "Rate:"; _SNDRATE
  4.  
  5.  
  6.     'queue some sound
  7.     DO UNTIL t >= 3 'you may wish to adjust this
  8.         sample1 = SIN(t * 440 * ATN(1) * 8) '440Hz sine wave (t * 440 * 2?)
  9.         sample1 = sample1 * EXP(-t * 3) 'fade out eliminates clicks after sound
  10.         '_SndRaw sample
  11.         ' t = t + 1 / _SNDRATE 'sound card sample frequency determines time
  12.  
  13.         sample2 = SIN(t * 110 * ATN(1) * 8) '440Hz sine wave (t * 440 * 2?)
  14.         sample2 = sample2 * EXP(-t * 3) 'fade out eliminates clicks after sound
  15.         '_SndRaw sample
  16.         ' t = t + 1 / _SndRate
  17.  
  18.         '  sample1 = (sample1 + sample2) / 2
  19.  
  20.         sample3 = SIN(t * 220 * ATN(1) * 8) '440Hz sine wave (t * 440 * 2?)
  21.         sample3 = sample3 * EXP(-t * 3) 'fade out eliminates clicks after sound
  22.  
  23.         ' sample1 = (sample1 + sample3) / 2
  24.  
  25.         t = t + 1 / _SNDRATE
  26.         _SNDRAW sample1, , S1
  27.         _SNDRAWDONE
  28.         _SNDRAW sample2, , S2
  29.         _SNDRAWDONE
  30.         _SNDRAW sample3, , S3
  31.         _SNDRAWDONE
  32.  
  33.         '   _SNDRAW sample1
  34.         '   _SNDRAW sample2
  35.         '   _SNDRAW sample3
  36.  
  37.         LOCATE 1, 1: PRINT USING tmp$; sample; t
  38.  
  39.  
  40.     LOOP
  41.  
  42.     _SNDCLOSE S1
  43.     _SNDCLOSE S2
  44.     _SNDCLOSE S3
  45.  
  46.     'do other stuff, but it may interrupt sound
  47.     LOCATE 1, 1: PRINT USING tmp$; sample; t
  48. LOOP WHILE t < 3.0 'play for 3 seconds
  49.  
  50. DO WHILE _SNDRAWLEN > 0 'Finish any left over queued sound!
  51.  
  52.  
Title: Re: Random Melodies and Chords (first complete and second rewrite - Bug Question)
Post by: bplus on September 17, 2021, 05:30:22 pm
Did that fix the melodies?
Title: Re: Random Melodies and Chords (first complete and second rewrite - Bug Question)
Post by: chlorophyll-zz on September 18, 2021, 12:14:57 am
Well, I think I've found a solution.
I cant test right now, but this looks very promising. I will test and work your example code in my Program and write back to you. Later ;-)
Title: Re: Random Melodies and Chords (first complete and second rewrite - Bug Question)
Post by: chlorophyll-zz on September 19, 2021, 01:24:15 am
Well, I think I've found a solution.

...

Code: QB64: [Select]
  1. t = 0
  2. tmp$ = "Sample = ##.#####   Time = ##.#####"
  3. LOCATE 1, 60: PRINT "Rate:"; _SNDRATE
  4.  
  5.  
  6.     'queue some sound
  7.     DO UNTIL t >= 3 'you may wish to adjust this
  8.         sample1 = SIN(t * 440 * ATN(1) * 8) '440Hz sine wave (t * 440 * 2?)
  9.         sample1 = sample1 * EXP(-t * 3) 'fade out eliminates clicks after sound
  10.         '_SndRaw sample
  11.         ' t = t + 1 / _SNDRATE 'sound card sample frequency determines time
  12.  
  13.         sample2 = SIN(t * 110 * ATN(1) * 8) '440Hz sine wave (t * 440 * 2?)
  14.         sample2 = sample2 * EXP(-t * 3) 'fade out eliminates clicks after sound
  15.         '_SndRaw sample
  16.         ' t = t + 1 / _SndRate
  17.  
  18.         '  sample1 = (sample1 + sample2) / 2
  19.  
  20.         sample3 = SIN(t * 220 * ATN(1) * 8) '440Hz sine wave (t * 440 * 2?)
  21.         sample3 = sample3 * EXP(-t * 3) 'fade out eliminates clicks after sound
  22.  
  23.         ' sample1 = (sample1 + sample3) / 2
  24.  
  25.         t = t + 1 / _SNDRATE
  26.         _SNDRAW sample1, , S1
  27.         _SNDRAWDONE
  28.         _SNDRAW sample2, , S2
  29.         _SNDRAWDONE
  30.         _SNDRAW sample3, , S3
  31.         _SNDRAWDONE
  32.  
  33.         '   _SNDRAW sample1
  34.         '   _SNDRAW sample2
  35.         '   _SNDRAW sample3
  36.  
  37.         LOCATE 1, 1: PRINT USING tmp$; sample; t
  38.  
  39.  
  40.     LOOP
  41.  
  42.     _SNDCLOSE S1
  43.     _SNDCLOSE S2
  44.     _SNDCLOSE S3
  45.  
  46.     'do other stuff, but it may interrupt sound
  47.     LOCATE 1, 1: PRINT USING tmp$; sample; t
  48. LOOP WHILE t < 3.0 'play for 3 seconds
  49.  
  50. DO WHILE _SNDRAWLEN > 0 'Finish any left over queued sound!
  51.  
  52.  

This produces only the last Sample (3) of 220Hz, a bit distorted, unfortunately.
You can hear that yourself when you compare the sound of your code 
to the first and second _sndraw commented out.:

Code: QB64: [Select]
  1. t = 0
  2. tmp$ = "Sample = ##.#####   Time = ##.#####"
  3. LOCATE 1, 60: PRINT "Rate:"; _SNDRATE
  4.  
  5.  
  6.     'queue some sound
  7.     DO UNTIL t >= 3 'you may wish to adjust this
  8.         sample1 = SIN(t * 440 * ATN(1) * 8) '440Hz sine wave (t * 440 * 2?)
  9.         sample1 = sample1 * EXP(-t * 3) 'fade out eliminates clicks after sound
  10.         '_SndRaw sample
  11.         ' t = t + 1 / _SNDRATE 'sound card sample frequency determines time
  12.  
  13.         sample2 = SIN(t * 110 * ATN(1) * 8) '440Hz sine wave (t * 440 * 2?)
  14.         sample2 = sample2 * EXP(-t * 3) 'fade out eliminates clicks after sound
  15.         '_SndRaw sample
  16.         ' t = t + 1 / _SndRate
  17.  
  18.         '  sample1 = (sample1 + sample2) / 2
  19.  
  20.         sample3 = SIN(t * 220 * ATN(1) * 8) '440Hz sine wave (t * 440 * 2?)
  21.         sample3 = sample3 * EXP(-t * 3) 'fade out eliminates clicks after sound
  22.  
  23.         ' sample1 = (sample1 + sample3) / 2
  24.  
  25.         t = t + 1 / _SNDRATE
  26.         '_SNDRAW sample1, , S1
  27.         '_SNDRAWDONE
  28.         '_SNDRAW sample2, , S2
  29.         '_SNDRAWDONE
  30.         _SNDRAW sample3, , S3
  31.         _SNDRAWDONE
  32.  
  33.         '   _SNDRAW sample1
  34.         '   _SNDRAW sample2
  35.         '   _SNDRAW sample3
  36.  
  37.         LOCATE 1, 1: PRINT USING tmp$; sample; t
  38.  
  39.  
  40.     LOOP
  41.  
  42.     _SNDCLOSE S1
  43.     _SNDCLOSE S2
  44.     _SNDCLOSE S3
  45.  
  46.     'do other stuff, but it may interrupt sound
  47.     LOCATE 1, 1: PRINT USING tmp$; sample; t
  48. LOOP WHILE t < 3.0 'play for 3 seconds
  49.  
  50. DO WHILE _SNDRAWLEN > 0 'Finish any left over queued sound!
  51.  
  52.  
Title: Re: Random Melodies and Chords (first complete and second rewrite - Bug Question)
Post by: Petr on September 19, 2021, 08:12:32 am
Not. I always have a different sound if uncomment the previous two _SNDRAW commands. Please make sure that when using _SNDRAW you have turned off all sound effects such as dolby and surround sound, it has a direct effect on the _SNDRAW output.
Title: Re: Random Melodies and Chords (first complete and second rewrite - Bug Question)
Post by: Petr on September 19, 2021, 08:15:51 am
You can try also some combinations als:

        _SNDRAW sample1, , S3
        '_SNDRAWDONE
        _SNDRAW sample2, , S3
        '_SNDRAWDONE
        _SNDRAW sample3, , S1
Title: Re: Random Melodies and Chords (first complete and second rewrite - Bug Question)
Post by: chlorophyll-zz on September 20, 2021, 04:14:02 am
Not. I always have a different sound if uncomment the previous two _SNDRAW commands. Please make sure that when using _SNDRAW you have turned off all sound effects such as dolby and surround sound, it has a direct effect on the _SNDRAW output.

First, I want to thank you for you for your compliment a few post before, I forgot that entirely.

Second, it turns out; You was right with _SNDRAWOPEN and I was wrong in my last post.
Please excuse me, I was in a hurry and had not the time to listen carefully.
Also, the 440+220+110 Hz example, was a bad example (from me).

I wrote the example Code now for C-Major (C-E-G),
and compared to this recording:



It turns out that the code from you produces indeed the right Chord.

Now I am editing my Melodies code to fit your good (and completely accurate) example.
Thank You for teaching me!

For completeness, this example code produces a C Major Chord in Octave 4 (C4-E4-G4)

Code: QB64: [Select]
  1. t = 0
  2. tmp$ = "Sample = ##.#####   Time = ##.#####"
  3. Locate 1, 60: Print "Rate:"; _SndRate
  4.  
  5.  
  6.     'queue some sound
  7.     Do Until t >= 3 'you may wish to adjust this
  8.         sample1 = Sin(t * 261.626 * Atn(1) * 8) '440Hz sine wave (t * 440 * 2?)
  9.         sample1 = sample1 * Exp(-t * 3) / 3 'fade out eliminates clicks after sound
  10.        
  11.         sample2 = Sin(t * 329.628 * Atn(1) * 8) '440Hz sine wave (t * 440 * 2?)
  12.         sample2 = sample2 * Exp(-t * 3) / 3 'fade out eliminates clicks after sound
  13.  
  14.         sample3 = Sin(t * 391.995 * Atn(1) * 8) '440Hz sine wave (t * 440 * 2?)
  15.         sample3 = sample3 * Exp(-t * 3) / 3 'fade out eliminates clicks after sound
  16.  
  17.         t = t + 1 / _SndRate
  18.         _SndRaw sample1, , S1
  19.         _SndRawDone
  20.         _SndRaw sample2, , S2
  21.         _SndRawDone
  22.         _SndRaw sample3, , S3
  23.         _SndRawDone
  24.  
  25.         Locate 1, 1: Print Using tmp$; sample; t
  26.  
  27.  
  28.     Loop
  29.  
  30.     _SndClose S1
  31.     _SndClose S2
  32.     _SndClose S3
  33.  
  34.     'do other stuff, but it may interrupt sound
  35.     Locate 1, 1: Print Using tmp$; sample; t
  36. Loop While t < 3.0 'play for 3 seconds
  37.  
  38. Do While _SndRawLen > 0 'Finish any left over queued sound!
  39.  
  40.  
  41.  

Title: Re: Random Melodies and Chords (first complete and second rewrite - Bug Question)
Post by: chlorophyll-zz on September 20, 2021, 05:39:05 am
I could not get the program to work with your  _SNDRAWOPEN method, it always failed on the first _SNDRAW
I reverted completely to your first method of adding the samples and dividing by 2.
This is the newest code. The Chords work and everything else too, so far.
Sometimes I have clicks, but I can live with that.

Code: QB64: [Select]
  1. Dim fm(88) As Double
  2. Dim tn(88) As String
  3.  
  4. Print "Welcone to Random Tones and Chords Generator v0.1b"
  5. Input "Please specify Chord possibility in % (25)"; uaw$
  6. If uaw$ = "" Or Val(uaw$) < 0 Or Val(uaw$) > 100 Then
  7.     aw = 25
  8. Else aw = Val(uaw$)
  9. Print "Chords possibility:"; aw; "%"
  10. bpm = 120
  11. 'interval 1 and 2 (semitones)
  12. int1 = 4
  13. int2 = 7
  14. 'interval possibility percent
  15. 'aw = 25
  16. 'tone minimum and maximum
  17. tmin = 35
  18. tmax = 47
  19. 'lengths min and max
  20. lmin = 2
  21. lmax = 4
  22. 'fill the tone names
  23. For i = 1 To 88
  24.     f = ((2 ^ ((i - 49) / 12)) * 440)
  25.     fm(i) = CInt(f)
  26.     Select Case i Mod 12
  27.         Case 1: tn(i) = "A"
  28.         Case 2: tn(i) = "A#"
  29.         Case 3: tn(i) = "B"
  30.         Case 4: tn(i) = "C"
  31.         Case 5: tn(i) = "C#"
  32.         Case 6: tn(i) = "D"
  33.         Case 7: tn(i) = "D#"
  34.         Case 8: tn(i) = "E"
  35.         Case 9: tn(i) = "F"
  36.         Case 10: tn(i) = "F#"
  37.         Case 11: tn(i) = "G"
  38.         Case 0: tn(i) = "G#"
  39.     End Select
  40.     'Select Case Int(i / 12)
  41.     '    Case 0: tn(i) = tn(i) + "0"
  42.  
  43.     'End Select
  44.     'DEBUG
  45.     'Print f; tn(i)
  46.     'If i > 24 Then Sleep
  47.  
  48. 'delete all sharp tones
  49. For i = 1 To 88
  50.     If Mid$(tn(i), 2, 1) = "#" Then
  51.         tn(i) = tn(i - 1)
  52.         fm(i) = fm(i - 1)
  53.     End If
  54. 'DEBUG fm(49)="A"
  55. 'Print fm(49)
  56.  
  57. 'fill the lengths
  58. For i = 1 To 7
  59.     lm(i) = 2 ^ i / 2
  60.     'DEBUG
  61.     'Print lm(i)
  62.  
  63.  
  64.     'tones between tmin and tmax
  65.     i = CInt((Rnd * (tmax - tmin)) + tmin)
  66.  
  67.     lr = lm(CInt(Rnd * (lmax - lmin)) + lmin) 'Length out of the lenght fields between 2 and 4
  68.     l = 1 / lr * 60 / bpm * 4 'Lenght is parts of a second from the length fields multiplied by bpm
  69.  
  70.     If Rnd < aw / 100 Then
  71.         akk = 1
  72.     Else akk = 0
  73.     End If
  74.  
  75.     t = 0
  76.     If akk = 1 Then
  77.         Print "Length 1/"; LTrim$(Str$(lr)); " Tone:"; tn(i); "+"; tn(i + int1); "+"; tn(i + 7)
  78.     Else Print "Length 1/"; LTrim$(Str$(lr)); " Tone:"; tn(i)
  79.     End If
  80.     Do
  81.         'queue some sound
  82.         Do While t < l 'you may wish to adjust this
  83.             sample1 = Sin(t * fm(i) * Atn(1) * 8) / 3 * 2
  84.             sample1 = sample1 * Exp(-t * l) / 3 * 2
  85.  
  86.             t = t + 1 / _SndRate
  87.  
  88.             If akk = 1 Then
  89.                 sample2 = Sin(t * fm(i + int1) * Atn(1) * 8) / 3 * 2
  90.                 sample2 = sample2 * Exp(-t * l) / 3 * 2
  91.                 sample1 = (sample1 + sample2) / 2
  92.                 '_SndRaw sample2
  93.                 't = t + 1 / _SndRate
  94.                 sample3 = Sin(t * fm(i + int2) * Atn(1) * 8) / 3 * 2
  95.                 sample3 = sample3 * Exp(-t * l) / 3 * 2
  96.                 sample1 = (sample1 + sample3) / 2
  97.                 '_SndRaw sample
  98.                 't = t + 1 / _SndRate
  99.             End If
  100.             _SndRaw sample1
  101.  
  102.         Loop
  103.  
  104.  
  105.     Loop While t < l 'play for l seconds
  106.  
  107.     Do While _SndRawLen > 0 'Finish any left over queued sound!
  108.     Loop
  109.  
  110.     in = InKey$
  111. Loop While in = ""
  112.  
  113.  
  114.  
Title: Re: Random Melodies and Chords (first complete and second rewrite - Bug Question)
Post by: bplus on September 20, 2021, 11:43:39 am
Ah, you've cleaned the crumbs from my speaker sounds better. :)
Title: Re: Random Melodies and Chords (first complete and second rewrite - Bug Question)
Post by: Petr on September 24, 2021, 01:49:02 pm
@chlorophyll-zz  Thank for reply. I played around with your program a bit, so now you can generate sound files, for example, as effects for games, or just like that. The attached source code creates a generated.wav file

Code: QB64: [Select]
  1.  
  2. DIM fm(88) AS DOUBLE
  3. DIM tn(88) AS STRING
  4. DIM Left AS _MEM, Right AS _MEM
  5.  
  6.  
  7.  
  8.  
  9. PRINT "Welcone to Random Tones and Chords Generator v0.1b"
  10. INPUT "Please specify Chord possibility in % (25)"; uaw$
  11. INPUT "Please set music time [sec] for generating WAV file:"; WAVLen
  12.  
  13. DIM RawMusic(_CEIL(_SNDRATE * WAVLen)) AS SINGLE
  14.  
  15. Left = _MEM(RawMusic())
  16. Right = _MEM(RawMusic()) 'mono signal, so both channels are the same
  17.  
  18. IF uaw$ = "" OR VAL(uaw$) < 0 OR VAL(uaw$) > 100 THEN
  19.     aw = 25
  20. ELSE aw = VAL(uaw$)
  21. PRINT "Chords possibility:"; aw; "%"
  22. bpm = 120
  23. 'interval 1 and 2 (semitones)
  24. int1 = 4
  25. int2 = 7
  26. 'interval possibility percent
  27. 'aw = 25
  28. 'tone minimum and maximum
  29. tmin = 35
  30. tmax = 47
  31. 'lengths min and max
  32. lmin = 2
  33. lmax = 4
  34. 'fill the tone names
  35. FOR i = 1 TO 88
  36.     f = ((2 ^ ((i - 49) / 12)) * 440)
  37.     fm(i) = CINT(f)
  38.     SELECT CASE i MOD 12
  39.         CASE 1: tn(i) = "A"
  40.         CASE 2: tn(i) = "A#"
  41.         CASE 3: tn(i) = "B"
  42.         CASE 4: tn(i) = "C"
  43.         CASE 5: tn(i) = "C#"
  44.         CASE 6: tn(i) = "D"
  45.         CASE 7: tn(i) = "D#"
  46.         CASE 8: tn(i) = "E"
  47.         CASE 9: tn(i) = "F"
  48.         CASE 10: tn(i) = "F#"
  49.         CASE 11: tn(i) = "G"
  50.         CASE 0: tn(i) = "G#"
  51.     END SELECT
  52.     'Select Case Int(i / 12)
  53.     '    Case 0: tn(i) = tn(i) + "0"
  54.  
  55.     'End Select
  56.     'DEBUG
  57.     'Print f; tn(i)
  58.     'If i > 24 Then Sleep
  59.  
  60. 'delete all sharp tones
  61. FOR i = 1 TO 88
  62.     IF MID$(tn(i), 2, 1) = "#" THEN
  63.         tn(i) = tn(i - 1)
  64.         fm(i) = fm(i - 1)
  65.     END IF
  66. 'DEBUG fm(49)="A"
  67. 'Print fm(49)
  68.  
  69. 'fill the lengths
  70. FOR i = 1 TO 7
  71.     lm(i) = 2 ^ i / 2
  72.     'DEBUG
  73.     'Print lm(i)
  74.  
  75.  
  76.     'tones between tmin and tmax
  77.     i = CINT((RND * (tmax - tmin)) + tmin)
  78.  
  79.     lr = lm(CINT(RND * (lmax - lmin)) + lmin) 'Length out of the lenght fields between 2 and 4
  80.     L = 1 / lr * 60 / bpm * 4 'Lenght is parts of a second from the length fields multiplied by bpm
  81.  
  82.     IF RND < aw / 100 THEN
  83.         akk = 1
  84.     ELSE akk = 0
  85.     END IF
  86.  
  87.     t = 0
  88.     IF akk = 1 THEN
  89.         PRINT "Length 1/"; LTRIM$(STR$(lr)); " Tone:"; tn(i); "+"; tn(i + int1); "+"; tn(i + 7)
  90.     ELSE PRINT "Length 1/"; LTRIM$(STR$(lr)); " Tone:"; tn(i)
  91.     END IF
  92.     DO
  93.         'queue some sound
  94.         DO WHILE t < L 'you may wish to adjust this
  95.             sample1 = SIN(t * fm(i) * ATN(1) * 8) / 3 * 2
  96.             sample1 = sample1 * EXP(-t * L) / 3 * 2
  97.  
  98.             t = t + 1 / _SNDRATE
  99.  
  100.             IF akk = 1 THEN
  101.                 sample2 = SIN(t * fm(i + int1) * ATN(1) * 8) / 3 * 2
  102.                 sample2 = sample2 * EXP(-t * L) / 3 * 2
  103.                 sample1 = (sample1 + sample2) / 2
  104.                 '_SndRaw sample2
  105.                 't = t + 1 / _SndRate
  106.                 sample3 = SIN(t * fm(i + int2) * ATN(1) * 8) / 3 * 2
  107.                 sample3 = sample3 * EXP(-t * L) / 3 * 2
  108.                 sample1 = (sample1 + sample3) / 2
  109.                 '_SndRaw sample
  110.                 't = t + 1 / _SndRate
  111.             END IF
  112.  
  113.             _SNDRAW sample1
  114.             RawMusic(rwi) = sample1
  115.             rwi = rwi + 1
  116.  
  117.             IF rwi > UBOUND(rawmusic) THEN
  118.                 PRINT "Sound generated, saving to file generated.wav"
  119.                 _DELAY .5
  120.                 SAVESOUND8S Left, Right, "generated.wav"
  121.                 _MEMFREE Left
  122.                 _MEMFREE Right
  123.                 ERASE RawMusic
  124.                 SYSTEM
  125.             END IF
  126.         LOOP
  127.  
  128.  
  129.     LOOP WHILE t < L 'play for l seconds
  130.  
  131.     DO WHILE _SNDRAWLEN > 0 'Finish any left over queued sound!
  132.     LOOP
  133.  
  134.  
  135. LOOP WHILE in = ""
  136.  
  137.  
  138.  
  139.  
  140.  
  141.  
  142.  
  143.  
  144.  
  145.  
  146. SUB SAVESOUND8S (Left AS _MEM, Right AS _MEM, file AS STRING) 'Left and Right memory blocks contains value -1 to 1 (_SNDRAW compatible)
  147.  
  148.     Size = OFFSET_to_I64(Left.SIZE) 'convertion is used for WAV file header, becuse offset value can not be used directly
  149.  
  150.     TYPE head8
  151.         chunk AS STRING * 4 '       4 bytes  (RIFF)
  152.         size AS LONG '              4 bytes  (file size)
  153.         fomat AS STRING * 4 '       4 bytes  (WAVE)
  154.         sub1 AS STRING * 4 '        4 bytes  (fmt )
  155.         subchunksize AS LONG '      4 bytes  (lo / hi), $00000010 for PCM audio
  156.         format AS INTEGER '         2 bytes  (0001 = standard PCM, 0101 = IBM mu-law, 0102 = IBM a-law, 0103 = IBM AVC ADPCM)
  157.         channels AS INTEGER '       2 bytes  (1 = mono, 2 = stereo)
  158.         rate AS LONG '              4 bytes  (sample rate, standard is 44100)
  159.         ByteRate AS LONG '          4 bytes  (= sample rate * number of channels * (bits per channel /8))
  160.         Block AS INTEGER '          2 bytes  (block align = number of channels * bits per sample /8)
  161.         Bits AS INTEGER '           2 bytes  (bits per sample. 8 = 8, 16 = 16)
  162.         subchunk2 AS STRING * 4 '   4 bytes  ("data")  contains begin audio samples
  163.         lenght AS LONG '            4 bytes  Data block size
  164.     END TYPE '                     44 bytes  total
  165.     DIM H8 AS head8
  166.     ch = FREEFILE
  167.  
  168.     H8.chunk = "RIFF"
  169.     H8.size = 44 + Size / 2
  170.  
  171.     H8.fomat = "WAVE"
  172.     H8.sub1 = "fmt "
  173.     H8.subchunksize = 16
  174.     H8.format = 1
  175.     H8.channels = 2
  176.     H8.rate = 44100
  177.     H8.ByteRate = 44100 * 2 * 8 / 8
  178.     H8.Block = 2
  179.     H8.Bits = 8
  180.     H8.subchunk2 = "data"
  181.     H8.lenght = Size / 2
  182.     IF _FILEEXISTS(file$) THEN KILL file$
  183.  
  184.     OPEN file$ FOR BINARY AS #ch
  185.     PUT #ch, , H8
  186.  
  187.     DIM LeftChannel8 AS _BYTE, RightChannel8 AS _BYTE, RawLeft AS SINGLE, RawRight AS SINGLE
  188.     DIM Recalc AS _MEM, size AS _OFFSET
  189.  
  190.     size = Left.SIZE 'now this value is for memory size used by SINGLE ARRAY - WAV 8bit need 1 byte for 1 channel and 1 sample
  191.  
  192.     'recalculate audiodata to file - byte - values
  193.  
  194.     Recalc = _MEMNEW(size / 2) 'this is value used by WAV - size is 4 byte per sample, we recording stereo (2 x 1 byte) therefore is this divided by 2
  195.  
  196.     start& = 0: LRO& = 0
  197.     DO UNTIL start& = Left.SIZE
  198.         RawLeft = _MEMGET(Left, Left.OFFSET + start&, SINGLE)
  199.         RawRight = _MEMGET(Right, Right.OFFSET + start&, SINGLE)
  200.  
  201.         LeftChannel8 = 128 - RawLeft * 128
  202.         RightChannel8 = 128 - RawRight * 128
  203.  
  204.         _MEMPUT Recalc, Recalc.OFFSET + s&, LeftChannel8
  205.         _MEMPUT Recalc, Recalc.OFFSET + s& + 1, RightChannel8
  206.         s& = s& + 2
  207.         start& = start& + 4
  208.     LOOP
  209.  
  210.     'write audio data to file
  211.  
  212.     WAVeRAW$ = SPACE$(s&)
  213.     _MEMGET Recalc, Recalc.OFFSET, WAVeRAW$
  214.     PUT #ch, , WAVeRAW$
  215.  
  216.     'erase memory
  217.     _MEMFREE Recalc
  218.     WAVeRAW$ = ""
  219.     CLOSE ch
  220.  
  221. FUNCTION OFFSET_to_I64 (value AS _OFFSET)
  222.     DIM m AS _MEM
  223.     $IF 32BIT THEN
  224.         dim num as long
  225.         m = _mem(num)
  226.         _memput m,m.offset, value
  227.         Offset_to_i64 = num
  228.         _memfree m
  229.     $ELSE
  230.         DIM num AS _INTEGER64
  231.         m = _MEM(num)
  232.         _MEMPUT m, m.OFFSET, value
  233.         OFFSET_to_I64 = num
  234.         _MEMFREE m
  235.     $END IF
  236.  
Title: Re: Random Melodies and Chords (first complete and second rewrite - Bug Question)
Post by: chlorophyll-zz on September 24, 2021, 03:32:13 pm
@you can generate sound files

Wow, how cool is that! I could never accomplish writing a WAV file.

I also thought about adding a load and save function,
and saving or loading the tone names and Octaves and Chords in a Text-File.

Or for example (this is (for me )a huge project,) adding a "like-key",
so the Program would play the same chord transitions that you liked more often.

But now the chords are note gone.
Thank you!
Title: Re: Random Melodies and Chords (first complete and second rewrite - Bug Question)
Post by: Petr on September 24, 2021, 03:43:11 pm
I'm glad you like it. You could still have a lot of fun with it. That LIKE button, it would mean that you write in the field where the individual tones starts and ends (from which sample to which sample).
Title: Re: Random Melodies and Chords (first complete and second rewrite - Bug Question)
Post by: chlorophyll-zz on September 25, 2021, 05:04:56 am
That LIKE button, it would mean that you write in the field where the individual tones starts and ends (from which sample to which sample).

I would save the sequence of the last x transitions, so for one example if I have a transition from C4 to E4, I would write a transition of 2 full tones in the like field.
Or for a melody, I would write the transitions from the last x tones and prefer these transitions more.
For that, I would have to calculate a whole block with lengths and tones before the tone generation starts.
So I could pick up the sequence that the user liked, again.
Title: Re: Random Melodies and Chords (first complete and second rewrite - Bug Question)
Post by: Petr on September 25, 2021, 05:15:07 am
Here's how: Let's say the user likes a combination of tones that played from 3 seconds to 4 seconds. All samples that have already been played are stored in the global field of all sound samples. Therefore, there is no need to regenerate tones and their combination, just copy it. The start of copying is in time 3 seconds, that is, from sample 44100 * 3, the end is in time 4 seconds, that is to sample 44100 * 4. That global field of all samples is RawMusic field, see line 124 of the program source code, which output saves as WAV.
Title: Re: Random Melodies and Chords (first complete and second rewrite - Bug Question)
Post by: chlorophyll-zz on September 25, 2021, 05:22:40 am
I added an input check and default (120s) for WaveLen and added a BPM input with check and default (120bpm).
Do you think 120 Seconds is to short for WaveLen?

Code: QB64: [Select]
  1. Dim fm(88) As Double
  2. Dim tn(88) As String
  3. Dim Left As _MEM, Right As _MEM
  4.  
  5.  
  6.  
  7.  
  8. Print "Welcone to Random Tones and Chords Generator v0.1b"
  9. Input "Please specify Chord possibility in % (25)"; uaw$
  10. Input "Please set BPM (120)"; ubpm$
  11. Input "Please set music time [sec] for generating WAV file:(120)"; uWAVLen$
  12.  
  13. Dim RawMusic(_Ceil(_SndRate * WAVLen)) As Single
  14.  
  15. Left = _Mem(RawMusic())
  16. Right = _Mem(RawMusic()) 'mono signal, so both channels are the same
  17.  
  18. If uaw$ = "" Or Val(uaw$) < 0 Or Val(uaw$) > 100 Then
  19.     aw = 25
  20. Else aw = Val(uaw$)
  21. Print "Chords possibility:"; aw; "%"
  22.  
  23.  
  24. If uWAVLen$ = "" Or Val(uWAVLen$) < 0 Or Val(uWAVLen$) > 1000 Then
  25.     WAVLen = 120
  26. Else WAVLen = Val(uWAVLen$)
  27. Print "Music Time:"; LTrim$(Str$(WAVLen)); "s"
  28.  
  29. If ubpm$ = "" Or Val(ubpm$) < 0 Or Val(ubpm$) > 1000 Then
  30.     bpm = 120
  31. Else bpm = Val(ubpm$)
  32. Print "BPM:"; bpm
  33.  
  34.  
  35. 'bpm = 120
  36. 'interval 1 and 2 (semitones)
  37. int1 = 4
  38. int2 = 7
  39. 'interval possibility percent
  40. 'aw = 25
  41. 'tone minimum and maximum
  42. tmin = 35
  43. tmax = 47
  44. 'lengths min and max
  45. lmin = 2
  46. lmax = 4
  47. 'fill the tone names
  48. For i = 1 To 88
  49.     f = ((2 ^ ((i - 49) / 12)) * 440)
  50.     fm(i) = CInt(f)
  51.     Select Case i Mod 12
  52.         Case 1: tn(i) = "A"
  53.         Case 2: tn(i) = "A#"
  54.         Case 3: tn(i) = "B"
  55.         Case 4: tn(i) = "C"
  56.         Case 5: tn(i) = "C#"
  57.         Case 6: tn(i) = "D"
  58.         Case 7: tn(i) = "D#"
  59.         Case 8: tn(i) = "E"
  60.         Case 9: tn(i) = "F"
  61.         Case 10: tn(i) = "F#"
  62.         Case 11: tn(i) = "G"
  63.         Case 0: tn(i) = "G#"
  64.     End Select
  65.     'Select Case Int(i / 12)
  66.     '    Case 0: tn(i) = tn(i) + "0"
  67.  
  68.     'End Select
  69.     'DEBUG
  70.     'Print f; tn(i)
  71.     'If i > 24 Then Sleep
  72.  
  73. 'delete all sharp tones
  74. For i = 1 To 88
  75.     If Mid$(tn(i), 2, 1) = "#" Then
  76.         tn(i) = tn(i - 1)
  77.         fm(i) = fm(i - 1)
  78.     End If
  79. 'DEBUG fm(49)="A"
  80. 'Print fm(49)
  81.  
  82. 'fill the lengths
  83. For i = 1 To 7
  84.     lm(i) = 2 ^ i / 2
  85.     'DEBUG
  86.     'Print lm(i)
  87.  
  88.  
  89.     'tones between tmin and tmax
  90.     i = CInt((Rnd * (tmax - tmin)) + tmin)
  91.  
  92.     lr = lm(CInt(Rnd * (lmax - lmin)) + lmin) 'Length out of the lenght fields between 2 and 4
  93.     L = 1 / lr * 60 / bpm * 4 'Lenght is parts of a second from the length fields multiplied by bpm
  94.  
  95.     If Rnd < aw / 100 Then
  96.         akk = 1
  97.     Else akk = 0
  98.     End If
  99.  
  100.     t = 0
  101.     If akk = 1 Then
  102.         Print "Length 1/"; LTrim$(Str$(lr)); " Tone:"; tn(i); "+"; tn(i + int1); "+"; tn(i + 7)
  103.     Else Print "Length 1/"; LTrim$(Str$(lr)); " Tone:"; tn(i)
  104.     End If
  105.     Do
  106.         'queue some sound
  107.         Do While t < L 'you may wish to adjust this
  108.             sample1 = Sin(t * fm(i) * Atn(1) * 8) / 3 * 2
  109.             sample1 = sample1 * Exp(-t * L) / 3 * 2
  110.  
  111.             t = t + 1 / _SndRate
  112.  
  113.             If akk = 1 Then
  114.                 sample2 = Sin(t * fm(i + int1) * Atn(1) * 8) / 3 * 2
  115.                 sample2 = sample2 * Exp(-t * L) / 3 * 2
  116.                 sample1 = (sample1 + sample2) / 2
  117.                 '_SndRaw sample2
  118.                 't = t + 1 / _SndRate
  119.                 sample3 = Sin(t * fm(i + int2) * Atn(1) * 8) / 3 * 2
  120.                 sample3 = sample3 * Exp(-t * L) / 3 * 2
  121.                 sample1 = (sample1 + sample3) / 2
  122.                 '_SndRaw sample
  123.                 't = t + 1 / _SndRate
  124.             End If
  125.  
  126.             _SndRaw sample1
  127.             RawMusic(rwi) = sample1
  128.             rwi = rwi + 1
  129.  
  130.             If rwi > UBound(RawMusic) Then
  131.                 Print "Sound generated, saving to file generated.wav"
  132.                 _Delay .5
  133.                 SAVESOUND8S Left, Right, "generated.wav"
  134.                 _MemFree Left
  135.                 _MemFree Right
  136.                 Erase RawMusic
  137.                 System
  138.             End If
  139.         Loop
  140.  
  141.  
  142.     Loop While t < L 'play for l seconds
  143.  
  144.     Do While _SndRawLen > 0 'Finish any left over queued sound!
  145.     Loop
  146.  
  147.  
  148. Loop While in = ""
  149.  
  150.  
  151.  
  152.  
  153.  
  154.  
  155.  
  156.  
  157.  
  158.  
  159. Sub SAVESOUND8S (Left As _MEM, Right As _MEM, file As String) 'Left and Right memory blocks contains value -1 to 1 (_SNDRAW compatible)
  160.  
  161.     Size = OFFSET_to_I64(Left.SIZE) 'convertion is used for WAV file header, becuse offset value can not be used directly
  162.  
  163.     Type head8
  164.         chunk As String * 4 '       4 bytes  (RIFF)
  165.         size As Long '              4 bytes  (file size)
  166.         fomat As String * 4 '       4 bytes  (WAVE)
  167.         sub1 As String * 4 '        4 bytes  (fmt )
  168.         subchunksize As Long '      4 bytes  (lo / hi), $00000010 for PCM audio
  169.         format As Integer '         2 bytes  (0001 = standard PCM, 0101 = IBM mu-law, 0102 = IBM a-law, 0103 = IBM AVC ADPCM)
  170.         channels As Integer '       2 bytes  (1 = mono, 2 = stereo)
  171.         rate As Long '              4 bytes  (sample rate, standard is 44100)
  172.         ByteRate As Long '          4 bytes  (= sample rate * number of channels * (bits per channel /8))
  173.         Block As Integer '          2 bytes  (block align = number of channels * bits per sample /8)
  174.         Bits As Integer '           2 bytes  (bits per sample. 8 = 8, 16 = 16)
  175.         subchunk2 As String * 4 '   4 bytes  ("data")  contains begin audio samples
  176.         lenght As Long '            4 bytes  Data block size
  177.     End Type '                     44 bytes  total
  178.     Dim H8 As head8
  179.     ch = FreeFile
  180.  
  181.     H8.chunk = "RIFF"
  182.     H8.size = 44 + Size / 2
  183.  
  184.     H8.fomat = "WAVE"
  185.     H8.sub1 = "fmt "
  186.     H8.subchunksize = 16
  187.     H8.format = 1
  188.     H8.channels = 2
  189.     H8.rate = 44100
  190.     H8.ByteRate = 44100 * 2 * 8 / 8
  191.     H8.Block = 2
  192.     H8.Bits = 8
  193.     H8.subchunk2 = "data"
  194.     H8.lenght = Size / 2
  195.     If _FileExists(file$) Then Kill file$
  196.  
  197.     Open file$ For Binary As #ch
  198.     Put #ch, , H8
  199.  
  200.     Dim LeftChannel8 As _Byte, RightChannel8 As _Byte, RawLeft As Single, RawRight As Single
  201.     Dim Recalc As _MEM, size As _Offset
  202.  
  203.     size = Left.SIZE 'now this value is for memory size used by SINGLE ARRAY - WAV 8bit need 1 byte for 1 channel and 1 sample
  204.  
  205.     'recalculate audiodata to file - byte - values
  206.  
  207.     Recalc = _MemNew(size / 2) 'this is value used by WAV - size is 4 byte per sample, we recording stereo (2 x 1 byte) therefore is this divided by 2
  208.  
  209.     start& = 0: LRO& = 0
  210.     Do Until start& = Left.SIZE
  211.         RawLeft = _MemGet(Left, Left.OFFSET + start&, Single)
  212.         RawRight = _MemGet(Right, Right.OFFSET + start&, Single)
  213.  
  214.         LeftChannel8 = 128 - RawLeft * 128
  215.         RightChannel8 = 128 - RawRight * 128
  216.  
  217.         _MemPut Recalc, Recalc.OFFSET + s&, LeftChannel8
  218.         _MemPut Recalc, Recalc.OFFSET + s& + 1, RightChannel8
  219.         s& = s& + 2
  220.         start& = start& + 4
  221.     Loop
  222.  
  223.     'write audio data to file
  224.  
  225.     WAVeRAW$ = Space$(s&)
  226.     _MemGet Recalc, Recalc.OFFSET, WAVeRAW$
  227.     Put #ch, , WAVeRAW$
  228.  
  229.     'erase memory
  230.     _MemFree Recalc
  231.     WAVeRAW$ = ""
  232.     Close ch
  233.  
  234. Function OFFSET_to_I64 (value As _Offset)
  235.     Dim m As _MEM
  236.     $If 32BIT Then
  237.         DIM num AS LONG
  238.         m = _MEM(num)
  239.         _MEMPUT m,m.offset, value
  240.         Offset_to_i64 = num
  241.         _MEMFREE m
  242.     $Else
  243.         Dim num As _Integer64
  244.         m = _Mem(num)
  245.         _MemPut m, m.OFFSET, value
  246.         OFFSET_to_I64 = num
  247.         _MemFree m
  248.     $End If
  249.  
  250.  
Title: Re: Random Melodies and Chords (first complete and second rewrite - Bug Question)
Post by: Petr on September 25, 2021, 05:32:15 am
Hi.

Repair row 18 in your last source code as:

DIM RawMusic(_CEIL(_SNDRATE * VAL(uWAVLen$))) AS SINGLE

for music record funcionality.

So - but this is just a suggestion - just graphically display the individual samples, or enter the start and end of the sequence via INPUT, which the user wants to repeat, and this can then be copied anywhere in the original samples and thus create a new sequence. You can even cut it this way and then save it individually, but in that case you have to back up the RawMusic field to another SINGLE type field, because RawMusic is taken as the source for saving. You can modify it as you wish and save it, you just have to get the intended output into the RawMusic field.

Another option (this is already offered by working with the fields itself) is the possibility of adding a shift of the left channel compared to the right one or reversing the direction of playback, it already depends on the imagination.
Title: Re: Random Melodies and Chords (first complete and second rewrite - Bug Question)
Post by: chlorophyll-zz on September 25, 2021, 07:48:59 am
Repair row 18 in your last source code as:

DIM RawMusic(_CEIL(_SNDRATE * VAL(uWAVLen$))) AS SINGLE

No. uWAVLen is just the user input. Therefore the u Character in front. I capture it as a String to validate against "" (just pressing enter for default).
After the check for "" and below 0 and above 1000, the uWAVLen gets converted to WAVLen=VAL(uWAVLen). (Line 32)
It works like you intended. ;-)
Title: Re: Random Melodies and Chords (first complete and second rewrite - Bug Question)
Post by: chlorophyll-zz on September 25, 2021, 07:55:23 am
Hi.

Repair row 18 in your last source code as:

DIM RawMusic(_CEIL(_SNDRATE * VAL(uWAVLen$))) AS SINGLE


Ah, now I see, when I press enter, the uWAVLen gets a DIM(0).
Now I put the DIM after the check.
Also, I implemented Pauses (5%)?

Code: QB64: [Select]
  1. Dim fm(88) As Double
  2. Dim tn(88) As String
  3. Dim Left As _MEM, Right As _MEM
  4.  
  5.  
  6.  
  7.  
  8. Print "Welcone to Random Tones and Chords Generator v0.1b"
  9. Input "Please specify Chord possibility in % (25):"; uaw$
  10. Input "Please set music time [sec] for generating WAV file (120):"; uWAVLen
  11. Input "Please set BPM (120):"; ubpm$
  12. Input "Please set Pauses possibility in % (5):"; up$
  13.  
  14. If uWAVLen$ = "" Or Val(uWAVLen$) < 0 Or Val(uWAVLen$) > 1000 Then
  15.     WAVLen = 120
  16. Else WAVLen = Val(uWAVLen$)
  17. Print "Music Time:"; LTrim$(Str$(WAVLen)); "s"
  18.  
  19. If uaw$ = "" Or Val(uaw$) < 0 Or Val(uaw$) > 100 Then
  20.     aw = 25
  21. Else aw = Val(uaw$)
  22. Print "Chords possibility:"; aw; "%"
  23.  
  24. If ubpm$ = "" Or Val(ubpm$) < 0 Or Val(ubpm$) > 1000 Then
  25.     bpm = 120
  26. Else bpm = Val(ubpm$)
  27. Print "BPM:"; bpm
  28.  
  29.  
  30. If up$ = "" Or Val(up$) < 0 Or Val(up$) > 100 Then
  31.     pperc = 5
  32. Else pperc = Val(up$)
  33. Print "Pauses:"; LTrim$(Str$(pperc)); "%"
  34.  
  35.  
  36. Dim RawMusic(_Ceil(_SndRate * WAVLen)) As Single
  37.  
  38. Left = _Mem(RawMusic())
  39. Right = _Mem(RawMusic()) 'mono signal, so both channels are the same
  40.  
  41.  
  42. 'bpm = 120
  43. 'interval 1 and 2 (semitones)
  44. int1 = 4
  45. int2 = 7
  46. 'interval possibility percent
  47. 'aw = 25
  48. 'tone minimum and maximum
  49. tmin = 35
  50. tmax = 47
  51. 'lengths min and max
  52. lmin = 2
  53. lmax = 4
  54. 'fill the tone names
  55. For i = 1 To 88
  56.     f = ((2 ^ ((i - 49) / 12)) * 440)
  57.     fm(i) = CInt(f)
  58.     Select Case i Mod 12
  59.         Case 1: tn(i) = "A"
  60.         Case 2: tn(i) = "A#"
  61.         Case 3: tn(i) = "B"
  62.         Case 4: tn(i) = "C"
  63.         Case 5: tn(i) = "C#"
  64.         Case 6: tn(i) = "D"
  65.         Case 7: tn(i) = "D#"
  66.         Case 8: tn(i) = "E"
  67.         Case 9: tn(i) = "F"
  68.         Case 10: tn(i) = "F#"
  69.         Case 11: tn(i) = "G"
  70.         Case 0: tn(i) = "G#"
  71.     End Select
  72.     'Select Case Int(i / 12)
  73.     '    Case 0: tn(i) = tn(i) + "0"
  74.  
  75.     'End Select
  76.     'DEBUG
  77.     'Print f; tn(i)
  78.     'If i > 24 Then Sleep
  79.  
  80. 'delete all sharp tones
  81. For i = 1 To 88
  82.     If Mid$(tn(i), 2, 1) = "#" Then
  83.         tn(i) = tn(i - 1)
  84.         fm(i) = fm(i - 1)
  85.     End If
  86. 'DEBUG fm(49)="A"
  87. 'Print fm(49)
  88.  
  89. 'fill the lengths
  90. For i = 1 To 7
  91.     lm(i) = 2 ^ i / 2
  92.     'DEBUG
  93.     'Print lm(i)
  94.  
  95.  
  96.     'tones between tmin and tmax
  97.     i = CInt((Rnd * (tmax - tmin)) + tmin)
  98.  
  99.     'Pauses
  100.     If Rnd < pperc / 100 Then p = 1 Else p = 0
  101.  
  102.     lr = lm(CInt(Rnd * (lmax - lmin)) + lmin) 'Length out of the lenght fields between 2 and 4
  103.     L = 1 / lr * 60 / bpm * 4 'Lenght is parts of a second from the length fields multiplied by bpm
  104.  
  105.     If Rnd < aw / 100 Then
  106.         akk = 1
  107.     Else akk = 0
  108.     End If
  109.  
  110.     t = 0
  111.     If akk = 1 And p = 0 Then
  112.         Print "1/"; LTrim$(Str$(lr)); " Tone:"; tn(i); "+"; tn(i + int1); "+"; tn(i + 7)
  113.     ElseIf akk = 0 And p = 0 Then Print "1/"; LTrim$(Str$(lr)); " Tone:"; tn(i)
  114.     ElseIf p = 1 Then Print "1/"; LTrim$(Str$(lr)); " Pause"
  115.     End If
  116.     Do
  117.         'queue some sound
  118.         Do While t < L 'you may wish to adjust this
  119.             sample1 = Sin(t * fm(i) * Atn(1) * 8) / 3 * 2
  120.             sample1 = sample1 * Exp(-t * L) / 3 * 2
  121.  
  122.             t = t + 1 / _SndRate
  123.  
  124.             If akk = 1 Then
  125.                 sample2 = Sin(t * fm(i + int1) * Atn(1) * 8) / 3 * 2
  126.                 sample2 = sample2 * Exp(-t * L) / 3 * 2
  127.                 sample1 = (sample1 + sample2) / 2
  128.  
  129.                 '_SndRaw sample2
  130.                 't = t + 1 / _SndRate
  131.                 sample3 = Sin(t * fm(i + int2) * Atn(1) * 8) / 3 * 2
  132.                 sample3 = sample3 * Exp(-t * L) / 3 * 2
  133.                 sample1 = (sample1 + sample3) / 2
  134.                 '_SndRaw sample
  135.                 't = t + 1 / _SndRate
  136.             End If
  137.             If p = 1 Then sample1 = 0
  138.             _SndRaw sample1
  139.             RawMusic(rwi) = sample1
  140.             rwi = rwi + 1
  141.  
  142.             If rwi > UBound(RawMusic) Then
  143.                 Print "Sound generated, saving to file generated.wav"
  144.                 _Delay .5
  145.                 SAVESOUND8S Left, Right, "generated.wav"
  146.                 _MemFree Left
  147.                 _MemFree Right
  148.                 Erase RawMusic
  149.                 System
  150.             End If
  151.         Loop
  152.  
  153.  
  154.     Loop While t < L 'play for l seconds
  155.  
  156.     Do While _SndRawLen > 0 'Finish any left over queued sound!
  157.     Loop
  158.  
  159.  
  160. Loop While in = ""
  161.  
  162.  
  163.  
  164.  
  165.  
  166.  
  167.  
  168.  
  169.  
  170.  
  171. Sub SAVESOUND8S (Left As _MEM, Right As _MEM, file As String) 'Left and Right memory blocks contains value -1 to 1 (_SNDRAW compatible)
  172.  
  173.     Size = OFFSET_to_I64(Left.SIZE) 'convertion is used for WAV file header, becuse offset value can not be used directly
  174.  
  175.     Type head8
  176.         chunk As String * 4 '       4 bytes  (RIFF)
  177.         size As Long '              4 bytes  (file size)
  178.         fomat As String * 4 '       4 bytes  (WAVE)
  179.         sub1 As String * 4 '        4 bytes  (fmt )
  180.         subchunksize As Long '      4 bytes  (lo / hi), $00000010 for PCM audio
  181.         format As Integer '         2 bytes  (0001 = standard PCM, 0101 = IBM mu-law, 0102 = IBM a-law, 0103 = IBM AVC ADPCM)
  182.         channels As Integer '       2 bytes  (1 = mono, 2 = stereo)
  183.         rate As Long '              4 bytes  (sample rate, standard is 44100)
  184.         ByteRate As Long '          4 bytes  (= sample rate * number of channels * (bits per channel /8))
  185.         Block As Integer '          2 bytes  (block align = number of channels * bits per sample /8)
  186.         Bits As Integer '           2 bytes  (bits per sample. 8 = 8, 16 = 16)
  187.         subchunk2 As String * 4 '   4 bytes  ("data")  contains begin audio samples
  188.         lenght As Long '            4 bytes  Data block size
  189.     End Type '                     44 bytes  total
  190.     Dim H8 As head8
  191.     ch = FreeFile
  192.  
  193.     H8.chunk = "RIFF"
  194.     H8.size = 44 + Size / 2
  195.  
  196.     H8.fomat = "WAVE"
  197.     H8.sub1 = "fmt "
  198.     H8.subchunksize = 16
  199.     H8.format = 1
  200.     H8.channels = 2
  201.     H8.rate = 44100
  202.     H8.ByteRate = 44100 * 2 * 8 / 8
  203.     H8.Block = 2
  204.     H8.Bits = 8
  205.     H8.subchunk2 = "data"
  206.     H8.lenght = Size / 2
  207.     If _FileExists(file$) Then Kill file$
  208.  
  209.     Open file$ For Binary As #ch
  210.     Put #ch, , H8
  211.  
  212.     Dim LeftChannel8 As _Byte, RightChannel8 As _Byte, RawLeft As Single, RawRight As Single
  213.     Dim Recalc As _MEM, size As _Offset
  214.  
  215.     size = Left.SIZE 'now this value is for memory size used by SINGLE ARRAY - WAV 8bit need 1 byte for 1 channel and 1 sample
  216.  
  217.     'recalculate audiodata to file - byte - values
  218.  
  219.     Recalc = _MemNew(size / 2) 'this is value used by WAV - size is 4 byte per sample, we recording stereo (2 x 1 byte) therefore is this divided by 2
  220.  
  221.     start& = 0: LRO& = 0
  222.     Do Until start& = Left.SIZE
  223.         RawLeft = _MemGet(Left, Left.OFFSET + start&, Single)
  224.         RawRight = _MemGet(Right, Right.OFFSET + start&, Single)
  225.  
  226.         LeftChannel8 = 128 - RawLeft * 128
  227.         RightChannel8 = 128 - RawRight * 128
  228.  
  229.         _MemPut Recalc, Recalc.OFFSET + s&, LeftChannel8
  230.         _MemPut Recalc, Recalc.OFFSET + s& + 1, RightChannel8
  231.         s& = s& + 2
  232.         start& = start& + 4
  233.     Loop
  234.  
  235.     'write audio data to file
  236.  
  237.     WAVeRAW$ = Space$(s&)
  238.     _MemGet Recalc, Recalc.OFFSET, WAVeRAW$
  239.     Put #ch, , WAVeRAW$
  240.  
  241.     'erase memory
  242.     _MemFree Recalc
  243.     WAVeRAW$ = ""
  244.     Close ch
  245.  
  246. Function OFFSET_to_I64 (value As _Offset)
  247.     Dim m As _MEM
  248.     $If 32BIT Then
  249.         DIM num AS LONG
  250.         m = _MEM(num)
  251.         _MEMPUT m,m.offset, value
  252.         Offset_to_i64 = num
  253.         _MEMFREE m
  254.     $Else
  255.         Dim num As _Integer64
  256.         m = _Mem(num)
  257.         _MemPut m, m.OFFSET, value
  258.         OFFSET_to_I64 = num
  259.         _MemFree m
  260.     $End If
  261.  
Title: Re: Random Melodies and Chords (first complete and second rewrite - Bug Question)
Post by: Petr on September 25, 2021, 08:21:29 am
Nicely done. It seems, it works as expected :)