SoundEasy.pbi include file adds CreateSound() command

Share your advanced PureBasic knowledge/code with the community.
User avatar
BasicallyPure
Enthusiast
Enthusiast
Posts: 536
Joined: Thu Mar 24, 2011 12:40 am
Location: Iowa, USA

SoundEasy.pbi include file adds CreateSound() command

Post by BasicallyPure »

This was built using modified core procedures from my application, AudioPlayground

This include file will allow you to add sounds to your programs without any external sound files.
You will have a new command: CreateSound().

Syntax:
Result = CreateSound(#sound, [frequency.f], [duration.f], [amplitude], [sweepStop.f], [waveform], [fadeIn], [fadeOut])

#sound { The number to identify the new sound. #PB_Any can be used to auto-generate this number. }
frequency { The frequency in hertz of the new sound. Default = 440. Range = 20 to sample rate / 2 - 1 }
duration { The length of the new sound in seconds. Default = 1.0 seconds. Minimum = 0.1 seconds. }
amplitude { the peak waveform magnitude from 0 to 100 (full scale). Default = 25 }
sweepStop { Sets the final frequency of a frequency sweep. Default = 0 (sweep disabled). Range is same as frequency. }
waveform { Selects 1 of 9 different waveforms. Default = #WF_Sinewave. }
............ { possible waveforms are #WF_Sinewave, #WF_SawTooth, #WF_BuzzSaw, #WF_SquareWave, #WF_Triangle, #WF_Chunosta,
............ #WF_Organ, #WF_Noise, #WF_GuidedNoise }
fadeIn { turns the fadeIn effect on/off. Default (0) = off , (1) = on }
fadeOut { turns the fadeOut effect on/off. Default (0) = off , (1) = on }

Edit: SoundEasy.pbi version 2.0
.......... InitAudioBuffer() is no longer needed in your code.
.......... This is now automatically handled by the CreateSound() procedure.
.......... A new procedure: SetSoundParameters() has been added.
.......... You will not need this procedure unless you want to change the default wav sound settings: sample rate, resolution, channels.

Edit: made compatible with EnableExplicit.

Here is a demo and the 'SoundEasy' include file.

Code: Select all

; Demo of SoundEasy.pbi include file.
; Demonstrates the CreateSound() command.
; Author: BasicallyPure
; Date:   2.24.2013

IncludeFile "SoundEasy.pbi"

flags.i = #PB_Window_SystemMenu | #PB_Window_ScreenCentered
If OpenWindow(0, 0, 0, 225, 105, "CreateSound() Demo", flags) And InitSound()
   playButton_1.i = ButtonGadget(#PB_Any,10,10,100,25,"Play Sound 1")
   playButton_2.i = ButtonGadget(#PB_Any,10,40,100,25,"Play Sound 2")
   playButton_3.i = ButtonGadget(#PB_Any,10,70,100,25,"Play Sound 3")
   
   ; Refer to the comments in the SoundEasy.pbi file for the correct CreateSound() syntax.
   snd_1.i = CreateSound(#PB_Any, 500, 2.5, 45, 1500, #WF_Triangle, 0, 1)
   snd_2.i = CreateSound(#PB_Any, 1000) ; a simple sine wave 1000 Hz, 1 second
   snd_3.i = CreateSound(#PB_Any, 800, 0.5, 10, 100, #WF_BuzzSaw, 1, 0)
   
   Repeat
      event = WaitWindowEvent()
      If event = #PB_Event_Gadget
         Select EventGadget()
            Case playButton_1
               If IsSound(snd_1) : PlaySound(snd_1) : EndIf
            Case playButton_2
               If IsSound(snd_2) : PlaySound(snd_2) : EndIf
            Case playButton_3
               If IsSound(snd_3) : PlaySound(snd_3) : EndIf
         EndSelect
      EndIf
   Until event = #PB_Event_CloseWindow
   
EndIf
Here is the include file.

Code: Select all

; Title:   SoundEasy.pbi
; Author:  BasicallyPure
; Date:    2.26.2013
; Date:    5.13.2016 edit: made EnableExplicit compatible
; Version: 2.1
; OS:      Windows, Linux, and Mac
; PB ver.  5.10
;
; Save this code with the filename "SoundEasy.pbi"
; Use IncludeFile "SoundEasy.pbi" in your code.
; You will have two new procedures you can use in your programs.
;
; 1. CreateSound(#sound, [frequency.f], [duration.f], [amplitude], [sweepStop.f], [waveform], [fadeIn], [fadeOut])
;    Refer to the comments in the CreateSound() procedure for a description of the syntax.
;
; 2. SetSoundParameters(sampleRate, resolution, channels)
;    You do not need to use this procedure unless you want to change the default sound parameters.
;    Refer to the comments in the SetSoundParameters() procedure for a description and syntax.
;

;{ waveform styles
Enumeration
   #WF_SineWave
   #WF_SawTooth
   #WF_BuzzSaw
   #WF_SquareWave
   #WF_Triangle
   #WF_Chunosta
   #WF_Organ
   #WF_Noise
   #WF_GuidedNoise
EndEnumeration
;}

Procedure SetSoundParameters(sampleRate = 44100, resolution = 2, channels = 1)
   ; use this procedure only if you need to change the default sound paramaters.
   ; sampleRate  { number of samples per second: 5512, 11025, 22050, 44100 only}
   ; resolution  { bytes per sample   (1 [8 bits] Or 2 [16 bits] only) }
   ; channels    { number of channels (1 [ mono ] Or 2 [ stereo] only) }
   
   Static sr = 44100, res = 2, ch = 1
   
   If sampleRate < 0
      Select sampleRate
         Case -1 ; return sampleRate
            ProcedureReturn sr
         Case -2 ; return resolution
            ProcedureReturn res
         Case -3 ; return number of channels
            ProcedureReturn ch
         Default
            ProcedureReturn 0
      EndSelect
   Else
      If     sampleRate <= 5512  : sr = 5512
      ElseIf sampleRate <= 11025 : sr = 11025
      ElseIf sampleRate <= 22050 : sr = 22050
      Else : sr = 44100          : EndIf
      
      If resolution <= 1 : res = 1 : Else : res = 2 : EndIf
      If channels   <= 1 : ch  = 1 : Else : ch  = 2 : EndIf
   EndIf
   
   ProcedureReturn 1
EndProcedure

Procedure.f InitAudioBuffer(duration.f = 10)
   
   ; purpose: create an empty PCM Wave format audio buffer in memory
   ; duration {sound buffer length in seconds}
   ; procedure returns a pointer that is the beginning of the wave sound header.
   ; this procedure is not intended to be used outside of this include file.
   
   Static *audBuff, length.f = 10
   Protected.i Fr, By, Nc
   
   If duration < 0
      Select duration
         Case -1 : ProcedureReturn *audBuff
         Case -2 : ProcedureReturn length
         Default : ProcedureReturn 0
      EndSelect
   Else
      length = duration
   EndIf
      
   If length < 0.1 : length = 0.1 : EndIf
   
   Fr = SetSoundParameters(-1) ; get samples per second
   By = SetSoundParameters(-2) ; get resolution { 1 byte or 2 byte }
   Nc = SetSoundParameters(-3) ; get number of channels
   
   ; calculations to complete the header info.
   Protected.l Av = Fr * By * Nc      ; average bytes per second
   Protected.w Ba = Nc *  By          ; block align
   Protected.w Bs = 08 *  By          ; bits per sample
   Protected.l Ns = Fr * length       ; total number of blocks
   Protected.l Nb = Ns * By * Nc      ; total number of audio bytes
   Protected.l Cs = 36 + By * Nc * Ns ; total chunk size (file size - 8)
   If Cs & 1 ; add pad byte if needed to make even number
      Cs + 1
   EndIf
   
   ; set a pointer to the first byte in the header DataSection
   Protected *waveHdr = ?wavHeader
   
   ; modify the default wave header as specified by the calculated parameters above
   PokeL(*waveHdr + 04, Cs) ; total size
   PokeW(*waveHdr + 22, Nc) ; number of channels
   PokeL(*waveHdr + 24, Fr) ; samples per second
   PokeL(*waveHdr + 28, Av) ; average bytes per second
   PokeW(*waveHdr + 32, Ba) ; block align
   PokeW(*waveHdr + 34, Bs) ; bits per sample
   PokeL(*waveHdr + 40, Nb) ; number of audio bytes
   
   If *audBuff : FreeMemory(*audBuff) : EndIf
   
   ; create space in memory for the audio data
   *audBuff = AllocateMemory(Cs + 8)
   
   If By = 1 ; 1 byte (8 bit) samples are unsigned so offset to 1/2 of full scale
      FillMemory(*audBuff, MemorySize(*audBuff), $80, #PB_Byte)
   ElseIf By = 2 ; 2 byte (16 bit) samples are signed so no offset is needed
      FillMemory(*audBuff, MemorySize(*audBuff), $0000, #PB_Word)
   EndIf
   
   ; put header information at the begining of sound buffer
   CopyMemory(*waveHdr,*audBuff,44)
   
   ProcedureReturn *audBuff ; return a pointer to the first byte
   
   DataSection ; don't change anything here
      wavHeader: ; placeholder values for header info.
      ; Master chunk (12 bytes) ;x xxxxx, offset
      Data.a 'R','I','F','F'    ;4 bytes, 0, chunk ID, "RIFF"
      Data.l $00000000          ;4 bytes, 4, (total file size - 8) = Cs
      Data.a 'W','A','V','E'    ;4 bytes, 8, wave ID, "WAVE"
      
      ; Format chunk (24 bytes)
      Data.a 'f','m','t',' '    ;4 bytes, 12, chunk ID "fmt "
      Data.l $00000010          ;4 bytes, 16, this chunk size = 16, (2+2+4+4+2+2)
      Data.w $0001              ;2 bytes, 20, Wave_Format_PCM
      Data.w $0000              ;2 bytes, 22, number fo channels, Nc
      Data.l $00000000          ;4 bytes, 24, samples per second, Fr
      Data.l $00000000          ;4 bytes, 28, avg bytes per second, Fr*By*Nc
      Data.w $0000              ;2 bytes, 32, block align, By*Nc
      Data.w $0000              ;2 bytes, 34, bits per sample, 8*By
      
      ; Begin data chunk (8 bytes)
      Data.a 'd','a','t','a'    ;4 bytes, 36, chunk ID, "data"
      Data.l $00000000          ;4 bytes, 40, number of audio bytes, By*Nc*Ns
   EndDataSection
   
EndProcedure

Procedure CreateSound(soundNum, frequency.f = 440, duration.f = 1.0, amplitude = 25, sweepStop.f = 0, waveform = #WF_SineWave, FadeIn = 0, FadeOut = 0)
   
   ; Syntax:
   ; Result = CreateSound(#sound, [frequency.f], [duration.f], [amplitude], [sweepStop.f], [waveform], [fadeIn], [fadeOut])
   ;
   ; #sound    { The number to identify the new sound. #PB_Any can be used to auto-generate this number. }
   ; frequency { The frequency in hertz of the new sound.  Default = 440.  Range = 20 to sample rate / 2 - 1
   ; duration  { The length of the new sound in seconds.  Default = 1.0 seconds. Minimum = 0.1 seconds.
   ; amplitude { the peak waveform magnitude from 0 to 100 (full scale).  Default = 25 }
   ; sweepStop { Sets the final frequency of a frequency sweep. Default = 0 (sweep disabled). Range is same as frequency.
   ; waveform  { Selects 1 of 9 different waveforms. Default = #WF_Sinewave. }
   ;           { possible waveforms are #WF_Sinewave, #WF_SawTooth, #WF_BuzzSaw, #WF_SquareWave, #WF_Triangle, #WF_Chunosta, #WF_Organ, #WF_Noise, #WF_GuidedNoise }
   ; fadeIn    { turns the fadeIn  effect on/off.  Default (0) = off , (1) = on }
   ; fadeOut   { turns the fadeOut effect on/off.  Default (0) = off , (1) = on }
   
   If duration < 0.1 : duration = 0.1 : EndIf
   If duration <> InitAudioBuffer(-2) : InitAudioBuffer(duration) : EndIf
   
   Protected *buffer = InitAudioBuffer(-1)
   
   If *buffer = 0 : ProcedureReturn 0 : EndIf
   
   Protected Nc.w = PeekW(*buffer + 22)   ; Nc {is number of channels}
   Protected Fr.l = PeekL(*buffer + 24)   ; Fr {is samples per second}
   Protected By.w = PeekW(*buffer + 34)/8 ; By {is bytes per sample (1 = 8 bits, 2 = 16 bits)}
   Protected Ba = Nc * By ; block align (bytes per block)
   
   Protected doSweep = #False : If sweepStop <> 0 : doSweep = #True : EndIf
   
   Static PIx2.f = #PI * 2
   Static HalfPI.f = #PI / 2
   
   If frequency >= Fr/2 ; maximum frequency is set by sample rate
      frequency = Fr/2 - 1
   ElseIf frequency < 20
      frequency = 20
   EndIf
   
   ; tweak frequency so waveform loops without glitches
   frequency = Round(frequency * duration,#PB_Round_Nearest)/duration
   
   If amplitude > 100 : amplitude = 100
   ElseIf amplitude < 0 : amplitude = 0
   EndIf
   
   If FadeIn  <> 0 : FadeIn  = 1 : EndIf
   If FadeOut <> 0 : FadeOut = 1 : EndIf
   Protected Fade = FadeIn | FadeOut << 1
   
   Protected *audioStart = *buffer + 44 ; pointer to the first audio data byte
   Protected *n = *audioStart
   Protected audBuffSize = MemorySize(*buffer) ; number of bytes in sound buffer
   Protected *audBuffEnd = *buffer + audBuffSize - 1 ; pointer to last byte in buffer
   
   If doSweep = #True
      Protected.f frequencyStep, frequencyThisInstant, frequencySpan, averageFrequency
      
      If sweepStop >= Fr/2
         sweepStop = Fr/2 - 1
      ElseIf sweepStop < 20
         sweepStop = 20
      EndIf
      
      ; linear sweep
      averageFrequency = (frequency + sweepStop) / 2
      averageFrequency = Round(averageFrequency * duration, #PB_Round_Nearest) / duration
      sweepStop = 2 * averageFrequency - frequency
      
      frequencySpan = sweepStop - frequency
   EndIf
   
   ; calculate the number of audio bytes
   Protected byteCount.l = duration * Fr * By * Nc
   If byteCount & 1 : byteCount + 1 : EndIf ; byteCount must be even number
   PokeL(*buffer + 40, byteCount)
   
   Protected.w value ; waveform magnitude at any instant in time
   Protected.f angStp = frequency / Fr * PIx2 ; angle step size in radians
   Protected.f ang = 0 ; starting angle (radians)
   Protected.i loops = byteCount / Ba ; number of loop iterations
   Protected.i loopCount = 0 ; loop counter
   Protected.f lastVal, bias ; used for guided noise
   
   If doSweep = #True : frequencyStep = frequencySpan / loops : EndIf
   
   ; if waveform formulas produce values from -1 to +1 use this scale factor
   Protected sf.f = (Pow(2,8*By)/2 - 1) * (amplitude / 100)
   
   ; adjustments for other waveforms
   Select waveform
      Case #WF_Chunosta
         sf * (1/(Sin(ACos(1/Sqr(3)))*Sin(2*ACos(1/Sqr(3)))))
         ang + HalfPI
      Case #WF_Organ
         sf / 2.25
      Case #WF_GuidedNoise
         sf / 2
      Case #WF_Triangle
         ang + HalfPI
      Case #WF_SawTooth, #WF_BuzzSaw
         ang + #PI
   EndSelect
   
   While loopCount < loops
      Select waveform
         Case #WF_SineWave
            value = Sin(ang) * sf
         Case #WF_SawTooth
            value = (ang-#PI) / #PI * sf
         Case #WF_BuzzSaw
            value = Pow(Abs((ang-#PI)/#PI), #PI) * Sign(ang-#PI) * sf
         Case #WF_SquareWave
            value = Sign(#PI-ang) * sf
         Case #WF_Triangle
            value = ((ang-#PI)/HalfPI * Sign(#PI-ang) + 1) * sf
         Case #WF_Chunosta
            value = Sin(ang) * Sin(2*ang) * sf
         Case #WF_Organ
            value = (Sin(ang) + Sin(2*ang) + Sin(4*ang)) * sf
         Case #WF_Noise
            value = sf * (((Random($7FFFFFFD)+1) / $40000000) - 1)
         Case #WF_GuidedNoise
            bias = (Sin(ang) - lastVal) * 0.5
            lastVal + ((Random($7FFFFFFD) / $40000000) - 1) + bias
            value = lastVal * sf
         Default
            ProcedureReturn 0 ; waveform was invalid
      EndSelect
      
      Select Fade
         Case %01 ; fade in
            value * (0.0 + loopCount / loops)
         Case %10 ; fade out
            value * (1.0 - loopCount / loops)
         Case %11 ; fade in & fade out
            value << 2 * (1.0 - loopCount / loops) * (loopCount / loops)
      EndSelect
      
      If By = 1 ; 8 bits/sample
         PokeA(*n, value+$80) ; left/mono
         If Nc = 2 ; stereo
            PokeA(*n+1, value+$80) ; right
         EndIf
      Else ; 16 bits/sample
         PokeW(*n, value) ; left/mono
         If Nc = 2 ; stereo
            PokeW(*n+2,value) ; right
         EndIf
      EndIf
      If doSweep
         frequencyThisInstant = loopCount * frequencyStep + frequency
         angStp = frequencyThisInstant / Fr * PIx2
      EndIf
      ang + angStp
      If ang > PIx2 : ang - PIx2  : EndIf
      *n + Ba ; point to the next audio sample
      If *n > *audBuffEnd : Break : EndIf
      loopCount + 1
   Wend
   
   ProcedureReturn CatchSound(soundNum, *buffer)
   
EndProcedure
Last edited by BasicallyPure on Fri May 13, 2016 11:50 pm, edited 5 times in total.
BasicallyPure
Until you know everything you know nothing, all you have is what you believe.
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: SoundEasy.pbi include file adds CreateSound() command

Post by wilbert »

Code: Select all

; Title:   SoundEasy.pbi
; Author:  BasicallyPure
; Date:    2.22.2013
; Version: 1.0
; OS:      Windows, Linux, and probably Mac
; PB ver.  5.10
You can remove the probably part.
It works fine on Mac both x86 and x64. :)

It would be nice if PureBasic had some sort of Music Macro Language
http://en.wikipedia.org/wiki/Music_Macro_Language
The first computer I programmed on (a MSX system) had a very nice Play command that made it easy to play simple 3-channel tunes.
Windows (x64)
Raspberry Pi OS (Arm64)
User avatar
BasicallyPure
Enthusiast
Enthusiast
Posts: 536
Joined: Thu Mar 24, 2011 12:40 am
Location: Iowa, USA

Re: SoundEasy.pbi include file adds CreateSound() command

Post by BasicallyPure »

wilbert wrote:You can remove the probably part.
Done.

Here is another demo that shows how SetSoundFrequency() changes the pitch and duration of a sound.
SetSoundFrequency() is really changing the playback sample rate so the sound duration changes as well as the pitch.
To hear a sound at the same pitch as it was created you need to use the original sample rate with SetSoundFrequency()
Example: if the original sound sample rate was 44100 (the default for SoundEasy) you would use SetSoundFrequency(44100).

This demo requires PB 5.10 and windows OS.
For older versions of PB rename SetSoundFrequency() to SoundFrequency().

BP.

Code: Select all

; Demo II of SoundEasy.pbi
; Demonstrates the CreateSound() command.
; This demo may ony work with windows because it uses the SetSoundFrequency() command.
; If your version of PureBasic is older than 5.10 you must rename SetSoundFrequency() to SoundFrequency().
; Author: BasicallyPure
; Date:   2.24.2013

IncludeFile "SoundEasy.pbi"

flags.i = #PB_Window_SystemMenu | #PB_Window_ScreenCentered | #PB_Window_MinimizeGadget
If OpenWindow(0, 0, 0, 325, 105, "CreateSound() Demo II", flags) And InitSound()
   check_1.i = CheckBoxGadget(#PB_Any,10,10,100,25,"Play Sound 1")
   check_2.i = CheckBoxGadget(#PB_Any,10,40,100,25,"Play Sound 2")
   check_3.i = CheckBoxGadget(#PB_Any,10,70,100,25,"Play Sound 3")
   track_1.i = TrackBarGadget(#PB_Any,105,10,200,25,10,210) : SetGadgetState(track_1,100)
   track_2.i = TrackBarGadget(#PB_Any,105,40,200,25,10,210) : SetGadgetState(track_2,100)
   track_3.i = TrackBarGadget(#PB_Any,105,70,200,25,10,210) : SetGadgetState(track_3,100)
   
   ; Refer to the comments in the SoundEasy.pbi file for the correct CreateSound() syntax.
   snd_1.i = CreateSound(#PB_Any, 200, 2.5, 45, 1000, #WF_Organ, 0, 1)
   snd_2.i = CreateSound(#PB_Any, 1000) ; a simple sine wave 1000 Hz, 1 second
   snd_3.i = CreateSound(#PB_Any, 800, 0.5, 15, 100, #WF_BuzzSaw, 1, 0)
   
   Repeat
      event = WaitWindowEvent()
      If event = #PB_Event_Gadget
         Select EventGadget()
            Case check_1
               If GetGadgetState(check_1)
                  If IsSound(snd_1) : PlaySound(snd_1,#PB_Sound_Loop) : EndIf
               Else
                  StopSound(snd_1)
               EndIf
            Case check_2
               If GetGadgetState(check_2)
                  If IsSound(snd_2) : PlaySound(snd_2,#PB_Sound_Loop) : EndIf
               Else
                  StopSound(snd_2)
               EndIf
            Case check_3
               If GetGadgetState(check_3)
                  If IsSound(snd_3) : PlaySound(snd_3,#PB_Sound_Loop) : EndIf
               Else
                  StopSound(snd_3)
               EndIf
            Case track_1
               SetSoundFrequency(snd_1, 441 * GetGadgetState(track_1))
            Case track_2
               SetSoundFrequency(snd_2, 441 * GetGadgetState(track_2))
            Case track_3
               SetSoundFrequency(snd_3, 441 * GetGadgetState(track_3))
         EndSelect
      EndIf
   Until event = #PB_Event_CloseWindow
   
EndIf
Last edited by BasicallyPure on Mon Feb 25, 2013 4:19 am, edited 1 time in total.
BasicallyPure
Until you know everything you know nothing, all you have is what you believe.
davido
Addict
Addict
Posts: 1890
Joined: Fri Nov 09, 2012 11:04 pm
Location: Uttoxeter, UK

Re: SoundEasy.pbi include file adds CreateSound() command

Post by davido »

BP:

Thanks for sharing.

Never tried music; always felt a bit daunted.
Thanks to you I'll now find it a whole lot easier. :D
DE AA EB
User avatar
Shield
Addict
Addict
Posts: 1021
Joined: Fri Jan 21, 2011 8:25 am
Location: 'stralia!
Contact:

Re: SoundEasy.pbi include file adds CreateSound() command

Post by Shield »

Nice. :)

Play all three sounds at the same time on high and you got yourself
a really nasty ring tone for an alarm clock. :mrgreen:
Image
Blog: Why Does It Suck? (http://whydoesitsuck.com/)
"You can disagree with me as much as you want, but during this talk, by definition, anybody who disagrees is stupid and ugly."
- Linus Torvalds
Joris
Addict
Addict
Posts: 885
Joined: Fri Oct 16, 2009 10:12 am
Location: BE

Re: SoundEasy.pbi include file adds CreateSound() command

Post by Joris »

Thanks for sharing BasicallyPure.
Works great !
(after disabling the mute on my soundsystem...)
Yeah I know, but keep in mind ... Leonardo da Vinci was also an autodidact.
Fred
Administrator
Administrator
Posts: 16664
Joined: Fri May 17, 2002 4:39 pm
Location: France
Contact:

Re: SoundEasy.pbi include file adds CreateSound() command

Post by Fred »

Cool stuff !
User avatar
eddy
Addict
Addict
Posts: 1479
Joined: Mon May 26, 2003 3:07 pm
Location: Nantes

Re: SoundEasy.pbi include file adds CreateSound() command

Post by eddy »

Hi,
It's really a usefull function.

can you add a CreateMemorySound() function ?
Imagewin10 x64 5.72 | IDE | PB plugin | Tools | Sprite | JSON | visual tool
User avatar
BasicallyPure
Enthusiast
Enthusiast
Posts: 536
Joined: Thu Mar 24, 2011 12:40 am
Location: Iowa, USA

Re: SoundEasy.pbi include file adds CreateSound() command

Post by BasicallyPure »

eddy wrote:can you add a CreateMemorySound() function ?
I don't know. Can you describe what a 'CreateMemorySound' function is?

To everyone, if you have saved this previously, you might want to get the latest version.
I have made a major change in the include file code.
You no longer have to use InitAudioBuffer() in your program before you can use CreateSound().

BP.
BasicallyPure
Until you know everything you know nothing, all you have is what you believe.
Joris
Addict
Addict
Posts: 885
Joined: Fri Oct 16, 2009 10:12 am
Location: BE

Re: SoundEasy.pbi include file adds CreateSound() command

Post by Joris »

Noticed and done.

Thanks again BP, really great stuff.
(It's gonna be of great use here, but not right now...)
Yeah I know, but keep in mind ... Leonardo da Vinci was also an autodidact.
User avatar
einander
Enthusiast
Enthusiast
Posts: 744
Joined: Thu Jun 26, 2003 2:09 am
Location: Spain (Galicia)

Re: SoundEasy.pbi include file adds CreateSound() command

Post by einander »

Hi BP:
Very good job!

Is there a way to change the sound frequency of a created sound?
I'm trying your SoundEasy code to obtain a sound to replace the base sound on this example,http://www.purebasic.fr/english/viewtop ... 12&t=53676, but without success.
Cheers!
User avatar
eddy
Addict
Addict
Posts: 1479
Joined: Mon May 26, 2003 3:07 pm
Location: Nantes

Re: SoundEasy.pbi include file adds CreateSound() command

Post by eddy »

BasicallyPure wrote:
eddy wrote:can you add a CreateMemorySound() function ?
I don't know. Can you describe what a 'CreateMemorySound' function is?
I want to load custom wave data.
For example, you can design wave data in a canvas gadget right after you test your sound.

Code: Select all

Procedure.f CreateDataSound(soundNum, Array waveData.f(2), amplitude = 25)
   
   ; Syntax:
   ; Result = CreateSound(#sound, waveData, [duration.f], [amplitude])
   ;
   ; #sound    { The number to identify the new sound. #PB_Any can be used to auto-generate this number. }
   ; wave data { The wave data. Array of floats [-1,+1] } 
   ; amplitude { the peak waveform magnitude from 0 to 100 (full scale).  Default = 25 }
   
   InitAudioBuffer(1.111) ;<--- default init
   Protected _byteCount=ArraySize(waveData(),2)
   Protected *_buffer = InitAudioBuffer(-1)
   Protected _Nc.w = PeekW(*_buffer + 22)   ; Nc {is number of channels}
   Protected _Fr.l = PeekL(*_buffer + 24)   ; Fr {is samples per second}
   Protected _By.w = PeekW(*_buffer + 34)/8 ; By {is bytes per sample (1 = 8 bits, 2 = 16 bits)}
   Protected duration.f=_byteCount/_Fr
   
   If duration < 0.1 : duration = 0.1 : EndIf
   If duration <> InitAudioBuffer(-2) : InitAudioBuffer(duration) : EndIf
   
   Protected *buffer = InitAudioBuffer(-1)
   
   If *buffer = 0 : ProcedureReturn 0 : EndIf
   
   Protected Nc.w = PeekW(*buffer + 22)   ; Nc {is number of channels}
   Protected Fr.l = PeekL(*buffer + 24)   ; Fr {is samples per second}
   Protected By.w = PeekW(*buffer + 34)/8 ; By {is bytes per sample (1 = 8 bits, 2 = 16 bits)}
   Protected Ba = Nc * By ; block align (bytes per block)
   
   
   If amplitude > 100 : amplitude = 100
   ElseIf amplitude < 0 : amplitude = 0
   EndIf
   
   Protected *audioStart = *buffer + 44 ; pointer to the first audio data byte
   Protected *n = *audioStart
   Protected audBuffSize = MemorySize(*buffer) ; number of bytes in sound buffer
   Protected *audBuffEnd = *buffer + audBuffSize - 1 ; pointer to last byte in buffer
   
   ; calculate the number of audio bytes
   Protected byteCount.l = duration * Fr * By * Nc
   If byteCount & 1 : byteCount + 1 : EndIf ; byteCount must be even number
   PokeL(*buffer + 40, byteCount)
   
   Protected.w value ; waveform magnitude at any instant in time
   Protected.i loops = byteCount / Ba ; number of loop iterations
   Protected.i loopCount = 0 ; loop counter
   
   ; if waveform formulas produce values from -1 to +1 use this scale factor
   Protected sf.f = (Pow(2,8*By)/2 - 1) * (amplitude / 100)
   
   
   While loopCount < loops
      
      If By = 1 ; 8 bits/sample
         value=waveData(0,loopCount)*sf
         PokeA(*n, value+$80) ; left/mono
         If Nc = 2 ; stereo
            value=waveData(1,loopCount)*sf
            PokeA(*n+1, value+$80) ; right
         EndIf
      Else ; 16 bits/sample
         value=waveData(0,loopCount)*sf
         PokeW(*n, value) ; left/mono
         If Nc = 2 ; stereo
            value=waveData(1,loopCount)*sf
            PokeW(*n+2,value) ; right
         EndIf
      EndIf
      
      *n + Ba ; point to the next audio sample
      If *n > *audBuffEnd : Break : EndIf
      loopCount + 1
   Wend
   
   ProcedureReturn CatchSound(soundNum, *buffer)
   
EndProcedure

Code: Select all

DisableExplicit
flags.i = #PB_Window_SystemMenu | #PB_Window_ScreenCentered
If OpenWindow(0, 0, 0, 225, 105, "CreateSound() Demo", flags) And InitSound()
   playButton_3.i = ButtonGadget(#PB_Any,10,70,100,25,"Play Sound 3")
   
   ; generate my custom wave data
   Dim waveData.f(0,22050)
   fi=4000
   For i=0 To ArraySize(waveData(),2)
      fi-10
      a.f=#PI*2.0*fi / 44100.0
      b.f=#PI*2.0*2300.0 / 44100.0      
      waveData.f(0,i)=0.5*Sin(i*a)+0.5*Cos(i*b)
   Next 
   snd_3.i = CreateDataSound(#PB_Any, waveData())
   
   Repeat
      event = WaitWindowEvent()
      If event = #PB_Event_Gadget
         Select EventGadget()
            Case playButton_3
               If IsSound(snd_3) : PlaySound(snd_3) : EndIf
         EndSelect
      EndIf
   Until event = #PB_Event_CloseWindow
   
EndIf
Imagewin10 x64 5.72 | IDE | PB plugin | Tools | Sprite | JSON | visual tool
jack
Addict
Addict
Posts: 1336
Joined: Fri Apr 25, 2003 11:10 pm

Re: SoundEasy.pbi include file adds CreateSound() command

Post by jack »

I get an error when trying demo 2, SetSoundFrequency() is not a function, array, list, map or macro.
User avatar
eddy
Addict
Addict
Posts: 1479
Joined: Mon May 26, 2003 3:07 pm
Location: Nantes

Re: SoundEasy.pbi include file adds CreateSound() command

Post by eddy »

jack wrote:I get an error when trying demo 2, SetSoundFrequency() is not a function, array, list, map or macro.
It's a recent change:
History 5.10
Changed: renamed SoundFrequency() to SetSoundFrequency()
Imagewin10 x64 5.72 | IDE | PB plugin | Tools | Sprite | JSON | visual tool
jack
Addict
Addict
Posts: 1336
Joined: Fri Apr 25, 2003 11:10 pm

Re: SoundEasy.pbi include file adds CreateSound() command

Post by jack »

OK thanks :)
Post Reply