Capturing Audio [COMPLETE SOURCE]

Share your advanced PureBasic knowledge/code with the community.
delta69
User
User
Posts: 15
Joined: Fri Apr 25, 2003 10:56 pm

Post by delta69 »

Hello,

Very nice job !

In my case, I had to add the Waveformatex structure to the previous code to get it compiled :

Code: Select all

Structure WAVEFORMATEX 
  wFormatTag.w 
  nChannels.w 
  nSamplesPerSec.l 
  nAvgBytesPerSec.l 
  nBlockAlign.w 
  wBitsPerSample.w 
  cbSize.w 
EndStructure 
Regards,
Nicolas
chris319
Enthusiast
Enthusiast
Posts: 782
Joined: Mon Oct 24, 2005 1:05 pm

Post by chris319 »

I don't know if anyone is still interested in this program, but here it is with some enhancements:

- Enlarged meter display, changed colors
- Added variable "scale" which is rec\w divided by full-scale sample value (32767 for 16 bits)
- Changed variable name to "sample" for audio samples
- Added variable "SOUND_NBYTES" which is equal to #SOUND_NBITS / 8
- Expanded sound buffer size to 1536
- In CAPTURE_Draw, resets "max" to zero before sweep of sound buffer
- Changed record buffer offset calculation to increment by SOUND_NBYTES (for 16-bit samples)
- Rewrite of record buffer addressing
- Frees memory allocated to record buffers

Code: Select all

;Corrected for current version of PB (3.93) (Hroudtwolf)
;Miscellaneous improvements (chris319)

;***********************************************************************************
;- DECLARATIONS                                ; SOUND CAPTURE, Flype (26-juil-2002)
;***********************************************************************************


Declare CAPTURE_Read( hWaveIn.l, lpWaveHdr.l )
Declare CAPTURE_Error( err.l )
Declare CAPTURE_Start()
Declare CAPTURE_Stop()
Declare CAPTURE_Draw()
Declare CAPTURE_Now()

Declare GUI_CallBack( hWnd.l, Message.l, wParam.l, lParam.l )
Declare GUI_Button( id.l, ico.l, tip.s )
Declare GUI_Init()
Declare GUI_Main()
Declare GUI_Resize()

Declare FILE_Recording( state.b )
Declare FILE_Create()
Declare FILE_Append()
Declare FILE_Close()


;***********************************************************************************
;- INIT CONFIGURABLE CONSTANTS
;***********************************************************************************


#SOUND_NCHANNELS   = 1      ; This example only supports Mono
#SOUND_NBITS       = 16     ; This example only supports 16 bits
#SOUND_NHERTZ      = 48000  ; Try 8000, 11050, 22100, 44100...
#SOUND_NBYTES      = 2

#HEADROOM          = 9      ; dB of headroom before clipping

#BUFFER_NUM        = 8      ; Number of buffers for capture
#BUFFER_SIZE       = 1536   ; Size of each buffer, should be x2 in Stereo
#BUFFER_TICK       = 10     ; Wave redraw delay : SetTimer_ in CAPTURE_Start()


;***********************************************************************************
;- INIT CONSTANTS
;***********************************************************************************



#gadRecord   =  0
#gadStop     =  1
#gadMode     =  2

#StatusBar   =  0
#StatusTime  =  0
#StatusInfo  =  1
#StatusFile  =  2

#COLOR_RECBACK  = $000050
#COLOR_RECLINE  = $000035
#COLOR_RECWAVE  = $1010E0
;#COLOR_CAPBACK  = $004900
#COLOR_CAPBACK  = $800000

#COLOR_CAPLINE  = $004000
#COLOR_CAPWAVE  = $20E020
#COLOR_VOLUME   = $00FFFF
#COLOR_GREEN    = $00EE00
#COLOR_RED      = $2222FF

#STR_ERROR      = "Error"
#STR_STOP       = "Stop"
#STR_RECORD     = "Record"
#STR_CLOSED     = "File saved"
#STR_SAVEREQ    = "Choose a file..."
#STR_MODE       = "Display mode"
#STR_RECORDED   = " bytes recorded"
#STR_PBFILE     = "Problem while creating file"
#STR_NODEVICE   = "Audio device not found"


;***********************************************************************************
;- INIT STRUCTURES
;***********************************************************************************


Structure RECORD_INFO
  x.l          ; Left
  y.l          ; Top
  w.l          ; Width
  h.l          ; Height
  m.l          ; YMiddle
  cback.l      ; Back color
  cline.l      ; Line color
  cwave.l      ; Wave color
  size.l       ; Wave buffer size
  buffer.l     ; Wave buffer pointer
  output.l     ; WindowOutput()
  mode.b       ; Wave mode (line or plain)
  wave.l       ; Address of waveform-audio input device
  frame.b      ; Counter for back clearing
  update.b     ; If true Wave have to be redrawn
  recorded.l   ; Number of bytes recorded
  recording.b  ; Record is running...
  time.s       ; Store the time string
EndStructure

Structure WAVEFORMATEX
  wFormatTag.w
  nChannels.w
  nSamplesPerSec.l
  nAvgBytesPerSec.l
  nBlockAlign.w
  wBitsPerSample.w
  cbSize.w
EndStructure

Dim inHdr.WAVEHDR(#BUFFER_NUM)
Global inHdr, rec.RECORD_INFO, now.SYSTEMTIME
rec\time = Space(9)
Global scale.f, sample.w, max.w

;***********************************************************************************
;- PROCS CAPTURE
;***********************************************************************************

Procedure CAPTURE_Error( err.l )
  If err
    text.s = Space( #MAXERRORLENGTH )
    waveInGetErrorText_( err, text, #MAXERRORLENGTH )
    MessageRequester( #STR_ERROR, text, #MB_ICONERROR )
    CAPTURE_Stop()
    End
  EndIf
EndProcedure

;==============================================================================

Procedure CAPTURE_Now()
  GetLocalTime_( @now )
  GetTimeFormat_(0, 0, @now, "HH:mm:ss:", @rec\time, 9 )
  StatusBarText( #StatusBar, #StatusTime, rec\time+Str(now\wMilliseconds) )
EndProcedure

;==============================================================================

Procedure CAPTURE_Draw()
  If rec\update = #True
    CAPTURE_Now()
    StartDrawing( rec\output )
    
      ;Draw Background...
      If rec\frame = 2
        Box( rec\x, rec\y, rec\w, rec\h, rec\cback )
        shift = rec\h >> 2
        Line( rec\x, rec\m - shift, rec\w, 0, rec\cline )
        Line( rec\x, rec\m + shift, rec\w, 0, rec\cline )
        rec\frame = 0
      Else
        rec\frame + 1
      EndIf

      ;Draw Wave Data
      oldx = rec\x : oldy = 0 : max = 0
    
      For i = 0 To rec\size - #SOUND_NBYTES Step #SOUND_NBYTES ;#Word

       sample = PeekW(rec\buffer + i)

       If sample > max: max = sample: EndIf ;FIND PEAK VALUE WITHIN THIS BUFFER
        x = rec\x + ( i * rec\w-1 ) / rec\size
        y = ( sample * rec\h ) / $FFFF

        If x <> oldx
           Select rec\mode
            Case #True  : LineXY(oldx,rec\m+oldy,x,rec\m+y,rec\cwave)
            Case #False : LineXY(oldx,rec\m+oldy,x,rec\m-y,rec\cwave)
           EndSelect
           oldx = x : oldy = y
        EndIf
      Next

      ;Draw Meter
      Box(rec\x + 2, rec\h + rec\y - 26, (max * scale) + 1, 24, #COLOR_GREEN)

    StopDrawing()

    rec\update = #False
  EndIf
EndProcedure

;==============================================================================

Procedure.s CAPTURE_GetDevice()
  Caps.WAVEINCAPS
  For i = 0 To waveInGetNumDevs_()-1
    CAPTURE_ERROR( waveInGetDevCaps_( i, @Caps, SizeOf( WAVEINCAPS ) ) )
    If Caps\dwFormats & #WAVE_FORMAT_1S08
      ProcedureReturn PeekS( @Caps\szPname, 32 )
    EndIf
  Next
  ProcedureReturn #STR_NODEVICE
EndProcedure

;==============================================================================

Procedure CAPTURE_Start()
  DeviceName.s = CAPTURE_GetDevice()
  If DeviceName
    SetWindowText_(WindowID(), DeviceName)
    format.WAVEFORMATEX
    format\wFormatTag      = 1
    format\nChannels       = #SOUND_NCHANNELS
    format\wBitsPerSample  = #SOUND_NBITS
    format\nSamplesPerSec  = #SOUND_NHERTZ
    format\nBlockAlign     = #SOUND_NCHANNELS * (#SOUND_NBITS / 8)
    format\nAvgBytesPerSec = #SOUND_NHERTZ * format\nBlockAlign
    format\cbSize          = 0

    CAPTURE_Error(waveInOpen_(@rec\wave, #WAVE_MAPPER, @format, WindowID(), #Null, #CALLBACK_WINDOW | #WAVE_FORMAT_DIRECT))

    For i = 0 To #BUFFER_NUM - 1
      inHdr(i)\lpData         = AllocateMemory(  #BUFFER_SIZE )
      inHdr(i)\dwBufferLength = #BUFFER_SIZE
      CAPTURE_Error(waveInPrepareHeader_( rec\wave, inHdr(i), SizeOf( WAVEHDR ) ) )
      CAPTURE_Error(waveInAddBuffer_    ( rec\wave, inHdr(i), SizeOf( WAVEHDR ) ) )
    Next
 
    CAPTURE_Error( waveInStart_( rec\wave ) )
    SetTimer_( WindowID(), 0, #BUFFER_TICK, 0 )
  EndIf
EndProcedure

;==============================================================================

Procedure CAPTURE_Stop()
  If rec\wave
    CAPTURE_Error( waveInReset_( rec\wave ) )
    CAPTURE_Error( waveInStop_ ( rec\wave ) )
    For i = 0 To #BUFFER_NUM - 1
      CAPTURE_Error( waveInUnprepareHeader_( rec\wave, inHdr(i), SizeOf( WAVEHDR ) ) )
    Next
    CAPTURE_Error( waveInClose_( rec\wave ) )
  EndIf
  KillTimer_( WindowID(), 0 )
EndProcedure

;==============================================================================

Procedure CAPTURE_Read( hWaveIn.l, lpWaveHdr.l )
  CAPTURE_Error( waveInAddBuffer_( hWaveIn, lpWaveHdr, SizeOf( WAVEHDR ) ) )
  rec\buffer  = PeekL( lpWaveHdr )
  rec\size    = PeekL( lpWaveHdr + 8 )
  rec\update  = #True
  FILE_Append()
EndProcedure


;***********************************************************************************
;- PROCS FILE
;***********************************************************************************


Procedure FILE_Create()
  File.s = SaveFileRequester( #STR_SAVEREQ, "C:\test.raw", "Son brut|(*.raw)", 0 )
  If File
    If CreateFile( 0, File )
      FILE_Recording( #True )
      StatusBarText( #StatusBar, #StatusFile, File )
    Else
      MessageRequester( #STR_ERROR, #STR_PBFILE, #MB_ICONERROR )
    EndIf
  EndIf
EndProcedure

;==============================================================================

Procedure FILE_Append()
  If rec\recording = #True
    rec\recorded + rec\size
    WriteData( rec\buffer, rec\size )
    StatusBarText( #StatusBar, #StatusInfo, Str(rec\recorded) + #STR_RECORDED )
  EndIf
EndProcedure

;==============================================================================

Procedure FILE_Recording( state.b )
  If state = #True
    rec\cback = #COLOR_RECBACK
    rec\cline = #COLOR_RECLINE
    rec\cwave = #COLOR_RECWAVE
  Else
    rec\cback = #COLOR_CAPBACK
    rec\cline = #COLOR_CAPLINE
    rec\cwave = #COLOR_CAPWAVE
  EndIf
  DisableToolBarButton( #gadRecord, state )
  DisableToolBarButton( #gadStop, #True-state )
  rec\recording = state
  rec\recorded = 0
EndProcedure

;==============================================================================

Procedure FILE_Close()
  If rec\recording = #True
    FILE_Recording( #False )
    CloseFile(0)
    StatusBarText( #StatusBar, #StatusFile, #STR_CLOSED )
  EndIf
EndProcedure


;***********************************************************************************
;- PROCS GUI
;***********************************************************************************


Procedure GUI_Button( id.l, ico.l, tip.s )
  ToolBarStandardButton( id, ico )
  ToolBarToolTip( id, tip )
EndProcedure

;==============================================================================

Procedure GUI_Init()
  hFlags.l = #PB_Window_SystemMenu | #PB_Window_SizeGadget | #PB_Window_MinimizeGadget | #PB_Window_ScreenCentered
  If OpenWindow( 0, 0, 0, 480, 240, hFlags, "" ) = #Null
    ProcedureReturn #False
  EndIf
 
  If CreateToolBar( 0, WindowID() ) = #Null
    ProcedureReturn #False
  EndIf
 
  If CreateGadgetList( WindowID() ) = #Null
    ProcedureReturn #False
  EndIf
 
  Frame3DGadget( 0, 0,0,0,0, "", #PB_Frame3D_Double )
 
  If CreateStatusBar( #StatusBar, WindowID() ) = #Null
    ProcedureReturn #False
  EndIf
 
  rec\output = WindowOutput()
 
  GUI_Button( #gadRecord, #PB_ToolBarIcon_Save,       #STR_RECORD )
  GUI_Button( #gadStop,   #PB_ToolBarIcon_Delete,     #STR_STOP   )
  GUI_Button( #gadMode,   #PB_ToolBarIcon_Properties, #STR_MODE   )
  AddStatusBarField(   80 )
  AddStatusBarField(  150 )
  AddStatusBarField( $FFF )
  ProcedureReturn #True
EndProcedure

;==============================================================================
Procedure GUI_Resize()
  rec\x = 2
  rec\y = 30
  rec\w = WindowWidth() - 4
  scale = (rec\w - 5) / ((Pow(2, #SOUND_NBITS) / 2) - 1)
  rec\h = WindowHeight() - 52
  rec\m = rec\y + rec\h / 2
  ResizeGadget( 0, rec\x-2, rec\y-2, rec\w+4, rec\h+4 )
EndProcedure
;==============================================================================

Procedure GUI_CallBack( hWnd.l, uMsg.l, wParam.l, lParam.l )
  Result.l = #PB_ProcessPureBasicEvents
  Select uMsg
    Case #MM_WIM_DATA : CAPTURE_Read( wParam, lParam )
    Case #WM_TIMER    : CAPTURE_Draw()
    Case #WM_SIZE     : GUI_Resize()
    Case #WM_CLOSE    : Quit = #True
    Case #WM_COMMAND
      Select wParam
        Case #gadRecord : FILE_Create()
        Case #gadStop   : FILE_Close()
        Case #gadMode   : rec\mode = #True-rec\mode
      EndSelect
  EndSelect
  ProcedureReturn Result
EndProcedure

;==============================================================================

Procedure GUI_Main()
  If GUI_Init()
    SetWindowCallback( @GUI_CallBack() )
    FILE_Recording(#False)
    GUI_Resize()
    CAPTURE_Start()
    While WaitWindowEvent()<>#WM_CLOSE : Wend
    CAPTURE_Stop()
  EndIf
EndProcedure


;***********************************************************************************
;- START
;***********************************************************************************

GUI_Main()
FreeMemory(-1)

End
goomoo
User
User
Posts: 42
Joined: Sun Dec 05, 2004 9:25 am
Location: China
Contact:

Post by goomoo »

WoW!! Excellent!! :D
Hello, Everyone.
Thanks for your help.
chris319
Enthusiast
Enthusiast
Posts: 782
Joined: Mon Oct 24, 2005 1:05 pm

Post by chris319 »

I have done more work on this program since posting that last bit of source code. My own need is for a monaural recorder with professional-quality metering, .wav file output and audio normalization. The latest version removes the waveform display altogether, resizes the meter when the window is resized, repaints the window when needed and generally improves display management, adds a colored scale to the meter, outputs .wav files and normalizes the audio. The meter display follows the EBU recommendation of 9 dB of headroom below full-scale clipping. The meter now displays green below -9 dBFS, red from -9 to 0 dBFS, and white when clipping occurs. These features are hard-coded now but maybe later some UI gadgets will be added to give the user some control. The sampling is fixed at 16 bits, 48 kHz monaural. Extensive testing has shown that this version of the multimedia library will not support 24-bit recording or sampling greater than 48 kHz. At the moment there are issues involving the sampling of the sound buffers and meter display.

During the development of this program a bug surfaced in connection with the Sound Blaster Audigy 2 ZS I was using. The Audigy was clipping the line input at half amplitude (-6 dB or a sample value of 16383). I chucked that sound card and replaced it with an M-Audio 2496 which I have been very pleased with. If you find you cannot get more than half-scale deflection on your sound card's line input, the problem could lie with the sound card.

I will post the source code if anyone is interested. As far as stereo is concerned, I'll leave that for someone else to implement.
User avatar
Rescator
Addict
Addict
Posts: 1769
Joined: Sat Feb 19, 2005 5:05 pm
Location: Norway

Post by Rescator »

xtensive testing has shown that this version of the multimedia library will not support 24-bit recording or sampling greater than 48 kH
I think that is a limitation of the wavein device,
using directsound input or ASIO should allow to get 16,24,32,32bit float and frequencies up to 192khz.
ASIO should easily handle 192khz at 32bit float.
And directsound input should handle at least 24bit 96khz, most likely 32bit float as well.

I haven't messed around with neither directsound nor ASIO so I'm afraid I can't help with any of that.
Myself I've resorted to using the BASS.dll library for recording,
saves me having to handle the wavein and directsound stuff myself.
(yeah, easy solution I know, but I was pressed for time on a certain app :)
Dare2
Moderator
Moderator
Posts: 3321
Joined: Sat Dec 27, 2003 3:55 am
Location: Great Southern Land

Post by Dare2 »

Hi Chris319,

I would be interested in seeing the source. These posts are very educational for me.



Thanks to all contributing.



PreSchooler question: The "raw" format used here is some standard? Can be converted to .wav?
@}--`--,-- A rose by any other name ..
tomijan
Enthusiast
Enthusiast
Posts: 107
Joined: Sun Dec 11, 2005 1:32 pm

Post by tomijan »

--------------------------------------------------------------------------------

Hi Chris319,

I'am interested in seeing the source to for the same reason, as above.
Is possible to hear recorded sound in real time (in another thread)?



Thanks.
chris319
Enthusiast
Enthusiast
Posts: 782
Joined: Mon Oct 24, 2005 1:05 pm

Post by chris319 »

I have not added playback capability to this program as yet, although I imagine it would be simple to do -- just call the appropriate PureBasic play function.
The "raw" format used here is some standard? Can be converted to .wav?
The raw format is simply raw PCM (digital) audio data. By adding an appropriate 44-byte .wav file header it becomes a .wav file. The header contains information on sample size and frequency, number of channels, etc. If you want more horsepower than 48 kHz/16 bits, you will have to use the libraries mentioned in another post.

Here is the low-horsepower wav file header format:

http://ccrma.stanford.edu/courses/422/p ... aveFormat/
User avatar
Flype
Addict
Addict
Posts: 1542
Joined: Tue Jul 22, 2003 5:02 pm
Location: In a long distant galaxy

Post by Flype »

for those who are interested in sound manipulation :

http://www.freesoundeditor.com/download ... editor.zip

i made a special version of the 'audio capturing source' for the SoundEditor project last year. You can find it in the archive as 'flypeRecorder.pb'

Best regards.
No programming language is perfect. There is not even a single best language.
There are only languages well suited or perhaps poorly suited for particular purposes. Herbert Mayer
User avatar
Flype
Addict
Addict
Posts: 1542
Joined: Tue Jul 22, 2003 5:02 pm
Location: In a long distant galaxy

Post by Flype »

Here is a quick and dirty revision of the code.

( you might need to re-write the following code using the chris319 corrections - see previous posts )

Updated to PB4.0 - Some enhancements.

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
;/

;
;- REC GLOBAL
;
Global QuitRec.l
;
;- REC CONSTANTES
;
#MinWidth      = 320
#MinHeight     = 240
;
;- REC INITIALISATION
;
Enumeration 0 ; #CHANNEL
  #CHANNEL_LEFT
  #CHANNEL_RIGHT
EndEnumeration
Enumeration 0 ; #GADGET
  #gadFrame1
  #gadFrame2
  #gadStart
  #gadStop
  #gadSndVol
  #gadFile
  #gadChannel
  #gadResolution
  #gadFrequence
  #gadDevice
EndEnumeration
Enumeration 0 ; #WINDOW
  #Window
EndEnumeration
Enumeration 0 ; #TOOLBAR
  #ToolBar
EndEnumeration
Enumeration 0 ; #STATUSBAR
  #StatusBar
EndEnumeration

Structure WAVEFORMATEX
  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.raw)
  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)
  nHertz.l            ; Capturing Frequency  (Hertz)
  nChannel.l          ; Capturing Channels number (Mono/Stereo)
  
  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
Config\RScope\channel    = #CHANNEL_RIGHT
;
;- REC DECLARATIONS
;
Declare Clamp(value.l,min.l,max.l)

Declare CONFIG_Load()
Declare CONFIG_Save()

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_Scope()
Declare DRAW_Wave(*scope.SCOPE)
Declare DRAW_Wave8M(*scope.SCOPE)
Declare DRAW_Wave8S(*scope.SCOPE)
Declare DRAW_Wave16M(*scope.SCOPE)
Declare DRAW_Wave16S(*scope.SCOPE)
Declare DRAW_LCD(*scope.SCOPE,lcd$)
Declare DRAW_Background(*scope.SCOPE)

Declare FILE_Close()
Declare FILE_Append()
Declare FILE_Create()
Declare FILE_Select()
Declare FILE_Recording(state.b)
Declare FILE_raw2wav(File.s)

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 Clamp(value.l,min.l,max.l)
  If value<min : ProcedureReturn min : EndIf
  If value>max : ProcedureReturn max : EndIf  
  ProcedureReturn value
EndProcedure
;
Procedure CONFIG_Load()
  
  OpenPreferences("Recorder.ini")
  
  PreferenceGroup("Files")
  Config\File   = ReadPreferenceString("FileName","c:\unnamed.raw")
  Config\SndVol = ReadPreferenceString("SndVol","sndvol32")
  
  PreferenceGroup("Capture")
  Config\nDev     = ReadPreferenceLong("Device",0)
  Config\nChannel = ReadPreferenceLong("Channel",1)
  Config\nHertz   = ReadPreferenceLong("Frequency",7)
  Config\nBit     = ReadPreferenceLong("Resolution",1)
  Config\nBuf     = ReadPreferenceLong("BufferNum",8)
  Config\lBuf     = ReadPreferenceLong("BufferLen",512)
  
  PreferenceGroup("Scope")
  Config\ScopeTimer   = ReadPreferenceLong("ScopeTimer",25)
  Config\ColRecLine   = ReadPreferenceLong("ColRecLine",  $000035)
  Config\ColRecWave   = ReadPreferenceLong("ColRecWave",  $1010E0)
  Config\ColRecBack   = ReadPreferenceLong("ColRecBack",  $000050)
  Config\ColCapLine   = ReadPreferenceLong("ColCapLine",  $004000)
  Config\ColCapWave   = ReadPreferenceLong("ColCapWave",  $20E020)
  Config\ColCapBack   = ReadPreferenceLong("ColCapBack",  $004900)
  
  PreferenceGroup("Window")
  pos.WINDOWPLACEMENT
  pos\length=SizeOf(WINDOWPLACEMENT)
  pos\ptMinPosition\x         = ReadPreferenceLong("MinPosX",0)
  pos\ptMinPosition\y         = ReadPreferenceLong("MinPoxY",0)
  pos\ptMaxPosition\x         = ReadPreferenceLong("MaxPosX",-1)
  pos\ptMaxPosition\y         = ReadPreferenceLong("MaxPosY",-1)
  pos\rcNormalPosition\left   = ReadPreferenceLong("PosX1",100)
  pos\rcNormalPosition\top    = ReadPreferenceLong("PosY1",100)
  pos\rcNormalPosition\right  = ReadPreferenceLong("PosX2",#MinWidth)
  pos\rcNormalPosition\bottom = ReadPreferenceLong("PosY2",#MinHeight)
  
  ClosePreferences()
  
  Config\cback   = Config\ColCapBack
  Config\cline   = Config\ColCapLine
  Config\cwave   = Config\ColCapWave
  
  StatusBarText(0,2,Config\File)
  SetGadgetState(#gadDevice,Config\nDev)
  SetGadgetState(#gadChannel,Config\nChannel)
  SetGadgetState(#gadFrequence,Config\nHertz)
  SetGadgetState(#gadResolution,Config\nBit)
  SetWindowPlacement_(Config\hWindow,@pos)
  ShowWindow_(Config\hWindow,#True)
  
EndProcedure
;
Procedure CONFIG_Save()
  
  pos.WINDOWPLACEMENT
  pos\length = SizeOf(WINDOWPLACEMENT)
  GetWindowPlacement_(Config\hWindow,@pos)
  
  If CreatePreferences("Recorder.ini")
    
    PreferenceGroup("Files")
    WritePreferenceString("FileName",Config\File)
    WritePreferenceString("SndVol",Config\SndVol)
    
    PreferenceGroup("Capture")
    WritePreferenceLong("Device",Config\nDev)
    WritePreferenceLong("Channel",Config\nChannel)
    WritePreferenceLong("Frequency",Config\nHertz)
    WritePreferenceLong("Resolution",Config\nBit)
    WritePreferenceLong("BufferNum",Config\nBuf)
    WritePreferenceLong("BufferLen",Config\lBuf)
    
    PreferenceGroup("Scope")
    WritePreferenceLong("ScopeTimer",Config\ScopeTimer)
    WritePreferenceLong("ColRecLine",Config\ColRecLine)
    WritePreferenceLong("ColRecWave",Config\ColRecWave)
    WritePreferenceLong("ColRecBack",Config\ColRecBack)
    WritePreferenceLong("ColCapLine",Config\ColCapLine)
    WritePreferenceLong("ColCapWave",Config\ColCapWave)
    WritePreferenceLong("ColCapBack",Config\ColCapBack)
    
    PreferenceGroup("Window")
    WritePreferenceLong("MinPosX",pos\ptMinPosition\x)
    WritePreferenceLong("MinPoxY",pos\ptMinPosition\y)
    WritePreferenceLong("MaxPosX",pos\ptMaxPosition\x)
    WritePreferenceLong("MaxPosY",pos\ptMaxPosition\y)
    WritePreferenceLong("PosX1",pos\rcNormalPosition\left)
    WritePreferenceLong("PosY1",pos\rcNormalPosition\top)
    WritePreferenceLong("PosX2",pos\rcNormalPosition\right)
    WritePreferenceLong("PosY2",pos\rcNormalPosition\bottom)
    
    ClosePreferences()
    ProcedureReturn #True
    
  EndIf
  
EndProcedure
;
Procedure CAPTURE_Error(err.l)

  If err
    text.s=Space(#MAXERRORLENGTH)
    waveInGetErrorText_(err,text,#MAXERRORLENGTH)
    MessageRequester("Error",text,#MB_ICONERROR)
    CAPTURE_Stop()
    End
  EndIf

EndProcedure
;
Procedure CAPTURE_Start()
  
  CAPTURE_Stop()
  
  Config\nDev = GetGadgetState(#gadDevice)
  
  Config\nChannel = GetGadgetState(#gadChannel)
  Select Config\nChannel
    Case 0 : Config\format\nChannels = 1
    Case 1 : Config\format\nChannels = 2
  EndSelect
  
  Config\nBit = GetGadgetState(#gadResolution)
  Select Config\nBit
    Case 0 : Config\format\wBitsPerSample = 8
    Case 1 : Config\format\wBitsPerSample = 16
  EndSelect
  
  Config\nHertz = GetGadgetState(#gadFrequence)
  Select Config\nHertz
    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
  
  Config\format\nBlockAlign     = (Config\format\nChannels*Config\format\wBitsPerSample)/8
  Config\format\nAvgBytesPerSec = Config\format\nSamplesPerSec*Config\format\nBlockAlign
  
  If #MMSYSERR_NOERROR = waveInOpen_(@Config\wave,#WAVE_MAPPER+Config\nDev,@Config\format,Config\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)
      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
  
  FILE_Append()
  
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_Scope()
  If Config\buffer
    StartDrawing(Config\output)
      DRAW_Background(Config\LScope)
      DRAW_Background(Config\RScope)
      DRAW_Wave(Config\LScope)
      If Config\nChannel
        DRAW_Wave(Config\RScope)
      EndIf
    StopDrawing()
  EndIf
EndProcedure
;
Procedure DRAW_Background(*scope.SCOPE)
  
  Box (*scope\left,*scope\top,*scope\width,*scope\height,Config\cback)
  Line(*scope\left,*scope\middleY-*scope\quarterY,*scope\width,0,Config\cline)
  Line(*scope\left,*scope\middleY+*scope\quarterY,*scope\width,0,Config\cline)
  
EndProcedure
;
Procedure DRAW_LCD(*scope.SCOPE,lcd$)
  
  DrawingMode(1)
  FrontColor($00FFFF)
  DrawingFont(Config\hfont)
  Select *scope\channel
    Case #CHANNEL_LEFT  : DrawText(*scope\left+1, *scope\top, "[L] "+lcd$)
    Case #CHANNEL_RIGHT : DrawText(*scope\left+1, *scope\top, "[R] "+lcd$)
  EndSelect
  DrawingMode(0)
  
EndProcedure
;
Procedure DRAW_Wave(*scope.SCOPE)
  Select Config\nBit
    Case 0
      DRAW_LCD(*scope.SCOPE,"8 bits")
      Select Config\nChannel
        Case 0 : DRAW_Wave8M(*scope.SCOPE)
        Case 1 : DRAW_Wave8S(*scope.SCOPE)
      EndSelect
    Case 1
      DRAW_LCD(*scope.SCOPE,"16 bits")
      Select Config\nChannel
        Case 0 : DRAW_Wave16M(*scope.SCOPE)
        Case 1 : DRAW_Wave16S(*scope.SCOPE)
      EndSelect
  EndSelect
EndProcedure
;
Procedure DRAW_Wave8M(*scope.SCOPE)
  
  oldx.l=*scope\left
  For i=0 To Config\size
    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
    LineXY(oldx,*scope\middleY+oldy,xx,*scope\middleY+yy,Config\cwave)
    oldx=xx:oldy=yy
  Next
  
EndProcedure
;
Procedure DRAW_Wave8S(*scope.SCOPE)
  
  oldx.l=*scope\left
  For i=0 To Config\size Step 2
    value.b=PeekB(Config\buffer+i+*scope\channel*2)+$7F
    xx=Clamp(*scope\left+(i**scope\width)/(Config\size+1),*scope\left,*scope\width)
    yy=(value**scope\height)/$FF
    LineXY(oldx,*scope\middleY+oldy,xx,*scope\middleY+yy,Config\cwave)
    oldx=xx:oldy=yy
  Next
  
EndProcedure
;
Procedure DRAW_Wave16M(*scope.SCOPE)
  
  oldx.l=*scope\left
  For i=0 To Config\size 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
    LineXY(oldx,*scope\middleY+oldy,xx,*scope\middleY+yy,Config\cwave)
    oldx=xx:oldy=yy
  Next
  
EndProcedure
;
Procedure DRAW_Wave16S(*scope.SCOPE)
  
  oldx.l=*scope\left
  For i=0 To Config\size Step 2
    value.w=PeekW(Config\buffer+i+*scope\channel*2)
    xx=Clamp(*scope\left+(i**scope\width)/(Config\size+1),*scope\left,*scope\width)
    yy=(value**scope\height)/$FFFF
    ayy=Abs(yy)
    If ayy>vmax:vmax=ayy:EndIf
    LineXY(oldx,*scope\middleY+oldy,xx,*scope\middleY+yy,Config\cwave)
    oldx=xx:oldy=yy
  Next
  
EndProcedure
;
Procedure FILE_Create()
  
  If Config\File
    Config\FileId = CreateFile(#PB_Any,Config\File)
    If Config\FileId
      DisableToolBarButton(#ToolBar, #gadStart,#True)
      DisableToolBarButton(#ToolBar, #gadFile,#True)
      DisableGadget(#gadChannel,#True)
      DisableGadget(#gadFrequence,#True)
      DisableGadget(#gadResolution,#True)
      DisableGadget(#gadDevice,#True)
      Config\Date = GetTickCount_()
      FILE_Recording(#True)
    Else
      MessageRequester("Error","Can't create file",#MB_ICONERROR)
    EndIf
  EndIf
  
EndProcedure
;
Procedure FILE_Append()
  
  If Config\recording = #True
    Config\recorded + Config\size
    WriteData(Config\FileId, Config\buffer,Config\size)
    StatusBarText(#StatusBar, 0,Str(Config\recorded)+" bytes",#PB_StatusBar_Center)
    StatusBarText(#StatusBar, 1,StrF((GetTickCount_()-Config\Date)/1000,3)+" secs",#PB_StatusBar_Center)
  EndIf
  
EndProcedure
;
Procedure FILE_Recording(state.b)
  
  If state
    Config\cback   = Config\ColRecBack
    Config\cline   = Config\ColRecLine
    Config\cwave   = Config\ColRecWave
  Else
    Config\cback   = Config\ColCapBack
    Config\cline   = Config\ColCapLine
    Config\cwave   = Config\ColCapWave
  EndIf
  
  Config\recording = state
  Config\recorded  = #Null

EndProcedure
;
Procedure FILE_Close()

  If Config\recording
    Config\Date = #Null
    FILE_Recording(#False)
    CloseFile(Config\FileId)
    Delay(1000)
    FILE_raw2wav(Config\File)    
    DisableToolBarButton(#ToolBar, #gadStart,#False)
    DisableToolBarButton(#ToolBar, #gadFile,#False)
    DisableGadget(#gadChannel,#False)
    DisableGadget(#gadFrequence,#False)
    DisableGadget(#gadResolution,#False)
    DisableGadget(#gadDevice,#False)
  EndIf

EndProcedure
;
Procedure FILE_Select()
  
  File.s = SaveFileRequester("Select a file",Config\File,"Raw Sound|(*.raw)",0)
  
  If File
    Config\File = File
  EndIf
  
  StatusBarText(0,2,Config\File)
  
EndProcedure
;
Procedure FILE_raw2wav(File.s)
  
  inId.l = ReadFile(#PB_Any,File)
  
  If inId = #Null
    ProcedureReturn #False
  EndIf
  
  lBuf.l = Lof(inId)
  pBuf.l = AllocateMemory(lBuf)
  
  If pBuf = #Null
    ProcedureReturn #False
  EndIf
  
  ReadData(inId, pBuf,lBuf)
  CloseFile(inId)
  
  f$ = GetFilePart(File)
  x$ = GetPathPart(File)+Left(f$,Len(f$)-Len(GetExtensionPart(File))-1)+".wav"
  
  outId.l = CreateFile(#PB_Any,x$)
  
  If outId = #Null
    ProcedureReturn #False
  EndIf
  
  b.w = Config\format\wBitsPerSample
  c.w = Config\format\nChannels
  h.l = Config\format\nSamplesPerSec
  
  WriteString(outId, "RIFF")
  WriteLong(outId, lBuf+44)
  WriteString(outId, "WAVE")
  WriteString(outId, "fmt ")
  WriteLong(outId, 16)
  WriteWord(outId, 1)
  WriteWord(outId, c)
  WriteLong(outId, h)
  WriteLong(outId, c*h*(b/8))
  WriteWord(outId, c*(h/8))
  WriteWord(outId, b)
  WriteString(outId, "data")
  WriteLong(outId, lBuf)
  WriteData(outId, pBuf,lBuf)
  
  CloseFile(outId)
  
  ProcedureReturn #True
  
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 : ProcedureReturn #False : EndIf
  
  Config\hWindow=WindowID(0)
  Config\output=WindowOutput(0)
  
  Config\hToolBar=CreateToolBar(#ToolBar,Config\hWindow)
  If Config\hToolBar=#Null : ProcedureReturn #False : EndIf
  
  If CreateGadgetList(Config\hWindow)=#Null
    ProcedureReturn #False
  EndIf
  
  If CreateStatusBar(#StatusBar,Config\hWindow)=#Null
    ProcedureReturn #False
  EndIf
  
  ToolBarStandardButton(#gadStart,#PB_ToolBarIcon_Open)
  ToolBarStandardButton(#gadStop,#PB_ToolBarIcon_Delete)
  
  ToolBarStandardButton(#gadFile,#PB_ToolBarIcon_Save)
  ToolBarSeparator()
  ToolBarStandardButton(#gadSndVol,#PB_ToolBarIcon_Properties)
  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,200) 
  AddGadgetItem(#gadChannel,-1,"Mono")
  AddGadgetItem(#gadChannel,-1,"Stereo")
  SetGadgetState(#gadChannel,0)
  
  GUI_TBCombo(#gadResolution,2,1,55,200)
  AddGadgetItem(#gadResolution,-1,"8 bits")
  AddGadgetItem(#gadResolution,-1,"16 bits")
  
  GUI_TBCombo(#gadFrequence,2,1,80,200)
  AddGadgetItem(#gadFrequence,-1,"8000 khz")
  AddGadgetItem(#gadFrequence,-1,"11025 khz")
  AddGadgetItem(#gadFrequence,-1,"12000 khz")
  AddGadgetItem(#gadFrequence,-1,"16000 khz")
  AddGadgetItem(#gadFrequence,-1,"22050 khz")
  AddGadgetItem(#gadFrequence,-1,"24000 khz")
  AddGadgetItem(#gadFrequence,-1,"32000 khz")
  AddGadgetItem(#gadFrequence,-1,"44100 khz")
  AddGadgetItem(#gadFrequence,-1,"48000 khz")
  
  GUI_TBCombo(#gadDevice,2,1,160,200)
  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.000 "+"secs",#PB_StatusBar_Center)
  
  SetWindowCallback(@GUI_CallBack())
  FILE_Recording(#False)
  GUI_Resize()
  
  ProcedureReturn #True
  
EndProcedure
;
Procedure GUI_Resize()
  
  WndW.l = ( WindowWidth(0)  -  4 )
  WndH.l = ( WindowHeight(0) - 60 ) >> 1

  Config\LScope\left     = 2
  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     = 2
  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 #WM_TIMER    : DRAW_Scope()
    Case #MM_WIM_DATA : CAPTURE_Read(wParam,lParam)
    Case #WM_COMMAND
      Select wParam & $FFFF
        Case #gadStart      : FILE_Create()
        Case #gadStop       : FILE_Close()
        Case #gadFile       : FILE_Select()
        Case #gadSndVol     : RunProgram(Config\SndVol,"","")
        Case #gadFrequence  : If EventType()=9 : CAPTURE_Start() : EndIf
        Case #gadResolution : If EventType()=9 : CAPTURE_Start() : EndIf
        Case #gadChannel    : If EventType()=9 : CAPTURE_Start() : EndIf
        Case #gadDevice     : If EventType()=9 : CAPTURE_Start() : EndIf
      EndSelect
    Case #WM_GETMINMAXINFO 
      *mmi.MINMAXINFO=lParam 
      *mmi\ptMinTrackSize\x=#MinWidth
      *mmi\ptMinTrackSize\y=#MinHeight
  EndSelect
  
  ProcedureReturn Result
  
EndProcedure
;
;- REC MAIN
;
Procedure StartRecorder()
  
  If GUI_Init()
    CONFIG_Load()
    CAPTURE_Start()
    Repeat
    Until WaitWindowEvent()=#WM_CLOSE Or QuitRec 
    CAPTURE_Stop()
    CONFIG_Save()
  EndIf
  
EndProcedure

StartRecorder()
No programming language is perfect. There is not even a single best language.
There are only languages well suited or perhaps poorly suited for particular purposes. Herbert Mayer
dna
User
User
Posts: 83
Joined: Wed Aug 30, 2006 12:07 am

Post by dna »

Your link is dead Aaron
User avatar
electrochrisso
Addict
Addict
Posts: 989
Joined: Mon May 14, 2007 2:13 am
Location: Darling River

Post by electrochrisso »

Hello Flype, great code, only one problem (for me anyhow).
The created wav file seems to be corrupted but you can still load it with some audio editors and it will play ok but will not save under the same filename.
Other programs like media player say the file is corrupted and PB code will not load it either.
No one else seems to have mentioned it so it might be my setup.
User avatar
Flype
Addict
Addict
Posts: 1542
Joined: Tue Jul 22, 2003 5:02 pm
Location: In a long distant galaxy

Re: Capturing Audio [COMPLETE SOURCE]

Post by Flype »

Flype wrote:Raw sound is saved in a file... which can be loaded and listened thanx a software like SoundForge.
Hi,

I said in the first line of the first post the answer of the problem. This old code saves a RAW file (filename.raw).
A raw sound is only composed with the sound data. There's no header (descriptive bloc at the start of the sound).
What you are looking for is '.WAV' for example. It's quite easy to append such header. There are many examples in the forum.

You should have posted your question tomorrow, for the "4th birthday" of this thread :)
No programming language is perfect. There is not even a single best language.
There are only languages well suited or perhaps poorly suited for particular purposes. Herbert Mayer
byo
Enthusiast
Enthusiast
Posts: 635
Joined: Mon Apr 02, 2007 1:43 am
Location: Brazil

Post by byo »

This code offers not only awesome audio techniques but some lessons on how to make code easy to read and well commented. Great one, Flype.
User avatar
electrochrisso
Addict
Addict
Posts: 989
Joined: Mon May 14, 2007 2:13 am
Location: Darling River

Post by electrochrisso »

I totally agree with you byo.

Thanks for the reply Flype.
I am not too advanced in programming, but fast learning a lot from you and all the great PB coders on this forum.
I simply thought this procedure of your updated version contained the header info as well. I'm not up there with headers, I will have to do a bit more investigation though.

Code: Select all

Procedure FILE_raw2wav(File.s)
 
  inId.l = ReadFile(#PB_Any,File)
 
  If inId = #Null
    ProcedureReturn #False
  EndIf
 
  lBuf.l = Lof(inId)
  pBuf.l = AllocateMemory(lBuf)
 
  If pBuf = #Null
    ProcedureReturn #False
  EndIf
 
  ReadData(inId, pBuf,lBuf)
  CloseFile(inId)
 
  f$ = GetFilePart(File)
  x$ = GetPathPart(File)+Left(f$,Len(f$)-Len(GetExtensionPart(File))-1)+".wav"
 
  outId.l = CreateFile(#PB_Any,x$)
 
  If outId = #Null
    ProcedureReturn #False
  EndIf
 
  b.w = Config\format\wBitsPerSample
  c.w = Config\format\nChannels
  h.l = Config\format\nSamplesPerSec
 
  WriteString(outId, "RIFF")
  WriteLong(outId, lBuf+44)
  WriteString(outId, "WAVE")
  WriteString(outId, "fmt ")
  WriteLong(outId, 16)
  WriteWord(outId, 1)
  WriteWord(outId, c)
  WriteLong(outId, h)
  WriteLong(outId, c*h*(b/8))
  WriteWord(outId, c*(h/8))
  WriteWord(outId, b)
  WriteString(outId, "data")
  WriteLong(outId, lBuf)
  WriteData(outId, pBuf,lBuf)
 
  CloseFile(outId)
 
  ProcedureReturn #True
 
EndProcedure
Post Reply