Author Topic: Do own sound effects with QB64!  (Read 1853 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
Do own sound effects with QB64!
« on: April 21, 2020, 01:32:43 pm »
Hi.

I will gradually add simple (sometimes literally trivial) programs to this thread, which will allow you to do some sound effects. Of course, in order to use it as well easy in other programs, the output is saved as an audio .wav file.

The only limitation is that the audio input file must be WAV audio, 16 bit stereo, with a sampling frequency of 44100.

This is the first program in this occasional series: Add an echo to the sound. Use the AddEcho handle, the time at which the echo starts (for example, one tenth of a second after the sound starts, the number of echoes).

Wait a moment, the file will be created and played. It then remains on the disc.

I added a 1 piano key sound from TerryRitchie Piano program as the source audio file.


Code: QB64: [Select]
  1. TYPE Snd
  2.     Left AS SINGLE
  3.     Right AS SINGLE
  4. REDIM SHARED OutputSound(-1) AS Snd
  5.  
  6. TYPE OtpSndHelper
  7.     Offset_Start AS LONG
  8.     Offset_End AS LONG
  9. REDIM SHARED OTP(1) AS OtpSndHelper
  10. '----------------------------------------
  11.  
  12. handle = WAVtoRAW("1.wav") '                           write sound content as binary data to array OutputSound
  13. REDIM echo(0) AS Snd
  14. AddEcho handle, .1, 5, echo() '                           create new sound from old sound using echo in with time .1 seconds and 5 echoes
  15.  
  16.  
  17. REDIM Longer(0) AS Snd
  18. SNDARRCOPY echo(), 2, Longer() '                       copy done sound effect as 2 sounds consecutively
  19. ERASE echo
  20.  
  21.  
  22. SAVESOUND16S Longer(), "New_echo.wav" '                save new sound as file New_echo.wav
  23. _SNDPLAYFILE "new_echo.wav" '                          play it
  24. erase longer
  25.  
  26.  
  27. SUB SNDARRCOPY (source() AS Snd, much AS INTEGER, dest() AS Snd)
  28.     u = UBOUND(source)
  29.     REDIM dest(u * much) AS Snd
  30.     FOR c& = 1 TO much
  31.         FOR d& = 1 TO u
  32.             dest(i&) = source(d&)
  33.             i& = i& + 1
  34.     NEXT d&, c&
  35.  
  36.  
  37.  
  38.  
  39. 'delka echa v sekundach, pocet ozven
  40. SUB AddEcho (handle, lenght AS SINGLE, echoes AS INTEGER, arr() AS Snd)
  41.     Re = RawSize(handle)
  42.     BLen& = 44100 * lenght
  43.  
  44.     size = Re + (BLen& * echoes)
  45.     REDIM arr(size) AS Snd
  46.     devol = 1 / echoes
  47.     vol = 1
  48.     FOR e = 1 TO echoes
  49.         FOR c = OTP(handle).Offset_Start TO OTP(handle).Offset_End
  50.             arr(i& + s).Left = OutputSound(c).Left * vol + arr(i& + s).Left
  51.             arr(i& + s).Right = OutputSound(c).Right * vol + arr(i& + s).Right
  52.             IF arr(i&).Left > .9 THEN arr(i&).Left = .9
  53.             IF arr(i&).Left < -.9 THEN arr(i&).Left = -.9
  54.             IF arr(i&).Right > .9 THEN arr(i&).Right = .9
  55.             IF arr(i&).Right < -.9 THEN arr(i&).Right = -.9
  56.             i& = i& + 1
  57.         NEXT c
  58.         s& = s& + BLen&
  59.         i& = s&
  60.         vol = vol - devol
  61.     NEXT e
  62.  
  63. FUNCTION RawSize& (handle)
  64.     RawSize = OTP(handle).Offset_End - OTP(handle).Offset_Start
  65.  
  66.  
  67. FUNCTION WAVtoRAW (file$) '                              Function load WAV file (this just 16bit, stereo format) and load it to array as RAW.
  68.     TYPE head
  69.         chunk AS STRING * 4 '       4 bytes  (RIFF)
  70.         size AS LONG '              4 bytes  (?E??)
  71.         fomat AS STRING * 4 '       4 bytes  (WAVE)
  72.         sub1 AS STRING * 4 '        4 bytes  (fmt )
  73.         subchunksize AS LONG '      4 bytes  (lo / hi), $00000010 for PCM audio
  74.         format AS STRING * 2 '      2 bytes  (0001 = standard PCM, 0101 = IBM mu-law, 0102 = IBM a-law, 0103 = IBM AVC ADPCM)
  75.         channels AS INTEGER '       2 bytes  (1 = mono, 2 = stereo)
  76.         rate AS LONG '              4 bytes  (sample rate, standard is 44100)
  77.         ByteRate AS LONG '          4 bytes  (= sample rate * number of channels * (bits per channel /8))
  78.         Block AS INTEGER '          2 bytes  (block align = number of channels * bits per sample /8)
  79.         Bits AS INTEGER '           2 bytes  (bits per sample. 8 = 8, 16 = 16)
  80.         subchunk2 AS STRING * 4 '   4 bytes  ("data")  contains begin audio samples
  81.     END TYPE '                     40 bytes  total
  82.     DIM H AS head
  83.     ch = FREEFILE
  84.  
  85.     IF _FILEEXISTS(file$) THEN OPEN file$ FOR BINARY AS #ch ELSE PRINT file$; " not found": SLEEP 2: SYSTEM
  86.     GET #ch, , H
  87.  
  88.     block = H.Block
  89.     RATE = H.rate
  90.     chan = H.channels
  91.     bits = H.Bits
  92.  
  93.     SEEK #ch, Find_data_area(file$)
  94.  
  95.     OTP_Size = UBOUND(otp)
  96.     OTP(OTP_Size).Offset_Start = UBOUND(outputsound) + 1
  97.  
  98.     DO WHILE NOT EOF(ch)
  99.         IF bits = 16 AND chan = 2 THEN
  100.             REDIM lefi AS INTEGER, righi AS INTEGER
  101.             GET #ch, , lefi
  102.             GET #ch, , righi
  103.             lef = lefi / 65535
  104.             righ = righi / 65535
  105.         END IF
  106.  
  107.         IF RATE > 44100 THEN frekvence = RATE ELSE frekvence = 44100
  108.  
  109.         oss = UBOUND(OutputSound)
  110.         REDIM _PRESERVE OutputSound(oss + (frekvence / RATE)) AS Snd
  111.  
  112.         FOR plll = 1 TO frekvence / RATE
  113.             OutputSound(oss + plll).Left = lef
  114.             OutputSound(oss + plll).Right = righ
  115.         NEXT plll
  116.  
  117.         DO WHILE _SNDRAWLEN > 0: LOOP: REM comment this
  118.     LOOP
  119.  
  120.     OTP(OTP_Size).Offset_End = UBOUND(outputsound)
  121.     REDIM _PRESERVE OTP(OTP_Size + 1) AS OtpSndHelper
  122.     CLOSE ch
  123.     WAVtoRAW = OTP_Size
  124.  
  125. FUNCTION Find_data_area (handle$)
  126.     REDIM D AS STRING * 1024
  127.     ff = FREEFILE
  128.     OPEN handle$ FOR BINARY AS #ff
  129.     GET #ff, 1, D$
  130.     CLOSE #ff
  131.     Find_data_area = INSTR(1, D$, "data") + 8
  132.  
  133. SUB SAVESOUND16S (arr() AS Snd, file AS STRING)
  134.  
  135.     TYPE head16
  136.         chunk AS STRING * 4 '       4 bytes  (RIFF)
  137.         size AS LONG '              4 bytes  (file size)
  138.         fomat AS STRING * 4 '       4 bytes  (WAVE)
  139.         sub1 AS STRING * 4 '        4 bytes  (fmt )
  140.         subchunksize AS LONG '      4 bytes  (lo / hi), $00000010 for PCM audio
  141.         format AS INTEGER '         2 bytes  (0001 = standard PCM, 0101 = IBM mu-law, 0102 = IBM a-law, 0103 = IBM AVC ADPCM)
  142.         channels AS INTEGER '       2 bytes  (1 = mono, 2 = stereo)
  143.         rate AS LONG '              4 bytes  (sample rate, standard is 44100)
  144.         ByteRate AS LONG '          4 bytes  (= sample rate * number of channels * (bits per channel /8))
  145.         Block AS INTEGER '          2 bytes  (block align = number of channels * bits per sample /8)
  146.         Bits AS INTEGER '           2 bytes  (bits per sample. 8 = 8, 16 = 16)
  147.         subchunk2 AS STRING * 4 '   4 bytes  ("data")  contains begin audio samples
  148.         lenght AS LONG '            4 bytes  Data block size
  149.     END TYPE '                     44 bytes  total
  150.     DIM H16 AS head16
  151.     ch = FREEFILE
  152.  
  153.     H16.chunk = "RIFF"
  154.     H16.size = 44 + UBOUND(arr) * 4 'two channels, it create 16 bit, stereo wav file, one sample use 2 bytes to one channel
  155.  
  156.     H16.fomat = "WAVE"
  157.     H16.sub1 = "fmt "
  158.     H16.subchunksize = 16
  159.     H16.format = 1
  160.     H16.channels = 2
  161.     H16.rate = 44100
  162.     H16.ByteRate = 44100 * 2 * 16 / 8
  163.     H16.Block = 4
  164.     H16.Bits = 16
  165.     H16.subchunk2 = "data"
  166.     H16.lenght = UBOUND(arr) * 4
  167.     IF _FILEEXISTS(file$) THEN KILL file$
  168.  
  169.     OPEN file$ FOR BINARY AS #ch
  170.     PUT #ch, , H16
  171.     DIM LeftChannel AS INTEGER, RightChannel AS INTEGER
  172.  
  173.     FOR audiodata = 0 TO UBOUND(arr)
  174.         LeftChannel = arr(audiodata).Left * 32768
  175.         RightChannel = arr(audiodata).Right * 32768
  176.  
  177.         PUT #ch, , LeftChannel
  178.         PUT #ch, , RightChannel
  179.     NEXT
  180.     CLOSE ch
  181.  

sound used as sound source in program:
 

one from many possible outputs:
 
« Last Edit: April 21, 2020, 01:40:05 pm by Petr »

Offline Pete

  • Forum Resident
  • Posts: 2361
  • Cuz I sez so, varmint!
    • View Profile
Re: Do own sound effects with QB64!
« Reply #1 on: April 21, 2020, 02:01:37 pm »
Great idea! Back in 1980's I had to mess around with the SOUND command, or whatever Atari and T.I, equivalent was around back then, probably SOUND, too, but anyway, it took some time to approximate sound distortions and mixes to make motor sounds, doors closing, etc. for adventure games. This could develop into a nice library of sounds for other developers to use.

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: Do own sound effects with QB64 - SOUND statement to WAV!
« Reply #2 on: May 01, 2020, 02:33:04 pm »
Hi guys,

First I had to study how to get a sound of a specific frequency (it was very interesting and beneficial for me) and then I wrote this program. This is an attempt. The program reliably creates sound samples as they are created by the SOUND statement and then saves them to a WAV file. The use is a bit more complicated so far, this is my first program of this type (focused on the SOUND statement).

Code: QB64: [Select]
  1. 'example how save frequency modulated sound created using SOUND statement to WAV file
  2.  
  3. TYPE snd
  4.     Left AS SINGLE
  5.     Right AS SINGLE
  6.  
  7. TYPE OtpSndHelper
  8.     Offset_Start AS LONG
  9.     Offset_End AS LONG
  10.  
  11. REDIM SHARED OTP(1) AS OtpSndHelper '   sound help array - contains start and end offsets values for every sound track
  12. REDIM SHARED SND(0) AS snd '           "swap" - for creating 1 SOUND as RAW
  13. REDIM SHARED OutputSound(44100) AS snd 'array, which contains all sounds (NOT OUTPUT WAV data!)
  14. REDIM SHARED WAVE(0) AS snd '           real WAV output audio raw data
  15.  
  16. i = 0
  17. DIM TotalSounds(19)
  18. PRINT "Playing something using SOUND and SOUND2..."
  19. L = 1
  20. FOR bzzz = 1 TO 5
  21.     FOR S = 100 TO 2000 STEP 100
  22.         SOUND2 S, L
  23.         SOUND S, L
  24.         REDIM SND(0) AS snd
  25.         SOUND2ARR S, L, SND()
  26.         Current = ADDtoRAW(SND())
  27.  
  28.         position_in_bytes = 1 / 18 * L * i * 44100 '1/18 is value for sound leght (18 = 1 seconds), 10 is sound lenght used in this loop
  29.         RAWCOPY Current, WAVE(), position_in_bytes, 1
  30.         i = i + 1
  31.     NEXT
  32. RealLenght WAVE(), (position_in_bytes + 1 / 18 * L * 44100) / _SNDRATE '1 second lenght sound using SOUND statement is> SOUND frequency, 18
  33. PRINT "Writing file Test-SOUND.wav"
  34. SAVESOUND16S WAVE(), "Test-SOUND.wav"
  35. PRINT "Playing output..."
  36. _SNDPLAYFILE ("Test-SOUND.wav")
  37.  
  38.  
  39.  
  40.  
  41.  
  42.  
  43.  
  44.  
  45. SUB RealLenght (arr() AS snd, time AS SINGLE)
  46.     Size = time * _SNDRATE
  47.     REDIM _PRESERVE arr(Size) AS snd
  48.  
  49.  
  50. SUB RAWCOPY (handle, arr() AS snd, position AS LONG, Volume AS SINGLE)
  51.     SoundLenghtInBytes = OTP(handle).Offset_End - OTP(handle).Offset_Start
  52.     IF UBOUND(arr) < UBOUND(arr) + SoundLenghtInBytes + position THEN REDIM _PRESERVE arr(UBOUND(arr) + SoundLenghtInBytes + position) AS snd
  53.     DIM rc AS LONG, OTPS AS LONG
  54.     OTPS = OTP(handle).Offset_Start
  55.     FOR rc = position TO position + SoundLenghtInBytes
  56.  
  57.         IF arr(rc).Left THEN OutLeft = (arr(rc).Left + (OutputSound(OTPS).Left) * Volume) / 1 ELSE OutLeft = OutputSound(OTPS).Left * Volume
  58.         IF arr(rc).Right THEN OutRight = (arr(rc).Right + (OutputSound(OTPS).Right) * Volume) / 1 ELSE OutRight = OutputSound(OTPS).Right * Volume
  59.  
  60.         IF OutLeft > .9 THEN OutLeft = .9
  61.         IF OutLeft < -.9 THEN OutLeft = -.9
  62.  
  63.         IF OutRight > .9 THEN OutRight = .9
  64.         IF OutRight < -.9 THEN OutRight = -.9
  65.  
  66.         arr(rc).Left = OutLeft
  67.         arr(rc).Right = OutRight
  68.         OTPS = OTPS + 1
  69.     NEXT rc
  70.  
  71.  
  72. FUNCTION ADDtoRAW (arr() AS snd) '                              Function add RAW samples to global RAW array
  73.     size = UBOUND(arr)
  74.     oss = UBOUND(OutputSound)
  75.  
  76.     OTP_Size = UBOUND(otp)
  77.     OTP(OTP_Size).Offset_Start = UBOUND(outputsound) + 1
  78.     REDIM _PRESERVE OutputSound(oss + size) AS snd
  79.  
  80.     FOR plll = 0 TO UBOUND(arr)
  81.         OutputSound(oss + plll).Left = arr(plll).Left
  82.         OutputSound(oss + plll).Right = arr(plll).Right
  83.     NEXT plll
  84.  
  85.     OTP(OTP_Size).Offset_End = UBOUND(outputsound)
  86.     REDIM _PRESERVE OTP(OTP_Size + 1) AS OtpSndHelper
  87.  
  88.     ADDtoRAW = OTP_Size
  89.  
  90.  
  91.  
  92. SUB SOUND2 (frequency AS INTEGER, Nlenght AS SINGLE)
  93.     lenght = Nlenght * 1 / 18
  94.     samples = _SNDRATE * lenght
  95.     s = _PI / (_SNDRATE / frequency) * 2
  96.  
  97.     DO UNTIL plsam >= samples
  98.         _SNDRAW SIN(u), COS(u)
  99.         u = u + s
  100.         IF ABS(s) = _SNDRATE / frequency THEN s = s * -1
  101.         plsam = plsam + 1
  102.     LOOP
  103.  
  104. SUB SOUND2ARR (frequency AS INTEGER, Nlenght AS SINGLE, arr() AS snd)
  105.     lenght = Nlenght * 1 / 18
  106.     samples = _SNDRATE * lenght
  107.     REDIM arr(samples) AS snd
  108.     s = _PI / (_SNDRATE / frequency) * 2
  109.  
  110.     DO UNTIL plsam >= samples
  111.         arr(plsam).Left = SIN(u) * .8
  112.         arr(plsam).Right = COS(u) * .8
  113.         REM        _SNDRAW SIN(u), COS(u)
  114.         u = u + s
  115.         IF ABS(s) = _SNDRATE / frequency THEN s = s * -1
  116.         plsam = plsam + 1
  117.     LOOP
  118.  
  119. SUB SAVESOUND16S (arr() AS snd, file AS STRING)
  120.  
  121.     TYPE head16
  122.         chunk AS STRING * 4 '       4 bytes  (RIFF)
  123.         size AS LONG '              4 bytes  (file size)
  124.         fomat AS STRING * 4 '       4 bytes  (WAVE)
  125.         sub1 AS STRING * 4 '        4 bytes  (fmt )
  126.         subchunksize AS LONG '      4 bytes  (lo / hi), $00000010 for PCM audio
  127.         format AS INTEGER '         2 bytes  (0001 = standard PCM, 0101 = IBM mu-law, 0102 = IBM a-law, 0103 = IBM AVC ADPCM)
  128.         channels AS INTEGER '       2 bytes  (1 = mono, 2 = stereo)
  129.         rate AS LONG '              4 bytes  (sample rate, standard is 44100)
  130.         ByteRate AS LONG '          4 bytes  (= sample rate * number of channels * (bits per channel /8))
  131.         Block AS INTEGER '          2 bytes  (block align = number of channels * bits per sample /8)
  132.         Bits AS INTEGER '           2 bytes  (bits per sample. 8 = 8, 16 = 16)
  133.         subchunk2 AS STRING * 4 '   4 bytes  ("data")  contains begin audio samples
  134.         lenght AS LONG '            4 bytes  Data block size
  135.     END TYPE '                     44 bytes  total
  136.     DIM H16 AS head16
  137.     ch = FREEFILE
  138.  
  139.     H16.chunk = "RIFF"
  140.     H16.size = 44 + UBOUND(arr) * 4 'two channels, it create 16 bit, stereo wav file, one sample use 2 bytes to one channel
  141.  
  142.     H16.fomat = "WAVE"
  143.     H16.sub1 = "fmt "
  144.     H16.subchunksize = 16
  145.     H16.format = 1
  146.     H16.channels = 2
  147.     H16.rate = 44100
  148.     H16.ByteRate = 44100 * 2 * 16 / 8
  149.     H16.Block = 4
  150.     H16.Bits = 16
  151.     H16.subchunk2 = "data"
  152.     H16.lenght = UBOUND(arr) * 4
  153.     IF _FILEEXISTS(file$) THEN KILL file$
  154.  
  155.     OPEN file$ FOR BINARY AS #ch
  156.     PUT #ch, , H16
  157.     DIM LeftChannel AS INTEGER, RightChannel AS INTEGER
  158.  
  159.     FOR audiodata = 0 TO UBOUND(arr)
  160.         LeftChannel = arr(audiodata).Left * 32768
  161.         RightChannel = arr(audiodata).Right * 32768
  162.  
  163.         PUT #ch, , LeftChannel
  164.         PUT #ch, , RightChannel
  165.     NEXT
  166.     CLOSE ch
  167.  
  168.  

Edit: Source code repaired, unexpected tone lenght change is corrected and now works right.
« Last Edit: May 01, 2020, 03:22:50 pm by Petr »

Offline johnno56

  • Forum Resident
  • Posts: 1270
  • Live long and prosper.
    • View Profile
Re: Do own sound effects with QB64!
« Reply #3 on: May 02, 2020, 09:28:23 am »
Nicely done. I tested the output file using VLC and the Media Play and Audacity, on my Linux machine, but I have to say the the sound played much better through QB64. The other applications lacked the "echo" effect that QB64 seems to produce. Looking forward to any other sounds you may wish to produce. Cool.
Logic is the beginning of wisdom.

Offline Petr

  • Forum Resident
  • Posts: 1720
  • The best code is the DNA of the hops.
    • View Profile
Re: Do own sound effects with QB64!
« Reply #4 on: May 02, 2020, 02:34:51 pm »
Thank you, Johnno56. Echo in previous source is unexpected effect, because is playing SOUND statement, SOUND2 SUB and created WAV file in one moment. This then do echo effect. For real echo effect to output file, you can try this next upgraded source code. Echo can be set on row 37 in source code, it is ECHO (source array AS SND, ouput array AS SND, time, repeat number)

This source code write this sound + echo to WAV file:

Code: QB64: [Select]
  1. 'example how save frequency modulated sound created using SOUND statement to WAV file
  2.  
  3. TYPE snd
  4.     Left AS SINGLE
  5.     Right AS SINGLE
  6.  
  7. TYPE OtpSndHelper
  8.     Offset_Start AS LONG
  9.     Offset_End AS LONG
  10.  
  11. REDIM SHARED OTP(0) AS OtpSndHelper '   sound help array - contains start and end offsets values for every sound track
  12. REDIM SHARED SND(0) AS snd '           "swap" - for creating 1 SOUND as RAW
  13. REDIM SHARED OutputSound(44100) AS snd 'array, which contains all sounds (NOT OUTPUT WAV data!)
  14. REDIM SHARED WAVE(0) AS snd '           real WAV output audio raw data
  15.  
  16. i = 0
  17. DIM TotalSounds(19)
  18. PRINT "Playing something using SOUND and SOUND2..."
  19. L = 1
  20. FOR bzzz = 1 TO 5
  21.     FOR S = 100 TO 2000 STEP 100
  22.         REM  SOUND2 S, L
  23.         SOUND S, L
  24.         REDIM SND(0) AS snd
  25.         SOUND2ARR S, L, SND()
  26.         Current = ADDtoRAW(SND())
  27.         position_in_bytes = 1 / 18 * L * i * 44100 - prunik '1/18 is value for sound leght (18 = 1 seconds), L is sound lenght used in this loop
  28.         RAWCOPY Current, WAVE(), position_in_bytes, 1
  29.         i = i + 1
  30.         prunik = prunik + 100
  31.     NEXT
  32. RealLenght WAVE(), (position_in_bytes + 1 / 18 * L * 44100) / _SNDRATE '1 second lenght sound using SOUND statement is> SOUND frequency, 18
  33. REDIM NewWave(0) AS snd
  34. Echo WAVE(), NewWave(), .1, 6
  35. ERASE WAVE
  36. PRINT "Writing file Test-SOUND.wav"
  37.  
  38.  
  39. SAVESOUND16S NewWave(), "Test-SOUND.wav"
  40. PRINT "Playing output..."
  41. _SNDPLAYFILE ("Test-SOUND.wav")
  42.  
  43.  
  44. FUNCTION RawSize& (handle)
  45.     RawSize = OTP(handle).Offset_End - OTP(handle).Offset_Start
  46.  
  47.  
  48.  
  49.  
  50.  
  51. SUB Echo (arr() AS snd, arr2() AS snd, time, p)
  52.     Newsize = 44100 * time * p + UBOUND(arr)
  53.     REDIM _PRESERVE arr2(Newsize) AS snd
  54.     FOR e = 1 TO p 'echoes number
  55.         Start& = 44100 * e * time
  56.         COPYSND arr(), arr2(), Start&
  57.     NEXT e
  58.  
  59. SUB COPYSND (source() AS snd, dest() AS snd, startindex AS LONG)
  60.     DIM c AS LONG, d AS LONG
  61.     c = LBOUND(source)
  62.     FOR d = startindex TO startindex + UBOUND(source)
  63.         IF dest(d).Left THEN dest(d).Left = (dest(d).Left + source(c).Left) / 2 ELSE dest(d).Left = source(c).Left
  64.         IF dest(d).Right THEN dest(d).Right = (dest(d).Right + source(c).Right) / 2 ELSE dest(d).Right = source(c).Right
  65.         IF dest(d).Left > 1 THEN dest(d).Left = 1
  66.         IF dest(d).Left < -1 THEN dest(d).Left = -1
  67.  
  68.         IF dest(d).Right > 1 THEN dest(d).Right = 1
  69.         IF dest(d).Right < -1 THEN dest(d).Right = -1
  70.         c = c + 1
  71.     NEXT d
  72.  
  73.  
  74.  
  75.  
  76.  
  77.  
  78. SUB RealLenght (arr() AS snd, time AS SINGLE)
  79.     Size = time * _SNDRATE
  80.     REDIM _PRESERVE arr(Size) AS snd
  81.  
  82.  
  83. SUB RAWCOPY (handle, arr() AS snd, position AS LONG, Volume AS SINGLE)
  84.     SoundLenghtInBytes = OTP(handle).Offset_End - OTP(handle).Offset_Start
  85.     IF UBOUND(arr) < UBOUND(arr) + SoundLenghtInBytes + position THEN REDIM _PRESERVE arr(UBOUND(arr) + SoundLenghtInBytes + position) AS snd
  86.     DIM rc AS LONG, OTPS AS LONG
  87.     OTPS = OTP(handle).Offset_Start
  88.     FOR rc = position TO position + SoundLenghtInBytes
  89.  
  90.         IF arr(rc).Left THEN OutLeft = (arr(rc).Left + (OutputSound(OTPS).Left) * Volume) / 1 ELSE OutLeft = OutputSound(OTPS).Left * Volume
  91.         IF arr(rc).Right THEN OutRight = (arr(rc).Right + (OutputSound(OTPS).Right) * Volume) / 1 ELSE OutRight = OutputSound(OTPS).Right * Volume
  92.  
  93.         IF OutLeft > .9 THEN OutLeft = .9
  94.         IF OutLeft < -.9 THEN OutLeft = -.9
  95.  
  96.         IF OutRight > .9 THEN OutRight = .9
  97.         IF OutRight < -.9 THEN OutRight = -.9
  98.  
  99.         arr(rc).Left = OutLeft
  100.         arr(rc).Right = OutRight
  101.         OTPS = OTPS + 1
  102.     NEXT rc
  103.  
  104.  
  105. FUNCTION ADDtoRAW (arr() AS snd) '                              Function add RAW samples to global RAW array
  106.     size = UBOUND(arr)
  107.     oss = UBOUND(OutputSound)
  108.  
  109.     OTP_Size = UBOUND(otp)
  110.     OTP(OTP_Size).Offset_Start = UBOUND(outputsound) + 1
  111.     REDIM _PRESERVE OutputSound(oss + size) AS snd
  112.  
  113.     FOR plll = 0 TO UBOUND(arr)
  114.         OutputSound(oss + plll).Left = arr(plll).Left
  115.         OutputSound(oss + plll).Right = arr(plll).Right
  116.     NEXT plll
  117.  
  118.     OTP(OTP_Size).Offset_End = UBOUND(outputsound)
  119.     REDIM _PRESERVE OTP(OTP_Size + 1) AS OtpSndHelper
  120.  
  121.     ADDtoRAW = OTP_Size
  122.  
  123.  
  124.  
  125. SUB SOUND2 (frequency AS INTEGER, Nlenght AS SINGLE)
  126.     lenght = Nlenght / 18
  127.     DIM SAMPLES AS LONG, plsam AS LONG
  128.     SAMPLES = (44100 * lenght) - 100
  129.     s = _PI / (44100 / frequency) * 2
  130.  
  131.     DO UNTIL plsam >= SAMPLES
  132.         _SNDRAW SIN(u), COS(u)
  133.         u = u + s
  134.         IF ABS(s) >= 44100 / frequency THEN s = s * -1
  135.         plsam = plsam + 1
  136.     LOOP
  137.  
  138. SUB SOUND2ARR (frequency AS INTEGER, Nlenght AS SINGLE, arr() AS snd)
  139.     lenght = Nlenght / 18
  140.     DIM SAMPLES AS LONG, plsam AS LONG
  141.     SAMPLES = 44100 * lenght
  142.     REDIM arr(SAMPLES) AS snd
  143.     S = _PI / (44100 / frequency) * 2
  144.  
  145.     DO UNTIL plsam >= SAMPLES
  146.         arr(plsam).Left = SIN(u)
  147.         arr(plsam).Right = SIN(u)
  148.         REM        _SNDRAW SIN(u), COS(u)
  149.         u = u + S
  150.         IF ABS(S) >= _SNDRATE / frequency THEN S = S * -1
  151.         plsam = plsam + 1
  152.     LOOP
  153.  
  154. SUB SAVESOUND16S (arr() AS snd, file AS STRING)
  155.  
  156.     TYPE head16
  157.         chunk AS STRING * 4 '       4 bytes  (RIFF)
  158.         size AS LONG '              4 bytes  (file size)
  159.         fomat AS STRING * 4 '       4 bytes  (WAVE)
  160.         sub1 AS STRING * 4 '        4 bytes  (fmt )
  161.         subchunksize AS LONG '      4 bytes  (lo / hi), $00000010 for PCM audio
  162.         format AS INTEGER '         2 bytes  (0001 = standard PCM, 0101 = IBM mu-law, 0102 = IBM a-law, 0103 = IBM AVC ADPCM)
  163.         channels AS INTEGER '       2 bytes  (1 = mono, 2 = stereo)
  164.         rate AS LONG '              4 bytes  (sample rate, standard is 44100)
  165.         ByteRate AS LONG '          4 bytes  (= sample rate * number of channels * (bits per channel /8))
  166.         Block AS INTEGER '          2 bytes  (block align = number of channels * bits per sample /8)
  167.         Bits AS INTEGER '           2 bytes  (bits per sample. 8 = 8, 16 = 16)
  168.         subchunk2 AS STRING * 4 '   4 bytes  ("data")  contains begin audio samples
  169.         lenght AS LONG '            4 bytes  Data block size
  170.     END TYPE '                     44 bytes  total
  171.     DIM H16 AS head16
  172.     ch = FREEFILE
  173.  
  174.     H16.chunk = "RIFF"
  175.     H16.size = 44 + UBOUND(arr) * 4 'two channels, it create 16 bit, stereo wav file, one sample use 2 bytes to one channel
  176.  
  177.     H16.fomat = "WAVE"
  178.     H16.sub1 = "fmt "
  179.     H16.subchunksize = 16
  180.     H16.format = 1
  181.     H16.channels = 2
  182.     H16.rate = 44100
  183.     H16.ByteRate = 44100 * 2 * 16 / 8
  184.     H16.Block = 4
  185.     H16.Bits = 16
  186.     H16.subchunk2 = "data"
  187.     H16.lenght = UBOUND(arr) * 4
  188.     IF _FILEEXISTS(file$) THEN KILL file$
  189.  
  190.     OPEN file$ FOR BINARY AS #ch
  191.     PUT #ch, , H16
  192.     DIM LeftChannel AS INTEGER, RightChannel AS INTEGER
  193.  
  194.     FOR audiodata = 0 TO UBOUND(arr)
  195.         LeftChannel = arr(audiodata).Left * 32768
  196.         RightChannel = arr(audiodata).Right * 32768
  197.  
  198.         PUT #ch, , LeftChannel
  199.         PUT #ch, , RightChannel
  200.     NEXT
  201.     CLOSE ch
  202.  

Offline Petr

  • Forum Resident
  • Posts: 1720
  • The best code is the DNA of the hops.
    • View Profile
Re: Do own sound effects with QB64!
« Reply #5 on: May 03, 2020, 08:44:00 am »
For those who want to try some effects, I'm adding a much faster version of the previous program here. This version also fixes a memory leak that was in previous versions. For comparison, try to generate the same number of repetitions of some tones with this and the previous version. The difference in processing speed is considerable.

Code: QB64: [Select]
  1. 'example how save frequency modulated sound created using SOUND statement to WAV file - SPEED UP version
  2.  
  3. TYPE snd
  4.     Left AS SINGLE
  5.     Right AS SINGLE
  6.  
  7. TYPE OtpSndHelper
  8.     Offset_Start AS LONG
  9.     Offset_End AS LONG
  10.  
  11. REDIM SHARED OTP(0) AS OtpSndHelper '   sound help array - contains start and end offsets values for every sound track
  12. REDIM SHARED SND(0) AS snd '           "swap" - for creating 1 SOUND as RAW
  13. REDIM SHARED OutputSound(44100) AS snd 'array, which contains all sounds (NOT OUTPUT WAV data!)
  14. REDIM SHARED WAVE(0) AS snd '           real WAV output audio raw data
  15.  
  16. i = 0
  17.  
  18. PRINT "Creating some tone..."
  19. L = .25 'tone lenght
  20.  
  21.  
  22.  
  23.  
  24. FOR bzzz = 1 TO 5 'repetition
  25.     PRINT "repetition:"; bzzz
  26.     FOR s = 100 TO 2000 STEP 10 'tone frequency
  27.  
  28.         REM  SOUND2 S, L
  29.         REM   SOUND S, L
  30.  
  31.         REDIM SND(0) AS snd
  32.         SOUND2ARR s, L, SND()
  33.         Current& = ADDtoRAW(SND())
  34.         position_in_bytes& = 1 / 18 * L * i& * 44100 - prunik '1/18 is value for sound leght (18 = 1 seconds), L is sound lenght used in this loop
  35.         RAWCOPY Current&, WAVE(), position_in_bytes&, 1
  36.         i& = i& + 1
  37.         prunik = prunik + 100
  38.     NEXT
  39. RealLenght WAVE(), (position_in_bytes& + 1 / 18 * L * 44100) / _SNDRATE '1 second lenght sound using SOUND statement is> SOUND frequency, 18
  40. REDIM NewWave(0) AS snd
  41. PRINT "add echo..."
  42. Echo WAVE(), NewWave(), .1, 6
  43. ERASE WAVE
  44. PRINT "Writing file Test-SOUND.wav"
  45. PRINT UBOUND(outputsound)
  46. ERASE OutputSound
  47.  
  48.  
  49. SAVESOUND16S NewWave(), "Test-SOUND.wav"
  50. PRINT "Playing output..."
  51. _SNDPLAYFILE ("Test-SOUND.wav")
  52.  
  53. SUB Echo (arr() AS snd, arr2() AS snd, time, p)
  54.     Newsize = 44100 * time * p + UBOUND(arr)
  55.     REDIM _PRESERVE arr2(Newsize) AS snd
  56.     FOR e = 1 TO p 'echoes number
  57.         Start& = 44100 * e * time
  58.         COPYSND arr(), arr2(), Start&
  59.     NEXT e
  60.  
  61. SUB COPYSND (source() AS snd, dest() AS snd, startindex AS LONG)
  62.  
  63.     DIM c AS LONG, d AS LONG
  64.     c = LBOUND(source)
  65.     d = startindex
  66.  
  67.     DO UNTIL d = startindex + UBOUND(source)
  68.         IF dest(d).Left THEN dest(d).Left = (dest(d).Left + source(c).Left) / 2 ELSE dest(d).Left = source(c).Left
  69.         IF dest(d).Right THEN dest(d).Right = (dest(d).Right + source(c).Right) / 2 ELSE dest(d).Right = source(c).Right
  70.         IF dest(d).Left > 1 THEN dest(d).Left = 1
  71.         IF dest(d).Left < -1 THEN dest(d).Left = -1
  72.  
  73.         IF dest(d).Right > 1 THEN dest(d).Right = 1
  74.         IF dest(d).Right < -1 THEN dest(d).Right = -1
  75.         c = c + 1
  76.         d = d + 1
  77.     LOOP
  78.  
  79.  
  80.  
  81.  
  82.  
  83.  
  84. SUB RealLenght (arr() AS snd, time AS SINGLE)
  85.     Size = time * _SNDRATE
  86.     REDIM _PRESERVE arr(Size) AS snd
  87.  
  88.  
  89. SUB RAWCOPY (handle, arr() AS snd, position AS LONG, Volume AS SINGLE) 'Repaired memory leak -> sub speed is now 20x higher
  90.     REDIM SoundLenghtInBytes AS LONG
  91.     REDIM rc AS LONG, OTPS AS _UNSIGNED LONG
  92.     REDIM OutLeft AS SINGLE, OutRight AS SINGLE
  93.  
  94.  
  95.     SoundLenghtInBytes = OTP(handle).Offset_End - OTP(handle).Offset_Start
  96.  
  97.  
  98.     OldLen = UBOUND(arr)
  99.     NewLen = position + SoundLenghtInBytes
  100.     REDIM _PRESERVE arr(NewLen) AS snd
  101.  
  102.     OTPS = OTP(handle).Offset_Start
  103.     rc = position
  104.  
  105.     DO UNTIL rc = position + SoundLenghtInBytes
  106.  
  107.         IF arr(rc).Left THEN OutLeft = (arr(rc).Left + (OutputSound(OTPS).Left) * Volume) ELSE OutLeft = OutputSound(OTPS).Left * Volume
  108.         IF arr(rc).Right THEN OutRight = (arr(rc).Right + (OutputSound(OTPS).Right) * Volume) ELSE OutRight = OutputSound(OTPS).Right * Volume
  109.  
  110.         IF OutLeft > .9 THEN OutLeft = .9
  111.         IF OutLeft < -.9 THEN OutLeft = -.9
  112.  
  113.         IF OutRight > .9 THEN OutRight = .9
  114.         IF OutRight < -.9 THEN OutRight = -.9
  115.  
  116.         arr(rc).Left = OutLeft
  117.         arr(rc).Right = OutRight
  118.         OTPS = OTPS + 1
  119.         rc = rc + 1
  120.     LOOP
  121.  
  122.  
  123. FUNCTION ADDtoRAW& (arr() AS snd) '                              Function add RAW samples to global RAW array
  124.     size = UBOUND(arr)
  125.     oss = UBOUND(OutputSound)
  126.     REDIM OTP_size AS LONG, outputsound AS LONG, plll AS LONG
  127.     OTP_size = UBOUND(OTP)
  128.     OTP(OTP_size).Offset_Start = UBOUND(outputsound) + 1
  129.     REDIM _PRESERVE OutputSound(oss + size) AS snd
  130.  
  131.     FOR plll = 0 TO UBOUND(arr)
  132.         OutputSound(oss + plll).Left = arr(plll).Left
  133.         OutputSound(oss + plll).Right = arr(plll).Right
  134.     NEXT plll
  135.  
  136.     OTP(OTP_size).Offset_End = UBOUND(outputsound)
  137.     REDIM _PRESERVE OTP(OTP_size + 1) AS OtpSndHelper
  138.  
  139.     ADDtoRAW& = OTP_size
  140.  
  141.  
  142.  
  143. SUB SOUND2 (frequency AS INTEGER, Nlenght AS SINGLE)
  144.     lenght = Nlenght / 18
  145.     DIM SAMPLES AS LONG, plsam AS LONG
  146.     SAMPLES = (44100 * lenght) - 100
  147.     s = _PI / (44100 / frequency) * 2
  148.  
  149.     DO UNTIL plsam >= SAMPLES
  150.         _SNDRAW SIN(u), COS(u)
  151.         u = u + s
  152.         IF ABS(s) >= 44100 / frequency THEN s = s * -1
  153.         plsam = plsam + 1
  154.     LOOP
  155.  
  156. SUB SOUND2ARR (frequency AS INTEGER, Nlenght AS SINGLE, arr() AS snd)
  157.     lenght = Nlenght / 18
  158.     DIM SAMPLES AS LONG, plsam AS LONG
  159.     SAMPLES = 44100 * lenght
  160.     REDIM arr(SAMPLES) AS snd
  161.     S = _PI / (44100 / frequency) * 2
  162.  
  163.     DO UNTIL plsam >= SAMPLES
  164.         arr(plsam).Left = SIN(u)
  165.         arr(plsam).Right = SIN(u)
  166.         REM        _SNDRAW SIN(u), COS(u)
  167.         u = u + S
  168.         IF ABS(S) >= _SNDRATE / frequency THEN S = S * -1
  169.         plsam = plsam + 1
  170.     LOOP
  171.  
  172. SUB SAVESOUND16S (arr() AS snd, file AS STRING) 'speed upgraded version (now much faster), first SINGLE values are recalculated as INTEGERs and THEN writed at once to file.
  173.  
  174.     TYPE head16
  175.         chunk AS STRING * 4 '       4 bytes  (RIFF)
  176.         size AS LONG '              4 bytes  (file size)
  177.         fomat AS STRING * 4 '       4 bytes  (WAVE)
  178.         sub1 AS STRING * 4 '        4 bytes  (fmt )
  179.         subchunksize AS LONG '      4 bytes  (lo / hi), $00000010 for PCM audio
  180.         format AS INTEGER '         2 bytes  (0001 = standard PCM, 0101 = IBM mu-law, 0102 = IBM a-law, 0103 = IBM AVC ADPCM)
  181.         channels AS INTEGER '       2 bytes  (1 = mono, 2 = stereo)
  182.         rate AS LONG '              4 bytes  (sample rate, standard is 44100)
  183.         ByteRate AS LONG '          4 bytes  (= sample rate * number of channels * (bits per channel /8))
  184.         Block AS INTEGER '          2 bytes  (block align = number of channels * bits per sample /8)
  185.         Bits AS INTEGER '           2 bytes  (bits per sample. 8 = 8, 16 = 16)
  186.         subchunk2 AS STRING * 4 '   4 bytes  ("data")  contains begin audio samples
  187.         lenght AS LONG '            4 bytes  Data block size
  188.     END TYPE '                     44 bytes  total
  189.     DIM H16 AS head16
  190.     ch = FREEFILE
  191.  
  192.     H16.chunk = "RIFF"
  193.     H16.size = 44 + UBOUND(arr) * 4 'two channels, it create 16 bit, stereo wav file, one sample use 2 bytes to one channel
  194.  
  195.     H16.fomat = "WAVE"
  196.     H16.sub1 = "fmt "
  197.     H16.subchunksize = 16
  198.     H16.format = 1
  199.     H16.channels = 2
  200.     H16.rate = 44100
  201.     H16.ByteRate = 44100 * 2 * 16 / 8
  202.     H16.Block = 4
  203.     H16.Bits = 16
  204.     H16.subchunk2 = "data"
  205.     H16.lenght = UBOUND(arr) * 4
  206.     IF _FILEEXISTS(file$) THEN KILL file$
  207.  
  208.     OPEN file$ FOR BINARY AS #ch
  209.     PUT #ch, , H16
  210.  
  211.     TYPE WavAudio
  212.         LeftChannel AS INTEGER
  213.         RightChannel AS INTEGER
  214.     END TYPE
  215.     DIM WavAudio(UBOUND(arr)) AS WavAudio, AudioData AS LONG
  216.  
  217.     DO UNTIL AudioData = UBOUND(arr)
  218.         WavAudio(AudioData).LeftChannel = arr(AudioData).Left * 32768
  219.         WavAudio(AudioData).RightChannel = arr(AudioData).Right * 32768
  220.         AudioData = AudioData + 1
  221.     LOOP
  222.  
  223.     PUT #ch, , WavAudio()
  224.     _DELAY .1
  225.     ERASE WavAudio
  226.     CLOSE ch
  227.  

Offline _vince

  • Seasoned Forum Regular
  • Posts: 422
    • View Profile
Re: Do own sound effects with QB64!
« Reply #6 on: May 04, 2020, 01:35:29 am »
I've managed to work in my routines as an example, it takes a few seconds to create the file

Code: QB64: [Select]
  1. TYPE Snd
  2.     Left AS SINGLE
  3.     Right AS SINGLE
  4. REDIM SHARED OutputSound(-1) AS Snd
  5.  
  6. TYPE OtpSndHelper
  7.     Offset_Start AS LONG
  8.     Offset_End AS LONG
  9. REDIM SHARED OTP(1) AS OtpSndHelper
  10. '----------------------------------------
  11. pi = 4*atn(1)
  12.  
  13.  
  14.  
  15. handle = WAVtoRAW("1.wav") ' write sound content as binary data to array OutputSound
  16. REDIM echo(0) AS Snd
  17. AddEcho handle, 0, 1, echo() '  create new sound from old sound using echo in with time .1 seconds and 5 echoes
  18.  
  19.  
  20. 'REDIM Longer(0) AS Snd
  21. 'SNDARRCOPY echo(), 2, Longer() ' copy done sound effect as 2 sounds consecutively
  22. 'ERASE echo
  23.  
  24.  
  25.  
  26.  
  27. sw = 1024
  28. sh = 768
  29. screen _newimage(sw,sh,32)
  30. m = ubound(echo)
  31.  
  32. n = 65536
  33. dim temp(n - 1)
  34. dim leftx(n - 1), rightx(n - 1)
  35. dim leftxx_r(n - 1), leftxx_i(n - 1)
  36. dim rightxx_r(n - 1), rightxx_i(n - 1)
  37.  
  38. for i = 0 to ubound(echo)
  39.         leftx(i) = echo(i).Left
  40.         rightx(i) = echo(i).Right
  41.  
  42. pset (0, sh/6)
  43. for x=0 to sw-1
  44.         line -(x, sh/6 - 100*leftx(x*n/sw))
  45.  
  46. pset (0, 2*sh/6)
  47. for x=0 to sw-1
  48.         line -(x, 2*sh/6 - 100*rightx(x*n/sw))
  49.  
  50. rfft leftxx_r(), leftxx_i(), leftx(), n
  51. rfft rightxx_r(), rightxx_i(), rightx(), n
  52.  
  53. pset (0, 4*sh/6)
  54. for x=0 to sw-1
  55.         line -(x, 4*sh/6 - sqr(leftxx_r(x*n/2/sw)^2 +  leftxx_i(x*n/2/sw)^2))
  56.  
  57. pset (0, 5*sh/6)
  58. for x=0 to sw-1
  59.         line -(x, 5*sh/6 - sqr(rightxx_r(x*n/2/sw)^2 +  rightxx_i(x*n/2/sw)^2))
  60.  
  61.  
  62. for i=1 to 40*n/sw 'to n/2 - 1
  63.         leftxx_r(i) = 0
  64.         leftxx_i(i) = 0
  65.         leftxx_r(n - i) = 0
  66.         leftxx_i(n - i) = 0
  67.  
  68.         rightxx_r(i) = 0
  69.         rightxx_i(i) = 0
  70.         rightxx_r(n - i) = 0
  71.         rightxx_i(n - i) = 0
  72. pset (0, 4*sh/6),_rgb(0,255,0)
  73. for x=0 to sw-1
  74.         line -(x, 4*sh/6 - sqr(leftxx_r(x*n/2/sw)^2 +  leftxx_i(x*n/2/sw)^2)),_rgb(0,255,0)
  75.  
  76. pset (0, 5*sh/6),_rgb(0,255,0)
  77. for x=0 to sw-1
  78.         line -(x, 5*sh/6 - sqr(rightxx_r(x*n/2/sw)^2 +  rightxx_i(x*n/2/sw)^2)),_rgb(0,255,0)
  79.  
  80. 'line (100*
  81.  
  82.  
  83. 'inverse
  84. for i=0 to n-1
  85.         leftxx_i(i) = -leftxx_i(i)
  86.         rightxx_i(i) = -rightxx_i(i)
  87.  
  88. fft leftx(), temp(), leftxx_r(), leftxx_i(), n
  89. fft rightx(), temp(), rightxx_r(), rightxx_i(), n
  90.  
  91. for i=0 to n-1
  92.         leftx(i) = leftx(i)/n
  93.         rightx(i) = rightx(i)/n
  94.  
  95. for i = 0 to ubound(echo)
  96.         echo(i).Left = leftx(i)
  97.         echo(i).Right = rightx(i)
  98.  
  99. SAVESOUND16S echo(), "high_freq.wav" ' save new sound as file New_echo.wav
  100.  
  101. locate 1,1: ? "high pass done"
  102. handle = WAVtoRAW("1.wav") ' write sound content as binary data to array OutputSound
  103. REDIM echo(0) AS Snd
  104. AddEcho handle, 0, 1, echo() '  create new sound from old sound using echo in with time .1 seconds and 5 echoes
  105. for i = 0 to ubound(echo)
  106.         leftx(i) = echo(i).Left
  107.         rightx(i) = echo(i).Right
  108.  
  109. pset (0, sh/6)
  110. for x=0 to sw-1
  111.         line -(x, sh/6 - 100*leftx(x*n/sw))
  112.  
  113. pset (0, 2*sh/6)
  114. for x=0 to sw-1
  115.         line -(x, 2*sh/6 - 100*rightx(x*n/sw))
  116.  
  117. rfft leftxx_r(), leftxx_i(), leftx(), n
  118. rfft rightxx_r(), rightxx_i(), rightx(), n
  119.  
  120. pset (0, 4*sh/6)
  121. for x=0 to sw-1
  122.         line -(x, 4*sh/6 - sqr(leftxx_r(x*n/2/sw)^2 +  leftxx_i(x*n/2/sw)^2))
  123.  
  124. pset (0, 5*sh/6)
  125. for x=0 to sw-1
  126.         line -(x, 5*sh/6 - sqr(rightxx_r(x*n/2/sw)^2 +  rightxx_i(x*n/2/sw)^2))
  127.  
  128.  
  129. for i=40*n/sw to n/2 - 1
  130.         leftxx_r(i) = 0
  131.         leftxx_i(i) = 0
  132.         leftxx_r(n - i) = 0
  133.         leftxx_i(n - i) = 0
  134.  
  135.         rightxx_r(i) = 0
  136.         rightxx_i(i) = 0
  137.         rightxx_r(n - i) = 0
  138.         rightxx_i(n - i) = 0
  139. pset (0, 4*sh/6),_rgb(0,255,0)
  140. for x=0 to sw-1
  141.         line -(x, 4*sh/6 - sqr(leftxx_r(x*n/2/sw)^2 +  leftxx_i(x*n/2/sw)^2)),_rgb(0,255,0)
  142.  
  143. pset (0, 5*sh/6),_rgb(0,255,0)
  144. for x=0 to sw-1
  145.         line -(x, 5*sh/6 - sqr(rightxx_r(x*n/2/sw)^2 +  rightxx_i(x*n/2/sw)^2)),_rgb(0,255,0)
  146.  
  147. 'line (100*
  148.  
  149.  
  150. 'inverse
  151. for i=0 to n-1
  152.         leftxx_i(i) = -leftxx_i(i)
  153.         rightxx_i(i) = -rightxx_i(i)
  154.  
  155. fft leftx(), temp(), leftxx_r(), leftxx_i(), n
  156. fft rightx(), temp(), rightxx_r(), rightxx_i(), n
  157.  
  158. for i=0 to n-1
  159.         leftx(i) = leftx(i)/n
  160.         rightx(i) = rightx(i)/n
  161.  
  162. for i = 0 to ubound(echo)
  163.         echo(i).Left = leftx(i)
  164.         echo(i).Right = rightx(i)
  165.  
  166. locate 1,1: ? "low pass done"
  167.  
  168. SAVESOUND16S echo(), "low_freq.wav" ' save new sound as file New_echo.wav
  169. '_SNDPLAYFILE "new_echo.wav" 'play it
  170. 'ERASE longer
  171.  
  172.  
  173. sub fft(xx_r(), xx_i(), x_r(), x_i(), n)
  174.         dim w_r as double, w_i as double, wm_r as double, wm_i as double
  175.         dim u_r as double, u_i as double, v_r as double, v_i as double
  176.  
  177.         log2n = log(n)/log(2)
  178.  
  179.         'bit rev copy
  180.         for i=0 to n - 1
  181.                 rev = 0
  182.                 for j=0 to log2n - 1
  183.                         if i and (2^j) then rev = rev + (2^(log2n - 1 - j))
  184.                 next
  185.  
  186.                 xx_r(i) = x_r(rev)
  187.                 xx_i(i) = x_i(rev)
  188.         next
  189.  
  190.  
  191.         for i=1 to log2n
  192.                 m = 2^i
  193.                 wm_r = cos(-2*pi/m)
  194.                 wm_i = sin(-2*pi/m)
  195.  
  196.                 for j=0 to n - 1 step m
  197.                         w_r = 1
  198.                         w_i = 0
  199.  
  200.                         for k=0 to m/2 - 1
  201.                                 p = j + k
  202.                                 q = p + (m \ 2)
  203.  
  204.                                 u_r = w_r*xx_r(q) - w_i*xx_i(q)
  205.                                 u_i = w_r*xx_i(q) + w_i*xx_r(q)
  206.                                 v_r = xx_r(p)
  207.                                 v_i = xx_i(p)
  208.  
  209.                                 xx_r(p) = v_r + u_r
  210.                                 xx_i(p) = v_i + u_i
  211.                                 xx_r(q) = v_r - u_r
  212.                                 xx_i(q) = v_i - u_i
  213.  
  214.                                 u_r = w_r
  215.                                 u_i = w_i
  216.                                 w_r = u_r*wm_r - u_i*wm_i
  217.                                 w_i = u_r*wm_i + u_i*wm_r
  218.                         next
  219.                 next
  220.         next
  221.  
  222. sub rfft(xx_r(), xx_i(), x_r(), n)
  223.         dim w_r as double, w_i as double, wm_r as double, wm_i as double
  224.         dim u_r as double, u_i as double, v_r as double, v_i as double
  225.  
  226.         log2n = log(n/2)/log(2)
  227.  
  228.         for i=0 to n/2 - 1
  229.                 rev = 0
  230.                 for j=0 to log2n - 1
  231.                         if i and (2^j) then rev = rev + (2^(log2n - 1 - j))
  232.                 next
  233.  
  234.                 xx_r(i) = x_r(2*rev)
  235.                 xx_i(i) = x_r(2*rev + 1)
  236.         next
  237.  
  238.         for i=1 to log2n
  239.                 m = 2^i
  240.                 wm_r = cos(-2*pi/m)
  241.                 wm_i = sin(-2*pi/m)
  242.  
  243.                 for j=0 to n/2 - 1 step m
  244.                         w_r = 1
  245.                         w_i = 0
  246.  
  247.                         for k=0 to m/2 - 1
  248.                                 p = j + k
  249.                                 q = p + (m \ 2)
  250.  
  251.                                 u_r = w_r*xx_r(q) - w_i*xx_i(q)
  252.                                 u_i = w_r*xx_i(q) + w_i*xx_r(q)
  253.                                 v_r = xx_r(p)
  254.                                 v_i = xx_i(p)
  255.  
  256.                                 xx_r(p) = v_r + u_r
  257.                                 xx_i(p) = v_i + u_i
  258.                                 xx_r(q) = v_r - u_r
  259.                                 xx_i(q) = v_i - u_i
  260.  
  261.                                 u_r = w_r
  262.                                 u_i = w_i
  263.                                 w_r = u_r*wm_r - u_i*wm_i
  264.                                 w_i = u_r*wm_i + u_i*wm_r
  265.                         next
  266.                 next
  267.         next
  268.  
  269.         xx_r(n/2) = xx_r(0)
  270.         xx_i(n/2) = xx_i(0)
  271.  
  272.         for i=1 to n/2 - 1
  273.                 xx_r(n/2 + i) = xx_r(n/2 - i)
  274.                 xx_i(n/2 + i) = xx_i(n/2 - i)
  275.         next
  276.  
  277.         dim xpr as double, xpi as double
  278.         dim xmr as double, xmi as double
  279.  
  280.         for i=0 to n/2 - 1
  281.                 xpr = (xx_r(i) + xx_r(n/2 + i)) / 2
  282.                 xpi = (xx_i(i) + xx_i(n/2 + i)) / 2
  283.  
  284.                 xmr = (xx_r(i) - xx_r(n/2 + i)) / 2
  285.                 xmi = (xx_i(i) - xx_i(n/2 + i)) / 2
  286.  
  287.                 xx_r(i) = xpr + xpi*cos(2*pi*i/n) - xmr*sin(2*pi*i/n)
  288.                 xx_i(i) = xmi - xpi*sin(2*pi*i/n) - xmr*cos(2*pi*i/n)
  289.         next
  290.  
  291.         'symmetry, complex conj
  292.         for i=0 to n/2 - 1
  293.                 xx_r(n/2 + i) = xx_r(n/2 - 1 - i)
  294.                 xx_i(n/2 + i) =-xx_i(n/2 - 1 - i)
  295.         next
  296.  
  297.  
  298. SUB SNDARRCOPY (source() AS Snd, much AS INTEGER, dest() AS Snd)
  299.     u = UBOUND(source)
  300.     REDIM dest(u * much) AS Snd
  301.     FOR c& = 1 TO much
  302.         FOR d& = 1 TO u
  303.             dest(i&) = source(d&)
  304.             i& = i& + 1
  305.     NEXT d&, c&
  306.  
  307.  
  308.  
  309.  
  310. 'delka echa v sekundach, pocet ozven
  311. SUB AddEcho (handle, lenght AS SINGLE, echoes AS INTEGER, arr() AS Snd)
  312.     Re = RawSize(handle)
  313.     BLen& = 44100 * lenght
  314.  
  315.     size = Re + (BLen& * echoes)
  316.     REDIM arr(size) AS Snd
  317.     devol = 1 / echoes
  318.     vol = 1
  319.     FOR e = 1 TO echoes
  320.         FOR c = OTP(handle).Offset_Start TO OTP(handle).Offset_End
  321.             arr(i& + s).Left = OutputSound(c).Left * vol + arr(i& + s).Left
  322.             arr(i& + s).Right = OutputSound(c).Right * vol + arr(i& + s).Right
  323.             IF arr(i&).Left > .9 THEN arr(i&).Left = .9
  324.             IF arr(i&).Left < -.9 THEN arr(i&).Left = -.9
  325.             IF arr(i&).Right > .9 THEN arr(i&).Right = .9
  326.             IF arr(i&).Right < -.9 THEN arr(i&).Right = -.9
  327.             i& = i& + 1
  328.         NEXT c
  329.         s& = s& + BLen&
  330.         i& = s&
  331.         vol = vol - devol
  332.     NEXT e
  333.  
  334. FUNCTION RawSize& (handle)
  335.     RawSize = OTP(handle).Offset_End - OTP(handle).Offset_Start
  336.  
  337.  
  338. FUNCTION WAVtoRAW (file$) '                              Function load WAV file (this just 16bit, stereo format) and load it to array as RAW.
  339.     TYPE head
  340.         chunk AS STRING * 4 '       4 bytes  (RIFF)
  341.         size AS LONG '              4 bytes  (?E??)
  342.         fomat AS STRING * 4 '       4 bytes  (WAVE)
  343.         sub1 AS STRING * 4 '        4 bytes  (fmt )
  344.         subchunksize AS LONG '      4 bytes  (lo / hi), $00000010 for PCM audio
  345.         format AS STRING * 2 '      2 bytes  (0001 = standard PCM, 0101 = IBM mu-law, 0102 = IBM a-law, 0103 = IBM AVC ADPCM)
  346.         channels AS INTEGER '       2 bytes  (1 = mono, 2 = stereo)
  347.         rate AS LONG '              4 bytes  (sample rate, standard is 44100)
  348.         ByteRate AS LONG '          4 bytes  (= sample rate * number of channels * (bits per channel /8))
  349.         Block AS INTEGER '          2 bytes  (block align = number of channels * bits per sample /8)
  350.         Bits AS INTEGER '           2 bytes  (bits per sample. 8 = 8, 16 = 16)
  351.         subchunk2 AS STRING * 4 '   4 bytes  ("data")  contains begin audio samples
  352.     END TYPE '                     40 bytes  total
  353.     DIM H AS head
  354.     ch = FREEFILE
  355.  
  356.     IF _FILEEXISTS(file$) THEN OPEN file$ FOR BINARY AS #ch ELSE PRINT file$; " not found": SLEEP 2: SYSTEM
  357.     GET #ch, , H
  358.  
  359.     block = H.Block
  360.     RATE = H.rate
  361.     chan = H.channels
  362.     bits = H.Bits
  363.  
  364.     SEEK #ch, Find_data_area(file$)
  365.  
  366.     OTP_Size = UBOUND(otp)
  367.     OTP(OTP_Size).Offset_Start = UBOUND(outputsound) + 1
  368.  
  369.     DO WHILE NOT EOF(ch)
  370.         IF bits = 16 AND chan = 2 THEN
  371.             REDIM lefi AS INTEGER, righi AS INTEGER
  372.             GET #ch, , lefi
  373.             GET #ch, , righi
  374.             lef = lefi / 65535
  375.             righ = righi / 65535
  376.         END IF
  377.  
  378.         IF RATE > 44100 THEN frekvence = RATE ELSE frekvence = 44100
  379.  
  380.         oss = UBOUND(OutputSound)
  381.         REDIM _PRESERVE OutputSound(oss + (frekvence / RATE)) AS Snd
  382.  
  383.         FOR plll = 1 TO frekvence / RATE
  384.             OutputSound(oss + plll).Left = lef
  385.             OutputSound(oss + plll).Right = righ
  386.         NEXT plll
  387.  
  388.         DO WHILE _SNDRAWLEN > 0: LOOP: REM comment this
  389.     LOOP
  390.  
  391.     OTP(OTP_Size).Offset_End = UBOUND(outputsound)
  392.     REDIM _PRESERVE OTP(OTP_Size + 1) AS OtpSndHelper
  393.     CLOSE ch
  394.     WAVtoRAW = OTP_Size
  395.  
  396. FUNCTION Find_data_area (handle$)
  397.     REDIM D AS STRING * 1024
  398.     ff = FREEFILE
  399.     OPEN handle$ FOR BINARY AS #ff
  400.     GET #ff, 1, D$
  401.     CLOSE #ff
  402.     Find_data_area = INSTR(1, D$, "data") + 8
  403.  
  404. SUB SAVESOUND16S (arr() AS Snd, file AS STRING)
  405.  
  406.     TYPE head16
  407.         chunk AS STRING * 4 '       4 bytes  (RIFF)
  408.         size AS LONG '              4 bytes  (file size)
  409.         fomat AS STRING * 4 '       4 bytes  (WAVE)
  410.         sub1 AS STRING * 4 '        4 bytes  (fmt )
  411.         subchunksize AS LONG '      4 bytes  (lo / hi), $00000010 for PCM audio
  412.         format AS INTEGER '         2 bytes  (0001 = standard PCM, 0101 = IBM mu-law, 0102 = IBM a-law, 0103 = IBM AVC ADPCM)
  413.         channels AS INTEGER '       2 bytes  (1 = mono, 2 = stereo)
  414.         rate AS LONG '              4 bytes  (sample rate, standard is 44100)
  415.         ByteRate AS LONG '          4 bytes  (= sample rate * number of channels * (bits per channel /8))
  416.         Block AS INTEGER '          2 bytes  (block align = number of channels * bits per sample /8)
  417.         Bits AS INTEGER '           2 bytes  (bits per sample. 8 = 8, 16 = 16)
  418.         subchunk2 AS STRING * 4 '   4 bytes  ("data")  contains begin audio samples
  419.         lenght AS LONG '            4 bytes  Data block size
  420.     END TYPE '                     44 bytes  total
  421.     DIM H16 AS head16
  422.     ch = FREEFILE
  423.  
  424.     H16.chunk = "RIFF"
  425.     H16.size = 44 + UBOUND(arr) * 4 'two channels, it create 16 bit, stereo wav file, one sample use 2 bytes to one channel
  426.  
  427.     H16.fomat = "WAVE"
  428.     H16.sub1 = "fmt "
  429.     H16.subchunksize = 16
  430.     H16.format = 1
  431.     H16.channels = 2
  432.     H16.rate = 44100
  433.     H16.ByteRate = 44100 * 2 * 16 / 8
  434.     H16.Block = 4
  435.     H16.Bits = 16
  436.     H16.subchunk2 = "data"
  437.     H16.lenght = UBOUND(arr) * 4
  438.     IF _FILEEXISTS(file$) THEN KILL file$
  439.  
  440.     OPEN file$ FOR BINARY AS #ch
  441.     PUT #ch, , H16
  442.     DIM LeftChannel AS INTEGER, RightChannel AS INTEGER
  443.  
  444.     FOR audiodata = 0 TO UBOUND(arr)
  445.         LeftChannel = arr(audiodata).Left * 32768
  446.         RightChannel = arr(audiodata).Right * 32768
  447.  
  448.         PUT #ch, , LeftChannel
  449.         PUT #ch, , RightChannel
  450.     NEXT
  451.     CLOSE ch
  452.  

Offline Petr

  • Forum Resident
  • Posts: 1720
  • The best code is the DNA of the hops.
    • View Profile
Re: Do own sound effects with QB64!
« Reply #7 on: May 05, 2020, 04:40:47 pm »
_vince, thank you for a beautiful example! I haven't had much time in the last two days, I'll look to it depthly as soon as possible.

Offline _vince

  • Seasoned Forum Regular
  • Posts: 422
    • View Profile
Re: Do own sound effects with QB64!
« Reply #8 on: May 05, 2020, 05:54:12 pm »
No pressure, Petr, take your time, da si zabraven, razberi! These are good exercises for me being a good application of my code and potentially good info for others.  The perfect demo for this would be something like an audio equalizer, I might go for it, unfortunately SNDPLAY doesnt work on my install but your WAV reading/writing code is good enough.
« Last Edit: May 05, 2020, 07:21:00 pm by _vince »

Offline Dav

  • Forum Resident
  • Posts: 792
    • View Profile
Re: Do own sound effects with QB64!
« Reply #9 on: May 05, 2020, 06:56:14 pm »
I'm still trying to wrap my brain around this stuff, but it's pretty neat! 
 
- Dav

Offline STxAxTIC

  • Library Staff
  • Forum Resident
  • Posts: 1091
  • he lives
    • View Profile
Re: Do own sound effects with QB64!
« Reply #10 on: May 05, 2020, 08:18:58 pm »
Ah, the good old fast fourier transform. How could I not chime in?

Yes vince - make an equalizer - you're in the right place at the right time!
You're not done when it works, you're done when it's right.

Offline Petr

  • Forum Resident
  • Posts: 1720
  • The best code is the DNA of the hops.
    • View Profile
Re: Do own sound effects with QB64!
« Reply #11 on: May 28, 2020, 05:24:15 pm »
My dream of sound equalization (especially bass boosting) is coming true. I didn't use the Fourrier transform, but the trick I came across by chance. I am attaching a wav, created in QB64. Low biterate is intentional in this case and it is exactly the part of the sound that the bass part of the amplifier will amplify. The other frequencies are obtained by the difference of the samples obtained in this way.


Important note: There is a serious risk of damage to the speakers this time! I love QB64!

Message for _Vince: YA ne znayu, chto vy imeyete v vidu.

* bass.wav (Filesize: 854.83 KB, Downloads: 193)
« Last Edit: May 28, 2020, 05:25:26 pm by Petr »