Звукозапись

Данную программу конечно можно было написать проще с помощью высокоуровневой функции mciSendString , но мы не ищем легких путей :)
Это простая по возможностям программа, которая умеет записывать одноканальный звук со звукового устройства с предустановленной частотой дискретизации 11000 гц и максимально возможным временем записи 2 часа. Конечно изменить эти величины возможно в коде. Так же программа воспроизводит записанный звук и при желании сохраняет на диск в формате WAV.

Платформа: Windows
Автор: Станислав Будинов

wavesave.png

#INCLUDE "windows.bi"
#INCLUDE "win/mmsystem.bi"

Const As Integer Lenght = 7200 ' maximum 2 hours

Type WaveFileHeader
    riff(3) As Byte = {82,73,70,70}
    Len_ As  Integer
    cWavFmt(7) As Byte = {87,65,86,69,102,109,116,32}
    dwHdrLen As Integer = 16
    wFormat As Short = 1
    wNumChannels As Short = 1
    dwSampleRate As Integer = 11000
    dwBytesPerSec As Integer = 22000
    wBlockAlign As Short = 4
    wBitsPerSample As Short = 16
    cData(3) As Byte = {100,97,116,97}
    dwDataLen As Integer
End Type

Type Sound
    As HWND hw
    As HWND StaticTime,ButtonRec,ButtonPlay,ButtonSave
    As WAVEHDR hdr
    As HWAVEIN hWaveIn
    As HWAVEOUT hWaveOut
    As WAVEFORMATEX wfx
    As WaveFileHeader Wavehdr
    As Byte buffer(11000*Lenght*2)
    As MSG msg
    As WNDCLASSEX wc
    As String NameClass="MyClass"
    As HINSTANCE Hinst=GetModuleHandle(0)
    As Double Tsec,SaveRecTime
    As BOOL Frec,Fplay,playtrue
    Declare Function init() As BOOL
    Declare Function Rec() As BOOL
    Declare Sub RecStop()
    Declare Function Play() As BOOL
    Declare Sub PlayStop()
    Declare Sub SaveSound()
    Declare Static Function wndproc(hwnd As HWND, msg As Uinteger,_
    wparam As WPARAM, lparam As LPARAM) As Integer
End Type

Dim Shared obj As Sound
obj.init

Function Sound.init() As BOOL
    With wc
        .cbSize=SizeOf(WNDCLASSEX)
        .style=CS_HREDRAW Or CS_VREDRAW
        .lpfnWndProc=@WndProc
        .hInstance=Hinst
        .hIcon=LoadIcon(0,IDI_WINLOGO)
        .hCursor=LoadCursor(0,IDC_ARROW)
        .hbrBackground=Cast(HBRUSH,COLOR_WINDOWFRAME)
        .lpszClassName=StrPtr(NameClass)
        .hIconSm=.hIcon
    End With
    If RegisterClassEx(@wc)=0 Then
        Print "Register error, press any key"
        Sleep
        End
    Endif
    hw=CreateWindowEx(0,NameClass,"Simple Recorder",_
    WS_VISIBLE Or WS_OVERLAPPEDWINDOW,100,100,235,110,0,0,Hinst,0)
    SetTimer(hw,1,1000,0)
    ' Цикл сообщений
    While GetMessage(@msg,0,0,0)
        TranslateMessage(@msg)
        DispatchMessage(@msg)
    Wend
    Return TRUE
End Function

Sub Sound.SaveSound()
    Var Hfile=CreateFile("SoundTest.wav",GENERIC_WRITE Or GENERIC_READ, FILE_SHARE_READ Or FILE_SHARE_WRITE,0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,0)
    Dim buff As Integer
    WriteFile(Hfile,Cast(LPCVOID,@Wavehdr),Sizeof(WaveFileHeader),@buff,0)
    WriteFile(Hfile,Cast(LPCVOID,@buffer(0)),Wavehdr.dwDataLen,@buff,0)
    CloseHandle(Hfile)
End Sub

Function Sound.Rec() As BOOL
    ZeroMemory(@buffer(0),Ubound(buffer)+1)
    With wfx
        .wFormatTag = WAVE_FORMAT_PCM
        .nChannels = 1
        .nSamplesPerSec = 11000
        .wBitsPerSample = 16
        .nBlockAlign = .nChannels * (.wBitsPerSample \ 8)
        .nAvgBytesPerSec = .nSamplesPerSec * .nBlockAlign
        .cbSize = Sizeof(WAVEFORMATEX)
    End With
    With hdr
        .lpData = @buffer(0)
        .dwBufferLength = Ubound(buffer)+1
    End With
    If waveInOpen(@hWaveIn, Cast(Uinteger, -1), @wfx, 0, 0, CALLBACK_NULL)<> MMSYSERR_NOERROR Then
        MessageBox(0,"Unable to mount device","Error",0)
        Return FALSE
    Endif
    waveInPrepareHeader(hWaveIn, @hdr, Sizeof(WAVEHDR))
    waveInAddBuffer(hWaveIn, @hdr, Sizeof(WAVEHDR))

    If waveInStart(hWaveIn)<> MMSYSERR_NOERROR Then
        MessageBox(0,"A bad start recording","Error",0)
        Return FALSE
    Endif
    Return TRUE
End Function

Sub Sound.RecStop()
    waveInReset(hWaveIn)
    waveInUnprepareHeader(hWaveIn, @hdr, Sizeof(WAVEHDR))
    waveInClose(hWaveIn)
End Sub

Function Sound.Play() As BOOL
    If waveOutOpen(@hWaveOut, Cast(Uinteger, -1), @wfx, 0, 0, CALLBACK_NULL)<> MMSYSERR_NOERROR Then
        MessageBox(0,"Unable to mount device","Error",0)
        Return FALSE
    Endif
    waveOutPrepareHeader(hWaveOut, @hdr, Sizeof(WAVEHDR))
    waveOutWrite(hWaveOut, @hdr, Sizeof(WAVEHDR))
    Return TRUE
End Function

Sub Sound.PlayStop()
    waveOutReset(hWaveOut)
    waveOutUnprepareHeader(hWaveOut, @hdr, Sizeof(WAVEHDR))
    waveOutClose(hWaveOut)
End Sub

Function Sound.wndproc(hwnd As HWND, msg As Uinteger,_
    wparam As WPARAM, lparam As LPARAM) As Integer
    Select Case msg
        Case WM_CREATE
            obj.ButtonRec  = CreateWindowEx(0,"button","Record",WS_CHILD Or WS_VISIBLE,10,10,60,20,hwnd,Cast(HMENU,1),0,0)
            obj.StaticTime = CreateWindowEx(0,"Static","0:00",WS_CHILD Or WS_VISIBLE Or SS_CENTER Or SS_CENTERIMAGE,80,10,60,20,hwnd,Cast(HMENU,2),0,0)
            obj.ButtonPlay = CreateWindowEx(0,"button","Play",WS_CHILD Or WS_VISIBLE,150,10,60,20,hwnd,Cast(HMENU,3),0,0)
            obj.ButtonSave = CreateWindowEx(0,"button","Save",WS_CHILD Or WS_VISIBLE,70,40,80,20,hwnd,Cast(HMENU,4),0,0)
        Case WM_TIMER
            If obj.Frec = 1 Or obj.Fplay = 1 Then
                Dim As Integer m = (Timer()- obj.Tsec)\60,s = (Timer()- obj.Tsec) Mod 60
                If obj.SaveRecTime<=Timer()- obj.Tsec And obj.Fplay = 1 Then
                    SendMessage(hwnd,WM_COMMAND,Makelparam(3,0),0)
                Endif
                If s<10 Then
                    SetWindowText(obj.StaticTime,m & ":" & "0" & s)
                Else
                    SetWindowText(obj.StaticTime,m & ":"  & s)
                Endif
            Endif
        Case WM_COMMAND
            Select Case Loword(WPARAM)
                Case 1
                    If obj.Frec = 0 Then
                        obj.Frec = 1
                        SetWindowText(obj.ButtonRec,"Stop")                     
                        obj.Tsec = Timer()
                        EnableWindow(obj.ButtonPlay,FALSE)
                        EnableWindow(obj.ButtonSave,FALSE)
                        obj.rec()
                    Else
                        obj.Frec = 0
                        SetWindowText(obj.ButtonRec,"Record")                   
                        obj.Tsec = Timer()- obj.Tsec
                        obj.RecStop()
                        obj.SaveRecTime = obj.Tsec
                        EnableWindow(obj.ButtonPlay,TRUE)
                        EnableWindow(obj.ButtonSave,TRUE)
                        obj.Wavehdr.dwDataLen = 11000*obj.Tsec*2
                    Endif
                    obj.playtrue= TRUE
                Case 3
                    If obj.playtrue= TRUE Then                      
                        If obj.Fplay = 0 Then
                            obj.Fplay = 1
                            SetWindowText(obj.ButtonPlay,"Stop")
                            obj.Play()
                            EnableWindow(obj.ButtonRec,FALSE)
                            EnableWindow(obj.ButtonSave,FALSE)
                            obj.Tsec = Timer()
                        Else
                            obj.Fplay = 0
                            SetWindowText(obj.ButtonPlay,"Play")
                            obj.PlayStop()
                            EnableWindow(obj.ButtonRec,TRUE)
                            EnableWindow(obj.ButtonSave,TRUE)
                        Endif
                    Endif
                Case 4
                    obj.SaveSound()
                    MessageBox(0,"SoundTest.wav file saved in a folder with the program","",0)
            End Select
        Case WM_DESTROY
            PostQuitMessage(0)
    End Select
    Return DefWindowProc(hwnd,msg,wparam,lparam)
End Function