Author Topic: Random Melodies and Chords (first complete and second rewrite - Bug Question)  (Read 8324 times)

0 Members and 1 Guest are viewing this topic.

Offline Petr

  • Forum Resident
  • Posts: 1720
  • The best code is the DNA of the hops.
    • View Profile
Re: Random Melodies and Chords (first complete and second rewrite - Bug Question)
« Reply #15 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.

Offline Petr

  • Forum Resident
  • Posts: 1720
  • The best code is the DNA of the hops.
    • View Profile
Re: Random Melodies and Chords (first complete and second rewrite - Bug Question)
« Reply #16 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

Offline chlorophyll-zz

  • Newbie
  • Posts: 15
    • View Profile
Re: Random Melodies and Chords (first complete and second rewrite - Bug Question)
« Reply #17 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.  


Offline chlorophyll-zz

  • Newbie
  • Posts: 15
    • View Profile
Re: Random Melodies and Chords (first complete and second rewrite - Bug Question)
« Reply #18 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.  

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Random Melodies and Chords (first complete and second rewrite - Bug Question)
« Reply #19 on: September 20, 2021, 11:43:39 am »
Ah, you've cleaned the crumbs from my speaker sounds better. :)

Offline Petr

  • Forum Resident
  • Posts: 1720
  • The best code is the DNA of the hops.
    • View Profile
Re: Random Melodies and Chords (first complete and second rewrite - Bug Question)
« Reply #20 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.  

Offline chlorophyll-zz

  • Newbie
  • Posts: 15
    • View Profile
Re: Random Melodies and Chords (first complete and second rewrite - Bug Question)
« Reply #21 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!

Offline Petr

  • Forum Resident
  • Posts: 1720
  • The best code is the DNA of the hops.
    • View Profile
Re: Random Melodies and Chords (first complete and second rewrite - Bug Question)
« Reply #22 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).

Offline chlorophyll-zz

  • Newbie
  • Posts: 15
    • View Profile
Re: Random Melodies and Chords (first complete and second rewrite - Bug Question)
« Reply #23 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.

Offline Petr

  • Forum Resident
  • Posts: 1720
  • The best code is the DNA of the hops.
    • View Profile
Re: Random Melodies and Chords (first complete and second rewrite - Bug Question)
« Reply #24 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.

Offline chlorophyll-zz

  • Newbie
  • Posts: 15
    • View Profile
Re: Random Melodies and Chords (first complete and second rewrite - Bug Question)
« Reply #25 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.  

Offline Petr

  • Forum Resident
  • Posts: 1720
  • The best code is the DNA of the hops.
    • View Profile
Re: Random Melodies and Chords (first complete and second rewrite - Bug Question)
« Reply #26 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.

Offline chlorophyll-zz

  • Newbie
  • Posts: 15
    • View Profile
Re: Random Melodies and Chords (first complete and second rewrite - Bug Question)
« Reply #27 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. ;-)

Offline chlorophyll-zz

  • Newbie
  • Posts: 15
    • View Profile
Re: Random Melodies and Chords (first complete and second rewrite - Bug Question)
« Reply #28 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.  
« Last Edit: September 25, 2021, 08:01:03 am by chlorophyll-zz »

Offline Petr

  • Forum Resident
  • Posts: 1720
  • The best code is the DNA of the hops.
    • View Profile
Re: Random Melodies and Chords (first complete and second rewrite - Bug Question)
« Reply #29 on: September 25, 2021, 08:21:29 am »
Nicely done. It seems, it works as expected :)