Author Topic: VU levels - newbie  (Read 14683 times)

Sheep

  • Posts: 243
VU levels - newbie
« on: 1 May '03 - 15:07 »
Hi, I am just starting with using Bass, but did read the samples the forum and the help and I am not getting it.

I am using Delphi first of all. Ofcourse I would like to make my own music player, and want to attach a VU meter. No EQ meter but just a VU meter.

The GetLevel function does the job of the last 15 ms. I have my VU updated every on user modified rate (FPS). When I have a 3 sec file with really small peaks of 0 dB and the overal is -90dB, well you just see nothing happening in the VU meters. Also with the Get Level function even on 1 ms updates with peak fall off. So I do not get it, GetLevel is too unclear for me what it actually does.

I just want a VU meter that watches every single bit, and takes the highest every let's say 100 ms, and I make my own falloff, that's not the problem.

How do I do this, Do I have to do this in the DSP callback? or with the getData function? And Do i have to worry about syncronisize the output and the visual? due the buffer?

Can someone please help me, I am really looking for some source code, but the SampleVis is also not 100% accurate. and misses peaks all the time. Even winamp also. And for example my Minidsik deck rocks the ass, because every tiny bit will be visible if needed.

prefer Delphi, but can analyze some C code too.

Big regards Sheep

Ian @ un4seen

  • Administrator
  • Posts: 26223
Re: VU levels - newbie
« Reply #1 on: 2 May '03 - 13:54 »
The BASS_ChannelGetLevel function is just a quick'n'simple function to get the level. As you say, it can miss short peaks if not called frequently enough, and it doesn't have great resolution either (return value ranges 0-128).

Internally, BASS_ChannelGetLevel uses the BASS_ChannelGetData function. For greater control, you should use BASS_ChannelGetData yourself, and do your own processing of the sample data that provides.

Sheep

  • Posts: 243
Re: VU levels - newbie
« Reply #2 on: 3 May '03 - 11:14 »
Thank you very much Iam, could you give a small example how to get the VU level with the GetData function? Delphi or C would be great!   :idea:

GetLevel uses 15ms you mentioned in an early post, but it skippes really small 1 bit peaks. If I call it every 15 ms. And even at 1 ms. I don't know what that function does but data is missing.  :-[

Ian @ un4seen

  • Administrator
  • Posts: 26223
Re: VU levels - newbie
« Reply #3 on: 3 May '03 - 17:36 »
How are you timing the delay between the BASS_ChannelGetLevel calls? Depending on the method you're using, it's quite possible that the delay is actually longer than you requested.

Anyway, as mentioned in the docs, BASS_ChannelGetData returns standard Windows PCM sample data. So, assuming the channel is 16-bit, you could do something like this...
Code: [Select]
int a,leftpeak=0,rightpeak=0;
int len=BASS_ChannelSeconds2Bytes(handle,number_of_seconds_to_check);
short *buf=malloc(len);
len=BASS_ChannelGetData(handle,buf,len)/sizeof(short);
for (a=0;a<len;a+=2) {
     if (abs(buf[a])>leftpeak) leftpeak=abs(buf[a]);
     if (abs(buf[a+1])>rightpeak) rightpeak=abs(buf[a+1]);
}
if (BASS_ChannelGetFlags(handle)&BASS_SAMPLE_MONO)
     leftpeak=max(leftpeak,rightpeak);
free(buf);

leftpeak and rightpeak (if stereo) contain the peak sample values, between 0 and 32768. If needed, it's quite straight-forward to modify the code to also support 8 and/or 32-bit sample data.

Sheep

  • Posts: 243
Re: VU levels - newbie
« Reply #4 on: 3 May '03 - 18:53 »
Thanks Ian!

Yeah I noticed this (the delay). It's 10MS your function (getLevel) not 15. Windows 2000 does it perfect but windows 98 really needs about 30 ms to do the GetLevel function. There's the point where it gets wrong I think.

Ok I am now gonna try to convert your code to delphi.

Sheep

  • Posts: 243
Re: VU levels - newbie
« Reply #5 on: 3 May '03 - 20:24 »
What do you mean with:

Code: [Select]
)
/sizeof(short);

and

leftpeak=max(leftpeak,rightpeak);


I can't convert this to Delphi because i am not understanding what you mean.

This forum engine rocks!

Olego

  • Posts: 557
Re: VU levels - newbie
« Reply #6 on: 3 May '03 - 20:47 »
Quote

What do you mean with:

Code: [Select]
)
/sizeof(short);

and

leftpeak=max(leftpeak,rightpeak);



The first one means that BASS_ChannelGetData returns the size in bytes, and since each entry is a short int, you divide by the size of the short int to get the number of short ints and not bytes.  Sizeof(short) is 2, the last time I checked.

The second one means that if you're listening to a mono channel, then there is no rightpeak; so you should combine the two, because rightpeak is not really rightpeak, but the peak of the half of your data.  (Max macro returns the largest of two numbers.  :P)

I hope I was right.  :-)

~Olego~

Sheep

  • Posts: 243
Re: VU levels - newbie
« Reply #7 on: 4 May '03 - 09:24 »
>>
The first one means that BASS_ChannelGetData returns the size in bytes, and since each entry is a short int, you divide by the size of the short int to get the number of short ints and not bytes.  Sizeof(short) is 2, the last time I checked.
>>

Yeah ok I understand that! But short *buf=malloc(len);, as the way i translate it to delphi setlengths an array of SmallInt's of the size "len". But that's probably wrong and i get the half of the data. If I divide it by 2 things go wrong

Could someone write the code from Ian to Delphi code? Please. That would be great! This is what I have so far:


Code: [Select]


procedure IanVU;
var
 len,a,leftpeak,rightpeak : integer;
 buf : array of smallint;
begin
 a := 0;
 leftpeak := 0;
 rightpeak := 0;

 len := BASS_ChannelSeconds2Bytes(channel,VUInterval/1000);
 setlength(buf,len);

 len := BASS_ChannelGetData(channel,buf,len);
 while a < len-1 do
 begin
   if (abs(buf[a])>leftpeak) then
     leftpeak:=abs(buf[a]);
   if (abs(buf[a+1])>rightpeak) then
     rightpeak:=abs(buf[a+1]);
   inc(a,2);
 end;

 if (BASS_ChannelGetFlags(channel) and BASS_SAMPLE_MONO) = BASS_SAMPLE_MONO then
   if rightpeak > leftpeak then
     leftpeak := rightpeak;
     
 setlength(buf,0);

// display VU...


Sheep

  • Posts: 243
Re: VU levels - newbie
« Reply #8 on: 4 May '03 - 09:28 »
Thanks btw it's really great that you helped! :)

Irrational86

  • Posts: 960
Re: VU levels - newbie
« Reply #9 on: 4 May '03 - 09:46 »
Here is the code translated, not tested though..
Code: [Select]

var
 buf, tbuf: PSmallInt;
 a, leftpeak, rightpeak, len: Integer;
begin
 a := 0;
 leftpeak := 0;
 rightpeak := 0;
 len := BASS_ChannelSeconds2Bytes(handle, number_of_seconds_to_check);
 GetMem(buf, len);
 tbuf := buf;
 len := BASS_ChannelGetData(handle, buf, len) div SizeOf(SmallInt);
 while (a < len) do
 begin
   if (Abs(tbuf^) > leftpeak) then
     leftpeak := Abs(tbuf^);
   Inc(tbuf);
   if (Abs(tbuf^) > rightpeak) then
     rightpeak := Abs(tbuf^);
   Inc(tbuf);
   a := a + 2;
 end;
 if (BASS_ChannelGetFlags(handle) and BASS_SAMPLE_MONO) then
   if (rightpeak > leftpeak) then
     leftpeak := rightpeak;
 FreeMem(buf);
end;

Sheep

  • Posts: 243
Re: VU levels - newbie
« Reply #10 on: 4 May '03 - 10:37 »
Thank you, the buf seems not always to be cleared because peaks are being hold if switching fast between songs. With my translation it doesn't, but yours is probably faster due all the pointers and stuff.

Regards Sheep

Ian @ un4seen

  • Administrator
  • Posts: 26223
Re: VU levels - newbie
« Reply #11 on: 4 May '03 - 17:50 »
Quote
Yeah I noticed this (the delay). It's 10MS your function (getLevel) not 15. Windows 2000 does it perfect but windows 98 really needs about 30 ms to do the GetLevel function. There's the point where it gets wrong I think.

Are you sure about that? BASS_ChannelGetLevel shouldn't take anything like that amount of time. Timing it just now on an XP1700, each call took between 0.04 to 0.08 ms :)

Sheep

  • Posts: 243
Re: VU levels - newbie
« Reply #12 on: 4 May '03 - 18:30 »
Nope I was wrong, sorry! :p

The TTimer from Delphi can't go lower than ~25 ms on a win98 machine, that was where all the trouble came from!

Then I made a thread with a "while true" loop where I do the VU with a sleep in it. This rocks ass! :) even on windows 98. I used your code, modified by me a little and try to use the code of XMinio, but that has some bugs. I think I have one of the greatest VU's now :) Thanks!

1. A different question though, why are you using about 5 or 6 threads and takes it about 400 ms to load the whole bass system? Is this the fastest?

2. When I close my app, my cpu usage (win 98!) goes to 70%! and stays there. My app closed. Then after starting my app again, it goes back to 2%. When i close it again it goes back to 70%. I use this when closing my app:
Code: [Select]

StopMusic;
BASS_ChannelStop(channel);
BASS_Stop;
BASS_StreamFree(channel);
BASS_MusicFree(channel);
BASS_Free();       // Close digital sound system
BASS_CDFree();   // Close CD system


Is this the best I can do to make really sure the bass dll is unloaded? It also takes about 700ms to close this app. Really strange. In win2000 it takes no time at all.


Ian @ un4seen

  • Administrator
  • Posts: 26223
Re: VU levels - newbie
« Reply #13 on: 6 May '03 - 14:44 »
Quote
1. A different question though, why are you using about 5 or 6 threads

BASS only creates 2 threads (with additional threads being created when required for internet/WMA/CD streaming). Not sure where you got 5 or 6 from :)

Quote
2. When I close my app, my cpu usage (win 98!) goes to 70%! and stays there. My app closed. Then after starting my app again, it goes back to 2%. When i close it again it goes back to 70%. I use this when closing my app:
Code: [Select]

StopMusic;
BASS_ChannelStop(channel);
BASS_Stop;
BASS_StreamFree(channel);
BASS_MusicFree(channel);
BASS_Free();       // Close digital sound system
BASS_CDFree();   // Close CD system

You only really need the BASS_Free call (and BASS_CDFree if using the CD functions), but it does no harm to call that other stuff.

Does that 70% CPU thing happen with any of the precompiled BASS examples?

Sheep

  • Posts: 243
Re: VU levels - newbie
« Reply #14 on: 7 May '03 - 00:46 »
I shall check that, but it happened to my 98 system and a friend's too. But it was my application, though I think it has something todo with not closing bass sucessfully. Why is it necesary actually? doesn't delphi apps close their stuff anyway? I never use Free calls.

Regards Sheep

Ian you did a hell of a great job by making bass, really appreciate it, you've heard this a millions of time, but someone has to say it again :) Good luck with business.

Sheep

  • Posts: 243
Re: VU levels - newbie
« Reply #15 on: 8 May '03 - 18:22 »
This:
Code: [Select]
BASS_Init(-1, 44100, BASS_DEVICE_LEAVEVOL, Application.Handle);
generates 5 threads, not 2 on my system. (Delphi, Win2K)  

Is there a way to speed this Init function up? Or is it already ultra fast?

regards Sheep

Ian @ un4seen

  • Administrator
  • Posts: 26223
Re: VU levels - newbie
« Reply #16 on: 9 May '03 - 14:56 »
BASS_Init definately only creates 2 threads. The additional threads are probably created by the drivers/DirectSound.

BASS_Init is already as fast as possible. You can make the call faster by using the "no sound" device, but that's only of use if you don't need BASS to output the sound.

Sheep

  • Posts: 243
Re: VU levels - newbie
« Reply #17 on: 9 May '03 - 18:44 »
okay, thank you Ian :P

Sheep

  • Posts: 243
Re: VU levels - newbie
« Reply #18 on: 9 May '03 - 23:00 »
I found another error in the code you send me, and that is that the function getdata gives error code 20 (invalid length) with length = 3527
Really strange, 3528 seems to work and 3529 not, and 3526 doesn't work neither.

Code: [Select]

lengte := BASS_ChannelSeconds2Byte(channel,VUInterval/1000);
setlength(buf,lengte);

showmessage(inttostr(lengte));
len := BASS_ChannelGetData(channel,buf,lengte+1);


Why is "lengte" different on differnt computers. 5 computers works the code great (lengte=1763), but my new laptop Pentium 4 gives the buggy lengte = 3527 which crashes the getdata function. has it to do with primes?

You stated:
Code: [Select]

len := BASS_ChannelGetData(channel,buf,lengte) div SizeOf(SmallInt);

but that division has never worked and it crashes at th getdata function, not behind that. i also made the buffer large enough, so it's not an overflow.

Please help. I just to add the lengte with 1. But i am prety sure that's not the right way :)


Sheep

  • Posts: 243
Re: VU levels - newbie
« Reply #19 on: 9 May '03 - 23:02 »
btw.
Code: [Select]


const VUInterval = 20


Ian @ un4seen

  • Administrator
  • Posts: 26223
Re: VU levels - newbie
« Reply #20 on: 10 May '03 - 12:46 »
Oops :) ... Need to the make sure "len" (or "lengte" in your case) is equivalent to a whole number of samples. This'll make sure of that in C/C++...
Code: [Select]
int len=BASS_ChannelSeconds2Bytes(handle,number_of_seconds_to_check) & ~7;
Not sure what the Delphi equivalent of '~' is, but this'll work too...
Code: [Select]
lengte := BASS_ChannelSeconds2Byte(channel,VUInterval/1000);
lengte := lengte - (lengte mod 8);

Ingolf

  • Posts: 81
Re: VU levels - newbie
« Reply #21 on: 11 May '03 - 19:26 »
Don't know if someone answered already:

When you create something, you need to free it too, to save memory. Otherwise the objects you create would reside in memory.

The objects that you place on a form for example are managed by the Delphi IDE, so no free'ing is needed, but when creating at runtime, you need to free what you create too:

Code: [Select]
var
 L: TStringList;
begin
 L := TStringList.Create;
 try
   L.Text := 'tralala';
 finally
   L.Free;
 end;
end;


Since you call BASS_Init, you need BASS_Free to free everything BASS did.

Sheep

  • Posts: 243
Re: VU levels - newbie
« Reply #22 on: 11 May '03 - 21:57 »
Thank you both!

Ian:
Thanks i shall use it.
Code: [Select]
lengte := lengte - (lengte mod 8);
why 8?

Ingolf:
You are sure about this? Isn't everything free-ed when the local function ends? Otherwise i have to recheck lots of code of my programs :)

Ian @ un4seen

  • Administrator
  • Posts: 26223
Re: VU levels - newbie
« Reply #23 on: 12 May '03 - 17:16 »
Quote
Code: [Select]
lengte := lengte - (lengte mod 8);
why 8?

It's just incase you decide to use 32-bit floating-point... that's 4 bytes per sample, times 2 if stereo, equals 8 :)

VU meter ...

  • Guest
Re: VU levels - newbie
« Reply #24 on: 4 Jun '03 - 18:26 »
Hi! I have problems with this code!! I use it for analize the streams recorded by mic-in...

The program crash! not always in the same point. I can play the recorded sound with the VUmeterREC( ), and sometimes crash at the first time and other times crash after several times to play it.

unsigned __int64 leng has a very high value when the program crash...

Code: [Select]
void SoundManager::VUmeterREC( ) {

     _leftpeak = 0;
     _rightpeak = 0;
     //unsigned __int64 leng = 0;
     unsigned __int64 len = 0;

     len = BASS_ChannelSeconds2Bytes( _recordStream, 1.00 ) & ~7;

     short *buf = new short[ ( int ) len ];

     len = BASS_ChannelGetData( _recordStream, buf, ( unsigned long ) len ) / sizeof( short );

     for ( int a = 0; a < len; a += 2 ) {
   
           if ( abs( buf[ a ] ) > _leftpeak ) _leftpeak = abs( buf[ a ] );
           if ( abs( buf[ a+1 ] ) > _rightpeak ) _rightpeak = abs( buf[ a+1 ] );
     }

     if ( BASS_ChannelGetFlags( _recordStream ) & BASS_SAMPLE_MONO )
   
           _leftpeak = max( _leftpeak, _rightpeak );
           _rightpeak = 0;

     delete[ ] buf;
     buf = NULL;      
     //len = NULL;
     //leng = NULL;
}


But, this code it's OK. I use it for normal streams...

Code: [Select]
void SoundManager::VUmeter( int streamid ) {

     _leftpeak = 0;
     _rightpeak = 0;

     unsigned __int64 len = BASS_ChannelSeconds2Bytes( _musicStream[ streamid ].stream, 1.00 ) & ~7;

     short *buf = new short[ ( int ) len ];

     len = BASS_ChannelGetData( _musicStream[ streamid ].stream, buf, ( unsigned long ) len ) / sizeof( short );
     
     for ( int a = 0; a < len; a += 2 ) {
   
           if ( abs( buf[ a ] ) > _leftpeak ) _leftpeak = abs( buf[ a ] );
           if ( abs( buf[ a+1 ] ) > _rightpeak ) _rightpeak = abs( buf[ a+1 ] );
     }

     if ( BASS_ChannelGetFlags( _musicStream[ streamid ].stream ) & BASS_SAMPLE_MONO )
   
           _leftpeak = max( _leftpeak, _rightpeak );

     delete[ ] buf;
     buf = NULL;
}


Thank You!