Convert frame parsing and chunking to generators

Use generators to split wave data into chunks and parse it into
sample values.

Since we're now iterating over the (potentially infinite) generator
of wave data chunks with for, EOFException and KeyboardInterrupt
are no longer our only exit paths for remove_silences(): The end of
iteration will terminate the for loop, so we need to move the
cleanup code from the except block into a finally block.

Change variable names `_max` and `_min` to `max_` and `min_` per
PEP-8's advice on names which conflict with builtins; leading
underscore implies they're "private" or "internal" variables.
Perf fix: Move metadata read/calc outside loops

Move reading of wave metadata and associated calculations (e.g.
figuring out the signedness of the input wave data) outside the
main read loops.

chunked_samples() is now the only reader function which accepts
an actual Wave_reader instance for its metadata; since it's called
only once per remove_silences() run and has direct access to the
lower level processing, we read/compute the metadata here and
pass it explicitly to the lower level functions.

Note that since frame_to_sample() throws away all but the first
channel's worth of data, it does not yet accept nchannels as an
argument. However, if it ever were to, it'd likely make sense to
change its signature.

These changes significantly speed up processing, reversing some
regressions from the latest refactor – in particular, the signedness
computation function introduced a few commits ago was quite
expensive and was inside the inner inner read loop.
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.
Merge pull request #3 from casebeer/generator-tagging-and-segmentation

Generator tagging and segmentation