Author Topic: Detect silence?  (Read 18213 times)

shadow2xp

  • Posts: 20
Detect silence?
« on: 25 Aug '02 - 10:35 »
I'm trying to detect the silence at the beginning of a song... Any ideas? ???

Ryzer

  • Posts: 73
Re: Detect silence?
« Reply #1 on: 25 Aug '02 - 11:31 »
levelmeters...

shadow2xp

  • Posts: 20
Re: Detect silence?
« Reply #2 on: 25 Aug '02 - 17:52 »
That is brilliant, but wouldn't that only work when the file is playing?

Keyman

  • Posts: 167
Re: Detect silence?
« Reply #3 on: 26 Aug '02 - 13:00 »

Quote

That is brilliant, but wouldn't that only work when the file is playing?

Just as briiliant. If it ain't playing you can be pretty sure that it is silent as well...  ;D
Somehow I do get the feeling that this isn't the answer you were looking for. What exactly do you want to do?
Getlevels & DSP's (In a DSP you can check if all values in the buffer are zero; if so then it's pretty silent) indeed work only when the file is beeing played.
Maybe you can overcome this by opening a seperate decoding-only channel to run in the background?!

Have Fun Now!
Cris

shadow2xp

  • Posts: 20
Re: Detect silence?
« Reply #4 on: 27 Aug '02 - 17:32 »
I want to auto cue in my program. So that would be detecting a silence before a song starts and skipping that silence.

Opening a seperate decoding channel could work.

What exactly does DSP mean and how would i go about setting up a decode only channel?

Keyman

  • Posts: 167
Re: Detect silence?
« Reply #5 on: 27 Aug '02 - 18:54 »
Quote
Opening a seperate decoding channel could work.

What exactly does DSP mean and how would i go about setting up a decode only channel?

DSP = Digital Sound (or Signal) Processing.
This means that you can process the sound in it's digital representation (bits & bytes). As opposed to Analog Sound Processing.

You can create a decode-ony channel with
BASS_StreamCreateFile() in conjunction of the BASS_STREAM_DECODE flag.
Please refer to the helpfile for more info...

Mind that you cannot play a decode-only channel.

Maybe an easier alternative is opening the file as you would normally do and set the volume to zero. Then you can play it and use getlevels as well.

Have Fun Now!
Cris


« Last Edit: 27 Aug '02 - 18:56 by keyman »

Ian @ un4seen

  • Administrator
  • Posts: 20396
Re: Detect silence?
« Reply #6 on: 28 Aug '02 - 12:57 »
This function will detect the silence at the start of a file stream...
Code: [Select]
DWORD GetSilenceLength(char *file)
{
     BYTE buf[10000];
     DWORD count=0;
     HSTREAM chan=BASS_StreamCreateFile(FALSE,file,0,0,BASS_STREAM_DECODE); // create decoding channel
     while (BASS_ChannelIsActive(chan)) {
           int a,b=BASS_ChannelGetData(chan,buf,10000); // decode some data
           for (a=0;a<b && !buf[a];a++) ; // count silent bytes
           count+=a; // add number of silent bytes
           if (a<b) break; // sound has begun!
     }
     BASS_StreamFree(chan);
     return count;
}


Use it something like this...
Code: [Select]
HSTREAM stream=BASS_StreamCreateFile(FALSE,file,0,0,0); // create stream
DWORD silence=GetSilenceLength(file); // get silence
BASS_ChannelSetPosition(stream,silence); // seek past it
BASS_StreamPlay(stream); // start playing

shadow2xp

  • Posts: 20
Re: Detect silence?
« Reply #7 on: 28 Aug '02 - 19:11 »
sweet :D

However, I am a VB user and do not know much c++ (learning). I can't convert all of the code you put there.

Code: [Select]

DWORD GetSilenceLength(char *file)
{
    BYTE buf[10000];
    DWORD count=0;
    HSTREAM chan=BASS_StreamCreateFile(FALSE,file,0,0,BASS_STREAM_DECODE); // create decoding channel
    while (BASS_ChannelIsActive(chan)) {
          int a,b=BASS_ChannelGetData(chan,buf,10000); // decode some data
          for (a=0;a<b && !buf[a];a++) ; // count silent bytes
          count+=a; // add number of silent bytes
          if (a<b) break; // sound has begun!
    }
    BASS_StreamFree(chan);
    return count;
}


(: JOBnik! :)

  • Posts: 1065
Re: Detect silence?
« Reply #8 on: 28 Aug '02 - 20:18 »
Hi ;D

That's my quick [not tested] translation:
Code: [Select]

public function GetSilenceLength(byval file as string) as long
    dim buf(10000) as byte
    dim count as long
    dim chan as long

    chan=BASS_StreamCreateFile(BASSFALSE,file,0,0,BASS_STREAM_DECODE) ' create decoding channel
    while (BASS_ChannelIsActive(chan))
          dim a as long, b as long
          b=BASS_ChannelGetData(chan,buf(0),10000) ' decode some data
          do 'count silent bytes
              count=count+a   'add number of silent bytes
              a=a+1
          loop while ((a<b) AND (buf(a)=0))
          if(a<b) exit while       'sound has begun!
     wend
    call BASS_StreamFree(chan)
    GetSilenceLength =count
end function

* Tell me if it doesn't work...

Have fun!

8) JOBnik! 8)
« Last Edit: 28 Aug '02 - 20:21 by JOBnik »

Keyman

  • Posts: 167
Re: Detect silence?
« Reply #9 on: 29 Aug '02 - 03:04 »
I don't think that's right JobNik, the Count gets increased to much...

It now says:
 count = count+1   ' count = 1
 count = count+2   ' count = 3
 count = count+3   ' count = 6
 etc.

With a little change it should work fine:
Code: [Select]

 while (BASS_ChannelIsActive(chan))
        dim a as long, b as long
        b=BASS_ChannelGetData(chan,buf(0),10000) ' decode some data
        a = 0
        do while ((a<b) AND (buf(a)=0))
            a=a+1
        loop          
        count=count+a   'add number of silent bytes
        if(a<b) exit while       'sound has begun!
    wend


BTW, I have assumed that buf is zero based...

Have Fun Now!
Cris

(: JOBnik! :)

  • Posts: 1065
Re: Detect silence?
« Reply #10 on: 29 Aug '02 - 03:13 »
Hi ;D

Yep, you're absolutely right :)
I didn't notice that sign ";" in the end of: for(..) ;
(as I said [not tested] quick translation :))

Thanx!

Have fun!

8) JOBnik! 8)
« Last Edit: 30 Aug '02 - 02:10 by JOBnik »

albertos

  • Posts: 29
Re: Detect silence?
« Reply #11 on: 29 Aug '02 - 11:56 »
A small change and will work....

Public Function GetSilenceLength(ByVal file As String) As Long
   Dim buf(10000) As Byte
   Dim count As Long
   Dim chan As Long

   chan = BASS_StreamCreateFile(BASSFALSE, file, 0, 0, BASS_STREAM_DECODE)  ' create decoding channel
   Do While (BASS_ChannelIsActive(chan))
       Dim a As Long, b As Long
       b = BASS_ChannelGetData(chan, buf(0), 10000) ' decode some data
       a = 0
       Do While ((a < b) And (buf(a) = 0))
           a = a + 1
       Loop
       count = count + a 'add number of silent bytes
       If (a < b) Then Exit Do    'sound has begun!
   Loop
   Call BASS_StreamFree(chan)
   GetSilenceLength = count
End Function

Ps. i was wondering... if u have a stream that has scratches in the beggining... how to detect the real music start not the first sound to appear... kinda like a sensitivity thing.

MB_SOFT

  • Posts: 260
Re: Detect silence?
« Reply #12 on: 29 Aug '02 - 14:01 »
Yes, how to optimize this function checking for average audio level in a second, instead to detect only the first incoming peak?

I tested this function but is not effective for real usage....it detect the first waveform change...not the real sound....

At the moment it's still better to play the stream at 0 volume and check for ChannelGetLevel.....

Maurizio

albertos

  • Posts: 29
Re: Detect silence?
« Reply #13 on: 29 Aug '02 - 15:23 »
another question also...
how to determine silence at the end of stream?

Keyman

  • Posts: 167
Re: Detect silence?
« Reply #14 on: 29 Aug '02 - 16:31 »
Quote

At the moment it's still better to play the stream at 0 volume and check for ChannelGetLevel.....
I think that was my whole point some reply's agoo  :P

To determine silence at the end of the stream (you're probably playing it and want to know when it becomes silent) the easiest way would be to check the ChannelGetLevels again.
You can create a callback that notifies you say 10 seconds before the end of file. In the callback you start a timer that monitors the output level.

To make sure it's ending en not only one dip in the output level you could use something like this (pseudo code)

Code: [Select]

CallBackEvent (10 seconds before EOF)
  Init DelayCounter to 0
  Start TimerX, interval 20 ms or so
End of CallBackEvent

TimerX-TimerEvent
  GetChannelLevels
  if both LeftLevel & RightLevel < 5 then  ' **      
     increase DelayCounter by 1
     if DelayCounter > 10 then             ' **
        Sound is almost silent...
        Start another file or so
     end if
  else
     DelayCounter = 0
  end if
end of TimerXEvent


** You can of course change these values to your needs.

With an interval of 20ms & a MaxDelay of 10 it takes MINIMAL 200 ms before you know that it's silent... (silent is defined as  levels < 5 in the example)

Have Fun Now!
Cris

ps. Please tell me you VB guys (pure curiousity), doesn't "while-wend" no longer exist in VB or can't you just exit a while-wend loop?
« Last Edit: 29 Aug '02 - 16:43 by keyman »

Ian @ un4seen

  • Administrator
  • Posts: 20396
Re: Detect silence?
« Reply #15 on: 29 Aug '02 - 20:24 »
Here's an update with a "threshold" setting, and ending silence detection too...
Code: [Select]
void GetSilenceLength(char *file, int threshold, DWORD *start, DWORD *end)
{
     short buf[50000];
     DWORD count=0,pos;
     HSTREAM chan=BASS_StreamCreateFile(FALSE,file,0,0,BASS_STREAM_DECODE); // create decoding channel
     if (!chan) return;

     while (BASS_ChannelIsActive(chan)) {
           int a,b=BASS_ChannelGetData(chan,buf,20000); // decode some data
           b/=2; // bytes -> samples
           for (a=0;a<b && abs(buf[a])<=threshold;a++) ; // count silent samples
           count+=a*2; // add number of silent bytes
           if (a<b) { // sound has begun!
                 // move back to a quieter sample (to avoid "click")
                 for (;a && abs(buf[a])>threshold/4;a--,count-=2) ;
                 break;
           }
     }
     *start=count;

     pos=BASS_StreamGetLength(chan);
     while (pos>count) {
           int a,b;
           pos=pos<100000?0:pos-100000; // step back a bit
           BASS_ChannelSetPosition(chan,pos);
           b=BASS_ChannelGetData(chan,buf,100000); // decode some data
           b/=2; // bytes -> samples
           for (a=b;a>0 && abs(buf[a-1])<=threshold/2;a--) ; // count silent samples
           if (a>0) { // sound has begun!
                 count=pos+a*2; // silence begins here
                 break;
           }
     }
     *end=count;

     BASS_StreamFree(chan);
}

Example...
Code: [Select]
DWORD start,end;
GetSilenceLength(file,500,&start,&end);


The positions could be QWORDs (for massive stream support :)), but I've used DWORD for the benefit of VB users. The ending silence detection will not be exact for MP3 files without using the BASS_MP3_SETPOS flag when creating the decoding channel, but that's obviously slower too.

shadow2xp

  • Posts: 20
Re: Detect silence?
« Reply #16 on: 29 Aug '02 - 23:02 »
Thank you all...

Oh and yes VB still has the "while-wend" loop.

Keyman

  • Posts: 167
Re: Detect silence?
« Reply #17 on: 30 Aug '02 - 00:33 »
Very cool solution Ian !!   :)

Cris

albertos

  • Posts: 29
Re: Detect silence?
« Reply #18 on: 30 Aug '02 - 01:20 »
@Shadow2xp:

yes vb still has while-wend loop structure... but u can't break the loop.
Only for-next and do while-loop can exit loop with
exit for and exit do commands...
that's what i know so far...

shadow2xp

  • Posts: 20
Re: Detect silence?
« Reply #19 on: 30 Aug '02 - 04:19 »
A "while-wend" loop can be broken with "Do Events".

Also can I get that code above translated into VB?
« Last Edit: 30 Aug '02 - 04:20 by shadow2xp »

(: JOBnik! :)

  • Posts: 1065
Re: Detect silence?
« Reply #20 on: 31 Aug '02 - 14:52 »
Hi ;D

I think that will work? :)
Code: [Select]
public sub GetSilenceLength(byval file as string, byval threshold as long, byref startp as long, byref endp as long)
     dim buf(50000) as integer
     dim count as long, pos as long
     dim chan as long

     count=0

     chan=BASS_StreamCreateFile(BASSFALSE,file,0,0,BASS_STREAM_DECODE) 'create decoding channel

     if (chan=0) then exit sub

     do
           dim a as long, b as long
           b=BASS_ChannelGetData(chan,buf(0),20000) 'decode some data
           b=b/2 'bytes -> samples
           a=0
           do      'count silent samples
                 a=a+1
           loop while((a<b) and (abs(buf(a))<=threshold))
           count=count+(a*2)
           if(a<b) then 'sound has bagun
                 'move back to a quieter sample (to avoid "click")
                 do
                       a=a-1
                       count=count-2
                 loop while((a) and (abs(buf(a))>threshold/4))
                 exit if      'or DO ???
           end if
     loop while (BASS_ChannelIsActive(chan))

     startp=count

     pos=BASS_StreamGetLength(chan)
     do
           dim c as long, d as long
           pos=iif(pos<100000,0,pos-100000) 'step back a bit
           BASS_ChannelSetPosition(chan,pos)
           d=BASS_ChannelGetData(chan,buf(0),100000) ' decode some data
           d=d/2 'bytes -> samples
           c=d
           do
                 c=c-1      'count silent samples
           loop while((c>0) and abs(buf(c-1))<=threshold/2))
           if(c>0) then      'sound has begun
                 count=pos+c*2
                 exit if ' or DO???
           end if
     loop while(pos>count)            
     endp=count

     BASS_StreamFree(chan)
end sub


Example...
Code: [Select]
dim startp as long,endp as long
call GetSilenceLength(file,500,startp,endp)


Have fun!

8) JOBnik! 8)
« Last Edit: 31 Aug '02 - 14:53 by JOBnik »

Hendrik Knaepen

  • Guest
Re: Detect silence?
« Reply #21 on: 31 Aug '02 - 21:10 »
Corrected code:

Code: [Select]

Public Sub GetSilenceLength(ByVal file As String, ByVal threshold As Long, ByRef startp As Long, ByRef endp As Long)
    Dim buf(50000) As Integer
    Dim count As Long, pos As Long
    Dim chan As Long
    Dim a As Long, b As Long
    Dim c As Long, d As Long
    count = 0

    chan = BASS_StreamCreateFile(BASSFALSE, file, 0, 0, BASS_STREAM_DECODE) 'create decoding channel

    If (chan = 0) Then Exit Sub

    Do
          b = BASS_ChannelGetData(chan, buf(0), 20000) 'decode some data
          b = b / 2 'bytes -> samples
          a = 0
          Do      'count silent samples
                a = a + 1
          Loop While ((a < b) And (Abs(buf(a)) <= threshold))
          count = count + (a * 2)
          If (a < b) Then 'sound has bagun
                'move back to a quieter sample (to avoid "click")
                Do
                      a = a - 1
                      count = count - 2
                Loop While ((a) And (Abs(buf(a)) > threshold / 4))
                Exit Do
          End If
    Loop While (BASS_ChannelIsActive(chan))

    startp = count

    pos = BASS_StreamGetLength(chan)
    Do
          pos = IIf(pos < 100000, 0, pos - 100000) 'step back a bit
          BASS_ChannelSetPosition chan, pos
          d = BASS_ChannelGetData(chan, buf(0), 100000) ' decode some data
          d = d / 2 'bytes -> samples
          c = d
          Do
                c = c - 1  'count silent samples
          Loop While (c > 0) And (Abs(buf(c - 1)) <= threshold / 2)
          If (c > 0) Then   'sound has begun
                count = pos + c * 2
                Exit Do
          End If
    Loop While (pos > count)
    endp = count
    BASS_StreamFree (chan)
End Sub



P.S.: my e-mail was rot-13'ed, don't like spam :-)

(: JOBnik! :)

  • Posts: 1065
Re: Detect silence?
« Reply #22 on: 31 Aug '02 - 23:43 »
Hi ;D

Corrected what?

I've already wrote there: IF or DO
So if - IF - wouldn't work then the user would have to put there - DO - instead :)

Have fun!

8) JOBnik! 8)

Hendrik Knaepen

  • Guest
Re: Detect silence?
« Reply #23 on: 1 Sep '02 - 00:51 »

Quote

Corrected what?


Your dim's where inside the loop, they should be before the loop, because else VB will try to create a variable that allready existed. ;-)

albertos

  • Posts: 29
Re: Detect silence?
« Reply #24 on: 2 Sep '02 - 13:20 »
a small correction somewhere and it work...

Code: [Select]
Public Sub GetSilenceLength(ByVal file As String, ByVal threshold As Long, ByRef startp As Long, ByRef endp As Long)
   Dim buf(50000) As Integer
   Dim count As Long, pos As Long
   Dim chan As Long
   Dim a As Long, b As Long
   Dim c As Long, d As Long
   count = 0

   chan = BASS_StreamCreateFile(BASSFALSE, file, 0, 0, BASS_STREAM_DECODE) 'create decoding channel

   If (chan = 0) Then Exit Sub

   Do
         b = BASS_ChannelGetData(chan, buf(0), 20000) 'decode some data
         b = b / 2 'bytes -> samples
         a = 0
         Do      'count silent samples
               a = a + 1
         Loop While ((a < b) And (Abs(buf(a)) <= threshold))
         count = count + (a * 2)
         If (a < b) Then 'sound has bagun
               'move back to a quieter sample (to avoid "click")
               Do
                     a = a - 1
                     count = count - 2
               Loop While ((a) And (Abs(buf(a)) > threshold / 4))
               Exit Do
         End If
   Loop While (BASS_ChannelIsActive(chan))

   startp = count

   pos = BASS_StreamGetLength(chan)
   Do
         pos = IIf(pos < 100000, 0, pos - 100000) 'step back a bit
         BASS_ChannelSetPosition chan, pos
         d = BASS_ChannelGetData(chan, buf(0), 100000) ' decode some data
         d = d / 2 'bytes -> samples
         c = d
         Do
               c = c - 1  'count silent samples
         Loop While (c > 0) And (Abs(buf(c)) <= threshold / 2) 'Here is the correction
         If (c > 0) Then   'sound has begun
               count = pos + c * 2
               Exit Do
         End If
   Loop While (pos > count)
   endp = count
   BASS_StreamFree (chan)
End Sub


re correction is in bold, replace buf(c-1) with buf(c)
« Last Edit: 2 Sep '02 - 13:21 by albertos »