25 May '13 - 08:10 *
Welcome, Guest. Please login or register.
Did you miss your activation email?

Login with username, password and session length
 
   Home   Help Search Login Register  
Pages: 1 2 [All]
  Reply  |  Print  
Author Topic: Determine level in RecordProc via ChannelGetLevel or buffer?  (Read 9652 times)
tmighty
Posts: 27


« on: 14 Jan '06 - 17:30 »
Reply with quoteQuote

Hello again!
I am a step further, but I still have a question please:
I am checking the volume of mic-input in RecordProc-Callback.
My question is:
Shall I call BASS_ChannelGetLevel(chan) in the RecordProc function or can I see the volume in the "buffer"-data? If I can get it from the buffer data, how do I get the volume from it? I am a VB6 programmer, so I won't be able to understand C oder Delphi or so, because data types are completely different in these languages. But if someone knows how to get the volume in VB6 without calling GetLevel then it would be very nice.
I think calling GetLevel is just to slow, right?
Thank you!
Tmighty.
Logged
Sebastian Andersson
Posts: 372


« Reply #1 on: 14 Jan '06 - 20:40 »
Reply with quoteQuote

http://www.un4seen.com/forum/?topic=4804.msg32055#msg32055

That code would probably look like this in VB (quick and non-tested code):

Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (ByRef Destination As Any, ByRef Source As Any, ByVal length As Long)

Dim maxL As Single, maxR As Single
Dim b() As Single
ReDim b(length / 4) As Single
maxL = 0.0: maxR = 0.0
Dim l4 As Long, a As Long
l4 = length / 4

CopyMemory b(0), ByVal buffer, length

For a = 0 To (length / 4) - 1
    If (a Mod 2) = 0 Then
        If b(a) > maxL Then maxL = b(a)
    Else
        If b(a) > maxR Then maxR = b(a)
    End If
Next a
Logged
tmighty
Posts: 27


« Reply #2 on: 14 Jan '06 - 21:29 »
Reply with quoteQuote

Thank you very much for your code.
I have changed Single to Long and now it works very good I think!
Dim maxL As Long, maxR As Long
Dim b() As Long
ReDim b(length / 4) As Long
maxL = 0#: maxR = 0#
Dim l4 As Long, a As Long

I have encountered a memory error which is likely to be VB6's fault and I have seen a thread with a way around it.

TM.
« Last Edit: 14 Jan '06 - 21:44 by tmighty » Logged
Sebastian Andersson
Posts: 372


« Reply #3 on: 14 Jan '06 - 23:32 »
Reply with quoteQuote

Thank you very much for your code.
I have changed Single to Long and now it works very good I think!

It depends if you specified BASS_SAMPLE_FLOAT in the BASS_RecordStart call. If you have, BASS will "send" floating-point data to your callback, and you should use a Single data type to get correct sample values. If not, Integer/Long will be fine as BASS defaults to 16-bit if neither BASS_SAMPLE_8BITS or BASS_SAMPLE_FLOAT is specified.

Just a little explanation. Smiley
Logged
tmighty
Posts: 27


« Reply #4 on: 15 Jan '06 - 00:04 »
Reply with quoteQuote

Hi and thanks for answering!
I am so confused about all the Pointers and stuff and I still have a big problem:
Every time I do something that forces VB to do something in the RecordProc it crashes down with an exception error of the memory kind.
Can you please tell me if this is the known callback safety problem or am I doing something very wrong?
For example:
If am checking the volume while in the RecordProc function -> It crashes with the memory error...
I am confused, desperate and angry...
Thank you for your help!
Tmighty.
Logged
Sebastian Andersson
Posts: 372


« Reply #5 on: 15 Jan '06 - 00:43 »
Reply with quoteQuote

Well, did you find out where it's crashing? I just wrapped up a quick test based on the DSPTEST example, and it worked fine (it atleast returned maxL/R values). Post your "RecordProc" callback.
Logged
tmighty
Posts: 27


« Reply #6 on: 15 Jan '06 - 01:37 »
Reply with quoteQuote

Hello.
This is my callback code:
Public Function RECORDPROC(ByVal handle As Long, ByVal buffer As Long, ByVal length As Long, ByVal user As Long) As Long

Dim maxL As Long, maxR As Long
Dim b() As Long
ReDim b(length / 4) As Long
maxL = 0#: maxR = 0#
Dim l4 As Long, a As Long
l4 = length / 4

CopyMemory b(0), ByVal buffer, length

For a = 0 To (length / 4) - 1
    If (a Mod 2) = 0 Then
        If b(a) > maxL Then maxL = b(a)
    Else
        If b(a) > maxR Then maxR = b(a)
    End If
Next a

Debug.Print maxR

End Function

And this is how I start the initializing and recording:

Public Sub StartEverything()

    'change and set the current path
    'so VB won't ever tell you that bass.dll isn't found
    ChDrive App.Path
    ChDir App.Path

    'check if bass.dll is exists
    If (Not FileExists(RPP(App.Path) & "bass.dll")) Then
        Call MsgBox("BASS.DLL does not exists", vbCritical, "BASS.DLL")
        End
    End If

    'Check that BASS 2.2 was loaded
    If (BASS_GetVersion <> MakeLong(2, 2)) Then
        Call MsgBox("BASS version 2.2 was not loaded", vbCritical, "Incorrect BASS.DLL")
        End
    End If

    'initialize BASS recording (default device)
    If (BASS_RecordInit(-1) = 0) Then
        MsgBox "Can't initialize device"
        End
    End If

    'start recording (44100hz mono 16-bit)
    chan = BASS_RecordStart(44100, 1, 0, AddressOf RECORDPROC, 0)
    If chan = 0 Then
        MsgBox "Can't start recording"
        End
    End If


'/////////////////////////////////////////////////////////////////////////////////////
'This is the code of the LiveSpectrum, which I wanted to have within my app

    specpos = 0
    specmode = False
    SPECWIDTH = 368
    SPECHEIGHT = 127

    'create bitmap to draw spectrum in - 8 bit for easy updating :)
    With bh.bmiHeader
        .biSize = Len(bh.bmiHeader)
        .biWidth = SPECWIDTH
        .biHeight = SPECHEIGHT  'upside down (line 0=bottom)
        .biPlanes = 1
        .biBitCount = 8
        .biClrUsed = 256
        .biClrImportant = 256
    End With

    Dim a As Byte

    'setup palette
    For a = 1 To 127
        bh.bmiColors(a).rgbGreen = 255 - 2 * a
        bh.bmiColors(a).rgbRed = 2 * a
    Next a
    For a = 0 To 31
        bh.bmiColors(128 + a).rgbBlue = 8 * a
        bh.bmiColors(128 + 32 + a).rgbBlue = 255
        bh.bmiColors(128 + 32 + a).rgbRed = 8 * a
        bh.bmiColors(128 + 64 + a).rgbRed = 255
        bh.bmiColors(128 + 64 + a).rgbBlue = 8 * (31 - a)
        bh.bmiColors(128 + 64 + a).rgbGreen = 8 * a
        bh.bmiColors(128 + 96 + a).rgbRed = 255
        bh.bmiColors(128 + 96 + a).rgbGreen = 255
        bh.bmiColors(128 + 96 + a).rgbBlue = 8 * a
    Next a

    'update timer 25ms (40hz)
    MainForm.tmrLiveSpec.Enabled = True
   
End Sub
Thank you for your help.
It always crashes, sometimes even if I only do something like:
Public Function RECORDPROC(ByVal handle As Long, ByVal buffer As Long, ByVal length As Long, ByVal user As Long) As Long

dim myNumber%
myNumber = 1
myNumber = myNumber+1

End Function

Thanks again.
Tm.
Logged
Sebastian Andersson
Posts: 372


« Reply #7 on: 15 Jan '06 - 13:41 »
Reply with quoteQuote

You're getting a crash because you're attempting to loop through stereo samples.

length / 4

This is the same as length / 2 (sample byte size, 16/8) / 2 (stereo, 2 channels).

I noticed you're recording in mono, so length / 4 has to be length / 2. I've uploaded an example for you to have a look at.

     http://www.un4seen.com/filez/2/volume.zip
Logged
tmighty
Posts: 27


« Reply #8 on: 15 Jan '06 - 14:40 »
Reply with quoteQuote

Thank you very much!
I have noticed the following:
VB users should not or can not test their applications within the ide/ debugmode or they will be getting crahes (also in your very kind sample extra written for me!)
There is this safecallback.dll, but it doesn't help in my case, application still crashes if not compiled.
But: If the project is being run as a compiled exe, it works fine (at last on my computer).
So this is a bit of a pitty, and I would like to know why this is so and what I can do to prevent this.
A question for the PROs:
Is it the same as with the HighResolution Media Timers where the timer fires faster than VB can process it?
Thanks for all the help so far again!
TM.
Logged
Sebastian Andersson
Posts: 372


« Reply #9 on: 15 Jan '06 - 14:51 »
Reply with quoteQuote

It could be due to BASS being multithreaded, and VB isn't. I'm never running my applications in the VB IDE unless a VB run-time error occurs and I can trace it in my code.
Logged
(: JOBnik! :)
Posts: 984


« Reply #10 on: 15 Jan '06 - 15:07 »
Reply with quoteQuote

Hi Grin

VB is very sensitive and always crashes if using a debugging in IDE mode (using CALLBACKS).
Multimedia Timer will sometimes crash in IDE and sometimes in Compiled modes, but will always crash in IDE DEBUG as it uses a CALLBACK function, the same with a simple API timer.
There might be a crash if your CPU has HT as well, as VB doesn't has support for it.
Logged
tmighty
Posts: 27


« Reply #11 on: 15 Jan '06 - 15:13 »
Reply with quoteQuote

! Oh yes !  Grin
I'm getting there!! VB hasn't crashed for 5 minutes now!!! Maybe it really was because of the "4" thingy which you have corrected to "2"...
Logged
tmighty
Posts: 27


« Reply #12 on: 15 Jan '06 - 16:01 »
Reply with quoteQuote

Okay, everything's fine so far, except that I wanted to call BassEnc in RECORDPROC if maxL is over my threshhold level.   Sad
What I did was:

Public Function RECORDPROC(ByVal handle As Long, ByVal buffer As Long, ByVal length As Long, ByVal user As Long) As Long

RECORDPROC = BASSTRUE

Dim maxL As Long, maxR As Long
Dim b() As Integer
ReDim b(length / 2) As Integer
maxL = 0: maxR = 0
Dim l2 As Long, a As Long
l2 = length / 2

CopyMemory b(0), ByVal buffer, length

For a = 0 To l2 - 1
    If (a Mod 2) = 0 Then
        If b(a) > maxL Then maxL = b(a)
    Else
        If b(a) > maxR Then maxR = b(a)
    End If
Next a

iLevel = maxR

If iLevel > 20000 Then
    If bEncoding = False Then 'If not already encoding
        bStartEncoding = True 'remember than encoding shall start
        Call DoBassEncStartEncoding 'call the sub that starts encoding
    End If
End If
End Function

And the "DoBassEncStartEncoding" looks like this:
Public Sub DoBassEncStartEncoding()

If bEncoding = True Then Exit Sub 'Encoding already started, so exit!
If bStartEncoding = True Then bStartEncoding = False'Remember that encoding was started

    'free old recording
    If (chan) Then
        Call BASS_StreamFree(chan)
        chan = 0
        MainForm.btnPlay.Enabled = False
    End If

    'Start recording @ 44100hz 16-bit stereo (paused to add encoder first)
    rchan = BASS_RecordStart(44100, 2, BASS_RECORD_PAUSE, AddressOf ENCODEPROC, 0)

    'Set command-lines and output files
    commands(1) = "lame.exe --alt-preset standard - bass.mp3"   ' lame (MP3)
    files(1) = "bass.mp3"

    'start encoding
    'get selected encoder (0=OGG, 1=MP3)
    encoder = 1

'(Some error handling which I delete for better reading)       

    'resume recoding
    Call BASS_ChannelPlay(rchan, BASSFALSE)
    MainForm.btnRecord.Caption = "Stop"
End Sub

Every time this "DoBassEncStartEncoding" is started it gives me a bad "memory read" error. Can you tell me what is wrong with my code?
Oh I forgot the purpose: Every time a certain level from the Mic-Input is received it shall start encoding a phrase from the Mic to mp3. The end of the phrase will be checked in the ENCODEPROC function.

Thanks.
Tm.
Logged
Sebastian Andersson
Posts: 372


« Reply #13 on: 15 Jan '06 - 16:54 »
Reply with quoteQuote

Do you want to overwrite the MP3 file everytime the threshold level has been reached? If not, you're better off using an EncodeProc and appending the encoded sample data to the MP3 file.

Edit:

I can't see any call to BASS_Encode_Start. Did you post the complete "DoBassEncStartEncoding" procedure?
« Last Edit: 15 Jan '06 - 17:21 by Sebastian Andersson » Logged
tmighty
Posts: 27


« Reply #14 on: 15 Jan '06 - 17:23 »
Reply with quoteQuote

This is something I did not include:
The name of the mp3 is being received from somewhere else, so there's no chance of overwriting. I just did not include that piece of code...
Can you help with the crash, anyways? Is there something terribly wrong?
I need to keep that one channel (=chan) for displaying a spectrum. So there is no problem in defining just another channel for encoding (=rchan), right?
Greetings,
TM.
« Last Edit: 15 Jan '06 - 17:26 by tmighty » Logged
Sebastian Andersson
Posts: 372


« Reply #15 on: 15 Jan '06 - 17:25 »
Reply with quoteQuote

Well, yes, if you include the call to BASS_Encode_Start, I might wrap up an example and see if it crashes on my side.
Logged
tmighty
Posts: 27


« Reply #16 on: 15 Jan '06 - 17:29 »
Reply with quoteQuote

It's just the function from the BassEnc.dll!

I have quoted the sub that calls this Function in the last post.


Declare Function BASS_Encode_Start Lib "bassenc.dll" (ByVal chan As Long, ByVal cmdline As String, ByVal flags As Long, ByVal proc As Long, ByVal user As Long) As Long

Sorry, our posts got mixed up. You were too quick!
Hope it's all still understandable!
« Last Edit: 15 Jan '06 - 17:31 by tmighty » Logged
Sebastian Andersson
Posts: 372


« Reply #17 on: 15 Jan '06 - 17:48 »
Reply with quoteQuote

Yes, but you have to call it to start encoding. Are you actually doing that?
Logged
tmighty
Posts: 27


« Reply #18 on: 15 Jan '06 - 18:41 »
Reply with quoteQuote

Oh sorry, I deleted it (only in my posting, not in my actual code). The real code I'm using is:
    'get selected encoder (0=OGG, 1=MP3)
    encoder = 1

       
    If (BASS_Encode_Start(rchan, commands(encoder), 0, 0, 0) = 0) Then
        Call Error_("Couldn't start encoding..." & vbCrLf & _
            "Make sure OGGENC.EXE (if encoding to OGG) is in the same" & vbCrLf & _
            "direcory as this RECTEST, or LAME.EXE (if encoding to MP3).")
        Call BASS_ChannelStop(rchan)
                rchan = 0
            End
        Exit Sub
    End If

    'resume recoding
    Call BASS_ChannelPlay(rchan, BASSFALSE)

I don't know what's still wrong  Huh
Logged
Ian @ un4seen
Administrator
Posts: 15276


« Reply #19 on: 15 Jan '06 - 19:14 »
Reply with quoteQuote

I'd suggest you start with simply using BASS_ChannelGetLevel, as in this thread. Once you get that working, then you could try scanning the level yourself. Note though that BASS_ChannelGetLevel is pretty quick, and will almost certainly be faster than scanning the level yourself, particularly in VB Smiley

Btw, it appears that you're recording twice from the device - once just to scan the level, and again to record/encode. Note that will only work on XP. I'm not sure you really want (or need Smiley) to do that. Also, as you're dealing with mono sample data, there's no need to have separate maxL/maxR readings when scanning the data.
Logged
Sebastian Andersson
Posts: 372


« Reply #20 on: 15 Jan '06 - 19:17 »
Reply with quoteQuote

You basically put this in the EncodeProc (assuming you've setup an encoder on the recording channel):

If maxR > 20000 Then
    BASS_Encode_Write chan, buffer, length
End If

VolumeProc = BASSTRUE ' This is necessary to continue recording

Note that I used the BASS_ENCODE_PAUSE flag in my BASS_Encode_Start call (when testing this).
Logged
tmighty
Posts: 27


« Reply #21 on: 15 Jan '06 - 20:24 »
Reply with quoteQuote

Talking about RECORDPROC....
do you really mean
VolumeProc = BASSTRUE ' This is necessary to continue recording

or
RECORDPROC = BASSTRUE ' This is necessary to continue recording
?
Knowing that will assumably prevent me from more mistakes.
For the 1000. times: Thank you!

Logged
Sebastian Andersson
Posts: 372


« Reply #22 on: 15 Jan '06 - 20:33 »
Reply with quoteQuote

Yes, I meant "RecordProc". I've just renamed the one in my example to "VolumeProc".
« Last Edit: 15 Jan '06 - 20:42 by Sebastian Andersson » Logged
tmighty
Posts: 27


« Reply #23 on: 16 Jan '06 - 11:28 »
Reply with quoteQuote

.
« Last Edit: 16 Jan '06 - 14:23 by tmighty » Logged
Sebastian Andersson
Posts: 372


« Reply #24 on: 16 Jan '06 - 14:24 »
Reply with quoteQuote

If I want to display the spectrum even while encoding, can I use the recording "chan" also to encode?

It worked in my example, so I guess you can.

Why is the encoding working with streams? I know it's only a FreeStream command, but anyway I would to know why this is done:
    'free old recording
    If (chan) Then
        Call BASS_StreamFree(chan)
        chan = 0
        MainForm.btnPlay.Enabled = False
    End If
Why is it saying:
Call BASS_StreamFree(chan)

and not
Call BASS_Free(chan)
Huh

BASS_StreamFree is used to free the resources (memory etc) for a particular stream. BASS_Free is used to free all resources within BASS, that is, all streams (and the memory they allocate), the device initialized and anything else that has been created by BASS. BASS_Free should only be called when an application is about to close. BASS_Free doesn't require any parameter, by the way.

And why would you start encoding directly in RECORDPROC (or VOLUMEPROC) and not just call a function which starts the encoding. I thought that RECORDPROC has to be quick.

I'm pretty much doing what Ian does in the link he posted, I start a paused encoder on the recording channel, and if the threshold level has been reached in the RecordProc, I simply (manually) write the sample data to the encoder - it's fairly quick and simple. There are of course other methods, but this one does the job.
Logged
tmighty
Posts: 27


« Reply #25 on: 16 Jan '06 - 14:34 »
Reply with quoteQuote

Okay, I think I'm starting to understand this. I must admit I went into this with wrong ideas of how easy it is. Or maybe it's just complicated for a bass newbie like me.
Could you post the code of how you start the encoding in pause mode? If it's in the sample apps then I am blind. I know I could figure it out in 1 hour from the manual, but if you have it already then it would be nice of you to post it for me (and maybe all VBlers who read this post because they are struggeling with the same problem).
Thanks for the time you spent here!
TM.

(Sorry I delete my last post, because I thought it was stupid).

One more thing:
Is that correct? ->
The good thing about the RecordProc is that is it being automatically fired when the buffer (lets say sound buffer of 5 ms) is full. And in the RecordProc we can check the level/ volume of the buffer. So this is why it's better than a timer which fires not often detect to e.g. detect a high level early enough to detect the first letter of a spoken word.
But when we check the volume in the RecordProc we can be sure that me are missing nothing! And we can even send that FIRST buffer (that has a high level) directly to the Encoding_Start, right? And this first buffer is not abandoned but directly send to the buffer. Because we do not say "Start the encoding now and record anything as soon as you are ready", but we say "Start the encoding with this buffer!".
Correct?
« Last Edit: 16 Jan '06 - 14:43 by tmighty » Logged
Sebastian Andersson
Posts: 372


« Reply #26 on: 16 Jan '06 - 15:01 »
Reply with quoteQuote

I've reuploaded my example application (same link as before).

Thanks for the time you spent here!

No worries. Smiley

Is that correct? ->
The good thing about the RecordProc is that is it being automatically fired when the buffer (lets say sound buffer of 5 ms) is full. And in the RecordProc we can check the level/ volume of the buffer. So this is why it's better than a timer which fires not often detect to e.g. detect a high level early enough to detect the first letter of a spoken word.
But when we check the volume in the RecordProc we can be sure that me are missing nothing! And we can even send that FIRST buffer (that has a high level) directly to the Encoding_Start, right? And this first buffer is not abandoned but directly send to the buffer. Because we do not say "Start the encoding now and record anything as soon as you are ready", but we say "Start the encoding with this buffer!".
Correct?

Yes. I'd call it "Initialize the encoding and write whenever threshold>=N" though.
Logged
tmighty
Posts: 27


« Reply #27 on: 16 Jan '06 - 18:41 »
Reply with quoteQuote

It all works fine now, looking at the encoding and the callbacks.
But I have encountered a serious problem which has to do with VB, I think.
There is a serious memory read error when I move my mouse, simply as that.
I do believe that this is the feared CallbackSafety problem...
And the SafeCallBack.dll doesn't help me, because I can't use the real callback anymore.  And I can only use safecallback with a timer which is a good as nothing.
I could have easily done all my work before with a timer and WITHOUT a safecallback, I guess.
So there is no point for me to try to work with the callbacks anymore because of the stupidity of VisualBasic.
That's right, isn't it? Or what else could it be that every time I do something that's a bit heavy for a computer (like moving a mouse pointer LOL  Cry) it crashes.
It's true isn't it... tell me it's not VB... PLEASE!!! Just tell me that's doing a mega mistake somewhere....

My code in the CallBack is:
Public Function RECORDPROC(ByVal handle As Long, ByVal buffer As Long, ByVal length As Long, ByVal user As Long) As Long

iLevel = LoWord(BASS_ChannelGetLevel(Chan)) 'check the record level

If bEncoderLoaded = True Then
            BASS_Encode_Write! handle, buffer, length
End If
End Function

TM.
Logged
Sebastian Andersson
Posts: 372


« Reply #28 on: 16 Jan '06 - 18:51 »
Reply with quoteQuote

My code in the CallBack is:
Public Function RECORDPROC(ByVal handle As Long, ByVal buffer As Long, ByVal length As Long, ByVal user As Long) As Long

iLevel = LoWord(BASS_ChannelGetLevel(Chan)) 'check the record level

If bEncoderLoaded = True Then
            BASS_Encode_Write! handle, buffer, length
End If
End Function

TM.

I guess the exclamation mark after BASS_Encode_Write is a typo? Anyway, you're missing the "RECORDPROC = BASSTRUE" again. Without it, BASS won't continue recording. I've not tested the SafeCallback system, so I can't really tell - but I've used callbacks in VB before, and it's all went good. Also, you probably should use the "handle" parameter in the RECORDPROC (not the "Chan" global variable):

Public Function RECORDPROC(ByVal handle As Long, ByVal buffer As Long, ByVal length As Long, ByVal user As Long) As Long

iLevel = LoWord(BASS_ChannelGetLevel(handle)) 'check the record level

If bEncoderLoaded = True Then
            BASS_Encode_Write handle, buffer, length
End If

RECORDPROC = BASSTRUE

End Function
Logged
tmighty
Posts: 27


« Reply #29 on: 16 Jan '06 - 21:09 »
Reply with quoteQuote

Hi again,
I had the RECORDPROC in my code, sorry I didn't type it in my posting.
I have changed the "(chan)" to "handle". Seems to crash less frequently...
But I have to do some more tests... thanks so far, thanks very much!
TM.
Logged
tmighty
Posts: 27


« Reply #30 on: 17 Jan '06 - 10:48 »
Reply with quoteQuote

I am totally amazed, it works. It really works without crashes WITH callbacks in VISUALBASIC (just to state that to any further VB-Programmers who are about to go crazy). I have fuddled a lot with variables, now my code looks like a dust bin. But I am going to clean it up and post it.
 Grin Grin Grin Grin Grin
Logged
Pages: 1 2 [All]
  Reply  |  Print  
 
Jump to:  

Powered by SMF 1.1.18 | SMF © 2013, Simple Machines