|
tmighty
Posts: 27
|
 |
« on: 14 Jan '06 - 17:30 » |
Quote
|
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 » |
Quote
|
http://www.un4seen.com/forum/?topic=4804.msg32055#msg32055That 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 » |
Quote
|
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 » |
Quote
|
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. 
|
|
|
|
|
Logged
|
|
|
|
|
tmighty
Posts: 27
|
 |
« Reply #4 on: 15 Jan '06 - 00:04 » |
Quote
|
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 » |
Quote
|
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 » |
Quote
|
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 » |
Quote
|
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 » |
Quote
|
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 » |
Quote
|
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 » |
Quote
|
Hi  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 » |
Quote
|
! Oh yes !  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 » |
Quote
|
Okay, everything's fine so far, except that I wanted to call BassEnc in RECORDPROC if maxL is over my threshhold level.  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 » |
Quote
|
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 » |
Quote
|
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 » |
Quote
|
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 » |
Quote
|
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 » |
Quote
|
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 » |
Quote
|
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 
|
|
|
|
|
Logged
|
|
|
|
|
Ian @ un4seen
Administrator
Posts: 15276
|
 |
« Reply #19 on: 15 Jan '06 - 19:14 » |
Quote
|
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  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  ) 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 » |
Quote
|
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 » |
Quote
|
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 » |
Quote
|
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 » |
Quote
|
.
|
|
|
|
« Last Edit: 16 Jan '06 - 14:23 by tmighty »
|
Logged
|
|
|
|
|
Sebastian Andersson
Posts: 372
|
 |
« Reply #24 on: 16 Jan '06 - 14:24 » |
Quote
|
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)  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 » |
Quote
|
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 » |
Quote
|
I've reuploaded my example application (same link as before). Thanks for the time you spent here!
No worries.  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 » |
Quote
|
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  ) 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 » |
Quote
|
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 » |
Quote
|
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 » |
Quote
|
|
|
|
|
|
Logged
|
|
|
|
|