PureAudioRecorder, record 'Line In' audio, playback, save

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

PureAudioRecorder, record 'Line In' audio, playback, save

Post by BasicallyPure »

PureAudioRecorder was inspired by this topic posted by Flype. http://www.purebasic.fr/english/viewtop ... d+recorder
I borrowed some of the code from that post, (mostly the API stuff that I didn't know how to do).
Thanks to everyone that contributed to Flype's audio recorder.

I have included several features in PureAudioRecorder that you may find useful.
With PureAudioRecorder you can record audio from your computer's audio line in jack.
You can record mono or stereo and choose from several different sample rates.
All audio is recorded with 16 bit resolution.

PureAudioRecorder provides signal level meters for right and left channels to monitor the recording levels.
The levels must be adjusted using your computer's audio mixer control or adjust the signal externally at line in.
The level meters do not function during playback.

Recorded audio can be played back and saved to disk as a wave file.
I have included a pause button that may be used for either recording or playback.
A track bar gadget is provided to change the playback position.

It is possible to load other wave files you may have on your drive and play them.

Image

edit 4/12/2017:
Here is a download link to the latest: Version 1.30

edit 7/5/2015:
download link to older: Version 1.28

code for the original version below.

Code: Select all

; Title:        PureAudioRecorder.pb
; Author:       BasicallyPure
; date:         5/15/2013
; PureBasic     5.11
; OS:           Windows
; License:      Free

EnableExplicit
Define version.f = 1.0

CompilerIf Not #PB_Compiler_Thread
   MessageRequester("WARNING!", "Set compiler options: 'Create threadsafe executable'")
   End
CompilerEndIf

;- constants
;{
#MainWin        = 0
#Win_Color      = $F0C9CD
#GUI_WIDTH      = 510
#GUI_HEIGHT     = 180
#StatusBar      = 0
#ToolBar        = 0
#Stat_Color_Def = $E1EDEE
#Stat_Color_Pla = $B4EDAF
#Stat_Color_Rec = $8A6BFF

#MONO   = 1
#STEREO = 2

; images
Enumeration
   #Img_Record
   #img_Play
   #Img_Stop
   #Img_Pause
   #img_Erase
EndEnumeration

; tools
Enumeration
   #Tool_Load
   #Tool_Save
   #Tool_Record
   #Tool_Play
   #Tool_Stop
   #Tool_Pause
   #Tool_Erase
EndEnumeration

; gadgets
Enumeration
   #Combo_Channel
   #Combo_Frequency
   #Combo_Device
   #Canvas_L
   #Canvas_R
   #Text_L
   #Text_R
   #Playback_Pos
EndEnumeration

; timers
Enumeration 1
   #PlaybackTimer
   #MeterTimer
EndEnumeration

; Constants used to configure tool buttons for various recorder conditions.
; bit order MSB to LSB:
; Device, Frequency, Channel, Erase, Pause, Stop, Play, Record, Save, Open
; 1=enabled, 0=disabled
#TB_State_1 = %1110000101 ; initial (erased)
#TB_State_2 = %0000110000 ; recording
#TB_State_3 = %0001001010 ; stop
#TB_State_4 = %0000110000 ; playback

;}

;- procedure declarations
;{
Declare INIT_GUI()
Declare GUI_CALLBACK(hWnd.l, Msg.l, wParam.l, lParam.l)
Declare DRAW_CUSTOM_BUTTONS()
Declare TB_COMBO(Id, x, y, w, h)
Declare RECORD_START()
Declare PAUSE()
Declare STOP()
Declare WRITE_HEADER(outID)
Declare LOAD_AUDIO()
Declare SAVE_AUDIO()
Declare PLAY_AUDIO()
Declare POSITION_PLAYBACK()
Declare ERASE_AUDIO()
Declare ASK(querry.s)
Declare CONFIG_BUTTONS(pattern)
Declare LOAD_CONFIG()
Declare SAVE_CONFIG()
Declare GET_CAPTURE_DEVICES(gadId.l)
Declare INIT_CAPTURE()
Declare CAPTURE_STOP()
Declare PLAYBACK_UPDATE()
Declare AUDIO_LOOP(null)
Declare METER_LOOP(null)
Declare VERIFY(result, text.s)
;}

;- structures
;{
Structure meter
   h.i    ; height
   w.i    ; width
   x.i    ; x location
   y.i[2] ; y location
EndStructure

Structure CONFIG
   format.WAVEFORMATEX ; Capturing WaveFormatEx
   Begin.l             ; Start time reference
   size.l              ; Wave buffer size
   buffer.l            ; Wave buffer pointer
   wave.l              ; Address of waveform-audio input device
   recorded.l          ; Number of bytes recorded
   recording.b         ; Record is runing...
   tempFile.s          ; File path and name for temporary audio data
   SavePath.s          ; Path for saving recorded audio files
   FileId.l            ; Recording FileId (#PB_Any)
   lBuf.l              ; Capturing Buffer size
   nBuf.l              ; Capturing Buffer number
   nDev.l              ; Capturing Device identifier
   MeterTimer.l        ; Meter Timer (mS)
   PlayBack.i          ; Sound number of playback sound
EndStructure
;}

;- global variables
Global mtr.meter
Global Config.CONFIG
   Config\format\cbSize = 0
   Config\format\wFormatTag = #WAVE_FORMAT_PCM
   Config\nBuf = 8
   Config\tempFile = GetTemporaryDirectory() + "tempAudio.snd"
   Config\recording = #False
   
Global winTitle.s = "Pure Audio Recorder  v. " + StrF(version,2)
Global Dim inHdr.WAVEHDR(Config\nBuf - 1)
Global hWindow  ; window handle
Global hToolBar ; toolbar handle
Global hStatus  ; status bar handle
Global threadID1, threadID2, audioSemaphore, meterSemaphore, wpa, lpa, pause, saved
Global *audioBuffer = AllocateMemory(32768)
Global m$ = "Recorded audio has not been saved." + #CRLF$
Global playback_nSPS ; number of samples per second for playback

VERIFY(InitSound(), "InitSound()")
INIT_GUI()
LOAD_CONFIG()
INIT_CAPTURE()

audioSemaphore = CreateSemaphore(0)
meterSemaphore = CreateSemaphore(0)
threadID1 = CreateThread(@AUDIO_LOOP(), #Null) ; THREAD TO HANDLE CALLBACK
threadID2 = CreateThread(@METER_LOOP(), #Null) ; THREAD TO HANDLE METER

;- EVENT LOOP
; -----------------------------------------------------------------------------------
Repeat
   Select WaitWindowEvent()
      Case #PB_Event_CloseWindow
         If config\recording = #True : STOP() : EndIf
         If saved = #False
            If ASK(m$ + "Quit anyway?") = #True : Break
            EndIf
         Else : Break : EndIf
      Case #PB_Event_Gadget
         Select EventGadget()
            Case #Combo_Channel   : INIT_CAPTURE()
            Case #Combo_Frequency : INIT_CAPTURE()
            Case #Combo_Device    : INIT_CAPTURE()
            Case #Playback_Pos    : POSITION_PLAYBACK()
         EndSelect
      Case #PB_Event_Menu
         Select EventMenu()
            Case #Tool_Load      : LOAD_AUDIO()
            Case #Tool_Save      : SAVE_AUDIO()
            Case #Tool_Record    : RECORD_START()
            Case #Tool_Play      : PLAY_AUDIO()
            Case #Tool_Stop      : STOP()
            Case #Tool_Pause     : PAUSE()
            Case #Tool_Erase     : ERASE_AUDIO()
         EndSelect
      Case #PB_Event_Timer
         Select EventTimer()
            Case #PlaybackTimer  : PLAYBACK_UPDATE()
            Case #MeterTimer     : SignalSemaphore(meterSemaphore)
         EndSelect
   EndSelect
ForEver
; -----------------------------------------------------------------------------------

CAPTURE_STOP()
If IsThread(threadID1) : KillThread(threadID1)    : EndIf
If IsThread(threadID2) : KillThread(threadID2)    : EndIf
If *audioBuffer <> 0   : FreeMemory(*audioBuffer) : EndIf
DeleteFile(Config\tempFile)
SAVE_CONFIG()

End ; -------------------------------------------------------------------------------

;- MACROS
Macro SHOW_SIZE()
   StatusBarText(#StatusBar, 0,StrF(Config\recorded / 1048576, 2) + " MB",#PB_StatusBar_Center)
EndMacro

Macro SHOW_TIME(SBF, mSEC)
   ; SBF = status bar field number
   StatusBarText(#StatusBar, SBF, Str(mSEC/60000) + " : " +
   RSet(Str(Second(mSEC/1000)), 2, "0"), #PB_StatusBar_Center)
EndMacro

Macro SHOW_PATH(text)
   StatusBarText(#StatusBar, 2,text)
EndMacro

;- PROCEDURES -

Procedure INIT_GUI()
   Protected null.w, Flags = #PB_Window_ScreenCentered | #PB_Window_SystemMenu | #PB_Window_MinimizeGadget
   hWindow = VERIFY(OpenWindow(#MainWin, 0, 0, #GUI_WIDTH, #GUI_HEIGHT, winTitle, Flags), "OpenWindow")
   
   SetWindowColor(#MainWin, #Win_Color)
   StickyWindow(#MainWin, 1)
   
   hStatus = VERIFY(CreateStatusBar(#StatusBar, WindowID(#MainWin)), "CreateStatusBar")
   AddStatusBarField( 100)
   AddStatusBarField(  80)
   AddStatusBarField(1000)
   StatusBarText(#StatusBar, 0, "", #PB_StatusBar_Center)
   StatusBarText(#StatusBar, 1, "", #PB_StatusBar_Center)
   ; remove any XP theme from StatusBar
   If OSVersion() >= #PB_OS_Windows_XP
      SetWindowTheme_(hStatus, @null, @null)
   EndIf
   SendMessage_(hStatus, #CCM_SETBKCOLOR, 0, #Stat_Color_Def) ; set StatusBar bkgnd color
   
   hToolBar = VERIFY(CreateToolBar(#ToolBar,WindowID(#MainWin)), "CreateToolBar")
   DRAW_CUSTOM_BUTTONS()
   ToolBarStandardButton(#Tool_Load, #PB_ToolBarIcon_Open)
   ToolBarStandardButton(#Tool_Save, #PB_ToolBarIcon_Save)
   ToolBarImageButton(#Tool_Record, ImageID(#img_Record))
   ToolBarImageButton(#Tool_Play , ImageID(#img_Play))
   ToolBarImageButton(#Tool_Stop , ImageID(#img_Stop))
   ToolBarImageButton(#Tool_Pause, ImageID(#Img_Pause), #PB_ToolBar_Toggle)
   ToolBarImageButton(#Tool_Erase, ImageID(#img_Erase))
   ToolBarSeparator()
   ToolBarToolTip(#ToolBar, #Tool_Record, "Record")
   ToolBarToolTip(#ToolBar, #Tool_Pause, "Pause")
   ToolBarToolTip(#ToolBar, #Tool_Play , "Play")
   ToolBarToolTip(#ToolBar, #Tool_Stop , "Stop")
   ToolBarToolTip(#ToolBar, #Tool_Load , "Load Audio")
   ToolBarToolTip(#ToolBar, #Tool_Save , "Save Audio")
   ToolBarToolTip(#ToolBar, #Tool_Erase, "Erase Buffer")
   
   TB_COMBO(#Combo_Channel, 5, 1, 60, 20)
   AddGadgetItem(#Combo_Channel, -1, "Mono")
   AddGadgetItem(#Combo_Channel, -1, "Stereo")
   SetGadgetState(#Combo_Channel, 0)
   
   TB_COMBO(#Combo_Frequency,50,1,80,20)

   AddGadgetItem(#Combo_Frequency,-1,"8.0 kHz")
   AddGadgetItem(#Combo_Frequency,-1,"11.025 kHz")
   AddGadgetItem(#Combo_Frequency,-1,"12.0 kHz")
   AddGadgetItem(#Combo_Frequency,-1,"16.0 kHz")
   AddGadgetItem(#Combo_Frequency,-1,"22.05 kHz")
   AddGadgetItem(#Combo_Frequency,-1,"24.0 kHz")
   AddGadgetItem(#Combo_Frequency,-1,"32.0 kHz")
   AddGadgetItem(#Combo_Frequency,-1,"44.1 kHz") ; default
   AddGadgetItem(#Combo_Frequency,-1,"48.0 kHz")
   SetGadgetState(#Combo_Frequency, 7)
   
   TB_COMBO(#Combo_Device,115,1,170,20)
   GET_CAPTURE_DEVICES(#Combo_Device)
   SetGadgetState(#Combo_Device,0)
   
   ; define meter size and location
   mtr\w = #GUI_WIDTH - 70
   mtr\h = 25
   mtr\x = 45
   mtr\y[0] = 10 + ToolBarHeight(#ToolBar)
   mtr\y[1] = 50 + ToolBarHeight(#ToolBar)
   
   VERIFY(CanvasGadget(#Canvas_L, mtr\x, mtr\y[0], mtr\w + 4, mtr\h + 4, #PB_Canvas_Border), "CanvasGadget")
   VERIFY(CanvasGadget(#Canvas_R, mtr\x, mtr\y[1], mtr\w + 4, mtr\h + 4, #PB_Canvas_Border), "CanvasGadget")
   
   TrackBarGadget(#Playback_Pos, 15, 15 + mtr\h + mtr\y[1], mtr\w + 32, 25, 0, 0)
   DisableGadget(#Playback_Pos, #True)
   GadgetToolTip(#Playback_Pos, "Playback Position")
   
   LoadFont(0, "Arial", 20)
   TextGadget(#Text_L, 15, mtr\y[0] - 2, 20, 32, "L")
   TextGadget(#Text_R, 15, mtr\y[1] - 2, 20, 32, "R")
   SetGadgetColor(#Text_L, #PB_Gadget_BackColor, #Win_Color)
   SetGadgetColor(#Text_R, #PB_Gadget_BackColor, #Win_Color)
   SetGadgetFont(#Text_L, FontID(0))
   SetGadgetFont(#Text_R, FontID(0))
   
   SetWindowCallback(@GUI_CALLBACK())
EndProcedure

Procedure GUI_CALLBACK(hWnd.l, Msg.l, wParam.l, lParam.l)
   If Msg = #MM_WIM_DATA
      wpa = wParam
      lpa = lParam
      SignalSemaphore(audioSemaphore)
   EndIf
   
   ProcedureReturn #PB_ProcessPureBasicEvents
EndProcedure

Procedure DRAW_CUSTOM_BUTTONS()
   
   CreateImage(#img_Record, 16, 16, 32)
   CreateImage(#img_Play  , 16, 16, 32)
   CreateImage(#img_Stop  , 16, 16, 32)
   CreateImage(#img_Pause , 16, 16, 32)
   CreateImage(#img_Erase , 16, 16, 32)
   
   StartDrawing(ImageOutput(#img_Record))
      Box(0,0,16,16,#Gray)
      DrawingMode(#PB_2DDrawing_Gradient)
      CircularGradient(8,8,14)
      FrontColor(#Blue)
      BackColor(#Red)
      Circle(7,7,6)
   StopDrawing()
   
   StartDrawing(ImageOutput(#img_Pause))
      DrawingMode(#PB_2DDrawing_Default)
      Box(0,0,16,16,#Gray)
      Box(3,3,04,10,#Yellow)
      Box(9,3,04,10,#Yellow)
   StopDrawing()
   
   StartDrawing(ImageOutput(#img_Stop))
      Box(0,0,16,16,#Gray)
      Box(3,3,10,10,#Cyan)
   StopDrawing()
   
   StartDrawing(ImageOutput(#img_Play))
      Box(0,0,16,16,#Gray)
      LineXY(03, 02, 13, 07, #Green)
      LineXY(03, 13, 13, 07, #Green)
      LineXY(03, 02, 03, 13, #Green)
      FillArea(7, 7, #Green, #Green)
   StopDrawing()
   
   LoadFont(1, "Arial", 11, #PB_Font_Bold)
   StartDrawing(ImageOutput(#img_Erase))
      DrawingFont(FontID(1))
      Box(0,0,16,16,0)
      BackColor(0)
      DrawText(3,0,"X",#Red)
   StopDrawing()
   
EndProcedure

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

Procedure RECORD_START()
   If Config\recording = #True : ProcedureReturn 0 : EndIf
   
   Config\recorded = #Null
   Config\FileId = CreateFile(#PB_Any,Config\tempFile)
   
   If Config\FileId
      WRITE_HEADER(Config\FileId) ; add wave header to file
      CONFIG_BUTTONS(#TB_State_2)
      Config\Begin = ElapsedMilliseconds()
      Config\recording = #True
      SendMessage_(hStatus, #CCM_SETBKCOLOR, 0, #Stat_Color_Rec)
      ProcedureReturn 1
   Else
      MessageRequester("Can't create file", Config\tempFile)
      ProcedureReturn 0
   EndIf
   
EndProcedure

Procedure PAUSE()
   ; used for both record and playback
   
   pause = GetToolBarButtonState(#ToolBar,#Tool_Pause)
   
   If Config\PlayBack <> 0 ; pause playback
      If pause
         PauseSound(Config\PlayBack)
      Else
         ResumeSound(Config\PlayBack)
      EndIf
      ProcedureReturn
   EndIf
   
   If Config\recording = #True ; pause recording
      Config\Begin = ElapsedMilliseconds() - Config\Begin
   Else
      SetToolBarButtonState(#ToolBar,#Tool_Pause, 0)
   EndIf

EndProcedure

Procedure STOP()
   ; used for both record and playback
   
   If Config\PlayBack <> 0 ; stop playback
      RemoveWindowTimer(#MainWin, #PlaybackTimer)
      FreeSound(Config\PlayBack)
      Config\PlayBack = 0
      SetGadgetState(#Playback_Pos, 0)
      SetToolBarButtonState(#ToolBar, #Tool_Pause, 0)
      pause = #False
      CONFIG_BUTTONS(#TB_State_3)
      SHOW_SIZE()
      ProcedureReturn
   EndIf
   
   If Config\recording = #True ; stop recording
      Config\recording = #False
      
      ; update wave header with final size
      FileSeek(Config\FileId, 4) ; position for chunk size location
      WriteLong(Config\FileId, Config\recorded + 36) ; chunk size
      FileSeek(Config\FileId, 40) ; position for byte count
      WriteLong(Config\FileId, Config\recorded) ; audio byte count
      CloseFile(Config\FileId)
      
      SendMessage_(hStatus, #CCM_SETBKCOLOR, 0, #Stat_Color_Pla)
      SetToolBarButtonState(#ToolBar, #Tool_Pause, 0)
      DisableGadget(#Playback_Pos, #False)
      CONFIG_BUTTONS(#TB_State_3)
      SHOW_PATH("unnamed.wav")
      
      If pause
         Config\Begin = ElapsedMilliseconds() - Config\Begin
         pause = #False
      EndIf
      
      SetGadgetAttribute(#Playback_Pos, #PB_TrackBar_Maximum, (ElapsedMilliseconds() - Config\Begin)/1000)
      saved = #False
      
   EndIf
   
EndProcedure

Procedure WRITE_HEADER(file.i)
   ; construct wave header at begining of file
   Protected b.W = Config\format\wBitsPerSample
   Protected c.W = Config\format\nChannels
   Protected h.l = Config\format\nSamplesPerSec
   ; final values of buffLen and chunkSize are not known at this time.
   ; they must be updated when recording is stopped and final size can be determined.
   Protected buffLen.q   = Config\recorded ; placeholder for number of audio bytes
   Protected chunkSize.q = Config\recorded + 36 ; placeholder for final chunksize.
   
   FileSeek(file, 0)
   
   ; Master chunk (12 bytes)
   WriteString(file, "RIFF")  ; 4 bytes -- chunk ID
   WriteLong(file, chunkSize) ; 4 bytes -- total file size - 8
   WriteString(file, "WAVE")  ; 4 bytes -- wave ID
   
   ; Format chunk (24 bytes)
   WriteString(file, "fmt ")  ; 4 bytes -- chunk ID
   WriteLong(file, 16)        ; 4 bytes -- this chunk size
   WriteWord(file, 1)         ; 2 bytes -- PCM flag
   WriteWord(file, c)         ; 2 bytes -- number of channels
   WriteLong(file, h)         ; 4 bytes -- samples per second
   WriteLong(file, c*h*(b/8)) ; 4 bytes -- bytes per second
   WriteWord(file, c * (b/8)) ; 2 bytes -- blockalign
   WriteWord(file, b)         ; 2 bytes -- bits per sample
   
   ; Begin data chunk (8 bytes)
   WriteString(file, "data")  ; 4 bytes -- chunk ID
   WriteLong(file, buffLen)   ; 4 bytes -- number of audio bytes
EndProcedure

Procedure LOAD_AUDIO()
   Protected file.s, inId.i, waveID.s, samplesPerSecond.l
   Protected blockAlign.w, nAudioBytes.l, PCMflag.w, seconds.i
   Static pattern.s = "Wave (*.wav)|*.wav"
   
   file = OpenFileRequester("Select wave file", Config\SavePath, pattern, 0)
   If file
      inId = ReadFile(#PB_Any, file)
      
      If inId
         FileSeek(inId, 8)  : waveID = ReadString(inId, #PB_Ascii, 4)
         FileSeek(inId, 20) : PCMflag = ReadWord(inId)
         FileSeek(inId, 24) : samplesPerSecond = ReadLong(inId)
         FileSeek(inId, 32) : blockAlign = ReadWord(inId)
         FileSeek(inId, 40) : nAudioBytes = ReadLong(inId)
         CloseFile(inId)
         
         If waveID = "WAVE" And PCMflag = 1
            If CopyFile(file, Config\tempFile)
               Config\recorded = nAudioBytes
               playback_nSPS = samplesPerSecond
               Seconds = (nAudioBytes / blockAlign) / samplesPerSecond
               SetGadgetAttribute(#Playback_Pos, #PB_TrackBar_Maximum, seconds)
               SHOW_TIME(1, Seconds * 1000)
               SHOW_SIZE()
               SendMessage_(hStatus, #CCM_SETBKCOLOR, 0, #Stat_Color_Pla)
               DisableGadget(#Playback_Pos, #False)
               CONFIG_BUTTONS(#TB_State_3)
               SHOW_PATH(GetFilePart(file))
               Config\SavePath = GetPathPart(File)
               ProcedureReturn 1
            Else
               ProcedureReturn 0
            EndIf
         Else
            MessageRequester("Error!", "File format is not PCM WAVE.")
            ProcedureReturn 0
         EndIf
      Else
         MessageRequester("Error!", "Unable to open file.")
         ProcedureReturn 0
      EndIf
      
   EndIf
EndProcedure

Procedure SAVE_AUDIO()
   Protected buffLen.q, outId.i, *pBuf
   Protected inId.i = ReadFile(#PB_Any, Config\tempFile)
   Protected ChunkSize.i, File.s
   Static pattern.s = "Wave (*.wav)|*.wav|All files|*.*"
   
   If inId = #Null
      MessageRequester("Unable to open file", Config\tempFile, #MB_ICONERROR)
      ProcedureReturn -1
   EndIf
   
   buffLen = Lof(inId)
   *pBuf = AllocateMemory(buffLen)
   If *pBuf = #Null
      MessageRequester("Error", "Unable to allocate buffer", #MB_ICONERROR)
      ProcedureReturn -1
   EndIf
   
   ReadData(inId, *pBuf, buffLen)
   CloseFile(inId)
   
   File = OpenFileRequester("Choose filename", Config\SavePath, pattern, 0)
   
   If File <> ""
      If GetFilePart(File) = ""
         File = GetPathPart(File) + "unnamed.wav"
      EndIf
      
      If GetExtensionPart(File) <> "wav"
         RTrim(File,".")
         File + ".wav"
      EndIf
      
      Config\SavePath = GetPathPart(File)
      StatusBarText(#StatusBar, 2, Config\SavePath)
   Else
      ProcedureReturn #False
   EndIf
   
   outId = CreateFile(#PB_Any, File) ; create empty file
   If outId = #Null
      MessageRequester("Error", "Unable to create file",#MB_ICONERROR)
      ProcedureReturn #False
   EndIf
   
   SHOW_PATH(GetFilePart(file))
   WriteData(outId, *pBuf, buffLen) ; this is the audio data
   CloseFile(outId)
   FreeMemory(*pBuf)
   saved = #True
   
   ProcedureReturn #True
EndProcedure

Procedure PLAY_AUDIO()
   If Config\PlayBack <> 0
      ProcedureReturn 0 
   Else
      Config\PlayBack = LoadSound(#PB_Any, Config\tempFile)
      If Config\PlayBack = 0
         ProcedureReturn 0
      Else
         DisableGadget(#Playback_Pos, #False)
         SetToolBarButtonState(#ToolBar, #Tool_Pause, 0)
         CONFIG_BUTTONS(#TB_State_4)
         
         AddWindowTimer(#MainWin, #PlaybackTimer, 1000)
         Delay(100)
         PlaySound(Config\PlayBack)
         POSITION_PLAYBACK()
      EndIf
   EndIf
   
EndProcedure

Procedure POSITION_PLAYBACK()
   Protected pos.i = GetGadgetState(#Playback_Pos)
   SHOW_TIME(0, pos * 1000)
   If Config\PlayBack
      SetSoundPosition(Config\PlayBack, pos * playback_nSPS, #PB_Sound_Frame)
   EndIf
EndProcedure

Procedure ERASE_AUDIO()
   If saved = #False
      If ASK(m$ + "Erase anyway?") = #False : ProcedureReturn : EndIf
   EndIf
   
   If Config\PlayBack <> 0
      FreeSound(Config\PlayBack)
      Config\PlayBack = 0
      RemoveWindowTimer(#MainWin, #PlaybackTimer)
   EndIf
   
   DeleteFile(Config\tempFile)
   Config\recorded = #Null
   
   SetGadgetState(#Playback_Pos, 0)
   DisableGadget(#Playback_Pos, #True)
   SetToolBarButtonState(#ToolBar,#Tool_Pause, 0)
   SendMessage_(hStatus, #CCM_SETBKCOLOR, 0, #Stat_Color_Def)
   
   CONFIG_BUTTONS(#TB_State_1)
   SHOW_SIZE()
   SHOW_TIME(1, 0)
   SHOW_PATH(Config\SavePath)
   playback_nSPS = Config\format\nSamplesPerSec
   saved = #True
EndProcedure

Procedure ASK(querry.s)
   If MessageRequester("WARNING!", querry, #PB_MessageRequester_YesNo) = #PB_MessageRequester_Yes
      ProcedureReturn #True
   Else
      ProcedureReturn #False
   EndIf
EndProcedure

Procedure CONFIG_BUTTONS(pattern)
   Protected tool.i, gadget.i
   
   For tool = #Tool_Load To #Tool_Erase
      DisableToolBarButton(#ToolBar, tool, (pattern & 1) ! 1)
      pattern >> 1
   Next tool
   
   For gadget = #Combo_Channel To #Combo_Device
      DisableGadget(gadget, (pattern & 1) ! 1)
      pattern >> 1
   Next gadget
   
EndProcedure

Procedure LOAD_CONFIG()
   OpenPreferences(GetHomeDirectory() + "PureAudioRecorder.prefs")
   
   PreferenceGroup("Files")
   Config\SavePath = ReadPreferenceString("FileName", GetHomeDirectory())
   
   PreferenceGroup("Capture")
   Config\nDev = ReadPreferenceLong("Device",0)
   Config\format\nChannels = ReadPreferenceLong("Channel", 1)
   Config\format\nSamplesPerSec = ReadPreferenceLong("Frequency",44100)
   Config\format\wBitsPerSample = 16
   
   ClosePreferences()
   
   StatusBarText(0,2,Config\SavePath)
   SetGadgetState(#Combo_Device, Config\nDev)
   SetGadgetState(#Combo_Channel, Config\format\nChannels - 1)
   
   Select Config\format\nSamplesPerSec
      Case  8000: SetGadgetState(#Combo_Frequency, 0)
      Case 11025: SetGadgetState(#Combo_Frequency, 1)
      Case 12000: SetGadgetState(#Combo_Frequency, 2)
      Case 16000: SetGadgetState(#Combo_Frequency, 3)
      Case 22050: SetGadgetState(#Combo_Frequency, 4)
      Case 24000: SetGadgetState(#Combo_Frequency, 5)
      Case 32000: SetGadgetState(#Combo_Frequency, 6)
      Case 44100: SetGadgetState(#Combo_Frequency, 7)
      Case 48000: SetGadgetState(#Combo_Frequency, 8)
   EndSelect
   
EndProcedure

Procedure SAVE_CONFIG()
   
   If CreatePreferences(GetHomeDirectory() + "PureAudioRecorder.prefs", #PB_Preference_GroupSeparator)
      
      PreferenceGroup("Files")
      WritePreferenceString("FileName",Config\SavePath)
      
      PreferenceGroup("Capture")
      WritePreferenceLong("Device",Config\nDev)
      WritePreferenceLong("Channel",Config\format\nChannels)
      WritePreferenceLong("Frequency",Config\format\nSamplesPerSec)
      
      ClosePreferences()
      ProcedureReturn #True
      
   EndIf

EndProcedure

Procedure GET_CAPTURE_DEVICES(gadId.l)
   Protected NumDevices.i = waveInGetNumDevs_()
   Protected DeviceID.i, Result.i
   Protected Caps.WAVEINCAPS
   
   If NumDevices
      For DeviceID = #WAVE_MAPPER To NumDevices - 1
         Result = waveInGetDevCaps_(DeviceID,@Caps, SizeOf(WAVEINCAPS))
         If Result = #MMSYSERR_NOERROR
            AddGadgetItem(gadId, -1, PeekS(@Caps\szPname, #MAXPNAMELEN))
         EndIf
      Next
   EndIf
   
EndProcedure

Procedure INIT_CAPTURE()
   Protected state, i, lBufopt.f
   
   If Config\recording = #True : ProcedureReturn : EndIf
   
   CAPTURE_STOP()
   CONFIG_BUTTONS(#TB_State_1)
   SHOW_SIZE()
   SHOW_TIME(1, 0)

   ; GET USER OPTIONS
   Config\nDev = GetGadgetState(#Combo_Device)
   
   state = GetGadgetState(#Combo_Channel)
   Select state
      Case 0 : Config\format\nChannels = #MONO
      Case 1 : Config\format\nChannels = #STEREO
   EndSelect
   
   state = GetGadgetState(#Combo_Frequency)
   Select state
      Case 0 : Config\format\nSamplesPerSec =  8000
      Case 1 : Config\format\nSamplesPerSec = 11025
      Case 2 : Config\format\nSamplesPerSec = 12000
      Case 3 : Config\format\nSamplesPerSec = 16000
      Case 4 : Config\format\nSamplesPerSec = 22050
      Case 5 : Config\format\nSamplesPerSec = 24000
      Case 6 : Config\format\nSamplesPerSec = 32000
      Case 7 : Config\format\nSamplesPerSec = 44100
      Case 8 : Config\format\nSamplesPerSec = 48000
   EndSelect
   
   playback_nSPS = Config\format\nSamplesPerSec
   Config\MeterTimer = 85 ; approximate meter refresh rate in milliseconds
   
   With Config\format
      ; calculate optimum buffer size using approximate MeterTimer rate
      lBufopt = 2 * \nSamplesPerSec * (Config\MeterTimer / 1000) * \nChannels
      ; actual buffer size must be a power of 2
      ; this calculates the next larger power of 2 from lBufopt
      Config\lBuf = Pow(2, Int(Log(lBufopt) / Log(2)) + 1)
      ; recalculate MeterTimer to optimum using actual buffer size
      Config\MeterTimer = 1000.0 * Config\lBuf / (2 * \nSamplesPerSec * \nChannels) + 1
      
      \nBlockAlign = (\nChannels * \wBitsPerSample) / 8
      \nAvgBytesPerSec = \nSamplesPerSec * \nBlockAlign
      \cbSize = 0
   EndWith
   
   If #MMSYSERR_NOERROR = waveInOpen_(@Config\wave,#WAVE_MAPPER+Config\nDev,@Config\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)
         AddWindowTimer(#MainWin, #MeterTimer, Config\MeterTimer)
      EndIf
   EndIf
   
   If IsThread(threadID1) <> 0 : ResumeThread(threadID1) : EndIf
   If IsThread(threadID2) <> 0 : ResumeThread(threadID2) : EndIf
   
   saved = #True
EndProcedure

Procedure CAPTURE_STOP()
   Protected i
   If IsThread(threadID1) <> 0 : PauseThread(threadID1) : EndIf
   If IsThread(threadID2) <> 0 : PauseThread(threadID2) : EndIf
   
   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
   
   KillTimer_(hWindow, #MeterTimer)
   
EndProcedure

Procedure PLAYBACK_UPDATE()
   Protected pos.i
   If pause = #False
      pos = GetSoundPosition(Config\PlayBack, #PB_Sound_Frame)
      If pos = 0 : STOP() : EndIf
      pos / playback_nSPS
      SetGadgetState(#Playback_Pos, pos)
      SHOW_TIME(0, pos * 1000)
   EndIf
EndProcedure

Procedure AUDIO_LOOP(null)
   Protected *hWave.WAVEHDR
   Static upCounter.f
   Repeat
      WaitSemaphore(audioSemaphore)
      
      *hWave = lpa
      Config\buffer = *hWave\lpData
      Config\size = *hWave\dwBytesRecorded
      
      waveInAddBuffer_(wpa, *hWave, SizeOf(WAVEHDR))
      
      If Config\buffer <> 0
         CopyMemory(Config\buffer, *audioBuffer, Config\size)
         If pause = #False
            If Config\recording = #True
               Config\recorded + Config\size
               
               WriteData(Config\FileId, *audioBuffer, Config\size) ; file append
               
               ; reduce status bar flicker by less frequent updates
               If upCounter > 0 : upCounter - 1
               Else : upCounter = Config\MeterTimer / 10.0
                  SHOW_SIZE()
                  SHOW_TIME(1, (ElapsedMilliseconds() - Config\Begin))
               EndIf
               
            EndIf
         EndIf
      EndIf
   ForEver
EndProcedure

Procedure METER_LOOP(null)
   Protected maxL, maxR, ch, i, amp, inc, limit, value.w
   
   Repeat
      WaitSemaphore(meterSemaphore)
      If Config\PlayBack = 0
         ; UPDATE METER
         inc = Config\format\nChannels * 2
         limit = Config\lBuf - inc
         maxL = 0 : maxR = 0 : i = 0
         
         For ch = 1 To Config\format\nChannels
            Repeat
               value = PeekW(*audioBuffer + i)
               If value < 0 : value = -value  : EndIf
               If value > maxR : maxR = value : EndIf
               i + inc
            Until i > limit
            
            If ch = 1 : Swap maxL, maxR : EndIf
            i = 2
            limit + 2
         Next ch
      Else
         maxL = 0 : maxR = 0
      EndIf
      
      amp = mtr\w * (maxL / 32767.0)
      
      For ch = #Canvas_L To #Canvas_R
         StartDrawing(CanvasOutput(ch))
            DrawingMode(#PB_2DDrawing_Gradient)
            BackColor($00FFFF)
            GradientColor(0.33, $00FF00)
            GradientColor(0.66, $00FF80)
            FrontColor($0000FF)
            LinearGradient(0, 0, mtr\w, 0)
            Box(0, 0 ,amp, mtr\h)
            DrawingMode(#PB_2DDrawing_Default)
            Box(amp, 0, mtr\w ,mtr\h, 0)
         StopDrawing()
         amp = mtr\w * (maxR / 32767.0)
      Next ch
   ForEver
   
EndProcedure

Procedure VERIFY(result, text.s)
   If result = 0
      MessageRequester("Fatal Error!", "Unable to execute --> " + text)
      End
   EndIf
   ProcedureReturn result
EndProcedure

Last edited by BasicallyPure on Thu Apr 13, 2017 2:45 am, edited 4 times in total.
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: PureAudioRecorder, record 'Line In' audio, playback, sav

Post by Joris »

Nice.

Thanks BasicallyPure.
Yeah I know, but keep in mind ... Leonardo da Vinci was also an autodidact.
User avatar
electrochrisso
Addict
Addict
Posts: 980
Joined: Mon May 14, 2007 2:13 am
Location: Darling River

Re: PureAudioRecorder, record 'Line In' audio, playback, sav

Post by electrochrisso »

This works great BP, thanks for sharing. :D
PureBasic! Purely one of the best 8)
Simo_na
Enthusiast
Enthusiast
Posts: 177
Joined: Sun Mar 03, 2013 9:01 am

Re: PureAudioRecorder, record 'Line In' audio, playback, sav

Post by Simo_na »

Thanks for sharing

:

Could you make available only the routine that fetches data logging levels (L and R ) and provides the two peak values in two separate variables ?

eg:

;get MaxL
;return amp_L
;get MaxR
;return amp_R

i want to send these two variables @ your : Meter Gadget.pbi

http://purebasic.fr/english/viewtopic.p ... 47#p409247

:)
User avatar
utopiomania
Addict
Addict
Posts: 1655
Joined: Tue May 10, 2005 10:00 pm
Location: Norway

Re: PureAudioRecorder, record 'Line In' audio, playback, sav

Post by utopiomania »

Thanks for sharing, works on Win 8 64 bit :) Cool
User avatar
BasicallyPure
Enthusiast
Enthusiast
Posts: 536
Joined: Thu Mar 24, 2011 12:40 am
Location: Iowa, USA

Re: PureAudioRecorder, record 'Line In' audio, playback, sav

Post by BasicallyPure »

Simo_na wrote:Could you make available only the routine that fetches data logging levels (L and R ) and provides the two peak values in two separate variables ?
Here is a working example using meter gadgets to monitor line in audio.

Image

Code: Select all

; Line_in_monitor
; by BasicallyPure
; 5.26.2013
; Windows only

;http://purebasic.fr/english/viewtopic.php?p=409247#p409247
IncludeFile"MeterGadget.pbi"

EnableExplicit

#Title = "Line_In_Monitor"
#MainWin = 0
#MainMenu = 0

Enumeration ;menu items
   #MenuItemExit = 0
   #Mono = 1
   #Stereo = 2
EndEnumeration

Enumeration ; gadgets
   #Meter_Left
   #Meter_Right
   #ChkBox_L
   #ChkBox_R
EndEnumeration

Global hWindow, threadID, threadID2, audioSemaphore, meterSemaphore
Global wpa.l, lpa.l
Global lBuf, nDev, size, nBuf = 8
Global *buffer, *wave, *audioBuffer = AllocateMemory(32768)
Global format.WAVEFORMATEX
Global Dim inHdr.WAVEHDR(nBuf - 1)

Procedure Verify(result, text.s)
   If result = 0
      MessageRequester("Fatal Error!", "Unable to execute --> " + text)
      End
   EndIf
   ProcedureReturn result
EndProcedure

Procedure GUI_CALLBACK(hWnd.l, Msg.l, wParam.l, lParam.l)
   
   If Msg = #MM_WIM_DATA
      wpa = wParam
      lpa = lParam
      SignalSemaphore(audioSemaphore) ; trigger for AUDIO_LOOP()
   EndIf
   
   ProcedureReturn #PB_ProcessPureBasicEvents
EndProcedure

Procedure INIT_GUI()
   Protected WinWidth  = 540
   Protected WinHeight = 220 + MenuHeight()
   Protected  Flags = #PB_Window_ScreenCentered | #PB_Window_SystemMenu | #PB_Window_MinimizeGadget
   hWindow = Verify(OpenWindow(#MainWin, 0, 0, WinWidth, WinHeight, #Title, Flags), "OpenWindow")
   Verify(CreateMenu(#MainMenu, WindowID(#MainWin)), "CreateMenu")
   MenuTitle("File")
      MenuItem(#MenuItemExit, "Exit")
   MenuTitle("Config")
      MenuItem(#Mono, "Mono")
      MenuItem(#stereo, "Stereo")
      SetMenuItemState(#MainMenu, #Stereo, #True)
   
   Flags = #MTR_STYLE_VU | #MTR_STYLE_LED
   Verify(MeterGadget(#Meter_Left, 10, 10, 250, "Left", Flags), "MeterGadget")
   Verify(MeterGadget(#Meter_Right, 280, 10, 250, "Right", Flags), "MeterGadget")
   
   CheckBoxGadget(#ChkBox_L, 010, 185, 120, 25, "Enable Smoothing")
   CheckBoxGadget(#ChkBox_R, 280, 185, 120, 25, "Enable Smoothing")
   
   SetWindowCallback(@GUI_CALLBACK())
EndProcedure

Procedure INIT_CAPTURE()
   Protected i, lBufopt.f, refreshTime.i, result
   
   If IsThread(threadID) <> 0 : PauseThread(threadID) : EndIf
   
   refreshTime = 85 ; approximate meter refresh rate in milliseconds
   
   With format
      \nChannels = GetMenuItemState(#MainMenu, #Stereo) + 1
      \nSamplesPerSec = 11025
      \wFormatTag = #WAVE_FORMAT_PCM
      \wBitsPerSample = 16
      \cbSize = 0
      \nBlockAlign = (\nChannels * \wBitsPerSample) / 8
      \nAvgBytesPerSec = \nSamplesPerSec * \nBlockAlign
   
      ; calculate optimum buffer size using approximate refreshTime
      lBufopt = 2 * \nSamplesPerSec * (refreshTime / 1000) * \nChannels
      ; actual buffer size must be a power of 2
      ; this calculates the next larger power of 2 from lBufopt
      lBuf = Pow(2, Int(Log(lBufopt) / Log(2)) + 1)
   EndWith
   
   If #MMSYSERR_NOERROR = waveInOpen_(@*wave,#WAVE_MAPPER,@format,hWindow,#Null,#CALLBACK_WINDOW|#WAVE_FORMAT_DIRECT)
      For i = 0 To nBuf - 1
         inHdr(i)\lpData = AllocateMemory(lBuf)
         inHdr(i)\dwBufferLength = lBuf
         waveInPrepareHeader_(*wave, inHdr(i), SizeOf(WAVEHDR))
         waveInAddBuffer_(*wave, inHdr(i), SizeOf(WAVEHDR))
      Next
      
      If waveInStart_(*wave) : Verify(#False, "waveInStart") : EndIf
   EndIf
   
   If IsThread(threadID) <> 0 : ResumeThread(threadID) : EndIf
   
EndProcedure

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

Procedure EVENT_LOOP()
   Protected Quit = #False
   
   Repeat
      Select WaitWindowEvent()
         Case #PB_Event_CloseWindow
            Quit = #True
            
         Case #PB_Event_Menu
            Select EventMenu()
               Case #MenuItemExit : Quit = #True
               Case #Mono
                  SetMenuItemState(#MainMenu, #Mono, #True)
                  SetMenuItemState(#MainMenu, #Stereo, #False)
                  CAPTURE_STOP() : INIT_CAPTURE()
               Case #Stereo
                  SetMenuItemState(#MainMenu, #Mono, #False)
                  SetMenuItemState(#MainMenu, #Stereo, #True)
                  CAPTURE_STOP() : INIT_CAPTURE()
            EndSelect
            
         Case #PB_Event_Gadget
            Select EventGadget()
               Case #ChkBox_L
                  If GetGadgetState(#ChkBox_L) : SetMeterSmoothing(#Meter_Left, 8)
                  Else : SetMeterSmoothing(#Meter_Left, 0)
                  EndIf
               Case #ChkBox_R
                  If GetGadgetState(#ChkBox_R) : SetMeterSmoothing(#Meter_Right, 8)
                  Else : SetMeterSmoothing(#Meter_Right, 0)
                  EndIf
            EndSelect
         
      EndSelect
      
   Until Quit = #True
   
EndProcedure

Procedure METER_REFRESH()
   Protected maxL, maxR, ch, i, amp, inc, limit, value.w
   
      inc = format\nChannels * 2
      limit = lBuf - inc
      maxL = 0 : maxR = 0 : i = 0
      
      For ch = 1 To format\nChannels
         Repeat
            value = PeekW(*audioBuffer + i)
            If value < 0 : value = -value  : EndIf
            If value > maxR : maxR = value : EndIf
            i + inc
         Until i > limit
         
         If ch = 1 : Swap maxL, maxR : EndIf
         i = 2
         limit + 2
      Next ch
      maxL * 100 / 32767 + 1
      maxR * 100 / 32767 + 1
      SetMeterState(#Meter_Left, maxL)
      SetMeterState(#Meter_Right, maxR)
   
EndProcedure

Procedure AUDIO_LOOP(null)
   Protected *hWave.WAVEHDR
   Repeat
      WaitSemaphore(audioSemaphore)
      
      *hWave = lpa
      *buffer = *hWave\lpData
      size = *hWave\dwBytesRecorded
      
      waveInAddBuffer_(wpa, *hWave, SizeOf(WAVEHDR))
      
      If *buffer <> 0
         CopyMemory(*buffer, *audioBuffer, size)
      EndIf
      
      METER_REFRESH()
   ForEver
EndProcedure

audioSemaphore = CreateSemaphore(0)
threadID = CreateThread(@AUDIO_LOOP(), #Null) ; THREAD TO HANDLE CALLBACK

INIT_GUI()
INIT_CAPTURE()
EVENT_LOOP()

End
Last edited by BasicallyPure on Mon May 27, 2013 7:16 pm, edited 1 time in total.
BasicallyPure
Until you know everything you know nothing, all you have is what you believe.
Simo_na
Enthusiast
Enthusiast
Posts: 177
Joined: Sun Mar 03, 2013 9:01 am

Re: PureAudioRecorder, record 'Line In' audio, playback, sav

Post by Simo_na »

Too good, thank you very much!!!!!!!!!!!!!
User avatar
utopiomania
Addict
Addict
Posts: 1655
Joined: Tue May 10, 2005 10:00 pm
Location: Norway

Re: PureAudioRecorder, record 'Line In' audio, playback, sav

Post by utopiomania »

Thanks for sharing! Is it possible to modify it to record 'What you hear' ? Also, is it possible to
modify the recording level to automatic level control? And also to display the replay audio level,
just like a cassette deck.... phew :mrgreen:
User avatar
BasicallyPure
Enthusiast
Enthusiast
Posts: 536
Joined: Thu Mar 24, 2011 12:40 am
Location: Iowa, USA

Re: PureAudioRecorder, record 'Line In' audio, playback, sav

Post by BasicallyPure »

@utopiomania

Those are all nice requests.
Unfortunately I don't know how to do any of them.
utopiomania wrote:Is it possible to modify it to record 'What you hear' ?
I think that a hardware limitation prevents this and also possibly the operating system.
I used to have a sound card and driver that would allow you to select 'what you hear' as the record source.
I was using windows XP at the time. When I got a new PC with windows 7 the sound card manufacturer did not have a compatible driver for windows 7. I suspect that now sound card manufacturers are discouraged/prevented from offering that feature. Because of copy protection issues? If it is possible to record 'what you hear' with the proper code I would be interested in that too. I miss having that option.
utopiomania wrote:Also, is it possible to modify the recording level to automatic level control?
With the proper code I would say 'yes'.
Simo_na wrote:And also to display the replay audio level, just like a cassette deck
I would say that is a definite 'yes'. The problem is, I am using native PureBasic commands for the playback. That doesn't give me access to the data stream. I'm sure it could be done with API but as stated in my first post, all of the API code here is 'borrowed' from someone else's code. My knowledge of API is too limited at this time.

BP
BasicallyPure
Until you know everything you know nothing, all you have is what you believe.
User avatar
utopiomania
Addict
Addict
Posts: 1655
Joined: Tue May 10, 2005 10:00 pm
Location: Norway

Re: PureAudioRecorder, record 'Line In' audio, playback, sav

Post by utopiomania »

Ok, thanks for sharing, great code :)

I use Replay Music 5 to record from Spotify on win 7 / 8, with input set to 'Windows Audio Driver',
other programs usually have a 'What You Hear' option, so its possible, and If I find out how, I'll share.
RASHAD
PureBasic Expert
PureBasic Expert
Posts: 4663
Joined: Sun Apr 12, 2009 6:27 am

Re: PureAudioRecorder, record 'Line In' audio, playback, sav

Post by RASHAD »

Hi BP
Nice code indeed
As I understood from utopiomania comment, next code will assign the active
mixer in and out so you can make it the default from the beginning

Code: Select all

no_mixer=mixerGetNumDevs_()
For x=0 To no_mixer-1
  mixerGetDevCaps_(x,mx.MIXERCAPS,SizeOf(mx))
  For name = 0 To 31
    name$=name$+Chr(mx\szPname[name])     
  Next
  Debug name$
  name$=""
Next
Egypt my love
User avatar
utopiomania
Addict
Addict
Posts: 1655
Joined: Tue May 10, 2005 10:00 pm
Location: Norway

Re: PureAudioRecorder, record 'Line In' audio, playback, sav

Post by utopiomania »

@RASHAD, your code shows 'microphone, speaker', Replay music shows 'windows audio driver, audio driver,
microphone, primary audio driver for recording'

It is possible that 'what you hear' in other programs is the same as the 'speaker' driver listed in your code?
RASHAD
PureBasic Expert
PureBasic Expert
Posts: 4663
Joined: Sun Apr 12, 2009 6:27 am

Re: PureAudioRecorder, record 'Line In' audio, playback, sav

Post by RASHAD »

Hi utopiomania
I think yes
It gives you the active drivers
So Speaker = What you hear (output)
microphone = (Input)

BP code is recording from the output(Speaker in your case)
Egypt my love
Zach
Addict
Addict
Posts: 1656
Joined: Sun Dec 12, 2010 12:36 am
Location: Somewhere in the midwest
Contact:

Re: PureAudioRecorder, record 'Line In' audio, playback, sav

Post by Zach »

It's definitely possible to expose "What u hear" for recording.

I can do it with Dxtory to get Stereo sound, and use it when I can't record multi-channel audio from a particular game.
Image
User avatar
BasicallyPure
Enthusiast
Enthusiast
Posts: 536
Joined: Thu Mar 24, 2011 12:40 am
Location: Iowa, USA

Re: PureAudioRecorder, record 'Line In' audio, playback, sav

Post by BasicallyPure »

I have just found a gold nugget. :!:
Take a look at this link.

http://www.sevenforums.com/tutorials/20 ... nable.html

It gives instructions on how to unhide and enable recording options.
It worked for me! :D
I never would have found these settings on my own.
Now I can record 'what you hear' audio using PureAudioRecorder.
No changes to my code were needed.
I successfully recorded streaming audio after following the instructions.
My PC has the RealTek on board audio device and I am using Windows 7.

BP
BasicallyPure
Until you know everything you know nothing, all you have is what you believe.
Post Reply