Author Topic: Upcoming feature (v1.5) - _MEMSOUND (Hi, Petr!)  (Read 16407 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: Upcoming feature (v1.5) - _MEMSOUND (Hi, Petr!)
« Reply #30 on: January 02, 2021, 07:31:22 am »
  • Best Answer
  • Reply which find, must be in inversion to this program, i wrote:

    Code: QB64: [Select]
    1. SOUND 300, 18 '18 is 1 second of time
    2. SOUND2 300, 18
    3.  
    4. SUB SOUND2 (frequency AS INTEGER, Nlenght AS SINGLE)
    5.     lenght = Nlenght * 1 / 18
    6.     samples = _SNDRATE * lenght
    7.     s = _PI / (_SNDRATE / frequency) * 2
    8.  
    9.     DO UNTIL plsam >= samples
    10.         _SNDRAW SIN(u), COS(u)
    11.         u = u + s
    12.         IF ABS(s) = _SNDRATE / frequency THEN s = s * -1
    13.         plsam = plsam + 1
    14.     LOOP
    15.  
    « Last Edit: January 02, 2021, 07:32:37 am by Petr »

    Offline Petr

    • Forum Resident
    • Posts: 1720
    • The best code is the DNA of the hops.
      • View Profile
    Re: Upcoming feature (v1.5) - _MEMSOUND (Hi, Petr!)
    « Reply #31 on: January 02, 2021, 07:58:02 am »
  • Best Answer
  • Damn, it's different. The frequency is determined by the zero crossing density - not how many times the wave passes through zero, but how far apart in time. The amplitude height determines the volume. I think. I have to verify this theory. Either it will be the density of the zero crossing, or the density of the change in the direction of the amplitude (how far apart the individual peaks are).

    Offline STxAxTIC

    • Library Staff
    • Forum Resident
    • Posts: 1091
    • he lives
      • View Profile
    Re: Upcoming feature (v1.5) - _MEMSOUND (Hi, Petr!)
    « Reply #32 on: January 02, 2021, 12:16:57 pm »
  • Best Answer
  • Good morning Petr,

    An excellent question. It took me a few minutes to figure out how to get this working, but now I couldn't be happier. Hopefully this answers all of your questions...

    Starting with what's given by Fellippe, we have a "sample rate" of 44100 samples per second. That is: every 1/44100 seconds, we are handed the instantaneous volume of the sound being played. By plotting the sound data horizontally, there is precisely one "piece of data" per pixel. This means that the so-called "speed of sound" is exactly 44100 pixels per second in this scheme.

    Let me label the speed of sound on screen as C = 44100 pixels/sec.

    Now for some terms. Suppose we are handed a very simple sine curve. No weird squiggles, no squares or sawtooth waves yet, just a sine.

    The "period" ( T ) of the wave is the TIME taken, like in seconds, for the entire wave to repeat itself.
    The "frequency" ( F ) of the wave, is one divided by the period. This is why we measure frequency in Hz, which are just inverse seconds.
    The "wavelength" ( L ) of the wave, is the peak-to-peak span, measured in pixels.

    Now the cool part: What ties the variables all together, is the following wave equation:

    C = L * F

    So that means we can measure L and calculate F.

    Here is a full-blown example measurement of a 200-hz sine wave:

     [ You are not allowed to view this attachment ]  



    Alright, so what about more complicated patterns?

    Well, any complicated wave is actually the sum of simple waves, and each of those simple waves obeys the equation above. However, it's a little harder to discern what's happening by just looking at the sum of two of more waves. For an example, I chose to overlay a 400hz sine wave onto a 500hz sine wave, both having equal volumes. This gives rise to a "binaural beat" which you can actually hear at not 500hz or 400hz, but the difference of the two, aka. 100hz. I have measured the 100hz beat frequency in the image below.

     [ You are not allowed to view this attachment ]  



    In conclusion: frequency can only be reliably understood using the construction(s) above. It's misguiding to try counting it from the number of times the graph crosses the the x-axis, or anything like that. It will only turn out correct by accident, and only for pure sine curves.

    tldr:
    (i) know what the speed of sound on screen is
    (ii) measure the wavelength of the thing you're measuring
    (iii) let the equation tell you the frequency



    « Last Edit: January 02, 2021, 12:25:03 pm by STxAxTIC »
    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: Upcoming feature (v1.5) - _MEMSOUND (Hi, Petr!)
    « Reply #33 on: January 02, 2021, 03:30:30 pm »
  • Best Answer
  • Thanks for reply, STxAxTIC.

    You don't even know (actually, you know, but I didn't know) how big you're right. Carve your words in stone. If I do it the way I intended, my bass frequency - the deep tones will completely overlap the tones of the higher frequencies. So the question is, but don't even bother to explain it to me, he's already tried it _Vince - but it came across my stupidity - how the hell to filter out the individual frequencies from each other. Each of the frequencies can start anywhere in time ... yeah, I know and some Furrier transforms, but as I wrote, I'm too stupid for that.

    So here I add a program - a demonstration of how not to do it. How my bad program works:

    You are somewhere in the audio position during playback, you get the position as INT (_SNGETPOS * _SNDRATE). From this point you go back in the _MEM field to the sample, which has the opposite polarity than the sample in the place where you started. You count the number of samples from your starting point. Then you do the same thing in opposite - again from the place in the field where the same starting position is, you go to the sample, which has the opposite polarity. During this, you also add the volumes (sizes) of these samples.
    When you encounter a sample with the opposite polarity, you calculate the total volume of that sample as the sum of the volumes / number of all samples.
    The numbers of samples are added (this will give you the duration of this sample - by calculating 1 / _SNDRATE * number of these samples)
    Finally, a modified formula is used to calculate the frequency, because the described process actually detects only one half-period of the frequency.

    Now I will deviate from frequency detection again and do something else again. :)

    Code: QB64: [Select]
    1. 'Example how it not works and how not writing this...
    2.  
    3. INPUT "Insert sound file name:"; s$
    4. PRINT "Opening file..."
    5.  
    6. s = _SNDOPEN(s$)
    7. IF s = 0 THEN PRINT "Invalid sound file.": END
    8. DIM left AS _MEM, right AS _MEM, SampleL AS _UNSIGNED INTEGER, SampleR AS _UNSIGNED INTEGER
    9. DIM VolL AS SINGLE, VolR AS SINGLE, BytePos AS LONG
    10. left = _MEMSOUND(s, 1)
    11. right = _MEMSOUND(s, 2)
    12.  
    13. SCREEN _NEWIMAGE(800, 600, 32)
    14. DIM LeftBox(7) AS INTEGER
    15. DIM RightBox(7) AS INTEGER
    16. DIM LeftVol(7) AS SINGLE
    17. DIM RightVol(7) AS SINGLE
    18. LeftVol(0) = 0
    19.  
    20. FOR an = 20 TO 244 STEP 32
    21.     LeftBox(i) = an + 1
    22.     LINE (an, 300)-(an + 29, 550), , B
    23.     RightBox(i) = an + 497
    24.     LINE (an + 496, 300)-(an + 525, 550), , B
    25.     i = i + 1
    26. i = 0
    27.     IF _SNDGETPOS(s) > .2 THEN
    28.         BytePos = INT(_SNDGETPOS(s) * _SNDRATE)
    29.         _MEMGET left, left.OFFSET + BytePos, SampleL
    30.         L = SGN(SampleL)
    31.         _MEMGET right, right.OFFSET + BytePos, SampleR
    32.         R = SGN(SampleR)
    33.  
    34.         i = 0
    35.         DO UNTIL SGN(SampleL) <> L
    36.             i = i - 2
    37.             IF i < left.OFFSET THEN EXIT DO
    38.             _MEMGET left, left.OFFSET + BytePos + i, SampleL
    39.             LeftVol = LeftVol + SampleL
    40.         LOOP
    41.         j = 0
    42.         _MEMGET left, left.OFFSET + BytePos, SampleL
    43.  
    44.         DO UNTIL SGN(SampleL) <> L
    45.             j = j + 2
    46.             IF j > left.SIZE THEN EXIT DO
    47.             _MEMGET left, left.OFFSET + BytePos + j, SampleL
    48.             LeftVol = LeftVol + SampleL
    49.         LOOP
    50.         DeltaL = ABS(i / 2) + j / 2
    51.  
    52.         i = 0
    53.         DO UNTIL SGN(SampleR) <> R
    54.             i = i - 2
    55.             IF i < right.OFFSET THEN EXIT DO
    56.             _MEMGET right, right.OFFSET + BytePos + i, SampleR
    57.             RightVol = RightVol + SampleR
    58.         LOOP
    59.         j = 0
    60.         _MEMGET right, right.OFFSET + BytePos, SampleR
    61.         DO UNTIL SGN(SampleR) <> R
    62.             IF j > right.SIZE THEN EXIT DO
    63.             j = j + 2
    64.             _MEMGET right, right.OFFSET + BytePos + j, SampleR
    65.             RightVol = RightVol + SampleR
    66.         LOOP
    67.         DeltaR = ABS(i / 2) + j / 2
    68.     END IF
    69.  
    70.  
    71.     FrequencyL = 1 / 44100 * DeltaL ' Time - amplitude duration between 2 samples - Left channel
    72.     FrequencyR = 1 / 44100 * DeltaR ' Time - amplitude duration between 2 samples - Right channel
    73.  
    74.     FL = FrequencyL * _SNDRATE / 2 / _PI(2) 'Frequency Left
    75.     FR = FrequencyR * _SNDRATE / 2 / _PI(2) 'Frequency Right
    76.  
    77.  
    78.     IF LeftVol THEN VolL = LeftVol / DeltaL / 32767: LeftVol = 0 ' This, current amplitude size (volume level) - Left
    79.     IF RightVol THEN VolR = RightVol / DeltaR / 32767: RightVol = 0 '                                             Right
    80.     '   PRINT FL; VolL; FR; VolR, FrequencyL; FrequencyR
    81.  
    82.     SELECT CASE FL
    83.         CASE 0 TO 150: BoxL = 0
    84.         CASE 151 TO 350: BoxL = 1
    85.         CASE 300 TO 500: BoxL = 2
    86.         CASE 501 TO 1000: BoxL = 3
    87.         CASE 1001 TO 2200: BoxL = 4
    88.         CASE 2201 TO 5000: BoxL = 5
    89.         CASE 5001 TO 6000: BoxL = 6
    90.         CASE IS > 6001: BoxL = 7
    91.  
    92.  
    93.     END SELECT
    94.  
    95.     SELECT CASE FR
    96.         CASE 0 TO 150: BoxR = 0
    97.         CASE 151 TO 350: BoxR = 1
    98.         CASE 300 TO 500: BoxR = 2
    99.         CASE 501 TO 1000: BoxR = 3
    100.         CASE 1001 TO 2200: BoxR = 4
    101.         CASE 2201 TO 5000: BoxR = 5
    102.         CASE 5001 TO 6000: BoxR = 6
    103.         CASE IS > 6001: BoxR = 7
    104.  
    105.     END SELECT
    106.  
    107.     PCOPY 1, _DISPLAY
    108.     LeftVol(BoxL) = LeftVol(BoxL) + VolL
    109.     IF LeftVol(BoxL) > 1 THEN LeftVol(BoxL) = 1
    110.  
    111.     RightVol(BoxR) = RightVol(BoxR) + VolR
    112.     IF RightVol(BoxR) > 1 THEN RightVol(BoxR) = 1
    113.  
    114.     FOR d = 0 TO 7
    115.         LINE (LeftBox(d), 549)-(LeftBox(d) + 27, 550 - (149 * LeftVol(d))), Red, BF
    116.         IF RightVol(d) <= 1 THEN LINE (RightBox(d), 549)-(RightBox(d) + 27, 550 - (149 * RightVol(d))), Red, BF
    117.         LeftVol(d) = LeftVol(d) - .003
    118.         IF LeftVol(d) < 0 THEN LeftVol(d) = 0
    119.         RightVol(d) = RightVol(d) - .003
    120.         IF RightVol(d) < 0 THEN RightVol(d) = 0
    121.     NEXT d
    122.     _DISPLAY
    123.  
    « Last Edit: January 02, 2021, 04:09:47 pm by Petr »

    Offline STxAxTIC

    • Library Staff
    • Forum Resident
    • Posts: 1091
    • he lives
      • View Profile
    Re: Upcoming feature (v1.5) - _MEMSOUND (Hi, Petr!)
    « Reply #34 on: January 02, 2021, 05:51:36 pm »
  • Best Answer
  • Hello again Petr,

    I was successful in implementing a very crude, non-optimized, but correct discrete Fourier transform. It's very crude for now, as expected - but rather than deliver a highly polished result, I thought I would share the early stage of the whole thing working. Below are are few screenshots. The "measured" frequencies are plotted in red. For pure tones, we should expect to see only one bar at a time, which is indeed evident. For the last picture, I used the 400hz + 500hz binaural beat, which predicts TWO red bars. Luckily, they show up! So, the whole thing now works - it just needs optimization to look nice.

    Code: QB64: [Select]
    1. CONST true = -1, false = 0
    2.  
    3. pi = 4 * ATN(1)
    4.  
    5. DIM theSound AS LONG, x0 AS LONG, i AS _OFFSET, j AS _OFFSET
    6. DIM a%, c~&, k&, trackerHeight AS INTEGER
    7. DIM y0 AS LONG, y1 AS LONG
    8. DIM mouseDown AS _BYTE, f$, mouseDownOn AS INTEGER
    9. DIM sleft AS _MEM, sright AS _MEM
    10. DIM stereo AS _BYTE
    11. DIM stereoScreen AS LONG, monoScreen AS LONG
    12.  
    13. '''
    14. DIM BarFrequency AS INTEGER
    15. DIM BarWavelength AS INTEGER
    16. DIM BarPhase AS INTEGER
    17. DIM SomeText AS STRING
    18. BarFrequency = 441
    19. BarPhase = 0
    20. BarWavelength = INT(44100 / BarFrequency)
    21.  
    22.  
    23. stereoScreen = _NEWIMAGE(882, 400, 32)
    24. monoScreen = _NEWIMAGE(882, 200, 32)
    25. SCREEN monoScreen
    26.  
    27. '''
    28. DIM SHARED Amplitudes(_WIDTH) AS DOUBLE
    29. DIM SHARED Frequencies(_WIDTH) AS DOUBLE
    30. '''
    31.  
    32.  
    33. _TITLE "QB64 Sound Player + Analyzer"
    34.  
    35.     f$ = COMMAND$
    36.     start:
    37.     INPUT "File to load: ", f$
    38.  
    39. PRINT "Loading '"; f$; "'..."
    40. theSound = _SNDOPEN(f$)
    41. IF theSound = 0 THEN PRINT "Load failed.": END
    42.  
    43. loaded:
    44. sleft = _MEMSOUND(theSound, 1)
    45. sright = _MEMSOUND(theSound, 2)
    46.  
    47. IF sright.SIZE > 0 THEN
    48.     SCREEN stereoScreen
    49.     stereo = true
    50.     y0 = _HEIGHT / 4
    51.     y1 = _HEIGHT - _HEIGHT / 4
    52.     SCREEN monoScreen
    53.     y0 = _HEIGHT / 2
    54.  
    55. trackerHeight = 50
    56. _SNDPLAY theSound
    57. _SNDPAUSE theSound
    58.  
    59.         IF (_KEYDOWN(100305) OR _KEYDOWN(100306)) THEN
    60.             'BarPhase = BarPhase + SGN(_MOUSEWHEEL)
    61.             BarFrequency = BarFrequency + SGN(_MOUSEWHEEL)
    62.             BarWavelength = INT(44100 / BarFrequency)
    63.         ELSE
    64.             BarFrequency = BarFrequency + 20 * SGN(_MOUSEWHEEL)
    65.             BarWavelength = INT(44100 / BarFrequency)
    66.         END IF
    67.     WEND
    68.  
    69.         IF mouseDown = false THEN
    70.             mouseDown = true
    71.             IF _MOUSEY < _HEIGHT - trackerHeight THEN
    72.                 mouseDownOn = 1 'plot area
    73.             ELSE
    74.                 mouseDownOn = 2 'track area
    75.             END IF
    76.         ELSE
    77.             IF mouseDownOn = 2 AND _SNDPLAYING(theSound) = 0 THEN
    78.                 _SNDSETPOS theSound, map(_MOUSEX, 0, _WIDTH, 0, _SNDLEN(theSound))
    79.             END IF
    80.         END IF
    81.         IF mouseDown = false THEN
    82.             mouseDown = true
    83.             BarPhase = _MOUSEX
    84.         END IF
    85.     ELSE
    86.         IF mouseDown THEN
    87.             'toggle play
    88.             IF mouseDownOn = 1 THEN
    89.                 toggleSoundState theSound
    90.             ELSEIF mouseDownOn = 2 THEN
    91.                 _SNDSETPOS theSound, map(_MOUSEX, 0, _WIDTH, 0, _SNDLEN(theSound))
    92.             END IF
    93.             mouseDown = false
    94.             mouseDownOn = 0
    95.         END IF
    96.     END IF
    97.  
    98.     k& = _KEYHIT
    99.     IF k& = 32 THEN toggleSoundState theSound
    100.  
    101.     DIM alpha AS INTEGER
    102.     alpha = 50 'IF _SNDPLAYING(theSound) THEN alpha = 40 ELSE alpha = 20
    103.     LINE (0, 0)-(_WIDTH - 1, _HEIGHT - 1), _RGB32(0, alpha), BF
    104.     GOSUB render
    105.     IF _SNDPLAYING(theSound) = 0 THEN _PRINTSTRING (_WIDTH / 2 - 16, _HEIGHT / 2 - _FONTHEIGHT), CHR$(221) + CHR$(222)
    106.  
    107.     _DISPLAY
    108.     _LIMIT 60
    109.  
    110. render:
    111.  
    112. o = INT((_SNDGETPOS(theSound)) * _SNDRATE)
    113. WHILE o MOD 2 <> 0: o = o + 1: WEND
    114. i = sleft.OFFSET + o
    115. IF stereo THEN j = sright.OFFSET + o
    116. FOR x0 = 0 TO _WIDTH - 1
    117.     _MEMGET sleft, i, a%
    118.     IF a% < 0 THEN
    119.         c~& = _RGB32(67, 150, 33, map(a%, 0, -32767, 80, 255))
    120.     ELSE
    121.         c~& = _RGB32(67, 150, 33, map(a%, 0, 32767, 80, 255))
    122.     END IF
    123.     LINE (x0, y0)-STEP(0, map(a%, -32768, 32767, -_HEIGHT / 4, _HEIGHT / 4)), c~&, B 'F
    124.  
    125.     '''
    126.     Amplitudes(x0 + 1) = 100 * a% / 32767
    127.     '''
    128.  
    129.     IF stereo THEN
    130.         _MEMGET sright, j, a%
    131.         IF a% < 0 THEN
    132.             c~& = _RGB32(67, 150, 33, map(a%, 0, -32767, 80, 255))
    133.         ELSE
    134.             c~& = _RGB32(67, 150, 33, map(a%, 0, 32767, 80, 255))
    135.         END IF
    136.         LINE (x0, y1)-STEP(0, map(a%, -32768, 32767, -_HEIGHT / 4, _HEIGHT / 4)), c~&, B 'F
    137.     END IF
    138.  
    139.     IF (x0 MOD BarWavelength) = 0 THEN
    140.         LINE (x0 + BarPhase, 0)-(x0 + BarPhase, _HEIGHT), _RGB32(0, 100, 100, 100)
    141.     END IF
    142.  
    143.     i = i + 2
    144.     IF stereo THEN j = j + 2
    145.     IF i + 2 >= sleft.OFFSET + sleft.SIZE THEN EXIT FOR
    146.  
    147. '''
    148. FOR ii = 0 TO _WIDTH - 1
    149.     Frequencies(ii + 1) = 0
    150.     FOR jj = 0 TO _WIDTH - 1
    151.         Frequencies(ii + 1) = Frequencies(ii + 1) + Amplitudes(jj + 1) * COS(2 * pi * (ii + 1) * (jj + 1) / _WIDTH)
    152.     NEXT
    153. FOR ii = 0 TO _WIDTH / 2 - 1
    154.     LINE (2 * ii, _HEIGHT)-(2 * ii, _HEIGHT - ABS(Frequencies(ii))), _RGB32(200, 0, 0, 100)
    155. '''
    156.  
    157. 'tracker:
    158. LINE (0, _HEIGHT - trackerHeight / 3)-STEP(map(_SNDGETPOS(theSound), 0, _SNDLEN(theSound), 0, _WIDTH), -trackerHeight / 3), _RGB32(255, 180), BF
    159. IF _MOUSEY > _HEIGHT - trackerHeight THEN
    160.     LINE (0, _HEIGHT - trackerHeight)-STEP(_WIDTH, trackerHeight), _RGB32(255, 30), BF
    161.  
    162. COLOR _RGB32(200, 200, 200, 255)
    163. LOCATE 1, 1: PRINT "Measured frequency:"; BarFrequency; "Hz"
    164. SomeText = "       MWheel = Coarse Freq.": LOCATE 1, _WIDTH / 8 - LEN(SomeText): PRINT SomeText
    165. SomeText = "CTRL + MWheel = Fine Freq.  ": LOCATE 2, _WIDTH / 8 - LEN(SomeText): PRINT SomeText
    166. SomeText = "  Right-Click = Phase       ": LOCATE 3, _WIDTH / 8 - LEN(SomeText): PRINT SomeText
    167.  
    168. FUNCTION map! (value!, minRange!, maxRange!, newMinRange!, newMaxRange!)
    169.     map! = ((value! - minRange!) / (maxRange! - minRange!)) * (newMaxRange! - newMinRange!) + newMinRange!
    170.  
    171. SUB toggleSoundState (theSound AS LONG)
    172.     IF _SNDPLAYING(theSound) THEN
    173.         _SNDPAUSE theSound
    174.     ELSE
    175.         DIM i AS INTEGER
    176.         FOR i = -20 TO 20
    177.             LINE (_WIDTH / 2 + 20, _HEIGHT / 2 - 5)-STEP(-40, i)
    178.         NEXT
    179.         _SNDLOOP theSound
    180.     END IF
    181.  
    182.  
    183.  

      [ You are not allowed to view this attachment ]  
      [ You are not allowed to view this attachment ]  
      [ You are not allowed to view this attachment ]  
      [ You are not allowed to view this attachment ]  

    « Last Edit: January 02, 2021, 06:48:17 pm by STxAxTIC »
    You're not done when it works, you're done when it's right.

    Offline Craz1000

    • Forum Regular
    • Posts: 111
    • I'm OK
      • View Profile
      • Craz1000.net
    Re: Upcoming feature (v1.5) - _MEMSOUND (Hi, Petr!)
    « Reply #35 on: January 02, 2021, 09:39:47 pm »
  • Best Answer
  • I have a game project that has been on hold for over 2 years because I needed a way to get an oscilloscope working. Yes!
    « Last Edit: January 02, 2021, 09:41:30 pm by Craz1000 »

    Offline Dav

    • Forum Resident
    • Posts: 792
      • View Profile
    Re: Upcoming feature (v1.5) - _MEMSOUND (Hi, Petr!)
    « Reply #36 on: January 02, 2021, 10:34:19 pm »
  • Best Answer
  • This all sounds very interesting!   Could something like this be used to analyze a played pitch and see if it's in tune?  Like frequency 440 Hz is the note A.    I tune pianos on the side, wonder if it would be possible to make a piano tuning app in QB64?

    - Dav 

    Offline Petr

    • Forum Resident
    • Posts: 1720
    • The best code is the DNA of the hops.
      • View Profile
    Re: Upcoming feature (v1.5) - _MEMSOUND (Hi, Petr!)
    « Reply #37 on: January 03, 2021, 03:57:14 am »
  • Best Answer
  • Hi everyone.

    @STxAxTIC: I had to leave the forum yesterday, it was half past one in the morning when I finished and I don't think much of it at that time.
    Thanks for the new example, I'll take a look right away.

    @Dav : I think this option is already here, but you have to use the 32-bit version of the IDE in version 0.954 and the SFML library. I have verified that in this version it is really possible to use a microphone with the SFML library with the QB64. Of course, is there a possibility (and I haven't looked so far) that maybe @SpriggsySpriggs has developed a library that allows it in newer versions?

    Before adding post to this thread by @STxAxTIC, I wrote a simple - and sometimes I think practical - thing - when someone does a music editor, so here's a simple example of how to mute the sound at the end of an audio track. Of course, this can be done anywhere with a easy modification.

    Code: QB64: [Select]
    1. INPUT "Insert music file name:"; m$
    2. INPUT "How long retreat at the end of the track do you want to create? [sec]"; delay
    3.  
    4.     PRINT "Opening file..."
    5.     music = _SNDOPEN(m$)
    6.     DIM left AS _MEM
    7.     DIM right AS _MEM
    8.     DIM NewSound AS _MEM
    9.     DIM position AS _OFFSET, D AS DOUBLE
    10.     DIM Size AS _OFFSET, DT AS _OFFSET, audio AS INTEGER
    11.  
    12.     left = _MEMSOUND(music, 1)
    13.     right = _MEMSOUND(music, 2)
    14.  
    15.     position = left.SIZE - _SNDRATE * delay * 2 'INTEGER, so * 2
    16.     downturn = 1 / (_SNDRATE * delay)
    17.  
    18.     IF right.SIZE > 0 THEN Size = left.SIZE * 2 ELSE Size = left.SIZE
    19.     NewSound = _MEMNEW(Size)
    20.  
    21.     'create downturn on both tracks...
    22.     PRINT "Building downturn..."
    23.  
    24.     DT = left.SIZE - 2
    25.  
    26.     DO UNTIL DT <= position
    27.         _MEMGET left, left.OFFSET + DT, audio
    28.         audio = audio * D
    29.         _MEMPUT left, left.OFFSET + DT, audio
    30.         IF right.SIZE > 0 THEN
    31.             _MEMGET right, right.OFFSET + DT, audio
    32.             audio = audio * D
    33.             _MEMPUT right, right.OFFSET + DT, audio
    34.         END IF
    35.         D = D + downturn
    36.         DT = DT - 2
    37.     LOOP
    38.     PRINT "Creating new track..."
    39.     'create one stereo track
    40.     DT = 0
    41.     i = 0
    42.     DO UNTIL i = left.SIZE
    43.         _MEMGET left, left.OFFSET + i, audio
    44.         _MEMPUT NewSound, NewSound.OFFSET + DT, audio
    45.         IF right.SIZE > 0 THEN
    46.             DT = DT + 2
    47.             _MEMGET right, right.OFFSET + i, audio
    48.             _MEMPUT NewSound, NewSound.OFFSET + DT, audio
    49.         END IF
    50.         DT = DT + 2
    51.         i = i + 2
    52.     LOOP
    53.  
    54.     'save work
    55.     f$ = "downturn_test.wav"
    56.     SAVESOUND16S NewSound, f$
    57.     PRINT "New audio file saved as "; f$
    58.     PRINT "Skipping 5 seconds before applyed downturn and playing..."
    59.     _SNDCLOSE music
    60.     music = _SNDOPEN(f$)
    61.     _SNDSETPOS music, _SNDLEN(music) - delay - 5
    62.     _SNDPLAY music
    63.     PRINT "File "; m$; "not exist."
    64.     END
    65.  
    66. DO UNTIL _SNDPLAYING(music) = 0
    67. PRINT "Done"
    68. _SNDCLOSE music
    69. _MEMFREE right
    70. _MEMFREE NewSound
    71.  
    72.  
    73. FUNCTION OFFSETasI64&& (OffsetVal AS _OFFSET) 'convert OFFSET value to _INTEGER64 value -> 8 byte lenght to 8 byte lenght
    74.     DIM o AS _INTEGER64, mo AS _MEM
    75.     mo = _MEM(o)
    76.     _MEMPUT mo, mo.OFFSET, OffsetVal
    77.     _MEMFREE mo
    78.     OFFSETasI64&& = o
    79.  
    80.  
    81. SUB SAVESOUND16S (arr AS _MEM, file AS STRING)
    82.  
    83.     TYPE head16
    84.         chunk AS STRING * 4 '       4 bytes  (RIFF)
    85.         size AS LONG '              4 bytes  (file size)
    86.         fomat AS STRING * 4 '       4 bytes  (WAVE)
    87.         sub1 AS STRING * 4 '        4 bytes  (fmt )
    88.         subchunksize AS LONG '      4 bytes  (lo / hi), $00000010 for PCM audio
    89.         format AS INTEGER '         2 bytes  (0001 = standard PCM, 0101 = IBM mu-law, 0102 = IBM a-law, 0103 = IBM AVC ADPCM)
    90.         channels AS INTEGER '       2 bytes  (1 = mono, 2 = stereo)
    91.         rate AS LONG '              4 bytes  (sample rate, standard is 44100)
    92.         ByteRate AS LONG '          4 bytes  (= sample rate * number of channels * (bits per channel /8))
    93.         Block AS INTEGER '          2 bytes  (block align = number of channels * bits per sample /8)
    94.         Bits AS INTEGER '           2 bytes  (bits per sample. 8 = 8, 16 = 16)
    95.         subchunk2 AS STRING * 4 '   4 bytes  ("data")  contains begin audio samples
    96.         lenght AS LONG '            4 bytes  Data block size
    97.     END TYPE '                     44 bytes  total
    98.     DIM H16 AS head16
    99.     ch = FREEFILE
    100.  
    101.     H16.chunk = "RIFF"
    102.     H16.size = 44 + OFFSETasI64(arr.SIZE) / _SNDRATE / 4 'two channels, it create 16 bit, stereo wav file, one sample use 2 bytes to one channel
    103.  
    104.     H16.fomat = "WAVE"
    105.     H16.sub1 = "fmt "
    106.     H16.subchunksize = 16
    107.     H16.format = 1
    108.     H16.channels = 2
    109.     H16.rate = 44100
    110.     H16.ByteRate = 44100 * 2 * 16 / 8
    111.     H16.Block = 4
    112.     H16.Bits = 16
    113.     H16.subchunk2 = "data"
    114.     H16.lenght = OFFSETasI64(arr.SIZE)
    115.     IF _FILEEXISTS(file$) THEN KILL file$
    116.  
    117.     Audio$ = SPACE$(OFFSETasI64(arr.SIZE))
    118.     _MEMGET arr, arr.OFFSET, Audio$
    119.  
    120.     OPEN file$ FOR BINARY AS #ch
    121.     PUT #ch, , H16
    122.     PUT #ch, , Audio$
    123.     Audio$ = ""
    124.  
    125.     CLOSE ch
    126.  
    127.  

    Offline Petr

    • Forum Resident
    • Posts: 1720
    • The best code is the DNA of the hops.
      • View Profile
    Re: Upcoming feature (v1.5) - _MEMSOUND (Hi, Petr!)
    « Reply #38 on: January 03, 2021, 06:11:35 am »
  • Best Answer
  • @STxAxTIC  Thank you again for your example. Extra thanks for ''' separators, its very useful for me for better orientation in source code.

    I have a few questions. In the source code, line 140. Why is the Amplitudes (x0 + 1) = 100 * a% / 32767 sample a% multiplied by one hundred? I have found that this affects the height of the amplitude. Is it an increase in sensitivity because the Amplitudes field is of type DOUBLE?

    On line 168, do I understand that correctly? Do you calculate the frequency for each amplitude on the screen? Definitely yes, well yeah!

    Oh yeah! And because in line 140 you calculate the amplitude depending on the width of the screen, the width of the screen is used everywhere for the calculation. So if I determine from how many samples in the _MEM field I want to count, I will do the same, but I will use the number of samples instead of the screen width.

    This rendering on line 171 - 173 is  always draw by one column, that's probably just for graphic effect, right?

    The BarFrequency, BarWaveLenght are not used for calculation, they are just for visualization, as It look.
    « Last Edit: January 03, 2021, 06:15:51 am by Petr »

    FellippeHeitor

    • Guest
    Re: Upcoming feature (v1.5) - _MEMSOUND (Hi, Petr!)
    « Reply #39 on: January 03, 2021, 08:18:00 pm »
  • Best Answer
  • _MEMSOUND has now made it to the development build, and there have been a few fixes since the alpha testing you guys started. @Petr make sure you get the latest update (_SNDCLOSE has been fixed, also reading the new .SOUND element of the _MEM udt has been fixed).

    Anyone willing to test the cool samples above in this thread can now get the development build from https://www.qb64.org/portal/development-build/ and just compile them - keep us updated if all goes as expected.
    « Last Edit: January 03, 2021, 08:19:32 pm by FellippeHeitor »

    Offline Petr

    • Forum Resident
    • Posts: 1720
    • The best code is the DNA of the hops.
      • View Profile
    Re: Upcoming feature (v1.5) - _MEMSOUND (Hi, Petr!)
    « Reply #40 on: January 04, 2021, 10:09:06 am »
  • Best Answer
  • @FellippeHeitor   Hi, I came home from work a while ago, I'll get to it right away!

    Offline Petr

    • Forum Resident
    • Posts: 1720
    • The best code is the DNA of the hops.
      • View Profile
    Re: Upcoming feature (v1.5) - _MEMSOUND (Hi, Petr!)
    « Reply #41 on: January 04, 2021, 10:33:21 am »
  • Best Answer
  • First test and problem occur: Downloaded version is 32 bit, 488c4a1. Try this source code in attachment. In previous version, in which i test it, it do new audio track and then play it. BUT NOW, in this version it do new audio track as expected, return valid handle for sound (it open created wav audio file and handle for it is 2) but nothing is playing. If i try play this file with windows media player, it plays correctly. Because valid handle is returned, i try _SNDLEN, if track time is writed correctly and - it is. But play not.

    eee :) source code...

    Code: QB64: [Select]
    1. 'Program create new WAV soundtrack + add echo
    2.  
    3. EchoLenght = 0.12 '0.06 seconds is echo duration
    4. OverSampling = 10 'number of echoes sample passes
    5.  
    6. Echo& = _SNDRATE * EchoLenght
    7.  
    8. DO UNTIL Echo& MOD 2 = 0
    9.     Echo& = Echo& + 1
    10. DIM Left AS _MEM, Right AS _MEM, NewSound AS _MEM, Audio AS INTEGER, Audio2 AS INTEGER, Audio3 AS INTEGER, Audio4 AS INTEGER
    11. REDIM PlayableFiles(0) AS STRING
    12.  
    13.  
    14. 'INPUT "Insert audio file name:"; a1$
    15.  
    16.     FUNCTION load_dir& (s AS STRING)
    17.     FUNCTION has_next_entry& ()
    18.     SUB close_dir ()
    19.     SUB get_next_entry (s AS STRING, flags AS LONG, file_size AS LONG)
    20.  
    21. REDIM Dir(0) AS STRING, File(0) AS STRING
    22. GetFileList _CWD$, Dir(), File()
    23.  
    24.  
    25. FOR s = LBOUND(file) TO UBOUND(file)
    26.     SELECT CASE UCASE$(RIGHT$(File(s), 3))
    27.         CASE "MP3", "OGG", "WAV"
    28.             PlayableFiles(i) = File(s)
    29.             i = i + 1
    30.             REDIM _PRESERVE PlayableFiles(i) AS STRING
    31.     END SELECT
    32.  
    33. PRINT "Files to play: "; i - 1
    34.  
    35.  
    36. FOR playit = 0 TO i - 1
    37.     a1$ = PlayableFiles(playit)
    38.     PRINT "Opening file "; playit + 1; "/"; i - 1; " - "; PlayableFiles(playit)
    39.     IF a THEN _SNDCLOSE a
    40.     a = _SNDOPEN(a1$)
    41.     IF a THEN PRINT "Audio file opened" ELSE PRINT "Audio file 1 opening error.": END
    42.     LENa = _CEIL(_SNDLEN(a)) + EchoLenght
    43.     PRINT "Audio file lenght:"; LENa
    44.     NewTrackTime = LENa
    45.  
    46.  
    47.  
    48.  
    49.     Left = _MEMSOUND(a, 1)
    50.     Right = _MEMSOUND(a, 2)
    51.  
    52.     NewSoundSize& = _SNDRATE * NewTrackTime * 2 * 2 'use stereo  + use INTEGER
    53.     NewSound = _MEMNEW(NewSoundSize&)
    54.  
    55.     PRINT "SndRate:"; _SNDRATE
    56.     PRINT "Track memory len:"; Left.SIZE
    57.     PRINT "Creating audio..."
    58.  
    59.     VolDown = 1 / OverSampling
    60.     Create& = 0
    61.     NewAudio& = 0
    62.     DO UNTIL Create& >= Left.SIZE - 2
    63.         _MEMGET Left, Left.OFFSET + Create&, Audio
    64.         _MEMGET Right, Right.OFFSET + Create&, Audio2
    65.  
    66.         IF Create& > Echo& THEN
    67.  
    68.             E& = Create&
    69.             Vol = 1
    70.             DO UNTIL E& <= Create& - OverSampling
    71.                 Vol = Vol - VolDown
    72.                 _MEMGET Left, Left.OFFSET + Create& - Echo&, Audio3
    73.                 _MEMGET Left, Left.OFFSET + Create& - 2, Audio4
    74.                 Audio = (Audio + Audio3 * (Vol + .01) + Audio4 * Vol) \ 3
    75.                 E& = E& - 2
    76.             LOOP
    77.  
    78.             E& = Create&
    79.             Vol = 1
    80.             DO UNTIL E& <= Create& - OverSampling
    81.                 Vol = Vol - VolDown
    82.                 _MEMGET Right, Right.OFFSET + Create& - Echo&, Audio3
    83.                 _MEMGET Right, Right.OFFSET + Create& - 2, Audio4
    84.                 Audio2 = (Audio2 + Audio3 * (Vol + .01) + Audio4 * Vol) \ 3
    85.                 E& = E& - 2
    86.             LOOP
    87.         END IF
    88.  
    89.         _MEMPUT NewSound, NewSound.OFFSET + NewAudio&, Audio 'left channel
    90.         _MEMPUT NewSound, NewSound.OFFSET + NewAudio& + 2, Audio2 'right channel
    91.         NewAudio& = NewAudio& + 4
    92.         Create& = Create& + 2
    93.     LOOP
    94.  
    95.     PRINT "New sound created. Saving as Tracks-mix4.wav..."
    96.     SAVESOUND16S NewSound, "Tracks-mix4.wav"
    97.     PRINT "Sound saved, erasing RAM..."
    98.     _MEMFREE Left
    99.     _MEMFREE Right
    100.     _MEMFREE NewSound
    101.  
    102.     PRINT "Playing mixed sound"
    103.     snd = _SNDOPEN("tracks-mix4.wav")
    104.     _SNDPLAY snd
    105.     DO UNTIL _SNDPLAYING(snd) = 0
    106.         LOCATE 12
    107.         PRINT _SNDGETPOS(snd)
    108.     LOOP
    109.     CLS
    110.     _SNDCLOSE snd
    111.     KILL "tracks-mix4.wav"
    112.  
    113.  
    114.  
    115. FUNCTION OFFSETasI64&& (OffsetVal AS _OFFSET) 'convert OFFSET value to _INTEGER64 value -> 8 byte lenght to 8 byte lenght
    116.     DIM o AS _INTEGER64, mo AS _MEM
    117.     mo = _MEM(o)
    118.     _MEMPUT mo, mo.OFFSET, OffsetVal
    119.     _MEMFREE mo
    120.     OFFSETasI64&& = o
    121.  
    122.  
    123. SUB SAVESOUND16S (arr AS _MEM, file AS STRING)
    124.  
    125.     TYPE head16
    126.         chunk AS STRING * 4 '       4 bytes  (RIFF)
    127.         size AS LONG '              4 bytes  (file size)
    128.         fomat AS STRING * 4 '       4 bytes  (WAVE)
    129.         sub1 AS STRING * 4 '        4 bytes  (fmt )
    130.         subchunksize AS LONG '      4 bytes  (lo / hi), $00000010 for PCM audio
    131.         format AS INTEGER '         2 bytes  (0001 = standard PCM, 0101 = IBM mu-law, 0102 = IBM a-law, 0103 = IBM AVC ADPCM)
    132.         channels AS INTEGER '       2 bytes  (1 = mono, 2 = stereo)
    133.         rate AS LONG '              4 bytes  (sample rate, standard is 44100)
    134.         ByteRate AS LONG '          4 bytes  (= sample rate * number of channels * (bits per channel /8))
    135.         Block AS INTEGER '          2 bytes  (block align = number of channels * bits per sample /8)
    136.         Bits AS INTEGER '           2 bytes  (bits per sample. 8 = 8, 16 = 16)
    137.         subchunk2 AS STRING * 4 '   4 bytes  ("data")  contains begin audio samples
    138.         lenght AS LONG '            4 bytes  Data block size
    139.     END TYPE '                     44 bytes  total
    140.     DIM H16 AS head16
    141.     ch = FREEFILE
    142.  
    143.     H16.chunk = "RIFF"
    144.     H16.size = 44 + OFFSETasI64(arr.SIZE) / _SNDRATE / 4 'two channels, it create 16 bit, stereo wav file, one sample use 2 bytes to one channel
    145.  
    146.     H16.fomat = "WAVE"
    147.     H16.sub1 = "fmt "
    148.     H16.subchunksize = 16
    149.     H16.format = 1
    150.     H16.channels = 2
    151.     H16.rate = 44100
    152.     H16.ByteRate = 44100 * 2 * 16 / 8
    153.     H16.Block = 4
    154.     H16.Bits = 16
    155.     H16.subchunk2 = "data"
    156.     H16.lenght = OFFSETasI64(arr.SIZE)
    157.     IF _FILEEXISTS(file$) THEN KILL file$
    158.  
    159.     Audio$ = SPACE$(OFFSETasI64(arr.SIZE))
    160.     _MEMGET arr, arr.OFFSET, Audio$
    161.  
    162.     OPEN file$ FOR BINARY AS #ch
    163.     PUT #ch, , H16
    164.     PUT #ch, , Audio$
    165.     Audio$ = ""
    166.  
    167.     CLOSE ch
    168.  
    169. SUB GetFileList (SearchDirectory AS STRING, DirList() AS STRING, FileList() AS STRING)
    170.     CONST IS_DIR = 1
    171.     CONST IS_FILE = 2
    172.     DIM flags AS LONG, file_size AS LONG
    173.  
    174.     REDIM _PRESERVE DirList(100), FileList(100)
    175.     DirCount = 0: FileCount = 0
    176.  
    177.     IF load_dir(SearchDirectory) THEN
    178.         DO
    179.             length = has_next_entry
    180.             IF length > -1 THEN
    181.                 nam$ = SPACE$(length)
    182.                 get_next_entry nam$, flags, file_size
    183.                 IF (flags AND IS_DIR) OR _DIREXISTS(SearchDirectory + nam$) THEN
    184.                     DirCount = DirCount + 1
    185.                     IF DirCount > UBOUND(DirList) THEN REDIM _PRESERVE DirList(UBOUND(DirList) + 100)
    186.                     DirList(DirCount) = nam$
    187.                 ELSEIF (flags AND IS_FILE) OR _FILEEXISTS(SearchDirectory + nam$) THEN
    188.                     FileCount = FileCount + 1
    189.                     IF FileCount > UBOUND(filelist) THEN REDIM _PRESERVE FileList(UBOUND(filelist) + 100)
    190.                     FileList(FileCount) = nam$
    191.                 END IF
    192.             END IF
    193.         LOOP UNTIL length = -1
    194.         close_dir
    195.     ELSE
    196.     END IF
    197.     REDIM _PRESERVE DirList(DirCount)
    198.     REDIM _PRESERVE FileList(FileCount)
    199.  
    200.  
    201.  


      [ You are not allowed to view this attachment ]  
    « Last Edit: January 04, 2021, 10:38:27 am by Petr »

    FellippeHeitor

    • Guest
    Re: Upcoming feature (v1.5) - _MEMSOUND (Hi, Petr!)
    « Reply #42 on: January 04, 2021, 10:38:48 am »
  • Best Answer
  • Because you are opening sound files in sequence, and using the same variable to hold the handle, it's important to reset it to 0 before attempting to _SNDOPEN again. See if this fixes it:

    Code: QB64: [Select]
    1. 'Program create new WAV soundtrack + add echo
    2.  
    3. EchoLenght = 0.12 '0.06 seconds is echo duration
    4. OverSampling = 10 'number of echoes sample passes
    5.  
    6. Echo& = _SNDRATE * EchoLenght
    7.  
    8. DO UNTIL Echo& MOD 2 = 0
    9.     Echo& = Echo& + 1
    10. DIM Left AS _MEM, Right AS _MEM, NewSound AS _MEM, Audio AS INTEGER, Audio2 AS INTEGER, Audio3 AS INTEGER, Audio4 AS INTEGER
    11. REDIM PlayableFiles(0) AS STRING
    12.  
    13.  
    14. 'INPUT "Insert audio file name:"; a1$
    15.  
    16.     FUNCTION load_dir& (s AS STRING)
    17.     FUNCTION has_next_entry& ()
    18.     SUB close_dir ()
    19.     SUB get_next_entry (s AS STRING, flags AS LONG, file_size AS LONG)
    20.  
    21. REDIM Dir(0) AS STRING, File(0) AS STRING
    22. GetFileList _CWD$, Dir(), File()
    23.  
    24.  
    25. FOR s = LBOUND(file) TO UBOUND(file)
    26.     SELECT CASE UCASE$(RIGHT$(File(s), 3))
    27.         CASE "MP3", "OGG", "WAV"
    28.             PlayableFiles(i) = File(s)
    29.             i = i + 1
    30.             REDIM _PRESERVE PlayableFiles(i) AS STRING
    31.     END SELECT
    32.  
    33. PRINT "Files to play: "; i - 1
    34.  
    35.  
    36. FOR playit = 0 TO i - 1
    37.     a1$ = PlayableFiles(playit)
    38.     PRINT "Opening file "; playit + 1; "/"; i - 1; " - "; PlayableFiles(playit)
    39.     IF a THEN _SNDCLOSE a: a = 0
    40.     a = _SNDOPEN(a1$)
    41.     IF a THEN PRINT "Audio file opened" ELSE PRINT "Audio file 1 opening error.": END
    42.     LENa = _CEIL(_SNDLEN(a)) + EchoLenght
    43.     PRINT "Audio file lenght:"; LENa
    44.     NewTrackTime = LENa
    45.  
    46.  
    47.  
    48.  
    49.     Left = _MEMSOUND(a, 1)
    50.     Right = _MEMSOUND(a, 2)
    51.  
    52.     NewSoundSize& = _SNDRATE * NewTrackTime * 2 * 2 'use stereo  + use INTEGER
    53.     NewSound = _MEMNEW(NewSoundSize&)
    54.  
    55.     PRINT "SndRate:"; _SNDRATE
    56.     PRINT "Track memory len:"; Left.SIZE
    57.     PRINT "Creating audio..."
    58.  
    59.     VolDown = 1 / OverSampling
    60.     Create& = 0
    61.     NewAudio& = 0
    62.     DO UNTIL Create& >= Left.SIZE - 2
    63.         _MEMGET Left, Left.OFFSET + Create&, Audio
    64.         _MEMGET Right, Right.OFFSET + Create&, Audio2
    65.  
    66.         IF Create& > Echo& THEN
    67.  
    68.             E& = Create&
    69.             Vol = 1
    70.             DO UNTIL E& <= Create& - OverSampling
    71.                 Vol = Vol - VolDown
    72.                 _MEMGET Left, Left.OFFSET + Create& - Echo&, Audio3
    73.                 _MEMGET Left, Left.OFFSET + Create& - 2, Audio4
    74.                 Audio = (Audio + Audio3 * (Vol + .01) + Audio4 * Vol) \ 3
    75.                 E& = E& - 2
    76.             LOOP
    77.  
    78.             E& = Create&
    79.             Vol = 1
    80.             DO UNTIL E& <= Create& - OverSampling
    81.                 Vol = Vol - VolDown
    82.                 _MEMGET Right, Right.OFFSET + Create& - Echo&, Audio3
    83.                 _MEMGET Right, Right.OFFSET + Create& - 2, Audio4
    84.                 Audio2 = (Audio2 + Audio3 * (Vol + .01) + Audio4 * Vol) \ 3
    85.                 E& = E& - 2
    86.             LOOP
    87.         END IF
    88.  
    89.         _MEMPUT NewSound, NewSound.OFFSET + NewAudio&, Audio 'left channel
    90.         _MEMPUT NewSound, NewSound.OFFSET + NewAudio& + 2, Audio2 'right channel
    91.         NewAudio& = NewAudio& + 4
    92.         Create& = Create& + 2
    93.     LOOP
    94.  
    95.     PRINT "New sound created. Saving as Tracks-mix4.wav..."
    96.     SAVESOUND16S NewSound, "Tracks-mix4.wav"
    97.     PRINT "Sound saved, erasing RAM..."
    98.     _MEMFREE Left
    99.     _MEMFREE Right
    100.     _MEMFREE NewSound
    101.  
    102.     PRINT "Playing mixed sound"
    103.     snd = _SNDOPEN("tracks-mix4.wav")
    104.     _SNDPLAY snd
    105.     DO UNTIL _SNDPLAYING(snd) = 0
    106.         LOCATE 12
    107.         PRINT _SNDGETPOS(snd)
    108.     LOOP
    109.     CLS
    110.     _SNDCLOSE snd
    111.     KILL "tracks-mix4.wav"
    112.  
    113.  
    114.  
    115. FUNCTION OFFSETasI64&& (OffsetVal AS _OFFSET) 'convert OFFSET value to _INTEGER64 value -> 8 byte lenght to 8 byte lenght
    116.     DIM o AS _INTEGER64, mo AS _MEM
    117.     mo = _MEM(o)
    118.     _MEMPUT mo, mo.OFFSET, OffsetVal
    119.     _MEMFREE mo
    120.     OFFSETasI64&& = o
    121.  
    122.  
    123. SUB SAVESOUND16S (arr AS _MEM, file AS STRING)
    124.  
    125.     TYPE head16
    126.         chunk AS STRING * 4 '       4 bytes  (RIFF)
    127.         size AS LONG '              4 bytes  (file size)
    128.         fomat AS STRING * 4 '       4 bytes  (WAVE)
    129.         sub1 AS STRING * 4 '        4 bytes  (fmt )
    130.         subchunksize AS LONG '      4 bytes  (lo / hi), $00000010 for PCM audio
    131.         format AS INTEGER '         2 bytes  (0001 = standard PCM, 0101 = IBM mu-law, 0102 = IBM a-law, 0103 = IBM AVC ADPCM)
    132.         channels AS INTEGER '       2 bytes  (1 = mono, 2 = stereo)
    133.         rate AS LONG '              4 bytes  (sample rate, standard is 44100)
    134.         ByteRate AS LONG '          4 bytes  (= sample rate * number of channels * (bits per channel /8))
    135.         Block AS INTEGER '          2 bytes  (block align = number of channels * bits per sample /8)
    136.         Bits AS INTEGER '           2 bytes  (bits per sample. 8 = 8, 16 = 16)
    137.         subchunk2 AS STRING * 4 '   4 bytes  ("data")  contains begin audio samples
    138.         lenght AS LONG '            4 bytes  Data block size
    139.     END TYPE '                     44 bytes  total
    140.     DIM H16 AS head16
    141.     ch = FREEFILE
    142.  
    143.     H16.chunk = "RIFF"
    144.     H16.size = 44 + OFFSETasI64(arr.SIZE) / _SNDRATE / 4 'two channels, it create 16 bit, stereo wav file, one sample use 2 bytes to one channel
    145.  
    146.     H16.fomat = "WAVE"
    147.     H16.sub1 = "fmt "
    148.     H16.subchunksize = 16
    149.     H16.format = 1
    150.     H16.channels = 2
    151.     H16.rate = 44100
    152.     H16.ByteRate = 44100 * 2 * 16 / 8
    153.     H16.Block = 4
    154.     H16.Bits = 16
    155.     H16.subchunk2 = "data"
    156.     H16.lenght = OFFSETasI64(arr.SIZE)
    157.     IF _FILEEXISTS(file$) THEN KILL file$
    158.  
    159.     Audio$ = SPACE$(OFFSETasI64(arr.SIZE))
    160.     _MEMGET arr, arr.OFFSET, Audio$
    161.  
    162.     OPEN file$ FOR BINARY AS #ch
    163.     PUT #ch, , H16
    164.     PUT #ch, , Audio$
    165.     Audio$ = ""
    166.  
    167.     CLOSE ch
    168.  
    169. SUB GetFileList (SearchDirectory AS STRING, DirList() AS STRING, FileList() AS STRING)
    170.     CONST IS_DIR = 1
    171.     CONST IS_FILE = 2
    172.     DIM flags AS LONG, file_size AS LONG
    173.  
    174.     REDIM _PRESERVE DirList(100), FileList(100)
    175.     DirCount = 0: FileCount = 0
    176.  
    177.     IF load_dir(SearchDirectory) THEN
    178.         DO
    179.             length = has_next_entry
    180.             IF length > -1 THEN
    181.                 nam$ = SPACE$(length)
    182.                 get_next_entry nam$, flags, file_size
    183.                 IF (flags AND IS_DIR) OR _DIREXISTS(SearchDirectory + nam$) THEN
    184.                     DirCount = DirCount + 1
    185.                     IF DirCount > UBOUND(DirList) THEN REDIM _PRESERVE DirList(UBOUND(DirList) + 100)
    186.                     DirList(DirCount) = nam$
    187.                 ELSEIF (flags AND IS_FILE) OR _FILEEXISTS(SearchDirectory + nam$) THEN
    188.                     FileCount = FileCount + 1
    189.                     IF FileCount > UBOUND(filelist) THEN REDIM _PRESERVE FileList(UBOUND(filelist) + 100)
    190.                     FileList(FileCount) = nam$
    191.                 END IF
    192.             END IF
    193.         LOOP UNTIL length = -1
    194.         close_dir
    195.     ELSE
    196.     END IF
    197.     REDIM _PRESERVE DirList(DirCount)
    198.     REDIM _PRESERVE FileList(FileCount)
    199.  
    200.  
    201.  

    Offline Petr

    • Forum Resident
    • Posts: 1720
    • The best code is the DNA of the hops.
      • View Profile
    Re: Upcoming feature (v1.5) - _MEMSOUND (Hi, Petr!)
    « Reply #43 on: January 04, 2021, 10:43:55 am »
  • Best Answer
  • NOT. I test also your new 64bit version now and there all works without any upgrades in my source code as expected. So bug is in 32 bit version only.

    FellippeHeitor

    • Guest
    Re: Upcoming feature (v1.5) - _MEMSOUND (Hi, Petr!)
    « Reply #44 on: January 04, 2021, 10:45:27 am »
  • Best Answer
  • I'll try to reproduce it on my end. Mind sending me the offending audio file?

    Keep in mind I've been developing _MEMSOUND in a 32bit environment, btw.

    Also, keep in mind the need to reset the handle variable before loading new data.