Write directly to output wave file

Eliminate the use of the cStringIO buffer in favor of writing
directly to the output wave file. This should reduce our memory
use – in particular, it sets up the possibility of processing
live input data indefinitely, which would quickly consume too
much memory under the previous buffer-the-entire-output pattern.

Given that we're processing incoming data in chunks of one second,
there's already some reasonable write buffer built in; if we
later reduce the chunk size, it should be fairly simply to
reintroduce some multi-chunk buffering that doesn't require
holding the entire output in memory.

This also permits listening to the output wave file from another
process while SNARP is still processing the input (e.g. to monitor
a live audio stream).

Remove the now-unused BufferedClassFile class.
Move silence detection to chunk tagger generator

Instead of detecting silent chunks in the remove_silences
loop, wrap the chunk generator in a chunk tagging generator,
allowing us to iterate over the pre-tagged data.
Move pre-roll/post-roll code to generator

Create generator which only returns audio chunks which belong to
a bona fide non-silent section. Preserves the pre-roll/post-roll
logic, meaning that some "shoulder" silent chunks are still parts
of valid "non-silent" audio "segments."
Refactor segmentation logic

Refactor the segmentation logic to make it clear when the various
output actions occur. There are four states:

1. State change + newly silent
2. State change + newly audible
3. No state change + currently silent
4. No state change + currently audible

In states (1) and (2), we've just changed state. Regardless of
whether we're newly silent or newly audible, we must emit the current
chunk: In the one case, as a post roll buffer, and in the other,
following any chunks we've buffered for the pre-roll, as the first
"actual" chunk of audible data.

In state (3), we simply push the current chunk into the pre-roll
ring buffer, which will automatically eject old values to keep the
pre-roll buffer size fixed (we're beginning with a pre-roll buffer
size of one chunk; this will change later as we reduce the chunk
size).

In state (4), we're in the midst of an audible segment, and have an
audible chunk, so we just emit it and move on.
Tag chunks as part of audible or silent segment

Switch processing a bit: Rather than yielding every chunk that's part of
an audible segment (i.e. pre-roll, audible, and post-roll chunks), yield
every chunk and merely tag it as part of a silent (True) or audible
(False) segment.

This permits us to separate the segmentation logic from the segmentation
action. For instance, the audible_chunks() generator, the "segmentation
action" function which actually suppresses the silent chunks and emits
the audible chunks, is now only a few lines and contains no pre/post
roll logic.

Additionally, by isolating the segmentation decision logic in the segment
tagger generator, we can later expand it with more complex rules without
worrying about the actions we're taking based on the segmentation. In
particular, this should make implementing things like hystersis and
lookahead easier.

Future segmentation actions could include doing more than just suppressing
silent segment chunks: For instance, saving them to a different, all-
silence file; emitting pure generated silence frames in their place, thus
implementing a "squelch;" or emitting both silent and non-silent frames
to out output, while emitting only non-silent frames to another output,
effectively allowing a bypass output, e.g. for live monitoring.
Implement chunk/segment tools using itertools

Reimplement audible_chunks using itertools functions.

Implement a "segmenter" generator which returns a generator
returning one pair per continuous segment. Each pair contains
a boolean flag indicating whether the segment is a silent (True)
or audible (False) segment, and a generator for that segment's
chunks.

Implement an "audible_segments" generator, which contains generators
for the chunks of each non-silent segment. This will be useful,
for example, in writing out a separate file per audible segment,
which can be done merely by iterating over audible_segments(...)
and dumping the contents of each yielded generator to a new file.
Implement bypass audio option

Add CLI option and corresponding remove_silences functionality
to write out to a bypass audio file – an output file which will
receive 100% of samples read from the input file.

This allows for archiving or monitoring all of a live input while
also producing a shortened version with silent periods removed.
Hysteresis and shorter chunks.

Implement hysteresis for silence detection: While we still immediately
switch to an audible segment when we see an audible chunk, we wait to
see HYSTERESIS_CHUNKS of silent data before we end an audible chunk.

This is independent of the pre-roll and post-roll lengths, which now
both come from one single chunk buffer, filled with silent chunks,
which is portioned between the previous and new segments as needed to
meed the PRE_ROLL_CHUNKS and POST_ROLL_CHUNKS settings.

Switch chunk length specification from seconds to milliseconds. We've
also got new timing global variables for configuration: PRE_ROLL_MS,
POST_ROLL_MS, and HYSTERESIS_MS. These values are dividied by CHUNK_MS
to compute the *_CHUNKS variables discussed above. As such, they should
be multiples of CHUNK_MS.

Also, fix a bug where the buffer/pre_roll_buffer would be discarded
once the end of file was hit. Since we were raising EOFError rather
propagating the normal StopITeration(), we weren't hitting any post-
for-loop cleanup code – not that there was any. Add said code and
switch to StopIteration to fix.
Match timings to old, pre-hysteresis behavior

Set chunk/hysteresis/pre-/post- timings so the behavior matches
the original. Passes original tests.
Peak to peak range and IQR for silence detection

Rather than specifying absolute sample limits for both the max
and min sample values in a given chunk, specify the maximum
difference – that is, the maximum allowable max_ - min_.

Using this delta helps us avoid problems with DC offset – if a signal
is not centered on the sample range midpoint, we'll still be able
to use the same silence limits, since we're only looking at how much
of the sample range the signal is covering.

For additional robustness, we also have a delta parameter for the
IRQ: The difference between the first and third quartile values
for the sample values in the given chunk.

This helps avoid false results from sporadic noise which might lead
to high max/min values but is less likely to falsely boost the
Q1/Q3 values – since if it did, it would mean a significant number
of samples in the chunk had large absolute value.

Note that since we can expect most audio signals to be fairly
symmetric across the zero point (ignoring any DC offset), the IQR
is effectively measuring the 50th percentile sample absolute values.

At the moment, both the peak and IQR ranges are specified as sample
values relative to a 16 bit sample width, then normalized relative
to the actual sample width at runtime.

Finally, to make this work, we need real sample values from
frame_to_sample: The padding to four byte values has to go.
WIP add stats tracking for input level deltas
Tune peak and IQR silence limits

Add some logging and reasonable presets/defaults for the new peak
and IQR delta silence limit settings.

We still don't have CLI for overriding these.
Comment out noisiest debug logs.
Update tests to use shorter chunk sizes

Update tests to use expected file lengths for the new, shorter
chunk sizes.

Remove hardcoded SILENCE_MIN and SILENCE_MAX since we're now using
deltas.
dbFS for silence limit specification

Change the silence limit specifications to decibels relative to
full scale (dbFS). This means, for example, that 0 dB is a full
scale wave signal, maxing out the sample width, and -6 dB is a
signal that only uses 50% of the sample width.

For reference, some of preset limits we're assuming are that
-15 dbFS peak to peak and -24 dbFS 50th percentile should pick up
conversational speech, while -27 dbFS peak/-36 dbFS 50th percentile
will pick up whispered speech.

Note that while we're stating dbFS for the level spec, we're actually
not measuring whether the signal comes that close to clipping.
Instead, we're measuring whether the peak-to-peak or quartile 1
to quartile 3 distance is greater than the relevant dbFS limit.

This makes us more robust against DC offsets in the input source
without requiring us to detect or fix any possible offsets.

Convert the default settings (whisper, quiet, and conversational)
to dbFS.

Provide a function to convert dbFS to sample value given a sample
width in bits.

Use this conversion function to convert the desired dbFS value to
sample values before passing them to the silence tagger generator.

Remove SILENCE_MIN and SILENCE_MAX CLI settings.

Add peak silence and IQR (i.e. 50th percentile) silence limit CLI
options, specified in dbFS.

Add CLI options to trigger the conversational, quiet, and whipser
silence level presets.
Clean up logging

Fix up logging to use Python 2.5 compatible formatters and
remove print statements.
Record statistics to file

Offer CLI option to record a "statistics" file containing dBFS values
for the peak and IQR ranges measured for silence detection. This file
can be analyzed to find the best silence limits to use for the
audio in question.
Add analysis script to show histograms of stats

Use pandas and matplotlib to raw histograms of the sample ranges
recorded in a stats file by SNARP. Useful for figuring out the best
silence dBFS limits to set.

N.B. The requirements file is probably over-large at the moment and
     should be tightened up.