While the PulseAudio module I posted yesterday (http://www.purebasic.fr/english/viewtop ... 90#p491890) seems to work fine even for an hour, it does not work if I use it in a thread. I run it on Kubuntu 14.04 (64 bit) and compile it using PB 5.24 in 64 bit.
Here is a small example of a program I wrote. I call the module functions all in the thread VUThread() but after a while (between 2 and 40 seconds), the program stops at a random position in the source with Program aborted. (by external library).
The console window in the background is saying
*** Error in `/tmp/purebasic_compilation0.out': double free or corruption (fasttop): 0x00007f1ad00008c0 ***
This is my current code (you need the above linked include for PulseAudio as "PulseAudio_module.pbi" in the same folder):
Code: Select all
; AudioCounter
EnableExplicit
CompilerIf #PB_Compiler_Unicode
#XmlEncoding = #PB_UTF8
CompilerElse
#XmlEncoding = #PB_Ascii
CompilerEndIf
#queue_size = 512
#LineColor = $FFAAAAAA; AABBGGRR
; Include PulseAudio module
; http://www.purebasic.fr/english/viewtopic.php?f=27&t=66240&p=491890#p491890
XIncludeFile "PulseAudio_module.pbi"
Structure vu_thread
stop.b
deviceName.s
mutex.i
EndStructure
Structure vu_queue
pL.f
pR.f
rL.f
rR.f
dL.f
dR.f
EndStructure
Global Dim queue.vu_queue(#queue_size)
Procedure fillDevices(gad.i)
Protected x.i, line.s, entry.s, lst.s
ClearGadgetItems(gad.i)
;
Protected proc.i = RunProgram("pactl", "list short sources", "", #PB_Program_Open | #PB_Program_Read)
If proc.i
While ProgramRunning(proc.i)
If AvailableProgramOutput(proc.i)
lst.s + ReadProgramString(proc.i) + Chr(13)
EndIf
Wend
CloseProgram(proc.i)
EndIf
For x.i = 1 To CountString(lst.s, Chr(13))
line.s = StringField(lst.s, x.i, Chr(13))
entry.s = Trim(StringField(line.s, 2, Chr(9)))
AddGadgetItem(gad.i, -1, entry.s)
Next
EndProcedure
Procedure incQueue()
Protected x.i
For x.i = #queue_size - 1 To 0 Step -1
queue(x+1) = queue(x)
Next
EndProcedure
Procedure drawQueue(gad.i)
Protected width.i = GadgetWidth(gad.i)
Protected height.i = GadgetHeight(gad.i)
Protected areaHeight.i = height.i / 3
Protected x.i
Protected y.f
Protected pos.f, st.f
Protected scale.f = 32768 / areaHeight
StartDrawing(CanvasOutput(gad.i))
DrawingMode(#PB_2DDrawing_AlphaBlend)
Box(0, 0, width, height, RGBA(255,255,255,255))
st.f = width / #queue_size
pos.f = 0
For x.i = 0 To #queue_size - 1
; left
y = queue(x)\rL / scale.f
LineXY(width - pos.f, areaHeight - y, width - pos.f, areaHeight, RGBA(0,0,255,255))
; right
y = queue(x)\rR / scale.f
LineXY(width - pos.f, 2 * areaHeight - y, width - pos.f, 2 * areaHeight, RGBA(0,0,255,255))
; dir / side
y = (queue(x)\rR - queue(x)\rL) / scale.f
LineXY(width - pos.f, 2.5 * areaHeight - y, width - pos.f, 2.5 * areaHeight, RGBA(255,0,0,255))
pos.f + st.f
Next
LineXY(0, areaHeight, width, areaHeight, #LineColor)
LineXY(0, 2 * areaHeight, width, 2 * areaHeight, #LineColor)
StopDrawing()
EndProcedure
Procedure.i VUThread(*values.vu_thread)
Protected valu.pulse::vu_result ; define return value
*values\stop = #False ; init
If pulse::initLibrary()
Protected err.i = pulse::initStream("AudioCounter", "record", *values\deviceName)
If err.i <> 0
Debug "initStream() error: " + pulse::errToStr(err.i)
*values\stop = #True ; stop!
Else
; get signals
Repeat
pulse::getVU(valu)
; move queue and add new value
LockMutex(*values\mutex)
incQueue()
queue(0)\pL = valu\peakLeft
queue(0)\pR = valu\peakRight
queue(0)\rL = valu\rmsLeft
queue(0)\rR = valu\rmsRight
queue(0)\dL = valu\dbLeft
queue(0)\dR = valu\dbRight
UnlockMutex(*values\mutex)
Delay(5)
Until *values\stop = #True
pulse::closeStream()
EndIf
EndIf
pulse::shutdown()
EndProcedure
Procedure main()
Protected XML.s, Event.i, evType.i, evGadget.i
Protected vuThreadId.i
XML = "<window id='#PB_Any' name='mainWin' text='AudioCounter' flags='#PB_Window_ScreenCentered | #PB_Window_SystemMenu | #PB_Window_SizeGadget'>" +
" <vbox expand='item:1'>" +
" <hbox expand='item:1'>" +
" <canvas name='draw' width='300' height='120'/>" +
" <vbox expand='item:2'>" +
" <button name='clear' text='clear' />" +
" <empty />" +
" </vbox>" +
" </hbox>" +
" <gridbox columns='2' colexpand='item:2'>" +
" <text text='Device:' name='lblDevice' flags='#PB_Text_Right'/>" +
" <combobox name='device' />" +
" </gridbox>" +
" </vbox>" +
"</window>"
Protected xm.i = CatchXML(#PB_Any, @XML, StringByteLength(XML), 0, #XmlEncoding)
If XMLStatus(xm.i) <> #PB_XML_Success
Debug "XML error: " + XMLError(xm) + " (Line: " + XMLErrorLine(xm) + ")"
ProcedureReturn 0
EndIf
Protected dialog.i = CreateDialog(#PB_Any)
If OpenXMLDialog(dialog.i, xm.i, "mainWin") = 0
Debug "Dialog error: " + DialogError(dialog.i)
ProcedureReturn 0
EndIf
; fill device list
fillDevices(DialogGadget(dialog.i, "device"))
SetGadgetState(DialogGadget(dialog.i, "device"), 1) ; TODO: Use ini file to set and get the ID
; start VU thread
Static p.vu_thread
p\deviceName = GetGadgetText(DialogGadget(dialog.i, "device"))
p\mutex = CreateMutex()
vuThreadId.i = CreateThread(@VUThread(), p)
; main GUI loop
Repeat
Event = WaitWindowEvent(10)
If Event = #PB_Event_Gadget
evType.i = EventType()
evGadget.i = EventGadget()
If evGadget = DialogGadget(dialog.i, "device") And evType.i = #PB_EventType_Change
; Stop running thread
p\stop = #True
WaitThread(vuThreadId.i, 2000)
If IsThread(vuThreadId.i): KillThread(vuThreadId.i): EndIf ; terminate if not auto-closing in 2 sec
; Start thread with new chosen device
p\deviceName = GetGadgetText(DialogGadget(dialog.i, "device"))
vuThreadId.i = CreateThread(@VUThread(), p)
EndIf
If evGadget = DialogGadget(dialog.i, "clear") And evType.i = #PB_EventType_LeftClick
Debug "Clear"
EndIf
EndIf
If Event = #PB_Event_SizeWindow
EndIf
LockMutex(p\mutex)
drawQueue(DialogGadget(dialog.i, "draw"))
UnlockMutex(p\mutex)
Until Event = #PB_Event_CloseWindow
; Shutdown and cleanup
p\stop = #True
WaitThread(vuThreadId.i, 1000)
EndProcedure
main()