Random Number Generator

Just starting out? Need help? Post your questions and find answers here.
chris319
Enthusiast
Enthusiast
Posts: 782
Joined: Mon Oct 24, 2005 1:05 pm

Random Number Generator

Post by chris319 »

I started out with the idea of creating a hardware random-number generator which would read Gaussian white noise from a sound card/audio interface. An 8-bit audio sample would become the random number. I repurposed a sound recorder program for this purpose.

There are two design requirements:

1. The random numbers must be confined to the range 1 to 80;

2. There must be no numbers that duplicate a previously-picked number.

When reading audio samples I have trouble satisfying condition #1, so I instead use CryptRandomData(). I then examine the picked number and filter out numbers < 1 or > 80. This satisfies requirement #1.

Where I'm having trouble is in filtering out duplicate picks, requirement #2. Line 323 of the code is supposed to do this but I'm still getting duplicate picks and skipped numbers. Any idea why this is so?

There is still a lot of code which can probably be deleted.

Thank you.

Code: Select all

    ;/ Object     Audio Recorder 1.0 (a)
    ;/
    ;/ Date       August 2004
    ;/ Author     Philippe Carpentier
    ;/ Contact    flype@altern.org
    ;/ Info       MS Windows only - winmm.lib - mmsystem.h
    ;/
    ;  Bug fixes by chris319 on September 2, 2007
    ;  04/02/2010 : DrGolf for PB 4.50
    ;  01/20/2011 : Vitor_Boss® -- Fixed clamping
    ;  6/8/2012 -- revised again by chris319 on PB 4.61

Global QuitRec.l
    ;
;- REC CONSTANTES
    ;
#MinWidth      = 320
#MinHeight     = 240

#MONO = 1


Global *Key = AllocateMemory(256)
  
If OpenCryptRandom() And *Key
    CryptRandomData(*Key, 256)
EndIf

Global Dim randomPicks(256)
Global Dim pickedNumbers.a(257)

Global pickCount = 0
For cnt = 0 To 80
pickedNumbers(cnt) = 0
Next

;- REC INITIALISATION

Enumeration 0 ; #CHANNEL
  #CHANNEL_LEFT
;  #CHANNEL_RIGHT
EndEnumeration

Enumeration 0 ; #GADGET
  #gadFrame1
  #gadFrame2
  #gadStart
  #gadStop
  #gadSndVol
  #gadFile
  #gadChannel
  #gadResolution
  #gadFrequence
  #gadDevice
  #gadText1
  #gadText2
EndEnumeration

Enumeration 0 ; #WINDOW
  #Window
EndEnumeration

Enumeration 0 ; #TOOLBAR
  #ToolBar
EndEnumeration

Enumeration 0 ; #STATUSBAR
  #StatusBar
EndEnumeration

;Structure WAVEFORMATEX -- NOT NEEDED IN PB 4.10 -- chris319
;  wFormatTag.w
;  nChannels.w
;  nSamplesPerSec.l
;  nAvgBytesPerSec.l
;  nBlockAlign.w
;  wBitsPerSample.w
;  cbSize.w
;EndStructure

Structure SCOPE
  channel.b
  left.l
  top.l
  width.l
  height.l
  middleY.l
  quarterY.l
EndStructure

Structure CONFIG
     
      hWindow.l           ; Window handle
      hToolBar.l          ; ToolBar handle
      hfont.l             ; Font handle
     
      Date.l              ; Start date
      size.l              ; Wave buffer size
      buffer.l            ; Wave buffer pointer
      output.l            ; WindowOutput()
      wave.l              ; Address of waveform-audio input device
      recorded.l          ; Number of bytes recorded
      recording.b         ; Record is running...
     
      File.s              ; Recording FileName (c:\unnamed.snd) ;chris319 6/7/12
      FileId.l            ; Recording FileId (#PB_Any)
      SndVol.s            ; Microsoft Volume Control (sndvol32.exe)

      format.WAVEFORMATEX ; Capturing WaveFormatEx
      lBuf.l              ; Capturing Buffer size
      nBuf.l              ; Capturing Buffer number
      nDev.l              ; Capturing Device identifier
;      nBit.l              ; Capturing Resolution (8/16) ;chris319 6/7/12
;      nHertz.l            ; Capturing Frequency  (Hertz) ;chris319 6/7/12
;      nChannel.l          ; Capturing Channels number (Mono/Stereo) ;chris319 6/7/12
     
      LScope.SCOPE        ; Wave form display
      RScope.SCOPE        ; Wave form display
      ScopeTimer.l        ; Scope Timer (redraw every n times)
      cback.l             ; Back color
      cline.l             ; Line color
      cwave.l             ; Wave color
      ColRecWave.l        ; Scope Record Wave Color
      ColRecBack.l        ; Scope Record Back Color
      ColRecLine.l        ; Scope Record Line Color
      ColCapWave.l        ; Scope Capture Wave Color
      ColCapBack.l        ; Scope Capture Back Color
      ColCapLine.l        ; Scope Capture Line Color
     
    EndStructure

    Global Config.CONFIG

    Global Dim inHdr.WAVEHDR(16)

    Config\hfont             = LoadFont(0,"Arial",7)
    Config\format\cbSize     = 0
    Config\format\wFormatTag = #WAVE_FORMAT_PCM
    Config\LScope\channel    = #CHANNEL_LEFT

;- REC DECLARATIONS

Declare CONFIG_Load()

Declare CAPTURE_Stop()
Declare CAPTURE_Start()
Declare CAPTURE_Error(err.l)
Declare CAPTURE_GetDevices(gadId.l)
Declare CAPTURE_Read(hWaveIn.l,lpWaveHdr.l)

Declare DRAW_Wave8M(*scope.SCOPE)
Declare DRAW_Wave16M(*scope.SCOPE)
;Declare DRAW_Background(*scope.SCOPE)

Declare DrawNumber()
Declare GUI_Init()
Declare GUI_Resize()
Declare GUI_TBCombo(Id,x,y,w,h)
Declare GUI_CallBack(hWnd.l,Msg.l,wParam.l,lParam.l)

;- REC PROCEDURES

Procedure CONFIG_Load()

Config\nDev = #Null
Config\format\nChannels = #MONO
Config\format\nSamplesPerSec = 48000
Config\format\wBitsPerSample = 8
Config\nBuf = 8
Config\lBuf = 2048

      pos.WINDOWPLACEMENT
      pos\length=SizeOf(WINDOWPLACEMENT)
      pos\ptMinPosition\x         = 0
      pos\ptMinPosition\y         = 0
      pos\ptMaxPosition\x         = -1
      pos\ptMaxPosition\y         = -1
      pos\rcNormalPosition\left   = 100
      pos\rcNormalPosition\top    = 100
      pos\rcNormalPosition\right  = #MinWidth
      pos\rcNormalPosition\bottom = #MinHeight
     
     
      Config\cback   = Config\ColCapBack
      Config\cline   = Config\ColCapLine
      Config\cwave   = Config\ColCapWave
     
      StatusBarText(0,2,Config\File)
      SetGadgetState(#gadDevice,Config\nDev)
      SetGadgetState(#gadChannel,Config\format\nChannels - 1)

Select Config\format\wBitsPerSample
  Case 8: SetGadgetState(#gadResolution, 0)
  Case 16: SetGadgetState(#gadResolution, 1)
EndSelect

      SetWindowPlacement_(Config\hWindow,@pos)
      ShowWindow_(Config\hWindow,#True)
     
    EndProcedure

Procedure CAPTURE_Start()
     
CAPTURE_Stop()

;GET USER SETTINGS     
     
Config\format\nChannels = #MONO

gui_Resize()

Config\format\wBitsPerSample = 8
   
  Config\format\nBlockAlign = (Config\format\nChannels * Config\format\wBitsPerSample) / 8
  Config\format\nAvgBytesPerSec = Config\format\nSamplesPerSec * Config\format\nBlockAlign
  Config\format\cbSize = 0
   
      If #MMSYSERR_NOERROR = waveInOpen_(@Config\wave,#WAVE_MAPPER+Config\nDev,@Config\format,Config\hWindow,#Null,#CALLBACK_WINDOW|#WAVE_FORMAT_DIRECT)
        For i = 0 To 7 ;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)
          ;SetTimer_(Config\hWindow,0,Config\ScopeTimer,0)
        EndIf
      EndIf

    EndProcedure
    ;
Procedure CAPTURE_Stop()
      If Config\wave
        waveInReset_(Config\wave)
        waveInStop_(Config\wave)
        For i = 0 To Config\nBuf - 1
          If inHdr(i)
            waveInUnprepareHeader_(Config\wave,inHdr(i),SizeOf(WAVEHDR))
          EndIf
        Next
        waveInClose_(Config\wave)
      EndIf
  KillTimer_(Config\hWindow,0)
EndProcedure

Procedure CAPTURE_Read(hWaveIn.l,lpWaveHdr.l)
    ;  waveInAddBuffer_(hWaveIn,lpWaveHdr,SizeOf(WAVEHDR))

      *hWave.WAVEHDR=lpWaveHdr
      Config\buffer=*hWave\lpData
      Config\size=*hWave\dwBytesRecorded
    ;  dwBytesRecorded MUST BE SAVED BEFORE CALLING waveInAddBuffer -- chris319
      waveInAddBuffer_(hWaveIn,lpWaveHdr,SizeOf(WAVEHDR))

DrawNumber()
     
    EndProcedure
    ;
    Procedure CAPTURE_GetDevices(gadId.l)
      MMNumDevice.l = waveInGetNumDevs_()
      If MMNumDevice
        For MMDeviceId=#WAVE_MAPPER To MMNumDevice-1
          MMResult.l = waveInGetDevCaps_(MMDeviceId,@Caps.WAVEINCAPS,SizeOf(WAVEINCAPS))
          If MMResult = #MMSYSERR_NOERROR
            AddGadgetItem(gadId,-1,PeekS(@Caps\szPname,#MAXPNAMELEN))
          EndIf
        Next
      EndIf
    EndProcedure

    Procedure DRAW_Wave8M(*scope.SCOPE)
      Protected Max_Y = (Config\RScope\height)/2
      oldx.l = *scope\left
      For i = 0 To Config\size - 1 ;chris319
        value.b=PeekB(Config\buffer+i)+$7F
        ;xx=Clamp(*scope\left+(i * *scope\width)/(Config\size+1),*scope\left,*scope\width)
        yy=(value**scope\height)/$FF
        If yy > Max_Y : yy = Max_Y : EndIf
        If yy < -Max_Y : yy = -Max_Y : EndIf
        LineXY(oldx,*scope\middleY+oldy,xx,*scope\middleY+yy, Config\cwave)
        oldx=xx:oldy=yy
      Next
     
    EndProcedure
    
Procedure DRAW_Wave16M(*scope.SCOPE) ;16 BITS MONO
Config\cwave = $00ffff
  Protected Max_Y = (Config\RScope\height)/2

      oldx.l = *scope\left
      For i = 0 To (Config\size - 2) Step 2
        value.w = PeekW(Config\buffer + i)
        ;xx = Clamp(*scope\left+(i**scope\width)/(Config\size+1),*scope\left,*scope\width)
        yy = (value * *scope\height)/$FFFF
        ;yy = (value * *scope\height) / $FFF
        ayy = Abs(yy)
        If ayy > vmax: vmax = ayy: EndIf
        If yy > Max_Y : yy = Max_Y : EndIf
        If yy < -Max_Y : yy = -Max_Y : EndIf
        LineXY(oldx,*scope\middleY+oldy,xx,*scope\middleY+yy,Config\cwave)
        oldx = xx: oldy = yy
      Next

  LineXY(oldx, *scope\middleY + oldy, xx, *scope\middleY + yy, Config\cwave)

oldx = xx: oldy = yy
 
EndProcedure

Procedure DrawNumber()
     
        Config\recorded + Config\size

CryptRandomData(*Key, 256)

;index.a = PeekA(@Config\buffer)
;pick.a = PeekA(@Config\buffer)
;pick.a = PeekA(*Key+index)
pick.a = PeekA(*Key)

isUnique = #True
For cnt = 0 To 81
If pick = pickedNumbers(cnt)
isUnique = #False
Else
pickedNumbers(cnt) = pick
EndIf
Next

If isUnique = #True And pick >= 1 And pick <= 80 And pickCount < 80
  pickCount + 1: Debug pick
EndIf

If pickCount > 80
For cnt = 0 To 255
pickedNumbers(cnt) = 255
Next
EndIf
     
    EndProcedure
    
    Procedure GUI_Init()
      QuitRec = 0
      WndId.l = OpenWindow(0,0,0,500,400,"Recorder",#PB_Window_ScreenCentered|#PB_Window_SystemMenu|#PB_Window_SizeGadget|#PB_Window_Invisible)
      If WndId=#Null
        MessageRequester("Error", "Unable to open window.") ;chris319 -- 6/8/12
        End
    EndIf

      LoadFont(0, "Arial", 24)

      Config\hWindow=WindowID(0)
      Config\output=WindowOutput(0)

      Config\hToolBar=CreateToolBar(#ToolBar,Config\hWindow)

      If Config\hToolBar=#Null : ProcedureReturn #False : EndIf
     
      If CreateStatusBar(#StatusBar,Config\hWindow)=#Null
        ProcedureReturn #False
      EndIf
     
      ToolBarStandardButton(#gadStart,#PB_ToolBarIcon_Open) ;START RECORDING
      ToolBarStandardButton(#gadStop,#PB_ToolBarIcon_Delete) ;STOP RECORDING
      ToolBarStandardButton(#gadFile,#PB_ToolBarIcon_Save) ;SAVE FILE
      ToolBarSeparator()
;      ToolBarStandardButton(#gadSndVol,#PB_ToolBarIcon_Properties) ;VOLUME CONTROL -- NOT IMPLEMENTED
      ToolBarSeparator()
     
      ToolBarToolTip(#ToolBar, #gadStart,"Record")
      ToolBarToolTip(#ToolBar, #gadStop,"Stop")
      ToolBarToolTip(#ToolBar, #gadFile,"Create a file")
;      ToolBarToolTip(#ToolBar, #gadSndVol,"Volume")
     
      GUI_TBCombo(#gadChannel,0,1,58,20) ; 04/02/2010 DrGolf
      AddGadgetItem(#gadChannel,-1,"Mono")
      AddGadgetItem(#gadChannel,-1,"Stereo")
      SetGadgetState(#gadChannel,0)
     
      GUI_TBCombo(#gadResolution,1,1,55,20) ; 04/02/2010 DrGolf
      AddGadgetItem(#gadResolution,-1,"8 bits")
      AddGadgetItem(#gadResolution,-1,"16 bits")
      SetGadgetState(#gadResolution, 1)
     
      GUI_TBCombo(#gadFrequence,2,1,80,20) ; 04/02/2010 DrGolf
      AddGadgetItem(#gadFrequence,-1,"8.0 kHz")
      AddGadgetItem(#gadFrequence,-1,"11.025 kHz")
      AddGadgetItem(#gadFrequence,-1,"12.0 kHz")
      AddGadgetItem(#gadFrequence,-1,"16.0 kHz")
      AddGadgetItem(#gadFrequence,-1,"22.05 kHz")
      AddGadgetItem(#gadFrequence,-1,"24.0 kHz")
      AddGadgetItem(#gadFrequence,-1,"32.0 kHz")
      AddGadgetItem(#gadFrequence,-1,"44.1 kHz")
      AddGadgetItem(#gadFrequence,-1,"48.0 kHz")
      SetGadgetState(#gadFrequence, 7) ;chris319 6/7/12
     
      GUI_TBCombo(#gadDevice,2,1,160,20) ; 04/02/2010 DrGolf
      CAPTURE_GetDevices(#gadDevice)
      SetGadgetState(#gadDevice,0)
     
      ;Frame3DGadget(#gadFrame1,0,0,0,0,"",#PB_Frame3D_Double)
      ;Frame3DGadget(#gadFrame2,0,0,0,0,"",#PB_Frame3D_Double)
     
      AddStatusBarField( 100)
      AddStatusBarField(  80)
      AddStatusBarField(1000)
      StatusBarText(0,0,"0 "+"bytes",#PB_StatusBar_Center)
      StatusBarText(0,1,"0.0 "+"secs",#PB_StatusBar_Center)
     
      SetWindowCallback(@GUI_CallBack())

      GUI_Resize()
      ProcedureReturn #True
    EndProcedure
    ;
Procedure GUI_Resize()
     
      WndW.l = ( WindowWidth(0)  -  4 )
      WndH.l = ( WindowHeight(0) - 60 ) >> 1

      Config\LScope\left     = 28
      Config\LScope\top      = 30
      Config\LScope\width    = WndW
      Config\LScope\height   = WndH
      Config\LScope\quarterY = Config\LScope\height >> 2
      Config\LScope\middleY  = Config\LScope\top + Config\LScope\height >> 1
;      ResizeGadget(#gadFrame1,Config\LScope\left-2,Config\LScope\top-2,Config\LScope\width+4,Config\LScope\height+4)
     
      Config\RScope\left     = 28
      Config\RScope\top      = Config\LScope\top + Config\LScope\height + 6
      Config\RScope\width    = WndW
      Config\RScope\height   = WndH
      Config\RScope\quarterY = Config\RScope\height >> 2
      Config\RScope\middleY  = Config\RScope\top + Config\RScope\height >> 1
;      ResizeGadget(#gadFrame2,Config\RScope\left-2,Config\RScope\top-2,Config\RScope\width+4,Config\RScope\height+4)
     
EndProcedure

Procedure GUI_TBCombo(Id,x,y,w,h)
      pos=SendMessage_(Config\hToolBar,#TB_BUTTONCOUNT,0,0)
      ToolBarSeparator()       
      SendMessage_(Config\hToolBar,#TB_GETBUTTON,pos,@separator.TBBUTTON)
      separator\iBitmap=x+w
      SendMessage_(Config\hToolBar,#TB_DELETEBUTTON,pos,0)
      SendMessage_(Config\hToolBar,#TB_INSERTBUTTON,pos,separator)
      SendMessage_(Config\hToolBar,#TB_GETITEMRECT,pos,@rc.RECT)   
      UseGadgetList(Config\hToolBar)
      ComboBoxGadget(Id,x+rc\left,y,w,h)
      UseGadgetList(Config\hWindow)
EndProcedure

Procedure GUI_CallBack(hWnd.l,Msg.l,wParam.l,lParam.l)
     
      Result.l = #PB_ProcessPureBasicEvents
     
      Select Msg
        Case #WM_KEYDOWN
          If GetAsyncKeyState_(#VK_ESCAPE)
            QuitRec = 1
          EndIf

        Case #WM_SIZE: GUI_Resize()

        Case #MM_WIM_DATA: CAPTURE_Read(wParam,lParam): ;DRAW_Scope()

        Case #WM_GETMINMAXINFO
          *mmi.MINMAXINFO=lParam
          *mmi\ptMinTrackSize\x=#MinWidth
          *mmi\ptMinTrackSize\y=#MinHeight
      EndSelect
     
      ProcedureReturn Result
     
EndProcedure

;START RECORDER
      If GUI_Init()

        CONFIG_Load()

        CAPTURE_Start()

        Repeat
        Until WaitWindowEvent() = #WM_CLOSE Or QuitRec
        CAPTURE_Stop()
      Else
        MessageRequester("Error", "Unable to initialize GUI.") ;chris319 6/7/12
        End
      EndIf
User avatar
Derren
Enthusiast
Enthusiast
Posts: 313
Joined: Sat Jul 23, 2011 1:13 am
Location: Germany

Re: Random Number Generator

Post by Derren »

Condition 1 can be met with a sigmoid function, like the logistic function

https://en.wikipedia.org/wiki/Sigmoid_function
https://en.wikipedia.org/wiki/Logistic_function

Basically it translates any number from minus to plus infinity into a number in a defined range.

So any data you get from your audio signal can be squashed into a 1-80 range.
chris319
Enthusiast
Enthusiast
Posts: 782
Joined: Mon Oct 24, 2005 1:05 pm

Re: Random Number Generator

Post by chris319 »

Or I could do as follows, assuming 16-bit sound samples:

Code: Select all

sample.u = ; get sample

flsample.f = sample / 65535

;we  have a sample from 0.0 to 1.0

sample = int(flsample * 80 + 0.5)

Post Reply