Oscilloscope

Applications, Games, Tools, User libs and useful stuff coded in PureBasic
User avatar
BasicallyPure
Enthusiast
Enthusiast
Posts: 536
Joined: Thu Mar 24, 2011 12:40 am
Location: Iowa, USA

Oscilloscope

Post by BasicallyPure »

Please report any problems.
Are you an experienced oscilloscope user?
I am especially interested in what other features you might think it needs.

Here is a link to a compiled .exe if you do not wish to make your own.
This is for windows and compiled as a 32 bit application.
Oscilloscope_0.83.exe

11/22/2013 edit: bug fix in 'GET_CAPTURE_DEVICES()' procedure.

Image

Image

Image

Here is the code if you are interested.
There is an include file that you will find at this topic.
http://www.purebasic.fr/english/viewtop ... 12&t=51078

Code: Select all

; Name     : Oscilloscope.pb
; Author   : BasicallyPure
; Platform : Windows only
; Compiler : PureBasic 5.20 LTS
; Date     : 11.22.2013
; License  : Free
;
; Forum topic : http://www.purebasic.fr/english/viewtopic.php?f=27&t=57462
EnableExplicit

; version
#ver = "0.83"

; Get the include file here.
; Forum Topic: http://www.purebasic.fr/english/viewtopic.php?f=12&t=51078
IncludeFile "ToggleButtonGadget.pbi"

#PopUp = 0 ; popup menu

; menu items
#MenuItem_Always_On_Top = 0
#MenuItem_Minimize      = 1
#MenuItem_Exit          = 2

;{ gadgets
Enumeration
   #SampleRate   ; combo gadget
   #InputDevice  ; combo gadget, list input devices
   #Aquire       ; container for input choices
   #Graph        ; container for sweep buttons
   #Trace        ; container gadget for trace controls
   #Trigger      ; container gadget for trigger controls
   #Pos_L        ; Track Bar to position trace L
   #Pos_R        ; Track Bar to position trace R
   #Pos_M        ; Track Bar to position math trace
   #Gain_L       ; Track Bar to set chan. L gain
   #Gain_R       ; Track Bar to set chan. R gain
   #Trig_Lvl_L   ; Track Bar to set chan. L trig. level
   #Trig_Lvl_R   ; Track Bar to set chan. R trig. level
   #Scope_Stats  ; editor gadget to display settings
   #Pause        ; Button for pause
   #Trace_L      ; Button to toggle trace L
   #Trace_R      ; Button to toggle trace R
   #Invert_L     ; Button to invert trace L
   #Invert_R     ; Button to invert trace R
   #Math         ; Button to activate L + R math trace
   #Graph_XY     ; Button to set XY mode
   #Graph_Polar  ; Button to set Polar mode
   #Graph_Time   ; Button to set normal sweep
   #LineType     ; Button to toggle line type
   #Trig_Slope   ; Button to select trigger slope
   ;
   #Trig_Auto    ; not a gadget, trigger from 'L' or 'R'
   #Trig_Free    ; not a gadget, used for free run mode
   #Source_None  ; not a gadget, used when no trig source selected
   ;
   ; trigger buttons, order must be preserved
   #Source_L     ; Button to select trigger source 'L'
   #Source_R     ; Button to select trigger source 'R'
   #Trig_Single  ; Button for Trigger menu
   #Trig_Arm     ; Button to arm single sweep
EndEnumeration : ;}

; images
#Grat_Norm  = 0 ; graticule for normal sweep mode
#Grat_Polar = 1 ; graticule for XY sweep mode

; sprites
#Screen_bkgnd = 0 ; background graticule for scope screen
#Time_Per_Div = 1 ; display horizontal time/div readout
#TrigLine_L   = 2 ; line to show trigger level for trace 'L'
#TrigLine_R   = 3 ; line to show trigger level for trace 'R'

;{ misc. constants
#WAVE_FORMAT_48S16 = $00008000 ; missing windows constant
#WAVE_FORMAT_96S16 = $00080000 ; missing windows constant
#TrigViewTime   = 25      ; time duration to display trig lines
#WinColor       = $6D6C48 ; window background
#Scope_Color    = $004720 ; scope background
#Readout_Color  = $00C080 ; text color on scope screen
#CB_Color       = $81A280 ; background color for containers
#Trace_L_Color  = $27E4D3 ; color for trace 'L'
#Trace_R_Color  = $ECDF45 ; color for trace 'R'
#Math_Color     = $6500F3 ; color for trace 'L + R'
#Trace_XY_Color = $AEFF0D ; color for cartesian and polar plots
#Positive       = 0       ; used for trigger slope
#Negative       = 1       ; used for trigger slope
#MainWin = 0
#MONO    = 1
#STEREO  = 2
#ScopeW  = 441 ; this gives 1mS/div. for 44100 samples per sec.
#ScopeH  = 353 ; this is #ScopeW * 0.8
#Dots    = 0
#Lines   = 1 : ;}

Structure CONFIG :;{
   wave.i         ; handle of wave device from WaveInOpen_
   BufLen.i       ; Buffer length in number of samples
   lBuf.i         ; Capturing Buffer length in bytes
   nBuf.i         ; Number of capturing Buffers
   nDev.i         ; Capturing Device identifier
   frameRate.i    ; Scope refresh rate
   tr_L_offset.i  ; offset for trace L position
   tr_R_offset.i  ; offset For trace R position
   tr_M_offset.i  ; offset for math trace position
   gain_L.f       ; gain for trace L
   gain_R.f       ; gain for trace R
   lineType.i     ; draw dots or lines for scope trace
   trace_L.i      ; trace A active, 1 = True
   trace_R.i      ; trace B active, 1 = True
   invert_L.i     ; flag to invert trace L
   invert_R.i     ; flag to invert trace R
   math.i         ; flag to enable L + R trace
   Display_Mode.i ; sets scope graph mode
   Trig_Lvl_L.i   ; channel 'L' trigger level
   Trig_Lvl_R.i   ; channel 'R' trigger level
   Trig_Mode.i    ; sets sweep trigger mode
   Trig_Source.i  ; signal source for trigger
   Trig_Armed.i   ; flag to ready single sweep trigger
   Trig_Slope.i   ; indicates trigger slope
EndStructure :    ;}

Global Config.CONFIG
   Config\nBuf = 4
   Config\tr_L_offset = -#ScopeH/8 * 1.5 ; offset 1.5 divisions
   Config\tr_R_offset =  #ScopeH/8 * 1.5
   Config\gain_L = 1
   Config\gain_R = 1
   Config\frameRate = 15
   Config\lineType = #Lines
   Config\Display_Mode = #Graph_Time
   Config\trace_L = #True
   Config\trace_R = #True
   Config\Trig_Mode = #Trig_Free
   Config\Trig_Source = #Source_None
   Config\Trig_Slope  = #Positive
   
Global Format.WAVEFORMATEX ; Capturing format
   Format\wFormatTag = #WAVE_FORMAT_PCM
   Format\nChannels  = #STEREO
   Format\wBitsPerSample = 16
   Format\cbSize = 0

Global Dim inHdr.WAVEHDR(Config\nBuf - 1)
Global captureActive ; flag
Global wpa, lpa, hWindow, trigView

Procedure SCOPE()
   Static *hWave.WAVEHDR, *idx, *lim
   Static x, y, n, my, lyl, lyr, lmy, lx, ly
   Static rds, ang, scale_L, scale_R
   Static xLim  = #ScopeW - 1, yLim  = #ScopeH - 1
   Static mid_Y = #ScopeH / 2, mid_X = #scopeW / 2
   Static scale = $FFFF / -#ScopeH
   
   ; use to prevent plotting beyond screen limits
   Macro HardLimit(v, lim)
      If v > lim : v = lim : EndIf
      If v < 0   : v = 0   : EndIf
   EndMacro
   
   Macro ScanForTrigger(level)
      If Config\Trig_Slope = #Positive
         While PeekW(*idx) > level And *idx < *lim : *idx + 4 : Wend
         While PeekW(*idx) < level And *idx < *lim : *idx + 4 : Wend
      Else ; negative slope
         While PeekW(*idx) < level And *idx < *lim : *idx + 4 : Wend
         While PeekW(*idx) > level And *idx < *lim : *idx + 4 : Wend
      EndIf
   EndMacro
   
   *hWave = lpa ; fill structure using lParam from Callback proc.
   waveInAddBuffer_(wpa, *hWave, SizeOf(WAVEHDR)) ; wpa = wParam from Callback proc.
   
   *idx = *hWave\lpData ; set index to first audio sample
   
   If *idx <> 0
      
      If Config\invert_L : scale_L = -scale : Else : scale_L = scale : EndIf
      If Config\invert_R : scale_R = -scale : Else : scale_R = scale : EndIf
      scale_L * Config\gain_L
      scale_R * Config\gain_R
      
      DisplaySprite(#Screen_bkgnd, 0, 0) ; draw this first on every frame
      
         If Config\Display_Mode = #Graph_Time ; normal plot
            
            ; if required display trigger level lines
            If trigView : trigView - 1
               If GetActiveGadget() = #Trig_Lvl_L
                  y = #ScopeH + Config\tr_L_offset - GetGadgetState(#Trig_Lvl_L)
                  HardLimit(y, yLim)
                  DisplayTransparentSprite(#TrigLine_L,0,y)
               ElseIf GetActiveGadget() = #Trig_Lvl_R
                  y = #ScopeH + Config\tr_R_offset - GetGadgetState(#Trig_Lvl_R)
                  HardLimit(y, yLim)
                  DisplayTransparentSprite(#TrigLine_R,0,y)
               ElseIf Config\Trig_Mode <> #Trig_Free
                  If GetButtonState(#Source_L) = #True
                     y = mid_Y + Config\tr_L_offset - GetGadgetState(#Trig_Lvl_L) + #ScopeH/2
                     HardLimit(y, yLim)
                     DisplayTransparentSprite(#TrigLine_L,0,y)
                  EndIf
                  If GetButtonState(#Source_R) = #True
                     y = mid_y + Config\tr_R_offset - GetGadgetState(#Trig_Lvl_R) + #ScopeH/2
                     HardLimit(y, yLim)
                     DisplayTransparentSprite(#TrigLine_R,0,y)
                  EndIf
               EndIf
            EndIf
            
            ;- begin trigger code
            *lim = *idx + *hWave\dwBufferLength - xLim << 2
            
            Select Config\Trig_Mode
               Case #Trig_Free
                  ;
               Case #Trig_Auto
                  If Config\Trig_Source = #Source_L
                     ScanForTrigger(Config\Trig_Lvl_L)
                  ElseIf Config\Trig_Source = #Source_R
                     *idx + 2 : ScanForTrigger(Config\Trig_Lvl_R) : *idx - 2
                  EndIf
               Case #Trig_Single
                  If trigView
                     ; take no action
                  ElseIf Config\Trig_Armed = #False
                     ProcedureReturn
                  EndIf
                  
                  Select Config\Trig_Source
                     Case #Source_L
                        ScanForTrigger(Config\Trig_Lvl_L)
                     Case #Source_R
                        *idx + 2
                        ScanForTrigger(Config\Trig_Lvl_R)
                        *idx - 2
                     Case #Source_None
                        ProcedureReturn
                  EndSelect
                  
                  If trigView
                     ; take no action
                  ElseIf *idx >= *lim
                     ProcedureReturn
                  Else
                     Config\Trig_Armed = #False
                     SetButtonState(#Trig_Arm, #False)
                  EndIf
            EndSelect
            
            If *idx >= *lim ; no trigger point found
               If Config\Trig_Mode = #Trig_Free Or trigView <> 0
                  *idx = *hWave\lpData
               Else
                  ProcedureReturn
               EndIf
            EndIf
            ; end trigger code
            
            DisplayTransparentSprite(#Time_Per_Div,10, #ScopeH - 20)
            StartDrawing(ScreenOutput())
               
            If Config\lineType = #Lines ; needed to draw the first line
               lyl = PeekW(*idx)  /scale_L + mid_Y + Config\tr_L_offset
               lyr = PeekW(*idx+2)/scale_R + mid_Y + Config\tr_R_offset
               lmy = lyl + lyr - mid_Y ; for first line of 'L+R' trace
            EndIf
            
            For x = 0 To xLim
               y = PeekW(*idx)/scale_L + mid_Y
               my = y ; for 'L+R' trace
               If Config\trace_L ; check if trace L is active
                  y + Config\tr_L_offset
                  If Config\lineType = #Dots
                     HardLimit(y, yLim)
                     Plot(x, y, #Trace_L_Color)
                  Else
                     LineXY(x-1, lyl, x, y, #Trace_L_Color) : lyl = y
                  EndIf
               EndIf
               
               *idx + 2
               
               y = PeekW(*idx)/scale_R
               my + y ; for 'L+R' trace
               If Config\trace_R ; if trace R is active draw it
                  y + mid_Y + Config\tr_R_offset
                  If Config\lineType = #Dots
                     HardLimit(y, yLim)
                     Plot(x, y, #Trace_R_Color)
                  Else
                     LineXY(x-1, lyr, x, y, #Trace_R_Color) : lyr = y
                  EndIf
               EndIf
               
               *idx + 2
               
               If Config\math ; if 'L+R' trace is active draw it
                  my + Config\tr_M_offset
                  If Config\lineType = #Dots
                     HardLimit(my, yLim)
                     Plot(x, my, #Math_Color)
                  Else
                     LineXY(x-1, lmy, x, my, #Math_Color) : lmy = my
                  EndIf
               EndIf
            Next x
            
         ElseIf Config\Display_Mode = #Graph_XY ; do XY plot
            StartDrawing(ScreenOutput())
            If Config\lineType = #Lines
               lx = PeekW(*idx)  /scale_L + mid_X
               ly = PeekW(*idx+2)/scale_R + mid_Y
            EndIf
            
            For n = 1 To Config\BufLen
               x = (PeekW(*idx)/ scale_L) + mid_X ; x is left channel
               *idx + 2
               y = (PeekW(*idx)/ scale_R) + mid_Y ; y is right channel
               *idx + 2
               If Config\lineType = #Dots
                  HardLimit(x, xLim) : HardLimit(y, yLim)
                  Plot(x, y, #Trace_XY_Color)
               Else
                  LineXY(lx, ly, x, y, #Trace_XY_Color)
                  lx = x : ly = y
               EndIf
            Next n
            
         Else ; polar plot, left channel = radius, right channel = angle
            StartDrawing(ScreenOutput())
            If Config\lineType = #Lines ; needed to draw the first line
               rds = PeekW(*idx)  / scale_L ; left channel
               ang = PeekW(*idx+2)/ scale_R ; right channel
               lx = Cos(Radian(ang)) * rds + mid_X
               ly = Sin(Radian(ang)) * rds + mid_Y
            EndIf
            
            For n = 1 To Config\BufLen
               rds = (PeekW(*idx)/ scale_L) : *idx + 2
               ang = (PeekW(*idx)/ scale_R) : *idx + 2
               x = Cos(Radian(ang)) * rds + mid_X
               y = Sin(Radian(ang)) * rds + mid_Y
               If Config\lineType = #Dots
                  HardLimit(x, xLim) : HardLimit(y, yLim)
                  Plot(x, y, #Trace_XY_Color)
               Else
                  LineXY(lx, ly, x, y, #Trace_XY_Color)
                  lx = x : ly = y
               EndIf
            Next n
         EndIf
      
      StopDrawing()
      FlipBuffers()
   EndIf
   
EndProcedure

Procedure WIN_CALLBACK(hWnd.l, Msg.l, wParam.l, lParam.l)

   If Msg = #MM_WIM_DATA
      wpa = wParam
      lpa = lParam
      SCOPE()
   EndIf
   
   ProcedureReturn #PB_ProcessPureBasicEvents
EndProcedure

Procedure CREATE_GRATICULES()
   Protected n
   
   CreateImage(#Grat_Norm, #ScopeW, #ScopeH)
   CreateImage(#Grat_Polar, #ScopeW, #ScopeH)
   CreateSprite(#Screen_bkgnd, #ScopeW, #ScopeH)
   CreateSprite(#Time_Per_Div, 175, 15)  ; sprite for time/div readout
   CreateSprite(#TrigLine_L, #ScopeW, 1) ; sprite for trigger line 'L'
   CreateSprite(#TrigLine_R, #ScopeW, 1) ; sprite for trigger line 'R'
   
   ; trig level lines
   StartDrawing(SpriteOutput(#TrigLine_L))
      LineXY(0,0,#ScopeW-1,0,#Trace_L_Color)
   StopDrawing()
   
   StartDrawing(SpriteOutput(#TrigLine_R))
      LineXY(0,0,#ScopeW-1,0,#Trace_R_Color)
   StopDrawing()
   
   ; normal graticule
   StartDrawing(ImageOutput(#Grat_Norm))
      Box(0, 0,  #ScopeW, #ScopeH, #Scope_Color)
      For n = 0 To #ScopeH-1 Step #ScopeH/08 : LineXY(0, n, #ScopeW-1, n, 0) : Next n
      For n = 0 To #ScopeW-1 Step #ScopeW/10 : LineXY(n, 0, n, #ScopeH-1, 0) : Next n
   StopDrawing()
   
   ; Polar graticule
   StartDrawing(ImageOutput(#Grat_Polar))
      Box(0, 0,  #ScopeW, #ScopeH, #Scope_Color)
      DrawingMode(#PB_2DDrawing_Outlined )
      For n = 0 To #ScopeW-1 Step #ScopeW/5
         Circle(#ScopeW/2, #ScopeH/2, n/2, 0)
      Next n
      For n = 0 To 330 Step 30
         LineXY(#ScopeW/2, #ScopeH/2, #ScopeW/2 + Sin(Radian(n)) * #ScopeW/2,
                #ScopeH/2 + Cos(Radian(n)) * #ScopeW/2,0)
      Next n
   StopDrawing()
   
EndProcedure

Procedure GET_CAPTURE_DEVICES()
   Protected DeviceID.i, n.i, items.i, waveFormats.i = $FFFFF
   Protected Caps.WAVEINCAPS
   Protected NumDevices.i = waveInGetNumDevs_()
   
   If NumDevices
      
      For DeviceID = #WAVE_MAPPER To NumDevices - 1
         If waveInGetDevCaps_(DeviceID,@Caps, SizeOf(WAVEINCAPS)) = #MMSYSERR_NOERROR
            AddGadgetItem(#InputDevice, -1, PeekS(@Caps\szPname, #MAXPNAMELEN))
            waveFormats & Caps\dwFormats
         EndIf
      Next
      
      ; assume 8000 sample rate is supported
      AddGadgetItem(#SampleRate, -1, "8000") : SetGadgetState(#SampleRate, 0)
      
      ; identify all available 16 bit stereo options
      If waveFormats & #WAVE_FORMAT_1S16  : AddGadgetItem(#SampleRate, -1, "11025"): EndIf
      If waveFormats & #WAVE_FORMAT_2S16  : AddGadgetItem(#SampleRate, -1, "22050"): EndIf
      If waveFormats & #WAVE_FORMAT_4S16  : AddGadgetItem(#SampleRate, -1, "44100"): EndIf
      If waveFormats & #WAVE_FORMAT_48S16 : AddGadgetItem(#SampleRate, -1, "48000"): EndIf
      If waveFormats & #WAVE_FORMAT_96S16 : AddGadgetItem(#SampleRate, -1, "96000"): EndIf
      
      items = CountGadgetItems(#SampleRate)
      
      If items > 1
         For n = 1 To items - 1
            SetGadgetState(#SampleRate, n)
            If GetGadgetText(#SampleRate) = "22050" ; choose this if available
               n = items ; exit for/next loop
            Else
               SetGadgetState(#SampleRate, 0) ; use 8000 if 22050 is not available
            EndIf
         Next n
      Else
         MessageRequester("", "No suitable wave capture devices found!")
         ProcedureReturn 0
      EndIf
      
   Else
      MessageRequester("", "No audio devices found!")
   EndIf
   
   ProcedureReturn NumDevices
EndProcedure

Procedure INIT_GUI()
   ; returns window handle or 0 if fail
   Protected n, flags = #PB_Window_ScreenCentered | #PB_Window_SystemMenu | #PB_Window_MinimizeGadget
   Protected hWin = OpenWindow(#MainWin, 0, 0, 770, 485, "Oscilloscope v." + #ver, flags)
   
   If hWin And InitSprite() And OpenWindowedScreen(hWin, 10, 10, #ScopeW, #ScopeH)
      SetWindowColor(#MainWin, #WinColor)
      
      CreatePopupMenu(#PopUp)
         MenuItem(#MenuItem_Always_On_Top, "Always On Top")
         MenuItem(#MenuItem_Minimize, "Minimize")
         MenuItem(#MenuItem_Exit, "Exit")
      
      ; trace controls
      ContainerGadget(#Trace, #ScopeW + 20, 10, 160, 180, #PB_Container_Raised)
         SetGadgetColor(#Trace, #PB_Gadget_BackColor, #CB_Color)
         SetGadgetColor(TextGadget(#PB_Any, 5, 5, 110, 15, "Trace Control"), #PB_Gadget_BackColor, #CB_Color)
         ToggleButtonGadget(#Trace_L , 05, 25, 55, 25, "LEFT" , "", 1)
         ToggleButtonGadget(#Trace_R , 05, 55, 55, 25, "RIGHT" , "", 1)
         ToggleButtonGadget(#Math    , 05, 85, 55, 25, " L + R", "")
            SetButtonColor(#Math, #PB_Ignore, #PB_Ignore, #Math_Color, $655DF3)
         ToggleButtonGadget(#Invert_L, 05, 115, 55, 25, "Inv. L", "")
         ToggleButtonGadget(#Invert_R, 05, 145, 55, 25, "Inv. R", "")
         TrackBarGadget(#Pos_L, 65, 25, 25, 145, 0, #ScopeH, #PB_TrackBar_Vertical)
            SetGadgetState(#Pos_L, #ScopeH/2 - Config\tr_L_offset)
            GadgetToolTip(#Pos_L, "LEFT position")
         TrackBarGadget(#Pos_R, 95, 25, 25, 145, 0, #ScopeH, #PB_TrackBar_Vertical)
            SetGadgetState(#Pos_R, #ScopeH/2 - Config\tr_R_offset)
            GadgetToolTip(#Pos_R, "RIGHT position")
         TrackBarGadget(#Pos_M, 125, 25, 25, 145, 0, #ScopeH, #PB_TrackBar_Vertical)
            SetGadgetState(#Pos_M, #ScopeH/2 - Config\tr_M_offset)
            GadgetToolTip(#Pos_M, "'L+R' position")
            DisableGadget(#Pos_M, #True)
      CloseGadgetList()
         
      ; trigger controls
      ContainerGadget(#Trigger, #ScopeW + 185, 10, 135, 180, #PB_Container_Raised)
         SetGadgetColor(#Trigger, #PB_Gadget_BackColor, #CB_Color)
         SetGadgetColor(TextGadget(#PB_Any, 10, 5, 800, 15, "Trigger Control"), #PB_Gadget_BackColor, #CB_Color)
         ToggleButtonGadget(#Source_L,      05, 025, 60, 25, "LEFT")
            GadgetToolTip(#Source_L, "Trigger on LEFT")
         ToggleButtonGadget(#Source_R,      05, 055, 60, 25, "RIGHT")
            GadgetToolTip(#Source_R, "Trigger on RIGHT")
         ToggleButtonGadget(#Trig_Slope, 05, 085, 60, 25, "Slope +", "Slope -")
            SetButtonColor(#Trig_Slope, $AEFF0D, $AEC00D)
         ToggleButtonGadget(#Trig_Single, 05, 115, 60, 25, "Single")
         ToggleButtonGadget(#Trig_Arm,    05, 145, 60, 25, "Arm", "Armed")
            DisableButton(#Trig_Arm, #True)
            SetButtonColor(#Trig_Arm, $1E64F0, $3A1FB4, $17D671, $17A21B)
         TrackBarGadget(#Trig_Lvl_L, 70, 25, 25, 145, 0, #ScopeH, #PB_TrackBar_Vertical)
            SetGadgetState(#Trig_Lvl_L, #ScopeH/2)
            GadgetToolTip(#Trig_Lvl_L, "LEFT Trig. Level")
         TrackBarGadget(#Trig_Lvl_R, 100, 25, 25, 145, 0, #ScopeH, #PB_TrackBar_Vertical)
            SetGadgetState(#Trig_Lvl_R, #ScopeH/2)
            GadgetToolTip(#Trig_Lvl_R, "RIGHT Trig. Level")
      CloseGadgetList()
      
      ; graph controls
      ContainerGadget(#Graph, #ScopeW + 185, 200, 135, 155, #PB_Container_Raised)
         SetGadgetColor(#Graph, #PB_Gadget_BackColor, #CB_Color)
         SetGadgetColor(TextGadget(#PB_Any, 10, 5, 85, 15, "Graph Control"), #PB_Gadget_BackColor, #CB_Color)
         ToggleButtonGadget(#Graph_Time  , 05, 025, 60, 25, "Time")
            GadgetToolTip(#Graph_Time, "Plot amplitude vs time")
         ToggleButtonGadget(#Graph_XY    , 05, 055, 60, 25, "X Y")
            GadgetToolTip(#Graph_XY, "XY plot, left vs right")
         ToggleButtonGadget(#Graph_Polar , 05, 085, 60, 25, "Polar")
            GadgetToolTip(#Graph_Polar, "radius (left) vs angle (right)")
         ToggleButtonGadget(#LineType    , 05, 120, 60, 25, "Dots", "Lines", 1)
            SetButtonColor(#LineType, $AEFF0D, $AEC00D)
            GadgetToolTip(#LineType, "show trace as lines or dots")
         TrackBarGadget(#Gain_L, 70, 25, 25, 120, 0, 8, #PB_TrackBar_Vertical | #PB_TrackBar_Ticks)
            SetGadgetState(#Gain_L, 4) ; gain is Pow(2, n-4)
            GadgetToolTip(#Gain_L, "LEFT gain")
         TrackBarGadget(#Gain_R, 100, 25, 25, 120, 0, 8, #PB_TrackBar_Vertical | #PB_TrackBar_Ticks)
            SetGadgetState(#Gain_R, 4) ; state 4 gives default gain of x1
            GadgetToolTip(#Gain_R, "RIGHT gain")
      CloseGadgetList()
         
      ; signal aquisition
      ContainerGadget(#Aquire, #ScopeW + 20, 200, 160, 155, #PB_Container_Raised)
         SetGadgetColor(#Aquire, #PB_Gadget_BackColor, #CB_Color)
         SetGadgetColor(TextGadget(#PB_Any, 5, 5, 155, 15, "Signal Aquisition"),
            #PB_Gadget_BackColor, #CB_Color)
         
         ComboBoxGadget(#InputDevice, 5, 25,145,20)
         
         ComboBoxGadget(#SampleRate, 5, 75, 70, 20)
         SetGadgetColor(TextGadget(#PB_Any, 80, 75, 80, 15,
               "sample rate"), #PB_Gadget_BackColor, #CB_Color)
         ToggleButtonGadget(#Pause,05, 120, 135, 25, "Pause", "Paused")
            SetButtonColor(#Pause, $17D671, $17A21B, $1E64F0, $3A1FB4)
      CloseGadgetList()
      
      EditorGadget(#Scope_Stats, #ScopeW + 20, 365, 300, 110, #PB_Editor_ReadOnly)
         If LoadFont(0,"Courier New", 10, #PB_Font_Bold)
            SetGadgetFont(#Scope_Stats, FontID(0))
         EndIf
         SetGadgetColor(#Scope_Stats, #PB_Gadget_FrontColor, $DBEC45)
         SetGadgetColor(#Scope_Stats, #PB_Gadget_BackColor , $51121E)
         For n = 0 To 5 : AddGadgetItem(#Scope_Stats,-1,RSet("",25,".")) : Next n
         SetGadgetItemText(#Scope_Stats, 0, "Left  chan. gain : x 1")
         SetGadgetItemText(#Scope_Stats, 1, "Right chan. gain : x 1")
         SetGadgetItemText(#Scope_Stats, 2, "Left  Trig level : " + Str(Config\Trig_Lvl_L))
         SetGadgetItemText(#Scope_Stats, 3, "Right Trig level : " + Str(Config\Trig_Lvl_R))
                  
         If GET_CAPTURE_DEVICES() = 0
            ProcedureReturn 0
         Else
            SetGadgetState(#InputDevice,0)
         EndIf
         
         CREATE_GRATICULES()
      
      SetWindowCallback(@WIN_CALLBACK())
      ProcedureReturn hWin
   Else
      ProcedureReturn 0
  EndIf
  
EndProcedure

Procedure CAPTURE_STOP()
   Protected i
   
   If Config\wave
      waveInReset_(Config\wave)
      waveInStop_(Config\wave)
      For i = 0 To Config\nBuf - 1
         If inHdr(i)\lpData
            waveInUnprepareHeader_(Config\wave,inHdr(i),SizeOf(WAVEHDR))
            FreeMemory(inHdr(i)\lpData)
            inHdr(i)\lpData = 0
         EndIf
      Next
      waveInClose_(Config\wave)
   EndIf
   
   captureActive = #False
EndProcedure

Procedure TOGGLE_LINE_TYPE()
   FlipButton(#LineType)
   If GetButtonState(#LineType)
      Config\lineType = #Lines
   Else
      Config\lineType = #Dots
   EndIf
EndProcedure

Procedure TOGGLE_TRACE(T$)
   If T$ = "L"
      FlipButton(#Trace_L)
      Config\trace_L = GetButtonState(#Trace_L)
   Else
      FlipButton(#Trace_R)
      Config\trace_R = GetButtonState(#Trace_R)
   EndIf
EndProcedure

Procedure PAUSE_CAPTURE()
   FlipButton(#Pause)
   If GetButtonState(#Pause) = #True
      waveInStop_(Config\wave)
   Else
      waveInStart_(Config\wave)
   EndIf
EndProcedure

Procedure INIT_CAPTURE()
   Protected state, i
   
   If captureActive : CAPTURE_STOP() : EndIf
   
   Config\nDev = GetGadgetState(#InputDevice) ; select signal source
   
   With Format
      Config\BufLen = \nSamplesPerSec / Config\frameRate ; \BufLen determines refresh rate
      \nBlockAlign = (#STEREO * \wBitsPerSample) / 8 ; bytes per sample * num channels
      Config\lBuf = Config\BufLen * \nBlockAlign ; multiply bufferLength by blockAlign
      \nAvgBytesPerSec = \nSamplesPerSec * \nBlockAlign ; blockAlign is bytes per sample
      \cbSize = 0
   EndWith
   
   If #MMSYSERR_NOERROR = waveInOpen_(@Config\wave,#WAVE_MAPPER+Config\nDev,@format,
                                      hWindow,#Null,#CALLBACK_WINDOW|#WAVE_FORMAT_DIRECT)
      For i = 0 To Config\nBuf - 1
         inHdr(i)\lpData = AllocateMemory(Config\lBuf)
         inHdr(i)\dwBufferLength = Config\lBuf
         waveInPrepareHeader_(Config\wave,inHdr(i),SizeOf(WAVEHDR))
         waveInAddBuffer_(Config\wave,inHdr(i),SizeOf(WAVEHDR))
      Next
      
      If #MMSYSERR_NOERROR = waveInStart_(Config\wave)
      Else
         MessageRequester("Error!","waveInStart_ fail"+#CRLF$+"INIT_CAPTURE() procedure")
      EndIf
   Else
      MessageRequester("Error!","waveInOpen_ fail"+#CRLF$+"INIT_CAPTURE() procedure")
   EndIf
   
   captureActive = #True
EndProcedure

Procedure SET_SAMPLE_RATE()
   Protected n, text.s
    format\nSamplesPerSec = Val(GetGadgetText(#SampleRate))
    
    text = "Time Per Div. = " +
            StrF(#ScopeW / format\nSamplesPerSec * 100, 3) + " mS."
   StartDrawing(SpriteOutput(#Time_Per_Div))
      DrawingMode(#PB_2DDrawing_Transparent)
      Box(0,0,OutputWidth(),OutputHeight(),0)
      DrawText(0, 0,text, #Readout_Color)
   StopDrawing()
    
   INIT_CAPTURE()
EndProcedure

Procedure SET_DISPLAY_MODE(Button)
   If GetButtonState(Button)
      ProcedureReturn
   Else
      FlipButton(Button)
      Config\Display_Mode = Button
      Select Button
         Case #Graph_Time
            SetButtonState(#Graph_XY    , #False)
            SetButtonState(#Graph_Polar , #False)
            StartDrawing(SpriteOutput(#Screen_bkgnd))
               DrawImage(ImageID(#Grat_Norm), 0, 0)
            StopDrawing()
            DisableButton(#Trace_L , #False)
            DisableButton(#Trace_R , #False)
            DisableButton(#Math    , #False)
            If Config\lineType = #Dots : TOGGLE_LINE_TYPE() : EndIf
         Case #Graph_Polar
            SetButtonState(#Graph_Time, #False)
            SetButtonState(#Graph_XY  , #False)
            StartDrawing(SpriteOutput(#Screen_bkgnd))
               DrawImage(ImageID(#Grat_Polar), 0, 0)
            StopDrawing()
            DisableButton(#Trace_L , #True)
            DisableButton(#Trace_R , #True)
            DisableButton(#Math    , #True)
            If Config\lineType = #Lines
               TOGGLE_LINE_TYPE()
            EndIf
         Case #Graph_XY
            StartDrawing(SpriteOutput(#Screen_bkgnd))
               DrawImage(ImageID(#Grat_Norm), 0, 0)
            StopDrawing()
            SetButtonState(#Graph_Time  , #False)
            SetButtonState(#Graph_Polar , #False)
            DisableButton(#Trace_L , #True)
            DisableButton(#Trace_R , #True)
            DisableButton(#Math    , #True)
            If Config\lineType = #Lines : TOGGLE_LINE_TYPE() : EndIf
      EndSelect
   EndIf
EndProcedure

Procedure SET_STICKY_WINDOW()
   Protected state = GetMenuItemState(#PopUp, #MenuItem_Always_On_Top) ! 1
   SetMenuItemState(#PopUp, #MenuItem_Always_On_Top, state)
   StickyWindow(#MainWin, state)
EndProcedure

Procedure TOGGLE_MATH()
   FlipButton(#Math)
   Config\math ! 1
   DisableGadget(#Pos_M, GetButtonState(#Math) ! 1)
EndProcedure

Procedure SET_TRIG_LEVEL(chan)
   Static mlt = $FFFF / #ScopeH
   Protected level = (GetGadgetState(chan) - #ScopeH >> 1) * mlt
   
   If Abs(level) < 228 : level = 0 : EndIf
   
   If chan = #Trig_Lvl_L
      Config\Trig_Lvl_L = level * Config\gain_L
      SetGadgetItemText(#Scope_Stats, 2, "Left  Trig level : " + Str(level/327.68))
   ElseIf chan = #Trig_Lvl_R
      Config\Trig_Lvl_R = level * Config\gain_R
      SetGadgetItemText(#Scope_Stats, 3, "Right Trig level : " + Str(level/327.68))
   EndIf
   
   trigView = #TrigViewTime ; display trig level lines for specified duration
   
EndProcedure

Procedure SET_GAIN(gadNum)
   Protected text.s
   
   If gadNum = #Gain_L ; set left channel gain
      Config\gain_L = Pow(2, 4 - GetGadgetState(gadNum))
      SET_TRIG_LEVEL(#Trig_Lvl_L)
      text = "Left  chan. gain : x " + StrF(1/Config\gain_L)
      SetGadgetItemText(#Scope_Stats, 0, text)
      
   ElseIf gadNum = #Gain_R ; set right channel gain
      Config\gain_R = Pow(2, 4 - GetGadgetState(gadNum))
      SET_TRIG_LEVEL(#Trig_Lvl_R)
      text = "Right chan. gain : x " + StrF(1/Config\gain_R)
      SetGadgetItemText(#Scope_Stats, 1, text)
      
   EndIf
EndProcedure

Procedure SET_TRIG_MODE(gadNum)
   Static text.s = " Trig. source? "
   Protected n
   
   FlipButton(gadNum)
   
   Select gadNum
      Case #Source_L, #Source_R
         If GetButtonState(gadNum) = #True
            Config\Trig_Source = gadNum
            If Config\Trig_Mode = #Trig_Free : Config\Trig_Mode = #Trig_Auto : EndIf
            SetButtonState(#Source_L + #Source_R - gadNum, #False)
         Else
            Config\Trig_Source = #Source_None
            If Config\Trig_Mode = #Trig_Auto : Config\Trig_Mode = #Trig_Free : EndIf
         EndIf
      Case #Trig_Single
         If GetButtonState(#Trig_Single) = #True
            DisableButton(#Trig_Arm, #False)
            SetButtonState(#Trig_Arm, #True)
            Config\Trig_Armed = #True
            Config\Trig_Mode = #Trig_Single
         Else
            SetButtonState(#Trig_Arm, #False)
            DisableButton(#Trig_Arm, #True)
            Config\Trig_Armed = #False
            If Config\Trig_Source = #Source_None
               Config\Trig_Mode = #Trig_Free
            Else
               Config\Trig_Mode = #Trig_Auto
            EndIf
         EndIf
      Case #Trig_Arm
         Config\Trig_Armed = GetButtonState(#Trig_Arm)
         ProcedureReturn
   EndSelect
   
   If Config\Trig_Mode = #Trig_Single
      If Config\Trig_Source = #Source_None
         StartDrawing(ScreenOutput())
            DrawText(#ScopeW/2 - TextWidth (text)/2,
                     #ScopeH/2 - TextHeight(text)/2,
                     text, #White, #Scope_Color)
         StopDrawing() : FlipBuffers()
      EndIf
   EndIf
   
   trigView = #TrigViewTime ; display trig level lines for specified duration
EndProcedure

Procedure EVENT_LOOP()
   Protected Quit = #False
   Repeat
      Select WaitWindowEvent()
         Case #PB_Event_CloseWindow : Quit = #True
            
         Case #PB_Event_MinimizeWindow : PAUSE_CAPTURE()
            
         Case #PB_Event_RightClick
            DisplayPopupMenu(#PopUp, hWindow)
            
         Case #PB_Event_Menu
            Select EventMenu()
               Case #MenuItem_Exit          : Quit = #True
               Case #MenuItem_Always_On_Top : SET_STICKY_WINDOW()
               Case #MenuItem_Minimize      : PAUSE_CAPTURE()
                  SetWindowState(#MainWin, #PB_Window_Minimize)
            EndSelect
            
         Case #PB_Event_Gadget
            Select EventGadget()
               Case #Pause        : PAUSE_CAPTURE()
               Case #Graph_Time   : SET_DISPLAY_MODE(#Graph_Time)
               Case #Graph_XY     : SET_DISPLAY_MODE(#Graph_XY)
               Case #Graph_Polar  : SET_DISPLAY_MODE(#Graph_Polar)
               Case #InputDevice  : INIT_CAPTURE()
               Case #SampleRate   : SET_SAMPLE_RATE()
               Case #LineType     : TOGGLE_LINE_TYPE()
               Case #Pos_L        : Config\tr_L_offset = #ScopeH/2 - GetGadgetState(#Pos_L)
               Case #Pos_R        : Config\tr_R_offset = #ScopeH/2 - GetGadgetState(#Pos_R)
               Case #Pos_M        : Config\tr_M_offset = #ScopeH/2 - GetGadgetState(#Pos_M)
               Case #Trace_L      : TOGGLE_TRACE("L")
               Case #Trace_R      : TOGGLE_TRACE("R")
               Case #Invert_L     : FlipButton(#Invert_L)   : Config\invert_L   ! 1
               Case #Invert_R     : FlipButton(#Invert_R)   : Config\invert_R   ! 1
               Case #Trig_Slope   : FlipButton(#Trig_Slope) : Config\Trig_Slope ! 1
               Case #Math         : TOGGLE_MATH()
               Case #Gain_L       : SET_GAIN(#Gain_L)
               Case #Gain_R       : SET_GAIN(#Gain_R)
               Case #Trig_Lvl_L   : SET_TRIG_LEVEL(#Trig_Lvl_L)
               Case #Trig_Lvl_R   : SET_TRIG_LEVEL(#Trig_Lvl_R)
               Case #Source_L To #Trig_Arm : SET_TRIG_MODE(EventGadget())
            EndSelect
      EndSelect
   Until Quit = #True
   
   CAPTURE_STOP()
   End
EndProcedure

hWindow = INIT_GUI()
If hWindow <> 0
   SET_SAMPLE_RATE()
   SET_DISPLAY_MODE(#Graph_Time)
   EVENT_LOOP()
EndIf
Last edited by BasicallyPure on Thu Apr 28, 2016 4:07 am, edited 7 times in total.
BasicallyPure
Until you know everything you know nothing, all you have is what you believe.
User avatar
BasicallyPure
Enthusiast
Enthusiast
Posts: 536
Joined: Thu Mar 24, 2011 12:40 am
Location: Iowa, USA

Re: Oscilloscope

Post by BasicallyPure »

Here is a small audio generator that you may find useful for testing the oscilloscope.

Code: Select all

; AudioGenerator.pb
; by BasicallyPure
; 11.22.2013
; windows only
; PB 5.20 LTS

EnableExplicit

;{ Constants
#MainWin = 0
#OutDevice_1 = 0
#PIx2 = 2*#PI
#Mono = 1
#Stereo = 2
#ScopeBkgndColor = $276724
#LeftColor  = $27CF24
#RighttColor = $27E4D3
#SpinLostFocus = 512
#Input_Finished = #PB_EventType_FirstCustomValue

; gadgets
#BtnHop     = 0
#BtnPause   = 1
#SpinLeft   = 2
#SpinRight  = 3
#TrackLeft  = 4
#TrackRight = 5

; menus
#Menu_1 = 0
;}

;{ Procedure declarations
Declare WinCallback(hwnd, uMsg, wParam, lParam)
Declare StartSoundOutput()
Declare StopSoundOutput()
Declare MAKE_WAVE(*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,350,100,"Audio generator",#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,#LeftColor)
      SpinGadget(#SpinRight,235,02,70,25,100,5000)
         SetGadgetState(#SpinRight,Frequency_Right) : SetGadgetText(#SpinRight,Str(Frequency_Right))
         SetGadgetColor(#SpinRight,#PB_Gadget_BackColor,#RighttColor)
      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)
      
      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
         MAKE_WAVE(*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 MAKE_WAVE(*SBuf)
   ; This routine generates Left and Right waveforms.
   
   Static.d Angle, Kl, Kr, La, Ra
   Static.i sample
   Static.l Vl, Vr
   
   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
   
   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
      Vr = Sin(Ra) * 32767 * Volume_Right
      Ra + Kr
      If Ra > #PIx2 : Ra - #PIx2 : EndIf
      PokeW(*SBuf,Vr)
      *SBuf + BytesPerSample
      
      sample + PlayFormat\nBlockAlign
   Until sample > BlockSize
   
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.
jack
Addict
Addict
Posts: 1336
Joined: Fri Apr 25, 2003 11:10 pm

Re: Oscilloscope

Post by jack »

looks nice, but can't download the exe, I receive a message 'this file is set to private'
User avatar
BasicallyPure
Enthusiast
Enthusiast
Posts: 536
Joined: Thu Mar 24, 2011 12:40 am
Location: Iowa, USA

Re: Oscilloscope

Post by BasicallyPure »

Thanks jack,
Please try again.
Mediafire has changed the way their system operates so I had to enable the 'share' option.

BP
BasicallyPure
Until you know everything you know nothing, all you have is what you believe.
mpz
Enthusiast
Enthusiast
Posts: 494
Joined: Sat Oct 11, 2008 9:07 pm
Location: Germany, Berlin > member German forum

Re: Oscilloscope

Post by mpz »

Hi BasicallyPure.

thanks for sharing. I have searched for these kind of code and now i have a solution...

P.S: That why i love Purebasic...

Greetings Michael
Working on - MP3D Library - PB 5.73 version ready for download
jack
Addict
Addict
Posts: 1336
Joined: Fri Apr 25, 2003 11:10 pm

Re: Oscilloscope

Post by jack »

thanks BasicallyPure, download works OK.
applePi
Addict
Addict
Posts: 1404
Joined: Sun Jun 25, 2006 7:28 pm

Re: Oscilloscope

Post by applePi »

at last it works now, yesterday i can't see the waves
this is an exrtaordinary work BasicallyPure , thank you
i will describe for the users what to do (in windows xp) if you have SoundMax HD audio 1 on your pc (check control panel)
click on the speaker icon on the taskbar
click on options >> properties
choose from the List : SoundMax HD audio 1 (this is for recording)

Image

check all check buttons _ then click OK
and the recording control will show up
slide all the sliders up, and Select the stereo Mix, then close the control, or what is suitable for your case, if it is microphone then select mic. if it is an electronic signal from Line-in then select it
Image

now run the Oscilloscope_v0.83 and select SoundMax HD audio 1 and run any music file

all the years i can't hear high tone sounds like the Mosquito tone , in contrast of the youngs. so going to the site http://journal.plasticmind.com/ears/mos ... a-youngun/ the mosquito tone displayed on the Oscilloscope while i can't hear it, it is 13 KHZ . they said universities students ring to each other using mosquito tones while the professor can't hear anything
User avatar
luis
Addict
Addict
Posts: 3876
Joined: Wed Aug 31, 2005 11:09 pm
Location: Italy

Re: Oscilloscope

Post by luis »

@basicallypure looks really nice, congratulations!
I saw many other oscilloscope-software based on soundcards in the past and they're certainly VERY interesting as a learning tool and the next best thing if you don't have an oscilloscope, only problem is the very limited bandwidth around 20Khz (half of the max sample freq.) and the fact you have to pay a little attention to the levels you fires at your poor sound card.
I'll try it as soon I have a little time, thanks for sharing it.

@applepi, people not hearing 13-16 KHz are not elderly, they are just deaf. Being old does not mean you can't hear or see anymore or that should be considered normal.
The proposed rule of youngster hearing the "mosquito tone" while their professor can't just because of the age difference is really ridicule. I can hear easily up to 20Khz (above actually) and I'm not a "youngster". Some people have physical deficiencies and some doesn't, age it's not a justification unless you are well above 90.
Seems the general population is deaf anyway, since all need to be loud for them: television, radio, talking, background music, etc.
People seems to constantly trying to bombard their brain though their ears to avoid the discomfort of having to think about something.
"Have you tried turning it off and on again ?"
A little PureBasic review
User avatar
BasicallyPure
Enthusiast
Enthusiast
Posts: 536
Joined: Thu Mar 24, 2011 12:40 am
Location: Iowa, USA

Re: Oscilloscope

Post by BasicallyPure »

@applePi,
thanks, I'm glad you got it working.

That reminds me, If you are using windows 7 and are not able to view your music,
this may be of some help.
http://www.sevenforums.com/tutorials/20 ... nable.html
Some of the sound driver settings with windows 7 are very difficult to locate.

@luis,
I use a small audio mixer device external to my computer to protect my sound card inputs and
give better control of the input signals.
Try using the little audio generator I provided above with the oscilloscope.
With the XY and polar graphs you can make some interesting patterns.
Run more than one instance of the audio generator to create more complex signals.

I don't think I have ever been able to hear 20 kHz, maybe 16 or 17 kHz when I was young.
Now I don't think I can hear anything much above 8 kHz.
I had a bad ear infection once that did some permanent damage but more to my sense of
balance than my hearing I think.
Now if you ever see me walking down the street you may wonder, is it his bad ear
or the beer in his belly that makes him wobble like that?

BP
BasicallyPure
Until you know everything you know nothing, all you have is what you believe.
User avatar
luis
Addict
Addict
Posts: 3876
Joined: Wed Aug 31, 2005 11:09 pm
Location: Italy

Re: Oscilloscope

Post by luis »

nice, you can get all the classic Lissajous figures

Image

The above is rotating around the Y axis on the screen

It's very good, a program so short (relatively speaking) and does a lot of work, a nice example of what can be done in PB, well done ! :)
"Have you tried turning it off and on again ?"
A little PureBasic review
User avatar
Kwai chang caine
Always Here
Always Here
Posts: 5342
Joined: Sun Nov 05, 2006 11:42 pm
Location: Lyon - France

Re: Oscilloscope

Post by Kwai chang caine »

Very nice
Thanks for sharing 8)
ImageThe happiness is a road...
Not a destination
User avatar
utopiomania
Addict
Addict
Posts: 1655
Joined: Tue May 10, 2005 10:00 pm
Location: Norway

Re: Oscilloscope

Post by utopiomania »

Same as Kwai, very nice contribution :) The exe won't run though, on Win 8.1 64 bit. I get a message saying that the application cannot be run on my pc.

I'm not an experienced scope user, but recently bought this: http://cgi.ebay.co.uk/ws/eBayISAPI.dll? ... OU:GB:3160

The bandwith is 20mhz, and will mainly be used for checking audio circuits, square wave performance and things like that.
applePi
Addict
Addict
Posts: 1404
Joined: Sun Jun 25, 2006 7:28 pm

Re: Oscilloscope

Post by applePi »

i have made this practical test with the BasicallyPure Oscilloscope.
the target is to measure the frequency of the mains electricity, which are either 50 HZ or 60 HZ.
i have used a microphone connector to connect to the Line-In socket of the sound card, i removed 2 cm of the insulation from the end part of the wire, the insulated white wire are positive
Image
some mic wires are too complex, the above wire are from a $1 mic and it is thick relative to some mic wires.
now as described in my above post how to set the recording device, now select the Line-In instead of stereo Mix in the Recording control. run the Oscilloscope, and select the proper sound device from the list, and also select the lowest frequency 8000 HZ. then touch with your finger the white wire (the positive) through a resistor , i have used 10 MegaOhm to reduce the noise. you can experiment with other values.
what you will see like this
Image
the number of divs between any 2 crests, or troughs are about between 3.6 to 3.7 divs , this is the range of one wave
let us calculate
1000 ms / (5.512 ms/div * 3.7 divs) = 49.03 HZ per second, ie we can say it is 50 HZ as declared by the electrical company. in US it is 60 HZ
so how our body produced 50HZ, it is from the mains electricity every where from the computer, tv, your neighbor etc.
look this http://www.electronics-related.com/sci. ... -noise.php

another way to measure the frequency of the mains is to use a transformer, i have used a general purpose small transformer , detaching its secondary coil from the rectifying diodes, and connecting one wire only to a big resistor such as 20 Mega Ohm for protection, then connecting the resistor to the positive of the line-In wire. it display this figure
Image
when you disconnect the transformer from the mains, the Oscilloscope still display the frequency but with a less amplitude, because the transformer coils induced by the mains waves everywhere.
yesterday using another computer with a sound card realteck and connecting it to the same transformer with the interface of 10 Mohm produced this display
Image
it is preferred to use old abandoned desktop pc to do all your electrical and electronics experiments.
The author of this post accepts no liability for any damage caused by
following the above instructions whether you are injured or you damaged your computer.
http://www.emaildisclaimers.com/Sample_disclaimers.htm :)
i have tried it myself with a great care and nothing happened bad. just use 20 megaOhm resistor or more on an old computer. your only problem is to open the transformer, it is extremely tight, and can electrocute you.
User avatar
luis
Addict
Addict
Posts: 3876
Joined: Wed Aug 31, 2005 11:09 pm
Location: Italy

Re: Oscilloscope

Post by luis »

Beware of programmers carrying screwdrivers !

Just joking, I'm one myself, nice experiment to visualize the annoying 50(60) Hz noise :)
"Have you tried turning it off and on again ?"
A little PureBasic review
Simo_na
Enthusiast
Enthusiast
Posts: 177
Joined: Sun Mar 03, 2013 9:01 am

Re: Oscilloscope

Post by Simo_na »

BasicallyPure wrote:Here is a small audio generator that you may find useful for testing the oscilloscope.
Thank you :D

Please, can you add squarewave ?? :wink:
Post Reply