Jump to content

Recommended Posts

Posted

Hi to all of you.

This is my first post, so I hope you will be mercifully with me.

The matter is I badly need to know the input level of some ASIO device input channels (namely a digidesign MBOX2 and a EMU 0404 externalUSB, but I think this is not so important).

As the examples for collecting input samples and reading the peak level from BASS ASIO devices I would able to come across to were less than a few, the development of a little piece of AutoIt software which could handle this topic is driving me mad :-(

At the moment I only tried using "purely" BASS_ASIO calls, but this really doesn't matter to me, what I only need is to read the peak level of an ASIO input channel from an ASIO device or, as an alternative, get the samples in an array and calculate it, so in any way it could be done it will be great!

The snippet I wrote tries to read this info by using a callback invoked by BASS_ASIO_ChannelEnable, but, as you can guess, it doesn't work, crashing as soon as the code reaches the end of the callback function.

To make things a bit more parametric, I developed a tiny GUI, which lets you insert both the device and the input channel numbers.

Using it, I first of all set up device and input channel, then I press the "INIT" button to initializate BASS_ASIO (which seems to me runs smoothly), and, at last, I supposed the reading of the input channel would be done by pressing the "START" button, which actually makes the program crashes.

I inserted some debug consolewrite to trace what is going on inside the code, and it seems the sleep function inside the callback "helps" the program to crash, as well as the attempt to write the read peak level onto the GUI, as they conflict with the ASIO core.

Furthermore it is not clear to me I would have to organize the code to read the input level continuously, suppose for 10 minutes, like looking at an input V-Meter, which is what this software is aimed to. Is the callback called continuously? Have I to put an infinite loop in the callback itself? Unfortunately this also is not clear to me.

Finally I have to say I'm using "BASSASIO for ALL" to test my script (so I've never tested it with the two audio cards I own) reading its device and channel numbers by using the "list.exe" little program which comes together with the BASS and BASS_ASIO packages and putting these values in the GUI.

To be honest, I'm much more interested in targeting my goal: to get the input peak value (or getting it by the samples array coming from an ASIO device input channel), rather than investigating in where I'm wrong, even if it could be interesting.

Indeed, this development is just a little piece of an huge audio research I've been doing from the last 4 years, and at this stage I only, even if badly, need a very precise numeric V-Meter to exactly read the audio input level (in case using the samples array to calculate it), so all of this is no more than an unavoidable stuff to get it.

Forgive me for this so long and perhaps boring description, for sure any help would be really appreciated as at the moment I'm lost in the middle of nowhere and this is delaying my research, which absolutely needs this instrument to go on.

Thank you so much to all of you for your help and support,

Daniele, Rome, Italy

Here follows my AutoIt script:

#include <BASS.au3>

#include <BASSConstants.au3>

#include <BASSAsio.au3>

#include <BASSAsioConstants.au3>

#include <ButtonConstants.au3>

#include <EditConstants.au3>

#include <GUIConstantsEx.au3>

#include <StaticConstants.au3>

#include <WindowsConstants.au3>

#Region ### START Koda GUI section ### Form=d:\measure\au3\asio sin meter\asiosinmeterform.kxf

$frmAsioInputMeter = GUICreate("Asio Input Meter", 302, 192, 192, 114)

$cmdAsioStart = GUICtrlCreateButton("START", 184, 48, 49, 41, $WS_GROUP)

$cmdAsioStop = GUICtrlCreateButton("STOP", 241, 48, 49, 41, $WS_GROUP)

$txtChannelLevel = GUICtrlCreateInput("", 8, 96, 281, 81, BitOR($ES_CENTER,$ES_AUTOHSCROLL,$ES_READONLY))

GUICtrlSetFont(-1, 48, 800, 0, "Times New Roman")

GUICtrlSetColor(-1, 0x0A246A)

$txtDevice = GUICtrlCreateInput("", 8, 64, 49, 21, BitOR($ES_CENTER,$ES_AUTOHSCROLL))

$txtChannel = GUICtrlCreateInput("", 64, 64, 49, 21, BitOR($ES_CENTER,$ES_AUTOHSCROLL))

$lblDevice = GUICtrlCreateLabel("Device", 11, 47, 38, 17)

$lblChannel = GUICtrlCreateLabel("Channel", 65, 47, 43, 17)

$lblTitle = GUICtrlCreateLabel("Asio Input Meter", 9, 2, 291, 41)

GUICtrlSetFont(-1, 24, 400, 0, "MS Sans Serif")

$cmdAsioInit = GUICtrlCreateButton("INIT", 127, 48, 49, 41, $WS_GROUP)

GUISetState(@SW_SHOW)

#EndRegion ### END Koda GUI section ###

$IsInput = 1

$UsesCurrentBufferLen = 0

global $cbSamplesBuffer = DllStructCreate("int Buffer[2048]")

global $cbSamplesBufferPTR = DllStructGetPtr($cbSamplesBuffer )

global $cblen = DllStructCreate("DWORD len")

global $cbChannel = DllStructCreate("DWORD cbChannel")

global $cbIsInput = DllStructCreate("DWORD cbIsInput")

global $cbDummyData = 0

global $cbDummyDataPTR = DllStructGetPtr($cbDummyData)

_BASS_STARTUP()

_BASS_ASIO_STARTUP()

While 1

$nMsg = GUIGetMsg()

Switch $nMsg

Case $GUI_EVENT_CLOSE

Exit

case $cmdAsioInit

Assert(_BASS_ASIO_Stop(), "_BASS_ASIO_Stop")

Assert(_BASS_ASIO_Free(), "_BASS_ASIO_Free")

$Device = GUICtrlRead($txtDevice)

$Channel = GUICtrlRead($txtChannel)

Assert(_BASS_ASIO_STARTUP(), "_BASS_ASIO_STARTUP")

Assert(_BASS_ASIO_Init($Device), "_BASS_ASIO_Init")

Assert(_BASS_ASIO_SetRate(44100.0), "_BASS_ASIO_SetRate")

Assert(_BASS_ASIO_ChannelSetFormat($IsInput, 0, $BASS_ASIO_FORMAT_16BIT), "_BASS_ASIO_ChannelSetFormat")

Assert(_BASS_ASIO_ChannelSetRate($IsInput, $Channel, 44100.0), "_BASS_ASIO_ChannelSetRate")

Assert(_BASS_ASIO_ChannelEnable($IsInput, $Channel, "cbAsioShowInputLevel", $cbDummyData), "_BASS_ASIO_ChannelEnable")

$cbIsInput = $IsInput

$cbChannel = $Channel

case $cmdAsioStart

Assert(_BASS_ASIO_Start($UsesCurrentBufferLen), "_BASS_ASIO_Start")

ConsoleWrite("now out from callback" & @CRLF)

case $cmdAsioStop

Assert(_BASS_ASIO_Stop(), "_BASS_ASIO_Stop")

Assert(_BASS_ASIO_Free(), "_BASS_ASIO_Free")

EndSwitch

WEnd

Func cbAsioShowInputLevel($cbIsInput, $cbChannel, $cbSamplesBufferPTR, $cblen, $cbDummyDataPTR)

$n = 1

;while 1

ConsoleWrite($n & ".1 cbSamplesBuffer = " & DllStructGetData ($cbSamplesBuffer, 1) & @CRLF)

$V = _BASS_ASIO_ChannelGetLevel($cbIsInput, $cbChannel)

;GUICtrlSetData($txtChannelLevel, $V)

ConsoleWrite($n & ".2 wrote " & $V & " on GUI" & @CRLF)

;;Sleep(50)

ConsoleWrite($n & ".3 callback sleept a bit" & @CRLF)

$n = $n +1

;wend

EndFunc

Func Assert($retVal, $CalledFunction)

If @error Then

MsgBox(0, $CalledFunction & " ERROR: " & @error, $CalledFunction)

Exit

Else

ConsoleWrite("OK " & $CalledFunction & @CRLF)

EndIf

EndFunc

  • 2 weeks later...
Posted (edited)

Hi

Download http://rapidshare.com/files/412849347/Bass_new.zip (unofficial beta)

and take a look at the asio examples.

Sorry, I can´t give better support at the moment...

E

Dear Eukalyptus,

you cannot imagine how your help has been important to me. Without your support I would be still lost, due to the exact measurement of the asio input level is crucial to my research.

Having had the opportunity to take a look at your example scripts made me able to definitively work out the solution I so desperately needed and, at last, got.

To be honest, it is still not perfectly clear to me the exact way in which your _Bass_Asio_ChannelGetLevel gets the result, but anyway it works, doing it damn well!

So, hoping this will be useful to any of you, I built up a GUI around your _Bass_Asio_ChannelGetLevel and added a bit of logic to manage the asio input device, the recorded file and the input channel levels, even if the core, which makes the piece of sw useful, is completely yours and I only made some minor changes to it, just to make the script more readable.

When compiled, this script exactly realizes what it is aimed to, a small, stand alone application to exactly show the asio device input levels.

I also added the visualization of the current maximum input level, which is hold for roughly one second.

It could be needed to change the directories in the #include statements to point to the position of the _BASS include files and, of course, the dlls must be in the script folder in order to make it works.

One more time, thank you. Should one day my research be complete, please let me quote your help (together with Ian, the author of the terrific bass dlls set), and in case I will ask you your permission to do so, asking you some further details other than a nick name on this forum to make people know more about you.

;;#AutoIt3Wrapper_Au3Check_Parameters=-d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6

;#include-once
;#include "Bass.au3"
#include "D:\Measure\au3\BASS_New By Eucaliptus\BASS\Bass.au3"

;#include "BassAsio.au3"
#include "D:\Measure\au3\BASS_New By Eucaliptus\BASS_ASIO\BassAsio.au3"

;#include "BassExt.au3"
#include "D:\Measure\au3\BASS_New By Eucaliptus\BASS_EXT\BassExt.au3"

;#include "BassEnc.au3"
#include "D:\Measure\au3\BASS_New By Eucaliptus\BASS_ENC\BassEnc.au3"

#include <ButtonConstants.au3>
#include <ComboConstants.au3>
#include <EditConstants.au3>
#include <GUIConstantsEx.au3>
#include <ProgressConstants.au3>
#include <StaticConstants.au3>
#include <WindowsConstants.au3>

#Region ### START Koda GUI section ### Form=d:\measure\au3\asio sin meter\formasiosinmeter.kxf
$frmAsioInputMeter = GUICreate("16 BIT 44.100 Hz ASIO DEVICE INPUT METER", 557, 185, 193, 114)
$cmbDevice = GUICtrlCreateCombo("", 72, 8, 337, 25)
$txtChannel1Level = GUICtrlCreateInput("", 8, 104, 249, 45, BitOR($ES_CENTER,$ES_AUTOHSCROLL,$ES_READONLY))
GUICtrlSetFont(-1, 24, 800, 0, "MS Sans Serif")
GUICtrlSetColor(-1, 0x800080)
$txtChannel2Level = GUICtrlCreateInput("", 296, 104, 249, 45, BitOR($ES_CENTER,$ES_AUTOHSCROLL,$ES_READONLY))
GUICtrlSetFont(-1, 24, 800, 0, "MS Sans Serif")
GUICtrlSetColor(-1, 0x800080)
$ProgressChannel1 = GUICtrlCreateProgress(8, 158, 249, 17)
$ProgressChannel2 = GUICtrlCreateProgress(296, 158, 249, 17)
$cmdStop = GUICtrlCreateButton("Stop", 484, 6, 60, 25, $WS_GROUP)
$cmdStart = GUICtrlCreateButton("Start", 416, 6, 60, 25, $WS_GROUP)
$txtRecordFile = GUICtrlCreateInput("", 71, 38, 425, 21)
$lblAsioDevice = GUICtrlCreateLabel("Asio device", 9, 12, 59, 17)
$cmdBrowseRecFile = GUICtrlCreateButton("...", 503, 36, 41, 25, $WS_GROUP)
GUICtrlSetFont(-1, 14, 800, 0, "MS Sans Serif")
$lblRecordInto = GUICtrlCreateLabel("Record into", 7, 40, 59, 17)
$lblChannel1 = GUICtrlCreateLabel("Value - Max Ch 1", 7, 82, 119, 20)
GUICtrlSetFont(-1, 10, 800, 0, "MS Sans Serif")
GUICtrlSetColor(-1, 0x800080)
$lblChannel2 = GUICtrlCreateLabel("Value - Max Ch 2", 431, 82, 119, 20)
GUICtrlSetFont(-1, 10, 800, 0, "MS Sans Serif")
GUICtrlSetColor(-1, 0x800080)
$txtSamplingRate = GUICtrlCreateInput("", 192, 64, 65, 21, BitOR($ES_CENTER,$ES_AUTOHSCROLL,$ES_READONLY))
$lblSamplingRate = GUICtrlCreateLabel("Sample Rate", 127, 68, 65, 17)
$lblBitDepth = GUICtrlCreateLabel("Bit depth", 367, 68, 46, 17)
$txtBitDepth = GUICtrlCreateInput("", 296, 64, 65, 21, BitOR($ES_CENTER,$ES_AUTOHSCROLL,$ES_READONLY))
$ProgressCPU = GUICtrlCreateProgress(268, 104, 17, 45, $PBS_VERTICAL)
$lblCPU = GUICtrlCreateLabel("CPU", 265, 86, 26, 17)
$txtCPULevel = GUICtrlCreateInput("", 262, 158, 29, 17, BitOR($ES_CENTER,$ES_AUTOHSCROLL,$ES_READONLY))
GUISetState(@SW_SHOW)
#EndRegion ### END Koda GUI section ###


    OnAutoItExitRegister("_FreeBass")

    HotKeySet("{ESC}", "_Exit")

    Global $hEncoder, $ReadSampleRate, $InputSampleRate, $hAsioBuffer, $aPipe[6], $aInfo
    Global $NoFlags, $AppName, $OneChannel, $NoFlags, $Channel1, $Channel2, $IsAnInputChannel
    Global $BitDepth, $BitDepthFullScale, $MilliSecMaxValueRetemption, $TwoChannels, $MilliSecForCycle
    Global $MaxChannel1, $MaxChannel2, $MilliSecElapsed

    $AppName = "ASIO Input Meter"

    $NoFlags = 0
    $Channel1 = 0
    $Channel2 = 1
    $IsAnInputChannel = True
    $BitDepth = 16
    $BitDepthFullScale = 32767
    $TwoChannels = 2
    $MilliSecForCycle = 50
    $MilliSecMaxValueRetemption = 1000

    StartApp()

    $Start = False
    $MilliSecElapsed = 0

    While 1

        $CanStart  = GUICtrlRead( $txtRecordFile ) <> "" and GUICtrlRead( $cmbDevice ) <> "" and $Start = False
        if $CanStart Then
            GUICtrlSetState($cmdStop,  $GUI_DISABLE)
            GUICtrlSetState($cmdStart, $GUI_ENABLE)
        Else
            GUICtrlSetState($cmdStop,  $GUI_ENABLE)
            GUICtrlSetState($cmdStart, $GUI_DISABLE)
        endif

        $nMsg = GUIGetMsg()
        Switch $nMsg
            Case $GUI_EVENT_CLOSE

                Exit

            Case $cmdBrowseRecFile

                if $Start = False Then
                    $RecordFile = FileSaveDialog("Choose WAV record file", @ScriptDir, "Wav file (*.wav)", 16)
                    if $RecordFile = ""  Then
                        $RecordFile = @ScriptDir & "\" & "asioinputmeter.wav"
                    endif
                    GUICtrlSetData($txtRecordFile, $RecordFile)
                EndIf

            Case $cmdStop

                $Start = False
                _FreeBass()

            Case $cmdStart

                $sDevice = GUICtrlRead($cmbDevice)
                $Device  = int(StringRight($sDevice, 1))

                $Start = True
                MeterStart($Device, $RecordFile, 44100)

        EndSwitch

        Sleep($MilliSecForCycle)
        $MilliSecElapsed += $MilliSecForCycle

        if $Start then

            $Ch1Level = round( _BASS_ASIO_ChannelGetLevel(True, $Channel1) * $BitDepthFullScale, 0)
            $Ch2Level = round( _BASS_ASIO_ChannelGetLevel(True, $Channel2) * $BitDepthFullScale, 0)

            if $Ch1Level > $MaxChannel1 Then
                $MaxChannel1 = $Ch1Level
                $MilliSecElapsed = 0
            EndIf

            if $Ch2Level > $MaxChannel2 Then
                $MaxChannel2 = $Ch2Level
                $MilliSecElapsed = 0
            EndIf

            if $MilliSecElapsed > $MilliSecMaxValueRetemption Then
                $MilliSecElapsed = 0
                $MaxChannel1 = $Ch1Level
                $MaxChannel2 = $Ch2Level
            EndIf

            GUICtrlSetData($txtChannel1Level, $Ch1Level & "-" & $MaxChannel1 )
            GUICtrlSetData($txtChannel2Level, $Ch2Level & "-" & $MaxChannel2 )

            GUICtrlSetData($ProgressChannel1, $Ch1Level * 100 / $BitDepthFullScale )
            GUICtrlSetData($ProgressChannel2, $Ch2Level * 100 / $BitDepthFullScale )

            GUICtrlSetData($txtSamplingRate, _BASS_ASIO_ChannelGetRate(True, $Channel1) )

            $CPU = Round( _BASS_ASIO_GetCPU()*100, 1)
            GUICtrlSetData($ProgressCPU, $CPU )
            GUICtrlSetData($txtCPULevel, $CPU )

            GUICtrlSetData($txtBitDepth, $BitDepth & "/" & $BitDepthFullScale )

        else

            GUICtrlSetData($txtChannel1Level, "" )
            GUICtrlSetData($txtChannel2Level, "" )
            GUICtrlSetData($ProgressCPU, 0 )
            GUICtrlSetData($txtCPULevel, "" )
            GUICtrlSetData($ProgressChannel1, 0 )
            GUICtrlSetData($ProgressChannel2, 0 )

        EndIf

    Wend


Func _Exit()
    Exit
EndFunc   ;==>_Exit

Func _FreeBass()

    Assert(0, "Stopping Asio Input Meter:" & @CRLF )

    Assert( _BASS_Encode_Stop($hEncoder), "  _BASS_Encode_Stop" )
    Assert( _BASS_ASIO_Stop(), "  _BASS_ASIO_Stop()" )
    Assert( _BASS_ASIO_Free(), "  _BASS_ASIO_Free()" )
    Assert( _BASS_Free(), "  _BASS_Free()" )

EndFunc   ;==>_FreeBass

Func ___DeBug($iError, $sAction)

    Switch $iError
        Case -1
            ConsoleWrite(@CRLF & "-" & $sAction & @CRLF)
        Case -2
            ConsoleWrite(@CRLF & ">" & $sAction & @CRLF)
        Case 0
            ;;ConsoleWrite(@CRLF & "+" & $sAction & " - OK" & @CRLF)
            ConsoleWrite("OK " & $sAction & @CRLF)
        Case Else
            ConsoleWrite(@CRLF & "!" & $sAction & " - FAILED, @error: " & $iError & @CRLF)
            Exit
    EndSwitch

EndFunc   ;==>___DeBug

Func Assert($retVal, $sAction)

    If @error Then
        MsgBox(0, $AppName, $sAction & " ERROR: " & @error, $sAction)
        Exit
    Else
        Consolewrite("OK " & $sAction & @CRLF)
    EndIf

EndFunc

Func StartApp()

    Assert(0, "Initializing App loading BASS dll"  & @CRLF )
    ;;*********************STARTUP BASS *********************************
    Assert (_BASS_Startup(), "  _BASS_Startup(): load bass.dll")
    ;; ___Debug(@error, "load bass.dll")

    Assert( _BASS_ASIO_Startup(), "  _BASS_ASIO_Startup(): load bassasio.dll")
    ;; ___Debug(@error, "load bassasio.dll")

    Assert( _BASS_ENCODE_Startup(), "  _BASS_ENCODE_Startup(): load bassenc.dll")
    ;; ___Debug(@error, "load bassenc.dll")

    Assert( _BASS_EXT_STARTUP(), "  _BASS_EXT_STARTUP(): load bassext.dll")
    ;; ___Debug(@error, "load bassext.dll")
    ;;*********************STARTUP BASS *********************************

    ;;Fill the device combo box
    $iDevice = 0
    $lstDevice = ""
    While 1
        $aDeviceInfoInfo = _BASS_ASIO_GetDeviceInfo($iDevice)
        If @error Then
            ExitLoop
        Else
            $lstDevice = $lstDevice & $aDeviceInfoInfo[1] & " - Device " & $iDevice & "|"
            $iDevice += 1
        EndIf
    Wend

    GUICtrlSetData($cmbDevice, $lstDevice)

EndFunc


Func MeterStart($Device, $RecordFile, $InputSampleRate)

    Assert(0, "Starting Asio Input Meter:" & @CRLF )

    Assert( _BASS_ASIO_Init($Device), "  _BASS_ASIO_Init device: initialize asio device number " & $Device )
    ;;___Debug(@error, "initialize asio")

    Assert( _Bass_ASIO_SetRate($InputSampleRate), "  _Bass_ASIO_SetRate: set Asio sample rate at " & $InputSampleRate & " Hz")
    ;;___Debug(@error, "set asio samplerate: " & $InputSampleRate)

    $ReadSampleRate = _BASS_ASIO_GetRate()
    ___Debug(@error, "  _BASS_ASIO_GetRate: get asio samplerate: " & $ReadSampleRate & " Hz")

    Assert( _BASS_Init($NoFlags, -1, $ReadSampleRate), "  _BASS_Init: initialize bass (same samplerate as asio)" )
    ;;___Debug(@error, "initialize bass (same samplerate as asio)")


    $hAsioBuffer = _BASS_StreamCreate($ReadSampleRate, $TwoChannels, $NoFlags, $STREAMPROC_PUSH, 0)
    ___Debug(@error, "  _BASS_StreamCreate: create push stream to act as buffer for asio in")

    Assert( _BASS_ChannelSetAttribute($hAsioBuffer, $BASS_ATTRIB_VOL, 0), "  _BASS_ChannelSetAttribute: mute channel" )
    ;;___Debug(@error, "mute channel")

    Assert( _BASS_ChannelPlay($hAsioBuffer, False), "  _BASS_ChannelPlay: start channel - data is sent to encoder when buffer is filled")
    ;; ___Debug(@error, "start channel - data is sent to encoder when buffer is filled")

    $hEncoder = _BASS_Encode_Start($hAsioBuffer, $RecordFile, BitOR($BASS_ENCODE_PCM, $BASS_ENCODE_FP_16BIT))
    ___Debug(@error, "  _BASS_Encode_Start: set encoder on the stream")

    $aInfo = _BASS_ASIO_GetInfo()
    ___Debug(@error, "  _BASS_ASIO_GetInfo: get asio infos")

    $aPipe = _BASS_EXT_StreamPipeCreate($hAsioBuffer, $BASS_EXT_STREAMPROC_PUSH)
    ___Debug(@error, "  _BASS_EXT_StreamPipeCreate: using created push stream as buffer for asio in")

    Assert( _BASS_ASIO_ChannelEnable($IsAnInputChannel, $Channel1, $BASS_EXT_AsioProc, $aPipe[0]), "  _BASS_ASIO_ChannelEnable: enable asio input channel 1" )
    ;; ___Debug(@error, "enable asio input channel 1")

    Assert( _BASS_ASIO_ChannelSetFormat($IsAnInputChannel, $Channel1, $BASS_ASIO_FORMAT_16BIT), "  _BASS_ASIO_ChannelSetFormat: set asio input channel 1 sampleformat to 16 bit" )
    ;; ___Debug(@error, "set asio input channel 1 sampleformat to 16bit")

    Assert( _BASS_ASIO_ChannelSetRate($IsAnInputChannel, $Channel1, $ReadSampleRate), "  _BASS_ASIO_ChannelSetRate: set asio input channel 1 samplerate to " & $ReadSampleRate )
    ;; ___Debug(@error, "set asio input channel 1 samplerate to " & $ReadSampleRate)

    Assert( _BASS_ASIO_ChannelJoin($IsAnInputChannel, $Channel2, $Channel1), "  _BASS_ASIO_ChannelJoin: join asio input channel 2 and 1" )
    ;; ___Debug(@error, "join asio input channel 2 and 1")

    Assert( _BASS_ASIO_Start($aInfo[5]), "  _BASS_ASIO_Start: start asio processing" )
    ;; ___Debug(@error, "start asio processing")

    ___Debug(-2, "Recording to " & $RecordFile)
    ___Debug(-1, "press ESC to quit")

    GUICtrlSetData($txtSamplingRate, $ReadSampleRate)

EndFunc
Edited by dabudabu
  • 3 weeks later...
Posted (edited)

This is great stuff!

As I am quite new to it all, I was wondering if anyone knows:

on _BASS_CD_GetInfo; - [4] = The drive's reading and writing capabilities, a combination of the following flags ...

when reading this array value you get a number like : 2138374207

so I need to find out how to interpret it.

so if one is trying to figure out how to query a specific 'rwflags' capability, say, $BASS_CD_RWFLAG_READDVDR, etc,

Is there a way to do that?

Edited by Zip
  • 2 weeks later...
Posted

hi, i am trying to write a simple script to start recording at the press of a hotkey and stop when the key is pressed again.

i have tried for days to get the bass recording functions to work, but no luck.

could someone please post a short example of how to start recording, then stop and save to a file?

if someone could do that i would be most appreciative as i need the functionality for a voice chat server i have written.

thanks in advance.

Please note: If you plan on submitting any code snippits to my posts, please refrain from using the code tags as i use a screen reader and can't read the code properly, let alone copy it the way it should be.Thanks for your understanding.Best regardsXY16

Posted

This is great stuff!

As I am quite new to it all, I was wondering if anyone knows:

on _BASS_CD_GetInfo; - [4] = The drive's reading and writing capabilities, a combination of the following flags ...

when reading this array value you get a number like : 2138374207

so I need to find out how to interpret it.

so if one is trying to figure out how to query a specific 'rwflags' capability, say, $BASS_CD_RWFLAG_READDVDR, etc,

Is there a way to do that?

_BASS_CD_GetInfo($drive) should return an array and I assume you are going and retrieving the 4 element in the array successfuly. You should be able to do something like...

If BitAnd ($RetturnValue[4], $flag) <> 0 Then ... or something similar.

hi, i am trying to write a simple script to start recording at the press of a hotkey and stop when the key is pressed again.

i have tried for days to get the bass recording functions to work, but no luck.

could someone please post a short example of how to start recording, then stop and save to a file?

if someone could do that i would be most appreciative as i need the functionality for a voice chat server i have written.

thanks in advance.

I'll try find my example for recording. I think eukalyptus might have one too? I'll ask him for you and if I can't find one, then I'll make one.

Cheers,

Brett

  • 3 months later...
Posted

BrettF , thanks man!

I had a talk with Ian from un4seen for a while about adding some BassCD features & fixing some issues ,and now the new BASSCD version has the fixes.

But, I have a hard time translating the new code /funcs to Autoit :).

These are the NEW funcs & constants from the VB File:

Global Const BASS_CDID_CDDB_QUERY = &H200
Global Const BASS_CDID_CDDB_READ = &H201 ' + entry #
Global Const BASS_CDID_CDDB_READ_CACHE = &H2FF

' BASS_CD_GetTOC modes
Global Const BASS_CD_TOC_TIME = &H100

' TOC structures
Type BASS_CD_TOC_TRACK
    res1 As Byte
    adrcon As Byte        ' ADR + control
    track As Byte         ' track number
    res2 As Byte
    lba As Long           ' start address (logical block address)
End Type

Type BASS_CD_TOC
    size As Integer       ' size of TOC
    first As Byte         ' first track
    last As Byte          ' last track
    tracks(0 To 99) As BASS_CD_TOC_TRACK ' up to 100 tracks
End Type

' BASS_CD_TOC_TRACK "adrcon" flags
Global Const BASS_CD_TOC_CON_PRE = 1
Global Const BASS_CD_TOC_CON_COPY = 2
Global Const BASS_CD_TOC_CON_DATA = 4

' CDDATAPROC "type" values
Global Const BASS_CD_DATA_SUBCHANNEL = 0
Global Const BASS_CD_DATA_C2 = 1
Declare Function BASS_CD_GetTOC Lib "basscd.dll" (ByVal drive As Long, ByVal mode As Long, ByRef toc As BASS_CD_TOC) As Long
Declare Function BASS_CD_SetOffset Lib "basscd.dll" (ByVal drive As Long, ByVal offset As Long) As Long
Declare Function BASS_CD_StreamCreateEx Lib "basscd.dll" (ByVal drive As Long, ByVal track As Long, ByVal flags As Long, ByVal proc As Long, ByVal user As Long) As Long
Declare Function BASS_CD_StreamCreateFileEx Lib "basscd.dll" (ByVal f As String, ByVal flags As Long, ByVal proc As Long, ByVal user As Long) As Long

' callback functions
Sub CDDATAPROC(ByVal handle As Long, ByVal pos As Long, ByVal type_ As Long, ByVal buffer As Long, ByVal length As Long, ByVal user As Long)
    
    'CALLBACK FUNCTION !!!

    ' Sub-channel/C2 reading callback function.
    ' handle : The CD stream handle
    ' pos    : The position of the data
    ' type   : The type of data (BASS_CD_DATA_xxx)
    ' buffer : Buffer containing the data.
    ' length : Number of bytes in the buffer
    ' user   : The 'user' parameter value given when calling BASS_CD_StreamCreate/FileEx
    
End Sub

And these 2 Structure Examples are also New, from the New Help File, How do these translate to Autoit, I noticed the struct type defs are sometimes different in Bass/CD.AU3

BASS_CD_TOC Structure Used with BASS_CD_GetTOC to retrieve the TOC from a CD:

typedef struct {
    WORD size;
    BYTE first;
    BYTE last;
    BASS_CD_TOC_TRACK tracks[100];
} BASS_CD_TOC;

another example (I don't know what this UNION is, & I couldnt see it in the VB defs above, so really confused now)

typedef struct {
    BYTE res1;
    BYTE adrcon;
    BYTE track;
    BYTE res2;
    union {
        DWORD lba;
        BYTE hmsf[4];
    };
} BASS_CD_TOC_TRACK;

Does anyone know how to translate it to Autoit?

Thanks for reading this, All the best.

Posted

Ahh Eukalyptus man, you are awesome friend, thank you so much, sounds like you should update the version to 10.01!

I am learning programming with Autoit as a hobby, & people like you guys are an inspiration for me.

Thanks a LOT for everything.

Posted

Eukalyptus, I will be going through it at my own pace, & hopefully get some insight using it,

is it ok I report some feedback to you here?

Posted (edited)

Eukalyptus, just plain great !

some feedback:

This should be added to the BASSCDconstants.au3 , to check for C2 support.

Global Const $BASS_CD_RWFLAG_READC2 = 0x10000000

Just tried the _BASS_CD_GetTOC , works great,If I am using $mode=$BASS_CD_TOC_TIME,

however if set to 0 the LBA output seems wrong, & I am not sure it is a problem in the Dll, maybe you can have a look,

insert a CD, & tell me if you get something like:

[00]    |10     |1  |9  |           |   |   |
                                LBA 
                                ------
[01]    |1      |0  |1  |0          |   |   |           
[02]    |1      |0  |2  |130        |   |   |
[03]    |1      |0  |3  |220        |   |   |
[04]    |1      |0  |4  |320        |   |   |
[05]    |1      |0  |5  |450        |   |   |
[06]    |1      |0  |6  |564        |   |   |
[07]    |1      |0  |7  |665        |   |   |
[08]    |1      |0  |8  |752        |   |   |
[09]    |1      |4  |9  |948        |   |   |

[10]    |1      |4  |170    |1274   |   |   |

even weirder, try $mode=-1 to get some different but still funky results from the dll.

Edited by Zip
Posted (edited)

Eukalyptus, great, that fixed it !

On another matter: Saving BassCD stream to wave file:

1. I was looking through the examples & functions, could not find/figure out a method to save to file.

-However, I did note the _Bass_Encode functions, & using em I did work something out, but I have to use _BASS_Init for it to work.

Q: As from what I managed to figure out so far, _BASS_Init, initializes an output device,

is this a must when only trying to save a cd track to wav file without playing it?

Q: Is that the right way to do it using _BASS_Encode ? what is the best way to go about saving the cd stream to a wave file ?

2. If you noticed there are now BASS_CD_StreamCreateEx, BASS_CD_StreamCreateFileEx , both capable of optionally providing a callback function to receive sub-channel data and/or C2 error info.

so effectively this will enable seeing C2 errors on the fly, potentially displayed graphically in a gui, when reading a cd track,

seeing your excellent examples, maybe you can figure out an example that can help me figure out how to go about it, thanks!

Edited by Zip
Posted

You can call _BASS_Init with device = 0 (= no sound)

there are more ways to rip an audiotrack from CD to wav.

example 1 using BassEnc:

#AutoIt3Wrapper_UseX64=n
#include "BASSCD.au3"
#include "BassEnc.au3"

HotKeySet("{ESC}", "_Exit")

Global $tBuffer = DllStructCreate("byte[100000]")
Global $pBuffer = DllStructGetPtr($tBuffer)
Global $iBuffer = DllStructGetSize($tBuffer)

_BASS_Startup()
_BASS_CD_Startup()
_BASS_Encode_Startup()

_BASS_Init(0, 0, 44100)

Global $hStream = _BASS_CD_StreamCreate(0, 0, $BASS_STREAM_DECODE)
Global $hEncoder = _BASS_Encode_Start($hStream, "Test.wav", $BASS_ENCODE_PCM)

Global $iLength = _BASS_ChannelGetLength($hStream, $BASS_POS_BYTE)
Global $iRead = 0

While _BASS_ChannelIsActive($hStream)
    $iRead += _BASS_ChannelGetData($hStream, $pBuffer, $iBuffer)
    If @error Then ExitLoop
    ConsoleWrite("> " & Floor($iRead * 100 / $iLength) & "% done" & @CRLF)
WEnd
_Exit()

Func _Exit()
    _BASS_Encode_Stop($hEncoder)
    _BASS_StreamFree($hStream)
    _BASS_Free()
    Exit
EndFunc   ;==>_Exit

example 2 using BassExt:

#AutoIt3Wrapper_UseX64=n
#include "BASSCD.au3"
#include "BassExt.au3"

HotKeySet("{ESC}", "_Exit")

_BASS_Startup()
_BASS_CD_Startup()
_BASS_EXT_Startup()

_BASS_Init(0, 0, 44100)

Global $hStream = _BASS_CD_StreamCreate(0, 0, $BASS_STREAM_DECODE)
Global $iLength = _BASS_ChannelGetLength($hStream, $BASS_POS_BYTE)

Global $tBuffer = DllStructCreate("byte[" & $iLength & "]")
Global $pBuffer = DllStructGetPtr($tBuffer)
Global $iBuffer = DllStructGetSize($tBuffer)

ConsoleWrite("! please wait - reading audiotrack to memory..." & @CRLF)
_BASS_ChannelGetData($hStream, $pBuffer, $iBuffer)

ConsoleWrite("! saving wav file to disk..." & @CRLF)
Global $tWave = _BASS_EXT_MakeWave(DllStructGetData($tBuffer, 1))
_BASS_EXT_SaveWave($tWave, "Test2.wav")

_Exit()

Func _Exit()
    _BASS_StreamFree($hStream)
    _BASS_Free()
    Exit
EndFunc   ;==>_Exit

to get the percentage in the second example you could use a smaller buffer (like example 1) and fill the data to the final buffer

(_Bass_Ext_MemoryBuffer-functions...)

and you can write your own callback function to write the wav file - take a look at "Using callback functions" in the help file

E

Posted

Great examples, thank you so much.

I will definitely read & try to learn the call back functions & get a grip on it.

Now that I have your examples I'll try figuring out a way to use the BASS_CD_StreamCreateEx, BASS_CD_StreamCreateFileEx ,

both capable of optionally providing a callback function to receive sub-channel data and/or C2 error info.

My goal is:

I am hoping to get something working for seeing C2 errors on the fly,when reading a cd track,

so further action can be decided upon the error count, etc.

I will try to figure out a way to do it graphically (displaying C2 read errors/correction graphically in a gui), after learning how to use the callback function for it as you advised.

Eternal gratitude Eukalyptus!

Posted (edited)

example to get C2 errors while ripping to wav (or mp3/ogg...):

#AutoIt3Wrapper_UseX64=n
#include "BASSCD.au3"
#include "BassEnc.au3"

HotKeySet("{ESC}", "_Exit")

Global $sC2_Error = ""

Global $tBuffer = DllStructCreate("byte[235200]")
Global $pBuffer = DllStructGetPtr($tBuffer)
Global $iBuffer = DllStructGetSize($tBuffer)

_BASS_Startup()
_BASS_CD_Startup()
_BASS_Encode_Startup()

_BASS_Init(0, 0, 44100)

Global $hProc = DllCallbackRegister("_Callback", "none", "dword;int;dword;ptr;dword;ptr")

Global $hStream = _BASS_CD_StreamCreateEx(0, 0, BitOR($BASS_STREAM_DECODE, $BASS_CD_C2ERRORS), DllCallbackGetPtr($hProc))
Global $hEncoder = _BASS_Encode_Start($hStream, "Test3.wav", $BASS_ENCODE_PCM)

Global $iLength = _BASS_ChannelGetLength($hStream, $BASS_POS_BYTE)
Global $iRead = 0

While _BASS_ChannelIsActive($hStream)
    $iRead += _BASS_ChannelGetData($hStream, $pBuffer, $iBuffer)
    If @error Then ExitLoop
    ConsoleWrite("> " & Floor($iRead * 100 / $iLength) & "% done" & @CRLF)
WEnd

_Exit()

Func _Callback($hHandle, $iPos, $iType, $pBuffer, $iLength, $pUser)
    If $iType <> $BASS_CD_DATA_C2 Then Return
    Local $iC2_Count = $iLength / 296 ; <- a C2-error block is 296 bytes long (2352 bytes audiodata / 8 bit)
    ;ConsoleWrite($iC2_Count & " C2_error_infos in buffer" & @CRLF)
    Local $tC2, $iOR_Byte, $iPos_Bytes
    For $i = 0 To $iC2_Count - 1
        $tC2 = DllStructCreate("byte[296];", $pBuffer + $i * 296) ; <- create struct to read from the buffer
        $iOR_Byte = DllStructGetData($tC2, 1, 295) ; <- if OR-Byte is set then there was an error in the last 2352 bytes of audiodata
        If $iOR_Byte Then
            For $j = 1 To 294 ; <- check every byte
                $iByte = DllStructGetData($tC2, 1, $j)
                If $iByte Then
                    For $k = 0 To 7 ; <- check every bit in the byte
                        If BitAND($iByte, 2^$k) Then
                            $iPos_Bytes = $iPos + $i * 2352 + ($j - 1) * 8 + $k ; <- calc position of error
                            $sC2_Error &= "C2 error at byte: " & $iPos_Bytes & " / " & _Sec2Time(_BASS_ChannelBytes2Seconds($hHandle, $iPos_Bytes)) & @CRLF
                        EndIf
                    Next
                EndIf
            Next
        EndIf
    Next
EndFunc   ;==>_Callback


Func _Sec2Time($fSec)
    Local $iSec, $iH, $iM, $iS, $iC
    $iSec = Floor($fSec)
    $iC = Floor(($fSec * 100) - ($iSec * 100))
    $iH = Int($iSec / 3600)
    $iSec = Mod($iSec, 3600)
    $iM = Int($iSec / 60)
    $iS = Mod($iSec, 60)
    Return StringFormat("%02s:%02s:%02s,%02s", $iH, $iM, $iS, $iC)
EndFunc   ;==>_Sec2Time


Func _Exit()
    _BASS_Encode_Stop($hEncoder)
    _BASS_StreamFree($hStream)
    DllCallbackFree($hProc)
    _BASS_Free()

    Local $hFile = FileOpen(@ScriptDir & "\C2_Error.txt", 2)
    FileWrite($hFile, $sC2_Error)
    FileClose($hFile)
    ShellExecute(@ScriptDir & "\C2_Error.txt")

    Exit
EndFunc   ;==>_Exit

E

Edited by eukalyptus
Posted

wow, I have to give this a try, looking at your example, I see how far I am from getting anywhere near this,

Thank you so much Eukalyptus! :)

Posted

I saw this nice example in example folder of BASS-UDF v10, see link above by eukalyptus.

This is the code:

#AutoIt3Wrapper_UseX64=n
#include "Bass.au3"
#include "BassMix.au3"
#include "BassFX.au3"

Opt("GUIOnEventMode", 1)

$iFreq_Lo = 500; Hz
$iFreq_Hi = 5000; Hz

HotKeySet("{ESC}", "_Exit")

$hGui = GUICreate("BassMix/FX Test", 200, 100)
GUISetOnEvent(-3, "_EXIT")
$hCB_Lo = GUICtrlCreateCheckbox("Play Low Frequency", 10, 10, 180, 20)
GUICtrlSetState(-1, 1)
GUICtrlSetOnEvent(-1, "_Enable")
$hCB_Mi = GUICtrlCreateCheckbox("Play Mid Frequency", 10, 40, 180, 20)
GUICtrlSetState(-1, 1)
GUICtrlSetOnEvent(-1, "_Enable")
$hCB_Hi = GUICtrlCreateCheckbox("Play High Frequency", 10, 70, 180, 20)
GUICtrlSetState(-1, 1)
GUICtrlSetOnEvent(-1, "_Enable")
GUISetState()

$sFile = FileOpenDialog("Open...", "..\audiofiles", "playable formats (*.MP3;*.MP2;*.MP1;*.OGG;*.WAV;*.AIFF;*.AIF)")

_BASS_Startup()
_BASS_MIX_Startup()
_BASS_FX_Startup()

_BASS_Init(0, -1, 44100, 0, "")

$hStream = _BASS_StreamCreateFile(False, $sFile, 0, 0, $BASS_STREAM_DECODE)

$hSplit_Lo = _BASS_Split_StreamCreate($hStream, $BASS_STREAM_DECODE, 0)
$hLFX_Lo = _BASS_ChannelSetFX($hSplit_Lo, $BASS_FX_BFX_BQF, 1)
_BASS_FXSetParameters($hLFX_Lo, $BASS_BFX_BQF_LOWPASS & "|" & $iFreq_Lo & "|0|1|0|0|" & $BASS_BFX_CHANALL)

$hSplit_Mi = _BASS_Split_StreamCreate($hStream, $BASS_STREAM_DECODE, 0)
$hLFX_MiLo = _BASS_ChannelSetFX($hSplit_Mi, $BASS_FX_BFX_BQF, 1)
_BASS_FXSetParameters($hLFX_MiLo, $BASS_BFX_BQF_HIGHPASS & "|" & $iFreq_Lo & "|0|1|0|0|" & $BASS_BFX_CHANALL)
$hLFX_MiHi = _BASS_ChannelSetFX($hSplit_Mi, $BASS_FX_BFX_BQF, 1)
_BASS_FXSetParameters($hLFX_MiHi, $BASS_BFX_BQF_LOWPASS & "|" & $iFreq_Hi & "|0|1|0|0|" & $BASS_BFX_CHANALL)

$hSplit_Hi = _BASS_Split_StreamCreate($hStream, $BASS_STREAM_DECODE, 0)
$hLFX_Hi = _BASS_ChannelSetFX($hSplit_Hi, $BASS_FX_BFX_BQF, 1)
_BASS_FXSetParameters($hLFX_Hi, $BASS_BFX_BQF_HIGHPASS & "|" & $iFreq_Hi & "|0|1|0|0|" & $BASS_BFX_CHANALL)

$aInfo = _BASS_ChannelGetInfo($hStream)
$hMixer = _BASS_Mixer_StreamCreate($aInfo[0], $aInfo[1], 0)
_BASS_Mixer_StreamAddChannel($hMixer, $hSplit_Lo, 0)
_BASS_Mixer_StreamAddChannel($hMixer, $hSplit_Mi, 0)
_BASS_Mixer_StreamAddChannel($hMixer, $hSplit_Hi, 0)

_BASS_ChannelPlay($hMixer, True)


While _BASS_ChannelIsActive($hMixer)
    Sleep(10)
WEnd

_Exit()

Func _Enable()
    Switch @GUI_CtrlId
        Case $hCB_Lo
            If GUICtrlRead($hCB_Lo) = 1 Then
                _BASS_ChannelSetAttribute($hSplit_Lo, $BASS_ATTRIB_VOL, 1)
            Else
                _BASS_ChannelSetAttribute($hSplit_Lo, $BASS_ATTRIB_VOL, 0)
            EndIf
        Case $hCB_Mi
            If GUICtrlRead($hCB_Mi) = 1 Then
                _BASS_ChannelSetAttribute($hSplit_Mi, $BASS_ATTRIB_VOL, 1)
            Else
                _BASS_ChannelSetAttribute($hSplit_Mi, $BASS_ATTRIB_VOL, 0)
            EndIf
        Case $hCB_Hi
            If GUICtrlRead($hCB_Hi) = 1 Then
                _BASS_ChannelSetAttribute($hSplit_Hi, $BASS_ATTRIB_VOL, 1)
            Else
                _BASS_ChannelSetAttribute($hSplit_Hi, $BASS_ATTRIB_VOL, 0)
            EndIf
    EndSwitch
EndFunc   ;==>_Enable


Func _Exit()
    _BASS_ChannelStop($hMixer)
    _BASS_Free()
    Exit
EndFunc   ;==>_Exit

What I want is to use instead of

$hStream = _BASS_StreamCreateFile(False, $sFile, 0, 0, $BASS_STREAM_DECODE)
a stream already created but without $BASS_STREAM_DECODE, like this:

$hStream = _BASS_StreamCreateFile(False, $sFile, 0, 0, 0)

Is this possible?

Posted

Hmm...

I don´t know how to explain :)

If you use a stream, that get it´s samples from another stream, then the source-stream has to be a decoding-channel

only "top-level-streams" are non-decoding

e.g.:

$hStream = _BASS_StreamCreateFile(False, $sFile, 0, 0, $BASS_STREAM_DECODE)
$hTempo = _BASS_FX_TempoCreate($hStream, $BASS_SAMPLE_LOOP)

_BASS_ChannelPlay($hTempo, 1)

$hTempo is playing and gets the sampledata from $hStream, so $hStream has to be a decoding-channel, otherwise not all of it´s samples would reach $hTempo

If you want to add a tempo effect to the example above, the tempo-stream has to be a decoding-channel too!

$hStream -> $hTempo -> $hSplit

$hSplit is playing, reading the samples from $hTempo and $hTempo is reading from $hStream

What do you want to do?

maybe there is another solution

it´s possible to read the samples from the source (non decoding) stream and put to another - see _Bass_ChannelGetData and _Bass_StreamPutData

E

Posted

I have created a MP3 player using bass.dll functions but after I saw so nice examples I want to make it more nice by add the possiblity to play just low, middle or high freq. The problem is that my code it's now very complicated using a global variable that store the handle of playing stream. I was thining to use the same stream to not have to stop the main stream and create another one for this task but now I understand how things works and I will try to find a good solution for my project. Thanks for your help.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...