Hack of the Whenever I Get Around to It

September 23, 2009

GSquelch – command-line silence filter

Filed under: Uncategorized — Chris Merck @ 9:50 am

I like to read. Quite often however I come across an unfamiliar word, especially while reading in a foreign language. If I am at a desk I will underline the word(s) and come back later with a dictionary to learn the meaning. However, if I’m trying to relax, lying down with a paperback, it is inconvinient to underline or write down words. I would prefer verbal note-taking, but if I just run a recorder, mainly silence is captured. I want the voice-activated recording feature of those 90s personal voice recorders on my PC. But it does not seem like a straight-forward, costless, and free solution is available. Moreover there is some demand, as seen by the five posts and partial solutions on LinuxQuestions.

So, I wrote GSquelch, which allows for the filtering-out of those silent sections automatically at run-time. A ring-buffer is employed to ensure that the dictations are not cut-off and that a lead-in and lead-out are provided to capture soft sounds which often are found at the beginning or end of phrases but are not loud enough to trigger a traditional squelch.

Currently only unsigned 8-bit mono is supported, but I will soon add support for other common formats.

Here is the C++ source code:


// gsquelch
// Christopher Merck - September 2009
//
// 21 Sept 09 - Conceptualized
// 22 Sept 09 - Implementation designed and begun
//              U8 support only (low quality)
//
// TODO: 16-bit support
//
// MOTIVATION:
//  While reading one often wishes to look up unfamiliar words, especially
//   when working in a foreign language. However, writing can be
//   distracting. Verbal note-taking is prefferable, but, if one just runs
//   a recorder, mainly silence is captured. GSquelch allows for the
//   filtering-out of those silent sections automatically at run-time.
//   A ring-buffer is employed to ensure that the dictations are not cut-off
//   and that a lead-in and lead-out are provided to capture soft sounds
//   which often are found at the beginning or end of phrases but are not
//   loud enough to trigger a traditional squelch.
//
// USAGE: 
// 
// arecord | gsquelch > out && cat out > /dev/dsp
// 
// (OR)
//
// arecord | gsquelch | lame -r -s 8 -m m -b 64 --bitwidth 8 - out.mp3
//
//

#include
#include
#include
using namespace std;

class Sample
// a single sample of audio in any format
{
private:

public:
  virtual float GetMagnitude ()
  {
    // from zero to one
  }

};

class U8
// unsigned 8-bit
{
private:
  unsigned char b;

public:
  void Set (const U8 & s)
  {
    b = s.b;
  }

  void Read ()
  {
    cin >> b;
  }

  void Write ()
  {
    cout &lt;<b class>&gt; b;
  }

  void Write ()
  {
    cout &lt;&lt; b;
  }

  const short &amp;Get ()
  {
    return b;
  }

  float GetMagnitude ()
  {
    return abs (b / 32768.);
  }

  void Zero ()
  {
    b = 0;
  }
};



template  class FIFO
// a ring buffer
{
private:
  T * buf;
  T *ptr;

  // number of samples and therefore squelch time-constant
  int length;

  // internal count of number of high samples
  int high_count;

public:

  // first pass threshold - the magnitude above which
  //  is concidered high
  float thresh1;

  // second pass threshold - percentage of samples which
  //  must be high for the buffer to be concidered high
  float thresh2;

  FIFO (const int length_ = 8000)
  {
    this-&gt;length = length_;
    thresh1 = .10;
    thresh2 = .05;

    ptr = buf = (T *) malloc (length * sizeof (T));

    if (buf == 0)
      {
	cerr &lt;&lt; &quot;ERROR: Insufficient memory&quot; &lt;&lt; endl;
	exit (-1);
      }

    high_count = 0;

    for (int i = 0; i  thresh2)
      return true;
    return false;
  }

  bool High (T a)
    // check high/low status of a sample
  {
    if (a.GetMagnitude () &gt; thresh1)
      return true;
    return false;
  }

  T Push (const T &amp; pushee)
    // push an element on the front and return (pop)
    //  the one from the back
  {

    if (High (*ptr))
      high_count--;

    T popee = *ptr;

    if (High (pushee))
      high_count++;

    *ptr = pushee;

    ptr++;

    if (ptr &gt; buf + length * sizeof (T))
      ptr = buf;

    return popee;
  }

};



int
main ()
{

  // stdin --&gt; squelch filter --&gt; stdout

  FIFO  *ring = new FIFO  ();
  U8 *s = new U8 ();

  //FIFO * ring = new FIFO(44100);
  //S16_LE * s = new S16_LE();

  long timer = 0;

  while (true)
    {
      s-&gt;Read ();
      s-&gt;Set (ring-&gt;Push (*s));

      if (ring-&gt;High ())
	{
	  timer = ring-&gt;get_length () / 2;
	}
      else
	{
	  timer--;
	  if (timer  0)
	s-&gt;Write ();

      fflush (stdout);
    }

  return 0;

}

About these ads

3 Comments »

  1. Do you have to source file yet ? It does not have include headers and & are replaced by &amp …

    Comment by Mahmudul Hasan — June 5, 2011 @ 2:21 am | Reply

    • Hey sorry about that! I’ll see if I can find the source once I get home in a few days. I’m actually working on a related real-time audio project so stay tuned for that!

      Comment by navaburo — June 5, 2011 @ 5:06 am | Reply

      • :) … Thanks

        Comment by Mahmudul Hasan — June 5, 2011 @ 8:10 pm


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

The Rubric Theme. Create a free website or blog at WordPress.com.

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: