Record a WAV file with BASS.dll

Post your working scripts, libraries and tools for AHK v1.1 and older
User avatar
metacognition
Posts: 117
Joined: 22 Oct 2014, 05:57
Location: Alaska
Contact:

Record a WAV file with BASS.dll

12 Jul 2017, 01:47

I might be several years too late for anyone who was excited about Bass.dll.

I never actually saw anyone do a successful record with bass.dll, there was input meters but nothing else. Below is a script to record to a wav file. Thanks Lexikos for your assistance over one of my hurdles.

Code: Select all

SetBatchLines, -1
#Persistent

Gui, Add, Button, x182 y99 w100 h30 gStartRecord, Start
Gui, Add, Button, x182 y149 w100 h30 gStopRecord, Stop
; Generated using SmartGUI Creator for SciTE
Gui, Show, w479 h379, Untitled GUI
return

GuiClose:
ExitApp


StartRecord:

Control, Disable, , Start

RecordCallback := RegisterCallback("RecordFunction" )

FileDelete, 0_test.wav

;//// WRITE WAV HEADER /////

file := fileopen("0_test.wav", "rw", CP1252)
file.write("RIFF    ") ; leave space for file size - 8

file.write("WAVEfmt ") ; Wave Container, 'fmt ' chunk, this takes up 8 bytes.

fmt_length = 0
numput(16, fmt_length, "UInt") ; 16 bytes, length of formatchunk in bytes
file.rawwrite(fmt_length, 4)

fmt_type = 0
numput(1,fmt_type, "UShort") ; format, 1 = PCM, linear aka non compressed
file.rawwrite(fmt_type, 2)

fmt_channels = 0
numput(2, fmt_channels, "UShort") ; 2 channels
file.rawwrite(fmt_channels, 2)

fmt_sample_rate = 0
numput(44100, fmt_sample_rate, "UInt") ; 44100 samples per second
file.rawwrite(fmt_sample_rate, 4)

fmt_byte_rate = 0
numput(176400, fmt_byte_rate, "UInt") ; bytes per second, 176400
file.rawwrite(fmt_byte_rate, 4)

fmt_blockalign=0		 
numput(4, fmt_blockalign, "UShort") ; NumChannels * BitsPerSample/8
file.rawwrite(fmt_blockalign, 2)

fmt_bitspersample=0		 
numput(16, fmt_bitspersample, "UShort") ; 16 bit resolution
file.rawwrite(fmt_bitspersample, 2)

file.write("data    ") ; this takes up 8 bytes. leave space to write in the size of data chunk.
datasize = 0

;////// WAV HEADER COMPLETE, MOSTLY ///////




;////// LOAD BASS AND START RECORDING  /////////
DllCall("LoadLibrary","str",A_ScriptDir "\bass.dll") 
DllCall(A_ScriptDir "\bass.dll\BASS_RecordInit", Int,-1)
DllCall(A_ScriptDir "\bass.dll\BASS_ErrorGetCode")
hrecord := DllCall(A_ScriptDir "\bass.dll\BASS_RecordStart", UInt, 44100, UInt, 2, UInt, 0, UInt, RecordCallback, UInt, 0)
return


StopRecord:
DllCall(A_ScriptDir "\bass.dll\BASS_ChannelStop", UInt,hrecord)

;///// BACK TO HEADER //////
file.seek(40)

data_datasize=0
numput(datasize, data_datasize, "UInt") ; little endian
file.rawwrite(data_datasize, 4)

datasize+=36

file.seek(4)

riff_size=0
numput(datasize, riff_size, "UInt") ; little endian
file.rawwrite(riff_size, 4)

;///// DONE ///////


ExitApp
return


RecordFunction(handle, buffer, length, user)
{
	global file
	global datasize

	file.rawwrite(buffer+0,length)
	datasize += length
	
	return true
}

User avatar
Delta Pythagorean
Posts: 627
Joined: 13 Feb 2017, 13:44
Location: Somewhere in the US
Contact:

Re: Record a WAV file with BASS.dll

12 Jul 2017, 03:05

There's a little problem, the file is corrupt and Windows will not read it. I've tried it in a few programs and they all say it's corrupted. I might have a different version or download of bass.dll

[AHK]......: v2.0.12 | 64-bit
[OS].......: Windows 11 | 23H2 (OS Build: 22621.3296)
[GITHUB]...: github.com/DelPyth
[PAYPAL]...: paypal.me/DelPyth
[DISCORD]..: tophatcat

User avatar
Masonjar13
Posts: 1555
Joined: 20 Jul 2014, 10:16
Location: Не Россия
Contact:

Re: Record a WAV file with BASS.dll

12 Jul 2017, 03:35

I've never used bass.dll before, but this is quite cool! I wrote a class wrapper for it.

Code: Select all

#singleInstance force
#persistent
#include *i <Lib_1>
SetBatchLines, -1
i:=0

Gui, Add, Button, x182 y99 w100 h30 gStartRecord, Start
Gui, Add, Button, x182 y149 w100 h30 gStopRecord disabled, Stop
; Generated using SmartGUI Creator for SciTE
Gui, Show, w479 h379, bassRecord Test
recording:=new bassRecord(a_scriptDir . "\bass64.dll")
return

GuiClose:
ExitApp


StartRecord:
recording.start("test_" . i++ ".wav")
control,disable,,Start
control,enable,,Stop
return

StopRecord:
recording.stop()
control,enable,,Start
control,disable,,Stop
return

class bassRecord {
    
    __new(bassLoc:=""){
        this.bassLoc:=bassLoc?bassLoc:a_scriptDir . "\bass.dll"
        return this
    }
    
    __delete(){
        if(this.currentFile)
            this.stop()
    }
    
    start(fileName){
        if(this.currentFile)
            return 1
        
        this.currentFile:=fileName

        this.recordCallback:=registerCallback("_callback","","",&this)
        dllCall(this.recordCallback,"int",42)
        this._writeWAV(fileName)
        
        if(!this.bToken)
            this.bToken:=DllCall("LoadLibrary","str",this.bassLoc) 
        DllCall(this.bassLoc . "\BASS_RecordInit", Int,-1)
        DllCall(this.bassLoc . "\BASS_ErrorGetCode")
        this.hrecord := DllCall(this.bassLoc . "\BASS_RecordStart", UInt, 44100, UInt, 2, UInt, 0, UInt, this.recordCallback, UInt, 0)
    }
    
    stop(){
        DllCall(this.bassLoc . "\BASS_ChannelStop", UInt,this.hrecord)
        this.file.seek(40)
    
        data_datasize=0
        numput(this.datasize, data_datasize, "UInt") ; little endian
        this.file.rawwrite(data_datasize, 4)

        this.datasize+=36

        this.file.seek(4)

        riff_size=0
        numput(this.datasize, riff_size, "UInt") ; little endian
        this.file.rawwrite(riff_size, 4)
        dllCall("GlobalFree","ptr",this.recordCallback)
        this.file.close()
        this.currentFile:=""
    }
    
    _writeWAV(fileName){
        this.file := fileopen(fileName, "rw", CP1252)
        this.file.write("RIFF    ") ; leave space for file size - 8

        this.file.write("WAVEfmt ") ; Wave Container, 'fmt ' chunk, this takes up 8 bytes.

        this.file.writeUInt(16) ; 16 bytes, length of formatchunk in bytes

        this.file.writeUShort(1) ; format, 1 = PCM, linear aka non compressed

        this.file.writeUShort(2) ; 2 channels

        this.file.writeUInt(44100) ; 44100 samples per second

        this.file.writeUInt(146400) ; bytes per second, 176400

        this.file.writeUShort(4) ; NumChannels * BitsPerSample/8

        this.file.writeUShort(16) ; 16 bit resolution

        this.file.write("data    ") ; this takes up 8 bytes. leave space to write in the size of data chunk.
        this.datasize := 0
    }
    
    callback(handle,buffer,length,user){
        this.file.rawwrite(buffer+0,length)
        this.datasize += length
        return true
    }
}

_callback(handle,buffer,length,user){
    return Object(a_eventInfo).callback(handle,buffer,length,user)
}
@Delta Pythagorean, try their official site (download links are at the very top of the page). Remember, if you're using 64-bit AHK, you'll need to use the 64-bit DLL, likewise to 32-bit versions.

Edit: changed code based on just me's suggestion.
OS: Windows 10 Pro | Editor: Notepad++
My Personal Function Library | Old Build - New Build
BoBo
Posts: 6564
Joined: 13 May 2014, 17:15

Re: Record a WAV file with BASS.dll

12 Jul 2017, 03:36

Delta Pythagorean wrote:There's a little problem, the file is corrupt and Windows will not read it. I've tried it in a few programs and they all say it's corrupted. I might have a different version or download of bass.dll
You've probably used the 32- <?> 64-bit version? [More..]

Code: Select all

BASS 2.4
Copyright (c) 1999-2016 Un4seen Developments Ltd. All rights reserved.

Files that you should have found in the BASS package
====================================================
Win32 version
-------------
BASS.TXT        This file
BASS.DLL        BASS module
BASS.CHM        BASS documentation
MP3-FREE\
  BASS.DLL        BASS module using the OS's MP3 decoder
X64\
  BASS.DLL        64-bit BASS module
  MP3-FREE
    BASS.DLL        64-bit BASS module using the OS's MP3 decoder 
 .
 .
 .   
just me
Posts: 9424
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Record a WAV file with BASS.dll

12 Jul 2017, 04:02

Code like

Code: Select all

fmt_length = 0
numput(16, fmt_length, "UInt") ; 16 bytes, length of formatchunk in bytes
file.rawwrite(fmt_length, 4)
might be replaced by

Code: Select all

fmt_length := 16 ; 16 bytes, length of formatchunk in bytes
file.WriteUInt(fmt_length)
User avatar
Masonjar13
Posts: 1555
Joined: 20 Jul 2014, 10:16
Location: Не Россия
Contact:

Re: Record a WAV file with BASS.dll

12 Jul 2017, 07:10

Updated to allow different sample and bit rates, as well as channels. Bass.dll only supports 8, 16, and 32-bit rates. If it's not one of those though, it'll default to 16, for safety. Being a WAV, it should support up to 2^16-1 channels and 2^32-1 sample rate, but I'm not sure of the limitations of Bass.dll.

Code: Select all

#singleInstance force
#persistent
#include *i <Lib_1>
setBatchLines, -1
i:=0

gui,add,button,x182 y99 w100 h30 gStartRecord,Start
gui,add,button,x182 y149 w100 h30 gStopRecord disabled,Stop
recording:=new bassRecord(a_scriptDir . "\bass64.dll")
recording.sampleRate:=48000
recording.bitRate:=16
recording.channelCount:=1
gui,show,w479 h379,bassRecord Test
return

guiClose:
exitApp


StartRecord:
recording.start("test_" . i++ ".wav")
control,disable,,Start
control,enable,,Stop
return

StopRecord:
recording.stop()
control,enable,,Start
control,disable,,Stop
return

class bassRecord {
    channelCount:=2
    sampleRate:=44100
    bitRate:=16
    
    __new(bassLoc:=""){
        this.bassLoc:=bassLoc?bassLoc:a_scriptDir . "\bass.dll"
        return this
    }
    
    __delete(){
        if(this.currentFile)
            this.stop()
    }
    
    start(fileName){
        if(this.currentFile)
            return 1
        
        this.currentFile:=fileName

        this.recordCallback:=registerCallback("_callback","","",&this)
        dllCall(this.recordCallback,"int",42)
        this._writeWAV(fileName)
        
        if(!this.bToken)
            this.bToken:=dllCall("LoadLibrary","str",this.bassLoc) 
        dllCall(this.bassLoc . "\BASS_RecordInit","Int",-1)
        ;dllCall(this.bassLoc . "\BASS_ErrorGetCode")
        this.hrecord:=dllCall(this.bassLoc . "\BASS_RecordStart","UInt",this.sampleRate,"UInt",this.channelCount,"UInt",bitRate=8?1:bitRate=32?256:0,"UInt",this.recordCallback,"UInt", 0)
    }
    
    stop(){
        dllCall(this.bassLoc . "\BASS_ChannelStop","UInt",this.hrecord)
        dllCall("GlobalFree","Ptr",this.recordCallback)
        this.file.seek(40)
    
        this.file.writeUInt(this.datasize) ;little endian

        this.datasize+=36

        this.file.seek(4)

        this.file.writeUInt(this.datasize)
        
        this.file.close()
        this.currentFile:=""
    }
    
    _writeWAV(fileName){
        this.file := fileopen(fileName, "rw", CP1252)
        this.file.write("RIFF    ") ; leave space for file size - 8

        this.file.write("WAVEfmt ") ; Wave Container, 'fmt ' chunk, this takes up 8 bytes.

        this.file.writeUInt(16) ; 16 bytes, length of formatchunk in bytes

        this.file.writeUShort(1) ; format, 1 = PCM, linear aka non compressed

        this.file.writeUShort(this.channelCount) ; 2 channels

        this.file.writeUInt(this.sampleRate) ; 44100 samples per second

        this.file.writeUInt(this.sampleRate*this.channelCount*this.bitRate/8) ; bytes per second, 176400

        this.file.writeUShort(this.channelCount*this.bitRate/8) ; NumChannels * BitsPerSample/8

        this.file.writeUShort(this.bitRate) ; 16 bit bitRate

        this.file.write("data    ") ; this takes up 8 bytes. leave space to write in the size of data chunk.
        this.datasize := 0
    }
    
    callback(handle,buffer,length,user){
        this.file.rawwrite(buffer+0,length)
        this.datasize += length
        return true
    }
}

_callback(handle,buffer,length,user){
    return Object(a_eventInfo).callback(handle,buffer,length,user)
}
OS: Windows 10 Pro | Editor: Notepad++
My Personal Function Library | Old Build - New Build
User avatar
metacognition
Posts: 117
Joined: 22 Oct 2014, 05:57
Location: Alaska
Contact:

Re: Record a WAV file with BASS.dll

12 Jul 2017, 14:58

@Masonjar13 Cool! Thanks!
@justme Thanks, I didn't know about that .WriteNum method! Simplifies things...
DanielToward13
Posts: 74
Joined: 18 May 2017, 10:56

Re: Record a WAV file with BASS.dll

29 Jul 2017, 10:01

Could you share the bass64.dll and the BASS LIB? I have tried three versions on the internet but they doesn't work. I could not find the 64bit DLL from https://www.un4seen.com
User avatar
metacognition
Posts: 117
Joined: 22 Oct 2014, 05:57
Location: Alaska
Contact:

Re: Record a WAV file with BASS.dll

29 Jul 2017, 10:17

http://www.un4seen.com/download.php?bass24

the 64 bit dll would be in the "x64" folder.
Janusz
Posts: 89
Joined: 18 Dec 2020, 17:47

Re: Record a WAV file with BASS.dll

25 Sep 2021, 08:19

It is very interesting. I had A luck to record randomly for a little time from MIC by using thirt script here. But routine do not work reliably. Once record, twice or more no. Very interesting. I Am running correct 64 Bit of bass.dll and I AM running The latest Autohotkey stable release. Very interesting routines, which do not work reliably. But good work I will try to find The cause of my issues.
Rafaews
Posts: 25
Joined: 16 Mar 2018, 21:19

Re: Record a WAV file with BASS.dll

16 Apr 2022, 15:24

I'm also having problems recording routinely. Unfortunately unreliable.
malcev
Posts: 1769
Joined: 12 Aug 2014, 12:37

Re: Record a WAV file with BASS.dll

16 Apr 2022, 19:19

Try to use RegisterSyncCallback instead of RegisterCallback.
Search forum for that function.
Also do not call BASS_ChannelStop from main thread.
To stop recording You can use "return" instead of "return true" in callback function.
User avatar
rommmcek
Posts: 1470
Joined: 15 Aug 2014, 15:18

Re: Record a WAV file with BASS.dll

17 Apr 2022, 11:18

Sometimes is less more! Here is slightly modified OP code which works for me repetedly:

Code: Select all

SetBatchLines, -1
#Persistent

i:= 0
Gui, Add, Button, x182 y99 w100 h30 gStartRecord, Start
Gui, Add, Button, x182 y149 w100 h30 gStopRecord Disabled, Stop
; Generated using SmartGUI Creator for SciTE
Gui, Show, w479 h379, Untitled GUI
return

GuiClose:
ExitApp


StartRecord:

Control, Disable, , Start
Control, Enable, , Stop

RecordCallback := RegisterCallback("RecordFunction" )

;FileDelete, 0_test.wav

;//// WRITE WAV HEADER /////

loop {
    if FileExist(++i "_test.wav") {
        MsgBox, 4,, % "File " i "_test.wav already exists! `n`n Do you want to overvrite it?"
        IfMsgBox Yes
        {
            FileDelete, % i "_test.wav"
            break
        }
    } else break
}

file := fileopen(i "_test.wav", "rw", CP1252)
file.write("RIFF    ") ; leave space for file size - 8

file.write("WAVEfmt ") ; Wave Container, 'fmt ' chunk, this takes up 8 bytes.

fmt_length = 0
numput(16, fmt_length, "UInt") ; 16 bytes, length of formatchunk in bytes
file.rawwrite(fmt_length, 4)

fmt_type = 0
numput(1,fmt_type, "UShort") ; format, 1 = PCM, linear aka non compressed
file.rawwrite(fmt_type, 2)

fmt_channels = 0
numput(2, fmt_channels, "UShort") ; 2 channels
file.rawwrite(fmt_channels, 2)

fmt_sample_rate = 0
numput(44100, fmt_sample_rate, "UInt") ; 44100 samples per second
file.rawwrite(fmt_sample_rate, 4)

fmt_byte_rate = 0
numput(176400, fmt_byte_rate, "UInt") ; bytes per second, 176400
file.rawwrite(fmt_byte_rate, 4)

fmt_blockalign=0		 
numput(4, fmt_blockalign, "UShort") ; NumChannels * BitsPerSample/8
file.rawwrite(fmt_blockalign, 2)

fmt_bitspersample=0		 
numput(16, fmt_bitspersample, "UShort") ; 16 bit resolution
file.rawwrite(fmt_bitspersample, 2)

file.write("data    ") ; this takes up 8 bytes. leave space to write in the size of data chunk.
datasize = 0

;////// WAV HEADER COMPLETE, MOSTLY ///////




;////// LOAD BASS AND START RECORDING  /////////
DllCall("LoadLibrary","str",A_ScriptDir "\bass.dll") 
DllCall(A_ScriptDir "\bass.dll\BASS_RecordInit", Int,-1)
DllCall(A_ScriptDir "\bass.dll\BASS_ErrorGetCode")
hrecord := DllCall(A_ScriptDir "\bass.dll\BASS_RecordStart", UInt, 44100, UInt, 2, UInt, 0, UInt, RecordCallback, UInt, 0)
return


StopRecord:
DllCall(A_ScriptDir "\bass.dll\BASS_ChannelStop", UInt,hrecord)

;///// BACK TO HEADER //////
file.seek(40)

data_datasize=0
numput(datasize, data_datasize, "UInt") ; little endian
file.rawwrite(data_datasize, 4)

datasize+=36

file.seek(4)

riff_size=0
numput(datasize, riff_size, "UInt") ; little endian
file.rawwrite(riff_size, 4)

Control, Enable,, Start
Control, Disable,, Stop

;///// DONE ///////


;ExitApp
return


RecordFunction(handle, buffer, length, user)
{
	global file
	global datasize

	file.rawwrite(buffer+0,length)
	datasize += length
	
	return true
}
User avatar
metacognition
Posts: 117
Joined: 22 Oct 2014, 05:57
Location: Alaska
Contact:

Re: Record a WAV file with BASS.dll

17 Apr 2022, 13:09

:thumbup:
ffmpeg and sox can be great command line tools to accomplish audio manipulation from an ahk script.
User avatar
rommmcek
Posts: 1470
Joined: 15 Aug 2014, 15:18

Re: Record a WAV file with BASS.dll

17 Apr 2022, 17:08

And here is slightly modified Masonjar13 's code to work repeatedly:

Code: Select all

#singleInstance force
#persistent
#include *i <Lib_1>
SetBatchLines, -1
i:=0

Gui, Add, Button, x182 y99 w100 h30 gStartRecord, Start
Gui, Add, Button, x182 y149 w100 h30 gStopRecord disabled, Stop
; Generated using SmartGUI Creator for SciTE
Gui, Show, w479 h379, bassRecord Test
return

GuiClose:
ExitApp


StartRecord:
recording:=new bassRecord(a_scriptDir . "\bass.dll")
loop {
    if FileExist("test_" . ++i ".wav") {
        MsgBox, 4,, % "File " i "_test.wav already exists! `n`n Do you want to overvrite it?"
        IfMsgBox Yes
        {
            FileDelete, % "test_" . i ".wav"
            break
        }
    } else break
}
recording.start("test_" . i ".wav")
control,disable,,Start
control,enable,,Stop
return

StopRecord:
recording.stop()
control,enable,,Start
control,disable,,Stop
return

class bassRecord {
    
    __new(bassLoc:=""){
        this.bassLoc:=bassLoc?bassLoc:a_scriptDir . "\bass.dll"
        return this
    }
    
    __delete(){
        if(this.currentFile)
            this.stop()
    }
    
    start(fileName){
        if(this.currentFile)
            return 1
        
        this.currentFile:=fileName

        this.recordCallback:=registerCallback("_callback","","",&this)
        dllCall(this.recordCallback,"int",42)
        this._writeWAV(fileName)
        
        if(!this.bToken)
            this.bToken:=DllCall("LoadLibrary","str",this.bassLoc) 
        DllCall(this.bassLoc . "\BASS_RecordInit", Int,-1)
        DllCall(this.bassLoc . "\BASS_ErrorGetCode")
        this.hrecord := DllCall(this.bassLoc . "\BASS_RecordStart", UInt, 44100, UInt, 2, UInt, 0, UInt, this.recordCallback, UInt, 0)
    }
    
    stop(){
        DllCall(this.bassLoc . "\BASS_ChannelStop", UInt,this.hrecord)
        this.file.seek(40)
    
        data_datasize=0
        numput(this.datasize, data_datasize, "UInt") ; little endian
        this.file.rawwrite(data_datasize, 4)

        this.datasize+=36

        this.file.seek(4)

        riff_size=0
        numput(this.datasize, riff_size, "UInt") ; little endian
        this.file.rawwrite(riff_size, 4)
        dllCall("GlobalFree","ptr",this.recordCallback)
        this.file.close()
        this.currentFile:=""
    }
    
    _writeWAV(fileName){
        this.file := fileopen(fileName, "rw", CP1252)
        this.file.write("RIFF    ") ; leave space for file size - 8

        this.file.write("WAVEfmt ") ; Wave Container, 'fmt ' chunk, this takes up 8 bytes.

        this.file.writeUInt(16) ; 16 bytes, length of formatchunk in bytes

        this.file.writeUShort(1) ; format, 1 = PCM, linear aka non compressed

        this.file.writeUShort(2) ; 2 channels

        this.file.writeUInt(44100) ; 44100 samples per second

        this.file.writeUInt(146400) ; bytes per second, 176400

        this.file.writeUShort(4) ; NumChannels * BitsPerSample/8

        this.file.writeUShort(16) ; 16 bit resolution

        this.file.write("data    ") ; this takes up 8 bytes. leave space to write in the size of data chunk.
        this.datasize := 0
    }
    
    callback(handle,buffer,length,user){
        this.file.rawwrite(buffer+0,length)
        this.datasize += length
        return true
    }
}

_callback(handle,buffer,length,user){
    return Object(a_eventInfo).callback(handle,buffer,length,user)
}
Rafaews
Posts: 25
Joined: 16 Mar 2018, 21:19

Re: Record a WAV file with BASS.dll

18 Apr 2023, 10:41

The reason why this method is unreliable, at least on my system, is that Windows keeps changing the device number of the audio devices.

So each time I start a new recording I check what's the device number for my target devices using a string comparison.

This is the added code:

Code: Select all


TargetDevice := "Microphone (2- Realtek High Definition Audio)" ; Change this

Loop
{
	SystemDeviceNumber := A_index - 1 ; starting with 0
	
	VarSetCapacity(info,16,0) 
	DllCall(BASSDLLPath "\BASS_RecordGetDeviceInfo", UInt, SystemDeviceNumber, UInt, &info)
	
	Name := NumGet(info, 0, "ptr")
	Name := StrGet(Name, "cp0")
	
	if (Name = TargetDevice)
	{
		DeviceNumber := SystemDeviceNumber 
		Break
	}
	else if not Name
	{
		MsgBox Recording device not found. Aborting.
		return	
	}
}

Return to “Scripts and Functions (v1)”

Who is online

Users browsing this forum: No registered users and 112 guests