Author Topic: Visualise music under QB64  (Read 4813 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
Visualise music under QB64
« on: June 04, 2019, 04:44:10 pm »
Hi. You can know this program from my Youtube channel. Of course, I improved it a bit, I added a frequency analyzer and offered this source code. Again, I came across several limits that I want to mention. First...

For everyone who uses ON TIMER in programming, I would like to write my experience here. The command works great, but it is such a C ++ god for QB64, because it is essentially driven by time, as the name implies. I needed to define the way the program ran so that one part of it was processed every one twentieth per second. After editing, the program started making unprecedented errors. I had to write out the contents of the fields to screen, because the caused errors was not logic. By doing so, I found out that ON TIMER simply call the subroutine even in the middle of a loop that is not complete, and then causes such errors. After I added a value validity test in the subroutine before it was processed, the program finally began to behave as expected.
That's one piece of information for everyone. One more information - sad for developers:. _SNDRAWLEN only works correctly if left or left and right channels are used. If I use just right channel or twice right chnnel, then _SNDRAW work not. Try it on row 152, 153 in attached example. Please use WAV file, 16 bit, stereo or attached link.
Next limit is inability to redefine the field type as needed (this is probably impossible in Basic and thus QB64 for compatibility).
It is a great pity. If it worked as expected, I'd like to. This is shown in lines 120-127.
What a pity, it is SNDRAW and its stereo which does not play as stereo. Please let developers get directions, I have minimal knowledge, but I assume that somewhere in C ++ code there will be some thing that does this. Where can I find the source code for SNDRAW? At worst, it will still be the same ...


Code: QB64: [Select]
  1.  
  2.  
  3. _TITLE "Celebrate your programming language with music!"
  4. SCREEN _NEWIMAGE(1024, 768, 32)
  5.  
  6. DIM SHARED image AS LONG, bckm AS LONG
  7. Virtual = _NEWIMAGE(1024, 768, 32)
  8. image& = _LOADIMAGE("qb.png", 32)
  9. bck = _LOADIMAGE("bg.jpg", 32)
  10. bckm& = _NEWIMAGE(1024, 768, 32)
  11. _PUTIMAGE , bck, bckm&
  12. Tim = TIMER + 10
  13. CONST MiddleX = 1024 / 2
  14. CONST MiddleY = 768 / 2
  15. CONST Radius = 200
  16. TextX = _PRINTWIDTH(text$) + _WIDTH
  17. _CLEARCOLOR _RGB32(0, 0, 0), Virtual
  18. _SETALPHA 0, _RGB32(240, 240, 240) TO _RGB32(255, 255, 255), image&
  19.  
  20.  
  21. INPUT "Insert WAV audio file (16 bit, stereo) name:"; file$
  22. IF INSTR(LCASE$(file$), ".wav") = 0 THEN file$ = file$ + ".wav"
  23. CONST CACHE = 441 '             minimal detected frequency for analyzer is 100 Hz, so this is enought value (with 44100 biterate)
  24. TYPE head
  25.     chunk AS STRING * 4 '       4 bytes  (RIFF)
  26.     size AS LONG '              4 bytes  (?E??)
  27.     fomat AS STRING * 4 '       4 bytes  (WAVE)
  28.     sub1 AS STRING * 4 '        4 bytes  (fmt )
  29.     subchunksize AS LONG '      4 bytes  (lo / hi), $00000010 for PCM audio
  30.     format AS STRING * 2 '      2 bytes  (0001 = standard PCM, 0101 = IBM mu-law, 0102 = IBM a-law, 0103 = IBM AVC ADPCM)
  31.     channels AS INTEGER '       2 bytes  (1 = mono, 2 = stereo)
  32.     rate AS LONG '              4 bytes  (sample rate, standard is 44100)
  33.     ByteRate AS LONG '          4 bytes  (= sample rate * number of channels * (bits per channel /8))
  34.     Block AS INTEGER '          2 bytes  (block align = number of channels * bits per sample /8)
  35.     Bits AS INTEGER '           2 bytes  (bits per sample. 8 = 8, 16 = 16)
  36.     subchunk2 AS STRING * 4 '   4 bytes  ("data")  contains begin audio samples
  37. END TYPE '                     40 bytes  total
  38.  
  39.  
  40. TYPE Wav8S
  41.     Left AS _UNSIGNED _BYTE
  42.     Right AS _UNSIGNED _BYTE
  43.  
  44. TYPE Wav8M
  45.     Left AS _UNSIGNED _BYTE
  46.  
  47.  
  48. TYPE Wav16S
  49.     Left AS INTEGER
  50.     Right AS INTEGER
  51.  
  52. TYPE Wav16M
  53.     Left AS INTEGER
  54.  
  55. TYPE Wav32S
  56.     left AS SINGLE
  57.     right AS SINGLE
  58.  
  59. TYPE Wav32M
  60.     left AS SINGLE
  61.  
  62. TYPE Wav64S
  63.     left AS DOUBLE
  64.     right AS DOUBLE
  65.  
  66. TYPE Wav64M
  67.     left AS DOUBLE
  68.  
  69. REDIM scache(CACHE) AS Wav16S
  70. 'REDIM scache(CACHE) AS ....  FLOAT can not be used, because is for decimaly numbers only, but program need integer values OR decimaly values!
  71.  
  72. TYPE Sour
  73.     X AS INTEGER
  74.     Y AS INTEGER
  75.     'next for analyzer use
  76.     t AS SINGLE
  77.     t2 AS SINGLE
  78.     max1 AS INTEGER
  79.     max2 AS INTEGER
  80.     L1 AS DOUBLE
  81.     L2 AS DOUBLE
  82.  
  83. DIM SHARED U(13) AS Sour 'for analyzer
  84. REDIM sour(62) AS Sour ' for visualisation
  85.  
  86. DIM H AS head, U AS _UNSIGNED LONG
  87.  
  88. IF _FILEEXISTS(file$) THEN OPEN file$ FOR BINARY AS #ch ELSE PRINT file$; " not found": SLEEP 2: SYSTEM
  89. GET #ch, , H
  90.  
  91. block = H.Block
  92. RATE = H.rate
  93. chan = H.channels
  94. bits = H.Bits
  95.  
  96. IF bits <> 16 OR chan <> 2 THEN BEEP: PRINT "QB64 limit: Your WAV format is "; bits; " bits and "; chan; " channels. For replaying it comment row 106 and set valid type on row 77.": SLEEP 3: SYSTEM
  97. 'STUPID BASIC LIMITS...
  98.  
  99.  
  100.  
  101.  
  102. ToScreen = 1 / 1256 '3140
  103. ON TIMER(ToScreen) GOSUB DrawTo
  104.  
  105.  
  106. '---- This work NOT under QB64v1.3-------------------------  Please comment row 77 for verify!
  107. IF bits = 8 AND chan = 2 THEN REDIM scache(CACHE) AS Wav8S
  108. IF bits = 8 AND chan = 1 THEN REDIM scache(CACHE) AS Wav8M
  109. IF bits = 16 AND chan = 2 THEN REDIM scache(CACHE) AS Wav16S
  110. IF bits = 16 AND chan = 1 THEN REDIM scache(CACHE) AS Wav16M
  111. IF bits = 32 AND chan = 2 THEN REDIM scache(CACHE) AS Wav32S
  112. IF bits = 32 AND chan = 1 THEN REDIM scache(CACHE) AS Wav32M
  113. IF bits = 64 AND chan = 2 THEN REDIM scache(CACHE) AS Wav64S
  114. IF bits = 64 AND chan = 1 THEN REDIM scache(CACHE) AS Wav64M
  115. '----------------------------------------------------------
  116.  
  117.     GET #ch, , scache()
  118.     FrekvLeft = 0
  119.     FrekvRight = 0
  120.     FOR P = 0 TO CACHE
  121.         f = f + 1
  122.         lef = scache(P).Left
  123.         IF chan = 1 THEN righ = lef ELSE righ = scache(P).Right
  124.         SELECT CASE bits
  125.             CASE 8
  126.                 lef = lef / 256
  127.                 righ = righ / 256
  128.             CASE 16
  129.                 lef = lef / RATE
  130.                 righ = righ / RATE
  131.             CASE 32 'values are the same (tested)
  132.             CASE 64 ' not tested
  133.         END SELECT
  134.  
  135.         IF RATE > 44100 THEN frekvence = RATE ELSE frekvence = 44100
  136.         FOR plll = 1 TO frekvence / RATE
  137.             _SNDRAW lef, L
  138.             _SNDRAW righ, , R
  139.         NEXT plll
  140.  
  141.         GOSUB analyzer
  142.         CurrentMovementL = WaveMovement(lef, lf)
  143.         CurrentMovementR = WaveMovement(righ, rf)
  144.         IF OldMovementL = 0 THEN OldMovementL = CurrentMovementL
  145.         IF OldMovementR = 0 THEN OldMovementR = CurrentMovementR
  146.         IF OldMovementL <> CurrentMovementL THEN OldMovementL = CurrentMovementL: FrekvLeft = FrekvLeft + 1
  147.         IF OldMovementR <> CurrentMovementR THEN OldMovementR = CurrentMovementR: FrekvRight = FrekvRight + 1
  148.  
  149.         lf = lef
  150.         rf = righ
  151.         DO: LOOP WHILE _SNDRAWLEN
  152.  
  153.     NEXT
  154.     _DISPLAY
  155.  
  156.  
  157.  
  158. DrawTo:
  159. IF lef >= -1 AND lef <= 1 THEN 'comment this two IF conditions for show, what ON TIMER then do.
  160.     IF righ >= -1 AND righ <= 1 THEN
  161.  
  162.         k = k + .1: IF k > _PI(2) THEN
  163.             k = 0
  164.             _PUTIMAGE , bckm&
  165.             _CLEARCOLOR _RGB32(0, 0, 0), Virtual
  166.             IF I = 63 THEN _PUTIMAGE , Virtual
  167.             COLOR &HFFFFFF00 ', &HFFAA56D7
  168.             _PRINTMODE _KEEPBACKGROUND
  169.             _PRINTSTRING (TextX, 20), text$, 0
  170.             FOR EqL = 100 TO 230 STEP 10
  171.                 LINE (EqL + 700, 600)-(EqL + 708, 700), _RGB32(255), B 'right
  172.                 LINE (EqL, 600)-(EqL + 8, 700), _RGB32(255), B
  173.             NEXT
  174.             FOR ff = 0 TO 13
  175.                 lenght = U(ff).L1
  176.                 lenght2 = U(ff).L2
  177.  
  178.                 IF lenght > 95 THEN lenght = 95
  179.                 IF lenght2 > 95 THEN lenght2 = 95
  180.  
  181.                 U(ff).L1 = 0
  182.                 U(ff).L2 = 0
  183.  
  184.                 IF lenght > U(ff).max1 THEN U(ff).max1 = lenght
  185.                 IF lenght > U(ff).X THEN U(ff).X = lenght
  186.                 IF lenght2 > U(ff).max2 THEN U(ff).max2 = lenght2
  187.                 IF lenght2 > U(ff).Y THEN U(ff).Y = lenght2
  188.  
  189.  
  190.                 IF U(ff).t > 0 THEN
  191.                     IF U(ff).t < TIMER THEN U(ff).X = U(ff).X - 6
  192.                     IF U(ff).X <= 0 THEN U(ff).X = 0: U(ff).t = 0: U(ff).max1 = 0
  193.                     IF U(ff).max1 - U(ff).X > 6 THEN U(ff).max1 = 0
  194.                 END IF
  195.                 IF U(ff).t2 > 0 THEN
  196.                     IF U(ff).t2 < TIMER THEN U(ff).Y = U(ff).Y - 6
  197.                     IF U(ff).Y <= 0 THEN U(ff).Y = 0: U(ff).t2 = 0: U(ff).max2 = 0
  198.                     IF U(ff).max2 - U(ff).Y > 6 THEN U(ff).max2 = 0
  199.                 END IF
  200.  
  201.                 U(ff).t = TIMER + .01
  202.                 U(ff).t2 = TIMER + .01
  203.  
  204.  
  205.                 IF U(ff).X > 0 AND U(ff).max1 THEN LINE (100 + ff * 10, 700 - U(ff).max1 - 5)-(100 + (ff * 10) + 8, 700 - U(ff).max1), &HFFFF0000, BF
  206.                 IF U(ff).Y > 0 AND U(ff).max2 THEN LINE (800 + ff * 10, 700 - U(ff).max2 - 5)-(800 + (ff * 10) + 8, 700 - U(ff).max2), &HFFFF0000, BF
  207.  
  208.                 LINE (100 + ff * 10, 700)-(100 + (ff * 10) + 8, 700 - U(ff).X), , BF
  209.                 LINE (800 + ff * 10, 700)-(800 + (ff * 10) + 8, 700 - U(ff).Y), , BF
  210.             NEXT
  211.             t = TIMER
  212.             I = 0
  213.         END IF
  214.  
  215.  
  216.  
  217.         lfV = (lef * 150)
  218.         riV = (righ * 150)
  219.  
  220.         X = MiddleX + SIN(k) * Radius + lfV
  221.         Y = MiddleY + COS(k) * Radius + riV
  222.         sour(I).X = X
  223.         sour(I).Y = Y
  224.         I = I + 1
  225.  
  226.         IF k = 0 THEN
  227.             predest = _DEST
  228.             _DEST Virtual
  229.             CLS
  230.             IF TIMER < Tim THEN
  231.                 FOR D = 1 TO 62
  232.                     oX = sour(D - 1).X
  233.                     oY = sour(D - 1).Y
  234.                     nX = sour(D).X
  235.                     nY = sour(D).Y
  236.                     LINE (oX, oY)-(nX, nY), _RGB32(255, 255, 255)
  237.                 NEXT D
  238.                 oX = sour(0).X
  239.                 oY = sour(0).Y
  240.                 nX = sour(62).X
  241.                 nY = sour(62).Y
  242.                 LINE (oX, oY)-(nX, nY), _RGB32(255, 255, 255)
  243.                 _DEST predest
  244.                 REDIM sour(62) AS Sour
  245.             ELSE
  246.                 mIx = _WIDTH(image) / 2
  247.                 mIh = _HEIGHT(image) / 2
  248.                 FOR D = 1 TO 62
  249.                     oX = sour(D - 1).X
  250.                     oY = sour(D - 1).Y
  251.                     nX = sour(D).X
  252.                     nY = sour(D).Y
  253.  
  254.                     iiX = mIx + SIN(D / 10) * mIx
  255.                     iiY = mIh + COS(D / 10) * mIh
  256.                     oiX = mIx + SIN((D - 1) / 10) * mIx
  257.                     oiY = mIh + COS((D - 1) / 10) * mIh
  258.                     mmIx = _WIDTH / 2
  259.                     mmiY = _HEIGHT / 2
  260.                     _MAPTRIANGLE (iiX, iiY)-(oiX, oiY)-(mIx, mIh), image& TO(oX, oY)-(nX, nY)-(mmIx, mmiY), Virtual&
  261.                 NEXT D
  262.                 oX = sour(0).X
  263.                 oY = sour(0).Y
  264.                 nX = sour(62).X
  265.                 nY = sour(62).Y
  266.  
  267.                 iiX = mIx + SIN(0) * mIx
  268.                 iiY = mIh + COS(0) * mIh
  269.                 oiX = mIx + SIN(6.2) * mIx
  270.                 oiY = mIh + COS(6.2) * mIh
  271.                 mmIx = _WIDTH / 2
  272.                 mmiY = _HEIGHT / 2
  273.                 _MAPTRIANGLE (iiX, iiY)-(oiX, oiY)-(mIx, mIh), image& TO(oX, oY)-(nX, nY)-(mmIx, mmiY), Virtual&
  274.  
  275.                 _DEST predest
  276.                 REDIM sour(62) AS Sour
  277.             END IF
  278.         END IF
  279.  
  280.         text$ = "Example how visualise music in QB64.    Writed Petr Preclik."
  281.         IF tim2 < TIMER THEN
  282.             TextX = TextX - 3: IF TextX < -_PRINTWIDTH(text$) THEN TextX = _PRINTWIDTH(text$) + _WIDTH
  283.             tim2 = TIMER + .1
  284.         END IF
  285.     END IF
  286.  
  287.  
  288. analyzer:
  289. FrekvL = FrekvLeft * 100: lf = 0
  290. FrekvR = FrekvRight * 100: rf = 0
  291.  
  292. SELECT CASE FrekvL
  293.     CASE 100 TO 300: sh = 0
  294.     CASE 300 TO 600: sh = 1
  295.     CASE 600 TO 1000: sh = 2
  296.     CASE 1000 TO 1500: sh = 3
  297.     CASE 1500 TO 2000: sh = 4
  298.     CASE 2000 TO 2500: sh = 5
  299.     CASE 2500 TO 3000: sh = 6
  300.     CASE 3000 TO 3500: sh = 7
  301.     CASE 3500 TO 4000: sh = 8
  302.     CASE 4000 TO 4500: sh = 9
  303.     CASE 4500 TO 5000: sh = 10
  304.     CASE 5500 TO 6000: sh = 11
  305.     CASE 6000 TO 10000: sh = 12
  306.     CASE 10000 TO 20000: sh = 13
  307. U(sh).L1 = U(sh).L1 + .09
  308.  
  309.  
  310. SELECT CASE FrekvR
  311.     CASE 100 TO 300: sh2 = 0
  312.     CASE 300 TO 600: sh2 = 1
  313.     CASE 600 TO 1000: sh2 = 2
  314.     CASE 1000 TO 1500: sh2 = 3
  315.     CASE 1500 TO 2000: sh2 = 4
  316.     CASE 2000 TO 2500: sh2 = 5
  317.     CASE 2500 TO 3000: sh2 = 6
  318.     CASE 3000 TO 3500: sh2 = 7
  319.     CASE 3500 TO 4000: sh2 = 8
  320.     CASE 4000 TO 4500: sh2 = 9
  321.     CASE 4500 TO 5000: sh2 = 10
  322.     CASE 5000 TO 5500: sh2 = 11
  323.     CASE 6000 TO 10000: sh2 = 12
  324.     CASE 10000 TO 20000: sh2 = 13
  325. U(sh2).L2 = U(sh2).L2 + .09
  326.  
  327. FUNCTION WaveMovement (New_Value, Old_Value) ' Tested with WAV from 100 Hz to 20 KHz and return correct outputs (see row 156 to 164, previous signal values on row 163, 164 are needed)
  328.     IF New_Value > Old_Value THEN WaveMovement = 1
  329.     IF New_Value < Old_Value THEN WaveMovement = -1
  330.  

If you need, download WAV file from https://drive.google.com/file/d/10HBu8_QD_fBRc_JO3P3K_iPZVSE-7O-_/view?usp=sharing
* files.ZIP (Filesize: 122.68 KB, Downloads: 200)
« Last Edit: June 04, 2019, 04:51:52 pm by Petr »

Offline Ashish

  • Forum Resident
  • Posts: 630
  • Never Give Up!
    • View Profile
Re: Visualise music under QB64
« Reply #1 on: June 04, 2019, 11:40:26 pm »
Hi Petr! This is really good program for music visualization! I like it.
And Yes, for TIMER, it can call the SUB faster than the loop if the time taken by the loop to be completed is more than the that of TIMER.
I also noticed it when making my game. For this I adjusted both loop and the timer SUB in the same execution speed.
if (Me.success) {Me.improve()} else {Me.tryAgain()}


My Projects - https://github.com/AshishKingdom?tab=repositories
OpenGL tutorials - https://ashishkingdom.github.io/OpenGL-Tutorials

Offline johnno56

  • Forum Resident
  • Posts: 1270
  • Live long and prosper.
    • View Profile
Re: Visualise music under QB64
« Reply #2 on: June 05, 2019, 01:42:11 am »
This is REALLY cool.... Thank you!!
Logic is the beginning of wisdom.

Offline Petr

  • Forum Resident
  • Posts: 1720
  • The best code is the DNA of the hops.
    • View Profile
Re: Visualise music under QB64
« Reply #3 on: July 07, 2019, 05:09:16 pm »
Next video (QB64 program output) released!
&feature=youtu.be

This time 3D, contains dog and cat from this forum.

Next time i do 3D tunnel + some new music. I think, it is not so hard to do it :-D
« Last Edit: July 07, 2019, 05:18:25 pm by Petr »

Offline Pete

  • Forum Resident
  • Posts: 2361
  • Cuz I sez so, varmint!
    • View Profile
Re: Visualise music under QB64
« Reply #4 on: July 07, 2019, 05:37:56 pm »
Nice! I posted the vid to the QBasic Forum: https://www.tapatalk.com/groups/qbasic/viewtopic.php?f=648955&t=39521

Pete
Want to learn how to write code on cave walls? https://www.tapatalk.com/groups/qbasic/qbasic-f1/

Offline Petr

  • Forum Resident
  • Posts: 1720
  • The best code is the DNA of the hops.
    • View Profile
Re: Visualise music under QB64
« Reply #5 on: July 07, 2019, 05:43:00 pm »
Thank you, Pete.