Hello,
I want to play around with audiomanipulation and realtime creation of sounds.
But I have a serious problem: How to play an endless, realtime generated sound with PureBasic nativ commands?
Is it possible to get the memorypointer to the waveform of a purebasic sound?
CatchSound() and LoadSound() returns some values which looks like pointer... maybe pointers to an internal PureBasic structure where I can find the address of the corresponding waveform values?
Does anybody know somoething about this?
I want to realize a double buffer playback of a realtime calculated soundstream, but without dealing with directX and Windows API. Precalculate the waveform to a memoryblock and catching it afterwards is not an option for me. I want to realize this with only two small soundbuffers - or with one memory block of an catched Purebasic sound which will be filled periodically in realtime after one half of the buffer is completely played.
To determine the 'playcursor-position' in a currently played waveform would be also helpful for this job. But for this task PureBasic provides GetSoundPosition(). This should do the job.
I did not understand too much of the PureBasic internas, so I have no idea how to handle this problem.
How to get a pointer to the Waveform data of CatchSound()?
How to get a pointer to the Waveform data of CatchSound()?
PB 6.02 x64, OS: Win 7 Pro x64 & Win 11 x64, Desktopscaling: 125%, CPU: I7 6500, RAM: 16 GB, GPU: Intel Graphics HD 520, User age in 2024: 56y
"Happiness is a pet." | "Never run a changing system!"
"Happiness is a pet." | "Never run a changing system!"
Re: How to get a pointer to the Waveform data of CatchSound(
Have you looked at the following examples (may contain some of what you're looking for):
- quick Google search revealed these and a bunch more
http://www.forums.purebasic.com/english ... 6&start=15
http://www.purebasic.fr/french/viewtopic.php?f=6&t=9891
http://www.purebasic.fr/english/viewtop ... 12&t=54421
- quick Google search revealed these and a bunch more
http://www.forums.purebasic.com/english ... 6&start=15
http://www.purebasic.fr/french/viewtopic.php?f=6&t=9891
http://www.purebasic.fr/english/viewtop ... 12&t=54421
If you're not investing in yourself, you're falling behind.
My PureBasic Stuff ➤ FREE STUFF, Scripts & Programs.
My PureBasic Forum ➤ Questions, Requests & Comments.
- BasicallyPure
- Enthusiast
- Posts: 539
- Joined: Thu Mar 24, 2011 12:40 am
- Location: Iowa, USA
Re: How to get a pointer to the Waveform data of CatchSound(
I don't know if we can do this native until we get some help from the PB team.
This is something close what you are asking for but with API calls.
I pieced this together a while back using code I found here on the forum.
Maybe something here is useful to you.
BP
This is something close what you are asking for but with API calls.
I pieced this together a while back using code I found here on the forum.
Maybe something here is useful to you.
BP
Code: Select all
; SoundOutContinuousWrite.pb
; by BasicallyPure, 11/18/2013
; windows only
; PB 5.20 LTS
EnableExplicit
;{ Constants
#MainWin = 0
#OutDevice_1 = 0
#PIx2 = 2*#PI
#Mono = 1
#Stereo = 2
#ScopeBkgndColor = $276724
#TraceLeftColor = $27CF24
#TraceRightColor = $27E4D3
#ScopeWidth = 512
#ScopeHeight = 256
#SpinLostFocus = 512
#Input_Finished = #PB_EventType_FirstCustomValue
; gadgets
#BtnHop = 0
#BtnPause = 1
#Canvas_1 = 2
#SpinLeft = 3
#SpinRight = 4
#TrackLeft = 5
#TrackRight = 6
; menus
#Menu_1 = 0
;}
;{ Procedure declarations
Declare WinCallback(hwnd, uMsg, wParam, lParam)
Declare StartSoundOutput()
Declare StopSoundOutput()
Declare CalcWave(*SBuf)
Declare Init_GUI()
Declare EventLoop()
Declare ProcessSpin(nGad,*Freq)
;}
;{ Global variables
Global SampleClock = 44100 ; Sampling frequency in 'samples per second'
Global BlockSize = 8192 ; Number of samples in block
Global BytesPerSample = 2 ; Number of bytes needed for each sample, don't change this
Global Channels = #Stereo ; Number of channels, 1 for mono, 2 for stereo.
Global nBuf = 8 ; Number of buffers
Global DevOut = 1 ; default audio output device
Global Frequency_Left = 1000
Global Frequency_Right = 440
Global Volume_Left.f = 0.25
Global Volume_Right.f = 0.25
Global hWaveOut
Global Hop = #False
Global Pause = #False
Global NumOutDevs
Global PlayFormat.WAVEFORMATEX
Global MyOutDevs.WAVEOUTCAPS
Global Dim outHdr.WAVEHDR(nBuf)
;}
If Init_GUI()
EventLoop()
StopSoundOutput()
EndIf
End
Procedure Init_GUI()
Protected n, result = 1
If OpenWindow(#MainWin,0,0,#ScopeWidth+20,#ScopeHeight+90,"Continuous sound out write test",#PB_Window_SystemMenu |#PB_Window_ScreenCentered)
ButtonGadget(#BtnHop,10,10,50,25,"Hop",#PB_Button_Toggle)
ButtonGadget(#BtnPause,70,10,50,25,"Pause",#PB_Button_Toggle)
SpinGadget(#SpinLeft,130,02,70,25,100,5000)
SetGadgetState(#SpinLeft,Frequency_Left) : SetGadgetText(#SpinLeft,Str(Frequency_Left))
SetGadgetColor(#SpinLeft,#PB_Gadget_BackColor,#TraceLeftColor)
SpinGadget(#SpinRight,235,02,70,25,100,5000)
SetGadgetState(#SpinRight,Frequency_Right) : SetGadgetText(#SpinRight,Str(Frequency_Right))
SetGadgetColor(#SpinRight,#PB_Gadget_BackColor,#TraceRightColor)
TrackBarGadget(#TrackLeft,125,30,100,25,0,100) : SetGadgetState(#TrackLeft,Volume_Left * 100)
TrackBarGadget(#TrackRight,230,30,100,25,0,100) : SetGadgetState(#TrackRight,Volume_Right * 100)
CanvasGadget(#Canvas_1,8,58,#ScopeWidth+4,#ScopeHeight+4,#PB_Canvas_Border)
StartDrawing(CanvasOutput(#Canvas_1))
Box(0,0,#ScopeWidth,#ScopeHeight,#ScopeBkgndColor)
StopDrawing()
CreateMenu(#Menu_1, WindowID(#MainWin))
; locate all sound output devices
OpenSubMenu("Sound Output Devices")
NumOutDevs = waveOutGetNumDevs_()
If NumOutDevs <> 0
For n = 0 To NumOutDevs - 1
If waveOutGetDevCaps_(n,@MyOutDevs,SizeOf(WAVEOUTCAPS)) = 0
MenuItem(n + #OutDevice_1,PeekS(@MyOutDevs\szPname))
EndIf
Next
CloseSubMenu()
SetMenuItemState(#Menu_1,#OutDevice_1,#True)
SetWindowCallback(@WinCallback()) ; Handle Sound Output callback
StartSoundOutput()
Else
MessageRequester("Error!","No audio output device found.")
result = 0
EndIf
Else
result = 0
EndIf
ProcedureReturn result
EndProcedure
Procedure EventLoop()
Protected menuSelection, ActiveGadget, n, Quit = #False
Repeat
Select WaitWindowEvent()
Case #PB_Event_CloseWindow
Quit = #True
Case #PB_Event_Gadget
Select EventGadget()
Case #BtnHop
Hop = GetGadgetState(#BtnHop)
Frequency_Left = GetGadgetState(#SpinLeft)
Frequency_Right = GetGadgetState(#SpinRight)
Case #BtnPause
Pause = GetGadgetState(#BtnPause)
If Pause
waveOutPause_(hWaveOut)
Else
waveOutRestart_(hWaveOut)
EndIf
Case #SpinLeft
ProcessSpin(#SpinLeft, @Frequency_Left)
Case #SpinRight
ProcessSpin(#SpinRight, @Frequency_Right)
Case #TrackLeft
Volume_Left = GetGadgetState(#TrackLeft) / 100
Case #TrackRight
Volume_Right = GetGadgetState(#TrackRight) / 100
EndSelect
Case #PB_Event_Menu
menuSelection = EventMenu()
If GetMenuItemState(#Menu_1,menuSelection) = #False
Select menuSelection
Case #OutDevice_1 To #OutDevice_1 + NumOutDevs - 1 ; Output device selection
For n = #OutDevice_1 To #OutDevice_1 + NumOutDevs - 1
If n = menuSelection
SetMenuItemState(#Menu_1,menuSelection,#True)
Else
SetMenuItemState(#Menu_1,n,#False)
EndIf
Next
DevOut = menuSelection - #OutDevice_1 + 1
StopSoundOutput()
StartSoundOutput()
EndSelect
EndIf
Case #WM_KEYUP
ActiveGadget = GetActiveGadget()
Select ActiveGadget
Case #SpinLeft To #SpinRight
If EventwParam() = #VK_RETURN
PostEvent(#PB_Event_Gadget,#MainWin,ActiveGadget,#Input_Finished)
EndIf
EndSelect
EndSelect
Until Quit = #True
EndProcedure
Procedure WinCallback(hwnd, uMsg, wParam, lParam)
; Window callback to service sound output message
Static *hWaveO.WAVEHDR
Select uMsg
Case #MM_WOM_DONE ; Sound output, a play buffer has been returned.
*hWaveO.WAVEHDR = lParam ; lParam has the address of WAVEHDR
CalcWave(*hWaveO\lpData) ; send pointer where to write NEW data
*hWaveO\dwBytesRecorded = BlockSize ; Number of bytes written into buffer
waveOutWrite_(hWaveOut,lParam, SizeOf(WAVEHDR)) ; Send to sound device => jack socket => cable =>
EndSelect
ProcedureReturn #PB_ProcessPureBasicEvents
EndProcedure
Procedure StartSoundOutput()
Protected T,i, *P
Static *OutBufMem
With PlayFormat
\wFormatTag = #WAVE_FORMAT_PCM
\nChannels = Channels
\wBitsPerSample = BytesPerSample * 8
\nSamplesPerSec = SampleClock
\nBlockAlign = Channels * BytesPerSample
\nAvgBytesPerSec = \nSamplesPerSec * \nBlockAlign
EndWith
If *OutBufMem : FreeMemory(*OutBufMem) : EndIf ; Free a prior assignement
*OutBufMem = AllocateMemory(BlockSize * nBuf) ; Reserve memory for all the buffers
T = waveOutOpen_(@hWaveOut, #WAVE_MAPPER+DevOut, @PlayFormat, WindowID(#MainWin), #True, #CALLBACK_WINDOW | #WAVE_FORMAT_DIRECT)
If T = #MMSYSERR_NOERROR
*P = *OutBufMem ; Pointer to start of memory
For i = 0 To nBuf-1 ; For each buffer
outHdr(i)\lpData = *P ; start of buffer
outHdr(i)\dwBufferLength = BlockSize ; size of buffer
outHdr(i)\dwFlags = 0
outHdr(i)\dwLoops = 0
T | waveOutPrepareHeader_(hWaveOut, outHdr(i), SizeOf(WAVEHDR))
*P + BlockSize
Next
For i = 0 To nBuf-1
PostMessage_(WindowID(#MainWin),#MM_WOM_DONE,0,outHdr(i))
Next
EndIf
If T = #MMSYSERR_NOERROR : ProcedureReturn 1 : Else : ProcedureReturn 0 : EndIf
EndProcedure
Procedure StopSoundOutput()
Protected i
waveOutReset_(hWaveOut)
For i = 0 To nBuf - 1
waveOutUnprepareHeader_(hWaveOut, outHdr(i), SizeOf(WAVEHDR))
Next
waveOutClose_(hWaveOut)
EndProcedure
Procedure CalcWave(*SBuf)
; This routine generates Left and Right waveforms.
Static.d Angle, Kl, Kr, La, Ra
Static.i sample, Sgn, scope_X, Trig
Static.l Vl, Vr, Ls = 1, Rs
Static HSH = #ScopeHeight/2 ; one half of the scope height
Static SW = #ScopeWidth -1
Static scaler = $10000 / #ScopeHeight
Static Dim eraser(#ScopeWidth-1,1)
Protected Trace_L = 1
If Hop
Frequency_Left = Random(1220,220)
Frequency_Right = Random(1220,220)
EndIf
; Calculate the frequency scaling factors
Kl = #PIx2 * Frequency_Left / SampleClock
Kr = #PIx2 * Frequency_Right / SampleClock
StartDrawing(CanvasOutput(#Canvas_1))
LineXY(0,HSH,SW,HSH,0)
sample = 1
Repeat ; Generate waveform data
; Left sample
Vl = Sin(La) * 32767 * Volume_Left
La + Kl ; Calculate angle for next time
If La > #PIx2 : La - #PIx2 : EndIf ; limit to 2*PI radians
PokeW(*SBuf,Vl) ; Put point in buffer
*SBuf + BytesPerSample ; move buffer pointer to next sample
; Right sample
If Channels = #Stereo
Vr = Sin(Ra) * 32767 * Volume_Right
Ra + Kr
If Ra > #PIx2 : Ra - #PIx2 : EndIf
PokeW(*SBuf,Vr)
*SBuf + BytesPerSample
EndIf
; draw the scope display
; trace drawing will alternate left then right so each can be triggered
If Trig ; trigger is active so continue drawing left or right trace
Vl/scaler : Vr/scaler
If Trace_L ; draw the left trace
Plot(scope_X,HSH + eraser(scope_X,0),#ScopeBkgndColor) ; erase old
eraser(scope_X,0) = Vl
Plot(scope_X,HSH + Vl,#TraceLeftColor)
ElseIf Channels = #Stereo ; draw the right trace
Plot(scope_X,HSH + eraser(scope_X,1),#ScopeBkgndColor) ; erase old
eraser(scope_X,1) = Vr
Plot(scope_X,HSH + Vr,#TraceRightColor)
EndIf
scope_X + 1 ; move 1 pixel right
If scope_X > SW ; trace has reached the right edge
scope_X = 0 ; move back to left edge
Trig = #False ; reset the trigger
; capture the sign of the next trace sample
Ls = Sign(Vl) : If Ls = 0 : Ls + 1 : EndIf
If channels = #Stereo
Rs = Sign(Vr) : If Rs = 0 : Rs + 1 : EndIf
Trace_L ! 1
EndIf
EndIf
Else ; wait for zero crossing to enable trigger
If Trace_L And Vl <> 0
If Ls <> Sign(Vl) ; the sign has changed (zero crossing)
If Ls < 0 : Trig = #True : Else : Ls = -Ls : EndIf
EndIf
ElseIf Channels = #Stereo
If Rs <> Sign(Vr) ; the sign has changed (zero crossing)
If Rs < 0 : Trig = #True : Else : Rs = -Rs : EndIf
EndIf
EndIf
EndIf
sample + PlayFormat\nBlockAlign
Until sample > BlockSize
StopDrawing()
EndProcedure
Procedure ProcessSpin(nGad,*Freq)
Select EventType()
Case #PB_EventType_Up, #PB_EventType_Down
SetGadgetText(nGad, Str(GetGadgetState(nGad)))
PokeI(*Freq, GetGadgetState(nGad))
Case #Input_Finished, #SpinLostFocus
SetGadgetState(nGad,Val(GetGadgetText(nGad)))
SetGadgetText(nGad, Str(GetGadgetState(nGad)))
PokeI(*Freq, GetGadgetState(nGad))
EndSelect
EndProcedure
BasicallyPure
Until you know everything you know nothing, all you have is what you believe.
Until you know everything you know nothing, all you have is what you believe.
Re: How to get a pointer to the Waveform data of CatchSound(
Hello JHPJHP,
yes I've used the forum search and looked for this topic, but I found only API-based examples - or I missed the important little thing that hides in the last two links which you provided in your post.
This two posts are hitting the nail on the head.
It seems that IsSound () is the command which returns the address of a sound structure. I have always thought that CatchSound () or LoadSound () returns this pointer.
Unfortunately I am currently not at my own computer (no PB here), but what I see in the examples looks exactly like what I need.
Thank you very much JHPJHP!
And also to you, BasicallyPure, a big thank you for your sample code. Even if it is based entirely on API, but I will surely use the routines in the future. Thank you for your effort!
yes I've used the forum search and looked for this topic, but I found only API-based examples - or I missed the important little thing that hides in the last two links which you provided in your post.

It seems that IsSound () is the command which returns the address of a sound structure. I have always thought that CatchSound () or LoadSound () returns this pointer.
Unfortunately I am currently not at my own computer (no PB here), but what I see in the examples looks exactly like what I need.
Thank you very much JHPJHP!
And also to you, BasicallyPure, a big thank you for your sample code. Even if it is based entirely on API, but I will surely use the routines in the future. Thank you for your effort!
PB 6.02 x64, OS: Win 7 Pro x64 & Win 11 x64, Desktopscaling: 125%, CPU: I7 6500, RAM: 16 GB, GPU: Intel Graphics HD 520, User age in 2024: 56y
"Happiness is a pet." | "Never run a changing system!"
"Happiness is a pet." | "Never run a changing system!"